2016-11-26 12 views
0

モジュール内のすべてのテストを実行する独自のmoduleUnitTesterを作成しようとしています。デフォルトの単位テスターはthisのように動作します:Dが1つでも失敗しても、モジュール内のすべてのテストを実行します

size_t failed = 0; 
foreach (m; ModuleInfo) { 
    if (m) { 
     auto fp = m.unitTest; 
     if (fp) { 
      try { 
       fp(); 
      } 
      catch (Throwable e) { 
       writeln(e); 
       failed++; 
      } 
     } 
    } 
} 

FP()最初に失敗した場合にスローされます。 m.unitTestは、モジュール内のすべての単体テストを実行する関数であるvoid関数を返します。これらの単体テストをリストし、それぞれを繰り返し処理する方法はありますか?これはうまくいきません。

foreach (m; ModuleInfo) 
{ 
    __traits(getUnitTests, m); 
} 

これは、すべての単体テストを取得し、それらを自由に反復することができます。 'm'はモジュールではない変数です。実際に私はこれを間違ってしか見つけられませんでした。実際に私はこれを誤って見つけました...

+0

私はちょうどそれを入力するにはあまりにも怠惰な答えを知っている...だから、短いバージョンです:ModuleInfoモジュールではない、モジュールを記述するランタイムオブジェクトです。それはテストを含む関数ポインタである 'unitTest'(Testの大文字Tに注意してください)メンバを持っていますが、すべてのモジュールテストを一つの関数として含んでいます。あなたはそのように壊すことはできません。 '__traits(getUnitTests)'はそれを分解しますが、コンパイル時のモジュールが必要です: '__traits(getUnitTests、mixin(__ MODULE __))'はあなたを始めますが、プロジェクト全体のインポートをトリックしています。タイプしたくない。 –

+0

:)。これは私が期待したものです。しかし、プロジェクトのすべてのモジュールに対して、どのように反復処理を行うことができますか?あるいは、どのようにしてすべてのモジュールのコンパイル時間リストを取得するのですか? –

+0

@ AdamD.Ruppeあなたはこれに似ていると思いますか?http://stackoverflow.com/questions/25555329/d-finding-all-functions-with-certain-attribute/25560800#25560800?これはプロジェクト全体からすべてのモジュールを抽出しますか? –

答えて

1

私はコメントに半分の答えを書いていたので、ここにもう少し書きますあまりにも。

ここにはいくつかのオプションがあります:コンパイル時間と実行時間。コンパイル時には、モジュールごとにUDAやその他の細分化された詳細にアクセスできますが、すべてのモジュールにアクセスできるということはありません。インポートグラフを表示しようとすることはできますが、ローカルインポートはこのメソッドでは使用できません。ビルドツールを使用してモジュールをリストすることはできますが、実際にその機能を持つビルドツールを実際に使用する必要があります(https://github.com/atilaneves/unit-threadedはダビングでこれを行うlibです)。

モジュールのマニュアルリストをテストランナーに渡すこともできますが、これはメンテナンス作業の中で最も優れていますが、柔軟性があります。

しかし、私はあなたが疑問に抱いているように、実行時にそれをやりたいと思います。どうやって?いくつかの低レベルのポインタのものを行うことによって!それは時々古いアセンブリ言語のハッカーであることを支払う:)

見よインラインコメントを持つコード:私は印刷ファイル+としてテストを、後続の行先に出デバッグシンボルを引っ張って残しておきます

module q.test; 

unittest { 
     assert(0, "Test 1 failed"); 
} 

unittest { 
     assert(true); 
} 

// module ctor to set our custom test runner instead of the default 
shared static this() { 
     import core.runtime; 
     Runtime.moduleUnitTester = &myhack; 
} 

bool myhack() { 

/* 

OK, so here's the situation. The compiler will take each unittest block 
and turn it into a function, then generate a function that calls each 
of these functions in turn. 

core.runtime does not give us access to the individual blocks... but DOES 
give us the unitTest property on each module compiled in (so we catch them 
all automatically, even with separate compilation, unlike with the CT 
reflection cases) which is a pointer to the auto-generated function-calling 
function. 

The machine code for this looks something like this: 

0000000000000000 <_D1q4test9__modtestFZv>: 
    0: 55      push rbp 
    1: 48 8b ec    mov rbp,rsp 
    4: e8 00 00 00 00   call 9 <_D1q4test9__modtestFZv+0x9> 
    9: e8 00 00 00 00   call e <_D1q4test9__modtestFZv+0xe> 
    e: 5d      pop rbp 
    f: c3      ret 


The push and mov are setting up a stack frame, irrelevant here. It is the 
calls we want: they give us pointers to the individual functions. Let's dive in. 

*/ 

     bool overallSuccess = true; 

     foreach(mod; ModuleInfo) { 
       // ModuleInfo is a runtime object that gives info about each 
       // module. One of those is the unitTest property, a pointer 
       // to the function described above. 
       if(mod.unitTest) { 
         // we don't want a function, we want raw bytes! 
         // time to cast to void* and start machine code 
         // hacking. 
         void* code = mod.unitTest(); 
         version(X86_64) { 
           code += 4; // skip function prolog, that push/mov stuff. 
         } else version(X86) { 
           code += 3; // a bit shorter on 32 bit 
         } else static assert(0); 

         // Opcode 0xe8 is the 32-bit relative call, 
         // as long as we see those calls, keep working. 
         while(* cast(ubyte*) code == 0xe8) { 
           code++; // skip the opcode... 
           // ...which lands us on the relative offset, a 32 bit value 
           // (yes, it is 32 bit even on a 64 bit build.) 
           auto relative = *(cast(int*) code); 

           // the actual address is the next instruction add + the value, 
           // so code+4 is address of next instruction, then + relative gets 
           // us the actual function address. 
           void* address = (code + 4) + relative; 
           auto func = cast(void function()) address; 

           // and run it, in a try/catch so we can handle failures. 
           try { 
             func(); 
             import std.stdio; 
             writeln("**Test Block Success**"); 
           } catch(Throwable t) { 
             import std.stdio; 
             writeln("**Failure: ", t.file, ":", t.line, " ", t.msg); 
             overallSuccess = false; 
           } 

           // move to the next instruction 
           code += 4; 
         } 
       } 
     } 

     // returning false means main is never run. When doing a 
     // unit test build, a lot of us feel running main is just 
     // silly regardless of test passing, so I will always return 
     // false. 

     // You might want to do something like C exit(1) on failure instead 
     // so a script can detect that. 
     return false && overallSuccess; 
} 

