기능 출시 플래그가 도입됨에 따라 준수해야 하는 새로운 테스트 정책이 있습니다.
- 테스트는 플래그의 사용 설정 및 사용 중지 동작을 모두 다루어야 합니다.
- 테스트 중에 플래그 값을 설정하려면 공식 메커니즘을 사용해야 합니다.
- xTS 테스트는 테스트에서 플래그 값을 재정의해서는 안 됩니다.
다음 섹션에서는 이러한 정책을 준수하는 데 사용해야 하는 공식 메커니즘을 제공합니다.
신고된 코드 테스트
테스트 시나리오 | 사용된 메커니즘 |
---|---|
플래그 값이 자주 변경되는 경우 로컬 테스트 | 런타임 시 플래그 값 변경에 설명된 Android 디버그 브리지 |
플래그 값이 자주 변경되지 않는 경우의 로컬 테스트 | 기능 출시 플래그 값 설정에 설명된 대로 플래그 값 파일 |
플래그 값이 변경되는 엔드 투 엔드 테스트 | FeatureFlagTargetPreparer : 엔드 투 엔드 테스트 만들기에 설명된 대로 |
플래그 값이 변경되는 단위 테스트 | 단위 테스트 만들기 (Java 및 Kotlin) 또는 단위 테스트 만들기 (C 및 C++)에 설명된 대로 @EnableFlags 및 @DisableFlags 가 있는 SetFlagsRule |
플래그 값을 변경할 수 없는 엔드 투 엔드 또는 단위 테스트 | 플래그 값이 변경되지 않는 엔드 투 엔드 또는 단위 테스트 만들기에 설명된 CheckFlagsRule |
엔드 투 엔드 테스트 만들기
AOSP는 기기에서 엔드 투 엔드 테스트를 지원하는 FeatureFlagTargetPreparer
라는 클래스를 제공합니다. 이 클래스는 플래그 값 재정의를 입력으로 받아 테스트 실행 전에 기기 구성에서 이러한 플래그를 설정하고 실행 후 플래그를 복원합니다.
테스트 모듈 및 테스트 구성 수준에서 FeatureFlagTargetPreparer
클래스의 기능을 적용할 수 있습니다.
테스트 모듈 구성에서 FeatureFlagTargetPreparer 적용
테스트 모듈 구성에 FeatureFlagTargetPreparer
를 적용하려면 AndroidTest.xml
테스트 모듈 구성 파일에 FeatureFlagTargetPreparer
및 플래그 값 재정의를 포함합니다.
<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
로 설정된 플래그 재정의입니다.
플래그 상태를 기반으로 매개변수화된 테스트 모듈 만들기
플래그 상태를 기반으로 매개변수화된 테스트 모듈을 만들려면 다음 단계를 따르세요.
AndroidTest.xml
테스트 모듈 구성 파일에FeatureFlagTargetPreparer
를 포함합니다.<target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer" >
Android.bp
빌드 파일의test_module_config
섹션에서 플래그 값 옵션을 지정합니다.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)
이 섹션에서는 Java 및 Kotlin 테스트에서 클래스 및 메서드 수준 (테스트별)에서 aconfig 플래그 값을 재정의하는 접근 방식을 설명합니다.
플래그가 많은 대규모 코드베이스에서 자동화된 단위 테스트를 작성하려면 다음 단계를 따르세요.
@EnableFlags
및@DisableFlags
주석과 함께SetFlagsRule
클래스를 사용하여 모든 코드 브랜치를 테스트합니다.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
는SetFlagsRule
클래스의 플래그-JUnit 종속 항목을 추가하는 데 사용되는 주석입니다.SetFlagsRule
는 플래그 값을 재정의하기 위해 제공된 도우미 클래스입니다.SetFlagsRule
가 기본값을 결정하는 방법에 관한 자세한 내용은 기기 기본값을 참고하세요.@EnableFlags
는 임의 개수의 플래그 이름을 허용하는 주석입니다. 플래그를 사용 중지할 때는@DisableFlags
를 사용하세요. 이러한 주석은 메서드 또는 클래스에 적용할 수 있습니다.
테스트의 @Before
주석이 달린 설정 메서드보다 먼저 오는 SetFlagsRule
부터 시작하여 전체 테스트 프로세스의 플래그 값을 설정합니다. 플래그 값은 SetFlagsRule
가 완료되면 이전 상태로 돌아갑니다. 즉, @After
주석이 달린 설정 메서드가 실행된 후에 돌아갑니다.
플래그가 올바르게 설정되어 있는지 확인
앞에서 언급한 대로 SetFlagsRule
는 JUnit @Rule
주석과 함께 사용됩니다. 즉, 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
클래스 규칙을 추가하면 DemoClass
의 생성자가 FLAG_FLAG_FOO
를 읽을 때 test_flag_foo_turned_on
가 실행되기 전에 실패합니다.
전체 클래스에 플래그를 사용 설정해야 하는 경우 @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
가 사용 중지된 경우에만 실행되며 이는 4가지 플래그 조합 중 2가지에서 발생합니다. 다른 두 조합의 경우 legacyBarLogic
가 건너뜁니다.
플래그의 매개변수를 만드는 방법에는 두 가지가 있습니다.
FlagsParameterization.allCombinationsOf(String...)
는 각 테스트를 2^n번 실행합니다. 예를 들어 플래그 하나는 테스트를 2배 실행하고 플래그 4개는 테스트를 16배 실행합니다.FlagsParameterization.progressionOf(String...)
은 각 테스트를 n+1번 실행합니다. 예를 들어 플래그 하나는 테스트를 2배 실행하고 플래그 4개는 테스트를 5배 실행합니다.
단위 테스트 만들기 (C 및 C++)
AOSP에는 GoogleTest 프레임워크로 작성된 C 및 C++ 테스트용 플래그 값 매크로가 포함되어 있습니다.
테스트 소스에 매크로 정의와 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
및TEST_F
매크로 대신TEST_WITH_FLAGS
및TEST_F_WITH_FLAGS
매크로가 사용됩니다.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)
플래그 값이 변경되지 않는 엔드 투 엔드 또는 단위 테스트 만들기
플래그를 재정의할 수 없고 테스트가 현재 플래그 상태를 기반으로 하는 경우에만 테스트를 필터링할 수 있는 테스트 사례의 경우 RequiresFlagsEnabled
및 RequiresFlagsDisabled
주석과 함께 CheckFlagsRule
규칙을 사용하세요.
다음 단계에서는 플래그 값을 재정의할 수 없는 엔드 투 엔드 또는 단위 테스트를 만들고 실행하는 방법을 보여줍니다.
테스트 코드에서
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() {} }
테스트의 빌드 파일
static_libs
섹션에jflag-unit
및 aconfig 생성 라이브러리를 추가합니다.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
는 Flags
의 FeatureFlags
인스턴스를 원래 FeatureFlagsImpl
로 복원하여 다른 테스트 메서드와 클래스에 부작용이 없도록 합니다.