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