私はコメントに半分の答えを書いていたので、ここにもう少し書きますあまりにも。
ここにはいくつかのオプションがあります:コンパイル時間と実行時間。コンパイル時には、モジュールごとに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と思います新しいテストランナーが本当に必要な場合は、コンパイル時のリフレクションと組み合わされたビルドツールや手動でメンテナンスされたモジュールリストのようなサポートされているテクニックを使用することをお勧めします:ユニットスレッドライブラリはこれを越えてたくさんの素晴らしいことを行います。
しかし、実行時のみのオプションが正確に行き止まりのいずれか:)
私はちょうどそれを入力するにはあまりにも怠惰な答えを知っている...だから、短いバージョンです:ModuleInfoモジュールではない、モジュールを記述するランタイムオブジェクトです。それはテストを含む関数ポインタである 'unitTest'(Testの大文字Tに注意してください)メンバを持っていますが、すべてのモジュールテストを一つの関数として含んでいます。あなたはそのように壊すことはできません。 '__traits(getUnitTests)'はそれを分解しますが、コンパイル時のモジュールが必要です: '__traits(getUnitTests、mixin(__ MODULE __))'はあなたを始めますが、プロジェクト全体のインポートをトリックしています。タイプしたくない。 –
:)。これは私が期待したものです。しかし、プロジェクトのすべてのモジュールに対して、どのように反復処理を行うことができますか?あるいは、どのようにしてすべてのモジュールのコンパイル時間リストを取得するのですか? –
@ AdamD.Ruppeあなたはこれに似ていると思いますか?http://stackoverflow.com/questions/25555329/d-finding-all-functions-with-certain-attribute/25560800#25560800?これはプロジェクト全体からすべてのモジュールを抽出しますか? –