Test Harness で VM をインスタンス化しスタックトレースのテスト

WEwLC で学んだ通りに進める。Test Harness で VM クラスのインスタンス化を試みる。クラス自体が少し大きい。
クラス自体ではなくまずはファイルを分割する。そうすればすくなくとも Build Dependency は減らす事が出来るし平均ビルド時間の向上もするだろう。Run-loop, Profiler, その他と分割。


よし VM インスタンス化できた。VMインスタンス化はテストに共通する作業なので Google Test の "test fixture" 機能を利用する。
theVM がグローバルなのは良くない。今はしようがないがいずれ改善しよう。Test クラスを継承し VMTest を用意。

using namespace scheme;

VM* theVM;

class VMTest : public testing::Test {
protected:
    virtual void SetUp() {
        Transcoder* transcoder = new Transcoder(new UTF8Codec, Transcoder::LF, Transcoder::IGNORE_ERROR);
        Object inPort    = Object::makeTextualInputPort(new FileBinaryInputPort(stdin), transcoder);
        Object outPort   = Object::makeTextualOutputPort(new FileBinaryOutputPort(stdout), transcoder);
        Object errorPort = Object::makeTextualOutputPort(new FileBinaryOutputPort(stderr), transcoder);
        theVM = new VM(10000, outPort, errorPort, inPort, false /* isProfiler */);
    }
};

TEST_F(VMTest, Ref) {
    Vector* v = new Vector(2, Object::Undef);
    EXPECT_TRUE(v->ref(0).isUndef());
    EXPECT_TRUE(v->ref(1).isUndef());
}

追加したいのはスタックトレースのテスト。コードを eval してスタックトレースを文字列を取り出して expected な結果を比較すれば良かろう。
まずは R5RS 相当のバックエンドのスタックトレース。動かない事は分かりきっているがテストコードを書いてみる。

TEST_F(VMTest, StackTrace) {
    theVM->loadFile(UC("./work.scm"));
}

問題点は3つ

  • 外部ファイルを読んでしまうものは WEwLC 的には Unit Test とは言わない。
  • このファイルを実行すると loadFile は最終的に exit してしまう。
  • スタックトレースが文字列として取り出せない。

(1)このファイルを実行すると load は最終的に exit してしまう

loadFile を virtual にして TestingVM クラスで load を override する。

class TestingVM : public VM
{
public:
    TestingVM(int stackSize, Object outPort, Object errorPort, Object inputPort, bool isProfiler = false) :
        VM(stackSize, outPort, errorPort, inputPort, isProfiler)
    {
    }

    virtual ~TestingVM()
    {
    }

    virtual void load(const ucs4string& file);
};

と思ったが、これは最善の策ではない。exit を override する方が良い。
これで exit しなくなりました。

(2)スタックトレースが文字列として取り出せない。

エラーの内容は errorObj_ にあるのでこれを取り出せば良い。EXPECT_STREQ には c_str を渡さなければいけないので変換して渡す。
まずは失敗するテストにしてみよう。

theVM->loadFile(UC("./work.scm"));
EXPECT_STREQ("", theVM->getLastError().toString()->data().ascii_c_str());
TestVector.cpp:65: Failure
Value of: theVM->getLastError().toString()->data().ascii_c_str()
  Actual: "    error in raise: unhandled exception has occurred

 Condition components:
    1. &assertion
    2. &who: display
    3. &message: "textual-output-port required, but got 3"
    4. &irritants: ()


 Stack trace:
    1. throw: <subr>
    2. sys-display: <subr>
    3. (a):  ./work.scm:7
    4. (b):  ./work.scm:12
    5. (<top-level>): <unknown location>

"
Expected: ""

失敗した。expected を挿入してもう一度やるとうまくいった。

(3)外部ファイルを読んでしまうものは WEwLC 的には Unit Test とは言わない。

考え方は2つ。

  • VM がファイルを読み込んだ際のスタックトレースだから外部ファイルはあり
  • テストしているのは object の eval 結果なのでうまくやれば分離できる

今回は前者という事にする。