مثال آزمایش سرتاسر TF

این آموزش شما را در ایجاد پیکربندی آزمایشی «سلام دنیا» در Trade Federation (Tradefed یا TF) راهنمایی می‌کند و مقدمه‌ای عملی بر چارچوب TF ارائه می‌دهد. با شروع از محیط توسعه، پیکربندی ساده‌ای ایجاد خواهید کرد و ویژگی‌هایی را اضافه خواهید کرد.

این آموزش گام‌به‌گام فرایند توسعه آزمایش را به‌صورت مجموعه‌ای از تمرین‌ها ارائه می‌دهد که هریک از آن‌ها شامل چندین مرحله است و نحوه ساخت و پالایش تدریجی پیکربندی را نشان می‌دهد. همه کد نمونه‌ای که برای تکمیل پیکربندی آزمایش نیاز دارید ارائه شده است و عنوان هر تمرین با حرفی که نقش‌های درگیر در آن مرحله را توصیف می‌کند حاشیه‌نویسی شده است:

  • D برای توسعه‌دهنده
  • I برای Integrator (یکپارچه‌ساز)
  • R برای «اجراکننده آزمون»

پس‌از تکمیل آموزش گام‌به‌گام، پیکربندی TF کارآمدی خواهید داشت و بسیاری از مفاهیم مهم در چارچوب TF را درک خواهید کرد.

راه‌اندازی Trade Federation

برای جزئیات مربوط به راه‌اندازی محیط توسعه TF، به راه‌اندازی ماشین مراجعه کنید. بقیه این آموزش فرض می‌کند که پوسته‌ای باز دارید که برای محیط TF مقداردهی اولیه شده است.

برای سادگی، این آموزش نحوه افزودن پیکربندی و کلاس‌های آن به کتابخانه اصلی چارچوب TF را نشان می‌دهد. این کار را می‌توان با کامپایل کردن فایل JAR مربوط به tradefed و سپس کامپایل کردن واحدهای خود دربرابر آن فایل 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 ذخیره کنید و tradefed را از پوسته خود بازسازی کنید:

m -jN

توجه داشته باشید که CLog.i در مثال بالا برای هدایت برونداد به کنسول استفاده می‌شود. اطلاعات بیشتر درباره ورود به سیستم «فدراسیون تجارت» در گزارش ورود به سیستم (D،‏ I،‏ R) شرح داده شده است.

اگر ساخت موفقیت‌آمیز نبود، به راه‌اندازی ماشین مراجعه کنید تا مطمئن شوید مرحله‌ای را ازدست نداده‌اید.

ایجاد پیکربندی (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 فایل «زبان نشانه‌گذاری توسعه‌پذیر پیکربندی» (معروف به 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> اجرا کرد. امتحان کنید:

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)

برای راحتی استقرار، می‌توانید پیکربندی‌ها را در خود فایل‌های tradefed JAR نیز دسته‌بندی کنید. Tradefed به‌طور خودکار همه پیکربندی‌های قرارگرفته در پوشه‌های config در مسیر کلاس را تشخیص می‌دهد.

برای مثال، فایل 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 به آزمایش اضافه کنیم.

آزمایش‌ها می‌توانند بااستفاده از TestInformation، که هنگام فراخوانی روش IRemoteTest#run توسط چارچوب ارائه می‌شود، به دستگاه Android ارجاع دهند.

بیایید پیام چاپ 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. گزارش‌های ضبط از دستگاه (معروف به logcat دستگاه)
  2. گزارش‌های چارچوب «فدراسیون تجارت» را که در ماشین میزبان اجرا می‌شود ضبط کن (با نام مستعار گزارش میزبان)

چارچوب TF به‌طور خودکار logcat را از دستگاه اختصاص‌یافته ضبط می‌کند و آن را برای پردازش به شنونده فراخوانی ارسال می‌کند. سپس XmlResultReporter گزارش ثبت دستگاه ضبط‌شده را به‌عنوان فایل ذخیره می‌کند.

گزارش‌های میزبان TF بااستفاده از CLog wrapper برای کلاس ddmlib Log گزارش می‌شوند. بیایید تماس 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 پیام‌های گزارش میزبان را به خروجی استاندارد ارسال می‌کند. ‫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";

Next, let's add a log message to display the value of the option in HelloWorldTest so we can demonstrate it was received correctly:

@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 World! پیام گزارش «دستگاه …» پس‌از اینکه به استفاده از FileLogger تغییر کردیم دیگر در stdout نمایش داده نمی‌شود. با ارسال آرگومان --log-level-display می‌توانید جزئیات گزارش‌گیری را در stdout افزایش دهید.

همین حالا این کار را امتحان کنید و باید پیام گزارش «دستگاه دارم» را دوباره در stdout ببینید، علاوه‌بر اینکه در فایل گزارش می‌شود:

tf> run example/helloworld --log-level-display info
…
05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548

همین بود، دوستان!

به‌عنوان یادآوری، اگر در جایی گیر کردید، کد منبع «فدراسیون تجارت» اطلاعات مفید زیادی دارد که در مستندات آشکار نشده است. اگر هیچ‌کدام از روش‌ها جواب نداد، در android-platform «گروه Google» با موضوع پیام «Trade Federation» سؤال بپرسید.