عند كتابة اختبار تجريبي، من المهم التفكير في قابلية التوسع. اسأل نفسك، "إذا كان على عداء الاختبار الخاص بي إجراء 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 لا يقوم بأي تجميع لنتائج الاختبار للاستدعاءات المجزأة، فأنت بحاجة للتأكد من أن خدمة التقارير الخاصة بك تدعمها.