Handle viSer 4.0
- The viSer proxy app is not required anymore.
- Set the built-in dialer as the default dialer.
- Update the app settings from a fresh export.
Issue: INFRA-225
Change-Id: I266bf682cb39b7118b57f05684f5e6ae5512b7f0
Depends-On: I55648872e2a87cdad22f82a29a17e7bb9824638d
diff --git a/deploy.py b/deploy.py
index d768583..b9b3ed0 100755
--- a/deploy.py
+++ b/deploy.py
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
+import abc
import contextlib
import dataclasses
import logging
@@ -16,8 +17,15 @@
ADB_DEVICES_PATTERN = re.compile(r"^([a-z0-9-]+)\s+device$", flags=re.M)
+ANDROID_6_SDK = 23
+ANDROID_7_SDK = 24
-class HostCommandError(BaseException):
+
+class BaseError(Exception, abc.ABC):
+ """An abstract error."""
+
+
+class HostCommandError(BaseError):
"""An error happened while issuing a command on the host."""
def __init__(self, command, error_message):
@@ -27,7 +35,7 @@
super(HostCommandError, self).__init__(message)
-class DeviceCommandError(BaseException):
+class DeviceCommandError(BaseError):
"""An error happened while sending a command to a device."""
def __init__(self, serial, command, error_message):
@@ -42,6 +50,14 @@
"""An intent was rejected by a device."""
+class UnlicensedDeviceError(BaseError):
+ """A device is missing a license."""
+
+ def __init__(self, device, provider):
+ self.device = device
+ super().__init__(f"{device} is missing a license for {provider}")
+
+
def adb(*args, serial=None, raise_on_error=True):
"""Run ADB command attached to serial.
@@ -164,6 +180,9 @@
# Cache the OS flavour
self.os_flavour = "gms" if self.is_gms_device() else "sibon"
+ def __str__(self) -> str:
+ return f"{DeviceUnderTest.__name__}({self.serial})"
+
def adb(self, *args) -> subprocess.CompletedProcess:
"""Execute an adb command on this device.
@@ -256,7 +275,7 @@
:raise DeviceCommandError: If the install command failed.
"""
command = ["install", "-r"]
- if self.sdk >= 23:
+ if self.sdk >= ANDROID_6_SDK:
# From Marshmallow onwards, adb has a flag to grant default permissions
command.append("-g")
@@ -369,32 +388,6 @@
@dataclasses.dataclass(frozen=True)
-class ViserProxyApp:
- """A template for the viSer Proxy app."""
-
- package: str
- """The app package name."""
- apk_filename_template: str
- """The string template of the APK filename, in the `str.format()` style (`{}`).
-
- The following place-holders can be used and will be replaced at runtime:
- - `{sdk}`: The Android SDK number, e.g. `25` for Android 7.1.
- - `{flavour}`: The Fairphone-specific Android build flavour, e.g. `gms`
- (Fairphone OS) or `sibon` (Fairphone Open).
- """
-
- def resolve(self, *, sdk: int, flavour: str) -> AndroidApp:
- """Resolve the app template into a Viser App."""
- if sdk >= 24:
- sdk = 24
- else:
- sdk = 19
- return AndroidApp(
- self.package, self.apk_filename_template.format(sdk=sdk, flavour=flavour)
- )
-
-
-@dataclasses.dataclass(frozen=True)
class Credentials:
"""Credentials to access a service."""
@@ -414,10 +407,6 @@
"""
- VISER_PROXY_APP_TEMPLATE = ViserProxyApp(
- "com.lunarlabs.panda.proxy",
- "com.lunarlabs.panda.proxy-latest-sdk{sdk}-{flavour}.apk",
- )
ANDROID_APPS = [
AndroidApp("com.smartviser.demogame", "com.smartviser.demogame-latest.apk"),
AndroidApp("com.lunarlabs.panda", "com.lunarlabs.panda-latest.apk"),
@@ -430,6 +419,9 @@
settings_file: pathlib.Path
target_path: pathlib.Path = pathlib.Path("/sdcard/Viser")
+ def __str__(self) -> str:
+ return "SmartViser viSer app suite"
+
@property
def target_scenarios_path(self) -> pathlib.Path:
return self.target_path
@@ -450,11 +442,7 @@
The sequence is ordered to satisfy the dependency graph
(i.e. required apps come first in the sequence).
"""
- return [
- self.VISER_PROXY_APP_TEMPLATE.resolve(
- sdk=device.sdk, flavour=device.os_flavour
- )
- ] + self.ANDROID_APPS
+ return self.ANDROID_APPS
def deploy(self, device: DeviceUnderTest) -> None:
"""Deploy the suite on a device.
@@ -515,6 +503,8 @@
device.install(self.prebuilts_path / app.apk)
with device.launch("com.lunarlabs.panda"):
+ _LOG.info("Initiate first run of viSer")
+
# Input the credentials
device.ui(resourceId="android:id/content").child(text="Username").child(
className="android.widget.EditText"
@@ -524,7 +514,7 @@
).set_text(self.credentials.password)
# Sign in
- signin_label = "SIGN IN" if device.sdk >= 24 else "Sign in"
+ signin_label = "SIGN IN" if device.sdk >= ANDROID_7_SDK else "Sign in"
device.ui(resourceId="android:id/content").child(
text=signin_label, className="android.widget.Button"
).click()
@@ -535,6 +525,15 @@
)
progress_bar.wait.gone(timeout=10000)
+ # Is the app licensed?
+ license_failed_info = device.ui(resourceId="android:id/content").child(
+ textContains="Licence verification failed"
+ )
+ if license_failed_info.exists:
+ raise UnlicensedDeviceError(device, self)
+
+ self._maybe_accept_viser_dialer(device)
+
def configure_suite(self, device: DeviceUnderTest) -> None:
"""Configure the suite on a device.
@@ -546,11 +545,14 @@
device.push(self.settings_file, self.target_settings_file)
with device.launch("com.lunarlabs.panda"):
- device.ui(text="Settings", className="android.widget.TextView").click()
- device.ui(resourceId="android:id/list").child_by_text(
+ device.ui(description="Open navigation drawer").click()
+ device.ui(
+ text="Settings", className="android.widget.CheckedTextView"
+ ).click()
+ device.ui(className="android.support.v7.widget.RecyclerView").child_by_text(
"Settings management", className="android.widget.LinearLayout"
).click()
- device.ui(resourceId="android:id/list").child_by_text(
+ device.ui(className="android.support.v7.widget.RecyclerView").child_by_text(
"Load settings", className="android.widget.LinearLayout"
).click()
device.ui(resourceId="android:id/list").child_by_text(
@@ -560,6 +562,56 @@
textMatches="(?i)Select", className="android.widget.Button"
).click()
+ self._maybe_accept_viser_dialer(device)
+
+ def _maybe_accept_viser_dialer(self, device: DeviceUnderTest) -> None:
+ """Accept the built-in viSer dialer as the default dialer, if necessary.
+
+ This method tries to accept the built-in viSer dialer from a
+ currently displayed modal. Two types of modals are handled, the
+ modal spawned by the viSer app itself, and the modal spawned by
+ the system UI.
+
+ This method is idempotent and will pass even if the modals are
+ missing.
+
+ :param device: The device to accept the modal on.
+ """
+ dialer_set = False
+
+ # Optional modal to set the the built-in dialer as default
+ set_dialer_modal = device.ui(resourceId="android:id/content").child(
+ textContains="Do you want to use viSer dialer as default ?"
+ )
+ if set_dialer_modal.exists:
+ device.ui(resourceId="android:id/content").child(
+ textMatches="(?i)Yes", className="android.widget.Button"
+ ).click()
+ dialer_set = True
+
+ # Optional system modal to really set the built-in dialer as default
+ set_dialer_system_modal = device.ui(resourceId="android:id/content").child(
+ text="Make viSer your default Phone app?"
+ )
+ if set_dialer_system_modal.exists:
+ device.ui(resourceId="android:id/content").child(
+ textMatches="(?i)Set default", className="android.widget.Button"
+ ).click()
+ dialer_set = True
+
+ # Optional system modal to change the default dialer
+ change_dialer_system_modal = device.ui(resourceId="android:id/content").child(
+ text="Change default Dialer app?"
+ )
+ if change_dialer_system_modal.exists:
+ device.ui(resourceId="android:id/content").child(
+ textMatches="(?i)OK", className="android.widget.Button"
+ ).click()
+ dialer_set = True
+
+ if dialer_set:
+ _LOG.info("Set the built-in viSer dialer as default dialer")
+
def deploy():
serials = []
@@ -586,12 +638,12 @@
device.unlock()
# Disable Privacy Impact popup on Android 5.
- if device.sdk <= 22:
+ if device.sdk < ANDROID_6_SDK:
disable_privacy_impact_popup(device)
# Push the scenarios, their data, and install the apps
suite.deploy(device)
- except (HostCommandError, DeviceCommandError, uiautomator.JsonRPCError):
+ except (BaseError, uiautomator.JsonRPCError):
_LOG.error("Failed to execute deployment", exc_info=True)
finally:
try:
diff --git a/settings/fairphone-bot_settings b/settings/fairphone-bot_settings
index f3f0aad..9f69433 100644
--- a/settings/fairphone-bot_settings
+++ b/settings/fairphone-bot_settings
@@ -1 +1 @@
-{"mAfterDefinedTime":false,"mAtDefinedTime":false,"mAutoAnswer":"disabled","mAutoAnswerDelay":"10","mAutoAnswerMax":"20","mAutoAnswerMin":"10","mAutoAnswerMode":false,"mAutoAnswerModeInject":true,"mAutoAnswerModeNative":false,"mAutoAnswerModeTap":false,"mAutoAnswerXCoordinate":"550","mAutoAnswerYCoordinate":"188","mBatteryEachUa":false,"mBatteryMonitoring":false,"mBatterySampling":false,"mBatterySamplingDuration":"60","mBatteryStatusChange":true,"mBuildScenarioMirror":false,"mBuildScenarioResults":true,"mBuildXmlReport":true,"mCallState":true,"mCampaignTitle":"","mCellLocation":true,"mCellNeighborhood":false,"mCellularSampling":true,"mCellularSamplingDuration":"10","mComparisonBatterySign":"1","mComparisonBatteryValue":"","mContinueIterateOnError":true,"mContinueLastCamp":false,"mContinueTestSuiteOnError":true,"mDataConnection":true,"mDataStall":true,"mDataStallInterval":"1000","mDefinedHours":"","mDefinedMinutes":"","mDefinedSeconds":"","mEmailAccount":"fairphone.viser@gmail.com","mEmailTo":"","mEnvironmentSensors":false,"mFilePattern":":date:-:time:_:name:","mFtpServerFolderPath":"","mFtpServerLogin":"","mFtpServerPort":"21","mFtpServerPwd":"","mFtpServerTo":"","mHardwareVersion":"","mInfiniteCampaign":false,"mLimitedBattery":false,"mLimitedDays":"","mLimitedHours":"","mLimitedMinutes":"","mLimitedRuns":false,"mLimitedTimes":false,"mLogIPChange":true,"mLogSignalsStrengths":true,"mNmeaLog":false,"mNumberRuns":"","mPhoneLocDistance":"5","mPhoneLocDuration":"5","mPhoneLocation":true,"mPrintAppsIcon":false,"mProductName":"","mQuestionFormPath":"","mReportApps":false,"mReportCpuUse":false,"mReportFTP":false,"mReportFTPCltsOnly":false,"mReportGroupSimilarTestEvents":true,"mReportMail":false,"mReportMemoryUse":false,"mReportSourceCode":false,"mReportTestEvents":true,"mRunMultiScenarioMethod":"ordered","mRunNoCpuWakeLock":false,"mSMSReport":false,"mSamplingCellInfo":false,"mSamplingDurationCPU":"250","mSamplingDurationCi":"1","mSamplingDurationPs":"2","mSamplingPs":false,"mSendMmsThroughSystem":false,"mServerAddress":"data.smartviser-data.com","mServiceState":true,"mSetRunExecScreenDarkTheme":false,"mSetRunListCollaped":false,"mShowFooter":true,"mShowReceivedMmsSms":false,"mShutdownAfterCompletion":false,"mSmsNumber":"","mStartBatteryLevel":false,"mStartBatterySign":"1","mStartBatteryValue":"","mTrackAPN":false,"mTrackHardwareVersion":true,"mTrackIMSI":true,"mTrackIPStatus":false,"mTrackImei":true,"mTrackLanguage":false,"mTrackModemVersion":true,"mTrackMsisdn":false,"mTrackNetworkName":false,"mTrackNetworkType":true,"mTrackProductName":true,"mTrackRoamingStatus":false,"mTrackSignalsStrengths":false,"mTrackSoftwareVersion":true,"mUDSamplingInterval":"2000","mUDSamplingUnit":"Mbps","mUplinkDownlink":true,"mUserMailPwd":"fairphoneviser2017","mVoltageMonitoring2":false,"mVwsSendCallstoServer":false,"mVwsSendOFilestoServer":false,"mVwsSendtoServer":true,"mWifiSampling":true,"mWifiSamplingDuration":"10"}
+{"mAfterDefinedTime":false,"mAtDefinedTime":false,"mAutoAnswer":"disabled","mAutoAnswerDelay":"10","mAutoAnswerMax":"20","mAutoAnswerMin":"10","mAutoAnswerMode":false,"mAutoAnswerModeInject":true,"mAutoAnswerModeNative":false,"mAutoAnswerModeTap":false,"mAutoAnswerXCoordinate":"550","mAutoAnswerYCoordinate":"188","mBatteryEachUa":false,"mBatteryMonitoring":false,"mBatterySampling":false,"mBatterySamplingDuration":"60","mBatteryStatusChange":true,"mBuildScenarioMirror":false,"mBuildScenarioResults":true,"mBuildXmlReport":true,"mCallState":true,"mCampaignTitle":"","mCellLocation":true,"mCellNeighborhood":false,"mCellularSampling":true,"mCellularSamplingDuration":"10","mComparisonBatterySign":"1","mComparisonBatteryValue":"","mContinueIterateOnError":true,"mContinueLastCamp":false,"mContinueTestSuiteOnError":true,"mDataConnection":true,"mDataStall":true,"mDataStallInterval":"1000","mDefinedHours":"","mDefinedMinutes":"","mDefinedSeconds":"","mEmailAccount":"fairphone.viser@gmail.com","mEmailTo":"","mEnvironmentSensors":false,"mExhaustedBattery":false,"mFilePattern":":date:-:time:_:name:","mFtpServerFolderPath":"","mFtpServerLogin":"","mFtpServerPort":"21","mFtpServerPwd":"","mFtpServerTo":"","mHardwareVersion":"","mInfiniteCampaign":false,"mLimitedBattery":false,"mLimitedDays":"","mLimitedHours":"","mLimitedMinutes":"","mLimitedRuns":false,"mLimitedTimes":false,"mLogIPChange":true,"mLogSignalsStrengths":true,"mNmeaLog":false,"mNumberRuns":"","mPhoneDialerApp":true,"mPhoneLocDistance":"5","mPhoneLocDuration":"5","mPhoneLocation":true,"mPrintAppsIcon":false,"mProductName":"","mQuestionFormPath":"","mReportApps":false,"mReportCpuUse":false,"mReportFTP":false,"mReportFTPCltsOnly":false,"mReportGroupSimilarTestEvents":true,"mReportMail":false,"mReportMemoryUse":false,"mReportSourceCode":false,"mReportTestEvents":true,"mRunMultiScenarioMethod":"ordered","mRunNoCpuWakeLock":false,"mSMSReport":false,"mSamplingCellInfo":false,"mSamplingDurationCPU":"250","mSamplingDurationCi":"1","mSamplingDurationPs":"2","mSamplingPs":false,"mSendMmsThroughSystem":false,"mServerAddress":"data.smartviser-data.com","mServiceState":true,"mSetRunExecScreenDarkTheme":false,"mSetRunListCollaped":false,"mShowFooter":true,"mShowReceivedMmsSms":false,"mShutdownAfterCompletion":false,"mSmsNumber":"","mStartBatteryLevel":false,"mStartBatterySign":"1","mStartBatteryValue":"","mTrackAPN":false,"mTrackHardwareVersion":true,"mTrackIMSI":true,"mTrackIPStatus":false,"mTrackImei":true,"mTrackLanguage":false,"mTrackModemVersion":true,"mTrackMsisdn":false,"mTrackNetworkName":false,"mTrackNetworkType":true,"mTrackProductName":true,"mTrackRoamingStatus":false,"mTrackSignalsStrengths":false,"mTrackSoftwareVersion":true,"mUDSamplingInterval":"2000","mUDSamplingUnit":"Mbps","mUplinkDownlink":true,"mUserMailPwd":"fairphoneviser2017","mVoltageMonitoring2":false,"mVwsSendCallstoServer":false,"mVwsSendOFilestoServer":false,"mVwsSendtoServer":true,"mWifiSampling":true,"mWifiSamplingDuration":"10"}