Add test framework using easyunit
authorJim Morris <morris@wolfman.com>
Sat, 12 Sep 2015 21:29:29 +0000 (14:29 -0700)
committerJim Morris <morris@wolfman.com>
Sat, 12 Sep 2015 21:29:29 +0000 (14:29 -0700)
add unit test for TemperatureSwitch
add unit test for some utils functions as examples
added unregister_for_event() to Kernel so modules can be tested
Hack FirmConfigSource to allow configs to be specified in memory for testing
allow config to be deleted
Modified Rakefile to allow buildong of test framework with rake testing=1
modify makefiles to ignore the src/testframework for builds

35 files changed:
Rakefile
build/common.mk
src/libs/Config.cpp
src/libs/Config.h
src/libs/ConfigSource.h
src/libs/ConfigSources/FirmConfigSource.cpp
src/libs/ConfigSources/FirmConfigSource.h
src/libs/Kernel.cpp
src/libs/Kernel.h
src/modules/tools/temperatureswitch/TemperatureSwitch.cpp
src/modules/tools/temperatureswitch/TemperatureSwitch.h
src/testframework/Test_kernel.cpp [new file with mode: 0644]
src/testframework/Test_kernel.h [new file with mode: 0644]
src/testframework/Test_main.cpp [new file with mode: 0644]
src/testframework/easyunit/defaulttestprinter.cpp [new file with mode: 0644]
src/testframework/easyunit/defaulttestprinter.h [new file with mode: 0644]
src/testframework/easyunit/simplestring.cpp [new file with mode: 0644]
src/testframework/easyunit/simplestring.h [new file with mode: 0644]
src/testframework/easyunit/test.cpp [new file with mode: 0644]
src/testframework/easyunit/test.h [new file with mode: 0644]
src/testframework/easyunit/testcase.cpp [new file with mode: 0644]
src/testframework/easyunit/testcase.h [new file with mode: 0644]
src/testframework/easyunit/testharness.h [new file with mode: 0644]
src/testframework/easyunit/testpartresult.cpp [new file with mode: 0644]
src/testframework/easyunit/testpartresult.h [new file with mode: 0644]
src/testframework/easyunit/testprinter.h [new file with mode: 0644]
src/testframework/easyunit/testregistry.cpp [new file with mode: 0644]
src/testframework/easyunit/testregistry.h [new file with mode: 0644]
src/testframework/easyunit/testresult.cpp [new file with mode: 0644]
src/testframework/easyunit/testresult.h [new file with mode: 0644]
src/testframework/easyunit/testrunner.cpp [new file with mode: 0644]
src/testframework/easyunit/testrunner.h [new file with mode: 0644]
src/testframework/prettyprint.hpp [new file with mode: 0644]
src/testframework/unittests/libs/TEST_utils.cpp [new file with mode: 0644]
src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp [new file with mode: 0644]

index 9c7aa71..105e13c 100644 (file)
--- a/Rakefile
+++ b/Rakefile
@@ -4,6 +4,7 @@ require 'fileutils'
 
 verbose(ENV['verbose'] == '1')
 DEBUG = ENV['debug'] == '1'
+TESTING = ENV['testing'] == '1'
 
 def pop_path(path)
   Pathname(path).each_filename.to_a[1..-1]
@@ -73,8 +74,10 @@ SIZE = "#{TOOLSBIN}size"
 
 # include a defaults file if present
 load 'rakefile.defaults' if File.exists?('rakefile.defaults')
+if TESTING
+  BUILDTYPE= 'Testing'
 
-if DEBUG
+elsif DEBUG
   BUILDTYPE= 'Debug'
   ENABLE_DEBUG_MONITOR= '0'
 end
@@ -89,7 +92,9 @@ ENABLE_DEBUG_MONITOR = ENV['ENABLE_DEBUG_MONITOR'] || '0' unless defined? ENABLE
 DEFAULT_SERIAL_BAUD_RATE= ENV['BAUDRATE'] || '115200' unless defined? DEFAULT_SERIAL_BAUD_RATE
 
 # set to true to eliminate all the network code
-NONETWORK= false unless defined? NONETWORK
+unless defined? NONETWORK
+  NONETWORK= false || TESTING
+end
 
 # list of modules to exclude, include directory it is in
 EXCLUDE_MODULES= %w(tools/touchprobe) unless defined? EXCLUDE_MODULES
@@ -109,9 +114,18 @@ else
   nonetwork= false
 end
 
-SRC = FileList['src/**/*.{c,cpp}'].exclude(/#{excludes.join('|')}/)
-
-puts "WARNING Excluding modules: #{EXCLUDE_MODULES.join(' ')}" unless exclude_defines.empty?
+if TESTING
+  # add modules to be tested here
+  TESTMODULES= %w(tools/temperatureswitch)
+  puts "Modules under test: #{TESTMODULES}"
+  excludes << %w(Kernel.cpp main.cpp) # we replace these with mock versions in testframework
+  testmodules= FileList['src/testframework/*.{c,cpp}', 'src/testframework/easyunit/*.{c,cpp}', 'src/modules/communication/SerialConsole.cpp', 'src/modules/communication/utils/Gcode.cpp'].include(TESTMODULES.collect { |e| "src/modules/#{e}/**/*.{c,cpp}"}).include(TESTMODULES.collect { |e| "src/testframework/unittests/#{e}/*.{c,cpp}"})
+  SRC = FileList['src/libs/**/*.{c,cpp}'].exclude(/#{excludes.join('|')}/) + testmodules
+else
+  excludes << %w(testframework)
+  SRC = FileList['src/**/*.{c,cpp}'].exclude(/#{excludes.join('|')}/)
+  puts "WARNING Excluding modules: #{EXCLUDE_MODULES.join(' ')}" unless exclude_defines.empty?
+end
 
 OBJDIR = 'OBJ'
 OBJ = SRC.collect { |fn| File.join(OBJDIR, pop_path(File.dirname(fn)), File.basename(fn).ext('o')) } +
@@ -155,6 +169,10 @@ when 'checked'
   OPTIMIZATION = 2
   MRI_ENABLE = 1
   MRI_SEMIHOST_STDIO = 1 unless defined? MRI_SEMIHOST_STDIO
+when 'testing'
+  OPTIMIZATION = 0
+  MRI_ENABLE = 1
+  MRI_SEMIHOST_STDIO = 0 unless defined? MRI_SEMIHOST_STDIO
 end
 
 MRI_ENABLE = 1  unless defined? MRI_ENABLE # set to 0 to disable MRI
@@ -177,8 +195,9 @@ DEFINES= defines.join(' ')
 
 # Compiler flags used to enable creation of header dependencies.
 DEPFLAGS = '-MMD '
-CFLAGS = DEPFLAGS + "-Wall -Wextra -Wno-unused-parameter -Wcast-align -Wpointer-arith -Wredundant-decls -Wcast-qual -Wcast-align -O#{OPTIMIZATION} -g3 -mcpu=cortex-m3 -mthumb -mthumb-interwork -ffunction-sections -fdata-sections  -fno-exceptions -fno-delete-null-pointer-checks"
-CPPFLAGS = CFLAGS + ' -fno-rtti -std=gnu++11'
+CFLAGS = DEPFLAGS + "-Wall -Wextra -Wno-unused-parameter -Wcast-align -Wpointer-arith -Wredundant-decls -Wcast-qual -Wcast-align -O#{OPTIMIZATION} -g3 -mcpu=cortex-m3 -mthumb -mthumb-interwork -ffunction-sections -fdata-sections -fno-delete-null-pointer-checks"
+CPPFLAGS = CFLAGS + ' -fno-rtti -std=gnu++11 -fno-exceptions'
+CXXFLAGS = CFLAGS + ' -fno-rtti -std=gnu++11 -fexceptions' # used for a .cxx file that needs to be compiled with exceptions
 
 MRI_WRAPS = MRI_ENABLE == 1 ? ',--wrap=_read,--wrap=_write,--wrap=semihost_connected' : ''
 
@@ -251,7 +270,7 @@ file MBED_LIB do
 end
 
 file "#{OBJDIR}/mbed_custom.o" => ['./build/mbed_custom.cpp'] do |t|
-  puts "Compiling #{t.source}"
+  puts "Compiling mbed_custom.cpp"
   sh "#{CCPP} #{CPPFLAGS} #{INCLUDE} #{DEFINES} -c -o #{t.name} #{t.prerequisites[0]}"
 end
 
@@ -276,6 +295,11 @@ rule '.o' => lambda{ |objfile| obj2src(objfile, 'cpp') } do |t|
   sh "#{CCPP} #{CPPFLAGS} #{INCLUDE} #{DEFINES} #{VERSION} -c -o #{t.name} #{t.source}"
 end
 
+rule '.o' => lambda{ |objfile| obj2src(objfile, 'cxx') } do |t|
+  puts "Compiling #{t.source}"
+  sh "#{CCPP} #{CXXFLAGS} #{INCLUDE} #{DEFINES} #{VERSION} -c -o #{t.name} #{t.source}"
+end
+
 rule '.o' => lambda{ |objfile| obj2src(objfile, 'c') } do |t|
   puts "Compiling #{t.source}"
   sh "#{CC} #{CFLAGS} #{INCLUDE} #{DEFINES} #{VERSION} -c -o #{t.name} #{t.source}"
index 9d30f28..0419301 100755 (executable)
@@ -79,12 +79,15 @@ OUTDIR=../$(DEVICE)
 CSRCS1 = $(wildcard $(SRC)/*.c $(SRC)/*/*.c $(SRC)/*/*/*.c $(SRC)/*/*/*/*.c $(SRC)/*/*/*/*/*.c $(SRC)/*/*/*/*/*/*.c)
 # Totally exclude network if NONETWORK is defined
 ifeq "$(NONETWORK)" "1"
-CSRCS = $(filter-out $(SRC)/libs/Network/%,$(CSRCS1))
+CSRCS2 = $(filter-out $(SRC)/libs/Network/%,$(CSRCS1))
 DEFINES += -DNONETWORK
 else
-CSRCS = $(CSRCS1)
+CSRCS2 = $(CSRCS1)
 endif
 
+# do not compile the src/testframework as that can only be done with rake
+CSRCS = $(filter-out $(SRC)/testframework/%,$(CSRCS2))
+
 ifeq "$(DISABLEMSD)" "1"
 DEFINES += -DDISABLEMSD
 endif
@@ -104,9 +107,12 @@ endif
 # uppercase function
 uc = $(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$1))))))))))))))))))))))))))
 EXL = $(patsubst %,$(SRC)/modules/%/%,$(EXCLUDED_MODULES))
-CPPSRCS = $(filter-out $(EXL),$(CPPSRCS2))
+CPPSRCS3 = $(filter-out $(EXL),$(CPPSRCS2))
 DEFINES += $(call uc, $(subst /,_,$(patsubst %,-DNO_%,$(EXCLUDED_MODULES))))
 
+# do not compile the src/testframework as that can only be done with rake
+CPPSRCS = $(filter-out $(SRC)/testframework/%,$(CPPSRCS3))
+
 # List of the objects files to be compiled/assembled
 OBJECTS = $(patsubst %.c,$(OUTDIR)/%.o,$(CSRCS)) $(patsubst %.s,$(OUTDIR)/%.o,$(patsubst %.S,$(OUTDIR)/%.o,$(ASRCS))) $(patsubst %.cpp,$(OUTDIR)/%.o,$(CPPSRCS))
 
index b4e11b7..af14f8b 100644 (file)
@@ -48,6 +48,20 @@ Config::Config()
         this->config_sources.push_back( fcs );
 }
 
+Config::Config(ConfigSource *cs)
+{
+    this->config_cache = NULL;
+    this->config_sources.push_back( cs );
+}
+
+Config::~Config()
+{
+    config_cache_clear();
+    for(auto i : this->config_sources) {
+        delete i;
+    }
+}
+
 void Config::on_module_loaded() {}
 
 void Config::on_console_line_received( void *argument ) {}
