1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.compatibility.common.tradefed.testtype;
18 
19 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
20 import com.android.compatibility.common.tradefed.testtype.ModuleRepo.ConfigFilter;
21 import com.android.tradefed.build.IBuildInfo;
22 import com.android.tradefed.config.Configuration;
23 import com.android.tradefed.config.ConfigurationDescriptor;
24 import com.android.tradefed.config.IConfiguration;
25 import com.android.tradefed.config.Option;
26 import com.android.tradefed.device.DeviceNotAvailableException;
27 import com.android.tradefed.result.ITestInvocationListener;
28 import com.android.tradefed.targetprep.ITargetPreparer;
29 import com.android.tradefed.testtype.Abi;
30 import com.android.tradefed.testtype.IAbi;
31 import com.android.tradefed.testtype.IAbiReceiver;
32 import com.android.tradefed.testtype.IRemoteTest;
33 import com.android.tradefed.testtype.IRuntimeHintProvider;
34 import com.android.tradefed.testtype.ITestCollector;
35 import com.android.tradefed.testtype.ITestFilterReceiver;
36 import com.android.tradefed.util.AbiUtils;
37 import com.android.tradefed.util.FileUtil;
38 import com.android.tradefed.util.MultiMap;
39 
40 import junit.framework.TestCase;
41 
42 import org.easymock.EasyMock;
43 
44 import java.io.File;
45 import java.io.IOException;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.Collections;
49 import java.util.HashMap;
50 import java.util.HashSet;
51 import java.util.LinkedHashSet;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Set;
55 
56 /**
57  * Unit Tests for {@link ModuleRepo}
58  */
59 public class ModuleRepoTest extends TestCase {
60 
61     private static final String TOKEN =
62             "<target_preparer class=\"com.android.compatibility.common.tradefed.targetprep.TokenRequirement\">\n"
63             + "<option name=\"token\" value=\"%s\" />\n"
64             + "</target_preparer>\n";
65     private static final String CONFIG =
66             "<configuration description=\"Auto Generated File\">\n" +
67             "%s" +
68             "<test class=\"com.android.compatibility.common.tradefed.testtype.%s\">\n" +
69             "<option name=\"module\" value=\"%s\" />" +
70             "</test>\n" +
71             "</configuration>";
72     private static final String FOOBAR_TOKEN = "foobar";
73     private static final String SERIAL1 = "abc";
74     private static final String SERIAL2 = "def";
75     private static final String SERIAL3 = "ghi";
76     private static final Set<String> SERIALS = new HashSet<>();
77     private static final Set<IAbi> ABIS = new LinkedHashSet<>();
78     private static final List<String> DEVICE_TOKENS = new ArrayList<>();
79     private static final List<String> TEST_ARGS= new ArrayList<>();
80     private static final List<String> MODULE_ARGS = new ArrayList<>();
81     private static final Set<String> INCLUDES = new HashSet<>();
82     private static final Set<String> EXCLUDES = new HashSet<>();
83     private static final MultiMap<String, String> METADATA_INCLUDES = new MultiMap<>();
84     private static final MultiMap<String, String> METADATA_EXCLUDES = new MultiMap<>();
85     private static final Set<String> FILES = new HashSet<>();
86     private static final String FILENAME = "%s.config";
87     private static final String ROOT_DIR_ATTR = "ROOT_DIR";
88     private static final String SUITE_NAME_ATTR = "SUITE_NAME";
89     private static final String START_TIME_MS_ATTR = "START_TIME_MS";
90     private static final String ABI_32 = "armeabi-v7a";
91     private static final String ABI_64 = "arm64-v8a";
92     private static final String MODULE_NAME_A = "FooModuleA";
93     private static final String MODULE_NAME_B = "FooModuleB";
94     private static final String MODULE_NAME_C = "FooModuleC";
95     private static final String NON_EXISTS_MODULE_NAME = "NonExistModule";
96     private static final String ID_A_32 = AbiUtils.createId(ABI_32, MODULE_NAME_A);
97     private static final String ID_A_64 = AbiUtils.createId(ABI_64, MODULE_NAME_A);
98     private static final String ID_B_32 = AbiUtils.createId(ABI_32, MODULE_NAME_B);
99     private static final String ID_B_64 = AbiUtils.createId(ABI_64, MODULE_NAME_B);
100     private static final String ID_C_32 = AbiUtils.createId(ABI_32, MODULE_NAME_C);
101     private static final String ID_C_64 = AbiUtils.createId(ABI_64, MODULE_NAME_C);
102     private static final String TEST_ARG = TestStub.class.getName() + ":foo:bar";
103     private static final String MODULE_ARG = "%s:blah:foobar";
104     private static final String TEST_STUB = "TestStub"; // Trivial test stub
105     private static final String SHARDABLE_TEST_STUB = "ShardableTestStub"; // Shardable and IBuildReceiver
106     private static final String [] EXPECTED_MODULE_IDS = new String[] {
107         "arm64-v8a FooModuleB",
108         "arm64-v8a FooModuleC",
109         "armeabi-v7a FooModuleA",
110         "arm64-v8a FooModuleA",
111         "armeabi-v7a FooModuleC",
112         "armeabi-v7a FooModuleB"
113     };
114 
115     static {
116         SERIALS.add(SERIAL1);
117         SERIALS.add(SERIAL2);
118         SERIALS.add(SERIAL3);
ABIS.add(new Abi(ABI_32, "32"))119         ABIS.add(new Abi(ABI_32, "32"));
ABIS.add(new Abi(ABI_64, "64"))120         ABIS.add(new Abi(ABI_64, "64"));
121         DEVICE_TOKENS.add(String.format("%s:%s", SERIAL3, FOOBAR_TOKEN));
122         TEST_ARGS.add(TEST_ARG);
String.format(MODULE_ARG, MODULE_NAME_A)123         MODULE_ARGS.add(String.format(MODULE_ARG, MODULE_NAME_A));
String.format(MODULE_ARG, MODULE_NAME_B)124         MODULE_ARGS.add(String.format(MODULE_ARG, MODULE_NAME_B));
String.format(MODULE_ARG, MODULE_NAME_C)125         MODULE_ARGS.add(String.format(MODULE_ARG, MODULE_NAME_C));
String.format(FILENAME, MODULE_NAME_A)126         FILES.add(String.format(FILENAME, MODULE_NAME_A));
String.format(FILENAME, MODULE_NAME_B)127         FILES.add(String.format(FILENAME, MODULE_NAME_B));
String.format(FILENAME, MODULE_NAME_C)128         FILES.add(String.format(FILENAME, MODULE_NAME_C));
129     }
130     private ModuleRepo mRepo;
131     private File mTestsDir;
132     private File mRootDir;
133     private IBuildInfo mMockBuildInfo;
134 
135     @Override
setUp()136     public void setUp() throws Exception {
137         mTestsDir = setUpConfigs();
138         mRepo = new ModuleRepo();
139         mMockBuildInfo = EasyMock.createMock(IBuildInfo.class);
140         // Flesh out the result directory structure so ModuleRepo can write to the test runs file
141         mRootDir = FileUtil.createTempDir("root");
142         File subRootDir = new File(mRootDir, String.format("android-suite"));
143         File resultsDir = new File(subRootDir, "results");
144         File resultDir = new File(resultsDir, CompatibilityBuildHelper.getDirSuffix(0));
145         resultDir.mkdirs();
146 
147         Map<String, String> mockBuildInfoMap = new HashMap<String, String>();
148         mockBuildInfoMap.put(ROOT_DIR_ATTR, mRootDir.getAbsolutePath());
149         mockBuildInfoMap.put(SUITE_NAME_ATTR, "suite");
150         mockBuildInfoMap.put(START_TIME_MS_ATTR, Long.toString(0));
151         EasyMock.expect(mMockBuildInfo.getBuildAttributes()).andReturn(mockBuildInfoMap).anyTimes();
152         EasyMock.replay(mMockBuildInfo);
153     }
154 
setUpConfigs()155     private File setUpConfigs() throws IOException {
156         File testsDir = FileUtil.createTempDir("testcases");
157         createConfig(testsDir, MODULE_NAME_A, null);
158         createConfig(testsDir, MODULE_NAME_B, null);
159         createConfig(testsDir, MODULE_NAME_C, FOOBAR_TOKEN);
160         return testsDir;
161     }
162 
createConfig(File testsDir, String name, String token)163     private void createConfig(File testsDir, String name, String token) throws IOException {
164         createConfig(testsDir, name, token, TEST_STUB);
165     }
166 
createConfig(File testsDir, String name, String token, String moduleClass)167     private void createConfig(File testsDir, String name, String token, String moduleClass)
168             throws IOException {
169         File config = new File(testsDir, String.format(FILENAME, name));
170         if (!config.createNewFile()) {
171             throw new IOException(String.format("Failed to create '%s'", config.getAbsolutePath()));
172         }
173         String preparer = "";
174         if (token != null) {
175             preparer = String.format(TOKEN, token);
176         }
177         FileUtil.writeToFile(String.format(CONFIG, preparer, moduleClass, name), config);
178     }
179 
180     @Override
tearDown()181     public void tearDown() throws Exception {
182         FileUtil.recursiveDelete(mTestsDir);
183         tearDownConfigs(mTestsDir);
184         tearDownConfigs(mRootDir);
185     }
186 
tearDownConfigs(File testsDir)187     private void tearDownConfigs(File testsDir) {
188         FileUtil.recursiveDelete(testsDir);
189     }
190 
testInitialization()191     public void testInitialization() throws Exception {
192         mRepo.initialize(3, null, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
193                 EXCLUDES, METADATA_INCLUDES, METADATA_EXCLUDES, mMockBuildInfo);
194         assertTrue("Should be initialized", mRepo.isInitialized());
195         assertEquals("Wrong number of shards", 3, mRepo.getNumberOfShards());
196         Map<String, Set<String>> deviceTokens = mRepo.getDeviceTokens();
197         assertEquals("Wrong number of devices with tokens", 1, deviceTokens.size());
198         Set<String> tokens = deviceTokens.get(SERIAL3);
199         assertEquals("Wrong number of tokens", 1, tokens.size());
200         assertTrue("Unexpected device token", tokens.contains(FOOBAR_TOKEN));
201         assertEquals("Wrong number of modules", 4, mRepo.getNonTokenModules().size());
202         List<IModuleDef> tokenModules = mRepo.getTokenModules();
203         assertEquals("Wrong number of modules with tokens", 2, tokenModules.size());
204     }
205 
testGetModules()206     public void testGetModules() throws Exception {
207         mRepo.initialize(1, null, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
208                 EXCLUDES, METADATA_INCLUDES, METADATA_EXCLUDES, mMockBuildInfo);
209         assertTrue("Should be initialized", mRepo.isInitialized());
210         assertEquals("Wrong number of tokens", 2, mRepo.getTokenModules().size());
211         assertEquals("Wrong number of tokens", 4, mRepo.getNonTokenModules().size());
212     }
213 
214     /**
215      * Test sharding with 2 shards of the 4 non token modules.
216      */
testGetModulesSharded()217     public void testGetModulesSharded() throws Exception {
218         mRepo.initialize(2, null, mTestsDir, ABIS, new ArrayList<String>(), TEST_ARGS, MODULE_ARGS,
219                 INCLUDES, EXCLUDES, METADATA_INCLUDES, METADATA_EXCLUDES, mMockBuildInfo);
220         assertTrue("Should be initialized", mRepo.isInitialized());
221         assertEquals("Wrong number of tokens", 2, mRepo.getTokenModules().size());
222         assertEquals("Wrong number of tokens", 4, mRepo.getNonTokenModules().size());
223         List<IModuleDef> shard1 = mRepo.getModules(SERIAL1, 0);
224         assertEquals(2, shard1.size());
225         assertEquals("armeabi-v7a FooModuleA", shard1.get(0).getId());
226         assertEquals("arm64-v8a FooModuleA", shard1.get(1).getId());
227         List<IModuleDef> shard2 = mRepo.getModules(SERIAL2, 1);
228         // last shard gets the token modules too
229         assertEquals(4, shard2.size());
230         assertEquals("armeabi-v7a FooModuleB", shard2.get(0).getId());
231         assertEquals("arm64-v8a FooModuleB", shard2.get(1).getId());
232     }
233 
234     /**
235      * Test running with only token modules.
236      */
testGetModules_onlyTokenModules()237     public void testGetModules_onlyTokenModules() throws Exception {
238         Set<String> includes = new HashSet<>();
239         includes.add(MODULE_NAME_C);
240         mRepo.initialize(1, null, mTestsDir, ABIS, new ArrayList<String>(), TEST_ARGS, MODULE_ARGS,
241                 includes, EXCLUDES, METADATA_INCLUDES, METADATA_EXCLUDES, mMockBuildInfo);
242         assertTrue("Should be initialized", mRepo.isInitialized());
243         assertEquals("Wrong number of tokens", 2, mRepo.getTokenModules().size());
244         assertEquals("Wrong number of tokens", 0, mRepo.getNonTokenModules().size());
245         List<IModuleDef> modules = mRepo.getModules(SERIAL1, 0);
246         assertNotNull(modules);
247         assertEquals(2, modules.size());
248     }
249 
250     /**
251      * Test running with only token modules, with sharded local run, we specify a token module
252      * for each device, tests should go in the right place.
253      */
testGetModules_TokenModules_multiDevices()254     public void testGetModules_TokenModules_multiDevices() throws Exception {
255         createConfig(mTestsDir, "FooModuleD", "foobar2");
256         Set<String> includes = new HashSet<>();
257         includes.add(MODULE_NAME_C);
258         includes.add("FooModuleD");
259         List<String> tokens = new ArrayList<>();
260         tokens.add(String.format("%s:%s", SERIAL1, FOOBAR_TOKEN));
261         tokens.add(String.format("%s:%s", SERIAL2, "foobar2"));
262         mRepo.initialize(2, null, mTestsDir, ABIS, tokens, TEST_ARGS, MODULE_ARGS,
263                 includes, EXCLUDES, METADATA_INCLUDES, METADATA_EXCLUDES, mMockBuildInfo);
264         assertTrue("Should be initialized", mRepo.isInitialized());
265         assertEquals("Wrong number of tokens", 4, mRepo.getTokenModules().size());
266         assertEquals("Wrong number of tokens", 0, mRepo.getNonTokenModules().size());
267         List<IModuleDef> modules1 = mRepo.getModules(SERIAL1, 0);
268         assertNotNull(modules1);
269         assertEquals(2, modules1.size());
270         // Only module C tokens with serial 1.
271         assertTrue(modules1.get(0).getId().contains(MODULE_NAME_C));
272         assertTrue(modules1.get(1).getId().contains(MODULE_NAME_C));
273         List<IModuleDef> modules2 = mRepo.getModules(SERIAL2, 1);
274         assertNotNull(modules2);
275         assertEquals(2, modules2.size());
276         assertTrue(modules2.get(0).getId().contains("FooModuleD"));
277         assertTrue(modules2.get(1).getId().contains("FooModuleD"));
278     }
279 
280     /**
281      * Test sharding with 4 shards of the 6 non token modules + 2 token modules.
282      */
testGetModulesSharded_uneven()283     public void testGetModulesSharded_uneven() throws Exception {
284         createConfig(mTestsDir, "FooModuleD", null);
285         mRepo.initialize(4, null, mTestsDir, ABIS, new ArrayList<String>(), TEST_ARGS, MODULE_ARGS,
286                 INCLUDES, EXCLUDES, METADATA_INCLUDES, METADATA_EXCLUDES, mMockBuildInfo);
287         assertTrue("Should be initialized", mRepo.isInitialized());
288         assertEquals("Wrong number of tokens", 2, mRepo.getTokenModules().size());
289         assertEquals("Wrong number of tokens", 6, mRepo.getNonTokenModules().size());
290 
291         List<IModuleDef> shard1 = mRepo.getModules(SERIAL1, 0);
292         assertEquals(1, shard1.size());
293         assertEquals("armeabi-v7a FooModuleA", shard1.get(0).getId());
294 
295         List<IModuleDef> shard2 = mRepo.getModules(SERIAL2, 1);
296         assertEquals(1, shard2.size());
297         assertEquals("arm64-v8a FooModuleA", shard2.get(0).getId());
298 
299         List<IModuleDef> shard3 = mRepo.getModules(SERIAL3, 2);
300         assertEquals(2, shard3.size());
301         assertEquals("armeabi-v7a FooModuleB", shard3.get(0).getId());
302         assertEquals("arm64-v8a FooModuleB", shard3.get(1).getId());
303 
304         List<IModuleDef> shard4 = mRepo.getModules(SERIAL2, 3);
305         assertEquals(4, shard4.size());
306         assertEquals("armeabi-v7a FooModuleC", shard4.get(0).getId());
307         assertEquals("arm64-v8a FooModuleC", shard4.get(1).getId());
308         assertEquals("armeabi-v7a FooModuleD", shard4.get(2).getId());
309         assertEquals("arm64-v8a FooModuleD", shard4.get(3).getId());
310     }
311 
testConfigFilter()312     public void testConfigFilter() throws Exception {
313         File[] configFiles = mTestsDir.listFiles(new ConfigFilter());
314         assertEquals("Wrong number of config files found.", 3, configFiles.length);
315         for (File file : configFiles) {
316             assertTrue(String.format("Unrecognised file: %s", file.getAbsolutePath()),
317                     FILES.contains(file.getName()));
318         }
319     }
320 
testFiltering()321     public void testFiltering() throws Exception {
322         Set<String> includeFilters = new HashSet<>();
323         includeFilters.add(MODULE_NAME_A);
324         Set<String> excludeFilters = new HashSet<>();
325         excludeFilters.add(ID_A_32);
326         excludeFilters.add(MODULE_NAME_B);
327         mRepo.initialize(1, null, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS,
328                 includeFilters, excludeFilters, METADATA_INCLUDES, METADATA_EXCLUDES,
329                 mMockBuildInfo);
330         List<IModuleDef> modules = mRepo.getModules(SERIAL1, 0);
331         assertEquals("Incorrect number of modules", 1, modules.size());
332         IModuleDef module = modules.get(0);
333         assertEquals("Incorrect ID", ID_A_64, module.getId());
334         checkArgs(module);
335     }
336 
337     /**
338      * Test that excluded module shouldn't be loaded.
339      */
testInitialization_ExcludeModule_SkipLoadingConfig()340     public void testInitialization_ExcludeModule_SkipLoadingConfig() {
341         try {
342             Set<String> excludeFilters = new HashSet<String>();
343             excludeFilters.add(NON_EXISTS_MODULE_NAME);
344             mRepo.initialize(1, null, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS,
345                     MODULE_ARGS, Collections.emptySet(), excludeFilters,
346                     METADATA_INCLUDES, METADATA_EXCLUDES,
347                     mMockBuildInfo);
348         } catch (Exception e) {
349             fail("Initialization should not fail if non-existing module is excluded");
350         }
351     }
352 
353     /**
354      * Test that {@link ModuleRepo#getModules(String, int)} handles well all module being filtered.
355      */
testFiltering_empty()356     public void testFiltering_empty() throws Exception {
357         Set<String> includeFilters = new HashSet<>();
358         Set<String> excludeFilters = new HashSet<>();
359         excludeFilters.add(MODULE_NAME_A);
360         excludeFilters.add(MODULE_NAME_B);
361         excludeFilters.add(MODULE_NAME_C);
362         mRepo.initialize(1, null, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS,
363                 includeFilters, excludeFilters,
364                 METADATA_INCLUDES, METADATA_EXCLUDES, mMockBuildInfo);
365         List<IModuleDef> modules = mRepo.getModules(SERIAL1, 0);
366         assertEquals("Incorrect number of modules", 0, modules.size());
367     }
368 
testParsing()369     public void testParsing() throws Exception {
370         mRepo.initialize(1, null, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
371                 EXCLUDES, METADATA_INCLUDES, METADATA_EXCLUDES, mMockBuildInfo);
372         List<IModuleDef> modules = mRepo.getModules(SERIAL3, 0);
373         Set<String> idSet = new HashSet<>();
374         for (IModuleDef module : modules) {
375             idSet.add(module.getId());
376         }
377         assertEquals("Incorrect number of IDs", 6, idSet.size());
378         assertTrue("Missing ID_A_32", idSet.contains(ID_A_32));
379         assertTrue("Missing ID_A_64", idSet.contains(ID_A_64));
380         assertTrue("Missing ID_B_32", idSet.contains(ID_B_32));
381         assertTrue("Missing ID_B_64", idSet.contains(ID_B_64));
382         assertTrue("Missing ID_C_32", idSet.contains(ID_C_32));
383         assertTrue("Missing ID_C_64", idSet.contains(ID_C_64));
384         for (IModuleDef module : modules) {
385             checkArgs(module);
386         }
387     }
388 
checkArgs(IModuleDef module)389     private void checkArgs(IModuleDef module) {
390         IRemoteTest test = module.getTest();
391         assertTrue("Incorrect test type", test instanceof TestStub);
392         TestStub stub = (TestStub) test;
393         assertEquals("Incorrect test arg", "bar", stub.mFoo);
394         assertEquals("Incorrect module arg", "foobar", stub.mBlah);
395     }
396 
testGetModuleIds()397     public void testGetModuleIds() {
398         mRepo.initialize(3, null, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, INCLUDES,
399                 EXCLUDES, METADATA_INCLUDES, METADATA_EXCLUDES, mMockBuildInfo);
400         assertTrue("Should be initialized", mRepo.isInitialized());
401 
402         assertArrayEquals(EXPECTED_MODULE_IDS, mRepo.getModuleIds());
403     }
404 
assertArrayEquals(Object[] expected, Object[] actual)405     private void assertArrayEquals(Object[] expected, Object[] actual) {
406         assertEquals(Arrays.asList(expected), Arrays.asList(actual));
407     }
408 
409     /**
410      * Test class to provide runtimeHint.
411      */
412     private class TestRuntime implements IRemoteTest, IRuntimeHintProvider, IAbiReceiver,
413             ITestCollector, ITestFilterReceiver {
414         public long runtimeHint = 0l;
415         @Override
getRuntimeHint()416         public long getRuntimeHint() {
417             return runtimeHint;
418         }
419         // ignore all the other calls
420         @Override
run(ITestInvocationListener arg0)421         public void run(ITestInvocationListener arg0) throws DeviceNotAvailableException {}
422         @Override
addAllExcludeFilters(Set<String> arg0)423         public void addAllExcludeFilters(Set<String> arg0) {}
424         @Override
addAllIncludeFilters(Set<String> arg0)425         public void addAllIncludeFilters(Set<String> arg0) {}
426         @Override
addExcludeFilter(String arg0)427         public void addExcludeFilter(String arg0) {}
428         @Override
addIncludeFilter(String arg0)429         public void addIncludeFilter(String arg0) {}
430         @Override
setCollectTestsOnly(boolean arg0)431         public void setCollectTestsOnly(boolean arg0) {}
432         @Override
setAbi(IAbi arg0)433         public void setAbi(IAbi arg0) {}
434         @Override
getAbi()435         public IAbi getAbi() {return null;}
436         @Override
getIncludeFilters()437         public Set<String> getIncludeFilters() {
438             return null;
439         }
440         @Override
getExcludeFilters()441         public Set<String> getExcludeFilters() {
442             return null;
443         }
444         @Override
clearIncludeFilters()445         public void clearIncludeFilters() {}
446         @Override
clearExcludeFilters()447         public void clearExcludeFilters() {}
448     }
449 
450     /**
451      * Balance the load of runtime of the modules for the same runtimehint everywhere.
452      */
testGetshard_allSameRuntime()453     public void testGetshard_allSameRuntime() throws Exception {
454         List<IModuleDef> testList = new ArrayList<>();
455         TestRuntime test1 = new TestRuntime();
456         test1.runtimeHint = 100l;
457         IModuleDef mod1 = new ModuleDef("test1", new Abi("arm", "32"), test1,
458                 new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
459         testList.add(mod1);
460         TestRuntime test2 = new TestRuntime();
461         test2.runtimeHint = 100l;
462         IModuleDef mod2 = new ModuleDef("test2", new Abi("arm", "32"), test2,
463                 new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
464         testList.add(mod2);
465         TestRuntime test3 = new TestRuntime();
466         test3.runtimeHint = 100l;
467         IModuleDef mod3 = new ModuleDef("test3", new Abi("arm", "32"), test3,
468                 new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
469         testList.add(mod3);
470         TestRuntime test4 = new TestRuntime();
471         test4.runtimeHint = 100l;
472         IModuleDef mod4 = new ModuleDef("test4", new Abi("arm", "32"), test4,
473                 new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
474         testList.add(mod4);
475         // if we don't shard everything is in one shard.
476         List<IModuleDef> res = mRepo.getShard(testList, 0, 1);
477         assertEquals(4, res.size());
478         res = mRepo.getShard(testList, 0, 2);
479         assertEquals(2, res.size());
480         assertEquals(mod1, res.get(0));
481         assertEquals(mod2, res.get(1));
482         res = mRepo.getShard(testList, 1, 2);
483         assertEquals(2, res.size());
484         assertEquals(mod3, res.get(0));
485         assertEquals(mod4, res.get(1));
486     }
487 
488     /**
489      * When reaching splitting time, we need to ensure that even after best effort, if we cannot
490      * split into the requested number of shardIndex, we simply return null to report an empty
491      * shard.
492      */
testGetShard_cannotSplitMore()493     public void testGetShard_cannotSplitMore() {
494         List<IModuleDef> testList = new ArrayList<>();
495         TestRuntime test1 = new TestRuntime();
496         test1.runtimeHint = 100l;
497         IModuleDef mod1 = new ModuleDef("test1", new Abi("arm", "32"), test1,
498                 new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
499         testList.add(mod1);
500         List<IModuleDef> res = mRepo.getShard(testList, 1, 2);
501         assertNull(res);
502     }
503 
504     /**
505      * When there are no metadata based filters specified, config should be included
506      * @throws Exception
507      */
testMetadataFilter_emptyFilters()508     public void testMetadataFilter_emptyFilters() throws Exception {
509         IConfiguration config = new Configuration("foo", "bar");
510         assertTrue("config not included when metadata filters are empty",
511                 mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, METADATA_EXCLUDES));
512     }
513 
514     /**
515      * When inclusion filter is specified, config matching the filter is included
516      * @throws Exception
517      */
testMetadataFilter_matchInclude()518     public void testMetadataFilter_matchInclude() throws Exception {
519         IConfiguration config = new Configuration("foo", "bar");
520         ConfigurationDescriptor desc = config.getConfigurationDescription();
521         MultiMap<String, String> metadata = new MultiMap<>();
522         metadata.put("component", "foo");
523         desc.setMetaData(metadata);
524         MultiMap<String, String> includeFilter = new MultiMap<>();
525         includeFilter.put("component", "foo");
526         assertTrue("config not included with matching inclusion filter",
527                 mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
528     }
529 
530     /**
531      * When inclusion filter is specified, config not matching the filter is excluded
532      * @throws Exception
533      */
testMetadataFilter_noMatchInclude_mismatchValue()534     public void testMetadataFilter_noMatchInclude_mismatchValue() throws Exception {
535         IConfiguration config = new Configuration("foo", "bar");
536         ConfigurationDescriptor desc = config.getConfigurationDescription();
537         MultiMap<String, String> metadata = new MultiMap<>();
538         metadata.put("component", "foo");
539         desc.setMetaData(metadata);
540         MultiMap<String, String> includeFilter = new MultiMap<>();
541         includeFilter.put("component", "bar");
542         assertFalse("config not excluded with mismatching inclusion filter",
543                 mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
544     }
545 
546     /**
547      * When inclusion filter is specified, config not matching the filter is excluded
548      * @throws Exception
549      */
testMetadataFilter_noMatchInclude_mismatchKey()550     public void testMetadataFilter_noMatchInclude_mismatchKey() throws Exception {
551         IConfiguration config = new Configuration("foo", "bar");
552         ConfigurationDescriptor desc = config.getConfigurationDescription();
553         MultiMap<String, String> metadata = new MultiMap<>();
554         metadata.put("component", "foo");
555         desc.setMetaData(metadata);
556         MultiMap<String, String> includeFilter = new MultiMap<>();
557         includeFilter.put("group", "bar");
558         assertFalse("config not excluded with mismatching inclusion filter",
559                 mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
560     }
561 
562     /**
563      * When exclusion filter is specified, config matching the filter is excluded
564      * @throws Exception
565      */
testMetadataFilter_matchExclude()566     public void testMetadataFilter_matchExclude() throws Exception {
567         IConfiguration config = new Configuration("foo", "bar");
568         ConfigurationDescriptor desc = config.getConfigurationDescription();
569         MultiMap<String, String> metadata = new MultiMap<>();
570         metadata.put("component", "foo");
571         desc.setMetaData(metadata);
572         MultiMap<String, String> excludeFilter = new MultiMap<>();
573         excludeFilter.put("component", "foo");
574         assertFalse("config not excluded with matching exclusion filter",
575                 mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
576     }
577 
578     /**
579      * When exclusion filter is specified, config not matching the filter is included
580      * @throws Exception
581      */
testMetadataFilter_noMatchExclude_mismatchKey()582     public void testMetadataFilter_noMatchExclude_mismatchKey() throws Exception {
583         IConfiguration config = new Configuration("foo", "bar");
584         ConfigurationDescriptor desc = config.getConfigurationDescription();
585         MultiMap<String, String> metadata = new MultiMap<>();
586         metadata.put("component", "foo");
587         desc.setMetaData(metadata);
588         MultiMap<String, String> excludeFilter = new MultiMap<>();
589         excludeFilter.put("component", "bar");
590         assertTrue("config not included with mismatching exclusion filter",
591                 mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
592     }
593 
594     /**
595      * When exclusion filter is specified, config not matching the filter is included
596      * @throws Exception
597      */
testMetadataFilter_noMatchExclude_mismatchValue()598     public void testMetadataFilter_noMatchExclude_mismatchValue() throws Exception {
599         IConfiguration config = new Configuration("foo", "bar");
600         ConfigurationDescriptor desc = config.getConfigurationDescription();
601         MultiMap<String, String> metadata = new MultiMap<>();
602         metadata.put("component", "foo");
603         desc.setMetaData(metadata);
604         MultiMap<String, String> excludeFilter = new MultiMap<>();
605         excludeFilter.put("group", "bar");
606         assertTrue("config not included with mismatching exclusion filter",
607                 mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
608     }
609 
610     /**
611      * When inclusion filter is specified, config with one of the metadata field matching the filter
612      * is included
613      * @throws Exception
614      */
testMetadataFilter_matchInclude_multipleMetadataField()615     public void testMetadataFilter_matchInclude_multipleMetadataField() throws Exception {
616         IConfiguration config = new Configuration("foo", "bar");
617         ConfigurationDescriptor desc = config.getConfigurationDescription();
618         MultiMap<String, String> metadata = new MultiMap<>();
619         metadata.put("component", "foo");
620         metadata.put("component", "bar");
621         desc.setMetaData(metadata);
622         MultiMap<String, String> includeFilter = new MultiMap<>();
623         includeFilter.put("component", "foo");
624         assertTrue("config not included with matching inclusion filter",
625                 mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
626     }
627 
628     /**
629      * When exclusion filter is specified, config with one of the metadata field matching the filter
630      * is excluded
631      * @throws Exception
632      */
testMetadataFilter_matchExclude_multipleMetadataField()633     public void testMetadataFilter_matchExclude_multipleMetadataField() throws Exception {
634         IConfiguration config = new Configuration("foo", "bar");
635         ConfigurationDescriptor desc = config.getConfigurationDescription();
636         MultiMap<String, String> metadata = new MultiMap<>();
637         metadata.put("component", "foo");
638         metadata.put("component", "bar");
639         desc.setMetaData(metadata);
640         MultiMap<String, String> excludeFilter = new MultiMap<>();
641         excludeFilter.put("component", "foo");
642         assertFalse("config not excluded with matching exclusion filter",
643                 mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
644     }
645 
646     /**
647      * When inclusion filters are specified, config with metadata field matching one of the filter
648      * is included
649      * @throws Exception
650      */
testMetadataFilter_matchInclude_multipleFilters()651     public void testMetadataFilter_matchInclude_multipleFilters() throws Exception {
652         IConfiguration config = new Configuration("foo", "bar");
653         ConfigurationDescriptor desc = config.getConfigurationDescription();
654         MultiMap<String, String> metadata = new MultiMap<>();
655         metadata.put("component", "foo");
656         desc.setMetaData(metadata);
657         MultiMap<String, String> includeFilter = new MultiMap<>();
658         includeFilter.put("component", "foo");
659         includeFilter.put("component", "bar");
660         assertTrue("config not included with matching inclusion filter",
661                 mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
662     }
663 
664     /**
665      * When exclusion filters are specified, config with metadata field matching one of the filter
666      * is excluded
667      * @throws Exception
668      */
testMetadataFilter_matchExclude_multipleFilters()669     public void testMetadataFilter_matchExclude_multipleFilters() throws Exception {
670         IConfiguration config = new Configuration("foo", "bar");
671         ConfigurationDescriptor desc = config.getConfigurationDescription();
672         MultiMap<String, String> metadata = new MultiMap<>();
673         metadata.put("component", "foo");
674         desc.setMetaData(metadata);
675         MultiMap<String, String> excludeFilter = new MultiMap<>();
676         excludeFilter.put("component", "foo");
677         excludeFilter.put("component", "bar");
678         assertFalse("config not excluded with matching exclusion filter",
679                 mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
680     }
681 
682     /**
683      * When inclusion filters are specified, config with metadata field matching one of the filter
684      * is included
685      * @throws Exception
686      */
testMetadataFilter_matchInclude_multipleMetadataAndFilters()687     public void testMetadataFilter_matchInclude_multipleMetadataAndFilters() throws Exception {
688         IConfiguration config = new Configuration("foo", "bar");
689         ConfigurationDescriptor desc = config.getConfigurationDescription();
690         MultiMap<String, String> metadata = new MultiMap<>();
691         metadata.put("component", "foo1");
692         metadata.put("group", "bar1");
693         desc.setMetaData(metadata);
694         MultiMap<String, String> includeFilter = new MultiMap<>();
695         includeFilter.put("component", "foo1");
696         includeFilter.put("group", "bar2");
697         assertTrue("config not included with matching inclusion filter",
698                 mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
699     }
700 
701     /**
702      * When exclusion filters are specified, config with metadata field matching one of the filter
703      * is excluded
704      * @throws Exception
705      */
testMetadataFilter_matchExclude_multipleMetadataAndFilters()706     public void testMetadataFilter_matchExclude_multipleMetadataAndFilters() throws Exception {
707         IConfiguration config = new Configuration("foo", "bar");
708         ConfigurationDescriptor desc = config.getConfigurationDescription();
709         MultiMap<String, String> metadata = new MultiMap<>();
710         metadata.put("component", "foo1");
711         metadata.put("group", "bar1");
712         desc.setMetaData(metadata);
713         MultiMap<String, String> excludeFilter = new MultiMap<>();
714         excludeFilter.put("component", "foo1");
715         excludeFilter.put("group", "bar2");
716         assertFalse("config not excluded with matching exclusion filter",
717                 mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
718     }
719 
720     /**
721      * When inclusion and exclusion filters are both specified, config can pass through the filters
722      * as expected.
723      * @throws Exception
724      */
testMetadataFilter_includeAndExclude()725     public void testMetadataFilter_includeAndExclude() throws Exception {
726         IConfiguration config = new Configuration("foo", "bar");
727         ConfigurationDescriptor desc = config.getConfigurationDescription();
728         MultiMap<String, String> metadata = new MultiMap<>();
729         metadata.put("component", "foo");
730         metadata.put("group", "bar1");
731         desc.setMetaData(metadata);
732         MultiMap<String, String> includeFilter = new MultiMap<>();
733         includeFilter.put("component", "foo");
734         MultiMap<String, String> excludeFilter = new MultiMap<>();
735         excludeFilter.put("group", "bar2");
736         assertTrue("config not included with matching inclusion and mismatching exclusion filters",
737                 mRepo.filterByConfigMetadata(config, includeFilter, excludeFilter));
738     }
739 
740     /**
741      * When inclusion and exclusion filters are both specified, config be excluded as specified
742      * @throws Exception
743      */
testMetadataFilter_includeThenExclude()744     public void testMetadataFilter_includeThenExclude() throws Exception {
745         IConfiguration config = new Configuration("foo", "bar");
746         ConfigurationDescriptor desc = config.getConfigurationDescription();
747         MultiMap<String, String> metadata = new MultiMap<>();
748         metadata.put("component", "foo");
749         metadata.put("group", "bar");
750         desc.setMetaData(metadata);
751         MultiMap<String, String> includeFilter = new MultiMap<>();
752         includeFilter.put("component", "foo");
753         MultiMap<String, String> excludeFilter = new MultiMap<>();
754         excludeFilter.put("group", "bar");
755         assertFalse("config not excluded with matching inclusion and exclusion filters",
756                 mRepo.filterByConfigMetadata(config, includeFilter, excludeFilter));
757     }
758 
759     public static class TestInject implements IRemoteTest {
760         @Option(name = "simple-string")
761         public String test = null;
762         @Option(name = "list-string")
763         public List<String> testList = new ArrayList<>();
764         @Option(name = "map-string")
765         public Map<String, String> testMap = new HashMap<>();
766 
767         @Override
run(ITestInvocationListener listener)768         public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
769         }
770     }
771 
772     /**
773      * Test that the different format for module-arg and test-arg can properly be passed to the
774      * configuration.
775      */
testInjectConfig()776     public void testInjectConfig() throws Exception {
777         IConfiguration config = new Configuration("foo", "bar");
778         TestInject checker = new TestInject();
779         config.setTest(checker);
780         Map<String, List<String>> optionMap = new HashMap<String, List<String>>();
781         List<String> option1 = new ArrayList<>();
782         option1.add("value1");
783         optionMap.put("simple-string", option1);
784 
785         List<String> option2 = new ArrayList<>();
786         option2.add("value2");
787         option2.add("value3");
788         option2.add("set-option:moreoption");
789         optionMap.put("list-string", option2);
790 
791         List<String> option3 = new ArrayList<>();
792         option3.add("set-option:=moreoption");
793         optionMap.put("map-string", option3);
794 
795         mRepo.injectOptionsToConfig(optionMap, config);
796 
797         assertEquals("value1", checker.test);
798         assertEquals(option2, checker.testList);
799         Map<String, String> resMap = new HashMap<>();
800         resMap.put("set-option", "moreoption");
801         assertEquals(resMap, checker.testMap);
802     }
803 }
804