1 /* 2 * Copyright (C) 2021 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 com.example.helloworld.lib; 18 19 import static org.hamcrest.MatcherAssert.assertThat; 20 import static org.hamcrest.core.IsEqual.equalTo; 21 import static org.hamcrest.core.IsInstanceOf.instanceOf; 22 import static org.junit.Assert.assertEquals; 23 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.res.AssetFileDescriptor; 29 import android.content.res.AssetManager; 30 import android.content.res.Resources; 31 import android.content.res.TypedArray; 32 import android.content.res.XmlResourceParser; 33 import android.os.Handler; 34 import android.os.HandlerThread; 35 import android.os.Process; 36 37 import com.android.compatibility.common.util.MatcherUtils; 38 import com.android.internal.util.XmlUtils; 39 40 import org.hamcrest.Matcher; 41 import org.hamcrest.Matchers; 42 import org.hamcrest.core.StringStartsWith; 43 import org.xmlpull.v1.XmlPullParser; 44 45 import java.io.FileNotFoundException; 46 import java.io.IOException; 47 import java.io.InputStream; 48 import java.util.concurrent.Semaphore; 49 import java.util.concurrent.TimeUnit; 50 51 public class TestUtils { 52 public static final String TEST_APP_PACKAGE = "com.example.helloworld"; 53 public static final String TEST_ACTIVITY_NAME = "com.example.helloworld.TestActivity"; 54 55 public static final String TEST_NAME_EXTRA_KEY = "TEST_NAME"; 56 public static final String TEST_ASSERT_SUCCESS_EXTRA_KEY = "ASSERT_SUCCESS"; 57 58 public static final String TEST_STATUS_ACTION = "android.content.pm.cts.TEST_STATUS_UPDATE"; 59 60 public static final String PID_STATUS_PID_KEY = "TEST_PID"; 61 public static final String TEST_STATUS_RESULT_KEY = "TEST_RESULT"; 62 public static final String TEST_STATUS_RESULT_SUCCESS = "Success"; 63 64 public static final String RES_XML_PATH = "res/xml/test_xml.xml"; 65 public static final String RES_XML_ATTRS_PATH = "res/xml/test_xml_attrs.xml"; 66 public static final String RES_DRAWABLE_HDPI_PATH = "res/drawable-hdpi-v4/background.jpg"; 67 public static final String RES_DRAWABLE_MDPI_PATH = "res/drawable-mdpi-v4/background.jpg"; 68 69 public static final String RES_IDENTIFIER = TEST_APP_PACKAGE + ":string/inc_string"; 70 71 public static final int RES_STRING = 0x7f021000; 72 public static final int RES_ARRAY = 0x7f051000; 73 public static final int RES_STYLE = 0x7f061000; 74 public static final int[] RES_STYLEABLE = {0x7f011000, 0x7f011001, 0x7f011002}; 75 76 public enum AssertionType { 77 ASSERT_SUCCESS, 78 ASSERT_READ_FAILURE, 79 } 80 81 public static final String TEST_GET_IDENTIFIER = "checkGetIdentifier"; 82 checkGetIdentifier(Resources res, AssertionType type)83 public static void checkGetIdentifier(Resources res, AssertionType type) throws Exception { 84 testReadSuccessAndFailure(type, 85 () -> res.getIdentifier(RES_IDENTIFIER, "", ""), 86 Matchers.not(equalTo(0)), equalTo(0)); 87 } 88 89 public static final String TEST_GET_RESOURCE_NAME = "checkGetResourceName"; 90 checkGetResourceName(Resources res, AssertionType type)91 public static void checkGetResourceName(Resources res, AssertionType type) throws Exception { 92 testReadSuccessAndFailureException(type, 93 () -> res.getResourceName(RES_STRING), 94 equalTo(RES_IDENTIFIER), instanceOf(Resources.NotFoundException.class)); 95 } 96 97 public static final String TEST_GET_STRING = "checkGetString"; 98 checkGetString(Resources res, AssertionType type)99 public static void checkGetString(Resources res, AssertionType type) throws Exception { 100 testReadSuccessAndFailureException(type, 101 () -> res.getString(RES_STRING), 102 equalTo("true"), instanceOf(Resources.NotFoundException.class)); 103 } 104 105 public static final String TEST_GET_STRING_ARRAY = "checkGetStringArray"; 106 checkGetStringArray(Resources res, AssertionType type)107 public static void checkGetStringArray(Resources res, AssertionType type) throws Exception { 108 testReadSuccessAndFailureException(type, 109 () -> res.getStringArray(RES_ARRAY), 110 equalTo(new String[]{"true"}), instanceOf(Resources.NotFoundException.class)); 111 } 112 113 public static final String TEST_OPEN_XML = "checkOpenXmlResourceParser"; 114 checkOpenXmlResourceParser(Resources res, AssertionType type)115 public static void checkOpenXmlResourceParser(Resources res, AssertionType type) 116 throws Exception { 117 testReadSuccessAndFailureException(type, 118 () -> { 119 final AssetManager assets = res.getAssets(); 120 try (XmlResourceParser p = assets.openXmlResourceParser(RES_XML_PATH)) { 121 XmlUtils.beginDocument(p, "Test"); 122 assertEquals(XmlPullParser.START_TAG, p.next()); 123 final String text = p.nextText(); 124 return text == null ? "" : text.trim(); 125 } 126 }, 127 StringStartsWith.startsWith("Lorem ipsum dolor"), 128 instanceOf(FileNotFoundException.class)); 129 } 130 131 public static final String TEST_APPLY_STYLE = "checkApplyStyle"; 132 checkApplyStyle(Resources res, AssertionType type)133 public static void checkApplyStyle(Resources res, AssertionType type) throws Exception { 134 testReadSuccessAndFailure(type, 135 () -> { 136 final Resources.Theme theme = res.newTheme(); 137 theme.applyStyle(RES_STYLE, true); 138 final TypedArray values = theme.obtainStyledAttributes(RES_STYLEABLE); 139 return new String[]{ 140 values.getString(0), 141 values.getString(1), 142 values.getString(2), 143 }; 144 }, 145 equalTo(new String[]{"true", "true", "1"}), 146 equalTo(new String[]{null, null, null})); 147 } 148 149 public static final String TEST_XML_ATTRIBUTES = "checkXmlAttributes"; 150 checkXmlAttributes(Resources res, AssertionType type)151 public static void checkXmlAttributes(Resources res, AssertionType type) throws Exception { 152 testReadSuccessAndFailure(type, 153 () -> { 154 final AssetManager assets = res.getAssets(); 155 try (XmlResourceParser p = assets.openXmlResourceParser(RES_XML_ATTRS_PATH)) { 156 XmlUtils.beginDocument(p, "Test"); 157 final TypedArray values = res.obtainAttributes(p, RES_STYLEABLE); 158 final String[] results = { 159 values.getString(0), 160 values.getString(1), 161 values.getString(2), 162 }; 163 values.recycle(); 164 return results; 165 } 166 }, 167 equalTo(new String[]{"true", "true", "1"}), 168 equalTo(new String[]{null, null, null})); 169 } 170 171 public static final String TEST_OPEN_FILE_MISSING = "checkOpenMissingFile"; 172 checkOpenMissingFile(Resources res, AssertionType type)173 public static void checkOpenMissingFile(Resources res, AssertionType type) throws Exception { 174 testReadSuccessAndFailureException(type, 175 () -> { 176 final AssetManager assets = res.getAssets(); 177 try (InputStream is = assets.openNonAsset(RES_DRAWABLE_HDPI_PATH)) { 178 return true; 179 } 180 }, 181 equalTo(true), instanceOf(FileNotFoundException.class)); 182 } 183 184 public static final String TEST_OPEN_FILE_FD_MISSING = "checkOpenMissingFdFile"; 185 checkOpenMissingFdFile(Resources res, AssertionType type)186 public static void checkOpenMissingFdFile(Resources res, AssertionType type) throws Exception { 187 testReadSuccessAndFailureException(type, 188 () -> { 189 final AssetManager assets = res.getAssets(); 190 try (AssetFileDescriptor is = assets.openNonAssetFd(RES_DRAWABLE_HDPI_PATH)) { 191 return true; 192 } 193 }, 194 equalTo(true), instanceOf(FileNotFoundException.class)); 195 } 196 197 private static class FailedWhileReadingException extends Exception { 198 } 199 200 public static final String TEST_OPEN_FILE = "checkOpen"; 201 checkOpen(Resources res, AssertionType type)202 public static void checkOpen(Resources res, AssertionType type) throws Exception { 203 testReadSuccessAndFailureException(type, 204 () -> { 205 final AssetManager assets = res.getAssets(); 206 try (InputStream is = assets.openNonAsset(RES_DRAWABLE_MDPI_PATH)) { 207 try { 208 readFullStream(is); 209 } catch (IOException e) { 210 throw new FailedWhileReadingException(); 211 } 212 return true; 213 } 214 }, equalTo(true), instanceOf(FailedWhileReadingException.class)); 215 } 216 217 public static final String TEST_OPEN_FILE_FD = "checkOpenFd"; 218 checkOpenFd(Resources res, AssertionType type)219 public static void checkOpenFd(Resources res, AssertionType type) throws Exception { 220 testReadSuccessAndFailureException(type, 221 () -> { 222 final AssetManager assets = res.getAssets(); 223 try (AssetFileDescriptor fd = assets.openNonAssetFd(RES_DRAWABLE_MDPI_PATH)) { 224 try { 225 readFullStream(fd.createInputStream()); 226 } catch (IOException e) { 227 throw new FailedWhileReadingException(); 228 } 229 return true; 230 } 231 }, equalTo(true), instanceOf(FailedWhileReadingException.class)); 232 } 233 234 /** 235 * Used to wait for the process to wait for a particular broadcast to be received. 236 */ 237 public static class BroadcastDetector implements AutoCloseable { 238 private final Context mContext; 239 private final HandlerThread mThread; 240 private final Handler mHandler; 241 private final BroadcastReceiver mReceiver; 242 private final Semaphore mLatch = new Semaphore(0); 243 private volatile Exception mException; 244 BroadcastDetector(Context context, IntentFilter intentFilter, IBroadcastCallback callback)245 public BroadcastDetector(Context context, IntentFilter intentFilter, 246 IBroadcastCallback callback) { 247 mContext = context; 248 mThread = new HandlerThread(callback.toString(), Process.THREAD_PRIORITY_FOREGROUND); 249 mThread.start(); 250 mHandler = new Handler(mThread.getLooper()); 251 mReceiver = new BroadcastReceiver() { 252 @Override 253 public void onReceive(Context context, Intent intent) { 254 try { 255 if (callback.onReceive(context, intent)) { 256 mLatch.release(); 257 } 258 } catch (Exception e) { 259 mException = e; 260 mLatch.release(); 261 } 262 } 263 }; 264 context.registerReceiver(mReceiver, intentFilter, null, mHandler, Context.RECEIVER_EXPORTED); 265 } 266 267 /** 268 * Returns true if the broadcast was received and 269 * {@link IBroadcastCallback#onReceive(Context, Intent)} returned true. 270 */ waitForBroadcast(long timeout, TimeUnit unit)271 public boolean waitForBroadcast(long timeout, TimeUnit unit) 272 throws Exception { 273 if (!mLatch.tryAcquire(timeout, unit)) { 274 return false; 275 } 276 if (mException != null) { 277 throw mException; 278 } 279 return true; 280 } 281 282 @Override close()283 public void close() { 284 mContext.unregisterReceiver(mReceiver); 285 mThread.quit(); 286 } 287 } 288 289 public interface IBroadcastCallback { onReceive(Context context, Intent intent)290 boolean onReceive(Context context, Intent intent) throws Exception; 291 } 292 293 private interface ThrowingFunction<R> { apply()294 R apply() throws Exception; 295 } 296 testReadSuccessAndFailure(AssertionType assertType, ThrowingFunction<T> getValue, Matcher<? super T> checkSuccess, Matcher<? super T> checkFailure)297 private static <T> void testReadSuccessAndFailure(AssertionType assertType, 298 ThrowingFunction<T> getValue, 299 Matcher<? super T> checkSuccess, 300 Matcher<? super T> checkFailure) throws Exception { 301 final T value = getValue.apply(); 302 if (assertType == AssertionType.ASSERT_SUCCESS) { 303 assertThat(value, checkSuccess); 304 } else { 305 assertThat(value, checkFailure); 306 } 307 } 308 testReadSuccessAndFailureException(AssertionType assertType, ThrowingFunction<T> getValue, Matcher<? super T> checkSuccess, Matcher<Throwable> checkFailure)309 private static <T> void testReadSuccessAndFailureException(AssertionType assertType, 310 ThrowingFunction<T> getValue, 311 Matcher<? super T> checkSuccess, 312 Matcher<Throwable> checkFailure) throws Exception { 313 if (assertType == AssertionType.ASSERT_SUCCESS) { 314 assertThat(getValue.apply(), checkSuccess); 315 } else { 316 MatcherUtils.assertThrows(checkFailure, getValue::apply); 317 } 318 } 319 readFullStream(InputStream is)320 private static void readFullStream(InputStream is) throws IOException { 321 final byte[] buffer = new byte[1024]; 322 while (is.read(buffer) != -1) { 323 } 324 } 325 } 326