index ac19973..d7bc7cc 100644 (file)
@@ -20,6 +20,8 @@ class ConfigCache;
 class Config : public Module {
     public:
         Config();
+        Config(ConfigSource*);
+        ~Config();
 
         void on_module_loaded();
         void on_console_line_received( void* argument );
index b5c8684..2ac2d02 100644 (file)
@@ -18,6 +18,7 @@ class ConfigCache;
 class ConfigSource {
     public:
         ConfigSource(){}
+        virtual ~ConfigSource(){}
 
         // Read each value, and append it as a ConfigValue to the config_cache we were passed
         virtual void transfer_values_to_cache( ConfigCache* ) = 0;
index aa9c13a..d5c362f 100644 (file)
@@ -20,20 +20,27 @@ using namespace std;
 extern char _binary_config_default_start;
 extern char _binary_config_default_end;
 
-
 FirmConfigSource::FirmConfigSource(const char* name){
     this->name_checksum = get_checksum(name);
+    this->start= &_binary_config_default_start;
+    this->end= &_binary_config_default_end;
+}
+
+FirmConfigSource::FirmConfigSource(const char* name, const char *start, const char *end){
+    this->name_checksum = get_checksum(name);
+    this->start= start;
+    this->end= end;
 }
 
 // Transfer all values found in the file to the passed cache
 void FirmConfigSource::transfer_values_to_cache( ConfigCache* cache ){
 
-    char* p = &_binary_config_default_start;
+    const char* p = this->start;
     // For each line
-    while( p < &_binary_config_default_end ){
+    while( p < this->end ){
         // find eol
-        char *eol= p;
-        while(eol < &_binary_config_default_end) {
+        const char *eol= p;
+        while(eol < this->end) {
             if(*eol++ == '\n') break;
         }
         string line(p, eol-p);
@@ -59,12 +66,12 @@ string FirmConfigSource::read( uint16_t check_sums[3] ){
 
     string value = "";
 
-    char* p = &_binary_config_default_start;
+    const char* p = this->start;
     // For each line
-    while( p < &_binary_config_default_end ){
+    while( p < this->end ){
         // find eol
-        char *eol= p;
-        while(eol < &_binary_config_default_end) {
+        const char *eol= p;
+        while(eol < this->end) {
             if(*eol++ == '\n') break;
         }
         string line(p, eol-p);
index f662e37..ea7e810 100644 (file)
@@ -16,14 +16,19 @@ class ConfigCache;
 using namespace std;
 #include <string>
 
-class FirmConfigSource : public ConfigSource {
-    public:
-        FirmConfigSource(const char* name);
-        void transfer_values_to_cache( ConfigCache* cache );
-        bool is_named( uint16_t check_sum );
-        bool write( string setting, string value );
-        string read( uint16_t check_sums[3] );
-
+class FirmConfigSource : public ConfigSource
+{
+public:
+    FirmConfigSource(const char *name);
+    FirmConfigSource(const char* name, const char *start, const char *end);
+
+    void transfer_values_to_cache( ConfigCache *cache );
+    bool is_named( uint16_t check_sum );
+    bool write( string setting, string value );
+    string read( uint16_t check_sums[3] );
+
+private:
+    const char *start, *end;
 };
 
 
index 3167582..5583b44 100644 (file)
@@ -151,16 +151,29 @@ void Kernel::register_for_event(_EVENT_ENUM id_event, Module *mod){
     this->hooks[id_event].push_back(mod);
 }
 
-// Call a specific event without arguments
-void Kernel::call_event(_EVENT_ENUM id_event){
+// Call a specific event with an argument
+void Kernel::call_event(_EVENT_ENUM id_event, void * argument){
     for (auto m : hooks[id_event]) {
-        (m->*kernel_callback_functions[id_event])(this);
+        (m->*kernel_callback_functions[id_event])(argument);
     }
 }
 
-// Call a specific event with an argument
-void Kernel::call_event(_EVENT_ENUM id_event, void * argument){
+// These are used by tests to test for various things. basically mocks
+bool Kernel::kernel_has_event(_EVENT_ENUM id_event, Module *mod)
+{
     for (auto m : hooks[id_event]) {
-        (m->*kernel_callback_functions[id_event])(argument);
+        if(m == mod) return true;
     }
+    return false;
 }
+
+void Kernel::unregister_for_event(_EVENT_ENUM id_event, Module *mod)
+{
+    for (auto i = hooks[id_event].begin(); i != hooks[id_event].end(); ++i) {
+        if(*i == mod) {
+            hooks[id_event].erase(i);
+            return;
+        }
+    }
+}
+
index 520597d..948ba2e 100644 (file)
@@ -39,8 +39,10 @@ class Kernel {
 
         void add_module(Module* module);
         void register_for_event(_EVENT_ENUM id_event, Module *module);
-        void call_event(_EVENT_ENUM id_event);
-        void call_event(_EVENT_ENUM id_event, void * argument);
+        void call_event(_EVENT_ENUM id_event, void * argument= nullptr);
+
+        bool kernel_has_event(_EVENT_ENUM id_event, Module *mod);
+        void unregister_for_event(_EVENT_ENUM id_event, Module *module);
 
         // These modules are available to all other modules
         SerialConsole*    serial;
index 151e4f2..b960ec4 100755 (executable)
@@ -27,6 +27,7 @@ Author: Michael Hackney, mhackney@eclecticangler.com
 #include "PublicData.h"
 #include "StreamOutputPool.h"
 #include "TemperatureControlPool.h"
+#include "mri.h"
 
 #define temperatureswitch_checksum                    CHECKSUM("temperatureswitch")
 #define enable_checksum                               CHECKSUM("enable")
@@ -45,6 +46,12 @@ TemperatureSwitch::TemperatureSwitch()
 {
 }
 
+TemperatureSwitch::~TemperatureSwitch()
+{
+    THEKERNEL->unregister_for_event(ON_SECOND_TICK, this);
+    THEKERNEL->unregister_for_event(ON_GCODE_RECEIVED, this);
+}
+
 // Load module
 void TemperatureSwitch::on_module_loaded()
 {
@@ -59,12 +66,11 @@ void TemperatureSwitch::on_module_loaded()
     delete this;
 }
 
-
-bool TemperatureSwitch::load_config(uint16_t modcs)
+TemperatureSwitch* TemperatureSwitch::load_config(uint16_t modcs)
 {
     // see if enabled
     if (!THEKERNEL->config->value(temperatureswitch_checksum, modcs, enable_checksum)->by_default(false)->as_bool()) {
-        return false;
+        return nullptr;
     }
 
     // create a temperature control and load settings
@@ -78,19 +84,23 @@ bool TemperatureSwitch::load_config(uint16_t modcs)
         designator= s[0];
     }
 
-    if(designator == 0) return false; // no designator then not valid
+    if(designator == 0) return nullptr; // no designator then not valid
 
     // create a new temperature switch module
     TemperatureSwitch *ts= new TemperatureSwitch();
 
+    //__debugbreak();
+
     // make a list of temperature controls with matching designators with the same first letter
     // the list is added t the controllers vector given below
-    std::vector<struct pad_temperature> controllers;
-    bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, &controllers);
-    if (ok) {
-        for (auto &c : controllers) {
-            if (c.designator[0] == designator) {
-                ts->temp_controllers.push_back(c.id);
+    {
+        std::vector<struct pad_temperature> controllers;
+        bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, &controllers);
+        if (ok) {
+            for (auto &c : controllers) {
+                if (c.designator[0] == designator) {
+                    ts->temp_controllers.push_back(c.id);
+                }
             }
         }
     }
@@ -98,7 +108,7 @@ bool TemperatureSwitch::load_config(uint16_t modcs)
     // if we don't have any matching controllers, then not valid
     if (ts->temp_controllers.empty()) {
         delete ts;
-        return false;
+        return nullptr;
     }
 
     // load settings from config file
@@ -109,7 +119,7 @@ bool TemperatureSwitch::load_config(uint16_t modcs)
         if(s.empty()) {
             // no switch specified so invalid entry
             delete this;
-            return false;
+            return nullptr;
         }
     }
 
@@ -147,7 +157,7 @@ bool TemperatureSwitch::load_config(uint16_t modcs)
     if(this->arm_mcode != 0) {
         ts->register_for_event(ON_GCODE_RECEIVED);
     }
-    return true;
+    return ts;
 }
 
 void TemperatureSwitch::on_gcode_received(void *argument)
index 4750cbb..99c8286 100755 (executable)
@@ -26,16 +26,18 @@ class TemperatureSwitch : public Module
 {
     public:
         TemperatureSwitch();
+        ~TemperatureSwitch();
         void on_module_loaded();
         void on_second_tick(void *argument);
         void on_gcode_received(void *argument);
+        TemperatureSwitch* load_config(uint16_t modcs);
+
+        bool is_armed() const { return armed; }
 
     private:
         enum TRIGGER_TYPE {LEVEL, RISING, FALLING};
         enum STATE {NONE, HIGH_TEMP, LOW_TEMP};
 
-        bool load_config(uint16_t modcs);
-
         // get the highest temperature from the set of configured temperature controllers
         float get_highest_temperature();
 
diff --git a/src/testframework/Test_kernel.cpp b/src/testframework/Test_kernel.cpp
new file mode 100644 (file)
index 0000000..f128967
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+      This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+      Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+      Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+      You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+This is aprt of the Smoothie test framework, it generates a Mockable Kernl so kernel calls can be tested for
+*/
+
+#include "libs/Kernel.h"
+#include "libs/Module.h"
+#include "libs/Config.h"
+#include "libs/nuts_bolts.h"
+#include "libs/SlowTicker.h"
+#include "libs/Adc.h"
+#include "libs/StreamOutputPool.h"
+#include <mri.h>
+#include "checksumm.h"
+#include "ConfigValue.h"
+
+#include "libs/StepTicker.h"
+#include "libs/PublicData.h"
+#include "modules/communication/SerialConsole.h"
+#include "modules/communication/GcodeDispatch.h"
+#include "modules/robot/Planner.h"
+#include "modules/robot/Robot.h"
+#include "modules/robot/Stepper.h"
+#include "modules/robot/Conveyor.h"
+#include "modules/robot/Pauser.h"
+
+#include "Config.h"
+#include "FirmConfigSource.h"
+
+#include <malloc.h>
+#include <array>
+#include <functional>
+#include <map>
+
+Kernel* Kernel::instance;
+
+// The kernel is the central point in Smoothie : it stores modules, and handles event calls
+Kernel::Kernel(){
+    instance= this; // setup the Singleton instance of the kernel
+
+    // serial first at fixed baud rate (DEFAULT_SERIAL_BAUD_RATE) so config can report errors to serial
+    // Set to UART0, this will be changed to use the same UART as MRI if it's enabled
+    this->serial = new SerialConsole(USBTX, USBRX, DEFAULT_SERIAL_BAUD_RATE);
+
+    // Config next, but does not load cache yet
+    // loads config from in memory source for test framework must be loaded by test
+    this->config = nullptr;
+
+    this->streams = new StreamOutputPool();
+    this->streams->append_stream(this->serial);
+
+    this->current_path   = "/";
+
+    // Configure UART depending on MRI config
+    // Match up the SerialConsole to MRI UART. This makes it easy to use only one UART for both debug and actual commands.
+    NVIC_SetPriorityGrouping(0);
+    NVIC_SetPriority(UART0_IRQn, 5);
+}
+
+// Add a module to Kernel. We don't actually hold a list of modules we just call its on_module_loaded
+void Kernel::add_module(Module* module){
+    module->on_module_loaded();
+}
+
+// Adds a hook for a given module and event
+void Kernel::register_for_event(_EVENT_ENUM id_event, Module *mod){
+    this->hooks[id_event].push_back(mod);
+}
+
+static std::map<_EVENT_ENUM, std::function<void(void*)> > event_callbacks;
+
+// Call a specific event with an argument
+void Kernel::call_event(_EVENT_ENUM id_event, void * argument){
+    for (auto m : hooks[id_event]) {
+        (m->*kernel_callback_functions[id_event])(argument);
+    }
+    if(event_callbacks.find(id_event) != event_callbacks.end()){
+        event_callbacks[id_event](argument);
+    }else{
+        printf("call_event for event: %d not handled\n", id_event);
+    }
+}
+
+// These are used by tests to test for various things. basically mocks
+bool Kernel::kernel_has_event(_EVENT_ENUM id_event, Module *mod)
+{
+    for (auto m : hooks[id_event]) {
+        if(m == mod) return true;
+    }
+    return false;
+}
+
+void Kernel::unregister_for_event(_EVENT_ENUM id_event, Module *mod)
+{
+    for (auto i = hooks[id_event].begin(); i != hooks[id_event].end(); ++i) {
+        if(*i == mod) {
+            hooks[id_event].erase(i);
+            return;
+        }
+    }
+}
+
+void test_kernel_setup_config(const char* start, const char* end)
+{
+    THEKERNEL->config= new Config(new FirmConfigSource("rom", start, end) );
+    // Pre-load the config cache
+    THEKERNEL->config->config_cache_load();
+}
+
+void test_kernel_teardown()
+{
+    delete THEKERNEL->config;
+    THEKERNEL->config= nullptr;
+    event_callbacks.clear();
+}
+
+void test_kernel_trap_event(_EVENT_ENUM id_event, std::function<void(void*)> fnc)
+{
+    event_callbacks[id_event]= fnc;
+}
+
+void test_kernel_untrap_event(_EVENT_ENUM id_event)
+{
+    event_callbacks.erase(id_event);
+}
diff --git a/src/testframework/Test_kernel.h b/src/testframework/Test_kernel.h
new file mode 100644 (file)
index 0000000..1c6a0c5
--- /dev/null
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "Module.h"
+#include <functional>
+
+void test_kernel_setup_config(const char* start, const char* end);
+void test_kernel_teardown();
+void test_kernel_trap_event(_EVENT_ENUM id_event, std::function<void(void*)> fnc);
+void test_kernel_untrap_event(_EVENT_ENUM id_event);
diff --git a/src/testframework/Test_main.cpp b/src/testframework/Test_main.cpp
new file mode 100644 (file)
index 0000000..e1ee90d
--- /dev/null
@@ -0,0 +1,35 @@
+#include <vector>
+#include <string>
+#include <iostream>
+
+#include "utils.h"
+#include "SerialConsole.h"
+#include "gpio.h"
+
+GPIO leds[5] = {
+    GPIO(P1_18),
+    GPIO(P1_19),
+    GPIO(P1_20),
+    GPIO(P1_21),
+    GPIO(P4_28)
+};
+
+#include "easyunit/testharness.h"
+#include "easyunit/test.h"
+
+int main( )
+{
+    Kernel* kernel = new Kernel();
+
+    printf("Starting tests...\n");
+
+    TestRegistry::runAndPrint();
+
+    kernel->serial->printf("Done\n");
+
+    // drop back into DFU upload
+    kernel->serial->printf("Entering DFU flash mode...\n");
+    system_reset(true);
+
+    for(;;) {}
+}
diff --git a/src/testframework/easyunit/defaulttestprinter.cpp b/src/testframework/easyunit/defaulttestprinter.cpp
new file mode 100644 (file)
index 0000000..a7ee684
--- /dev/null
@@ -0,0 +1,215 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#include "defaulttestprinter.h"\r
+\r
+#include "testpartresult.h"\r
+\r
+#include <stdio.h>\r
+\r
+\r
+DefaultTestPrinter::DefaultTestPrinter()\r
+: testsTotal_(0),testFailuresTotal_(0),failuresTotal_(0),\r
+       level_(normal), showSuccessDetail_(false), output_(stdout)\r
+{\r
+}\r
+\r
+DefaultTestPrinter::~DefaultTestPrinter()\r
+{\r
+}\r
+\r
+void DefaultTestPrinter::print(const TestResult *testResult)\r
+{\r
+       int failures;\r
+       int successes;\r
+       int errors;\r
+       SimpleString state;\r
+       SimpleString name;\r
+       TestCase *testCase = testResult->getTestCases();\r
+       int size = testResult->getTestCaseCount();\r
+\r
+       printHeader(testResult);\r
+\r
+       if (testResult->getTestCaseRanCount() == 0) {\r
+               fprintf(output_,"\nNo test ran\n");\r
+       }\r
+\r
+       for (int i=0;i<size;i++) {\r
+\r
+         if (testCase->ran()) {\r
+\r
+               name = testCase->getName();\r
+               failures = testCase->getFailuresCount();\r
+               successes = testCase->getSuccessesCount();\r
+               errors = testCase->getErrorsCount();\r
+\r
+               if (failures > 0 || errors > 0) {\r
+                       state = "FAILED";\r
+               }\r
+               else {\r
+                       state = "SUCCEEDED";\r
+               }\r
+\r
+               fprintf(output_, "\n\nTest case \"%s\" %s with %d error(s), %d failure(s) and %d success(es): \n",name.asCharString(),state.asCharString(),errors,failures,successes);\r
+\r
+               printTests(testCase);\r
+               }\r
+\r
+               testCase = testCase->getNext();\r
+       }\r
+}\r
+\r
+void DefaultTestPrinter::setHeaderLevel(headerLevel level)\r
+{\r
+       level_ = level;\r
+}\r
+\r
+void DefaultTestPrinter::showSuccessDetail(bool show)\r
+{\r
+       showSuccessDetail_ = show;\r
+}\r
+\r
+void DefaultTestPrinter::setOutput(FILE *output)\r
+{\r
+       output_ = output;\r
+}\r
+\r
+void DefaultTestPrinter::printHeader(const TestResult *testResult)\r
+{\r
+       fprintf(output_ , "-- EasyUnit Results --\n");\r
+\r
+       if (level_ != off) {\r
+               fprintf(output_ , "\nSUMMARY\n\n");\r
+               fprintf(output_ , "Test summary: ");\r
+\r
+               if (testResult->getErrors() > 0 || testResult->getFailures() > 0) {\r
+                       fprintf(output_ , "FAIL\n");\r
+               }\r
+               else {\r
+                       fprintf(output_ , "SUCCESS\n");\r
+               }\r
+\r
+               if (level_ == normal) {\r
+                       printNormalHeader(testResult);\r
+               }\r
+               else {\r
+                       printCompleteHeader(testResult);\r
+               }\r
+       }\r
+\r
+       fprintf(output_ , "\n");\r
+       fprintf(output_ , "\nDETAILS");\r
+}\r
+\r
+void DefaultTestPrinter::printCompleteHeader(const TestResult *testResult)\r
+{\r
+       fprintf(output_ , "Number of test cases: %d\n",testResult->getTestCaseCount());\r
+       fprintf(output_ , "Number of test cases ran: %d\n",testResult->getTestCaseRanCount());\r
+       fprintf(output_ , "Test cases that succeeded: %d\n",testResult->getSuccesses());\r
+       fprintf(output_ , "Test cases with errors: %d\n",testResult->getErrors());\r
+       fprintf(output_ , "Test cases that failed: %d\n",testResult->getFailures());\r
+       fprintf(output_ , "Number of tests ran: %d\n",testResult->getTestRanCount());\r
+       fprintf(output_ , "Tests that succeeded: %d\n",testResult->getTotalSuccesses());\r
+       fprintf(output_ , "Tests with errors: %d\n",testResult->getTotalErrors());\r
+       fprintf(output_ , "Tests that failed: %d\n",testResult->getTotalFailures());\r
+\r
+}\r
+\r
+void DefaultTestPrinter::printNormalHeader(const TestResult *testResult)\r
+{\r
+       fprintf(output_ , "Number of test cases ran: %d\n",testResult->getTestCaseRanCount());\r
+       fprintf(output_ , "Test cases that succeeded: %d\n",testResult->getSuccesses());\r
+       fprintf(output_ , "Test cases with errors: %d\n",testResult->getErrors());\r
+       fprintf(output_ , "Test cases that failed: %d\n",testResult->getFailures());\r
+}\r
+\r
+void DefaultTestPrinter::printTests(TestCase *testCase)\r
+{\r
+       const char *indent = " ";\r
+       Test *test = testCase->getTests();\r
+       int size = testCase->getTestsCount();\r
+       SimpleString state;\r
+\r
+\r
+\r
+       for (int i=0;i<size;i++) {\r
+               if (test->getFailuresCount() > 0 || test->getErrorsCount() > 0) {\r
+                       state = "FAILED :";\r
+               }\r
+               else {\r
+                       state = "SUCCEEDED!";\r
+               }\r
+\r
+               fprintf(output_, "%s Test \"%s\" %s\n",indent,test->getTestName().asCharString(),state.asCharString());\r
+               printResults(test);\r
+               test = test->getNext();\r
+       }\r
+}\r
+\r
+void DefaultTestPrinter::printResults(Test *test)\r
+{\r
+       const char *indent = "    ";\r
+       TestPartResult *testPR = test->getTestPartResult();\r
+       int size = test->getFailuresCount() + test->getSuccessesCount() + test->getErrorsCount();\r
+       int type;\r
+\r
+       for (int i=0;i<size;i++) {\r
+\r
+               type = testPR->getType();\r
+\r
+               if (type == failure) {\r
+                       fprintf (output_, "%s%s%s%s%s%ld%s%s\n",\r
+                               indent,\r
+                               "Failure: \"",\r
+                               testPR->getMessage().asCharString (),\r
+                               "\" " ,\r
+                               "line ",\r
+                               testPR->getLineNumber(),\r
+                               " in ",\r
+                               testPR->getFileName().asCharString ());\r
+               }\r
+               else if (type == error) {\r
+                       fprintf (output_, "%s%s%s%s%s%s\n",\r
+                               indent,\r
+                               "Error in ",\r
+                               test->getTestName().asCharString(),\r
+                               ": \"",\r
+                               testPR->getMessage().asCharString (),\r
+                               "\"");\r
+               }\r
+               else if (type == success && showSuccessDetail_) {\r
+                       fprintf (output_, "%s%s%s%s%s%ld%s%s\n",\r
+                               indent,\r
+                               "Success: \"",\r
+                               testPR->getMessage().asCharString (),\r
+                               "\" " ,\r
+                               "line ",\r
+                               testPR->getLineNumber(),\r
+                               " in ",\r
+                               testPR->getFileName().asCharString ());\r
+               }\r
+               testPR = testPR->getNext();\r
+       }\r
+}\r
+\r
+\r
+\r
diff --git a/src/testframework/easyunit/defaulttestprinter.h b/src/testframework/easyunit/defaulttestprinter.h
new file mode 100644 (file)
index 0000000..3824430
--- /dev/null
@@ -0,0 +1,125 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#ifndef DEFAULTTESTPRINTER_H\r
+#define DEFAULTTESTPRINTER_H\r
+\r
+#include "testprinter.h"\r
+#include "testcase.h"\r
+#include "test.h"\r
+#include "testresult.h"\r
+#include <stdio.h>\r
+\r
+\r
+/**\r
+ * Complete header level means that a header will be printed\r
+ * before the test details with all information available in\r
+ * the test result.\r
+ * \r
+ * Normal header level means that a header will be printed\r
+ * before the test details with the most useful information\r
+ * available in the test result.\r
+ *\r
+ * Off header level means that no header will be printed\r
+ * before the test details.\r
+ * \r
+ * Whatever the level, there will always be a clear indication\r
+ * telling if there was a failure/error or not at the global\r
+ * level.\r
+ */\r
+enum headerLevel {complete,normal,off};\r
+\r
+/**\r
+ * This is the default testprinter used by easyunit testregistry\r
+ * when the user calls the runAndPrint() method without specifying\r
+ * a testprinter.\r
+ *\r
+ * This testprinter writes plain text result to any supplied file.\r
+ * The default file is the standard output.\r
+ *\r
+ * You may customize the outpur format by specifying the header level\r
+ * and if you wish the testprinter to print details about each success.\r
+ *\r
+ * The default header level is normal and by default, the testprinter\r
+ * does not print details about each success.\r
+ */\r
+class DefaultTestPrinter : public TestPrinter\r
+{\r
+       public:\r
+       \r
+       /**\r
+        * Default constructor that sets the header level\r
+        * to normal and the output source to the standard\r
+        * output.\r
+        */\r
+               DefaultTestPrinter();\r
+               \r
+       /**\r
+        * Empty destructor.\r
+        */\r
+               virtual ~DefaultTestPrinter();\r
+       /**\r
+        * Prints a header depending of the header level and\r
+        * details about each test to the output_.\r
+        *\r
+        * @param testResult Results of all tests that were ran.\r
+        */\r
+               virtual void print(const TestResult *testResult);       \r
+               \r
+       /**\r
+        * Set the header level of the printer.\r
+        *\r
+        * @param level Header level that will be used during print()\r
+        */\r
+               void setHeaderLevel(headerLevel level);\r
+               \r
+       /**\r
+        * Set whether or not the printer should display the details\r
+        * of test that succeeded.\r
+        *\r
+        * @param show Set to true to display details about success\r
+        */\r
+               void showSuccessDetail(bool show);\r
+               \r
+       /**\r
+        * Set the output to which the printer will print results.\r
+        *\r
+        * @param output Output used to print the results\r
+        */\r
+               void setOutput(FILE *output);\r
+               \r
+       protected:\r
+               virtual void printHeader(const TestResult *testResult);\r
+               virtual void printTests(TestCase *testCase);\r
+               virtual void printResults(Test *test);\r
+               virtual void printCompleteHeader(const TestResult *testResult);\r
+               virtual void printNormalHeader(const TestResult *testResult);\r
+               int testsTotal_;\r
+               int testFailuresTotal_;\r
+               int failuresTotal_;\r
+               headerLevel level_;\r
+               bool showSuccessDetail_;\r
+               FILE *output_;\r
+};\r
+\r
+#endif // DEFAULTTESTPRINTER_H\r
+\r
diff --git a/src/testframework/easyunit/simplestring.cpp b/src/testframework/easyunit/simplestring.cpp
new file mode 100644 (file)
index 0000000..34232f5
--- /dev/null
@@ -0,0 +1,137 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#include "simplestring.h"\r
+#include <string.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+\r
+static const int DEFAULT_SIZE = 20;\r
+\r
+SimpleString::SimpleString ()\r
+: buffer(new char [1])\r
+{\r
+       buffer [0] = '\0';\r
+}\r
+\r
+\r
+SimpleString::SimpleString (const char *otherBuffer)\r
+: buffer (new char [strlen (otherBuffer) + 1])\r
+{\r
+       strcpy (buffer, otherBuffer);\r
+}\r
+\r
+SimpleString::SimpleString (const SimpleString& other)\r
+{\r
+       buffer = new char [other.size() + 1];\r
+       strcpy(buffer, other.buffer);\r
+}\r
+\r
+\r
+SimpleString SimpleString::operator= (const SimpleString& other)\r
+{\r
+       delete buffer;\r
+       buffer = new char [other.size() + 1];\r
+       strcpy(buffer, other.buffer);   \r
+       return *this;\r
+}\r
+\r
+SimpleString SimpleString::operator+ (const SimpleString& other)\r
+{\r
+       SimpleString newS;\r
+       delete [] newS.buffer;\r
+       newS.buffer = new char[this->size()+other.size()+1];\r
+       strcpy(newS.buffer,this->asCharString());\r
+       newS.buffer= strcat(newS.buffer,other.asCharString());\r
+       return newS;\r
+}\r
+\r
+char *SimpleString::asCharString () const\r
+{\r
+       return buffer;\r
+}\r
+\r
+int SimpleString::size() const\r
+{\r
+       return strlen (buffer);\r
+}\r
+\r
+SimpleString::~SimpleString ()\r
+{\r
+       delete [] buffer;\r
+}\r
+\r
+bool operator== (const SimpleString& left, const SimpleString& right)\r
+{\r
+       return !strcmp (left.asCharString (), right.asCharString ());\r
+}\r
+\r
+bool operator!= (const SimpleString& left, const SimpleString& right)\r
+{\r
+       return !(left == right);\r
+}\r
+\r
+SimpleString StringFrom (bool value)\r
+{\r
+       char buffer [sizeof ("false") + 1];\r
+       sprintf (buffer, "%s", value ? "true" : "false");\r
+       return SimpleString(buffer);\r
+}\r
+\r
+SimpleString StringFrom (const char *value)\r
+{\r
+       return SimpleString(value);\r
+}\r
+\r
+SimpleString StringFrom (long value)\r
+{\r
+       char buffer [DEFAULT_SIZE];\r
+       sprintf (buffer, "%ld", value);\r
+\r
+       return SimpleString(buffer);\r
+}\r
+\r
+SimpleString StringFrom (int value)\r
+{\r
+       char buffer [DEFAULT_SIZE];\r
+       sprintf (buffer, "%d", value);\r
+\r
+       return SimpleString(buffer);\r
+}\r
+\r
+SimpleString StringFrom (double value)\r
+{\r
+       char buffer [DEFAULT_SIZE];\r
+       sprintf (buffer, "%lf", value);\r
+\r
+       return SimpleString(buffer);\r
+}\r
+\r
+SimpleString StringFrom (const SimpleString& value)\r
+{\r
+       return SimpleString(value);\r
+}\r
+\r
+\r
+\r
+\r
diff --git a/src/testframework/easyunit/simplestring.h b/src/testframework/easyunit/simplestring.h
new file mode 100644 (file)
index 0000000..9f8df45
--- /dev/null
@@ -0,0 +1,70 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+\r
+This class was originally created by Michael Feathers and was modified\r
+by Barthelemy Dagenais.\r
+*/\r
+\r
+\r
+#ifndef SIMPLE_STRING\r
+#define SIMPLE_STRING\r
+\r
+\r
+/**\r
+ * SimpleString is a simple implementation of the std class String and is\r
+ * provided to ease the manipulation of strings without using any other\r
+ * libraries.\r
+ */\r
+class SimpleString\r
+{\r
+       friend bool     operator== (const SimpleString& left, const SimpleString& right);\r
+\r
+       friend bool     operator!= (const SimpleString& left, const SimpleString& right);\r
+\r
+  public:\r
+    SimpleString ();\r
+               SimpleString (const char *value);\r
+               SimpleString (const SimpleString& other);\r
+               ~SimpleString ();\r
+\r
+         SimpleString operator= (const SimpleString& other);\r
+       \r
+    SimpleString operator+ (const SimpleString& other);\r
+  \r
+         char *asCharString () const;\r
+         int size() const;\r
+\r
+  private:\r
+         char *buffer;\r
+};\r
+\r
+// Those functions are provided to ease the conversion between\r
+// primary datatypes and SimpleString. Feel free to extend this list\r
+// to support your own datatype.\r
+SimpleString StringFrom (bool value);\r
+SimpleString StringFrom (const char *value);\r
+SimpleString StringFrom (long value);\r
+SimpleString StringFrom (int value);\r
+SimpleString StringFrom (double value);\r
+SimpleString StringFrom (const SimpleString& other);\r
+\r
+#endif\r
+\r
diff --git a/src/testframework/easyunit/test.cpp b/src/testframework/easyunit/test.cpp
new file mode 100644 (file)
index 0000000..a9453a4
--- /dev/null
@@ -0,0 +1,143 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#include "test.h"\r
+#include "testregistry.h"\r
+\r
+\r
+Test::Test(const SimpleString& testCaseName, const SimpleString& testName)\r
+: testCaseName_(testCaseName), testName_(testName), testPartResult_(0), nextTest_(0), failuresCount_(0),\r
+       successesCount_(0)\r
+{\r
+  TestRegistry::addTest(this);\r
+}\r
+\r
+Test::~Test() {\r
+  TestPartResult *tmp;\r
+  int size = failuresCount_ + successesCount_;\r
+\r
+  for (int i = 0; i<size; i++) {\r
+       tmp = testPartResult_;\r
+       testPartResult_ = testPartResult_->getNext();\r
+       delete tmp;\r
+  }\r
+}\r
+\r
+void Test::setUp()\r
+{\r
+}\r
+\r
+void Test::tearDown()\r
+{\r
+}\r
+\r
+void Test::run()\r
+{\r
+}\r
+\r
+\r
+TestCase* Test::getTestCase() const\r
+{\r
+       return testCase_;\r
+}\r
+\r
+\r
+void Test::setTestCase(TestCase *testCase)\r
+{\r
+       testCase_ = testCase;\r
+}\r
+\r
+void Test::addTestPartResult(TestPartResult *testPartResult)\r
+{\r
+  TestPartResult *tmp;\r
+  int type = testPartResult->getType();\r
+\r
+  if (testPartResult_ == 0) {\r
+               testPartResult_ = testPartResult;\r
+               testPartResult_->setNext(testPartResult_);\r
+       }\r
+       else {\r
+               tmp = testPartResult_;\r
+               testPartResult_ = testPartResult;\r
+               testPartResult_->setNext(tmp->getNext());\r
+               tmp->setNext(testPartResult_);\r
+       }\r
+\r
+       if (type == failure) {\r
+         failuresCount_++;\r
+       }\r
+       else if (type == error) {\r
+               errorsCount_++;\r
+       }\r
+       else {\r
+               successesCount_++;\r
+       }\r
+}\r
+\r
+TestPartResult* Test::getTestPartResult() const\r
+{\r
+  TestPartResult *tpr = testPartResult_;\r
+\r
+  if (tpr != 0) {\r
+       tpr = tpr->getNext();\r
+  }\r
+\r
+  return tpr;\r
+}\r
+\r
+int Test::getFailuresCount() const\r
+{\r
+       return failuresCount_;\r
+}\r
+\r
+int Test::getSuccessesCount() const\r
+{\r
+       return successesCount_;\r
+}\r
+\r
+int Test::getErrorsCount() const\r
+{\r
+       return errorsCount_;\r
+}\r
+\r
+void Test::setNext(Test *nextTest)\r
+{\r
+       nextTest_ = nextTest;\r
+}\r
+\r
+\r
+Test* Test::getNext() const\r
+{\r
+       return nextTest_;\r
+}\r
+\r
+const SimpleString& Test::getTestName() const\r
+{\r
+  return testName_;\r
+}\r
+\r
+const SimpleString& Test::getTestCaseName() const\r
+{\r
+       return testCaseName_;\r
+}\r
+\r
+\r
diff --git a/src/testframework/easyunit/test.h b/src/testframework/easyunit/test.h
new file mode 100644 (file)
index 0000000..215ce29
--- /dev/null
@@ -0,0 +1,433 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#ifndef TEST_H\r
+#define TEST_H\r
+\r
+#include "testcase.h"\r
+#include "testpartresult.h"\r
+\r
+\r
+\r
+\r
+\r
+/**\r
+ * EasyUnit namespace.\r
+ * This is the namespace containing all easyunit classes.\r
+ */\r
+\r
+/**\r
+ * Test class containing all macros to do unit testing.\r
+ * A test object represents a test that will be executed. Once it has been\r
+ * executed, it reports all results in the testPartResult linked list.\r
+ *\r
+ * A failure occurs when a test fails (condition is false).\r
+ * An error occurs when an exception is thrown during a test.\r
+ * A success occurs if a test succeed (condition is true).\r
+ */\r
+class Test\r
+{\r
+       public:\r
+\r
+               /**\r
+                * Main Test constructor. Used to create a test that will register itself\r
+                * with TestRegistry and with its test case.\r
+                * @param testCaseName Name of the test case this test belongs to\r
+                * @param testName Name of this test\r
+                */\r
+               Test(const SimpleString& testCaseName, const SimpleString& testName);\r
+\r
+               /**\r
+                * Main Test desctructor\r
+                * Delete the testPartResult linked list. This is why the user should\r
+                * only use the macro provided by easyunit to report a test result.\r
+                */\r
+               virtual ~Test();\r
+\r
+               /**\r
+                * Fixtures that will be called after run().\r
+                */\r
+               virtual void tearDown();\r
+\r
+               /**\r
+                * Fixtures that will be called before run().\r
+                */\r
+               virtual void setUp();\r
+\r
+               /**\r
+                * Test code should be in this method.\r
+                * run() will be called by the Test's TestCase, hence subclasses of Test\r
+                * should override this method.\r
+                */\r
+               virtual void run();\r
+\r
+               /**\r
+                * Set the TestCase this test belongs to.\r
+                *\r
+                * @param testCase The TestCase this test belongs to\r
+                */\r
+       void setTestCase(TestCase *testCase);\r
+\r
+       /**\r
+        * Get the TestCase this test belongs to. A test always belongs to\r
+        * only one TestCase. This is the TestCase identified by the first\r
+        * parameter of the test declaration. For example, if there is a\r
+        * test declared as TEST(TESTCASE1, TEST1), this test will be\r
+        * associated with the TestCase TESTCASE1.\r
+        *\r
+        * @return The TestCase this test belongs to\r
+        */\r
+               TestCase* getTestCase() const;\r
+\r
+               /**\r
+                * Add a testpartresult to the testpartresult list of this test.\r
+                * This method is used by the assertion macros to report success,\r
+                * failure or error.\r
+                *\r
+                * @param testPartResult The testpartresult to be added to the list\r
+                */\r
+               virtual void addTestPartResult(TestPartResult *testPartResult);\r
+\r
+               /**\r
+                * Get the testpartresult list of this test. If assertion macros\r
+                * and TEST and TESTF macros are used, there may be more than\r
+                * one successful testpartresult and no more than one error or failure.\r
+                *\r
+                * @return testPartResult The list of testpartresults of this test\r
+                */\r
+               TestPartResult* getTestPartResult() const;\r
+\r
+               /**\r
+                * Returns number of failures found in this test.\r
+                * If macro TEST or TESTF is used, failuresCount <= 1.\r
+                * If Test class is extended and ASSERT macros are used in different\r
+                * test methods, than failuresCount may be more than 1.\r
+                *\r
+                * @return Number of failures in this test\r
+                */\r
+               int getFailuresCount() const;\r
+\r
+               /**\r
+                * Returns number of successes found in this test.\r
+                * There may be more than one success since each ASSERT macro\r
+                * that succeeded generate a success.\r
+                *\r
+                * @return Number of successes in this test\r
+                */\r
+               int getSuccessesCount() const;\r
+\r
+               /**\r
+                * Returns number of errors found in this test.\r
+                * ErrorsCount <= 1, since exception are caught\r
+                * for the whole run() method.\r
+                *\r
+                * @return Number of errors in this test\r
+                */\r
+               int getErrorsCount() const;\r
+\r
+\r
+    /**\r
+     * Set the next test in the linked list.\r
+     *\r
+     * @param nextTest Next test in the linked list\r
+     */\r
+               void setNext(Test *nextTest);\r
+\r
+               /**\r
+        * Get the next test in the linked list.\r
+        *\r
+                * @return The next test in the linked list\r
+                */\r
+               Test* getNext() const;\r
+\r
+               /**\r
+                * Get the name of the TestCase this test belongs to. The name of the\r
+                * TestCase is the first parameter of the test declaration. For example,\r
+                * if a test is declared as TEST(TESTCASE1, TEST1), this method will return\r
+                * "TESTCASE1".\r
+                *\r
+                * @return The TestCase name of this test\r
+                */\r
+               const SimpleString& getTestCaseName() const;\r
+\r
+               /**\r
+                * Get the name of this test. The name of the test is the second\r
+                * parameter of the test declaration. For example,\r
+                * if a test is declared as TEST(TESTCASE1, TEST1), this method will return\r
+                * "TEST1".\r
+                *\r
+                * @return The name of this test.\r
+                */\r
+               const SimpleString& getTestName() const;\r
+\r
+ protected:\r
+               SimpleString testCaseName_;\r
+               SimpleString testName_;\r
+               TestCase *testCase_;\r
+               TestPartResult *testPartResult_;\r
+               Test *nextTest_;\r
+               int failuresCount_;\r
+               int successesCount_;\r
+               int errorsCount_;\r
+};\r
+\r
+\r
+\r
+\r
+/*\r
+ * Helper macros\r
+ */\r
+\r
+#define EQUALS_DELTA(expected,actual,delta)\\r
+  (actual - expected) <= delta && actual >= expected || (expected - actual) <= delta && expected >= actual\r
+\r
+#define TO_STRING_EQUALS_F(expected,actual)\\r
+  StringFrom("Expected : ") + StringFrom(expected) + StringFrom(" but Actual : ") + StringFrom(actual)\r
+\r
+#define TO_STRING_EQUALS_S(expected,actual)\\r
+  StringFrom(expected) + StringFrom(" == ") + StringFrom(actual)\r
+\r
+#define TO_S_E_DELTA_F(expected,actual,delta)\\r
+  StringFrom("Expected : ") + StringFrom(expected) + StringFrom(" but Actual : ") + StringFrom(actual) + StringFrom(" with delta = ") + StringFrom(delta)\r
+\r
+#define TO_S_E_DELTA_S(expected,actual,delta)\\r
+  StringFrom(expected) + StringFrom(" == ") + StringFrom(actual) + StringFrom(" with delta = ") + StringFrom(delta)\r
+\r
+/**\r
+ * Asserts that a condition is true.\r
+ * If the condition is not true, a failure is generated.\r
+ * @param condition Condition to fullfill for the assertion to pass\r
+ */\r
+#define ASSERT_TRUE(condition)\\r
+       { if (condition) {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,#condition,success));\\r
+       } else {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__, #condition,failure)); return;\\r
+       }}\r
+\r
+/**\r
+ * Asserts that a condition is true.\r
+ * If the condition is not true, a failure is generated.\r
+ * @param condition Condition to fullfill for the assertion to pass\r
+ * @param message Message that will be displayed if this assertion fails\r
+ */\r
+#define ASSERT_TRUE_M(condition,message)\\r
+       { if (condition) {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,#condition,success));\\r
+       } else {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__, message,failure)); return;\\r
+       }}\r
+\r
+/**\r
+ * Asserts that the two parameters are equals. Operator == must be defined.\r
+ * If the two parameters are not equals, a failure is generated.\r
+ * @param expected Expected value\r
+ * @param actual Actual value to be compared\r
+ */\r
+#define ASSERT_EQUALS(expected,actual)\\r
+{ if (expected == actual) {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_STRING_EQUALS_S(#expected,#actual),success));\\r
+       } else {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_STRING_EQUALS_F(#expected,#actual),failure)); return;\\r
+       }}\r
+\r
+/**\r
+ * Asserts that the two parameters are equals. Operator == must be defined.\r
+ * If the two parameters are not equals, a failure is generated.\r
+ *\r
+ * Parameters must be primitive data types or StringFrom (custom type) must\r
+ * be overloaded.\r
+ *\r
+ * @see SimpleString\r
+ * @param expected Expected value\r
+ * @param actual Actual value to be compared\r
+ */\r
+#define ASSERT_EQUALS_V(expected,actual)\\r
+{ if (expected == actual) {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_STRING_EQUALS_S(expected,actual),success));\\r
+       } else {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_STRING_EQUALS_F(expected,actual),failure)); return;\\r
+       }}\r
+\r
+/**\r
+ * Asserts that the two parameters are equals. Operator == must be defined.\r
+ * If the two parameters are not equals, a failure is generated.\r
+ * @param expected Expected value\r
+ * @param actual Actual value to be compared\r
+ * @param message Message that will be displayed if this assertion fails\r
+ */\r
+#define ASSERT_EQUALS_M(expected,actual,message)\\r
+{ if (expected == actual) {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,#expected,success));\\r
+       } else {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,message,failure)); return;\\r
+       }}\r
+\r
+/**\r
+ * Asserts that the two parameters are equals within a delta. Operators == and - must be defined.\r
+ * If the two parameters are not equals, a failure is generated.\r
+ * @param expected Expected value\r
+ * @param actual Actual value to be compared\r
+ * @param delta Delta accepted between the two values\r
+ */\r
+#define ASSERT_EQUALS_DELTA(expected,actual,delta)\\r
+{ if (EQUALS_DELTA(expected,actual,delta) ) {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_S_E_DELTA_S(#expected,#actual,#delta),success));\\r
+       } else {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_S_E_DELTA_F(#expected,#actual,#delta),failure)); return;\\r
+       }}\r
+\r
+/**\r
+ * Asserts that the two parameters are equals within a delta. Operators == and - must be defined.\r
+ * If the two parameters are not equals, a failure is generated.\r
+ * @param expected Expected value\r
+ * @param actual Actual value to be compared\r
+ * @param delta Delta accepted between the two values\r
+ * @param message Message that will be displayed if this assertion fails\r
+ */\r
+#define ASSERT_EQUALS_DELTA_M(expected,actual,delta,message)\\r
+{ if (EQUALS_DELTA(expected,actual,delta)) {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,#expected,success));\\r
+       } else {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,message,failure)); return;\\r
+       }}\r
+\r
+/**\r
+ * Asserts that the two parameters are equals within a delta. Operators == and - must be defined.\r
+ * If the two parameters are not equals, a failure is generated.\r
+ *\r
+ * Parameters must be primitive data types or StringFrom (custom type) must\r
+ * be overloaded.\r
+ *\r
+ * @see SimpleString\r
+ * @param expected Expected value\r
+ * @param actual Actual value to be compared\r
+ * @param delta Delta accepted between the two values\r
+ */\r
+#define ASSERT_EQUALS_DELTA_V(expected,actual,delta)\\r
+{ if (EQUALS_DELTA(expected,actual,delta)) {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_S_E_DELTA_S(expected,actual,delta),success));\\r
+       } else {\\r
+       addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_S_E_DELTA_F(expected,actual,delta),failure)); return;\\r
+       }}\r
+\r
+\r
+/**\r
+ * Make a test fails.\r
+ */\r
+#define FAIL()\\r
+  { addTestPartResult(new TestPartResult(this, __FILE__, __LINE__,("Test failed."),failure)); return; }\r
+\r
+/**\r
+ * Make a test fails with the given message.\r
+ * @param text Failure message\r
+ */\r
+#define FAIL_M(text)\\r
+       { addTestPartResult(new TestPartResult(this, __FILE__, __LINE__,text,failure)); return; }\r
+\r
+\r
+/**\r
+ * Define a test in a TestCase.\r
+ * User should put his test code between brackets after using this macro.\r
+ * @param testCaseName TestCase name where the test belongs to\r
+ * @param testName Unique test name\r
+ */\r
+#define TEST(testCaseName, testName)\\r
+  class testCaseName##testName##Test : public Test \\r
+       { public: testCaseName##testName##Test() : Test (#testCaseName , #testName) {} \\r
+            void run(); } \\r
+    testCaseName##testName##Instance; \\r
+       void testCaseName##testName##Test::run ()\r
+\r
+\r
+/**\r
+ * Define a test in a TestCase using test fixtures.\r
+ * User should put his test code between brackets after using this macro.\r
+ *\r
+ * This macro should only be used if test fixtures were declared earlier in\r
+ * this order: DECLARE, SETUP, TEARDOWN.\r
+ * @param testCaseName TestCase name where the test belongs to. Should be\r
+ * the same name of DECLARE, SETUP and TEARDOWN.\r
+ * @param testName Unique test name.\r
+ */\r
+#define TESTF(testCaseName, testName)\\r
+  class testCaseName##testName##Test : public testCaseName##Declare##Test \\r
+       { public: testCaseName##testName##Test() : testCaseName##Declare##Test (#testCaseName , #testName) {} \\r
+            void run(); } \\r
+    testCaseName##testName##Instance; \\r
+       void testCaseName##testName##Test::run ()\r
+\r
+\r
+/**\r
+ * Setup code for test fixtures.\r
+ * This code is executed before each TESTF.\r
+ *\r
+ * User should put his setup code between brackets after using this macro.\r
+ *\r
+ * @param testCaseName TestCase name of the fixtures.\r
+ */\r
+#define SETUP(testCaseName)\\r
+       void testCaseName##Declare##Test::setUp ()\r
+\r
+\r
+/**\r
+ * Teardown code for test fixtures.\r
+ * This code is executed after each TESTF.\r
+ *\r
+ * User should put his setup code between brackets after using this macro.\r
+ *\r
+ * @param testCaseName TestCase name of the fixtures.\r
+ */\r
+#define TEARDOWN(testCaseName)\\r
+       void testCaseName##Declare##Test::tearDown ()\r
+\r
+\r
+/**\r
+ * Location to declare variables and objets.\r
+ * This is where user should declare members accessible by TESTF,\r
+ * SETUP and TEARDOWN.\r
+ *\r
+ * User should not use brackets after using this macro. User should\r
+ * not initialize any members here.\r
+ *\r
+ * @param testCaseName TestCase name of the fixtures\r
+ * @see END_DECLARE for more information.\r
+ */\r
+#define DECLARE(testCaseName)\\r
+       class testCaseName##Declare##Test : public Test \\r
+       { public: testCaseName##Declare##Test(const SimpleString& testCaseName, const SimpleString& testName) : Test (testCaseName , testName) {} \\r
+       virtual void run() = 0; void setUp(); void tearDown(); \\r
+       protected:\r
+\r
+\r
+/**\r
+ * Ending macro used after DECLARE.\r
+ *\r
+ * User should use this macro after declaring members with\r
+ * DECLARE macro.\r
+ */\r
+#define END_DECLARE \\r
+       };\r
+\r
+#endif // TEST_H\r
+\r
+\r
diff --git a/src/testframework/easyunit/testcase.cpp b/src/testframework/easyunit/testcase.cpp
new file mode 100644 (file)
index 0000000..608f25d
--- /dev/null
@@ -0,0 +1,170 @@
+#define ECPP\r
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#include "testcase.h"\r
+#include "test.h"\r
+#include "testresult.h"\r
+\r
+#ifndef ECPP\r
+#include <exception>\r
+#endif\r
+\r
+\r
+TestCase::TestCase(const SimpleString& name, TestResult *testResult)\r
+: name_(name), testResult_(testResult)\r
+{\r
+}\r
+\r
+TestCase::~TestCase()\r
+{\r
+}\r
+\r
+void TestCase::addTest(Test *test)\r
+{\r
+       Test *tmp;\r
+\r
+       if (tests_ == 0) {\r
+               tests_ = test;\r
+               tests_->setNext(tests_);\r
+       }\r
+       else {\r
+               tmp = tests_;\r
+               tests_ = test;\r
+               tests_->setNext(tmp->getNext());\r
+               tmp->setNext(tests_);\r
+       }\r
+\r
+       testsCount_++;\r
+}\r
+\r
+Test* TestCase::getTests() const\r
+{\r
+       Test *test = tests_;\r
+\r
+       if (test != 0) {\r
+               test = test->getNext();\r
+       }\r
+\r
+       return test;\r
+}\r
+\r
+void TestCase::run()\r
+{\r
+       Test *test = tests_->getNext();\r
+\r
+       runTests(test);\r
+\r
+       ran_ = true;\r
+\r
+       testResult_->addResult(this);\r
+}\r
+\r
+int TestCase::getTestsCount() const\r
+{\r
+       return testsCount_;\r
+}\r
+\r
+int TestCase::getFailuresCount() const\r
+{\r
+  return failuresCount_;\r
+}\r
+\r
+int TestCase::getSuccessesCount() const\r
+{\r
+  return successesCount_;\r
+}\r
+\r
+int TestCase::getErrorsCount() const\r
+{\r
+       return errorsCount_;\r
+}\r
+\r
+bool TestCase::ran() const\r
+{\r
+       return ran_;\r
+}\r
+\r
+const SimpleString& TestCase::getName() const\r
+{\r
+       return name_;\r
+}\r
+\r
+void TestCase::updateCount(Test *test)\r
+{\r
+  if (test->getErrorsCount() > 0) {\r
+       errorsCount_++;\r
+  }\r
+  else if (test->getFailuresCount() > 0) {\r
+       failuresCount_++;\r
+  }\r
+  else {\r
+       successesCount_++;\r
+  }\r
+}\r
+\r
+TestCase* TestCase::getNext() const\r
+{\r
+       return nextTestCase_;\r
+}\r
+\r
+void TestCase::setNext(TestCase *testCase)\r
+{\r
+       nextTestCase_ = testCase;\r
+}\r
+\r
+void TestCase::runTests(Test *test)\r
+{\r
+\r
+       for (int i = 0; i<testsCount_; i++) {\r
+               test->setUp();\r
+               runTest(test);\r
+               test->tearDown();\r
+               updateCount(test);\r
+               test = test->getNext();\r
+       }\r
+\r
+}\r
+\r
+#ifdef ECPP\r
+\r
+void TestCase::runTest(Test *test)\r
+{\r
+       test->run();\r
+}\r
+\r
+#else\r
+\r
+void TestCase::runTest(Test *test)\r
+{\r
+       try {\r
+               test->run();\r
+       }\r
+       catch (std::exception &e) {\r
+               test->addTestPartResult(new TestPartResult(test,"",-1,e.what(),error));\r
+       }\r
+       catch (...) {\r
+               test->addTestPartResult(new TestPartResult(test,"",-1,"Unexpected error occured",error));\r
+       }\r
+}\r
+#endif\r
+\r
diff --git a/src/testframework/easyunit/testcase.h b/src/testframework/easyunit/testcase.h
new file mode 100644 (file)
index 0000000..7943850
--- /dev/null
@@ -0,0 +1,154 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#include "simplestring.h"\r
+\r
+#ifndef TESTCASE_H\r
+#define TESTCASE_H\r
+\r
+\r
+class Test;\r
+class TestResult;\r
+\r
+/**\r
+ * A TestCase is a collection of unit tests (instance of Test) and is\r
+ * always specified by the first parameter of a Test declaration.\r
+ */\r
+class TestCase\r
+{\r
+       public:\r
+\r
+               /**\r
+                * Main TestCase constructor.\r
+                *\r
+                * @param name TestCase name\r
+                * @param testResult Pointer to the TestResult used to report results\r
+                * of executed Test\r
+                */\r
+               TestCase(const SimpleString& name, TestResult *testResult);\r
+\r
+               virtual ~TestCase();\r
+\r
+               /**\r
+                * Add a Test to the Test list. This method is used by TestRegistry.\r
+                *\r
+                * @param test Test instance to add to the Test list.\r
+                */\r
+               void addTest(Test *test);\r
+\r
+               /**\r
+                * Get the Test list.\r
+                *\r
+                * @return Test list\r
+                */\r
+               Test* getTests() const;\r
+\r
+    /**\r
+     * Execute all Tests in the Test list of this TestCase. In fact, it calls\r
+     * the run() method of all Tests.\r
+     */\r
+               void run();\r
+\r
+    /**\r
+     * Get the Test list size (number of Tests in this TestCase).\r
+     *\r
+     * @return The Test list size\r
+     */\r
+               int getTestsCount() const;\r
+\r
+               /**\r
+                * Get the total number of failures reported by all Tests.\r
+                *\r
+                * @return The total number of failures reported by all Tests. 0\r
+                * if no test were run or if no failures were reported.\r
+                */\r
+               int getFailuresCount() const;\r
+\r
+               /**\r
+                * Get the total number of successes reported by all Tests.\r
+                *\r
+                * @return The total number of successes reported by all Tests. 0\r
+                * if no test were run or if no successes were reported.\r
+                */\r
+               int getSuccessesCount() const;\r
+\r
+               /**\r
+                * Get the total number of errors reported by all Tests.\r
+                *\r
+                * @return The total number of errors reported by all Tests. 0\r
+                * if no test were run, if this is the embedded version or if\r
+                * no errors were reported.\r
+                */\r
+               int getErrorsCount() const;\r
+\r
+               /**\r
+                * Indicates whether or not this TestCase was executed.\r
+                *\r
+                * @return true if the method run() of this TestCase was called. false\r
+                * otherwise\r
+                */\r
+               bool ran() const;\r
+\r
+               /**\r
+                * Get the TestCase name. This name is specified by the first parameter\r
+                * of the Test declaration. For example, if a test was declared as\r
+                * TEST(TESTCASE1, TEST1), the TestCase name would be "TESTCASE1".\r
+                *\r
+                * @return The name of the TestCase\r
+                */\r
+               const SimpleString& getName() const;\r
+\r
+               /**\r
+                * Get the next TestCase in the list.\r
+                *\r
+                * @return The next TestCase in the TestCase linked list\r
+                */\r
+               TestCase* getNext() const;\r
+\r
+               /**\r
+                * Set the next TestCase in the list.\r
+                *\r
+                * @return The next TestCase in the TestCase linked list\r
+                */\r
+               void setNext(TestCase *testCase);\r
+\r
+       protected:\r
+               int failuresCount_{0};\r
+               int successesCount_{0};\r
+               int errorsCount_{0};\r
+               int testsCount_{0};\r
+               Test *tests_{0};\r
+               SimpleString name_;\r
+               TestCase *nextTestCase_{0};\r
+               TestResult *testResult_;\r
+\r
+       private:\r
+         void updateCount(Test *test);\r
+         void runTests(Test *test);\r
+         void runTest(Test *test);\r
+         bool ran_{false};\r
+\r
+};\r
+\r
+#endif // TESTCASE_H\r
+\r
+\r
diff --git a/src/testframework/easyunit/testharness.h b/src/testframework/easyunit/testharness.h
new file mode 100644 (file)
index 0000000..e0a78ec
--- /dev/null
@@ -0,0 +1,38 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#ifndef TESTHARNESS_H\r
+#define TESTHARNESS_H\r
+\r
+#include "test.h"\r
+#include "testcase.h"\r
+#include "testpartresult.h"\r
+#include "testregistry.h"\r
+#include "simplestring.h"\r
+#include "testprinter.h"\r
+#include "testresult.h"\r
+#include "testrunner.h"\r
+#include "defaulttestprinter.h"\r
+\r
+#endif\r
+\r
+\r
diff --git a/src/testframework/easyunit/testpartresult.cpp b/src/testframework/easyunit/testpartresult.cpp
new file mode 100644 (file)
index 0000000..f382c4a
--- /dev/null
@@ -0,0 +1,67 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#include "testpartresult.h"\r
+#include "test.h"\r
+\r
+\r
+TestPartResult::TestPartResult (Test *test,\r
+       const SimpleString&     fileName, \r
+       long lineNumber,\r
+       const SimpleString&     message,\r
+       testType type) \r
+  : message_ (message), \r
+    test_ (test), \r
+    fileName_ (fileName), \r
+    lineNumber_ (lineNumber),\r
+    type_ (type)\r
+{\r
+}\r
+\r
+void TestPartResult::setNext(TestPartResult *next) {\r
+  next_ = next;\r
+}  \r
+\r
+TestPartResult* TestPartResult::getNext() const {\r
+  return next_;\r
+}  \r
+\r
+testType TestPartResult::getType() const {\r
+       return type_; \r
+} \r
+\r
+const SimpleString& TestPartResult::getMessage() const {\r
+  return message_;\r
+}\r
+       \r
+Test* TestPartResult::getTest() const {\r
+  return test_;\r
+}\r
+       \r
+const SimpleString& TestPartResult::getFileName() const {\r
+  return fileName_;\r
+}\r
+       \r
+long TestPartResult::getLineNumber() const {\r
+  return lineNumber_;\r
+}\r
+\r
diff --git a/src/testframework/easyunit/testpartresult.h b/src/testframework/easyunit/testpartresult.h
new file mode 100644 (file)
index 0000000..5f04a0b
--- /dev/null
@@ -0,0 +1,131 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#ifndef TESTPARTRESULT_H\r
+#define TESTPARTRESULT_H\r
+\r
+#include "simplestring.h"\r
+\r
+\r
+/**\r
+ * This enumeration contains the three states a TestPartResult can take.\r
+ *\r
+ * A failure means that an assertion failed during the test.\r
+ *\r
+ * A success means that all assertion succeeded during the test.\r
+ *\r
+ * An error means that an exception was thrown during the test.\r
+ */\r
+enum testType {failure,success,error};\r
+\r
+class Test;\r
+\r
+/**\r
+ * This class contains details about assertion processed during the\r
+ * execution of a Test. It contains the line and the file of the assertion, \r
+ * the result (success, failure or error), the condition (or message), and\r
+ * the Test class where the assertion was processed.\r
+ */ \r
+class TestPartResult\r
+{\r
+       public:\r
+               /**\r
+                * Main constructor used to initialize all details about the result\r
+                * of an assertion.\r
+                *\r
+                * @param test The test where the assertion was processed\r
+                * @param fileName The file name where the assertion is located\r
+                * @param lineNumber The line number where the assertion is located\r
+                * @param message The assertion condition or message\r
+                * @param type The result of the assertion (failure, success or error)\r
+                */\r
+               TestPartResult (Test *test,\r
+               const SimpleString& fileName, \r
+               long lineNumber,\r
+               const SimpleString& message,\r
+       testType type);\r
+\r
+    /**\r
+     * Set the next TestPartResult in the list.\r
+     *\r
+     * @param next The next TestPartResult in the linked list\r
+     */\r
+               void setNext(TestPartResult* next);\r
+               \r
+               /**\r
+                * Get the next TestPartResult in the list.\r
+                *\r
+                * @return The next TestPartResult in the linked list\r
+                */\r
+       TestPartResult* getNext() const;\r
+\r
+    /**\r
+     * Get the type of the TestPartResult. This represents the result\r
+     * of the assertion.\r
+     *\r
+     * @return The type of the TestPartResult (failure, success or error)\r
+     */\r
+       testType getType() const;\r
+       \r
+       /**\r
+        * Get the message (or condition) of the assertion.\r
+        *\r
+        * @return The message (or condition) of the assertion\r
+        */\r
+       const SimpleString& getMessage() const;\r
+       \r
+       /**\r
+        * Get the Test where the assertion is located.\r
+        *\r
+        * @return The Test where the assertion is located\r
+        */\r
+       Test* getTest() const;\r
+       \r
+       /**\r
+        * Get the file name where the assertion is located.\r
+        *\r
+        * @return The file name where the assertion is located\r
+        */\r
+       const SimpleString& getFileName() const;\r
+       \r
+       /**\r
+        * Get the line number where the assertion is located.\r
+        *\r
+        * @return The line number where the assertion is located\r
+        */\r
+       long getLineNumber() const;\r
+       \r
+       \r
+  protected:\r
+       SimpleString message_;\r
+               Test *test_;\r
+       SimpleString fileName_;\r
+       long lineNumber_;\r
+\r
+       private:\r
+               TestPartResult *next_;\r
+       testType type_;         \r
+};\r
+\r
+#endif // TESTPARTRESULT_H\r
+\r
+\r
diff --git a/src/testframework/easyunit/testprinter.h b/src/testframework/easyunit/testprinter.h
new file mode 100644 (file)
index 0000000..4d2f871
--- /dev/null
@@ -0,0 +1,52 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#ifndef TESTPRINTER_H\r
+#define TESTPRINTER_H\r
+\r
+#include "testresult.h"\r
+\r
+\r
+/**\r
+ * A TestPrinter is a class used by the TestRegistry to print results\r
+ * of executed TestCases. This is an abstract class, so no default behavior\r
+ * for the print method is provided.\r
+ *\r
+ * @see DefaultTestPrinter\r
+ */\r
+class TestPrinter\r
+{\r
+       public:\r
+        virtual ~TestPrinter(){};\r
+       /**\r
+        * Print the details of a given TestResult instance. This\r
+        * method must be overridden by subclasses since it is\r
+        * abstract.\r
+        *\r
+        * @param testResult TestResult instance that the user wish to print\r
+        */\r
+               virtual void print(const TestResult *testResult) = 0;\r
+};\r
+\r
+#endif // TESTPRINTER_H\r
+\r
+\r
diff --git a/src/testframework/easyunit/testregistry.cpp b/src/testframework/easyunit/testregistry.cpp
new file mode 100644 (file)
index 0000000..aad38ba
--- /dev/null
@@ -0,0 +1,140 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#include "testregistry.h"\r
+#include "defaulttestprinter.h"\r
+\r
+\r
+int TestRegistry::nextName = 0;\r
+\r
+TestRegistry::TestRegistry()\r
+: currentTC_(0), defaultPrinter_(new DefaultTestPrinter()),testCaseCount_(0),\r
+       defaultRunner_(new TestRunner())\r
+{\r
+}  \r
+\r
+TestRegistry::~TestRegistry()\r
+{\r
+       TestCase *tmp;\r
+       for (int i = 0; i<testCaseCount_; i++) {\r
+               tmp = currentTC_;\r
+               currentTC_ = currentTC_->getNext();\r
+               delete tmp;\r
+       }\r
+       \r
+       delete defaultPrinter_;\r
+       delete defaultRunner_;\r
+}\r
+\r
+void TestRegistry::addTest(Test *test)\r
+{\r
+       instance().add(test);\r
+}  \r
+\r
+const TestResult* TestRegistry::run()\r
+{\r
+       return instance().runTests(instance().defaultRunner_);\r
+}\r
+\r
+const TestResult* TestRegistry::run(TestRunner *runner)\r
+{\r
+       return instance().runTests(runner);\r
+}\r
+\r
+const TestResult* TestRegistry::runAndPrint()\r
+{\r
+       return runAndPrint(instance().defaultPrinter_,instance().defaultRunner_);\r
+}\r
+\r
+const TestResult* TestRegistry::runAndPrint(TestRunner *runner)\r
+{\r
+       return runAndPrint(instance().defaultPrinter_,runner);\r
+}\r
+\r
+const TestResult* TestRegistry::runAndPrint(TestPrinter *printer)\r
+{\r
+       return runAndPrint(printer,instance().defaultRunner_);\r
+}\r
+\r
+\r
+const TestResult* TestRegistry::runAndPrint(TestPrinter *printer, TestRunner *runner)\r
+{\r
+       const TestResult *testResult = instance().runTests(runner);\r
+       printer->print(testResult);\r
+       return testResult;\r
+}\r
+\r
+               \r
+TestRegistry& TestRegistry::instance()\r
+{\r
+       static TestRegistry registry;\r
+       return registry;\r
+}  \r
+\r
+void TestRegistry::add(Test *test)\r
+{\r
+       const SimpleString tcName = test->getTestCaseName();\r
+       const SimpleString tName = test->getTestName();\r
+       \r
+       if ((currentTC_ == 0) || (currentTC_->getName() != tcName)) {\r
+                       addTestCase(new TestCase(tcName,&testResult_));\r
+       }\r
+       \r
+       currentTC_->addTest(test);\r
+       \r
+}\r
+\r
+const TestResult* TestRegistry::runTests(TestRunner *runner)\r
+{\r
+       TestCase *tc = currentTC_;\r
+       \r
+       if (tc != 0) {\r
+               tc = tc->getNext();\r
+               runner->run(tc,testCaseCount_);\r
+       }\r
+       \r
+       testResult_.setTestCases(tc,testCaseCount_);\r
+       \r
+       return &testResult_;\r
+} \r
+\r
+\r
+\r
+void TestRegistry::addTestCase(TestCase *testCase)\r
+{\r
+       TestCase *tmp;\r
+       \r
+       if (currentTC_ == 0) {\r
+               currentTC_ = testCase;\r
+               currentTC_->setNext(currentTC_);\r
+       }\r
+       else {\r
+               tmp = currentTC_;\r
+               currentTC_ = testCase;\r
+               currentTC_->setNext(tmp->getNext());\r
+               tmp->setNext(currentTC_);\r
+       }\r
+       \r
+       testCaseCount_++;\r
+}   \r
+\r
+\r
diff --git a/src/testframework/easyunit/testregistry.h b/src/testframework/easyunit/testregistry.h
new file mode 100644 (file)
index 0000000..5d0d1df
--- /dev/null
@@ -0,0 +1,128 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#ifndef TESTREGISTRY_H\r
+#define TESTREGISTRY_H\r
+\r
+#include "test.h"\r
+#include "testcase.h"\r
+#include "testprinter.h"\r
+#include "simplestring.h"\r
+#include "testrunner.h"\r
+#include "testresult.h"\r
+\r
+\r
+/**\r
+ * The TestRegistry is the main class used to register all tests,\r
+ * and create appropriate TestCase. It can then be used to run\r
+ * tests and print results. All methods that should be used by\r
+ * the user are static.\r
+ */\r
+class TestRegistry\r
+{\r
+       public:\r
+         TestRegistry();\r
+               ~TestRegistry();\r
+\r
+               /**\r
+                * Add a test in the registry. If the previous TestCase was not the same\r
+                * as the one of the current test, a new TestCase is created.\r
+                *\r
+                * @param test Test to be added\r
+                */\r
+               static void addTest (Test *test);\r
+\r
+               /**\r
+                * Run all tests in the registry (default test runner) and return\r
+                * the test results.\r
+                *\r
+                * @return The test results\r
+                */\r
+               static const TestResult* run();\r
+\r
+               /**\r
+                * Pass all tests in the registry to the TestRunner runner and\r
+                * return the results of all tests ran.\r
+                *\r
+                * @param runner The custom runner used to decided which test to run\r
+                * @return The test results of all tests ran\r
+                */\r
+               static const TestResult* run(TestRunner *runner);\r
+\r
+               /**\r
+                * Run all tests in the registry (default test runner) and return\r
+                * the test results. This will also print the results using the\r
+                * default test printer (normal level of details and to the standard\r
+                * output).\r
+                *\r
+                * @return The test results\r
+                */\r
+               static const TestResult* runAndPrint();\r
+\r
+               /**\r
+                * Pass all tests in the registry to the TestRunner runner and\r
+                * return the results of all tests ran. This will also print the results\r
+                * using the default test printer (normal level of details and to the\r
+                * standard output).\r
+                *\r
+                * @param runner The custom runner used to decided which test to run\r
+                * @return The test results\r
+                */\r
+               static const TestResult* runAndPrint(TestRunner *runner);\r
+\r
+               /**\r
+                * Run all tests in the registry (default test runner) and return\r
+                * the test results. Results will also be given to\r
+                * to the TestPrinter printer.\r
+                *\r
+                * @param printer The custom printer used to print the test results\r
+                * @return The test results\r
+                */\r
+               static const TestResult* runAndPrint(TestPrinter *printer);\r
+\r
+               /**\r
+                * Pass all tests in the registry to the TestRunner runner and\r
+                * return the results of all tests ran. Results will also be given to\r
+                * to the TestPrinter printer.\r
+                *\r
+                * @param printer The custom printer used to print the test results\r
+                * @param runner The custom runner used to decided which test to run\r
+                * @return The test results\r
+                */\r
+               static const TestResult* runAndPrint(TestPrinter *printer, TestRunner *runner);\r
+\r
+       private:\r
+               static TestRegistry& instance();\r
+               static int nextName;\r
+               void add(Test *test);\r
+               void addTestCase(TestCase *testCase);\r
+               const TestResult* runTests(TestRunner *runner);\r
+               TestCase *currentTC_;\r
+               TestPrinter *defaultPrinter_;\r
+               int testCaseCount_;\r
+               TestRunner *defaultRunner_;\r
+               TestResult testResult_;\r
+};\r
+\r
+#endif // TESTREGISTRY_H\r
+\r
+\r
diff --git a/src/testframework/easyunit/testresult.cpp b/src/testframework/easyunit/testresult.cpp
new file mode 100644 (file)
index 0000000..e23e58f
--- /dev/null
@@ -0,0 +1,117 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#include "testresult.h"\r
+\r
+\r
+TestResult::TestResult()\r
+{\r
+}\r
+\r
+\r
+TestResult::~TestResult()\r
+{\r
+}\r
+\r
+int TestResult::getTotalSuccesses() const\r
+{\r
+       return totalSuccesses_;\r
+}\r
+\r
+int TestResult::getTotalErrors() const\r
+{\r
+       return totalErrors_;\r
+}\r
+\r
+int TestResult::getTotalFailures() const\r
+{\r
+       return totalFailures_;\r
+}\r
+\r
+\r
+int TestResult::getSuccesses() const\r
+{\r
+       return successes_;\r
+}\r
+\r
+int TestResult::getFailures() const\r
+{\r
+       return failures_;\r
+}\r
+\r
+int TestResult::getErrors() const\r
+{\r
+       return errors_;\r
+}\r
+\r
+int TestResult::getTestCaseCount() const\r
+{\r
+       return testCaseCount_;\r
+}\r
+\r
+int TestResult::getTestRanCount() const\r
+{\r
+       return testRanCount_;\r
+}\r
+\r
+int TestResult::getTestCaseRanCount() const\r
+{\r
+       return testCaseRanCount_;\r
+}\r
+\r
+TestCase* TestResult::getTestCases() const\r
+{\r
+       return testCases_;\r
+}\r
+\r
+void TestResult::setTestCases(TestCase *testCases, int testCaseCount)\r
+{\r
+       testCases_ = testCases;\r
+       testCaseCount_ = testCaseCount;\r
+}\r
+\r
+void TestResult::addResult(TestCase *testCase)\r
+{\r
+       int tcSuccesses = testCase->getSuccessesCount();\r
+       int tcErrors = testCase->getErrorsCount();\r
+       int tcFailures = testCase->getFailuresCount();\r
+\r
+       testCaseRanCount_++;\r
+\r
+       totalSuccesses_ += tcSuccesses;\r
+       totalErrors_ += tcErrors;\r
+       totalFailures_ += tcFailures;\r
+       testRanCount_ += testCase->getTestsCount();\r
+\r
+       if (tcErrors == 0 && tcFailures == 0) {\r
+               successes_++;\r
+       }\r
+       else if (tcErrors > 0) {\r
+               errors_++;\r
+       }\r
+       else {\r
+               failures_++;\r
+       }\r
+}\r
+\r
+\r
+\r
diff --git a/src/testframework/easyunit/testresult.h b/src/testframework/easyunit/testresult.h
new file mode 100644 (file)
index 0000000..ce44b11
--- /dev/null
@@ -0,0 +1,146 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#ifndef testresult_H\r
+#define testresult_H\r
+\r
+#include "testcase.h"\r
+\r
+\r
+class TestResult\r
+{\r
+public:\r
+       TestResult();\r
+       virtual ~TestResult();\r
+\r
+\r
+       /**\r
+        * Get the total number of successes registered by all\r
+        * test cases ran. This is the sum of all TestCase->getSuccessesCount().\r
+        *\r
+        *@return The number of successes registered by all testcases.\r
+        */\r
+       int getTotalSuccesses() const;\r
+\r
+       /**\r
+        * Get the total number of errors registered by all\r
+        * test cases ran. This is the sum of all TestCase->getErrorsCount().\r
+        *\r
+        *@return The number of errors registered by all testcases.\r
+        */\r
+       int getTotalErrors() const;\r
+\r
+       /**\r
+        * Get the total number of failures registered by all\r
+        * test cases ran. This is the sum of all TestCase->getFailuresCount().\r
+        *\r
+        * @return The number of failures registered by all testcases.\r
+        */\r
+       int getTotalFailures() const;\r
+\r
+       /**\r
+        * Get the number of testcases ran that succeeded.\r
+        *\r
+        * @return The number of testcases ran that succeeded.\r
+        */\r
+       int getSuccesses() const;\r
+\r
+       /**\r
+        * Get the number of testcases ran that failed.\r
+        *\r
+        * @return The number of testcases ran that failed.\r
+        */\r
+       int getFailures() const;\r
+\r
+       /**\r
+        * Get the number of testcases ran that reported an error.\r
+        *\r
+        * @return The number of testcases ran that reported an error.\r
+        */\r
+       int getErrors() const;\r
+\r
+       /**\r
+        * Get the number of testcases in the TestCase list.\r
+        *\r
+        * @return The size of the TestCase list\r
+        */\r
+       int getTestCaseCount() const;\r
+\r
+       /**\r
+        * Get the number of tests\r
+        *\r
+        * @return The number of tests ran that succeeded\r
+        */\r
+       int getTestRanCount() const;\r
+\r
+       /**\r
+        * Get the number of testcases ran.\r
+        *\r
+        * @return The number of testcases ran\r
+        */\r
+       int getTestCaseRanCount() const;\r
+\r
+       /**\r
+        * Get the TestCase list. This list contains all TestCase registered and\r
+        * not only those that were ran.\r
+        *\r
+        * @return The TestCase list\r
+        */\r
+       TestCase* getTestCases() const;\r
+\r
+       /**\r
+        * Set the TestCase list and the size of the list.\r
+        *\r
+        * @param testCases TestCase list\r
+        * @param testCaseCount size of the TestCase list\r
+        */\r
+       void setTestCases(TestCase *testCases, int testCaseCount);\r
+\r
+       /**\r
+        * Add a TestCase result. This is used by a TestCase after it has\r
+        * completed.\r
+        *\r
+        * @param testCase TestCase that ran and contains results to add to\r
+        * global results\r
+        */\r
+       virtual void addResult(TestCase *testCase);\r
+\r
+protected:\r
+       int testCaseCount_{0};\r
+       int testRanCount_{0};\r
+       int testCaseRanCount_{0};\r
+\r
+       int totalSuccesses_{0};\r
+       int totalErrors_{0};\r
+       int totalFailures_{0};\r
+\r
+       int successes_{0};\r
+       int errors_{0};\r
+       int failures_{0};\r
+\r
+       TestCase* testCases_{0};\r
+\r
+};\r
+\r
+\r
+#endif // testresult_H\r
+\r
diff --git a/src/testframework/easyunit/testrunner.cpp b/src/testframework/easyunit/testrunner.cpp
new file mode 100644 (file)
index 0000000..09119a2
--- /dev/null
@@ -0,0 +1,45 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#include "testrunner.h"\r
+\r
+\r
+TestRunner::TestRunner()\r
+{\r
+}\r
+\r
+\r
+TestRunner::~TestRunner()\r
+{\r
+}\r
+\r
+void TestRunner::run(TestCase *testCase, int size)\r
+{\r
+       for (int i=0; i<size; i++) {\r
+               testCase->run();\r
+               testCase = testCase->getNext();\r
+       }\r
+}\r
+\r
+\r
+\r
+\r
diff --git a/src/testframework/easyunit/testrunner.h b/src/testframework/easyunit/testrunner.h
new file mode 100644 (file)
index 0000000..38498fb
--- /dev/null
@@ -0,0 +1,57 @@
+/*\r
+EasyUnit : Simple C++ Unit testing framework\r
+Copyright (C) 2004 Barthelemy Dagenais\r
+\r
+This library is free software; you can redistribute it and/or\r
+modify it under the terms of the GNU Lesser General Public\r
+License as published by the Free Software Foundation; either\r
+version 2.1 of the License, or (at your option) any later version.\r
+\r
+This library is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+Lesser General Public License for more details.\r
+\r
+You should have received a copy of the GNU Lesser General Public\r
+License along with this library; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+Barthelemy Dagenais\r
+barthelemy@prologique.com\r
+*/\r
+\r
+#ifndef TestRunner_H\r
+#define TestRunner_H\r
+\r
+#include "testcase.h"\r
+\r
+\r
+\r
+/**\r
+ * Test runner used to determine which test to run.\r
+ * \r
+ * User may extends this class to provide a custom test runner\r
+ * to TestRegistry.\r
+ */\r
+class TestRunner\r
+{\r
+public:\r
+       TestRunner();\r
+       virtual ~TestRunner();\r
+       \r
+       /**\r
+        * Method used to run testcases by TestRegistry.\r
+        * \r
+        * User should override this method in order to provide custom\r
+        * behavior.\r
+        *\r
+        * @param testCase Linked list of testcases\r
+        * @param size Size of the linked list\r
+        */\r
+       virtual void run(TestCase *testCase, int size);\r
+\r
+};\r
+\r
+\r
+#endif // TestRunner_H\r
+\r
diff --git a/src/testframework/prettyprint.hpp b/src/testframework/prettyprint.hpp
new file mode 100644 (file)
index 0000000..6bf2543
--- /dev/null
@@ -0,0 +1,445 @@
+//          Copyright Louis Delacroix 2010 - 2014.
+// Distributed under the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE_1_0.txt or copy at
+//          http://www.boost.org/LICENSE_1_0.txt)
+//
+// A pretty printing library for C++
+//
+// Usage:
+// Include this header, and operator<< will "just work".
+
+#ifndef H_PRETTY_PRINT
+#define H_PRETTY_PRINT
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <tuple>
+#include <type_traits>
+#include <unordered_set>
+#include <utility>
+#include <valarray>
+
+namespace pretty_print
+{
+    namespace detail
+    {
+        // SFINAE type trait to detect whether T::const_iterator exists.
+
+        struct sfinae_base
+        {
+            using yes = char;
+            using no  = yes[2];
+        };
+
+        template <typename T>
+        struct has_const_iterator : private sfinae_base
+        {
+        private:
+            template <typename C> static yes & test(typename C::const_iterator*);
+            template <typename C> static no  & test(...);
+        public:
+            static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
+            using type =  T;
+        };
+
+        template <typename T>
+        struct has_begin_end : private sfinae_base
+        {
+        private:
+            template <typename C>
+            static yes & f(typename std::enable_if<
+                std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)),
+                             typename C::const_iterator(C::*)() const>::value>::type *);
+
+            template <typename C> static no & f(...);
+
+            template <typename C>
+            static yes & g(typename std::enable_if<
+                std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)),
+                             typename C::const_iterator(C::*)() const>::value, void>::type*);
+
+            template <typename C> static no & g(...);
+
+        public:
+            static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
+            static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes);
+        };
+
+    }  // namespace detail
+
+
+    // Holds the delimiter values for a specific character type
+
+    template <typename TChar>
+    struct delimiters_values
+    {
+        using char_type = TChar;
+        const char_type * prefix;
+        const char_type * delimiter;
+        const char_type * postfix;
+    };
+
+
+    // Defines the delimiter values for a specific container and character type
+
+    template <typename T, typename TChar>
+    struct delimiters
+    {
+        using type = delimiters_values<TChar>;
+        static const type values; 
+    };
+
+
+    // Functor to print containers. You can use this directly if you want
+    // to specificy a non-default delimiters type. The printing logic can
+    // be customized by specializing the nested template.
+
+    template <typename T,
+              typename TChar = char,
+              typename TCharTraits = ::std::char_traits<TChar>,
+              typename TDelimiters = delimiters<T, TChar>>
+    struct print_container_helper
+    {
+        using delimiters_type = TDelimiters;
+        using ostream_type = std::basic_ostream<TChar, TCharTraits>;
+
+        template <typename U>
+        struct printer
+        {
+            static void print_body(const U & c, ostream_type & stream)
+            {
+                using std::begin;
+                using std::end;
+
+                auto it = begin(c);
+                const auto the_end = end(c);
+
+                if (it != the_end)
+                {
+                    for ( ; ; )
+                    {
+                        stream << *it;
+
+                    if (++it == the_end) break;
+
+                    if (delimiters_type::values.delimiter != NULL)
+                        stream << delimiters_type::values.delimiter;
+                    }
+                }
+            }
+        };
+
+        print_container_helper(const T & container)
+        : container_(container)
+        { }
+
+        inline void operator()(ostream_type & stream) const
+        {
+            if (delimiters_type::values.prefix != NULL)
+                stream << delimiters_type::values.prefix;
+
+            printer<T>::print_body(container_, stream);
+
+            if (delimiters_type::values.postfix != NULL)
+                stream << delimiters_type::values.postfix;
+        }
+
+    private:
+        const T & container_;
+    };
+
+    // Specialization for pairs
+
+    template <typename T, typename TChar, typename TCharTraits, typename TDelimiters>
+    template <typename T1, typename T2>
+    struct print_container_helper<T, TChar, TCharTraits, TDelimiters>::printer<std::pair<T1, T2>>
+    {
+        using ostream_type = print_container_helper<T, TChar, TCharTraits, TDelimiters>::ostream_type;
+
+        static void print_body(const std::pair<T1, T2> & c, ostream_type & stream)
+        {
+            stream << c.first;
+            if (print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL)
+                stream << print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter;
+            stream << c.second;
+        }
+    };
+
+    // Specialization for tuples
+
+    template <typename T, typename TChar, typename TCharTraits, typename TDelimiters>
+    template <typename ...Args>
+    struct print_container_helper<T, TChar, TCharTraits, TDelimiters>::printer<std::tuple<Args...>>
+    {
+        using ostream_type = print_container_helper<T, TChar, TCharTraits, TDelimiters>::ostream_type;
+        using element_type = std::tuple<Args...>;
+
+        template <std::size_t I> struct Int { };
+
+        static void print_body(const element_type & c, ostream_type & stream)
+        {
+            tuple_print(c, stream, Int<0>());
+        }
+
+        static void tuple_print(const element_type &, ostream_type &, Int<sizeof...(Args)>)
+        {
+        }
+
+        static void tuple_print(const element_type & c, ostream_type & stream,
+                                typename std::conditional<sizeof...(Args) != 0, Int<0>, std::nullptr_t>::type)
+        {
+            stream << std::get<0>(c);
+            tuple_print(c, stream, Int<1>());
+        }
+
+        template <std::size_t N>
+        static void tuple_print(const element_type & c, ostream_type & stream, Int<N>)
+        {
+            if (print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter != NULL)
+                stream << print_container_helper<T, TChar, TCharTraits, TDelimiters>::delimiters_type::values.delimiter;
+
+            stream << std::get<N>(c);
+
+            tuple_print(c, stream, Int<N + 1>());
+        }
+    };
+
+    // Prints a print_container_helper to the specified stream.
+
+    template<typename T, typename TChar, typename TCharTraits, typename TDelimiters>
+    inline std::basic_ostream<TChar, TCharTraits> & operator<<(
+        std::basic_ostream<TChar, TCharTraits> & stream,
+        const print_container_helper<T, TChar, TCharTraits, TDelimiters> & helper)
+    {
+        helper(stream);
+        return stream;
+    }
+
+
+    // Basic is_container template; specialize to derive from std::true_type for all desired container types
+
+    template <typename T>
+    struct is_container : public std::integral_constant<bool,
+                                                        detail::has_const_iterator<T>::value &&
+                                                        detail::has_begin_end<T>::beg_value  &&
+                                                        detail::has_begin_end<T>::end_value> { };
+
+    template <typename T, std::size_t N>
+    struct is_container<T[N]> : std::true_type { };
+
+    template <std::size_t N>
+    struct is_container<char[N]> : std::false_type { };
+
+    template <typename T>
+    struct is_container<std::valarray<T>> : std::true_type { };
+
+    template <typename T1, typename T2>
+    struct is_container<std::pair<T1, T2>> : std::true_type { };
+
+    template <typename ...Args>
+    struct is_container<std::tuple<Args...>> : std::true_type { };
+
+
+    // Default delimiters
+
+    template <typename T> struct delimiters<T, char> { static const delimiters_values<char> values; };
+    template <typename T> const delimiters_values<char> delimiters<T, char>::values = { "[", ", ", "]" };
+    template <typename T> struct delimiters<T, wchar_t> { static const delimiters_values<wchar_t> values; };
+    template <typename T> const delimiters_values<wchar_t> delimiters<T, wchar_t>::values = { L"[", L", ", L"]" };
+
+
+    // Delimiters for (multi)set and unordered_(multi)set
+
+    template <typename T, typename TComp, typename TAllocator>
+    struct delimiters< ::std::set<T, TComp, TAllocator>, char> { static const delimiters_values<char> values; };
+
+    template <typename T, typename TComp, typename TAllocator>
+    const delimiters_values<char> delimiters< ::std::set<T, TComp, TAllocator>, char>::values = { "{", ", ", "}" };
+
+    template <typename T, typename TComp, typename TAllocator>
+    struct delimiters< ::std::set<T, TComp, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
+
+    template <typename T, typename TComp, typename TAllocator>
+    const delimiters_values<wchar_t> delimiters< ::std::set<T, TComp, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
+
+    template <typename T, typename TComp, typename TAllocator>
+    struct delimiters< ::std::multiset<T, TComp, TAllocator>, char> { static const delimiters_values<char> values; };
+
+    template <typename T, typename TComp, typename TAllocator>
+    const delimiters_values<char> delimiters< ::std::multiset<T, TComp, TAllocator>, char>::values = { "{", ", ", "}" };
+
+    template <typename T, typename TComp, typename TAllocator>
+    struct delimiters< ::std::multiset<T, TComp, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
+
+    template <typename T, typename TComp, typename TAllocator>
+    const delimiters_values<wchar_t> delimiters< ::std::multiset<T, TComp, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    struct delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, char> { static const delimiters_values<char> values; };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    const delimiters_values<char> delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, char>::values = { "{", ", ", "}" };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    struct delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    const delimiters_values<wchar_t> delimiters< ::std::unordered_set<T, THash, TEqual, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    struct delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, char> { static const delimiters_values<char> values; };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    const delimiters_values<char> delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, char>::values = { "{", ", ", "}" };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    struct delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
+
+    template <typename T, typename THash, typename TEqual, typename TAllocator>
+    const delimiters_values<wchar_t> delimiters< ::std::unordered_multiset<T, THash, TEqual, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" };
+
+
+    // Delimiters for pair and tuple
+
+    template <typename T1, typename T2> struct delimiters<std::pair<T1, T2>, char> { static const delimiters_values<char> values; };
+    template <typename T1, typename T2> const delimiters_values<char> delimiters<std::pair<T1, T2>, char>::values = { "(", ", ", ")" };
+    template <typename T1, typename T2> struct delimiters< ::std::pair<T1, T2>, wchar_t> { static const delimiters_values<wchar_t> values; };
+    template <typename T1, typename T2> const delimiters_values<wchar_t> delimiters< ::std::pair<T1, T2>, wchar_t>::values = { L"(", L", ", L")" };
+
+    template <typename ...Args> struct delimiters<std::tuple<Args...>, char> { static const delimiters_values<char> values; };
+    template <typename ...Args> const delimiters_values<char> delimiters<std::tuple<Args...>, char>::values = { "(", ", ", ")" };
+    template <typename ...Args> struct delimiters< ::std::tuple<Args...>, wchar_t> { static const delimiters_values<wchar_t> values; };
+    template <typename ...Args> const delimiters_values<wchar_t> delimiters< ::std::tuple<Args...>, wchar_t>::values = { L"(", L", ", L")" };
+
+
+    // Type-erasing helper class for easy use of custom delimiters.
+    // Requires TCharTraits = std::char_traits<TChar> and TChar = char or wchar_t, and MyDelims needs to be defined for TChar.
+    // Usage: "cout << pretty_print::custom_delims<MyDelims>(x)".
+
+    struct custom_delims_base
+    {
+        virtual ~custom_delims_base() { }
+        virtual std::ostream & stream(::std::ostream &) = 0;
+        virtual std::wostream & stream(::std::wostream &) = 0;
+    };
+
+    template <typename T, typename Delims>
+    struct custom_delims_wrapper : custom_delims_base
+    {
+        custom_delims_wrapper(const T & t_) : t(t_) { }
+
+        std::ostream & stream(std::ostream & s)
+        {
+            return s << print_container_helper<T, char, std::char_traits<char>, Delims>(t);
+        }
+
+        std::wostream & stream(std::wostream & s)
+        {
+            return s << print_container_helper<T, wchar_t, std::char_traits<wchar_t>, Delims>(t);
+        }
+
+    private:
+        const T & t;
+    };
+
+    template <typename Delims>
+    struct custom_delims
+    {
+        template <typename Container>
+        custom_delims(const Container & c) : base(new custom_delims_wrapper<Container, Delims>(c)) { }
+
+        std::unique_ptr<custom_delims_base> base;
+    };
+
+    template <typename TChar, typename TCharTraits, typename Delims>
+    inline std::basic_ostream<TChar, TCharTraits> & operator<<(std::basic_ostream<TChar, TCharTraits> & s, const custom_delims<Delims> & p)
+    {
+        return p.base->stream(s);
+    }
+
+
+    // A wrapper for a C-style array given as pointer-plus-size.
+    // Usage: std::cout << pretty_print_array(arr, n) << std::endl;
+
+    template<typename T>
+    struct array_wrapper_n
+    {
+        typedef const T * const_iterator;
+        typedef T value_type;
+
+        array_wrapper_n(const T * const a, size_t n) : _array(a), _n(n) { }
+        inline const_iterator begin() const { return _array; }
+        inline const_iterator end() const { return _array + _n; }
+
+    private:
+        const T * const _array;
+        size_t _n;
+    };
+
+
+    // A wrapper for hash-table based containers that offer local iterators to each bucket.
+    // Usage: std::cout << bucket_print(m, 4) << std::endl;  (Prints bucket 5 of container m.)
+
+    template <typename T>
+    struct bucket_print_wrapper
+    {
+        typedef typename T::const_local_iterator const_iterator;
+        typedef typename T::size_type size_type;
+
+        const_iterator begin() const
+        {
+            return m_map.cbegin(n);
+        }
+
+        const_iterator end() const
+        {
+            return m_map.cend(n);
+        }
+
+        bucket_print_wrapper(const T & m, size_type bucket) : m_map(m), n(bucket) { }
+
+    private:
+        const T & m_map;
+        const size_type n;
+    };
+
+}   // namespace pretty_print
+
+
+// Global accessor functions for the convenience wrappers
+
+template<typename T>
+inline pretty_print::array_wrapper_n<T> pretty_print_array(const T * const a, size_t n)
+{
+    return pretty_print::array_wrapper_n<T>(a, n);
+}
+
+template <typename T> pretty_print::bucket_print_wrapper<T>
+bucket_print(const T & m, typename T::size_type n)
+{
+    return pretty_print::bucket_print_wrapper<T>(m, n);
+}
+
+
+// Main magic entry point: An overload snuck into namespace std.
+// Can we do better?
+
+namespace std
+{
+    // Prints a container to the stream using default delimiters
+
+    template<typename T, typename TChar, typename TCharTraits>
+    inline typename enable_if< ::pretty_print::is_container<T>::value,
+                              basic_ostream<TChar, TCharTraits> &>::type
+    operator<<(basic_ostream<TChar, TCharTraits> & stream, const T & container)
+    {
+        return stream << ::pretty_print::print_container_helper<T, TChar, TCharTraits>(container);
+    }
+}
+
+
+
+#endif  // H_PRETTY_PRINT
diff --git a/src/testframework/unittests/libs/TEST_utils.cpp b/src/testframework/unittests/libs/TEST_utils.cpp
new file mode 100644 (file)
index 0000000..df80cb7
--- /dev/null
@@ -0,0 +1,36 @@
+#include "utils.h"
+
+#include <vector>
+#include <stdio.h>
+
+#include "easyunit/test.h"
+
+TEST(UtilsTest,split)
+{
+    const char *s= "one two three";
+    std::vector<std::string> v= split(s, ' ');
+    ASSERT_TRUE(v.size() == 3);
+    ASSERT_TRUE(v[0] == "one");
+    ASSERT_TRUE(v[1] == "two");
+    ASSERT_TRUE(v[2] == "three");
+}
+
+TEST(UtilsTest,split_empty_string)
+{
+    const char *s= "";
+    std::vector<std::string> v= split(s, ' ');
+
+     ASSERT_TRUE(v.size() == 1);
+     ASSERT_TRUE(v[0].empty());
+     ASSERT_TRUE(v[0] == "");
+}
+
+TEST(UtilsTest,parse_number_list)
+{
+    const char *s= "1.1,2.2,3.3";
+    std::vector<float> v= parse_number_list(s);
+    ASSERT_TRUE(v.size() == 3);
+    ASSERT_TRUE(v[0] == 1.1F);
+    ASSERT_TRUE(v[1] == 2.2F);
+    ASSERT_TRUE(v[2] == 3.3F);
+}
diff --git a/src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp b/src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp
new file mode 100644 (file)
index 0000000..cfcd3c8
--- /dev/null
@@ -0,0 +1,257 @@
+#include "TemperatureSwitch.h"
+#include "Kernel.h"
+#include "checksumm.h"
+#include "utils.h"
+#include "Test_kernel.h"
+#include "PublicDataRequest.h"
+#include "PublicData.h"
+#include "TemperatureControlPublicAccess.h"
+#include "SwitchPublicAccess.h"
+#include "Gcode.h"
+
+#include <stdio.h>
+#include <memory>
+
+#include "easyunit/test.h"
+
+DECLARE(TemperatureSwitch)
+  TemperatureSwitch *ts;
+END_DECLARE
+
+SETUP(TemperatureSwitch)
+{
+  ts = new TemperatureSwitch();
+  //printf("...Setup TemperatureSwitch\n");
+}
+
+TEARDOWN(TemperatureSwitch)
+{
+  delete ts;
+  test_kernel_teardown();
+
+  //printf("...Teardown TemperatureSwitch\n");
+}
+
+const static char edge_low_config[]= "\
+temperatureswitch.psu_off.enable true \n\
+temperatureswitch.psu_off.designator T \n\
+temperatureswitch.psu_off.switch fan \n\
+temperatureswitch.psu_off.threshold_temp 50.0 \n\
+temperatureswitch.psu_off.heatup_poll 5 \n\
+temperatureswitch.psu_off.cooldown_poll 5 \n\
+temperatureswitch.psu_off.arm_mcode 1100 \n\
+temperatureswitch.psu_off.trigger falling \n\
+temperatureswitch.psu_off.inverted false \n\
+";
+
+const static char level_config[]= "\
+temperatureswitch.psu_off.enable true \n\
+temperatureswitch.psu_off.designator T \n\
+temperatureswitch.psu_off.switch fan \n\
+temperatureswitch.psu_off.threshold_temp 50.0 \n\
+temperatureswitch.psu_off.heatup_poll 5 \n\
+temperatureswitch.psu_off.cooldown_poll 5 \n\
+";
+
+// handle mock call to temperature control
+static void on_get_public_data_tc1(void *argument)
+{
+    PublicDataRequest *pdr = static_cast<PublicDataRequest *>(argument);
+
+    if(!pdr->starts_with(temperature_control_checksum)) return;
+    if(!pdr->second_element_is(poll_controls_checksum)) return;
+
+    std::vector<struct pad_temperature> *v= static_cast<std::vector<pad_temperature>*>(pdr->get_data_ptr());
+
+    struct pad_temperature t;
+    // setup data
+    t.designator= "T";
+    t.id= 123;
+    v->push_back(t);
+    pdr->set_taken();
+    pdr->clear_returned_data();
+}
+
+// Handle temperature request and switch status request
+static bool switch_state;
+static bool switch_get_hit= false;
+static int hitcnt= 0;
+static float return_current_temp= 0;
+static void on_get_public_data_tc2(void *argument)
+{
+    PublicDataRequest *pdr = static_cast<PublicDataRequest *>(argument);
+
+    if(pdr->starts_with(temperature_control_checksum)) {
+        if(!pdr->second_element_is(current_temperature_checksum)) return;
+        if(!pdr->third_element_is(123)) return;
+
+        hitcnt++;
+
+        struct pad_temperature *t= static_cast<pad_temperature*>(pdr->get_data_ptr());
+        t->current_temperature = return_current_temp;
+        t->target_temperature = 0;
+        t->pwm = 0;
+        t->designator= "T";
+        t->id= 123;
+        pdr->set_taken();
+        pdr->clear_returned_data();
+
+    } else if(pdr->starts_with(switch_checksum)){
+
+        if(!pdr->second_element_is(get_checksum("fan"))) return;
+
+        switch_get_hit= true;
+
+        static struct pad_switch pad;
+        pad.name = get_checksum("fan");
+        pad.state = switch_state;
+        pad.value = 0;
+
+        pdr->set_data_ptr(&pad);
+        pdr->set_taken();
+    }
+}
+
+static bool switch_set_hit= false;
+static void on_set_public_data_switch(void *argument)
+{
+    PublicDataRequest *pdr = static_cast<PublicDataRequest *>(argument);
+
+    if(!pdr->starts_with(switch_checksum)) return;
+    if(!pdr->second_element_is(get_checksum("fan"))) return;
+    if(!pdr->third_element_is(state_checksum)) return;
+
+    switch_set_hit= true;
+
+    bool t = *static_cast<bool *>(pdr->get_data_ptr());
+    switch_state = t;
+    pdr->set_taken();
+}
+
+
+static bool set_temp(TemperatureSwitch *nts, float t)
+{
+    // trap public data request to TemperatureControl and return a temperature
+    hitcnt= 0;
+    return_current_temp= t;
+    test_kernel_trap_event(ON_GET_PUBLIC_DATA, on_get_public_data_tc2);
+
+    // tick 5 times for 5 seconds
+    for (int i = 0; i < 5; ++i) {
+        nts->on_second_tick(nullptr);
+    }
+
+    // make sure the temp was read at least once
+    return (hitcnt > 0);
+}
+
+TESTF(TemperatureSwitch,level_low_high)
+{
+    // load config with required settings for this test
+    test_kernel_setup_config(level_config, &level_config[sizeof(level_config)]);
+
+    // trap public data request to TemperatureControl and return a mock tempcontrol
+    test_kernel_trap_event(ON_GET_PUBLIC_DATA, on_get_public_data_tc1);
+
+    // make module load the config
+    uint16_t cs= get_checksum("psu_off");
+    std::unique_ptr<TemperatureSwitch> nts(ts->load_config(cs));
+
+    if(nts.get() == nullptr) {
+        FAIL_M("load config failed");
+    }
+
+    // stop handling this event
+    test_kernel_untrap_event(ON_GET_PUBLIC_DATA);
+
+    // test it registered the event
+    ASSERT_TRUE(THEKERNEL->kernel_has_event(ON_GCODE_RECEIVED, nts.get()));
+
+    set_temp(nts.get(), 25);
+
+    // capture any call to the switch to turn it on or off
+    switch_state= false;
+    switch_set_hit= false;
+    switch_get_hit= false;
+    test_kernel_trap_event(ON_SET_PUBLIC_DATA, on_set_public_data_switch);
+
+    // increase temp low -> high
+    set_temp(nts.get(), 60);
+
+    ASSERT_TRUE(switch_get_hit);
+    // make sure switch was set
+    ASSERT_TRUE(switch_set_hit);
+
+    // and make sure it was turned on
+    ASSERT_TRUE(switch_state);
+
+    // now make sure it turns off when temp drops
+    set_temp(nts.get(), 30);
+
+    // and make sure it was turned off
+    ASSERT_TRUE(!switch_state);
+}
+
+TESTF(TemperatureSwitch,edge_high_low)
+{
+    // load config with required settings for this test
+    test_kernel_setup_config(edge_low_config, &edge_low_config[sizeof(edge_low_config)]);
+
+    // trap public data request to TemperatureControl and return a mock tempcontrol
+    test_kernel_trap_event(ON_GET_PUBLIC_DATA, on_get_public_data_tc1);
+
+    // make module load the config
+    uint16_t cs= get_checksum("psu_off");
+    std::unique_ptr<TemperatureSwitch> nts(ts->load_config(cs));
+
+    if(nts.get() == nullptr) {
+        FAIL_M("load config failed");
+    }
+
+    // stop handling this event
+    test_kernel_untrap_event(ON_GET_PUBLIC_DATA);
+
+    // test it registered the event
+    ASSERT_TRUE(THEKERNEL->kernel_has_event(ON_GCODE_RECEIVED, nts.get()));
+    ASSERT_TRUE(!nts->is_armed());
+
+    // set initial temp low
+    set_temp(nts.get(), 25);
+
+    // capture any call to the switch to turn it on or off
+    switch_state= true;
+    test_kernel_trap_event(ON_SET_PUBLIC_DATA, on_set_public_data_switch);
+
+    // increase temp low -> high
+    set_temp(nts.get(), 60);
+
+    // make sure it was not turned off
+    ASSERT_TRUE(switch_state);
+
+    // drop temp
+    set_temp(nts.get(), 30);
+
+    // and make sure it was still on
+    ASSERT_TRUE(switch_state);
+
+    // now arm it
+    Gcode gc("M1100 S1", (StreamOutput *)THEKERNEL->serial, false);
+    nts->on_gcode_received(&gc);
+
+    ASSERT_TRUE(nts->is_armed());
+
+    // increase temp low -> high
+    set_temp(nts.get(), 60);
+
+    // make sure it was not turned off
+    ASSERT_TRUE(switch_state);
+
+    // drop temp
+    set_temp(nts.get(), 30);
+
+    // and make sure it was tunred off
+    ASSERT_TRUE(!switch_state);
+
+    // make sure it is not armed anymore
+    ASSERT_TRUE(!nts->is_armed());
+}