Skip to main content

OSQL Testing

OSQL support unit testing through the validate statement. It contains a description and one or more check clauses.

For example, let's 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 not exists(select d
from Integer d
where d in range(2,floor(sqrt(n)))
and 0 = mod(n,d));

Now we can test that we get the expected return for various parameters with a validate statement:

validate "my prime"
check prime(0) => true
check prime(13) => true
check prime(4) => null
check prime(20) => null
check prime(31) => true;
Exercise

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 converting the finite stream elements into vectors or bags, respectively. For example, consider a function that returns a stream of sliding windows over stream s:

create function slider(Stream s) -> Stream of Vector
as winagg(s,1,5);

The following statement validates the output from slider on finite streams of integers converted to vectors:

validate "slider with vectorof"
check vectorof(slider(diota(0,1,10))) =>
[[1],[6]];

The following converts the streams to bags instead of vectors:

validate "slider with extract"
check extract(slider(diota(0,1,10))) => values [1],[6]
check extract(slider(diota(0,1,10))) => values [6],[1];
Note

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.

For infinite real-time streams, a useful function is timeout that runs a stream a specified number of seconds.

timeout(heartbeat(1),2.5);

This allows to convert an infinite stream to a finite stream for real-time validation:

validate "heartbeat"
check count(extract(timeout(heartbeat(1),2.5))) =>
3;

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 popuiolating database tables. Tables can be cleared by SQL's delete from mytable that deletes all rows in mytable.

Testing performance

The system function time_function(Charstring msg,Charstring fn,Vector args,Integer times)->Real can be used to compare the performance of different implementations of an OSQL function fn. It runs fn(arg) times times. Notice that external factors influence the execution time, so it must be interpreted with care.

Example: Let's define a slow slow_primes function where the argument is declared Number.

create function slow_primes(Number n) -> Bag of Number
as select p
from Number p
where not exists divisors(n)=p

Let's test the performance of primes:

set :tm = 

running time_function on the prime function a couple of times. The time_function prints the total time for all calls to the console but returns the average time per call.

set :tm = time_function("Running prime(9856323)","prime",[9856323],100000);