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.tradefed.invoker.shard; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertNotNull; 20 import static org.junit.Assert.assertNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 24 import com.android.tradefed.config.OptionSetter; 25 import com.android.tradefed.device.DeviceNotAvailableException; 26 import com.android.tradefed.device.DeviceUnresponsiveException; 27 import com.android.tradefed.device.ITestDevice; 28 import com.android.tradefed.device.metric.IMetricCollector; 29 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; 30 import com.android.tradefed.result.ITestInvocationListener; 31 import com.android.tradefed.testtype.IRemoteTest; 32 import com.android.tradefed.testtype.StubTest; 33 34 import org.junit.Before; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 import org.junit.runners.JUnit4; 38 import org.mockito.Mockito; 39 40 import java.util.ArrayList; 41 import java.util.HashMap; 42 import java.util.List; 43 import java.util.concurrent.CountDownLatch; 44 45 /** Unit tests for {@link TestsPoolPoller}. */ 46 @RunWith(JUnit4.class) 47 public class TestsPoolPollerTest { 48 49 private ITestInvocationListener mListener; 50 private ITestDevice mDevice; 51 private List<IMetricCollector> mMetricCollectors; 52 53 @Before 54 public void setUp() { 55 mListener = Mockito.mock(ITestInvocationListener.class); 56 mDevice = Mockito.mock(ITestDevice.class); 57 Mockito.doReturn("serial").when(mDevice).getSerialNumber(); 58 mMetricCollectors = new ArrayList<>(); 59 } 60 61 /** 62 * Tests that {@link TestsPoolPoller#poll()} returns a {@link IRemoteTest} from the pool or null 63 * when the pool is empty. 64 */ 65 @Test 66 public void testMultiPolling() { 67 int numTests = 5; 68 List<IRemoteTest> testsList = new ArrayList<>(); 69 for (int i = 0; i < numTests; i++) { 70 testsList.add(new StubTest()); 71 } 72 CountDownLatch tracker = new CountDownLatch(2); 73 TestsPoolPoller poller1 = new TestsPoolPoller(testsList, tracker); 74 TestsPoolPoller poller2 = new TestsPoolPoller(testsList, tracker); 75 // initial size 76 assertEquals(numTests, testsList.size()); 77 assertNotNull(poller1.poll()); 78 assertEquals(numTests - 1, testsList.size()); 79 assertNotNull(poller2.poll()); 80 assertEquals(numTests - 2, testsList.size()); 81 assertNotNull(poller1.poll()); 82 assertNotNull(poller1.poll()); 83 assertNotNull(poller2.poll()); 84 assertTrue(testsList.isEmpty()); 85 // once empty poller returns null 86 assertNull(poller1.poll()); 87 assertNull(poller2.poll()); 88 } 89 90 /** 91 * Tests that {@link TestsPoolPoller#run(ITestInvocationListener)} is properly running and 92 * redirecting the invocation callbacks. 93 */ 94 @Test 95 public void testPollingRun() throws Exception { 96 int numTests = 5; 97 List<IRemoteTest> testsList = new ArrayList<>(); 98 for (int i = 0; i < numTests; i++) { 99 IRemoteTest test = new StubTest(); 100 OptionSetter setter = new OptionSetter(test); 101 setter.setOptionValue("run-a-test", "true"); 102 testsList.add(test); 103 } 104 CountDownLatch tracker = new CountDownLatch(1); 105 TestsPoolPoller poller = new TestsPoolPoller(testsList, tracker); 106 poller.setMetricCollectors(mMetricCollectors); 107 poller.run(mListener); 108 Mockito.verify(mListener, Mockito.times(numTests)) 109 .testRunStarted(Mockito.anyString(), Mockito.anyInt()); 110 Mockito.verify(mListener, Mockito.times(numTests)) 111 .testRunEnded(Mockito.anyLong(), (HashMap<String, Metric>) Mockito.any()); 112 assertEquals(0, tracker.getCount()); 113 } 114 115 /** 116 * Tests that {@link TestsPoolPoller#run(ITestInvocationListener)} will continue to run tests 117 * even if one of them throws a {@link RuntimeException}. 118 */ 119 @Test 120 public void testRun_runtimeException() throws Exception { 121 List<IRemoteTest> testsList = new ArrayList<>(); 122 // Add one bad test first that will throw an exception. 123 IRemoteTest badTest = new StubTest(); 124 OptionSetter setter = new OptionSetter(badTest); 125 setter.setOptionValue("test-throw-runtime", "true"); 126 testsList.add(badTest); 127 // Add tests that can run 128 int numTests = 5; 129 for (int i = 0; i < numTests; i++) { 130 IRemoteTest test = new StubTest(); 131 OptionSetter s = new OptionSetter(test); 132 s.setOptionValue("run-a-test", "true"); 133 testsList.add(test); 134 } 135 CountDownLatch tracker = new CountDownLatch(1); 136 TestsPoolPoller poller = new TestsPoolPoller(testsList, tracker); 137 poller.setMetricCollectors(mMetricCollectors); 138 poller.run(mListener); 139 Mockito.verify(mListener, Mockito.times(numTests)) 140 .testRunStarted(Mockito.anyString(), Mockito.anyInt()); 141 Mockito.verify(mListener, Mockito.times(numTests)) 142 .testRunEnded(Mockito.anyLong(), (HashMap<String, Metric>) Mockito.any()); 143 assertEquals(0, tracker.getCount()); 144 } 145 146 /** 147 * Tests that {@link TestsPoolPoller#run(ITestInvocationListener)} will continue to run tests 148 * even if one of them throws a {@link DeviceUnresponsiveException}. 149 */ 150 @Test 151 public void testRun_deviceUnresponsive() throws Exception { 152 List<IRemoteTest> testsList = new ArrayList<>(); 153 // Add one bad test first that will throw an exception. 154 IRemoteTest badTest = new StubTest(); 155 OptionSetter setter = new OptionSetter(badTest); 156 setter.setOptionValue("test-throw-unresponsive", "true"); 157 testsList.add(badTest); 158 // Add tests that can run 159 int numTests = 5; 160 for (int i = 0; i < numTests; i++) { 161 IRemoteTest test = new StubTest(); 162 OptionSetter s = new OptionSetter(test); 163 s.setOptionValue("run-a-test", "true"); 164 testsList.add(test); 165 } 166 CountDownLatch tracker = new CountDownLatch(1); 167 TestsPoolPoller poller = new TestsPoolPoller(testsList, tracker); 168 poller.setMetricCollectors(mMetricCollectors); 169 poller.run(mListener); 170 Mockito.verify(mListener, Mockito.times(numTests)) 171 .testRunStarted(Mockito.anyString(), Mockito.anyInt()); 172 Mockito.verify(mListener, Mockito.times(numTests)) 173 .testRunEnded(Mockito.anyLong(), (HashMap<String, Metric>) Mockito.any()); 174 assertEquals(0, tracker.getCount()); 175 } 176 177 /** 178 * Tests that {@link TestsPoolPoller#run(ITestInvocationListener)} will stop to run tests if one 179 * of them throws a {@link DeviceNotAvailableException}. 180 */ 181 @Test 182 public void testRun_dnae() throws Exception { 183 List<IRemoteTest> testsList = new ArrayList<>(); 184 // Add one bad test first that will throw an exception. 185 IRemoteTest badTest = new StubTest(); 186 OptionSetter setter = new OptionSetter(badTest); 187 setter.setOptionValue("test-throw-not-available", "true"); 188 testsList.add(badTest); 189 // Add tests that can run 190 int numTests = 5; 191 for (int i = 0; i < numTests; i++) { 192 IRemoteTest test = new StubTest(); 193 OptionSetter s = new OptionSetter(test); 194 s.setOptionValue("run-a-test", "true"); 195 testsList.add(test); 196 } 197 CountDownLatch tracker = new CountDownLatch(1); 198 TestsPoolPoller poller = new TestsPoolPoller(testsList, tracker); 199 poller.setMetricCollectors(mMetricCollectors); 200 poller.setDevice(mDevice); 201 try { 202 poller.run(mListener); 203 fail("Should have thrown an exception."); 204 } catch (DeviceNotAvailableException expected) { 205 // expected 206 } 207 // We expect no callbacks on these, poller should stop early. 208 Mockito.verify(mListener, Mockito.times(0)) 209 .testRunStarted(Mockito.anyString(), Mockito.anyInt()); 210 Mockito.verify(mListener, Mockito.times(0)) 211 .testRunEnded(Mockito.anyLong(), (HashMap<String, Metric>) Mockito.any()); 212 assertEquals(0, tracker.getCount()); 213 } 214 215 /** 216 * If a device not available exception is thrown from a tests, and the poller is not the last 217 * one alive, we wait and attempt to recover the device. In case of success, execution will 218 * proceed. 219 */ 220 @Test 221 public void testRun_dnae_NotLastDevice() throws Exception { 222 List<IRemoteTest> testsList = new ArrayList<>(); 223 // Add one bad test first that will throw an exception. 224 IRemoteTest badTest = new StubTest(); 225 OptionSetter setter = new OptionSetter(badTest); 226 setter.setOptionValue("test-throw-not-available", "true"); 227 testsList.add(badTest); 228 // Add tests that can run 229 int numTests = 5; 230 for (int i = 0; i < numTests; i++) { 231 IRemoteTest test = new StubTest(); 232 OptionSetter s = new OptionSetter(test); 233 s.setOptionValue("run-a-test", "true"); 234 testsList.add(test); 235 } 236 CountDownLatch tracker = new CountDownLatch(3); 237 TestsPoolPoller poller = new TestsPoolPoller(testsList, tracker); 238 poller.setMetricCollectors(mMetricCollectors); 239 poller.setDevice(mDevice); 240 241 poller.run(mListener); 242 // The callbacks from all the other tests because the device was recovered 243 Mockito.verify(mListener, Mockito.times(numTests)) 244 .testRunStarted(Mockito.anyString(), Mockito.anyInt()); 245 Mockito.verify(mListener, Mockito.times(numTests)).testStarted(Mockito.any()); 246 Mockito.verify(mListener, Mockito.times(numTests)) 247 .testEnded(Mockito.any(), (HashMap<String, Metric>) Mockito.any()); 248 Mockito.verify(mListener, Mockito.times(numTests)) 249 .testRunEnded(Mockito.anyLong(), (HashMap<String, Metric>) Mockito.any()); 250 Mockito.verify(mDevice).waitForDeviceAvailable(Mockito.anyLong()); 251 Mockito.verify(mDevice).reboot(); 252 assertEquals(2, tracker.getCount()); 253 } 254 } 255