Skip to main content

OSQL Testing

This is an interactive guide using SA Engine for Wasm. To learn more about what this means you can read the Interactive documentation! section.

A few things to think about when using Wasm guides:

  1. 📋 Execute code blocks in order: Code blocks in interactive guides are often designed to be executed in the order they appear. So if you skip a codeblock subsequent codeblocks might fail due to missing dependencies.
  2. 📝 Edit examples: You can edit the code in the code blocks if you you want to try changing the example.
  3. 📈 Change Visualization: You can change how the output of a query is visualized by specifying //plot: <vis-format> at the top of the code block. Available visualization formats are described in the Visualization chapter of the SA Studio manual.
  4. 🔃 Page reload clears state: The SA Engine instance is cleared every time the page is reloaded. So if you are partway through a guide or tutorial and reload the page, you have to execute the code blocks from the beginning again before you continue with the rest of the guide. Otherwise remaining codeblocks might fail due to missing dependencies.
Guide specification
Guide type:Wasm code
Requirements:None
Recommended reading:None

Introduction

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;
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 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]];
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.

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();