Discussion:
How to iterate through all modules for use with the new getUnitTests trait?
(too old to reply)
Gary Willoughby
2013-11-06 19:31:33 UTC
Permalink
As part of developing the DUnit framework i'm looking into
executing the unit test at a more fine grain level. The new
'getUnitTests' trait looks interesting but it's value is a symbol
of an aggregate (e.g. struct/class/module).

foreach (module_; ModuleInfo)
{
if (module_)
{
foreach (unitTest; __traits(getUnitTests, module_.name))
{
...
}
}

}

Obviously the 'module_.name' property doesn't work in this
scenario so i wondered how can i iterate through the all of the
compiling modules to make this code work?
Dicebot
2013-11-06 19:56:17 UTC
Permalink
module aaa;

import std.string;

template Alias(alias S)
{
alias Alias = S;
}

void main()
{
import std.string;

foreach (symbol_name; __traits(allMembers, aaa))
{
alias symbol = Alias!(__traits(getMember, aaa, symbol_name));
static if (symbol.stringof.startsWith("module "))
{
}
else static if (symbol.stringof.startsWith("package "))
{
// recursion
}
}
}
Gary Willoughby
2013-11-06 21:07:46 UTC
Permalink
Post by Dicebot
module aaa;
import std.string;
template Alias(alias S)
{
alias Alias = S;
}
void main()
{
import std.string;
foreach (symbol_name; __traits(allMembers, aaa))
{
alias symbol = Alias!(__traits(getMember, aaa, symbol_name));
static if (symbol.stringof.startsWith("module "))
{
}
else static if (symbol.stringof.startsWith("package "))
{
// recursion
}
}
}
Unfortunately this still suffers the same problem in that you
need a module symbol name to do anything. I need to get all
module symbols at compile time.
Dicebot
2013-11-06 21:26:07 UTC
Permalink
On Wednesday, 6 November 2013 at 21:07:47 UTC, Gary Willoughby
Post by Gary Willoughby
Unfortunately this still suffers the same problem in that you
need a module symbol name to do anything. I need to get all
module symbols at compile time.
You need only symbol name of your root compiled module which
imports all others. All imported ones will be available in its
member list, exactly what this snippet shows.
Daniel Davidson
2013-11-06 21:36:50 UTC
Permalink
Post by Dicebot
On Wednesday, 6 November 2013 at 21:07:47 UTC, Gary Willoughby
Post by Gary Willoughby
Unfortunately this still suffers the same problem in that you
need a module symbol name to do anything. I need to get all
module symbols at compile time.
You need only symbol name of your root compiled module which
imports all others. All imported ones will be available in its
member list, exactly what this snippet shows.
I think what OP wants to do is not as straightforward. I believe
the goal is to have a module somewhere "out there" that looks at
all other modules in the executable. I don't think it works that
way, as how could a compile time test module know about modules
that have pulled it in?

