Moritz Lenz wrote:
A few months ago Larry proposed to add some testing
facilites to the language itself, because we want to
culturally encourage testing, and because the test
suite defines the language, so we need to specify the
behaviour of our testing facilities anyway.
If we're going to revamp the testing primitives, then I'd like to
suggest importing some concepts from hardware verification langauges,
whose entire purpose is to define tests. Not too much, but just a few defns:
* Define a "property" as an expression whose truth is of interest
(properties may be named, or may be anonymous inline).
* An "assert <property>" statement (aka "ok <property>") indicates that
a violation of the property is to be considered an error
* An "assume <property>" statement indicates that a violation of the
property implies an incorrect test.
Assumptions are very important when you write automated test generators,
or need to validate that your tests are not doing something illegal. If
you violate an assumption when running normal code then it's not really
any different from hitting an assertion. You want as many assumptions as
possible to be part of the type system (which we already do with "where"
clauses).
(I'm using the phrase "assert" instead of "ok" because that's the
standard terminology: In perl, "ok" is standard, so no need to rename
it. But I do think that we need to qualify it with whether it's an
assumption or an assertion. You can also think of an assumption as a
precondition. But adding "PRE" blocks to every function tends to
encourage cargo-cult DBC programming.)
The other thing I'd like to point out is that the concept of a
"property" can be very general. We shouldn't assume that they just sit
in the middle of procedural code. Specifically, it should be possible to
define invariants on objects, which should be true at some specified
point in time (however, it's not always obvious what that point in time is).
I'm thinking we might have something like:
class Foo {
has $.a;
has $.b;
property conserve_sum { $.a + $.b == 42 },
"a+b must sum to 42, but a=$.a + b=$.b == { $.a+$.b }";
method foo() does assume<conserve_sum> { ... }
method bar() does ensure<conserve_sum> { ... }
method baz() { bar; ok conserve_sum; foo; }
}
A property is just a method that returns a Bool but, if you associate
the failure message with it, then it becomes simple to assert/assumme it
in multiple places without needing to keep repeating the message.
An interesting type of property is one that tracks a series of events
through time: a so called "temporal" property. A simple idea might be
that "conserve_sum" should actually mean "sum does't change", instead of
"is constant 42":
class Foo {
...
coro property conserve_sum {
my $sum = $.a + $.b;
leave True;
ok $.a + $.b == $sum,
"sum not conserved: expected $sum, actual {$.a+$.b}"
}
method foo() does maintain<conserve_sum> { --$.a; ++$.b }
}
I don't think that it is necessary to be too cute with huffmannization
of testing primitives. All my examples here are just thinking aloud.