www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Lookahead in unittest

reply Raiderium <raiderium.neo gmail.com> writes:
Heyo,

On 2.074.0, the following test fails with "Error: undefined 
identifier 'B' "

unittest
{
	class A { B b; }
	class B { }
}

I can't figure out if this is intended behaviour. It's making a 
template-heavy module difficult to test. Would appreciate any 
help.

First post here, be gentle :)
May 10 2017
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 10 May 2017 at 16:09:06 UTC, Raiderium wrote:
 Heyo,

 On 2.074.0, the following test fails with "Error: undefined 
 identifier 'B' "

 unittest
 {
 	class A { B b; }
 	class B { }
 }

 I can't figure out if this is intended behaviour. It's making a 
 template-heavy module difficult to test. Would appreciate any 
 help.

 First post here, be gentle :)
It looks like this unitest-test block are treated like a function. What is the surrounding code ? If this is at module level then it is a bug.
May 10 2017
parent Jacob Carlborg <doob me.com> writes:
On 2017-05-10 18:17, Stefan Koch wrote:

 It looks like this unitest-test block are treated like a function.
unittest blocks are lowered to functions. -- /Jacob Carlborg
May 11 2017
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 10 May 2017 at 16:09:06 UTC, Raiderium wrote:
 I can't figure out if this is intended behaviour.
