CTS 문제 분석 7

17217 단어
CTS/GTS 문제 분석 7
이전에cts 프레임워크가 초래한 문제를 완전하게 기록한 적이 없기 때문에 이 문제를 기록하여 분석하는 방법
문제의 초심자
테스트 명령: run cts-on-gsi-m CtsMediaTestCases
host log에서 다음과 같이 오류를 보고합니다.
09-29 17:24:12 I/RemoteAndroidTest: Running am instrument -w -r --no-hidden-api-checks --abi arm64-v8a -e testFile /data/local/tmp/ajur/includes.txt -e debug false -e newRunListenerMode true -e log true -e notAnnotation android.platform.test.annotations.RestrictedBuildTest,android.platform.test.annotations.AppModeInstant -e notTestFile /data/local/tmp/ajur/excludes.txt -e timeout_msec 1800000 android.media.cts/android.support.test.runner.AndroidJUnitRunner on unknown-aosp_on_arm64-8bec9538
09-29 17:24:12 I/InstrumentationResultParser: test run failed: 'Instrumentation run failed due to 'Process crashed.''

device log에서 오류 보고:
09-26 20:22:55.132 18904 18904 D AndroidRuntime: Shutting down VM
09-26 20:22:55.132 18904 18904 E AndroidRuntime: FATAL EXCEPTION: main
09-26 20:22:55.132 18904 18904 E AndroidRuntime: Process: android.media.cts, PID: 18904
09-26 20:22:55.132 18904 18904 E AndroidRuntime: java.lang.RuntimeException: Exception thrown in onCreate() of ComponentInfo{android.media.cts/android.support.test.runner.AndroidJUnitRunner}: java.lang.IllegalArgumentException: "android.media.cts.DecoderTest#testH265HDR10StaticMetadata " not recognized as valid package name
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5868)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.app.ActivityThread.access$1100(ActivityThread.java:199)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1650)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.os.Handler.dispatchMessage(Handler.java:106)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.os.Looper.loop(Looper.java:193)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.app.ActivityThread.main(ActivityThread.java:6669)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at java.lang.reflect.Method.invoke(Native Method)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
09-26 20:22:55.132 18904 18904 E AndroidRuntime: Caused by: java.lang.IllegalArgumentException: "android.media.cts.DecoderTest#testH265HDR10StaticMetadata " not recognized as valid package name
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.support.test.internal.runner.RunnerArgs$Builder.validatePackage(RunnerArgs.java:506)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.support.test.internal.runner.RunnerArgs$Builder.parseFromFile(RunnerArgs.java:449)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.support.test.internal.runner.RunnerArgs$Builder.fromBundle(RunnerArgs.java:242)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.support.test.runner.AndroidJUnitRunner.parseRunnerArgs(AndroidJUnitRunner.java:336)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.support.test.runner.AndroidJUnitRunner.onCreate(AndroidJUnitRunner.java:275)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5863)
09-26 20:22:55.132 18904 18904 E AndroidRuntime:    ... 8 more
09-26 20:22:55.136 18904 18904 I Process : Sending signal. PID: 18904 SIG: 9

