مثال على اختبار TF الشامل

يرشدك هذا البرنامج التعليمي خلال إنشاء تكوين اختبار "hello World" للاتحاد التجاري (Tradefed أو TF) ويمنحك مقدمة عملية لإطار عمل TF. بدءًا من بيئة التطوير، ستقوم بإنشاء تكوين بسيط وإضافة ميزات.

يعرض البرنامج التعليمي عملية تطوير الاختبار كمجموعة من التمارين، يتكون كل منها من عدة خطوات، توضح كيفية إنشاء التكوين الخاص بك وتحسينه تدريجيًا. يتم توفير كافة نماذج التعليمات البرمجية التي تحتاجها لإكمال تكوين الاختبار، ويتم توضيح عنوان كل تمرين بحرف يصف الأدوار المتضمنة في تلك الخطوة:

  • د للمطور
  • أنا للتكامل
  • R لعداء الاختبار

بعد إكمال البرنامج التعليمي، سيكون لديك تكوين TF فعال وستفهم العديد من المفاهيم المهمة في إطار عمل TF.

إنشاء الاتحاد التجاري

للحصول على تفاصيل حول إعداد بيئة تطوير TF، راجع إعداد الجهاز . يفترض الجزء المتبقي من هذا البرنامج التعليمي أن لديك غلافًا مفتوحًا تمت تهيئته لبيئة TF.

للتبسيط، يوضح هذا البرنامج التعليمي إضافة التكوين وفئاته إلى المكتبة الأساسية لإطار عمل TF. يمكن أن يمتد هذا إلى تطوير الوحدات النمطية خارج الشجرة المصدر عن طريق تجميع JAR المتداول، ثم تجميع الوحدات النمطية الخاصة بك مقابل JAR هذا.

إنشاء فئة اختبار (د)

لنقم بإنشاء اختبار مرحبًا بالعالم يقوم فقط بتفريغ رسالة إلى stdout. يقوم اختبار tradefed عمومًا بتطبيق واجهة 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) .

إذا لم ينجح البناء، فاستشر Machine Setup للتأكد من عدم تفويت أي خطوة.

إنشاء التكوين (I)

أصبحت اختبارات الاتحاد التجاري قابلة للتنفيذ عن طريق إنشاء ملف 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 بتحليل ملف Configuration XML (المعروف أيضًا باسم config )، وتحميل الفئة المحددة باستخدام الانعكاس، وإنشاء مثيل لها، وإرسالها إلى IRemoteTest ، واستدعاء طريقة run الخاصة بها.

قم بتشغيل التكوين (R)

من الصدفة الخاصة بك، قم بتشغيل وحدة التحكم المتداولة:

tradefed.sh

تأكد من توصيل الجهاز بالجهاز المضيف وأنه مرئي للتداول:

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 أو li في موجه وحدة التحكم، ويجب ألا تتم طباعة أي شيء. إذا كانت الأوامر قيد التشغيل حاليًا، فسيتم عرضها كما يلي:

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 تلقائيًا على جميع التكوينات الموضوعة في مجلدات التكوين على مسار الفصل.

للتوضيح، انقل ملف helloworld.xml إلى مكتبة tradefed الأساسية ( <tree>/tools/tradefederation/core/res/config/example/helloworld.xml ). أعد بناء tradefed، وأعد تشغيل وحدة التحكم tradefed، ثم اطلب من tradefed عرض قائمة التكوينات من مسار الفصل:

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 ، الذي يوفره إطار العمل عند استدعاء الأسلوب 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

من المفترض أن تشاهد رسالة الطباعة الجديدة التي تعرض الرقم التسلسلي للجهاز.

إرسال نتائج الاختبار (د)

يقوم 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 بتنسيق مشابه لذلك الذي يستخدمه كاتب ant JUnit XML. لتحديد 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 وأعد تشغيل نموذج helloworld:

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 القدرة على:

  1. التقاط السجلات من الجهاز (المعروف أيضًا باسم جهاز logcat)
  2. سجل السجلات من إطار عمل الاتحاد التجاري الذي يعمل على الجهاز المضيف (المعروف أيضًا باسم سجل المضيف)

يقوم إطار عمل TF تلقائيًا بالتقاط السجل من الجهاز المخصص وإرساله إلى مستمع الاستدعاء للمعالجة. يقوم XmlResultReporter بعد ذلك بحفظ سجل الجهاز الذي تم التقاطه كملف.

يتم الإبلاغ عن سجلات مضيف TF باستخدام غلاف CLog لفئة سجل 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 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! لدي جهاز..." على stdout بعد أن تحولنا إلى استخدام FileLogger . يمكنك زيادة الإسهاب في التسجيل إلى stdout عن طريق تمرير --log-level-display وسيطة --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

هذا كل ما لدي أيها الناس!

للتذكير، إذا كنت عالقًا في شيء ما، فإن كود مصدر الاتحاد التجاري يحتوي على الكثير من المعلومات المفيدة التي لم يتم عرضها في الوثائق. إذا فشلت كل الخطوات الأخرى، فحاول السؤال على مجموعة Google الخاصة بنظام Android ، مع وضع "الاتحاد التجاري" في موضوع الرسالة.