Home | History | Annotate | Download | only in apicoverage
      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.apicoverage;
     18 
     19 
     20 import com.android.cts.apicoverage.TestSuiteProto.*;
     21 
     22 import org.xml.sax.InputSource;
     23 import org.xml.sax.XMLReader;
     24 import org.xml.sax.helpers.XMLReaderFactory;
     25 
     26 import java.io.File;
     27 import java.io.FileReader;
     28 import java.io.FileInputStream;
     29 import java.io.FileOutputStream;
     30 import java.io.InputStream;
     31 import java.io.IOException;
     32 import java.nio.charset.StandardCharsets;
     33 import java.nio.file.Path;
     34 import java.nio.file.Paths;
     35 import java.security.NoSuchAlgorithmException;
     36 import java.security.MessageDigest;
     37 import java.util.ArrayList;
     38 import java.util.Enumeration;
     39 import java.util.List;
     40 import java.util.zip.ZipEntry;
     41 import java.util.zip.ZipFile;
     42 
     43 import javax.xml.parsers.SAXParser;
     44 import javax.xml.parsers.SAXParserFactory;
     45 
     46 import org.xml.sax.Attributes;
     47 import org.xml.sax.SAXException;
     48 import org.xml.sax.helpers.DefaultHandler;
     49 
     50 class TestSuiteContentReport {
     51     // configuration option
     52     private static final String NOT_SHARDABLE_TAG = "not-shardable";
     53     // test class option
     54     private static final String RUNTIME_HIT_TAG = "runtime-hint";
     55     // com.android.tradefed.testtype.AndroidJUnitTest option
     56     private static final String PACKAGE_TAG = "package";
     57     // com.android.compatibility.common.tradefed.testtype.JarHostTest option
     58     private static final String JAR_NAME_TAG = "jar";
     59     // com.android.tradefed.testtype.GTest option
     60     private static final String NATIVE_TEST_DEVICE_PATH_TAG = "native-test-device-path";
     61     private static final String MODULE_TAG = "module-name";
     62 
     63     private static final String SUITE_API_INSTALLER_TAG = "com.android.tradefed.targetprep.suite.SuiteApkInstaller";
     64     private static final String JAR_HOST_TEST_TAG = "com.android.compatibility.common.tradefed.testtype.JarHostTest";
     65     // com.android.tradefed.targetprep.suite.SuiteApkInstaller option
     66     private static final String TEST_FILE_NAME_TAG = "test-file-name";
     67     // com.android.compatibility.common.tradefed.targetprep.FilePusher option
     68     private static final String PUSH_TAG = "push";
     69 
     70     // test class
     71     private static final String ANDROID_JUNIT_TEST_TAG = "com.android.tradefed.testtype.AndroidJUnitTest";
     72 
     73     // Target File Extensions
     74     private static final String CONFIG_EXT_TAG = ".config";
     75     private static final String JAR_EXT_TAG = ".jar";
     76     private static final String APK_EXT_TAG = ".apk";
     77     private static final String SO_EXT_TAG = ".so";
     78     private static final String TEST_SUITE_HARNESS = "-tradefed.jar";
     79     private static final String KNOWN_FAILURES_XML_FILE = "-known-failures.xml";
     80 
     81     private static final String OPTION_TAG = "option";
     82     private static final String NAME_TAG = "name";
     83     private static final String EXCLUDE_FILTER_TAG = "compatibility:exclude-filter";
     84     private static final String VALUE_TAG = "value";
     85 
     86 
     87     private static void printUsage() {
     88         System.out.println("Usage: test-suite-content-report [OPTION]...");
     89         System.out.println();
     90         System.out.println("Generates test suite content protocal buffer message.");
     91         System.out.println();
     92         System.out.println(
     93                 "$ANDROID_HOST_OUT/bin/test-suite-content-report "
     94                         + "-i out/host/linux-x86/cts/android-cts "
     95                         + "-o ./cts-content.pb"
     96                         + "-t ./cts-list.pb");
     97         System.out.println();
     98         System.out.println("Options:");
     99         System.out.println("  -i PATH                path to the Test Suite Folder");
    100         System.out.println("  -o FILE                output file of Test Content Protocal Buffer");
    101         System.out.println("  -t FILE                output file of Test Case List Protocal Buffer");
    102         System.out.println();
    103         System.exit(1);
    104     }
    105 
    106     /** Get the argument or print out the usage and exit. */
    107     private static String getExpectedArg(String[] args, int index) {
    108         if (index < args.length) {
    109             return args[index];
    110         } else {
    111             printUsage();
    112             return null;    // Never will happen because printUsage will call exit(1)
    113         }
    114     }
    115 
    116     public static TestSuiteContent parseTestSuiteFolder(String testSuitePath)
    117             throws IOException, NoSuchAlgorithmException {
    118 
    119         TestSuiteContent.Builder testSuiteContent = TestSuiteContent.newBuilder();
    120         testSuiteContent.addFileEntries(parseFolder(testSuiteContent, testSuitePath, testSuitePath));
    121         return testSuiteContent.build();
    122     }
    123 
    124     // Parse a file
    125     private static FileMetadata parseFileMetadata(Entry.Builder fEntry, File file)
    126             throws Exception {
    127         if (file.getName().endsWith(CONFIG_EXT_TAG)) {
    128             fEntry.setType(Entry.EntryType.CONFIG);
    129             return parseConfigFile(file);
    130         } else if (file.getName().endsWith(APK_EXT_TAG)) {
    131             fEntry.setType(Entry.EntryType.APK);
    132         } else if (file.getName().endsWith(JAR_EXT_TAG)) {
    133             fEntry.setType(Entry.EntryType.JAR);
    134         } else if (file.getName().endsWith(SO_EXT_TAG)) {
    135             fEntry.setType(Entry.EntryType.SO);
    136         } else {
    137             // Just file in general
    138             fEntry.setType(Entry.EntryType.FILE);
    139         }
    140         return null;
    141     }
    142 
    143     private static FileMetadata parseConfigFile(File file)
    144             throws Exception {
    145         XMLReader xmlReader = XMLReaderFactory.createXMLReader();
    146         TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler(file.getName());
    147         xmlReader.setContentHandler(testModuleXmlHandler);
    148         FileReader fileReader = null;
    149         try {
    150             fileReader = new FileReader(file);
    151             xmlReader.parse(new InputSource(fileReader));
    152             return testModuleXmlHandler.getFileMetadata();
    153         } finally {
    154             if (null != fileReader) {
    155                 fileReader.close();
    156             }
    157         }
    158     }
    159 
    160     private static class KnownFailuresXmlHandler extends DefaultHandler {
    161         private TestSuiteContent.Builder mTsBld = null;
    162 
    163         KnownFailuresXmlHandler(TestSuiteContent.Builder tsBld) {
    164             mTsBld = tsBld;
    165         }
    166 
    167         @Override
    168         public void startElement(String uri, String localName, String name, Attributes attributes)
    169                 throws SAXException {
    170             super.startElement(uri, localName, name, attributes);
    171 
    172             System.err.printf(
    173                     "ele %s: %s: %s \n",
    174                     localName, attributes.getValue(NAME_TAG), attributes.getValue(VALUE_TAG));
    175             if (EXCLUDE_FILTER_TAG.equals(attributes.getValue(NAME_TAG))) {
    176                 String kfFilter = attributes.getValue(VALUE_TAG).replace(' ', '.');
    177                 mTsBld.addKnownFailures(kfFilter);
    178             }
    179         }
    180     }
    181 
    182     private static void parseKnownFailures(TestSuiteContent.Builder tsBld, File file)
    183             throws Exception {
    184 
    185         ZipFile zip = new ZipFile(file);
    186         try {
    187             Enumeration<? extends ZipEntry> entries = zip.entries();
    188             while (entries.hasMoreElements()) {
    189                 ZipEntry entry = entries.nextElement();
    190 
    191                 if (entry.getName().endsWith(KNOWN_FAILURES_XML_FILE)) {
    192                     SAXParserFactory spf = SAXParserFactory.newInstance();
    193                     spf.setNamespaceAware(false);
    194                     SAXParser saxParser = spf.newSAXParser();
    195                     InputStream xmlStream = zip.getInputStream(entry);
    196                     KnownFailuresXmlHandler kfXmlHandler = new KnownFailuresXmlHandler(tsBld);
    197                     saxParser.parse(xmlStream, kfXmlHandler);
    198                     xmlStream.close();
    199                 }
    200             }
    201         } finally {
    202             zip.close();
    203         }
    204     }
    205 
    206     // Parse a folder to add all entries
    207     private static Entry.Builder parseFolder(TestSuiteContent.Builder testSuiteContent, String fPath, String rPath)
    208             throws IOException, NoSuchAlgorithmException {
    209         Entry.Builder folderEntry = Entry.newBuilder();
    210 
    211         File folder = new File(fPath);
    212         File rFolder = new File(rPath);
    213         Path folderPath = Paths.get(folder.getAbsolutePath());
    214         Path rootPath = Paths.get(rFolder.getAbsolutePath());
    215         String folderRelativePath = rootPath.relativize(folderPath).toString();
    216         String folderId = getId(folderRelativePath);
    217         File[] fileList = folder.listFiles();
    218         Long folderSize = 0L;
    219         List <Entry> entryList = new ArrayList<Entry> ();
    220         for (File file : fileList){
    221             if (file.isFile()){
    222                 String fileRelativePath = rootPath.relativize(Paths.get(file.getAbsolutePath())).toString();
    223                 Entry.Builder fileEntry = Entry.newBuilder();
    224                 fileEntry.setId(getId(fileRelativePath));
    225                 fileEntry.setName(file.getName());
    226                 fileEntry.setSize(file.length());
    227                 fileEntry.setContentId(getFileContentId(file));
    228                 fileEntry.setRelativePath(fileRelativePath);
    229                 fileEntry.setParentId(folderId);
    230                 try {
    231                     FileMetadata fMetadata = parseFileMetadata(fileEntry, file);
    232                     if (null != fMetadata) {
    233                         fileEntry.setFileMetadata(fMetadata);
    234                     }
    235                     // get [cts]-known-failures.xml
    236                     if (file.getName().endsWith(TEST_SUITE_HARNESS)) {
    237                         parseKnownFailures(testSuiteContent, file);
    238                     }
    239                 } catch (Exception ex) {
    240                     System.err.println(
    241                             String.format("Cannot parse %s",
    242                                     file.getAbsolutePath()));
    243                     ex.printStackTrace();
    244                 }
    245                 testSuiteContent.addFileEntries(fileEntry);
    246                 entryList.add(fileEntry.build());
    247                 folderSize += file.length();
    248             } else if (file.isDirectory()){
    249                 Entry.Builder subFolderEntry = parseFolder(testSuiteContent, file.getAbsolutePath(), rPath);
    250                 subFolderEntry.setParentId(folderId);
    251                 testSuiteContent.addFileEntries(subFolderEntry);
    252                 folderSize += subFolderEntry.getSize();
    253                 entryList.add(subFolderEntry.build());
    254             }
    255         }
    256 
    257         folderEntry.setId(folderId);
    258         folderEntry.setName(folderRelativePath);
    259         folderEntry.setSize(folderSize);
    260         folderEntry.setType(Entry.EntryType.FOLDER);
    261         folderEntry.setContentId(getFolderContentId(folderEntry, entryList));
    262         folderEntry.setRelativePath(folderRelativePath);
    263         return folderEntry;
    264     }
    265 
    266     private static String getFileContentId(File file)
    267             throws IOException, NoSuchAlgorithmException {
    268         MessageDigest md = MessageDigest.getInstance("SHA-256");
    269         FileInputStream fis = new FileInputStream(file);
    270 
    271         byte[] dataBytes = new byte[10240];
    272 
    273         int nread = 0;
    274         while ((nread = fis.read(dataBytes)) != -1) {
    275           md.update(dataBytes, 0, nread);
    276         };
    277         byte[] mdbytes = md.digest();
    278 
    279         // Converts to Hex String
    280         StringBuffer hexString = new StringBuffer();
    281         for (int i=0;i<mdbytes.length;i++) {
    282             hexString.append(Integer.toHexString(0xFF & mdbytes[i]));
    283         }
    284         return hexString.toString();
    285     }
    286 
    287     private static String getFolderContentId(Entry.Builder folderEntry, List<Entry> entryList)
    288             throws IOException, NoSuchAlgorithmException {
    289         MessageDigest md = MessageDigest.getInstance("SHA-256");
    290 
    291         for (Entry entry: entryList) {
    292             md.update(entry.getContentId().getBytes(StandardCharsets.UTF_8));
    293         }
    294         byte[] mdbytes = md.digest();
    295 
    296         // Converts to Hex String
    297         StringBuffer hexString = new StringBuffer();
    298         for (int i=0;i<mdbytes.length;i++) {
    299             hexString.append(Integer.toHexString(0xFF & mdbytes[i]));
    300         }
    301         return hexString.toString();
    302     }
    303 
    304     private static String getId(String name)
    305             throws IOException, NoSuchAlgorithmException {
    306         MessageDigest md = MessageDigest.getInstance("SHA-256");
    307         md.update(name.getBytes(StandardCharsets.UTF_8));
    308         byte[] mdbytes = md.digest();
    309 
    310         // Converts to Hex String
    311         StringBuffer hexString = new StringBuffer();
    312         for (int i=0;i<mdbytes.length;i++) {
    313             hexString.append(Integer.toHexString(0xFF & mdbytes[i]));
    314         }
    315         return hexString.toString();
    316     }
    317 
    318     // Iterates though all test suite content and prints them.
    319     static void printTestSuiteContent(TestSuiteContent tsContent) {
    320         //Header
    321         System.out.printf("no,type,name,size,relative path,id,content id,parent id,description,test class");
    322         // test class header
    323         System.out.printf(",%s,%s,%s,%s,%s",
    324                 RUNTIME_HIT_TAG, PACKAGE_TAG, JAR_NAME_TAG, NATIVE_TEST_DEVICE_PATH_TAG, MODULE_TAG);
    325         // target preparer header
    326         System.out.printf(",%s,%s\n",
    327                 TEST_FILE_NAME_TAG, PUSH_TAG);
    328 
    329         int i = 1;
    330         for (Entry entry: tsContent.getFileEntriesList()) {
    331             System.out.printf("%d,%s,%s,%d,%s,%s,%s,%s",
    332                 i++, entry.getType(), entry.getName(), entry.getSize(),
    333                 entry.getRelativePath(), entry.getId(), entry.getContentId(),
    334                 entry.getParentId());
    335 
    336             if (Entry.EntryType.CONFIG == entry.getType()) {
    337                 ConfigMetadata config = entry.getFileMetadata().getConfigMetadata();
    338                 System.out.printf(",%s", entry.getFileMetadata().getDescription());
    339                 List<Option> optList;
    340                 List<ConfigMetadata.TestClass> testClassesList = config.getTestClassesList();
    341                 String rtHit = "";
    342                 String pkg = "";
    343                 String jar = "";
    344                 String ntdPath = "";
    345                 String module = "";
    346 
    347                 for (ConfigMetadata.TestClass tClass : testClassesList) {
    348                     System.out.printf(",%s", tClass.getTestClass());
    349                     optList = tClass.getOptionsList();
    350                     for (Option opt : optList) {
    351                         if (RUNTIME_HIT_TAG.equalsIgnoreCase(opt.getName())) {
    352                             rtHit = rtHit + opt.getValue() + " ";
    353                         } else if (PACKAGE_TAG.equalsIgnoreCase(opt.getName())) {
    354                             pkg = pkg + opt.getValue() + " ";
    355                         } else if (JAR_NAME_TAG.equalsIgnoreCase(opt.getName())) {
    356                             jar = jar + opt.getValue() + " ";
    357                         } else if (NATIVE_TEST_DEVICE_PATH_TAG.equalsIgnoreCase(opt.getName())) {
    358                             ntdPath = ntdPath + opt.getValue() + " ";
    359                         } else if (MODULE_TAG.equalsIgnoreCase(opt.getName())) {
    360                             module = module + opt.getValue() + " ";
    361                         }
    362                     }
    363                 }
    364                 System.out.printf(",%s,%s,%s,%s,%s", rtHit.trim(), pkg.trim(),
    365                         jar.trim(), module.trim(), ntdPath.trim());
    366 
    367                 List<ConfigMetadata.TargetPreparer> tPrepList = config.getTargetPreparersList();
    368                 String testFile = "";
    369                 String pushList = "";
    370                 for (ConfigMetadata.TargetPreparer tPrep : tPrepList) {
    371                     optList = tPrep.getOptionsList();
    372                     for (Option opt : optList) {
    373                         if (TEST_FILE_NAME_TAG.equalsIgnoreCase(opt.getName())) {
    374                             testFile = testFile + opt.getValue() + " ";
    375                         } else if (PUSH_TAG.equalsIgnoreCase(opt.getName())) {
    376                             pushList = pushList + opt.getValue() + " ";
    377                         }
    378                     }
    379                 }
    380                 System.out.printf(",%s,%s", testFile.trim(), pushList.trim());
    381             }
    382             System.out.printf("\n");
    383         }
    384 
    385         System.out.printf("\nKnown Failures\n");
    386         for (String kf : tsContent.getKnownFailuresList()) {
    387             System.out.printf("%s\n", kf);
    388         }
    389     }
    390 
    391     public static void main(String[] args)
    392             throws IOException, NoSuchAlgorithmException {
    393         String outputFilePath = "./tsContentMessage.pb";
    394         String outputTestCaseListFilePath = "./tsTestCaseList.pb";
    395         String tsPath = "";
    396 
    397         for (int i = 0; i < args.length; i++) {
    398             if (args[i].startsWith("-")) {
    399                 if ("-o".equals(args[i])) {
    400                     outputFilePath = getExpectedArg(args, ++i);
    401                 } else if ("-t".equals(args[i])) {
    402                     outputTestCaseListFilePath = getExpectedArg(args, ++i);
    403                 } else if ("-i".equals(args[i])) {
    404                     tsPath = getExpectedArg(args, ++i);
    405                     File file = new File(tsPath);
    406                     // Only acception a folder
    407                     if (!file.isDirectory()) {
    408                         printUsage();
    409                     }
    410                 } else {
    411                     printUsage();
    412                 }
    413             }
    414         }
    415 
    416         TestSuiteContent tsContent = parseTestSuiteFolder(tsPath);
    417 
    418         // Write test suite content message to disk.
    419         FileOutputStream output = new FileOutputStream(outputFilePath);
    420         try {
    421           tsContent.writeTo(output);
    422         } finally {
    423           output.close();
    424         }
    425 
    426         // Read message from the file and print them out
    427         TestSuiteContent tsContent1 =
    428                 TestSuiteContent.parseFrom(new FileInputStream(outputFilePath));
    429         printTestSuiteContent(tsContent1);
    430     }
    431 }
    432