しばらく前に、 Androidでのメールの自動テストについてお話しし 、読者から膨大な数の質問を受けました。 本日、iOSのセルフテストに関連する「内部キッチン」の一部を公開します。 各アセンブリをテストするために、1時間未満で実行される500以上の自動テストを実施しています。 それらをどのように実装したのですか? どのような問題に直面し、どのように解決できましたか? カットの下でこれについてすべて読んでください。
内容
Gerritコードレビュー
コマンドシステムとCI通信
CIプラットフォーム
組立
テストビルド-ステージ1
アルファ版-ステージ2
ベータステージ3
リリースステージ4
アプリケーションの組み立て
アプリケーション署名とプロビジョニングプロファイル
問題
チェックとテスト
UIテスト
フレームワークはどうあるべきか
MonkeyTalk Mods
スクリプトによるUIテストの管理
スケーリング-並列起動
最初のバージョン
現在のバージョン
起動時の分離
各シミュレーター!
シミュレーターコミュニティ
Xcode、シミュレーターの問題などの回避策
iOSシミュレーターとの相互作用の安定性には、多くの要望があります
シミュレーターのプールとロード
完了の兆候のない非同期simctl
アプリケーションのシステムダイアログ
写真と連絡先へのアクセス
サーバー応答のなりすまし
報告
ドキュメント生成
システム概要
短所
メリット
プロジェクトチェック
同じターゲット
ローカリゼーション
カテゴリー
テストヘッダー
報告
HipChatとレポート
新しいタスクのレポート
アセンブリ、テスト、チェックの結果に関するレポート
概略的に、ワークフローは次のとおりです。
この図は、リリースの途中のタスクの最初の、最も厳格で重要なステップを示しています。 最初のコンポーネントの1つであるCode Reviewから始めましょう。
Gerritコードレビュー
コードレビューには、Gerrit Code Reviewを使用します。 一部のアクションには多くのリソースが必要なため、別のノードで動作します。 コードはgitリポジトリに保存されます。 さらに、追加ノードへのリポジトリのミラーリングが構成され、Time Machineを介したディスク全体のバックアップが常にサポートされます。
ワークフローはGerritと密接に関連しています。 自動チェックだけでなく、QAテストも実行されます。 どの段階でも承認/失敗は、スコアとしてGerritに記録されます:+ 1 / -1。 自動検証が失敗するか、テストアセンブリを組み立てられない場合、-1が設定されます。
最初の段階が成功した場合、QAチームが引き継ぎます。 これらはJiraとのみ相互作用し、タスクをOpenまたはQA Approved状態に移行するため、遷移中に対応するラベルはGerritに設定されます。 これを行うために、Jira用の小さなプラグインが実装され、まさにこのタスクを実行します-それ以外は何もしません。
コマンドシステムとCI通信
Gerritでは、追加のプラグインでサーバー側のフックを使用できます。 rubyに次のフックを実装します。
- 変更マージ;
- コメント追加;
- マージ失敗;
- パッチセット作成;
- ref-update;
- レビュアー追加。
ほとんどのフックは、変更についてチームに通知したり、Jiraでタスクの状態を同期したりするために使用されます。 たとえば、Gerritで次のパッチセットを作成すると、対応するフックが呼び出され、Jiraタスクがコードレビュー状態になり、このパッチセットの自動チェックがまだ実行されていないため、カスタムのMerge ApprovedフィールドがNoに設定されます。 ただし、たとえば、 コメントが追加されたフックなど、より飽和した実装があります。 これにより、CIプラットフォームでチェックを実行し、変更を作成します。 これを行うために、特定のパッチセット上のCIで特定のジョブを実行できる独自のコマンドシステムを実装しました。 コマンドセットを拡張するタスクを容易にするために、ジョブでのコメントの表示を記述する小さなDSLを実装しました。 たとえば、自動チェックを起動するコマンドの説明は次のようになります。
JobMapping.register do command 'flow' job BUILD_FLOW_JOB arguments_required processor do |args| { 'CHECKS_ENABLED' => args.key?('checks') } end processor do |args| { 'ANALYSIS_ENABLED' => args.key?('analysis') } end processor do |args| { 'UNITTESTS_ENABLED' => args.key?('unit') } end processor do |args| if args.key?('ui') tags = args['ui'] { 'UITESTS_ENABLED' => true, 'UI_TESTS_TAGS' => tags.nil? ? '' : tags.tr(',', ' ') } else { 'UITESTS_ENABLED' => false } end end end
自動チェックはいくつかの部分で構成されており、今必要なものを実行できるようにしたいと考えています。 各JobMappingは特定のコマンドにアタッチされ、 コマンドメソッドによって設定されます。 また、このコマンドはJenkinsのJobに表示される必要があり、どのメソッド( job )を推測するように設定されます。 コマンドに引数がある場合、JobMappingはarguments_requiredメソッドによってその必要性を示します。
Jenkinsジョブは環境変数を介して設定されます。これを行うには、 プロセッサメソッドを使用してマッピングメソッドにブロックプロセッサを追加し、コマンドラインを環境変数のセットに変換します。
ほとんどの場合、これらのメソッドは必要なマッピングを作成するのに十分です。 その後、怠inessが発生し、 「!Flow ui unit」の代わりに、単純に「!Tests」と記述します。 したがって、放送が登場しました:
JobMapping.register do command 'tests' translates_to '!flow ui unit' rebase end
すべてがそれ自体を物語る 怠laz 純粋な快適さ。
CIプラットフォーム
プラットフォームのほとんどは、Rubyでゼロから作成されています。 各テストまたはタスクは、ローカルとCIの両方で実行できる個別のモジュールによって実装されます。
タスクを実行するには、Rakeを使用します。 したがって、モジュールごとに、Rakefileに個別のRakeタスクがあります。
開発者がローカルの起動時にコンソールログに依存できる場合、CIで最終レポートとアラートが表示されることを期待しています。 これは開始原則の 1つです。Jenkinsのジョブログは、クライアントではなくCI開発者に必要です 。 したがって、すべてのテストモジュールは、CIで実行するときにタスクの最終状態とエラーの説明(ある場合)を取得するために、同じインターフェースを実装します。 したがって、CIには、Rakeタスクを実行し、特定のリビジョンのコードの複製、証明書の内容、アーティファクトの保存などの基本的な操作を実行できるシステムが必要です。 ジェンキンスはベースとして習得されました。 彼から私達は使用します:
- スレーブプラグインとノード上のタグ/ラベル-10個のMac Miniを自由に使用できます。
- Gitプラグイン
- アーティファクトストレージ。
- チェーンおよび並列タスクを作成するためのBuildFlowプラグイン (時代遅れであり、サポートされていません-知っていますが、これまでのところ、それらはしっかりと接続されています);
- 実行中のジョブの名前を、タスクに関連付けられたものに変更します。
すべてのジョブは、リポジトリの複製後に実行される一連のshスクリプトです。 ほとんどの場合、Jobのshスクリプトは単にRakeタスクを呼び出します。 他のすべての操作(xcodebuild、scan-build、プロジェクトおよびソースチェッカーモジュール、UIテストシステムの起動、結果の処理)は、rubyスクリプトのフレームワークに実装されます。
CIプラットフォームでは、2種類のタスクを区別できます。
- 集会
- チェックとテスト。
組立
アセンブリとチェック/テストは互いに独立しています。アセンブリが成功すると、アプリケーションとタスクはすぐにQAチームに送られ、特定の新しいケースを手動で検証します。 リリースの各段階で、アセンブリが次のようになります。
- 特定のタスクのアセンブリのテスト。 つまり、機能ブランチアセンブリ。
- Gerritのメインプロジェクトブランチの現在のアルファ。
- ベータ版
- リリース。
HockeyAppでリリースされているため、最初の2つのステージのみが完全に自動的に機能します。 ベータ版とリリースはまれであり、注意が必要なため、必要に応じて手動でトリガーされます。
テストビルド-ステージ1
変更がCode Reviewに合格すると、テストビルドが準備されます。これは、まだメインブランチにない変更を含むバージョンです。 正常にアセンブルされたアプリケーションは、テストビルド用の個別の識別子でHockeyAppにすぐにアップロードされます。
HockeyAppのバージョンリンクは、Jiraのタスクに添付されます。 アセンブリが成功すると、タスクはテストの準備完了状態になります。 失敗した場合-さらなる改良とエラー修正のためにオープン状態にします。
QAチームは、さまざまなデバイスおよびiOSバージョンでのタスクのコンプライアンスについてアセンブリをチェックします。 誰もが変更に満足しており、その時点までに自動チェック、コードレビューとデザインレビューが既に合格している場合、Jiraで合格をクリックするとすぐに変更がメインブランチにプッシュされ、それによって第2ステージが開始されます。
アルファ版-ステージ2
変更がプロジェクトのメインブランチに注がれたら、アプリケーションのアルファバージョンをリリースする必要があります。 このアセンブリのトリガーは、Gerrit change-mergedのフックです。 変更のマージ直後に、ジョブはアプリケーションの現在のアルファバージョンのアセンブルを開始します。
このジョブでは、10分の遅延が設定されています。これは、いくつかの依存(または独立)タスクをマージする場合に必要です。 ジョブ全体の平均所要時間は11〜12分であるため、各タスクに個別に時間を費やすよりも、複数のタスクのマージを待つ方が簡単です。 アセンブルされたアルファバージョンには、最後に正常にアセンブルされたバージョン以降のすべての変更が含まれます。
正常に完了すると、アプリケーションの最新バージョンがHockeyAppにアップロードされます。 Jiraのタスクは、その時点で閉じられていると見なされます-アプリケーションのアルファ版で利用可能になるとすぐに。 したがって、HockeyAppに公開すると、バージョンに含まれるすべてのタスクが閉じられます。 これを行うには、Jira APIを使用します。 また、JenkinsのJobとHockeyAppのアプリケーション自体へのリンクを含むコメントを残しています。
手順2でのアセンブリ中の障害はほとんどありません。 これが発生する場合、HockeyAppまたはJira APIの長時間のクラッシュが原因である可能性が最も高いです。 これはまれですが、...
次の影響力のある変更が同じジョブを起動し、以前の変更をキャプチャするため、これに問題はありません。
ベータステージ3
3番目の段階はベータ版です。 Jenkinsの個別のジョブ。ベータ版の収集元となるブランチの名前のみが必要です。 ビルドは手動で開始されます。ベータ版は正確な日にスケジュールされていますが、特定の開始時間/トリガー期間またはトリガーはありません。
ビルド結果は自動的にHockeyAppに送られ、QAチームが利用できるようになります。 同じブランチからリリースバージョンのジョブがすぐに起動され、アセンブリがTestFlightに読み込まれ、内部および外部のテストに対して発行されます。
リリースステージ4
4番目の段階はリリースです。 3番目のステップでiTunesConnectとTestFlightにすでにロードされているベータ版のビルドがリリースされます。
アプリケーションの組み立て
ビルドはxcodebuildによって直接実行されます。 各ステージには、個別の構成と引数が並んでいます。 たとえば、最初の段階で、リンク時最適化を完全に無効にして、タスクがテストに入るまでの時間を節約します。 また、アプリケーションの最初の段階では、コンパイル時にアクティブ化される機能を使用できます。これは、問題の追跡とデバッグに役立ち、QAチームのいくつかのタスクも促進します。
- データベースの再作成;
- Cookieの削除。
- デバッグレベルのログとデータベースを別のボックスに送信します。
- 使用するいくつかのパラメーターの状態を示す追加の視覚要素(たとえば、接続の品質の表示)。
- アプリケーションを具体的に閉じる機能。
- メインスレッドで指定されたクラスの指定されたメソッドの呼び出しを確認します。
アプリケーション署名とプロビジョニングプロファイル
現時点では、自動アセンブリ中に、使用する証明書とプロビジョニングプロファイルを厳密に設定します。 アセンブリの前に、ノードで使用可能なプロファイルを調べ、現在のアセンブリに適したプロファイルを選択し、環境変数を使用してプロジェクト設定で設定します。 もちろん、このステップはスクリプトによって自動的に実行されます。
それには理由がありました。 Xcodeの新しいメジャーバージョンが表示されたらすぐに切り替えようとしていますが、Xcode 7.0のベータバージョンの1つでxcodebuildのバグに遭遇しました:自動プロビジョニングプロファイル検出を使用できませんでした。 したがって、利用可能なすべてのプロファイルから必要なバンドルIDを見つける同じRubyモジュールに実装し、プロファイルの代わりにenv変数をプロジェクトに追加しました。 それらに、アセンブリ中に、プロファイルの識別子を配置します。
Xcode 7.0では、これにより不都合が生じることはありませんでした。 env変数が空の場合、プロファイルは引き続き開発環境によって自動的に置き換えられました。 しかし、Xcode 8.0と新しい自動署名モードでは、この方法は機能しませんでした。 自動署名を使用すると、環境はデバッグバージョンとリリースバージョンの開発証明書とプロファイルの両方を自動的に置き換えます。 リリースバージョンで配布証明書を設定するのが難しい場合、xcodebuildは動作しません。
つまり、xcarchiveを作成するとき、開発用にサインアップされたバージョンが常に出てくると想定されています。 次に、第2段階が続きます。IPAをエクスポートします。IPAの場合、-exportOptionsPlistを使用して構成を設定し、それに応じて、目的のディストリビューションにアプリケーションを再署名できます。 当初、このようなルールは私たちに合わなかったため、自動署名モードをオフにし、手動でプロファイルを置き換えました。 構成は次のとおりです。
問題
Xcodebuildには回避できない制限があります。ランタイムを犠牲にしないと、複数のプロセスを開始できません。 さまざまなenv変数とキャッシュとデータへのパスを試しましたが、役に立ちませんでした。 どこかに、彼はまだ共有リソースに依存しています。
同時アセンブリでは、各プロセスの代替作業が非常に顕著です。 2つの並列xcodebuildの合計所要時間はほぼ2倍になります。 したがって、Jenkinsスレーブごとに1つのエグゼキューターをインストールします。 これにより、追加のプラグインなしで2つのタスクの干渉がなくなります。これは、Jenkinsで常に適切に機能するわけではありません。 同時に、すべてのジョブは専用のエグゼキューターを最大限に活用します。複数のタスクを並行して実行することは特に意味がなく、相互に干渉するだけです。
チェックとテスト
チェックとテストには、対応するRakeタスクがあります。チェックごとに個別のタスクがあります。 以下のチェックの目的と実装についてより具体的に説明しますが、現時点では-高レベルの職務記述書です。
JenkinsはBuildFlowプラグインを使用します。これにより、並列タスクの実行、それらのマージ、チェーンの作成などが可能になります。自動化されたチェックとテストは、BuildFlowタスクの一部として実行されます。 フルバージョンでは、次のものが並行して起動されます。
- プロジェクトとソースのチェック-文字列のローカライズ、カテゴリの使用、UIテストのヘッダー、ターゲットへのソースの包含。
- 静的コード分析;
- 単体テスト
- UIテスト。
最初の3つのタスクは1つのJenkinsジョブで実行されますが、UIテストシステムはxcodebuildを介してテストを実行するよりも少し複雑です。 これは別のチェーンであり、その最初のステップ-ビルド前-は次のとおりです。
- テストアプリケーションのアセンブリ。
- 並列実行のためのUIテストのグループへの配布。
- アセンブリをアーカイブにパックします。
次に、配布結果に基づいて、BuildFlowはJenkinsで必要な数のタスクを起動します。各タスクには独自のテストグループがあります。 各ジョブは、ビルド前のアーティファクトからUIテスト、構成、アセンブリ、およびスクリプトを単一のアーカイブとして受け取ります。 BuildFlowが次のタスクに進んだ後、その本質は最終的な全体レポートを作成し、Jira、HipChat、Gerritでアラートを実行することです。 各パスの結果については、このタスクは成果物に変わります。 BuildFlowが構成されています。必要に応じて、任意のパスをオフにすることができます。
UIテスト
フレームワークはどうあるべきか
UIテストのフレームワークには次のものが必要であると考えています。
- iOSの開発者とプログラマーだけが処理できる基本構文。
- ベストプラクティスを再利用する機能。 UIテストを作成するとき、コマンドはアクセシビリティ識別子を使用してターゲットにされます(ほとんどの場合)。 理想的には、標準で利用可能なコマンドの上に独自のライブラリを構築したいと考えています。 これにより、基本コードの重複を放棄し、アプリケーションコードを少し修正するだけで作業を容易にすることができます。 識別子は変更されましたか? 検索と置換なし:レイヤーに移動し、そこで1つの識別子を変更します。
MonkeyTalk(現在Oracleによって購入されていますが、それ以上のパスはまだ不明です)が私たちの基準に達し、他の応募者を大幅に追い越しました。 シンプルな独自の基本構文があり、一般的なテストフラグメントを個別のスクリプトに作成して再利用できます。 この言語でテストを作成するには、プログラミングをまったく理解する必要はありません。AccessibilityIdentifierを見つけるだけです。Reveal 、Accessibility Inspector、 Flexなどのユーティリティで抽出し、最悪の場合は開発者に尋ねてください。
結果のスクリプトは、QAチームによって記述されたテストケースをほぼ完全に繰り返します。 同時に、MonkeyTalkはJavaScriptをサポートしています。 ここでは、アクションのスコープにより、標準のコマンドと要素識別子の上に同じレイヤーを実装できます。 徐々に、 ページオブジェクトに非常によく似たものが作成されました 。 各画面、ダイアログ、およびアプリケーション要素は最終的にJavaScriptで独自のモジュールを取得し、UIテストの開発を大幅に加速しました。
同時に、プログラミングのすべての利点(サイクル、条件、機能)が利用可能ですが、これには反対です。 テストは単純で簡単で、少し冗長になっている必要があります。 テストが失敗した場合は、スクリプトに目を向ける必要がある場合があり、編集モードで削除した後にメッセージ名が検証されなかったことを明確かつ正確に伝える機能が必要です。 そして、コマンドによる3つの条件分割と引数の1対のラムダを持つレジームのサイクルの存在下でこれを行うことは非常に難しく、非常に不愉快です。 ただし、一部のテストではまだ(ただし適度に)サイクルを使用しています。これにより、テストスクリプトの長さが大幅に短縮されます。
MonkeyTalkにはもう1つの大きな利点があります。これは、大幅に拡張されたUIテストのベースの実行を最適化および加速するときに将来非常に役立ちました。
MonkeyTalk Mods
MonkeyTalkはXCTestから独立しています。 この重要な機能のおかげで、UIテストを実行するための分散システムを実装できました。 他のフレームワーク( KIF 、 EarlGrey 、および公式のXCUITest)は、xcodebuildを介して起動されます。 XCUITestでは、すべてが非常に困難です。 テストの実行時に機能するプロセスに注意を払うと、 testmanagerdに気付くでしょう 。 彼は進行中の1つのテストに完全に従事しています。 別のセッションを並行して開始した場合、testmanagerdには何も起こりません。テストは開始されません。
KIFとEarlGreyは、XCTestを使用しています。 同じノード内で並列に実行することを考え始めたとき、それほど多くの開発はありませんでした。 FBSimulatorControlの開発が始まり、 pxctestが登場しました。 自身の試みは、多かれ少なかれ有用なものにはつながりませんでした。 MonkeyTalkオプションはよりシンプルで手頃な価格でした。
MonkeyTalkは、XCTestまたはXCUITestに決して依存しません。 個別のテスト実行と実行システムがあります。 httpサーバーを備えたエージェントがアプリケーションに組み込まれています。 スクリプトの側では、UIテストのスクリプトを解析し、指定されたアドレスにコマンドを送信し、コマンドの結果で応答を待つランナーが使用されます。
その結果、シミュレータでアプリケーションを実行し、起動を待ってから、ランナーを実行して結果を処理するだけで済みます。
時間が経ち、次のiOSのアップデートで、いくつかの困難に直面しました。それらを解決するために、MonkeyTalkを自分用に修正して実装しました。
- 「正直」はキーボードで動作します。
- イベントUIApplicationレベルでの「正直な」テープとジェスチャー。
- ヘルパーの制御された呼び出しのための追加コマンド;
- 要素の可視性の完全なチェック。
また、UIKitのいくつかの要素とネットワーク相互作用の一部との相互作用を書き直し、フレームワーク全体を大幅に高速化しました。
UIテストは、できるだけ実際の条件に近く、ブラックボックスの原則を維持する必要があります。 実際の状態でのテープとジェスチャーは、ユーザーの手によって実行されます。 自動化で達成できる最も近い方法は、アプリケーションのイベントレベルでクリックをシミュレートすることです。 当初、MonkeyTalkはUIControlsおよびUIGestureRecognizersにメッセージを直接送信しましたが、これはまったく私たちに合わず、場合によってはケースをカバーすることができませんでした。
また、アプリケーションの起動時を含むスタブを含む/無効化を含む、テストスクリプトを介してすべてのネットワークの相互作用を制御することができます。 それについては後で詳しく説明します。 追加の利点は、突然デバイスに切り替えることにした場合、実際のデバイスとまったく同じプロセスであることです。
スクリプトによるUIテストの管理
MonkeyTalkには特別な起動方法があるため、追加の制御スクリプトを実装する必要がありました。 彼らのタスク:
- 実行するテストのセットを定義する
- シミュレーターを管理します。
- UIテストを実行します。
- 結果を処理します。
UIテストスクリプト自体については、見出しを導入しました。これについては、後で詳しく説明します。 ここで、テストを実行するデバイスのタイプとiOSのバージョンが設定されているのはヘッダーにあることを指摘する価値があります。 複数の引数が指定されている場合、デカルト積が取得されます。 テスト前にアプリケーションを構成するために、アプリケーションにさまざまな引数を渡すこともできます。
これらはすべて実装が非常に簡単ですが、UIテストは長いプロセスであり、プロジェクトのすべての変更をチェックすることが目標です。
スケーリング-並列起動
最初のバージョン
当初、UIテストを実行するための自己記述システムは、リソース使用率が低いという特徴がありました。 70個のテストをフォルダーに分割しました。 各フォルダーは、UIテストのカテゴリ(たとえば、承認)に対応していました。 起動時に、フォルダはMac Miniに配布され、利用可能になりました。 それらのそれぞれで、一度に1つのシミュレータのみが機能し、再起動され、新しいUIテストによって完全に上書きされました。 明らかな欠点:
- i5と16 GBのRAMを搭載したMac Miniは、リソースの平均0%を使用します。
- フォルダのカテゴリへの分布は不均一であり、検索テストはテストを書くよりもはるかに少ないことがわかりました。
- テストの数は増えましたが、まだゆっくり実行されていて、CIに深刻なキューが形成されていたため、テストに合格しないとタスクを管理できません。
次に、テストベースをわずかに拡張し、最も必要かつ基本的なものをカバーして、リソースの使用を最大化する方法を探し始めました。 そして彼らは見つけた。
現在のバージョン
つまり、同じマシンで複数のシミュレータを実行および制御することを学び、マシンのグループにテスト配布システムを実装しました。 :
- , .
- / , , .
- HTTP- MonkeyTalk . MonkeyTalk , . environment variable launch argument.
, . i7, 16 SSD - . , . . , env-, . UI-.
Jenkins API. env- . , , , . API.
— . , . , , . , Jenkins. :
- .
- json .
- .
- .
json UI-. UI- , . UI-. :
- .
- json , .
- ( ) .
- — « » => «», .
- UI- Jenkins , .
- , .
- .
UI- :
- .
- .
. , UI- . , . , . , .
UI- . , . , :
- , , . . ;
- , ;
- .
json , :
INFO IOSMail::UITestsGrouper : Getting all available tests... INFO IOSMail::UITestsGrouper : Found 506 available tests. Took 0.8894939422607422 secs. INFO IOSMail::UITestsGrouper : Reading stats json from ./ui-tests/uitests_stats.json... INFO IOSMail::UITestsGrouper : Gluing available tests and stats... WARN IOSMail::UITestsGrouper : No statistics for ScrollToFirstLetterInListFromMiddle.mt_iPhone5s. Using mean tests time. INFO IOSMail::UITestsGrouper : Statistical total duration = 33603.8670472377 INFO IOSMail::UITestsGrouper : Computing capacities counters... INFO IOSMail::UITestsGrouper : Capacities counters received and computed. Took 3.4377992153167725 secs. INFO IOSMail::UITestsGrouper : Partitioning for 7 parts... INFO IOSMail::UITestsGrouper : Allocated partitions = [{:capacity=>4, :label=>"4sim", :tests=>[]}, {:capacity=>4, :label=>"4sim", :tests=>[]}, {:capacity=>5, :label=>"5sim", :tests=>[]}, {:capacity=>4, :label=>"4sim", :tests=>[]}, {:capacity=>5, :label=>"5sim", :tests=>[]}, {:capacity=>3, :label=>"3sim", :tests=>[]}, {:capacity=>4, :label=>"4sim", :tests=>[]}] INFO IOSMail::UITestsGrouper : Partitions constructed, took 0.039823055267333984 secs. INFO IOSMail::UITestsGrouper : Partitions sizes = [72, 70, 87, 73, 83, 46, 75] INFO IOSMail::UITestsGrouper : Partitions durations = [1157.0547642111776, 1160.5415018200872, 1157.1358834599146, 1159.2893925905228, 1158.044078969956, 1161.813697735469, 1158.7458768486974]
. 4—5 , API Jenkins. UI-, . , .
UI-. . , — 40 . . , 5 , , , . , , 10—15 UI-. . : 10 , .
UI- . . UI- .
!
, . ruby wrapper simctl — xcode iOS-. :
simulator = Simulators::MRSimulator.new(test_name, type, runtime) simulator.launch simulator.prepare(app) simulator.install_app(app) If access_allowed simulator.enable_access(app) else simulator.disable_access(app) end simulator.run_app(app, arguments, env) # MonkeyTalk runner simulator.shutdown # (, -, , , ) simulator.destroy
- , simctl, , , . ́ syslog' . . — .
, . :
- .
- .
- .
- .
- — , keychain, , (TCC).
- , , — 2.
, . , 20 . , .
keychain. data- appgroup- . . keychain . keychain-2-debug.db. , - , . keychain:
def clear_keychain `sqlite3 ~/Library/Developer/CoreSimulator/Devices/#{@id}/data/Library/Keychains/keychain-2-debug.db "delete from genp;"` end
, ( , ), keychain — .
xcode, simulator . .
C iOS-
iOS- , . , , , , . , , - . . iOS- , dlopen, . — , Jenkins , . . , - .
, . -, , watchdog springboard. — 20 . , FBSimulatorControl Facebook. , preferences springboard , - Bundle Id. , springboard, preferences , ? — .
— . File IO. CI, , launchd, kernel_task CoreSimulator. . . XCode 8.0 - , -, 160 . , .
simctl
simctl, XCode, — . , , launch screen. (. — watchdog). simctl , syslog' . , , :
- ;
- ;
- ;
- .
. MonkeyTalk , . . sqlite TCC.db, .
, , . . , .
, /. sqlite TCC.db, :
def self.setup_access(simulator_id, app, disabled, simulator_set_path: "#{ENV['HOME']}/Library/Developer/CoreSimulator/Devices") tcc_path = File.join(simulator_set_path, "#{simulator_id}/data/Library/TCC/TCC.db") Utilities::FileMonitoring.wait_for_file_creation(tcc_path, :max_wait => 30) { raise Simulators::Errors::SimulatorTimeoutError.new("Timed out while waiting for TCC!") } rights = disabled ? '0' : '1' ['kTCCServiceAddressBook', 'kTCCServicePhotos', 'kTCCServiceCalendar'].each do |access_target| `sqlite3 #{tcc_path} "replace into access(service, client, client_type, allowed, prompt_count) values ('#{access_target}','#{app.bundle_id}',0,#{rights},1)" 2>&1` end end
UI- . CI , , , , . . URLProtocol, . URLProtocol' FakeResponse. UI- FakeResponse , . . :
- ;
- ;
- .
Jira Hipchat UI- . , , Jenkins Job'. HTML- UI-, UI-, , UI- . ( UI- — //) :
- ;
- UI-;
- HTML- XML- UI-;
- data- ;
- appgroup- ;
- ;
- -, ;
- .
UI- debug-. HTML- json-, , N . Job' json- - , , .
HTML- Jenkins, Jira :
Jira, , HipChat:
HTML- :
— -, :
- UI-;
- ;
- ;
- ;
- ;
- runtime;
- .
HTML- . UI-. CI-.
BuildFlow — UI-. :
- UI- — 506;
- UI- — 9 20 ;
- UI- — 45—50 ;
- UI- — 66 ;
- UI- — 397 .
Prebuild, 5—6 , Gerrit. CI 506 UI- 55 .
短所
, XCode. , . — :
- MonkeyTalk , iOS 8.0;
- simctl springboard , XCode ( );
- XCode , , XCode 8.0, .
- , . .
メリット
- 9,33 UI- 60 ;
- ( , , );
- — . CI 50 — , , 50 50 iOS.
, , :
- , : ;
- UI-, XCUITest;
- , UI- , .
Same Targets
alpha- . , , iTunes.
: Alpha, Beta AppStore. Alpha Alpha-. , - . . (, ) , . CI . - , . . , , . Alpha-, . json. :
{ "compare-sets": [ { "targets": ["MRMail-Alpha-Enterprise", "MRMail-Pub-Beta-Enterprise", "MRMail-AppStore", "MRMail-MonkeyTalk"], "exclusive": { "MRMail-Alpha-Enterprise": [ "^pod-packages/Reveal-iOS-SDK/", "^src/infrastructure/MainThreadGuard/UIKitThreadGuard\\.m$",
, xcodeproj , .
ローカリゼーション
, . , . , , :
- NL (Not Localized): ;
- L (Localized): — , .
Build Phase, . . . - , CI, , Open.
Xcode — , , , . /. :
- ;
- action extension;
- share extension;
- today extension;
- shared framework.
, , . , , . , , , . — Ruby, :
- ;
- import , import .
— , . :
- Protected/Private- — ;
- — .
, . . — — , , , . :
{ "rules": [ { "name": "GeneralExternalImplementation", "type": "ExternalImplCheck", "imports": { "AccountEnvironmentImpl+Protected.h": "AccountEnvironmentImpl.m", …….
— AccountEnvironmentImpl+Protected.h AccountEnvironmentImpl.m.
CI- .
xcodeproj .
, UI- — . . UI- :
- Jira-;
- ;
- ( , , );
- / ( ).
UI-:
# tasks: IOSMAIL-6020 # name: # category: Compose # description: , «» , , , # .
, Jenkins Job, , Rake-. , , .
, HTML- , BuildFlow. 次のようになります。
scan-build
scan-build + xcodebuild. : . scan-build . , , , . — .
scan-build, , HTML-.
fbinfer
CI infer . — . 1 10 . . , . ́ . CI-, , , .
, CI, , . — , infer scan-build, .
alpha- Downstream Job, .
Job , .
250 . InfluxDB, Grafana. , , , , - : , ( , ).
単体テスト
Unit- — xcodebuild + xcpretty . HTML- xcpretty, , . Unit- — BuildFlow. 1451 Unit-, 10—11 .
HipChat
Jira + HipChat. , , .
, ,
— CI-. . . :
- ;
- ;
- ;
- HTML-;
- Jira, Gerrit HipChat.
, . CI — Unit-, UI-, , — 55—60 .
, , /. , . , , ( ): , CI, . Continuous Reporting , . , , , .
/
. :
- iOS-, ;
- , same targets.
, , , , . , , , , . .
́ , , . .