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 printUsage()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. */ getExpectedArg(String[] args, int index)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 parseTestSuiteFolder(String testSuitePath)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 parseFileMetadata(Entry.Builder fEntry, File 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 parseConfigFile(File file)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 KnownFailuresXmlHandler(TestSuiteContent.Builder tsBld)163 KnownFailuresXmlHandler(TestSuiteContent.Builder tsBld) { 164 mTsBld = tsBld; 165 } 166 167 @Override startElement(String uri, String localName, String name, Attributes attributes)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 parseKnownFailures(TestSuiteContent.Builder tsBld, File file)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 parseFolder(TestSuiteContent.Builder testSuiteContent, String fPath, String rPath)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 getFileContentId(File file)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 getFolderContentId(Entry.Builder folderEntry, List<Entry> entryList)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 getId(String name)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. printTestSuiteContent(TestSuiteContent tsContent)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 main(String[] args)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