사실 여기를 보면 구글의 문제임을 알 수 있습니다. 케이스 문제가 아니라 프레임워크의 문제입니다. 급할 때 직접 피드백을 할 수 있고 저한테 와서 판단할 필요가 없습니다.
문제 분석
케이스 문제인지 프레임 문제인지 확인하려면 먼저 v3과 v4의 CtsMediaTestCases를 사용하십시오.apk 이 테스트는 apk를 모두 끌어내어 반컴파일하면 됩니다. 필요할 때 010-Editor를 빌려 마수 헤더를 고치고 먼저 apk를jar 패키지로 반컴파일하여case에서 잘못된 위치의 대체적인 논리를 파악합니다.
케이스를 비교해 봤는데 수정이 안 됐어요. 그럼 프레임 문제예요.잘못된 논리는 안드로이드 JUnit Runner에 있습니다.java에 원본 코드가 없으면 이 문제는 매우 쉽다.원본 코드가 없으면 smali를 통해log를 추가할 수 있어서 좀 번거롭습니다.먼저 잘못된 창고를 정리하다
public void onCreate(Bundle paramBundle)
 {
   this.mArguments = paramBundle;
   parseRunnerArgs(this.mArguments);
   if (waitForDebugger(this.mRunnerArgs))
   {
     Log.i("AndroidJUnitRunner", "Waiting for debugger to connect...");
     Debug.waitForDebugger();
     Log.i("AndroidJUnitRunner", "Debugger connected.");
   }
   if (isPrimaryInstrProcess(this.mRunnerArgs.targetProcess)) {
     this.mUsageTrackerFacilitator = new UsageTrackerFacilitator(this.mRunnerArgs);
   } else {
     this.mUsageTrackerFacilitator = new UsageTrackerFacilitator(false);
   }
   super.onCreate(paramBundle);
   paramBundle = this.mRunnerArgs.appListeners.iterator();
   while (paramBundle.hasNext())
   {
     ApplicationLifecycleCallback localApplicationLifecycleCallback = (ApplicationLifecycleCallback)paramBundle.next();
     ApplicationLifecycleMonitorRegistry.getInstance().addLifecycleCallback(localApplicationLifecycleCallback);
   }
   addScreenCaptureProcessors(this.mRunnerArgs);
   if ((this.mRunnerArgs.orchestratorService != null) && (isPrimaryInstrProcess(this.mRunnerArgs.targetProcess)))
   {
     this.mOrchestratorListener = new OrchestratedInstrumentationListener(this);
     this.mOrchestratorListener.connect(getContext());
     return;
   }
   start();
 }