必要に応じて、読者のための運動。

私はこの不潔なハックを役に立つと思っていますが、商品性や特定の目的への適合性を含め、いかなる種類の保証もありません。私はLinux上でDMDでテスト

が、それは他の場所に動作しない場合があります、GDC及びLDCは、同じ機能を生成する場合、私は知らない、またはその最適化は違いを作る場合など

Iと思います新しいテストランナーが本当に必要な場合は、コンパイル時のリフレクションと組み合わされたビルドツールや手動でメンテナンスされたモジュールリストのようなサポートされているテクニックを使用することをお勧めします:ユニットスレッドライブラリはこれを越えてたくさんの素晴らしいことを行います。

しかし、実行時のみのオプションが正確に行き止まりのいずれか:)

+0

まあ、私は正直言って、バイトで物事を期待していないと言っている。これを書いてくれてありがとう!しかし、私はdubとpreBuildCommandを使って2パスで行くことにしました。これはユニットスレッドと同じように、UDAを利用したいからです。 Dはあなたにモジュールを習得する能力を与えるようですが、コンパイル時のすべてのモジュールのリストはそれほど簡単ではありません。私はあなたの答えを受け入れています:)、私は後であなたに感謝して思いついた私の最も単純な解決策を投稿します。 –

+0

ええ、それは、すべてのモジュールのリストがコンパイル時にはしばしば不可能であるからです。 'lib_interface.d'を公開しているライブラリがあり、' lib_implementation.d'を内部的に使用していますが、 'lib.a'ファイルの後ろにコンパイルされています。コンパイラはインターフェイスが存在することを知っていますが、実装については考えていません。リンカだけがそれを認識します。これは、コンパイル時に基本クラスを取得できるのと同じ理由ですが、子クラスのリストは(最高でも!)を実行時に実行すると、サードパーティ製の動的ロードされたlibも元のソースとは独立して継承することができます。 –

0

ではありません、私はまあ、生憎私は何私を達成することができませんでした..私は私が思い付く私の最も簡単な解決策を投稿したいと述べました私の解決策が欲しかったので、結局私は行ってunit-threadedを使いました。私がやってみたかったアプローチがある: あなたは、あなたのコードにこのような何かをダブルパスを行うことができますダブ使用:

"configurations": [ 
    { "name": "executable" }, 
    { 
     "name": "unittest", 
     "targetType": "executable", 
     "preBuildCommands": ["dub test -c gen_ut_main"], 
     "sourceFiles": ["bin/generated_ut_main.d"], 
    }, 
    { 
     "name": "gen_ut_main", 
     "targetType": "executable", 
     "versions": ["GenUtMain"], 
    } 
] 

これが原因となり、それは最初のバージョンに設定してgen_ut_mainターゲットを実行します「テストダブ」 GenUtMain。これで、次のようなことができます:

generate_fileは、すべてのモジュールに関する情報を含む新しい '.d'ファイルを生成することができます。 'ModuleInfo'を使用すると、ModuleInfoがgenerate_file関数のランタイムオブジェクトを使用するため、またはユニットスレッドとしてプロジェクトのファイル構造を繰り返し処理し、すべての.dファイルを取得してパッケージリストを取得できます。 ModuleInfoには1つの注意点があります。あなたが望むものではない可能性のあるすべてのモジュールのリストを提供します。標準モジュールにユニットテストが組み込まれているため、ウェルを間違いなく選択する必要があります。これらをフィルタリングすることができ、ユニットスレッドアプローチを使用することに決めました。

ターゲットgen_ut_mainが完了すると、bin/generated_ut_main.dが再生成され、unittestターゲットによってコンパイルされます。このようにして、すべてのユニットテストをモジュール単位で実行して、自由にUDAをチェックし、モジュールにすべてのユニットテストを実行する能力があるかどうかをチェックすることができます。

おそらく、ダブと統合された「テスト済み」パッケージがあります。テストされたモジュールは、すべてのモジュールをリストする可能性があり、動作させることができませんでした。

+0

私はModuleInfoのすべてのモジュールのリストには、別々にコンパイルされたライブラリモジュールのunittestは含まれていないと思います( 'dmd -unittest'に特に渡されたモジュールのみが含まれているので、そのような方法でいくつかコンパイルすることができます。テンプレートなしでビルドされていますが、これはテンプレート埋め込みテストでは濁っているかもしれません。 –

関連する問題