Algebraic Data Types - Testing 2018-06-22
In this post:
- I wrote a few tests. Possibly, I could have written more, but this week wiped me out.
- The tests give me some ideas for breaking up the 500+ line source file into manageable chunks.
This week had a lot going on, so I didn't test as thoroughly as I would have liked, but I did test a good portion of the error paths in the code, and I got the coverage above 50%, though just barely.
My initial testing focused on confirming the behavior of the Ctor type (which I may be able to simplify somewhat if I switch entirely to Python 3.7 semantics; can't do that yet though), with such things as:
- Indexing with the empty tuple is the same as the base class. I may want to relax this in the future, depending on how much I care about having a single representation of Ctor stuff (especially since the size of the index is the only thing that's supposed to matter at runtime).
- Indexing with a non-tuple is the same as indexing with a 1-tuple that contains the non-tuple. Currently, this is by object identity, but, again, there are many possibilities. (I mean, in 3.7, indexing could be instance creation, and Ctor a subclass of int! I think.)
- Can't subclass Ctor freely. I'd rather we weren't subclassing it at all. Next week, oh well.
- Can't index an already-indexed Ctor. I'll be really happy to see the end of these metaclasses.
- An exercise of most of the pattern-matching machinery. Some of the ergonomics here are bad, and I'm trying to figure out how to improve them. The test is dense enough that I'm going to include it here.
@structured_data.enum
class TestClass:
StrPair: structured_data.Ctor[str, str]
matcher = structured_data.ValueMatcher(
((1, 2), TestClass.StrPair('a', 'b')))
assert not matcher.match((
(structured_data.Pattern('_'), 4),
structured_data.Pattern('_')))
assert matcher.matches is None
assert matcher.match((
structured_data.Pattern('tup') @ (1, structured_data.Pattern('a')),
TestClass.StrPair(
structured_data.Pattern('b'), structured_data.Pattern('c'))))
assert matcher.matches == dict(tup=(1, 2), a=2, b='a', c='b')
This test is perhaps too big, in that it tests matching against tuple literals, against sum types, using at patterns, using discards... Given my wish to separate things, perhaps the if-statements that ultimately power the match function should be converted into a loop that acts on (predicate, generator) pairs. When the predicate matches, extend the work list from the generator, then do the next iteration. Anyway, then they could be put into their own little modules, and be given little test cases.
The next big win for testing would be creating custom modules to import, to test the logic at the top level. I'll have to see if divide-and-cover is okay with putting random non-discoverable files in the test tree, because that pattern of organization did not occur to me until this week.
(Also, it seriously bothers me that the cookiecutter's linting settings aren't generating something for that test code up there. I'm using to having pretty strict lints on all of the code, including, at minimum, requiring docstrings everywhere.)
Next time, I get into the documentation. What's incorrect? What should be added? What should I do to the changelog?