בדיקת קוד בתוך תכונות ניסיוניות של הפעלת תכונות

עם השקת התכונות הניסיוניות, יש כללי מדיניות חדשים לבדיקת הנתונים שצריך לפעול לפיהם:

  • הבדיקות צריכות לכלול את ההתנהגויות של הדגל גם כשהוא מופעל וגם כשהוא מושבת.
  • עליכם להשתמש במנגנונים הרשמיים כדי להגדיר את ערכי הסימונים במהלך הבדיקה.
  • בדיקות xTS לא אמורות לבטל את ערכי הדגלים בבדיקות.

בקטע הבא מפורטים המנגנונים הרשמיים שבהם עליכם להשתמש כדי לעמוד בדרישות המדיניות האלה.

בדיקת הקוד שסומן

תרחיש בדיקה המנגנון שבו נעשה שימוש
בדיקה מקומית כאשר ערכי הדגל משתנים לעתים קרובות גשר לניפוי באגים ב-Android כמו שמתואר במאמר שינוי ערך של דגל בזמן ריצה
בדיקה מקומית כשערכי הדגל לא משתנים לעיתים קרובות קובץ ערכים של דגלים, כפי שמתואר בקטע הגדרת ערכים של דגלים להשקת תכונות
בדיקות מקצה לקצה – כאשר ערכי הדגלים משתנים FeatureFlagTargetPreparer כמו שמוסבר במאמר יצירת בדיקות מקצה לקצה
בדיקת יחידה שבה משתנים ערכי הדגלים SetFlagsRule עם @EnableFlags ו-@DisableFlags כמו שמוסבר ביצירת בדיקות יחידה (Java ו-Kotlin) או ביצירת בדיקות יחידה (C ו-C++ ).
בדיקות מקצה לקצה או בדיקות יחידה (unit testing) שבהן ערכי הדגלים לא יכולים להשתנות 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.

יצירת מודולים של בדיקות עם פרמטרים על סמך מצבי דגלים

כדי ליצור מודולים לבדיקה עם פרמטרים על סמך מצבי סימון:

  1. כוללים את FeatureFlagTargetPreparer בקובץ התצורה של מודול הבדיקה AndroidTest.xml:

    <target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >
    
  2. מציינים אפשרויות של ערך דגל בקטע test_module_config בקובץ build מסוג 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)

בקטע הזה מתוארת הגישה לשינוי ערכי דגלי הגדרה ברמת המחלקה וה-method (לכל בדיקה) בבדיקות Java ו-Kotlin.

כדי לכתוב בדיקות יחידה אוטומטיות ב-codebase גדול עם מספר גדול של דגלים, מבצעים את הפעולות הבאות:

  1. משתמשים במחלקה SetFlagsRule עם ההערות @EnableFlags ו-@DisableFlags כדי לבדוק את כל הסתעפויות הקוד.
  2. כדאי להשתמש בשיטה SetFlagsRule.ClassRule כדי להימנע מבאגים נפוצים בבדיקה.
  3. תוכלו להשתמש ב-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 הוא הערה שמשמשת להוספת התלות של דגל-JUnit של המחלקה SetFlagsRule.
  • SetFlagsRule היא כיתת עזר שמיועדת לשינוי ערכים של דגלים. במאמר ערכי ברירת מחדל של המכשיר מוסבר איך SetFlagsRule קובע את ערכי ברירת המחדל.
  • @EnableFlags הוא הערה שמקבלת מספר שרירותי של שמות דגלים. כשמשביתים דגלים, משתמשים ב-@DisableFlags. אפשר להחיל את ההערות האלה על שיטה או על כיתה.

מגדירים ערכי דגל לכל תהליך הבדיקה, החל מ-SetFlagsRule, שלפני כל שיטת הגדרה עם הערות עם @Before בבדיקה. ערכי הדגלים חוזרים למצבם הקודם בסיום SetFlagsRule, כלומר אחרי כל שיטות ההגדרה עם הערות @After.

מוודאים שהדגלים מוגדרים בצורה נכונה

כפי שציינו קודם, SetFlagsRule משמש עם ההערה @Rule של ה-JUnit, והמשמעות היא ש-SetFlagsRule לא יכול לוודא שהדגלים מוגדרים נכון במהלך ה-constructor של מחלקת הבדיקה, או בשיטות אחרות עם הערות @BeforeClass או @AfterClass.

כדי לוודא שהאביזר לבדיקה נוצר עם ערך הכיתה הנכון, צריך להשתמש ב-method‏ SetFlagsRule.ClassRule כדי שהאביזר לא נוצר עד ש-method ההגדרה מתויג ב-@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 נקרא על ידי ה-constructor של DemoClass.

אם צריך להפעיל את הדגל בכיתה כולה, מעבירים את ההערה @EnableFlags לרמת הכיתה (לפני הצהרת הכיתה). העברת ההערה לרמת המחלקה מאפשרת ל-SetFlagsRule.ClassRule לוודא שהדגל מוגדר כראוי במהלך ה-constructor של מחלקה הבדיקה, או בכל שיטה של @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). ה-method 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 הפעלות של כל בדיקה. לדוגמה, דגל אחד מפעיל פי 2 בדיקות או ארבעה דגלים מריצים בדיקות 16x.

  • FlagsParameterization.progressionOf(String...) מריץ n+1 הפעלות בכל בדיקה. לדוגמה, דגל אחד מפעיל 2x בדיקות וארבעה דגלים מפעילים 5x דגלים.

יצירת בדיקות יחידה (C ו-C++)

AOSP כולל פקודות מאקרו של ערכי דגלים לבדיקות C ו-C++ שנכתבו במסגרת GoogleTest.

  1. במקור הבדיקה, צריך לכלול את הגדרות המאקרו ואת הספריות שנוצרו על ידי aconfig:

    #include <flag_macros.h>
    #include "android_cts_flags.h"
    
  2. במקור הבדיקה, במקום להשתמש בפקודות מאקרו 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) הוא מאקרו שמשמש לדגלים שמוגדרים בקובצי aconfig. המאקרו הזה מקבל מרחב שמות (TEST_NS) ושם דגל (readwrite_enabled_flag).
    • LEGACY_FLAG(aconfig_flags.cts, TEST_NS, readwrite_disabled_flag) הוא מאקרו שמשמש לדגלים שמוגדרים בהגדרות המכשיר כברירת מחדל.
  3. בקובץ ה-build של Android.bp, מוסיפים את הספריות שנוצרות על ידי הגדרה ואת ספריות המאקרו הרלוונטיות כתלות בבדיקה:

    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"],
      ...
    }
    
  4. מריצים את הבדיקות באופן מקומי באמצעות הפקודה הבאה:

    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.

בשלבים הבאים מוסבר איך ליצור ולהריץ בדיקה מקצה לקצה או בדיקת יחידה שבה לא ניתן לשנות את ערכי הדגלים:

  1. בקוד הבדיקה, משתמשים ב-CheckFlagsRule כדי להחיל סינון של בדיקות. כמו כן, השתמשו בהערות RequiresFlagsEnabled ו-RequiredFlagsDisabled ב-Java כדי לציין את דרישות הדגל לבדיקה.

    בבדיקה בצד המכשיר נעשה שימוש במחלקה 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() {}
    }
    
  2. מוסיפים את 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"],
    }
    
  3. כדי להריץ את הבדיקה באופן מקומי, משתמשים בפקודה הבאה:

    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 המקורי שלה, כך שלא יהיו לה תופעות לוואי על שיטות בדיקה וסיווגים אחרים.