OSDN Git Service

Add ResolverActivity testing
authorHakan Seyalioglu <hseya@google.com>
Tue, 17 Jan 2017 20:20:01 +0000 (12:20 -0800)
committerHakan Seyalioglu <hseya@google.com>
Mon, 23 Jan 2017 18:43:03 +0000 (10:43 -0800)
Still very basic, but will be adding as we find more issues pop up.

Test: Ran the tests
Change-Id: Ib9863509d140ff425ce446ecc876f53494d4fd0e

core/java/com/android/internal/app/ResolverActivity.java
core/java/com/android/internal/app/ResolverListController.java
core/tests/coretests/AndroidManifest.xml
core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java [new file with mode: 0644]
core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java [moved from core/tests/coretests/src/com/android/internal/app/ChooserDataProvider.java with 94% similarity]
core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java [new file with mode: 0644]

index d734d17..ab1d9b9 100644 (file)
@@ -749,10 +749,7 @@ public class ResolverActivity extends Activity {
                     }
                 } else {
                     try {
-                        AppGlobals.getPackageManager().setLastChosenActivity(intent,
-                                intent.resolveType(getContentResolver()),
-                                PackageManager.MATCH_DEFAULT_ONLY,
-                                filter, bestMatch, intent.getComponent());
+                        mAdapter.mResolverListController.setLastChosen(intent, filter, bestMatch);
                     } catch (RemoteException re) {
                         Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
                     }
@@ -1312,10 +1309,7 @@ public class ResolverActivity extends Activity {
         protected boolean rebuildList() {
             List<ResolvedComponentInfo> currentResolveList = null;
             try {
-                final Intent primaryIntent = getTargetIntent();
-                mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
-                        primaryIntent, primaryIntent.resolveTypeIfNeeded(getContentResolver()),
-                        PackageManager.MATCH_DEFAULT_ONLY);
+                mLastChosen = mResolverListController.getLastChosen();
             } catch (RemoteException re) {
                 Log.d(TAG, "Error calling getLastChosenActivity\n" + re);
             }
index f88f6f9..00faf65 100644 (file)
@@ -19,13 +19,16 @@ package com.android.internal.app;
 
 import android.annotation.WorkerThread;
 import android.app.ActivityManager;
+import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
 import android.util.Log;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -66,6 +69,22 @@ public class ResolverListController {
     }
 
     @VisibleForTesting
+    ResolveInfo getLastChosen() throws RemoteException {
+        return AppGlobals.getPackageManager().getLastChosenActivity(
+                mTargetIntent, mTargetIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                PackageManager.MATCH_DEFAULT_ONLY);
+    }
+
+    @VisibleForTesting
+    void setLastChosen(Intent intent, IntentFilter filter, int match)
+            throws RemoteException {
+        AppGlobals.getPackageManager().setLastChosenActivity(intent,
+                intent.resolveType(mContext.getContentResolver()),
+                PackageManager.MATCH_DEFAULT_ONLY,
+                filter, match, intent.getComponent());
+    }
+
+    @VisibleForTesting
     public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntent(
             boolean shouldGetResolvedFilter,
             boolean shouldGetActivityMetadata,
index cba485a..91ce7a4 100644 (file)
         <activity android:name="android.app.EmptyActivity">
         </activity>
         <activity android:name="com.android.internal.app.ChooserWrapperActivity"/>
+        <activity android:name="com.android.internal.app.ResolverWrapperActivity"/>
 
         <receiver android:name="android.app.activity.AbortReceiver">
             <intent-filter android:priority="1">
index aab4698..daebf88 100644 (file)
@@ -222,7 +222,7 @@ public class ChooserActivityTest {
     private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
         List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
         for (int i = 0; i < numberOfResults; i++) {
-            infoList.add(ChooserDataProvider.createResolvedComponentInfo(i));
+            infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
         }
         return infoList;
     }
index 41016e1..c446f3c 100644 (file)
@@ -24,11 +24,10 @@ import java.util.function.Function;
 
 import static org.mockito.Mockito.mock;
 
-
-/**
- * Simple wrapper around chooser activity to be able to initiate it under test
- */
 public class ChooserWrapperActivity extends ChooserActivity {
+    /*
+     * Simple wrapper around chooser activity to be able to initiate it under test
+     */
     static final OverrideData sOverrides = new OverrideData();
     private UsageStatsManager mUsm;
 
@@ -94,4 +93,4 @@ public class ChooserWrapperActivity extends ChooserActivity {
             resolverListController = mock(ResolverListController.class);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
new file mode 100644 (file)
index 0000000..84b844a
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 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.internal.app;
+
+import com.android.internal.R;
+import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static android.os.SystemClock.sleep;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Resolver activity instrumentation tests
+ */
+@RunWith(AndroidJUnit4.class)
+public class ResolverActivityTest {
+    @Rule
+    public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
+            new ActivityTestRule<>(ResolverWrapperActivity.class, false,
+                    false);
+
+    @Before
+    public void cleanOverrideData() {
+        sOverrides.reset();
+    }
+
+    @Test
+    public void twoOptionsAndUserSelectsOne() throws InterruptedException {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+
+        assertThat(activity.getAdapter().getCount(), is(2));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
+        onView(withText(toChoose.activityInfo.name))
+                .perform(click());
+        onView(withId(R.id.button_once))
+                .perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
+    @Test
+    public void hasLastChosenActivity() throws Exception {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+        when(sOverrides.resolverListController.getLastChosen())
+                .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+
+        // The other entry is filtered to the last used slot
+        assertThat(activity.getAdapter().getCount(), is(1));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
+        onView(withId(R.id.title)).perform(click());
+        onView(withId(R.id.button_once))
+                .perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
+    private Intent createSendImageIntent() {
+        Intent sendIntent = new Intent();
+        sendIntent.setAction(Intent.ACTION_SEND);
+        sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
+        sendIntent.setType("image/jpeg");
+        return sendIntent;
+    }
+
+    private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
+        List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+        for (int i = 0; i < numberOfResults; i++) {
+            infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
+        }
+        return infoList;
+    }
+
+    private void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+}
\ No newline at end of file
@@ -22,16 +22,15 @@ import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.os.UserHandle;
-import android.service.chooser.ChooserTarget;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * Utility class used by chooser tests to create mock data
+ * Utility class used by resolver tests to create mock data
  */
-class ChooserDataProvider {
+class ResolverDataProvider {
 
     static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfo(int i) {
         return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
new file mode 100644 (file)
index 0000000..163211e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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.internal.app;
+
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+
+import java.util.function.Function;
+
+import static org.mockito.Mockito.mock;
+
+
+/*
+ * Simple wrapper around chooser activity to be able to initiate it under test
+ */
+public class ResolverWrapperActivity extends ResolverActivity {
+    static final OverrideData sOverrides = new OverrideData();
+    private UsageStatsManager mUsm;
+
+    ResolveListAdapter getAdapter() {
+        return mAdapter;
+    }
+
+    @Override
+    public boolean isVoiceInteraction() {
+        if (sOverrides.isVoiceInteraction != null) {
+            return sOverrides.isVoiceInteraction;
+        }
+        return super.isVoiceInteraction();
+    }
+
+    @Override
+    public void safelyStartActivity(TargetInfo cti) {
+        if (sOverrides.onSafelyStartCallback != null &&
+                sOverrides.onSafelyStartCallback.apply(cti)) {
+            return;
+        }
+        super.safelyStartActivity(cti);
+    }
+
+    @Override
+    protected ResolverListController createListController() {
+        return sOverrides.resolverListController;
+    }
+
+    @Override
+    public PackageManager getPackageManager() {
+        if (sOverrides.createPackageManager != null) {
+            return sOverrides.createPackageManager.apply(super.getPackageManager());
+        }
+        return super.getPackageManager();
+    }
+
+    /**
+     * We cannot directly mock the activity created since instrumentation creates it.
+     * <p>
+     * Instead, we use static instances of this object to modify behavior.
+     */
+    static class OverrideData {
+        @SuppressWarnings("Since15")
+        public Function<PackageManager, PackageManager> createPackageManager;
+        public Function<TargetInfo, Boolean> onSafelyStartCallback;
+        public ResolverListController resolverListController;
+        public Boolean isVoiceInteraction;
+
+        public void reset() {
+            onSafelyStartCallback = null;
+            isVoiceInteraction = null;
+            createPackageManager = null;
+            resolverListController = mock(ResolverListController.class);
+        }
+    }
+}
\ No newline at end of file