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