BaseCompilationTest.java revision 24bec1cc542de69e0e21cc774469f20d34811ad9
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
17package android.databinding.compilationTest;
18
19import org.apache.commons.io.FileUtils;
20import org.apache.commons.io.IOUtils;
21import org.apache.commons.lang3.StringUtils;
22import org.junit.Before;
23import org.junit.Rule;
24import org.junit.rules.TestName;
25
26import android.databinding.tool.store.Location;
27
28import java.io.File;
29import java.io.FileOutputStream;
30import java.io.IOException;
31import java.io.InputStream;
32import java.net.URISyntaxException;
33import java.net.URL;
34import java.nio.file.Files;
35import java.nio.file.Paths;
36import java.nio.file.attribute.PosixFilePermission;
37import java.util.ArrayList;
38import java.util.Collections;
39import java.util.HashMap;
40import java.util.HashSet;
41import java.util.List;
42import java.util.Map;
43import java.util.Set;
44import java.util.regex.Matcher;
45import java.util.regex.Pattern;
46
47import static org.junit.Assert.assertEquals;
48import static org.junit.Assert.assertNotNull;
49import static org.junit.Assert.assertTrue;
50
51
52public class BaseCompilationTest {
53
54    private static final String PRINT_ENCODED_ERRORS_PROPERTY
55            = "android.databinding.injected.print.encoded.errors";
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<>();
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<>();
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"), new File(testFolder, "propLoaderClone.gradle"));
220        FileUtils.copyFile(new File("../gradlew"), new File(testFolder, "gradlew"));
221        FileUtils.copyDirectory(new File("../gradle"), new File(testFolder, "gradle"));
222    }
223
224    protected void prepareModule(String moduleName, String packageName,
225            Map<String, String> replacements) throws IOException, URISyntaxException {
226        replacements = addDefaults(replacements);
227        replacements.put(KEY_MANIFEST_PACKAGE, packageName);
228        File moduleFolder = new File(testFolder, moduleName);
229        if (moduleFolder.exists()) {
230            FileUtils.forceDelete(moduleFolder);
231        }
232        FileUtils.forceMkdir(moduleFolder);
233        copyResourceTo("/AndroidManifest.xml",
234                new File(moduleFolder, "src/main/AndroidManifest.xml"), replacements);
235        copyResourceTo("/module_build.gradle", new File(moduleFolder, "build.gradle"),
236                replacements);
237    }
238
239    protected CompilationResult runGradle(String... params)
240            throws IOException, InterruptedException {
241        setExecutable();
242        File pathToExecutable = new File(testFolder, "gradlew");
243        List<String> args = new ArrayList<>();
244        args.add(pathToExecutable.getAbsolutePath());
245        args.add("-P" + PRINT_ENCODED_ERRORS_PROPERTY + "=true");
246        args.add("--project-cache-dir");
247        args.add(new File("../.caches/", name.getMethodName()).getAbsolutePath());
248        Collections.addAll(args, params);
249        ProcessBuilder builder = new ProcessBuilder(args);
250        builder.environment().putAll(System.getenv());
251        String javaHome = System.getProperty("java.home");
252        if (StringUtils.isNotBlank(javaHome)) {
253            builder.environment().put("JAVA_HOME", javaHome);
254        }
255        builder.directory(testFolder);
256        Process process = builder.start();
257        String output = IOUtils.toString(process.getInputStream());
258        String error = IOUtils.toString(process.getErrorStream());
259        int result = process.waitFor();
260        return new CompilationResult(result, output, error);
261    }
262
263    private void setExecutable() throws IOException {
264        Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
265        //add owners permission
266        perms.add(PosixFilePermission.OWNER_READ);
267        perms.add(PosixFilePermission.OWNER_WRITE);
268        perms.add(PosixFilePermission.OWNER_EXECUTE);
269        //add group permissions
270        perms.add(PosixFilePermission.GROUP_READ);
271        //add others permissions
272        perms.add(PosixFilePermission.OTHERS_READ);
273        Files.setPosixFilePermissions(Paths.get(new File(testFolder, "gradlew").getAbsolutePath()),
274                perms);
275    }
276
277
278}
279