1 /* 2 * Copyright (C) 2015 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 android.databinding.compilationTest; 18 19 import org.apache.commons.io.FileUtils; 20 import org.apache.commons.io.IOUtils; 21 import org.apache.commons.lang3.StringUtils; 22 import org.junit.Before; 23 import org.junit.Rule; 24 import org.junit.rules.TestName; 25 26 import android.databinding.tool.store.Location; 27 28 import java.io.BufferedInputStream; 29 import java.io.BufferedReader; 30 import java.io.File; 31 import java.io.FileOutputStream; 32 import java.io.IOException; 33 import java.io.InputStream; 34 import java.io.InputStreamReader; 35 import java.net.URISyntaxException; 36 import java.net.URL; 37 import java.util.ArrayList; 38 import java.util.Collections; 39 import java.util.HashMap; 40 import java.util.HashSet; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Set; 44 import java.util.regex.Matcher; 45 import java.util.regex.Pattern; 46 47 import static org.junit.Assert.assertEquals; 48 import static org.junit.Assert.assertNotNull; 49 import static org.junit.Assert.assertTrue; 50 51 52 public class BaseCompilationTest { 53 54 private static final String PRINT_ENCODED_ERRORS_PROPERTY 55 = "android.injected.invoked.from.ide"; 56 @Rule 57 public TestName name = new TestName(); 58 static Pattern VARIABLES = Pattern.compile("!@\\{([A-Za-z0-9_-]*)}"); 59 60 public static final String KEY_MANIFEST_PACKAGE = "PACKAGE"; 61 public static final String KEY_DEPENDENCIES = "DEPENDENCIES"; 62 public static final String KEY_SETTINGS_INCLUDES = "SETTINGS_INCLUDES"; 63 public static final String DEFAULT_APP_PACKAGE = "com.android.databinding.compilationTest.test"; 64 public static final String KEY_CLASS_NAME = "CLASSNAME"; 65 public static final String KEY_CLASS_TYPE = "CLASSTYPE"; 66 public static final String KEY_IMPORT_TYPE = "IMPORTTYPE"; 67 public static final String KEY_INCLUDE_ID = "INCLUDEID"; 68 public static final String KEY_VIEW_ID = "VIEWID"; 69 70 protected final File testFolder = new File("./build/build-test"); 71 72 protected void copyResourceTo(String name, String path) throws IOException { 73 copyResourceTo(name, new File(testFolder, path)); 74 } 75 76 protected void copyResourceTo(String name, String path, Map<String, String> replacements) 77 throws IOException { 78 copyResourceTo(name, new File(testFolder, path), replacements); 79 } 80 81 protected void copyResourceDirectory(String name, String targetPath) 82 throws URISyntaxException, IOException { 83 URL dir = getClass().getResource(name); 84 assertNotNull(dir); 85 assertEquals("file", dir.getProtocol()); 86 File folder = new File(dir.toURI()); 87 assertTrue(folder.isDirectory()); 88 File target = new File(testFolder, targetPath); 89 int len = folder.getAbsolutePath().length() + 1; 90 for (File item : FileUtils.listFiles(folder, null, true)) { 91 if (item.getAbsolutePath().equals(folder.getAbsolutePath())) { 92 continue; 93 } 94 String resourcePath = item.getAbsolutePath().substring(len); 95 96 copyResourceTo(name + "/" + resourcePath, new File(target, resourcePath)); 97 } 98 } 99 100 @Before 101 public void clear() throws IOException { 102 if (testFolder.exists()) { 103 FileUtils.forceDelete(testFolder); 104 } 105 } 106 107 /** 108 * Extracts the text in the given location from the the at the given application path. 109 * 110 * @param pathInApp The path, relative to the root of the application under test 111 * @param location The location to extract 112 * @return The string that is contained in the given location 113 * @throws IOException If file is invalid. 114 */ 115 protected String extract(String pathInApp, Location location) throws IOException { 116 File file = new File(testFolder, pathInApp); 117 assertTrue(file.exists()); 118 StringBuilder result = new StringBuilder(); 119 List<String> lines = FileUtils.readLines(file); 120 for (int i = location.startLine; i <= location.endLine; i++) { 121 if (i > location.startLine) { 122 result.append("\n"); 123 } 124 String line = lines.get(i); 125 int start = 0; 126 if (i == location.startLine) { 127 start = location.startOffset; 128 } 129 int end = line.length() - 1; // inclusive 130 if (i == location.endLine) { 131 end = location.endOffset; 132 } 133 result.append(line.substring(start, end + 1)); 134 } 135 return result.toString(); 136 } 137 138 protected void copyResourceTo(String name, File targetFile) throws IOException { 139 File directory = targetFile.getParentFile(); 140 FileUtils.forceMkdir(directory); 141 InputStream contents = getClass().getResourceAsStream(name); 142 FileOutputStream fos = new FileOutputStream(targetFile); 143 IOUtils.copy(contents, fos); 144 IOUtils.closeQuietly(fos); 145 IOUtils.closeQuietly(contents); 146 } 147 148 protected static Map<String, String> toMap(String... keysAndValues) { 149 assertEquals(0, keysAndValues.length % 2); 150 Map<String, String> map = new HashMap<String, String>(); 151 for (int i = 0; i < keysAndValues.length; i += 2) { 152 map.put(keysAndValues[i], keysAndValues[i + 1]); 153 } 154 return map; 155 } 156 157 protected void copyResourceTo(String name, File targetFile, Map<String, String> replacements) 158 throws IOException { 159 if (replacements.isEmpty()) { 160 copyResourceTo(name, targetFile); 161 } 162 InputStream inputStream = getClass().getResourceAsStream(name); 163 final String contents = IOUtils.toString(inputStream); 164 IOUtils.closeQuietly(inputStream); 165 166 StringBuilder out = new StringBuilder(contents.length()); 167 final Matcher matcher = VARIABLES.matcher(contents); 168 int location = 0; 169 while (matcher.find()) { 170 int start = matcher.start(); 171 if (start > location) { 172 out.append(contents, location, start); 173 } 174 final String key = matcher.group(1); 175 final String replacement = replacements.get(key); 176 if (replacement != null) { 177 out.append(replacement); 178 } 179 location = matcher.end(); 180 } 181 if (location < contents.length()) { 182 out.append(contents, location, contents.length()); 183 } 184 185 FileUtils.writeStringToFile(targetFile, out.toString()); 186 } 187 188 protected void prepareProject() throws IOException, URISyntaxException { 189 prepareApp(null); 190 } 191 192 private Map<String, String> addDefaults(Map<String, String> map) { 193 if (map == null) { 194 map = new HashMap<String, String>(); 195 } 196 if (!map.containsKey(KEY_MANIFEST_PACKAGE)) { 197 map.put(KEY_MANIFEST_PACKAGE, DEFAULT_APP_PACKAGE); 198 } 199 if (!map.containsKey(KEY_SETTINGS_INCLUDES)) { 200 map.put(KEY_SETTINGS_INCLUDES, "include ':app'"); 201 } 202 return map; 203 } 204 205 protected void prepareApp(Map<String, String> replacements) throws IOException, 206 URISyntaxException { 207 replacements = addDefaults(replacements); 208 // how to get build folder, pass from gradle somehow ? 209 FileUtils.forceMkdir(testFolder); 210 copyResourceTo("/AndroidManifest.xml", 211 new File(testFolder, "app/src/main/AndroidManifest.xml"), replacements); 212 copyResourceTo("/project_build.gradle", new File(testFolder, "build.gradle"), replacements); 213 copyResourceTo("/app_build.gradle", new File(testFolder, "app/build.gradle"), replacements); 214 copyResourceTo("/settings.gradle", new File(testFolder, "settings.gradle"), replacements); 215 File localProperties = new File("../local.properties"); 216 if (localProperties.exists()) { 217 FileUtils.copyFile(localProperties, new File(testFolder, "local.properties")); 218 } 219 FileUtils.copyFile(new File("../propLoader.gradle"), 220 new File(testFolder, "propLoaderClone.gradle")); 221 FileUtils.copyFile(new File("../gradlew"), new File(testFolder, "gradlew")); 222 FileUtils.copyDirectory(new File("../gradle"), new File(testFolder, "gradle")); 223 } 224 225 protected void prepareModule(String moduleName, String packageName, 226 Map<String, String> replacements) throws IOException, URISyntaxException { 227 replacements = addDefaults(replacements); 228 replacements.put(KEY_MANIFEST_PACKAGE, packageName); 229 File moduleFolder = new File(testFolder, moduleName); 230 if (moduleFolder.exists()) { 231 FileUtils.forceDelete(moduleFolder); 232 } 233 FileUtils.forceMkdir(moduleFolder); 234 copyResourceTo("/AndroidManifest.xml", 235 new File(moduleFolder, "src/main/AndroidManifest.xml"), replacements); 236 copyResourceTo("/module_build.gradle", new File(moduleFolder, "build.gradle"), 237 replacements); 238 } 239 240 protected CompilationResult runGradle(String... params) 241 throws IOException, InterruptedException { 242 setExecutable(); 243 File pathToExecutable = new File(testFolder, "gradlew"); 244 List<String> args = new ArrayList<String>(); 245 args.add(pathToExecutable.getAbsolutePath()); 246 args.add("-P" + PRINT_ENCODED_ERRORS_PROPERTY + "=true"); 247 if ("true".equals(System.getProperties().getProperty("useReleaseVersion", "false"))) { 248 args.add("-PuseReleaseVersion=true"); 249 } 250 if ("true".equals(System.getProperties().getProperty("addRemoteRepos", "false"))) { 251 args.add("-PaddRemoteRepos=true"); 252 } 253 args.add("--project-cache-dir"); 254 args.add(new File("../.caches/", name.getMethodName()).getAbsolutePath()); 255 Collections.addAll(args, params); 256 ProcessBuilder builder = new ProcessBuilder(args); 257 builder.environment().putAll(System.getenv()); 258 String javaHome = System.getProperty("java.home"); 259 if (StringUtils.isNotBlank(javaHome)) { 260 builder.environment().put("JAVA_HOME", javaHome); 261 } 262 builder.directory(testFolder); 263 Process process = builder.start(); 264 String output = collect(process.getInputStream()); 265 String error = collect(process.getErrorStream()); 266 int result = process.waitFor(); 267 return new CompilationResult(result, output, error); 268 } 269 270 private void setExecutable() throws IOException { 271 File gw = new File(testFolder, "gradlew"); 272 gw.setExecutable(true); 273 } 274 275 /** 276 * Use this instead of IO utils so that we can easily log the output when necessary 277 */ 278 private static String collect(InputStream stream) throws IOException { 279 StringBuilder sb = new StringBuilder(); 280 String line; 281 final BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); 282 while ((line = reader.readLine()) != null) { 283 sb.append(line).append("\n"); 284 } 285 return sb.toString(); 286 } 287 } 288