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