SimpleCompilationTest.java revision ddeffcc2d89275528b2001836da2795b14ea7909
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
19
20import org.apache.commons.io.FileUtils;
21import org.apache.commons.io.filefilter.PrefixFileFilter;
22import org.apache.commons.io.filefilter.SuffixFileFilter;
23import org.apache.commons.lang3.StringUtils;
24import org.junit.Test;
25
26import android.databinding.tool.processing.ErrorMessages;
27import android.databinding.tool.processing.ScopedErrorReport;
28import android.databinding.tool.processing.ScopedException;
29import android.databinding.tool.store.Location;
30
31import java.io.File;
32import java.io.IOException;
33import java.net.URISyntaxException;
34import java.util.Collection;
35import java.util.List;
36
37import static org.junit.Assert.assertEquals;
38import static org.junit.Assert.assertNotEquals;
39import static org.junit.Assert.assertNotNull;
40import static org.junit.Assert.assertTrue;
41import static org.junit.Assert.fail;
42
43@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
44public class SimpleCompilationTest extends BaseCompilationTest {
45
46    @Test
47    public void listTasks() throws IOException, URISyntaxException, InterruptedException {
48        prepareProject();
49        CompilationResult result = runGradle("tasks");
50        assertEquals(0, result.resultCode);
51        assertTrue("there should not be any errors", StringUtils.isEmpty(result.error));
52        assertTrue("Test sanity, empty project tasks",
53                result.resultContainsText("All tasks runnable from root project"));
54    }
55
56    @Test
57    public void testEmptyCompilation() throws IOException, URISyntaxException, InterruptedException {
58        prepareProject();
59        CompilationResult result = runGradle("assembleDebug");
60        assertEquals(result.error, 0, result.resultCode);
61        assertTrue("there should not be any errors " + result.error,
62                StringUtils.isEmpty(result.error));
63        assertTrue("Test sanity, should compile fine",
64                result.resultContainsText("BUILD SUCCESSFUL"));
65    }
66
67    @Test
68    public void testMultipleConfigs() throws IOException, URISyntaxException, InterruptedException {
69        prepareProject();
70        copyResourceTo("/layout/basic_layout.xml",
71                "/app/src/main/res/layout/main.xml");
72        copyResourceTo("/layout/basic_layout.xml",
73                "/app/src/main/res/layout-sw100dp/main.xml");
74        CompilationResult result = runGradle("assembleDebug");
75        assertEquals(result.error, 0, result.resultCode);
76        File debugOut = new File(testFolder, "/app/build/intermediates/res/merged/debug/");
77        Collection<File> layoutFiles = FileUtils.listFiles(debugOut, new SuffixFileFilter(".xml"),
78                new PrefixFileFilter("layout"));
79        assertTrue("test sanity", layoutFiles.size() > 1);
80        for (File layout : layoutFiles) {
81            if (layout.getParent().contains("sw100")) {
82                assertTrue("File has wrong tag:" + layout.getPath(),
83                        FileUtils.readFileToString(layout)
84                                .indexOf("android:tag=\"layout-sw100dp/main_0\"") > 0);
85            } else {
86                assertTrue("File has wrong tag:" + layout.getPath(),
87                        FileUtils.readFileToString(layout).indexOf("android:tag=\"layout/main_0\"")
88                                > 0);
89            }
90        }
91    }
92
93    private ScopedException singleFileErrorTest(String resource, String targetFile,
94            String expectedExtract, String errorMessage)
95            throws IOException, URISyntaxException, InterruptedException {
96        prepareProject();
97        copyResourceTo(resource, targetFile);
98        CompilationResult result = runGradle("assembleDebug");
99        assertNotEquals(0, result.resultCode);
100        ScopedException scopedException = result.getBindingException();
101        assertNotNull(result.error, scopedException);
102        ScopedErrorReport report = scopedException.getScopedErrorReport();
103        assertNotNull(report);
104        assertEquals(1, report.getLocations().size());
105        Location loc = report.getLocations().get(0);
106        if (expectedExtract != null) {
107            String extract = extract(targetFile, loc);
108            assertEquals(expectedExtract, extract);
109        }
110        final File errorFile = new File(report.getFilePath());
111        assertTrue(errorFile.exists());
112        assertEquals(new File(testFolder, targetFile).getCanonicalFile(),
113                errorFile.getCanonicalFile());
114        if (errorMessage != null) {
115            assertEquals(errorMessage, scopedException.getBareMessage());
116        }
117        return scopedException;
118    }
119
120    @Test
121    public void testMultipleExceptionsInDifferentFiles()
122            throws IOException, URISyntaxException, InterruptedException {
123        prepareProject();
124        copyResourceTo("/layout/undefined_variable_binding.xml",
125                "/app/src/main/res/layout/broken.xml");
126        copyResourceTo("/layout/invalid_setter_binding.xml",
127                "/app/src/main/res/layout/invalid_setter.xml");
128        CompilationResult result = runGradle("assembleDebug");
129        assertNotEquals(result.output, 0, result.resultCode);
130        List<ScopedException> bindingExceptions = result.getBindingExceptions();
131        assertEquals(result.error, 2, bindingExceptions.size());
132        File broken = new File(testFolder, "/app/src/main/res/layout/broken.xml");
133        File invalidSetter = new File(testFolder, "/app/src/main/res/layout/invalid_setter.xml");
134        for (ScopedException exception : bindingExceptions) {
135            ScopedErrorReport report = exception.getScopedErrorReport();
136            final File errorFile = new File(report.getFilePath());
137            String message = null;
138            String expectedErrorFile = null;
139            if (errorFile.getCanonicalPath().equals(broken.getCanonicalPath())) {
140                message = String.format(ErrorMessages.UNDEFINED_VARIABLE, "myVariable");
141                expectedErrorFile = "/app/src/main/res/layout/broken.xml";
142            } else if (errorFile.getCanonicalPath().equals(invalidSetter.getCanonicalPath())) {
143                message = String.format(ErrorMessages.CANNOT_FIND_SETTER_CALL, "android:textx",
144                        String.class.getCanonicalName());
145                expectedErrorFile = "/app/src/main/res/layout/invalid_setter.xml";
146            } else {
147                fail("unexpected exception " + exception.getBareMessage());
148            }
149            assertEquals(1, report.getLocations().size());
150            Location loc = report.getLocations().get(0);
151            String extract = extract(expectedErrorFile, loc);
152            assertEquals("myVariable", extract);
153            assertEquals(message, exception.getBareMessage());
154        }
155    }
156
157    @Test
158    public void testBadSyntax() throws IOException, URISyntaxException, InterruptedException {
159        singleFileErrorTest("/layout/layout_with_bad_syntax.xml",
160                "/app/src/main/res/layout/broken.xml",
161                "myVar.length())",
162                String.format(ErrorMessages.SYNTAX_ERROR,
163                        "extraneous input ')' expecting {<EOF>, ',', '.', '[', '+', '-', '*', '/', "
164                                + "'%', '<<', '>>>', '>>', '<=', '>=', '>', '<', 'instanceof', "
165                                + "'==', '!=', '&', '^', '|', '&&', '||', '?', '??'}"));
166    }
167
168    @Test
169    public void testBrokenSyntax() throws IOException, URISyntaxException, InterruptedException {
170        singleFileErrorTest("/layout/layout_with_completely_broken_syntax.xml",
171                "/app/src/main/res/layout/broken.xml",
172                "new String()",
173                String.format(ErrorMessages.SYNTAX_ERROR,
174                        "mismatched input 'String' expecting {<EOF>, ',', '.', '[', '+', '-', '*', "
175                                + "'/', '%', '<<', '>>>', '>>', '<=', '>=', '>', '<', 'instanceof',"
176                                + " '==', '!=', '&', '^', '|', '&&', '||', '?', '??'}"));
177    }
178
179    @Test
180    public void testUndefinedVariable() throws IOException, URISyntaxException,
181            InterruptedException {
182        ScopedException ex = singleFileErrorTest("/layout/undefined_variable_binding.xml",
183                "/app/src/main/res/layout/broken.xml", "myVariable",
184                String.format(ErrorMessages.UNDEFINED_VARIABLE, "myVariable"));
185    }
186
187    @Test
188    public void testInvalidSetterBinding() throws IOException, URISyntaxException,
189            InterruptedException {
190        prepareProject();
191        ScopedException ex = singleFileErrorTest("/layout/invalid_setter_binding.xml",
192                "/app/src/main/res/layout/invalid_setter.xml", "myVariable",
193                String.format(ErrorMessages.CANNOT_FIND_SETTER_CALL, "android:textx",
194                        String.class.getCanonicalName()));
195    }
196
197    @Test
198    public void testRootTag() throws IOException, URISyntaxException,
199            InterruptedException {
200        prepareProject();
201        copyResourceTo("/layout/root_tag.xml", "/app/src/main/res/layout/root_tag.xml");
202        CompilationResult result = runGradle("assembleDebug");
203        assertNotEquals(0, result.resultCode);
204        assertNotNull(result.error);
205        final String expected = String.format(ErrorMessages.ROOT_TAG_NOT_SUPPORTED, "hello");
206        assertTrue(result.error.contains(expected));
207    }
208
209    @Test
210    public void testInvalidVariableType() throws IOException, URISyntaxException,
211            InterruptedException {
212        prepareProject();
213        ScopedException ex = singleFileErrorTest("/layout/invalid_variable_type.xml",
214                "/app/src/main/res/layout/invalid_variable.xml", "myVariable",
215                String.format(ErrorMessages.CANNOT_RESOLVE_TYPE, "myVariable~"));
216    }
217
218    @Test
219    public void testSingleModule() throws IOException, URISyntaxException, InterruptedException {
220        prepareApp(toMap(KEY_DEPENDENCIES, "compile project(':module1')",
221                KEY_SETTINGS_INCLUDES, "include ':app'\ninclude ':module1'"));
222        prepareModule("module1", "com.example.module1", toMap());
223        copyResourceTo("/layout/basic_layout.xml", "/module1/src/main/res/layout/module_layout.xml");
224        copyResourceTo("/layout/basic_layout.xml", "/app/src/main/res/layout/app_layout.xml");
225        CompilationResult result = runGradle("assembleDebug");
226        assertEquals(result.error, 0, result.resultCode);
227    }
228
229    @Test
230    public void testTwoLevelDependency() throws IOException, URISyntaxException, InterruptedException {
231        prepareApp(toMap(KEY_DEPENDENCIES, "compile project(':module1')",
232                KEY_SETTINGS_INCLUDES, "include ':app'\ninclude ':module1'\n"
233                        + "include ':module2'"));
234        prepareModule("module1", "com.example.module1", toMap(KEY_DEPENDENCIES,
235                "compile project(':module2')"));
236        prepareModule("module2", "com.example.module2", toMap());
237        copyResourceTo("/layout/basic_layout.xml",
238                "/module2/src/main/res/layout/module2_layout.xml");
239        copyResourceTo("/layout/basic_layout.xml", "/module1/src/main/res/layout/module1_layout.xml");
240        copyResourceTo("/layout/basic_layout.xml", "/app/src/main/res/layout/app_layout.xml");
241        CompilationResult result = runGradle("assembleDebug");
242        assertEquals(result.error, 0, result.resultCode);
243    }
244
245    @Test
246    public void testIncludeInMerge() throws Throwable {
247        prepareProject();
248        copyResourceTo("/layout/merge_include.xml", "/app/src/main/res/layout/merge_include.xml");
249        CompilationResult result = runGradle("assembleDebug");
250        assertNotEquals(0, result.resultCode);
251        List<ScopedException> errors = ScopedException.extractErrors(result.error);
252        assertEquals(result.error, 1, errors.size());
253        final ScopedException ex = errors.get(0);
254        final ScopedErrorReport report = ex.getScopedErrorReport();
255        final File errorFile = new File(report.getFilePath());
256        assertTrue(errorFile.exists());
257        assertEquals(
258                new File(testFolder, "/app/src/main/res/layout/merge_include.xml")
259                        .getCanonicalFile(),
260                errorFile.getCanonicalFile());
261        assertEquals("Merge shouldn't support includes as root. Error message was '" + result.error,
262                ErrorMessages.INCLUDE_INSIDE_MERGE, ex.getBareMessage());
263    }
264}
265