כשכותבים מפעיל בדיקות, חשוב להביא בחשבון את יכולת ההתאמה לעומס. לשאלה בצ'אט "אם הרצת המבחן שלי הייתה צריכה להריץ 200 אלף מקרי בדיקה" כמה זמן זה ייקח?
פיצול הוא אחת מהתשובות הזמינות באיחוד שירותי הסחר. לשם כך צריך לפצל את כל הבדיקות שנדרשות למפעיל לכמה קטעים שאפשר לבצע במקביל.
בדף הזה נסביר איך להפוך את ה-runner לניתן לחלוקה לחלקים ב-Tradefed.
ממשק להטמעה
הממשק החשוב ביותר שצריך להטמיע כדי שייחשב לפיצול
TF הוא
IShardableTest,
שמכיל שתי שיטות: split(int numShard)
ו-split()
.
אם החלוקה תלויה במספר הפיצולים שביקשתם,
צריך להטמיע את split(int numShard)
. אחרת, מטמיעים את split()
.
כשמפעילים פקודת בדיקה של TF עם פרמטרים של חלוקה לפלחים --shard-count
ו---shard-index
, TF מבצע חזרה על כל IRemoteTest
כדי לחפש אלה שמטמיעים את IShardableTest
. אם הוא ימצא, הוא יפעיל את split
כדי לקבל אובייקט IRemoteTest
חדש להרצת קבוצת משנה של מקרי בדיקה לשבר ספציפי.
מה חשוב לדעת על ההטמעה המפוצלת?
- הריצה יכולה לפצל תנאים מסוימים בלבד. במקרה הזה, צריך להחזיר
null
כשלא פיצול. - נסו לפצל כמה שאפשר: פצלו את ה-runner ליחידות ביצוע שמתאימות לו. זה תלוי בגורם שמריץ את הבדיקה. עבור דוגמה: בדיקת מארח הם מחולקים ברמת הכיתה, כל כיתה למבחן מחולקת לפיצול נפרד.
- אם הגיוני, הוסיפו כמה אפשרויות כדי לשלוט קצת בפיצול.
מוצרים לדוגמה:
AndroidJUnitTest
מכיל
ajur-max-shard
כדי לציין את מספר הפיצולים המקסימלי שהוא יכול לפצל אותן, בלי קשר למספר המבוקש.
הטמעה מפורטת לדוגמה
הנה קטע קוד לדוגמה עם הטמעה של IShardableTest
. הקוד המלא זמין בכתובת (https://android.googlesource.com/platform/tools/tradefederation/+/refs/heads/main/test_framework/com/android/tradefed/testtype/InstalledInstrumentationsTest.java)
/**
* Runs all instrumentation found on current device.
*/
@OptionClass(alias = "installed-instrumentation")
public class InstalledInstrumentationsTest
implements IDeviceTest, IResumableTest, IShardableTest {
...
/** {@inheritDoc} */
@Override
public Collection<IRemoteTest> split(int shardCountHint) {
if (shardCountHint > 1) {
Collection<IRemoteTest> shards = new ArrayList<>(shardCountHint);
for (int index = 0; index < shardCountHint; index++) {
shards.add(getTestShard(shardCountHint, index));
}
return shards;
}
// Nothing to shard
return null;
}
private IRemoteTest getTestShard(int shardCount, int shardIndex) {
InstalledInstrumentationsTest shard = new InstalledInstrumentationsTest();
try {
OptionCopier.copyOptions(this, shard);
} catch (ConfigurationException e) {
CLog.e("failed to copy instrumentation options: %s", e.getMessage());
}
shard.mShardIndex = shardIndex;
shard.mTotalShards = shardCount;
return shard;
}
...
}
בדוגמה הזו פשוט נוצרת מכונה חדשה של עצמה ומגדירים לה פרמטרים של פלחים. עם זאת, הלוגיקה של הפיצול יכולה להיות שונה לגמרי בין בדיקה לבדיקה, כל עוד היא גורמת ליצירת קבוצות משנה מקיפות באופן קולקטיבי.
עצמאות
פיצולים צריכים להיות בלתי תלויים! לשני שברי המידע שנוצרו על ידי ההטמעה של split
ב-runner לא צריכות להיות יחסי תלות זה בזה, והם לא צריכים לשתף משאבים.
פיצול פיצולים צריך להיות דטרמיניסטי! זה חובה גם כי באותו מצב, שיטת split
תמיד צריכה להחזיר את אותה רשימה של פלחים באותו סדר.
הערה: מאחר שכל שריד יכול לפעול במכונות TF שונות, חשוב לוודא שהלוגיקה של split
מניבה קבוצות משנה שהן בלתי ניתנות להפרדה ומקיפות באופן דטרמיניסטי.
חלוקה של בדיקה למקטעים באופן מקומי
כדי לפצל בדיקה ב-TF מקומי, אפשר פשוט להוסיף את האפשרות --shard-count
כדי
בשורת הפקודה.
tf >run host --class com.android.tradefed.UnitTests --shard-count 3
לאחר מכן, TF ייצור פקודות באופן אוטומטי לכל שריד ויפעיל אותן.
tf >l i
Command Id Exec Time Device State
3 0m:03 [null-device-2] running stub on build 0 (shard 1 of 3)
3 0m:03 [null-device-1] running stub on build 0 (shard 0 of 3)
3 0m:03 [null-device-3] running stub on build 0 (shard 2 of 3)
צבירת תוצאות בדיקה
מכיוון ש-TF לא מבצע צבירת תוצאות בדיקה עבור הפעלות מפוצלות, צריך לוודא ששירות הדיווח שלכם תומך בכך.