Merge pull request #736 from Smoothieware/development/test-framework
authorJim Morris <morris@wolfman.com>
Mon, 14 Sep 2015 06:48:14 +0000 (23:48 -0700)
committerJim Morris <morris@wolfman.com>
Mon, 14 Sep 2015 06:48:14 +0000 (23:48 -0700)
Development/test framework

38 files changed:
Rakefile
build/common.mk
rakefile.defaults.example [new file with mode: 0644]
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/Readme.md [new file with mode: 0644]
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/switch/TEST_Switch.cpp [new file with mode: 0644]
src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp [new file with mode: 0644]

index 9c7aa71..5213d6b 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,21 @@ else
   nonetwork= false
 end
 
-SRC = FileList['src/**/*.{c,cpp}'].exclude(/#{excludes.join('|')}/)
+if TESTING
+  # add modules to be tested here
+  TESTMODULES= %w(tools/temperatureswitch) unless defined? EXCLUDE_MODULES
+  puts "Modules under test: #{TESTMODULES}"
+  excludes << %w(Kernel.cpp main.cpp) # we replace these with mock versions in testframework
 
-puts "WARNING Excluding modules: #{EXCLUDE_MODULES.join(' ')}" unless exclude_defines.empty?
+  frameworkfiles= FileList['src/testframework/*.{c,cpp}', 'src/testframework/easyunit/*.{c,cpp}']
+  extrafiles= FileList['src/modules/communication/SerialConsole.cpp', 'src/modules/communication/utils/Gcode.cpp', 'src/modules/robot/Conveyor.cpp', 'src/modules/robot/Block.cpp']
+  testmodules= FileList['src/libs/**/*.{c,cpp}'].include(TESTMODULES.collect { |e| "src/modules/#{e}/**/*.{c,cpp}"}).include(TESTMODULES.collect { |e| "src/testframework/unittests/#{e}/*.{c,cpp}"}).exclude(/#{excludes.join('|')}/)
+  SRC =  frameworkfiles + extrafiles + 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 +172,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 +198,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 +273,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 +298,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))
 
