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