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

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

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

  • د للمطوّر
  • I للشركة المُدمِجة
  • R لتشغيل الاختبار

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

إعداد اتحاد التجارة

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

للتبسيط، يوضّح هذا الدليل التعليمي كيفية إضافة إعدادات و فئاتها إلى مكتبة إطار الشفافية والموافقة الأساسية. يمكن توسيع نطاق هذا الإجراء ليشمل تطوير الوحدات خارج شجرة المصدر من خلال تجميع حزمة JAR الخاصة بتنسيق tradefed، ثم تجميع الوحدات باستخدام حزمة 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 في المثال أعلاه يُستخدَم لتوجيه الإخراج إلى وحدة التحكّم. يمكنك الاطّلاع على مزيد من المعلومات حول التسجيل في Trade Federation في مقالة التسجيل (D وI وR).

إذا لم تنجح عملية الإنشاء، يمكنك الرجوع إلى مقالة إعداد الجهاز للتأكّد من عدم تخطّي أي خطوة.

إنشاء عملية ضبط (1)

يمكن تنفيذ اختبارات 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)

من وحدة التحكّم، افتح وحدة تحكّم Tradefed:

tradefed.sh

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

tf> list devices
Serial            State      Product   Variant   Build   Battery
004ad9880810a548  Available  mako      mako      JDQ39   100

يمكن تنفيذ الإعدادات باستخدام الأمر run <config> console. ننصحك بما يلي:

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!

من المفترض أن يظهر لك الإخراج "مرحبًا، 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 على مسار تحميل الحِزم.

على سبيل المثال، انقل ملف helloworld.xml إلى مكتبة <tree>/tools/tradefederation/core/res/config/example/helloworld.xml الأساسية لـ tradefed. أعِد إنشاء 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

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

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

يُبلغ 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). للحصول على التفاصيل، اطّلِع على أنواع الاختبار.

نتائج اختبارات المتجر (1)

إنّ التنفيذ التلقائي لمُستمع الاختبار لإعدادات 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 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. تسجيل السجلات من الجهاز (المعروف أيضًا باسم logcat للجهاز)
  2. تسجيل السجلات من إطار عمل Trade Federation الذي يعمل على الجهاز المضيف (المعروف أيضًا باسم سجلّ المضيف)

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

يتم تسجيل سجلات مضيف TF باستخدام CLog wrapper لفئة 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 وإعادة تشغيله، من المفترض أن تظهر رسالة log في 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. في وقت سابق من البرنامج التعليمي، ربما لاحظت الرسالة "مرحبًا، عالم TF! لقد توقفت رسالة السجلّ "لديّ جهاز …" عن الظهور على 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

لا يوجد المزيد.

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