1 /* 2 * Copyright (C) 2011 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 util.build; 18 19 import java.io.File; 20 import java.io.IOException; 21 import java.util.Collection; 22 import java.util.Collections; 23 import java.util.Comparator; 24 import java.util.List; 25 26 /** 27 * Main class to generate data from the test suite to later run from a shell 28 * script. the project's home folder.<br> 29 * <project-home>/src must contain the java sources<br> 30 * <project-home>/src/<for-each-package>/Main_testN1.java will be generated<br> 31 * (one Main class for each test method in the Test_... class 32 */ 33 public class BuildCTSHostSources extends BuildUtilBase { 34 35 public static final String TARGET_MAIN_FILE = "mains.jar"; 36 37 // the folder for the generated junit-files for the cts host (which in turn 38 // execute the real vm tests using adb push/shell etc) 39 private static String HOSTJUNIT_SRC_OUTPUT_FOLDER = ""; 40 41 private static final String TARGET_JAR_ROOT_PATH = "/data/local/tmp/vm-tests"; 42 private static final String APEX_ART_BIN_PATH = "/apex/com.android.art/bin"; 43 44 /** 45 * @param args 46 * args 0 must be the project root folder (where src, lib etc. 47 * resides) 48 * @throws IOException 49 */ main(String[] args)50 public static void main(String[] args) throws IOException { 51 BuildCTSHostSources cat = new BuildCTSHostSources(); 52 53 if (!cat.parseArgs(args)) { 54 printUsage(); 55 System.exit(-1); 56 } 57 58 long start = System.currentTimeMillis(); 59 cat.run(cat::handleTest); 60 long end = System.currentTimeMillis(); 61 62 System.out.println("elapsed seconds: " + (end - start) / 1000); 63 } 64 parseArgs(String[] args)65 private boolean parseArgs(String[] args) { 66 if (args.length == 1) { 67 HOSTJUNIT_SRC_OUTPUT_FOLDER = args[0]; 68 return true; 69 } else { 70 return false; 71 } 72 } 73 printUsage()74 private static void printUsage() { 75 System.out.println("usage: java-src-folder output-folder classpath " + 76 "generated-main-files compiled_output generated-main-files " + 77 "[restrict-to-opcode]"); 78 } 79 80 private static class HostState { 81 private String fileName; 82 private StringBuilder fileData; 83 HostState(String fileName)84 public HostState(String fileName) { 85 this.fileName = fileName; 86 fileData = new StringBuilder(); 87 } 88 append(String s)89 public void append(String s) { 90 fileData.append(s); 91 } 92 addCTSHostMethod(String pName, String method, Collection<String> dependentTestClassNames)93 private void addCTSHostMethod(String pName, String method, 94 Collection<String> dependentTestClassNames) { 95 fileData.append("public void " + method + "() throws Exception {\n"); 96 final String targetCoreJarPath = String.format("%s/dot/junit/dexcore.jar", 97 TARGET_JAR_ROOT_PATH); 98 99 String mainsJar = String.format("%s/%s", TARGET_JAR_ROOT_PATH, TARGET_MAIN_FILE); 100 101 String cp = String.format("%s:%s", targetCoreJarPath, mainsJar); 102 for (String depFqcn : dependentTestClassNames) { 103 String sourceName = depFqcn.replaceAll("\\.", "/") + ".jar"; 104 String targetName= String.format("%s/%s", TARGET_JAR_ROOT_PATH, 105 sourceName); 106 cp += ":" + targetName; 107 // dot.junit.opcodes.invoke_interface_range.ITest 108 // -> dot/junit/opcodes/invoke_interface_range/ITest.jar 109 } 110 111 //"dot.junit.opcodes.add_double_2addr.Main_testN2"; 112 String mainclass = pName + ".Main_" + method; 113 fileData.append(getShellExecJavaLine(cp, mainclass)); 114 fileData.append("\n}\n\n"); 115 } 116 end()117 public void end() { 118 fileData.append("\n}\n"); 119 } 120 getFileToWrite()121 public File getFileToWrite() { 122 return new File(fileName); 123 } getData()124 public String getData() { 125 return fileData.toString(); 126 } 127 } 128 flushHostState(HostState state)129 private void flushHostState(HostState state) { 130 state.end(); 131 132 File toWrite = state.getFileToWrite(); 133 writeToFileMkdir(toWrite, state.getData()); 134 } 135 openCTSHostFileFor(String pName, String classOnlyName)136 private HostState openCTSHostFileFor(String pName, String classOnlyName) { 137 String sourceName = classOnlyName; 138 139 String modPackage = pName; 140 { 141 // Given a class name of "Test_zzz" and a package of "xxx.yyy.zzz," strip 142 // "zzz" from the package to reduce duplication (and dashboard clutter). 143 int lastDot = modPackage.lastIndexOf('.'); 144 if (lastDot > 0) { 145 String lastPackageComponent = modPackage.substring(lastDot + 1); 146 if (classOnlyName.equals("Test_" + lastPackageComponent)) { 147 // Drop the duplication. 148 modPackage = modPackage.substring(0, lastDot); 149 } 150 } 151 } 152 153 String fileName = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/" + modPackage.replaceAll("\\.", "/") 154 + "/" + sourceName + ".java"; 155 156 HostState newState = new HostState(fileName); 157 158 newState.append(getWarningMessage()); 159 newState.append("package " + modPackage + ";\n"); 160 newState.append("import java.io.IOException;\n" + 161 "import java.util.concurrent.TimeUnit;\n\n" + 162 "import com.android.tradefed.device.CollectingOutputReceiver;\n" + 163 "import com.android.tradefed.testtype.IAbi;\n" + 164 "import com.android.tradefed.testtype.IAbiReceiver;\n" + 165 "import com.android.tradefed.testtype.DeviceTestCase;\n" + 166 "import com.android.tradefed.util.AbiFormatter;\n" + 167 "\n"); 168 newState.append("public class " + sourceName + " extends DeviceTestCase implements " + 169 "IAbiReceiver {\n"); 170 171 newState.append("\n" + 172 "protected IAbi mAbi;\n" + 173 "@Override\n" + 174 "public void setAbi(IAbi abi) {\n" + 175 " mAbi = abi;\n" + 176 "}\n\n"); 177 178 return newState; 179 } 180 getShellExecJavaLine(String classpath, String mainclass)181 private static String getShellExecJavaLine(String classpath, String mainclass) { 182 String cmd = String.format("ANDROID_DATA=%s %s/dalvikvm|#ABI#| -Xmx512M -Xss32K " + 183 "-Djava.io.tmpdir=%s -classpath %s %s", TARGET_JAR_ROOT_PATH, APEX_ART_BIN_PATH, 184 TARGET_JAR_ROOT_PATH, classpath, mainclass); 185 StringBuilder code = new StringBuilder(); 186 code.append(" String cmd = AbiFormatter.formatCmdForAbi(\"") 187 .append(cmd) 188 .append("\", mAbi.getBitness());\n") 189 .append(" CollectingOutputReceiver receiver = new CollectingOutputReceiver();\n") 190 .append(" getDevice().executeShellCommand(cmd, receiver, 6, TimeUnit.MINUTES, 1);\n") 191 .append(" // A sucessful adb shell command returns an empty string.\n") 192 .append(" assertEquals(cmd, \"\", receiver.getOutput());"); 193 return code.toString(); 194 } 195 getWarningMessage()196 private String getWarningMessage() { 197 return "//Autogenerated code by " + this.getClass().getName() + "; do not edit.\n"; 198 } 199 handleTest(String fqcn, List<String> methods)200 private void handleTest(String fqcn, List<String> methods) { 201 int lastDotPos = fqcn.lastIndexOf('.'); 202 String pName = fqcn.substring(0, lastDotPos); 203 String classOnlyName = fqcn.substring(lastDotPos + 1); 204 205 HostState hostState = openCTSHostFileFor(pName, classOnlyName); 206 207 Collections.sort(methods, new Comparator<String>() { 208 @Override 209 public int compare(String s1, String s2) { 210 // TODO sort according: test ... N, B, E, VFE 211 return s1.compareTo(s2); 212 } 213 }); 214 for (String method : methods) { 215 // e.g. testN1 216 if (!method.startsWith("test")) { 217 throw new RuntimeException("no test method: " + method); 218 } 219 220 // generate the Main_xx java class 221 222 // a Main_testXXX.java contains: 223 // package <packagenamehere>; 224 // public class Main_testxxx { 225 // public static void main(String[] args) { 226 // new dxc.junit.opcodes.aaload.Test_aaload().testN1(); 227 // } 228 // } 229 MethodData md = parseTestMethod(pName, classOnlyName, method); 230 String methodContent = md.methodBody; 231 232 List<String> dependentTestClassNames = parseTestClassName(pName, 233 classOnlyName, methodContent); 234 235 hostState.addCTSHostMethod(pName, method, dependentTestClassNames); 236 } 237 238 flushHostState(hostState); 239 } 240 } 241