From e6b3d64c67c9a0aa2ffbfd845ef53751887c64e9 Mon Sep 17 00:00:00 2001 From: Jim Morris Date: Sat, 12 Sep 2015 14:29:29 -0700 Subject: [PATCH 1/4] Add test framework using easyunit add unit test for TemperatureSwitch add unit test for some utils functions as examples added unregister_for_event() to Kernel so modules can be tested Hack FirmConfigSource to allow configs to be specified in memory for testing allow config to be deleted Modified Rakefile to allow buildong of test framework with rake testing=1 modify makefiles to ignore the src/testframework for builds --- Rakefile | 40 +- build/common.mk | 12 +- src/libs/Config.cpp | 14 + src/libs/Config.h | 2 + src/libs/ConfigSource.h | 1 + src/libs/ConfigSources/FirmConfigSource.cpp | 25 +- src/libs/ConfigSources/FirmConfigSource.h | 21 +- src/libs/Kernel.cpp | 25 +- src/libs/Kernel.h | 6 +- .../temperatureswitch/TemperatureSwitch.cpp | 36 +- .../temperatureswitch/TemperatureSwitch.h | 6 +- src/testframework/Test_kernel.cpp | 131 ++++++ src/testframework/Test_kernel.h | 9 + src/testframework/Test_main.cpp | 35 ++ .../easyunit/defaulttestprinter.cpp | 215 +++++++++ .../easyunit/defaulttestprinter.h | 125 +++++ src/testframework/easyunit/simplestring.cpp | 137 ++++++ src/testframework/easyunit/simplestring.h | 70 +++ src/testframework/easyunit/test.cpp | 143 ++++++ src/testframework/easyunit/test.h | 433 +++++++++++++++++ src/testframework/easyunit/testcase.cpp | 170 +++++++ src/testframework/easyunit/testcase.h | 154 ++++++ src/testframework/easyunit/testharness.h | 38 ++ src/testframework/easyunit/testpartresult.cpp | 67 +++ src/testframework/easyunit/testpartresult.h | 131 ++++++ src/testframework/easyunit/testprinter.h | 52 ++ src/testframework/easyunit/testregistry.cpp | 140 ++++++ src/testframework/easyunit/testregistry.h | 128 +++++ src/testframework/easyunit/testresult.cpp | 117 +++++ src/testframework/easyunit/testresult.h | 146 ++++++ src/testframework/easyunit/testrunner.cpp | 45 ++ src/testframework/easyunit/testrunner.h | 57 +++ src/testframework/prettyprint.hpp | 445 ++++++++++++++++++ .../unittests/libs/TEST_utils.cpp | 36 ++ .../TEST_TemperatureSwitch.cpp | 257 ++++++++++ 35 files changed, 3418 insertions(+), 51 deletions(-) create mode 100644 src/testframework/Test_kernel.cpp create mode 100644 src/testframework/Test_kernel.h create mode 100644 src/testframework/Test_main.cpp create mode 100644 src/testframework/easyunit/defaulttestprinter.cpp create mode 100644 src/testframework/easyunit/defaulttestprinter.h create mode 100644 src/testframework/easyunit/simplestring.cpp create mode 100644 src/testframework/easyunit/simplestring.h create mode 100644 src/testframework/easyunit/test.cpp create mode 100644 src/testframework/easyunit/test.h create mode 100644 src/testframework/easyunit/testcase.cpp create mode 100644 src/testframework/easyunit/testcase.h create mode 100644 src/testframework/easyunit/testharness.h create mode 100644 src/testframework/easyunit/testpartresult.cpp create mode 100644 src/testframework/easyunit/testpartresult.h create mode 100644 src/testframework/easyunit/testprinter.h create mode 100644 src/testframework/easyunit/testregistry.cpp create mode 100644 src/testframework/easyunit/testregistry.h create mode 100644 src/testframework/easyunit/testresult.cpp create mode 100644 src/testframework/easyunit/testresult.h create mode 100644 src/testframework/easyunit/testrunner.cpp create mode 100644 src/testframework/easyunit/testrunner.h create mode 100644 src/testframework/prettyprint.hpp create mode 100644 src/testframework/unittests/libs/TEST_utils.cpp create mode 100644 src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp diff --git a/Rakefile b/Rakefile index 9c7aa719d8..105e13cb87 100644 --- a/Rakefile +++ b/Rakefile @@ -4,6 +4,7 @@ require 'fileutils' verbose(ENV['verbose'] == '1') DEBUG = ENV['debug'] == '1' +TESTING = ENV['testing'] == '1' def pop_path(path) Pathname(path).each_filename.to_a[1..-1] @@ -73,8 +74,10 @@ SIZE = "#{TOOLSBIN}size" # include a defaults file if present load 'rakefile.defaults' if File.exists?('rakefile.defaults') +if TESTING + BUILDTYPE= 'Testing' -if DEBUG +elsif DEBUG BUILDTYPE= 'Debug' ENABLE_DEBUG_MONITOR= '0' end @@ -89,7 +92,9 @@ ENABLE_DEBUG_MONITOR = ENV['ENABLE_DEBUG_MONITOR'] || '0' unless defined? ENABLE DEFAULT_SERIAL_BAUD_RATE= ENV['BAUDRATE'] || '115200' unless defined? DEFAULT_SERIAL_BAUD_RATE # set to true to eliminate all the network code -NONETWORK= false unless defined? NONETWORK +unless defined? NONETWORK + NONETWORK= false || TESTING +end # list of modules to exclude, include directory it is in EXCLUDE_MODULES= %w(tools/touchprobe) unless defined? EXCLUDE_MODULES @@ -109,9 +114,18 @@ else nonetwork= false end -SRC = FileList['src/**/*.{c,cpp}'].exclude(/#{excludes.join('|')}/) - -puts "WARNING Excluding modules: #{EXCLUDE_MODULES.join(' ')}" unless exclude_defines.empty? +if TESTING + # add modules to be tested here + TESTMODULES= %w(tools/temperatureswitch) + puts "Modules under test: #{TESTMODULES}" + excludes << %w(Kernel.cpp main.cpp) # we replace these with mock versions in testframework + testmodules= FileList['src/testframework/*.{c,cpp}', 'src/testframework/easyunit/*.{c,cpp}', 'src/modules/communication/SerialConsole.cpp', 'src/modules/communication/utils/Gcode.cpp'].include(TESTMODULES.collect { |e| "src/modules/#{e}/**/*.{c,cpp}"}).include(TESTMODULES.collect { |e| "src/testframework/unittests/#{e}/*.{c,cpp}"}) + SRC = FileList['src/libs/**/*.{c,cpp}'].exclude(/#{excludes.join('|')}/) + testmodules +else + excludes << %w(testframework) + SRC = FileList['src/**/*.{c,cpp}'].exclude(/#{excludes.join('|')}/) + puts "WARNING Excluding modules: #{EXCLUDE_MODULES.join(' ')}" unless exclude_defines.empty? +end OBJDIR = 'OBJ' OBJ = SRC.collect { |fn| File.join(OBJDIR, pop_path(File.dirname(fn)), File.basename(fn).ext('o')) } + @@ -155,6 +169,10 @@ when 'checked' OPTIMIZATION = 2 MRI_ENABLE = 1 MRI_SEMIHOST_STDIO = 1 unless defined? MRI_SEMIHOST_STDIO +when 'testing' + OPTIMIZATION = 0 + MRI_ENABLE = 1 + MRI_SEMIHOST_STDIO = 0 unless defined? MRI_SEMIHOST_STDIO end MRI_ENABLE = 1 unless defined? MRI_ENABLE # set to 0 to disable MRI @@ -177,8 +195,9 @@ DEFINES= defines.join(' ') # Compiler flags used to enable creation of header dependencies. DEPFLAGS = '-MMD ' -CFLAGS = DEPFLAGS + "-Wall -Wextra -Wno-unused-parameter -Wcast-align -Wpointer-arith -Wredundant-decls -Wcast-qual -Wcast-align -O#{OPTIMIZATION} -g3 -mcpu=cortex-m3 -mthumb -mthumb-interwork -ffunction-sections -fdata-sections -fno-exceptions -fno-delete-null-pointer-checks" -CPPFLAGS = CFLAGS + ' -fno-rtti -std=gnu++11' +CFLAGS = DEPFLAGS + "-Wall -Wextra -Wno-unused-parameter -Wcast-align -Wpointer-arith -Wredundant-decls -Wcast-qual -Wcast-align -O#{OPTIMIZATION} -g3 -mcpu=cortex-m3 -mthumb -mthumb-interwork -ffunction-sections -fdata-sections -fno-delete-null-pointer-checks" +CPPFLAGS = CFLAGS + ' -fno-rtti -std=gnu++11 -fno-exceptions' +CXXFLAGS = CFLAGS + ' -fno-rtti -std=gnu++11 -fexceptions' # used for a .cxx file that needs to be compiled with exceptions MRI_WRAPS = MRI_ENABLE == 1 ? ',--wrap=_read,--wrap=_write,--wrap=semihost_connected' : '' @@ -251,7 +270,7 @@ file MBED_LIB do end file "#{OBJDIR}/mbed_custom.o" => ['./build/mbed_custom.cpp'] do |t| - puts "Compiling #{t.source}" + puts "Compiling mbed_custom.cpp" sh "#{CCPP} #{CPPFLAGS} #{INCLUDE} #{DEFINES} -c -o #{t.name} #{t.prerequisites[0]}" end @@ -276,6 +295,11 @@ rule '.o' => lambda{ |objfile| obj2src(objfile, 'cpp') } do |t| sh "#{CCPP} #{CPPFLAGS} #{INCLUDE} #{DEFINES} #{VERSION} -c -o #{t.name} #{t.source}" end +rule '.o' => lambda{ |objfile| obj2src(objfile, 'cxx') } do |t| + puts "Compiling #{t.source}" + sh "#{CCPP} #{CXXFLAGS} #{INCLUDE} #{DEFINES} #{VERSION} -c -o #{t.name} #{t.source}" +end + rule '.o' => lambda{ |objfile| obj2src(objfile, 'c') } do |t| puts "Compiling #{t.source}" sh "#{CC} #{CFLAGS} #{INCLUDE} #{DEFINES} #{VERSION} -c -o #{t.name} #{t.source}" diff --git a/build/common.mk b/build/common.mk index 9d30f281bd..041930193d 100755 --- a/build/common.mk +++ b/build/common.mk @@ -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/src/libs/Config.cpp b/src/libs/Config.cpp index b4e11b7bf6..af14f8bc2d 100644 --- a/src/libs/Config.cpp +++ b/src/libs/Config.cpp @@ -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 ) {} diff --git a/src/libs/Config.h b/src/libs/Config.h index ac19973957..d7bc7cc63c 100644 --- a/src/libs/Config.h +++ b/src/libs/Config.h @@ -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 ); diff --git a/src/libs/ConfigSource.h b/src/libs/ConfigSource.h index b5c86844f8..2ac2d02681 100644 --- a/src/libs/ConfigSource.h +++ b/src/libs/ConfigSource.h @@ -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; diff --git a/src/libs/ConfigSources/FirmConfigSource.cpp b/src/libs/ConfigSources/FirmConfigSource.cpp index aa9c13ae0f..d5c362ff47 100644 --- a/src/libs/ConfigSources/FirmConfigSource.cpp +++ b/src/libs/ConfigSources/FirmConfigSource.cpp @@ -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); diff --git a/src/libs/ConfigSources/FirmConfigSource.h b/src/libs/ConfigSources/FirmConfigSource.h index f662e37a16..ea7e810d52 100644 --- a/src/libs/ConfigSources/FirmConfigSource.h +++ b/src/libs/ConfigSources/FirmConfigSource.h @@ -16,14 +16,19 @@ class ConfigCache; using namespace std; #include -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; }; diff --git a/src/libs/Kernel.cpp b/src/libs/Kernel.cpp index 3167582a7c..5583b440c2 100644 --- a/src/libs/Kernel.cpp +++ b/src/libs/Kernel.cpp @@ -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; + } + } +} + diff --git a/src/libs/Kernel.h b/src/libs/Kernel.h index 520597d51a..948ba2e70d 100644 --- a/src/libs/Kernel.h +++ b/src/libs/Kernel.h @@ -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; diff --git a/src/modules/tools/temperatureswitch/TemperatureSwitch.cpp b/src/modules/tools/temperatureswitch/TemperatureSwitch.cpp index 151e4f2967..b960ec4777 100755 --- a/src/modules/tools/temperatureswitch/TemperatureSwitch.cpp +++ b/src/modules/tools/temperatureswitch/TemperatureSwitch.cpp @@ -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 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 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) diff --git a/src/modules/tools/temperatureswitch/TemperatureSwitch.h b/src/modules/tools/temperatureswitch/TemperatureSwitch.h index 4750cbbf0e..99c8286609 100755 --- a/src/modules/tools/temperatureswitch/TemperatureSwitch.h +++ b/src/modules/tools/temperatureswitch/TemperatureSwitch.h @@ -26,16 +26,18 @@ class TemperatureSwitch : public Module { public: TemperatureSwitch(); + ~TemperatureSwitch(); void on_module_loaded(); void on_second_tick(void *argument); void on_gcode_received(void *argument); + TemperatureSwitch* load_config(uint16_t modcs); + + bool is_armed() const { return armed; } private: enum TRIGGER_TYPE {LEVEL, RISING, FALLING}; enum STATE {NONE, HIGH_TEMP, LOW_TEMP}; - bool load_config(uint16_t modcs); - // get the highest temperature from the set of configured temperature controllers float get_highest_temperature(); diff --git a/src/testframework/Test_kernel.cpp b/src/testframework/Test_kernel.cpp new file mode 100644 index 0000000000..f12896704d --- /dev/null +++ b/src/testframework/Test_kernel.cpp @@ -0,0 +1,131 @@ +/* + This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). + Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with Smoothie. If not, see . +*/ + +/** +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 +#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 +#include +#include +#include + +Kernel* Kernel::instance; + +// The kernel is the central point in Smoothie : it stores modules, and handles event calls +Kernel::Kernel(){ + instance= this; // setup the Singleton instance of the kernel + + // serial first at fixed baud rate (DEFAULT_SERIAL_BAUD_RATE) so config can report errors to serial + // Set to UART0, this will be changed to use the same UART as MRI if it's enabled + this->serial = new SerialConsole(USBTX, USBRX, DEFAULT_SERIAL_BAUD_RATE); + + // Config next, but does not load cache yet + // loads config from in memory source for test framework must be loaded by test + this->config = nullptr; + + this->streams = new StreamOutputPool(); + this->streams->append_stream(this->serial); + + this->current_path = "/"; + + // Configure UART depending on MRI config + // Match up the SerialConsole to MRI UART. This makes it easy to use only one UART for both debug and actual commands. + NVIC_SetPriorityGrouping(0); + NVIC_SetPriority(UART0_IRQn, 5); +} + +// Add a module to Kernel. We don't actually hold a list of modules we just call its on_module_loaded +void Kernel::add_module(Module* module){ + module->on_module_loaded(); +} + +// Adds a hook for a given module and event +void Kernel::register_for_event(_EVENT_ENUM id_event, Module *mod){ + this->hooks[id_event].push_back(mod); +} + +static std::map<_EVENT_ENUM, std::function > 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 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 index 0000000000..1c6a0c51b3 --- /dev/null +++ b/src/testframework/Test_kernel.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Module.h" +#include + +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 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 index 0000000000..e1ee90d475 --- /dev/null +++ b/src/testframework/Test_main.cpp @@ -0,0 +1,35 @@ +#include +#include +#include + +#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 index 0000000000..a7ee684ac1 --- /dev/null +++ b/src/testframework/easyunit/defaulttestprinter.cpp @@ -0,0 +1,215 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#include "defaulttestprinter.h" + +#include "testpartresult.h" + +#include + + +DefaultTestPrinter::DefaultTestPrinter() +: testsTotal_(0),testFailuresTotal_(0),failuresTotal_(0), + level_(normal), showSuccessDetail_(false), output_(stdout) +{ +} + +DefaultTestPrinter::~DefaultTestPrinter() +{ +} + +void DefaultTestPrinter::print(const TestResult *testResult) +{ + int failures; + int successes; + int errors; + SimpleString state; + SimpleString name; + TestCase *testCase = testResult->getTestCases(); + int size = testResult->getTestCaseCount(); + + printHeader(testResult); + + if (testResult->getTestCaseRanCount() == 0) { + fprintf(output_,"\nNo test ran\n"); + } + + for (int i=0;iran()) { + + name = testCase->getName(); + failures = testCase->getFailuresCount(); + successes = testCase->getSuccessesCount(); + errors = testCase->getErrorsCount(); + + if (failures > 0 || errors > 0) { + state = "FAILED"; + } + else { + state = "SUCCEEDED"; + } + + 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); + + printTests(testCase); + } + + testCase = testCase->getNext(); + } +} + +void DefaultTestPrinter::setHeaderLevel(headerLevel level) +{ + level_ = level; +} + +void DefaultTestPrinter::showSuccessDetail(bool show) +{ + showSuccessDetail_ = show; +} + +void DefaultTestPrinter::setOutput(FILE *output) +{ + output_ = output; +} + +void DefaultTestPrinter::printHeader(const TestResult *testResult) +{ + fprintf(output_ , "-- EasyUnit Results --\n"); + + if (level_ != off) { + fprintf(output_ , "\nSUMMARY\n\n"); + fprintf(output_ , "Test summary: "); + + if (testResult->getErrors() > 0 || testResult->getFailures() > 0) { + fprintf(output_ , "FAIL\n"); + } + else { + fprintf(output_ , "SUCCESS\n"); + } + + if (level_ == normal) { + printNormalHeader(testResult); + } + else { + printCompleteHeader(testResult); + } + } + + fprintf(output_ , "\n"); + fprintf(output_ , "\nDETAILS"); +} + +void DefaultTestPrinter::printCompleteHeader(const TestResult *testResult) +{ + fprintf(output_ , "Number of test cases: %d\n",testResult->getTestCaseCount()); + fprintf(output_ , "Number of test cases ran: %d\n",testResult->getTestCaseRanCount()); + fprintf(output_ , "Test cases that succeeded: %d\n",testResult->getSuccesses()); + fprintf(output_ , "Test cases with errors: %d\n",testResult->getErrors()); + fprintf(output_ , "Test cases that failed: %d\n",testResult->getFailures()); + fprintf(output_ , "Number of tests ran: %d\n",testResult->getTestRanCount()); + fprintf(output_ , "Tests that succeeded: %d\n",testResult->getTotalSuccesses()); + fprintf(output_ , "Tests with errors: %d\n",testResult->getTotalErrors()); + fprintf(output_ , "Tests that failed: %d\n",testResult->getTotalFailures()); + +} + +void DefaultTestPrinter::printNormalHeader(const TestResult *testResult) +{ + fprintf(output_ , "Number of test cases ran: %d\n",testResult->getTestCaseRanCount()); + fprintf(output_ , "Test cases that succeeded: %d\n",testResult->getSuccesses()); + fprintf(output_ , "Test cases with errors: %d\n",testResult->getErrors()); + fprintf(output_ , "Test cases that failed: %d\n",testResult->getFailures()); +} + +void DefaultTestPrinter::printTests(TestCase *testCase) +{ + const char *indent = " "; + Test *test = testCase->getTests(); + int size = testCase->getTestsCount(); + SimpleString state; + + + + for (int i=0;igetFailuresCount() > 0 || test->getErrorsCount() > 0) { + state = "FAILED :"; + } + else { + state = "SUCCEEDED!"; + } + + fprintf(output_, "%s Test \"%s\" %s\n",indent,test->getTestName().asCharString(),state.asCharString()); + printResults(test); + test = test->getNext(); + } +} + +void DefaultTestPrinter::printResults(Test *test) +{ + const char *indent = " "; + TestPartResult *testPR = test->getTestPartResult(); + int size = test->getFailuresCount() + test->getSuccessesCount() + test->getErrorsCount(); + int type; + + for (int i=0;igetType(); + + if (type == failure) { + fprintf (output_, "%s%s%s%s%s%ld%s%s\n", + indent, + "Failure: \"", + testPR->getMessage().asCharString (), + "\" " , + "line ", + testPR->getLineNumber(), + " in ", + testPR->getFileName().asCharString ()); + } + else if (type == error) { + fprintf (output_, "%s%s%s%s%s%s\n", + indent, + "Error in ", + test->getTestName().asCharString(), + ": \"", + testPR->getMessage().asCharString (), + "\""); + } + else if (type == success && showSuccessDetail_) { + fprintf (output_, "%s%s%s%s%s%ld%s%s\n", + indent, + "Success: \"", + testPR->getMessage().asCharString (), + "\" " , + "line ", + testPR->getLineNumber(), + " in ", + testPR->getFileName().asCharString ()); + } + testPR = testPR->getNext(); + } +} + + + diff --git a/src/testframework/easyunit/defaulttestprinter.h b/src/testframework/easyunit/defaulttestprinter.h new file mode 100644 index 0000000000..3824430d78 --- /dev/null +++ b/src/testframework/easyunit/defaulttestprinter.h @@ -0,0 +1,125 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#ifndef DEFAULTTESTPRINTER_H +#define DEFAULTTESTPRINTER_H + +#include "testprinter.h" +#include "testcase.h" +#include "test.h" +#include "testresult.h" +#include + + +/** + * Complete header level means that a header will be printed + * before the test details with all information available in + * the test result. + * + * Normal header level means that a header will be printed + * before the test details with the most useful information + * available in the test result. + * + * Off header level means that no header will be printed + * before the test details. + * + * Whatever the level, there will always be a clear indication + * telling if there was a failure/error or not at the global + * level. + */ +enum headerLevel {complete,normal,off}; + +/** + * This is the default testprinter used by easyunit testregistry + * when the user calls the runAndPrint() method without specifying + * a testprinter. + * + * This testprinter writes plain text result to any supplied file. + * The default file is the standard output. + * + * You may customize the outpur format by specifying the header level + * and if you wish the testprinter to print details about each success. + * + * The default header level is normal and by default, the testprinter + * does not print details about each success. + */ +class DefaultTestPrinter : public TestPrinter +{ + public: + + /** + * Default constructor that sets the header level + * to normal and the output source to the standard + * output. + */ + DefaultTestPrinter(); + + /** + * Empty destructor. + */ + virtual ~DefaultTestPrinter(); + /** + * Prints a header depending of the header level and + * details about each test to the output_. + * + * @param testResult Results of all tests that were ran. + */ + virtual void print(const TestResult *testResult); + + /** + * Set the header level of the printer. + * + * @param level Header level that will be used during print() + */ + void setHeaderLevel(headerLevel level); + + /** + * Set whether or not the printer should display the details + * of test that succeeded. + * + * @param show Set to true to display details about success + */ + void showSuccessDetail(bool show); + + /** + * Set the output to which the printer will print results. + * + * @param output Output used to print the results + */ + void setOutput(FILE *output); + + protected: + virtual void printHeader(const TestResult *testResult); + virtual void printTests(TestCase *testCase); + virtual void printResults(Test *test); + virtual void printCompleteHeader(const TestResult *testResult); + virtual void printNormalHeader(const TestResult *testResult); + int testsTotal_; + int testFailuresTotal_; + int failuresTotal_; + headerLevel level_; + bool showSuccessDetail_; + FILE *output_; +}; + +#endif // DEFAULTTESTPRINTER_H + diff --git a/src/testframework/easyunit/simplestring.cpp b/src/testframework/easyunit/simplestring.cpp new file mode 100644 index 0000000000..34232f533f --- /dev/null +++ b/src/testframework/easyunit/simplestring.cpp @@ -0,0 +1,137 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#include "simplestring.h" +#include +#include +#include + + +static const int DEFAULT_SIZE = 20; + +SimpleString::SimpleString () +: buffer(new char [1]) +{ + buffer [0] = '\0'; +} + + +SimpleString::SimpleString (const char *otherBuffer) +: buffer (new char [strlen (otherBuffer) + 1]) +{ + strcpy (buffer, otherBuffer); +} + +SimpleString::SimpleString (const SimpleString& other) +{ + buffer = new char [other.size() + 1]; + strcpy(buffer, other.buffer); +} + + +SimpleString SimpleString::operator= (const SimpleString& other) +{ + delete buffer; + buffer = new char [other.size() + 1]; + strcpy(buffer, other.buffer); + return *this; +} + +SimpleString SimpleString::operator+ (const SimpleString& other) +{ + SimpleString newS; + delete [] newS.buffer; + newS.buffer = new char[this->size()+other.size()+1]; + strcpy(newS.buffer,this->asCharString()); + newS.buffer= strcat(newS.buffer,other.asCharString()); + return newS; +} + +char *SimpleString::asCharString () const +{ + return buffer; +} + +int SimpleString::size() const +{ + return strlen (buffer); +} + +SimpleString::~SimpleString () +{ + delete [] buffer; +} + +bool operator== (const SimpleString& left, const SimpleString& right) +{ + return !strcmp (left.asCharString (), right.asCharString ()); +} + +bool operator!= (const SimpleString& left, const SimpleString& right) +{ + return !(left == right); +} + +SimpleString StringFrom (bool value) +{ + char buffer [sizeof ("false") + 1]; + sprintf (buffer, "%s", value ? "true" : "false"); + return SimpleString(buffer); +} + +SimpleString StringFrom (const char *value) +{ + return SimpleString(value); +} + +SimpleString StringFrom (long value) +{ + char buffer [DEFAULT_SIZE]; + sprintf (buffer, "%ld", value); + + return SimpleString(buffer); +} + +SimpleString StringFrom (int value) +{ + char buffer [DEFAULT_SIZE]; + sprintf (buffer, "%d", value); + + return SimpleString(buffer); +} + +SimpleString StringFrom (double value) +{ + char buffer [DEFAULT_SIZE]; + sprintf (buffer, "%lf", value); + + return SimpleString(buffer); +} + +SimpleString StringFrom (const SimpleString& value) +{ + return SimpleString(value); +} + + + + diff --git a/src/testframework/easyunit/simplestring.h b/src/testframework/easyunit/simplestring.h new file mode 100644 index 0000000000..9f8df45080 --- /dev/null +++ b/src/testframework/easyunit/simplestring.h @@ -0,0 +1,70 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com + +This class was originally created by Michael Feathers and was modified +by Barthelemy Dagenais. +*/ + + +#ifndef SIMPLE_STRING +#define SIMPLE_STRING + + +/** + * SimpleString is a simple implementation of the std class String and is + * provided to ease the manipulation of strings without using any other + * libraries. + */ +class SimpleString +{ + friend bool operator== (const SimpleString& left, const SimpleString& right); + + friend bool operator!= (const SimpleString& left, const SimpleString& right); + + public: + SimpleString (); + SimpleString (const char *value); + SimpleString (const SimpleString& other); + ~SimpleString (); + + SimpleString operator= (const SimpleString& other); + + SimpleString operator+ (const SimpleString& other); + + char *asCharString () const; + int size() const; + + private: + char *buffer; +}; + +// Those functions are provided to ease the conversion between +// primary datatypes and SimpleString. Feel free to extend this list +// to support your own datatype. +SimpleString StringFrom (bool value); +SimpleString StringFrom (const char *value); +SimpleString StringFrom (long value); +SimpleString StringFrom (int value); +SimpleString StringFrom (double value); +SimpleString StringFrom (const SimpleString& other); + +#endif + diff --git a/src/testframework/easyunit/test.cpp b/src/testframework/easyunit/test.cpp new file mode 100644 index 0000000000..a9453a408c --- /dev/null +++ b/src/testframework/easyunit/test.cpp @@ -0,0 +1,143 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#include "test.h" +#include "testregistry.h" + + +Test::Test(const SimpleString& testCaseName, const SimpleString& testName) +: testCaseName_(testCaseName), testName_(testName), testPartResult_(0), nextTest_(0), failuresCount_(0), + successesCount_(0) +{ + TestRegistry::addTest(this); +} + +Test::~Test() { + TestPartResult *tmp; + int size = failuresCount_ + successesCount_; + + for (int i = 0; igetNext(); + delete tmp; + } +} + +void Test::setUp() +{ +} + +void Test::tearDown() +{ +} + +void Test::run() +{ +} + + +TestCase* Test::getTestCase() const +{ + return testCase_; +} + + +void Test::setTestCase(TestCase *testCase) +{ + testCase_ = testCase; +} + +void Test::addTestPartResult(TestPartResult *testPartResult) +{ + TestPartResult *tmp; + int type = testPartResult->getType(); + + if (testPartResult_ == 0) { + testPartResult_ = testPartResult; + testPartResult_->setNext(testPartResult_); + } + else { + tmp = testPartResult_; + testPartResult_ = testPartResult; + testPartResult_->setNext(tmp->getNext()); + tmp->setNext(testPartResult_); + } + + if (type == failure) { + failuresCount_++; + } + else if (type == error) { + errorsCount_++; + } + else { + successesCount_++; + } +} + +TestPartResult* Test::getTestPartResult() const +{ + TestPartResult *tpr = testPartResult_; + + if (tpr != 0) { + tpr = tpr->getNext(); + } + + return tpr; +} + +int Test::getFailuresCount() const +{ + return failuresCount_; +} + +int Test::getSuccessesCount() const +{ + return successesCount_; +} + +int Test::getErrorsCount() const +{ + return errorsCount_; +} + +void Test::setNext(Test *nextTest) +{ + nextTest_ = nextTest; +} + + +Test* Test::getNext() const +{ + return nextTest_; +} + +const SimpleString& Test::getTestName() const +{ + return testName_; +} + +const SimpleString& Test::getTestCaseName() const +{ + return testCaseName_; +} + + diff --git a/src/testframework/easyunit/test.h b/src/testframework/easyunit/test.h new file mode 100644 index 0000000000..215ce29e14 --- /dev/null +++ b/src/testframework/easyunit/test.h @@ -0,0 +1,433 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#ifndef TEST_H +#define TEST_H + +#include "testcase.h" +#include "testpartresult.h" + + + + + +/** + * EasyUnit namespace. + * This is the namespace containing all easyunit classes. + */ + +/** + * Test class containing all macros to do unit testing. + * A test object represents a test that will be executed. Once it has been + * executed, it reports all results in the testPartResult linked list. + * + * A failure occurs when a test fails (condition is false). + * An error occurs when an exception is thrown during a test. + * A success occurs if a test succeed (condition is true). + */ +class Test +{ + public: + + /** + * Main Test constructor. Used to create a test that will register itself + * with TestRegistry and with its test case. + * @param testCaseName Name of the test case this test belongs to + * @param testName Name of this test + */ + Test(const SimpleString& testCaseName, const SimpleString& testName); + + /** + * Main Test desctructor + * Delete the testPartResult linked list. This is why the user should + * only use the macro provided by easyunit to report a test result. + */ + virtual ~Test(); + + /** + * Fixtures that will be called after run(). + */ + virtual void tearDown(); + + /** + * Fixtures that will be called before run(). + */ + virtual void setUp(); + + /** + * Test code should be in this method. + * run() will be called by the Test's TestCase, hence subclasses of Test + * should override this method. + */ + virtual void run(); + + /** + * Set the TestCase this test belongs to. + * + * @param testCase The TestCase this test belongs to + */ + void setTestCase(TestCase *testCase); + + /** + * Get the TestCase this test belongs to. A test always belongs to + * only one TestCase. This is the TestCase identified by the first + * parameter of the test declaration. For example, if there is a + * test declared as TEST(TESTCASE1, TEST1), this test will be + * associated with the TestCase TESTCASE1. + * + * @return The TestCase this test belongs to + */ + TestCase* getTestCase() const; + + /** + * Add a testpartresult to the testpartresult list of this test. + * This method is used by the assertion macros to report success, + * failure or error. + * + * @param testPartResult The testpartresult to be added to the list + */ + virtual void addTestPartResult(TestPartResult *testPartResult); + + /** + * Get the testpartresult list of this test. If assertion macros + * and TEST and TESTF macros are used, there may be more than + * one successful testpartresult and no more than one error or failure. + * + * @return testPartResult The list of testpartresults of this test + */ + TestPartResult* getTestPartResult() const; + + /** + * Returns number of failures found in this test. + * If macro TEST or TESTF is used, failuresCount <= 1. + * If Test class is extended and ASSERT macros are used in different + * test methods, than failuresCount may be more than 1. + * + * @return Number of failures in this test + */ + int getFailuresCount() const; + + /** + * Returns number of successes found in this test. + * There may be more than one success since each ASSERT macro + * that succeeded generate a success. + * + * @return Number of successes in this test + */ + int getSuccessesCount() const; + + /** + * Returns number of errors found in this test. + * ErrorsCount <= 1, since exception are caught + * for the whole run() method. + * + * @return Number of errors in this test + */ + int getErrorsCount() const; + + + /** + * Set the next test in the linked list. + * + * @param nextTest Next test in the linked list + */ + void setNext(Test *nextTest); + + /** + * Get the next test in the linked list. + * + * @return The next test in the linked list + */ + Test* getNext() const; + + /** + * Get the name of the TestCase this test belongs to. The name of the + * TestCase is the first parameter of the test declaration. For example, + * if a test is declared as TEST(TESTCASE1, TEST1), this method will return + * "TESTCASE1". + * + * @return The TestCase name of this test + */ + const SimpleString& getTestCaseName() const; + + /** + * Get the name of this test. The name of the test is the second + * parameter of the test declaration. For example, + * if a test is declared as TEST(TESTCASE1, TEST1), this method will return + * "TEST1". + * + * @return The name of this test. + */ + const SimpleString& getTestName() const; + + protected: + SimpleString testCaseName_; + SimpleString testName_; + TestCase *testCase_; + TestPartResult *testPartResult_; + Test *nextTest_; + int failuresCount_; + int successesCount_; + int errorsCount_; +}; + + + + +/* + * Helper macros + */ + +#define EQUALS_DELTA(expected,actual,delta)\ + (actual - expected) <= delta && actual >= expected || (expected - actual) <= delta && expected >= actual + +#define TO_STRING_EQUALS_F(expected,actual)\ + StringFrom("Expected : ") + StringFrom(expected) + StringFrom(" but Actual : ") + StringFrom(actual) + +#define TO_STRING_EQUALS_S(expected,actual)\ + StringFrom(expected) + StringFrom(" == ") + StringFrom(actual) + +#define TO_S_E_DELTA_F(expected,actual,delta)\ + StringFrom("Expected : ") + StringFrom(expected) + StringFrom(" but Actual : ") + StringFrom(actual) + StringFrom(" with delta = ") + StringFrom(delta) + +#define TO_S_E_DELTA_S(expected,actual,delta)\ + StringFrom(expected) + StringFrom(" == ") + StringFrom(actual) + StringFrom(" with delta = ") + StringFrom(delta) + +/** + * Asserts that a condition is true. + * If the condition is not true, a failure is generated. + * @param condition Condition to fullfill for the assertion to pass + */ +#define ASSERT_TRUE(condition)\ + { if (condition) {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,#condition,success));\ + } else {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__, #condition,failure)); return;\ + }} + +/** + * Asserts that a condition is true. + * If the condition is not true, a failure is generated. + * @param condition Condition to fullfill for the assertion to pass + * @param message Message that will be displayed if this assertion fails + */ +#define ASSERT_TRUE_M(condition,message)\ + { if (condition) {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,#condition,success));\ + } else {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__, message,failure)); return;\ + }} + +/** + * Asserts that the two parameters are equals. Operator == must be defined. + * If the two parameters are not equals, a failure is generated. + * @param expected Expected value + * @param actual Actual value to be compared + */ +#define ASSERT_EQUALS(expected,actual)\ +{ if (expected == actual) {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_STRING_EQUALS_S(#expected,#actual),success));\ + } else {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_STRING_EQUALS_F(#expected,#actual),failure)); return;\ + }} + +/** + * Asserts that the two parameters are equals. Operator == must be defined. + * If the two parameters are not equals, a failure is generated. + * + * Parameters must be primitive data types or StringFrom (custom type) must + * be overloaded. + * + * @see SimpleString + * @param expected Expected value + * @param actual Actual value to be compared + */ +#define ASSERT_EQUALS_V(expected,actual)\ +{ if (expected == actual) {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_STRING_EQUALS_S(expected,actual),success));\ + } else {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_STRING_EQUALS_F(expected,actual),failure)); return;\ + }} + +/** + * Asserts that the two parameters are equals. Operator == must be defined. + * If the two parameters are not equals, a failure is generated. + * @param expected Expected value + * @param actual Actual value to be compared + * @param message Message that will be displayed if this assertion fails + */ +#define ASSERT_EQUALS_M(expected,actual,message)\ +{ if (expected == actual) {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,#expected,success));\ + } else {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,message,failure)); return;\ + }} + +/** + * Asserts that the two parameters are equals within a delta. Operators == and - must be defined. + * If the two parameters are not equals, a failure is generated. + * @param expected Expected value + * @param actual Actual value to be compared + * @param delta Delta accepted between the two values + */ +#define ASSERT_EQUALS_DELTA(expected,actual,delta)\ +{ if (EQUALS_DELTA(expected,actual,delta) ) {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_S_E_DELTA_S(#expected,#actual,#delta),success));\ + } else {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_S_E_DELTA_F(#expected,#actual,#delta),failure)); return;\ + }} + +/** + * Asserts that the two parameters are equals within a delta. Operators == and - must be defined. + * If the two parameters are not equals, a failure is generated. + * @param expected Expected value + * @param actual Actual value to be compared + * @param delta Delta accepted between the two values + * @param message Message that will be displayed if this assertion fails + */ +#define ASSERT_EQUALS_DELTA_M(expected,actual,delta,message)\ +{ if (EQUALS_DELTA(expected,actual,delta)) {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,#expected,success));\ + } else {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,message,failure)); return;\ + }} + +/** + * Asserts that the two parameters are equals within a delta. Operators == and - must be defined. + * If the two parameters are not equals, a failure is generated. + * + * Parameters must be primitive data types or StringFrom (custom type) must + * be overloaded. + * + * @see SimpleString + * @param expected Expected value + * @param actual Actual value to be compared + * @param delta Delta accepted between the two values + */ +#define ASSERT_EQUALS_DELTA_V(expected,actual,delta)\ +{ if (EQUALS_DELTA(expected,actual,delta)) {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_S_E_DELTA_S(expected,actual,delta),success));\ + } else {\ + addTestPartResult(new TestPartResult(this, __FILE__,__LINE__,TO_S_E_DELTA_F(expected,actual,delta),failure)); return;\ + }} + + +/** + * Make a test fails. + */ +#define FAIL()\ + { addTestPartResult(new TestPartResult(this, __FILE__, __LINE__,("Test failed."),failure)); return; } + +/** + * Make a test fails with the given message. + * @param text Failure message + */ +#define FAIL_M(text)\ + { addTestPartResult(new TestPartResult(this, __FILE__, __LINE__,text,failure)); return; } + + +/** + * Define a test in a TestCase. + * User should put his test code between brackets after using this macro. + * @param testCaseName TestCase name where the test belongs to + * @param testName Unique test name + */ +#define TEST(testCaseName, testName)\ + class testCaseName##testName##Test : public Test \ + { public: testCaseName##testName##Test() : Test (#testCaseName , #testName) {} \ + void run(); } \ + testCaseName##testName##Instance; \ + void testCaseName##testName##Test::run () + + +/** + * Define a test in a TestCase using test fixtures. + * User should put his test code between brackets after using this macro. + * + * This macro should only be used if test fixtures were declared earlier in + * this order: DECLARE, SETUP, TEARDOWN. + * @param testCaseName TestCase name where the test belongs to. Should be + * the same name of DECLARE, SETUP and TEARDOWN. + * @param testName Unique test name. + */ +#define TESTF(testCaseName, testName)\ + class testCaseName##testName##Test : public testCaseName##Declare##Test \ + { public: testCaseName##testName##Test() : testCaseName##Declare##Test (#testCaseName , #testName) {} \ + void run(); } \ + testCaseName##testName##Instance; \ + void testCaseName##testName##Test::run () + + +/** + * Setup code for test fixtures. + * This code is executed before each TESTF. + * + * User should put his setup code between brackets after using this macro. + * + * @param testCaseName TestCase name of the fixtures. + */ +#define SETUP(testCaseName)\ + void testCaseName##Declare##Test::setUp () + + +/** + * Teardown code for test fixtures. + * This code is executed after each TESTF. + * + * User should put his setup code between brackets after using this macro. + * + * @param testCaseName TestCase name of the fixtures. + */ +#define TEARDOWN(testCaseName)\ + void testCaseName##Declare##Test::tearDown () + + +/** + * Location to declare variables and objets. + * This is where user should declare members accessible by TESTF, + * SETUP and TEARDOWN. + * + * User should not use brackets after using this macro. User should + * not initialize any members here. + * + * @param testCaseName TestCase name of the fixtures + * @see END_DECLARE for more information. + */ +#define DECLARE(testCaseName)\ + class testCaseName##Declare##Test : public Test \ + { public: testCaseName##Declare##Test(const SimpleString& testCaseName, const SimpleString& testName) : Test (testCaseName , testName) {} \ + virtual void run() = 0; void setUp(); void tearDown(); \ + protected: + + +/** + * Ending macro used after DECLARE. + * + * User should use this macro after declaring members with + * DECLARE macro. + */ +#define END_DECLARE \ + }; + +#endif // TEST_H + + diff --git a/src/testframework/easyunit/testcase.cpp b/src/testframework/easyunit/testcase.cpp new file mode 100644 index 0000000000..608f25d79e --- /dev/null +++ b/src/testframework/easyunit/testcase.cpp @@ -0,0 +1,170 @@ +#define ECPP +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#include "testcase.h" +#include "test.h" +#include "testresult.h" + +#ifndef ECPP +#include +#endif + + +TestCase::TestCase(const SimpleString& name, TestResult *testResult) +: name_(name), testResult_(testResult) +{ +} + +TestCase::~TestCase() +{ +} + +void TestCase::addTest(Test *test) +{ + Test *tmp; + + if (tests_ == 0) { + tests_ = test; + tests_->setNext(tests_); + } + else { + tmp = tests_; + tests_ = test; + tests_->setNext(tmp->getNext()); + tmp->setNext(tests_); + } + + testsCount_++; +} + +Test* TestCase::getTests() const +{ + Test *test = tests_; + + if (test != 0) { + test = test->getNext(); + } + + return test; +} + +void TestCase::run() +{ + Test *test = tests_->getNext(); + + runTests(test); + + ran_ = true; + + testResult_->addResult(this); +} + +int TestCase::getTestsCount() const +{ + return testsCount_; +} + +int TestCase::getFailuresCount() const +{ + return failuresCount_; +} + +int TestCase::getSuccessesCount() const +{ + return successesCount_; +} + +int TestCase::getErrorsCount() const +{ + return errorsCount_; +} + +bool TestCase::ran() const +{ + return ran_; +} + +const SimpleString& TestCase::getName() const +{ + return name_; +} + +void TestCase::updateCount(Test *test) +{ + if (test->getErrorsCount() > 0) { + errorsCount_++; + } + else if (test->getFailuresCount() > 0) { + failuresCount_++; + } + else { + successesCount_++; + } +} + +TestCase* TestCase::getNext() const +{ + return nextTestCase_; +} + +void TestCase::setNext(TestCase *testCase) +{ + nextTestCase_ = testCase; +} + +void TestCase::runTests(Test *test) +{ + + for (int i = 0; isetUp(); + runTest(test); + test->tearDown(); + updateCount(test); + test = test->getNext(); + } + +} + +#ifdef ECPP + +void TestCase::runTest(Test *test) +{ + test->run(); +} + +#else + +void TestCase::runTest(Test *test) +{ + try { + test->run(); + } + catch (std::exception &e) { + test->addTestPartResult(new TestPartResult(test,"",-1,e.what(),error)); + } + catch (...) { + test->addTestPartResult(new TestPartResult(test,"",-1,"Unexpected error occured",error)); + } +} +#endif + diff --git a/src/testframework/easyunit/testcase.h b/src/testframework/easyunit/testcase.h new file mode 100644 index 0000000000..79438506ef --- /dev/null +++ b/src/testframework/easyunit/testcase.h @@ -0,0 +1,154 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#include "simplestring.h" + +#ifndef TESTCASE_H +#define TESTCASE_H + + +class Test; +class TestResult; + +/** + * A TestCase is a collection of unit tests (instance of Test) and is + * always specified by the first parameter of a Test declaration. + */ +class TestCase +{ + public: + + /** + * Main TestCase constructor. + * + * @param name TestCase name + * @param testResult Pointer to the TestResult used to report results + * of executed Test + */ + TestCase(const SimpleString& name, TestResult *testResult); + + virtual ~TestCase(); + + /** + * Add a Test to the Test list. This method is used by TestRegistry. + * + * @param test Test instance to add to the Test list. + */ + void addTest(Test *test); + + /** + * Get the Test list. + * + * @return Test list + */ + Test* getTests() const; + + /** + * Execute all Tests in the Test list of this TestCase. In fact, it calls + * the run() method of all Tests. + */ + void run(); + + /** + * Get the Test list size (number of Tests in this TestCase). + * + * @return The Test list size + */ + int getTestsCount() const; + + /** + * Get the total number of failures reported by all Tests. + * + * @return The total number of failures reported by all Tests. 0 + * if no test were run or if no failures were reported. + */ + int getFailuresCount() const; + + /** + * Get the total number of successes reported by all Tests. + * + * @return The total number of successes reported by all Tests. 0 + * if no test were run or if no successes were reported. + */ + int getSuccessesCount() const; + + /** + * Get the total number of errors reported by all Tests. + * + * @return The total number of errors reported by all Tests. 0 + * if no test were run, if this is the embedded version or if + * no errors were reported. + */ + int getErrorsCount() const; + + /** + * Indicates whether or not this TestCase was executed. + * + * @return true if the method run() of this TestCase was called. false + * otherwise + */ + bool ran() const; + + /** + * Get the TestCase name. This name is specified by the first parameter + * of the Test declaration. For example, if a test was declared as + * TEST(TESTCASE1, TEST1), the TestCase name would be "TESTCASE1". + * + * @return The name of the TestCase + */ + const SimpleString& getName() const; + + /** + * Get the next TestCase in the list. + * + * @return The next TestCase in the TestCase linked list + */ + TestCase* getNext() const; + + /** + * Set the next TestCase in the list. + * + * @return The next TestCase in the TestCase linked list + */ + void setNext(TestCase *testCase); + + protected: + int failuresCount_{0}; + int successesCount_{0}; + int errorsCount_{0}; + int testsCount_{0}; + Test *tests_{0}; + SimpleString name_; + TestCase *nextTestCase_{0}; + TestResult *testResult_; + + private: + void updateCount(Test *test); + void runTests(Test *test); + void runTest(Test *test); + bool ran_{false}; + +}; + +#endif // TESTCASE_H + + diff --git a/src/testframework/easyunit/testharness.h b/src/testframework/easyunit/testharness.h new file mode 100644 index 0000000000..e0a78ecd99 --- /dev/null +++ b/src/testframework/easyunit/testharness.h @@ -0,0 +1,38 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#ifndef TESTHARNESS_H +#define TESTHARNESS_H + +#include "test.h" +#include "testcase.h" +#include "testpartresult.h" +#include "testregistry.h" +#include "simplestring.h" +#include "testprinter.h" +#include "testresult.h" +#include "testrunner.h" +#include "defaulttestprinter.h" + +#endif + + diff --git a/src/testframework/easyunit/testpartresult.cpp b/src/testframework/easyunit/testpartresult.cpp new file mode 100644 index 0000000000..f382c4a2d9 --- /dev/null +++ b/src/testframework/easyunit/testpartresult.cpp @@ -0,0 +1,67 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#include "testpartresult.h" +#include "test.h" + + +TestPartResult::TestPartResult (Test *test, + const SimpleString& fileName, + long lineNumber, + const SimpleString& message, + testType type) + : message_ (message), + test_ (test), + fileName_ (fileName), + lineNumber_ (lineNumber), + type_ (type) +{ +} + +void TestPartResult::setNext(TestPartResult *next) { + next_ = next; +} + +TestPartResult* TestPartResult::getNext() const { + return next_; +} + +testType TestPartResult::getType() const { + return type_; +} + +const SimpleString& TestPartResult::getMessage() const { + return message_; +} + +Test* TestPartResult::getTest() const { + return test_; +} + +const SimpleString& TestPartResult::getFileName() const { + return fileName_; +} + +long TestPartResult::getLineNumber() const { + return lineNumber_; +} + diff --git a/src/testframework/easyunit/testpartresult.h b/src/testframework/easyunit/testpartresult.h new file mode 100644 index 0000000000..5f04a0b49b --- /dev/null +++ b/src/testframework/easyunit/testpartresult.h @@ -0,0 +1,131 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#ifndef TESTPARTRESULT_H +#define TESTPARTRESULT_H + +#include "simplestring.h" + + +/** + * This enumeration contains the three states a TestPartResult can take. + * + * A failure means that an assertion failed during the test. + * + * A success means that all assertion succeeded during the test. + * + * An error means that an exception was thrown during the test. + */ +enum testType {failure,success,error}; + +class Test; + +/** + * This class contains details about assertion processed during the + * execution of a Test. It contains the line and the file of the assertion, + * the result (success, failure or error), the condition (or message), and + * the Test class where the assertion was processed. + */ +class TestPartResult +{ + public: + /** + * Main constructor used to initialize all details about the result + * of an assertion. + * + * @param test The test where the assertion was processed + * @param fileName The file name where the assertion is located + * @param lineNumber The line number where the assertion is located + * @param message The assertion condition or message + * @param type The result of the assertion (failure, success or error) + */ + TestPartResult (Test *test, + const SimpleString& fileName, + long lineNumber, + const SimpleString& message, + testType type); + + /** + * Set the next TestPartResult in the list. + * + * @param next The next TestPartResult in the linked list + */ + void setNext(TestPartResult* next); + + /** + * Get the next TestPartResult in the list. + * + * @return The next TestPartResult in the linked list + */ + TestPartResult* getNext() const; + + /** + * Get the type of the TestPartResult. This represents the result + * of the assertion. + * + * @return The type of the TestPartResult (failure, success or error) + */ + testType getType() const; + + /** + * Get the message (or condition) of the assertion. + * + * @return The message (or condition) of the assertion + */ + const SimpleString& getMessage() const; + + /** + * Get the Test where the assertion is located. + * + * @return The Test where the assertion is located + */ + Test* getTest() const; + + /** + * Get the file name where the assertion is located. + * + * @return The file name where the assertion is located + */ + const SimpleString& getFileName() const; + + /** + * Get the line number where the assertion is located. + * + * @return The line number where the assertion is located + */ + long getLineNumber() const; + + + protected: + SimpleString message_; + Test *test_; + SimpleString fileName_; + long lineNumber_; + + private: + TestPartResult *next_; + testType type_; +}; + +#endif // TESTPARTRESULT_H + + diff --git a/src/testframework/easyunit/testprinter.h b/src/testframework/easyunit/testprinter.h new file mode 100644 index 0000000000..4d2f8712dd --- /dev/null +++ b/src/testframework/easyunit/testprinter.h @@ -0,0 +1,52 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#ifndef TESTPRINTER_H +#define TESTPRINTER_H + +#include "testresult.h" + + +/** + * A TestPrinter is a class used by the TestRegistry to print results + * of executed TestCases. This is an abstract class, so no default behavior + * for the print method is provided. + * + * @see DefaultTestPrinter + */ +class TestPrinter +{ + public: + virtual ~TestPrinter(){}; + /** + * Print the details of a given TestResult instance. This + * method must be overridden by subclasses since it is + * abstract. + * + * @param testResult TestResult instance that the user wish to print + */ + virtual void print(const TestResult *testResult) = 0; +}; + +#endif // TESTPRINTER_H + + diff --git a/src/testframework/easyunit/testregistry.cpp b/src/testframework/easyunit/testregistry.cpp new file mode 100644 index 0000000000..aad38ba486 --- /dev/null +++ b/src/testframework/easyunit/testregistry.cpp @@ -0,0 +1,140 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#include "testregistry.h" +#include "defaulttestprinter.h" + + +int TestRegistry::nextName = 0; + +TestRegistry::TestRegistry() +: currentTC_(0), defaultPrinter_(new DefaultTestPrinter()),testCaseCount_(0), + defaultRunner_(new TestRunner()) +{ +} + +TestRegistry::~TestRegistry() +{ + TestCase *tmp; + for (int i = 0; igetNext(); + delete tmp; + } + + delete defaultPrinter_; + delete defaultRunner_; +} + +void TestRegistry::addTest(Test *test) +{ + instance().add(test); +} + +const TestResult* TestRegistry::run() +{ + return instance().runTests(instance().defaultRunner_); +} + +const TestResult* TestRegistry::run(TestRunner *runner) +{ + return instance().runTests(runner); +} + +const TestResult* TestRegistry::runAndPrint() +{ + return runAndPrint(instance().defaultPrinter_,instance().defaultRunner_); +} + +const TestResult* TestRegistry::runAndPrint(TestRunner *runner) +{ + return runAndPrint(instance().defaultPrinter_,runner); +} + +const TestResult* TestRegistry::runAndPrint(TestPrinter *printer) +{ + return runAndPrint(printer,instance().defaultRunner_); +} + + +const TestResult* TestRegistry::runAndPrint(TestPrinter *printer, TestRunner *runner) +{ + const TestResult *testResult = instance().runTests(runner); + printer->print(testResult); + return testResult; +} + + +TestRegistry& TestRegistry::instance() +{ + static TestRegistry registry; + return registry; +} + +void TestRegistry::add(Test *test) +{ + const SimpleString tcName = test->getTestCaseName(); + const SimpleString tName = test->getTestName(); + + if ((currentTC_ == 0) || (currentTC_->getName() != tcName)) { + addTestCase(new TestCase(tcName,&testResult_)); + } + + currentTC_->addTest(test); + +} + +const TestResult* TestRegistry::runTests(TestRunner *runner) +{ + TestCase *tc = currentTC_; + + if (tc != 0) { + tc = tc->getNext(); + runner->run(tc,testCaseCount_); + } + + testResult_.setTestCases(tc,testCaseCount_); + + return &testResult_; +} + + + +void TestRegistry::addTestCase(TestCase *testCase) +{ + TestCase *tmp; + + if (currentTC_ == 0) { + currentTC_ = testCase; + currentTC_->setNext(currentTC_); + } + else { + tmp = currentTC_; + currentTC_ = testCase; + currentTC_->setNext(tmp->getNext()); + tmp->setNext(currentTC_); + } + + testCaseCount_++; +} + + diff --git a/src/testframework/easyunit/testregistry.h b/src/testframework/easyunit/testregistry.h new file mode 100644 index 0000000000..5d0d1df8d1 --- /dev/null +++ b/src/testframework/easyunit/testregistry.h @@ -0,0 +1,128 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#ifndef TESTREGISTRY_H +#define TESTREGISTRY_H + +#include "test.h" +#include "testcase.h" +#include "testprinter.h" +#include "simplestring.h" +#include "testrunner.h" +#include "testresult.h" + + +/** + * The TestRegistry is the main class used to register all tests, + * and create appropriate TestCase. It can then be used to run + * tests and print results. All methods that should be used by + * the user are static. + */ +class TestRegistry +{ + public: + TestRegistry(); + ~TestRegistry(); + + /** + * Add a test in the registry. If the previous TestCase was not the same + * as the one of the current test, a new TestCase is created. + * + * @param test Test to be added + */ + static void addTest (Test *test); + + /** + * Run all tests in the registry (default test runner) and return + * the test results. + * + * @return The test results + */ + static const TestResult* run(); + + /** + * Pass all tests in the registry to the TestRunner runner and + * return the results of all tests ran. + * + * @param runner The custom runner used to decided which test to run + * @return The test results of all tests ran + */ + static const TestResult* run(TestRunner *runner); + + /** + * Run all tests in the registry (default test runner) and return + * the test results. This will also print the results using the + * default test printer (normal level of details and to the standard + * output). + * + * @return The test results + */ + static const TestResult* runAndPrint(); + + /** + * Pass all tests in the registry to the TestRunner runner and + * return the results of all tests ran. This will also print the results + * using the default test printer (normal level of details and to the + * standard output). + * + * @param runner The custom runner used to decided which test to run + * @return The test results + */ + static const TestResult* runAndPrint(TestRunner *runner); + + /** + * Run all tests in the registry (default test runner) and return + * the test results. Results will also be given to + * to the TestPrinter printer. + * + * @param printer The custom printer used to print the test results + * @return The test results + */ + static const TestResult* runAndPrint(TestPrinter *printer); + + /** + * Pass all tests in the registry to the TestRunner runner and + * return the results of all tests ran. Results will also be given to + * to the TestPrinter printer. + * + * @param printer The custom printer used to print the test results + * @param runner The custom runner used to decided which test to run + * @return The test results + */ + static const TestResult* runAndPrint(TestPrinter *printer, TestRunner *runner); + + private: + static TestRegistry& instance(); + static int nextName; + void add(Test *test); + void addTestCase(TestCase *testCase); + const TestResult* runTests(TestRunner *runner); + TestCase *currentTC_; + TestPrinter *defaultPrinter_; + int testCaseCount_; + TestRunner *defaultRunner_; + TestResult testResult_; +}; + +#endif // TESTREGISTRY_H + + diff --git a/src/testframework/easyunit/testresult.cpp b/src/testframework/easyunit/testresult.cpp new file mode 100644 index 0000000000..e23e58fc9a --- /dev/null +++ b/src/testframework/easyunit/testresult.cpp @@ -0,0 +1,117 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#include "testresult.h" + + +TestResult::TestResult() +{ +} + + +TestResult::~TestResult() +{ +} + +int TestResult::getTotalSuccesses() const +{ + return totalSuccesses_; +} + +int TestResult::getTotalErrors() const +{ + return totalErrors_; +} + +int TestResult::getTotalFailures() const +{ + return totalFailures_; +} + + +int TestResult::getSuccesses() const +{ + return successes_; +} + +int TestResult::getFailures() const +{ + return failures_; +} + +int TestResult::getErrors() const +{ + return errors_; +} + +int TestResult::getTestCaseCount() const +{ + return testCaseCount_; +} + +int TestResult::getTestRanCount() const +{ + return testRanCount_; +} + +int TestResult::getTestCaseRanCount() const +{ + return testCaseRanCount_; +} + +TestCase* TestResult::getTestCases() const +{ + return testCases_; +} + +void TestResult::setTestCases(TestCase *testCases, int testCaseCount) +{ + testCases_ = testCases; + testCaseCount_ = testCaseCount; +} + +void TestResult::addResult(TestCase *testCase) +{ + int tcSuccesses = testCase->getSuccessesCount(); + int tcErrors = testCase->getErrorsCount(); + int tcFailures = testCase->getFailuresCount(); + + testCaseRanCount_++; + + totalSuccesses_ += tcSuccesses; + totalErrors_ += tcErrors; + totalFailures_ += tcFailures; + testRanCount_ += testCase->getTestsCount(); + + if (tcErrors == 0 && tcFailures == 0) { + successes_++; + } + else if (tcErrors > 0) { + errors_++; + } + else { + failures_++; + } +} + + + diff --git a/src/testframework/easyunit/testresult.h b/src/testframework/easyunit/testresult.h new file mode 100644 index 0000000000..ce44b11383 --- /dev/null +++ b/src/testframework/easyunit/testresult.h @@ -0,0 +1,146 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#ifndef testresult_H +#define testresult_H + +#include "testcase.h" + + +class TestResult +{ +public: + TestResult(); + virtual ~TestResult(); + + + /** + * Get the total number of successes registered by all + * test cases ran. This is the sum of all TestCase->getSuccessesCount(). + * + *@return The number of successes registered by all testcases. + */ + int getTotalSuccesses() const; + + /** + * Get the total number of errors registered by all + * test cases ran. This is the sum of all TestCase->getErrorsCount(). + * + *@return The number of errors registered by all testcases. + */ + int getTotalErrors() const; + + /** + * Get the total number of failures registered by all + * test cases ran. This is the sum of all TestCase->getFailuresCount(). + * + * @return The number of failures registered by all testcases. + */ + int getTotalFailures() const; + + /** + * Get the number of testcases ran that succeeded. + * + * @return The number of testcases ran that succeeded. + */ + int getSuccesses() const; + + /** + * Get the number of testcases ran that failed. + * + * @return The number of testcases ran that failed. + */ + int getFailures() const; + + /** + * Get the number of testcases ran that reported an error. + * + * @return The number of testcases ran that reported an error. + */ + int getErrors() const; + + /** + * Get the number of testcases in the TestCase list. + * + * @return The size of the TestCase list + */ + int getTestCaseCount() const; + + /** + * Get the number of tests + * + * @return The number of tests ran that succeeded + */ + int getTestRanCount() const; + + /** + * Get the number of testcases ran. + * + * @return The number of testcases ran + */ + int getTestCaseRanCount() const; + + /** + * Get the TestCase list. This list contains all TestCase registered and + * not only those that were ran. + * + * @return The TestCase list + */ + TestCase* getTestCases() const; + + /** + * Set the TestCase list and the size of the list. + * + * @param testCases TestCase list + * @param testCaseCount size of the TestCase list + */ + void setTestCases(TestCase *testCases, int testCaseCount); + + /** + * Add a TestCase result. This is used by a TestCase after it has + * completed. + * + * @param testCase TestCase that ran and contains results to add to + * global results + */ + virtual void addResult(TestCase *testCase); + +protected: + int testCaseCount_{0}; + int testRanCount_{0}; + int testCaseRanCount_{0}; + + int totalSuccesses_{0}; + int totalErrors_{0}; + int totalFailures_{0}; + + int successes_{0}; + int errors_{0}; + int failures_{0}; + + TestCase* testCases_{0}; + +}; + + +#endif // testresult_H + diff --git a/src/testframework/easyunit/testrunner.cpp b/src/testframework/easyunit/testrunner.cpp new file mode 100644 index 0000000000..09119a293d --- /dev/null +++ b/src/testframework/easyunit/testrunner.cpp @@ -0,0 +1,45 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#include "testrunner.h" + + +TestRunner::TestRunner() +{ +} + + +TestRunner::~TestRunner() +{ +} + +void TestRunner::run(TestCase *testCase, int size) +{ + for (int i=0; irun(); + testCase = testCase->getNext(); + } +} + + + + diff --git a/src/testframework/easyunit/testrunner.h b/src/testframework/easyunit/testrunner.h new file mode 100644 index 0000000000..38498fb98c --- /dev/null +++ b/src/testframework/easyunit/testrunner.h @@ -0,0 +1,57 @@ +/* +EasyUnit : Simple C++ Unit testing framework +Copyright (C) 2004 Barthelemy Dagenais + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Barthelemy Dagenais +barthelemy@prologique.com +*/ + +#ifndef TestRunner_H +#define TestRunner_H + +#include "testcase.h" + + + +/** + * Test runner used to determine which test to run. + * + * User may extends this class to provide a custom test runner + * to TestRegistry. + */ +class TestRunner +{ +public: + TestRunner(); + virtual ~TestRunner(); + + /** + * Method used to run testcases by TestRegistry. + * + * User should override this method in order to provide custom + * behavior. + * + * @param testCase Linked list of testcases + * @param size Size of the linked list + */ + virtual void run(TestCase *testCase, int size); + +}; + + +#endif // TestRunner_H + diff --git a/src/testframework/prettyprint.hpp b/src/testframework/prettyprint.hpp new file mode 100644 index 0000000000..6bf254313c --- /dev/null +++ b/src/testframework/prettyprint.hpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + struct has_const_iterator : private sfinae_base + { + private: + template static yes & test(typename C::const_iterator*); + template static no & test(...); + public: + static const bool value = sizeof(test(nullptr)) == sizeof(yes); + using type = T; + }; + + template + struct has_begin_end : private sfinae_base + { + private: + template + static yes & f(typename std::enable_if< + std::is_same(&C::begin)), + typename C::const_iterator(C::*)() const>::value>::type *); + + template static no & f(...); + + template + static yes & g(typename std::enable_if< + std::is_same(&C::end)), + typename C::const_iterator(C::*)() const>::value, void>::type*); + + template static no & g(...); + + public: + static bool const beg_value = sizeof(f(nullptr)) == sizeof(yes); + static bool const end_value = sizeof(g(nullptr)) == sizeof(yes); + }; + + } // namespace detail + + + // Holds the delimiter values for a specific character type + + template + 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 + struct delimiters + { + using type = delimiters_values; + 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 TDelimiters = delimiters> + struct print_container_helper + { + using delimiters_type = TDelimiters; + using ostream_type = std::basic_ostream; + + template + 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::print_body(container_, stream); + + if (delimiters_type::values.postfix != NULL) + stream << delimiters_type::values.postfix; + } + + private: + const T & container_; + }; + + // Specialization for pairs + + template + template + struct print_container_helper::printer> + { + using ostream_type = print_container_helper::ostream_type; + + static void print_body(const std::pair & c, ostream_type & stream) + { + stream << c.first; + if (print_container_helper::delimiters_type::values.delimiter != NULL) + stream << print_container_helper::delimiters_type::values.delimiter; + stream << c.second; + } + }; + + // Specialization for tuples + + template + template + struct print_container_helper::printer> + { + using ostream_type = print_container_helper::ostream_type; + using element_type = std::tuple; + + template 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) + { + } + + static void tuple_print(const element_type & c, ostream_type & stream, + typename std::conditional, std::nullptr_t>::type) + { + stream << std::get<0>(c); + tuple_print(c, stream, Int<1>()); + } + + template + static void tuple_print(const element_type & c, ostream_type & stream, Int) + { + if (print_container_helper::delimiters_type::values.delimiter != NULL) + stream << print_container_helper::delimiters_type::values.delimiter; + + stream << std::get(c); + + tuple_print(c, stream, Int()); + } + }; + + // Prints a print_container_helper to the specified stream. + + template + inline std::basic_ostream & operator<<( + std::basic_ostream & stream, + const print_container_helper & helper) + { + helper(stream); + return stream; + } + + + // Basic is_container template; specialize to derive from std::true_type for all desired container types + + template + struct is_container : public std::integral_constant::value && + detail::has_begin_end::beg_value && + detail::has_begin_end::end_value> { }; + + template + struct is_container : std::true_type { }; + + template + struct is_container : std::false_type { }; + + template + struct is_container> : std::true_type { }; + + template + struct is_container> : std::true_type { }; + + template + struct is_container> : std::true_type { }; + + + // Default delimiters + + template struct delimiters { static const delimiters_values values; }; + template const delimiters_values delimiters::values = { "[", ", ", "]" }; + template struct delimiters { static const delimiters_values values; }; + template const delimiters_values delimiters::values = { L"[", L", ", L"]" }; + + + // Delimiters for (multi)set and unordered_(multi)set + + template + struct delimiters< ::std::set, char> { static const delimiters_values values; }; + + template + const delimiters_values delimiters< ::std::set, char>::values = { "{", ", ", "}" }; + + template + struct delimiters< ::std::set, wchar_t> { static const delimiters_values values; }; + + template + const delimiters_values delimiters< ::std::set, wchar_t>::values = { L"{", L", ", L"}" }; + + template + struct delimiters< ::std::multiset, char> { static const delimiters_values values; }; + + template + const delimiters_values delimiters< ::std::multiset, char>::values = { "{", ", ", "}" }; + + template + struct delimiters< ::std::multiset, wchar_t> { static const delimiters_values values; }; + + template + const delimiters_values delimiters< ::std::multiset, wchar_t>::values = { L"{", L", ", L"}" }; + + template + struct delimiters< ::std::unordered_set, char> { static const delimiters_values values; }; + + template + const delimiters_values delimiters< ::std::unordered_set, char>::values = { "{", ", ", "}" }; + + template + struct delimiters< ::std::unordered_set, wchar_t> { static const delimiters_values values; }; + + template + const delimiters_values delimiters< ::std::unordered_set, wchar_t>::values = { L"{", L", ", L"}" }; + + template + struct delimiters< ::std::unordered_multiset, char> { static const delimiters_values values; }; + + template + const delimiters_values delimiters< ::std::unordered_multiset, char>::values = { "{", ", ", "}" }; + + template + struct delimiters< ::std::unordered_multiset, wchar_t> { static const delimiters_values values; }; + + template + const delimiters_values delimiters< ::std::unordered_multiset, wchar_t>::values = { L"{", L", ", L"}" }; + + + // Delimiters for pair and tuple + + template struct delimiters, char> { static const delimiters_values values; }; + template const delimiters_values delimiters, char>::values = { "(", ", ", ")" }; + template struct delimiters< ::std::pair, wchar_t> { static const delimiters_values values; }; + template const delimiters_values delimiters< ::std::pair, wchar_t>::values = { L"(", L", ", L")" }; + + template struct delimiters, char> { static const delimiters_values values; }; + template const delimiters_values delimiters, char>::values = { "(", ", ", ")" }; + template struct delimiters< ::std::tuple, wchar_t> { static const delimiters_values values; }; + template const delimiters_values delimiters< ::std::tuple, wchar_t>::values = { L"(", L", ", L")" }; + + + // Type-erasing helper class for easy use of custom delimiters. + // Requires TCharTraits = std::char_traits and TChar = char or wchar_t, and MyDelims needs to be defined for TChar. + // Usage: "cout << pretty_print::custom_delims(x)". + + struct custom_delims_base + { + virtual ~custom_delims_base() { } + virtual std::ostream & stream(::std::ostream &) = 0; + virtual std::wostream & stream(::std::wostream &) = 0; + }; + + template + 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, Delims>(t); + } + + std::wostream & stream(std::wostream & s) + { + return s << print_container_helper, Delims>(t); + } + + private: + const T & t; + }; + + template + struct custom_delims + { + template + custom_delims(const Container & c) : base(new custom_delims_wrapper(c)) { } + + std::unique_ptr base; + }; + + template + inline std::basic_ostream & operator<<(std::basic_ostream & s, const custom_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 + 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 + 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 +inline pretty_print::array_wrapper_n pretty_print_array(const T * const a, size_t n) +{ + return pretty_print::array_wrapper_n(a, n); +} + +template pretty_print::bucket_print_wrapper +bucket_print(const T & m, typename T::size_type n) +{ + return pretty_print::bucket_print_wrapper(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 + inline typename enable_if< ::pretty_print::is_container::value, + basic_ostream &>::type + operator<<(basic_ostream & stream, const T & container) + { + return stream << ::pretty_print::print_container_helper(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 index 0000000000..df80cb7372 --- /dev/null +++ b/src/testframework/unittests/libs/TEST_utils.cpp @@ -0,0 +1,36 @@ +#include "utils.h" + +#include +#include + +#include "easyunit/test.h" + +TEST(UtilsTest,split) +{ + const char *s= "one two three"; + std::vector 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 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 v= parse_number_list(s); + ASSERT_TRUE(v.size() == 3); + ASSERT_TRUE(v[0] == 1.1F); + ASSERT_TRUE(v[1] == 2.2F); + ASSERT_TRUE(v[2] == 3.3F); +} diff --git a/src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp b/src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp new file mode 100644 index 0000000000..cfcd3c88c4 --- /dev/null +++ b/src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp @@ -0,0 +1,257 @@ +#include "TemperatureSwitch.h" +#include "Kernel.h" +#include "checksumm.h" +#include "utils.h" +#include "Test_kernel.h" +#include "PublicDataRequest.h" +#include "PublicData.h" +#include "TemperatureControlPublicAccess.h" +#include "SwitchPublicAccess.h" +#include "Gcode.h" + +#include +#include + +#include "easyunit/test.h" + +DECLARE(TemperatureSwitch) + TemperatureSwitch *ts; +END_DECLARE + +SETUP(TemperatureSwitch) +{ + ts = new TemperatureSwitch(); + //printf("...Setup TemperatureSwitch\n"); +} + +TEARDOWN(TemperatureSwitch) +{ + delete ts; + test_kernel_teardown(); + + //printf("...Teardown TemperatureSwitch\n"); +} + +const static char edge_low_config[]= "\ +temperatureswitch.psu_off.enable true \n\ +temperatureswitch.psu_off.designator T \n\ +temperatureswitch.psu_off.switch fan \n\ +temperatureswitch.psu_off.threshold_temp 50.0 \n\ +temperatureswitch.psu_off.heatup_poll 5 \n\ +temperatureswitch.psu_off.cooldown_poll 5 \n\ +temperatureswitch.psu_off.arm_mcode 1100 \n\ +temperatureswitch.psu_off.trigger falling \n\ +temperatureswitch.psu_off.inverted false \n\ +"; + +const static char level_config[]= "\ +temperatureswitch.psu_off.enable true \n\ +temperatureswitch.psu_off.designator T \n\ +temperatureswitch.psu_off.switch fan \n\ +temperatureswitch.psu_off.threshold_temp 50.0 \n\ +temperatureswitch.psu_off.heatup_poll 5 \n\ +temperatureswitch.psu_off.cooldown_poll 5 \n\ +"; + +// handle mock call to temperature control +static void on_get_public_data_tc1(void *argument) +{ + PublicDataRequest *pdr = static_cast(argument); + + if(!pdr->starts_with(temperature_control_checksum)) return; + if(!pdr->second_element_is(poll_controls_checksum)) return; + + std::vector *v= static_cast*>(pdr->get_data_ptr()); + + struct pad_temperature t; + // setup data + t.designator= "T"; + t.id= 123; + v->push_back(t); + pdr->set_taken(); + pdr->clear_returned_data(); +} + +// Handle temperature request and switch status request +static bool switch_state; +static bool switch_get_hit= false; +static int hitcnt= 0; +static float return_current_temp= 0; +static void on_get_public_data_tc2(void *argument) +{ + PublicDataRequest *pdr = static_cast(argument); + + if(pdr->starts_with(temperature_control_checksum)) { + if(!pdr->second_element_is(current_temperature_checksum)) return; + if(!pdr->third_element_is(123)) return; + + hitcnt++; + + struct pad_temperature *t= static_cast(pdr->get_data_ptr()); + t->current_temperature = return_current_temp; + t->target_temperature = 0; + t->pwm = 0; + t->designator= "T"; + t->id= 123; + pdr->set_taken(); + pdr->clear_returned_data(); + + } else if(pdr->starts_with(switch_checksum)){ + + if(!pdr->second_element_is(get_checksum("fan"))) return; + + switch_get_hit= true; + + static struct pad_switch pad; + pad.name = get_checksum("fan"); + pad.state = switch_state; + pad.value = 0; + + pdr->set_data_ptr(&pad); + pdr->set_taken(); + } +} + +static bool switch_set_hit= false; +static void on_set_public_data_switch(void *argument) +{ + PublicDataRequest *pdr = static_cast(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(pdr->get_data_ptr()); + switch_state = t; + pdr->set_taken(); +} + + +static bool set_temp(TemperatureSwitch *nts, float t) +{ + // trap public data request to TemperatureControl and return a temperature + hitcnt= 0; + return_current_temp= t; + test_kernel_trap_event(ON_GET_PUBLIC_DATA, on_get_public_data_tc2); + + // tick 5 times for 5 seconds + for (int i = 0; i < 5; ++i) { + nts->on_second_tick(nullptr); + } + + // make sure the temp was read at least once + return (hitcnt > 0); +} + +TESTF(TemperatureSwitch,level_low_high) +{ + // load config with required settings for this test + test_kernel_setup_config(level_config, &level_config[sizeof(level_config)]); + + // trap public data request to TemperatureControl and return a mock tempcontrol + test_kernel_trap_event(ON_GET_PUBLIC_DATA, on_get_public_data_tc1); + + // make module load the config + uint16_t cs= get_checksum("psu_off"); + std::unique_ptr nts(ts->load_config(cs)); + + if(nts.get() == nullptr) { + FAIL_M("load config failed"); + } + + // stop handling this event + test_kernel_untrap_event(ON_GET_PUBLIC_DATA); + + // test it registered the event + ASSERT_TRUE(THEKERNEL->kernel_has_event(ON_GCODE_RECEIVED, nts.get())); + + set_temp(nts.get(), 25); + + // capture any call to the switch to turn it on or off + switch_state= false; + switch_set_hit= false; + switch_get_hit= false; + test_kernel_trap_event(ON_SET_PUBLIC_DATA, on_set_public_data_switch); + + // increase temp low -> high + set_temp(nts.get(), 60); + + ASSERT_TRUE(switch_get_hit); + // make sure switch was set + ASSERT_TRUE(switch_set_hit); + + // and make sure it was turned on + ASSERT_TRUE(switch_state); + + // now make sure it turns off when temp drops + set_temp(nts.get(), 30); + + // and make sure it was turned off + ASSERT_TRUE(!switch_state); +} + +TESTF(TemperatureSwitch,edge_high_low) +{ + // load config with required settings for this test + test_kernel_setup_config(edge_low_config, &edge_low_config[sizeof(edge_low_config)]); + + // trap public data request to TemperatureControl and return a mock tempcontrol + test_kernel_trap_event(ON_GET_PUBLIC_DATA, on_get_public_data_tc1); + + // make module load the config + uint16_t cs= get_checksum("psu_off"); + std::unique_ptr nts(ts->load_config(cs)); + + if(nts.get() == nullptr) { + FAIL_M("load config failed"); + } + + // stop handling this event + test_kernel_untrap_event(ON_GET_PUBLIC_DATA); + + // test it registered the event + ASSERT_TRUE(THEKERNEL->kernel_has_event(ON_GCODE_RECEIVED, nts.get())); + ASSERT_TRUE(!nts->is_armed()); + + // set initial temp low + set_temp(nts.get(), 25); + + // capture any call to the switch to turn it on or off + switch_state= true; + test_kernel_trap_event(ON_SET_PUBLIC_DATA, on_set_public_data_switch); + + // increase temp low -> high + set_temp(nts.get(), 60); + + // make sure it was not turned off + ASSERT_TRUE(switch_state); + + // drop temp + set_temp(nts.get(), 30); + + // and make sure it was still on + ASSERT_TRUE(switch_state); + + // now arm it + Gcode gc("M1100 S1", (StreamOutput *)THEKERNEL->serial, false); + nts->on_gcode_received(&gc); + + ASSERT_TRUE(nts->is_armed()); + + // increase temp low -> high + set_temp(nts.get(), 60); + + // make sure it was not turned off + ASSERT_TRUE(switch_state); + + // drop temp + set_temp(nts.get(), 30); + + // and make sure it was tunred off + ASSERT_TRUE(!switch_state); + + // make sure it is not armed anymore + ASSERT_TRUE(!nts->is_armed()); +} From 58e97d8ef29c89e3a2cd67de0682c441eda08ce8 Mon Sep 17 00:00:00 2001 From: Jim Morris Date: Sat, 12 Sep 2015 14:54:41 -0700 Subject: [PATCH 2/4] add Readme for test framework --- src/testframework/Readme.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/testframework/Readme.md diff --git a/src/testframework/Readme.md b/src/testframework/Readme.md new file mode 100644 index 0000000000..0cb70a7839 --- /dev/null +++ b/src/testframework/Readme.md @@ -0,0 +1,32 @@ +# Test framework Usage + +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 the Rakefile to specify which module you are going to test 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 stimualte the modules by sending it commands via on_gcode_received(), and monitor its actions my mockin gthe 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` + +you compile a unit test and framework using rake... + +```shell +rake testing=1 +rake upload +``` + +The unit test will run, the results are printed to the serial uart port, and then it is lieft in DFU mode so rake upload can be run again for the next test. + + + From 443a8cb405327a943b5e6c62a588232e2699153d Mon Sep 17 00:00:00 2001 From: Jim Morris Date: Sat, 12 Sep 2015 15:40:31 -0700 Subject: [PATCH 3/4] Update readme add more comments to the temperature switch unit test --- src/testframework/Readme.md | 24 +++++++---- .../TEST_TemperatureSwitch.cpp | 42 ++++++++++++------- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/testframework/Readme.md b/src/testframework/Readme.md index 0cb70a7839..241d245c75 100644 --- a/src/testframework/Readme.md +++ b/src/testframework/Readme.md @@ -1,4 +1,7 @@ -# Test framework Usage +# 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. @@ -6,12 +9,14 @@ It runs on the target and the output is written to the serial UART, it runs well You can edit the Rakefile to specify which module you are going to test 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 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 stimualte the modules by sending it commands via on_gcode_received(), and monitor its actions my mockin gthe ON_PUBLIC_SET/GET calls it makes. -A properly written module does not access other modules any other way. +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. @@ -19,14 +24,17 @@ An example is provided here... `src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp` -you compile a unit test and framework using rake... +## Usage + +You compile a unit test and framework using [rake](http://rake.rubyforge.org/)... ```shell -rake testing=1 -rake upload +> rake testing=1 +> rake upload ``` -The unit test will run, the results are printed to the serial uart port, and then it is lieft in DFU mode so rake upload can be run again for the next test. +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. + diff --git a/src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp b/src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp index cfcd3c88c4..b6a6f3dc0c 100644 --- a/src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp +++ b/src/testframework/unittests/tools/temperatureswitch/TEST_TemperatureSwitch.cpp @@ -14,24 +14,32 @@ #include "easyunit/test.h" +// this declares any global variables the test needs DECLARE(TemperatureSwitch) TemperatureSwitch *ts; END_DECLARE +// called before each test SETUP(TemperatureSwitch) { - ts = new TemperatureSwitch(); - //printf("...Setup TemperatureSwitch\n"); + // create the module we want to test + ts = new TemperatureSwitch(); + //printf("...Setup TemperatureSwitch\n"); } +// called after each test TEARDOWN(TemperatureSwitch) { - delete ts; - test_kernel_teardown(); + // delete the module + delete ts; - //printf("...Teardown TemperatureSwitch\n"); + // 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\ @@ -54,6 +62,7 @@ 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(argument); @@ -82,6 +91,7 @@ static void on_get_public_data_tc2(void *argument) PublicDataRequest *pdr = static_cast(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; @@ -97,7 +107,7 @@ static void on_get_public_data_tc2(void *argument) pdr->clear_returned_data(); } 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; @@ -112,6 +122,7 @@ static void on_get_public_data_tc2(void *argument) } } +// 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) { @@ -128,7 +139,7 @@ static void on_set_public_data_switch(void *argument) 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 @@ -167,7 +178,8 @@ TESTF(TemperatureSwitch,level_low_high) // test it registered the event ASSERT_TRUE(THEKERNEL->kernel_has_event(ON_GCODE_RECEIVED, nts.get())); - set_temp(nts.get(), 25); + // 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; @@ -176,7 +188,7 @@ TESTF(TemperatureSwitch,level_low_high) test_kernel_trap_event(ON_SET_PUBLIC_DATA, on_set_public_data_switch); // increase temp low -> high - set_temp(nts.get(), 60); + ASSERT_TRUE(set_temp(nts.get(), 60)); ASSERT_TRUE(switch_get_hit); // make sure switch was set @@ -186,7 +198,7 @@ TESTF(TemperatureSwitch,level_low_high) ASSERT_TRUE(switch_state); // now make sure it turns off when temp drops - set_temp(nts.get(), 30); + ASSERT_TRUE(set_temp(nts.get(), 30)); // and make sure it was turned off ASSERT_TRUE(!switch_state); @@ -216,20 +228,20 @@ TESTF(TemperatureSwitch,edge_high_low) ASSERT_TRUE(!nts->is_armed()); // set initial temp low - set_temp(nts.get(), 25); + 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 - set_temp(nts.get(), 60); + ASSERT_TRUE(set_temp(nts.get(), 60)); // make sure it was not turned off ASSERT_TRUE(switch_state); // drop temp - set_temp(nts.get(), 30); + ASSERT_TRUE(set_temp(nts.get(), 30)); // and make sure it was still on ASSERT_TRUE(switch_state); @@ -241,13 +253,13 @@ TESTF(TemperatureSwitch,edge_high_low) ASSERT_TRUE(nts->is_armed()); // increase temp low -> high - set_temp(nts.get(), 60); + ASSERT_TRUE(set_temp(nts.get(), 60)); // make sure it was not turned off ASSERT_TRUE(switch_state); // drop temp - set_temp(nts.get(), 30); + ASSERT_TRUE(set_temp(nts.get(), 30)); // and make sure it was tunred off ASSERT_TRUE(!switch_state); From ad433ba8a5cb461f4c6d71b854e5affe133b6193 Mon Sep 17 00:00:00 2001 From: Jim Morris Date: Sat, 12 Sep 2015 16:28:21 -0700 Subject: [PATCH 4/4] Allow test modules to be specifed in the rakefile.defaults file Update Readme --- Rakefile | 2 +- rakefile.defaults.example | 8 ++++++++ src/testframework/Readme.md | 19 ++++++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 rakefile.defaults.example diff --git a/Rakefile b/Rakefile index 105e13cb87..060147d1a7 100644 --- a/Rakefile +++ b/Rakefile @@ -116,7 +116,7 @@ end if TESTING # add modules to be tested here - TESTMODULES= %w(tools/temperatureswitch) + 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 testmodules= FileList['src/testframework/*.{c,cpp}', 'src/testframework/easyunit/*.{c,cpp}', 'src/modules/communication/SerialConsole.cpp', 'src/modules/communication/utils/Gcode.cpp'].include(TESTMODULES.collect { |e| "src/modules/#{e}/**/*.{c,cpp}"}).include(TESTMODULES.collect { |e| "src/testframework/unittests/#{e}/*.{c,cpp}"}) diff --git a/rakefile.defaults.example b/rakefile.defaults.example new file mode 100644 index 0000000000..3026938c57 --- /dev/null +++ b/rakefile.defaults.example @@ -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) diff --git a/src/testframework/Readme.md b/src/testframework/Readme.md index 241d245c75..defffe5f02 100644 --- a/src/testframework/Readme.md +++ b/src/testframework/Readme.md @@ -7,7 +7,7 @@ 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 the Rakefile to specify which module you are going to test the example has tools/temperaturecontrol selected. +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 @@ -35,6 +35,23 @@ You compile a unit test and framework using [rake](http://rake.rubyforge.org/).. 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. + +