| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.compatibility.common.tradefed.presubmit; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.android.compatibility.SuiteInfo; |
| import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; |
| import com.android.compatibility.common.tradefed.result.ResultReporter; |
| import com.android.compatibility.common.tradefed.testtype.CompatibilityTest; |
| import com.android.compatibility.common.tradefed.testtype.IModuleDef; |
| import com.android.compatibility.common.util.IInvocationResult; |
| import com.android.compatibility.common.util.TestStatus; |
| import com.android.ddmlib.testrunner.TestIdentifier; |
| import com.android.tradefed.build.IBuildInfo; |
| import com.android.tradefed.config.ConfigurationFactory; |
| import com.android.tradefed.config.OptionSetter; |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.device.ITestDevice; |
| import com.android.tradefed.invoker.IInvocationContext; |
| import com.android.tradefed.invoker.InvocationContext; |
| import com.android.tradefed.invoker.ShardListener; |
| import com.android.tradefed.result.ITestInvocationListener; |
| import com.android.tradefed.result.ResultForwarder; |
| import com.android.tradefed.testtype.IBuildReceiver; |
| import com.android.tradefed.testtype.IDeviceTest; |
| import com.android.tradefed.testtype.IInvocationContextReceiver; |
| import com.android.tradefed.testtype.IRemoteTest; |
| import com.android.tradefed.util.AbiUtils; |
| import com.android.tradefed.util.FileUtil; |
| |
| import org.easymock.EasyMock; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Integration tests between {@link CompatibilityTest} and {@link ResultReporter} to ensure proper |
| * flow of run and results. |
| */ |
| @RunWith(JUnit4.class) |
| public class IntegrationTest { |
| |
| private static final String CONFIG = |
| "<configuration description=\"Auto Generated File\">\n" + |
| "<option name=\"config-descriptor:metadata\" key=\"component\" value=\"%s\" />\n" + |
| "<test class=\"com.android.compatibility.common.tradefed.testtype.%s\">\n" + |
| " <option name=\"report-test\" value=\"%s\" />\n" + |
| " <option name=\"run-complete\" value=\"%s\" />\n" + |
| " <option name=\"test-fail\" value=\"%s\" />\n" + |
| " <option name=\"internal-retry\" value=\"%s\" />\n" + |
| "</test>\n" + |
| "</configuration>"; |
| private static final String FILENAME = "%s.config"; |
| private static final String TEST_STUB = "TestStub"; // Test stub |
| private static final String SIMPLE_TEST_STUB = "SimpleTestStub"; // Simple test stub |
| private static final String TEST_STUB_SHARDABLE = "TestStubShardable"; |
| private static final String COMMAND_LINE = "run cts"; |
| |
| private CompatibilityTest mTest; |
| private ResultReporter mReporter; |
| private ITestDevice mMockDevice; |
| private IBuildInfo mMockBuildInfo; |
| private IInvocationContext mContext; |
| |
| private File mRootDir; |
| private File mAndroidFolder; |
| private File mTestDir; |
| private Map<String, String> mAttributes; |
| |
| @Before |
| public void setUp() throws IOException { |
| mAttributes = new HashMap<>(); |
| mTest = new CompatibilityTest() { |
| @Override |
| protected Set<String> getAbisForBuildTargetArch() { |
| Set<String> abis = new HashSet<>(); |
| abis.add("arm64-v8a"); |
| abis.add("armeabi-v7a"); |
| return abis; |
| } |
| }; |
| mReporter = new ResultReporter(); |
| mMockDevice = EasyMock.createMock(ITestDevice.class); |
| mMockBuildInfo = EasyMock.createMock(IBuildInfo.class); |
| mTest.setBuild(mMockBuildInfo); |
| mTest.setDevice(mMockDevice); |
| mRootDir = FileUtil.createTempDir("fake-cts-root-dir"); |
| mAndroidFolder = FileUtil.createTempDir("android-", mRootDir); |
| mTestDir = new File(mAndroidFolder, "testcases"); |
| mTestDir.mkdirs(); |
| String suiteName = mAndroidFolder.getName().split("-")[1]; |
| // Create fake build attributes |
| mAttributes.put(CompatibilityBuildHelper.ROOT_DIR, mRootDir.getAbsolutePath()); |
| mAttributes.put(CompatibilityBuildHelper.SUITE_NAME, suiteName); |
| mAttributes.put(CompatibilityBuildHelper.START_TIME_MS, "0"); |
| mAttributes.put(CompatibilityBuildHelper.SUITE_VERSION, "10"); |
| mAttributes.put(CompatibilityBuildHelper.SUITE_PLAN, "cts"); |
| mAttributes.put(CompatibilityBuildHelper.SUITE_BUILD, "good-build"); |
| mAttributes.put(CompatibilityBuildHelper.COMMAND_LINE_ARGS, COMMAND_LINE); |
| |
| // these attributes seems necessary for re-run, not for run |
| mAttributes.put("cts:build_fingerprint", "fingerprint"); |
| mAttributes.put("cts:build_product", "product"); |
| mAttributes.put("cts:build_id", "bid"); |
| |
| EasyMock.expect(mMockBuildInfo.getBuildAttributes()).andStubReturn(mAttributes); |
| |
| EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn("SERIAL"); |
| EasyMock.expect(mMockBuildInfo.getDeviceSerial()).andStubReturn("SERIAL"); |
| |
| mContext = new InvocationContext(); |
| mContext.addAllocatedDevice("default", mMockDevice); |
| mContext.addDeviceBuildInfo("default", mMockBuildInfo); |
| mTest.setInvocationContext(mContext); |
| } |
| |
| @After |
| public void tearDown() { |
| FileUtil.recursiveDelete(mRootDir); |
| } |
| |
| /** |
| * Create a CTS configuration with a fake tests to exercise all cases. |
| * |
| * @param testsDir The testcases/ dir where to put the module |
| * @param name the name of the module. |
| * @param moduleClass the fake test class to use. |
| * @param reportTest True if the test report some tests |
| * @param runComplete True if the test run is complete |
| * @param doesOneTestFail True if one of the test is going to fail |
| * @param internalRetry True if the test will retry the module itself once |
| */ |
| private void createConfig(File testsDir, String name, String moduleClass, boolean reportTest, |
| boolean runComplete, boolean doesOneTestFail, boolean internalRetry) |
| throws IOException { |
| createConfig(testsDir, name, moduleClass, reportTest, runComplete, doesOneTestFail, |
| internalRetry, "foo"); |
| |
| } |
| |
| /** |
| * Create a CTS configuration with a fake tests to exercise all cases. |
| * |
| * @param testsDir The testcases/ dir where to put the module |
| * @param name the name of the module. |
| * @param moduleClass the fake test class to use. |
| * @param reportTest True if the test report some tests |
| * @param runComplete True if the test run is complete |
| * @param doesOneTestFail True if one of the test is going to fail |
| * @param internalRetry True if the test will retry the module itself once |
| * @param component the platform component name that the module can be categorized under |
| */ |
| private void createConfig(File testsDir, String name, String moduleClass, boolean reportTest, |
| boolean runComplete, boolean doesOneTestFail, boolean internalRetry, String component) |
| throws IOException { |
| File config = new File(testsDir, String.format(FILENAME, name)); |
| FileUtil.deleteFile(config); |
| if (!config.createNewFile()) { |
| throw new IOException(String.format("Failed to create '%s'", config.getAbsolutePath())); |
| } |
| |
| FileUtil.writeToFile(String.format(CONFIG, component, moduleClass, reportTest, runComplete, |
| doesOneTestFail, internalRetry), config); |
| } |
| |
| /** |
| * Simple tests running in one module that should be marked complete. |
| */ |
| @Test |
| public void testSingleModuleRun() throws Exception { |
| final String moduleName = "module_run"; |
| final String mAbi = "arm64-v8a"; |
| createConfig(mTestDir, moduleName, TEST_STUB, true, true, true, false); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi); |
| |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.eq(AbiUtils.createId(mAbi, moduleName))); |
| EasyMock.expectLastCall(); |
| |
| EasyMock.replay(mMockDevice, mMockBuildInfo); |
| mReporter.invocationStarted(mContext); |
| mTest.run(mReporter); |
| mReporter.invocationEnded(500); |
| EasyMock.verify(mMockDevice, mMockBuildInfo); |
| IInvocationResult result = mReporter.getResult(); |
| assertEquals(2, result.countResults(TestStatus.PASS)); |
| assertEquals(1, result.countResults(TestStatus.FAIL)); |
| assertEquals(1, result.getModules().size()); |
| assertEquals(1, result.getModuleCompleteCount()); |
| } |
| |
| /** |
| * Verify that result reporters test run ended callback can receive component name as configured |
| * in module config metadata field. |
| */ |
| @Test |
| public void testSingleModuleRun_checkMetadata() throws Exception { |
| final String moduleName = "AwsomeModule"; |
| final String mAbi = "arm64-v8a"; |
| final String component = "CriticalComponent"; |
| final List<String> receivedComponentsTestEnded = new ArrayList<>(); |
| final List<String> receivedModuleNameTestEnded = new ArrayList<>(); |
| final List<String> receivedAbiTestEnded = new ArrayList<>(); |
| final List<String> receivedComponentsTestRunEnded = new ArrayList<>(); |
| final List<String> receivedModuleNameTestRunEnded = new ArrayList<>(); |
| final List<String> receivedAbiTestRunEnded = new ArrayList<>(); |
| createConfig(mTestDir, moduleName, SIMPLE_TEST_STUB, true, true, true, false, component); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi); |
| |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.eq(AbiUtils.createId(mAbi, moduleName))); |
| EasyMock.expectLastCall(); |
| |
| EasyMock.replay(mMockDevice, mMockBuildInfo); |
| ITestInvocationListener myListener = new ITestInvocationListener() { |
| private IInvocationContext myContext; |
| @Override |
| public void invocationStarted(IInvocationContext context) { |
| myContext = context; |
| } |
| @Override |
| public void testRunEnded(long elapsedTimeMillis, Map<String, String> runMetrics) { |
| receivedComponentsTestRunEnded.addAll(myContext.getModuleInvocationContext() |
| .getConfigurationDescriptor().getMetaData("component")); |
| receivedModuleNameTestRunEnded.addAll(myContext.getModuleInvocationContext() |
| .getAttributes().get(IModuleDef.MODULE_NAME)); |
| receivedAbiTestRunEnded.addAll(myContext.getModuleInvocationContext() |
| .getAttributes().get(IModuleDef.MODULE_ABI)); |
| } |
| @Override |
| public void testEnded(TestIdentifier test, long endTime, |
| Map<String, String> testMetrics) { |
| receivedComponentsTestEnded.addAll(myContext.getModuleInvocationContext() |
| .getConfigurationDescriptor().getMetaData("component")); |
| receivedModuleNameTestEnded.addAll(myContext.getModuleInvocationContext() |
| .getAttributes().get(IModuleDef.MODULE_NAME)); |
| receivedAbiTestEnded.addAll(myContext.getModuleInvocationContext() |
| .getAttributes().get(IModuleDef.MODULE_ABI)); |
| } |
| }; |
| myListener.invocationStarted(mContext); |
| mTest.run(myListener); |
| myListener.invocationEnded(500); |
| EasyMock.verify(mMockDevice, mMockBuildInfo); |
| // verify metadata was retrieved during testRunEnded callbacks |
| assertEquals("[testRunEnded] wrong number of metadata collected", |
| 1, receivedComponentsTestRunEnded.size()); |
| assertEquals("[testRunEnded] wrong component metadata field received", |
| component, receivedComponentsTestRunEnded.get(0)); |
| assertEquals("[testRunEnded] wrong number of module name collected", |
| 1, receivedModuleNameTestRunEnded.size()); |
| assertEquals(moduleName, receivedModuleNameTestRunEnded.get(0)); |
| assertEquals("[testEnded] wrong number of module abi collected", |
| 1, receivedAbiTestRunEnded.size()); |
| assertEquals(mAbi, receivedAbiTestRunEnded.get(0)); |
| // verify metadata was retrieved during testEnded callbacks |
| assertEquals("[testEnded] wrong number of metadata collected", |
| 1, receivedComponentsTestEnded.size()); |
| assertEquals("[testEnded] wrong component metadata field received", |
| component, receivedComponentsTestEnded.get(0)); |
| assertEquals("[testEnded] wrong number of module name collected", |
| 1, receivedModuleNameTestEnded.size()); |
| assertEquals(moduleName, receivedModuleNameTestEnded.get(0)); |
| assertEquals("[testEnded] wrong number of module abi collected", |
| 1, receivedAbiTestEnded.size()); |
| assertEquals(mAbi, receivedAbiTestEnded.get(0)); |
| } |
| |
| /** |
| * Simple tests running in one module that run some tests but not all of them. |
| */ |
| @Test |
| public void testSingleModuleRun_incomplete() throws Exception { |
| final String moduleName = "module_run_incomplete"; |
| final String mAbi = "arm64-v8a"; |
| createConfig(mTestDir, moduleName, TEST_STUB, true, false, true, false); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi); |
| |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.eq(AbiUtils.createId(mAbi, moduleName))); |
| EasyMock.expectLastCall(); |
| |
| EasyMock.replay(mMockDevice, mMockBuildInfo); |
| mReporter.invocationStarted(mContext); |
| mTest.run(mReporter); |
| mReporter.invocationEnded(500); |
| EasyMock.verify(mMockDevice, mMockBuildInfo); |
| IInvocationResult result = mReporter.getResult(); |
| assertEquals(1, result.countResults(TestStatus.PASS)); |
| assertEquals(1, result.countResults(TestStatus.FAIL)); |
| // Module should not be seen as complete. |
| assertEquals(1, result.getModules().size()); |
| assertEquals(0, result.getModuleCompleteCount()); |
| } |
| |
| /** |
| * Simple tests running in one module that should be marked complete since it runs all its |
| * tests after an internal retry (like InstrumentationTest). |
| * FIXME: Fix the expectation of this test |
| */ |
| @Test |
| public void testSingleModuleRun_completeAfterInternalRetry() throws Exception { |
| final String moduleName = "module_completeAfterRetry"; |
| final String mAbi = "arm64-v8a"; |
| createConfig(mTestDir, moduleName, TEST_STUB, true, true, true, true); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi); |
| |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.eq(AbiUtils.createId(mAbi, moduleName))); |
| EasyMock.expectLastCall(); |
| |
| EasyMock.replay(mMockDevice, mMockBuildInfo); |
| mReporter.invocationStarted(mContext); |
| mTest.run(mReporter); |
| mReporter.invocationEnded(500); |
| EasyMock.verify(mMockDevice, mMockBuildInfo); |
| IInvocationResult result = mReporter.getResult(); |
| // FIXME: All tests should be marked as executed and not aggregating the count. |
| assertEquals(2, result.countResults(TestStatus.PASS)); |
| assertEquals(1, result.countResults(TestStatus.FAIL)); |
| // FIXME: Module should be complete since all its test have run. |
| assertEquals(1, result.getModules().size()); |
| assertEquals(0, result.getModuleCompleteCount()); |
| } |
| |
| /** |
| * Simple tests running in one module that run some tests but not all of them, then we |
| * attempt to run it again and they still didn't run. |
| * FIXME: This test expectation needs to be fixed |
| */ |
| @Test |
| public void testSingleModuleRun_incomplete_rerun_incomplete() throws Exception { |
| final String moduleName = "module_incomplete_rerun"; |
| final String mAbi = "arm64-v8a"; |
| createConfig(mTestDir, moduleName, TEST_STUB, true, false, true, false); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi); |
| |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.eq(AbiUtils.createId(mAbi, moduleName))); |
| EasyMock.expectLastCall(); |
| |
| // extra calls for retry |
| EasyMock.expect(mMockDevice.getProperty("ro.build.fingerprint")).andReturn("fingerprint"); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi); |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.eq(AbiUtils.createId(mAbi, moduleName))); |
| EasyMock.expectLastCall(); |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq("retry_command_line_args"), |
| EasyMock.eq(COMMAND_LINE)); |
| EasyMock.expectLastCall(); |
| |
| EasyMock.replay(mMockDevice, mMockBuildInfo); |
| mReporter.invocationStarted(mContext); |
| mTest.run(mReporter); |
| mReporter.invocationEnded(500); |
| |
| IInvocationResult result = mReporter.getResult(); |
| assertEquals(1, result.countResults(TestStatus.PASS)); |
| assertEquals(1, result.countResults(TestStatus.FAIL)); |
| // Module should not be seen as complete. |
| assertEquals(0, result.getModuleCompleteCount()); |
| |
| // We re-run it |
| mReporter = new ResultReporter(); |
| mTest = new CompatibilityTest() { |
| @Override |
| protected Set<String> getAbisForBuildTargetArch() { |
| Set<String> abis = new HashSet<>(); |
| abis.add("arm64-v8a"); |
| return abis; |
| } |
| }; |
| mTest.setDevice(mMockDevice); |
| mTest.setBuild(mMockBuildInfo); |
| mTest.setInvocationContext(mContext); |
| OptionSetter setter = new OptionSetter(mTest, mReporter); |
| setter.setOptionValue("retry", "0"); |
| |
| mReporter.invocationStarted(mContext); |
| mTest.run(mReporter); |
| mReporter.invocationEnded(500); |
| EasyMock.verify(mMockDevice, mMockBuildInfo); |
| |
| // Check retry results |
| result = mReporter.getResult(); |
| // FIXME: We should only have 1 not_executed in the retry too. They should not aggregate |
| // from one run to another. |
| assertEquals(1, result.countResults(TestStatus.PASS)); |
| assertEquals(1, result.countResults(TestStatus.FAIL)); |
| // Module should not be seen as complete. |
| assertEquals(1, result.getModules().size()); |
| assertEquals(0, result.getModuleCompleteCount()); |
| } |
| |
| /** |
| * Simple tests running in one module that run some tests but not all of them, we then attempt |
| * to retry run them and this time the not_executed is executed. |
| */ |
| @Test |
| public void testSingleModuleRun_incomplete_rerun_complete() throws Exception { |
| final String moduleName = "module_incom_rerun_complete"; |
| final String mAbi = "arm64-v8a"; |
| createConfig(mTestDir, moduleName, TEST_STUB, true, false, true, false); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi); |
| |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.eq(AbiUtils.createId(mAbi, moduleName))); |
| EasyMock.expectLastCall(); |
| |
| // extra calls for retry |
| EasyMock.expect(mMockDevice.getProperty("ro.build.fingerprint")).andReturn("fingerprint"); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi); |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.eq(AbiUtils.createId(mAbi, moduleName))); |
| EasyMock.expectLastCall(); |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq("retry_command_line_args"), |
| EasyMock.eq(COMMAND_LINE)); |
| EasyMock.expectLastCall(); |
| |
| EasyMock.replay(mMockDevice, mMockBuildInfo); |
| mReporter.invocationStarted(mContext); |
| mTest.run(mReporter); |
| mReporter.invocationEnded(500); |
| |
| IInvocationResult result = mReporter.getResult(); |
| assertEquals(1, result.countResults(TestStatus.PASS)); |
| assertEquals(1, result.countResults(TestStatus.FAIL)); |
| // Module should not be seen as complete. |
| assertEquals(0, result.getModuleCompleteCount()); |
| |
| // We replace the config by one that runs all tests without failures. |
| createConfig(mTestDir, moduleName, TEST_STUB, true, true, false, false); |
| // Usually configs do not change during the same session so we clear the map to have |
| // the new version of the config. |
| ((ConfigurationFactory)ConfigurationFactory.getInstance()).clearMapConfig(); |
| |
| // We re-run it |
| mReporter = new ResultReporter(); |
| mTest = new CompatibilityTest() { |
| @Override |
| protected Set<String> getAbisForBuildTargetArch() { |
| Set<String> abis = new HashSet<>(); |
| abis.add("arm64-v8a"); |
| return abis; |
| } |
| }; |
| mTest.setDevice(mMockDevice); |
| mTest.setBuild(mMockBuildInfo); |
| mTest.setInvocationContext(mContext); |
| OptionSetter setter = new OptionSetter(mTest, mReporter); |
| setter.setOptionValue("retry", "0"); |
| |
| mReporter.invocationStarted(mContext); |
| mTest.run(mReporter); |
| mReporter.invocationEnded(500); |
| EasyMock.verify(mMockDevice, mMockBuildInfo); |
| |
| // Check retry results |
| result = mReporter.getResult(); |
| assertEquals(3, result.countResults(TestStatus.PASS)); |
| assertEquals(0, result.countResults(TestStatus.FAIL)); |
| // Module should be marked as complete after retry. |
| assertEquals(1, result.getModules().size()); |
| assertEquals(1, result.getModuleCompleteCount()); |
| } |
| |
| // ***** Case for sharding interaction ***** |
| |
| /** |
| * Helper to create a shard listener with the original reporter as master. |
| */ |
| private ITestInvocationListener getShardListener(ResultReporter masterReporter) { |
| List<ITestInvocationListener> shardListeners = new ArrayList<ITestInvocationListener>(); |
| ShardListener origConfigListener = new ShardListener(masterReporter); |
| ResultReporter reporterClone = (ResultReporter) masterReporter.clone(); |
| shardListeners.add(reporterClone); |
| shardListeners.add(origConfigListener); |
| ResultForwarder shard = new ResultForwarder(shardListeners); |
| return shard; |
| } |
| |
| /** |
| * Helper Thread to run the IShardableTest. |
| */ |
| private class ShardThread extends Thread { |
| private IRemoteTest mShardTest; |
| private ResultReporter mMasterReporter; |
| private IBuildInfo mBuild; |
| private ITestDevice mDevice; |
| private IInvocationContext mShardContext; |
| |
| public ShardThread(IRemoteTest test, ResultReporter masterReporter, IBuildInfo build, |
| ITestDevice device, IInvocationContext context) { |
| mShardTest = test; |
| mMasterReporter = masterReporter; |
| mBuild = build; |
| mDevice = device; |
| mShardContext = context; |
| } |
| |
| @Override |
| public void run() { |
| ITestInvocationListener listener = getShardListener(mMasterReporter); |
| ((IBuildReceiver)mShardTest).setBuild(mBuild); |
| ((IDeviceTest)mShardTest).setDevice(mDevice); |
| ((IInvocationContextReceiver)mShardTest).setInvocationContext(mContext); |
| listener.invocationStarted(mShardContext); |
| try { |
| mShardTest.run(listener); |
| } catch (DeviceNotAvailableException e) { |
| throw new RuntimeException(e); |
| } finally { |
| listener.invocationEnded(500); |
| } |
| } |
| } |
| |
| /** |
| * Simple tests running in one module that should be marked complete when each shard run a test |
| * from the module. Each Module is going to run 1 pass 1 fail. 2 modules and 2 shards. |
| * Using the {@link CompatibilityTest#split()}. |
| */ |
| @Test |
| public void testSingleModuleRun_sharded() throws Exception { |
| final String moduleName = "module_sharded"; |
| Set<String> abis = AbiUtils.getAbisForArch(SuiteInfo.TARGET_ARCH); |
| Iterator<String> ite = abis.iterator(); |
| final String abi1 = ite.next(); |
| final String abi2 = ite.next(); |
| createConfig(mTestDir, moduleName, TEST_STUB_SHARDABLE, true, true, true, false); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn( |
| String.format("%s,%s", abi1, abi2)); |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.anyObject()); |
| EasyMock.expectLastCall(); |
| |
| EasyMock.replay(mMockDevice, mMockBuildInfo); |
| |
| OptionSetter setter = new OptionSetter(mTest); |
| setter.setOptionValue("shards", "2"); |
| List<IRemoteTest> tests = (List<IRemoteTest>) mTest.split(); |
| // We expect 2 shards |
| assertEquals(2, tests.size()); |
| |
| List<ShardThread> threads = new ArrayList<>(); |
| // Run all shards |
| for (IRemoteTest test : tests) { |
| ShardThread st = new ShardThread(test, mReporter, mMockBuildInfo, mMockDevice, |
| mContext); |
| threads.add(st); |
| st.start(); |
| } |
| for (ShardThread thread : threads) { |
| thread.join(5000); |
| } |
| // Allow some time for ResultReport to finalize the results coming from the threads. |
| boolean finalized = mReporter.waitForFinalized(2, TimeUnit.MINUTES); |
| assertTrue(finalized); |
| EasyMock.verify(mMockDevice, mMockBuildInfo); |
| // Check aggregated results to make sure it's consistent. |
| IInvocationResult result = mReporter.getResult(); |
| assertEquals(4, result.countResults(TestStatus.PASS)); |
| assertEquals(4, result.countResults(TestStatus.FAIL)); |
| assertEquals(2, result.getModules().size()); |
| assertEquals(2, result.getModuleCompleteCount()); |
| } |
| |
| /** |
| * Simple tests running in one module that should be marked incomplete when shards do not |
| * complete. Each shard is going to run 1 pass 1 fail 1 not_executed. |
| * Using the {@link CompatibilityTest#split()}. |
| */ |
| @Test |
| public void testSingleModuleRun_sharded_incomplete() throws Exception { |
| final String moduleName = "module_sharded_incomplete"; |
| Set<String> abis = AbiUtils.getAbisForArch(SuiteInfo.TARGET_ARCH); |
| Iterator<String> ite = abis.iterator(); |
| final String abi1 = ite.next(); |
| final String abi2 = ite.next(); |
| createConfig(mTestDir, moduleName, TEST_STUB_SHARDABLE, true, false, true, false); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn( |
| String.format("%s,%s", abi1, abi2)); |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.anyObject()); |
| EasyMock.expectLastCall(); |
| |
| EasyMock.replay(mMockDevice, mMockBuildInfo); |
| OptionSetter setter = new OptionSetter(mTest); |
| setter.setOptionValue("shards", "2"); |
| List<IRemoteTest> tests = (List<IRemoteTest>) mTest.split(); |
| // We expect 2 shards |
| assertEquals(2, tests.size()); |
| |
| List<ShardThread> threads = new ArrayList<>(); |
| // Run all shards |
| for (IRemoteTest test : tests) { |
| ShardThread st = new ShardThread(test, mReporter, mMockBuildInfo, mMockDevice, |
| mContext); |
| threads.add(st); |
| st.start(); |
| } |
| for (ShardThread thread : threads) { |
| thread.join(5000); |
| } |
| // Allow some time for ResultReport to finalize the results coming from the threads. |
| boolean finalized = mReporter.waitForFinalized(2, TimeUnit.MINUTES); |
| assertTrue(finalized); |
| EasyMock.verify(mMockDevice, mMockBuildInfo); |
| // Check aggregated results to make sure it's consistent. |
| IInvocationResult result = mReporter.getResult(); |
| assertEquals(4, result.countResults(TestStatus.PASS)); |
| assertEquals(4, result.countResults(TestStatus.FAIL)); |
| assertEquals(2, result.getModules().size()); |
| assertEquals(0, result.getModuleCompleteCount()); |
| } |
| |
| /** |
| * Simple tests running in one module that should be marked complete when each shard run a test |
| * from the module. |
| * We are going to run only one of the shard since IStrictShardable allows it. |
| * Using the {@link CompatibilityTest#getTestShard(int, int)}. |
| * FIXME: Fix expectation of this test. |
| */ |
| @Test |
| public void testSingleModuleRun_sharded_getTestShard() throws Exception { |
| final String moduleName = "module_sharded_getTestShard"; |
| Set<String> abis = AbiUtils.getAbisForArch(SuiteInfo.TARGET_ARCH); |
| Iterator<String> ite = abis.iterator(); |
| final String abi1 = ite.next(); |
| final String abi2 = ite.next(); |
| createConfig(mTestDir, moduleName, TEST_STUB_SHARDABLE, true, true, true, false); |
| EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn( |
| String.format("%s,%s", abi1, abi2)); |
| |
| String expectedAdd = AbiUtils.createId(abi1, moduleName) + "," |
| + AbiUtils.createId(abi2, moduleName); |
| mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS), |
| EasyMock.anyObject()); |
| EasyMock.expectLastCall(); |
| mAttributes.put(CompatibilityBuildHelper.MODULE_IDS, expectedAdd); |
| |
| EasyMock.replay(mMockDevice, mMockBuildInfo); |
| |
| List<IRemoteTest> tests = new ArrayList<>(); |
| tests.add(mTest.getTestShard(3, 0)); |
| // We are only running one of the shards since they should be independent. |
| assertEquals(1, tests.size()); |
| |
| ((IBuildReceiver)tests.get(0)).setBuild(mMockBuildInfo); |
| ((IDeviceTest)tests.get(0)).setDevice(mMockDevice); |
| ((IInvocationContextReceiver)tests.get(0)).setInvocationContext(mContext); |
| mReporter.invocationStarted(mContext); |
| try { |
| tests.get(0).run(mReporter); |
| } catch (DeviceNotAvailableException e) { |
| throw new RuntimeException(e); |
| } finally { |
| mReporter.invocationEnded(500); |
| } |
| EasyMock.verify(mMockDevice, mMockBuildInfo); |
| |
| IInvocationResult result = mReporter.getResult(); |
| assertEquals(2, result.countResults(TestStatus.PASS)); |
| assertEquals(2, result.countResults(TestStatus.FAIL)); |
| // FIXME: Only one module should be expected since within the one shard requested to run |
| // only one module existed. |
| assertEquals(2, result.getModules().size()); |
| // FIXME: The module for the shard should be completed since all tests run. |
| // TestRunHandler in this case create an expectation of 3 testRunStarted just because of |
| // the number of shards. |
| assertEquals(0, result.getModuleCompleteCount()); |
| } |
| } |