Bug 12269 – Unittest within template struct scope is not executed
Status
RESOLVED
Resolution
INVALID
Severity
normal
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-02-26T15:23:00Z
Last change time
2014-02-28T05:22:43Z
Assigned to
nobody
Creator
growlercab
Comments
Comment #0 by growlercab — 2014-02-26T15:23:01Z
using:
$ rdmd --main -unittest -debug utbug.d
(also tried with just dmd but same result)
---
import std.stdio;
struct S(T)
{
T val;
this(T v) {val=v;}
unittest { // << UT-1
writeln("Here 1!");
auto s = S!int(10);
assert(s.val == 1); // <<-- this should fail
s=writeln("abcd"); // <<-- this should not compile
}
}
// Uncomment to make the struct unittest run
/*
unittest { // << UT-2
writeln("Here 2");
auto s = S!int(10);
assert(s.val == 10);
}*/
---
If UT-2 is commented out the UT-1 unit test does not get compiled and executed.
Only occurs with template struct, non-template S works as expected.
I am running Arch Linux x86_64, dmd 2065. I have not tried to reproduce with other configurations.
Thanks,
ed
Comment #1 by growlercab — 2014-02-26T18:10:55Z
After some more testing I have some more information which may help.
Firstly, the problem is also present in DMD 2.064.
(I have no DMD older than this)
Secondly, I found that the unittest within struct scope is run only if S is instantiated in a module scope unit test.
For example the following incorrectly compiles and executes without an error:
$ rdmd --main -unittest -debug utbug.d
---
// utbug.d
struct S(T) {
unittest {
BUGME("should not compile"); // <<-- should break the build
}
}
unittest {
}
----
But instantiating S!int() in the module scope unit test works as expected:
$ rdmd --main -unittest -debug utbug.d
utbug.d(5): Error: undefined identifier BUGME
utbug.d(9): Error: template instance utbug.S!int error instantiating
---
// utbug.d
struct S(T) {
unittest {
BUGME("should not compile"); // <<-- Should break the build
}
}
unittest {
auto s = S!int();
}
----
Thanks,
ed
Comment #2 by andrej.mitrovich — 2014-02-27T04:49:47Z
It's by design. How would the unittest code know what to replace T with if you don't instantiate the struct?
Comment #3 by growlercab — 2014-02-27T18:12:29Z
Thanks for the clarification. As I understand it this is the situation:
Within a template unittests will be silently ignored by the compiler unless another unittest, outside the template scope, instantiates that template.
Is it really a good idea? To treat unittests like regular functions w.r.t templates regarding how/when they're compiled.
IMO all unittests should be lifted to module scope by the compiler, excepting static-if and version() blocks. This way there would be no unittests that are missed because a template was not instantiated.
This all came about because someone elsewhere in the code removed an one line that instantiated S. We were blissfully unaware that most of out S unittests were not actually being compiled, let alone executed. A simple mistake but it happens.
Thanks,
ed
Comment #4 by andrej.mitrovich — 2014-02-28T00:45:54Z
(In reply to comment #3)
> IMO all unittests should be lifted to module scope by the compiler, excepting
> static-if and version() blocks. This way there would be no unittests that are
> missed because a template was not instantiated.
I don't think you're following me here, if you have:
struct S(T)
{
T val;
}
It doesn't matter where you put the unittest, it cannot possibly know what to instantiate 'S' with. Here's an example of a unittest within a struct block that could be both compilable and non-compilable based on what S is instantiated with:
-----
struct S(T)
{
T val;
unittest
{
/// this will fail if 'T' is not int
static assert(is(T == int));
}
}
alias S_Int = S!int;
alias S_Float = S!float; // triggers compile-time failure
-----
Comment #5 by growlercab — 2014-02-28T04:21:45Z
struct S(T)
{
T val;
unittest
{
/// this will fail if 'T' is not int
static assert(is(T == int));
}
}
alias S_Int = S!int;
alias S_Float = S!float; // triggers compile-time failure
---
Yes, I understand this and I'd expect the compile failure.
I don't think it is a good idea to conditionally compile/execute unittests according to whether templates are instantiated, which is why I was saying they should not be treated as regular functions that are subject to template/struct/class scope.
Of course that may raise other issues I haven't thought of...I'm not a compiler/language developer by any means :)
Anyway, thanks for taking the time to look into this issue. It is only a minor problem due to my preferred unittest structure/workflow. I can work around it easily enough.
Cheers,
ed
Comment #6 by dlang-bugzilla — 2014-02-28T04:28:09Z
This is a clearly invalid issue.
The compiler can't execute the unit test because it can't POSSIBLY know what to instantiate the template with. It's like asking someone to verify that a mathematical equation is sound, but leaving half of the variables as blanks.
The compiler will generate a unit test for every unique instantiation of the template. This way, the unit test will effectively test every combination of parameters - out of those used in your program. There is no problem and no need to restructure any code, as long as you build your entire program with the -unittest switch, as opposed to separate modules that only contain template declarations without instantiating them.
Comment #7 by growlercab — 2014-02-28T05:21:40Z
The compiler can't execute the unit test because it can't POSSIBLY know what to
instantiate the template with. It's like asking someone to verify that a
mathematical equation is sound, but leaving half of the variables as blanks.
---
No, I'm arguing the unittest should not be part of the equation as they are now relying on template parameters (when in template scope).
A unittest should be forced to define all variables to test the equation with, irrespective of where it is defined.
But it is minor and I may not be seeing the bigger picture here so I'll drop it and move on :D
Thanks again,
ed
Comment #8 by dlang-bugzilla — 2014-02-28T05:22:43Z
(In reply to comment #7)
> A unittest should be forced to define all variables to test the equation with,
> irrespective of where it is defined.
But then that would break the current usage of unit tests within templates (to test each instantiation).