عند كتابة أداة لتشغيل الاختبارات، من المهم التفكير في قابلية التوسّع. اسأل نفسك، "إذا كان على أداة تنفيذ الاختبار تنفيذ 200 ألف حالة اختبار"، كم من الوقت ستستغرق؟
التقسيم إلى أجزاء هو إحدى الإجابات المتاحة في الاتحاد التجاري. يتطلب وتقسيم جميع الاختبارات التي يحتاجها العداء إلى عدة أجزاء يمكن بشكل موازٍ.
توضح هذه الصفحة كيفية جعل العداء قابلاً للتجزئة في Tradefed.
واجهة التطبيق
الواجهة الفردية الأكثر أهمية التي يجب تنفيذها ليتم اعتبارها قابلة للتجزئة
TF هو
IShardableTest،
والذي يحتوي على طريقتين: split(int numShard)
وsplit()
.
إذا كان التقسيم سيعتمد على عدد الأجزاء المطلوبة،
يجب تنفيذ split(int numShard)
. في الحالات الأخرى، يمكنك تنفيذ split()
.
عند تنفيذ أمر اختبار TF باستخدام مَعلمتَي التجزئة --shard-count
و
--shard-index
، ينتقل TF إلى جميع IRemoteTest
للبحث عن IShardableTest
التي يتم تنفيذها. في حال العثور على الرقم، سيتم الاتصال بالرقم split
من أجل
الحصول على كائن IRemoteTest
جديد لتنفيذ مجموعة فرعية من حالات الاختبار على مستوى محدّد
جزء.
ما الذي يجب أن أعرفه عن تنفيذ التقسيم؟
- قد يقسّم برنامج التشغيل البيانات إلى شرائح استنادًا إلى بعض الشروط فقط، وفي هذه الحالة، يمكنك عرض
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;
}
...
}
ينشئ هذا المثال ببساطة مثيلًا جديدًا من نفسه ويضبط مَعلمات القسم عليه. ومع ذلك، يمكن أن يختلف منطق التقسيم تمامًا عن من الاختبار إلى الاختبار وما دامت حاسمة تؤدي إلى نتائج مجموعات فرعية شاملة، فلا بأس بذلك.
الاستقلال
يجب أن تكون الشرائح مستقلة. يجب ألا تكون هناك تبعيات بين الشذرتين اللتين تم إنشاؤهما من خلال تنفيذ
split
في أداة التشغيل أو أن تشتركا في
الموارد.
يجب أن يكون تقسيم الأجزاء حتميًا! ويُعد هذا إلزاميًا أيضًا، نظرًا
الشروط نفسها، يجب أن تعرض طريقة 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 لا تُجري أي تجميع لنتائج الاختبار لطلبات التنفيذ المقسّمة، عليك التأكّد من أنّ خدمة إعداد التقارير تتيح ذلك.