• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
20 import org.apache.commons.io.FileUtils;
21 import org.apache.commons.io.filefilter.PrefixFileFilter;
22 import org.apache.commons.io.filefilter.SuffixFileFilter;
23 import org.apache.commons.lang3.StringUtils;
24 import org.junit.Test;
25 
26 import android.databinding.tool.processing.ErrorMessages;
27 import android.databinding.tool.processing.ScopedErrorReport;
28 import android.databinding.tool.processing.ScopedException;
29 import android.databinding.tool.store.Location;
30 
31 import java.io.File;
32 import java.io.IOException;
33 import java.net.URISyntaxException;
34 import java.util.Collection;
35 import java.util.List;
36 
37 import static org.junit.Assert.assertEquals;
38 import static org.junit.Assert.assertNotEquals;
39 import static org.junit.Assert.assertNotNull;
40 import static org.junit.Assert.assertTrue;
41 import static org.junit.Assert.fail;
42 
43 @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
44 public class SimpleCompilationTest extends BaseCompilationTest {
45 
46     @Test
listTasks()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
testEmptyCompilation()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
testMultipleConfigs()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 
singleFileErrorTest(String resource, String targetFile, String expectedExtract, String errorMessage)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
testMultipleExceptionsInDifferentFiles()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
testBadSyntax()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
testBrokenSyntax()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
testUndefinedVariable()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
testInvalidSetterBinding()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
testRootTag()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
testInvalidVariableType()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
testSingleModule()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
testModuleDependencyChange()231     public void testModuleDependencyChange() throws IOException, URISyntaxException,
232             InterruptedException {
233         prepareApp(toMap(KEY_DEPENDENCIES, "compile project(':module1')",
234                 KEY_SETTINGS_INCLUDES, "include ':app'\ninclude ':module1'"));
235         prepareModule("module1", "com.example.module1", toMap(
236                 KEY_DEPENDENCIES, "compile 'com.android.support:appcompat-v7:23.1.1'"
237         ));
238         copyResourceTo("/layout/basic_layout.xml", "/module1/src/main/res/layout/module_layout.xml");
239         copyResourceTo("/layout/basic_layout.xml", "/app/src/main/res/layout/app_layout.xml");
240         CompilationResult result = runGradle("assembleDebug");
241         assertEquals(result.error, 0, result.resultCode);
242         File moduleFolder = new File(testFolder, "module1");
243         copyResourceTo("/module_build.gradle", new File(moduleFolder, "build.gradle"),
244                 toMap());
245         result = runGradle("assembleDebug");
246         assertEquals(result.error, 0, result.resultCode);
247     }
248 
249     @Test
testTwoLevelDependency()250     public void testTwoLevelDependency() throws IOException, URISyntaxException, InterruptedException {
251         prepareApp(toMap(KEY_DEPENDENCIES, "compile project(':module1')",
252                 KEY_SETTINGS_INCLUDES, "include ':app'\ninclude ':module1'\n"
253                         + "include ':module2'"));
254         prepareModule("module1", "com.example.module1", toMap(KEY_DEPENDENCIES,
255                 "compile project(':module2')"));
256         prepareModule("module2", "com.example.module2", toMap());
257         copyResourceTo("/layout/basic_layout.xml",
258                 "/module2/src/main/res/layout/module2_layout.xml");
259         copyResourceTo("/layout/basic_layout.xml", "/module1/src/main/res/layout/module1_layout.xml");
260         copyResourceTo("/layout/basic_layout.xml", "/app/src/main/res/layout/app_layout.xml");
261         CompilationResult result = runGradle("assembleDebug");
262         assertEquals(result.error, 0, result.resultCode);
263     }
264 
265     @Test
testIncludeInMerge()266     public void testIncludeInMerge() throws Throwable {
267         prepareProject();
268         copyResourceTo("/layout/merge_include.xml", "/app/src/main/res/layout/merge_include.xml");
269         CompilationResult result = runGradle("assembleDebug");
270         assertNotEquals(0, result.resultCode);
271         List<ScopedException> errors = ScopedException.extractErrors(result.error);
272         assertEquals(result.error, 1, errors.size());
273         final ScopedException ex = errors.get(0);
274         final ScopedErrorReport report = ex.getScopedErrorReport();
275         final File errorFile = new File(report.getFilePath());
276         assertTrue(errorFile.exists());
277         assertEquals(
278                 new File(testFolder, "/app/src/main/res/layout/merge_include.xml")
279                         .getCanonicalFile(),
280                 errorFile.getCanonicalFile());
281         assertEquals("Merge shouldn't support includes as root. Error message was '" + result.error,
282                 ErrorMessages.INCLUDE_INSIDE_MERGE, ex.getBareMessage());
283     }
284 }
285