Más contenido relacionado La actualidad más candente (20) Similar a CEDEC2021 Android iOS 実機上での自動テストをより楽に有意義にする為に ~端末管理・イメージ転送・動画記録等の周辺情報のノウハウ共有~ (20) CEDEC2021 Android iOS 実機上での自動テストをより楽に有意義にする為に ~端末管理・イメージ転送・動画記録等の周辺情報のノウハウ共有~2. © SEGA
目次 - Android Android
準備フェーズ テスト実行フェーズ(繰り返し) 終了フェーズ
3. © SEGA
目次 -iOS
準備フェーズ テスト実行フェーズ(繰り返し) 終了フェーズ
iOS
XCodeビルド
アプリを転送・起動
ミラーリング映像の録画開始
テスト実行
ミラーリング映像の録画停止
アプリの終了
録画ファイルのリネーム
4. © SEGA
ゲームコンテンツ&サービス事業本部 技術本部 開発技術部
廣島 岳史
自己紹介
SEGA入社後
アーケード向けのライブラリ開発やツール作成などに従事
最近ではプランナー・デザイナに向けたGitHubEnterprise用のツール
の作成やUnity向け自動テスト支援ソリューションを開発しています
5. © SEGA
• 本講演内容の撮影、SNS投稿は OK
• 後日、CEDIL で資料を公開
• 講演中も質疑が可能です。
コメントに書き込んでください
• 講演後にZoomによる質疑応答を実施
• 資料に記載されている会社名、システム名、製品名、サービス名は各社の登録商標または
商標です
• Android ロボットは、Google が作成および提供している作品から複製または変更したもの
であり、
クリエイティブ・コモンズ表示 3.0 ライセンスに記載された条件に従って使用しています
12. © SEGA
モバイル端末 (iOS / Android)
主な役割
アプリケーションの実行
テストフレームワークとの通信
ビルドマシンとはUSBを用いて接続
テストフレームワークとはWi-Fiで通信
32. © SEGA
• お値段
• 使用可能なテストフレームワークに制限がかか
る事もある
• 他の人が端末を利用していて使えない事も
クラウドサービスの特徴(デメリット)
33. © SEGA
• お値段
• 自前のテストフレームワークの採用が可能
• テスト実行していない時は別の用途に転用可能
オンプレミス環境の特徴(メリット)
36. © SEGA
• お値段
• 独自形式のテストフレームワーク
• 導入するプロジェクトの秘匿性が高くテスト環
境を外に出すリスクが取れなかった
オンプレミスを採用しました
45. © SEGA
• Andoroidでの録画方法
– MediaProjection APIで録画
– REQUEST_MEDIA_PROJECTIONなどの権限
が必要
• iOSでの録画方法
– ReplayKit について
端末での録画を行う場合(補足)
47. © SEGA
ゲームコンテンツ&サービス事業本部 技術本部 開発技術部
竹原 涼
自己紹介
【登壇歴】
CEDEC 2020 「技術同人作家になろう ~働き方改革時代におけるエン
ジニアのレベルアップの一例~」
Unite Tokyo 2019 「大量のアセットも怖くない!~HTTP/2による高
速な通信の実装例~」
CEDEC 2018、CEDEC 2016、CEDEC 2015 プロダクション関連のラ
ウンドテーブル
GDC2016 報告会 「GDC16にみる自動化技術とテストのトレンド」
54. © SEGA
Extended Choice Parameter Plugin を利用してビルドパラメータで
端末IDを選択可能に
def p = "adb devices -l".execute()
p.waitFor()
def idList = []
def deviceNameList = []
def lines = p.inputStream.readLines()
lines.each {
def matcher = (it =~ /([0-9a-zA-Z]+) (.+)model:(.+) device:/)
while (matcher.find()) {
idList.add(matcher.group(1))
deviceNameList.add(matcher.group(3))
}
}
return deviceNameList
端末情報の取得 - Jenkins例 Android
55. © SEGA
選択されたデバイスIDからIPアドレスを取得する
def id = ““ // id には先ほど選択された値を入れる
def ip = ”“
def ipaddr = String.format(“adb -s %s shell ip addr show”, id).execute()
ipaddr.waitFor()
def ipLines = ipaddr.inputStream.readLines()
ipLines.each {
def ipMatcher = (it =~ /inet ([0-9.]+)/([0-9]+) brd/)
while (ipMatcher.find()) {
ip = ipMatcher.group(1)
}
}
// ここでipをファイルに書き込むなり何なりして下流に受け渡す
端末情報の取得 - Jenkins例 Android
VPNを張っている場合はVPNソフトに合わせた条件に
変えてあげる必要有り
例
def ipMatcher = (it =~ /inet ([0-9.]+)/([0-9]+) scope global tun0/)
61. © SEGA
Android同様Extended Choice Parameterでビルドパラメータ化
def devices = “xcrun instruments -s”.execute()
devices.waitFor()
def deviceNameList = []
def lines = devices.inputStream.readLines()
lines.each {
def matcher = (it =~ /([^ ]+) (([0-9.]+)) [([-A-Za-z0-9]+)]$/)
while (matcher.find()) {
deviceNameList.add(matcher.group(1))
}
}
return deviceNameList
端末情報の取得 - Jenkins例 iOS
63. © SEGA
• 公式のミラーリングソフトは存在しない
OSS やフリーのツールを利用するのが一般的
• 検証を行ったもの
〇 scrcpy 採用!
△ ApowerMirror
✖ Androidtool-mac, Vysor, Mobizen
ソフトウェアの選定 Android
64. © SEGA
• shell から操作可能
• オプションが豊富
• 端末にアプリケーションのインストールが不要
• USB経由でミラーリング可能
• ミラーリングされた画面からタッチ操作可能
scrcpyの特徴 Android
66. © SEGA
• 以下の3通りのいずれかで対応可能
1. ProcessTreeKillerから逃れる (お勧め!)
2. Pipelineで並列化する
3. AppleScriptを使ってJenkinsから切り離す
scrcpyをJenkinsで使う場合 Android
68. © SEGA
• shellの場合は環境変数 BUILD_ID に
dontKillMe プロセスパス(名) を入れて回避
• shell設定例
export BUILD_ID=dontKillMe scrcpy
nohup scrcpy <options> &
ProcessTreeKiller補足 Android
71. © SEGA
• Android 10から使用可能
内部的にAudioPlaybackCapture APIを使用
• 端末へのアプリケーションインストールが必要
sndcpy注意事項 Android
75. © SEGA
• Apple 独自のスクリプト言語
• shellから呼び出し可能
shellの先頭に #!/usr/bin/env osascript を付ける
• キーボードやマウス等の操作が可能
AppleScript Android
76. © SEGA
• 資料が少ない
オンラインの公式ドキュメントは情報が古いことも
• AppleScriptメニュー内にある用語説明がお勧め
AppleScript注意事項 Android
86. © SEGA
doandwait_process.sh
#!/usr/bin/env osascript
on run {do_command, process_name}
tell application "Terminal"
do script do_command in currentTab
delay 0.5
tell application "System Events"
repeat
if not (exists process process_name) then
exit repeat
end if
delay 0.5
end repeat
end tell
end tell
end run
Automatorアプリの終了を待つ Android
引数で受け取ったコマンドを実行
起動したプロセスが存在する限り待つ
87. © SEGA
1. sndcpyを端末へインストール
2. sndcpy起動
3. 端末側でポップアップが出る
4. ポップアップを閉じる
5. PC側でVLCを起動する
音声ミラーリングまでの流れ Android
sndcpyを分割して自前のAutomatorの処理を挟む
strat_vlc.sh
sndcpy
Automator処理
89. © SEGA
#!/bin/bash
set –e
export VLC=/Applications/VLC.app/Contents/MacOS/VLC
VLC=${VLC:-vlc}
SNDCPY_PORT=${SNDCPY_PORT:-28200}
"$VLC" -Idummy --demux rawaud --network-caching=50 --play-and-exit tcp://localhost:"$SNDCPY_PORT"
sndcpy分割 (start_vlc) Android
90. © SEGA
export BUILD_ID=dontKillMe scrcpy
nohup scrcpy –b 30M –window-title ‘Title’ –turn-screen-off –serial $ANDROID_ID &
scrcpy - Jenkins例 Android
オプションの意味は以下の通り
• 端末のモニタの電源を切って起動
• ビットレートは30Mbps
• ウィンドウのタイトルを「Title」にする
後述する録画の際に必要になってくるので必ず付ける
• ANDROID_IDで指定したIDの端末をミラーリング
91. © SEGA
# sndcpyインストールと起動
./sndcpy
# ポップアップの抑制
./doandwait_process.sh "exec open -a SupSndcpyPopup" "SupSndcpyPopup"
# VLCの起動
export BUILD_ID=dontKillMe start_vlc
nohup ./start_vlc &
sndcpy - Jenkins例 Android
93. © SEGA
• Quick Time Playerでミラーリングが可能
MacOSに標準インストール
• Quick Time Player以外の候補は見つからず
• 自動化についてはハマり所がいくつかある
ソフトウェアの選定 iOS
94. © SEGA
• Quick Time Playerで録画することは可能
ただしエンコーディング無し
Quick Time Playerでの録画 iOS
100. © SEGA
AppleScriptを実行(devtech-iphoneを選択)の書き換え
元の実装
set uiScript to “click menu item ”devtech-iphone“ of menu 1 of UI Element 5 of window 1 of application process ”QuickTime Player““
修正後の実装
set deviceName to system attribute "DeviceName“
set uiScript to "click menu item "" & deviceName & "" of menu 1 of UI Element 5 of window 1 of application process "QuickTime Player""
複数デバイス対応 -AppleScript iOS
デバイス名が埋まっているのでここを動的に差し替える
• 呼び出し元で環境変数DeviceNameにデバイス名をexportしておく
• 環境変数はAppleScriptからは to system attribute で読める
101. © SEGA
複数デバイス対応- Jenkins例 iOS
export BUILD_ID=dontKillMe start_iphone_mirroring
export DeviceName=“devtech-iphone“
nohup open -a start_iphone_mirroring &
Jenkins側は環境変数設定を挟むだけでOK
“あいふぉん”のような日本語はアウトなので注意
105. © SEGA
adb -s $ANDROID_ID install $APPLICATION_PATH
アプリの転送 – Jenkins例 Android
108. © SEGA
• シェルから起動オプションが指定可能
• ほぼ全ての操作のキーボードショートカット有
• websocketによる操作も可能
• 録画と同時にエンコーディング可能
• 配信でよく利用されており更新が非常に活発
OBS Studioの特徴 Android iOS
110. © SEGA
• プロファイル毎に設定可能
• 複数の端末でテストを行う場合
プロファイルを端末毎に作成
端末数が多い場合にプロファイルの作成が手間
全ての解像度を包含できるような解像度で作成
余白ができてしまい見栄えが悪い(がテスト用途なので気にしない)
事前設定 – キャンバス Android iOS
114. © SEGA
• --startrecording で起動後自動で録画開始する
• --profile “name” でプロファイル指定可能
• その他のパラメータは公式ドキュメント参照
https://github.com/obsproject/obs-studio/wiki/Launch-Parameters
OBSの起動時引数 Android iOS
115. © SEGA
export BUILD_ID=dontKillMe obs
nohup open -a obs --args --profile $PROFILE_NAME &
OBS – Jenkins例 Android iOS
• 起動後自動で録画開始したい場合は --startrecording を入れる
• 構築した環境ではテスト毎にOBS起動するコストが無駄なので起動と録
画開始は切り離している為にここでは –startrecording は入れていない
121. © SEGA
adb -s $ANDROID_ID shell am start "$PACKAGE_NAME/$ACTIVITY_NAME"
アプリの起動 – Jenkins例 Android
128. © SEGA
• キャッシュされた.appのパス
/Library/Developer/Xcode/DerivedData/"xcodeproj名"-"ハッシュ値"
/Build/Products/Debug-iphoneos/"プロダクト名".app
• ハッシュ値の動的取得方法は不明
一度決まると変わることはないので、環境構築時に一
度手動でビルドして確認
test-without-building連続実行 iOS
130. © SEGA
cd "Xcodeプロジェクト配置パス"
# .appを削除
rm -rf /Library/Developer/Xcode/DerivedData/$XCODE_PROJ_NAME-
$XCODE_DERIVED_HASH/Build/Products/Debug-iphoneos/$PRODUCT_NAME.app
# xcodebuildでアプリケーションビルドのみ実行
xcodebuild build-for-testing "platform=iOS,id=$IPHONE_UUID" -scheme "実行したいXcodeのScheme名"
ARCHS=$ARCH_TYPE
アプリのビルド – Jenkins例 iOS
131. © SEGA
cd “Xcodeプロジェクト配置パス”
# xcodebuildでアプリケーションを転送/起動する
xcodebuild test-without-building -destination “platform=iOS,id=$IPHONE_UUID” -scheme
“実行したいXcodeのScheme名" ARCHS=$ARCH_TYPE
アプリの転送/起動 – Jenkins例 iOS
141. © SEGA
• アプリの起動タイミングに仕込む
実行時の引数
adbは引数オプション有り
xcodebuildはプリプロセッサマクロを活用
テスト用コンフィグファイル
アプリ起動前に毎回端末に転送し直す等
テストの実行(非通信型) Android iOS
142. © SEGA
録画停止 Android iOS
USB
アプリを起動
ミラーリン
グした映像
の録画開始
テストの実
行
ミラーリン
グした映像
の録画停止
アプリを終了
録画ファイ
ルのリネー
ム
1 2 3 4 5 6
146. © SEGA
adb -s $ANDROID_ID shell am force-stop $PACKAGE_NAME
アプリの終了 – Jenkins例 Android
175. © SEGA
set CURRENT_SESSION_ID=-1
for /f "tokens=3-4" %%a in ('query session %username%') do @if "%%b"=="Active" set CURRENT_SESSION_ID=%%a
echo %CURRENT_SESSION_ID%
exit /b %CURRENT_SESSION_ID%
セッションの確認実装例
180. © SEGA
• 自作以外のアプリはshellから起動が難しい
xcodebuild経由でないといけない
QuickTimePlayerでミラーリングした画面をAutomator
で操作してアプリ起動する方法は前述の切断問題がある為
にNG
• 自前で以下の対応をするしかない
専用のアプリを作成する
テストを行うアプリに仕込みを入れる
端末から通信を行う iOS
183. © SEGA
rvictlを使って仮想インターフェースを作成(Pipeline)
def rviStart = "rvictl -s ${params.IPHONE_UUID}".execute()
rviStart.waitFor()
def rviLines = rviStart.inputStream.readLines()
def viName = "“
rviLines.each {
def matcher = (it =~ /interface ([0-9a-zA-Z]+)/)
while (matcher.find()) {
viName = matcher.group(1)
}
}
Jenkins - Unity例 iOS
184. © SEGA
ポート54997のパケットからIPを抽出(Pipeline)
def dump = "sudo tcpdump -i ${viName} -t -c 1 dst port 54997".execute()
dump.waitFor()
def lines = dump.inputStream.readLines()
def ip = "“
lines.each {
def matcher = (it =~ /IP ([0-9]+.[0-9]+.[0-9]+.[0-9]+)/)
while (matcher.find()) {
ip = matcher.group(1)
}
}
Jenkins - Unity例 iOS
前ページで作成した仮想インターフェース名
188. © SEGA
• VPN接続しているとrvictlでキャプチャできない
VPNで生成された仮想インターフェース側にパケット
が流れてしまう
• 環境はVPN不要なネットワーク上に構築しよう
scrcpyもQuickTimePlayerもミラーリング映像をマ
ウスで操作できるので、いざという時でもVPN越しに
端末の操作を行うことが可能
補足 - VPN環境下の問題