Spectatio es un framework de pruebas de código abierto desarrollado para probar el SO Android Automotive (AAOS) en dispositivos reales y virtuales. Spectatio proporciona APIs para probar apps en un dispositivo automotriz y es una solución extensible y escalable que se usa para verificar la capacidad y el rendimiento del AAOS y sus apps.
Diseño de alto nivel
El framework de Spectatio es adaptable y expandible para varias implementaciones de IU de AAOS. Se usa para probar la capacidad y el rendimiento de AAOS en el hardware del dispositivo, los emuladores y los entornos virtualizados.
En la siguiente figura, se explica el diseño de alto nivel del framework de Spectatio.
Figura 1: Diseño de alto nivel del framework de Spectatio.
El framework de Spectatio, creado sobre UI Automator, proporciona un conjunto de APIs para compilar pruebas de IU que interactúan con las apps del usuario y del sistema en AAOS. Las pruebas de Automotive usan las APIs que proporciona el framework de Spectatio para las pruebas, lo que las hace independientes del dispositivo en prueba (DUT) y escalables para probar varios dispositivos, si son compatibles.
En la Figura 1, se muestra que el framework de Spectatio está modularizado en función de apps de referencia, como Dialer, Medicenter y Configuración, con interfaces y ayudantes específicos de la app, lo que permite que se extienda fácilmente a apps nuevas. El framework de Spectatio reutiliza las clases auxiliares estándar y de utilidad comunes. La clase auxiliar estándar es la clase superior de todas las funciones auxiliares de la app y proporciona funciones estándar que son específicas del dispositivo o aplicables a todas las apps. Las clases auxiliares de utilidad proporcionan utilidades como la lectura o escritura de archivos desde el dispositivo.
Arquitectura
Para proporcionar un conjunto de APIs para compilar pruebas de IU, el framework de Spectatio implementa interfaces y ayudantes específicos de la app, mientras extiende la clase de ayuda estándar existente y, además, importa las clases de ayuda de utilidad.
En la Figura 2, se ilustra la arquitectura de alto nivel del framework de Spectatio y todas las entidades involucradas en la implementación de APIs para probar una app.
Figura 2: Arquitectura de alto nivel del framework de Spectatio
La interfaz de ayuda de la app proporciona un modelo para la implementación de un ayudante de apps. Consiste en varias funciones auxiliares que se necesitan para probar apps. Cada app tiene su propia interfaz, como IAutoSettingHelper
y IAutoDialHelper
.
Para obtener más información y una lista de funciones de la interfaz, consulta las funciones de la interfaz de la app helper en AOSP.
La clase auxiliar estándar consta de atributos y funciones estándar que son necesarios para la configuración del dispositivo, pero no son específicos de ninguna app, como pressHome
y scroll
. La clase auxiliar estándar se define en AbstractAutoStandardAppHelper.java
.
El framework usa las clases de ayuda de utilidad. Por ejemplo, AutoJsonUtility.java
es una clase de utilidad que carga el archivo de configuración JSON del dispositivo determinado y actualiza las configuraciones del framework durante el tiempo de ejecución.
El módulo de implementación del asistente de la app es el núcleo del framework de Spectatio. Contiene la implementación de las funciones auxiliares definidas en la interfaz de ayuda de la app, que son necesarias para probar apps en un dispositivo automotriz. Cada app tiene su propia implementación, como SettingHelperImpl
y DialHelperImpl
, que usan las pruebas de Automotive para probar las apps. Para obtener más información y una lista de implementaciones, consulta las funciones de implementación de ayudantes de apps en AOSP.
Las pruebas de Automotive usan las funciones de implementación de la app auxiliar para probar varias operaciones relacionadas con la app. Usa la clase HelperAccessor
para obtener acceso a las funciones de implementación de la app auxiliar.
En el siguiente código, se muestra la configuración, la limpieza y la ejecución de una prueba automotriz de muestra.
@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());
}
}
Personalización
El framework de Spectatio es independiente de la IU del dispositivo, por lo que es escalable para probar dispositivos con hardware y IU variados. Para lograr esta escalabilidad, Spectatio usa configuraciones de dispositivos predeterminadas según el dispositivo de referencia. Para admitir configuraciones de dispositivos que no son predeterminadas, el framework usa un archivo de configuración JSON durante el tiempo de ejecución para establecer los cambios de IU deseados para el dispositivo. Un archivo de configuración JSON admite elementos de la IU, como TEXT
, DESCRIPTION
y RESOURCE_ID
, junto con la configuración de path
y debe contener solo la información sobre los cambios de la IU del DUT. El resto de los elementos de la IU usan los valores de configuración predeterminados que se proporcionan en el framework.
Configuraciones predeterminadas del dispositivo
En el siguiente archivo de configuración JSON de muestra, se muestran las configuraciones de dispositivos disponibles y sus valores predeterminados.
Haz clic aquí para mostrar un archivo de configuración JSON de muestra
{ "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" } } } }
Configuraciones de dispositivos alternativas
En la siguiente muestra de código, se muestra un ejemplo del archivo de configuración JSON en el que la configuración predeterminada se anula con la configuración del DUT. En este ejemplo:
La configuración de Internet se denomina Internet y redes en los dispositivos de referencia y Conectividad en el DUT.
La configuración de fecha y hora está disponible en Configuración > Fecha y hora para los dispositivos de referencia y en Configuración > Sistema > Fecha y hora para el 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"
},
....
}
Cuando el archivo de configuración JSON esté listo, se proporcionará en el tiempo de ejecución, como se muestra en el siguiente bloque de código:
# Push The JSON configuration file to the device
adb -s DEVICE-SERIAL push PATH-OF-JSON-FILE /data/local/tmp/runtimeSpectatioConfig.json
En este comando:
DEVICE-SERIAL: Es el ID de serie del DUT. Este parámetro no es obligatorio si solo un dispositivo está conectado al host.
PATH-TO-JSON-FILE: Es la ruta del archivo JSON en la máquina host.
Formato de configuración
Hay cinco objetos de nivel superior en la configuración, con las siguientes claves y valores:
Objeto | Descripción |
---|---|
PACKAGES |
Es un objeto que describe el paquete principal de varias apps, que se usan para determinar cuándo esa app está en primer plano. |
ACTIONS |
Un objeto que indica los tipos de acciones y los parámetros de varias acciones. Por ejemplo, si se deben usar botones o un gesto para desplazarse. |
COMMANDS |
Un objeto que especifica comandos que realizan varias acciones. |
UI_ELEMENTS |
Es un objeto que se usa para construir "BySelectors" de UI Automator que seleccionan elementos de la IU (se describen en detalle a continuación). |
WORKFLOWS |
Secuencias de acciones que realizan tareas de alto nivel (que se describen en detalle a continuación). |
Elementos de la IU
Cada elemento de la IU tiene un TYPE
que especifica lo que UI Automator buscará para identificar el elemento (como el ID de recurso, el texto y la descripción) y los valores de configuración asociados con ese tipo. En general, cada vez que un ayudante identifica un elemento en la pantalla con esta configuración, obtiene exactamente un elemento. Si varios elementos coinciden con la configuración, se usa uno arbitrario en la prueba. Por lo tanto, la configuración debe (en general) escribirse de manera lo suficientemente específica como para reducirse a un elemento en el contexto relevante.
TEXTO
Este es el tipo de elemento de IU más simple. El elemento de la IU se identifica por su texto y requiere una coincidencia exacta.
"CALL_HISTORY_MENU": {
"TYPE": "TEXT",
"VALUE": "Recents"
}
TEXT_CONTAINS
Es igual que TEXT
, excepto que el VALUE
especificado solo debe aparecer en algún lugar del texto del elemento que se debe hacer coincidir.
"PRIVACY_CALENDAR": {
"TYPE": "TEXT_CONTAINS",
"VALUE": "Calendar"
}
DESCRIPCIÓN
Identifica el elemento por su atributo de descripción del contenido, lo que requiere una coincidencia exacta.
"APP_GRID_SCROLL_BACKWARD_BUTTON": {
"TYPE": "DESCRIPTION",
"VALUE": "Scroll up"
}
RESOURCE_ID
Identifica el elemento por su ID de recurso y, de manera opcional, también verifica el componente del paquete de ese ID. La clave PACKAGE
es opcional. Si se omite, coincidirá cualquier paquete y solo se considerará la parte del ID que sigue a :id/
.
"APP_LIST_SCROLL_ELEMENT": {
"TYPE": "RESOURCE_ID",
"VALUE": "apps_grid",
"PACKAGE": "com.android.car.carlauncher"
}
HACER CLIC, DESPLAZABLE
Identifica el elemento según si se puede hacer clic en él (o no) o si se puede desplazar.
Estos son tipos de elementos muy amplios y, por lo general, solo deben usarse en un MULTIPLE
para ayudar a acotar otro tipo de elemento. La clave FLAG
es opcional y se establece de forma predeterminada en true
.
"SAMPLE_ELEMENT": {
"TYPE": "CLICKABLE",
"FLAG": false
}
CLASE
Identifica el elemento según su clase.
"SECURITY_SETTINGS_ENTER_PASSWORD": {
"TYPE": "CLASS",
"VALUE": "android.widget.EditText"
}
HAS_ANCESTOR
Para identificar el elemento, busca la jerarquía de widgets en sus ancestros. La clave ANCESTOR
contiene un objeto que identifica al ancestro. La clave DEPTH
especifica hasta dónde se debe buscar en la jerarquía. DEPTH
es opcional y tiene un valor predeterminado de 1
.
"SAMPLE_ELEMENT": {
"TYPE": "HAS_ANCESTOR",
"DEPTH": 2,
"ANCESTOR": {
"TYPE": "CLASS",
"VALUE": "android.view.ViewGroup"
}
}
HAS_DESCENDANT
Para identificar el elemento, busca en la jerarquía sus elementos secundarios. La clave DESCENDANT
contiene un objeto que especifica el elemento secundario que se debe buscar. La clave DEPTH
especifica hasta dónde se debe buscar en la jerarquía. DEPTH
es opcional y tiene un valor predeterminado de 1
.
"SAMPLE_ELEMENT": {
"TYPE": "HAS_DESCENDANT",
"DEPTH": 2,
"DESCENDANT": {
"TYPE": "CLASS",
"VALUE": "android.view.ViewGroup"
}
}
MULTIPLE
Identifica el elemento en función de varias condiciones simultáneas, que deben cumplirse todas.
"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"
}
}
]
}
En este ejemplo, la configuración identifica un RelativeLayout
que tiene un descendiente en la profundidad 2
, que tiene el texto Permission manager
.
Workflows
Un flujo de trabajo representa una secuencia de acciones que se usan para realizar una tarea particular, que puede diferir bastante de un tipo de dispositivo a otro y es más flexible para representarse en la configuración que en el código.
"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"
}
}
}
]
}
Cada flujo de trabajo es un par clave-valor en el que la clave es el nombre del flujo de trabajo y el valor es un array de acciones que se deben realizar. Cada acción tiene un NAME
, un TYPE
, (por lo general) un CONFIG
y, a veces, un SWIPE_CONFIG
o SCROLL_CONFIG
. Para la mayoría de los TYPE, CONFIG
es un objeto con una clave UI_ELEMENT
cuyo valor tiene la misma forma que una entrada de elemento de IU (consulta más arriba). Esos TYPE son los siguientes:
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 |
Para los otros TYPE, los detalles de configuración son los siguientes:
Objeto | Descripción |
---|---|
COMMAND |
Un objeto con un valor TEXT que contiene el comando que se ejecutará. |
HAS_PACKAGE_IN_FOREGROUND |
Un objeto con un valor TEXT que contiene el paquete. |
SWIPE |
Omite CONFIG key para una acción SWIPE . Esto solo usa SWIPE_CONFIG . |
WAIT_MS |
Un objeto con un valor TEXT que contiene la cantidad de
milisegundos que se deben esperar. |
Las acciones relacionadas con el desplazamiento y el deslizamiento requieren una configuración adicional, como se indica a continuación:
SCROLL_CONFIG
Objeto | Descripción |
---|---|
SCROLL_ACTION |
USE_GESTURE o USE_BUTTON |
SCROLL_DIRECTION |
HORIZONTAL o VERTICAL |
SCROLL_ELEMENT |
Un objeto que indica el contenedor que se debe desplazar, con el mismo formato que la configuración de un elemento de la IU (consulta más arriba). |
SCROLL_FORWARD , SCROLL_BACKWARD |
Los botones de desplazamiento hacia adelante y hacia atrás (obligatorios cuando SCROLL_ACTION es USE_BUTTON ) |
SCROLL_MARGIN |
Si SCROLL_ACTION es USE_GESTURE , la distancia desde el borde del contenedor para iniciar y detener el arrastre que se usará para realizar el desplazamiento (opcional, valor predeterminado = 10). |
SCROLL_WAIT_TIME |
Si SCROLL_ACTION es USE_GESTURE , es el tiempo en
milisegundos que se espera entre los gestos de desplazamiento cuando se busca un objeto en el que hacer
clic.
(Opcional, valor predeterminado = 1). |
SWIPE_CONFIG
Objeto | Descripción |
---|---|
SWIPE_DIRECTION |
TOP_TO_BOTTOM , BOTTOM_TO_TOP ,
LEFT_TO_RIGHT o RIGHT_TO_LEFT |
SWIPE_FRACTION |
Una de las siguientes opciones:
|
NUMBER_OF_STEPS |
Es la cantidad de pasos que se usarán para realizar el deslizamiento. Consulta los
segmentSteps .
|
Compila y ejecuta
El framework de Spectatio se compila automáticamente como parte del APK de prueba. Para compilar el APK de prueba, la base de código de AOSP debe residir en la estación de trabajo local. Después de compilar el APK de prueba, el usuario debe instalarlo en el dispositivo y ejecutar la prueba.
En la siguiente muestra de código, se muestra la compilación, la instalación y la ejecución de un APK de prueba.
# 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
En estos comandos, se ilustra lo siguiente:
TEST-APK-NAME: Es el nombre de la app que se probará. Por ejemplo, establece TEST-APK-NAME en
AndroidAutomotiveSettingsTests
para probar la configuración de Wi-Fi como se especifica en el archivoAndroid.bp
. El nombre del APK se puede encontrar en el archivoAndroid.bp
correspondiente de la prueba de Automotive.DEVICE-SERIAL: El ID de serie del DUT. Este parámetro no es obligatorio si solo un dispositivo está conectado al host.
config-file-path
: Es un parámetro opcional que solo se requiere para proporcionar configuraciones de IU del dispositivo no predeterminadas, como se especifica en el archivo de configuración JSON. Si no se proporcionan, el framework usa valores predeterminados para ejecutar las pruebas.PATH-FOR-BUILT-TEST-APK: Es la ruta de acceso en la que se compila el APK de prueba cuando se ejecuta el comando
make
.TEST-PACKAGE: Es el nombre del paquete de prueba.
TEST-CLASSNAME: Es el nombre de la clase de prueba. Por ejemplo, para la prueba Configuración de Wi-Fi, el paquete de prueba es
android.platform.tests
y el nombre de la clase de prueba esWifiSettingTest
.
Biblioteca de fragmentos de Automotive
La biblioteca de fragmentos de Automotive es un conjunto de bibliotecas de Android Test para el Proyecto de código abierto de Android (AOSP) diseñado para interactuar con apps y servicios de Automotive. Aprovecha Spectatio con un mecanismo conveniente para ejecutar llamadas de procedimiento remoto (RPC) desde una máquina host (de prueba) a un dispositivo con Android.
Comenzar
Antes de comenzar, revisa estas secciones.
Requisitos previos
- Python 3.x instalado en la máquina host
- Configuración del entorno de AOSP con las herramientas de compilación necesarias
- Un dispositivo Android Automotive (emulador o dispositivo físico) con acceso a ADB
Compilación
Para compilar los diversos fragmentos que proporciona la biblioteca de fragmentos de Automotive, puedes
usar el archivo android.bp
proporcionado. Los siguientes comandos de la sección anterior para compilar el APK
Implementación
Después de compilar correctamente las bibliotecas de fragmentos, implementa los APK resultantes en el dispositivo de destino con el comando adb install
que se mencionó en la sección anterior.
Cómo ejecutar pruebas
Las bibliotecas de fragmentos exponen varios métodos de RPC para interactuar con el sistema automotriz. Estos métodos se pueden invocar a través del framework de Mobly desde la máquina anfitrión. Si tienes configurado el entorno de prueba de Mobly, puedes usar la secuencia de comandos snippet_shell.py
para abrir una shell interactiva de Python, en la que puedes invocar manualmente métodos de RPC en el dispositivo. Invocación de ejemplo:
python3 snippet_shell.py com.google.android.mobly.snippet.bundled -s <serial>
Reemplaza <serial>
por el número de serie del dispositivo, que puedes obtener con dispositivos adb si hay varios dispositivos conectados.
Bibliotecas incluidas
La biblioteca de fragmentos de Automotive incluye las siguientes bibliotecas y ayudas de fragmentos:
AutomotiveSnippet: Proporciona APIs relacionadas con las operaciones del vehículo, como la marcación, el control de volumen, las teclas duras del vehículo y la interacción con el centro multimedia.
PhoneSnippet: Proporciona APIs relacionadas con la telefonía, como el manejo de llamadas, la navegación de contactos y las operaciones de SMS.
El fragmento Automotive y el PhoneSnippet comparten una lógica común.
Específicamente, puedes invadir las llamadas de RCP relacionadas con Bluetooth para vincular un dispositivo automotriz y un teléfono. En este bt_discovery_test
, se muestra cómo hacerlo.
- TEST-CLASSNAME: Es el nombre de la clase de prueba. Por ejemplo, para la prueba de Configuración de Wi-Fi, el paquete de prueba es
android.platform.tests
y el nombre de la clase de prueba esWifiSettingTest
.