[phobos] Silent failure of std.container unittests

Michel Fortin michel.fortin at michelf.com
Sat Jul 17 05:17:09 PDT 2010


Le 2010-07-17 à 1:07, Walter Bright a écrit :

> Yes, but it's not hard. For me, the issue is making things for the user
> more complicated. D is already a very large language, and adding more
> and more stuff like this makes it larger, buggier, and less
> approachable. For example, nothing stands out to say what "unittest
> assert" does vs "assert".

I just want to point out that "unittest assert(x);" would just be a shortcut for "unittest { assert(x); }", which is quite similar to how you can omit the braces for conditional and loop statements in regular code. This is only a small shift from the earlier concept of unittest in D: instead of having unittest blocks scattered in your module, you have unittest statements (which can be blocks) scattered around. It's not much of a change, it's just being more permissive.

	module abc;

	unittest assert(sqrt(4) == 2); // single-statement unit test
	unittest assert(sqrt(16) == 4);

As for nesting unittests, I agree that it might get harder to conceptualize how it works. But still, there isn't anything really 'special' about it, they have the same semantics as the module-level ones. It's better explained with a concrete example. Say a module includes those unit tests:

	module abc;

	unittest assert(sqrt(4) == 2);
	unittest assert(sqrt(16) == 4);

	unittest {
		int[] result = makeSomeTable(399, 383, 927);
		assert(result.length == 3); // guard against invalid state

		unittest assert(result[0] == 281);
		unittest assert(result[1] == 284);
		unittest assert(result[2] == 283);

		unittest {
			int[] derivedResult = derive(result);
			assert(derivedResult.length == 2); // guard against invalid state

			unittest assert(derivedResult[0] ==  3);
			unittest assert(derivedResult[1] == -1);
		}
		// ... other tests reusing 'result'
	}

How do you translate this to a module-level unittest function? First define this function in the runtime:

	void __doUnitTest(void delegate() test) {
		try { test(); }
		catch (AssertionError e) { __unitTestLogFailure(e); }
	}

Then replace each unittest statement or block with a call to the above function, passing the unittest code as the argument. The three module-level unittests I wrote above would translate to this module unittest function:

	void __module_abc_unittests() {
		__doUnitTest({  assert(sqrt(4) == 2); });
		__doUnitTest({  assert(sqrt(16) == 4); });

		__doUnitTest({
			int[] result = makeSomeTable(399, 383, 927);
			assert(result.length == 3); // guard against invalid state
	
			__doUnitTest({  assert(result[0] == 281);  });
			__doUnitTest({  assert(result[1] == 284);  });
			__doUnitTest({  assert(result[2] == 283);  });

			__doUnitTest({
				int[] derivedResult = derive(result);
				assert(derivedResult.length == 2); // guard against invalid state

				__doUnitTest({  assert(derivedResult[0] ==  3);  });
				__doUnitTest({  assert(derivedResult[1] == -1);  });
			});
			// ... other tests reusing 'result'
		});
	}

As other mentioned, what I do here with nested unit tests could easily be implemented by offering the user a new library function too. I just happen to think what I'm proposing here is more elegant.

-- 
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/





More information about the phobos mailing list