1 /* 2 * Copyright (C) 2010 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 package com.android.tradefed.config; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertNotSame; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import static java.util.Map.entry; 27 28 import com.android.tradefed.build.IBuildInfo; 29 import com.android.tradefed.build.IBuildProvider; 30 import com.android.tradefed.build.IDeviceBuildProvider; 31 import com.android.tradefed.build.LocalDeviceBuildProvider; 32 import com.android.tradefed.config.ConfigurationDef.ConfigObjectDef; 33 import com.android.tradefed.config.ConfigurationFactory.ConfigId; 34 import com.android.tradefed.config.remote.IRemoteFileResolver.ResolvedFile; 35 import com.android.tradefed.log.ILeveledLogOutput; 36 import com.android.tradefed.log.Log.LogLevel; 37 import com.android.tradefed.log.LogUtil.CLog; 38 import com.android.tradefed.targetprep.DeviceWiper; 39 import com.android.tradefed.targetprep.ILabPreparer; 40 import com.android.tradefed.targetprep.StubTargetPreparer; 41 import com.android.tradefed.targetprep.multi.StubMultiTargetPreparer; 42 import com.android.tradefed.util.FileUtil; 43 import com.android.tradefed.util.StreamUtil; 44 45 import com.google.common.collect.ImmutableSet; 46 47 import org.junit.Before; 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 import org.junit.runners.JUnit4; 51 import org.mockito.Mockito; 52 53 import java.io.ByteArrayOutputStream; 54 import java.io.File; 55 import java.io.IOException; 56 import java.io.InputStream; 57 import java.io.PrintStream; 58 import java.net.URI; 59 import java.util.ArrayList; 60 import java.util.Arrays; 61 import java.util.Enumeration; 62 import java.util.HashMap; 63 import java.util.HashSet; 64 import java.util.LinkedHashMap; 65 import java.util.List; 66 import java.util.Map; 67 import java.util.Map.Entry; 68 import java.util.Set; 69 import java.util.jar.JarEntry; 70 import java.util.jar.JarFile; 71 72 /** Unit tests for {@link ConfigurationFactory} */ 73 @RunWith(JUnit4.class) 74 public class ConfigurationFactoryTest { 75 76 private ConfigurationFactory mFactory; 77 // Create a real instance for tests that checks the content of our configs. making it static to 78 // reduce the runtime of reloading the config thanks to the caching of configurations. 79 private static ConfigurationFactory mRealFactory = new ConfigurationFactory(); 80 81 /** the test config name that is built into this jar */ 82 private static final String TEST_CONFIG = "test-config"; 83 private static final String GLOBAL_TEST_CONFIG = "global-config"; 84 private static final String INCLUDE_CONFIG = "include-config"; 85 86 private static final List<String> JAR_TO_CHECK = new ArrayList<>(); 87 88 static { 89 JAR_TO_CHECK.add("tradefed-contrib.jar"); 90 JAR_TO_CHECK.add("google-tradefed-contrib.jar"); 91 } 92 93 @Before setUp()94 public void setUp() throws Exception { 95 96 mFactory = 97 new ConfigurationFactory() { 98 @Override 99 protected String getConfigPrefix() { 100 return "testconfigs/"; 101 } 102 }; 103 } 104 105 /** Initial test to ensure all config names on classpath are loadable */ 106 @Test testLoadAllConfigs()107 public void testLoadAllConfigs() throws Exception { 108 ConfigurationFactory spyFactory = Mockito.spy(mRealFactory); 109 Mockito.doReturn(new HashSet<String>()).when(spyFactory).getConfigNamesFromTestCases(null); 110 111 // we dry-run the templates otherwise it will always fail. 112 spyFactory.loadAllConfigs(false); 113 assertTrue(spyFactory.getMapConfig().size() > 0); 114 Mockito.verify(spyFactory, Mockito.times(1)).getConfigNamesFromTestCases(null); 115 116 // Verify that there is no cross-referencing between core and contrib: 117 // core configs should not use any contrib object. 118 Map<String, String> configAndJar = spyFactory.getConfigSetFromClasspathFromJar(null); 119 Map<String, List<String>> classInContribJars = getClassInContribJar(); 120 121 List<String> exceptionConfigJar = new ArrayList<>(); 122 for (Entry<ConfigId, ConfigurationDef> entry : spyFactory.getMapConfig().entrySet()) { 123 String configName = entry.getKey().name; 124 String jarName = configAndJar.get(configName); 125 ConfigurationDef cDef = entry.getValue(); 126 if (JAR_TO_CHECK.contains(jarName)) { 127 continue; 128 } 129 130 for (List<ConfigObjectDef> cObjects : cDef.getObjectClassMap().values()) { 131 for (ConfigObjectDef o : cObjects) { 132 for (String contribJar : classInContribJars.keySet()) { 133 if (classInContribJars.get(contribJar).contains(o.mClassName)) { 134 exceptionConfigJar.add( 135 String.format( 136 "%s is a core-configuration (%s). It shouldn't contain" 137 + " a contrib objects '%s' from %s.", 138 configName, jarName, o.mClassName, contribJar)); 139 } 140 } 141 } 142 } 143 } 144 if (!exceptionConfigJar.isEmpty()) { 145 fail( 146 String.format( 147 "Found some dependencies of core on contrib:\n%s", 148 String.join("\n", exceptionConfigJar))); 149 } 150 } 151 152 /** Initial test to ensure all configs on classpath can be fully loaded and parsed */ 153 @Test testLoadAndPrintAllConfigs()154 public void testLoadAndPrintAllConfigs() throws ConfigurationException { 155 ConfigurationFactory spyFactory = Mockito.spy(mRealFactory); 156 Mockito.doReturn(new HashSet<String>()).when(spyFactory).getConfigNamesFromTestCases(null); 157 158 // Printing the help involves more checks since it tries to resolve the config objects. 159 spyFactory.loadAndPrintAllConfigs(); 160 assertTrue(spyFactory.getMapConfig().size() > 0); 161 Mockito.verify(spyFactory, Mockito.times(1)).getConfigNamesFromTestCases(null); 162 } 163 164 /** Test that a config xml defined in this test jar can be read as a built-in */ 165 @Test testGetConfiguration_extension()166 public void testGetConfiguration_extension() throws ConfigurationException { 167 assertConfigValid(TEST_CONFIG); 168 } 169 buildMap(String... args)170 private Map<String, String> buildMap(String... args) { 171 if ((args.length % 2) != 0) { 172 throw new IllegalArgumentException(String.format( 173 "Expected an even number of args; got %d", args.length)); 174 } 175 176 final Map<String, String> map = new HashMap<String, String>(args.length / 2); 177 for (int i = 0; i < args.length; i += 2) { 178 map.put(args[i], args[i + 1]); 179 } 180 181 return map; 182 } 183 184 /** Make sure that ConfigId behaves in the right way to serve as a hash key */ 185 @Test testConfigId_equals()186 public void testConfigId_equals() { 187 final ConfigId config1a = new ConfigId("one"); 188 final ConfigId config1b = new ConfigId("one"); 189 final ConfigId config2 = new ConfigId("two"); 190 final ConfigId config3a = new ConfigId("one", buildMap("target", "foo")); 191 final ConfigId config3b = new ConfigId("one", buildMap("target", "foo")); 192 final ConfigId config4 = new ConfigId("two", buildMap("target", "bar")); 193 194 assertEquals(config1a, config1b); 195 assertEquals(config3a, config3b); 196 197 // Check for false equivalences, and don't depend on #equals being commutative 198 assertFalse(config1a.equals(config2)); 199 assertFalse(config1a.equals(config3a)); 200 assertFalse(config1a.equals(config4)); 201 202 assertFalse(config2.equals(config1a)); 203 assertFalse(config2.equals(config3a)); 204 assertFalse(config2.equals(config4)); 205 206 assertFalse(config3a.equals(config1a)); 207 assertFalse(config3a.equals(config2)); 208 assertFalse(config3a.equals(config4)); 209 210 assertFalse(config4.equals(config1a)); 211 assertFalse(config4.equals(config2)); 212 assertFalse(config4.equals(config3a)); 213 } 214 215 /** Make sure that ConfigId behaves in the right way to serve as a hash key */ 216 @Test testConfigId_hashKey()217 public void testConfigId_hashKey() { 218 final Map<ConfigId, String> map = new HashMap<>(); 219 final ConfigId config1a = new ConfigId("one"); 220 final ConfigId config1b = new ConfigId("one"); 221 final ConfigId config2 = new ConfigId("two"); 222 final ConfigId config3a = new ConfigId("one", buildMap("target", "foo")); 223 final ConfigId config3b = new ConfigId("one", buildMap("target", "foo")); 224 final ConfigId config4 = new ConfigId("two", buildMap("target", "bar")); 225 226 // Make sure that keys config1a and config1b behave identically 227 map.put(config1a, "1a"); 228 assertEquals("1a", map.get(config1a)); 229 assertEquals("1a", map.get(config1b)); 230 231 map.put(config1b, "1b"); 232 assertEquals("1b", map.get(config1a)); 233 assertEquals("1b", map.get(config1b)); 234 235 assertFalse(map.containsKey(config2)); 236 assertFalse(map.containsKey(config3a)); 237 assertFalse(map.containsKey(config4)); 238 239 // Make sure that keys config3a and config3b behave identically 240 map.put(config3a, "3a"); 241 assertEquals("3a", map.get(config3a)); 242 assertEquals("3a", map.get(config3b)); 243 244 map.put(config3b, "3b"); 245 assertEquals("3b", map.get(config3a)); 246 assertEquals("3b", map.get(config3b)); 247 248 assertEquals(2, map.size()); 249 assertFalse(map.containsKey(config2)); 250 assertFalse(map.containsKey(config4)); 251 252 // It's unlikely for these to fail if the above tests all passed, but just fill everything 253 // out for completeness 254 map.put(config2, "2"); 255 map.put(config4, "4"); 256 257 assertEquals(4, map.size()); 258 assertEquals("1b", map.get(config1a)); 259 assertEquals("1b", map.get(config1b)); 260 assertEquals("2", map.get(config2)); 261 assertEquals("3b", map.get(config3a)); 262 assertEquals("3b", map.get(config3b)); 263 assertEquals("4", map.get(config4)); 264 } 265 266 /** Test specifying a config xml by file path */ 267 @Test testGetConfiguration_xmlpath()268 public void testGetConfiguration_xmlpath() throws ConfigurationException, IOException { 269 // extract the test-config.xml into a tmp file 270 InputStream configStream = getClass().getResourceAsStream( 271 String.format("/testconfigs/%s.xml", TEST_CONFIG)); 272 File tmpFile = FileUtil.createTempFile(TEST_CONFIG, ".xml"); 273 try { 274 FileUtil.writeToFile(configStream, tmpFile); 275 assertConfigValid(tmpFile.getAbsolutePath()); 276 // check reading it again - should grab the cached version 277 assertConfigValid(tmpFile.getAbsolutePath()); 278 } finally { 279 FileUtil.deleteFile(tmpFile); 280 } 281 } 282 283 /** Test that a config xml defined in this test jar can be read as a built-in */ 284 @Test testGetGlobalConfiguration_extension()285 public void testGetGlobalConfiguration_extension() throws ConfigurationException { 286 assertGlobalConfigValid(GLOBAL_TEST_CONFIG); 287 } 288 289 /** Test specifying a config xml by file path */ 290 @Test testGetGlobalConfiguration_xmlpath()291 public void testGetGlobalConfiguration_xmlpath() throws ConfigurationException, IOException { 292 // extract the test-config.xml into a tmp file 293 InputStream configStream = getClass().getResourceAsStream( 294 String.format("/testconfigs/%s.xml", GLOBAL_TEST_CONFIG)); 295 File tmpFile = FileUtil.createTempFile(GLOBAL_TEST_CONFIG, ".xml"); 296 try { 297 FileUtil.writeToFile(configStream, tmpFile); 298 assertGlobalConfigValid(tmpFile.getAbsolutePath()); 299 // check reading it again - should grab the cached version 300 assertGlobalConfigValid(tmpFile.getAbsolutePath()); 301 } finally { 302 FileUtil.deleteFile(tmpFile); 303 } 304 } 305 306 /** 307 * Checks all config attributes are non-null 308 */ assertConfigValid(String name)309 private void assertConfigValid(String name) throws ConfigurationException { 310 IConfiguration config = mFactory.createConfigurationFromArgs(new String[] {name}); 311 assertNotNull(config); 312 } 313 314 /** 315 * Checks all config attributes are non-null 316 */ assertGlobalConfigValid(String name)317 private void assertGlobalConfigValid(String name) throws ConfigurationException { 318 List<String> unprocessed = new ArrayList<String>(); 319 IGlobalConfiguration config = 320 mFactory.createGlobalConfigurationFromArgs(new String[] {name}, unprocessed); 321 assertNotNull(config); 322 assertNotNull(config.getDeviceMonitors()); 323 assertNotNull(config.getWtfHandler()); 324 assertTrue(unprocessed.isEmpty()); 325 } 326 327 /** 328 * Test calling {@link ConfigurationFactory#createConfigurationFromArgs(String[])} with a name 329 * that does not exist. 330 */ 331 @Test testCreateConfigurationFromArgs_missing()332 public void testCreateConfigurationFromArgs_missing() { 333 try { 334 mFactory.createConfigurationFromArgs(new String[] {"non existent"}); 335 fail("did not throw ConfigurationException"); 336 } catch (ConfigurationException e) { 337 // expected 338 } 339 } 340 341 /** 342 * Test calling {@link ConfigurationFactory#createConfigurationFromArgs(String[])} with config 343 * that has unset mandatory options. 344 * 345 * <p>Expect this to succeed, since mandatory option validation no longer happens at 346 * configuration instantiation time. 347 */ 348 @Test testCreateConfigurationFromArgs_mandatory()349 public void testCreateConfigurationFromArgs_mandatory() throws ConfigurationException { 350 assertNotNull(mFactory.createConfigurationFromArgs(new String[] {"mandatory-config"})); 351 } 352 353 /** 354 * Test passing empty arg list to {@link 355 * ConfigurationFactory#createConfigurationFromArgs(String[])}. 356 */ 357 @Test testCreateConfigurationFromArgs_empty()358 public void testCreateConfigurationFromArgs_empty() { 359 try { 360 mFactory.createConfigurationFromArgs(new String[] {}); 361 fail("did not throw ConfigurationException"); 362 } catch (ConfigurationException e) { 363 // expected 364 } 365 } 366 367 /** Test {@link ConfigurationFactory#createConfigurationFromArgs(String[])} using TEST_CONFIG */ 368 @Test testCreateConfigurationFromArgs()369 public void testCreateConfigurationFromArgs() throws ConfigurationException { 370 // pick an arbitrary option to test to ensure it gets populated 371 IConfiguration config = mFactory.createConfigurationFromArgs(new String[] {TEST_CONFIG, 372 "--log-level", LogLevel.VERBOSE.getStringValue()}); 373 ILeveledLogOutput logger = config.getLogOutput(); 374 assertEquals(LogLevel.VERBOSE, logger.getLogLevel()); 375 } 376 377 /** 378 * Test {@link ConfigurationFactory#createConfigurationFromArgs(String[])} when extra positional 379 * arguments are supplied 380 */ 381 @Test testCreateConfigurationFromArgs_unprocessedArgs()382 public void testCreateConfigurationFromArgs_unprocessedArgs() { 383 try { 384 mFactory.createConfigurationFromArgs(new String[] {TEST_CONFIG, "--log-level", 385 LogLevel.VERBOSE.getStringValue(), "blah"}); 386 fail("ConfigurationException not thrown"); 387 } catch (ConfigurationException e) { 388 // expected 389 } 390 } 391 392 /** Test {@link ConfigurationFactory#printHelp(PrintStream)} */ 393 @Test testPrintHelp()394 public void testPrintHelp() { 395 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 396 PrintStream mockPrintStream = new PrintStream(outputStream); 397 mRealFactory.printHelp(mockPrintStream); 398 // verify all the instrument config names are present 399 final String usageString = outputStream.toString(); 400 assertTrue(usageString.contains("instrument")); 401 } 402 403 /** 404 * Test {@link ConfigurationFactory#printHelpForConfig(String[], boolean, PrintStream)} when 405 * config referenced by args exists 406 */ 407 @Test testPrintHelpForConfig_configExists()408 public void testPrintHelpForConfig_configExists() { 409 String[] args = new String[] {TEST_CONFIG}; 410 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 411 PrintStream mockPrintStream = new PrintStream(outputStream); 412 mFactory.printHelpForConfig(args, true, mockPrintStream); 413 414 // verify the default configs name used is present 415 final String usageString = outputStream.toString(); 416 assertTrue(usageString.contains(TEST_CONFIG)); 417 // TODO: add stricter verification 418 } 419 420 /** Test {@link ConfigurationFactory#getConfigList()} */ 421 @Test testListAllConfigs()422 public void testListAllConfigs() { 423 List<String> listConfigs = mRealFactory.getConfigList(); 424 assertTrue(listConfigs.size() != 0); 425 // Check that our basic configs are always here 426 assertTrue(listConfigs.contains("empty")); 427 assertTrue(listConfigs.contains("host")); 428 assertTrue(listConfigs.contains("instrument")); 429 } 430 431 /** 432 * Test {@link ConfigurationFactory#getConfigList(String, boolean)} where we list the config in 433 * a sub path only 434 */ 435 @Test testListSubConfig()436 public void testListSubConfig() { 437 final String subDir = "suite/"; 438 List<String> listConfigs = mRealFactory.getConfigList(subDir, false); 439 assertTrue(listConfigs.size() != 0); 440 // Check that our basic configs are always here 441 assertTrue(listConfigs.contains("suite/stub1")); 442 assertTrue(listConfigs.contains("suite/stub2")); 443 // Validate that all listed config are indeed from the subdir. 444 for (String config : listConfigs) { 445 assertTrue(config.startsWith(subDir)); 446 } 447 } 448 449 /** Test loading a config that includes another config. */ 450 @Test testCreateConfigurationFromArgs_includeConfig()451 public void testCreateConfigurationFromArgs_includeConfig() throws Exception { 452 IConfiguration config = mFactory.createConfigurationFromArgs( 453 new String[]{INCLUDE_CONFIG}); 454 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 455 assertTrue(config.getTests().get(1) instanceof StubOptionTest); 456 StubOptionTest fromTestConfig = (StubOptionTest) config.getTests().get(0); 457 StubOptionTest fromIncludeConfig = (StubOptionTest) config.getTests().get(1); 458 assertEquals("valueFromTestConfig", fromTestConfig.mOption); 459 assertEquals("valueFromIncludeConfig", fromIncludeConfig.mOption); 460 } 461 462 /** 463 * Test loading a config that uses the "default" attribute of a template-include tag to include 464 * another config. 465 */ 466 @Test testCreateConfigurationFromArgs_defaultTemplateInclude_default()467 public void testCreateConfigurationFromArgs_defaultTemplateInclude_default() throws Exception { 468 // The default behavior is to include test-config directly. Nesting is such that innermost 469 // elements come first. 470 IConfiguration config = mFactory.createConfigurationFromArgs( 471 new String[]{"template-include-config-with-default"}); 472 assertEquals(2, config.getTests().size()); 473 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 474 assertTrue(config.getTests().get(1) instanceof StubOptionTest); 475 StubOptionTest innerConfig = (StubOptionTest) config.getTests().get(0); 476 StubOptionTest outerConfig = (StubOptionTest) config.getTests().get(1); 477 assertEquals("valueFromTestConfig", innerConfig.mOption); 478 assertEquals("valueFromTemplateIncludeWithDefaultConfig", outerConfig.mOption); 479 } 480 481 /** 482 * Test using {@code <include>} to load a config that uses the "default" attribute of a 483 * template-include tag to include a third config. 484 */ 485 @Test testCreateConfigurationFromArgs_includeTemplateIncludeWithDefault()486 public void testCreateConfigurationFromArgs_includeTemplateIncludeWithDefault() 487 throws Exception { 488 // The default behavior is to include test-config directly. Nesting is such that innermost 489 // elements come first. 490 IConfiguration config = mFactory.createConfigurationFromArgs( 491 new String[]{"include-template-config-with-default"}); 492 assertEquals(3, config.getTests().size()); 493 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 494 assertTrue(config.getTests().get(1) instanceof StubOptionTest); 495 assertTrue(config.getTests().get(2) instanceof StubOptionTest); 496 StubOptionTest innerConfig = (StubOptionTest) config.getTests().get(0); 497 StubOptionTest middleConfig = (StubOptionTest) config.getTests().get(1); 498 StubOptionTest outerConfig = (StubOptionTest) config.getTests().get(2); 499 assertEquals("valueFromTestConfig", innerConfig.mOption); 500 assertEquals("valueFromTemplateIncludeWithDefaultConfig", middleConfig.mOption); 501 assertEquals("valueFromIncludeTemplateConfigWithDefault", outerConfig.mOption); 502 } 503 504 /** 505 * Test loading a config that uses the "default" attribute of a template-include tag to include 506 * another config. In this case, we override the default attribute on the commandline. 507 */ 508 @Test testCreateConfigurationFromArgs_defaultTemplateInclude_alternate()509 public void testCreateConfigurationFromArgs_defaultTemplateInclude_alternate() 510 throws Exception { 511 IConfiguration config = mFactory.createConfigurationFromArgs( 512 new String[]{"template-include-config-with-default", "--template:map", "target", 513 INCLUDE_CONFIG}); 514 assertEquals(3, config.getTests().size()); 515 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 516 assertTrue(config.getTests().get(1) instanceof StubOptionTest); 517 assertTrue(config.getTests().get(2) instanceof StubOptionTest); 518 519 StubOptionTest innerConfig = (StubOptionTest) config.getTests().get(0); 520 StubOptionTest middleConfig = (StubOptionTest) config.getTests().get(1); 521 StubOptionTest outerConfig = (StubOptionTest) config.getTests().get(2); 522 523 assertEquals("valueFromTestConfig", innerConfig.mOption); 524 assertEquals("valueFromIncludeConfig", middleConfig.mOption); 525 assertEquals("valueFromTemplateIncludeWithDefaultConfig", outerConfig.mOption); 526 } 527 528 /** Test loading a config that uses template-include to include another config. */ 529 @Test testCreateConfigurationFromArgs_templateInclude()530 public void testCreateConfigurationFromArgs_templateInclude() throws Exception { 531 IConfiguration config = mFactory.createConfigurationFromArgs( 532 new String[]{"template-include-config", "--template:map", "target", 533 "test-config"}); 534 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 535 assertTrue(config.getTests().get(1) instanceof StubOptionTest); 536 StubOptionTest fromTestConfig = (StubOptionTest) config.getTests().get(0); 537 StubOptionTest fromTemplateIncludeConfig = (StubOptionTest) config.getTests().get(1); 538 assertEquals("valueFromTestConfig", fromTestConfig.mOption); 539 assertEquals("valueFromTemplateIncludeConfig", fromTemplateIncludeConfig.mOption); 540 } 541 542 @Test testCreateConfigurationFromArgs_repeatedTemplate()543 public void testCreateConfigurationFromArgs_repeatedTemplate() throws Exception { 544 try { 545 mFactory.createConfigurationFromArgs( 546 new String[] {"repeated-template", "--template:map", "target", "empty"}); 547 fail("Should have thrown an exception."); 548 } catch (ConfigurationException expected) { 549 assertEquals( 550 "Failed to parse config xml 'repeated-template'. Reason: Template named " 551 + "'target' appeared more than once.", 552 expected.getMessage()); 553 } 554 } 555 556 /** Test loading a config that uses template-include to include another config. */ 557 @Test testCreateConfigurationFromArgs_templateInclude_multiKey()558 public void testCreateConfigurationFromArgs_templateInclude_multiKey() throws Exception { 559 try { 560 mFactory.createConfigurationFromArgs( 561 new String[] { 562 "template-include-config", 563 "--template:map", 564 "target", 565 "test-config", 566 "--template:map", 567 "target", 568 "test-config" 569 }); 570 fail("Should have thrown an exception."); 571 } catch (ConfigurationException expected) { 572 // Expected 573 assertEquals( 574 "More than one template specified for key 'target'", expected.getMessage()); 575 } 576 } 577 578 /** Make sure that we throw a useful error when template-include usage is underspecified. */ 579 @Test testCreateConfigurationFromArgs_templateInclude_unspecified()580 public void testCreateConfigurationFromArgs_templateInclude_unspecified() throws Exception { 581 final String configName = "template-include-config"; 582 try { 583 mFactory.createConfigurationFromArgs(new String[]{configName}); 584 fail ("ConfigurationException not thrown"); 585 } catch (ConfigurationException e) { 586 // Make sure that we get the expected error message 587 final String msg = e.getMessage(); 588 assertNotNull(msg); 589 590 assertTrue(String.format("Error message does not mention the name of the broken " + 591 "config. msg was: %s", msg), msg.contains(configName)); 592 593 // Error message should help people to resolve the problem 594 assertTrue(String.format("Error message should help user to resolve the " + 595 "template-include. msg was: %s", msg), 596 msg.contains(String.format("--template:map %s", "target"))); 597 CLog.e(msg); 598 assertTrue( 599 String.format( 600 "Error message should mention the ability to specify a " 601 + "default resolution. msg was: %s", 602 msg), 603 msg.contains("'default'")); 604 } 605 } 606 607 /** 608 * Make sure that we throw a useful error when template-include mentions a target configuration 609 * that doesn't exist. 610 */ 611 @Test testCreateConfigurationFromArgs_templateInclude_missing()612 public void testCreateConfigurationFromArgs_templateInclude_missing() throws Exception { 613 final String configName = "template-include-config"; 614 final String includeName = "no-exist"; 615 616 try { 617 mFactory.createConfigurationFromArgs( 618 new String[]{configName, "--template:map", "target", includeName}); 619 fail ("ConfigurationException not thrown"); 620 } catch (ConfigurationException e) { 621 // Make sure that we get the expected error message 622 final String msg = e.getMessage(); 623 assertNotNull(msg); 624 625 assertTrue(String.format("Error message does not mention the name of the broken " + 626 "config. msg was: %s", msg), msg.contains(configName)); 627 assertTrue(String.format("Error message does not mention the name of the missing " + 628 "include target. msg was: %s", msg), msg.contains(includeName)); 629 } 630 } 631 632 /** Test loading a config that includes a local config. */ 633 @Test testCreateConfigurationFromArgs_templateInclude_local()634 public void testCreateConfigurationFromArgs_templateInclude_local() throws Exception { 635 final String configName = "template-include-config"; 636 InputStream configStream = getClass().getResourceAsStream( 637 String.format("/testconfigs/%s.xml", INCLUDE_CONFIG)); 638 File tmpConfig = FileUtil.createTempFile(INCLUDE_CONFIG, ".xml"); 639 try { 640 FileUtil.writeToFile(configStream, tmpConfig); 641 final String includeName = tmpConfig.getAbsolutePath(); 642 IConfiguration config = mFactory.createConfigurationFromArgs( 643 new String[]{configName, "--template:map", "target", includeName}); 644 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 645 assertTrue(config.getTests().get(1) instanceof StubOptionTest); 646 StubOptionTest fromTestConfig = (StubOptionTest) config.getTests().get(0); 647 StubOptionTest fromIncludeConfig = (StubOptionTest) config.getTests().get(1); 648 assertEquals("valueFromTestConfig", fromTestConfig.mOption); 649 assertEquals("valueFromIncludeConfig", fromIncludeConfig.mOption); 650 } finally { 651 FileUtil.deleteFile(tmpConfig); 652 } 653 } 654 655 /** 656 * This unit test codifies the expectation that an inner {@code <template-include>} tag is 657 * properly found and replaced by a config containing another template that is resolved. MAIN 658 * CONFIG -> Template 1 -> Template 2 = Works! 659 */ 660 @Test testCreateConfigurationFromArgs_templateInclude_dependent()661 public void testCreateConfigurationFromArgs_templateInclude_dependent() throws Exception { 662 final String configName = "depend-template-include-config"; 663 final String depTargetName = "template-include-config"; 664 final String targetName = "test-config"; 665 666 IConfiguration config = 667 mFactory.createConfigurationFromArgs( 668 new String[] { 669 configName, 670 "--template:map", 671 "dep-target", 672 depTargetName, 673 "--template:map", 674 "target", 675 targetName 676 }); 677 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 678 assertTrue(config.getTests().get(1) instanceof StubOptionTest); 679 } 680 681 /** 682 * This unit test codifies the expectation that an inner {@code <template-include>} tag replaced 683 * by a missing config raise a failure. MAIN CONFIG -> Template 1 -> Template 2(missing) = error 684 */ 685 @Test testCreateConfigurationFromArgs_templateInclude_dependent_missing()686 public void testCreateConfigurationFromArgs_templateInclude_dependent_missing() 687 throws Exception { 688 final String configName = "depend-template-include-config"; 689 final String depTargetName = "template-include-config"; 690 final String targetName = "test-config-missing"; 691 final String expError = String.format( 692 "Bundled config '%s' is including a config '%s' that's neither local nor bundled.", 693 depTargetName, targetName); 694 695 try { 696 mFactory.createConfigurationFromArgs(new String[]{configName, 697 "--template:map", "dep-target", depTargetName, 698 "--template:map", "target", targetName}); 699 fail ("ConfigurationException not thrown"); 700 } catch (ConfigurationException e) { 701 // Make sure that we get the expected error message 702 assertEquals(expError, e.getMessage()); 703 } 704 } 705 706 /** 707 * This unit test codifies the expectation that an inner {@code <template-include>} tag that 708 * doesn't have a default attribute will fail because cannot be resolved. MAIN CONFIG -> 709 * Template 1 -> Template 2(no Default, no args override) = error 710 */ 711 @Test testCreateConfigurationFromArgs_templateInclude_dependent_nodefault()712 public void testCreateConfigurationFromArgs_templateInclude_dependent_nodefault() 713 throws Exception { 714 final String configName = "depend-template-include-config"; 715 final String depTargetName = "template-include-config"; 716 final String expError = String.format( 717 "Failed to parse config xml '%s'. Reason: " + 718 ConfigurationXmlParser.ConfigHandler.INNER_TEMPLATE_INCLUDE_ERROR, 719 configName, configName, depTargetName); 720 try { 721 mFactory.createConfigurationFromArgs(new String[]{configName, 722 "--template:map", "dep-target", depTargetName}); 723 fail ("ConfigurationException not thrown"); 724 } catch (ConfigurationException e) { 725 assertEquals(expError, e.getMessage()); 726 } 727 } 728 729 /** 730 * This unit test codifies the expectation that an inner {@code <template-include>} tag that is 731 * inside an include tag will be correctly replaced by arguments. Main Config -> Include 1 -> 732 * Template 1 -> test-config.xml 733 */ 734 @Test testCreateConfigurationFromArgs_include_dependent()735 public void testCreateConfigurationFromArgs_include_dependent() throws Exception { 736 final String configName = "include-template-config"; 737 final String targetName = "test-config"; 738 try { 739 IConfiguration config = mFactory.createConfigurationFromArgs(new String[]{configName, 740 "--template:map", "target", targetName}); 741 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 742 assertTrue(config.getTests().get(1) instanceof StubOptionTest); 743 } catch (ConfigurationException e) { 744 fail(e.getMessage()); 745 } 746 } 747 748 /** 749 * This unit test ensure that when a configuration use included configuration the top 750 * configuration description is kept. 751 */ 752 @Test testTopDescriptionIsPreserved()753 public void testTopDescriptionIsPreserved() throws ConfigurationException { 754 final String configName = "top-config"; 755 Map<String, String> fakeTemplate = new HashMap<>(); 756 fakeTemplate.put("target", "included-config"); 757 ConfigurationDef test = mFactory.new ConfigLoader(false) 758 .getConfigurationDef(configName, fakeTemplate); 759 assertEquals("top config description", test.getDescription()); 760 } 761 762 /** 763 * This unit test codifies the expectation that an inner {@code <template-include>} tag that is 764 * inside an include tag will be correctly rejected if no arguments can match it and no default 765 * value is present. Main Config -> Include 1 -> Template 1 (No default, no args override) = 766 * error 767 */ 768 @Test testCreateConfigurationFromArgs_include_dependent_nodefault()769 public void testCreateConfigurationFromArgs_include_dependent_nodefault() throws Exception { 770 final String configName = "include-template-config"; 771 final String includeTargetName = "template-include-config"; 772 final String expError = String.format( 773 "Failed to parse config xml '%s'. Reason: " + 774 ConfigurationXmlParser.ConfigHandler.INNER_TEMPLATE_INCLUDE_ERROR, 775 configName, configName, includeTargetName); 776 try { 777 mFactory.createConfigurationFromArgs(new String[]{configName}); 778 fail ("ConfigurationException not thrown"); 779 } catch (ConfigurationException e) { 780 assertEquals(expError, e.getMessage()); 781 } 782 } 783 784 /** Test loading a config that tries to include itself */ 785 @Test testCreateConfigurationFromArgs_recursiveInclude()786 public void testCreateConfigurationFromArgs_recursiveInclude() throws Exception { 787 try { 788 mFactory.createConfigurationFromArgs(new String[] {"recursive-config"}); 789 fail("ConfigurationException not thrown"); 790 } catch (ConfigurationException e) { 791 // expected 792 } 793 } 794 795 /** 796 * Test loading a config that tries to replace a template with itself will fail because it 797 * creates a cycle of configuration. 798 */ 799 @Test testCreateConfigurationFromArgs_recursiveTemplate()800 public void testCreateConfigurationFromArgs_recursiveTemplate() throws Exception { 801 final String configName = "depend-template-include-config"; 802 final String depTargetName = "depend-template-include-config"; 803 final String expError = String.format( 804 "Circular configuration include: config '%s' is already included", depTargetName); 805 try { 806 mFactory.createConfigurationFromArgs(new String[]{configName, 807 "--template:map", "dep-target", depTargetName}); 808 fail ("ConfigurationException not thrown"); 809 } catch (ConfigurationException e) { 810 assertEquals(expError, e.getMessage()); 811 } 812 } 813 814 /** 815 * Re-apply a template on a lower config level. Should result in a fail to parse because each 816 * template:map can only be applied once. So missing the default will throw exception. 817 */ 818 @Test testCreateConfigurationFromArgs_template_multilevel()819 public void testCreateConfigurationFromArgs_template_multilevel() throws Exception { 820 final String configName = "depend-template-include-config"; 821 final String depTargetName = "template-include-config"; 822 final String depTargetName2 = "template-collision-include-config"; 823 final String expError = String.format( 824 "Failed to parse config xml '%s'. Reason: " + 825 ConfigurationXmlParser.ConfigHandler.INNER_TEMPLATE_INCLUDE_ERROR, 826 configName, configName, depTargetName2); 827 try { 828 mFactory.createConfigurationFromArgs(new String[]{configName, 829 "--template:map", "dep-target", depTargetName, 830 "--template:map", "target", depTargetName2}); 831 fail ("ConfigurationException not thrown"); 832 } catch (ConfigurationException e) { 833 assertEquals(expError, e.getMessage()); 834 } 835 } 836 837 /** 838 * Re-apply a template twice. Should result in an error only because the configs included have 839 * several build_provider 840 */ 841 @Test testCreateConfigurationFromArgs_templateCollision()842 public void testCreateConfigurationFromArgs_templateCollision() throws Exception { 843 final String configName = "template-collision-include-config"; 844 final String depTargetName = "template-include-config-with-default"; 845 final String expError = 846 "Failed to parse config xml 'template-collision-include-config'. Reason: " 847 + "Template named 'target' appeared more than once."; 848 try { 849 mFactory.createConfigurationFromArgs(new String[]{configName, 850 "--template:map", "target-col", depTargetName, 851 "--template:map", "target-col2", depTargetName}); 852 fail ("ConfigurationException not thrown"); 853 } catch (ConfigurationException e) { 854 assertEquals(expError, e.getMessage()); 855 } 856 } 857 858 /** Test loading a config that tries to include a non-bundled config. */ 859 @Test testCreateConfigurationFromArgs_nonBundledInclude()860 public void testCreateConfigurationFromArgs_nonBundledInclude() throws Exception { 861 try { 862 mFactory.createConfigurationFromArgs(new String[] {"non-bundled-config"}); 863 fail("ConfigurationException not thrown"); 864 } catch (ConfigurationException e) { 865 // expected 866 } 867 } 868 869 /** Test reloading a config after it has been updated. */ 870 @Test testCreateConfigurationFromArgs_localConfigReload()871 public void testCreateConfigurationFromArgs_localConfigReload() 872 throws ConfigurationException, IOException { 873 File localConfigFile = FileUtil.createTempFile("local-config", ".xml"); 874 try { 875 // Copy the config to the local filesystem 876 InputStream source = getClass().getResourceAsStream("/testconfigs/local-config.xml"); 877 FileUtil.writeToFile(source, localConfigFile); 878 879 // Depending on the system, file modification times might not have greater than 1 second 880 // resolution. Backdate the original contents so that when we write to the file later, 881 // it shows up as a new change. 882 localConfigFile.setLastModified(System.currentTimeMillis() - 5000); 883 884 // Load the configuration from the local file 885 IConfiguration config = mFactory.createConfigurationFromArgs( 886 new String[] { localConfigFile.getAbsolutePath() }); 887 if (!(config.getTests().get(0) instanceof StubOptionTest)) { 888 fail(String.format("Expected a StubOptionTest, but got %s", 889 config.getTests().get(0).getClass().getName())); 890 return; 891 } 892 StubOptionTest test = (StubOptionTest)config.getTests().get(0); 893 assertEquals("valueFromOriginalConfig", test.mOption); 894 895 // Change the contents of the local file 896 source = getClass().getResourceAsStream("/testconfigs/local-config-update.xml"); 897 FileUtil.writeToFile(source, localConfigFile); 898 899 // Get the configuration again and verify that it picked up the update 900 config = mFactory.createConfigurationFromArgs( 901 new String[] { localConfigFile.getAbsolutePath() }); 902 if (!(config.getTests().get(0) instanceof StubOptionTest)) { 903 fail(String.format("Expected a StubOptionTest, but got %s", 904 config.getTests().get(0).getClass().getName())); 905 return; 906 } 907 test = (StubOptionTest)config.getTests().get(0); 908 assertEquals("valueFromUpdatedConfig", test.mOption); 909 } finally { 910 FileUtil.deleteFile(localConfigFile); 911 } 912 } 913 914 /** Test loading a config that has a circular include */ 915 @Test testCreateConfigurationFromArgs_circularInclude()916 public void testCreateConfigurationFromArgs_circularInclude() throws Exception { 917 try { 918 mFactory.createConfigurationFromArgs(new String[] {"circular-config"}); 919 fail("ConfigurationException not thrown"); 920 } catch (ConfigurationException e) { 921 // expected 922 } 923 } 924 925 /** 926 * If a template:map argument is passed but doesn't match any {@code <template-include>} tag a 927 * configuration exception will be thrown for unmatched arguments. 928 */ 929 @Test testCreateConfigurationFromArgs_templateName_notExist()930 public void testCreateConfigurationFromArgs_templateName_notExist() throws Exception { 931 final String configName = "include-template-config-with-default"; 932 final String targetName = "test-config"; 933 final String missingNameTemplate = "NOTEXISTINGNAME"; 934 Map<String, String> expected = new HashMap<String,String>(); 935 expected.put(missingNameTemplate, targetName); 936 final String expError = String.format( 937 "Unused template:map parameters: %s", expected); 938 939 try { 940 mFactory.createConfigurationFromArgs(new String[]{configName, 941 "--template:map", missingNameTemplate, targetName}); 942 fail ("ConfigurationException not thrown"); 943 } catch (ConfigurationException e) { 944 // Make sure that we get the expected error message 945 assertEquals(expError, e.getMessage()); 946 } 947 } 948 949 /** 950 * If a configuration is called a second time, ensure that the cached config is also properly 951 * returned, and that template:map did not cause issues. 952 */ 953 @Test testCreateConfigurationFromArgs_templateName_notExistTest()954 public void testCreateConfigurationFromArgs_templateName_notExistTest() throws Exception { 955 final String configName = "template-include-config-with-default"; 956 final String targetName = "local-config"; 957 final String nameTemplate = "target"; 958 IConfiguration tmp = null; 959 try { 960 tmp = mFactory.createConfigurationFromArgs(new String[]{configName, 961 "--template:map", nameTemplate, targetName}); 962 } catch (ConfigurationException e) { 963 fail("ConfigurationException thrown: " + e.getMessage()); 964 } 965 assertTrue(tmp.getTests().size() == 2); 966 967 // Call the same config a second time to make sure the cached version works. 968 try { 969 tmp = mFactory.createConfigurationFromArgs(new String[]{configName, 970 "--template:map", nameTemplate, targetName}); 971 } catch (ConfigurationException e) { 972 fail("ConfigurationException thrown: " + e.getMessage()); 973 } 974 assertTrue(tmp.getTests().size() == 2); 975 } 976 977 /** 978 * If a configuration is called a second time with bad template name, it should still throw the 979 * unused config template:map 980 */ 981 @Test testCreateConfigurationFromArgs_templateName_stillThrow()982 public void testCreateConfigurationFromArgs_templateName_stillThrow() throws Exception { 983 final String configName = "template-include-config-with-default"; 984 final String targetName = "local-config"; 985 final String nameTemplate = "target_not_exist"; 986 try { 987 mFactory.createConfigurationFromArgs(new String[]{configName, 988 "--template:map", nameTemplate, targetName}); 989 fail("ConfigurationException should have been thrown"); 990 } catch (ConfigurationException e) { 991 // expected 992 } 993 994 // Call the same config a second time to make sure it is also rejected. 995 try { 996 mFactory.createConfigurationFromArgs(new String[]{configName, 997 "--template:map", nameTemplate, targetName}); 998 fail("ConfigurationException should have been thrown"); 999 } catch (ConfigurationException e) { 1000 // expected 1001 } 1002 } 1003 1004 /** Parse a config with 3 different device configuration specified. */ 1005 @Test testCreateConfigurationFromArgs_multidevice()1006 public void testCreateConfigurationFromArgs_multidevice() throws Exception { 1007 IConfiguration config = mFactory.createConfigurationFromArgs( 1008 new String[]{"multi-device"}); 1009 assertEquals(1, config.getTests().size()); 1010 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 1011 // Verify that all attributes are in the right place: 1012 assertNotNull(config.getDeviceConfigByName("device1")); 1013 assertEquals("10", config.getDeviceConfigByName("device1") 1014 .getBuildProvider().getBuild().getBuildId()); 1015 assertEquals("stub", config.getDeviceConfigByName("device1") 1016 .getBuildProvider().getBuild().getTestTag()); 1017 assertEquals(0, config.getDeviceConfigByName("device1") 1018 .getTargetPreparers().size()); 1019 1020 assertNotNull(config.getDeviceConfigByName("device2")); 1021 assertEquals("0", config.getDeviceConfigByName("device2") 1022 .getBuildProvider().getBuild().getBuildId()); 1023 assertEquals("stub", config.getDeviceConfigByName("device2") 1024 .getBuildProvider().getBuild().getTestTag()); 1025 assertEquals(1, config.getDeviceConfigByName("device2") 1026 .getTargetPreparers().size()); 1027 assertTrue(config.getDeviceConfigByName("device2") 1028 .getTargetPreparers().get(0) instanceof StubTargetPreparer); 1029 1030 assertNotNull(config.getDeviceConfigByName("device3")); 1031 assertEquals("0", config.getDeviceConfigByName("device3") 1032 .getBuildProvider().getBuild().getBuildId()); 1033 assertEquals("build-flavor3", config.getDeviceConfigByName("device3") 1034 .getBuildProvider().getBuild().getBuildFlavor()); 1035 assertEquals(2, config.getDeviceConfigByName("device3") 1036 .getTargetPreparers().size()); 1037 assertTrue(config.getDeviceConfigByName("device3") 1038 .getTargetPreparers().get(0) instanceof StubTargetPreparer); 1039 } 1040 1041 /** 1042 * Test that if an object inside a <device> tag is implementing {@link IConfigurationReceiver} 1043 * it will receives the config properly like non-device object. 1044 */ 1045 @Test testCreateConfigurationFromArgs_injectConfiguration()1046 public void testCreateConfigurationFromArgs_injectConfiguration() throws Exception { 1047 IConfiguration config = mFactory.createConfigurationFromArgs(new String[] {"multi-device"}); 1048 assertEquals(1, config.getTests().size()); 1049 1050 assertNotNull(config.getDeviceConfigByName("device2")); 1051 assertEquals(1, config.getDeviceConfigByName("device2").getTargetPreparers().size()); 1052 assertTrue( 1053 config.getDeviceConfigByName("device2").getTargetPreparers().get(0) 1054 instanceof StubTargetPreparer); 1055 StubTargetPreparer stubDevice2 = 1056 (StubTargetPreparer) 1057 config.getDeviceConfigByName("device2").getTargetPreparers().get(0); 1058 assertEquals(config, stubDevice2.getConfiguration()); 1059 1060 assertNotNull(config.getDeviceConfigByName("device3")); 1061 assertEquals(2, config.getDeviceConfigByName("device3").getTargetPreparers().size()); 1062 assertTrue( 1063 config.getDeviceConfigByName("device3").getTargetPreparers().get(0) 1064 instanceof StubTargetPreparer); 1065 StubTargetPreparer stubDevice3 = 1066 (StubTargetPreparer) 1067 config.getDeviceConfigByName("device3").getTargetPreparers().get(0); 1068 assertEquals(config, stubDevice3.getConfiguration()); 1069 } 1070 1071 /** 1072 * Parse a config with 3 different device configuration specified. And apply a command line to 1073 * override some attributes. 1074 */ 1075 @Test testCreateConfigurationFromArgs_multidevice_applyCommandLine()1076 public void testCreateConfigurationFromArgs_multidevice_applyCommandLine() throws Exception { 1077 IConfiguration config = mFactory.createConfigurationFromArgs( 1078 new String[]{"multi-device", "--{device2}build-id","20", "--{device1}null-device", 1079 "--{device3}com.android.tradefed.build.StubBuildProvider:build-id","30"}); 1080 assertEquals(1, config.getTests().size()); 1081 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 1082 // Verify that all attributes are in the right place: 1083 assertNotNull(config.getDeviceConfigByName("device1")); 1084 assertEquals("10", config.getDeviceConfigByName("device1") 1085 .getBuildProvider().getBuild().getBuildId()); 1086 assertEquals("stub", config.getDeviceConfigByName("device1") 1087 .getBuildProvider().getBuild().getTestTag()); 1088 assertEquals(0, config.getDeviceConfigByName("device1") 1089 .getTargetPreparers().size()); 1090 assertTrue(config.getDeviceConfigByName("device1") 1091 .getDeviceRequirements().nullDeviceRequested()); 1092 1093 assertNotNull(config.getDeviceConfigByName("device2")); 1094 // Device2 build provider is modified independently 1095 assertEquals("20", config.getDeviceConfigByName("device2") 1096 .getBuildProvider().getBuild().getBuildId()); 1097 assertEquals("stub", config.getDeviceConfigByName("device2") 1098 .getBuildProvider().getBuild().getTestTag()); 1099 assertEquals(1, config.getDeviceConfigByName("device2") 1100 .getTargetPreparers().size()); 1101 assertTrue(config.getDeviceConfigByName("device2") 1102 .getTargetPreparers().get(0) instanceof StubTargetPreparer); 1103 assertFalse(config.getDeviceConfigByName("device2") 1104 .getDeviceRequirements().nullDeviceRequested()); 1105 1106 // Device3 build provider is modified independently 1107 assertNotNull(config.getDeviceConfigByName("device3")); 1108 assertEquals("30", config.getDeviceConfigByName("device3") 1109 .getBuildProvider().getBuild().getBuildId()); 1110 assertEquals("build-flavor3", config.getDeviceConfigByName("device3") 1111 .getBuildProvider().getBuild().getBuildFlavor()); 1112 assertEquals(2, config.getDeviceConfigByName("device3") 1113 .getTargetPreparers().size()); 1114 assertTrue(config.getDeviceConfigByName("device3") 1115 .getTargetPreparers().get(0) instanceof StubTargetPreparer); 1116 assertFalse(config.getDeviceConfigByName("device3") 1117 .getDeviceRequirements().nullDeviceRequested()); 1118 } 1119 1120 /** 1121 * Parse a config with 3 different device configuration specified. And apply a command line to 1122 * override some attributes. 1123 */ 1124 @Test testCreateConfigurationFromArgs_multidevice_singletag()1125 public void testCreateConfigurationFromArgs_multidevice_singletag() throws Exception { 1126 IConfiguration config = 1127 mFactory.createConfigurationFromArgs( 1128 new String[] { 1129 "multi-device-empty", 1130 "--{device2}build-id", 1131 "20", 1132 "--{device1}null-device", 1133 "--{device3}com.android.tradefed.build.StubBuildProvider:build-id", 1134 "30" 1135 }); 1136 assertEquals(1, config.getTests().size()); 1137 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 1138 // Verify that all attributes are in the right place: 1139 assertNotNull(config.getDeviceConfigByName("device1")); 1140 assertEquals( 1141 "0", 1142 config.getDeviceConfigByName("device1").getBuildProvider().getBuild().getBuildId()); 1143 assertEquals( 1144 "stub", 1145 config.getDeviceConfigByName("device1").getBuildProvider().getBuild().getTestTag()); 1146 assertEquals(0, config.getDeviceConfigByName("device1").getTargetPreparers().size()); 1147 assertTrue( 1148 config.getDeviceConfigByName("device1") 1149 .getDeviceRequirements() 1150 .nullDeviceRequested()); 1151 1152 assertNotNull(config.getDeviceConfigByName("device2")); 1153 // Device2 build provider is modified independently 1154 assertEquals( 1155 "20", 1156 config.getDeviceConfigByName("device2").getBuildProvider().getBuild().getBuildId()); 1157 assertEquals( 1158 "stub", 1159 config.getDeviceConfigByName("device2").getBuildProvider().getBuild().getTestTag()); 1160 assertEquals(0, config.getDeviceConfigByName("device2").getTargetPreparers().size()); 1161 assertFalse( 1162 config.getDeviceConfigByName("device2") 1163 .getDeviceRequirements() 1164 .nullDeviceRequested()); 1165 1166 // Device3 build provider is modified independently 1167 assertNotNull(config.getDeviceConfigByName("device3")); 1168 assertEquals( 1169 "30", 1170 config.getDeviceConfigByName("device3").getBuildProvider().getBuild().getBuildId()); 1171 assertEquals(0, config.getDeviceConfigByName("device3").getTargetPreparers().size()); 1172 assertFalse( 1173 config.getDeviceConfigByName("device3") 1174 .getDeviceRequirements() 1175 .nullDeviceRequested()); 1176 } 1177 1178 /** 1179 * Parse a config with 3 different device configuration specified. And apply a command line to 1180 * override all attributes. 1181 */ 1182 @Test testCreateConfigurationFromArgs_multidevice_applyCommandLineGlobal()1183 public void testCreateConfigurationFromArgs_multidevice_applyCommandLineGlobal() 1184 throws Exception { 1185 IConfiguration config = mFactory.createConfigurationFromArgs( 1186 new String[]{"multi-device", "--build-id","20"}); 1187 assertEquals(1, config.getTests().size()); 1188 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 1189 // Verify that all attributes are in the right place: 1190 // All build id are now modified since option had a global scope 1191 assertNotNull(config.getDeviceConfigByName("device1")); 1192 assertEquals("20", config.getDeviceConfigByName("device1") 1193 .getBuildProvider().getBuild().getBuildId()); 1194 assertEquals("stub", config.getDeviceConfigByName("device1") 1195 .getBuildProvider().getBuild().getTestTag()); 1196 assertEquals(0, config.getDeviceConfigByName("device1") 1197 .getTargetPreparers().size()); 1198 1199 assertNotNull(config.getDeviceConfigByName("device2")); 1200 assertEquals("20", config.getDeviceConfigByName("device2") 1201 .getBuildProvider().getBuild().getBuildId()); 1202 assertEquals("stub", config.getDeviceConfigByName("device2") 1203 .getBuildProvider().getBuild().getTestTag()); 1204 assertEquals(1, config.getDeviceConfigByName("device2") 1205 .getTargetPreparers().size()); 1206 assertTrue(config.getDeviceConfigByName("device2") 1207 .getTargetPreparers().get(0) instanceof StubTargetPreparer); 1208 1209 assertNotNull(config.getDeviceConfigByName("device3")); 1210 assertEquals("20", config.getDeviceConfigByName("device3") 1211 .getBuildProvider().getBuild().getBuildId()); 1212 assertEquals("build-flavor3", config.getDeviceConfigByName("device3") 1213 .getBuildProvider().getBuild().getBuildFlavor()); 1214 assertEquals(2, config.getDeviceConfigByName("device3") 1215 .getTargetPreparers().size()); 1216 assertTrue(config.getDeviceConfigByName("device3") 1217 .getTargetPreparers().get(0) instanceof StubTargetPreparer); 1218 } 1219 1220 /** 1221 * Test that when <device> tags are out of order (device 1 - device 2 - device 1) and an option 1222 * is specified in the last device 1 with an increased frequency (a same class object from the 1223 * first device 1 or 2), the option is properly found and assigned. 1224 */ 1225 @Test testCreateConfigurationFromArgs_frequency()1226 public void testCreateConfigurationFromArgs_frequency() throws Exception { 1227 IConfiguration config = 1228 mFactory.createConfigurationFromArgs(new String[] {"multi-device-mix"}); 1229 assertNotNull(config.getDeviceConfigByName("device1")); 1230 assertEquals(3, config.getDeviceConfigByName("device1").getTargetPreparers().size()); 1231 assertTrue( 1232 config.getDeviceConfigByName("device1").getTargetPreparers().get(0) 1233 instanceof DeviceWiper); 1234 DeviceWiper prep1 = 1235 (DeviceWiper) config.getDeviceConfigByName("device1").getTargetPreparers().get(0); 1236 assertTrue(prep1.isDisabled()); 1237 assertTrue( 1238 config.getDeviceConfigByName("device1").getTargetPreparers().get(2) 1239 instanceof DeviceWiper); 1240 DeviceWiper prep3 = 1241 (DeviceWiper) config.getDeviceConfigByName("device1").getTargetPreparers().get(2); 1242 assertFalse(prep3.isDisabled()); 1243 1244 assertNotNull(config.getDeviceConfigByName("device2")); 1245 assertEquals(1, config.getDeviceConfigByName("device2").getTargetPreparers().size()); 1246 assertTrue( 1247 config.getDeviceConfigByName("device2").getTargetPreparers().get(0) 1248 instanceof DeviceWiper); 1249 DeviceWiper prep2 = 1250 (DeviceWiper) config.getDeviceConfigByName("device2").getTargetPreparers().get(0); 1251 // Only device 1 preparer has been targeted. 1252 assertTrue(prep2.isDisabled()); 1253 } 1254 1255 /** 1256 * Tests a different usage of options for a multi device interleaved config where options are 1257 * specified. 1258 */ 1259 @Test testCreateConfigurationFromArgs_frequency_withOptionOpen()1260 public void testCreateConfigurationFromArgs_frequency_withOptionOpen() throws Exception { 1261 IConfiguration config = 1262 mFactory.createConfigurationFromArgs(new String[] {"multi-device-mix-options"}); 1263 assertNotNull(config.getDeviceConfigByName("device1")); 1264 assertEquals(3, config.getDeviceConfigByName("device1").getTargetPreparers().size()); 1265 assertTrue( 1266 config.getDeviceConfigByName("device1").getTargetPreparers().get(0) 1267 instanceof DeviceWiper); 1268 DeviceWiper prep1 = 1269 (DeviceWiper) config.getDeviceConfigByName("device1").getTargetPreparers().get(0); 1270 assertTrue(prep1.isDisabled()); 1271 assertTrue( 1272 config.getDeviceConfigByName("device1").getTargetPreparers().get(2) 1273 instanceof DeviceWiper); 1274 DeviceWiper prep3 = 1275 (DeviceWiper) config.getDeviceConfigByName("device1").getTargetPreparers().get(2); 1276 assertFalse(prep3.isDisabled()); 1277 1278 assertNotNull(config.getDeviceConfigByName("device2")); 1279 assertEquals(1, config.getDeviceConfigByName("device2").getTargetPreparers().size()); 1280 assertTrue( 1281 config.getDeviceConfigByName("device2").getTargetPreparers().get(0) 1282 instanceof DeviceWiper); 1283 DeviceWiper prep2 = 1284 (DeviceWiper) config.getDeviceConfigByName("device2").getTargetPreparers().get(0); 1285 // Only device 1 preparer has been targeted. 1286 assertTrue(prep2.isDisabled()); 1287 } 1288 1289 /** 1290 * Configuration for multi device is wrong since it contains a build_provider tag outside the 1291 * devices tags. 1292 */ 1293 @Test testCreateConfigurationFromArgs_multidevice_exception()1294 public void testCreateConfigurationFromArgs_multidevice_exception() throws Exception { 1295 String expectedException = 1296 "You seem to want a multi-devices configuration but you have " 1297 + "[build_provider] tags outside the <device> tags"; 1298 try { 1299 mFactory.createConfigurationFromArgs(new String[]{"multi-device-outside-tag"}); 1300 fail("Should have thrown a Configuration Exception"); 1301 } catch(ConfigurationException e) { 1302 assertEquals(expectedException, e.getMessage()); 1303 } 1304 } 1305 1306 /** 1307 * Parse a config with no multi device config, and expect the new device holder to still be 1308 * there and adding a default device. 1309 */ 1310 @Test testCreateConfigurationFromArgs_old_config_with_deviceHolder()1311 public void testCreateConfigurationFromArgs_old_config_with_deviceHolder() throws Exception { 1312 IConfiguration config = mFactory.createConfigurationFromArgs( 1313 new String[]{"test-config", "--build-id","20", "--serial", "test"}); 1314 assertEquals(1, config.getTests().size()); 1315 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 1316 // Verify that all attributes are in the right place: 1317 // All build id are now modified since option had a global scope 1318 assertNotNull(config.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME)); 1319 assertEquals("20", config.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME) 1320 .getBuildProvider().getBuild().getBuildId()); 1321 assertEquals("stub", config.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME) 1322 .getBuildProvider().getBuild().getTestTag()); 1323 assertEquals(1, config.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME) 1324 .getTargetPreparers().size()); 1325 List<String> serials = new ArrayList<String>(); 1326 serials.add("test"); 1327 assertEquals(serials, config.getDeviceRequirements().getSerials(null)); 1328 assertEquals( 1329 serials, 1330 config.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME) 1331 .getDeviceRequirements() 1332 .getSerials(null)); 1333 } 1334 1335 /** 1336 * Test that when parsing command line options, boolean options with Device tag and namespace 1337 * are correctly assigned. 1338 */ 1339 @Test testCreateConfiguration_injectDeviceBooleanOption()1340 public void testCreateConfiguration_injectDeviceBooleanOption() throws Exception { 1341 IConfiguration config = 1342 mFactory.createConfigurationFromArgs( 1343 new String[] { 1344 "test-config-multi", 1345 "--{device1}no-test-boolean-option", 1346 "--{device1}test-boolean-option-false", 1347 // testing with namespace too 1348 "--{device2}stub-preparer:no-test-boolean-option", 1349 "--{device2}stub-preparer:test-boolean-option-false" 1350 }); 1351 assertEquals(2, config.getDeviceConfig().size()); 1352 IDeviceConfiguration device1 = config.getDeviceConfigByName("device1"); 1353 StubTargetPreparer deviceSetup1 = (StubTargetPreparer) device1.getTargetPreparers().get(0); 1354 // default value of test-boolean-option is true, we set it to false 1355 assertFalse(deviceSetup1.getTestBooleanOption()); 1356 // default value of test-boolean-option-false is false, we set it to true. 1357 assertTrue(deviceSetup1.getTestBooleanOptionFalse()); 1358 1359 IDeviceConfiguration device2 = config.getDeviceConfigByName("device2"); 1360 StubTargetPreparer deviceSetup2 = (StubTargetPreparer) device2.getTargetPreparers().get(0); 1361 assertFalse(deviceSetup2.getTestBooleanOption()); 1362 assertTrue(deviceSetup2.getTestBooleanOptionFalse()); 1363 } 1364 1365 /** Test that when an <include> tag is used inside a <device> tag we correctly resolve it. */ 1366 @Test testCreateConfiguration_includeInDevice()1367 public void testCreateConfiguration_includeInDevice() throws Exception { 1368 IConfiguration config = 1369 mFactory.createConfigurationFromArgs( 1370 new String[] {"test-config-multi-include", "--test-dir", "faketestdir"}); 1371 assertEquals(2, config.getDeviceConfig().size()); 1372 IDeviceConfiguration device1 = config.getDeviceConfigByName("device1"); 1373 assertTrue(device1.getTargetPreparers().get(0) instanceof StubTargetPreparer); 1374 // The included config in device2 loads a different build_provider 1375 IDeviceConfiguration device2 = config.getDeviceConfigByName("device2"); 1376 assertTrue(device2.getBuildProvider() instanceof LocalDeviceBuildProvider); 1377 LocalDeviceBuildProvider provider = (LocalDeviceBuildProvider) device2.getBuildProvider(); 1378 // command line options are properly propagated to the included object in device tag. 1379 assertEquals("faketestdir", provider.getTestDir().getName()); 1380 } 1381 1382 @Test testPartialCreateMultiDevices()1383 public void testPartialCreateMultiDevices() throws Exception { 1384 IConfiguration config = 1385 mFactory.createPartialConfigurationFromArgs( 1386 new String[] {"test-config-multi-include", "--test-dir", "faketestdir"}, 1387 null, 1388 ImmutableSet.of(Configuration.BUILD_PROVIDER_TYPE_NAME), 1389 null); 1390 assertEquals(2, config.getDeviceConfig().size()); 1391 IDeviceConfiguration device2 = config.getDeviceConfigByName("device2"); 1392 assertTrue(device2.getBuildProvider() instanceof LocalDeviceBuildProvider); 1393 } 1394 1395 /** 1396 * Test when an <include> tag tries to load a <device> tag inside another <device> tag. This 1397 * should throw an exception. 1398 */ 1399 @Test testCreateConfiguration_includeInDevice_inDevice()1400 public void testCreateConfiguration_includeInDevice_inDevice() throws Exception { 1401 try { 1402 mFactory.createConfigurationFromArgs( 1403 new String[] { 1404 "multi-device-incorrect-include", 1405 }); 1406 fail("Should have thrown an exception."); 1407 } catch (ConfigurationException expected) { 1408 assertEquals( 1409 "<device> tag cannot be included inside another device", expected.getMessage()); 1410 } 1411 } 1412 1413 /** Test that {@link ConfigurationFactory#reorderArgs(String[])} is properly reordering args. */ 1414 @Test testReorderArgs_check_ordering()1415 public void testReorderArgs_check_ordering() throws Throwable { 1416 String[] args = 1417 new String[] { 1418 "config", 1419 "--option1", 1420 "o1", 1421 "--template:map", 1422 "tm=tm1", 1423 "--option2", 1424 "--option3", 1425 "o3", 1426 "--template:map", 1427 "tm", 1428 "tm2" 1429 }; 1430 String[] wantArgs = 1431 new String[] { 1432 "config", 1433 "--template:map", 1434 "tm=tm1", 1435 "--template:map", 1436 "tm", 1437 "tm2", 1438 "--option1", 1439 "o1", 1440 "--option2", 1441 "--option3", 1442 "o3" 1443 }; 1444 1445 assertEquals(Arrays.toString(wantArgs), Arrays.toString(mFactory.reorderArgs(args))); 1446 } 1447 1448 /** 1449 * Test that {@link ConfigurationFactory#reorderArgs(String[])} properly handles a short arg 1450 * after a template arg. 1451 */ 1452 @Test testReorderArgs_template_with_short_arg()1453 public void testReorderArgs_template_with_short_arg() throws Throwable { 1454 String[] args = 1455 new String[] { 1456 "config", 1457 "--option1", 1458 "o1", 1459 "--template:map", 1460 "tm=tm1", 1461 "-option2", 1462 "--option3", 1463 "o3", 1464 "--template:map", 1465 "tm", 1466 "tm2" 1467 }; 1468 String[] wantArgs = 1469 new String[] { 1470 "config", 1471 "--template:map", 1472 "tm=tm1", 1473 "--template:map", 1474 "tm", 1475 "tm2", 1476 "--option1", 1477 "o1", 1478 "-option2", 1479 "--option3", 1480 "o3" 1481 }; 1482 1483 assertEquals(Arrays.toString(wantArgs), Arrays.toString(mFactory.reorderArgs(args))); 1484 } 1485 1486 /** 1487 * Test that {@link ConfigurationFactory#reorderArgs(String[])} properly handles a incomplete 1488 * template arg. 1489 */ 1490 @Test testReorderArgs_incomplete_template_arg()1491 public void testReorderArgs_incomplete_template_arg() throws Throwable { 1492 String[] args = 1493 new String[] { 1494 "config", 1495 "--option1", 1496 "o1", 1497 "--template:map", 1498 "tm=tm1", 1499 "-option2", 1500 "--option3", 1501 "o3", 1502 "--template:map", 1503 }; 1504 String[] wantArgs = 1505 new String[] { 1506 "config", 1507 "--template:map", 1508 "tm=tm1", 1509 "--template:map", 1510 "--option1", 1511 "o1", 1512 "-option2", 1513 "--option3", 1514 "o3" 1515 }; 1516 1517 assertEquals(Arrays.toString(wantArgs), Arrays.toString(mFactory.reorderArgs(args))); 1518 } 1519 1520 /** 1521 * Test that when doing a dry-run with keystore arguments, we skip the keystore validation. We 1522 * accept the argument, as long as the key exists. 1523 */ 1524 @Test testCreateConfigurationFromArgs_dryRun_keystore()1525 public void testCreateConfigurationFromArgs_dryRun_keystore() throws Exception { 1526 IConfiguration res = 1527 mFactory.createConfigurationFromArgs( 1528 new String[] { 1529 "test-config", 1530 "--build-id", 1531 "USE_KEYSTORE@test_string", 1532 "--dry-run", 1533 "--online-wait-time=USE_KEYSTORE@test_long", 1534 "--min-battery-after-recovery", 1535 "USE_KEYSTORE@test_int", 1536 "--disable-unresponsive-reboot=USE_KEYSTORE@test_boolean", 1537 }); 1538 res.validateOptions(); 1539 // we still throw exception if the option itself doesn't exists. 1540 try { 1541 mFactory.createConfigurationFromArgs( 1542 new String[] { 1543 "test-config", "--does-not-exists", "USE_KEYSTORE@test_string", "--dry-run" 1544 }); 1545 fail("Should have thrown an exception."); 1546 } catch (ConfigurationException expected) { 1547 // expected 1548 } 1549 } 1550 1551 /** 1552 * Test that when mandatory option are set with a keystore during a dry-run, they can still be 1553 * validated. 1554 */ 1555 @Test testCreateConfigurationFromArgs_dryRun_keystore_required_arg()1556 public void testCreateConfigurationFromArgs_dryRun_keystore_required_arg() throws Exception { 1557 IConfiguration res = 1558 mFactory.createConfigurationFromArgs( 1559 new String[] { 1560 "mandatory-config", 1561 "--build-dir", 1562 "USE_KEYSTORE@test_string", 1563 "--dry-run", 1564 }); 1565 // Check that mandatory option was properly set, otherwise it will throw. 1566 res.validateOptions(); 1567 } 1568 1569 /** 1570 * This unit test ensures that the code will search for missing test configs in directories 1571 * specified in certain environment variables. 1572 */ 1573 @Test testSearchConfigFromEnvVar()1574 public void testSearchConfigFromEnvVar() throws IOException { 1575 File externalConfig = FileUtil.createTempFile("external-config", ".config"); 1576 String configName = FileUtil.getBaseName(externalConfig.getName()); 1577 File tmpDir = externalConfig.getParentFile(); 1578 1579 ConfigurationFactory spyFactory = Mockito.spy(mFactory); 1580 Mockito.doReturn(Arrays.asList(tmpDir)).when(spyFactory).getExternalTestCasesDirs(); 1581 1582 try { 1583 File config = spyFactory.getTestCaseConfigPath(configName); 1584 assertEquals(config.getAbsolutePath(), externalConfig.getAbsolutePath()); 1585 } finally { 1586 FileUtil.deleteFile(externalConfig); 1587 } 1588 } 1589 1590 /** 1591 * This unit test ensures that the code will search for missing test configs in directories 1592 * specified in certain environment variables, and fail as the test config still can't be found. 1593 */ 1594 @Test testSearchConfigFromEnvVarFailed()1595 public void testSearchConfigFromEnvVarFailed() throws Exception { 1596 File tmpDir = FileUtil.createTempDir("config-check-var"); 1597 try { 1598 ConfigurationFactory spyFactory = Mockito.spy(mFactory); 1599 Mockito.doReturn(Arrays.asList(tmpDir)).when(spyFactory).getExternalTestCasesDirs(); 1600 File config = spyFactory.getTestCaseConfigPath("non-exist"); 1601 assertNull(config); 1602 Mockito.verify(spyFactory, Mockito.times(1)).getExternalTestCasesDirs(); 1603 } finally { 1604 FileUtil.recursiveDelete(tmpDir); 1605 } 1606 } 1607 1608 /** 1609 * Tests that {@link ConfigurationFactory#getConfigNamesFromTestCases(String)} returns the 1610 * proper files of the subpath only. 1611 */ 1612 @Test testGetConfigNamesFromTestCases_subpath()1613 public void testGetConfigNamesFromTestCases_subpath() throws Exception { 1614 File tmpDir = FileUtil.createTempDir("test-config-dir"); 1615 try { 1616 File config1 = FileUtil.createTempFile("testconfig1", ".config", tmpDir); 1617 FileUtil.writeToFile("<configuration></configuration>", config1); 1618 File subDir = FileUtil.createTempDir("subdir", tmpDir); 1619 File config2 = FileUtil.createTempFile("testconfig2", ".xml", subDir); 1620 FileUtil.writeToFile("<configuration></configuration>", config2); 1621 ConfigurationFactory spyFactory = Mockito.spy(mFactory); 1622 Mockito.doReturn(Arrays.asList(tmpDir)).when(spyFactory).getExternalTestCasesDirs(); 1623 // looking at full path we get both configs 1624 Set<String> res = spyFactory.getConfigNamesFromTestCases(null); 1625 assertEquals(2, res.size()); 1626 res = spyFactory.getConfigNamesFromTestCases(subDir.getName()); 1627 assertEquals(1, res.size()); 1628 assertTrue(res.iterator().next().contains("testconfig2")); 1629 } finally { 1630 FileUtil.recursiveDelete(tmpDir); 1631 } 1632 } 1633 1634 /** 1635 * Test that if the base configuration is single device and a template attempt to make it 1636 * multi-devices, it will fail and provide a clear message about which tags are not in the right 1637 * place. 1638 */ 1639 @Test testCreateConfigurationFromArgs_singleDeviceTemplate_includeMulti()1640 public void testCreateConfigurationFromArgs_singleDeviceTemplate_includeMulti() 1641 throws Exception { 1642 try { 1643 mFactory.createConfigurationFromArgs( 1644 new String[] { 1645 "local-preparer-base", "--template:map", "config", "test-config-multi" 1646 }); 1647 fail("Should have thrown an exception"); 1648 } catch (ConfigurationException expected) { 1649 assertEquals( 1650 "You seem to want a multi-devices configuration but you have " 1651 + "[target_preparer] tags outside the <device> tags", 1652 expected.getMessage()); 1653 } 1654 } 1655 1656 /** 1657 * Opposite scenario of {@link 1658 * #testCreateConfigurationFromArgs_singleDeviceTemplate_includeMulti()} where the base template 1659 * is multi-devices and the template included has a single device tag. 1660 */ 1661 @Test testCreateConfigurationFromArgs_multiDeviceTemplate_includeSingle()1662 public void testCreateConfigurationFromArgs_multiDeviceTemplate_includeSingle() 1663 throws Exception { 1664 try { 1665 mFactory.createConfigurationFromArgs( 1666 new String[] { 1667 "multi-device", "--template:map", "preparers", "local-preparer-base" 1668 }); 1669 fail("Should have thrown an exception"); 1670 } catch (ConfigurationException expected) { 1671 assertEquals( 1672 "You seem to want a multi-devices configuration but you have " 1673 + "[target_preparer] tags outside the <device> tags", 1674 expected.getMessage()); 1675 } 1676 } 1677 1678 /** 1679 * Test that if we load a configuration with some unfound class, we throw an exception and 1680 * report the objects that did not load. 1681 */ 1682 @Test testCreateConfigurationFromArgs_failedToLoadClass()1683 public void testCreateConfigurationFromArgs_failedToLoadClass() throws Exception { 1684 try { 1685 mFactory.createConfigurationFromArgs(new String[] {"multi-device-incorrect"}); 1686 fail("Should have thrown an exception"); 1687 } catch (ClassNotFoundConfigurationException expected) { 1688 assertTrue( 1689 expected.getMessage() 1690 .contains( 1691 "Failed to load some objects in the configuration " 1692 + "'multi-device-incorrect':")); 1693 assertEquals( 1694 "device1:build_provider", 1695 expected.getRejectedObjects().get("com.android.tradefed.build.doesnotexists")); 1696 assertEquals( 1697 "device3:target_preparer", 1698 expected.getRejectedObjects() 1699 .get("com.android.tradefed.targetprep.doesnotexistseither")); 1700 } 1701 } 1702 1703 /** 1704 * Test that a configuration with one real device and one fake device (isFake=true) will be 1705 * loaded like a single device config: The objects outside the <device> tags will be part of the 1706 * real device. 1707 */ 1708 @Test testCreateConfiguration_multiDevice_fake()1709 public void testCreateConfiguration_multiDevice_fake() throws Exception { 1710 IConfiguration config = 1711 mFactory.createConfigurationFromArgs( 1712 new String[] { 1713 "test-config-multi-fake", 1714 "--{device1}no-test-boolean-option", 1715 "--{device1}test-boolean-option-false", 1716 // testing with namespace too 1717 "--{device1}stub-preparer:no-test-boolean-option", 1718 "--{device1}stub-preparer:test-boolean-option-false" 1719 }); 1720 assertEquals(2, config.getDeviceConfig().size()); 1721 IDeviceConfiguration device1 = config.getDeviceConfigByName("device1"); 1722 // One target preparer from inside the device tag, one from outside. 1723 assertEquals(2, device1.getTargetPreparers().size()); 1724 StubTargetPreparer deviceSetup1 = (StubTargetPreparer) device1.getTargetPreparers().get(0); 1725 // default value of test-boolean-option is true, we set it to false 1726 assertFalse(deviceSetup1.getTestBooleanOption()); 1727 // default value of test-boolean-option-false is false, we set it to true. 1728 assertTrue(deviceSetup1.getTestBooleanOptionFalse()); 1729 1730 // Check that the second preparer, outside device1 can still receive option as {device1}. 1731 StubTargetPreparer deviceSetup2 = (StubTargetPreparer) device1.getTargetPreparers().get(1); 1732 // default value of test-boolean-option is true, we set it to false 1733 assertFalse(deviceSetup2.getTestBooleanOption()); 1734 // default value of test-boolean-option-false is false, we set it to true. 1735 assertTrue(deviceSetup2.getTestBooleanOptionFalse()); 1736 1737 assertFalse(config.isDeviceConfiguredFake("device1")); 1738 assertTrue(config.isDeviceConfiguredFake("device2")); 1739 } 1740 1741 /** 1742 * Test when a single device configuration (standard flat) add a fake device. Configuration 1743 * objects should be added to the default device. 1744 */ 1745 @Test testCreateConfiguration_singleDeviceConfig_withFake()1746 public void testCreateConfiguration_singleDeviceConfig_withFake() throws Exception { 1747 IConfiguration config = 1748 mFactory.createConfigurationFromArgs( 1749 new String[] { 1750 "single-config-and-fake", 1751 "--no-test-boolean-option", 1752 "--test-boolean-option-false", 1753 // testing with namespace too 1754 "--stub-preparer:no-test-boolean-option", 1755 "--stub-preparer:test-boolean-option-false" 1756 }); 1757 assertEquals(2, config.getDeviceConfig().size()); 1758 // Ensure the first device is the default one. 1759 assertEquals( 1760 ConfigurationDef.DEFAULT_DEVICE_NAME, 1761 config.getDeviceConfig().get(0).getDeviceName()); 1762 1763 IDeviceConfiguration device1 = 1764 config.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME); 1765 // One target preparer from inside the device tag, one from outside. 1766 assertEquals(2, device1.getTargetPreparers().size()); 1767 StubTargetPreparer deviceSetup1 = (StubTargetPreparer) device1.getTargetPreparers().get(0); 1768 // default value of test-boolean-option is true, we set it to false 1769 assertFalse(deviceSetup1.getTestBooleanOption()); 1770 // default value of test-boolean-option-false is false, we set it to true. 1771 assertTrue(deviceSetup1.getTestBooleanOptionFalse()); 1772 assertTrue(device1.getDeviceRequirements().gceDeviceRequested()); 1773 assertFalse(device1.getDeviceRequirements().nullDeviceRequested()); 1774 1775 // Check that the second preparer, outside device1 can still receive option as {device1}. 1776 StubTargetPreparer deviceSetup2 = (StubTargetPreparer) device1.getTargetPreparers().get(1); 1777 // default value of test-boolean-option is true, we set it to false 1778 assertFalse(deviceSetup2.getTestBooleanOption()); 1779 // default value of test-boolean-option-false is false, we set it to true. 1780 assertTrue(deviceSetup2.getTestBooleanOptionFalse()); 1781 1782 assertFalse(config.isDeviceConfiguredFake(ConfigurationDef.DEFAULT_DEVICE_NAME)); 1783 assertTrue(config.isDeviceConfiguredFake("device2")); 1784 IDeviceConfiguration device2 = config.getDeviceConfigByName("device2"); 1785 assertFalse(device2.getDeviceRequirements().gceDeviceRequested()); 1786 assertTrue(device2.getDeviceRequirements().nullDeviceRequested()); 1787 } 1788 1789 /** Test that a configuration with all the device marked as isReal=false will be rejected. */ 1790 @Test testCreateConfiguration_multiDevice_real_notReal()1791 public void testCreateConfiguration_multiDevice_real_notReal() throws Exception { 1792 try { 1793 mFactory.createConfigurationFromArgs(new String[] {"test-config-real-not-real"}); 1794 fail("Should have thrown an exception"); 1795 } catch (ConfigurationException expected) { 1796 assertEquals( 1797 "Failed to parse config xml 'test-config-real-not-real'. Reason: Mismatch for " 1798 + "device 'device1'. It was defined once as isFake=false, once as " 1799 + "isFake=true", 1800 expected.getMessage()); 1801 } 1802 } 1803 1804 /** 1805 * Test that a configuration with two real devices and one fake one (isFake=false) will reject 1806 * object that are at the root: We cannot decide where to put these objects. 1807 */ 1808 @Test testCreateConfiguration_multiDevice_twoReal_oneFake()1809 public void testCreateConfiguration_multiDevice_twoReal_oneFake() throws Exception { 1810 try { 1811 mFactory.createConfigurationFromArgs(new String[] {"test-config-multi-3-fake"}); 1812 fail("Should have thrown an exception"); 1813 } catch (ConfigurationException expected) { 1814 assertEquals( 1815 "You seem to want a multi-devices configuration but you have [target_preparer] " 1816 + "tags outside the <device> tags", 1817 expected.getMessage()); 1818 } 1819 } 1820 1821 /** Class to test out lab preparer parsing */ 1822 public static final class TestLabPreparer extends StubTargetPreparer implements ILabPreparer {} 1823 1824 @Test testParse_labPreparer()1825 public void testParse_labPreparer() throws Exception { 1826 String normalConfig = 1827 "<configuration description=\"desc\" >\n" 1828 + " <lab_preparer class=\"" 1829 + TestLabPreparer.class.getName() 1830 + "\">\n" 1831 + " <option name=\"test-boolean-option\" value=\"false\"/>" 1832 + " </lab_preparer>\n" 1833 + "</configuration>"; 1834 File tmpConfig = FileUtil.createTempFile("tmp-config-tests", ".xml"); 1835 try { 1836 FileUtil.writeToFile(normalConfig, tmpConfig); 1837 IConfiguration config = 1838 mFactory.createConfigurationFromArgs( 1839 new String[] {tmpConfig.getAbsolutePath()}); 1840 assertEquals(1, config.getLabPreparers().size()); 1841 assertFalse( 1842 ((StubTargetPreparer) config.getLabPreparers().get(0)).getTestBooleanOption()); 1843 } finally { 1844 FileUtil.deleteFile(tmpConfig); 1845 } 1846 } 1847 1848 /** 1849 * Test that even if multi_pre_target_prep and multi_target_prep share the same type, we do not 1850 * mix the objects internally. 1851 */ 1852 @Test testParse_multiTargetPrep()1853 public void testParse_multiTargetPrep() throws Exception { 1854 String normalConfig = 1855 "<configuration description=\"desc\" >\n" 1856 + " <multi_pre_target_preparer class=\"" 1857 + StubMultiTargetPreparer.class.getName() 1858 + "\" />\n" 1859 + " <multi_target_preparer class=\"" 1860 + StubMultiTargetPreparer.class.getName() 1861 + "\" />\n" 1862 + "</configuration>"; 1863 File tmpConfig = FileUtil.createTempFile("tmp-config-tests", ".xml"); 1864 try { 1865 FileUtil.writeToFile(normalConfig, tmpConfig); 1866 IConfiguration config = 1867 mFactory.createConfigurationFromArgs( 1868 new String[] {tmpConfig.getAbsolutePath()}); 1869 assertEquals(1, config.getMultiPreTargetPreparers().size()); 1870 assertEquals(1, config.getMultiTargetPreparers().size()); 1871 // Different objects have been created for each. 1872 assertNotSame( 1873 config.getMultiPreTargetPreparers().get(0), 1874 config.getMultiTargetPreparers().get(0)); 1875 } finally { 1876 FileUtil.deleteFile(tmpConfig); 1877 } 1878 } 1879 1880 /** Test that an unexpected extension for a config file doesn't parse. */ 1881 @Test testParseUnexpectedFormat()1882 public void testParseUnexpectedFormat() throws Exception { 1883 File testConfigFile = FileUtil.createTempFile("test-config-file", ".txt"); 1884 try { 1885 mFactory.createConfigurationFromArgs(new String[] {testConfigFile.getAbsolutePath()}); 1886 fail("Should have thrown an exception"); 1887 } catch (ConfigurationException expected) { 1888 assertTrue(expected.getMessage().contains("not supported.")); 1889 } finally { 1890 FileUtil.deleteFile(testConfigFile); 1891 } 1892 } 1893 1894 /** Test that a YAML config command line parse correctly. */ 1895 @Test testCreateConfigurationFromArgs_yaml()1896 public void testCreateConfigurationFromArgs_yaml() throws Exception { 1897 IConfiguration config = 1898 mFactory.createConfigurationFromArgs( 1899 new String[] { 1900 "yaml/test-config.tf_yaml", 1901 "--build-id", 1902 "5", 1903 "--build-flavor", 1904 "test", 1905 "--branch", 1906 "main" 1907 }); 1908 assertNotNull(config); 1909 IBuildProvider provider = config.getBuildProvider(); 1910 assertTrue(provider instanceof IDeviceBuildProvider); 1911 IBuildInfo info = ((IDeviceBuildProvider) provider).getBuild(null); 1912 try { 1913 assertEquals("5", info.getBuildId()); 1914 assertEquals("test", info.getBuildFlavor()); 1915 assertEquals("main", info.getBuildBranch()); 1916 } finally { 1917 info.cleanUp(); 1918 } 1919 } 1920 1921 /** 1922 * Test that the direct config regex is working as expected 1923 * 1924 * <p>This test contains examples that should fail the regex. 1925 */ 1926 @Test testIsDirectConfigurationFalses()1927 public void testIsDirectConfigurationFalses() throws Exception { 1928 Map<String, Boolean> testMatrixMap = 1929 Map.ofEntries( 1930 entry("yaml/test-config.tf_yaml", false), 1931 entry("suite/test-mapping", false), 1932 entry( 1933 "./out/host/linux-x86/testcases/HelloWorldHostTest/HelloWorldHostTest.config", 1934 false), 1935 entry("gs/proxy-config", false)); 1936 1937 for (Map.Entry<String, Boolean> entry : testMatrixMap.entrySet()) { 1938 assertEquals(entry.getValue(), mFactory.isDirectConfiguration(entry.getKey())); 1939 } 1940 } 1941 1942 /** 1943 * Test that the direct config regex is working as expected 1944 * 1945 * <p>This test contains examples that should match the regex. 1946 */ 1947 @Test testIsDirectConfigurationTrues()1948 public void testIsDirectConfigurationTrues() throws Exception { 1949 Map<String, Boolean> testMatrixMap = 1950 Map.ofEntries( 1951 entry("gs://tradefed_test_resources/configs/HelloWorldHostTest.xml", true), 1952 entry("gs://other_teams_bucket/SomeOtherTest.xml", true), 1953 entry( 1954 "https://android-build.googleplex.com/objects/configs/SomeABConfig.xml", 1955 true), 1956 entry("http://source.android.com/code/AConfig.config", true), 1957 entry("file://localhost/home/tradefed/config.xml", true)); 1958 1959 for (Map.Entry<String, Boolean> entry : testMatrixMap.entrySet()) { 1960 assertEquals(entry.getValue(), mFactory.isDirectConfiguration(entry.getKey())); 1961 } 1962 } 1963 1964 /** Test that the direct configuration method is minimally working */ 1965 @Test testLoadDirectConfiguration()1966 public void testLoadDirectConfiguration() throws Exception { 1967 ConfigurationFactory spyFactory = Mockito.spy(mRealFactory); 1968 // extract the test-config.xml into a tmp file 1969 InputStream configStream = 1970 getClass().getResourceAsStream(String.format("/testconfigs/%s.xml", TEST_CONFIG)); 1971 File tmpFile = FileUtil.createTempFile(TEST_CONFIG, ".xml"); 1972 ResolvedFile resolvedFile = new ResolvedFile(tmpFile); 1973 String cfgPath = "gs://tradefed_test_resources/configs/test-config.xml"; 1974 try { 1975 FileUtil.writeToFile(configStream, tmpFile); 1976 1977 // Inject it into the direct config resolver, then try to load a direct config 1978 URI cfgUri = new URI(cfgPath); 1979 Mockito.doReturn(resolvedFile) 1980 .when(spyFactory) 1981 .resolveRemoteFile(Mockito.eq(cfgUri), Mockito.<URI>any()); 1982 1983 String args[] = {cfgPath, "--null-device"}; 1984 IConfiguration config = spyFactory.createConfigurationFromArgs(args); 1985 1986 assertNotNull(config); 1987 1988 // ensure it looks like what we'd expect 1989 assertTrue(config.getDeviceRequirements().nullDeviceRequested()); 1990 assertEquals(1, config.getTests().size()); 1991 assertTrue(config.getTests().get(0) instanceof StubOptionTest); 1992 } finally { 1993 FileUtil.deleteFile(tmpFile); 1994 } 1995 } 1996 getClassName(String name)1997 private static String getClassName(String name) { 1998 // -6 because of .class 1999 return name.substring(0, name.length() - 6).replace('/', '.'); 2000 } 2001 getClassInContribJar()2002 private Map<String, List<String>> getClassInContribJar() throws IOException { 2003 Map<String, List<String>> jarToObject = new LinkedHashMap<String, List<String>>(); 2004 for (File jar : getListOfBuiltJars()) { 2005 List<String> objects = new ArrayList<>(); 2006 JarFile jarFile = null; 2007 try { 2008 jarFile = new JarFile(jar); 2009 Enumeration<JarEntry> e = jarFile.entries(); 2010 2011 while (e.hasMoreElements()) { 2012 JarEntry je = e.nextElement(); 2013 if (je.isDirectory() 2014 || !je.getName().endsWith(".class") 2015 || je.getName().contains("$")) { 2016 continue; 2017 } 2018 String className = getClassName(je.getName()); 2019 objects.add(className); 2020 } 2021 } finally { 2022 StreamUtil.close(jarFile); 2023 } 2024 jarToObject.put(jar.getName(), objects); 2025 } 2026 return jarToObject; 2027 } 2028 getListOfBuiltJars()2029 private List<File> getListOfBuiltJars() { 2030 // testJarPath is the path of the jar file that contains this test 2031 // class. We assume the other jars live in the same dir as this test 2032 // class' jar. 2033 String testJarPath = 2034 ConfigurationFactoryTest.class 2035 .getProtectionDomain() 2036 .getCodeSource() 2037 .getLocation() 2038 .getPath(); 2039 File jarFilePath = new File(testJarPath); 2040 String jarFileParentPath = jarFilePath.getParent(); 2041 List<File> listOfJars = new ArrayList<File>(); 2042 File jarToCheck; 2043 for (String jar : JAR_TO_CHECK) { 2044 jarToCheck = new File(jarFileParentPath, jar); 2045 if (jarToCheck.exists()) { 2046 listOfJars.add(jarToCheck); 2047 } 2048 } 2049 return listOfJars; 2050 } 2051 } 2052