ユーザー モードのモニター
ユーザー モード モニターを使用すると、"テスト中のプロセス" の実行に関する詳細なコンテキストを取得して、不合格になったテストを調査するための詳しいコンテキストを取得したり、既存のテストの検証精度を高めたりできます。 現在実装されているユーザー モード モニターは基本的な機能を搭載していますが、今後のリリースで、より豊富なカスタマイズと構成が可能になる予定です。
はじめに
ユーザー モード モニター (UMM) は、下位レベルの Windows API を使用して、特定のプロセス (ほんの一例を挙げるとスレッドの開始と停止、モジュールの読み込み、クラッシュ、処理された例外など) から発生したすべての "デバッガー" イベントの通知を受け取ります。 デバッガー イベントが届いたら、UMM コードを通じて、コメントのログ記録、エラーのログ記録 (テストを不合格にするため)、Process Under Test のミニダンプの取得など、いくつかのアクションのいずれかを実行できます。
ユーザー モード モニターを有効にする
特定のテスト ケースで UMM を有効にするには、構成に以下の 2 つの要素を含める必要があります。
- テストが "ProcessUnderTest" メタデータ値でマークされること。 これは、UMM がテスト対象のプロセスを識別できるようにするためです。
- UMM 機能をオンにするための Te.exe コマンド ラインに "/userModeMonitor" が含まれていること。
UMM コードを使用する場合は、以下のことを考慮する必要があります。
- "Process Under Test" という名前が付いたインスタンスが複数存在する場合は、最初に検出されたインスタンスが使用されます。
- テストの自動化を実行しているユーザーには、Process Under Test からデバッガー イベントを受信するための十分なアクセス許可が必要です。
- UMM コードは、すべてのセットアップ フィクスチャが実行された後で Process Under Test に "アタッチ" され、クリーンアップ フィクスチャが実行される前に "デタッチ" されます。 これにより、テストのセットアップ フィクスチャで Process Under Test を開始して、必要な初期化を実行してテストの準備を行えるようになります。
サポートされているユーザー モード モニターの "アクション"
ユーザー モード モニターには、監視対象のプロセスで特定のデバッガー イベントが発生したときに実行できる一連の "アクション" があります。 現在の実装では、特定のイベントで呼び出されるのは既定のアクションだけです。現時点で、構成のサポートはありません。
アクション | 説明 |
---|---|
LogComment | イベントからのコンテキスト情報を含むコメントをログに追加します。 |
LogError | エラーをログに記録し、現在のテストを不合格にします。 |
Minidump | ミニダンプを書き出してログに保存します。 |
Ignore | 何も実行しません。 |
サポートされるユーザー モード モニターの "イベント"
ユーザー モード モニターには、先ほどの一覧に掲載されている "アクション" の 1 つを適用できる "イベント" が表示されます。 次の表に、現時点で報告されるイベントと、イベントの受信時に実行される既定のアクションをまとめています。
イベント | 既定のアクション (2 回目の既定のアクション) |
---|---|
スレッドの作成 | Ignore |
スレッドの終了 | Ignore |
プロセスの作成 | Ignore |
プロセスの終了 | LogError |
モジュールの読み込み | LogComment |
モジュールのアンロード | Ignore |
システム エラー | Ignore |
初期ブレークポイント | LogError |
初期モジュールの読み込み | Ignore |
デバッグ対象の出力 | LogComment |
アクセス違反 | LogError (LogError) |
アサーション エラー | LogError (LogError) |
アプリケーションのハング | LogError (LogError) |
中断命令の例外 | LogError |
中断命令の例外の継続 | Ignore |
C++ EH の例外 | LogError (LogError) |
CLR 例外 | LogError (LogError) |
CLR 通知の例外 | LogError (Ignore) |
Control-LogError の例外 | LogError |
Control-LogError の例外の継続 | Ignore |
Control-C の例外 | LogError |
Control-C の例外の継続 | Ignore |
データの不整合 | LogError (LogError) |
デバッガー コマンドの例外 | Ignore |
ガード ページ違反 | LogError (LogError) |
無効な命令 | LogError (LogError) |
ページ内 I/O エラー | LogError (LogError) |
整数を 0 で除算 | LogError (LogError) |
整数オーバーフロー | LogError (LogError) |
ハンドルが無効です | LogError |
無効なハンドルの継続 | LogError |
無効なロック シーケンス | LogError (LogError) |
無効なシステム呼び出し | LogError (LogError) |
ポートの切断 | LogError (LogError) |
サービスのハング | LogError (LogError) |
単一ステップの例外 | LogError |
単一ステップの例外の継続 | Ignore |
スタックのバッファー オーバーフロー | LogError (LogError) |
スタック オーバーフロー | LogError (LogError) |
検証ツールの停止 | LogError (LogError) |
Visual C++ の例外 | Ignore (Ignore) |
デバッガーのスリープ解除 | LogError (LogError) |
WOW64 ブレークポイント | LogError (Ignore) |
WOW64 単一ステップの例外 | LogError (Ignore) |
その他の例外 | LogError (LogError) |
例
UMM の機能の使い方を紹介するために、"MSPaint" を自動化するテストの (少し強引な) 例を見てみましょう。
namespace UserModeMonitorExample
{
using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WEX.Logging.Interop;
using WEX.TestExecution;
[TestClass]
public class BasicExample
{
[TestInitialize]
public void TestInitialize()
{
Process[] runningPaintInstances = Process.GetProcessesByName("mspaint.exe");
Verify.IsTrue(runningPaintInstances.Length == 0, "There are no running instances of mspaint.exe");
this.mspaintUnderTest = Process.Start("mspaint.exe");
}
[TestCleanup]
public void TestCleanup()
{
// Close the 'mspaint under test' - if it's already gone, this will throw, but that's no big deal.
this.mspaintUnderTest.CloseMainWindow();
}
[TestMethod]
[TestProperty("ProcessUnderTest", "mspaint.exe")]
[TestProperty("Description", "Shows how a test can be failed if the UI is closed from underneath the test.")]
public void SimpleInteraction()
{
Log.Comment("If the 'user mode monitor' is enabled and mspaint.exe is closed,");
Log.Comment("then this test will be failed.");
Log.Comment("Sleeping for 5 seconds");
Thread.Sleep(TimeSpan.FromSeconds(5));
}
private Process mspaintUnderTest;
}
}
テストの構造を大まかに分解すると、以下のようになります。
- "SimpleInteraction" テストは、UI ベースのアプリケーション (この場合は "MSPaint.exe") とやり取りするテストを表します。 これが "mspaint.exe" プロセスのテストであることをコール アウトするために、"ProcessUnderTest" メタデータが適用されていることに注目してください。
- テストには、既存のインスタンスが実行されていないことを確認し、テストするインスタンスを 1 つ起動するためのセットアップ フィクスチャがあります。
- テストには、セットアップ フィクスチャで起動されたインスタンスを閉じるためのクリーンアップ フィクスチャもあります。
"テスト" はとても単純です。考えられる結果を以下に検討してみましょう。
- テストが問題なく実行される。 これが最良の結果です。
- UMM が有効になっておらず、ユーザーが実行中に MSPaint インスタンスを閉じる。 この場合、テストは合格になりますが、クリーンアップには失敗して "InvalidOperationException" が返されます。
- UMM が有効になっていて、ユーザーが実行中に MSPaint インスタンスを閉じる。 この場合、UMM コードによって、プロセスが終了してテストが不合格になったことを示すエラーがログに記録されます。 クリーンアップは、(2) の場合と同じく失敗します。
UMM が有効になっていると、誤った動作が直ちに報告され、テスト結果に直接影響します。 エラーが速やかに報告され、テスト不合格のデバッグや理解に役立つ追加のコンテキストが提供されるので、テストのパターンとしてはこれがはるかに優れています。