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

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

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

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

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

תרחיש בדיקה המנגנון שבו נעשה שימוש
בדיקה מקומית כשערכי הדגלים משתנים לעיתים קרובות Android debug bridge כפי שמתואר בקטע שינוי הערך של דגל בזמן הריצה
בדיקה מקומית כשערכי הדגלים לא משתנים לעיתים קרובות קובץ ערכים של דגלים, כפי שמתואר בקטע הגדרת ערכים של דגלים להשקת תכונות
בדיקה מקצה לקצה שבה ערכי הדגלים משתנים 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)

בקטע הזה מוסבר איך לשנות את ערכי הדגלים של aconfig ברמת הכיתה והשיטה (לכל בדיקה) בבדיקות Java ובבדיקות Kotlin.

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

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