It is. A unittest is a function, and in functions, all declarations must be defined before used (just like local variables). Sometimes, you can wrap it in a struct: unittest { struct Decls { // put your decls here } with(Decls()) { // call funcs here } }
May 10 2017
parent reply Raiderium <raiderium.neo gmail.com> writes:
On Wednesday, 10 May 2017 at 16:32:11 UTC, Adam D. Ruppe wrote:
 On Wednesday, 10 May 2017 at 16:09:06 UTC, Raiderium wrote:
 I can't figure out if this is intended behaviour.
It is. A unittest is a function, and in functions, all declarations must be defined before used (just like local variables). Sometimes, you can wrap it in a struct: unittest { struct Decls { // put your decls here } with(Decls()) { // call funcs here } }
Ah. I wasn't aware class declarations within functions (including unittest) were sensitive to their order, so that's something I've learned today. :) I tried the with(Decls()) syntax and it worked perfectly, thanks Adam. I'd been haphazardly nesting unittest{} blocks within the struct, and it felt less than sanitary. For full disclosure, the test I'm writing needs to create a reference cycle (as in, class B holding a reference to A), and it works properly if the classes are declared at module/class/struct level, but then either the class names pollute the module (which is just eww) or they're nested within a class/struct, which leads me to the current situation. Consider my problem solved :) Thanks again Stefan and Adam for the replies.
May 10 2017
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/10/17 12:53 PM, Raiderium wrote:
 On Wednesday, 10 May 2017 at 16:32:11 UTC, Adam D. Ruppe wrote:
 On Wednesday, 10 May 2017 at 16:09:06 UTC, Raiderium wrote:
 I can't figure out if this is intended behaviour.
It is. A unittest is a function, and in functions, all declarations must be defined before used (just like local variables). Sometimes, you can wrap it in a struct: unittest { struct Decls { // put your decls here } with(Decls()) { // call funcs here } }
Ah. I wasn't aware class declarations within functions (including unittest) were sensitive to their order, so that's something I've learned today. :) I tried the with(Decls()) syntax and it worked perfectly, thanks Adam. I'd been haphazardly nesting unittest{} blocks within the struct, and it felt less than sanitary. For full disclosure, the test I'm writing needs to create a reference cycle (as in, class B holding a reference to A), and it works properly if the classes are declared at module/class/struct level, but then either the class names pollute the module (which is just eww) or they're nested within a class/struct, which leads me to the current situation. Consider my problem solved :) Thanks again Stefan and Adam for the replies.
Note, you can achieve what you want with version(unittest): version(unittest) { class A { B b; } class B { } } unittest { // use A and B here } -Steve
May 12 2017
next sibling parent pineapple <meapineapple gmail.com> writes:
On Friday, 12 May 2017 at 21:23:23 UTC, Steven Schveighoffer 
wrote:
 Note, you can achieve what you want with version(unittest):
Please prefer `private version(unittest){...}` if the module might be imported by someone else's code, as to not pollute it with unneeded symbols
May 12 2017
prev sibling next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Fri, May 12, 2017 at 05:23:23PM -0400, Steven Schveighoffer via
Digitalmars-d-learn wrote:
[...]
 Note, you can achieve what you want with version(unittest):
 
 version(unittest)
 {
    class A { B b; }
    class B { }
 }
 
 unittest
 {
    // use A and B here
 }
[...] This advice, unfortunately, needs to be tempered with caution about namespace pollution and accidental dependency of things outside unittests on things inside a version(unittest) block. There's also the issue of library code introducing extraneous import dependencies that are really only necessary for unittesting, but get pulled in anyway because user code happens to compile with -unittest. T -- "Real programmers can write assembly code in any language. :-)" -- Larry Wall
May 12 2017
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/12/17 5:46 PM, H. S. Teoh via Digitalmars-d-learn wrote:
 On Fri, May 12, 2017 at 05:23:23PM -0400, Steven Schveighoffer via
Digitalmars-d-learn wrote:
 [...]
 Note, you can achieve what you want with version(unittest):

 version(unittest)
 {
    class A { B b; }
    class B { }
 }

 unittest
 {
    // use A and B here
 }
[...] This advice, unfortunately, needs to be tempered with caution about namespace pollution and accidental dependency of things outside unittests on things inside a version(unittest) block. There's also the issue of library code introducing extraneous import dependencies that are really only necessary for unittesting, but get pulled in anyway because user code happens to compile with -unittest.
This actually already happens. Any imports count, even inside unittests. But yes, this does mean that symbols in the version(unittest) are in the module namespace. You could create a struct or class namespace to avoid the pollution, as long as you use really horrible names that wouldn't possibly conflict. -Steve
May 13 2017
parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Sat, May 13, 2017 at 12:39:43PM -0400, Steven Schveighoffer via
Digitalmars-d-learn wrote:
 On 5/12/17 5:46 PM, H. S. Teoh via Digitalmars-d-learn wrote:
[...]
 This advice, unfortunately, needs to be tempered with caution about
 namespace pollution and accidental dependency of things outside
 unittests on things inside a version(unittest) block.  There's also
 the issue of library code introducing extraneous import dependencies
 that are really only necessary for unittesting, but get pulled in
 anyway because user code happens to compile with -unittest.
This actually already happens. Any imports count, even inside unittests.
[...] You're right, it does already happen. This is unfortunate, since it means that 3rd party libraries will still get their unittests run when users happen to compile their own code with -unittest. Perhaps there should be a DIP for this? T -- Bare foot: (n.) A device for locating thumb tacks on the floor.
May 14 2017
prev sibling parent Jonathan M Davis via Digitalmars-d-learn writes:
On Friday, May 12, 2017 14:46:30 H. S. Teoh via Digitalmars-d-learn wrote:
 On Fri, May 12, 2017 at 05:23:23PM -0400, Steven Schveighoffer via
 Digitalmars-d-learn wrote: [...]

 Note, you can achieve what you want with version(unittest):

 version(unittest)
 {

    class A { B b; }
    class B { }

 }

 unittest
 {

    // use A and B here

 }
[...] This advice, unfortunately, needs to be tempered with caution about namespace pollution and accidental dependency of things outside unittests on things inside a version(unittest) block. There's also the issue of library code introducing extraneous import dependencies that are really only necessary for unittesting, but get pulled in anyway because user code happens to compile with -unittest.
Yeah, that can get annoying, though I think that the only real problem in general is imports. If you use version(unittest) on imports, then you run the risk of having imports that are required for normal operation being only available when you compile with -unittest and not catching it. But unless you have a problem using version(unittest) types or functions in your actual code (which would be kind of weird), then I wouldn't expect it to be a problem. However, in many cases, it makes sense to just move the declarations into the unittest blocks (especally if they're only used in one unittest block), and then you avoid the whole problem (though in this case, you'd want to mark the classes as static if you put them in the unittest block). - Jonathan M Davis
May 13 2017