במדריך הזה נסביר איך יוצרים הגדרת בדיקה של 'hello world' ב-Trade Federation (Tradefed או TF), ומספקים מבוא מעשי למסגרת TF. נתחיל בסביבת פיתוח, ניצור הגדרה פשוטה ונוסיף תכונות.
המדריך מציג את תהליך פיתוח הבדיקות כקבוצה של תרגילים, כל אחד מהם מורכב מכמה שלבים, שממחישים איך ליצור את ההגדרות ולשפר אותן בהדרגה. כל קוד הדוגמה הדרוש להשלמת הגדרת הבדיקה מופיע, ולכל תרגיל מצורפת הערה עם אות שמתארת את התפקידים שקשורים לשלב הזה:
- D למפתחים
- I עבור חברה מתאמת
- R ל-Test Runner
בסיום המדריך תהיה לכם הגדרה תקינה של TF ותבינו מושגים חשובים רבים במסגרת TF.
הגדרת איחוד סוחרים
פרטים על הגדרת סביבת הפיתוח של TF מופיעים במאמר הגדרת המכונה. בהמשך המדריך אנו מניחים שפתחתם מעטפת שהוגדרה לסביבת TF.
כדי לפשט את העניין, במדריך הזה נסביר איך מוסיפים הגדרה והכיתות שלה לספריית הליבה של מסגרת TF. אפשר להרחיב את השיטה הזו לפיתוח מודולים מחוץ לעץ המקור. לשם כך, צריך לקמפל את קובץ ה-JAR של ה-tradefed ואז לקמפל את המודולים שלכם מול קובץ ה-JAR הזה.
יצירת כיתה לבדיקה (D)
נוצר בדיקת hello world שמפיקה הודעה ל-stdout. בדרך כלל, בדיקה של קוד מובנה מטמיעה את הממשק IRemoteTest. זוהי הטמעה של HelloWorldTest:
package com.android.tradefed.example; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.invoker.TestInformation; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.result.ITestInvocationListener; import com.android.tradefed.testtype.IRemoteTest; public class HelloWorldTest implements IRemoteTest { @Override public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World!"); } }
שומרים את הקוד לדוגמה ב-<tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java
ובונים מחדש את tradefed מהמעטפת:
m -jN
שימו לב ש-CLog.i
בדוגמה שלמעלה משמש להפניית הפלט למסוף. מידע נוסף על רישום ביומן באיחוד הסחר מפורט במאמר רישום ביומן (D, I, R).
אם ה-build לא מצליח, כדאי לעיין במאמר הגדרת המכונה כדי לוודא שלא שכחתם שלב.
יצירת הגדרה אישית (I)
כדי להריץ בדיקות של Trade Federation, צריך ליצור קובץ תצורה – קובץ XML שמנחה את TradeFed אילו בדיקות (או כמה בדיקות) להריץ, ואילו מודולים אחרים להריץ ובאיזה סדר.
נוצרת הגדרה חדשה ל-HelloWorldTest (שימו לב לשם הכיתה המלא של HelloWorldTest):
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> </configuration>
שומרים את הנתונים האלה בקובץ helloworld.xml
בכל מקום במערכת הקבצים המקומית (למשל /tmp/helloworld.xml
). TF ינתח את קובץ ה-XML של ההגדרות (נקרא גם config), יטמיע את הכיתה שצוינה באמצעות רפלקציה, ייצור מופע שלה, יבצע לה המרה ל-IRemoteTest
ויקרא לשיטה run
שלה.
מריצים את קובץ התצורה (R)
ממסוף ה-shell, מריצים את מסוף tradefed:
tradefed.sh
מוודאים שמכשיר מחובר למכונה המארחת ושהמכשיר גלוי ל-tradefed:
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
אפשר להריץ הגדרות באמצעות הפקודה run <config>
במסוף. כדאי לבצע את הפעולות הבאות:
tf> run /tmp/helloworld.xml 05-12 13:19:36 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548 Hello, TF World!
הפלט בטרמינל אמור להיות 'Hello, TF World!'.
כדי לוודא שהפקודה הושלמה, אפשר להשתמש ב-list invocations
או ב-l i
בהודעת ההתחלה במסוף. לא אמורה להופיע שום הדפסה. אם יש פקודות שפועלות כרגע, הן יוצגו באופן הבא:
tf >l i Command Id Exec Time Device State 10 0m:00 [876X00GNG] running stub on build(s) 'BuildInfo{bid=0, target=stub, serial=876X00GNG}'
מוסיפים את התצורה לנתיב הספריות (D, I, R)
כדי להקל על הפריסה, אפשר גם לארוז את ההגדרות בקובצי ה-JAR של חבילות ה-tradefed. Tradefed מזהה באופן אוטומטי את כל ההגדרות שממוקמות בתיקיות config ב-classpath.
לדוגמה, מעבירים את הקובץ helloworld.xml
לספריית הליבה של tradefed (<tree>/tools/tradefederation/core/res/config/example/helloworld.xml
). יוצרים מחדש את tradefed, מפעילים מחדש את מסוף tradefed ומבקשים מ-tradefed להציג את רשימת ההגדרות מ-classpath:
tf> list configs […] example/helloworld: Runs the hello world test
עכשיו אפשר להריץ את הגדרת helloworld באמצעות:
tf> run example/helloworld 05-12 13:21:21 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548 Hello, TF World!
אינטראקציה עם מכשיר (D, R)
עד עכשיו, הפונקציה HelloWorldTest לא עושה שום דבר מעניין. המומחיות של Tradefed היא הפעלת בדיקות באמצעות מכשירי Android, לכן נוסיף מכשיר Android לבדיקה.
אפשר לקבל הפניה למכשיר Android באמצעות TestInformation
, שמספק המסגרת כשמתבצעת קריאה ל-method IRemoteTest#run
.
נשנה את הודעת ההדפסה HelloWorldTest כך שתציג את המספר הסידורי של המכשיר:
@Override public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber()); }
עכשיו צריך ליצור מחדש את tradefed ולבדוק את רשימת המכשירים:
tradefed.sh
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
שימו לב למספר הסידורי שמופיע כזמין. זהו המכשיר שצריך להקצות ל-HelloWorld:
tf> run example/helloworld 05-12 13:26:18 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548 Hello, TF World! I have device 004ad9880810a548
אמורה להופיע הודעת ההדפסה החדשה עם המספר הסידורי של המכשיר.
שליחת תוצאות הבדיקה (D)
IRemoteTest
מדווחת על תוצאות באמצעות קריאה לשיטות במכונה של ITestInvocationListener שסופקה לשיטה #run
. מסגרת TF עצמה אחראית לדיווח על ההתחלה (דרך ITestInvocationListener#invocationStarted) ועל הסיום (דרך ITestInvocationListener#invocationEnded) של כל קריאה.
הרצת בדיקה היא אוסף לוגי של בדיקות. כדי לדווח על תוצאות הבדיקה, IRemoteTest
אחראי לדווח על תחילת הרצה של בדיקה, על תחילת כל בדיקה ועל סיום ההרצה.
כך יכול להיראות היישום של HelloWorldTest עם תוצאת בדיקה אחת שנכשלה.
@Override public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber()); TestDescription testId = new TestDescription("com.example.TestClassName", "sampleTest"); listener.testRunStarted("helloworldrun", 1); listener.testStarted(testId); listener.testFailed(testId, "oh noes, test failed"); listener.testEnded(testId, Collections.emptyMap()); listener.testRunEnded(0, Collections.emptyMap()); }
TF כולל כמה הטמעות של IRemoteTest
שאפשר לעשות בהן שימוש חוזר במקום לכתוב הטמעה משלכם מאפס. לדוגמה, אפשר להשתמש ב-InstrumentationTest כדי להריץ בדיקות של אפליקציית Android מרחוק במכשיר Android, לנתח את התוצאות ולהעביר אותן ל-ITestInvocationListener
. פרטים נוספים זמינים במאמר סוגי בדיקות.
שמירת תוצאות הבדיקה (I)
הטמעת ברירת המחדל של מאזין הבדיקה בתצורת TF היא TextResultReporter, שמפיקה את תוצאות ההפעלה ב-stdout. לדוגמה, מריצים את ההגדרה HelloWorldTest מהקטע הקודם:
./tradefed.sh
tf> run example/helloworld 04-29 18:25:55 I/TestInvocation: Invocation was started with cmd: /tmp/helloworld.xml 04-29 18:25:55 I/TestInvocation: Starting invocation for 'stub' with '[ BuildInfo{bid=0, target=stub, serial=876X00GNG} on device '876X00GNG'] 04-29 18:25:55 I/HelloWorldTest: Hello, TF World! I have device 876X00GNG 04-29 18:25:55 I/InvocationToJUnitResultForwarder: Running helloworldrun: 1 tests 04-29 18:25:55 W/InvocationToJUnitResultForwarder: Test com.example.TestClassName#sampleTest failed with stack: oh noes, test failed 04-29 18:25:55 I/InvocationToJUnitResultForwarder: Run ended in 0 ms
כדי לשמור את התוצאות של קריאה במקום אחר, למשל בקובץ, צריך לציין הטמעה מותאמת אישית של ITestInvocationListener
באמצעות התג result_reporter
בתצורה.
TF כולל גם את מאזין XmlResultReporter, שכותב את תוצאות הבדיקה בקובץ XML בפורמט דומה לזה שבו משתמש הכלי לכתיבה של קבצי XML ב-JUnit של ant. כדי לציין את result_reporter בתצורה, עורכים את קובץ התצורה …/res/config/example/helloworld.xml
:
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> <result_reporter class="com.android.tradefed.result.XmlResultReporter" /> </configuration>
עכשיו צריך ליצור מחדש את tradefed ולהריץ מחדש את הדוגמה Hello World:
tf> run example/helloworld 05-16 21:07:07 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548 Hello, TF World! I have device 004ad9880810a548 05-16 21:07:07 I/XmlResultReporter: Saved device_logcat log to /tmp/0/inv_2991649128735283633/device_logcat_6999997036887173857.txt 05-16 21:07:07 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_2991649128735283633/host_log_6307746032218561704.txt 05-16 21:07:07 I/XmlResultReporter: XML test result file generated at /tmp/0/inv_2991649128735283633/test_result_536358148261684076.xml. Total tests 1, Failed 1, Error 0
שימו לב להודעה ביומן על כך שנוצר קובץ XML. הקובץ שנוצר אמור להיראות כך:
<?xml version='1.0' encoding='UTF-8' ?> <testsuite name="stub" tests="1" failures="1" errors="0" time="9" timestamp="2011-05-17T04:07:07" hostname="localhost"> <properties /> <testcase name="sampleTest" classname="com.example.TestClassName" time="0"> <failure>oh noes, test failed </failure> </testcase> </testsuite>
אפשר גם לכתוב מאזינים מותאמים אישית להפעלה – פשוט צריך להטמיע את הממשק ITestInvocationListener.
Tradefed תומך במספר מאזינים להפעלות, כך שאפשר לשלוח את תוצאות הבדיקה למספר יעדים עצמאיים. כדי לעשות זאת, פשוט מציינים כמה תגי <result_reporter>
בתצורה.
מתקני רישום ביומן (D, I, R)
כלי הרישום ב-TF כוללים את היכולות הבאות:
- תיעוד יומנים מהמכשיר (נקרא גם device logcat)
- רישום יומנים מהמסגרת של Trade Federation שפועלת במכונה המארחת (נקרא גם יומן המארח)
מסגרת TF מתעדת באופן אוטומטי את ה-logcat מהמכשיר שהוקצה, ושולחת אותו לעיבוד על ידי מאזין הקריאה.
XmlResultReporter
שומר את ה-logcat של המכשיר שצולם כקובץ.
דיווח על יומני המארח של TF מתבצע באמצעות עטיפה של CLog עבור הכיתה Log של ddmlib. נמיר את הקריאה הקודמת ל-System.out.println
ב-HelloWorldTest לקריאה ל-CLog
:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device %s", getDevice().getSerialNumber());
CLog
מטפלת ישירות בהוספת ערכים למחרוזות, בדומה ל-String.format
. כשמפעילים מחדש את TF לאחר היצירה מחדש, ההודעה ביומן אמורה להופיע ב-stdout:
tf> run example/helloworld … 05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548 …
כברירת מחדל, הפלט של tradefed הוא הודעות ביומן המארח ל-stdout. TF כולל גם הטמעה של יומן שכותב הודעות לקובץ: FileLogger.
כדי להוסיף רישום ביומן של קבצים, מוסיפים תג logger
לתצורה ומציינים את שם הכיתה המלא של FileLogger
:
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> <result_reporter class="com.android.tradefed.result.XmlResultReporter" /> <logger class="com.android.tradefed.log.FileLogger" /> </configuration>
עכשיו יוצרים מחדש את הדוגמה helloworld ומפעילים אותה שוב:
tf >run example/helloworld … 05-16 21:38:21 I/XmlResultReporter: Saved device_logcat log to /tmp/0/inv_6390011618174565918/device_logcat_1302097394309452308.txt 05-16 21:38:21 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt …
הודעת היומן מציינת את הנתיב של יומן המארח, שבהצגתו אמורה להופיע הודעת היומן HelloWorldTest:
more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt
פלט לדוגמה:
… 05-16 21:38:21 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
אפשרויות טיפול (D, I, R)
אובייקטים שנטענים מתצורת TF (שנקראים גם אובייקטי תצורה) יכולים גם לקבל נתונים מארגומנטים של שורת הפקודה באמצעות ההערה @Option
.
כדי להשתתף, סוג של אובייקט תצורה מחיל את ההערה @Option
על שדה חבר ומספק לו שם ייחודי. כך אפשר לאכלס את הערך של שדה החבר באמצעות אפשרות בשורת הפקודה (והאפשרות הזו תתווסף באופן אוטומטי למערכת העזרה של ההגדרות).
הערה: לא כל סוגי השדות נתמכים. תיאור של הסוגים הנתמכים זמין במאמר OptionSetter.
נוסיף @Option
ל-HelloWorldTest:
@Option(name="my_option", shortName='m', description="this is the option's help text", // always display this option in the default help text importance=Importance.ALWAYS) private String mMyOption = "thisisthedefault";
בשלב הבא, נוסיף הודעת יומן כדי להציג את הערך של האפשרות ב-HelloWorldTest, כדי שנוכל להוכיח שהיא התקבלה בצורה נכונה:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { … CLog.logAndDisplay(LogLevel.INFO, "I received option '%s'", mMyOption);
לבסוף, יוצרים מחדש את TF ומריצים את helloworld. אמורה להופיע הודעה ביומן עם ערך ברירת המחדל my_option
:
tf> run example/helloworld … 05-24 18:30:05 I/HelloWorldTest: I received option 'thisisthedefault'
העברת ערכים משורת הפקודה
מעבירים ערך ל-my_option
. הערך הזה אמור לאכלס את my_option
:
tf> run example/helloworld --my_option foo … 05-24 18:33:44 I/HelloWorldTest: I received option 'foo'
הגדרות TF כוללות גם מערכת עזרה, שמציגה באופן אוטומטי טקסט עזרה לשדות @Option
. עכשיו אפשר לנסות, ואמור להופיע טקסט העזרה של my_option
:
tf> run example/helloworld --help Printing help for only the important options. To see help for all options, use the --help-all flag cmd_options options: --[no-]help display the help text for the most important/critical options. Default: false. --[no-]help-all display the full help text for all options. Default: false. --[no-]loop keep running continuously. Default: false. test options: -m, --my_option this is the option's help text Default: thisisthedefault. 'file' logger options: --log-level-display the minimum log level to display on stdout. Must be one of verbose, debug, info, warn, error, assert. Default: error.
שימו לב להודעה 'הדפסה של האפשרויות החשובות בלבד'. כדי לצמצם את העומס של תפריטי העזרה, TF משתמש במאפיין Option#importance
כדי לקבוע אם להציג טקסט עזרה ספציפי של שדה @Option
כשמציינים את הערך --help
. --help-all
תמיד מציג עזרה לכל השדות של @Option
, ללא קשר לרמת החשיבות שלהם. פרטים נוספים זמינים במאמר Option.Importance.
העברת ערכים מתצורה
אפשר גם לציין ערך של Option בתצורה על ידי הוספת רכיב <option name="" value="">
. בודקים את זה באמצעות helloworld.xml
:
<test class="com.android.tradefed.example.HelloWorldTest" > <option name="my_option" value="fromxml" /> </test>
עכשיו, לאחר היצירה מחדש וההרצה של helloworld, הפלט אמור להיות:
05-24 20:38:25 I/HelloWorldTest: I received option 'fromxml'
גם עזרת ההגדרה צריכה להתעדכן כך שתציין את ערך ברירת המחדל של my_option
:
tf> run example/helloworld --help test options: -m, --my_option this is the option's help text Default: fromxml.
אובייקטים אחרים של הגדרות שכלולים בתצורה של helloworld, כמו FileLogger
, גם מקבלים אפשרויות. האפשרות --log-level-display
מעניינת כי היא מסננת את היומנים שמופיעים ב-stdout. קודם במדריך, יכול להיות שראיתם את ההודעה "Hello, TF
World! I have device …' הפסיקה להופיע ביומן stdout אחרי שעברנו להשתמש ב-FileLogger
. כדי להגדיל את רמת הפירוט של הרישום ביומן ל-stdout, מעבירים את הארגומנט --log-level-display
.
נסו את זה עכשיו, והודעת היומן 'יש לי מכשיר' אמורה להופיע שוב ב-stdout, בנוסף לכך שהיא תירשם בקובץ:
tf> run example/helloworld --log-level-display info … 05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
זהו, כיבוי אורות.
רצינו רק להזכיר לכם: אם אתם נתקלים בבעיה, בקוד המקור של Trade Federation יש הרבה מידע שימושי שלא מופיע במסמכי העזרה. אם כל השיטות האחרות נכשלו, אפשר לנסות לשאול בקבוצת Google android-platform, ולציין בנושא של ההודעה 'Trade Federation'.