DATE : 2006/11/26 (Sun)
BREW では機種によって CPU が異なるため、エンディアンが固定ではありません。そのため、バイナリデータを機種に依存せずに扱うには、エンディアンを機種に合わせて内部で変換しなければなりません。
機種のエンディアンに変換するには、以下のマクロ関数を使用します。(以下は BREW 3.1.2 API リファレンスから抜粋したものです)
- uint16 NTOHS(uint16 us)
- unsigned long NTOHL(unsigned long ul)
「N」はネットワークバイトオーダを表し、「H」は機種(ホスト)のエンディアンを表します。つまり「N」TO「H」とは、ネットワークバイトオーダから機種のエンディアンに変換する関数であることを表しています。
なお、ネットワークバイトオーダはビッグエンディアンとされています。そのため、バイナリデータをビッグエンディアンとしておけば、これらの関数を介すことで機種のエンディアンへの依存を防ぐことができます。
ところで、リファレンスを見るかぎりでは、NTOHS の扱うビット数は16ビットと定義されていますが、NTOHL は定義されていないように見えます。
しかし、NTOHL の定義(AEEStdLib.h)を覗いてみると、uint32 と定義されていました。
そのため、以下のように見なしても問題ないと思います。
- uint16 NTOHS(uint16 us)
- uint32 NTOHL(uint32 ul)
(逆に機種のエンディアンからネットワークバイトオーダに変換するマクロ関数もありますが、この記事では触れません)
DATE : 2006/11/06 (Mon)
「経緯度を整数値で扱う」では、TIA/EIA IS-801規格(PDF)を使用して経緯度を整数で表現しました。
進行方向(0度 ≦ 方向 < 360度)についても、TIA/EIA IS-801 規格を利用すると整数で表現できます。
再び拾い読みしたところ、360/2^10単位で表すことで10ビット幅で表現できるようです。
経緯度の場合と同様、BREW で取得できる形式と同じなので、進行方向もこの形式で格納しておくと便利です。
方位を TIA/EIA IS-801 形式に変換するには、進行方向に2を乗じます。
(TIA/EIA IS-801形式) = (進行方向) × 2
方位は度単位で、結果に小数があれば切り捨てます。
なお、「2」という数字は、360/2^10 の逆数から小数を切り捨てたものです。
ちなみにこの場合、TIA/EIA IS-801形式の1は0.5度に相当します。
すると、0.5度未満の進行方向は表現できないということになります。
DATE : 2006/11/04 (Sat)
(Brew Test Unit 1.0.4 対象)
想定した浮動小数点数が得られたかどうかを確認するマクロ関数に、ASSERT_EQUALS_DELTA というものがあります。(参照:テストケースの基本)
このマクロ関数では、想定値と実際の値、誤差を指定できます。
一見すると、実際の値と想定値との差が誤差の範囲内に収まっているか否かを判定するマクロ関数のように思えます。
しかし、Brew Test Unit 1.0.4 では、誤差に等しいか否かの判定になっています。
以下に示すのは、その判定部分(UnitAssert.cpp, 110~118、インデントはレイアウトにあわせて変更)のコードです。
「aExpected」は想定値、「aActual」が実際の値、「aDelta」が誤差を表します。
bool Assert::assertEquals(String aMessage, double aExpected, double aActual, double aDelta, String aFile, int aLine) { // Do comparison. if((aExpected <= aActual && aActual - aExpected == aDelta) || (aExpected > aActual && aExpected - aActual == aDelta)) { // Return True if equal. return true; }
誤差を判断している部分が、「==」となっています。そのため、この部分では、実際の値と想定値との差が誤差と等しいか否かを判断していると分かります。
参考として、JUnit で同じ機能を持つメソッド org.junit.Assert#assertEquals(String, double, double, double) のコードを挙げておきます。(Assert.java, 159~164、インデントはレイアウトに合わせて変更)
「expected」が想定値、「actual」が実際の値、「delta」が誤差を表します。
static public void assertEquals(String message, double expected, double actual, double delta) { if (Double.compare(expected, actual) == 0) return; if (!(Math.abs(expected - actual) <= delta)) failNotEquals(message, new Double(expected), new Double(actual)); }
こちらは、想定値と実際の値との差が誤差以内に収まるかどうかを判断していることが分かります。
なお、Brew Test Unit の ASSERT_EQUALS_DELTA も 誤差の範囲内に収まるような判断に変更する場合は、以下のようにコードを書き換えます。
if((aExpected <= aActual && aActual - aExpected <= aDelta) || (aExpected > aActual && aExpected - aActual <= aDelta)) { // Return True if equal. return true; }
気付くまで相当苦しめられました……orz
DATE : 2006/11/03 (Fri)
(前回の記事)
テスト用アプリケーションの記述
ユニットテストを実行するアプリケーションを記述します。
テスト用のアプリケーションは、Brew Unit Test に付属するサンプルコードを流用すると、簡単に記述できます。
サンプルコードは、「src\platforms\brew」にある brewunit.cpp です。
以下に、 brewunit.cpp の一部を転載します。(インデントなどは、記事に合わせて変更してあります)
#include "stdafx.h" // include base files for BrewTestUnit library #include "UnitTestCase.h" #include "UnitTestResult.h" #include "UnitTestSuite.h" // add brew-specific listeners support #include "stdlistener.h" #include "filelistener.h" #include "cppunit.bid" // applet class ID // add multiplatform examples #include "../examples/testcase1.h" #include "../examples/testcase2.h" static bool CppUnit_HandleEvent(IApplet * pi, AEEEvent eCode, uint16 wParam, uint32 dwParam); int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell, IModule * po,void ** ppObj) { *ppObj = NULL; if(ClsId == AEECLSID_CPPUNIT) { if(AEEApplet_New(sizeof(AEEApplet), ClsId, pIShell, po,(IApplet**)ppObj, (AEEHANDLER)CppUnit_HandleEvent,NULL) == TRUE) { return AEE_SUCCESS; } } return EFAILED; } static bool CppUnit_HandleEvent(IApplet * pi, AEEEvent eCode, uint16 wParam, uint32 dwParam) { AEEApplet * pMe = (AEEApplet*)pi; switch (eCode) { case EVT_APP_START: AECHAR * forDraw; { CppUnit::TestResult tr; // accumulate test's statistics // create and register some listeners StandardListener stdListener; FileListener fileListener; tr.addListener(stdListener); tr.addListener(fileListener); // create main test suite with some test cases // (or test suites) and run its MyTestCase1 tc1(_T("check account")); MyTestCase2 tc2(_T("security test")); CppUnit::TestSuite ts; ts.addTestCase(tc1); ts.addTestCase(tc2); ts.run(tr); // we can use TestResult in direct way // (instead of use listeners) if (tr.failureCount() == 0) forDraw = L"Tests OK"; else forDraw = L"Tests FAILED"; } // Display result string on screen IDISPLAY_DrawText(pMe->m_pIDisplay, AEE_FONT_BOLD, forDraw, -1, 0, 0, 0, IDF_ALIGN_CENTER | IDF_ALIGN_MIDDLE); IDISPLAY_Update (pMe->m_pIDisplay); return true; case EVT_APP_STOP: return true; default: break; } return false; }
まず次の部分をテスト用アプリケーションのために生成した bid ファイルに変更します。
#include "cppunit.bid" // applet class ID
続いて以下の部分を、作成したテストケースのヘッダファイルを取り込むように変更します。
// add multiplatform examples #include "../examples/testcase1.h" #include "../examples/testcase2.h"
最後に、テストケースを生成してテストスイートに登録します。変更する部分は、次の通りです。
// create main test suite with some test cases // (or test suites) and run its MyTestCase1 tc1(_T("check account")); MyTestCase2 tc2(_T("security test")); CppUnit::TestSuite ts; ts.addTestCase(tc1); ts.addTestCase(tc2); ts.run(tr);
例えば、「SampleTest」(SampleTest.h)というテストケースを登録する場合は、次のようになります。
#include "SampleTest.h"
// create main test suite with some test cases // (or test suites) and run its SampleTest sampleTest; CppUnit::TestSuite ts; ts.addTestCase(sampleTest); ts.run(tr);
テストケースの分だけ、テストケースの取り込み、テストケースの生成、テストスイートに登録という作業を繰り返すことになります。
以上で、ユニットテストを実行するアプリケーションができました。あとは、このアプリケーションをビルドして BREW シミュレータで実行するだけです。
なお、シミュレータの「BREW 出力ウィンドウ」とファイルにテストの結果が出力されます。
(了)