1 /*
2  * Copyright (C) 2020 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.targetprep;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import static org.testng.Assert.assertThrows;
21 
22 import com.android.ddmlib.Log;
23 import com.android.ddmlib.Log.ILogOutput;
24 import com.android.ddmlib.Log.LogLevel;
25 import com.android.tradefed.build.BuildInfo;
26 import com.android.tradefed.config.OptionSetter;
27 import com.android.tradefed.device.ITestDevice;
28 import com.android.tradefed.invoker.IInvocationContext;
29 import com.android.tradefed.invoker.InvocationContext;
30 import com.android.tradefed.invoker.TestInformation;
31 import com.android.tradefed.targetprep.TargetSetupError;
32 import com.android.tradefed.util.CommandResult;
33 import com.android.tradefed.util.CommandStatus;
34 
35 import com.google.common.truth.Correspondence;
36 
37 import org.junit.After;
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 import org.junit.runners.JUnit4;
42 import org.mockito.Mockito;
43 
44 import java.util.ArrayList;
45 
46 @RunWith(JUnit4.class)
47 public final class CheckGmsPreparerTest {
48 
49     private CheckGmsPreparer mPreparer;
50     private LogCaptor mLogCaptor;
51 
52     @Before
setUp()53     public void setUp() throws Exception {
54         mPreparer = new CheckGmsPreparer();
55         new OptionSetter(mPreparer).setOptionValue(CheckGmsPreparer.OPTION_ENABLE, "true");
56 
57         mLogCaptor = new LogCaptor();
58         Log.addLogger(mLogCaptor);
59     }
60 
61     @After
tearDown()62     public void tearDown() {
63         Log.removeLogger(mLogCaptor);
64     }
65 
66     @Test
setUp_checkDisabledAndGmsAbsent_doesNotReboot()67     public void setUp_checkDisabledAndGmsAbsent_doesNotReboot() throws Exception {
68         ITestDevice device = createDeviceWithGmsAbsent();
69         disablePreparer(mPreparer);
70 
71         mPreparer.setUp(createTestInfo(device));
72 
73         Mockito.verify(device, Mockito.never()).reboot();
74         assertThat(mLogCaptor.getLogItems())
75                 .comparingElementsUsing(createContainsErrorLogCorrespondence())
76                 .doesNotContain("GMS");
77     }
78 
79     @Test
tearDown_checkDisabledAndGmsAbsent_doesNotLog()80     public void tearDown_checkDisabledAndGmsAbsent_doesNotLog() throws Exception {
81         ITestDevice device = createDeviceWithGmsAbsent();
82         disablePreparer(mPreparer);
83 
84         mPreparer.tearDown(createTestInfo(device), null);
85 
86         assertThat(mLogCaptor.getLogItems())
87                 .comparingElementsUsing(createContainsErrorLogCorrespondence())
88                 .doesNotContain("GMS");
89     }
90 
91     @Test
tearDown_setUpThrows_doesNotCheck()92     public void tearDown_setUpThrows_doesNotCheck() throws Exception {
93         ITestDevice device = createDeviceWithGmsAbsent();
94         TestInformation testInfo = createTestInfo(device);
95         assertThrows(TargetSetupError.class, () -> mPreparer.setUp(testInfo));
96         mLogCaptor.reset();
97         Mockito.reset(device);
98         Mockito.when(device.executeShellV2Command(Mockito.any()))
99                 .thenReturn(createFailedCommandResult());
100 
101         mPreparer.tearDown(testInfo, null);
102 
103         Mockito.verify(device, Mockito.never()).executeShellV2Command(Mockito.any());
104     }
105 
106     @Test
tearDown_setUpRecoveredGms_checksGms()107     public void tearDown_setUpRecoveredGms_checksGms() throws Exception {
108         ITestDevice device = createDeviceWithGmsAbsentAndRecoverable();
109         TestInformation testInfo = createTestInfo(device);
110         mPreparer.setUp(testInfo);
111         mLogCaptor.reset();
112         Mockito.reset(device);
113         Mockito.when(device.executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND))
114                 .thenReturn(createSuccessfulCommandResult());
115 
116         mPreparer.tearDown(testInfo, null);
117 
118         Mockito.verify(device, Mockito.atLeast(1))
119                 .executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND);
120     }
121 
122     @Test
tearDown_setUpFoundGms_checksGms()123     public void tearDown_setUpFoundGms_checksGms() throws Exception {
124         ITestDevice device = createDeviceWithGmsPresent();
125         TestInformation testInfo = createTestInfo(device);
126         mPreparer.setUp(testInfo);
127         Mockito.reset(device);
128         mLogCaptor.reset();
129         Mockito.when(device.executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND))
130                 .thenReturn(createSuccessfulCommandResult());
131 
132         mPreparer.tearDown(testInfo, null);
133 
134         Mockito.verify(device, Mockito.atLeast(1))
135                 .executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND);
136     }
137 
138     @Test
setUp_gmsPresent_doesNotReboot()139     public void setUp_gmsPresent_doesNotReboot() throws Exception {
140         ITestDevice device = createDeviceWithGmsPresent();
141 
142         mPreparer.setUp(createTestInfo(device));
143 
144         Mockito.verify(device, Mockito.never()).reboot();
145         assertThat(mLogCaptor.getLogItems())
146                 .comparingElementsUsing(createContainsErrorLogCorrespondence())
147                 .doesNotContain("GMS");
148     }
149 
150     @Test
setUp_gmsProcessRecoveredAfterReboot_doesNotThrow()151     public void setUp_gmsProcessRecoveredAfterReboot_doesNotThrow() throws Exception {
152         ITestDevice device = createDeviceWithGmsAbsentAndRecoverable();
153 
154         mPreparer.setUp(createTestInfo(device));
155 
156         Mockito.verify(device, Mockito.times(1)).reboot();
157         assertThat(mLogCaptor.getLogItems())
158                 .comparingElementsUsing(createContainsErrorLogCorrespondence())
159                 .contains("GMS");
160     }
161 
162     @Test
setUp_gmsProcessNotRecoveredAfterReboot_throwsException()163     public void setUp_gmsProcessNotRecoveredAfterReboot_throwsException() throws Exception {
164         ITestDevice device = createDeviceWithGmsAbsent();
165 
166         assertThrows(TargetSetupError.class, () -> mPreparer.setUp(createTestInfo(device)));
167         Mockito.verify(device, Mockito.times(1)).reboot();
168         assertThat(mLogCaptor.getLogItems())
169                 .comparingElementsUsing(createContainsErrorLogCorrespondence())
170                 .contains("GMS");
171     }
172 
173     @Test
tearDown_gmsProcessPresent_doesNotLog()174     public void tearDown_gmsProcessPresent_doesNotLog() throws Exception {
175         ITestDevice device = createDeviceWithGmsPresent();
176 
177         mPreparer.tearDown(createTestInfo(device), null);
178 
179         assertThat(mLogCaptor.getLogItems())
180                 .comparingElementsUsing(createContainsErrorLogCorrespondence())
181                 .doesNotContain("GMS");
182     }
183 
184     @Test
tearDown_gmsProcessAbsent_logsError()185     public void tearDown_gmsProcessAbsent_logsError() throws Exception {
186         ITestDevice device = createDeviceWithGmsAbsent();
187 
188         mPreparer.tearDown(createTestInfo(device), null);
189 
190         assertThat(mLogCaptor.getLogItems())
191                 .comparingElementsUsing(createContainsErrorLogCorrespondence())
192                 .contains("GMS");
193     }
194 
disablePreparer(CheckGmsPreparer preparer)195     private static void disablePreparer(CheckGmsPreparer preparer) throws Exception {
196         new OptionSetter(preparer).setOptionValue(CheckGmsPreparer.OPTION_ENABLE, "false");
197     }
198 
createContainsErrorLogCorrespondence()199     private static Correspondence<LogItem, String> createContainsErrorLogCorrespondence() {
200         return Correspondence.from(
201                 (LogItem actual, String expected) -> {
202                     return actual.getLogLevel() == LogLevel.ERROR
203                             && actual.getMessage().contains(expected);
204                 },
205                 "has an error log that contains");
206     }
207 
createDeviceWithGmsAbsentAndRecoverable()208     private static ITestDevice createDeviceWithGmsAbsentAndRecoverable() throws Exception {
209         ITestDevice device = Mockito.mock(ITestDevice.class);
210         Mockito.doReturn(createFailedCommandResult())
211                 .doReturn(createSuccessfulCommandResult())
212                 .when(device)
213                 .executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND);
214         return device;
215     }
216 
createDeviceWithGmsPresent()217     private static ITestDevice createDeviceWithGmsPresent() throws Exception {
218         ITestDevice device = Mockito.mock(ITestDevice.class);
219         Mockito.when(device.executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND))
220                 .thenReturn(createSuccessfulCommandResult());
221         return device;
222     }
223 
createDeviceWithGmsAbsent()224     private static ITestDevice createDeviceWithGmsAbsent() throws Exception {
225         ITestDevice device = Mockito.mock(ITestDevice.class);
226         Mockito.when(device.executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND))
227                 .thenReturn(createFailedCommandResult());
228         return device;
229     }
230 
231     private static final class LogCaptor implements ILogOutput {
232         private ArrayList<LogItem> mLogItems = new ArrayList<>();
233 
reset()234         void reset() {
235             mLogItems.clear();
236         }
237 
getLogItems()238         ArrayList<LogItem> getLogItems() {
239             return mLogItems;
240         }
241 
242         @Override
printLog(LogLevel logLevel, String tag, String message)243         public void printLog(LogLevel logLevel, String tag, String message) {
244             mLogItems.add(new LogItem(logLevel, tag, message));
245         }
246 
247         @Override
printAndPromptLog(LogLevel logLevel, String tag, String message)248         public void printAndPromptLog(LogLevel logLevel, String tag, String message) {
249             printLog(logLevel, tag, message);
250         }
251     }
252 
253     private static final class LogItem {
254         private LogLevel mLogLevel;
255         private String mMessage;
256 
getLogLevel()257         LogLevel getLogLevel() {
258             return mLogLevel;
259         }
260 
getMessage()261         String getMessage() {
262             return mMessage;
263         }
264 
LogItem(LogLevel logLevel, String tag, String message)265         LogItem(LogLevel logLevel, String tag, String message) {
266             mLogLevel = logLevel;
267             mMessage = message;
268         }
269     }
270 
createTestInfo(ITestDevice device)271     private static TestInformation createTestInfo(ITestDevice device) {
272         IInvocationContext context = new InvocationContext();
273         context.addAllocatedDevice("device1", device);
274         context.addDeviceBuildInfo("device1", new BuildInfo());
275         return TestInformation.newBuilder().setInvocationContext(context).build();
276     }
277 
createSuccessfulCommandResult()278     private static CommandResult createSuccessfulCommandResult() {
279         CommandResult commandResult = new CommandResult(CommandStatus.SUCCESS);
280         commandResult.setExitCode(0);
281         return commandResult;
282     }
283 
createFailedCommandResult()284     private static CommandResult createFailedCommandResult() {
285         CommandResult commandResult = new CommandResult(CommandStatus.FAILED);
286         commandResult.setExitCode(1);
287         return commandResult;
288     }
289 }
290