summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--media/tests/mediatestutils/Android.bp6
-rw-r--r--media/tests/mediatestutils/TEST_MAPPING7
-rw-r--r--media/tests/mediatestutils/java/com/android/media/mediatestutils/TestUtils.java89
-rw-r--r--media/tests/mediatestutils/tests/Android.bp22
-rw-r--r--media/tests/mediatestutils/tests/AndroidManifest.xml30
-rw-r--r--media/tests/mediatestutils/tests/src/java/com/android/media/mediatestutils/GetFutureForIntentTest.java108
6 files changed, 261 insertions, 1 deletions
diff --git a/media/tests/mediatestutils/Android.bp b/media/tests/mediatestutils/Android.bp
index e50e69ac6f4f..15bc1774d4d4 100644
--- a/media/tests/mediatestutils/Android.bp
+++ b/media/tests/mediatestutils/Android.bp
@@ -24,9 +24,13 @@ java_library {
java_library {
name: "mediatestutils",
+ srcs: [
+ "java/com/android/media/mediatestutils/TestUtils.java",
+ ],
static_libs: [
+ "androidx.concurrent_concurrent-futures",
+ "guava",
"mediatestutils_host",
- "junit",
],
visibility: [
"//cts/tests/tests/media:__subpackages__",
diff --git a/media/tests/mediatestutils/TEST_MAPPING b/media/tests/mediatestutils/TEST_MAPPING
new file mode 100644
index 000000000000..6dd09ae9c501
--- /dev/null
+++ b/media/tests/mediatestutils/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "mediatestutilstests"
+ }
+ ]
+}
diff --git a/media/tests/mediatestutils/java/com/android/media/mediatestutils/TestUtils.java b/media/tests/mediatestutils/java/com/android/media/mediatestutils/TestUtils.java
new file mode 100644
index 000000000000..73dbe6780fec
--- /dev/null
+++ b/media/tests/mediatestutils/java/com/android/media/mediatestutils/TestUtils.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.mediatestutils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+/**
+ *
+ */
+public class TestUtils {
+ public static final String TAG = "MediaTestUtils";
+
+ public static ListenableFuture<Intent> getFutureForIntent(Context context, String action,
+ Predicate<Intent> pred) {
+ // These are evaluated async
+ Objects.requireNonNull(action);
+ Objects.requireNonNull(pred);
+ // Doesn't need to be thread safe since the resolver is called inline
+ final WeakReference<BroadcastReceiver> wrapper[] = new WeakReference[1];
+ ListenableFuture<Intent> future = CallbackToFutureAdapter.getFuture(completer -> {
+ var receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ if (action.equals(intent.getAction()) && pred.test(intent)) {
+ completer.set(intent);
+ }
+ } catch (Exception e) {
+ completer.setException(e);
+ }
+ }
+ };
+ wrapper[0] = new WeakReference(receiver);
+ context.registerReceiver(receiver, new IntentFilter(action),
+ Context.RECEIVER_NOT_EXPORTED);
+ return "Intent receiver future for ";
+ });
+ if (wrapper[0] == null) {
+ throw new AssertionError("CallbackToFutureAdapter resolver should be called inline");
+ }
+ final var weakref = wrapper[0];
+ future.addListener(() -> {
+ try {
+ var recv = weakref.get();
+ // If there is no reference left, the receiver has already been unregistered
+ if (recv != null) {
+ context.unregisterReceiver(recv);
+ return;
+ }
+ } catch (IllegalArgumentException e) {
+ // Receiver already unregistered, nothing to do.
+ }
+ Log.d(TAG, "Intent receiver future for action: " + action +
+ "unregistered prior to future completion/cancellation.");
+ } , MoreExecutors.directExecutor()); // Direct executor is fine since lightweight
+ return future;
+ }
+}
diff --git a/media/tests/mediatestutils/tests/Android.bp b/media/tests/mediatestutils/tests/Android.bp
new file mode 100644
index 000000000000..24a8360d1187
--- /dev/null
+++ b/media/tests/mediatestutils/tests/Android.bp
@@ -0,0 +1,22 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "mediatestutilstests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "mockito-target-minus-junit4",
+ "androidx.test.runner",
+ "androidx.test.core",
+ "mediatestutils",
+ "junit",
+ "truth",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/media/tests/mediatestutils/tests/AndroidManifest.xml b/media/tests/mediatestutils/tests/AndroidManifest.xml
new file mode 100644
index 000000000000..662bc45abc5d
--- /dev/null
+++ b/media/tests/mediatestutils/tests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.media.mediatestutils">
+
+ <application android:testOnly="false"
+ android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.media.mediatestutils"
+ android:label="mediatestutils tests" />
+
+</manifest>
diff --git a/media/tests/mediatestutils/tests/src/java/com/android/media/mediatestutils/GetFutureForIntentTest.java b/media/tests/mediatestutils/tests/src/java/com/android/media/mediatestutils/GetFutureForIntentTest.java
new file mode 100644
index 000000000000..70b46ad86448
--- /dev/null
+++ b/media/tests/mediatestutils/tests/src/java/com/android/media/mediatestutils/GetFutureForIntentTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.mediatestutils;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.media.mediatestutils.TestUtils.getFutureForIntent;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemClock;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.ExecutionException;
+import java.util.function.Predicate;
+
+@RunWith(AndroidJUnit4.class)
+public class GetFutureForIntentTest {
+ public static final String INTENT_ACTION = "com.android.media.mediatestutils.TEST_ACTION";
+ public static final String INTENT_EXTRA = "com.android.media.mediatestutils.TEST_EXTRA";
+ public static final int MAGIC_VALUE = 7;
+
+ public final Context mContext = getApplicationContext();
+ public final Predicate<Intent> mPred =
+ i -> (i != null) && (i.getIntExtra(INTENT_EXTRA, -1) == MAGIC_VALUE);
+
+ @Test
+ public void futureCompletes_afterBroadcastFiresPredicatePasses() throws Exception {
+ final var future = getFutureForIntent(mContext, INTENT_ACTION, mPred);
+ sendIntent(true);
+ var intent = future.get();
+ assertThat(intent.getAction()).isEqualTo(INTENT_ACTION);
+ assertThat(intent.getIntExtra(INTENT_EXTRA, -1)).isEqualTo(MAGIC_VALUE);
+ }
+
+ @Test
+ public void futureDoesNotComplete_afterBroadcastFiresPredicateFails() throws Exception {
+ final var future = getFutureForIntent(mContext, INTENT_ACTION, mPred);
+ sendIntent(false);
+
+ // Wait a bit, and ensure the future hasn't completed
+ SystemClock.sleep(100);
+ assertThat(future.isDone()).isFalse();
+
+ // Future should still respond to subsequent passing intent
+ sendIntent(true);
+ var intent = future.get();
+ assertThat(intent.getAction()).isEqualTo(INTENT_ACTION);
+ assertThat(intent.getIntExtra(INTENT_EXTRA, -1)).isEqualTo(MAGIC_VALUE);
+ }
+
+ @Test
+ public void futureCompletesExceptionally_afterBroadcastFiresPredicateThrows() throws Exception {
+ final var future =
+ getFutureForIntent(
+ mContext,
+ INTENT_ACTION,
+ i -> {
+ throw new IllegalStateException();
+ });
+
+ sendIntent(true);
+ try {
+ var intent = future.get();
+ fail("Exception expected if predicate throws");
+ } catch (ExecutionException e) {
+ assertThat(e.getCause().getClass()).isEqualTo(IllegalStateException.class);
+ }
+ }
+
+ @Test
+ public void doesNotThrow_whenDoubleSet() throws Exception {
+ final var future = getFutureForIntent(mContext, INTENT_ACTION, mPred);
+ sendIntent(true);
+ sendIntent(true);
+ var intent = future.get();
+ assertThat(intent.getAction()).isEqualTo(INTENT_ACTION);
+ assertThat(intent.getIntExtra(INTENT_EXTRA, -1)).isEqualTo(MAGIC_VALUE);
+ }
+
+ private void sendIntent(boolean correctValue) {
+ final Intent intent = new Intent(INTENT_ACTION).setPackage(mContext.getPackageName());
+ intent.putExtra(INTENT_EXTRA, correctValue ? MAGIC_VALUE : MAGIC_VALUE + 1);
+ mContext.sendBroadcast(intent);
+ }
+}