www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Feasible Idea?: Range Tester

reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
Suppose you create a range: struct MyRange{...}

Now you have to unittest it. And there's a lot of ways a range
implementation can go wrong. There's a fair amount of things to test.
And they all, ideally, need to be done for *every* range type created.

But, ranges are all supposed to conform to a standard interface with
standardized semantics. So can't most of this testing be generalized
into a generic InputRangeTester, RandomAccessRageTester, etc? All you'd
do is feed it an array of elements to test with, and, in theory, it
should be able to unittest the range's semantics for conformance.

Obviously it couldn't guarantee 100% conformance, because no unittest
can do that, but it seems reasonable that it could be just as good as a
well-designed set of hand-written tests.

I think this would be an enormously useful thing to have in Phobos. Can
any Phobos/range gurus say fur sure how feasible this is? Does Phobos
perhaps already do something similar internally that could just be
generalized?

(This message has been brought to you by three random access ranges I
just created but really, really don't feel like unittesting ;) )
Mar 21 2013
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, March 21, 2013 13:08:58 Nick Sabalausky wrote:
 Suppose you create a range: struct MyRange{...}
 
 Now you have to unittest it. And there's a lot of ways a range
 implementation can go wrong. There's a fair amount of things to test.
 And they all, ideally, need to be done for *every* range type created.
 
 But, ranges are all supposed to conform to a standard interface with
 standardized semantics. So can't most of this testing be generalized
 into a generic InputRangeTester, RandomAccessRageTester, etc? All you'd
 do is feed it an array of elements to test with, and, in theory, it
 should be able to unittest the range's semantics for conformance.
 
 Obviously it couldn't guarantee 100% conformance, because no unittest
 can do that, but it seems reasonable that it could be just as good as a
 well-designed set of hand-written tests.
 
 I think this would be an enormously useful thing to have in Phobos. Can
 any Phobos/range gurus say fur sure how feasible this is? Does Phobos
 perhaps already do something similar internally that could just be
 generalized?
 
 (This message has been brought to you by three random access ranges I
 just created but really, really don't feel like unittesting ;) )
Hmmm. I've been working on stuff which makes creating ranges to test range- based functions easier, but I've never thought about creating something to test conformance. If I need something like that I just use static asserts to test isInputRange, isForwardRange, etc. and make sure that each of the ones that are supposed to pass for that range do and that those that aren't supposed to pass don't. All that that's going to test though is the API, not behavior. But I don't know how you'd automate testing behavior when the exact behavior of the range is very much dependent on the range itself. In most cases though, you end up with a new range type, because it's being returned from a range-based function, in which case, simply unit testing the function with a variety of range types generally takes care of it, and those aren't particularly automatable, because what the tests need to test depends entirely on what the function is supposed to do. - Jonathan M Davis
Mar 21 2013
next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Thu, 21 Mar 2013 13:29:35 -0400
"Jonathan M Davis" <jmdavisProg gmx.com> wrote:
 
 But I don't
 know how you'd automate testing behavior when the exact behavior of
 the range is very much dependent on the range itself.
I don't think that's true at all. Granted, you can't automate testing of the range's internal state, but there's a lot about the external behavior which is very strictly defined: Examples: - Multiple calls to front return the same element. - Calling popFront and then front returns the next element. - If range has length, then 'empty' returns true IFF popFront has been called at least 'myRange.length' times. - Calling popFront doesn't throw unless range is empty. - Calling popFront on an empty range throws a RangeError. - Upon instantiating a new random access range, 'r.front == r[0]', and then after 'r.popFront()', 'r.front == r[1]', etc. - 'r.save.front == r.front'. And then call popFront on both ranges, and 'r1.front == r2.front' is still true. - Iterating via back/popBack yields the same elements as front/popFront, but in reverse order. And I'm sure there's a whole bunch more. If you give a hypothetical generic range-testing tool both an instantiated range to be tested and an array of all the elements you expect to be in the range, in the same order, then I'd think all of the above and more should be testable.
Mar 21 2013
next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Thu, 21 Mar 2013 13:48:54 -0400
Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> wrote:
 - Calling popFront doesn't throw unless range is empty.
 - Calling popFront on an empty range throws a RangeError.
Actually, I'm not certain about those two, but I know they're true if you just s/popFront/front/
Mar 21 2013
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, March 21, 2013 13:52:12 Nick Sabalausky wrote:
 On Thu, 21 Mar 2013 13:48:54 -0400
 
 Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> wrote:
 - Calling popFront doesn't throw unless range is empty.
 - Calling popFront on an empty range throws a RangeError.
Actually, I'm not certain about those two, but I know they're true if you just s/popFront/front/
There are no such requirements on ranges. popFront most definitely _can_ throw even if the range is non-empty (e.g. UTFException due to invalid UTF), and I'm not aware of any range in Phobos which currently throws a RangeError when trying to call popFront on an empty range. Normally, an assertion is used, and the range's behavior when popping an element from an empty range in release mode is undefined. But I could definitely see an argument for adopting the policy of having popFront throw RangeError when the range is empty. - Jonathan M Davis
Mar 21 2013
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Thursday, 21 March 2013 at 18:19:00 UTC, Jonathan M Davis 
wrote:
 I could definitely see an argument for adopting the
 policy of having popFront throw RangeError when the range is 
 empty.

 - Jonathan M Davis
In release mode, assert expressions are removed. This would not be possible for throwing a RangeError, preventing a useful optimisation in well tested code.
Mar 21 2013
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, March 21, 2013 19:46:08 John Colvin wrote:
 On Thursday, 21 March 2013 at 18:19:00 UTC, Jonathan M Davis
 
 wrote:
 I could definitely see an argument for adopting the
 policy of having popFront throw RangeError when the range is
 empty.
 
 - Jonathan M Davis
In release mode, assert expressions are removed. This would not be possible for throwing a RangeError, preventing a useful optimisation in well tested code.
A valid point as to why such a change in policy should not be made. Regardless, there's definitely no requirement right now that popFront throw RangeError when the range is empty. What Phobos normally does at this point is assert. Anything else would be a change that would have to be discussed, so specifically testing that a conformant range throws a RangeError from popFront when it's empty (as Nick was suggesting) would not be correct at this point. - Jonathan M Davis
Mar 21 2013
parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Thu, 21 Mar 2013 21:55:10 -0400
"Jonathan M Davis" <jmdavisProg gmx.com> wrote:

 Anything else would be a change that would
 have to be discussed, so specifically testing that a conformant range
 throws a RangeError from popFront when it's empty (as Nick was
 suggesting) would not be correct at this point.
 
I wasn't suggesting that, merely trying to list a bunch of examples of testable things. Clearly I got a few of the examples wrong ;) But that does kinda speak to the usefulness of having something standard that can be expected to actually do the *correct* tests, instead of all range authors just winging it on their own and potentially misunderstanding the rules.
Mar 21 2013
next sibling parent reply "timotheecour" <timothee.cour2 gmail.com> writes:
  testing that a conformant range throws a RangeError from 
 popFront when it's empty (as Nick was suggesting) would not be 
 correct at this point
ok what about the following: should work regardless it's RangeError or an assert(0) in th eparticular implementation of a range. ---- bool isThrown(E)(lazy E expression){ import std.exception; import core.exception : RangeError; try expression(); catch(RangeError t) return true; catch(Error t) return true; return false; } unittest(){ int[]x; assert(isThrown(x[1])); } ----
Mar 22 2013
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, March 22, 2013 08:07:01 timotheecour wrote:
  testing that a conformant range throws a RangeError from
 
 popFront when it's empty (as Nick was suggesting) would not be
 correct at this point
ok what about the following: should work regardless it's RangeError or an assert(0) in th eparticular implementation of a range. ---- bool isThrown(E)(lazy E expression){ import std.exception; import core.exception : RangeError; try expression(); catch(RangeError t) return true; catch(Error t) return true; return false; } unittest(){ int[]x; assert(isThrown(x[1])); } ----
Sure, you could do that, but I'd be completely against putting anything in Phobos to test ranges which tested anything which wasn't agreed upon as the correct way to do it. That being the case, it would have to be agreed upon whether assertions should be used or whether RangeError should be used (or that using either was valid, though that's a bad idea IMHO). As for the implementation, it would make more sense to simply do assertThrown!Error(x[1]); if you don't care which type of Error is thrown, and if you do care but are allowing both (since you can only give one type to assertThrown), then you could use auto e = collectException!Error(x[1]); assert((cast(AssertError)e) !is null || (cast(RangeError)) !is null); But if you're writing try-catch blocks in unit tests to test exceptions being thrown, odds are that you're not taking proper advantage of std.exception. - Jonathan M Davis
Mar 22 2013
parent reply "timotheecour" <timothee.cour2 gmail.com> writes:
 it would have to be agreed upon
 whether assertions should be used or whether RangeError should 
 be used (or
 that using either was valid, though that's a bad idea IMHO).
if we force range code to throw RangeError, wouldn't that prevent optimization in release mode? If so, that's no good. Also, simply using assert(...); is much more convenient in lots of cases (think user written ranges that don't wanna be bogged down by boilerplate). So why not just say: only AssertError or RangeError are valid in that case, that doesn't seem controversial.
Mar 22 2013
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, March 22, 2013 09:37:32 timotheecour wrote:
 it would have to be agreed upon
 whether assertions should be used or whether RangeError should
 be used (or
 that using either was valid, though that's a bad idea IMHO).
if we force range code to throw RangeError, wouldn't that prevent optimization in release mode? If so, that's no good. Also, simply using assert(...); is much more convenient in lots of cases (think user written ranges that don't wanna be bogged down by boilerplate). So why not just say: only AssertError or RangeError are valid in that case, that doesn't seem controversial.
My main point was that it makes no sense to test for standard behavior if that standard behavior hasn't been agreed upon. My secondary point was that I think that it's a very bad idea to try and "standardize" the behavior an then let do different things depending on what the programmer felt like. In this particular situation, I think that I'd favor an assertion, but regardless, the way that we've been using RangeError in Phobos is in a version(assert) block, so they wouldn't even be enabled with -release anyway. And I question that it makes sense to test a type to make sure that it asserts something, which is basically what we'd be doing if we verifying that an AssertError (or a RangeError without -release) was thrown when calling popFront on an empty range. We just treat that sort of thing as undefined behavior. So, I really don't think that it makes any sense to test for how a range behaves when you call popFront on it when it's empty. - Jonathan M Davis
Mar 22 2013
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, March 21, 2013 22:03:25 Nick Sabalausky wrote:
 On Thu, 21 Mar 2013 21:55:10 -0400
 
 "Jonathan M Davis" <jmdavisProg gmx.com> wrote:
 Anything else would be a change that would
 have to be discussed, so specifically testing that a conformant range
 throws a RangeError from popFront when it's empty (as Nick was
 suggesting) would not be correct at this point.
I wasn't suggesting that, merely trying to list a bunch of examples of testable things. Clearly I got a few of the examples wrong ;)
You were suggesting it in the sense that you listed it as something to test for, not that you were proposing it.
 But that does kinda speak to the usefulness of having something
 standard that can be expected to actually do the *correct* tests,
 instead of all range authors just winging it on their own and
 potentially misunderstanding the rules.
Yes, though some of those are really undefined behavior (e.g. what happens when you call popFront on an empty range), so it wouldn't really make sense to test them. But having ways to test for standard behavior would make it easier to ensure that that behavior is actually standard. - Jonathan M Davis
Mar 22 2013
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/21/2013 7:03 PM, Nick Sabalausky wrote:
 But that does kinda speak to the usefulness of having something
 standard that can be expected to actually do the *correct* tests,
 instead of all range authors just winging it on their own and
 potentially misunderstanding the rules.
I agree.
Mar 23 2013
prev sibling next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
21-Mar-2013 21:48, Nick Sabalausky пишет:
 On Thu, 21 Mar 2013 13:29:35 -0400
 "Jonathan M Davis" <jmdavisProg gmx.com> wrote:
 But I don't
 know how you'd automate testing behavior when the exact behavior of
 the range is very much dependent on the range itself.
I don't think that's true at all. Granted, you can't automate testing of the range's internal state, but there's a lot about the external behavior which is very strictly defined: Examples: - Multiple calls to front return the same element. - Calling popFront and then front returns the next element. - If range has length, then 'empty' returns true IFF popFront has been called at least 'myRange.length' times. - Calling popFront doesn't throw unless range is empty. - Calling popFront on an empty range throws a RangeError. - Upon instantiating a new random access range, 'r.front == r[0]', and then after 'r.popFront()', 'r.front == r[1]', etc. - 'r.save.front == r.front'. And then call popFront on both ranges, and 'r1.front == r2.front' is still true. - Iterating via back/popBack yields the same elements as front/popFront, but in reverse order. And I'm sure there's a whole bunch more. If you give a hypothetical generic range-testing tool both an instantiated range to be tested and an array of all the elements you expect to be in the range, in the same order, then I'd think all of the above and more should be testable.
Indeed there is a host of invariants that must hold for any range of certain kind. I'd say this along with other stuff should go into std.test or std.testing (or package within) etc. -- Dmitry Olshansky
Mar 21 2013
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Mar 21, 2013 at 01:48:54PM -0400, Nick Sabalausky wrote:
 On Thu, 21 Mar 2013 13:29:35 -0400
 "Jonathan M Davis" <jmdavisProg gmx.com> wrote:
 
 But I don't know how you'd automate testing behavior when the exact
 behavior of the range is very much dependent on the range itself.
I don't think that's true at all. Granted, you can't automate testing of the range's internal state, but there's a lot about the external behavior which is very strictly defined: Examples: - Multiple calls to front return the same element.
This is easy to test.
 - Calling popFront and then front returns the next element.
How do you know if something is the "next element"? What if the range is repeat(1)?
 - If range has length, then 'empty' returns true IFF popFront has been
   called at least 'myRange.length' times.
This should also be easy to test. But it requires the ability to instantiate the range with some given number of elements. So you still need a range-specific function to create the range in the first place.
 - Calling popFront doesn't throw unless range is empty.
Should be easy to check.
 - Calling popFront on an empty range throws a RangeError.
Is this part of the official requirement on ranges? I've been guilty of writing ranges whose popFront are no-ops when the range is empty (simply because for some ranges, it's a pain to explicitly check for this and throw, when the popFront computation doesn't actually assume anything about emptiness).
 - Upon instantiating a new random access range, 'r.front == r[0]', and
   then after 'r.popFront()', 'r.front == r[1]', etc.
This should be relatively easy to check.
 - 'r.save.front == r.front'. And then call popFront on both ranges, and
   'r1.front == r2.front' is still true.
Should be easy to check too. I was gonna say that you'd have to watch out for RNGs, as popFront on an RNG that bases its output on, say, random environmental factors, will break this rule; but on second thought, in this case .save is invalid on such ranges (they can only be input ranges) so this check *should* fail.
 - Iterating via back/popBack yields the same elements as
   front/popFront, but in reverse order.
Doesn't work on infinite ranges. :) (You *can* have infinite ranges with two ends. There's no good reason to do that, of course, because they are effectively just two infinite forward ranges stuck together at their infinite ends, which is kinda pointless, but in theory, this *can* happen.)
 And I'm sure there's a whole bunch more.

 If you give a hypothetical generic range-testing tool both an
 instantiated range to be tested and an array of all the elements you
 expect to be in the range, in the same order, then I'd think all of
 the above and more should be testable.
This wouldn't work on infinite ranges. :) But you could at least test some initial segment of an infinite range, I suppose, so this shouldn't be too big of an issue. T -- Amateurs built the Ark; professionals built the Titanic.
Mar 21 2013
parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Thu, 21 Mar 2013 11:04:31 -0700
"H. S. Teoh" <hsteoh quickfur.ath.cx> wrote:
 - Calling popFront and then front returns the next element.
How do you know if something is the "next element"? What if the range is repeat(1)?
 - If range has length, then 'empty' returns true IFF popFront has
 been called at least 'myRange.length' times.
This should also be easy to test. But it requires the ability to instantiate the range with some given number of elements. So you still need a range-specific function to create the range in the first place.
What I had in mind was something like: void testForwardRange(R, E)(R range, E[] content) { // Deliberately not contraints, because this *is* a test, after all. // *Or* maybe it just auto-detects range type and leaves it // up to the user to check for "isForwardRange!R" or whatever. static assert(isForwardRange!R); static assert(is(ElementTypeOf!R == E)); static if(hasLength!R) assert(range.length == content.length); foreach(i; 0...content.length) { assert(range.front == content[i]); range.popFront(); } // More tests } unittest{ testForwardRange(createMyRangeSomehow(), [...expected elements...]); }
 
 - Calling popFront on an empty range throws a RangeError.
Is this part of the official requirement on ranges? I've been guilty of writing ranges whose popFront are no-ops when the range is empty (simply because for some ranges, it's a pain to explicitly check for this and throw, when the popFront computation doesn't actually assume anything about emptiness).
Actually, I'm not sure on that one. I would think that calling front on an empty range should throw. Or at least in a typical well-behaved range it should, even if it's not specifically stated as being technically mandatory. But in any case, it was only an example.
 
 - Iterating via back/popBack yields the same elements as
   front/popFront, but in reverse order.
Doesn't work on infinite ranges. :) (You *can* have infinite ranges with two ends. There's no good reason to do that, of course, because they are effectively just two infinite forward ranges stuck together at their infinite ends, which is kinda pointless, but in theory, this *can* happen.)
Naturally, the testing would be different on different types of ranges. The tester could auto-detect things like "isInfiniteRage" and "hasLength", and act accordingly. And of course, if you want to guarantee your range is indeed, for example, a "hasLength", then you can also just add "static assert(hasLength!MyRange);" alongside your call to the tester.
Mar 21 2013
parent reply "jerro" <a a.com> writes:
 void testForwardRange(R, E)(R range, E[] content)
 {
     // Deliberately not contraints, because this *is* a test, 
 after all.
     // *Or* maybe it just auto-detects range type and leaves it
     // up to the user to check for "isForwardRange!R" or 
 whatever.
     static assert(isForwardRange!R);
     static assert(is(ElementTypeOf!R == E));

     static if(hasLength!R)
         assert(range.length == content.length);
 
     foreach(i; 0...content.length)
     {
         assert(range.front == content[i]);
         range.popFront();
     }

     // More tests
 }
Would it make sense to just name the function testRange and have it test conformance for any range primitives that the type supports? Your example already uses static "if(hasLenght!R)" for length, it may be a good idea to expand that.
Mar 21 2013
parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Thu, 21 Mar 2013 21:41:52 +0100
"jerro" <a a.com> wrote:

 void testForwardRange(R, E)(R range, E[] content)
 {
     // Deliberately not contraints, because this *is* a test, 
 after all.
     // *Or* maybe it just auto-detects range type and leaves it
     // up to the user to check for "isForwardRange!R" or 
 whatever.
     static assert(isForwardRange!R);
     static assert(is(ElementTypeOf!R == E));

     static if(hasLength!R)
         assert(range.length == content.length);
 
     foreach(i; 0...content.length)
     {
         assert(range.front == content[i]);
         range.popFront();
     }

     // More tests
 }
Would it make sense to just name the function testRange and have it test conformance for any range primitives that the type supports? Your example already uses static "if(hasLenght!R)" for length, it may be a good idea to expand that.
Possibly. After all, a person really should already be using "static assert(isBlahBlahRange!MyRange);" to make sure their range is the type they expect it to be anyway.
Mar 21 2013
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, March 21, 2013 13:48:54 Nick Sabalausky wrote:
 On Thu, 21 Mar 2013 13:29:35 -0400
 
 "Jonathan M Davis" <jmdavisProg gmx.com> wrote:
 But I don't
 know how you'd automate testing behavior when the exact behavior of
 the range is very much dependent on the range itself.
I don't think that's true at all. Granted, you can't automate testing of the range's internal state, but there's a lot about the external behavior which is very strictly defined: Examples: - Multiple calls to front return the same element. - Calling popFront and then front returns the next element. - If range has length, then 'empty' returns true IFF popFront has been called at least 'myRange.length' times. - Calling popFront doesn't throw unless range is empty. - Calling popFront on an empty range throws a RangeError. - Upon instantiating a new random access range, 'r.front == r[0]', and then after 'r.popFront()', 'r.front == r[1]', etc. - 'r.save.front == r.front'. And then call popFront on both ranges, and 'r1.front == r2.front' is still true. - Iterating via back/popBack yields the same elements as front/popFront, but in reverse order. And I'm sure there's a whole bunch more. If you give a hypothetical generic range-testing tool both an instantiated range to be tested and an array of all the elements you expect to be in the range, in the same order, then I'd think all of the above and more should be testable.
I confess that it pretty much never occurs to me to test most of this stuff. It's always the exact state of the range that I care about, because that has to do with whether the function returns the correct result. But the sort of testing that you're listing here could probably be automated. - Jonathan M Davis
Mar 21 2013
prev sibling parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
 - Upon instantiating a new random access range, 'r.front == r[0]', and
   then after 'r.popFront()', 'r.front == r[1]', etc.
This should be relatively easy to check.
Is that really the behaviour for RA ranges? I'd say that after calling r.popFront(), then r.front == r[0]. The test could be: assert(r has at least two elements); auto nextOne = r[1]; r.popFront(); assert(r.front == nextOne);
Mar 21 2013
parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On Thu, 21 Mar 2013 20:24:40 +0100
Philippe Sigaud <philippe.sigaud gmail.com> wrote:

 - Upon instantiating a new random access range, 'r.front == r[0]',
 and then after 'r.popFront()', 'r.front == r[1]', etc.
This should be relatively easy to check.
Is that really the behaviour for RA ranges? I'd say that after calling r.popFront(), then r.front == r[0]. The test could be: assert(r has at least two elements); auto nextOne = r[1]; r.popFront(); assert(r.front == nextOne);
You might be right. See why such a tester would be helpful? ;)
Mar 21 2013
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 21 March 2013 at 17:29:46 UTC, Jonathan M Davis 
wrote:
 Hmmm. I've been working on stuff which makes creating ranges to 
 test range-
 based functions easier, but I've never thought about creating 
 something to
 test conformance.

 - Jonathan M Davis
One of the things I've been trying to deploy is a trait that would help identify *why* a range is (statically) non-compliant. For example, when I was still not 100% up to speed on what it takes for a range to conform to this and that, I'd forget something, and it would take a lot of effort to realize that "dang, my save is not a property, that's why my range is not a random access range". I wrote some traits called things like "assertIsRandomAccessRange!R". Contrary to "assert(isRandomAccessRange!R)", my function would output: "Assertion failed: R is not a random access range because it does not provide the save property". This was quite a helpful tool but... ...It was mostly just code duplication. It would have been an helpful feature, but I'm not sure the code maintenance it entailed in std.range would have made it worth it.
Mar 21 2013
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Mar 21, 2013 at 07:52:32PM +0100, monarch_dodra wrote:
 On Thursday, 21 March 2013 at 17:29:46 UTC, Jonathan M Davis wrote:
Hmmm. I've been working on stuff which makes creating ranges to
test range-
based functions easier, but I've never thought about creating
something to
test conformance.

- Jonathan M Davis
One of the things I've been trying to deploy is a trait that would help identify *why* a range is (statically) non-compliant. For example, when I was still not 100% up to speed on what it takes for a range to conform to this and that, I'd forget something, and it would take a lot of effort to realize that "dang, my save is not a property, that's why my range is not a random access range". I wrote some traits called things like "assertIsRandomAccessRange!R". Contrary to "assert(isRandomAccessRange!R)", my function would output: "Assertion failed: R is not a random access range because it does not provide the save property". This was quite a helpful tool but... ...It was mostly just code duplication. It would have been an helpful feature, but I'm not sure the code maintenance it entailed in std.range would have made it worth it.
I think this is worth doing if you already wrote the templates to do these checks. Ranges are an extremely powerful concept in D, but they are also very obscure for a beginner, and the current error messages don't help. Improving the assertions' messages would be a big improvement, IMO. T -- People tell me I'm stubborn, but I refuse to accept it!
Mar 21 2013