• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 android.device.collectors;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertNotNull;
20 import static org.junit.Assert.assertTrue;
21 import static org.mockito.Mockito.atLeast;
22 import static org.mockito.Mockito.doReturn;
23 import static org.mockito.Mockito.eq;
24 import static org.mockito.Mockito.verify;
25 import static org.mockito.Mockito.when;
26 
27 import android.app.Instrumentation;
28 import android.device.collectors.util.SendToInstrumentation;
29 import android.os.Bundle;
30 import android.os.Environment;
31 import androidx.test.InstrumentationRegistry;
32 import androidx.test.runner.AndroidJUnit4;
33 
34 import com.android.helpers.ICollectorHelper;
35 
36 import java.io.File;
37 import java.nio.charset.Charset;
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.nio.file.Paths;
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.regex.Matcher;
46 import java.util.regex.Pattern;
47 
48 import org.junit.After;
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.junit.runner.Description;
52 import org.junit.runner.Result;
53 import org.junit.runner.RunWith;
54 import org.mockito.ArgumentCaptor;
55 import org.mockito.Mock;
56 import org.mockito.MockitoAnnotations;
57 
58 /** Android Unit tests for {@link ScheduledRunCollectionListener}. */
59 @RunWith(AndroidJUnit4.class)
60 public class ScheduledRunCollectionListenerTest {
61 
62     private static final String TEST_METRIC_KEY = "test_metric_key";
63     private static final Integer[] TEST_METRIC_VALUES = {0, 1, 2, 3, 4};
64     private static final long TEST_INTERVAL = 100L;
65     private static final long TEST_DURATION = 450L;
66     // Collects at 0ms, 100ms, 200ms, and so on.
67     private static final long NUMBER_OF_COLLECTIONS = TEST_DURATION / TEST_INTERVAL + 1;
68     private static final String DATA_REGEX =
69             "(?<timestamp>[0-9]+)\\s+," + TEST_METRIC_KEY + "\\s+,(?<value>[0-9])\\s+";
70 
71     @Mock private ICollectorHelper mHelper;
72 
73     @Mock private Instrumentation mInstrumentation;
74 
75     private ScheduledRunCollectionListener mListener;
76 
initListener()77     private ScheduledRunCollectionListener initListener() {
78         Bundle b = new Bundle();
79         b.putString(ScheduledRunCollectionListener.INTERVAL_ARG_KEY, Long.toString(TEST_INTERVAL));
80         doReturn(true).when(mHelper).startCollecting();
81         Map<String, Integer> first = new HashMap<>();
82         first.put(TEST_METRIC_KEY, TEST_METRIC_VALUES[0]);
83         Map<String, Integer>[] rest =
84                 Arrays.stream(TEST_METRIC_VALUES)
85                         .skip(1)
86                         .map(
87                                 testMetricValue -> {
88                                     Map<String, Integer> m = new HashMap<>();
89                                     m.put(TEST_METRIC_KEY, testMetricValue);
90                                     return m;
91                                 })
92                         .toArray(Map[]::new);
93         // <code>thenReturn</code> call signature requires thenReturn(T value, T... values).
94         when(mHelper.getMetrics()).thenReturn(first, rest);
95         doReturn(true).when(mHelper).stopCollecting();
96         ScheduledRunCollectionListener listener =
97                 new ScheduledRunCollectionListener<Integer>(b, mHelper);
98         // Mock getUiAutomation method for the purpose of enabling createAndEmptyDirectory method
99         // from BaseMetricListener.
100         doReturn(InstrumentationRegistry.getInstrumentation().getUiAutomation())
101                 .when(mInstrumentation)
102                 .getUiAutomation();
103         listener.setInstrumentation(mInstrumentation);
104         return listener;
105     }
106 
107     @Before
setUp()108     public void setUp() {
109         MockitoAnnotations.initMocks(this);
110         mListener = initListener();
111     }
112 
113     @After
tearDown()114     public void tearDown() {
115         // Remove files/directories that have been created.
116         Path outputFilePath =
117                 Paths.get(
118                         Environment.getExternalStorageDirectory().toString(),
119                         ScheduledRunCollectionListener.OUTPUT_ROOT,
120                         ScheduledRunCollectionListener.class.getSimpleName());
121         mListener.executeCommandBlocking("rm -rf " + outputFilePath.toString());
122     }
123 
124     @Test
testCompleteRun()125     public void testCompleteRun() throws Exception {
126         testRun(true);
127     }
128 
129     @Test
testIncompleteRun()130     public void testIncompleteRun() throws Exception {
131         testRun(false);
132     }
133 
134     @Test
testInstrumentationResult()135     public void testInstrumentationResult() throws Exception {
136         Description runDescription = Description.createSuiteDescription("run");
137         mListener.testRunStarted(runDescription);
138 
139         Thread.sleep(TEST_DURATION);
140         mListener.testRunFinished(new Result());
141         // AJUR runner is then gonna call instrumentationRunFinished.
142         Bundle result = new Bundle();
143         mListener.instrumentationRunFinished(System.out, result, new Result());
144         int expectedMin = Arrays.stream(TEST_METRIC_VALUES).min(Integer::compare).get();
145         assertEquals(
146                 expectedMin,
147                 Integer.parseInt(
148                         result.getString(
149                                 TEST_METRIC_KEY + ScheduledRunCollectionListener.MIN_SUFFIX)));
150         int expectedMax = Arrays.stream(TEST_METRIC_VALUES).max(Integer::compare).get();
151         assertEquals(
152                 expectedMax,
153                 Integer.parseInt(
154                         result.getString(
155                                 TEST_METRIC_KEY + ScheduledRunCollectionListener.MAX_SUFFIX)));
156         double expectedMean =
157                 Arrays.stream(TEST_METRIC_VALUES).mapToDouble(i -> i.doubleValue()).sum()
158                         / NUMBER_OF_COLLECTIONS;
159         assertEquals(
160                 expectedMean,
161                 Double.parseDouble(
162                         result.getString(
163                                 TEST_METRIC_KEY + ScheduledRunCollectionListener.MEAN_SUFFIX)),
164                 0.1);
165     }
166 
testRun(boolean isComplete)167     private void testRun(boolean isComplete) throws Exception {
168         Description runDescription = Description.createSuiteDescription("run");
169         mListener.testRunStarted(runDescription);
170 
171         Thread.sleep(TEST_DURATION);
172         // If incomplete run happens, for example, when a system crash happens half way through the
173         // run, <code>testRunFinished</code> method will be skipped, but the output file should
174         // still be present, and the time-series up to the point when the crash happens should still
175         // be recorded.
176         if (isComplete) {
177             mListener.testRunFinished(new Result());
178         }
179 
180         ArgumentCaptor<Bundle> bundle = ArgumentCaptor.forClass(Bundle.class);
181 
182         // Verify that the path of the time-series output file has been sent to instrumentation.
183         verify(mInstrumentation, atLeast(1))
184                 .sendStatus(eq(SendToInstrumentation.INST_STATUS_IN_PROGRESS), bundle.capture());
185         Bundle pathBundle = bundle.getAllValues().get(0);
186         String pathKey =
187                 String.format(
188                         ScheduledRunCollectionListener.OUTPUT_FILE_PATH,
189                         ScheduledRunCollectionListener.class.getSimpleName());
190         String path = pathBundle.getString(pathKey);
191         assertNotNull(path);
192 
193         // Check the output file exists.
194         File outputFile = new File(path);
195         assertTrue(outputFile.exists());
196 
197         // Check that output file contains some of the periodic run results, sample results are
198         // like:
199         //
200         // time  ,metric_key       ,value
201         // 2     ,test_metric_key  ,0
202         // 102   ,test_metric_key  ,0
203         // 203   ,test_metric_key  ,0
204         // ...
205         List<String> lines = Files.readAllLines(outputFile.toPath(), Charset.defaultCharset());
206         assertEquals(NUMBER_OF_COLLECTIONS, lines.size() - 1);
207         assertEquals(lines.get(0), ScheduledRunCollectionListener.TIME_SERIES_HEADER);
208         for (int i = 1; i != lines.size(); ++i) {
209             Pattern p = Pattern.compile(DATA_REGEX);
210             Matcher m = p.matcher(lines.get(i));
211             assertTrue(m.matches());
212             long timestamp = Long.parseLong(m.group("timestamp"));
213             long delta = TEST_INTERVAL / 2;
214             assertEquals((i - 1) * TEST_INTERVAL, timestamp, delta);
215             Integer value = Integer.valueOf(m.group("value"));
216             assertEquals(TEST_METRIC_VALUES[i - 1], value);
217         }
218 
219         // For incomplete run, invoke testRunFinished in the end to prevent resource leak.
220         if (!isComplete) {
221             mListener.testRunFinished(new Result());
222         }
223     }
224 }
225