Spectatio היא מסגרת בדיקה בקוד פתוח שפותחה לבדיקה של Android Automotive OS (AAOS) במכשירים אמיתיים וווירטואליים. חברת Spectatio מספקת ממשקי API לבדיקת אפליקציות במכשירי רכב, והפתרון הזה ניתן להרחבה ולהתאמה, שמשמש לאימות היכולת והביצועים של AAOS ושל האפליקציות שלו.
העיצוב הכללי
אפשר להתאים את המסגרת של Spectatio ולהרחיב אותה להטמעות שונות של ממשק המשתמש של AAOS. הוא משמש לבדיקת היכולות והביצועים של AAOS בחומרה של המכשיר, במהדמנים ובסביבות וירטואליות.
האיור הבא מסביר את העיצוב הכללי של מסגרת Spectatio.
איור 1. עיצוב ברמה גבוהה של Spectatio framework.
מסגרת 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 של המכשיר, ומעדכנת את הגדרות ה-framework בזמן הריצה.
מודול הטמעת העזרה לאפליקציה הוא הליבה של מסגרת 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());
}
}
התאמה אישית
ה-framework של 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"
}
}
מרובה
זיהוי הרכיב על סמך מספר תנאים בו-זמנית, שכל אחד מהם חייב להתקיים.
"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 האלה הם:
PRESS Long_PRESS Click Long_CLICK CLICK_IF_EXIST |
HAS_UI_ELEMENT_IN_FOREGROUND SCROLL_TO_FIND_AND_CLICK SCROLL_TO_FIND_AND_CLICK_IF_EXIST SWIPE_TO_FIND_AND_CLICK SWIPE_TO_FIND_AND_CLICK_IF_EXIST |
בשאר הערכים של TYPE, פרטי ההגדרה הם:
אובייקט | תיאור |
---|---|
COMMAND |
אובייקט עם ערך TEXT שמכיל את הפקודה לביצוע. |
HAS_PACKAGE_IN_FOREGROUND |
אובייקט עם ערך TEXT שמכיל את החבילה. |
SWIPE |
משמיטים את 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
.
ספריית קטעי מידע על רכבים
ספריית Automotive Snippet היא קבוצה של ספריות בדיקה של Android לפרויקט Android Open Source Project (AOSP), שנועדו לתקשר עם אפליקציות ושירותים לכלי רכב. הוא משתמש ב-Spectatio עם מנגנון נוח לביצוע קריאות לפעולות מרחוק (RPCs) ממכונה מארח (בדיקה) למכשיר מבוסס 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.
ספריות כלולות
ספריית הקטעים לכלי רכב כוללת את ספריות הקטעים ואת הספריות העזר הבאות:
AutomotiveSnippet: ממשקי API שקשורים לפעולות ברכב, כמו חיוג, בקרת עוצמת קול, מקשים פיזיים ברכב ואינטראקציה עם מרכז המדיה.
PhoneSnippet: ממשקי API שקשורים לטלפוניה, כולל טיפול בשיחות, גלישה באנשי קשר ופעולות של שליחת הודעות SMS.
לקטע הקוד לכלי רכב ולקטע הקוד PhoneSnippet יש לוגיקה משותפת מסוימת.
באופן ספציפי, אפשר לפרוץ לשיחות RCP שקשורות ל-Bluetooth כדי להתאים מכשיר רכב למכשיר טלפון. bt_discovery_test
מראה איך עושים את זה.
- TEST-CLASSNAME: השם של מחלקת הבדיקה. לדוגמה, בבדיקה Wifi Settings, שם החבילה של הבדיקה הוא
android.platform.tests
ושם הכיתה של הבדיקה הואWifiSettingTest
.