2010年12月7日火曜日

Android monkeyrunner を訳して試してみた。

訳しました。

monkeyrunner


-----------------------------------------

monkeyrunner ツールは、Android code 外から Android device やエミュレータをコントロールするプログラムを書くためのAPIを提供します。 monkeyrunner を使って、Android アプケーションやテストパッケージをインストールしたり、実行させたり、キーストロークを送ったり、ユーザーインタフェースのスクリーンショットを取ってワークステーションに保存したりする、Python プログラムを書くことができます。もともと、monkeyrunner ツールはアプリケーションや機能/フレームワークレベルでのデバイステストや、ユニットテストのためにデザインされましたが、他のどんな目的で使おうと自由です。

monkeyrunner ツールは monkey ツールとして知られている、UI/Application Exerciser Monkey とは関係ありません。monkey ツールはデバイスやエミューレータ上の adb shell ディレクトリ内で実行し、ユーザーとシステムの擬似ランダムイベントを発生させます。一方、monkeyrunner ツールは特定のコマンドや API のイベントを送ることで、ワースくテーションからデバイスとエミュレータをコントロールします。

monkeyrunner ツールは次のユニークな機能を Android テストにもたらします:

・マルチデバイスコントロール: monkeyrunner API は1つ以上のテストを複数のデバイスやエミュレータに対して適用することができます。一度、物理的にすべてのデバイスを起動したり、エミュレータをスタートアップしたりすれば、プログラムが順番にそれぞれに接続し1つ以上のテストを実行します。また、プログラムからエミュレータの設定をスタートアップし、1つ以上のテストを実行し、そしてエミュレータをシャットダウンさせることができます。

・機能テスト(Functional testing): monkeyrunner は Android アプリケーションの自動 start-to-finish テストを実行することができます。キーストロークやタッチイベントで入力値を提供し、スクリーンショットとして結果をみることができます。

・回帰テスト(Regression testing): monkeyrunner はアプリケーションを実行し、その出力スクリーンショットを正しいスクリーンショットと比較することで、アプリケーションの安定性をテストすることができます。

・拡張可能な自動化(Extensible automation): monkeyrunner は API toolkit なので、Android デバイスをコントールする Python-based モジュールとプログラムのシステム全体を開発することができます。monkeyrunner のAPIを自体を使用するだけでなく、標準的な Python ossubprocess モジュールを使って、Android Debug Bridgeのような Android tools を呼び出すことができます。

 monkeyrunner API に独自クラスを追加することも可能です。詳しくは Extending monkeyrunner with plugins


monkeyrunner ツールは Jython (Java プログラミング言語を使う Python 実装)を使っています。Jython によって、monkeyrunner API は簡単に Android フレームワークとインタラクトできます。Jythonを使用すると、API の定数,クラス,メソッドにアクセスするために Python の構文を使用することができます。



A Simple monkeyrunner Program

これは、MonkeyDevice オブジェクトを生成し、デバイスに接続する簡単な monkeyrunner プログラムです。MonkeyDevice オブジェクトを使うと、プログラムは Android アプリケーションパッケージをインストールし、そのアクティビティの1つを実行し、アクティビティにキーイベントを送ります。プログラムは結果のスクリーンショットを取り、MonkeyImage オブジェクトを生成します。このオブジェクトから、プログラムはスクリーンショットを含む a.png ファイルを書き出します。


# Imports the monkeyrunner modules used by this program
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice

# Connects to the current device, returning a MonkeyDevice object
device = MonkeyRunner.waitForConnection()

# Installs the Android package. Notice that this method returns a boolean, so you can test
# to see if the installation worked.
device.installPackage('myproject/bin/MyApplication.apk')

# Runs an activity in the application
device.startActivity(component='com.example.android.myapplication.MainActivity')

# Presses the Menu button
device.press('KEYCODE_MENU','DOWN_AND_UP')

# Takes a screenshot
result = device.takeSnapshot()

# Writes the screenshot to a file
result.writeToFile('myproject/shot1.png','png')



The monkeyrunnner API

monkeyrunner API は com.android.monkeyrunner パッケージ内の3つのモジュールに含まれます。

・MonkeyRunner: monkeyrunner プログラムのユーティリティメソッドクラスです。このクラスは monkeyrunnner とデバイスやエミュレータを接続するためのメソッドを提供します。さらに、monkeyrunner プログラムのための UI生成メソッドと、ビルトインヘルプを表示するためのメソッドを提供します。

・MonkeyDevice: デバイスとエミュレータを表します。このクラスはパッケージのインストール・アンインストールするためのメソッド、アクティビティをスタートするためのメソッド、キーボードやタッチイベントをアプリケーションに送るためのメソッドを提供します。また、テストパッケージを実行するためにこのクラスを使うこともできます。

・MonkeyImage: スクリーンキャプチャイメージを表します。このクラスはスクリーンキャプチャをするためのメソッド、ビットマップイメージをいくつかのフォーマットに変換するためのメソッド、2つの MonkeyImage オブジェクトを比較するためのメソッド、イメージをファイルに書き込むためのメソッドを提供します。


Python プログラムでは、Python モジュールとして各クラスにアクセスします。monkeyrunner ツールはこれらのモジュールを自動的にはインポートしません。これらのモジュールをインポートするために、Python の from 構文を使います。


from com.android.monkeyrunner import <module>


<module> にはインポートしたいクラス名を入れます。モジュール名をカンマで区切ることで、同じ from 文内で1つ以上のモジュールをインポートすることができます。



Running monekyrunner

