مع طرح علامات إطلاق الميزات، هناك سياسات اختبار جديدة يجب الالتزام بها:
- يجب أن تشمل اختباراتك كلّ من سلوكَي تفعيل العلامة وإيقافها.
- يجب استخدام الآليات الرسمية لضبط قيم العلامات أثناء الاختبار.
- يجب ألا تلغي اختبارات xTS قيم العلامات في الاختبارات.
يقدّم القسم التالي الآليات الرسمية التي يجب استخدامها للالتزام بهذه السياسات.
اختبار الرمز الذي تم الإبلاغ عنه
سيناريو الاختبار | الآلية المستخدَمة |
---|---|
الاختبار على الجهاز عندما تتغيّر قيم العلامة بشكل متكرّر | أداة Android Debug Bridge كما هو موضّح في مقالة تغيير قيمة علامة أثناء التشغيل |
الاختبار على الجهاز عندما لا تتغيّر قيم العلامات كثيرًا | ملف قيم العلامة كما هو موضّح في مقالة ضبط قيم علامة إطلاق الميزة |
الاختبار الشامل الذي تتغيّر فيه قيم العلامة | FeatureFlagTargetPreparer كما هو موضّح في مقالة إنشاء اختبارات شاملة |
اختبار الوحدة الذي تتغيّر فيه قيم العلامة | SetFlagsRule مع @EnableFlags و@DisableFlags كما هو موضّح في مقالة إنشاء اختبارات الوحدة (Java وKotlin) أو
إنشاء اختبارات الوحدة (C وC++) |
الاختبار الشامل أو اختبار الوحدة حيث لا يمكن تغيير قيم العلامة | CheckFlagsRule كما هو موضّح في مقالة إنشاء اختبارات شاملة أو اختبارات وحدات لا تتغيّر فيها قيم العلامة |
إنشاء اختبارات شاملة
يوفّر إطار عمل AOSP فئة باسم FeatureFlagTargetPreparer
، ما يتيح
الاختبار الشامل على الجهاز. تقبل هذه الفئة قيم العلامات التي يتم استبدالها كأحد
مدخلات الاختبار، وتضبط هذه العلامات في إعدادات الأجهزة قبل تنفيذ الاختبار،
وتستعيد العلامات بعد التنفيذ.
يمكنك تطبيق وظائف فئة FeatureFlagTargetPreparer
على مستوى
وحدة الاختبار ومستوى إعدادات الاختبار.
تطبيق FeatureFlagTargetPreparer في إعداد وحدة اختبار
لتطبيق FeatureFlagTargetPreparer
في إعداد وحدة الاختبار، أدرِج
FeatureFlagTargetPreparer
وعمليات إلغاء قيمة العلامة في AndroidTest.xml
ملف إعدادات
وحدة الاختبار:
<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer">
<option name="flag-value"
value="permissions/com.android.permission.flags.device_aware_permission_grant=true"/>
<option name="flag-value"
value="virtual_devices/android.companion.virtual.flags.stream_permissions=true"/>
</target_preparer>
المكان:
- يتم ضبط
target.preparer class
دائمًا علىcom.android.tradefed.targetprep.FeatureFlagTargetPreparer
. option
هي علامة إلغاء العلامة مع ضبطname
دائمًا علىflag-value
وvalue
علىnamespace/aconfigPackage.flagName=true|false
.
إنشاء وحدات اختبار مستندة إلى مَعلمات استنادًا إلى حالات العلامة
لإنشاء وحدات اختبار مستندة إلى مَعلمات استنادًا إلى حالات العلامة:
أدرِج
FeatureFlagTargetPreparer
في ملف إعداداتAndroidTest.xml
وحدة الاختبار:<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >
حدِّد خيارات قيمة العلامة في قسم
test_module_config
من ملف الإنشاءAndroid.bp
:android_test { name: "MyTest" ... } test_module_config { name: "MyTestWithMyFlagEnabled", base: "MyTest", ... options: [ {name: "flag-value", value: "telephony/com.android.internal.telephony.flags.oem_enabled_satellite_flag=true"}, ], } test_module_config { name: "MyTestWithMyFlagDisabled", base: "MyTest", ... options: [ {name: "flag-value", value: "telephony/com.android.internal.telephony.flags.carrier_enabled_satellite_flag=true"}, ], }
يحتوي حقل
options
على عمليات إلغاء العلامة مع ضبطname
دائمًا علىflag-value
وvalue
علىnamespace/aconfigPackage.flagName=true|false
.
إنشاء اختبارات وحدات (Java وKotlin)
يوضّح هذا القسم طريقة إلغاء قيم علامة aconfig على مستوى الصف والطريقة (لكل اختبار) في اختبارات Java وKotlin.
لكتابة اختبارات وحدات آلية في قاعدة بيانات كبيرة تتضمّن عددًا كبيرًا من الإشارات، اتّبِع الخطوات التالية:
- استخدِم فئة
SetFlagsRule
مع التعليقات التوضيحية@EnableFlags
و@DisableFlags
لاختبار جميع فروع الرموز البرمجية. - استخدِم طريقة
SetFlagsRule.ClassRule
لتجنُّب أخطاء الاختبار الشائعة. - استخدِم
FlagsParameterization
لاختبار صفوفك على مستوى مجموعة كبيرة من إعدادات العلامات.
اختبار جميع فروع الرموز البرمجية
بالنسبة إلى المشاريع التي تستخدِم الفئة الثابتة للوصول إلى العلامات، يتم توفير فئة المساعدة
SetFlagsRule
لإلغاء قيم العلامة. يوضّح المقتطف التالي من الرمز البرمجي كيفية تضمين SetFlagsRule
وتفعيل عدة علامات في آنٍ واحد:
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import com.example.android.aconfig.demo.flags.Flags;
...
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Test
@EnableFlags({Flags.FLAG_FLAG_FOO, Flags.FLAG_FLAG_BAR})
public void test_flag_foo_and_flag_bar_turned_on() {
...
}
المكان:
@Rule
هو تعليق توضيحي يُستخدَم لإضافة الاعتماد على flag-JUnit لفئةSetFlagsRule
.SetFlagsRule
هي فئة مساعدة مقدَّمة لتجاهل قيم العلامة. للحصول على معلومات عن كيفية تحديدSetFlagsRule
للقيم التلقائية، اطّلِع على القيم التلقائية للأجهزة.@EnableFlags
هو تعليق توضيحي يقبل عددًا عشوائيًا من أسماء العلامات. عند إيقاف الإشارات، استخدِم@DisableFlags
. يمكنك تطبيق هذه التعليقات التوضيحية إما على طريقة أو فئة.
اضبط قيم العلامة لعملية الاختبار بأكملها، بدءًا من SetFlagsRule
، قبل أي من طُرق الإعداد التي تمت عليها تعليقات توضيحية @Before
في الاختبار. تعود قيم العلامة إلى حالتها السابقة عند انتهاء
SetFlagsRule
، أي بعد أي طُرق إعداد مُشارَك فيها @After
.
التأكّد من ضبط الإشارات بشكلٍ صحيح
كما ذكرنا سابقًا، يتم استخدام SetFlagsRule
مع التعليق التوضيحي @Rule
في JUnit، ما يعني أنّه
لا يمكن أن تضمن SetFlagsRule
ضبط علاماتك بشكل صحيح أثناء إنشاء SetFlagsRule
الفئة الاختبارية أو أي طرق تمّت إضافة تعليق توضيحي لها باستخدام @BeforeClass
أو @AfterClass
.
لضمان إنشاء أدوات الاختبار باستخدام قيمة الفئة الصحيحة، استخدِم
طريقة SetFlagsRule.ClassRule
حتى لا يتم
إنشاء أدوات الاختبار إلى أن يتم استخدام طريقة إعداد تمّت عليها تعليقات توضيحية باستخدام @Before
:
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import com.example.android.aconfig.demo.flags.Flags;
class ExampleTest {
@ClassRule public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
@Rule public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
private DemoClass underTest = new DemoClass();
@Test
@EnableFlags(Flags.FLAG_FLAG_FOO)
public void test_flag_foo_turned_on() {
...
}
}
عند إضافة قاعدة فئة SetFlagsRule.ClassRule
، تتعذّر قراءة test_flag_foo_turned_on
قبل تشغيلها عندما تقرأ الدالة الإنشائية لDemoClass
FLAG_FLAG_FOO
.
إذا كان يجب تفعيل علامة في صفك بأكمله، انقل التعليق التوضيحي @EnableFlags
إلى
مستوى الصف (قبل بيان الصف). يؤدي نقل التعليق التوضيحي إلى
مستوى الفئة إلى السماح SetFlagsRule.ClassRule
بضمان ضبط العلامة بشكل صحيح
أثناء إنشاء فئة الاختبار، أو أثناء أي طرق تمّت عليها تعليقات توضيحية باستخدام@BeforeClass
أو
@AfterClass
.
إجراء اختبارات على إعدادات علامات متعددة
بما أنّه يمكنك ضبط قيم العلامات لكل اختبار على حدة، يمكنك أيضًا استخدام المَعلمات لإجراء اختبارات على مستوى إعدادات علامات متعددة:
...
import com.example.android.aconfig.demo.flags.Flags;
...
@RunWith(ParameterizedAndroidJunit4::class)
class FooBarTest {
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
return FlagsParameterization.allCombinationsOf(Flags.FLAG_FOO, Flags.FLAG_BAR);
}
@Rule
public SetFlagsRule mSetFlagsRule;
public FooBarTest(FlagsParameterization flags) {
mSetFlagsRule = new SetFlagsRule(flags);
}
@Test public void fooLogic() {...}
@DisableFlags(Flags.FLAG_BAR)
@Test public void legacyBarLogic() {...}
@EnableFlags(Flags.FLAG_BAR)
@Test public void newBarLogic() {...}
}
يُرجى العِلم أنّ هذه الفئة تُجري
ثلاثة
اختبارات (fooLogic
وlegacyBarLogic
وnewBarLogic
) باستخدام SetFlagsRule
، ولكن بدون وضع مَعلمات. ويتم تنفيذ الأسلوبfooLogic
بغض النظر عن قيمFLAG_FOO
وFLAG_BAR
التي تم ضبطها على
الجهاز.
عند إضافة المَعلمات، تنشئ الطريقة FlagsParameterization.allCombinationsOf
جميع التركيبات الممكنة للعلامتَين FLAG_FOO
وFLAG_BAR
:
- الموقع الجغرافي
FLAG_FOO
هوtrue
والموقع الجغرافيFLAG_BAR
هوtrue
- الموقع الجغرافي
FLAG_FOO
هوtrue
والموقع الجغرافيFLAG_BAR
هوfalse
. - الموقع الجغرافي
FLAG_FOO
هوfalse
والموقع الجغرافيFLAG_BAR
هوtrue
. FLAG_FOO
خطأ وFLAG_BAR
هوfalse
بدلاً من تغيير قيم العلامة مباشرةً، تعدّل التعليقات التوضيحية @DisableFlags
و
@EnableFlags
قيم العلامة استنادًا إلى شروط المَعلمات. على سبيل المثال، لا يتم تشغيل legacyBarLogic
إلا عندما تكون FLAG_BAR
غير مفعّلة، ويحدث ذلك في
اثنتان من مجموعات العلامات الأربع. يتم تخطّي legacyBarLogic
في التركيبتين
الأخرتين.
هناك طريقتان لإنشاء المَعلمات للعلامات:
تنفِّذ
FlagsParameterization.allCombinationsOf(String...)
2^n عملية تنفيذ لكل اختبار. على سبيل المثال، يؤدي علامة واحدة إلى إجراء اختبارَين أو تؤدي أربع علامات إلى إجراء 16 اختبارًا.تنفِّذ
FlagsParameterization.progressionOf(String...)
عمليات تنفيذ n+1 لكل اختبار. على سبيل المثال، يؤدي رمز واحد إلى تشغيل اختبارَين، ويؤدي أربعة رموز إلى تشغيل 5 رموز.
إنشاء اختبارات الوحدة (C وC++)
يتضمّن AOSP وحدات ماكرو لقيم العلامة لاختبارات C وC++ المكتوبة في إطار عمل GoogleTest.
في مصدر الاختبار، أدرِج تعريفات الوحدات النمطية والمكتبات التي تم إنشاؤها باستخدام aconfig:
#include <flag_macros.h> #include "android_cts_flags.h"
في مصدر الاختبار، استخدِم
TEST_WITH_FLAGS
وTEST_F_WITH_FLAGS
بدلاً من وحدات الماكروTEST
وTESTF
لحالات الاختبار:#define TEST_NS android::cts::flags::tests ... TEST_F_WITH_FLAGS( TestFWithFlagsTest, requies_disabled_flag_enabled_skip, REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(TEST_NS, readwrite_enabled_flag)) ) { TestFail(); } ... TEST_F_WITH_FLAGS( TestFWithFlagsTest, multi_flags_for_same_state_skip, REQUIRES_FLAGS_ENABLED( ACONFIG_FLAG(TEST_NS, readwrite_enabled_flag), LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag) ) ) { TestFail(); } ... TEST_WITH_FLAGS( TestWithFlagsTest, requies_disabled_flag_enabled_skip, REQUIRES_FLAGS_DISABLED( LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_enabled_flag)) ) { FAIL(); } ... TEST_WITH_FLAGS( TestWithFlagsTest, requies_enabled_flag_enabled_executed, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_NS, readwrite_enabled_flag)) ) { TestWithFlagsTestHelper::executed_tests.insert( "requies_enabled_flag_enabled_executed"); }
المكان:
- يتم استخدام وحدات الماكرو
TEST_WITH_FLAGS
وTEST_F_WITH_FLAGS
بدلاً من وحدات الماكروTEST
وTEST_F
. REQUIRES_FLAGS_ENABLED
تحدِّد مجموعة من علامات إصدار الميزات التي يجب أن تُستوفي شرط التفعيل. يمكنك كتابة هذه العلامات في وحدات الماكروACONFIG_FLAG
أوLEGACY_FLAG
.REQUIRES_FLAGS_DISABLED
تحدِّد مجموعة من علامات الميزات التي يجب أن تستوفي شرط إيقاف الميزة. يمكنك كتابة هذه العلامات في وحدات الماكروACONFIG_FLAG
أوLEGACY_FLAG
.ACONFIG_FLAG (TEST_NS, readwrite_enabled_flag)
هو رمز مخصّص للعلامات المحدّدة في ملفات الإعداد. يقبل هذا الماكرو مساحة اسم (TEST_NS
) واسم علامة (readwrite_enabled_flag
).LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag)
هو رمز macro يُستخدَم للعلامات التي تم ضبطها في إعدادات الجهاز تلقائيًا.
- يتم استخدام وحدات الماكرو
في ملف الإنشاء
Android.bp
، أضِف المكتبات التي تم إنشاؤها باستخدام aconfig و المكتبات الموسّعة ذات الصلة كمورد تابع للاختبار:cc_test { name: "FlagMacrosTests", srcs: ["src/FlagMacrosTests.cpp"], static_libs: [ "libgtest", "libflagtest", "my_aconfig_lib", ], shared_libs: [ "libbase", "server_configurable_flags", ], test_suites: ["general-tests"], ... }
يمكنك تنفيذ الاختبارات محليًا باستخدام هذا الأمر:
atest FlagMacrosTests
إذا كانت العلامة
my_namespace.android.myflag.tests.my_flag
غير مفعّلة، تكون نتيجة الاختبار على النحو التالي:[1/2] MyTest#test1: IGNORED (0ms) [2/2] MyTestF#test2: PASSED (0ms)
إذا كانت العلامة
my_namespace.android.myflag.tests.my_flag
مفعّلة، تكون نتيجة الاختبار على النحو التالي:[1/2] MyTest#test1: PASSED (0ms) [2/2] MyTestF#test2: IGNORED (0ms)
إنشاء اختبارات شاملة أو اختبارات وحدات لا تتغيّر فيها قيم العلامات
بالنسبة إلى حالات الاختبار التي لا يمكنك فيها إلغاء الإشارات ولا يمكنك فلترة الاختبارات إلا إذا كانت مستندة إلى حالة العلامة الحالية، استخدِم القاعدة CheckFlagsRule
مع التعليقات التوضيحية
RequiresFlagsEnabled
وRequiresFlagsDisabled
.
توضّح لك الخطوات التالية كيفية إنشاء اختبار شامل أو اختبار وحده وتشغيله حيث لا يمكن إلغاء قيم العلامة:
في رمز الاختبار، استخدِم
CheckFlagsRule
لتطبيق فلترة الاختبار. بالإضافة إلى ذلك، استخدِم التعليقات التوضيحية لرمز JavaRequiresFlagsEnabled
وRequiredFlagsDisabled
لتحديد متطلبات العلامة لاختبارك.يستخدم الاختبار على الجهاز فئة
DeviceFlagsValueProvider
:@RunWith(JUnit4.class) public final class FlagAnnotationTest { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Test @RequiresFlagsEnabled(Flags.FLAG_FLAG_NAME_1) public void test1() {} @Test @RequiresFlagsDisabled(Flags.FLAG_FLAG_NAME_1) public void test2() {} }
يستخدم الاختبار من جهة المضيف فئة
HostFlagsValueProvider
:@RunWith(DeviceJUnit4ClassRunner.class) public final class FlagAnnotationTest extends BaseHostJUnit4Test { @Rule public final CheckFlagsRule mCheckFlagsRule = HostFlagsValueProvider.createCheckFlagsRule(this::getDevice); @Test @RequiresFlagsEnabled(Flags.FLAG_FLAG_NAME_1) public void test1() {} @Test @RequiresFlagsDisabled(Flags.FLAG_FLAG_NAME_1) public void test2() {} }
أضِف
jflag-unit
والمكتبات التي تم إنشاؤها باستخدام aconfig إلى القسمstatic_libs
من ملف الإنشاء لاختبارك:android_test { name: "FlagAnnotationTests", srcs: ["*.java"], static_libs: [ "androidx.test.rules", "my_aconfig_lib", "flag-junit", "platform-test-annotations", ], test_suites: ["general-tests"], }
استخدِم الأمر التالي لإجراء الاختبار على الجهاز:
atest FlagAnnotationTests
إذا كانت العلامة
Flags.FLAG_FLAG_NAME_1
غير مفعّلة، تكون نتيجة الاختبار:[1/2] com.cts.flags.FlagAnnotationTest#test1: ASSUMPTION_FAILED (10ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: PASSED (2ms)
أما إذا لم يكن الأمر كذلك، تكون نتيجة الاختبار:
[1/2] com.cts.flags.FlagAnnotationTest#test1: PASSED (2ms) [2/2] com.cts.flags.FlagAnnotationTest#test2: ASSUMPTION_FAILED (10ms)
القيم التلقائية للجهاز
يستخدم SetFlagsRule
الذي تم إعداده قيم علامة من الجهاز. إذا لم يتم إلغاء قيمة العلامة
على الجهاز، مثل استخدام adb، ستكون القيمة الافتراضية
هي نفسها
مثل إعدادات الإصدار للإصدار. إذا تم تجاوز القيمة على الجهاز، يستخدم SetFlagsRule
قيمة التجاوز كقيمة
تلقائية.
في حال تنفيذ الاختبار نفسه ضمن إعدادات إصدار مختلفة، يمكن أن تختلف
قيمة العلامات التي لم يتم ضبطها صراحةً باستخدام SetFlagsRule
.
بعد كل اختبار، يُعيد SetFlagsRule
مثيل FeatureFlags
في Flags
إلى FeatureFlagsImpl
الأصلي، كي لا يكون له تأثيرات جانبية على methods وclasses الأخرى للاختبار.