1 /*
2  * Copyright 2018 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 
17 package android.media.cts;
18 
19 import static android.content.pm.PackageManager.MATCH_APEX;
20 
21 import static org.junit.Assume.assumeTrue;
22 
23 import android.content.Context;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.os.Bundle;
28 import android.util.Log;
29 
30 import androidx.test.core.app.ApplicationProvider;
31 import androidx.test.platform.app.InstrumentationRegistry;
32 
33 import org.junit.Assert;
34 import org.junit.AssumptionViolatedException;
35 
36 import java.util.Locale;
37 import java.util.Objects;
38 
39 /**
40  * Utilities for tests.
41  */
42 public final class TestUtils {
43     private static String TAG = "TestUtils";
44     private static final int WAIT_TIME_MS = 1000;
45     private static final int WAIT_SERVICE_TIME_MS = 5000;
46 
47     /**
48      * Compares contents of two bundles.
49      *
50      * @param a a bundle
51      * @param b another bundle
52      * @return {@code true} if two bundles are the same. {@code false} otherwise. This may be
53      *     incorrect if any bundle contains a bundle.
54      */
equals(Bundle a, Bundle b)55     public static boolean equals(Bundle a, Bundle b) {
56         if (a == b) {
57             return true;
58         }
59         if (a == null || b == null) {
60             return false;
61         }
62         if (!a.keySet().containsAll(b.keySet())
63                 || !b.keySet().containsAll(a.keySet())) {
64             return false;
65         }
66         for (String key : a.keySet()) {
67             if (!Objects.equals(a.get(key), b.get(key))) {
68                 return false;
69             }
70         }
71         return true;
72     }
73 
74     /**
75      * Checks {@code module} is at least {@code minVersion}
76      *
77      * The tests are skipped by throwing a {@link AssumptionViolatedException}.  CTS test runners
78      * will report this as a {@code ASSUMPTION_FAILED}.
79      *
80      * @param module     the apex module name
81      * @param minVersion the minimum version
82      * @throws AssumptionViolatedException if module version < minVersion
83      */
assumeMainlineModuleAtLeast(String module, long minVersion)84     public static void assumeMainlineModuleAtLeast(String module, long minVersion) {
85         try {
86             long actualVersion = getModuleVersion(module);
87             assumeTrue("Assume  module  " + module + " version " + actualVersion + " < minVersion"
88                     + minVersion, actualVersion >= minVersion);
89         } catch (PackageManager.NameNotFoundException e) {
90             Assert.fail(e.getMessage());
91         }
92     }
93 
94     /**
95      * Checks if {@code module} is < {@code minVersion}
96      *
97      * <p>
98      * {@link AssumptionViolatedException} is not handled properly by {@code JUnit3} so just return
99      * the test
100      * early instead.
101      *
102      * @param module     the apex module name
103      * @param minVersion the minimum version
104      * @deprecated convert test to JUnit4 and use
105      * {@link #assumeMainlineModuleAtLeast(String, long)} instead.
106      */
107     @Deprecated
skipTestIfMainlineLessThan(String module, long minVersion)108     public static boolean skipTestIfMainlineLessThan(String module, long minVersion) {
109         try {
110             long actualVersion = getModuleVersion(module);
111             if (actualVersion < minVersion) {
112                 Log.i(TAG, "Skipping test because Module  " + module + " minVersion " + minVersion
113                         + " > "
114                         + minVersion
115                 );
116                 return true;
117             } else {
118                 return false;
119             }
120         } catch (PackageManager.NameNotFoundException e) {
121             Assert.fail(e.getMessage());
122             return false;
123         }
124     }
125 
getModuleVersion(String module)126     private static long getModuleVersion(String module)
127             throws PackageManager.NameNotFoundException {
128         Context context = ApplicationProvider.getApplicationContext();
129         PackageInfo info = context.getPackageManager().getPackageInfo(module,
130                 MATCH_APEX);
131         return info.getLongVersionCode();
132     }
133 
134 
135     /**
136      * Reports whether {@code module} is the version shipped with the original system image
137      * or if it has been updated via a mainline update.
138      *
139      * @param module     the apex module name
140      * @return {@code true} if the apex module is the original version shipped with the device.
141      */
isMainlineModuleFactoryVersion(String module)142     public static boolean isMainlineModuleFactoryVersion(String module) {
143         try {
144             Context context = ApplicationProvider.getApplicationContext();
145             PackageInfo info = context.getPackageManager().getPackageInfo(module,
146                     MATCH_APEX);
147             if (info != null) {
148                 return (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
149             }
150         } catch (PackageManager.NameNotFoundException e) {
151             // Ignore the exception on devices that do not have this module
152         }
153         return true;
154     }
155 
156     /*
157      * decide whether we are in CTS, MCTS, or MTS mode.
158      * return the appropriate constant value
159      */
160     public static final int TESTMODE_CTS = 0;
161     public static final int TESTMODE_MCTS = 1;
162     public static final int TESTMODE_MTS = 2;
163 
164     /**
165      * Report the current testing mode, as an enumeration.
166      * Testing mode is determined by argument 'media-testing-mode'
167      * which specifies 'cts', 'mcts', or 'mts'
168      * If missing, we use the older boolean "mts-media" to generate either 'cts' or 'mts'
169      *
170      * This is most often specified in a CtsMedia* app's AndroidTest.xml, using
171      * a line like:
172      * <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
173      * ...
174      * <option name="instrumentation-arg" key="media-testing-mode" value="CTS" />
175      * </test>
176      *
177      * @return {@code} one of the values TESTMODE_CTS, TESTMODE_MCTS, or TESTMODE_MTS.
178      *
179      */
currentTestMode()180     public static int currentTestMode() {
181         Bundle bundle = InstrumentationRegistry.getArguments();
182         String value = bundle.getString("media-testing-mode");
183         if (value == null) {
184             value = bundle.getString("mts-media");
185             if (value == null || !value.equals("true")) {
186                 value = "CTS";
187             } else {
188                 value = "MTS";
189             }
190         }
191         int mode;
192         value = value.toUpperCase(Locale.ROOT);
193         if (value.equals("CTS")) {
194             mode = TESTMODE_CTS;
195         } else if (value.equals("MCTS")) {
196             mode = TESTMODE_MCTS;
197         } else if (value.equals("MTS")) {
198             mode = TESTMODE_MTS;
199         } else {
200             mode = TESTMODE_CTS;
201         }
202         return mode;
203     }
204 
205     /**
206      * Report the current testing mode, as a string.
207      * Testing mode is determined by argument 'media-testing-mode'
208      * which specifies 'cts', 'mcts', or 'mts'
209      * If missing, we use the older boolean "mts-media" to generate either 'cts' or 'mts'
210      *
211      * @return {@code} "CTS", "MCTS", or "MTS" corresponding to the mode.
212      */
currentTestModeName()213     public static String currentTestModeName() {
214         Bundle bundle = InstrumentationRegistry.getArguments();
215         String value = bundle.getString("media-testing-mode");
216         if (value == null) {
217             value = bundle.getString("mts-media");
218             if (value == null || !value.equals("true")) {
219                 value = "CTS";
220             } else {
221                 value = "MTS";
222             }
223         }
224         value = value.toUpperCase(Locale.ROOT);
225         if (value.equals("CTS")) {
226             return "CTS";
227         } else if (value.equals("MCTS")) {
228             return "MCTS";
229         } else if (value.equals("MTS")) {
230             return "MTS";
231         } else {
232             // same default as currentTestMode()
233             return "CTS";
234         }
235     }
236 
237     /**
238      * Report whether this test run should evaluate module functionality.
239      * Some tests (or parts of tests) are restricted to a particular mode.
240      *
241      * @return {@code} true is the current test mode is MCTS or MTS.
242      */
isTestingModules()243     public static boolean isTestingModules() {
244         int mode = currentTestMode();
245         switch (mode) {
246             case TESTMODE_MCTS:
247             case TESTMODE_MTS:
248                 return true;
249             default:
250                 break;
251         }
252         return false;
253     }
254 
255     /**
256      * Report whether we are in MTS mode (vs CTS or MCTS) mode.
257      * Some tests (or parts of tests) are restricted to a particular mode.
258      *
259      * @return {@code} true is the current test mode is MTS.
260      */
isMtsMode()261     public static boolean isMtsMode() {
262         int mode = currentTestMode();
263         return mode == TESTMODE_MTS;
264     }
265 
266     /*
267      * Report whether we want to test a particular code in the current test mode.
268      * CTS is pretty much "test them all".
269      * MTS should only be testing codecs that are part of the swcodec module; all of these
270      * begin with "c2.android."
271      *
272      * Used in spots throughout the test suite where we want to limit our testing to relevant
273      * codecs. This avoids false alarms that are sometimes triggered by non-compliant,
274      * non-mainline codecs.
275      *
276      * @param name    the name of a codec
277      * @return {@code} true is the codec should be tested in the current operating mode.
278      */
isTestableCodecInCurrentMode(String name)279     public static boolean isTestableCodecInCurrentMode(String name) {
280         if (name == null) {
281             return true;
282         }
283         int mode = currentTestMode();
284         boolean result = false;
285         switch (mode) {
286             case TESTMODE_CTS:
287                 result = !isMainlineCodec(name);
288                 break;
289             case TESTMODE_MCTS:
290             case TESTMODE_MTS:
291                 result = isMainlineCodec(name);
292                 break;
293         }
294         Log.d(TAG, "codec " + name + (result ? " is " : " is not ")
295                    + "tested in mode " + currentTestModeName());
296         return result;
297     }
298 
299     /*
300      * Report whether this codec is a google-supplied codec that lives within the
301      * mainline modules.
302      *
303      * @param name    the name of a codec
304      * @return {@code} true if the codec is one that lives within the mainline boundaries
305      */
isMainlineCodec(String name)306     public static boolean isMainlineCodec(String name) {
307         if (name.startsWith("c2.android.")) {
308             return true;
309         }
310         return false;
311     }
312 
TestUtils()313     private TestUtils() {
314     }
315 
316     public static class Monitor {
317         private int mNumSignal;
318 
reset()319         public synchronized void reset() {
320             mNumSignal = 0;
321         }
322 
signal()323         public synchronized void signal() {
324             mNumSignal++;
325             notifyAll();
326         }
327 
waitForSignal()328         public synchronized boolean waitForSignal() throws InterruptedException {
329             return waitForCountedSignals(1) > 0;
330         }
331 
waitForCountedSignals(int targetCount)332         public synchronized int waitForCountedSignals(int targetCount) throws InterruptedException {
333             while (mNumSignal < targetCount) {
334                 wait();
335             }
336             return mNumSignal;
337         }
338 
waitForSignal(long timeoutMs)339         public synchronized boolean waitForSignal(long timeoutMs) throws InterruptedException {
340             return waitForCountedSignals(1, timeoutMs) > 0;
341         }
342 
waitForCountedSignals(int targetCount, long timeoutMs)343         public synchronized int waitForCountedSignals(int targetCount, long timeoutMs)
344                 throws InterruptedException {
345             if (timeoutMs == 0) {
346                 return waitForCountedSignals(targetCount);
347             }
348             long deadline = System.currentTimeMillis() + timeoutMs;
349             while (mNumSignal < targetCount) {
350                 long delay = deadline - System.currentTimeMillis();
351                 if (delay <= 0) {
352                     break;
353                 }
354                 wait(delay);
355             }
356             return mNumSignal;
357         }
358 
isSignalled()359         public synchronized boolean isSignalled() {
360             return mNumSignal >= 1;
361         }
362 
getNumSignal()363         public synchronized int getNumSignal() {
364             return mNumSignal;
365         }
366     }
367 }
368