Difference between revisions of "Basic Single-Node Unit Test"

From TinyOS Wiki
Jump to: navigation, search
Line 1: Line 1:
== Your First Test ==
+
= Your First Test =
  
 
Let's create a [[Test Suite]] with a few [http://docs.tinyos.net/index.php/TUnit_Assertions assertions] in it to see what TUnit can do.  This test won't really be testing anything, it's just going to make some assertions and exit.  
 
Let's create a [[Test Suite]] with a few [http://docs.tinyos.net/index.php/TUnit_Assertions assertions] in it to see what TUnit can do.  This test won't really be testing anything, it's just going to make some assertions and exit.  
Line 10: Line 10:
 
   |  |  |-- MyFirstTest
 
   |  |  |-- MyFirstTest
  
=== Test Configuration File ===
+
== MyFirstTestC Configuration File ==
  
 
Create a configuration file, called ''MyFirstTestC.nc'', and start writing it like you would any other TinyOS application. This will be the main entry point for your program.
 
Create a configuration file, called ''MyFirstTestC.nc'', and start writing it like you would any other TinyOS application. This will be the main entry point for your program.
Line 40: Line 40:
 
The signature of the TestCaseC component looks like this:
 
The signature of the TestCaseC component looks like this:
  
   generic configuration TestCaseC() {
+
   ''generic configuration TestCaseC()'' {
 
     provides {
 
     provides {
 
       interface TestCase;
 
       interface TestCase;
Line 52: Line 52:
 
The interface we're interested in to run our basic assertion test is the TestCase interface.  The TestCase interface is defined in [http://tinyos.cvs.sourceforge.net/*checkout*/tinyos/tinyos-2.x-contrib/tunit/tos/interfaces/TestCase.nc?content-type=text%2Fplain tinyos-2.x-contrib/tunit/tos/interfaces/TestCase.nc]:
 
The interface we're interested in to run our basic assertion test is the TestCase interface.  The TestCase interface is defined in [http://tinyos.cvs.sourceforge.net/*checkout*/tinyos/tinyos-2.x-contrib/tunit/tos/interfaces/TestCase.nc?content-type=text%2Fplain tinyos-2.x-contrib/tunit/tos/interfaces/TestCase.nc]:
  
   interface TestCase {
+
   ''interface TestCase'' {
 
    
 
    
 
     event void run();
 
     event void run();
Line 62: Line 62:
 
Pretty simple interface, right?  Your TestCase signals you to run(), and you '''must''' call back when your test is done().  With that in mind, we have a module to build...
 
Pretty simple interface, right?  Your TestCase signals you to run(), and you '''must''' call back when your test is done().  With that in mind, we have a module to build...
  
=== Test Module File ===
+
== MyFirstTestP Module File ==
  
 
As the configuration file above dictated, we need to have a module called MyFirstTestP that ''uses'' the interface ''TestCase as BasicAssertionTest''.  Let's create the MyFirstTestP module now:
 
As the configuration file above dictated, we need to have a module called MyFirstTestP that ''uses'' the interface ''TestCase as BasicAssertionTest''.  Let's create the MyFirstTestP module now:
Line 87: Line 87:
 
There are a couple things to point out here.  
 
There are a couple things to point out here.  
  
Line 0 is the all-important ''TestCase.h'' import. This header file allows TUnit to make [http://docs.tinyos.net/index.php/TUnit_Assertions assertions]. The [http://tinyos.cvs.sourceforge.net/tinyos/tinyos-2.x-contrib/tunit/tos/lib/tunit/TestCase.h?view=markup TestCase.h] file is mixed in with the TUnit embedded library. Don't think about it, just import it and refer back to the list of assertions when necessary.
+
Line 0 is the all-important ''TestCase.h'' import. This header file allows TUnit to make assertions. The [http://tinyos.cvs.sourceforge.net/tinyos/tinyos-2.x-contrib/tunit/tos/lib/tunit/TestCase.h?view=markup TestCase.h] file is mixed in with the TUnit embedded library. Don't think about it, just import it and refer back to the list of [http://docs.tinyos.net/index.php/TUnit_Assertions assertions] when necessary.
  
 
Line 10 is the entry point for our first test. TUnit will tell each test when to run(), and your responsibility is to tell TUnit when that test is done(). TinyOS is ''split-phase'', so there's no way for TUnit to know when your test is actually done without you explicitly saying so.
 
Line 10 is the entry point for our first test. TUnit will tell each test when to run(), and your responsibility is to tell TUnit when that test is done(). TinyOS is ''split-phase'', so there's no way for TUnit to know when your test is actually done without you explicitly saying so.
Line 102: Line 102:
 
   ''you '''MUST''' call back to TUnit telling it your test is done()!''
 
   ''you '''MUST''' call back to TUnit telling it your test is done()!''
  
If we didn't call BasicAssertionTest.done() on line 12, TUnit would have timed out after a default of 1 minute (expandable using [[suite.properties]] rules, described later). This timeout would normally occur if the test locked up your microcontroller, allowing the TUnit framework on the PC side to continue safely executing other tests.
+
If we didn't ''call BasicAssertionTest.done()'' on line 12, TUnit would have timed out after a default of 1 minute (expandable using [[suite.properties]] rules, described later). This timeout would normally occur if the test locked up your microcontroller, allowing the TUnit framework on the PC side to continue safely executing other tests.
  
 
   '''TUnit Tip'''
 
   '''TUnit Tip'''
 
   ''If TUnit times out for apparently no reason,''
 
   ''If TUnit times out for apparently no reason,''
 
   ''you probably forgot issue a done() command somewhere.''
 
   ''you probably forgot issue a done() command somewhere.''
 +
 +
== Makefile ==
 +
 +
Just like every other TinyOS application, this one needs a Makefile so it can be compiled:
 +
 +
  '''tinyos-2.x-contrib/tunit/tests/MyFirstTest/Makefile'''
 +
 
 +
    ''0|''  COMPONENT=MyFirstTestC
 +
    ''1|''  include $(MAKERULES)
 +
 +
Notice we don't try to include anything in the Makefile except the name of the entry test configuration, and the makerules reference.  Don't try to include compile flags or anything else in the Makefile except these two lines.  The [[suite.properties]] file is the place to put compiler options, and is discussed later.
 +
 +
== Run TUnit ==
  
  

Revision as of 09:59, 11 January 2008

Your First Test

Let's create a Test Suite with a few assertions in it to see what TUnit can do. This test won't really be testing anything, it's just going to make some assertions and exit.

First, create a directory to store this test. This directory can go anywhere, but I'll put it in tinyos-2.x-contrib/tunit/tests/MyFirstTest for now:

 tinyos-2.x-contrib
 |-- tunit
 |   |-- tests
 |   |   |-- MyFirstTest

MyFirstTestC Configuration File

Create a configuration file, called MyFirstTestC.nc, and start writing it like you would any other TinyOS application. This will be the main entry point for your program.

 tinyos-2.x-contrib/tunit/tests/MyFirstTest/MyFirstTestC.nc
 
   0|  configuration MyFirstTestC {
   1|  }
   2|  
   3|  implementation {
   4|  
   5|    components new TestCaseC() as BasicAssertionTestC;
   6|  
   7|    components MyFirstTestP;
   8|  
   9|    MyFirstTestP.BasicAssertionTest -> BasicAssertionTestC;
  10|  
  11|  }

On line 5, we create a new instance of TestCaseC. This is saying "Add a new test to our suite". Notice we immediately rename all instances of TestCaseC's as SomeReallyLongAndDescriptiveTestNameC. In this case, we called it BasicAssertionTestC.

 TUnit Tip
 It's important to rename your TestCaseC instances with descriptive names. 
 When that test fails, TUnit will magically tell you the name of the offending 
 test so you can go look it up.

If you're worried about what the TestCaseC component actually refers to, it comes from the TUnit embedded libraries. The library is found in tinyos-2.x-contrib/tunit/tos. TUnit will take care of referencing this entire library for you at compile time so you don't have to think about it.

The signature of the TestCaseC component looks like this:

 generic configuration TestCaseC() {
   provides {
     interface TestCase;
     interface TestControl as SetUpOneTime @atmostonce();
     interface TestControl as SetUp @atmostonce();
     interface TestControl as TearDown @atmostonce();
     interface TestControl as TearDownOneTime @atmostonce();
   }
 }

The interface we're interested in to run our basic assertion test is the TestCase interface. The TestCase interface is defined in tinyos-2.x-contrib/tunit/tos/interfaces/TestCase.nc:

 interface TestCase {
 
   event void run();
   
   async command void done();
 
 }

Pretty simple interface, right? Your TestCase signals you to run(), and you must call back when your test is done(). With that in mind, we have a module to build...

MyFirstTestP Module File

As the configuration file above dictated, we need to have a module called MyFirstTestP that uses the interface TestCase as BasicAssertionTest. Let's create the MyFirstTestP module now:

 tinyos-2.x-contrib/tunit/tests/MyFirstTest/MyFirstTestP.nc
 
   0|  #include "TestCase.h"
   1|  
   2|  configuration MyFirstTestP {
   3|    uses {
   4|      interface TestCase as BasicAssertionTest;
   5|    }
   6|  }
   7|  
   8|  implementation {
   9|  
  10|    event void BasicAssertionTest.run() {
  11|      assertSuccess();
  12|      call BasicAssertionTest.done();
  13|    }
  14|  
  15|  }

There are a couple things to point out here.

Line 0 is the all-important TestCase.h import. This header file allows TUnit to make assertions. The TestCase.h file is mixed in with the TUnit embedded library. Don't think about it, just import it and refer back to the list of assertions when necessary.

Line 10 is the entry point for our first test. TUnit will tell each test when to run(), and your responsibility is to tell TUnit when that test is done(). TinyOS is split-phase, so there's no way for TUnit to know when your test is actually done without you explicitly saying so.

Inside the test, line 11 makes an assertion. In this case, we assertSuccess(), which is the equivalent of saying "If the test made it this far in the code, call it successful."

Each Test Case must make at least one assertion. If a test doesn't make any assertions, TUnit will give you a warning. It's possible your test failed to execute correctly and test what it was supposed to test.

Line 12 is very important. I cannot stress this enough: when your test is done running, you must tell TUnit that the test is done(). This deserves a huge tip comment on it's own:

 TUnit Tip
 TUnit's tells your tests when to run().
 After receiving the signal to run() and your test is completes,
 you MUST call back to TUnit telling it your test is done()!

If we didn't call BasicAssertionTest.done() on line 12, TUnit would have timed out after a default of 1 minute (expandable using suite.properties rules, described later). This timeout would normally occur if the test locked up your microcontroller, allowing the TUnit framework on the PC side to continue safely executing other tests.

 TUnit Tip
 If TUnit times out for apparently no reason,
 you probably forgot issue a done() command somewhere.

Makefile

Just like every other TinyOS application, this one needs a Makefile so it can be compiled:

 tinyos-2.x-contrib/tunit/tests/MyFirstTest/Makefile
 
   0|  COMPONENT=MyFirstTestC
   1|  include $(MAKERULES)

Notice we don't try to include anything in the Makefile except the name of the entry test configuration, and the makerules reference. Don't try to include compile flags or anything else in the Makefile except these two lines. The suite.properties file is the place to put compiler options, and is discussed later.

Run TUnit

State Test

Let's create a test to verify the behavior of the State interface, provided in the tinyos-2.x baseline by the State component (tinyos-2.x/tos/system/StateC.nc). Here's a look at the State interface:

 tinyos-2.x/tos/interfaces/State.nc
 
 interface State {
 
   /**
    * This will allow a state change so long as the current
    * state is S_IDLE.
    * @return SUCCESS if the state is change, FAIL if it isn't
    */
   async command error_t requestState(uint8_t reqState);
   
   /**
    * Force the state machine to go into a certain state,
    * regardless of the current state it's in.
    */
   async command void forceState(uint8_t reqState);
   
   /**
    * Set the current state back to S_IDLE
    */
   async command void toIdle();
   
   /**
    * @return TRUE if the state machine is in S_IDLE
    */
   async command bool isIdle();
   
   /**
    * @return TRUE if the state machine is in the given state
    */
   async command bool isState(uint8_t myState);
   
   /**
    * Get the current state
    */
   async command uint8_t getState();
 
 }


Setup

First, create a new directory to store your test in.

 tinyos-2.x-contrib


Configuration