From d90403ff078159f161bdc0e12860e2615852322e Mon Sep 17 00:00:00 2001 From: Erica Chang Date: Thu, 14 Apr 2016 12:19:08 -0700 Subject: Contacts InCall metrics test -added espresso & uiautomator for UI automation -added test cases for metrics -INAPP_NUDGE_CONTACTS_TAB_LOGIN -DIRECTORY_SEARCH (from contacts tab) -CONTACTS_MANUAL_MERGED -CONTACTS_AUTO_MERGED -INVITES_SENT -DIRECTORY_SEARCH -INAPP_NUDGE_CONTACTS_LOGIN -INAPP_NUDGE_CONTACTS_INSTALL Change-Id: I86e5827b19ed4f313d55902198d5f3ccb25b20e8 --- androidTest/.gitignore | 2 + androidTest/Android.mk | 49 +++ androidTest/AndroidManifest.xml | 22 + androidTest/README.md | 36 ++ androidTest/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 4811 bytes androidTest/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 3033 bytes androidTest/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 6887 bytes androidTest/res/values/strings.xml | 19 + .../androidtest/InCallMetricsContactTest.java | 460 +++++++++++++++++++++ .../androidtest/InCallMetricsSendTest.java | 131 ++++++ .../androidtest/InCallMetricsTestDbUtils.java | 103 +++++ .../androidtest/InCallMetricsTestUtils.java | 264 ++++++++++++ 12 files changed, 1086 insertions(+) create mode 100644 androidTest/.gitignore create mode 100644 androidTest/Android.mk create mode 100644 androidTest/AndroidManifest.xml create mode 100755 androidTest/README.md create mode 100644 androidTest/res/drawable-hdpi/ic_launcher.png create mode 100644 androidTest/res/drawable-mdpi/ic_launcher.png create mode 100644 androidTest/res/drawable-xhdpi/ic_launcher.png create mode 100644 androidTest/res/values/strings.xml create mode 100644 androidTest/src/com/android/contacts/androidtest/InCallMetricsContactTest.java create mode 100644 androidTest/src/com/android/contacts/androidtest/InCallMetricsSendTest.java create mode 100644 androidTest/src/com/android/contacts/androidtest/InCallMetricsTestDbUtils.java create mode 100644 androidTest/src/com/android/contacts/androidtest/InCallMetricsTestUtils.java (limited to 'androidTest') diff --git a/androidTest/.gitignore b/androidTest/.gitignore new file mode 100644 index 000000000..abba8972d --- /dev/null +++ b/androidTest/.gitignore @@ -0,0 +1,2 @@ +/gen +/*.iml diff --git a/androidTest/Android.mk b/androidTest/Android.mk new file mode 100644 index 000000000..973ef102d --- /dev/null +++ b/androidTest/Android.mk @@ -0,0 +1,49 @@ +# +# Copyright (C) 2016 The CyanogenMod 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +LOCAL_STATIC_JAVA_LIBRARIES := espresso \ + contacts-ub-uiautomator + +# Include all test java files. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +# Notice that we don't have to include the src files of Contacts because, by +# running the tests using an instrumentation targeting Contacts, we +# automatically get all of its classes loaded into our environment. + +LOCAL_PACKAGE_NAME := ContactsAndroidTests + +LOCAL_INSTRUMENTATION_FOR := Contacts + +LOCAL_CERTIFICATE := shared + +LOCAL_PROGUARD_ENABLED := disabled + +include $(BUILD_PACKAGE) + +include $(CLEAR_VARS) + +LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \ + espresso:../../../../prebuilts/misc/common/android-support-test/espresso-core.jar \ + contacts-ub-uiautomator:../../../../prebuilts/misc/common/ub-uiautomator/ub-uiautomator.jar + +include $(BUILD_MULTI_PREBUILT) diff --git a/androidTest/AndroidManifest.xml b/androidTest/AndroidManifest.xml new file mode 100644 index 000000000..a9304831e --- /dev/null +++ b/androidTest/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/androidTest/README.md b/androidTest/README.md new file mode 100755 index 000000000..e2902df7d --- /dev/null +++ b/androidTest/README.md @@ -0,0 +1,36 @@ +To run the espresso tests: + +# Test setup +## 1) Build the binary first +packages/apps/Contacts/androidtest/mm +## 2) Install the test apk on target device +adb install -r $OUT/target/product/[device]/data/app/ContactsAndroidTests/ContactsAndroidTests.apk +## 3) Install an InCall plugin, its dependency app and sign in. It is + recommended to use a test version of InCall plugin that supports the + ACTION "com.android.contacts.androidtest.AUTH_STATE" to help with + the plugin state automation. test1 ~ test6 uses this ACTION to + automate the plugin authentication state + +# Execute InCallMetricsContactTest (all Contact UI related) tests +* test1: CONTACTS_MANUAL_MERGED +* test2: CONTACTS_AUTO_MERGED +* test3: INVITES_SENT +* test4: DIRECTORY_SEARCH (from Contacts card) +* test5: INAPP_NUDGE_CONTACTS_LOGIN +* test6: DIRECTORY_SEARCH (from Contacts plugin tab) +* test7: INAPP_NUDGE_CONTACTS_TAB_LOGIN (precondition: hard signed out) +* test8: INAPP_NUDGE_CONTACTS_INSTALL (precondition: uninstall dependency) +adb shell am instrument -w -e class com.android.contacts.androidtest.InCallMetricsContactTest \ +com.android.contacts.androidtest/android.support.test.runner.AndroidJUnitRunner + +## Note. It's best to run test cases individually since they have +preconditions such as the auth state needs to be signed in/signed +out, or plugin state needs to be ENABLED or HIDDEN (eg. test1 in InCallMetricsContactTest) +adb shell am instrument -w -e class com.android.contacts.androidtest.InCallMetricsContactTest#test1 \ +com.android.contacts.androidtest/android.support.test.runner.AndroidJUnitRunner + +# Execute InCallMetricsSendTest (auto generate metrics and send) tests +adb shell am instrument -w -e class com.android.contacts.androidtest.InCallMetricsSendTest \ +com.android.contacts.androidtest/android.support.test.runner.AndroidJUnitRunner + + diff --git a/androidTest/res/drawable-hdpi/ic_launcher.png b/androidTest/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..6705a79da Binary files /dev/null and b/androidTest/res/drawable-hdpi/ic_launcher.png differ diff --git a/androidTest/res/drawable-mdpi/ic_launcher.png b/androidTest/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..7d7c4881a Binary files /dev/null and b/androidTest/res/drawable-mdpi/ic_launcher.png differ diff --git a/androidTest/res/drawable-xhdpi/ic_launcher.png b/androidTest/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..51e4604bd Binary files /dev/null and b/androidTest/res/drawable-xhdpi/ic_launcher.png differ diff --git a/androidTest/res/values/strings.xml b/androidTest/res/values/strings.xml new file mode 100644 index 000000000..9f5465f5f --- /dev/null +++ b/androidTest/res/values/strings.xml @@ -0,0 +1,19 @@ + + + + Contacts InCall Metrics Test + diff --git a/androidTest/src/com/android/contacts/androidtest/InCallMetricsContactTest.java b/androidTest/src/com/android/contacts/androidtest/InCallMetricsContactTest.java new file mode 100644 index 000000000..26bd36010 --- /dev/null +++ b/androidTest/src/com/android/contacts/androidtest/InCallMetricsContactTest.java @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.contacts.androidtest; + +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.action.ViewActions.swipeLeft; +import static android.support.test.espresso.action.ViewActions.swipeRight; +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.matcher.ViewMatchers.withId; + +import android.content.Context; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.support.test.espresso.NoMatchingViewException; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject; +import android.support.test.uiautomator.UiSelector; +import android.support.test.uiautomator.UiObjectNotFoundException; + +import android.net.Uri; +import android.support.test.rules.ActivityTestRule; +import android.test.suitebuilder.annotation.MediumTest; +import android.util.Log; + +import com.android.contacts.activities.PeopleActivity; +import com.android.contacts.ContactSaveService; +import com.android.contacts.incall.InCallMetricsDbHelper; +import com.android.contacts.incall.InCallMetricsHelper; +import com.android.contacts.R; + +import com.android.phone.common.incall.CallMethodInfo; +import com.android.phone.common.incall.ContactsDataSubscription; +import com.android.phone.common.incall.utils.CallMethodFilters; + +import org.hamcrest.Matcher; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Set; + +@RunWith(AndroidJUnit4.class) +@MediumTest +public class InCallMetricsContactTest { + private static final String TAG = InCallMetricsContactTest.class.getSimpleName(); + private Context mContext; + private PeopleActivity mActivity; + private CallMethodInfo mCm; + private UiDevice mDevice; + + private static final String TESTER1_NAME = "Tester1"; + private static final String TESTER2_NAME = "Tester2"; + private static final String TESTER1_PHONE = "777-777-7777"; + private static final String TESTER2_PHONE = "123-456-7890"; + private static final String MENU_MERGE = "Merge"; + private static final String SIGN_IN = "Sign in"; + private static final String GET = "Get"; + + @Rule + public ActivityTestRule mActivityRule + = new ActivityTestRule(PeopleActivity.class); + + @Before + public void setUp() { + // get Activity handle under test + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + // get CallMethodInfo under test + //InCallMetricsTestUtils.waitFor(500); + HashMap cmMap = + CallMethodFilters.getAllEnabledAndHiddenCallMethods( + ContactsDataSubscription.get(mContext)); + Assert.assertNotNull(cmMap); + Set cmKeySet = cmMap.keySet(); + if (cmKeySet.size() == 0) { + Log.d(TAG, "No InCall plugin installed"); + return; + } + // test the first plugin only + ComponentName cn = cmKeySet.iterator().next(); + mCm = cmMap.get(cn); + Assert.assertNotNull(mCm); + + // Initialize UiDevice instance + mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + } + + /* + * test metrics: CONTACTS_MANUAL_MERGED + * precondition : signed in + */ + @Test + public void test1() { + mActivity = (PeopleActivity) mActivityRule.getActivity(); + String testName = InCallMetricsHelper.Events.CONTACTS_MANUAL_MERGED.value(); + Log.d(TAG, "-----test1 start -----" + testName); + InCallMetricsTestUtils.setInCallPluginAuthState(mContext, true); + InCallMetricsTestUtils.waitFor(100); + + // clear out db + Assert.assertTrue(InCallMetricsTestDbUtils.clearAllEntries(mActivity)); + // check current impression count + // show contacts tab login 2x + // select InCall plugin tab + ContentResolver cr = mActivity.getContentResolver(); + Uri contactUri1 = + InCallMetricsTestUtils.createContact(null, TESTER1_NAME, TESTER1_PHONE, cr); + Uri contactUri2 = + InCallMetricsTestUtils.createContact(null, TESTER2_NAME, TESTER2_PHONE, cr); + + InCallMetricsTestUtils.openContactCard(mActivity, contactUri1); + InCallMetricsTestUtils.waitFor(1000); + + // Click on edit + UiObject editButton = mDevice.findObject(new UiSelector() + .resourceId("com.android.contacts:id/menu_edit")); + try { + if (editButton.exists()) { + editButton.click(); + InCallMetricsTestUtils.waitFor(1000); + // Open menu + mDevice.pressMenu(); + InCallMetricsTestUtils.waitFor(1000); + // Click on merge + UiObject mergeMenu = mDevice.findObject(new UiSelector() + .text(MENU_MERGE)); + if (mergeMenu.exists()) { + mergeMenu.click(); + InCallMetricsTestUtils.waitFor(1000); + // Select the merge target contact + UiObject mergeContact = mDevice.findObject(new UiSelector() + .text(TESTER2_NAME)); + if (mergeContact.exists()) { + mergeContact.click(); + } else { + Log.d(TAG, "ERROR: mergeCount does not exist"); + } + } + } else { + Log.d(TAG, "ERROR: edit button not found"); + } + InCallMetricsTestUtils.waitFor(100); + ContentValues entry = InCallMetricsTestDbUtils.getEntry(mActivity, + InCallMetricsDbHelper.Tables.USER_ACTIONS_TABLE, + InCallMetricsHelper.Events.CONTACTS_MANUAL_MERGED); + InCallMetricsTestUtils.verifyUserActionsMetrics(testName, entry, 1); + } catch (UiObjectNotFoundException e) { + Log.d(TAG, "ERROR: No matching view"); + } + // get uri and return uri + Uri mergedUri = InCallMetricsTestUtils.findContactUriByDisplayName(TESTER1_NAME, cr); + + // clean up contact + mActivity.startService(ContactSaveService.createDeleteContactIntent(mActivity, mergedUri)); + Log.d(TAG, "-----test1 finish -----"); + } + + /* + * test metrics: CONTACTS_AUTO_MERGED + * precondition : signed in + */ + @Test + public void test2() { + mActivity = (PeopleActivity) mActivityRule.getActivity(); + String testName = InCallMetricsHelper.Events.CONTACTS_AUTO_MERGED.value(); + Log.d(TAG, "-----test2 start -----" + testName); + InCallMetricsTestUtils.setInCallPluginAuthState(mActivity, true); + InCallMetricsTestUtils.waitFor(100); + // clear out db + Assert.assertTrue(InCallMetricsTestDbUtils.clearAllEntries(mActivity)); + + // query + ContentResolver cr = mActivity.getContentResolver(); + Uri matchUri = InCallMetricsTestUtils.createContact(null, TESTER1_NAME, TESTER1_PHONE, cr); + Uri contactUri = + InCallMetricsTestUtils.createContact(null, TESTER1_NAME, TESTER1_PHONE, cr); + + InCallMetricsTestUtils.waitFor(500); + + // check count + ContentValues entry = InCallMetricsTestDbUtils.getEntry(mActivity, + InCallMetricsDbHelper.Tables.USER_ACTIONS_TABLE, + InCallMetricsHelper.Events.CONTACTS_AUTO_MERGED); + InCallMetricsTestUtils.verifyUserActionsMetrics(testName, entry, 1); + + // clean up contact + InCallMetricsTestUtils.deleteContact(contactUri, cr); + InCallMetricsTestUtils.deleteContact(matchUri, cr); + Log.d(TAG, "-----test2 finish -----"); + } + + /* + * test metrics: INVITES_SENT + * precondition : signed in + */ + @Test + public void test3() { + mActivity = (PeopleActivity) mActivityRule.getActivity(); + String testName = InCallMetricsHelper.Events.INVITES_SENT.value(); + Log.d(TAG, "-----test3 start -----" + testName); + // precondition signed in + InCallMetricsTestUtils.setInCallPluginAuthState(mActivity, true); + InCallMetricsTestUtils.waitFor(100); + // clear out db + Assert.assertTrue(InCallMetricsTestDbUtils.clearAllEntries(mActivity)); + + // launch activity + ContentResolver cr = mActivity.getContentResolver(); + Uri contactUri = + InCallMetricsTestUtils.createContact(null, TESTER1_NAME, TESTER1_PHONE, cr); + InCallMetricsTestUtils.openContactCard(mActivity, contactUri); + InCallMetricsTestUtils.waitFor(1000); + try { + UiObject inviteButton = mDevice.findObject(new UiSelector().text( + mActivity.getResources() + .getString(R.string.incall_plugin_invite))); + if (inviteButton.exists()) { + inviteButton.click(); + InCallMetricsTestUtils.waitFor(100); + // check count + ContentValues entry = InCallMetricsTestDbUtils.getEntry(mActivity, + InCallMetricsDbHelper.Tables.USER_ACTIONS_TABLE, + InCallMetricsHelper.Events.INVITES_SENT); + InCallMetricsTestUtils.verifyUserActionsMetrics(testName, entry, 1); + } else { + Log.d(TAG, "ERROR: No matching view"); + } + } catch (UiObjectNotFoundException e) { + Log.d(TAG, "ERROR: invite not found"); + } + // clean up contact + InCallMetricsTestUtils.deleteContact(contactUri, cr); + Log.d(TAG, "-----test3 finish -----"); + } + + /* + * test metrics: DIRECTORY_SEARCH + * precondition : signed in + */ + @Test + public void test4() { + mActivity = (PeopleActivity) mActivityRule.getActivity(); + String testName = InCallMetricsHelper.Events.DIRECTORY_SEARCH.value(); + Log.d(TAG, "-----test4 start -----" + testName); + // precondition: signed in + InCallMetricsTestUtils.setInCallPluginAuthState(mActivity, true); + InCallMetricsTestUtils.waitFor(100); + + Assert.assertTrue(InCallMetricsTestDbUtils.clearAllEntries(mActivity)); + ContentResolver cr = mActivity.getContentResolver(); + Uri contactUri = + InCallMetricsTestUtils.createContact(null, TESTER1_NAME, TESTER1_PHONE, cr); + InCallMetricsTestUtils.openContactCard(mActivity, contactUri); + InCallMetricsTestUtils.waitFor(1000); + try { + UiObject searchButton = mDevice.findObject(new UiSelector().text( + mActivity.getResources() + .getString(R.string.incall_plugin_directory_search, mCm.mName))); + if (searchButton.exists()) { + searchButton.click(); + InCallMetricsTestUtils.waitFor(100); + // check count + ContentValues entry = InCallMetricsTestDbUtils.getEntry(mActivity, + InCallMetricsDbHelper.Tables.USER_ACTIONS_TABLE, + InCallMetricsHelper.Events.DIRECTORY_SEARCH); + InCallMetricsTestUtils.verifyUserActionsMetrics(testName, entry, 1); + } else { + Log.d(TAG, "ERROR: search not found"); + } + } catch (UiObjectNotFoundException e) { + Log.d(TAG, "ERROR: No matching view"); + } + // clean up contact + InCallMetricsTestUtils.deleteContact(contactUri, cr); + Log.d(TAG, "-----test4 finish -----"); + } + /* + * test metrics: INAPP_NUDGE_CONTACTS_LOGIN + * precondition : signed out + */ + @Test + public void test5() { + mActivity = (PeopleActivity) mActivityRule.getActivity(); + String testName = InCallMetricsHelper.Events.INAPP_NUDGE_CONTACTS_LOGIN.value(); + Log.d(TAG, "-----test5 start -----" + testName); + + // sign out + InCallMetricsTestUtils.setInCallPluginAuthState(mActivity, false); + InCallMetricsTestUtils.waitFor(100); + + ContentResolver cr = mActivity.getContentResolver(); + Assert.assertTrue(InCallMetricsTestDbUtils.clearAllEntries(mActivity)); + Uri contactUri = + InCallMetricsTestUtils.createContact(null, TESTER1_NAME, TESTER1_PHONE, cr); + InCallMetricsTestUtils.openContactCard(mActivity, contactUri); + InCallMetricsTestUtils.waitFor(1000); + try { + UiObject signinButton = mDevice.findObject(new UiSelector().text(SIGN_IN)); + if (signinButton.exists()) { + signinButton.click(); + InCallMetricsTestUtils.waitFor(100); + // check count + ContentValues entry = InCallMetricsTestDbUtils.getEntry(mActivity, + InCallMetricsDbHelper.Tables.INAPP_TABLE, + InCallMetricsHelper.Events.INAPP_NUDGE_CONTACTS_LOGIN); + InCallMetricsTestUtils.verifyUserActionsMetrics(testName, entry, 1); + } else { + Log.d(TAG, "ERROR: " + SIGN_IN + " not found"); + } + } catch (UiObjectNotFoundException e) { + Log.d(TAG, "ERROR: No matching view"); + } + // clean up contact + InCallMetricsTestUtils.deleteContact(contactUri, cr); + + Log.d(TAG, "-----test5 finish -----"); + } + + /* + * test metrics: DIRECTORY_SEARCH (from contacts plugin tab) + * precondition : signed in + */ + @Test + public void test6() { + mActivity = (PeopleActivity) mActivityRule.getActivity(); + String testName = InCallMetricsHelper.Events.DIRECTORY_SEARCH.value(); + Log.d(TAG, "-----test6 start -----" + testName); + // clear out db + Assert.assertTrue(InCallMetricsTestDbUtils.clearAllEntries(mActivity)); + InCallMetricsTestUtils.setInCallPluginAuthState(mActivity, true); + InCallMetricsTestUtils.waitFor(100); + try { + // focus on plugin tab + onView(withId(R.id.tab_pager)).perform(swipeLeft()); + + // click on directory search button + onView(withId(R.id.floating_action_button)).perform(click()); + InCallMetricsTestUtils.waitFor(100); + ContentValues entry = InCallMetricsTestDbUtils.getEntry(mActivity, + InCallMetricsDbHelper.Tables.USER_ACTIONS_TABLE, + InCallMetricsHelper.Events.DIRECTORY_SEARCH); + InCallMetricsTestUtils.verifyUserActionsMetrics(testName, entry, 1); + } catch (NoMatchingViewException e) { + Log.d(TAG, "ERROR: No matching view"); + } + Log.d(TAG, "-----test6 finish -----"); + } + + /* + * test metrics: INAPP_NUDGE_CONTACTS_TAB_LOGIN + * precondition : hard signed out + */ + @Test + public void test7() { + mActivity = (PeopleActivity) mActivityRule.getActivity(); + String testName = InCallMetricsHelper.Events.INAPP_NUDGE_CONTACTS_TAB_LOGIN.value(); + Log.d(TAG, "-----test7 start -----" + testName); + Log.d(TAG, "Please hard sign out..."); + // clear out db + Assert.assertTrue(InCallMetricsTestDbUtils.clearAllEntries(mActivity)); + InCallMetricsTestUtils.setInCallPluginAuthState(mActivity, false); + InCallMetricsTestUtils.waitFor(100); + Log.d(TAG, "inAppNudgeContactsTabLogin cm:" + mCm.mName); + + // check current impression count + // show contacts tab login 2x + // select InCall plugin tab + try { + onView(withId(R.id.tab_pager)).perform(swipeLeft()); + InCallMetricsTestUtils.waitFor(1000); + // swipe away + onView(withId(R.id.tab_pager)).perform(swipeRight()); + // select InCall plugin tab again + onView(withId(R.id.tab_pager)).perform(swipeLeft()); + InCallMetricsTestUtils.waitFor(1000); + + long currentTime = System.currentTimeMillis(); + + onView(withId(R.id.plugin_login_button)).perform(click()); + InCallMetricsTestUtils.waitFor(100); + ContentValues entry = InCallMetricsTestDbUtils.getEntry(mActivity, + InCallMetricsDbHelper.Tables.INAPP_TABLE, + InCallMetricsHelper.Events.INAPP_NUDGE_CONTACTS_TAB_LOGIN); + InCallMetricsTestUtils.verifyInAppMetrics(testName, entry, 2, currentTime); + + } catch (NoMatchingViewException e) { + Log.d(TAG, "ERROR: No matching view"); + } + Log.d(TAG, "-----test7 finish -----"); + } + /* + * test metrics: INAPP_NUDGE_CONTACTS_INSTALL + * precondition : plugin hidden (uninstall dependency) + */ + @Test + public void test8() { + mActivity = (PeopleActivity) mActivityRule.getActivity(); + String testName = InCallMetricsHelper.Events.INAPP_NUDGE_CONTACTS_INSTALL.value(); + Log.d(TAG, "-----test8 start -----" + testName); + Log.d(TAG, "Please uninsatll InCall plugin dependency package...(waiting 10 seconds)"); + InCallMetricsTestUtils.setInCallPluginAuthState(mActivity, true); + InCallMetricsTestUtils.waitFor(100); + + ContentResolver cr = mActivity.getContentResolver(); + InCallMetricsTestDbUtils.clearAllEntries(mActivity); + Uri contactUri = + InCallMetricsTestUtils.createContact(null, TESTER1_NAME, TESTER1_PHONE, cr); + InCallMetricsTestUtils.openContactCard(mActivity, contactUri); + InCallMetricsTestUtils.waitFor(1000); + try { + UiObject button = mDevice.findObject(new UiSelector().text(GET)); + if (button.exists()) { + button.click(); + InCallMetricsTestUtils.waitFor(100); + // check count + ContentValues entry = InCallMetricsTestDbUtils.getEntry(mActivity, + InCallMetricsDbHelper.Tables.INAPP_TABLE, + InCallMetricsHelper.Events.INAPP_NUDGE_CONTACTS_INSTALL); + InCallMetricsTestUtils.verifyUserActionsMetrics(testName, entry, 1); + } else { + Log.d(TAG, "ERROR: " + GET + " not found"); + } + } catch (UiObjectNotFoundException e) { + Log.d(TAG, "ERROR: No matching View"); + } + // clean up contact + InCallMetricsTestUtils.deleteContact(contactUri, cr); + + Log.d(TAG, "-----test8 finish -----"); + } + + @After + public void cleanup() { + // change plugin auth state back to signed in + InCallMetricsTestUtils.setInCallPluginAuthState(mActivity, true); + // clear out db metrics entries + InCallMetricsTestDbUtils.clearAllEntries(mActivity); + } +} + diff --git a/androidTest/src/com/android/contacts/androidtest/InCallMetricsSendTest.java b/androidTest/src/com/android/contacts/androidtest/InCallMetricsSendTest.java new file mode 100644 index 000000000..aea748cdf --- /dev/null +++ b/androidTest/src/com/android/contacts/androidtest/InCallMetricsSendTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.contacts.androidtest; + +import android.content.ComponentName; +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.rules.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.MediumTest; +import android.util.Log; + +import com.android.contacts.activities.PeopleActivity; +import com.android.contacts.incall.InCallMetricsDbHelper; +import com.android.contacts.incall.InCallMetricsHelper; +import com.android.phone.common.incall.CallMethodInfo; +import com.android.phone.common.incall.ContactsDataSubscription; +import com.android.phone.common.incall.utils.CallMethodFilters; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.Set; + +@RunWith(AndroidJUnit4.class) +@MediumTest +public class InCallMetricsSendTest { + private static final String TAG = InCallMetricsSendTest.class.getSimpleName(); + + private Context mContext; + private CallMethodInfo mCm; + + @Rule + public ActivityTestRule mActivityTestRule = new + ActivityTestRule(PeopleActivity.class); + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + // Query plugins + HashMap cmMap = + CallMethodFilters.getAllEnabledAndHiddenCallMethods( + ContactsDataSubscription.get(mContext)); + Assert.assertNotNull(cmMap); + Set cmKeySet = cmMap.keySet(); + if (cmKeySet.size() == 0) { + Log.d(TAG, "No InCall plugin installed"); + return; + } + // test the first one + ComponentName cn = cmKeySet.iterator().next(); + mCm = cmMap.get(cn); + Assert.assertNotNull(mCm); + } + + @Test + public void sendTest() { + populateMetricsInDb(); + InCallMetricsTestUtils.waitFor(500); + InCallMetricsHelper.prepareAndSend(mContext); + } + + private void populateMetricsInDb() { + // Category: USER_ACTIONS + // CONTACTS_MANUAL_MERGED + InCallMetricsDbHelper.getInstance(mContext).incrementUserActionsParam( + mCm.mComponent.flattenToString(), + "", + InCallMetricsHelper.Events.CONTACTS_MANUAL_MERGED.value(), + InCallMetricsHelper.Categories.USER_ACTIONS.value(), + InCallMetricsHelper.Parameters.COUNT.toCol()); + // CONTACTS_AUTO_MERGED + InCallMetricsDbHelper.getInstance(mContext).incrementUserActionsParam( + mCm.mComponent.flattenToString(), + "", + InCallMetricsHelper.Events.CONTACTS_AUTO_MERGED.value(), + InCallMetricsHelper.Categories.USER_ACTIONS.value(), + InCallMetricsHelper.Parameters.COUNT.toCol()); + // INVITES_SENT + InCallMetricsHelper.increaseCount(mContext, InCallMetricsHelper.Events.INVITES_SENT, + mCm.mComponent.flattenToString()); + // DIRECTORY_SEARCH + InCallMetricsHelper.increaseCount(mContext, InCallMetricsHelper.Events.DIRECTORY_SEARCH, + mCm.mComponent.flattenToString()); + // Category: In App Nudges + // INAPP_NUDGE_CONTACTS_TAB_LOGIN + InCallMetricsHelper.setValue( + mContext, + mCm.mComponent, + InCallMetricsHelper.Categories.INAPP_NUDGES, + InCallMetricsHelper.Events.INAPP_NUDGE_CONTACTS_TAB_LOGIN, + InCallMetricsHelper.Parameters.EVENT_ACCEPTANCE, + InCallMetricsHelper.EVENT_ACCEPT, + InCallMetricsHelper.generateNudgeId(mCm.mLoginSubtitle)); + // INAPP_NUDGE_CONTACTS_LOGIN + InCallMetricsHelper.setValue( + mContext, + mCm.mComponent, + InCallMetricsHelper.Categories.INAPP_NUDGES, + InCallMetricsHelper.Events.INAPP_NUDGE_CONTACTS_LOGIN, + InCallMetricsHelper.Parameters.EVENT_ACCEPTANCE, + InCallMetricsHelper.EVENT_ACCEPT, + InCallMetricsHelper.generateNudgeId(mCm.mLoginNudgeSubtitle)); + // INAPP_NUDGE_CONTACTS_INSTALL + InCallMetricsHelper.setValue( + mContext, + mCm.mComponent, + InCallMetricsHelper.Categories.INAPP_NUDGES, + InCallMetricsHelper.Events.INAPP_NUDGE_CONTACTS_INSTALL, + InCallMetricsHelper.Parameters.EVENT_ACCEPTANCE, + InCallMetricsHelper.EVENT_DISMISS, + InCallMetricsHelper.generateNudgeId(mCm.mInstallNudgeSubtitle)); + } +} diff --git a/androidTest/src/com/android/contacts/androidtest/InCallMetricsTestDbUtils.java b/androidTest/src/com/android/contacts/androidtest/InCallMetricsTestDbUtils.java new file mode 100644 index 000000000..820868255 --- /dev/null +++ b/androidTest/src/com/android/contacts/androidtest/InCallMetricsTestDbUtils.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.contacts.androidtest; + +import com.android.contacts.incall.InCallMetricsDbHelper; +import com.android.contacts.incall.InCallMetricsHelper; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import java.util.LinkedList; +import java.util.List; + +public class InCallMetricsTestDbUtils { + private static final String TAG = InCallMetricsTestDbUtils.class.getSimpleName(); + + private static final String[] INAPP_PROJECTION = new String[] { + InCallMetricsDbHelper.InAppColumns._ID, + InCallMetricsDbHelper.InAppColumns.CATEGORY, + InCallMetricsDbHelper.InAppColumns.EVENT_NAME, + InCallMetricsDbHelper.InAppColumns.COUNT, + InCallMetricsDbHelper.InAppColumns.NUDGE_ID, + InCallMetricsDbHelper.InAppColumns.EVENT_ACCEPTANCE, + InCallMetricsDbHelper.InAppColumns.EVENT_ACCEPTANCE_TIME, + InCallMetricsDbHelper.InAppColumns.PROVIDER_NAME + }; + private static final String[] USER_ACTIONS_PROJECTION = new String[] { + InCallMetricsDbHelper.UserActionsColumns._ID, + InCallMetricsDbHelper.UserActionsColumns.CATEGORY, + InCallMetricsDbHelper.UserActionsColumns.EVENT_NAME, + InCallMetricsDbHelper.UserActionsColumns.COUNT, + InCallMetricsDbHelper.UserActionsColumns.PROVIDER_NAME, + InCallMetricsDbHelper.UserActionsColumns.RAW_ID + }; + private static final String SELECT_EVENT = + InCallMetricsDbHelper.UserActionsColumns.EVENT_NAME + " ==?"; + + public static boolean clearAllEntries(Context context) { + List list = new LinkedList(); + SQLiteDatabase db = InCallMetricsDbHelper.getInstance(context).getWritableDatabase(); + if (db == null) { + Log.d(TAG, "No valid db"); + return false; + } + db.delete(InCallMetricsDbHelper.Tables.USER_ACTIONS_TABLE, null, null); + db.delete(InCallMetricsDbHelper.Tables.INAPP_TABLE, null, null); + return true; + } + + public static ContentValues getEntry(Context context, String table, + InCallMetricsHelper.Events event) { + InCallMetricsDbHelper dbHelper = InCallMetricsDbHelper.getInstance(context); + SQLiteDatabase db = dbHelper.getWritableDatabase(); + String[] projection = new String[] {}; + if (table.equals(InCallMetricsDbHelper.Tables.USER_ACTIONS_TABLE)) { + projection = USER_ACTIONS_PROJECTION; + } else if (table.equals(InCallMetricsDbHelper.Tables.INAPP_TABLE)) { + projection = INAPP_PROJECTION; + } + Cursor cursor = db.query( + table, + projection, + SELECT_EVENT, + new String[]{event.value()}, + null, + null, + null); + ContentValues cv = getContentValues(cursor); + if (cursor != null) { + cursor.close(); + } + return cv; + } + + private static ContentValues getContentValues(Cursor cursor) { + ContentValues map = new ContentValues();; + if (cursor != null && cursor.moveToFirst()) { + if (cursor.moveToFirst()) { + DatabaseUtils.cursorRowToContentValues(cursor, map); + Log.d(TAG, "getContentValues:" + map); + } + } + return map; + } +} diff --git a/androidTest/src/com/android/contacts/androidtest/InCallMetricsTestUtils.java b/androidTest/src/com/android/contacts/androidtest/InCallMetricsTestUtils.java new file mode 100644 index 000000000..23c2ecf92 --- /dev/null +++ b/androidTest/src/com/android/contacts/androidtest/InCallMetricsTestUtils.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.contacts.androidtest; + +import android.app.Activity; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; + +import android.net.Uri; +import android.provider.ContactsContract; +import android.util.Log; + +import com.android.contacts.common.util.ImplicitIntentsUtil; +import com.android.contacts.incall.InCallMetricsDbHelper; +import com.android.contacts.incall.InCallMetricsHelper; +import com.android.contacts.quickcontact.QuickContactActivity; +import org.junit.Assert; + +import java.util.ArrayList; + +public class InCallMetricsTestUtils { + private static final String TAG = InCallMetricsTestUtils.class.getSimpleName(); + + // threshold for user interaction (click) and current timestamp difference, ensure + // the metric timestamp is corret + public static final double TIMESTAMP_THRESHOLD = 10000; + + public static void waitFor(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + + } + } + + public static void setInCallPluginAuthState(Context context, boolean state) { + Intent intent = new Intent("com.android.contacts.androidtest.AUTH_STATE"); + intent.putExtra("state", state); + context.sendBroadcast(intent); + + } + + public static Intent getContactCardIntent(Uri uri) { + final Intent intent = new Intent(ContactsContract.QuickContact.ACTION_QUICK_CONTACT); + intent.setData(uri); + intent.putExtra(ContactsContract.QuickContact.EXTRA_MODE, + QuickContactActivity.MODE_FULLY_EXPANDED); + // Make sure not to show QuickContacts on top of another QuickContacts. + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + return intent; + } + + public static Uri createContact(Uri matchUri, String displayName, String phone, ContentResolver + contentResolver) { + String name = displayName; + int phoneType = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE; + if (matchUri != null) { + // look up matchUri + String selection = ContactsContract.Data.MIMETYPE + " =? OR " + + ContactsContract.Data.MIMETYPE + " =?"; + Cursor cursor = contentResolver.query( + Uri.withAppendedPath(matchUri, ContactsContract.Contacts.Entity + .CONTENT_DIRECTORY), + null, + selection, + new String[] { + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE}, + null); + boolean foundName = false; + boolean foundPhone = false; + while (cursor.moveToNext()) { + Log.d(TAG, "found matchUri"); + if (cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)) + .equals(ContactsContract.CommonDataKinds.StructuredName + .CONTENT_ITEM_TYPE)) { + name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts + .DISPLAY_NAME)); + foundPhone = true; + } + if (cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)) + .equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) { + phone = cursor.getString(cursor.getColumnIndex(ContactsContract + .CommonDataKinds.Phone.NUMBER)); + phoneType = cursor.getInt(cursor.getColumnIndex(ContactsContract + .CommonDataKinds.Phone.TYPE)); + foundPhone = true; + } + if (foundName && foundPhone) { + Log.d(TAG, "found matchUri name:" + name + " phone:"+phone); + break; + } + } + cursor.close(); + } + + ArrayList ops = new ArrayList(); + ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.android.localphone") + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, "PHONE") + .build()); + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name) + .build()); + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) + .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType) + .build()); + + + // Ask the Contact provider to create a new contact + Log.i(TAG,"Creating contact: " + name); + try { + contentResolver.applyBatch(ContactsContract.AUTHORITY, ops); + } catch (Exception e) { + // Display warning + Log.e(TAG, "Exceptoin encoutered while inserting contact: " + e); + } + + // get uri and return uri + Uri contactUri = null; + String[] projection = { + ContactsContract.Contacts._ID, + ContactsContract.Contacts.LOOKUP_KEY}; + String selection = ContactsContract.Contacts.DISPLAY_NAME + " =?"; + Cursor cursor = contentResolver.query( + ContactsContract.Contacts.CONTENT_URI, + projection, + selection, + new String[] {name}, + null); + if (cursor != null && cursor.moveToFirst()) { + contactUri = ContactsContract.Contacts.getLookupUri( + cursor.getLong(0), cursor.getString(1)); + } + cursor.close(); + return contactUri; + } + + public static void deleteContact(Uri contactUri, ContentResolver contentResolver) { + //ContentProviderOperation.newDelete(contactUri).build(); + contentResolver.delete(contactUri, null, null); + } + + public static Uri findFirstContactWithPhoneNumber(String accountType, + ContentResolver contentResolver) { + Uri contactUri = null; + Log.d(TAG, "RawContact accountType:" + accountType); + String selection = ContactsContract.RawContacts.ACCOUNT_TYPE + " =?"; + Cursor cursor = contentResolver.query( + ContactsContract.RawContacts.CONTENT_URI, + null, + selection, + new String[] {accountType}, + null); + + while (cursor.moveToNext()) { + int rawId = cursor.getInt(cursor.getColumnIndex(ContactsContract.RawContacts._ID)); + int contactId = cursor.getInt(cursor.getColumnIndex(ContactsContract.RawContacts + .CONTACT_ID)); + Log.d(TAG, "RawContact rawId " + rawId + " contactId:" + contactId); + Cursor dataCursor = contentResolver.query( + ContactsContract.Data.CONTENT_URI, + null, + ContactsContract.Data.RAW_CONTACT_ID + " =? AND " + ContactsContract.Data + .MIMETYPE + " =?", + new String[] {String.valueOf(rawId), ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE}, + null); + while (dataCursor.moveToNext()) { + Log.d(TAG, "dataCursor found" + dataCursor.getInt(0)); + contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, + String.valueOf(contactId)); + cursor.close(); + dataCursor.close(); + return contactUri; + } + dataCursor.close(); + } + cursor.close(); + return contactUri; + } + + public static void openContactCard(Activity activity, Uri contactUri) { + final Intent cardIntent = ImplicitIntentsUtil.composeQuickContactIntent( + contactUri, QuickContactActivity.MODE_FULLY_EXPANDED); + ImplicitIntentsUtil.startActivityInApp(activity, cardIntent); + } + + public static void verifyInAppMetrics(String testName, ContentValues entry, int expectedCount, + long currentTime) { + int newCount = entry.containsKey(InCallMetricsDbHelper.InAppColumns.COUNT) ? + entry.getAsInteger(InCallMetricsDbHelper.InAppColumns.COUNT) : 0; + boolean eventAccept = + entry.containsKey(InCallMetricsDbHelper.InAppColumns.EVENT_ACCEPTANCE) + ? entry.getAsBoolean( + InCallMetricsDbHelper.InAppColumns.EVENT_ACCEPTANCE) : false; + int timestamp = + entry.containsKey(InCallMetricsDbHelper.InAppColumns.EVENT_ACCEPTANCE_TIME) + ? entry.getAsInteger( + InCallMetricsDbHelper.InAppColumns.EVENT_ACCEPTANCE_TIME) : -1; + + Log.d(TAG, testName + " newCount:" + newCount); + + // check count + Assert.assertEquals(expectedCount, newCount); + // check accept value + Assert.assertTrue(eventAccept); + // check user accept timestamp is within threshold + Assert.assertTrue( + (timestamp - currentTime) < InCallMetricsTestUtils.TIMESTAMP_THRESHOLD); + } + + public static void verifyUserActionsMetrics(String testName, ContentValues entry, int + expectedCount) { + int newCount = entry.containsKey(InCallMetricsDbHelper.UserActionsColumns.COUNT) ? + entry.getAsInteger(InCallMetricsDbHelper.UserActionsColumns.COUNT) : 0; + Log.d(TAG, testName + " newCount:" + newCount); + Assert.assertEquals(expectedCount, newCount); + } + + public static Uri findContactUriByDisplayName(String displayName, ContentResolver cr) { + Uri contactUri = null; + String[] projection = { + ContactsContract.Contacts._ID, + ContactsContract.Contacts.LOOKUP_KEY}; + String selection = ContactsContract.Contacts.DISPLAY_NAME + " =?"; + Cursor cursor = cr.query( + ContactsContract.Contacts.CONTENT_URI, + projection, + selection, + new String[] {displayName}, + null); + if (cursor != null && cursor.moveToFirst()) { + contactUri = ContactsContract.Contacts.getLookupUri( + cursor.getLong(0), cursor.getString(1)); + } + cursor.close(); + return contactUri; + } +} -- cgit v1.2.3