OSQL Testing
It is recommended practice to create a test.osql
in each model to state and test its intended result.
The test file must load it's corresponding model to gain access to the functions it is testing. Use the command models:load(Charstring model)->Charstring
.
There is a utility command models:test(Charstring model)->Charstring
which will execute test.osql
. This command automatically loads the model first.
The Validate-Check syntax
OSQL support unit testing through the validate
statement. It contains a description and one or more check
clauses.
For example, define a function prime
that takes a number as input and return true if it is a prime:
create function prime(Integer n) -> boolean
as select true where notany(select d
from Integer d
where d in range(2,floor(sqrt(n)))
and 0 = mod(n,d));
Now we can validate that we get the expected return for various input.
validate "my prime"
check prime(0) => true
check prime(13) => true
check prime(4) => null
check prime(20) => null
check prime(31) => true;
Try changing the statement so that it fails.
Validating streams
When working with streams, the functions vectorof(Stream b)->Vector v
and extract(Stream s)->Bag
are useful for extracting the stream elements into vectors and bags respectively. For example, consider a function that samples a stream:
create function sample(Stream s) -> stream of Vector
as winagg(s,1,5);
to validate its output we will convert it to a vector or bag.
validate "sample with extract"
check extract(sample(diota(0,1,10))) => bag([1],[6])
check extract(sample(diota(0,1,10))) => bag([6],[1]);
validate "sample with vectorof"
check vectorof(sample(diota(0,1,10))) =>
[[1],[6]];
A vector is an ordered sequence of objects while a bag is an unordered collection. This makes vectorof
the preferred choice for testing stream output where the order is known. If the order is undeterministic, as may be the case with pivot streams, use extract
.
The inverse of extract
is streamof(Bag b)->Stream
.
Another useful function is timeout
, that reads a stream but stops after a specified number of seconds.
timeout(heartbeat(1),2.5);
This makes it possibe to validate it as a finite stream.
validate "heartbeat"
check count(extract(timeout(heartbeat(1),2.5))) =>
3;
Other useful testing functions
For more complex tests where the output is large, it can be useful to store the testdata in a CSV or JSON file.
Setting up or running a test may also involve populating stored functions. After the test has run these should be cleared to not contaminate later tests. This can be achieved by clear_function(Charstring fn)->Function
which permanently removes the contents of the stored function fn.
The time_function can be used to compare the performance of different implementations. However, since external factors influence execution times, it must be interpreted with care. Try running time_function on the prime function a couple of times. Note that time_functions prints the total time to the console but returns the average time. It is the average time that is shown below.
time_function("Running prime(9856323)","prime",[9856323],100000);
When it is not possible to wait for a certain input, the sleep(Number seconds)
is an option.
now();
sleep(1);
now();