private void parseRunnerArgs(Bundle paramBundle)
{
  this.mRunnerArgs = new RunnerArgs.Builder().fromManifest(this).fromBundle(this, paramBundle).build();
}
public Builder fromBundle(Instrumentation paramInstrumentation, Bundle paramBundle)
{
  this.debug = parseBoolean(paramBundle.getString("debug"));
  this.delayInMillis = parseUnsignedInt(paramBundle.get("delay_msec"), "delay_msec");
  this.tests.addAll(parseTestClasses(paramBundle.getString("class")));
  this.notTests.addAll(parseTestClasses(paramBundle.getString("notClass")));
  this.testPackages.addAll(parseTestPackages(paramBundle.getString("package")));
  this.notTestPackages.addAll(parseTestPackages(paramBundle.getString("notPackage")));
  RunnerArgs.TestFileArgs localTestFileArgs = parseFromFile(paramInstrumentation, paramBundle.getString("testFile"));
  this.tests.addAll(RunnerArgs.TestFileArgs.access$2900(localTestFileArgs));
  this.testPackages.addAll(RunnerArgs.TestFileArgs.access$3000(localTestFileArgs));
  paramInstrumentation = parseFromFile(paramInstrumentation, paramBundle.getString("notTestFile"));
  this.notTests.addAll(RunnerArgs.TestFileArgs.access$2900(paramInstrumentation));
  this.notTestPackages.addAll(RunnerArgs.TestFileArgs.access$3000(paramInstrumentation));
  this.listeners.addAll(parseLoadAndInstantiateClasses(paramBundle.getString("listener"), RunListener.class, null));
  this.filters.addAll(parseLoadAndInstantiateClasses(paramBundle.getString("filter"), Filter.class, paramBundle));
  this.runnerBuilderClasses.addAll(parseAndLoadClasses(paramBundle.getString("runnerBuilder"), RunnerBuilder.class));
  this.testSize = paramBundle.getString("size");
  this.annotation = paramBundle.getString("annotation");
  this.notAnnotations.addAll(parseStrings(paramBundle.getString("notAnnotation")));
  this.testTimeout = parseUnsignedLong(paramBundle.getString("timeout_msec"), "timeout_msec");
  this.numShards = parseUnsignedInt(paramBundle.get("numShards"), "numShards");
  this.shardIndex = parseUnsignedInt(paramBundle.get("shardIndex"), "shardIndex");
  this.logOnly = parseBoolean(paramBundle.getString("log"));
  this.disableAnalytics = parseBoolean(paramBundle.getString("disableAnalytics"));
  this.appListeners.addAll(parseLoadAndInstantiateClasses(paramBundle.getString("appListener"), ApplicationLifecycleCallback.class, null));
  this.codeCoverage = parseBoolean(paramBundle.getString("coverage"));
  this.codeCoveragePath = paramBundle.getString("coverageFile");
  this.suiteAssignment = parseBoolean(paramBundle.getString("suiteAssignment"));
  this.classLoader = ((ClassLoader)parseLoadAndInstantiateClass(paramBundle.getString("classLoader"), ClassLoader.class));
  this.classpathToScan = parseClasspath(paramBundle.getString("classpathToScan"));
  if (paramBundle.containsKey("remoteMethod")) {
    this.remoteMethod = parseTestClass(paramBundle.getString("remoteMethod"));
  }
  this.orchestratorService = paramBundle.getString("orchestratorService");
  this.listTestsForOrchestrator = parseBoolean(paramBundle.getString("listTestsForOrchestrator"));
  this.targetProcess = paramBundle.getString("targetProcess");
  this.screenCaptureProcessors.addAll(parseLoadAndInstantiateClasses(paramBundle.getString("screenCaptureProcessors"), ScreenCaptureProcessor.class, null));
  this.shellExecBinderKey = paramBundle.getString("shellExecBinderKey");
  this.newRunListenerMode = parseBoolean(paramBundle.getString("newRunListenerMode"));
  return this;
}
/* Error */
 private RunnerArgs.TestFileArgs parseFromFile(Instrumentation paramInstrumentation, String paramString)
 {
   // Byte code:
   //   0: aconst_null
   //   1: astore 4
   //   3: aconst_null
   //   4: astore 5
   //   6: aconst_null
   //   7: astore_3
   //   8: new 379   android/support/test/internal/runner/RunnerArgs$TestFileArgs
   //   11: dup
   //   12: aconst_null
   //   13: invokespecial 382    android/support/test/internal/runner/RunnerArgs$TestFileArgs:   (Landroid/support/test/internal/runner/RunnerArgs$1;)V
   //   16: astore 7
   //   18: aload_2
   //   19: ifnonnull +6 -> 25
   //   22: aload 7
   //   24: areturn
   //   25: aload_0
   //   26: aload_1
   //   27: aload_2
   //   28: invokespecial 384    android/support/test/internal/runner/RunnerArgs$Builder:openFile    (Landroid/app/Instrumentation;Ljava/lang/String;)Ljava/io/BufferedReader;
   //   31: astore_1
   //   32: aload_1
   //   33: astore_3
   //   34: aload_1
   //   35: astore 4
   //   37: aload_1
   //   38: astore 5
   //   40: aload_1
   //   41: invokevirtual 387    java/io/BufferedReader:readLine ()Ljava/lang/String;
   //   44: astore 8
   //   46: aload 8
   //   48: ifnull +165 -> 213
   //   51: aload_1
   //   52: astore_3
   //   53: aload_1
   //   54: astore 4
   //   56: aload_1
   //   57: astore 5
   //   59: aload 8
   //   61: invokestatic 208 java/lang/String:valueOf    (Ljava/lang/Object;)Ljava/lang/String;
   //   64: astore 6
   //   66: aload_1
   //   67: astore_3
   //   68: aload_1
   //   69: astore 4
   //   71: aload_1
   //   72: astore 5
   //   74: aload 6
   //   76: invokevirtual 182    java/lang/String:length ()I
   //   79: ifeq +24 -> 103
   //   82: aload_1
   //   83: astore_3
   //   84: aload_1
   //   85: astore 4
   //   87: aload_1
   //   88: astore 5
   //   90: ldc_w 389
   //   93: aload 6
   //   95: invokevirtual 231    java/lang/String:concat (Ljava/lang/String;)Ljava/lang/String;
   //   98: astore 6
   //   100: goto +23 -> 123
   //   103: aload_1
   //   104: astore_3
   //   105: aload_1
   //   106: astore 4
   //   108: aload_1
   //   109: astore 5
   //   111: new 168 java/lang/String
   //   114: dup
   //   115: ldc_w 389
   //   118: invokespecial 232   java/lang/String:   (Ljava/lang/String;)V
   //   121: astore 6
   //   123: aload_1
   //   124: astore_3
   //   125: aload_1
   //   126: astore 4
   //   128: aload_1
   //   129: astore 5
   //   131: ldc_w 391
   //   134: aload 6
   //   136: invokestatic 397    android/util/Log:i  (Ljava/lang/String;Ljava/lang/String;)I
   //   139: pop
   //   140: aload_1
   //   141: astore_3
   //   142: aload_1
   //   143: astore 4
   //   145: aload_1
   //   146: astore 5
   //   148: aload 8
   //   150: invokestatic 399    android/support/test/internal/runner/RunnerArgs$Builder:isClassOrMethod (Ljava/lang/String;)Z
   //   153: ifeq +30 -> 183
   //   156: aload_1
   //   157: astore_3
   //   158: aload_1
   //   159: astore 4
   //   161: aload_1
   //   162: astore 5
   //   164: aload 7
   //   166: invokestatic 403    android/support/test/internal/runner/RunnerArgs$TestFileArgs:access$2900    (Landroid/support/test/internal/runner/RunnerArgs$TestFileArgs;)Ljava/util/List;
   //   169: aload 8
   //   171: invokestatic 407    android/support/test/internal/runner/RunnerArgs$Builder:parseTestClass  (Ljava/lang/String;)Landroid/support/test/internal/runner/RunnerArgs$TestArg;
   //   174: invokeinterface 198 2 0
   //   179: pop
   //   180: goto -148 -> 32
   //   183: aload_1
   //   184: astore_3
   //   185: aload_1
   //   186: astore 4
   //   188: aload_1
   //   189: astore 5
   //   191: aload 7
   //   193: invokestatic 410    android/support/test/internal/runner/RunnerArgs$TestFileArgs:access$3000    (Landroid/support/test/internal/runner/RunnerArgs$TestFileArgs;)Ljava/util/List;
   //   196: aload 8
   //   198: invokestatic 413    android/support/test/internal/runner/RunnerArgs$Builder:validatePackage (Ljava/lang/String;)Ljava/lang/String;
   //   201: invokestatic 417    android/support/test/internal/runner/RunnerArgs$Builder:parseTestPackages   (Ljava/lang/String;)Ljava/util/List;

가장 관건적인 단계는 반편이 없고 바이트 코드만 있어서 쉽게 판단할 수 없기 때문에 smali와 결합하여 논리를 정리할 수 밖에 없었다.
@VisibleForTesting
static String validatePackage(String paramString)
{
  if (paramString.matches("^([\\p{L}_$][\\p{L}\\p{N}_$]*\\.)*[\\p{L}_$][\\p{L}\\p{N}_$]*$")) {
    return paramString;
  }
  throw new IllegalArgumentException(String.format("\"%s\" not recognized as valid package name", new Object[] { paramString }));
}

validatePackage 오류 보고, 창고에 따라packagename는 "android.media.cts.DecoderTestH265HDR10StaticMetadata"입니다.
그렇다면 왜 이걸 보고했을까.validatePackage에서smali를 통해 원본이 없는 곳에 log를 추가한 결과 오류가 발생했을 때만 추가된 log가 있는 것을 발견했다.이는 오류가 발생했을 때만 validatePackage를 실행할 수 있다는 것을 의미한다. 이 정보는 매우 관건적이다.
parseFromFile에서validatePackage를 검색하면 한 군데만 호출되고 논리를 정리합니다. 다음은 관건적인 정보입니다.
.line 445
invoke-static {v3}, Landroid/support/test/internal/runner/RunnerArgs$Builder;->isClassOrMethod(Ljava/lang/String;)Z
 
move-result v2
 
if-eqz v2, :cond_2
 
.line 446
invoke-static {v0}, Landroid/support/test/internal/runner/RunnerArgs$TestFileArgs;->access$2900(Landroid/support/test/internal/runner/RunnerArgs$TestFileArgs;)Ljava/util/List;
 
move-result-object v2
 
invoke-static {v3}, Landroid/support/test/internal/runner/RunnerArgs$Builder;->parseTestClass(Ljava/lang/String;)Landroid/support/test/internal/runner/RunnerArgs$TestArg;
 
move-result-object v4
 
invoke-interface {v2, v4}, Ljava/util/List;->add(Ljava/lang/Object;)Z
 
goto :goto_0
 
.line 449
:cond_2
invoke-static {v0}, Landroid/support/test/internal/runner/RunnerArgs$TestFileArgs;->access$3000(Landroid/support/test/internal/runner/RunnerArgs$TestFileArgs;)Ljava/util/List;
 
move-result-object v2
 
invoke-static {v3}, Landroid/support/test/internal/runner/RunnerArgs$Builder;->validatePackage(Ljava/lang/String;)Ljava/lang/String;
 
move-result-object v4

다시 말하면 isClassOrMethod를 호출하여false로 인한 문제를 되돌려줍니다
@VisibleForTesting
static boolean isClassOrMethod(String paramString)
{
  return paramString.matches("^([\\p{L}_$][\\p{L}\\p{N}_$]*\\.)*[\\p{Lu}_$][\\p{L}\\p{N}_$]*(#[\\p{L}_$][\\p{L}\\p{N}_$]*)?$");
}

"android.media.cts.DecoderTest#testH265HDR10StaticMetadata"는 정규 표현식을 통과하지 않고validatePackage 논리에 들어가면 자연히 오류가 발생합니다.
그럼 이 "android.media.cts.DecoderTest#testH265HDR10StaticMetadata"는 어디에 값을 붙일까요? 프레임에 vts-tradefed.jar
vts-tradefed 참조.jar, 그중의cts-on-gsi-exclude.xml, 역시 공백이 하나 더 있습니다
문제 해결 및 검증
https://partnerissuetracker.corp.google.com/u/1/issues/116722365
https://android-review.googlesource.com/c/platform/test/vts/+/773225
위의change 패키지를 가지고 9.0R4의tools 폴더에 넣어서 교체하는 것을 발견했는데 사용할 수 없습니다. 구글에는 이런 문제가 자주 발생합니다. 그 프레임워크 도구는 어느 버전으로 편집되었는지 알 수 없습니다. 이것도 구글과 소통할 수 있는 점인 Constance Wu 오효단입니다.
물론 우리는 편집된 vts-tradefed를 로컬로 풀 수 있다.jar는 사용할 수 있지만, 다행히도 여기는 이렇게 번거로울 필요가 없습니다. 왜냐하면 우리가 해석한 것은 excludes이기 때문입니다.txt에서 해석된 오류 결과
우리는 두 개의 파일includes1을 새로 만듭니다.txt(includes.txt), excludes1.txt (excludes.txt의 여분의 공백을 제거합니다)
그리고 나서
adb install CtsMediaTestCases.apk

adb push includes1.txt /data/local/tmp/ajur

adb push excludes1.txt /data/local/tmp/ajur

adb shell am instrument -w -r --no-hidden-api-checks --abi arm64-v8a -e testFile /data/local/tmp/ajur/includes1.txt -e debug false -e newRunListenerMode true -e log true -e notAnnotation android.platform.test.annotations.RestrictedBuildTest,android.platform.test.annotations.AppModeInstant -e notTestFile /data/local/tmp/ajur/excludes1.txt -e timeout_msec 1800000 android.media.cts/android.support.test.runner.AndroidJUnitRunner

case가 정상적으로 실행됨,change가 절대 적용될 수 있음을 설명합니다
총결산
구글 케이스 내보내기 절대 테스트 안 했는데;그리고 프레임 문제지만 사실 논리는 케이스에 있어요.

좋은 웹페이지 즐겨찾기