Have a look at
(https://github.com/patefacio/d-help/blob/master/d-help/opmix/ut.d)
where I used dicebot's previous example code to find all
unittests of the *current module*. The trick is to mixin code
that examines the current __MODULE__ for your tests. This may not
be the best way but it is working well for me.

The thread was `selectively running unittest functions`
http://forum.dlang.org/post/***@forum.dlang.org
Dicebot
2013-11-06 21:48:53 UTC
Permalink
On Wednesday, 6 November 2013 at 21:36:52 UTC, Daniel Davidson
I don't think it works that way, as how could a compile time
test module know about modules that have pulled it in?
Exactly. __traits work during compile-time. If module is not
imported, it is not known during compile-time -> no trait
unittest access possible. In lot of programs it will be
accessible recursively via some indirect import though.
Gary Willoughby
2013-11-06 22:33:46 UTC
Permalink
foreach (module_; ModuleInfo)
{
auto func = module_.unitTest;
func(); // run tests;
}

The above code retrieves all of the current project's modules and
then grabs each module's unit test blocks. The only trouble is
that the module's unit tests are kinda rolled into one function
as show by the 'func' variable above. It would be nice to do this
but get each individual unit test instead of dealing with these
rolled up versions.
Dicebot
2013-11-06 22:37:59 UTC
Permalink
On Wednesday, 6 November 2013 at 22:33:48 UTC, Gary Willoughby
Post by Gary Willoughby
The above code retrieves all of the current project's modules
and then grabs each module's unit test blocks. The only trouble
is that the module's unit tests are kinda rolled into one
function as show by the 'func' variable above. It would be nice
to do this but get each individual unit test instead of dealing
with these rolled up versions.
It is done via runtime reflection and completely different from
__traits approach. You will also 100% loose all User Defined
Attributes attached to test blocks because of their compile-time
nature. This is IMHO why it was somewhat abandoned as less
promising approach.
Jacob Carlborg
2013-11-07 08:38:01 UTC
Permalink
Post by Gary Willoughby
foreach (module_; ModuleInfo)
{
auto func = module_.unitTest;
func(); // run tests;
}
The above code retrieves all of the current project's modules and then
grabs each module's unit test blocks. The only trouble is that the
module's unit tests are kinda rolled into one function as show by the
'func' variable above. It would be nice to do this but get each
individual unit test instead of dealing with these rolled up versions.
See my reply to Dicebot:
http://forum.dlang.org/thread/***@forum.dlang.org#post-l5fjfe:242nvb:241:40digitalmars.com
--
/Jacob Carlborg
Jacob Carlborg
2013-11-07 08:37:02 UTC
Permalink
You need only symbol name of your root compiled module which imports all
others. All imported ones will be available in its member list, exactly
what this snippet shows.
That is the problem. One needs to import all other modules. That's not a
good solution when creating a unit test framework. One would basically
have to scan a directory for all D files. Then generate a new file that
imports all these files, with a main function that runs all tests.

Alternatively have something like RTInfo but for modules:

Bugzilla: https://d.puremagic.com/issues/show_bug.cgi?id=10023
Pull request DMD: https://github.com/D-Programming-Language/dmd/pull/2271
Pull request druntime:
https://github.com/D-Programming-Language/druntime/pull/534

Unfortunately my solution has the same problem as RTInfo, one needs to
modify object.d. I had an idea that would solve this both for RTInfo and
the new RMInfo, but that would most likely require building an
associative array at runtime. That was not liked. See the comments in
the pull requests and this:

https://d.puremagic.com/issues/show_bug.cgi?id=10023#c3
--
/Jacob Carlborg
Sönke Ludwig
2013-11-07 10:04:52 UTC
Permalink
Post by Jacob Carlborg
You need only symbol name of your root compiled module which imports all
others. All imported ones will be available in its member list, exactly
what this snippet shows.
That is the problem. One needs to import all other modules. That's not a
good solution when creating a unit test framework. One would basically
have to scan a directory for all D files. Then generate a new file that
imports all these files, with a main function that runs all tests.
Bugzilla: https://d.puremagic.com/issues/show_bug.cgi?id=10023
Pull request DMD: https://github.com/D-Programming-Language/dmd/pull/2271
https://github.com/D-Programming-Language/druntime/pull/534
Unfortunately my solution has the same problem as RTInfo, one needs to
modify object.d. I had an idea that would solve this both for RTInfo and
the new RMInfo, but that would most likely require building an
associative array at runtime. That was not liked. See the comments in
https://d.puremagic.com/issues/show_bug.cgi?id=10023#c3
Can't you do something like this? Or is the unit test framework supposed
to provide its own "root"/main module?

---
module unittestframework;

void runTests(string root_module = __MODULE__)()
{
// start recursion from root_module
}
---

---
module appmain;
import unittestframework;

void main()
{
runTests();
}
---
Dicebot
2013-11-07 12:07:38 UTC
Permalink
Post by Sönke Ludwig
Can't you do something like this? Or is the unit test framework
supposed to provide its own "root"/main module?
...
void runTests(string root_module = __MODULE__)()
{
// start recursion from root_module
}
---
Only issue is .di file usage - you can't access implementation
declarations when importing those via compile-time reflection.
Other than that I don't see any possible failure sources.
Dicebot
2013-11-07 12:10:40 UTC
Permalink
Post by Dicebot
Only issue is .di file usage - you can't access implementation
declarations when importing those via compile-time reflection.
Other than that I don't see any possible failure sources.
As there is also an issue of having modules "a" and "b" both
importing "c" (and being binary coupled via it) and not aware of
each other. Probably not a typical architecture but possible.
Dicebot
2013-11-07 12:22:21 UTC
Permalink
On Thursday, 7 November 2013 at 08:37:02 UTC, Jacob Carlborg
Post by Jacob Carlborg
That is the problem. One needs to import all other modules.
That's not a good solution when creating a unit test framework.
One would basically have to scan a directory for all D files.
Then generate a new file that imports all these files, with a
main function that runs all tests.
I don't see this much an issue as expect good testing framework
to be coupled with a build system anyway. Also in really _lot_ of
programs simply adding mixin to your `app.d` / `main.d` is enough
as everything else is transitively imported from there.

But in general yes, this an inherent flaw of compile-time
reflection.
Post by Jacob Carlborg
Bugzilla: https://d.puremagic.com/issues/show_bug.cgi?id=10023
https://github.com/D-Programming-Language/dmd/pull/2271
https://github.com/D-Programming-Language/druntime/pull/534
I kind of both like it and hate it. Improving runtime reflection
power is useful but it implies using special RT-tied UDA's and
won't be able to use same declarations as compile-time version.
Whatever solution is chosen for RT approach, I'd love to see it
implicit and compatible with CT one.
Jacob Carlborg
2013-11-07 15:03:40 UTC
Permalink
I don't see this much an issue as expect good testing framework to be
coupled with a build system anyway. Also in really _lot_ of programs
simply adding mixin to your `app.d` / `main.d` is enough as everything
else is transitively imported from there.
That doesn't work with libraries. You can have a library consisting of
two separate files that doesn't import each other.

I also prefer to put my tests in its own files, in a separate directory.
They are not imported by any other file.
--
/Jacob Carlborg
Dicebot
2013-11-07 15:05:21 UTC
Permalink
On Thursday, 7 November 2013 at 15:03:40 UTC, Jacob Carlborg
Post by Jacob Carlborg
That doesn't work with libraries. You can have a library
consisting of two separate files that doesn't import each other.
I also prefer to put my tests in its own files, in a separate
directory. They are not imported by any other file.
->
Post by Jacob Carlborg
Post by Dicebot
I don't see this much an issue as expect good testing
framework to be
coupled with a build system anyway.
Loading...