Characterization Testing with Statistics
Statistics allow TUnit to characterize performance, giving us a better insight into what's happening above and beyond simple threshold and assertion testing. This is very important for embedded systems, where hardware speed and interaction can be critical.
Statistics Introduction
Interface
Statistics, like assertions, must be issued only while a test is running, before calling done(). The Statistics interface looks like this:
interface Statistics { /** * Log some statistics. TUnit takes care of the split-phase stuff so * your application doesn't have to. If you call log() properly, you can * safely assume your statistics will get sent to the computer before * the test is allowed to stop. */ command error_t log(char *units, uint32_t value); }
Configuration Usage
You create a new Statistics logger in your test's configuration file. Below is a truncated example, and the full version of this throughput test can be found in tinyos-2.x-contrib/tunit/tests/tinyos-2.x/tos/chips/cc2420/TestTxThroughputNoCca/.
configuration TestTunitC { } implementation { components TestThroughputP, new TestCaseC() as TestThroughputC, new StatisticsC() as ThroughputStatsC; ... TestThroughputP.Statistics -> ThroughputStatsC; ... }
We can create as many StatisticsC instances as we want and give them unique names. The name we give each StatisticsC instance is the name used at the top of the Statistics graph. Each StatisticsC instance will plot exactly one integer value over time, stored on the computer. Notice the name of the graph on the right is the same as name we gave the StatisticsC component.
Module Usage
Call the Statistics interface a maximum of one time for each Test Suite to plot a single value over time. TUnit will ensure that each Statistics message gets across to the computer before running the next Test Case or exiting the Test Suite. Again, the full test case is in the repository.
module TestTunitP { uses { interface TestCase as TestThroughput; interface Statistics; interface Timer<TMilli>; ... } } implementation ... /***************** Timer Events ****************/ event void Timer.fired() { call RunState.toIdle(); call Statistics.log("[packets/sec]", (uint32_t) ((float) sent / (float) 60)); assertResultIsAbove("Throughput is too low", LOWER_BOUNDS, sent); call TestThroughput.done(); } }
We see that the log command provides a unit for the statistics in [packets/sec], and gives the single-value statistics to be logged in integer form. In this case, we find out how many packets were sent over the course of 60 seconds. The Timer we show takes 60 seconds to fire, during which time the mote is sending packets as fast as possible. When the Timer fires, dividing the number of packets sent by 60 gives us packets per second.
The next line defines a threshold of failure. Our throughput must be above some LOWER_BOUNDS or the test fails.
Finally, after assertions and statistics were logged, the test is done().
TUnit Tip Keep the unit description succinct. A better way to write the units [packets/sec] would have been [pkts/sec]. We use [ and ]'s around our unit descriptions for clarity.