مع طرح علامات إطلاق الميزات، أصبحت هناك سياسات اختبار جديدة يجب الالتزام بها:
- يجب أن تغطّي اختباراتك السلوكيات المفعَّلة وغير المفعَّلة للعَلم.
- يجب استخدام الآليات الرسمية لضبط قيم العلامات أثناء الاختبار.
- يجب ألا تتجاوز اختبارات 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
-annotated في الاختبار. تعود قيم العلامات إلى حالتها السابقة عند انتهاء SetFlagsRule
، أي بعد أي طرق إعداد تمّت إضافة التعليقات التوضيحية @After
إليها.
التأكّد من ضبط العلامات بشكلٍ صحيح
كما ذكرنا سابقًا، يتم استخدام SetFlagsRule
مع التعليق التوضيحي @Rule
في JUnit، ما يعني أنّ 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() {...}
}
يُرجى العِلم أنّه باستخدام SetFlagsRule
، ولكن بدون تحديد المَعلمات، سيتم تشغيل ثلاثة اختبارات (fooLogic
وlegacyBarLogic
وnewBarLogic
) في هذه الفئة. يتم تشغيل الطريقة 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
هي false و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
وTESTF
لحالات الاختبار، استخدِم وحدتَي الماكروTEST_WITH_FLAGS
وTEST_F_WITH_FLAGS
:#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)
هي وحدة ماكرو تُستخدم في العلامات التي يتم ضبطها تلقائيًا في إعدادات الجهاز.
- يتم استخدام وحدتَي الماكرو
في ملف
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
لتطبيق فلترة الاختبار. استخدِم أيضًا تعليقات Java التوضيحيةRequiresFlagsEnabled
و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
الأصلي، وذلك حتى لا يكون له آثار جانبية على طرق وفئات الاختبار الأخرى.