SimpleCompilationTest.java revision 9784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3
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,
77                "app/build/intermediates/data-binding-layout-out/debug");
78        Collection<File> layoutFiles = FileUtils.listFiles(debugOut, new SuffixFileFilter(".xml"),
79                new PrefixFileFilter("layout"));
80        assertTrue("test sanity", layoutFiles.size() > 1);
81        for (File layout : layoutFiles) {
82            final String contents = FileUtils.readFileToString(layout);
83            if (layout.getParent().contains("sw100")) {
84                assertTrue("File has wrong tag:" + layout.getPath(),
85                        contents.indexOf("android:tag=\"layout-sw100dp/main_0\"") > 0);
86            } else {
87                assertTrue("File has wrong tag:" + layout.getPath() + "\n" + contents,
88                        contents.indexOf("android:tag=\"layout/main_0\"")
89                                > 0);
90            }
91        }
92    }
93
94    private ScopedException singleFileErrorTest(String resource, String targetFile,
95            String expectedExtract, String errorMessage)
96            throws IOException, URISyntaxException, InterruptedException {
97        prepareProject();
98        copyResourceTo(resource, targetFile);
99        CompilationResult result = runGradle("assembleDebug");
100        assertNotEquals(0, result.resultCode);
101        ScopedException scopedException = result.getBindingException();
102        assertNotNull(result.error, scopedException);
103        ScopedErrorReport report = scopedException.getScopedErrorReport();
104        assertNotNull(report);
105        assertEquals(1, report.getLocations().size());
106        Location loc = report.getLocations().get(0);
107        if (expectedExtract != null) {
108            String extract = extract(targetFile, loc);
109            assertEquals(expectedExtract, extract);
110        }
111        final File errorFile = new File(report.getFilePath());
112        assertTrue(errorFile.exists());
113        assertEquals(new File(testFolder, targetFile).getCanonicalFile(),
114                errorFile.getCanonicalFile());
115        if (errorMessage != null) {
116            assertEquals(errorMessage, scopedException.getBareMessage());
117        }
118        return scopedException;
119    }
120
121    @Test
122    public void testMultipleExceptionsInDifferentFiles()
123            throws IOException, URISyntaxException, InterruptedException {
124        prepareProject();
125        copyResourceTo("/layout/undefined_variable_binding.xml",
126                "/app/src/main/res/layout/broken.xml");
127        copyResourceTo("/layout/invalid_setter_binding.xml",
128                "/app/src/main/res/layout/invalid_setter.xml");
129        CompilationResult result = runGradle("assembleDebug");
130        assertNotEquals(result.output, 0, result.resultCode);
131        List<ScopedException> bindingExceptions = result.getBindingExceptions();
132        assertEquals(result.error, 2, bindingExceptions.size());
133        File broken = new File(testFolder, "/app/src/main/res/layout/broken.xml");
134        File invalidSetter = new File(testFolder, "/app/src/main/res/layout/invalid_setter.xml");
135        for (ScopedException exception : bindingExceptions) {
136            ScopedErrorReport report = exception.getScopedErrorReport();
137            final File errorFile = new File(report.getFilePath());
138            String message = null;
139            String expectedErrorFile = null;
140            if (errorFile.getCanonicalPath().equals(broken.getCanonicalPath())) {
141                message = String.format(ErrorMessages.UNDEFINED_VARIABLE, "myVariable");
142                expectedErrorFile = "/app/src/main/res/layout/broken.xml";
143            } else if (errorFile.getCanonicalPath().equals(invalidSetter.getCanonicalPath())) {
144                message = String.format(ErrorMessages.CANNOT_FIND_SETTER_CALL, "android:textx",
145                        String.class.getCanonicalName());
146                expectedErrorFile = "/app/src/main/res/layout/invalid_setter.xml";
147            } else {
148                fail("unexpected exception " + exception.getBareMessage());
149            }
150            assertEquals(1, report.getLocations().size());
151            Location loc = report.getLocations().get(0);
152            String extract = extract(expectedErrorFile, loc);
153            assertEquals("myVariable", extract);
154            assertEquals(message, exception.getBareMessage());
155        }
156    }
157
158    @Test
159    public void testBadSyntax() throws IOException, URISyntaxException, InterruptedException {
160        singleFileErrorTest("/layout/layout_with_bad_syntax.xml",
161                "/app/src/main/res/layout/broken.xml",
162                "myVar.length())",
163                String.format(ErrorMessages.SYNTAX_ERROR,
164                        "extraneous input ')' expecting {<EOF>, ',', '.', '[', '+', '-', '*', '/', "
165                                + "'%', '<<', '>>>', '>>', '<=', '>=', '>', '<', 'instanceof', "
166                                + "'==', '!=', '&', '^', '|', '&&', '||', '?', '??'}"));
167    }
168
169    @Test
170    public void testBrokenSyntax() throws IOException, URISyntaxException, InterruptedException {
171        singleFileErrorTest("/layout/layout_with_completely_broken_syntax.xml",
172                "/app/src/main/res/layout/broken.xml",
173                "new String()",
174                String.format(ErrorMessages.SYNTAX_ERROR,
175                        "mismatched input 'String' expecting {<EOF>, ',', '.', '[', '+', '-', '*', "
176                                + "'/', '%', '<<', '>>>', '>>', '<=', '>=', '>', '<', 'instanceof',"
177                                + " '==', '!=', '&', '^', '|', '&&', '||', '?', '??'}"));
178    }
179
180    @Test
181    public void testUndefinedVariable() throws IOException, URISyntaxException,
182            InterruptedException {
183        ScopedException ex = singleFileErrorTest("/layout/undefined_variable_binding.xml",
184                "/app/src/main/res/layout/broken.xml", "myVariable",
185                String.format(ErrorMessages.UNDEFINED_VARIABLE, "myVariable"));
186    }
187
188    @Test
189    public void testInvalidSetterBinding() throws IOException, URISyntaxException,
190            InterruptedException {
191        prepareProject();
192        ScopedException ex = singleFileErrorTest("/layout/invalid_setter_binding.xml",
193                "/app/src/main/res/layout/invalid_setter.xml", "myVariable",
194                String.format(ErrorMessages.CANNOT_FIND_SETTER_CALL, "android:textx",
195                        String.class.getCanonicalName()));
196    }
197
198    @Test
199    public void testRootTag() throws IOException, URISyntaxException,
200            InterruptedException {
201        prepareProject();
202        copyResourceTo("/layout/root_tag.xml", "/app/src/main/res/layout/root_tag.xml");
203        CompilationResult result = runGradle("assembleDebug");
204        assertNotEquals(0, result.resultCode);
205        assertNotNull(result.error);
206        final String expected = String.format(ErrorMessages.ROOT_TAG_NOT_SUPPORTED, "hello");
207        assertTrue(result.error.contains(expected));
208    }
209
210    @Test
211    public void testInvalidVariableType() throws IOException, URISyntaxException,
212            InterruptedException {
213        prepareProject();
214        ScopedException ex = singleFileErrorTest("/layout/invalid_variable_type.xml",
215                "/app/src/main/res/layout/invalid_variable.xml", "myVariable",
216                String.format(ErrorMessages.CANNOT_RESOLVE_TYPE, "myVariable~"));
217    }
218
219    @Test
220    public void testSingleModule() throws IOException, URISyntaxException, InterruptedException {
221        prepareApp(toMap(KEY_DEPENDENCIES, "compile project(':module1')",
222                KEY_SETTINGS_INCLUDES, "include ':app'\ninclude ':module1'"));
223        prepareModule("module1", "com.example.module1", toMap());
224        copyResourceTo("/layout/basic_layout.xml", "/module1/src/main/res/layout/module_layout.xml");
225        copyResourceTo("/layout/basic_layout.xml", "/app/src/main/res/layout/app_layout.xml");
226        CompilationResult result = runGradle("assembleDebug");
227        assertEquals(result.error, 0, result.resultCode);
228    }
229
230    @Test
231    public void testTwoLevelDependency() throws IOException, URISyntaxException, InterruptedException {
232        prepareApp(toMap(KEY_DEPENDENCIES, "compile project(':module1')",
233                KEY_SETTINGS_INCLUDES, "include ':app'\ninclude ':module1'\n"
234                        + "include ':module2'"));
235        prepareModule("module1", "com.example.module1", toMap(KEY_DEPENDENCIES,
236                "compile project(':module2')"));
237        prepareModule("module2", "com.example.module2", toMap());
238        copyResourceTo("/layout/basic_layout.xml",
239                "/module2/src/main/res/layout/module2_layout.xml");
240        copyResourceTo("/layout/basic_layout.xml", "/module1/src/main/res/layout/module1_layout.xml");
241        copyResourceTo("/layout/basic_layout.xml", "/app/src/main/res/layout/app_layout.xml");
242        CompilationResult result = runGradle("assembleDebug");
243        assertEquals(result.error, 0, result.resultCode);
244    }
245
246    @Test
247    public void testIncludeInMerge() throws Throwable {
248        prepareProject();
249        copyResourceTo("/layout/merge_include.xml", "/app/src/main/res/layout/merge_include.xml");
250        CompilationResult result = runGradle("assembleDebug");
251        assertNotEquals(0, result.resultCode);
252        List<ScopedException> errors = ScopedException.extractErrors(result.error);
253        assertEquals(result.error, 1, errors.size());
254        final ScopedException ex = errors.get(0);
255        final ScopedErrorReport report = ex.getScopedErrorReport();
256        final File errorFile = new File(report.getFilePath());
257        assertTrue(errorFile.exists());
258        assertEquals(
259                new File(testFolder, "/app/src/main/res/layout/merge_include.xml")
260                        .getCanonicalFile(),
261                errorFile.getCanonicalFile());
262        assertEquals("Merge shouldn't support includes as root. Error message was '" + result.error,
263                ErrorMessages.INCLUDE_INSIDE_MERGE, ex.getBareMessage());
264    }
265}
266