Spectatio 是一个开源测试框架,用于在真实设备和虚拟设备上测试 Android Automotive OS (AAOS)。Spectatio 提供用于在汽车设备上测试应用的 API,它是一种可扩展、可伸缩的解决方案,用于验证 AAOS 及其应用的功能和性能。
概要设计
Spectatio 框架可适应各种 AAOS 界面实现,并相应地扩展。该框架用于在设备硬件、模拟器和虚拟环境中测试 AAOS 的功能和性能。
下图说明了 Spectatio 框架的概要设计。
图 1. Spectatio 框架概要设计
Spectatio 框架基于 UI Automator 构建,该框架提供了一组 API,这些 API 可用于构建与 AAOS 用户和系统应用交互的界面测试。Automotive 测试使用 Spectatio 框架提供的 API 进行测试,所以这些测试独立于受测设备 (DUT),并且可扩展为测试各种设备(如果支持)。
图 1 显示 Spectatio 框架基于拨号器、Medicenter 和“设置”等参考应用实现了模块化,并使用应用专用的接口和辅助程序,使其能够针对新应用轻松扩展。Spectatio 框架会重复利用通用标准和实用程序辅助类。标准辅助类是所有应用辅助函数的父类,它提供设备专用或适用于各种应用的标准函数。实用程序辅助类提供了各种实用程序,例如从设备读取或写入文件的实用程序。
架构
为了提供一组 API 来构建界面测试,Spectatio 框架实现了应用专用的界面和辅助程序,同时扩展现有的标准辅助类,并导入实用程序辅助类。
图 2 展示了 Spectatio 框架的概要架构,以及为应用测试实现 API 时所涉及的所有实体。
图 2. Spectatio 框架的概要架构。
应用辅助接口提供了实现应用辅助程序的蓝图。它包含测试应用所需的各种辅助函数。每个应用都有自己的接口,例如 IAutoSettingHelper
和 IAutoDialHelper
。如需了解详情及查看接口函数列表,请参阅 AOSP 上的应用辅助接口函数。
标准辅助类包含设备设置所需的标准属性和函数(但不特定于任何应用,例如 pressHome
和 scroll
)。标准辅助类需在 AbstractAutoStandardAppHelper.java
中定义。
Spectatio 框架会使用实用工具辅助类。例如,AutoJsonUtility.java
是一个实用程序类,用于加载给定的设备 JSON 配置文件并在运行时更新框架配置。
应用辅助程序实现模块是 Spectatio 框架的核心。该模块包含应用辅助接口中所定义辅助函数的实现,而相应辅助函数是在汽车设备上测试应用所必需的函数。每个应用都有自己的实现(如 SettingHelperImpl
和 DialHelperImpl
),Automotive 测试会利用这些实现来测试应用。如需了解详情和实现列表,请参阅 AOSP 上的应用辅助程序实现函数。
Automotive 测试使用应用辅助程序实现函数来测试与应用相关的各种操作。使用 HelperAccessor
类获取对应用帮助程序实现函数的访问权限。
以下代码显示了一个汽车测试示例的设置、清理和执行过程。
@RunWith(AndroidJUnit4.class)
public class AutoApplicationTest {
static HelperAccessor<IAutoApplicationHelper> autoApplicationHelper =
new HelperAccessor<>(IAutoApplicationHelper.class);
public AutoApplicationTest() {
// constructor
// Initialize any attributes that are required for the test execution
}
@Before
public void beforeTest() {
// Initial setup before each test
// For example - open the app
autoApplicationHelper.open();
}
@After
public void afterTest() {
// Cleanup after each test.
// For example - exit the app
autoApplicationHelper.exit();
}
@Test
public void testApplicationFeature() {
// Test
// For example - Test if app is open
assertTrue("Application is not open.", autoApplicationHelper.isOpen());
}
}
自定义
Spectatio 框架独立于设备界面,因此具有可伸缩性,便于测试具有不同界面和硬件的设备。为了实现这种可伸缩性,Spectatio 使用基于参考设备的默认设备配置。 为支持非默认的设备配置,该框架在运行时会使用 JSON 配置文件为设备设置所需的界面更改。JSON 配置文件支持 TEXT
、DESCRIPTION
和 RESOURCE_ID
等界面元素以及 path
设置,并且只能包含有关 DUT 的界面更改的信息。其余界面元素会使用该框架中提供的默认配置值。
默认设备配置
下方的 JSON 配置文件示例显示了可用的设备配置及其默认值。
点击此处可显示 JSON 配置文件示例
{ "SETTINGS": { "APPLICATION_CONFIG": { "SETTINGS_TITLE_TEXT": "Settings", "SETTINGS_PACKAGE": "com.android.car.settings", "SETTINGS_RRO_PACKAGE": "com.android.car.settings.googlecarui.rro", "OPEN_SETTINGS_COMMAND": "am start -a android.settings.SETTINGS", "OPEN_QUICK_SETTINGS_COMMAND": "am start -n com.android.car.settings/com.android.car.settings.common.CarSettingActivity" }, "QUICK_SETTINGS": { "OPEN_MORE_SETTINGS": { "TYPE": "RESOURCE_ID", "VALUE": "toolbar_menu_item_1", "PACKAGE": "com.android.car.settings" }, "NIGHT_MODE": { "TYPE": "TEXT", "VALUE": "Night mode" } }, "DISPLAY": { "PATH": "Settings > Display", "OPTIONS": [ "Brightness level" ], "BRIGHTNESS_LEVEL": { "TYPE": "RESOURCE_ID", "VALUE": "seekbar", "PACKAGE": "com.android.car.settings" } }, "SOUND": { "PATH": "Settings > Sound", "OPTIONS": [ "Media volume", "Alarm volume" ] }, "NETWORK_AND_INTERNET": { "PATH": "Settings > Network & internet", "OPTIONS": [ ], "TOGGLE_WIFI": { "TYPE": "RESOURCE_ID", "VALUE": "master_switch", "PACKAGE": "com.android.car.settings" } }, "BLUETOOTH": { "PATH": "Settings > Bluetooth", "OPTIONS": [ ], "TOGGLE_BLUETOOTH": { "TYPE": "RESOURCE_ID", "VALUE": "car_ui_toolbar_menu_item_switch", "PACKAGE": "com.android.car.settings" } }, "APPS_AND_NOTIFICATIONS": { "PATH": "Settings > Apps & notifications", "OPTIONS": [ ], "SHOW_ALL_APPS": { "TYPE": "TEXT", "VALUE": "Show all apps" }, "ENABLE_DISABLE_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "car_ui_toolbar_menu_item_text", "PACKAGE": "com.android.car.settings" }, "DISABLE_BUTTON_TEXT": { "TYPE": "TEXT", "VALUE": "Disable" }, "ENABLE_BUTTON_TEXT": { "TYPE": "TEXT", "VALUE": "Enable" }, "DISABLE_APP_BUTTON": { "TYPE": "TEXT", "VALUE": "DISABLE APP" }, "FORCE_STOP_BUTTON": { "TYPE": "TEXT", "VALUE": "Force stop" }, "OK_BUTTON": { "TYPE": "TEXT", "VALUE": "OK" }, "PERMISSIONS_MENU": { "TYPE": "TEXT", "VALUE": "Permissions" }, "ALLOW_BUTTON": { "TYPE": "TEXT", "VALUE": "Allow" }, "DENY_BUTTON": { "TYPE": "TEXT", "VALUE": "Deny" }, "DENY_ANYWAY_BUTTON": { "TYPE": "TEXT", "VALUE": "Deny anyway" } }, "DATE_AND_TIME": { "PATH": "Settings > Date & time", "OPTIONS": [ "Automatic date & time", "Automatic time zone" ], "AUTOMATIC_DATE_AND_TIME": { "TYPE": "TEXT", "VALUE": "Automatic date & time" }, "AUTOMATIC_TIME_ZONE": { "TYPE": "TEXT", "VALUE": "Automatic time zone" }, "SET_DATE": { "TYPE": "TEXT", "VALUE": "Set date" }, "SET_TIME": { "TYPE": "TEXT", "VALUE": "Set time" }, "SELECT_TIME_ZONE": { "TYPE": "TEXT", "VALUE": "Select time zone" }, "USE_24_HOUR_FORMAT": { "TYPE": "TEXT", "VALUE": "Use 24-hour format" }, "OK_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "toolbar_menu_item_0", "PACKAGE": "com.android.car.settings" }, "NUMBER_PICKER_WIDGET": { "TYPE": "CLASS", "VALUE": "android.widget.NumberPicker" }, "EDIT_TEXT_WIDGET": { "TYPE": "CLASS", "VALUE": "android.widget.EditText" } }, "USERS": { "PATH": "Settings > Users", "OPTIONS": [ "Guest" ] }, "ACCOUNTS": { "PATH": "Settings > Accounts", "OPTIONS": [ "Automatically sync data" ], "ADD_ACCOUNT": { "TYPE": "TEXT", "VALUE": "ADD ACCOUNT" }, "ADD_GOOGLE_ACCOUNT": { "TYPE": "TEXT", "VALUE": "Google" }, "SIGN_IN_ON_CAR_SCREEN": { "TYPE": "TEXT", "VALUE": "Sign in on car screen" }, "GOOGLE_SIGN_IN_SCREEN": { "TYPE": "TEXT", "VALUE": "Sign in to your Google Account" }, "ENTER_EMAIL": { "TYPE": "CLASS", "VALUE": "android.widget.EditText" }, "ENTER_PASSWORD": { "TYPE": "CLASS", "VALUE": "android.widget.EditText" }, "NEXT_BUTTON": { "TYPE": "TEXT", "VALUE": "Next" }, "DONE_BUTTON": { "TYPE": "TEXT", "VALUE": "Done" }, "REMOVE_BUTTON": { "TYPE": "TEXT", "VALUE": "Remove" }, "REMOVE_ACCOUNT_BUTTON": { "TYPE": "TEXT", "VALUE": "Remove Account" } }, "SYSTEM": { "PATH": "Settings > System", "OPTIONS": [ "About", "Legal information" ], "ABOUT_MENU": { "TYPE": "TEXT", "VALUE": "About" }, "RESET_OPTIONS_MENU": { "TYPE": "TEXT", "VALUE": "Reset options" }, "LANGUAGES_AND_INPUT_MENU": { "TYPE": "TEXT", "VALUE": "Languages & input" }, "DEVICE_MODEL": { "TYPE": "TEXT", "VALUE": "Model" }, "ANDROID_VERSION": { "TYPE": "TEXT", "VALUE": "Android version" }, "ANDROID_SECURITY_PATCH_LEVEL": { "TYPE": "TEXT", "VALUE": "Android security patch level" }, "KERNEL_VERSION": { "TYPE": "TEXT", "VALUE": "Kernel version" }, "BUILD_NUMBER": { "TYPE": "TEXT", "VALUE": "Build number" }, "RECYCLER_VIEW_WIDGET": { "TYPE": "CLASS", "VALUE": "androidx.recyclerview.widget.RecyclerView" }, "RESET_NETWORK": { "TYPE": "TEXT", "VALUE": "Reset network" }, "RESET_SETTINGS": { "TYPE": "TEXT", "VALUE": "RESET SETTINGS" }, "RESET_APP_PREFERENCES": { "TYPE": "TEXT", "VALUE": "Reset app preferences" }, "RESET_APPS": { "TYPE": "TEXT", "VALUE": "RESET APPS" }, "LANGUAGES_MENU": { "TYPE": "TEXT", "VALUE": "Languages" }, "LANGUAGES_MENU_IN_SELECTED_LANGUAGE": { "TYPE": "TEXT", "VALUE": "Idiomas" } }, "SECURITY": { "PATH": "Settings > Security", "OPTIONS": [ ], "TITLE": { "TYPE": "RESOURCE_ID", "VALUE": "car_ui_toolbar_title", "PACKAGE": "com.android.car.settings.googlecarui.rro" }, "CHOOSE_LOCK_TYPE": { "TYPE": "TEXT", "VALUE": "Choose a lock type" }, "LOCK_TYPE_PASSWORD": { "TYPE": "TEXT", "VALUE": "Password" }, "LOCK_TYPE_PIN": { "TYPE": "TEXT", "VALUE": "PIN" }, "LOCK_TYPE_NONE": { "TYPE": "TEXT", "VALUE": "None" }, "CONTINUE_BUTTON": { "TYPE": "TEXT", "VALUE": "Continue" }, "CONFIRM_BUTTON": { "TYPE": "TEXT", "VALUE": "Confirm" }, "ENTER_PASSWORD": { "TYPE": "CLASS", "VALUE": "android.widget.EditText" }, "PIN_PAD": { "TYPE": "RESOURCE_ID", "VALUE": "pin_pad", "PACKAGE": "com.android.car.settings" }, "ENTER_PIN_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "key_enter", "PACKAGE": "com.android.car.settings" }, "REMOVE_BUTTON": { "TYPE": "TEXT", "VALUE": "Remove" } } }, "PHONE": { "APPLICATION_CONFIG": { "DIAL_PACKAGE": "com.android.car.dialer", "PHONE_ACTIVITY": "com.android.car.dialer/.ui.TelecomActivity", "OPEN_DIAL_PAD_COMMAND": "am start -a android.intent.action.DIAL" }, "IN_CALL_VIEW": { "DIALED_CONTACT_TITLE": { "TYPE": "RESOURCE_ID", "VALUE": "user_profile_title", "PACKAGE": "com.android.car.dialer" }, "DIALED_CONTACT_NUMBER": { "TYPE": "RESOURCE_ID", "VALUE": "user_profile_phone_number", "PACKAGE": "com.android.car.dialer" }, "END_CALL": { "TYPE": "RESOURCE_ID", "VALUE": "end_call_button", "PACKAGE": "com.android.car.dialer" }, "MUTE_CALL": { "TYPE": "RESOURCE_ID", "VALUE": "mute_button", "PACKAGE": "com.android.car.dialer" }, "SWITCH_TO_DIAL_PAD": { "TYPE": "RESOURCE_ID", "VALUE": "toggle_dialpad_button", "PACKAGE": "com.android.car.dialer" }, "CHANGE_VOICE_CHANNEL": { "TYPE": "RESOURCE_ID", "VALUE": "voice_channel_view", "PACKAGE": "com.android.car.dialer" }, "VOICE_CHANNEL_CAR": { "TYPE": "TEXT", "VALUE": "Car speakers" }, "VOICE_CHANNEL_PHONE": { "TYPE": "TEXT", "VALUE": "Phone" } }, "DIAL_PAD_VIEW": { "DIAL_PAD_MENU": { "TYPE": "TEXT", "VALUE": "Dial Pad" }, "DIAL_PAD_FRAGMENT": { "TYPE": "RESOURCE_ID", "VALUE": "dialpad_fragment", "PACKAGE": "com.android.car.dialer" }, "DIALED_NUMBER": { "TYPE": "RESOURCE_ID", "VALUE": "title", "PACKAGE": "com.android.car.dialer" }, "MAKE_CALL": { "TYPE": "RESOURCE_ID", "VALUE": "call_button", "PACKAGE": "com.android.car.dialer" }, "DELETE_NUMBER": { "TYPE": "RESOURCE_ID", "VALUE": "delete_button", "PACKAGE": "com.android.car.dialer" } }, "CONTACTS_VIEW": { "CONTACTS_MENU": { "TYPE": "TEXT", "VALUE": "Contacts" }, "CONTACT_INFO": { "TYPE": "RESOURCE_ID", "VALUE": "call_action_id", "PACKAGE": "com.android.car.dialer" }, "CONTACT_NAME": { "TYPE": "RESOURCE_ID", "VALUE": "title", "PACKAGE": "com.android.car.dialer" }, "CONTACT_DETAIL": { "TYPE": "RESOURCE_ID", "VALUE": "show_contact_detail_id", "PACKAGE": "com.android.car.dialer" }, "ADD_CONTACT_TO_FAVORITE": { "TYPE": "RESOURCE_ID", "VALUE": "contact_details_favorite_button", "PACKAGE": "com.android.car.dialer" }, "SEARCH_CONTACT": { "TYPE": "RESOURCE_ID", "VALUE": "menu_item_search", "PACKAGE": "com.android.car.dialer" }, "CONTACT_SEARCH_BAR": { "TYPE": "RESOURCE_ID", "VALUE": "car_ui_toolbar_search_bar", "PACKAGE": "com.android.car.dialer" }, "SEARCH_RESULT": { "TYPE": "RESOURCE_ID", "VALUE": "contact_name", "PACKAGE": "com.android.car.dialer" }, "CONTACT_SETTINGS": { "TYPE": "RESOURCE_ID", "VALUE": "menu_item_setting", "PACKAGE": "com.android.car.dialer" }, "CONTACT_ORDER": { "TYPE": "TEXT", "VALUE": "Contact order" }, "SORT_BY_FIRST_NAME": { "TYPE": "TEXT", "VALUE": "First name" }, "SORT_BY_LAST_NAME": { "TYPE": "TEXT", "VALUE": "Last Name" }, "CONTACT_TYPE_WORK": { "TYPE": "TEXT", "VALUE": "Work" }, "CONTACT_TYPE_MOBILE": { "TYPE": "TEXT", "VALUE": "Mobile" }, "CONTACT_TYPE_HOME": { "TYPE": "TEXT", "VALUE": "Home" } }, "CALL_HISTORY_VIEW": { "CALL_HISTORY_MENU": { "TYPE": "TEXT", "VALUE": "Recents" }, "CALL_HISTORY_INFO": { "TYPE": "RESOURCE_ID", "VALUE": "call_action_id", "PACKAGE": "com.android.car.dialer" } }, "FAVORITES_VIEW": { "FAVORITES_MENU": { "TYPE": "TEXT", "VALUE": "Favorites" } } }, "NOTIFICATIONS": { "APPLICATION_CONFIG": { "OPEN_NOTIFICATIONS_COMMAND": "service call statusbar 1" }, "EXPANDED_NOTIFICATIONS_SCREEN": { "NOTIFICATION_VIEW": { "TYPE": "RESOURCE_ID", "VALUE": "notification_view", "PACKAGE": "com.android.systemui" }, "CLEAR_ALL_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "clear_all_button", "PACKAGE": "com.android.systemui" }, "STATUS_BAR": { "TYPE": "RESOURCE_ID", "VALUE": "car_top_navigation_bar_container", "PACKAGE": "com.android.systemui" }, "APP_ICON": { "TYPE": "RESOURCE_ID", "VALUE": "app_icon", "PACKAGE": "com.android.systemui" }, "APP_NAME": { "TYPE": "RESOURCE_ID", "VALUE": "header_text", "PACKAGE": "com.android.systemui" }, "NOTIFICATION_TITLE": { "TYPE": "RESOURCE_ID", "VALUE": "notification_body_title", "PACKAGE": "com.android.systemui" }, "NOTIFICATION_BODY": { "TYPE": "RESOURCE_ID", "VALUE": "notification_body_content", "PACKAGE": "com.android.systemui" }, "CARD_VIEW": { "TYPE": "RESOURCE_ID", "VALUE": "card_view", "PACKAGE": "com.android.systemui" } } }, "MEDIA_CENTER": { "APPLICATION_CONFIG": { "MEDIA_CENTER_PACKAGE": "com.android.car.media", "MEDIA_ACTIVITY": "com.android.bluetooth/com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService" }, "MEDIA_CENTER_SCREEN": { "PLAY_PAUSE_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "play_pause_stop", "PACKAGE": "com.android.car.media" }, "NEXT_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "skip_next", "PACKAGE": "com.android.car.media" }, "PREVIOUS_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "skip_prev", "PACKAGE": "com.android.car.media" }, "SHUFFLE_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "overflow_on", "PACKAGE": "com.android.car.media" }, "PLAY_QUEUE_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "play_queue", "PACKAGE": "com.android.car.media" }, "MINIMIZED_MEDIA_CONTROLS": { "TYPE": "RESOURCE_ID", "VALUE": "minimized_playback_controls", "PACKAGE": "com.android.car.media" }, "TRACK_NAME": { "TYPE": "RESOURCE_ID", "VALUE": "title", "PACKAGE": "com.android.car.media" }, "TRACK_NAME_MINIMIZED_CONTROL": { "TYPE": "RESOURCE_ID", "VALUE": "minimized_control_bar_title", "PACKAGE": "com.android.car.media" }, "BACK_BUTTON": { "TYPE": "DESCRIPTION", "VALUE": "Back" } }, "MEDIA_CENTER_ON_HOME_SCREEN": { "PLAY_PAUSE_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "play_pause_stop", "PACKAGE": "com.android.car.carlauncher" }, "NEXT_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "skip_next", "PACKAGE": "com.android.car.carlauncher" }, "PREVIOUS_BUTTON": { "TYPE": "RESOURCE_ID", "VALUE": "skip_prev", "PACKAGE": "com.android.car.carlauncher" }, "TRACK_NAME": { "TYPE": "RESOURCE_ID", "VALUE": "title", "PACKAGE": "com.android.car.carlauncher" } } } }
备选设备配置
下方的代码是 JSON 配置文件的示例,其中默认设置已由 DUT 上的设置替换。在此示例中:
网络设置在参考设备上被命名为网络和互联网,在 DUT 上被命名为网络连接。
对于参考设备,可在设置 > 日期和时间下找到日期和时间设置;对于 DUT,可在设置 > 系统 > 日期和时间下找到日期和时间设置。
// Default configuration file
{
....
"SECURITY_SETTINGS_SCROLL_ELEMENT": {
"TYPE": "RESOURCE_ID",
"VALUE": "fragment_container",
},
....
}
// JSON configuration file for non-reference device
{
....
"SECURITY_SETTINGS_SCROLL_ELEMENT": {
"TYPE": "RESOURCE_ID",
"VALUE": "car_ui_recycler_view"
},
....
}
当 JSON 配置文件准备就绪后,系统会在运行时提供该文件,如以下代码块所示。
# Push The JSON configuration file to the device
adb -s DEVICE-SERIAL push PATH-OF-JSON-FILE /data/local/tmp/runtimeSpectatioConfig.json
在此命令中:
DEVICE-SERIAL:DUT 的序列号。如果只有 1 部设备连接到宿主机,则无需使用此参数。
PATH-TO-JSON-FILE:宿主机上的 JSON 文件路径。
配置格式
配置中有五个顶级对象,它们各有以下键和值:
对象 | 说明 |
---|---|
PACKAGES |
用于描述各种应用的主要软件包的对象,用于确定相应应用何时处于前台。 |
ACTIONS |
一个对象,用于指明各种操作的操作类型和参数。例如,是使用按钮还是使用手势来滚动。 |
COMMANDS |
一个对象,用于指定执行各种操作的命令。 |
UI_ELEMENTS |
一个对象,用于构建会选择界面元素的界面自动化程序“BySelectors”(详见下文)。 |
WORKFLOWS |
一个操作序列,用于完成高级任务(详见下文)。 |
界面元素
每个界面元素都有一个 TYPE
,以指定 UI Automator 将查找的元素识别信息(例如资源 ID、文本和说明),以及与该类型关联的配置值。一般来说,每当辅助程序使用此配置识别屏幕上的元素时,它都会获得恰好 1 个元素。如果有多个元素与配置匹配,则测试中会使用任意一个元素。因此,配置(通常)应写得足够具体,以便缩小到相关上下文中的某个元素。
TEXT
这是最简单的界面元素类型。界面元素由其文本标识,且需要完全匹配。
"CALL_HISTORY_MENU": {
"TYPE": "TEXT",
"VALUE": "Recents"
}
TEXT_CONTAINS
与 TEXT
相同,但指定的 VALUE
只需出现在要匹配的元素文本内的某个位置即可。
"PRIVACY_CALENDAR": {
"TYPE": "TEXT_CONTAINS",
"VALUE": "Calendar"
}
说明
通过内容描述属性识别元素,要求完全匹配。
"APP_GRID_SCROLL_BACKWARD_BUTTON": {
"TYPE": "DESCRIPTION",
"VALUE": "Scroll up"
}
RESOURCE_ID
通过资源 ID 识别元素,并可选择检查该 ID 的软件包组件。PACKAGE
键是可选的;如果省略,则任何软件包都将匹配,并且系统只会考虑 ID 中 :id/
后面的部分。
"APP_LIST_SCROLL_ELEMENT": {
"TYPE": "RESOURCE_ID",
"VALUE": "apps_grid",
"PACKAGE": "com.android.car.carlauncher"
}
CLICKABLE, SCROLLABLE
根据元素是否可点击或可滚动来识别元素。这些元素类型非常宽泛,通常应仅在 MULTIPLE
中使用,以帮助缩小其他元素类型的范围。FLAG
键是可选的,默认是 true
。
"SAMPLE_ELEMENT": {
"TYPE": "CLICKABLE",
"FLAG": false
}
CLASS
根据元素的类来识别元素。
"SECURITY_SETTINGS_ENTER_PASSWORD": {
"TYPE": "CLASS",
"VALUE": "android.widget.EditText"
}
HAS_ANCESTOR
通过查找其祖先的 widget 层次结构来识别元素。ANCESTOR
键包含用于标识祖先的对象。DEPTH
键用于指定要查找的层次结构深度。DEPTH
是可选字段,默认值为 1
。
"SAMPLE_ELEMENT": {
"TYPE": "HAS_ANCESTOR",
"DEPTH": 2,
"ANCESTOR": {
"TYPE": "CLASS",
"VALUE": "android.view.ViewGroup"
}
}
HAS_DESCENDANT
沿着层次结构向下查看其子元素,以识别该元素。DESCENDANT
键包含一个对象,用于指定要查找的子项。DEPTH
键指定要查找的层次结构的深度。DEPTH
是可选的,默认值为 1
。
"SAMPLE_ELEMENT": {
"TYPE": "HAS_DESCENDANT",
"DEPTH": 2,
"DESCENDANT": {
"TYPE": "CLASS",
"VALUE": "android.view.ViewGroup"
}
}
MULTIPLE
根据多个同时满足的条件来识别元素,所有这些条件都必须满足。
"APP_INFO_SETTINGS_PERMISSION_MANAGER": {
"TYPE": "MULTIPLE",
"SPECIFIERS": [
{
"TYPE": "CLASS",
"VALUE": "android.widget.RelativeLayout"
},
{
"TYPE": "HAS_DESCENDANT",
"MAX_DEPTH": 2,
"DESCENDANT": {
"TYPE": "TEXT",
"VALUE": "Permission manager"
}
}
]
}
在此示例中,配置会识别深度为 2
且包含文本 Permission manager
的子项的 RelativeLayout
。
Workflows
工作流代表用于完成特定任务的一系列操作,这些操作可能因设备类型而异,并且在配置中的表示方式比在代码中更灵活。
"WORKFLOWS": {
"OPEN_SOUND_SETTINGS_WORKFLOW": [
{
"NAME": "Go to Home",
"TYPE": "PRESS",
"CONFIG": {
"TEXT": "HOME"
}
},
{
"NAME": "Open Settings",
"TYPE": "COMMAND",
"CONFIG": {
"TEXT": "am start -a android.settings.SETTINGS"
}
},
{
"NAME": "Open Sound Settings",
"TYPE": "SCROLL_TO_FIND_AND_CLICK",
"CONFIG": {
"UI_ELEMENT": {
"TYPE": "TEXT",
"VALUE": "Sound"
}
},
"SCROLL_CONFIG": {
"SCROLL_ACTION": "USE_GESTURE",
"SCROLL_DIRECTION": "VERTICAL",
"SCROLL_ELEMENT": {
"TYPE": "RESOURCE_ID",
"VALUE": "car_ui_recycler_view"
}
}
}
]
}
每个工作流都是一个键值对,其中键是工作流的名称,值是要执行的操作数组。每项操作都有一个 NAME
、一个 TYPE
、(通常)一个 CONFIG
和(有时)一个 SWIPE_CONFIG
或 SCROLL_CONFIG
。对于大多数 TYPE,CONFIG
是一个具有 UI_ELEMENT
键的对象,其值的形式与界面元素条目相同(见上文)。这些类型包括:
PRESS LONG_PRESS CLICK LONG_CLICK CLICK_IF_EXIST |
HAS_UI_ELEMENT_IN_FOREGROUND SCROLL_TO_FIND_AND_CLICK SCROLL_TO_FIND_AND_CLICK_IF_EXIST SWIPE_TO_FIND_AND_CLICK SWIPE_TO_FIND_AND_CLICK_IF_EXIST |
对于其他 TYPE,配置详情如下:
对象 | 说明 |
---|---|
COMMAND |
一个具有 TEXT 值的对象,值包含要执行的命令。 |
HAS_PACKAGE_IN_FOREGROUND |
一个具有 TEXT 值的对象,值含有软件包。 |
SWIPE |
对于 SWIPE 操作,请省略 CONFIG key 。此方法仅使用 SWIPE_CONFIG |
WAIT_MS |
一个具有 TEXT 值的对象,值包含要等待的毫秒数。 |
与滚动和滑动相关的操作需要进行额外的配置,如下所示:
SCROLL_CONFIG
对象 | 说明 |
---|---|
SCROLL_ACTION |
USE_GESTURE 或 USE_BUTTON 。 |
SCROLL_DIRECTION |
HORIZONTAL 或 VERTICAL 。 |
SCROLL_ELEMENT |
用于指明要滚动的容器的对象,其形式与界面元素配置(见上文)相同。 |
SCROLL_FORWARD 、SCROLL_BACKWARD |
前进和后退滚动按钮(当 SCROLL_ACTION 为 USE_BUTTON 时,此项为必需)。 |
SCROLL_MARGIN |
如果 SCROLL_ACTION 为 USE_GESTURE ,则表示在与容器边缘间隔一定距离时开始和停止拖动,以便执行滚动操作(可选,默认值为 10)。 |
SCROLL_WAIT_TIME |
如果 SCROLL_ACTION 为 USE_GESTURE ,则表示在搜索要点击的对象时,两次滚动手势之间要等待的时长(以毫秒为单位)。(可选,默认值为 1)。 |
SWIPE_CONFIG
对象 | 说明 |
---|---|
SWIPE_DIRECTION |
TOP_TO_BOTTOM 、BOTTOM_TO_TOP 、
LEFT_TO_RIGHT 或 RIGHT_TO_LEFT |
SWIPE_FRACTION |
下列各项之一:
|
NUMBER_OF_STEPS |
用于执行滑动的步数。请参阅
segmentSteps 。
|
构建和执行
Spectatio 框架会自动构建为测试 APK 的一部分。如需构建测试 APK,AOSP 代码库必须位于本地工作站上。构建测试 APK 后,用户必须在设备上安装 APK,然后执行测试。
以下代码示例展示了测试 APK 的构建、安装和执行过程。
# Build Test APK make TEST-APK-NAME
# Install Test APK adb -s DEVICE-SERIAL install -r PATH-FOR-BUILT-TEST-APK
# Execute Test with the JSON file adb -s DEVICE-SERIAL shell am instrument -w -r -e debug false -e config-file-path /data/local/tmp/jsonFile.json -e class TEST-PACKAGE.TEST-CLASSNAME TEST-PACKAGE/androidx.test.runner.AndroidJUnitRunner
在以下命令中:
TEST-APK-NAME:待测试应用的名称。例如,将 TEST-APK-NAME 设置为
AndroidAutomotiveSettingsTests
便可测试Android.bp
文件中指定的 Wi-Fi 设置。您可以在 Automotive 测试对应的Android.bp
文件中找到 APK 的名称。DEVICE-SERIAL:DUT 的序列号。如果只有 1 部设备连接到宿主机,则无需使用此参数。
config-file-path
:可选参数,只有在按 JSON 配置文件中指定的方式提供非默认设备界面配置时才需要使用。如果未提供该参数,则 Spectatio 框架会使用默认值来执行测试。PATH-FOR-BUILT-TEST-APK:系统在执行
make
命令时构建测试 APK 的路径。TEST-PACKAGE:测试软件包的名称。
TEST-CLASSNAME:测试类的名称。例如,对于 Wi-Fi 设置测试,测试软件包的名称为
android.platform.tests
,测试类的名称为WifiSettingTest
。
Automotive Snippet 库
Automotive Snippet Library 是一组适用于 Android 开源项目 (AOSP) 的 Android 测试库,旨在与汽车应用和服务交互。它利用 Spectatio 提供的便捷机制,从宿主机(测试)机器向搭载 Android 的设备执行远程过程调用 (RPC)。
开始使用
在开始之前,请查看以下部分。
前提条件
- 在宿主机上安装了 Python 3.x。
- 设置了 AOSP 环境并安装了必要的构建工具。
- 一部具有 adb 访问权限的 Android 汽车设备(模拟器或实体设备)。
编译
如需编译 Automotive Snippet 库提供的各种代码段,您可以使用提供的 android.bp
文件。按照上一部分中的命令编译 APK。
部署
成功编译代码段库后,使用上一部分中提到的 adb install
命令将生成的 APK 部署到目标设备。
运行测试
这些代码段库公开了多种 RPC 方法来与汽车系统进行交互。这些方法可通过宿主机中的 Mobly 框架进行调用。假设您已设置 Mobly 测试环境,则可以使用 snippet_shell.py
脚本打开交互式 Python shell,然后您可从中手动在设备上调用 RPC 方法。示例调用:
python3 snippet_shell.py com.google.android.mobly.snippet.bundled -s <serial>
将 <serial>
替换为设备序列号,如果连接了多个设备,您可以使用 adb 设备获取该序列号。
包含的库
Automotive Snippet 库包含以下代码段库和帮助程序:
AutomotiveSnippet:提供与车辆操作相关的 API,例如拨号、音量控制、车辆硬按键和媒体中心互动。
PhoneSnippet:提供与电话相关的 API,包括通话处理、浏览联系人和短信操作。
Automotive 代码段和 PhoneSnippet 共享一些常见逻辑。具体而言,您可以入侵与蓝牙相关的 RCP 调用,以配对汽车设备和手机设备。此 bt_discovery_test
演示了操作方法。
- TEST-CLASSNAME:测试类的名称。例如,对于 Wi-Fi 设置测试,测试软件包的名称为
android.platform.tests
,测试类的名称为WifiSettingTest
。