כשכותבים מפעיל בדיקות, חשוב להביא בחשבון את יכולת ההתאמה לעומס. כדאי לשאול את עצמכם: "אם הכלי להרצת הבדיקות שלי יצטרך להריץ 200,000 תרחישי בדיקה, כמה זמן זה ייקח?"
חלוקה למקטעים היא אחת מהתשובות הזמינות ב-Trade Federation. לשם כך צריך לפצל את כל הבדיקות שנדרשות למפעיל לכמה קטעים שאפשר לבצע במקביל.
בדף הזה נסביר איך להפוך את ה-runner לניתן לחלוקה לחלקים ב-Tradefed.
ממשק להטמעה
הממשק החשוב ביותר שצריך להטמיע כדי ש-TF יחשב את האובייקט כניתן לחלוקה הוא IShardableTest, שמכיל שתי שיטות: split(int numShard)
ו-split()
.
אם חלוקת המשנה תלויה במספר פלחים המבוקשים, צריך להטמיע את split(int numShard)
. אחרת, צריך להטמיע את split()
.
כשמפעילים פקודת בדיקה של TF עם פרמטרים של חלוקה לפלחים --shard-count
ו---shard-index
, TF מבצע חזרה על כל IRemoteTest
כדי לחפש אלה שמטמיעים את IShardableTest
. אם הוא ימצא, הוא יפעיל את split
כדי לקבל אובייקט IRemoteTest
חדש להרצת קבוצת משנה של מקרי בדיקה לשבר ספציפי.
מה חשוב לדעת על ההטמעה של חלוקת המודעות?
- ה-runner יכול לפצל לפי תנאים מסוימים בלבד. במקרה כזה, צריך להחזיר את הערך
null
כשלא בוצע פיצול. - כדאי לחלק את הריצה כמה שיותר הגיוני: לחלק את הריצה ליחידת ביצוע שמתאימה לה. זה תלוי בגורם שמריץ את הבדיקה. לדוגמה: HostTest מחולק למקטעים ברמת הכיתה, כל כיתה של בדיקות מועברת למקטע נפרד.
- אם הגיוני, הוסיפו כמה אפשרויות כדי לשלוט קצת בפיצול.
לדוגמה: ל-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;
}
...
}
בדוגמה הזו פשוט נוצרת מכונה חדשה של עצמה ומגדירים לה פרמטרים של פלחים. עם זאת, הלוגיקה של הפיצול יכולה להיות שונה לגמרי מבדיקת A/B לבדיקת A/B אחרת. כל עוד היא גורמת ליצירת קבוצות משנה מקיפות באופן קולקטיבי, היא תקינה.
עצמאות
אסור שהפלחים יהיו תלויים זה בזה! לשני שבבים שנוצרו על ידי ההטמעה של 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 לא מבצע צבירת תוצאות בדיקה להפעלות מחולקות, צריך לוודא ששירות הדיווח תומך בכך.