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.assertNotEquals;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.verify;
27 
28 import com.android.ddmlib.Log.LogLevel;
29 import com.android.tradefed.build.BuildRetrievalError;
30 import com.android.tradefed.build.IBuildProvider;
31 import com.android.tradefed.command.CommandOptions;
32 import com.android.tradefed.command.ICommandOptions;
33 import com.android.tradefed.device.DeviceNotAvailableException;
34 import com.android.tradefed.device.IDeviceRecovery;
35 import com.android.tradefed.device.IDeviceSelection;
36 import com.android.tradefed.device.TestDeviceOptions;
37 import com.android.tradefed.invoker.InvocationContext;
38 import com.android.tradefed.invoker.TestInformation;
39 import com.android.tradefed.log.ILeveledLogOutput;
40 import com.android.tradefed.result.ITestInvocationListener;
41 import com.android.tradefed.result.TextResultReporter;
42 import com.android.tradefed.targetprep.BaseTargetPreparer;
43 import com.android.tradefed.targetprep.ITargetPreparer;
44 import com.android.tradefed.testtype.IRemoteTest;
45 import com.android.tradefed.util.FileUtil;
46 import com.android.tradefed.util.IDisableable;
47 
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 import org.junit.runners.JUnit4;
52 
53 import java.io.ByteArrayOutputStream;
54 import java.io.File;
55 import java.io.IOException;
56 import java.io.PrintStream;
57 import java.io.PrintWriter;
58 import java.io.StringWriter;
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.HashMap;
62 import java.util.List;
63 import java.util.Map;
64 
65 /** Unit tests for {@link Configuration}. */
66 @RunWith(JUnit4.class)
67 public class ConfigurationTest {
68 
69     private static final String CONFIG_NAME = "name";
70     private static final String CONFIG_DESCRIPTION = "config description";
71     private static final String CONFIG_OBJECT_TYPE_NAME = "object_name";
72     private static final String OPTION_DESCRIPTION = "bool description";
73     private static final String OPTION_NAME = "bool";
74     private static final String ALT_OPTION_NAME = "map";
75 
76     /** Interface for test object stored in a {@link IConfiguration}. */
77     private static interface TestConfig {
78 
getBool()79         public boolean getBool();
80     }
81 
82     private static class TestConfigObject implements TestConfig, IDisableable {
83 
84         @Option(name = OPTION_NAME, description = OPTION_DESCRIPTION, requiredForRerun = true)
85         private boolean mBool;
86 
87         @Option(name = ALT_OPTION_NAME, description = OPTION_DESCRIPTION)
88         private Map<String, Boolean> mBoolMap = new HashMap<String, Boolean>();
89 
90         @Option(name = "mandatory-option", mandatory = true)
91         private String mMandatory = null;
92 
93         private boolean mIsDisabled = false;
94 
95         @Override
getBool()96         public boolean getBool() {
97             return mBool;
98         }
99 
getMap()100         public Map<String, Boolean> getMap() {
101             return mBoolMap;
102         }
103 
104         @Override
setDisable(boolean isDisabled)105         public void setDisable(boolean isDisabled) {
106             mIsDisabled = isDisabled;
107         }
108 
109         @Override
isDisabled()110         public boolean isDisabled() {
111             return mIsDisabled;
112         }
113     }
114 
115     private Configuration mConfig;
116 
117     @Before
setUp()118     public void setUp() throws Exception {
119         mConfig = new Configuration(CONFIG_NAME, CONFIG_DESCRIPTION);
120 
121         try {
122             GlobalConfiguration.createGlobalConfiguration(new String[] {"empty"});
123         } catch (IllegalStateException ignored) {
124         }
125     }
126 
127     /**
128      * Test that {@link Configuration#getConfigurationObject(String)} can retrieve a previously
129      * stored object.
130      */
131     @Test
testGetConfigurationObject()132     public void testGetConfigurationObject() throws ConfigurationException {
133         TestConfigObject testConfigObject = new TestConfigObject();
134         mConfig.setConfigurationObject(CONFIG_OBJECT_TYPE_NAME, testConfigObject);
135         Object fromConfig = mConfig.getConfigurationObject(CONFIG_OBJECT_TYPE_NAME);
136         assertEquals(testConfigObject, fromConfig);
137     }
138 
139     /** Test {@link Configuration#getConfigurationObjectList(String)} */
140     @SuppressWarnings("unchecked")
141     @Test
testGetConfigurationObjectList()142     public void testGetConfigurationObjectList() throws ConfigurationException {
143         TestConfigObject testConfigObject = new TestConfigObject();
144         mConfig.setConfigurationObject(CONFIG_OBJECT_TYPE_NAME, testConfigObject);
145         List<TestConfig> configList =
146                 (List<TestConfig>) mConfig.getConfigurationObjectList(CONFIG_OBJECT_TYPE_NAME);
147         assertEquals(testConfigObject, configList.get(0));
148     }
149 
150     /**
151      * Test that {@link Configuration#getConfigurationObject(String)} with a name that does not
152      * exist.
153      */
154     @Test
testGetConfigurationObject_wrongname()155     public void testGetConfigurationObject_wrongname() {
156         assertNull(mConfig.getConfigurationObject("non-existent"));
157     }
158 
159     /**
160      * Test that calling {@link Configuration#getConfigurationObject(String)} for a built-in config
161      * type that supports lists.
162      */
163     @Test
testGetConfigurationObject_typeIsList()164     public void testGetConfigurationObject_typeIsList() {
165         try {
166             mConfig.getConfigurationObject(Configuration.TEST_TYPE_NAME);
167             fail("IllegalStateException not thrown");
168         } catch (IllegalStateException e) {
169             // expected
170         }
171     }
172 
173     /**
174      * Test that calling {@link Configuration#getConfigurationObject(String)} for a config type that
175      * is a list.
176      */
177     @Test
testGetConfigurationObject_forList()178     public void testGetConfigurationObject_forList() throws ConfigurationException {
179         List<TestConfigObject> list = new ArrayList<TestConfigObject>();
180         list.add(new TestConfigObject());
181         list.add(new TestConfigObject());
182         mConfig.setConfigurationObjectList(CONFIG_OBJECT_TYPE_NAME, list);
183         try {
184             mConfig.getConfigurationObject(CONFIG_OBJECT_TYPE_NAME);
185             fail("IllegalStateException not thrown");
186         } catch (IllegalStateException e) {
187             // expected
188         }
189     }
190 
191     /**
192      * Test that setConfigurationObject throws a ConfigurationException when config object provided
193      * is not the correct type
194      */
195     @Test
testSetConfigurationObject_wrongtype()196     public void testSetConfigurationObject_wrongtype() {
197         try {
198             // arbitrarily, use the "Test" type as expected type
199             mConfig.setConfigurationObject(Configuration.TEST_TYPE_NAME, new TestConfigObject());
200             fail("setConfigurationObject did not throw ConfigurationException");
201         } catch (ConfigurationException e) {
202             // expected
203         }
204     }
205 
206     /**
207      * Test {@link Configuration#getConfigurationObjectList(String)} when config object with given
208      * name does not exist.
209      */
210     @Test
testGetConfigurationObjectList_wrongname()211     public void testGetConfigurationObjectList_wrongname() {
212         assertNull(mConfig.getConfigurationObjectList("non-existent"));
213     }
214 
215     /**
216      * Test {@link Configuration#setConfigurationObjectList(String, List)} when config object is the
217      * wrong type
218      */
219     @Test
testSetConfigurationObjectList_wrongtype()220     public void testSetConfigurationObjectList_wrongtype() {
221         try {
222             List<TestConfigObject> myList = new ArrayList<TestConfigObject>(1);
223             myList.add(new TestConfigObject());
224             // arbitrarily, use the "Test" type as expected type
225             mConfig.setConfigurationObjectList(Configuration.TEST_TYPE_NAME, myList);
226             fail("setConfigurationObject did not throw ConfigurationException");
227         } catch (ConfigurationException e) {
228             // expected
229         }
230     }
231 
232     /** Test method for {@link Configuration#getBuildProvider()}. */
233     @Test
testGetBuildProvider()234     public void testGetBuildProvider() throws BuildRetrievalError {
235         // check that the default provider is present and doesn't blow up
236         assertNotNull(mConfig.getBuildProvider().getBuild());
237         // check set and get
238         final IBuildProvider provider = mock(IBuildProvider.class);
239         mConfig.setBuildProvider(provider);
240         assertEquals(provider, mConfig.getBuildProvider());
241     }
242 
243     /** Test method for {@link Configuration#getTargetPreparers()}. */
244     @Test
testGetTargetPreparers()245     public void testGetTargetPreparers() throws Exception {
246         // check that the callback is working and doesn't blow up
247         assertEquals(0, mConfig.getTargetPreparers().size());
248         // test set and get
249         final ITargetPreparer prep = mock(ITargetPreparer.class);
250         mConfig.setTargetPreparer(prep);
251         assertEquals(prep, mConfig.getTargetPreparers().get(0));
252     }
253 
254     /** Test method for {@link Configuration#getTests()}. */
255     @Test
testGetTests()256     public void testGetTests() throws DeviceNotAvailableException {
257         // check that the default test is present and doesn't blow up
258         mConfig.getTests()
259                 .get(0)
260                 .run(TestInformation.newBuilder().build(), new TextResultReporter());
261         IRemoteTest test1 = mock(IRemoteTest.class);
262         mConfig.setTest(test1);
263         assertEquals(test1, mConfig.getTests().get(0));
264     }
265 
266     /** Test method for {@link Configuration#getDeviceRecovery()}. */
267     @Test
testGetDeviceRecovery()268     public void testGetDeviceRecovery() {
269         // check that the default recovery is present
270         assertNotNull(mConfig.getDeviceRecovery());
271         final IDeviceRecovery recovery = mock(IDeviceRecovery.class);
272         mConfig.setDeviceRecovery(recovery);
273         assertEquals(recovery, mConfig.getDeviceRecovery());
274     }
275 
276     /** Test method for {@link Configuration#getLogOutput()}. */
277     @Test
testGetLogOutput()278     public void testGetLogOutput() {
279         // check that the default logger is present and doesn't blow up
280         mConfig.getLogOutput().printLog(LogLevel.INFO, "testGetLogOutput", "test");
281         final ILeveledLogOutput logger = mock(ILeveledLogOutput.class);
282         mConfig.setLogOutput(logger);
283         assertEquals(logger, mConfig.getLogOutput());
284     }
285 
286     /**
287      * Test method for {@link Configuration#getTestInvocationListeners()}.
288      *
289      * @throws ConfigurationException
290      */
291     @Test
testGetTestInvocationListeners()292     public void testGetTestInvocationListeners() throws ConfigurationException {
293         // check that the default listener is present and doesn't blow up
294         ITestInvocationListener defaultListener = mConfig.getTestInvocationListeners().get(0);
295         defaultListener.invocationStarted(new InvocationContext());
296         defaultListener.invocationEnded(1);
297 
298         final ITestInvocationListener listener1 = mock(ITestInvocationListener.class);
299         mConfig.setTestInvocationListener(listener1);
300         assertEquals(listener1, mConfig.getTestInvocationListeners().get(0));
301     }
302 
303     /** Test method for {@link Configuration#getCommandOptions()}. */
304     @Test
testGetCommandOptions()305     public void testGetCommandOptions() {
306         // check that the default object is present
307         assertNotNull(mConfig.getCommandOptions());
308         final ICommandOptions cmdOptions = mock(ICommandOptions.class);
309         mConfig.setCommandOptions(cmdOptions);
310         assertEquals(cmdOptions, mConfig.getCommandOptions());
311     }
312 
313     /** Test method for {@link Configuration#getDeviceRequirements()}. */
314     @Test
testGetDeviceRequirements()315     public void testGetDeviceRequirements() {
316         // check that the default object is present
317         assertNotNull(mConfig.getDeviceRequirements());
318         final IDeviceSelection deviceSelection = mock(IDeviceSelection.class);
319         mConfig.setDeviceRequirements(deviceSelection);
320         assertEquals(deviceSelection, mConfig.getDeviceRequirements());
321     }
322 
323     /**
324      * Test {@link Configuration#setConfigurationObject(String, Object)} with a {@link
325      * IConfigurationReceiver}
326      */
327     @Test
testSetConfigurationObject_configReceiver()328     public void testSetConfigurationObject_configReceiver() throws ConfigurationException {
329         final IConfigurationReceiver mockConfigReceiver = mock(IConfigurationReceiver.class);
330 
331         mConfig.setConfigurationObject("example", mockConfigReceiver);
332 
333         verify(mockConfigReceiver).setConfiguration(mConfig);
334     }
335 
336     /** Test {@link Configuration#injectOptionValue(String, String)} */
337     @Test
testInjectOptionValue()338     public void testInjectOptionValue() throws ConfigurationException {
339         TestConfigObject testConfigObject = new TestConfigObject();
340         mConfig.setConfigurationObject(CONFIG_OBJECT_TYPE_NAME, testConfigObject);
341         mConfig.injectOptionValue(OPTION_NAME, Boolean.toString(true));
342         assertTrue(testConfigObject.getBool());
343         assertEquals(1, mConfig.getConfigurationDescription().getRerunOptions().size());
344         OptionDef optionDef = mConfig.getConfigurationDescription().getRerunOptions().get(0);
345         assertEquals(OPTION_NAME, optionDef.name);
346     }
347 
348     /** Test {@link Configuration#injectOptionValue(String, String, String)} */
349     @Test
testInjectMapOptionValue()350     public void testInjectMapOptionValue() throws ConfigurationException {
351         final String key = "hello";
352 
353         TestConfigObject testConfigObject = new TestConfigObject();
354         mConfig.setConfigurationObject(CONFIG_OBJECT_TYPE_NAME, testConfigObject);
355         assertEquals(0, testConfigObject.getMap().size());
356         mConfig.injectOptionValue(ALT_OPTION_NAME, key, Boolean.toString(true));
357 
358         Map<String, Boolean> map = testConfigObject.getMap();
359         assertEquals(1, map.size());
360         assertNotNull(map.get(key));
361         assertTrue(map.get(key).booleanValue());
362     }
363 
364     /**
365      * Test {@link Configuration#injectOptionValue(String, String)} is throwing an exception for map
366      * options without no map key provided in the option value
367      */
368     @Test
testInjectParsedMapOptionValueNoKey()369     public void testInjectParsedMapOptionValueNoKey() throws ConfigurationException {
370         TestConfigObject testConfigObject = new TestConfigObject();
371         mConfig.setConfigurationObject(CONFIG_OBJECT_TYPE_NAME, testConfigObject);
372         assertEquals(0, testConfigObject.getMap().size());
373 
374         try {
375             mConfig.injectOptionValue(ALT_OPTION_NAME, "wrong_value");
376             fail("ConfigurationException is not thrown for a map option without retrievable key");
377         } catch (ConfigurationException ignore) {
378             // expected
379         }
380     }
381 
382     /**
383      * Test {@link Configuration#injectOptionValue(String, String)} is throwing an exception for map
384      * options with ambiguous map key provided in the option value (multiple equal signs)
385      */
386     @Test
testInjectParsedMapOptionValueAmbiguousKey()387     public void testInjectParsedMapOptionValueAmbiguousKey() throws ConfigurationException {
388         TestConfigObject testConfigObject = new TestConfigObject();
389         mConfig.setConfigurationObject(CONFIG_OBJECT_TYPE_NAME, testConfigObject);
390         assertEquals(0, testConfigObject.getMap().size());
391 
392         try {
393             mConfig.injectOptionValue(ALT_OPTION_NAME, "a=b=c");
394             fail("ConfigurationException is not thrown for a map option with ambiguous key");
395         } catch (ConfigurationException ignore) {
396             // expected
397         }
398     }
399 
400     /**
401      * Test {@link Configuration#injectOptionValue(String, String)} is correctly parsing map options
402      */
403     @Test
testInjectParsedMapOptionValue()404     public void testInjectParsedMapOptionValue() throws ConfigurationException {
405         final String key = "hello\\=key";
406 
407         TestConfigObject testConfigObject = new TestConfigObject();
408         mConfig.setConfigurationObject(CONFIG_OBJECT_TYPE_NAME, testConfigObject);
409         assertEquals(0, testConfigObject.getMap().size());
410         mConfig.injectOptionValue(ALT_OPTION_NAME, key + "=" + Boolean.toString(true));
411 
412         Map<String, Boolean> map = testConfigObject.getMap();
413         assertEquals(1, map.size());
414         assertNotNull(map.get(key));
415         assertTrue(map.get(key));
416     }
417 
418     /** Test {@link Configuration#injectOptionValues(List)} */
419     @Test
testInjectOptionValues()420     public void testInjectOptionValues() throws ConfigurationException {
421         final String key = "hello";
422         List<OptionDef> options = new ArrayList<>();
423         options.add(new OptionDef(OPTION_NAME, Boolean.toString(true), null));
424         options.add(new OptionDef(ALT_OPTION_NAME, key, Boolean.toString(true), null));
425 
426         TestConfigObject testConfigObject = new TestConfigObject();
427         mConfig.setConfigurationObject(CONFIG_OBJECT_TYPE_NAME, testConfigObject);
428         mConfig.injectOptionValues(options);
429 
430         assertTrue(testConfigObject.getBool());
431         Map<String, Boolean> map = testConfigObject.getMap();
432         assertEquals(1, map.size());
433         assertNotNull(map.get(key));
434         assertTrue(map.get(key).booleanValue());
435         assertEquals(1, mConfig.getConfigurationDescription().getRerunOptions().size());
436         OptionDef optionDef = mConfig.getConfigurationDescription().getRerunOptions().get(0);
437         assertEquals(OPTION_NAME, optionDef.name);
438     }
439 
440     /** Basic test for {@link Configuration#printCommandUsage(boolean, java.io.PrintStream)}. */
441     @Test
testPrintCommandUsage()442     public void testPrintCommandUsage() throws ConfigurationException {
443         TestConfigObject testConfigObject = new TestConfigObject();
444         mConfig.setConfigurationObject(CONFIG_OBJECT_TYPE_NAME, testConfigObject);
445         // dump the print stream results to the ByteArrayOutputStream, so contents can be evaluated
446         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
447         PrintStream mockPrintStream = new PrintStream(outputStream);
448         mConfig.printCommandUsage(false, mockPrintStream);
449 
450         // verifying exact contents would be prone to high-maintenance, so instead, just validate
451         // all expected names are present
452         final String usageString = outputStream.toString();
453         assertTrue("Usage text does not contain config name", usageString.contains(CONFIG_NAME));
454         assertTrue(
455                 "Usage text does not contain config description",
456                 usageString.contains(CONFIG_DESCRIPTION));
457         assertTrue(
458                 "Usage text does not contain object name",
459                 usageString.contains(CONFIG_OBJECT_TYPE_NAME));
460         assertTrue("Usage text does not contain option name", usageString.contains(OPTION_NAME));
461         assertTrue(
462                 "Usage text does not contain option description",
463                 usageString.contains(OPTION_DESCRIPTION));
464 
465         // ensure help prints out options from default config types
466         assertTrue(
467                 "Usage text does not contain --serial option name", usageString.contains("serial"));
468     }
469 
470     /**
471      * Test that {@link Configuration#validateOptions()} doesn't throw when all mandatory fields are
472      * set.
473      */
474     @Test
testValidateOptions()475     public void testValidateOptions() throws ConfigurationException {
476         mConfig.validateOptions();
477     }
478 
479     /**
480      * Test that {@link Configuration#validateOptions()} throw when all mandatory fields are not set
481      * and object is not disabled.
482      */
483     @Test
testValidateOptions_nonDisabledObject()484     public void testValidateOptions_nonDisabledObject() throws ConfigurationException {
485         TestConfigObject object = new TestConfigObject();
486         object.setDisable(false);
487         mConfig.setConfigurationObject("helper", object);
488         try {
489             mConfig.validateOptions();
490             fail("Should have thrown an exception.");
491         } catch (ConfigurationException expected) {
492             assertTrue(expected.getMessage().contains("Found missing mandatory options"));
493         }
494     }
495 
496     /**
497      * Test that {@link Configuration#validateOptions()} doesn't throw when all mandatory fields are
498      * not set but the object is disabled.
499      */
500     @Test
testValidateOptions_disabledObject()501     public void testValidateOptions_disabledObject() throws ConfigurationException {
502         TestConfigObject object = new TestConfigObject();
503         object.setDisable(true);
504         mConfig.setConfigurationObject("helper", object);
505         mConfig.validateOptions();
506     }
507 
508     /**
509      * Test that {@link Configuration#validateOptions()} throws a config exception when shard count
510      * is negative number.
511      */
512     @Test
testValidateOptionsShardException()513     public void testValidateOptionsShardException() throws ConfigurationException {
514         ICommandOptions option =
515                 new CommandOptions() {
516                     @Override
517                     public Integer getShardCount() {
518                         return -1;
519                     }
520                 };
521         mConfig.setConfigurationObject(Configuration.CMD_OPTIONS_TYPE_NAME, option);
522         try {
523             mConfig.validateOptions();
524             fail("Should have thrown an exception.");
525         } catch (ConfigurationException expected) {
526             assertEquals("a shard count must be a positive number", expected.getMessage());
527         }
528     }
529 
530     /**
531      * Test that {@link Configuration#validateOptions()} throws a config exception when shard index
532      * is not valid.
533      */
534     @Test
testValidateOptionsShardIndexException()535     public void testValidateOptionsShardIndexException() throws ConfigurationException {
536         ICommandOptions option =
537                 new CommandOptions() {
538                     @Override
539                     public Integer getShardIndex() {
540                         return -1;
541                     }
542                 };
543         mConfig.setConfigurationObject(Configuration.CMD_OPTIONS_TYPE_NAME, option);
544         try {
545             mConfig.validateOptions();
546             fail("Should have thrown an exception.");
547         } catch (ConfigurationException expected) {
548             assertEquals("a shard index must be in range [0, shard count)", expected.getMessage());
549         }
550     }
551 
552     /**
553      * Test that {@link Configuration#validateOptions()} throws a config exception when shard index
554      * is above the shard count.
555      */
556     @Test
testValidateOptionsShardIndexAboveShardCount()557     public void testValidateOptionsShardIndexAboveShardCount() throws ConfigurationException {
558         ICommandOptions option =
559                 new CommandOptions() {
560                     @Override
561                     public Integer getShardIndex() {
562                         return 3;
563                     }
564 
565                     @Override
566                     public Integer getShardCount() {
567                         return 2;
568                     }
569                 };
570         mConfig.setConfigurationObject(Configuration.CMD_OPTIONS_TYPE_NAME, option);
571         try {
572             mConfig.validateOptions();
573             fail("Should have thrown an exception.");
574         } catch (ConfigurationException expected) {
575             assertEquals("a shard index must be in range [0, shard count)", expected.getMessage());
576         }
577     }
578 
579     /**
580      * Ensure that dynamic file download is not triggered in the parent invocation of local
581      * sharding. If that was the case, the downloaded files would be cleaned up right after the
582      * shards are kicked-off in new invocations.
583      */
584     @Test
testValidateOptions_localSharding_skipDownload()585     public void testValidateOptions_localSharding_skipDownload() throws Exception {
586         mConfig =
587                 new Configuration(CONFIG_NAME, CONFIG_DESCRIPTION) {
588                     @Override
589                     protected boolean isRemoteEnvironment() {
590                         return false;
591                     }
592                 };
593         CommandOptions options = new CommandOptions();
594         options.setShardCount(5);
595         options.setShardIndex(null);
596         mConfig.setCommandOptions(options);
597         TestDeviceOptions deviceOptions = new TestDeviceOptions();
598         File fakeConfigFile = new File("gs://bucket/remote/file/path");
599         deviceOptions.setAvdConfigFile(fakeConfigFile);
600         mConfig.setDeviceOptions(deviceOptions);
601 
602         // No exception for download is thrown because no download occurred.
603         mConfig.validateOptions();
604         mConfig.resolveDynamicOptions(new DynamicRemoteFileResolver());
605         // Dynamic file is not resolved.
606         assertEquals(fakeConfigFile, deviceOptions.getAvdConfigFile());
607     }
608 
609     /** Test that {@link Configuration#dumpXml(PrintWriter)} produce the xml output. */
610     @Test
testDumpXml()611     public void testDumpXml() throws IOException {
612         File test = FileUtil.createTempFile("dumpxml", "xml");
613         try {
614             PrintWriter out = new PrintWriter(test);
615             mConfig.dumpXml(out);
616             out.flush();
617             String content = FileUtil.readStringFromFile(test);
618             assertTrue(content.length() > 100);
619             assertTrue(content.contains("<configuration>"));
620             assertTrue(content.contains("<test class"));
621         } finally {
622             FileUtil.deleteFile(test);
623         }
624     }
625 
626     /**
627      * Test that {@link Configuration#dumpXml(PrintWriter)} produce the xml output without objects
628      * that have been filtered.
629      */
630     @Test
testDumpXml_withFilter()631     public void testDumpXml_withFilter() throws IOException {
632         File test = FileUtil.createTempFile("dumpxml", "xml");
633         try {
634             PrintWriter out = new PrintWriter(test);
635             List<String> filters = new ArrayList<>();
636             filters.add(Configuration.TEST_TYPE_NAME);
637             mConfig.dumpXml(out, filters);
638             out.flush();
639             String content = FileUtil.readStringFromFile(test);
640             assertTrue(content.length() > 100);
641             assertTrue(content.contains("<configuration>"));
642             assertFalse(content.contains("<test class"));
643         } finally {
644             FileUtil.deleteFile(test);
645         }
646     }
647 
648     /**
649      * Test that {@link Configuration#dumpXml(PrintWriter)} produce the xml output even for a multi
650      * device situation.
651      */
652     @Test
testDumpXml_multi_device()653     public void testDumpXml_multi_device() throws Exception {
654         List<IDeviceConfiguration> deviceObjectList = new ArrayList<IDeviceConfiguration>();
655         deviceObjectList.add(new DeviceConfigurationHolder("device1"));
656         deviceObjectList.add(new DeviceConfigurationHolder("device2"));
657         mConfig.setConfigurationObjectList(Configuration.DEVICE_NAME, deviceObjectList);
658         File test = FileUtil.createTempFile("dumpxml", "xml");
659         try {
660             PrintWriter out = new PrintWriter(test);
661             mConfig.dumpXml(out);
662             out.flush();
663             String content = FileUtil.readStringFromFile(test);
664             assertTrue(content.length() > 100);
665             assertTrue(content.contains("<device name=\"device1\">"));
666             assertTrue(content.contains("<device name=\"device2\">"));
667         } finally {
668             FileUtil.deleteFile(test);
669         }
670     }
671 
672     /**
673      * Test that {@link Configuration#dumpXml(PrintWriter)} produce the xml output even for a multi
674      * device situation when one of the device is fake.
675      */
676     @Test
testDumpXml_multi_device_fake()677     public void testDumpXml_multi_device_fake() throws Exception {
678         List<IDeviceConfiguration> deviceObjectList = new ArrayList<IDeviceConfiguration>();
679         deviceObjectList.add(new DeviceConfigurationHolder("device1", true));
680         deviceObjectList.add(new DeviceConfigurationHolder("device2"));
681         mConfig.setConfigurationObjectList(Configuration.DEVICE_NAME, deviceObjectList);
682         File test = FileUtil.createTempFile("dumpxml", "xml");
683         try {
684             PrintWriter out = new PrintWriter(test);
685             mConfig.dumpXml(out);
686             out.flush();
687             String content = FileUtil.readStringFromFile(test);
688             assertTrue(content.length() > 100);
689             assertTrue(content.contains("<device name=\"device1\" isFake=\"true\">"));
690             assertTrue(content.contains("<device name=\"device2\">"));
691         } finally {
692             FileUtil.deleteFile(test);
693         }
694     }
695 
696     /** Ensure that the dump xml only considere trully changed option on the same object. */
697     @Test
testDumpChangedOption()698     public void testDumpChangedOption() throws Exception {
699         CommandOptions options1 = new CommandOptions();
700         Configuration one = new Configuration("test", "test");
701         one.setCommandOptions(options1);
702         StringWriter sw = new StringWriter();
703         PrintWriter pw = new PrintWriter(sw);
704         one.dumpXml(pw, new ArrayList<>(), true, false);
705         String noOption = sw.toString();
706         assertTrue(
707                 noOption.contains(
708                         "<cmd_options class=\"com.android.tradefed.command.CommandOptions\" />"));
709 
710         OptionSetter setter = new OptionSetter(options1);
711         setter.setOptionValue("test-tag", "tag-value");
712         sw = new StringWriter();
713         pw = new PrintWriter(sw);
714         one.dumpXml(pw, new ArrayList<>(), true, false);
715         String withOption = sw.toString();
716         assertTrue(withOption.contains("<option name=\"test-tag\" value=\"tag-value\" />"));
717 
718         CommandOptions options2 = new CommandOptions();
719         one.setCommandOptions(options2);
720         sw = new StringWriter();
721         pw = new PrintWriter(sw);
722         one.dumpXml(pw, new ArrayList<>(), true, false);
723         String differentObject = sw.toString();
724         assertTrue(
725                 differentObject.contains(
726                         "<cmd_options class=\"com.android.tradefed.command.CommandOptions\" />"));
727     }
728 
729     /** Ensure we print modified option if they are structures. */
730     @Test
testDumpChangedOption_structure()731     public void testDumpChangedOption_structure() throws Exception {
732         CommandOptions options1 = new CommandOptions();
733         Configuration one = new Configuration("test", "test");
734         one.setCommandOptions(options1);
735         StringWriter sw = new StringWriter();
736         PrintWriter pw = new PrintWriter(sw);
737         one.dumpXml(pw, new ArrayList<>(), true, false);
738         String noOption = sw.toString();
739         assertTrue(
740                 noOption.contains(
741                         "<cmd_options class=\"com.android.tradefed.command.CommandOptions\" />"));
742 
743         OptionSetter setter = new OptionSetter(options1);
744         setter.setOptionValue("invocation-data", "key", "value");
745         setter.setOptionValue("auto-collect", "LOGCAT_ON_FAILURE");
746         sw = new StringWriter();
747         pw = new PrintWriter(sw);
748         one.dumpXml(pw, new ArrayList<>(), true, false);
749         String withOption = sw.toString();
750         assertTrue(
751                 withOption.contains(
752                         "<option name=\"invocation-data\" key=\"key\" value=\"value\" />"));
753         assertTrue(
754                 withOption.contains(
755                         "<option name=\"auto-collect\" value=\"LOGCAT_ON_FAILURE\" />"));
756     }
757 
758     @Test
testDeepClone()759     public void testDeepClone() throws Exception {
760         Configuration original =
761                 (Configuration)
762                         ConfigurationFactory.getInstance()
763                                 .createConfigurationFromArgs(
764                                         new String[] {"instrumentations"}, null, null);
765         IConfiguration copy =
766                 original.partialDeepClone(
767                         Arrays.asList(Configuration.DEVICE_NAME, Configuration.TEST_TYPE_NAME),
768                         null);
769         assertNotEquals(
770                 original.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME),
771                 copy.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME));
772         assertNotEquals(original.getTargetPreparers().get(0), copy.getTargetPreparers().get(0));
773         assertNotEquals(
774                 original.getDeviceConfig().get(0).getTargetPreparers().get(0),
775                 copy.getDeviceConfig().get(0).getTargetPreparers().get(0));
776         assertNotEquals(original.getTests().get(0), copy.getTests().get(0));
777         copy.validateOptions();
778     }
779 
780     @Test
testDeepClone_innerDevice()781     public void testDeepClone_innerDevice() throws Exception {
782         Configuration original =
783                 (Configuration)
784                         ConfigurationFactory.getInstance()
785                                 .createConfigurationFromArgs(
786                                         new String[] {"instrumentations"}, null, null);
787         IConfiguration copy =
788                 original.partialDeepClone(
789                         Arrays.asList(
790                                 Configuration.TARGET_PREPARER_TYPE_NAME,
791                                 Configuration.TEST_TYPE_NAME),
792                         null);
793         assertNotEquals(
794                 original.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME),
795                 copy.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME));
796         assertNotEquals(original.getTargetPreparers().get(0), copy.getTargetPreparers().get(0));
797         assertNotEquals(
798                 original.getDeviceConfig().get(0).getTargetPreparers().get(0),
799                 copy.getDeviceConfig().get(0).getTargetPreparers().get(0));
800         assertNotEquals(original.getTests().get(0), copy.getTests().get(0));
801         copy.validateOptions();
802     }
803 
804     @Test
testDeepClone_configReceiver()805     public void testDeepClone_configReceiver() throws Exception {
806         Configuration original = new Configuration("test", "test");
807         ConfigReceiverPreparer oriReceiver = new ConfigReceiverPreparer();
808         oriReceiver.setConfiguration(original);
809         original.setTargetPreparer(oriReceiver);
810         assertNotNull(oriReceiver.getConfiguration());
811 
812         IConfiguration copy =
813                 original.partialDeepClone(
814                         Arrays.asList(Configuration.TARGET_PREPARER_TYPE_NAME), null);
815         assertNotEquals(
816                 original.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME),
817                 copy.getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME));
818         assertNotEquals(original.getTargetPreparers().get(0), copy.getTargetPreparers().get(0));
819         ConfigReceiverPreparer copyReceiver =
820                 (ConfigReceiverPreparer) copy.getTargetPreparers().get(0);
821         assertNotNull(copyReceiver.getConfiguration());
822     }
823 
824     public static class ConfigReceiverPreparer extends BaseTargetPreparer
825             implements IConfigurationReceiver {
826 
827         private IConfiguration mConfig;
828 
829         @Override
setConfiguration(IConfiguration configuration)830         public void setConfiguration(IConfiguration configuration) {
831             mConfig = configuration;
832         }
833 
getConfiguration()834         public IConfiguration getConfiguration() {
835             return mConfig;
836         }
837     }
838 }
839