monkeyrunner プログラムは、 a file からでも interactive session で monkeyrunner statements を入力する方法からでも実行することができます。いずれの場合でも、まず (SDK ディレクトリの) tools サブディレクトリ 内の monkeyrunner を実行します。ファイル名を引数で指定した場合、monkeyrunnner はそのファイル内の コンテンツを Python プログラムとして実行します。引数を指定しない場合は interactive session がスタートします。

mokeyrunner コマンドの構文は次のようになってます。


mokeyrunner -plugin <plugin_jar> <program_filename> <program_options>


 -plugin <plugin_jar>  (optional) monkeyrunner のプラグインを含む a.jar ファイルを指定します。複数のファイルを指定するには、複数回引数を含ませます。

 <program_filename>  この引数を指定した場合は、monkeyrunner コマンドはファイルのコンテンツを Python プログラムとして実行します。引数を指定しない場合は、コマンドは interactive session をスタートします。

 <program_options>  (optional) <program_file> 内のプログラムのためのフラグと引数



monkeyrunner Build-in Help

monkeyrunner の API リファレンスを生成するには、次のコマンドを実行します。


monkeyrunner <format> help.py <outfile>


 <format>  text か html を指定する。text は plain text 出力用、html は HTML 出力用
 <outfile>  出力ファイルのパス + 修飾子名



Extending monkeyrunner with Plugins

Java プログラミング言語で記述し、1つ以上の .jar ファイルにビルドしたクラスを使って monkeyrunner API を拡張することができます。独自のクラスを使って monkeyrunner API を拡張したり、既存のクラスを拡張するのにこの機能が使えます。また、monkeyrunner 環境を初期化する機能にも使えます。

プラグインを monkeyrunner に提供するには、-plugin <plugin_jar> 引数で .jar ファイルを指定して monkeyrunner コマンドを実行します。

プラグインコードでは、com.android.monkeyrunner の main monkeyrunner class の MonkeyDevice, MonkeyrImage, MonkeyRunner をインポートし、拡張することができます。(see the monkeyrunner API)

プラグインは Android SDK へのアクセスを提供するものではないことに注意してください。com.android.app などのパッケージをインポートすることはできません。monkeyrunner は framework API 以下のレベルでデバイスエミュレータとインタラクトします。

The plugin startup class

plugin の .jar ファイルはスクリプト処理がスタートする前にインスタンス化されるクラスを指定することができます。このクラスを指定するには、 .jar ファイルのマニフェストに MonkeyRunnerStartupRunner キーを追加してください。この値はスタートアップ時に実行されるクラスの名前でなければなりません。以下は ant build script 内でどのようにすればいいかのスニペットです。


<jar jarfile="myplugin" basedir="${build.dir}">
<manifest>
<attribute name="MonkeyRunnerStartupRunner" value="com.myapp.myplugin"/>
</manifest>
</jar>


monkeyrunner's runtime environment へのアクセスを得るには、スタートアップクラスは com.google.common.base.Predicate<PythonInterpreter> を実装する必要があります。例えば、このクラスは default namespace のいくつかの変数を設定します。


package com.android.example;

import com.google.common.base.Predicate;
import org.python.util.PythonInterpreter;

public class Main implements Predicate {
@Override
public boolean apply(PythonInterpreter anInterpreter) {

/*
* Examples of creating and initializing variables in the monkeyrunner environment's
* namespace. During execution, the monkeyrunner program can refer to the variables "newtest"
* and "use_emulator"
*
*/
anInterpreter.set("newtest", "enabled");
anInterpreter.set("use_emulator", 1);

return true;
}
}


-----------------------------------------

上記のサンプル Python コードをエミュレータで走らせてみました。

注意点

 ・http://developer.android.com/guide/developing/tools/monkeyrunner_concepts.html のコードには誤植があります。上記では修正してあります。
  間違い: device.takeSnapShot
  正解 : device.takeSnapshot()


 ・startActivityに指定するクラスが / を付けないとダメでした。
   起動しなかった : yanzm.products.hoge.Hoge
   起動した : yanzm.products.hoge/.Hoge

 ・エミュレータで試したんだけど、なんかポートのエラーがでる。(でもちゃんと実行される。。。)

 ・アプリケーションの起動が遅いのか、起動する前にメニューボタン押されてスナップショット取られてる気が、、、(保存されるスナップショットが全部ホーム画面になっちゃうんだけど。。。ポートのエラーの問題???)

 ・実行するとこんな感じのログ↓ 

101207 23:02:20.810:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: wake.
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] Error starting command: monkey --port 12345
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice]com.android.ddmlib.ShellCommandUnresponsiveException
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at com.android.ddmlib.AdbHelper.executeRemoteCommand(AdbHelper.java:408)
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at com.android.ddmlib.Device.executeShellCommand(Device.java:276)
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at com.android.monkeyrunner.adb.AdbMonkeyDevice$1.run(AdbMonkeyDevice.java:89)
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.util.concurrent.FutureTask.run(FutureTask.java:166)
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
101207 23:02:23.866:S [pool-1-thread-1] [com.android.monkeyrunner.adb.AdbMonkeyDevice] at java.lang.Thread.run(Thread.java:636)
101207 23:02:52.764:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: press KEYCODE_MENU.
101207 23:02:55.543:I [main] [com.android.monkeyrunner.MonkeyManager] Monkey Command: quit.

 
 ・保存されたスナップショット



















 

1 件のコメント:

  1. Activityを起動したり、メニューを開くとき、時間がかかるので、pythonスクリプトで、import timeして、時間のかかる処理を呼んだ後、time.sleep(時間)を呼ぶ(時間は秒)と、アプリの画面でスナップショットを撮れるようになります。

    返信削除