diff --git a/rakefile.defaults.example b/rakefile.defaults.example
new file mode 100644 (file)
index 0000000..3026938
--- /dev/null
@@ -0,0 +1,8 @@
+# if regular build the modules to exclude from the build
+NONETWORK = true
+EXCLUDE_MODULES = %w(tools/touchprobe tools/laser tools/temperaturecontrol tools/extruder)
+
+# if test framework build the modules to include in the test...
+# here we enable unit tests for tools/temperatureswitch module and any unit tests in /src/testframework/unittests/tools/temperatureswitch/
+# and any unittests in the src/testframework/unittests/libs folder
+TESTMODULES= %w(tools/temperatureswitch libs)
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 0ed3109..8f83f29 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/Readme.md b/src/testframework/Readme.md
new file mode 100644 (file)
index 0000000..defffe5
--- /dev/null
@@ -0,0 +1,57 @@
+# Test framework
+
+## Background
+
+
+This is a frame work for writing unit tests for modules or libs.
+It compiles only src/libs/... and a few select needed modules by default.
+It runs on the target and the output is written to the serial UART, it runs well with MRI so you can also debug with GDB.
+
+You can edit a file to specify which module you are going to test by default the example has tools/temperaturecontrol selected.
+
+The unit tests go in a directory named src/testframework/unittests/*XXXX*/*YYYY* where *XXXX* is the module subdirectory (tools,utils) and *YYYY* is the modulename
+
+The Kernel and main are replaced with special versions for testing.
+
+The main way to test is to stimulate the modules by sending it commands via
+on_gcode_received() (or other events it is registered for), and monitor its
+actions my mocking the ON_PUBLIC_SET/GET calls it makes. A properly written
+module does not access other modules any other way.
+
+Many HAL calls are made via THEKERNEL->xxx (eg THEKERNEL->adc) and these can be intercepted by mocks to provide the module with its external data.
+
+An example is provided here...
+
+`src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp`
+
+## Usage
+
+You compile a unit test and framework using [rake](http://rake.rubyforge.org/)...
+
+```shell
+> rake testing=1
+> rake upload
+```
+
+The unit test will run, the results are printed to the serial uart port, and then it is left in DFU mode so `rake upload`  can be run again for the next test.
+
+You select which tests and modules you want to compile by creating a file called rakefile.defaults in the same directory as the Rakefile is found.
+
+```shell
+TESTMODULES= %w(tools/temperatureswitch libs)
+```
+
+Here we enable unit tests for the tools/temperatureswitch module and any unit tests in src/testframework/unittests/tools/temperatureswitch/.
+Also any unittests in the src/testframework/unittests/libs folder will be included
+
+In this case all files in src/libs/... are compiled anyway so we only need to enable unit tests in the src/testframework/unittests/libs folder.
+
+the tools/temperatureswitch both compiles all files in the src/modules/tools/temperatureswitch/... directory and enables the
+unit tests in src/testframework/unittests/tools/temperatureswitch/...
+
+by default no other files in the src/modules/... directory tree are compiled unless specified above.
+
+
+
+
+
diff --git a/src/testframework/Test_kernel.cpp b/src/testframework/Test_kernel.cpp
new file mode 100644 (file)
index 0000000..f9ee86c
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+      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   = "/";
+
+    this->slow_ticker = new SlowTicker();
+
+    // dummies (would be nice to refactor to not have to create a conveyor)
+    this->conveyor= new Conveyor();
+
+    // 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..d339654
--- /dev/null
@@ -0,0 +1,47 @@
+#include "utils.h"
+
+#include <vector>
+#include <stdio.h>
+#include <string.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);
+}
+
+TEST(UtilsTest,append_parameters)
+{
+    char buf[132];
+
+    int n= append_parameters(buf, {{'X', 1}, {'Y', 2}, {'Z', 3}}, sizeof(buf));
+    //printf("%d - %s\n", n, buf);
+    ASSERT_TRUE(n == 24);
+    ASSERT_TRUE(strcmp(buf, "X1.0000 Y2.0000 Z3.0000 ") == 0);
+}
diff --git a/src/testframework/unittests/tools/switch/TEST_Switch.cpp b/src/testframework/unittests/tools/switch/TEST_Switch.cpp
new file mode 100644 (file)
index 0000000..7201393
--- /dev/null
@@ -0,0 +1,90 @@
+#include "Kernel.h"
+#include "checksumm.h"
+#include "utils.h"
+#include "Test_kernel.h"
+#include "PublicDataRequest.h"
+#include "PublicData.h"
+#include "SwitchPublicAccess.h"
+#include "Gcode.h"
+#include "Switch.h"
+#include "mri.h"
+
+#include <stdio.h>
+#include <memory>
+
+#include "easyunit/test.h"
+
+// this declares any global variables the test needs
+DECLARE(Switch)
+  Switch *ts;
+  uint16_t switch_id;
+END_DECLARE
+
+// called before each test
+SETUP(Switch)
+{
+    // create the module we want to test
+    switch_id= get_checksum("fan");
+    ts = new Switch(switch_id);
+    //printf("...Setup Switch\n");
+}
+
+// called after each test
+TEARDOWN(Switch)
+{
+    // delete the module
+    delete ts;
+
+    // have kernel reset to a clean state
+    test_kernel_teardown();
+
+    //printf("...Teardown Switch\n");
+}
+
+// define various configs here, these are in the same formate they would appeat in the config file (comments removed for clarity)
+const static char switch_config[]= "\
+switch.fan.enable true \n\
+switch.fan.input_on_command M106 \n\
+switch.fan.input_off_command M107 \n\
+switch.fan.output_pin 2.6  \n\
+switch.fan.output_type digital  \n\
+";
+
+static bool get_switch_state(Switch *ts, struct pad_switch& s)
+{
+    // inquire if the switch is on or off (simulate an ON_GET_PUBLIC_DATA)
+    PublicDataRequest pdr(switch_checksum, fan_checksum, 0);
+    pdr.set_data_ptr(&s, false); // caller providing storage
+    ts->on_get_public_data(&pdr);
+    return(pdr.is_taken() && !pdr.has_returned_data());
+}
+
+TESTF(Switch,set_on_off_with_gcode)
+{
+    // load config with required settings for this test
+    test_kernel_setup_config(switch_config, &switch_config[sizeof(switch_config)]);
+
+    // make module load the config
+    ts->on_config_reload(nullptr);
+
+    // make sure switch starts off
+    struct pad_switch s;
+    ASSERT_TRUE(get_switch_state(ts, s));
+    ASSERT_EQUALS(s.name, switch_id);
+    ASSERT_TRUE(!s.state);
+
+    // if we want to enter the debugger here
+    //__debugbreak();
+
+    // now turn it on
+    Gcode gc1("M106", (StreamOutput *)THEKERNEL->serial);
+    ts->on_gcode_received(&gc1);
+    ASSERT_TRUE(get_switch_state(ts, s));
+    ASSERT_TRUE(s.state);
+
+    // now turn it off
+    Gcode gc2("M107", (StreamOutput *)THEKERNEL->serial);
+    ts->on_gcode_received(&gc2);
+    ASSERT_TRUE(get_switch_state(ts, s));
+    ASSERT_TRUE(!s.state);
+}
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..c5cf33f
--- /dev/null
@@ -0,0 +1,265 @@
+#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"
+
+// this declares any global variables the test needs
+DECLARE(TemperatureSwitch)
+  TemperatureSwitch *ts;
+END_DECLARE
+
+// called before each test
+SETUP(TemperatureSwitch)
+{
+    // create the module we want to test
+    ts = new TemperatureSwitch();
+    //printf("...Setup TemperatureSwitch\n");
+}
+
+// called after each test
+TEARDOWN(TemperatureSwitch)
+{
+    // delete the module
+    delete ts;
+
+    // have kernel reset to a clean state
+    test_kernel_teardown();
+
+    //printf("...Teardown TemperatureSwitch\n");
+}
+
+// define various configs here, these are in the same formate they would appeat in the config file (comments removed for clarity)
+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
+// simulates one temperature control with T designator and id of 123
+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();
+}
+
+// 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)) {
+        // return a current temperature of return_current_temp when requested
+        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();
+
+    } else if(pdr->starts_with(switch_checksum)){
+        // return status of the fan switch
+        if(!pdr->second_element_is(get_checksum("fan"))) return;
+
+        switch_get_hit= true;
+
+        static struct pad_switch *pad= static_cast<pad_switch*>(pdr->get_data_ptr());;
+        pad->name = get_checksum("fan");
+        pad->state = switch_state;
+        pad->value = 0;
+        pdr->set_taken();
+    }
+}
+
+// simulates the switch being turned on or off so we can test the state
+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();
+}
+
+// stimulates the module with on_second ticks in which it asks for a temperature which we simulate above
+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 the first temperature
+    ASSERT_TRUE(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
+    ASSERT_TRUE(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
+    ASSERT_TRUE(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
+    ASSERT_TRUE(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
+    ASSERT_TRUE(set_temp(nts.get(), 60));
+
+    // make sure it was not turned off
+    ASSERT_TRUE(switch_state);
+
+    // drop temp
+    ASSERT_TRUE(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
+    ASSERT_TRUE(set_temp(nts.get(), 60));
+
+    // make sure it was not turned off
+    ASSERT_TRUE(switch_state);
+
+    // drop temp
+    ASSERT_TRUE(set_temp(nts.get(), 30));
+
+    // and make sure it was turned off
+    ASSERT_TRUE(!switch_state);
+
+    // make sure it is not armed anymore
+    ASSERT_TRUE(!nts->is_armed());
+}