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