1 /*
2  * Copyright (C) 2017 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.compatibility.common.tradefed.presubmit;
17 
18 import static org.junit.Assert.assertEquals;
19 
20 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
21 import com.android.compatibility.common.tradefed.result.ResultReporter;
22 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
23 import com.android.compatibility.common.tradefed.testtype.IModuleDef;
24 import com.android.compatibility.common.util.IInvocationResult;
25 import com.android.compatibility.common.util.TestStatus;
26 import com.android.tradefed.build.IBuildInfo;
27 import com.android.tradefed.config.ConfigurationFactory;
28 import com.android.tradefed.config.OptionSetter;
29 import com.android.tradefed.device.DeviceNotAvailableException;
30 import com.android.tradefed.device.ITestDevice;
31 import com.android.tradefed.invoker.IInvocationContext;
32 import com.android.tradefed.invoker.InvocationContext;
33 import com.android.tradefed.invoker.ShardListener;
34 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
35 import com.android.tradefed.result.ITestInvocationListener;
36 import com.android.tradefed.result.ResultForwarder;
37 import com.android.tradefed.result.TestDescription;
38 import com.android.tradefed.testtype.IBuildReceiver;
39 import com.android.tradefed.testtype.IDeviceTest;
40 import com.android.tradefed.testtype.IInvocationContextReceiver;
41 import com.android.tradefed.testtype.IRemoteTest;
42 import com.android.tradefed.util.AbiUtils;
43 import com.android.tradefed.util.FileUtil;
44 
45 import org.easymock.EasyMock;
46 import org.junit.After;
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.junit.runners.JUnit4;
51 
52 import java.io.File;
53 import java.io.IOException;
54 import java.util.ArrayList;
55 import java.util.Collections;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Set;
61 
62 /**
63  * Integration tests between {@link CompatibilityTest} and {@link ResultReporter} to ensure proper
64  * flow of run and results.
65  */
66 @RunWith(JUnit4.class)
67 public class IntegrationTest {
68 
69     private static final String CONFIG =
70             "<configuration description=\"Auto Generated File\">\n" +
71             "<option name=\"config-descriptor:metadata\" key=\"component\" value=\"%s\" />\n" +
72             "<test class=\"com.android.compatibility.common.tradefed.testtype.%s\">\n" +
73             "    <option name=\"report-test\" value=\"%s\" />\n" +
74             "    <option name=\"run-complete\" value=\"%s\" />\n" +
75             "    <option name=\"test-fail\" value=\"%s\" />\n" +
76             "    <option name=\"internal-retry\" value=\"%s\" />\n" +
77             "</test>\n" +
78             "</configuration>";
79     private static final String FILENAME = "%s.config";
80     private static final String TEST_STUB = "TestStub"; // Test stub
81     private static final String SIMPLE_TEST_STUB = "SimpleTestStub"; // Simple test stub
82     private static final String TEST_STUB_SHARDABLE = "TestStubShardable";
83     private static final String COMMAND_LINE = "run cts";
84 
85     private CompatibilityTest mTest;
86     private ResultReporter mReporter;
87     private ITestDevice mMockDevice;
88     private IBuildInfo mMockBuildInfo;
89     private IInvocationContext mContext;
90 
91     private File mRootDir;
92     private File mAndroidFolder;
93     private File mTestDir;
94     private Map<String, String> mAttributes;
95 
96     @Before
setUp()97     public void setUp() throws IOException {
98         mAttributes = new HashMap<>();
99         mTest = new CompatibilityTest() {
100             @Override
101             protected Set<String> getAbisForBuildTargetArch() {
102                 Set<String> abis = new HashSet<>();
103                 abis.add("arm64-v8a");
104                 abis.add("armeabi-v7a");
105                 return abis;
106             }
107         };
108         mReporter = new ResultReporter();
109         mMockDevice = EasyMock.createMock(ITestDevice.class);
110         mMockBuildInfo = EasyMock.createMock(IBuildInfo.class);
111         mTest.setBuild(mMockBuildInfo);
112         mTest.setDevice(mMockDevice);
113         mRootDir = FileUtil.createTempDir("fake-cts-root-dir");
114         mAndroidFolder = FileUtil.createTempDir("android-", mRootDir);
115         mTestDir = new File(mAndroidFolder, "testcases");
116         mTestDir.mkdirs();
117         String suiteName = mAndroidFolder.getName().split("-")[1];
118         // Create fake build attributes
119         mAttributes.put(CompatibilityBuildHelper.ROOT_DIR, mRootDir.getAbsolutePath());
120         mAttributes.put(CompatibilityBuildHelper.SUITE_NAME, suiteName);
121         mAttributes.put(CompatibilityBuildHelper.START_TIME_MS, "0");
122         mAttributes.put(CompatibilityBuildHelper.SUITE_VERSION, "10");
123         mAttributes.put(CompatibilityBuildHelper.SUITE_PLAN, "cts");
124         mAttributes.put(CompatibilityBuildHelper.SUITE_BUILD, "good-build");
125         mAttributes.put(CompatibilityBuildHelper.COMMAND_LINE_ARGS, COMMAND_LINE);
126 
127         // these attributes seems necessary for re-run, not for run
128         mAttributes.put("cts:build_fingerprint", "fingerprint");
129         mAttributes.put("cts:build_product", "product");
130         mAttributes.put("cts:build_id", "bid");
131 
132         EasyMock.expect(mMockBuildInfo.getBuildAttributes()).andStubReturn(mAttributes);
133 
134         EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn("SERIAL");
135         EasyMock.expect(mMockBuildInfo.getDeviceSerial()).andStubReturn("SERIAL");
136 
137         EasyMock.expect(mMockBuildInfo.getFiles()).andStubReturn(Collections.emptyList());
138 
139         mContext = new InvocationContext();
140         mContext.addAllocatedDevice("default", mMockDevice);
141         mContext.addDeviceBuildInfo("default", mMockBuildInfo);
142         mTest.setInvocationContext(mContext);
143     }
144 
145     @After
tearDown()146     public void tearDown() {
147         FileUtil.recursiveDelete(mRootDir);
148     }
149 
150     /**
151      * Create a CTS configuration with a fake tests to exercise all cases.
152      *
153      * @param testsDir The testcases/ dir where to put the module
154      * @param name the name of the module.
155      * @param moduleClass the fake test class to use.
156      * @param reportTest True if the test report some tests
157      * @param runComplete True if the test run is complete
158      * @param doesOneTestFail True if one of the test is going to fail
159      * @param internalRetry True if the test will retry the module itself once
160      */
createConfig(File testsDir, String name, String moduleClass, boolean reportTest, boolean runComplete, boolean doesOneTestFail, boolean internalRetry)161     private void createConfig(File testsDir, String name, String moduleClass, boolean reportTest,
162             boolean runComplete, boolean doesOneTestFail, boolean internalRetry)
163                     throws IOException {
164         createConfig(testsDir, name, moduleClass, reportTest, runComplete, doesOneTestFail,
165                 internalRetry, "foo");
166 
167     }
168 
169     /**
170      * Create a CTS configuration with a fake tests to exercise all cases.
171      *
172      * @param testsDir The testcases/ dir where to put the module
173      * @param name the name of the module.
174      * @param moduleClass the fake test class to use.
175      * @param reportTest True if the test report some tests
176      * @param runComplete True if the test run is complete
177      * @param doesOneTestFail True if one of the test is going to fail
178      * @param internalRetry True if the test will retry the module itself once
179      * @param component the platform component name that the module can be categorized under
180      */
createConfig(File testsDir, String name, String moduleClass, boolean reportTest, boolean runComplete, boolean doesOneTestFail, boolean internalRetry, String component)181     private void createConfig(File testsDir, String name, String moduleClass, boolean reportTest,
182             boolean runComplete, boolean doesOneTestFail, boolean internalRetry, String component)
183                     throws IOException {
184         File config = new File(testsDir, String.format(FILENAME, name));
185         FileUtil.deleteFile(config);
186         if (!config.createNewFile()) {
187             throw new IOException(String.format("Failed to create '%s'", config.getAbsolutePath()));
188         }
189 
190         FileUtil.writeToFile(String.format(CONFIG, component, moduleClass, reportTest, runComplete,
191                 doesOneTestFail, internalRetry), config);
192     }
193 
194     /**
195      * Simple tests running in one module that should be marked complete.
196      */
197     @Test
testSingleModuleRun()198     public void testSingleModuleRun() throws Exception {
199         final String moduleName = "module_run";
200         final String mAbi = "arm64-v8a";
201         createConfig(mTestDir, moduleName, TEST_STUB, true, true, true, false);
202         EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi);
203 
204         mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS),
205                 EasyMock.eq(AbiUtils.createId(mAbi, moduleName)));
206         EasyMock.expectLastCall();
207 
208         EasyMock.replay(mMockDevice, mMockBuildInfo);
209         mReporter.invocationStarted(mContext);
210         mTest.run(mReporter);
211         mReporter.invocationEnded(500);
212         EasyMock.verify(mMockDevice, mMockBuildInfo);
213         IInvocationResult result = mReporter.getResult();
214         assertEquals(2, result.countResults(TestStatus.PASS));
215         assertEquals(1, result.countResults(TestStatus.FAIL));
216         assertEquals(1, result.getModules().size());
217         assertEquals(1, result.getModuleCompleteCount());
218     }
219 
220     /**
221      * Verify that result reporters test run ended callback can receive component name as configured
222      * in module config metadata field.
223      */
224     @Test
testSingleModuleRun_checkMetadata()225     public void testSingleModuleRun_checkMetadata() throws Exception {
226         final String moduleName = "AwsomeModule";
227         final String mAbi = "arm64-v8a";
228         final String component = "CriticalComponent";
229         final List<String> receivedComponentsTestEnded = new ArrayList<>();
230         final List<String> receivedModuleNameTestEnded = new ArrayList<>();
231         final List<String> receivedAbiTestEnded = new ArrayList<>();
232         final List<String> receivedComponentsTestRunEnded = new ArrayList<>();
233         final List<String> receivedModuleNameTestRunEnded = new ArrayList<>();
234         final List<String> receivedAbiTestRunEnded = new ArrayList<>();
235         createConfig(mTestDir, moduleName, SIMPLE_TEST_STUB, true, true, true, false, component);
236         EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi);
237 
238         mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS),
239                 EasyMock.eq(AbiUtils.createId(mAbi, moduleName)));
240         EasyMock.expectLastCall();
241 
242         EasyMock.replay(mMockDevice, mMockBuildInfo);
243         ITestInvocationListener myListener = new ITestInvocationListener() {
244             private IInvocationContext myContext;
245             @Override
246             public void invocationStarted(IInvocationContext context) {
247                 myContext = context;
248             }
249             @Override
250             public void testRunEnded(long elapsedTimeMillis, HashMap<String, Metric> runMetrics) {
251                 receivedComponentsTestRunEnded.addAll(myContext.getModuleInvocationContext()
252                         .getConfigurationDescriptor().getMetaData("component"));
253                 receivedModuleNameTestRunEnded.addAll(myContext.getModuleInvocationContext()
254                         .getAttributes().get(IModuleDef.MODULE_NAME));
255                 receivedAbiTestRunEnded.addAll(myContext.getModuleInvocationContext()
256                         .getAttributes().get(IModuleDef.MODULE_ABI));
257             }
258             @Override
259             public void testEnded(TestDescription test, long endTime,
260                     HashMap<String, Metric> testMetrics) {
261                 receivedComponentsTestEnded.addAll(myContext.getModuleInvocationContext()
262                         .getConfigurationDescriptor().getMetaData("component"));
263                 receivedModuleNameTestEnded.addAll(myContext.getModuleInvocationContext()
264                         .getAttributes().get(IModuleDef.MODULE_NAME));
265                 receivedAbiTestEnded.addAll(myContext.getModuleInvocationContext()
266                         .getAttributes().get(IModuleDef.MODULE_ABI));
267             }
268         };
269         myListener.invocationStarted(mContext);
270         mTest.run(myListener);
271         myListener.invocationEnded(500);
272         EasyMock.verify(mMockDevice, mMockBuildInfo);
273         // verify metadata was retrieved during testRunEnded callbacks
274         assertEquals("[testRunEnded] wrong number of metadata collected",
275                 1, receivedComponentsTestRunEnded.size());
276         assertEquals("[testRunEnded] wrong component metadata field received",
277                 component, receivedComponentsTestRunEnded.get(0));
278         assertEquals("[testRunEnded] wrong number of module name collected",
279                 1, receivedModuleNameTestRunEnded.size());
280         assertEquals(moduleName, receivedModuleNameTestRunEnded.get(0));
281         assertEquals("[testEnded] wrong number of module abi collected",
282                 1, receivedAbiTestRunEnded.size());
283         assertEquals(mAbi, receivedAbiTestRunEnded.get(0));
284         // verify metadata was retrieved during testEnded callbacks
285         assertEquals("[testEnded] wrong number of metadata collected",
286                 1, receivedComponentsTestEnded.size());
287         assertEquals("[testEnded] wrong component metadata field received",
288                 component, receivedComponentsTestEnded.get(0));
289         assertEquals("[testEnded] wrong number of module name collected",
290                 1, receivedModuleNameTestEnded.size());
291         assertEquals(moduleName, receivedModuleNameTestEnded.get(0));
292         assertEquals("[testEnded] wrong number of module abi collected",
293                 1, receivedAbiTestEnded.size());
294         assertEquals(mAbi, receivedAbiTestEnded.get(0));
295     }
296 
297     /**
298      * Simple tests running in one module that run some tests but not all of them.
299      */
300     @Test
testSingleModuleRun_incomplete()301     public void testSingleModuleRun_incomplete() throws Exception {
302         final String moduleName = "module_run_incomplete";
303         final String mAbi = "arm64-v8a";
304         createConfig(mTestDir, moduleName, TEST_STUB, true, false, true, false);
305         EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi);
306 
307         mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS),
308                 EasyMock.eq(AbiUtils.createId(mAbi, moduleName)));
309         EasyMock.expectLastCall();
310 
311         EasyMock.replay(mMockDevice, mMockBuildInfo);
312         mReporter.invocationStarted(mContext);
313         mTest.run(mReporter);
314         mReporter.invocationEnded(500);
315         EasyMock.verify(mMockDevice, mMockBuildInfo);
316         IInvocationResult result = mReporter.getResult();
317         assertEquals(1, result.countResults(TestStatus.PASS));
318         assertEquals(1, result.countResults(TestStatus.FAIL));
319         // Module should not be seen as complete.
320         assertEquals(1, result.getModules().size());
321         assertEquals(0, result.getModuleCompleteCount());
322     }
323 
324     /**
325      * Simple tests running in one module that should be marked complete since it runs all its
326      * tests after an internal retry (like InstrumentationTest).
327      * FIXME: Fix the expectation of this test
328      */
329     @Test
testSingleModuleRun_completeAfterInternalRetry()330     public void testSingleModuleRun_completeAfterInternalRetry() throws Exception {
331         final String moduleName = "module_completeAfterRetry";
332         final String mAbi = "arm64-v8a";
333         createConfig(mTestDir, moduleName, TEST_STUB, true, true, true, true);
334         EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi);
335 
336         mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS),
337                 EasyMock.eq(AbiUtils.createId(mAbi, moduleName)));
338         EasyMock.expectLastCall();
339 
340         EasyMock.replay(mMockDevice, mMockBuildInfo);
341         mReporter.invocationStarted(mContext);
342         mTest.run(mReporter);
343         mReporter.invocationEnded(500);
344         EasyMock.verify(mMockDevice, mMockBuildInfo);
345         IInvocationResult result = mReporter.getResult();
346         // FIXME: All tests should be marked as executed and not aggregating the count.
347         assertEquals(2, result.countResults(TestStatus.PASS));
348         assertEquals(1, result.countResults(TestStatus.FAIL));
349         // FIXME: Module should be complete since all its test have run.
350         assertEquals(1, result.getModules().size());
351         assertEquals(0, result.getModuleCompleteCount());
352     }
353 
354     /**
355      * Simple tests running in one module that run some tests but not all of them, then we
356      * attempt to run it again and they still didn't run.
357      * FIXME: This test expectation needs to be fixed
358      */
359     @Test
testSingleModuleRun_incomplete_rerun_incomplete()360     public void testSingleModuleRun_incomplete_rerun_incomplete() throws Exception {
361         final String moduleName = "module_incomplete_rerun";
362         final String mAbi = "arm64-v8a";
363         createConfig(mTestDir, moduleName, TEST_STUB, true, false, true, false);
364         EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi);
365 
366         mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS),
367                 EasyMock.eq(AbiUtils.createId(mAbi, moduleName)));
368         EasyMock.expectLastCall();
369 
370         // extra calls for retry
371         EasyMock.expect(mMockDevice.getProperty("ro.build.fingerprint")).andReturn("fingerprint");
372         EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi);
373         mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS),
374                 EasyMock.eq(AbiUtils.createId(mAbi, moduleName)));
375         EasyMock.expectLastCall();
376         mMockBuildInfo.addBuildAttribute(EasyMock.eq("retry_command_line_args"),
377                 EasyMock.eq(COMMAND_LINE));
378         EasyMock.expectLastCall();
379 
380         EasyMock.replay(mMockDevice, mMockBuildInfo);
381         mReporter.invocationStarted(mContext);
382         mTest.run(mReporter);
383         mReporter.invocationEnded(500);
384 
385         IInvocationResult result = mReporter.getResult();
386         assertEquals(1, result.countResults(TestStatus.PASS));
387         assertEquals(1, result.countResults(TestStatus.FAIL));
388         // Module should not be seen as complete.
389         assertEquals(0, result.getModuleCompleteCount());
390 
391         // We re-run it
392         mReporter = new ResultReporter();
393         mTest = new CompatibilityTest() {
394             @Override
395             protected Set<String> getAbisForBuildTargetArch() {
396                 Set<String> abis = new HashSet<>();
397                 abis.add("arm64-v8a");
398                 return abis;
399             }
400         };
401         mTest.setDevice(mMockDevice);
402         mTest.setBuild(mMockBuildInfo);
403         mTest.setInvocationContext(mContext);
404         OptionSetter setter = new OptionSetter(mTest, mReporter);
405         setter.setOptionValue("retry", "0");
406 
407         mReporter.invocationStarted(mContext);
408         mTest.run(mReporter);
409         mReporter.invocationEnded(500);
410         EasyMock.verify(mMockDevice, mMockBuildInfo);
411 
412         // Check retry results
413         result = mReporter.getResult();
414         // FIXME: We should only have 1 not_executed in the retry too. They should not aggregate
415         // from one run to another.
416         assertEquals(1, result.countResults(TestStatus.PASS));
417         assertEquals(1, result.countResults(TestStatus.FAIL));
418         // Module should not be seen as complete.
419         assertEquals(1, result.getModules().size());
420         assertEquals(0, result.getModuleCompleteCount());
421     }
422 
423     /**
424      * Simple tests running in one module that run some tests but not all of them, we then attempt
425      * to retry run them and this time the not_executed is executed.
426      */
427     @Test
testSingleModuleRun_incomplete_rerun_complete()428     public void testSingleModuleRun_incomplete_rerun_complete() throws Exception {
429         final String moduleName = "module_incom_rerun_complete";
430         final String mAbi = "arm64-v8a";
431         createConfig(mTestDir, moduleName, TEST_STUB, true, false, true, false);
432         EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi);
433 
434         mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS),
435                 EasyMock.eq(AbiUtils.createId(mAbi, moduleName)));
436         EasyMock.expectLastCall();
437 
438         // extra calls for retry
439         EasyMock.expect(mMockDevice.getProperty("ro.build.fingerprint")).andReturn("fingerprint");
440         EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi);
441         mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS),
442                 EasyMock.eq(AbiUtils.createId(mAbi, moduleName)));
443         EasyMock.expectLastCall();
444         mMockBuildInfo.addBuildAttribute(EasyMock.eq("retry_command_line_args"),
445                 EasyMock.eq(COMMAND_LINE));
446         EasyMock.expectLastCall();
447 
448         EasyMock.replay(mMockDevice, mMockBuildInfo);
449         mReporter.invocationStarted(mContext);
450         mTest.run(mReporter);
451         mReporter.invocationEnded(500);
452 
453         IInvocationResult result = mReporter.getResult();
454         assertEquals(1, result.countResults(TestStatus.PASS));
455         assertEquals(1, result.countResults(TestStatus.FAIL));
456         // Module should not be seen as complete.
457         assertEquals(0, result.getModuleCompleteCount());
458 
459         // We replace the config by one that runs all tests without failures.
460         createConfig(mTestDir, moduleName, TEST_STUB, true, true, false, false);
461         // Usually configs do not change during the same session so we clear the map to have
462         // the new version of the config.
463         ((ConfigurationFactory)ConfigurationFactory.getInstance()).clearMapConfig();
464 
465         // We re-run it
466         mReporter = new ResultReporter();
467         mTest = new CompatibilityTest() {
468             @Override
469             protected Set<String> getAbisForBuildTargetArch() {
470                 Set<String> abis = new HashSet<>();
471                 abis.add("arm64-v8a");
472                 return abis;
473             }
474         };
475         mTest.setDevice(mMockDevice);
476         mTest.setBuild(mMockBuildInfo);
477         mTest.setInvocationContext(mContext);
478         OptionSetter setter = new OptionSetter(mTest, mReporter);
479         setter.setOptionValue("retry", "0");
480 
481         mReporter.invocationStarted(mContext);
482         mTest.run(mReporter);
483         mReporter.invocationEnded(500);
484         EasyMock.verify(mMockDevice, mMockBuildInfo);
485 
486         // Check retry results
487         result = mReporter.getResult();
488         assertEquals(3, result.countResults(TestStatus.PASS));
489         assertEquals(0, result.countResults(TestStatus.FAIL));
490         // Module should be marked as complete after retry.
491         assertEquals(1, result.getModules().size());
492         assertEquals(1, result.getModuleCompleteCount());
493     }
494 
495     // ***** Case for sharding interaction *****
496 
497     /**
498      * Helper to create a shard listener with the original reporter as master.
499      */
getShardListener(ResultReporter masterReporter)500     private ITestInvocationListener getShardListener(ResultReporter masterReporter) {
501         List<ITestInvocationListener> shardListeners = new ArrayList<ITestInvocationListener>();
502         ShardListener origConfigListener = new ShardListener(masterReporter);
503         ResultReporter reporterClone = (ResultReporter) masterReporter.clone();
504         shardListeners.add(reporterClone);
505         shardListeners.add(origConfigListener);
506         ResultForwarder shard = new ResultForwarder(shardListeners);
507         return shard;
508     }
509 
510     /**
511      * Helper Thread to run the IShardableTest.
512      */
513     private class ShardThread extends Thread {
514         private IRemoteTest mShardTest;
515         private ResultReporter mMasterReporter;
516         private IBuildInfo mBuild;
517         private ITestDevice mDevice;
518         private IInvocationContext mShardContext;
519 
ShardThread(IRemoteTest test, ResultReporter masterReporter, IBuildInfo build, ITestDevice device, IInvocationContext context)520         public ShardThread(IRemoteTest test, ResultReporter masterReporter, IBuildInfo build,
521                 ITestDevice device, IInvocationContext context) {
522             mShardTest = test;
523             mMasterReporter = masterReporter;
524             mBuild = build;
525             mDevice = device;
526             mShardContext = context;
527         }
528 
529         @Override
run()530         public void run() {
531             ITestInvocationListener listener = getShardListener(mMasterReporter);
532             ((IBuildReceiver)mShardTest).setBuild(mBuild);
533             ((IDeviceTest)mShardTest).setDevice(mDevice);
534             ((IInvocationContextReceiver)mShardTest).setInvocationContext(mContext);
535             listener.invocationStarted(mShardContext);
536             try {
537                 mShardTest.run(listener);
538             } catch (DeviceNotAvailableException e) {
539                 throw new RuntimeException(e);
540             } finally {
541                 listener.invocationEnded(500);
542             }
543         }
544     }
545 }
546