Spectatio ist ein Open-Source-Testframework, das für das Testen von Android Automotive OS (AAOS) auf echten und virtuellen Geräten entwickelt wurde. Spectatio bietet APIs zum Testen von Apps auf einem Automobilgerät und ist eine erweiterbare und skalierbare Lösung, mit der die Funktionen und die Leistung von AAOS und seinen Apps überprüft werden.
Konzeption
Das Spectatio-Framework ist anpassbar und erweiterbar für verschiedene AAOS-UI-Implementierungen. Es wird zum Testen der Funktionen und der Leistung von AAOS auf Gerätehardware, Emulatoren und virtualisierten Umgebungen verwendet.
Die folgende Abbildung zeigt das allgemeine Design des Spectatio-Frameworks.
Abbildung 1: Übersicht über das Spectatio-Framework.
Das Spectatio-Framework basiert auf UI Automator und bietet eine Reihe von APIs zum Erstellen von UI-Tests, die mit Nutzer- und System-Apps auf AAOS interagieren. Bei Automotive-Tests werden die APIs des Spectatio-Frameworks verwendet. Dadurch sind diese Tests unabhängig vom zu testenden Gerät und können auf verschiedene Geräte skaliert werden, sofern diese unterstützt werden.
Abbildung 1 zeigt, dass das Spectatio-Framework auf Referenz-Apps wie Dialer, Medicenter und Settings basiert und app-spezifische Schnittstellen und Hilfsprogramme verwendet. Dadurch lässt es sich problemlos für neue Apps erweitern. Das Spectatio-Framework verwendet die gängigen Standard- und Dienstprogramm-Helper-Klassen wieder. Die Standard-Hilfsklasse ist die übergeordnete Klasse für alle App-Hilfsfunktionen und bietet Standardfunktionen, die gerätespezifisch oder appübergreifend anwendbar sind. Die Utility-Helferklassen bieten Dienstprogramme wie das Lesen oder Schreiben von Dateien auf dem Gerät.
Architektur
Um eine Reihe von APIs zum Erstellen von UI-Tests bereitzustellen, implementiert das Spectatio-Framework app-spezifische Schnittstellen und Hilfsklassen, erweitert die vorhandene Standard-Hilfsklasse und importiert die Utility-Hilfsklassen.
Abbildung 2 zeigt die allgemeine Architektur des Spectatio-Frameworks und alle Entitäten, die an der Implementierung von APIs zum Testen einer App beteiligt sind.
Abbildung 2: Übersicht über die Spectatio-Framework-Architektur.
Die App-Helper-Schnittstelle bietet eine Vorlage für die Implementierung eines App-Helpers. Es besteht aus verschiedenen Hilfsfunktionen, die zum Testen von Apps benötigt werden. Jede App hat eine eigene Schnittstelle, z. B. IAutoSettingHelper
und IAutoDialHelper
.
Weitere Informationen und eine Liste der Schnittstellenfunktionen finden Sie unter App-Helper-Schnittstellenfunktionen auf AOSP.
Die Standard-Helferklasse besteht aus Standardattributen und -funktionen, die für die Geräteeinrichtung erforderlich sind, aber nicht für eine bestimmte App gelten, z. B. pressHome
und scroll
. Die Standard-Helferklasse ist in AbstractAutoStandardAppHelper.java
definiert.
Die Dienstprogramm-Hilfsklassen werden vom Framework verwendet. AutoJsonUtility.java
ist beispielsweise eine Utility-Klasse, die die angegebene JSON-Konfigurationsdatei für Geräte lädt und die Framework-Konfigurationen zur Laufzeit aktualisiert.
Das Modul für die Implementierung des App-Helpers ist das Herzstück des Spectatio-Frameworks. Sie enthält die Implementierung für die in der App-Helfer-Schnittstelle definierten Hilfsfunktionen, die zum Testen von Apps auf einem Automotive-Gerät erforderlich sind. Jede App hat ihre eigene Implementierung, z. B. SettingHelperImpl
und DialHelperImpl
, die von den Automotive-Tests zum Testen der Apps verwendet wird. Weitere Informationen und eine Liste der Implementierungen finden Sie unter App-Helper-Implementierungsfunktionen in AOSP.
Bei Automotive-Tests werden die Implementierungsfunktionen des App-Helpers verwendet, um verschiedene Vorgänge im Zusammenhang mit der App zu testen. Verwenden Sie die Klasse HelperAccessor
, um auf die Implementierungsfunktionen des App-Helpers zuzugreifen.
Der folgende Code zeigt die Einrichtung, Bereinigung und Ausführung eines Beispieltests für die Automobilbranche.
@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());
}
}
Personalisierung
Das Spectatio-Framework ist unabhängig von der Geräte-Benutzeroberfläche und lässt sich daher skalieren, um Geräte mit unterschiedlichen Benutzeroberflächen und Hardware zu testen. Um diese Skalierbarkeit zu erreichen, verwendet Spectatio Standardgerätekonfigurationen, die auf dem Referenzgerät basieren. Zur Unterstützung von nicht standardmäßigen Gerätekonfigurationen verwendet das Framework zur Laufzeit eine JSON-Konfigurationsdatei, um die gewünschten Änderungen an der Benutzeroberfläche für das Gerät festzulegen. Eine JSON-Konfigurationsdatei unterstützt UI-Elemente wie TEXT
, DESCRIPTION
und RESOURCE_ID
sowie path
-Einstellungen und darf nur die Informationen zu den UI-Änderungen für das DUT enthalten. Für die restlichen UI-Elemente werden die Standardkonfigurationswerte verwendet, die im Framework bereitgestellt werden.
Standardkonfigurationen für Geräte
Die folgende JSON-Beispielkonfigurationsdatei zeigt die verfügbaren Gerätekonfigurationen und ihre Standardwerte.
Klicken Sie hier, um eine JSON-Beispielkonfigurationsdatei aufzurufen.
{ "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" } } } }
Alternative Gerätekonfigurationen
Das folgende Codebeispiel zeigt ein Beispiel für die JSON-Konfigurationsdatei, in der die Standardeinstellungen durch die Einstellungen auf dem DUT überschrieben werden. In diesem Beispiel gilt Folgendes:
Die Interneteinstellungen heißen auf Referenzgeräten Netzwerk & Internet und auf dem zu testenden Gerät Verbindung.
Die Einstellungen für Datum und Uhrzeit sind für Referenzgeräte unter Einstellungen > Datum und Uhrzeit und für das Prüfling unter Einstellungen > System > Datum und Uhrzeit verfügbar.
// 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"
},
....
}
Wenn die JSON-Konfigurationsdatei fertig ist, wird sie zur Laufzeit bereitgestellt, wie im folgenden Codeblock gezeigt:
# Push The JSON configuration file to the device
adb -s DEVICE-SERIAL push PATH-OF-JSON-FILE /data/local/tmp/runtimeSpectatioConfig.json
In diesem Befehl gilt:
DEVICE-SERIAL: Serien-ID des zu testenden Geräts. Dieser Parameter ist nicht erforderlich, wenn nur ein Gerät mit dem Host verbunden ist.
PATH-TO-JSON-FILE: Pfad der JSON-Datei auf dem Hostcomputer.
Konfigurationsformat
Die Konfiguration enthält fünf Objekte der obersten Ebene mit den folgenden Schlüsseln und Werten:
Objekt | Beschreibung |
---|---|
PACKAGES |
Ein Objekt, das das Hauptpaket für verschiedene Apps beschreibt, mit dem ermittelt wird, wann die App im Vordergrund ist. |
ACTIONS |
Ein Objekt, das Aktionstypen und Parameter für verschiedene Aktionen angibt. Sie können beispielsweise festlegen, ob zum Scrollen Schaltflächen oder eine Geste verwendet werden soll. |
COMMANDS |
Ein Objekt, das Befehle für verschiedene Aktionen enthält. |
UI_ELEMENTS |
Ein Objekt, das zum Erstellen von UI Automator-`BySelectors` verwendet wird, mit denen UI-Elemente ausgewählt werden (siehe unten). |
WORKFLOWS |
Folgen von Aktionen, mit denen übergeordnete Aufgaben ausgeführt werden (unten ausführlich beschrieben). |
UI-Elemente
Jedes UI-Element hat ein TYPE
, das angibt, wonach UI Automator suchen soll, um das Element zu identifizieren (z. B. Ressourcen-ID, Text und Beschreibung), sowie Konfigurationswerte, die mit diesem Typ verknüpft sind. Im Allgemeinen wird immer genau ein Element zurückgegeben, wenn ein Helfer mit dieser Konfiguration ein Element auf dem Bildschirm identifiziert. Wenn mehrere Elemente der Konfiguration entsprechen, wird ein beliebiges Element im Test verwendet. Daher sollte die Konfiguration (in der Regel) so spezifisch sein, dass sie auf ein Element im relevanten Kontext eingegrenzt wird.
TEXT
Dies ist der einfachste UI-Elementtyp. Das UI-Element wird anhand seines Texts identifiziert und erfordert eine genaue Übereinstimmung.
"CALL_HISTORY_MENU": {
"TYPE": "TEXT",
"VALUE": "Recents"
}
TEXT_CONTAINS
Wie TEXT
, mit dem Unterschied, dass das angegebene VALUE
nur irgendwo im Text des abzugleichenden Elements vorkommen muss.
"PRIVACY_CALENDAR": {
"TYPE": "TEXT_CONTAINS",
"VALUE": "Calendar"
}
BESCHREIBUNG
Identifizieren Sie das Element anhand des Attributs „content description“ (Inhaltsbeschreibung). Hier ist eine genaue Übereinstimmung erforderlich.
"APP_GRID_SCROLL_BACKWARD_BUTTON": {
"TYPE": "DESCRIPTION",
"VALUE": "Scroll up"
}
RESOURCE_ID
Identifizieren Sie das Element anhand seiner Ressourcen-ID und prüfen Sie optional auch die Paketkomponente dieser ID. Der Schlüssel PACKAGE
ist optional. Wenn er weggelassen wird, wird jedes Paket als Übereinstimmung betrachtet und nur der Teil der ID nach :id/
wird berücksichtigt.
"APP_LIST_SCROLL_ELEMENT": {
"TYPE": "RESOURCE_ID",
"VALUE": "apps_grid",
"PACKAGE": "com.android.car.carlauncher"
}
ANKLICKBAR, SCROLLBAR
Identifizieren Sie das Element anhand dessen, ob es angeklickt oder gescrollt werden kann.
Diese Elementtypen sind sehr allgemein und sollten in der Regel nur in einem MULTIPLE
verwendet werden, um einen anderen Elementtyp einzugrenzen. Der Schlüssel FLAG
ist optional und hat standardmäßig den Wert true
.
"SAMPLE_ELEMENT": {
"TYPE": "CLICKABLE",
"FLAG": false
}
KLASSE
Identifizieren Sie das Element anhand seiner Klasse.
"SECURITY_SETTINGS_ENTER_PASSWORD": {
"TYPE": "CLASS",
"VALUE": "android.widget.EditText"
}
HAS_ANCESTOR
Ermitteln Sie das Element, indem Sie die Widget-Hierarchie der übergeordneten Elemente aufrufen. Der ANCESTOR
-Schlüssel enthält ein Objekt, das den übergeordneten Knoten identifiziert. Mit dem Schlüssel DEPTH
wird angegeben, wie weit in der Hierarchie gesucht werden soll. DEPTH
ist optional und hat den Standardwert 1
.
"SAMPLE_ELEMENT": {
"TYPE": "HAS_ANCESTOR",
"DEPTH": 2,
"ANCESTOR": {
"TYPE": "CLASS",
"VALUE": "android.view.ViewGroup"
}
}
HAS_DESCENDANT
Identifizieren Sie das Element, indem Sie sich die untergeordneten Elemente in der Hierarchie ansehen. Der Schlüssel DESCENDANT
enthält ein Objekt, das das untergeordnete Element angibt, nach dem gesucht werden soll. Mit dem Schlüssel DEPTH
wird angegeben, wie weit in der Hierarchie gesucht werden soll. DEPTH
ist optional und hat den Standardwert 1
.
"SAMPLE_ELEMENT": {
"TYPE": "HAS_DESCENDANT",
"DEPTH": 2,
"DESCENDANT": {
"TYPE": "CLASS",
"VALUE": "android.view.ViewGroup"
}
}
MULTIPLE
Identifizieren Sie das Element anhand mehrerer gleichzeitiger Bedingungen, die alle erfüllt sein müssen.
"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"
}
}
]
}
In diesem Beispiel wird ein RelativeLayout
mit einem untergeordneten Element in Tiefe 2
mit dem Text Permission manager
angegeben.
Workflows
Ein Workflow stellt eine Abfolge von Aktionen dar, die zum Ausführen einer bestimmten Aufgabe verwendet werden. Diese kann sich je nach Gerätetyp unterscheiden und lässt sich in der Konfiguration flexibler darstellen als im Code.
"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"
}
}
}
]
}
Jeder Workflow ist ein Schlüssel/Wert-Paar, wobei der Schlüssel der Name des Workflows und der Wert ein Array von auszuführenden Aktionen ist. Jede Aktion hat eine NAME
, eine TYPE
, (in der Regel) eine CONFIG
und (manchmal) eine SWIPE_CONFIG
oder SCROLL_CONFIG
. Bei den meisten TYPEs ist CONFIG
ein Objekt mit dem Schlüssel UI_ELEMENT
, dessen Wert dieselbe Form wie ein UI-Elementeintrag hat (siehe oben). Die folgenden TYPEs sind verfügbar:
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 |
Für die anderen TYPEN sind die Konfigurationsdetails:
Objekt | Beschreibung |
---|---|
COMMAND |
Ein Objekt mit einem TEXT -Wert, der den auszuführenden Befehl enthält. |
HAS_PACKAGE_IN_FOREGROUND |
Ein Objekt mit einem TEXT -Wert, der das Paket enthält. |
SWIPE |
Lassen Sie die CONFIG key für eine SWIPE -Aktion weg. Hier wird nur SWIPE_CONFIG verwendet. |
WAIT_MS |
Ein Objekt mit einem TEXT -Wert, der die Anzahl der Millisekunden angibt, die gewartet werden soll. |
Für Scroll- und Wischvorgänge ist eine zusätzliche Konfiguration erforderlich:
SCROLL_CONFIG
Objekt | Beschreibung |
---|---|
SCROLL_ACTION |
Entweder USE_GESTURE oder USE_BUTTON |
SCROLL_DIRECTION |
Entweder HORIZONTAL oder VERTICAL |
SCROLL_ELEMENT |
Ein Objekt, das den zu scrollenden Container angibt. Es hat dieselbe Form wie eine UI-Elementkonfiguration (siehe oben). |
SCROLL_FORWARD , SCROLL_BACKWARD |
Die Schaltflächen zum Vor- und Zurückscrollen (erforderlich, wenn SCROLL_ACTION gleich USE_BUTTON ist). |
SCROLL_MARGIN |
Wenn SCROLL_ACTION gleich USE_GESTURE ist, wird die Distanz vom Rand des Containers zum Starten und Beenden des Ziehens verwendet, um den Bildlauf auszuführen (optional, Standardwert = 10). |
SCROLL_WAIT_TIME |
Wenn SCROLL_ACTION USE_GESTURE ist, die Zeit in Millisekunden, die zwischen Scrollvorgängen gewartet wird, wenn nach einem Objekt gesucht wird, auf das geklickt werden soll.
Optional,Standardwert = 1. |
SWIPE_CONFIG
Objekt | Beschreibung |
---|---|
SWIPE_DIRECTION |
Entweder TOP_TO_BOTTOM , BOTTOM_TO_TOP ,
LEFT_TO_RIGHT oder RIGHT_TO_LEFT |
SWIPE_FRACTION |
Eines der folgenden Dokumente:
|
NUMBER_OF_STEPS |
Die Anzahl der Schritte, die zum Ausführen des Wischvorgangs verwendet werden sollen.
segmentSteps ansehen.
|
Erstellen und ausführen
Das Spectatio-Framework wird automatisch als Teil des Test-APKs erstellt. Um das Test-APK zu erstellen, muss sich die AOSP-Codebasis auf der lokalen Workstation befinden. Nachdem das Test-APK erstellt wurde, muss der Nutzer es auf dem Gerät installieren und den Test ausführen.
Das folgende Codebeispiel zeigt, wie eine Test-APK erstellt, installiert und ausgeführt wird.
# 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
Dabei gilt:
TEST-APK-NAME: Der Name der zu testenden App. Setzen Sie beispielsweise TEST-APK-NAME auf
AndroidAutomotiveSettingsTests
, um die in der DateiAndroid.bp
angegebenen WLAN-Einstellungen zu testen. Der Name des APKs ist in der entsprechendenAndroid.bp
-Datei für den Automotive-Test zu finden.DEVICE-SERIAL: Die Serien-ID des zu testenden Geräts. Dieser Parameter ist nicht erforderlich, wenn nur ein Gerät mit dem Host verbunden ist.
config-file-path
: Optionaler Parameter, der nur erforderlich ist, um nicht standardmäßige Geräte-UI-Konfigurationen anzugeben, wie in der JSON-Konfigurationsdatei beschrieben. Wenn nicht angegeben, verwendet das Framework Standardwerte für die Ausführung der Tests.PATH-FOR-BUILT-TEST-APK: Der Pfad, unter dem das Test-APK erstellt wird, wenn der Befehl
make
ausgeführt wird.TEST-PACKAGE: Der Name des Testpakets.
TEST-CLASSNAME: Der Name der Testklasse. Für den Test WLAN-Einstellungen ist das Testpaket beispielsweise
android.platform.tests
und der Name der TestklasseWifiSettingTest
.
Automotive Snippet Library
Die Automotive Snippet Library ist eine Reihe von Android-Testbibliotheken für das Android Open Source Project (AOSP), die für die Interaktion mit Automotive-Apps und ‑Diensten entwickelt wurden. Es nutzt Spectatio mit einem praktischen Mechanismus zum Ausführen von Remoteprozeduraufrufen (RPCs) von einem Hostcomputer (Testcomputer) auf einem Android-Gerät.
Erste Schritte
Lesen Sie zuerst die folgenden Abschnitte.
Voraussetzungen
- Python 3.x ist auf dem Hostcomputer installiert.
- AOSP-Umgebung mit den erforderlichen Build-Tools einrichten
- Ein Android Automotive-Gerät (Emulator oder physisches Gerät) mit ADB-Zugriff.
Compilation
Um die verschiedenen Snippets aus der Automotive Snippet Library zu kompilieren, können Sie die bereitgestellte Datei android.bp
verwenden. Führen Sie die Befehle im vorherigen Abschnitt aus, um die APK zu kompilieren.
Bereitstellung
Nachdem Sie die Snippet-Bibliotheken erfolgreich kompiliert haben, stellen Sie die resultierenden APKs mit dem im vorherigen Abschnitt erwähnten adb install
-Befehl auf dem Zielgerät bereit.
Tests ausführen
Die Snippet-Bibliotheken stellen mehrere RPC-Methoden zur Interaktion mit dem Automotive-System zur Verfügung. Diese Methoden können über das Mobly-Framework vom Hostcomputer aus aufgerufen werden. Wenn Sie die Mobly-Testumgebung eingerichtet haben, können Sie mit dem Skript snippet_shell.py
eine interaktive Python-Shell öffnen, in der Sie RPC-Methoden auf dem Gerät manuell aufrufen können. Beispielaufruf:
python3 snippet_shell.py com.google.android.mobly.snippet.bundled -s <serial>
Ersetzen Sie <serial>
durch die Seriennummer des Geräts. Diese können Sie mit „adb devices“ abrufen, wenn mehrere Geräte verbunden sind.
Enthaltene Bibliotheken
Die Automotive Snippet Library enthält die folgenden Snippet-Bibliotheken und Hilfsprogramme:
AutomotiveSnippet: Bietet APIs für Fahrzeugvorgänge wie Wählen, Lautstärkeregelung, physische Fahrzeugtasten und Interaktion mit dem Media Center.
PhoneSnippet: Stellt telefoniebezogene APIs bereit, darunter Anrufbearbeitung, Kontaktsuche und SMS-Vorgänge.
Das Automotive-Snippet und das PhoneSnippet haben einige Gemeinsamkeiten.
Insbesondere können Sie in Bluetooth-bezogene RCP-Aufrufe eingreifen, um ein Automobilgerät und ein Smartphone zu koppeln. bt_discovery_test
- TEST-CLASSNAME: Der Name der Testklasse. Für den Test WLAN-Einstellungen ist das Testpaket beispielsweise
android.platform.tests
und der Name der TestklasseWifiSettingTest
.