1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.releaseparser;
18 
19 import com.android.cts.releaseparser.ReleaseProto.*;
20 import com.android.tools.smali.dexlib2.AccessFlags;
21 import com.android.tools.smali.dexlib2.DexFileFactory;
22 import com.android.tools.smali.dexlib2.Opcodes;
23 import com.android.tools.smali.dexlib2.iface.Annotation;
24 import com.android.tools.smali.dexlib2.iface.AnnotationElement;
25 import com.android.tools.smali.dexlib2.iface.ClassDef;
26 import com.android.tools.smali.dexlib2.iface.DexFile;
27 import com.android.tools.smali.dexlib2.iface.Method;
28 import com.android.tools.smali.dexlib2.iface.value.TypeEncodedValue;
29 import com.android.tradefed.testtype.IRemoteTest;
30 
31 import junit.framework.Test;
32 
33 import org.junit.runner.RunWith;
34 import org.junit.runners.Suite.SuiteClasses;
35 
36 import java.io.File;
37 import java.io.FileWriter;
38 import java.io.IOException;
39 import java.io.PrintWriter;
40 import java.lang.reflect.Modifier;
41 import java.net.URL;
42 import java.net.URLClassLoader;
43 import java.nio.charset.StandardCharsets;
44 import java.nio.file.Paths;
45 import java.security.MessageDigest;
46 import java.security.NoSuchAlgorithmException;
47 import java.util.ArrayList;
48 import java.util.Base64;
49 import java.util.Collection;
50 import java.util.Enumeration;
51 import java.util.List;
52 import java.util.Set;
53 import java.util.jar.JarEntry;
54 import java.util.jar.JarFile;
55 
56 class TestSuiteParser {
57     // JUNIT3 Test suffix
58     private static final String TEST_TAG = "Test;";
59     // Some may ends with Tests e.g. cts/tests/tests/accounts/src/android/accounts/cts/AbstractAuthenticatorTests.java
60     private static final String TESTS_TAG = "Tests;";
61     private static final String TEST_PREFIX_TAG = "test";
62     private static final String DEPRECATED_ANNOTATION_TAG = "Ljava/lang/Deprecated;";
63     private static final String RUN_WITH_ANNOTATION_TAG = "Lorg/junit/runner/RunWith;";
64     private static final String TEST_ANNOTATION_TAG = "Lorg/junit/Test;";
65     private static final String SUPPRESS_ANNOTATION_TAG = "/Suppress;";
66     private static final String ANDROID_JUNIT4_TEST_TAG =
67             "Landroid/support/test/runner/AndroidJUnit4;";
68     private static final String PARAMETERIZED_TEST_TAG = "Lorg/junit/runners/Parameterized;";
69 
70     // configuration option
71     private static final String NOT_SHARDABLE_TAG = "not-shardable";
72     // test class option
73     private static final String RUNTIME_HIT_TAG = "runtime-hint";
74     // com.android.tradefed.testtype.AndroidJUnitTest option
75     private static final String PACKAGE_TAG = "package";
76     // com.android.compatibility.common.tradefed.testtype.JarHostTest option
77     private static final String JAR_TAG = "jar";
78     // com.android.tradefed.testtype.GTest option
79     private static final String NATIVE_TEST_DEVICE_PATH_TAG = "native-test-device-path";
80     private static final String MODULE_TAG = "module-name";
81     private static final String TESTCASES_FOLDER_FORMAT = "testcases/%s";
82 
83     private static final String SUITE_API_INSTALLER_TAG =
84             "com.android.tradefed.targetprep.suite.SuiteApkInstaller";
85     private static final String HOST_TEST_CLASS_TAG =
86             "com.android.compatibility.common.tradefed.testtype.JarHostTest";
87     // com.android.tradefed.targetprep.suite.SuiteApkInstaller option
88     private static final String TEST_FILE_NAME_TAG = "test-file-name";
89     // com.android.compatibility.common.tradefed.targetprep.FilePusher option
90     private static final String PUSH_TAG = "push";
91 
92     // test class
93     private static final String ANDROID_JUNIT_TEST_TAG =
94             "com.android.tradefed.testtype.AndroidJUnitTest";
95     private static final String DEQP_TEST_TAG = "com.drawelements.deqp.runner.DeqpTestRunner";
96     private static final String GTEST_TAG = "com.android.tradefed.testtype.GTest";
97     private static final String LIBCORE_TEST_TAG = "com.android.compatibility.testtype.LibcoreTest";
98     private static final String DALVIK_TEST_TAG = "com.android.compatibility.testtype.DalvikTest";
99 
100     // Target File Extensions
101     private static final String CONFIG_EXT_TAG = ".config";
102     private static final String CONFIG_REGEX = ".config$";
103     private static final String JAR_EXT_TAG = ".jar";
104     private static final String APK_EXT_TAG = ".apk";
105     private static final String SO_EXT_TAG = ".so";
106 
107     // [module].[class]#[method]
108     public static final String TESTCASE_NAME_FORMAT = "%s.%s#%s";
109 
110     private final String mFolderPath;
111     private ReleaseContent mRelContent;
112     private TestSuite.Builder mTSBuilder;
113 
TestSuiteParser(ReleaseContent relContent, String folder)114     TestSuiteParser(ReleaseContent relContent, String folder) {
115         mFolderPath = folder;
116         mRelContent = relContent;
117     }
118 
getTestSuite()119     public TestSuite getTestSuite() {
120         if (mTSBuilder == null) {
121             mTSBuilder = praseTestSuite();
122         }
123         return mTSBuilder.build();
124     }
125 
praseTestSuite()126     private TestSuite.Builder praseTestSuite() {
127         TestSuite.Builder tsBuilder = TestSuite.newBuilder();
128 
129         tsBuilder.setName(mRelContent.getName());
130         tsBuilder.setVersion(mRelContent.getVersion());
131         tsBuilder.setBuildNumber(mRelContent.getBuildNumber());
132 
133         // Iterates all file
134         for (Entry entry : getFileEntriesList(mRelContent)) {
135             // Only parses test module config files
136             if (Entry.EntryType.TEST_MODULE_CONFIG == entry.getType()) {
137                 TestModuleConfig config = entry.getTestModuleConfig();
138                 TestSuite.Module.Builder moduleBuilder = praseModule(config);
139                 moduleBuilder.setConfigFile(entry.getRelativePath());
140                 tsBuilder.addModules(moduleBuilder);
141             }
142         }
143         return tsBuilder;
144     }
145 
praseModule(TestModuleConfig config)146     private TestSuite.Module.Builder praseModule(TestModuleConfig config) {
147         TestSuite.Module.Builder moduleBuilder = TestSuite.Module.newBuilder();
148         // parse test package and class
149         List<TestModuleConfig.TestClass> testClassesList = config.getTestClassesList();
150         moduleBuilder.setName(config.getModuleName());
151         for (TestModuleConfig.TestClass tClass : testClassesList) {
152             String testClass = tClass.getTestClass();
153             moduleBuilder.setTestClass(testClass);
154             switch (testClass) {
155                 case ANDROID_JUNIT_TEST_TAG:
156                     moduleBuilder.setTestType(TestSuite.TestType.ANDROIDJUNIT);
157                     parseAndroidJUnitTest(moduleBuilder, config, tClass.getTestClass());
158                     break;
159                 case HOST_TEST_CLASS_TAG:
160                     moduleBuilder.setTestType(TestSuite.TestType.JAVAHOST);
161                     parseJavaHostTest(moduleBuilder, config, tClass.getTestClass());
162                     break;
163                 default:
164                     //ToDo
165                     moduleBuilder.setTestType(TestSuite.TestType.UNKNOWN);
166                     ApiPackage.Builder pkgBuilder = ApiPackage.newBuilder();
167                     moduleBuilder.addPackages(pkgBuilder);
168                     System.err.printf(
169                             "ToDo Test Type: %s %s\n", tClass.getTestClass(), tClass.getPackage());
170             }
171         }
172         return moduleBuilder;
173     }
174 
parseAndroidJUnitTest( TestSuite.Module.Builder moduleBuilder, TestModuleConfig config, String tClass)175     private void parseAndroidJUnitTest(
176             TestSuite.Module.Builder moduleBuilder, TestModuleConfig config, String tClass) {
177         // getting apk list from Test Module Configuration
178         List<TestModuleConfig.TargetPreparer> tPrepList = config.getTargetPreparersList();
179         for (TestModuleConfig.TargetPreparer tPrep : tPrepList) {
180             for (Option opt : tPrep.getOptionsList()) {
181                 if (TEST_FILE_NAME_TAG.equalsIgnoreCase(opt.getName())) {
182                     ApiPackage.Builder pkgBuilder = ApiPackage.newBuilder();
183                     String testFileName = opt.getValue();
184                     Entry tEntry = getFileEntry(testFileName);
185                     pkgBuilder.setName(testFileName);
186                     pkgBuilder.setPackageFile(tEntry.getRelativePath());
187                     pkgBuilder.setContentId(tEntry.getContentId());
188                     parseApkTestCase(pkgBuilder, config);
189                     moduleBuilder.addPackages(pkgBuilder);
190                 }
191             }
192         }
193     }
194 
getFileEntry(String name)195     private Entry getFileEntry(String name) {
196         Entry fEntry = null;
197         for (Entry et : getFileEntriesList(mRelContent)) {
198             if (name.equals(et.getName())) {
199                 fEntry = et;
200                 break;
201             }
202         }
203         return fEntry;
204     }
205     // Parses test case list from an APK
parseApkTestCase(ApiPackage.Builder pkgBuilder, TestModuleConfig config)206     private void parseApkTestCase(ApiPackage.Builder pkgBuilder, TestModuleConfig config) {
207         DexFile dexFile = null;
208         String apkPath = Paths.get(mFolderPath, pkgBuilder.getPackageFile()).toString();
209         String moduleName = config.getModuleName();
210 
211         // Loads a Dex file
212         try {
213             dexFile = DexFileFactory.loadDexFile(apkPath, Opcodes.getDefault());
214 
215             // Iterates through all clesses in the Dex file
216             for (ClassDef classDef : dexFile.getClasses()) {
217                 // adjust the format Lclass/y;
218                 String className = classDef.getType().replace('/', '.');
219                 // remove L...;
220                 if (className.length() > 2) {
221                     className = className.substring(1, className.length() - 1);
222                 }
223 
224                 // Parses test classes
225                 TestClassType cType = chkTestClassType(classDef);
226                 ApiClass.Builder tClassBuilder = ApiClass.newBuilder();
227                 switch (cType) {
228                     case JUNIT3:
229                         tClassBuilder.setTestClassType(cType);
230                         tClassBuilder.setName(className);
231                         // Checks all test method
232                         for (Method method : classDef.getMethods()) {
233                             // Only care about Public
234                             if ((method.getAccessFlags() & AccessFlags.PUBLIC.getValue()) != 0) {
235                                 String mName = method.getName();
236                                 // Warn current test result accounting does not work well with Supress
237                                 if (hasAnnotationSuffix(
238                                         method.getAnnotations(), SUPPRESS_ANNOTATION_TAG)) {
239                                     System.err.printf("%s#%s with Suppress:\n", className, mName);
240                                 } else if (mName.startsWith(TEST_PREFIX_TAG)) {
241                                     // Junit3 style test case name starts with test
242                                     tClassBuilder.addMethods(
243                                             newTestBuilder(
244                                                     moduleName, className, method.getName()));
245                                 } else if (hasAnnotationSuffix(
246                                         method.getAnnotations(), TEST_ANNOTATION_TAG)) {
247                                     tClassBuilder.addMethods(
248                                             newTestBuilder(
249                                                     moduleName, className, method.getName()));
250                                     System.err.printf(
251                                             "%s#%s JUNIT3 mixes with %s annotation:\n",
252                                             className, mName, TEST_ANNOTATION_TAG);
253                                 }
254                             }
255                         }
256                         pkgBuilder.addClasses(tClassBuilder);
257                         break;
258                     case PARAMETERIZED:
259                         // ToDo need to find a way to count Parameterized tests
260                         System.err.printf("To count Parameterized tests: %s\n", className);
261                         tClassBuilder.setTestClassType(cType);
262                         tClassBuilder.setName(className);
263                         for (Method method : classDef.getMethods()) {
264                             // Junit4 style test case annotated with @Test
265                             if (hasAnnotation(method.getAnnotations(), TEST_ANNOTATION_TAG)) {
266                                 tClassBuilder.addMethods(
267                                         newTestBuilder(moduleName, className, method.getName()));
268                             }
269                         }
270                         pkgBuilder.addClasses(tClassBuilder);
271                         break;
272                     case JUNIT4:
273                         tClassBuilder.setTestClassType(cType);
274                         tClassBuilder.setName(className);
275                         for (Method method : classDef.getMethods()) {
276                             // Junit4 style test case annotated with @Test
277                             if (hasAnnotation(method.getAnnotations(), TEST_ANNOTATION_TAG)) {
278                                 tClassBuilder.addMethods(
279                                         newTestBuilder(moduleName, className, method.getName()));
280                             }
281                         }
282                         pkgBuilder.addClasses(tClassBuilder);
283                         break;
284                     default:
285                         // Not a known test class
286                 }
287             }
288         } catch (IOException | DexFileFactory.DexFileNotFoundException ex) {
289             System.err.println("Unable to load dex file: " + apkPath);
290             // ex.printStackTrace();
291         }
292     }
293 
newTestBuilder(String moduleName, String className, String testName)294     private ApiMethod.Builder newTestBuilder(String moduleName, String className, String testName) {
295         ApiMethod.Builder testBuilder = ApiMethod.newBuilder();
296         testBuilder.setName(testName);
297         // Check if it's an known failure
298         String nfFilter = getKnownFailureFilter(moduleName, className, testName);
299         if (null != nfFilter) {
300             testBuilder.setKnownFailureFilter(nfFilter);
301         }
302         return testBuilder;
303     }
304 
parseJavaHostTest( TestSuite.Module.Builder moduleBuilder, TestModuleConfig config, String tClass)305     private void parseJavaHostTest(
306             TestSuite.Module.Builder moduleBuilder, TestModuleConfig config, String tClass) {
307         ApiPackage.Builder pkgBuilder = ApiPackage.newBuilder();
308         //Assuming there is only one test Jar
309         String testFileName = config.getTestJars(0);
310         Entry tEntry = getFileEntry(testFileName);
311         String jarPath = tEntry.getRelativePath();
312 
313         pkgBuilder.setName(testFileName);
314         pkgBuilder.setPackageFile(jarPath);
315         pkgBuilder.setContentId(tEntry.getContentId());
316         Collection<Class<?>> classes =
317                 getJarTestClasses(
318                         Paths.get(mFolderPath, jarPath).toFile(),
319                         // Includes [x]-tradefed.jar for classes such as CompatibilityHostTestBase
320                         Paths.get(mFolderPath, mRelContent.getTestSuiteTradefed()).toFile());
321 
322         for (Class<?> c : classes) {
323             ApiClass.Builder tClassBuilder = ApiClass.newBuilder();
324             tClassBuilder.setTestClassType(TestClassType.JAVAHOST);
325             tClassBuilder.setName(c.getName());
326 
327             for (java.lang.reflect.Method m : c.getMethods()) {
328                 int mdf = m.getModifiers();
329                 if (Modifier.isPublic(mdf) || Modifier.isProtected(mdf)) {
330                     if (m.getName().startsWith(TEST_PREFIX_TAG)) {
331                         ApiMethod.Builder methodBuilder = ApiMethod.newBuilder();
332                         methodBuilder.setName(m.getName());
333                         // Check if it's an known failure
334                         String nfFilter =
335                                 getKnownFailureFilter(
336                                         config.getModuleName(), c.getName(), m.getName());
337                         if (null != nfFilter) {
338                             methodBuilder.setKnownFailureFilter(nfFilter);
339                         }
340                         tClassBuilder.addMethods(methodBuilder);
341                     }
342                 }
343             }
344             pkgBuilder.addClasses(tClassBuilder);
345         }
346         moduleBuilder.addPackages(pkgBuilder);
347     }
348 
hasAnnotation(Set<? extends Annotation> annotations, String tag)349     private static boolean hasAnnotation(Set<? extends Annotation> annotations, String tag) {
350         for (Annotation annotation : annotations) {
351             if (annotation.getType().equals(tag)) {
352                 return true;
353             }
354         }
355         return false;
356     }
357 
hasAnnotationSuffix(Set<? extends Annotation> annotations, String tag)358     private static boolean hasAnnotationSuffix(Set<? extends Annotation> annotations, String tag) {
359         for (Annotation annotation : annotations) {
360             if (annotation.getType().endsWith(tag)) {
361                 return true;
362             }
363         }
364         return false;
365     }
366 
chkTestClassType(ClassDef classDef)367     private static TestClassType chkTestClassType(ClassDef classDef) {
368         // Only care about Public Class
369         if ((classDef.getAccessFlags() & AccessFlags.PUBLIC.getValue()) == 0) {
370             return TestClassType.UNKNOWN;
371         }
372 
373         for (Annotation annotation : classDef.getAnnotations()) {
374             if (annotation.getType().equals(DEPRECATED_ANNOTATION_TAG)) {
375                 return TestClassType.UNKNOWN;
376             }
377             if (annotation.getType().equals(RUN_WITH_ANNOTATION_TAG)) {
378                 for (AnnotationElement annotationEle : annotation.getElements()) {
379                     if ("value".equals(annotationEle.getName())) {
380                         String aValue = ((TypeEncodedValue) annotationEle.getValue()).getValue();
381                         if (ANDROID_JUNIT4_TEST_TAG.equals(aValue)) {
382                             return TestClassType.JUNIT4;
383                         } else if (PARAMETERIZED_TEST_TAG.equals(aValue)) {
384                             return TestClassType.PARAMETERIZED;
385                         }
386                     }
387                 }
388                 System.err.printf("Unknown test class type: %s\n", classDef.getType());
389                 return TestClassType.JUNIT4;
390             }
391         }
392 
393         if (classDef.getType().endsWith(TEST_TAG) || classDef.getType().endsWith(TESTS_TAG)) {
394             return TestClassType.JUNIT3;
395         } else {
396             return TestClassType.UNKNOWN;
397         }
398     }
399 
isTargetClass(List<String> pkgList, String className)400     private static boolean isTargetClass(List<String> pkgList, String className) {
401         boolean found = false;
402         for (String pkg : pkgList) {
403             if (className.startsWith(pkg)) {
404                 found = true;
405                 break;
406             }
407         }
408         return found;
409     }
410 
getJarTestClasses(File jarTestFile, File tfFile)411     private static Collection<Class<?>> getJarTestClasses(File jarTestFile, File tfFile)
412             throws IllegalArgumentException {
413         List<Class<?>> classes = new ArrayList<>();
414 
415         try (JarFile jarFile = new JarFile(jarTestFile)) {
416             Enumeration<JarEntry> e = jarFile.entries();
417 
418             URL[] urls = {
419                 new URL(String.format("jar:file:%s!/", jarTestFile.getAbsolutePath())),
420                 new URL(String.format("jar:file:%s!/", tfFile.getAbsolutePath()))
421             };
422             URLClassLoader cl =
423                     URLClassLoader.newInstance(urls, JarTestFinder.class.getClassLoader());
424 
425             while (e.hasMoreElements()) {
426                 JarEntry je = e.nextElement();
427                 if (je.isDirectory()
428                         || !je.getName().endsWith(".class")
429                         || je.getName().contains("$")
430                         || je.getName().contains("junit/")) {
431                     continue;
432                 }
433                 String className = getClassName(je.getName());
434 
435                 /*if (!className.endsWith("Test")) {
436                     continue;
437                 }*/
438                 try {
439                     Class<?> cls = cl.loadClass(className);
440 
441                     if (IRemoteTest.class.isAssignableFrom(cls)
442                             || Test.class.isAssignableFrom(cls)) {
443                         classes.add(cls);
444                     } else if (!Modifier.isAbstract(cls.getModifiers())
445                             && hasJUnit4Annotation(cls)) {
446                         classes.add(cls);
447                     }
448                 } catch (ClassNotFoundException | Error x) {
449                     System.err.println(
450                             String.format(
451                                     "Cannot find test class %s from %s",
452                                     className, jarTestFile.getName()));
453                     x.printStackTrace();
454                 }
455             }
456         } catch (IOException e) {
457             e.printStackTrace();
458         }
459         return classes;
460     }
461 
462     /** Helper to determine if we are dealing with a Test class with Junit4 annotations. */
hasJUnit4Annotation(Class<?> classObj)463     protected static boolean hasJUnit4Annotation(Class<?> classObj) {
464         if (classObj.isAnnotationPresent(SuiteClasses.class)) {
465             return true;
466         }
467         if (classObj.isAnnotationPresent(RunWith.class)) {
468             return true;
469         }
470         /*for (Method m : classObj.getMethods()) {
471             if (m.isAnnotationPresent(org.junit.Test.class)) {
472                 return true;
473             }
474         }*/
475         return false;
476     }
477 
getClassName(String name)478     private static String getClassName(String name) {
479         // -6 because of .class
480         return name.substring(0, name.length() - 6).replace('/', '.');
481     }
482 
getKnownFailureFilter(String tModule, String tClass, String tMethod)483     private String getKnownFailureFilter(String tModule, String tClass, String tMethod) {
484         List<String> knownFailures = mRelContent.getKnownFailuresList();
485         String tsName = String.format(TESTCASE_NAME_FORMAT, tModule, tClass, tMethod);
486         for (String kf : knownFailures) {
487             if (tsName.startsWith(kf)) {
488                 return kf;
489             }
490         }
491         return null;
492     }
493 
494     // Iterates though all test suite content and prints them.
writeCsvFile(String relNameVer, String csvFile)495     public void writeCsvFile(String relNameVer, String csvFile) {
496         TestSuite ts = getTestSuite();
497         try {
498             FileWriter fWriter = new FileWriter(csvFile);
499             PrintWriter pWriter = new PrintWriter(fWriter);
500             //Header
501             pWriter.println(
502                     "release,module,test_class,test,test_package,test_type,known_failure_filter,package_content_id");
503             for (TestSuite.Module module : ts.getModulesList()) {
504                 for (ApiPackage pkg : module.getPackagesList()) {
505                     for (ApiClass cls : pkg.getClassesList()) {
506                         for (ApiMethod mtd : cls.getMethodsList()) {
507                             pWriter.printf(
508                                     "%s,%s,%s,%s,%s,%s,%s,%s\n",
509                                     relNameVer,
510                                     module.getName(),
511                                     cls.getName(),
512                                     mtd.getName(),
513                                     pkg.getPackageFile(),
514                                     cls.getTestClassType(),
515                                     mtd.getKnownFailureFilter(),
516                                     getTestTargetContentId(pkg.getPackageFile()));
517                         }
518                     }
519                 }
520             }
521             pWriter.flush();
522             pWriter.close();
523         } catch (IOException e) {
524             System.err.println("IOException:" + e.getMessage());
525         }
526     }
527 
528     // Iterates though all test module and prints them.
writeModuleCsvFile(String relNameVer, String csvFile)529     public void writeModuleCsvFile(String relNameVer, String csvFile) {
530         TestSuite ts = getTestSuite();
531         try {
532             FileWriter fWriter = new FileWriter(csvFile);
533             PrintWriter pWriter = new PrintWriter(fWriter);
534 
535             //Header
536             pWriter.print(
537                     "release,module,test_no,known_failure_no,test_type,test_class,component,description,test_config_file,test_file_names,test_jars,module_content_id\n");
538 
539             for (TestSuite.Module module : ts.getModulesList()) {
540                 int classCnt = 0;
541                 int methodCnt = 0;
542                 int kfCnt = 0;
543                 for (ApiPackage pkg : module.getPackagesList()) {
544                     for (ApiClass cls : pkg.getClassesList()) {
545                         for (ApiMethod mtd : cls.getMethodsList()) {
546                             // Filter out known failures
547                             if (mtd.getKnownFailureFilter().isEmpty()) {
548                                 methodCnt++;
549                             } else {
550                                 kfCnt++;
551                             }
552                         }
553                         classCnt++;
554                     }
555                 }
556                 String config = module.getConfigFile();
557                 Entry entry = mRelContent.getEntries().get(config);
558                 TestModuleConfig tmConfig = entry.getTestModuleConfig();
559                 pWriter.printf(
560                         "%s,%s,%d,%d,%s,%s,%s,%s,%s,%s,%s,%s\n",
561                         relNameVer,
562                         module.getName(),
563                         methodCnt,
564                         kfCnt,
565                         module.getTestType(),
566                         module.getTestClass(),
567                         tmConfig.getComponent(),
568                         tmConfig.getDescription(),
569                         config,
570                         String.join(" ", tmConfig.getTestFileNamesList()),
571                         String.join(" ", tmConfig.getTestJarsList()),
572                         getTestModuleContentId(
573                                 entry,
574                                 tmConfig.getTestFileNamesList(),
575                                 tmConfig.getTestJarsList()));
576             }
577             pWriter.flush();
578             pWriter.close();
579         } catch (IOException e) {
580             System.err.println("IOException:" + e.getMessage());
581         }
582     }
583 
getFileEntriesList(ReleaseContent relContent)584     public static Collection<Entry> getFileEntriesList(ReleaseContent relContent) {
585         return relContent.getEntries().values();
586     }
587 
588     // get Test Module Content Id = config cid + apk cids + jar cids
getTestModuleContentId(Entry config, List<String> apks, List<String> jars)589     private String getTestModuleContentId(Entry config, List<String> apks, List<String> jars) {
590         String id = null;
591         //Starts with config file content_id
592         String idStr = config.getContentId();
593         try {
594             MessageDigest md = MessageDigest.getInstance("SHA-256");
595             //Add all apk content_id
596             for (String apk : apks) {
597                 idStr += getTestTargetContentId(String.format(TESTCASES_FOLDER_FORMAT, apk));
598             }
599             //Add all jar content_id
600             for (String jar : jars) {
601                 idStr += getTestTargetContentId(String.format(TESTCASES_FOLDER_FORMAT, jar));
602             }
603             md.update(idStr.getBytes(StandardCharsets.UTF_8));
604             // Converts to Base64 String
605             id = Base64.getEncoder().encodeToString(md.digest());
606             // System.out.println("getTestModuleContentId: " + idStr);
607         } catch (NoSuchAlgorithmException e) {
608             System.err.println("NoSuchAlgorithmException:" + e.getMessage());
609         }
610         return id;
611     }
612 
getTestTargetContentId(String targetFile)613     private String getTestTargetContentId(String targetFile) {
614         Entry entry = mRelContent.getEntries().get(targetFile);
615         if (entry != null) {
616             return entry.getContentId();
617         } else {
618             System.err.println("No getTestTargetContentId: " + targetFile);
619             return "";
620         }
621     }
622 
623 }
624