Unity を検索

C++を欺き、擬態し、乗っ取る

2015年11月25日 カテゴリ: テクノロジー | 3 分 で読めます
取り上げているトピック
シェア

Is this article helpful for you?

Thank you for your feedback!

今日は私たちが現在試している、テストに適していない巨大なレガシーC++コードベースに対する新しいユニットテストのアプローチの現状を紹介します。

デベロッパーの皆さん、私たちはテストが好きです。テストについて沢山考えています。私たち自身が変更することについてもっと気楽に考えられるようになりたいのと、多くの…本当に多くの開発者の皆さんにとって今日Unityが安定していて信頼できることがとても重要だと認識しているからです。私たちはこの点について、今後も継続して改善していきたいのです。

以前、どのように私たちがC#からハイレベルなテストを行っているかの ブログ記事 を投稿しました。しかし、時が経つにつれて、私たちはもっともっと低レベルで、もっと高速で粒度の細かいテストが必要だと認識するに至りました。問題は…そう、問題は私たちのC++コードベースがそういうテストには向いていないということです。レガシーなコードが沢山入っている巨大なC++コードベースをテストするのは難題です。

しかし…なんとか、この問題をちょっとだけ簡単にすることができました。

まず、見渡してみても私たちが解決しなければならない問題をちゃんと解決している人は誰もいないという結論に至りました。TypeMock がやっていることが最も近い内容でしたが、Windowsのみのソリューションだったので私たちの用途には耐えませんでした。私たちは座って熟考し、何ができるかを検討しました。当然ですが、テストのためにコードベースを全部書き換える、なんてことはできません。どういう解決策を持ち込むとしても、現在私たちが抱えているコードと、私たちのC++コードの書き方でうまく動く必要があります。さぁ、どうしたものか。

そうだ、ハイジャックしよう…

この点から、私たちはテスト時には関数をハイジャックできるようにしたいのだということに気がつきました。つまり、乗っ取り、擬態し、欺き、代用し、そして制するのです。私たちのケースでは、関数Xが関数Yを直接呼ぶことについては、テストが関数Yの呼び出しをハイジャックして望むものに変更できる限りOKなのです。

そこでまず実行時にコードにパッチを当てることを思いつきました。じつはプロファイリングのために動的なコードパッチングのための便利なフレームワークをちょうど作ったところだったのです。ただ、問題は多くのプラットフォームでは実行時のコードパッチングは利用できないので、その方法は使えないことがわかりました。

… コンパイル時に …

そして私たちは考えました。「よろしい、ならばコンパイル時だ!」トリッキーなやり方です!しかし先駆者はおり、たとえば私たちがC++のコードカバレッジ・モニターに使っているBullseyeの人たちも実現しています。もしやるならば、満身の力をこめて事にあたる必要があります。特に私たちは、様々なプラットフォームにわたる多くの異なるコンパイラーに対して戦いを挑まなければなりません……うん、やめましょう。私たちのビルドはもうすでに十分複雑なのでした。これ以上いけない…この方法も使えません。

… マクロを使って

C++では、もう他にどうにもやりようがないときにも、まだマクロとテンプレートが残っています。わたしは座って、< キーと > キーをつかって、マクロをちょいとまぶしたテンプレートコードを書きました。

考え方は実は結構シンプルです。どんな関数にもフックできる仕掛けを作ったら、その上にテストが必要な関数をなんでも再プログラムするフレームワークをまるっと構築する…そんな感じです。

では、まず最初のステップ - フックです。フックはこんな感じのコードです:

Screen Shot 2015-11-24 at 11.02.19

シンプルでしょう?これはデフォルトでは何もしませんし、コンパイルしても出荷するコードには何も生成されませんが、テストがフックを掴むと魔法が発動します。この方法は多少の割り込みが入りますが、大きな影響は与えません。

次のステップは、フックをテスト時に有効にする部分です:

Screen Shot 2015-11-24 at 11.02.37

これはフックを関数のシグネチャで検索し、偽関数がスコープ内にいるかぎりそのフックを掴みます。 

そして最後に、フックをプログラムします:

Screen Shot 2015-11-24 at 11.02.51

ここから、良く行うようなモックアップ処理…たとえば別に作った自分の関数にリダイレクトしたり、引数をチェックして値を返したり、元の関数をよりコントロールされた状況で呼んだりといったことが可能になります。通常の関数に加えてインスタンスのメソッドをサポートするのも簡単です。 実際、このシステムはクラス全体をリプレースすることすら簡単にしてくれます。

Screen Shot 2015-11-24 at 11.03.03

結論

この新しいアプローチが今後もたらすインパクトの全貌をまだしっかりと確認できた訳ではありませんが、すでに今まででは書けなかったテストが沢山作成出来ることがわかっています。私たちは機能テストへの注力を今後もやめることなく、Unityの堅牢性に寄与する、高速で網羅的で粒度の細かいネイティブテストスイートを追加していきたいと願っています。

2015年11月25日 カテゴリ: テクノロジー | 3 分 で読めます

Is this article helpful for you?

Thank you for your feedback!

取り上げているトピック