Spectatio היא מסגרת בדיקה בקוד פתוח שפותחה לבדיקה של Android Automotive OS (AAOS) במכשירים אמיתיים וווירטואליים. Spectatio מספק ממשקי API לבדיקת אפליקציות במכשיר לרכב. זהו פתרון שניתן להרחבה ולתאימות, המשמש לאימות היכולות והביצועים של AAOS והאפליקציות שלו.
העיצוב הכללי
אפשר להתאים את המסגרת של Spectatio ולהרחיב אותה להטמעות שונות של ממשק המשתמש של AAOS. הוא משמש לבדיקת היכולות והביצועים של AAOS בחומרה של המכשיר, במהדמנים ובסביבות וירטואליות.
בתרשים הבא מוסבר העיצוב הכללי של מסגרת Spectatio.
איור 1. העיצוב הכללי של מסגרת Spectatio.
מסגרת Spectatio, שנבנתה על גבי UI Automator, מספקת קבוצה של ממשקי API ליצירת בדיקות ממשק משתמש שמקיימות אינטראקציה עם אפליקציות של משתמשים ושל מערכות ב-AAOS. בבדיקות הרכב נעשה שימוש בממשקי ה-API שסופקו על ידי מסגרת Spectatio לצורך בדיקה. כך הבדיקות האלה עצמאיות מהמכשיר שנבדק (DUT) וניתן להתאים אותן לבדיקה של מכשירים שונים, אם הם נתמכים.
באיור 1 אפשר לראות ש-Spectatio הוא מסגרת מודולרית שמבוססת על אפליקציות עזר כמו Dialer, Medicenter והגדרות, באמצעות ממשקים ורכיבי עזר ספציפיים לאפליקציות. כך אפשר להרחיב אותו בקלות לאפליקציות חדשות. המסגרת של Spectatio משתמשת שוב בסטנדרטים הנפוצים ובכיתות העזר של התשתית. כיתת העזר הרגילה היא הכיתה ההורה של כל פונקציות העזר של האפליקציה, והיא מספקת פונקציות רגילות ספציפיות למכשיר או פונקציות שחלות על אפליקציות שונות. כיתות העזר של השירותים מספקות שירותים כמו קריאה או כתיבת קבצים מהמכשיר.
ארכיטקטורה
כדי לספק קבוצה של ממשקי API ליצירת בדיקות של ממשק המשתמש, מסגרת Spectatio מטמיעה ממשקים ועוזרנים ספציפיים לאפליקציה, תוך הרחבת סוג העוזרן הסטנדרטי הקיים וייבוא של סוגים של עוזרני שירות.
באיור 2 מוצגת הארכיטקטורה ברמה גבוהה של מסגרת Spectatio וכל הישויות שמעורבות בהטמעת ממשקי API לבדיקה של אפליקציה.
איור 2. הארכיטקטורה ברמה גבוהה של מסגרת Spectatio.
ממשק ה-App Helper מספק תוכנית להטמעה של App Helper. הוא מורכב מפונקציות עזר שונות שנדרשות לבדיקת אפליקציות. לכל אפליקציה יש ממשק משלה, כמו IAutoSettingHelper
ו-IAutoDialHelper
.
מידע נוסף ורשימת פונקציות ממשק זמינים במאמר פונקציות ממשק של כלי עזר לאפליקציות ב-AOSP.
סיווג העזרה הרגיל מורכב ממאפיינים ופונקציות רגילים שנדרשים להגדרת המכשיר, אבל הם לא ספציפיים לאף אפליקציה, כמו pressHome
ו-scroll
. כיתה העזר הרגילה מוגדרת ב-AbstractAutoStandardAppHelper.java
.
המסגרת משתמשת בכיתות העזר של התשתית. לדוגמה, AutoJsonUtility.java
הוא סוג שירות לטעינת קובץ התצורה של המכשיר בפורמט JSON ועדכון הגדרות המסגרת בזמן הריצה.
מודול הטמעת העזרה לאפליקציה הוא הליבה של מסגרת Spectatio. הוא מכיל את ההטמעה של פונקציות העזר שמוגדרות בממשק העזר של האפליקציה, שנדרשות לבדיקה של אפליקציות במכשיר לכלי רכב. לכל אפליקציה יש הטמעה משלה, כמו SettingHelperImpl
ו-DialHelperImpl
, שבהן נעשה שימוש בבדיקות הרכב לצורך בדיקת האפליקציות. למידע נוסף ורשימת הטמעות, ראו פונקציות להטמעת כלי עזר לאפליקציות ב-AOSP.
בבדיקות לכלי רכב נעשה שימוש בפונקציות ההטמעה של ה-App Helper כדי לבדוק פעולות שונות שקשורות לאפליקציה. כדי לקבל גישה לפונקציות ההטמעה של ה-App Helper, משתמשים בכיתה 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. הפרמטר הזה לא נדרש אם רק מכשיר אחד מחובר למארח.
PATH-TO-JSON-FILE: הנתיב של קובץ ה-JSON במכונה המארחת.
פורמט ההגדרות האישיות
בתצורה יש חמישה אובייקטים ברמה העליונה, עם המפתחות והערכים הבאים:
אובייקט | תיאור |
---|---|
PACKAGES |
אובייקט שמתאר את החבילה הראשית של אפליקציות שונות, שמשמשות לקביעת המצב שבו האפליקציה הזו נמצאת בחזית. |
ACTIONS |
אובייקט שמציין סוגי פעולות ופרמטרים של פעולות שונות. לדוגמה, אם להשתמש בלחצנים או בתנועה כדי לגלול. |
COMMANDS |
אובייקט שמציין פקודות שמבצעות פעולות שונות. |
UI_ELEMENTS |
אובייקט שמשמש ליצירת 'BySelectors' של UI Automator שבוחרים רכיבי UI (מתוארים בפירוט בהמשך). |
WORKFLOWS |
רצפי פעולות שמבצעים משימות ברמה גבוהה (מתוארים בפירוט בהמשך). |
רכיבים בממשק המשתמש
לכל רכיב בממשק המשתמש יש TYPE
שמציין את מה ש-UI Automator יחפש כדי לזהות את הרכיב (למשל, מזהה המשאב, הטקסט והתיאור) ואת ערכי התצורה שמשויכים לסוג הזה. באופן כללי, בכל פעם שרכיב עזר מזהה רכיב במסך באמצעות ההגדרה הזו, הוא מקבל בדיוק רכיב אחד. אם יש כמה רכיבים שתואמים להגדרה, המערכת תשתמש באחד מהם באופן שרירותי. לכן, ההגדרה צריכה (בדרך כלל) להיות ספציפית מספיק כדי לצמצם אותה לרכיב אחד בהקשר הרלוונטי.
טקסט
זהו סוג הרכיב הפשוט ביותר בממשק המשתמש. אלמנט ממשק המשתמש מזוהה לפי הטקסט שלו, והוא מחייב התאמה מדויקת.
"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
מזהים את הרכיב לפי מזהה המשאב שלו, ואפשר גם לבדוק את רכיב החבילה של המזהה הזה. המפתח PACKAGE
הוא אופציונלי. אם לא מציינים אותו, כל חבילת קוד תתאים, וייבוא הנתונים יתבצע רק לגבי החלק של המזהה שמופיע אחרי :id/
.
"APP_LIST_SCROLL_ELEMENT": {
"TYPE": "RESOURCE_ID",
"VALUE": "apps_grid",
"PACKAGE": "com.android.car.carlauncher"
}
ניתנים ללחיצה ולגלילה
מזהים את האלמנט על סמך האפשרות ללחוץ עליו (או לא) או לגלול בו.
אלה סוגים רחבים מאוד של רכיבים, ובדרך כלל צריך להשתמש בהם רק ב-MULTIPLE
כדי לצמצם את הסוג של רכיב אחר. המפתח FLAG
הוא אופציונלי, והברירת המחדל שלו היא true
.
"SAMPLE_ELEMENT": {
"TYPE": "CLICKABLE",
"FLAG": false
}
CLASS
מזהים את הרכיב על סמך הכיתה שלו.
"SECURITY_SETTINGS_ENTER_PASSWORD": {
"TYPE": "CLASS",
"VALUE": "android.widget.EditText"
}
HAS_ANCESTOR
כדי לזהות את הרכיב, מחפשים את היררכיית הווידג'ט אצל אביו. המפתח 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"
}
}
]
}
בדוגמה הזו, ההגדרה מזהה RelativeLayout
שיש לו צאצא בעומק 2
, עם הטקסט Permission manager
.
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
שהערך שלו זהה לפורמט של רשומה של רכיב ממשק משתמש (ראו למעלה). סוגי ה-TYPE האלה הם:
לחיצה לחיצה ארוכה קליק קליק ארוך קליק אם קיים |
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 |
משמיטים את CONFIG key עבור פעולת SWIPE . הקוד הזה משתמש רק ב-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 .
|
פיתוח והרצה
ה-framework של Spectatio נוצר באופן אוטומטי כחלק מ-APK הבדיקה. כדי ליצור את קובץ ה-APK לבדיקה, קוד המקור של AOSP צריך להיות בתחנת העבודה המקומית. אחרי יצירת קובץ ה-APK לבדיקה, המשתמש צריך להתקין את קובץ ה-APK במכשיר ולהריץ את הבדיקה.
בדוגמת הקוד הבאה מוצגים השלבים של יצירת גרסת build, ההתקנה וההפעלה של קובץ 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
כדי לבדוק את הגדרות ה-Wi-Fi כפי שצוינו בקובץAndroid.bp
. שם ה-APK מופיע בקובץAndroid.bp
המתאים של בדיקת Automotive.DEVICE-SERIAL: המזהה הסידורי של ה-DUT. הפרמטר הזה לא נדרש אם רק מכשיר אחד מחובר למארח.
config-file-path
: פרמטר אופציונלי שנדרש רק כדי לספק הגדרות של ממשק המשתמש של המכשיר שאינן ברירת המחדל, כפי שצוין בקובץ התצורה בפורמט JSON. אם לא מציינים את הפרמטרים האלה, המערכת תשתמש בערכי ברירת המחדל להרצת הבדיקות.PATH-FOR-BUILT-TEST-APK: הנתיב שבו נוצר קובץ ה-APK לבדיקה כשמפעילים את הפקודה
make
.TEST-PACKAGE: השם של חבילת הבדיקה.
TEST-CLASSNAME: השם של מחלקת הבדיקה. לדוגמה, בבדיקה Wifi Settings, שם החבילה הוא
android.platform.tests
ושם הכיתה של הבדיקה הואWifiSettingTest
.
ספריית קטעי מידע על רכבים
ספריית Automotive Snippet היא קבוצה של ספריות בדיקה של Android לפרויקט Android Open Source Project (AOSP), שנועדו לתקשר עם אפליקציות ושירותים לכלי רכב. הוא משתמש ב-Spectatio עם מנגנון נוח להפעלת קריאות לפרוצדורות מרוחקות (RPC) ממכונת מארח (בדיקה) למכשיר מבוסס-Android.
שנתחיל?
לפני שמתחילים, כדאי לעיין בקטעים הבאים.
דרישות מוקדמות
- Python 3.x מותקן במכונה המארחת.
- הגדרת סביבה של AOSP עם כלי ה-build הנדרשים.
- מכשיר Android לכלי רכב (מכונה וירטואלית או מכשיר פיזי) עם גישה ל-adb.
קומפילציה
כדי לקמפל את קטעי הקוד השונים שסופקו על ידי ספריית הקטעים לכלי רכב, תוכלו להשתמש בקובץ android.bp
שסופק. פועלים לפי הפקודות בקטע הקודם כדי לקמפל את קובץ ה-APK.
פריסה
אחרי שמאגרי ה-snippets יקובצו, אפשר לפרוס את חבילות ה-APK שנוצרו במכשיר היעד באמצעות הפקודה adb install
שצוינה בקטע הקודם.
הרצת בדיקות
ספריות הקטעים חושפות כמה שיטות RPC ליצירת אינטראקציה עם מערכת הרכב. אפשר להפעיל את השיטות האלה דרך מסגרת Mobly מהמכונה המארחת. בהנחה שהגדרתם את סביבת הבדיקה של Mobly, תוכלו להשתמש בסקריפט snippet_shell.py
כדי לפתוח מעטפת Python אינטראקטיבית, שבה תוכלו להפעיל באופן ידני שיטות RPC במכשיר. דוגמה להפעלה:
python3 snippet_shell.py com.google.android.mobly.snippet.bundled -s <serial>
מחליפים את <serial>
במספר הסידורי של המכשיר. אם יש כמה מכשירים מחוברים, אפשר לקבל את המספר באמצעות adb devices.
ספריות כלולות
ספריית הקטעים של Automotive כוללת את ספריות הקטעים ואת הספריות העזר הבאות:
AutomotiveSnippet: ממשקי API שקשורים לפעולות ברכב, כמו חיוג, בקרת עוצמת קול, מקשים פיזיים ברכב ואינטראקציה עם מרכז המדיה.
PhoneSnippet: ממשקי API שקשורים לטלפוניה, כולל טיפול בשיחות, גלישה באנשי קשר ופעולות של שליחת הודעות SMS.
לקטע הקוד לכלי רכב ולקטע הקוד PhoneSnippet יש לוגיקה משותפת מסוימת.
באופן ספציפי, אפשר לפרוץ לשיחות RCP שקשורות ל-Bluetooth כדי להתאים מכשיר רכב למכשיר טלפון. bt_discovery_test
מראה איך עושים את זה.
- TEST-CLASSNAME: השם של מחלקת הבדיקה. לדוגמה, בבדיקה Wifi Settings, שם החבילה של הבדיקה הוא
android.platform.tests
ושם הכיתה של הבדיקה הואWifiSettingTest
.