Spectatio היא מסגרת בדיקה בקוד פתוח שפותחה לבדיקת Android Automotive OS (AAOS) במכשירים אמיתיים וגם במכשירים וירטואליים. Spectatio מספקת ממשקי API לבדיקת אפליקציות במכשיר לרכב, והיא פתרון ניתן להרחבה וניתן להתאמה שמשמש לאימות היכולות והביצועים של AAOS והאפליקציות שלה.
עיצוב ברמה גבוהה
ה-framework של Spectatio הוא גמיש וניתן להרחבה כדי להתאים להטמעות שונות של ממשקי משתמש ב-AAOS. היא משמשת לבדיקת היכולות והביצועים של AAOS בחומרה של מכשירים, באמולטורים ובסביבות וירטואליות.
באיור הבא מוסבר העיצוב הכללי של מסגרת Spectatio.
איור 1. העיצוב הכללי של מסגרת Spectatio.
מסגרת Spectatio מבוססת על UI Automator, ומספקת קבוצה של ממשקי API לבניית בדיקות של ממשק משתמש שמתקשרות עם אפליקציות של משתמשים ומערכת ב-AAOS. בבדיקות של מערכות לרכב נעשה שימוש בממשקי ה-API שסופקו על ידי מסגרת Spectatio לבדיקה, מה שהופך את הבדיקות האלה לבלתי תלויות במכשיר שנבדק (DUT) ולניתנות להרחבה כדי לבדוק מכשירים שונים, אם הם נתמכים.
באיור 1 אפשר לראות שהמסגרת של Spectatio היא מודולרית ומבוססת על אפליקציות לדוגמה כמו Dialer, Medicenter ו-Settings, שמשתמשות בממשקי עזר ובכלי עזר ספציפיים לאפליקציות. כך אפשר להרחיב אותה בקלות לאפליקציות חדשות. במסגרת Spectatio נעשה שימוש חוזר במחלקות העזר של כלי השירות והתקן הנפוץ. מחלקת העזר הרגילה היא מחלקת האב של כל פונקציות העזר של האפליקציה, והיא מספקת פונקציות רגילות שספציפיות למכשיר או שרלוונטיות לכל האפליקציות. מחלקות העזר של כלי השירות מספקות כלי עזר כמו קריאה או כתיבה של קבצים מהמכשיר.
ארכיטקטורה
כדי לספק קבוצה של ממשקי API לבניית בדיקות של ממשק משתמש, מסגרת Spectatio מטמיעה ממשקים ועוזרים ספציפיים לאפליקציה, תוך הרחבה של מחלקת העזר הרגילה הקיימת וייבוא של מחלקות העזר של כלי השירות.
איור 2 מציג את הארכיטקטורה ברמה גבוהה של מסגרת Spectatio ואת כל הישויות שמשתתפות בהטמעה של ממשקי API לבדיקת אפליקציה.
איור 2. ארכיטקטורה כללית של מסגרת Spectatio.
ממשק העזר של האפליקציה מספק תוכנית להטמעה של עזר לאפליקציה. הוא כולל פונקציות עזר שונות שנדרשות לבדיקת אפליקציות. לכל אפליקציה יש ממשק משלה, כמו IAutoSettingHelper
ו-IAutoDialHelper
.
מידע נוסף ורשימה של פונקציות ממשק מופיעים במאמר פונקציות ממשק של כלי העזר לאפליקציות ב-AOSP.
מחלקת העזר הרגילה מורכבת ממאפיינים ופונקציות רגילים שנדרשים להגדרת המכשיר, אבל לא ספציפיים לאפליקציה מסוימת, כמו pressHome
ו-scroll
. מחלקת העזר הרגילה מוגדרת ב-AbstractAutoStandardAppHelper.java
.
המסגרת משתמשת במחלקות עזר לשימוש. לדוגמה, AutoJsonUtility.java
הוא מחלקה של כלי עזר שמעמיסה את קובץ התצורה של מכשיר JSON שצוין ומעדכנת את הגדרות המסגרת בזמן הריצה.
מודול ההטמעה של כלי העזר לאפליקציות הוא הליבה של מסגרת Spectatio. הוא מכיל את ההטמעה של פונקציות העזר שמוגדרות בממשק העזר של האפליקציה, שנדרשות לבדיקת אפליקציות במכשיר לרכב. לכל אפליקציה יש הטמעה משלה, כמו SettingHelperImpl
ו-DialHelperImpl
, שמשמשות את הבדיקות של Automotive לבדיקת האפליקציות. מידע נוסף ורשימה של הטמעות זמינים במאמר בנושא פונקציות הטמעה של כלי העזר לאפליקציות ב-AOSP.
בדיקות של אפליקציות לרכב
משתמשות בפונקציות של הטמעת כלי העזר לאפליקציות כדי לבדוק פעולות שונות
שקשורות לאפליקציה. כדי לקבל גישה לפונקציות של הטמעת כלי העזר לאפליקציות, צריך להשתמש במחלקה 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
. הוא חייב להכיל רק את המידע על השינויים בממשק המשתמש של המכשיר הנבדק. כל שאר רכיבי ממשק המשתמש משתמשים בערכי ברירת המחדל של ההגדרות שמופיעים במסגרת.
הגדרות ברירת מחדל של מכשירים
בקובץ ההגדרות הבא בפורמט 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 מחליפות את הגדרות ברירת המחדל. בדוגמה הזו:
ההגדרות של האינטרנט נקראות רשת ואינטרנט במכשירי ההשוואה וקישוריות במכשיר הנבדק.
הגדרות התאריך והשעה זמינות בהגדרות > תאריך ושעה במכשירי העיון ובהגדרות > מערכת > תאריך ושעה במכשיר הנבדק.
// 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 שבוחרים רכיבי ממשק משתמש (מתואר בפירוט בהמשך). |
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
. ברוב המקרים, 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 |
לסוגים האחרים, פרטי ההגדרה הם:
אובייקט | תיאור |
---|---|
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 .
|
בנייה והפעלה
המסגרת 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
כדי לבדוק את הגדרות ה-Wi-Fi כפי שצוין בקובץAndroid.bp
. שם ה-APK מופיע בקובץAndroid.bp
המתאים לבדיקה של אפליקציה לרכב.DEVICE-SERIAL: המזהה הסידורי של ה-DUT. הפרמטר הזה לא נדרש אם רק מכשיר אחד מחובר למארח.
config-file-path
: פרמטר אופציונלי שנדרש רק כדי לספק הגדרות של ממשק משתמש במכשיר שאינן ברירת המחדל, כפי שמצוין בקובץ התצורה בפורמט JSON. אם לא מספקים ערך, המסגרת משתמשת בערכי ברירת מחדל להרצת הבדיקות.PATH-FOR-BUILT-TEST-APK: הנתיב שבו נוצר ה-APK של הבדיקה כשמריצים את הפקודה
make
.TEST-PACKAGE: השם של חבילת הבדיקה.
TEST-CLASSNAME: השם של מחלקת הבדיקה. לדוגמה, בבדיקה Wifi Settings, חבילת הבדיקה היא
android.platform.tests
ושם מחלקת הבדיקה הואWifiSettingTest
.
מאגר קטעי מידע על רכבים
ספריית קטעי הקוד לרכב היא אוסף של ספריות בדיקה של Android לפרויקט הקוד הפתוח של Android (AOSP), שנועדו ליצור אינטראקציה עם אפליקציות ושירותים לרכב. הוא משתמש ב-Spectatio עם מנגנון נוח להפעלת קריאות לפרוצדורות מרוחקות (RPC) ממכונת מארח (בדיקה) למכשיר מבוסס-Android.
שנתחיל?
לפני שמתחילים, כדאי לעיין בקטעים האלה.
דרישות מוקדמות
- Python 3.x מותקן במחשב המארח.
- הגדרת סביבת AOSP עם כלי ה-build הנדרשים.
- מכשיר Android Automotive (אמולטור או מכשיר פיזי) עם גישת adb.
קומפילציה
כדי לקמפל את קטעי הקוד השונים שמופיעים בספריית קטעי הקוד לרכב, אפשר להשתמש בקובץ android.bp
שסופק. מריצים את הפקודות הבאות שמופיעות בקטע הקודם כדי לקמפל את קובץ ה-APK.
פריסה
אחרי שמהדרים את ספריות הקטעים, פורסים את קובצי ה-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.
יש היגיון משותף לקטע הקוד להחלפת מספר טלפון ולקטע הקוד להחלפת מספר טלפון בענף הרכב.
באופן ספציפי, אפשר לפרוץ לקריאות RCP שקשורות ל-Bluetooth כדי להתאים בין מכשיר לרכב לבין טלפון. בbt_discovery_test
הזה מוסבר איך.
- TEST-CLASSNAME: השם של מחלקת הבדיקה. לדוגמה, בבדיקה Wifi Settings, חבילת הבדיקה היא
android.platform.tests
ושם מחלקת הבדיקה הואWifiSettingTest
.