עם ההשקה של דגלים להשקת תכונות, יש מדיניות חדשה בנושא בדיקות שאתם צריכים לפעול לפיה:
- הבדיקות צריכות לכלול את ההתנהגויות של התכונה כשהיא מופעלת וגם כשהיא מושבתת.
- במהלך הבדיקה, חובה להשתמש במנגנונים הרשמיים כדי להגדיר ערכי דגל.
- בבדיקות xTS לא אמורה להיות אפשרות לשנות את ערכי הדגלים.
בקטע הבא מפורטים המנגנונים הרשמיים שבהם צריך להשתמש כדי לעמוד בדרישות המדיניות האלה.
בדיקת הקוד שסומן
תרחיש בדיקה | המנגנון שבו נעשה שימוש |
---|---|
בדיקות מקומיות כשערכי הדגלים משתנים לעיתים קרובות | Android debug bridge (גשר לניפוי באגים ב-Android) כמו שמתואר במאמר בנושא שינוי הערך של דגל בזמן ריצה |
בדיקה מקומית כשערכי הדגלים לא משתנים לעיתים קרובות | קובץ ערכי הסימונים, כפי שמוסבר במאמר הגדרת ערכי סימונים להשקת תכונות |
בדיקת קצה לקצה שבה ערכי הדגלים משתנים | 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
של קובץ buildAndroid.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
helper כדי לבטל את ערכי הדגלים. בקטע הקוד הבא אפשר לראות איך לכלול את 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
היא הערה שמשמשת להוספת התלות בדגל JUnit של המחלקהSetFlagsRule
. -
SetFlagsRule
היא מחלקה מסייעת שנועדה לשנות את ערכי הסימון. מידע על האופן שבוSetFlagsRule
קובע ערכי ברירת מחדל זמין במאמר ערכי ברירת מחדל של מכשירים. -
@EnableFlags
היא הערה שמקבלת מספר שרירותי של שמות של דגלים. כשמשביתים דגלים, משתמשים בדגל@DisableFlags
. אפשר להחיל את ההערות האלה על שיטה או על מחלקה.
מגדירים ערכי דגל לכל תהליך הבדיקה, החל מ-SetFlagsRule
, שהוא לפני כל שיטות ההגדרה עם ההערה @Before
בבדיקה. ערכי הדגלים חוזרים למצב הקודם שלהם כשהפונקציה 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
class rule, test_flag_foo_turned_on
הבדיקה נכשלת לפני ההפעלה כש-FLAG_FLAG_FOO
נקרא על ידי ה-constructor של
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
הוא false והערך שלFLAG_BAR
הואfalse
במקום לשנות ישירות את ערכי הדגלים, ההערות @DisableFlags
ו-@EnableFlags
משנות את ערכי הדגלים על סמך תנאי הפרמטרים. לדוגמה, legacyBarLogic
פועל רק כש-FLAG_BAR
מושבת, וזה קורה בשניים מתוך ארבעת השילובים של הדגלים. השילוב legacyBarLogic
לא נכלל בשני השילובים האחרים.
יש שתי שיטות ליצירת פרמטרים לדגלים:
FlagsParameterization.allCombinationsOf(String...)
מבצע 2 בחזקת n הרצות של כל בדיקה. לדוגמה, דגל אחד מריץ 2x בדיקות, או ארבעה דגלים מריצים 16x בדיקות.
FlagsParameterization.progressionOf(String...)
מבצע n+1 הרצות של כל בדיקה. לדוגמה, דגל אחד מפעיל 2x בדיקות וארבעה דגלים מפעילים 5x דגלים.
יצירת בדיקות יחידה (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)
הוא מאקרו שמשמש כברירת מחדל להגדרת דגלים בהגדרות המכשיר.
- פקודות המאקרו
בקובץ ה-build
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
בקובץ ה-build של הבדיקה: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, ערך ברירת המחדל זהה להגדרת הגרסה של ה-build. אם הערך במכשיר שונה מברירת המחדל, SetFlagsRule
משתמש בערך ששונה כברירת המחדל.
אם אותה בדיקה מופעלת בהגדרות שחרור שונות, הערך של הדגלים שלא הוגדרו באופן מפורש באמצעות SetFlagsRule
עשוי להשתנות.
אחרי כל בדיקה, SetFlagsRule
משחזר את מופע FeatureFlags
ב-Flags
למצב FeatureFlagsImpl
המקורי שלו, כדי שלא יהיו לו תופעות לוואי על שיטות וסוגים אחרים של בדיקות.