مثال على اختبار TF من طرف إلى طرف

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

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

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

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

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

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

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

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

لنقم بإنشاء اختبار عالم مرحبًا يقوم فقط بتفريغ رسالة إلى 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 /core/src/com/android/tradefed/example/HelloWorldTest.java وأعد بناء التجارة من قوقعتك:

m -jN

لاحظ أن CLog.i في المثال أعلاه يُستخدم لتوجيه الإخراج إلى وحدة التحكم. مزيد من المعلومات حول تسجيل الدخول إلى الاتحاد التجاري موصوفة في التسجيل (D ، I ، R) .

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

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

تكون اختبارات الاتحاد التجاري قابلة للتنفيذ من خلال إنشاء تكوين ، وهو ملف XML يرشد التجارة بشأن الاختبار (أو الاختبارات) المطلوب تشغيله ، بالإضافة إلى الوحدات النمطية الأخرى التي يجب تنفيذها وبأي ترتيب.

دعنا ننشئ تكوينًا جديدًا لاختبار HelloWorldTest الخاص بنا (لاحظ اسم فئة HelloWorldTest بالكامل):

<configuration description="Runs the hello world test">
    <test class="com.android.tradefed.example.HelloWorldTest" />
</configuration>

احفظ هذه البيانات في ملف helloworld.xml في أي مكان على نظام الملفات المحلي لديك (على سبيل المثال /tmp/helloworld.xml ). سيقوم TF بتحليل ملف تكوين XML (المعروف أيضًا باسم التكوين ) ، وتحميل الفئة المحددة باستخدام الانعكاس ، وإنشاء مثيل لها ، وإرسالها إلى IRemoteTest ، واستدعاء طريقة run الخاصة بها.

تشغيل config (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}'

إضافة التكوين إلى classpath (D ، I ، R)

لسهولة النشر ، يمكنك أيضًا تجميع التكوينات في JARs التجارية نفسها. يتعرف Tradefed تلقائيًا على جميع التكوينات الموضوعة في مجلدات التكوين على مسار الفصل.

للتوضيح ، انقل ملف helloworld.xml إلى المكتبة الأساسية tradefederation ( <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 ، التي يوفرها إطار العمل عند استدعاء أسلوب 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 ant JUnit. لتحديد result_reporter في التكوين ، قم بتحرير …/res/config/example/helloworld.xml config:

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

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

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

تم الإبلاغ عن سجلات مضيف 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 لتحديد ما إذا كان سيتم إظهار نص تعليمات حقل --help معين عند تحديد @Option . يعرض @Option --help-all دائمًا المساعدة لجميع حقول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. في وقت سابق من البرنامج التعليمي ، ربما لاحظت أن رسالة السجل "مرحبًا ، TF World! لدي جهاز ..." توقف عرضها على stdout بعد أن قمنا بالتبديل إلى استخدام FileLogger . يمكنك زيادة الإسهاب في التسجيل إلى stdout عن طريق تمرير --log-level-display arg.

جرب هذا الآن ، وسترى رسالة سجل "لدي جهاز" تظهر مرة أخرى على 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 ، مع وضع "الاتحاد التجاري" في موضوع الرسالة.