مع إطلاق عمليات الإبلاغ عن إطلاق الميزات، أصبحت هناك سياسات جديدة لإجراء الاختبارات عليك الالتزام بها:
- يجب أن تشمل اختباراتك كلاً من السلوكيات المفعَّلة والمتوقفة المتعلقة بهذه العلامة.
- يجب استخدام الآليات الرسمية لضبط قيم العلامة أثناء الاختبار.
- يجب ألا تلغي اختبارات xTS قيم العلامات في الاختبارات.
يوضّح القسم التالي الآليات الرسمية التي يجب استخدامها للالتزام بهذه السياسات.
اختبار الرمز الذي تم الإبلاغ عنه
سيناريو الاختبار | الآلية المستخدَمة |
---|---|
الاختبار على الجهاز عندما تتغيّر قيم العلامة بشكل متكرّر | كما هو موضّح في مقالة تغيير قيمة العلامة أثناء وقت التشغيل. |
اختبار محلي عندما لا تتغيّر قيم العلامات بشكل متكرر | وضع علامة على ملف القيم كما هو موضَّح في مقالة ضبط قيم علامات إطلاق الميزات |
الاختبار الشامل الذي تتغيّر فيه قيم العلامة | 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
هو تعليق توضيحي يُستخدَم لإضافة تبعية tag-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
قبل تشغيل FLAG_FLAG_FOO
بواسطة الدالة الإنشائية
DemoClass
.
إذا كان الصف بأكمله بحاجة إلى تفعيل علامة، انقل التعليق التوضيحي @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
خاطئة وFLAG_BAR
هيfalse
بدلاً من تغيير قيم العلامات مباشرةً، تعدّل تعليقات @DisableFlags
و@EnableFlags
التوضيحية قيم العلامات استنادًا إلى شروط المَعلمات. على سبيل المثال، لا يتم تشغيل legacyBarLogic
إلا عند إيقاف FLAG_BAR
، وهو ما يحدث في اثنتين من تركيبات العلامات الأربع. ويتم تخطّي legacyBarLogic
للتركيبتين الأخريين.
تتوفّر طريقتان لإنشاء مَعلمات العلامات:
تنفِّذ
FlagsParameterization.allCombinationsOf(String...)
2^n عملية تنفيذ لكل اختبار. على سبيل المثال، تُجري إحدى العلامات اختبارات 2x أو تُجري أربع علامات اختبار 16x.ينفذ
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)
هو ماكرو يُستخدم للعلامات التي يتم ضبطها في إعداد الجهاز تلقائيًا.
- يتم استخدام وحدات ماكرو
في ملف الإصدار
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
، حتى لا تكون له أي آثار جانبية على طرق الاختبار وفئاته الأخرى.