無限ループがある関数に対して単体テストを実行することは、アサーションをチェックする場合に問題を引き起こします。なぜなら、テスト ケースがアサーションを検証するには、テスト対象関数が終了する必要があるからです。ただし場合によっては、無限ループがある関数にテスト ケースを実装できます。それには、無限ループのコード実行を中断できる特別なマクロと関数を使用します。
名前 | 説明 |
---|---|
CPPTEST_REGISTER_JMP (expression) | setjmp または sigsetjmp を使って内部的なジャンプ バッファーを設定し、渡される式を評価します。CPPTEST_JMP API の呼び出しを使ってバッファーにジャンプすることができます。 通常このマクロはテスト ケースの内部で使用され、テスト対象関数への呼び出しをラップします (下記の例を参照)。 |
CPPTEST_JMP (value) | longjmp の呼び出しを実行します (longjmp または siglongjmp が使用されます)。実行ステータスを最新の CPPTEST_REGISTER_JMP の呼び出しに戻します。 通常このマクロはスタブの中で使用され、整数の引数を受け取ります。この引数は cpptestGetJmpReturn 関数によってテスト ケース内で返却されます。引数を使って、実行されるジャンプの正しさを検証できます (下記の例を参照)。 |
int CDECL_CALL cpptestGetJmpReturn(); | 最新の CPPTEST_JMP の呼び出しの戻り値、つまり longjmp/siglongjmp の引数を返します。これは setmp/sigsetjmp からの戻り値です。 |
これらのマクロは、組み合わせて使用することで、ユーザーがテスト対象コードの実行を (たとえばスタブの中で) 中断し、テスト ケースに戻ってアサーションのチェックを実行できるようにすることを目的とします。これらのマクロは、標準的な setjmp.h で宣言される setjmp および longjmp の呼び出しと同様に動作します。
この機能は、無限ループを含む、イベント処理のための埋め込まれた関数をテストするのに役立ちます。CPPTEST_JMP の呼び出しによって、テスト コードの実行が中断されます。CPPTEST_JMP への呼び出しを挿入するために推奨する方法は、スタブ本体を使用する方法です。
次のサンプル コードは、これらのマクロと関数の最も一般的な使い方を表しています。
/* テスト対象の関数 */ void signalData(void) { while(1) { // FrequencySensor から返される頻度を取得 if (scanFrequencySensor (returningFrequency) == FAILURE) { // エラーの場合、セマフォを与えず、 // エラーをレポートし、リカバリを呼び出して再試行する log_error("The sensor has encountered an error, retrying...\n"); recover(); } else { semGive(dataSemId); taskDelay(DELAY_TICKS); } } } /* テスト ケー */ /* CPPTEST_TEST_CASE_BEGIN test_signalData */ /* CPPTEST_TEST_CASE_CONTEXT void signalData(void) */ void TestSuite_main_c_883563c3_test_signalData() { /* 事前条件の初期化 */ /* グローバル変数 returningFrequency の初期化 */ returningFrequency = 0; /* テスト対象関数の呼び出し */ CPPTEST_REGISTER_JMP(signalData()) /* 事後条件のチェック */ CPPTEST_POST_CONDITION_INTEGER("cpptestGetJmpReturn()=", ( cpptestGetJmpReturn() )); CPPTEST_ASSERT_INTEGER_EQUAL(0, ( returningFrequency )); } /* CPPTEST_TEST_CASE_END test_signalData */ /* Stub */ /** 関数のユーザー スタブ定義: int scanFrequencySensor(int) */ EXTERN_C_LINKAGE int scanFrequencySensor (int returningValue) ; EXTERN_C_LINKAGE int CppTest_Stub_scanFrequencySensor (int returningValue) { static unsigned int counter = 0; unsigned int ret = 0; counter++; //センサーを 5 回スキャンするたびに失敗をシミュレーション if (counter == 5) { CPPTEST_REPORT("Simulating sensor scan failure") ret = FAILURE; } else if (counter == 10) { CPPTEST_REPORT("Testing accomplished - going back to test case for assertions check") CPPTEST_JMP(counter) } else { ret = scanFrequencySensor(returningValue); } return ret; }
TestSuite_main_c_883563c3_test_signalData テスト ケースを実行する場合、C++test はテスト対象関数を呼び出す前にジャンプ バッファーを保存します。テスト対象関数を実行すると、テスト対象関数は CppTest_Stub_scanFrequencySensor スタブから CPPTEST_JMP の呼び出しを実行します。これは、アサーションのチェックを実行するために、テストケースにコードの実行を移します。