Thread overview
[Issue 12269] New: Unittest within template struct scope is not executed
Feb 27, 2014
Andrej Mitrovic
Feb 28, 2014
Andrej Mitrovic
Feb 28, 2014
Vladimir Panteleev
Feb 28, 2014
Vladimir Panteleev
February 26, 2014
https://d.puremagic.com/issues/show_bug.cgi?id=12269

           Summary: Unittest within template struct scope is not executed
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: DMD
        AssignedTo: nobody@puremagic.com
        ReportedBy: growlercab@gmail.com


--- Comment #0 from growlercab@gmail.com 2014-02-26 15:23:01 PST ---
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

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
February 27, 2014
https://d.puremagic.com/issues/show_bug.cgi?id=12269



--- Comment #1 from growlercab@gmail.com 2014-02-26 18:10:55 PST ---
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

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
February 27, 2014
https://d.puremagic.com/issues/show_bug.cgi?id=12269


Andrej Mitrovic <andrej.mitrovich@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |andrej.mitrovich@gmail.com


--- Comment #2 from Andrej Mitrovic <andrej.mitrovich@gmail.com> 2014-02-27 04:49:47 PST ---
It's by design. How would the unittest code know what to replace T with if you don't instantiate the struct?

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
February 28, 2014
https://d.puremagic.com/issues/show_bug.cgi?id=12269



--- Comment #3 from growlercab@gmail.com 2014-02-27 18:12:29 PST ---
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

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
February 28, 2014
https://d.puremagic.com/issues/show_bug.cgi?id=12269



--- Comment #4 from Andrej Mitrovic <andrej.mitrovich@gmail.com> 2014-02-28 00:45:54 PST ---
(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
-----

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
February 28, 2014
https://d.puremagic.com/issues/show_bug.cgi?id=12269



--- Comment #5 from growlercab@gmail.com 2014-02-28 04:21:45 PST ---
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

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
February 28, 2014
https://d.puremagic.com/issues/show_bug.cgi?id=12269


Vladimir Panteleev <thecybershadow@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
                 CC|                            |thecybershadow@gmail.com
         Resolution|                            |INVALID


--- Comment #6 from Vladimir Panteleev <thecybershadow@gmail.com> 2014-02-28 14:28:09 EET ---
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.

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
February 28, 2014
https://d.puremagic.com/issues/show_bug.cgi?id=12269



--- Comment #7 from growlercab@gmail.com 2014-02-28 05:21:40 PST ---
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

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
February 28, 2014
https://d.puremagic.com/issues/show_bug.cgi?id=12269



--- Comment #8 from Vladimir Panteleev <thecybershadow@gmail.com> 2014-02-28 15:22:43 EET ---
(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).

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------