initial base commit
This commit is contained in:
parent
d1dad7e64e
commit
da6314f6ac
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
import glob
|
||||
import re
|
||||
|
||||
if sys.version_info < (3,):
|
||||
def isbasestring(s):
|
||||
return isinstance(s,basestring)
|
||||
else:
|
||||
def isbasestring(s):
|
||||
return isinstance(s, (str,bytes))
|
||||
|
||||
def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""):
|
||||
|
||||
if isbasestring(filetype):
|
||||
dir_path = self.Dir('.').abspath
|
||||
filetype = sorted(glob.glob(dir_path+"/"+filetype))
|
||||
|
||||
for path in filetype:
|
||||
target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path )
|
||||
if shared:
|
||||
target_name+='.os'
|
||||
sources.append( self.SharedObject( target=target_name, source=path ) )
|
||||
else:
|
||||
target_name+='.o'
|
||||
sources.append( self.StaticObject( target=target_name, source=path ) )
|
||||
pass
|
||||
|
||||
env=Environment(CPPPATH=['#source'],
|
||||
CXX='clang++',
|
||||
CPPDEFINES=['GIN_UNIX'],
|
||||
CXXFLAGS=['-std=c++17','-g','-Wall','-Wextra'],
|
||||
LIBS=[])
|
||||
env.__class__.add_source_files = add_kel_source_files
|
||||
|
||||
env.sources = []
|
||||
env.headers = []
|
||||
env.objects = []
|
||||
|
||||
Export('env')
|
||||
SConscript('source/SConscript')
|
||||
SConscript('test/SConscript')
|
||||
|
||||
# Clang format part
|
||||
env.Append(BUILDERS={'ClangFormat' : Builder(action = 'clang-format --style=file -i $SOURCE')})
|
||||
env.format_actions = []
|
||||
def format_iter(env,files):
|
||||
for f in files:
|
||||
env.format_actions.append(env.AlwaysBuild(env.ClangFormat(target=f+"-clang-format",source=f)))
|
||||
pass
|
||||
|
||||
format_iter(env,env.sources + env.headers)
|
||||
|
||||
env.Alias('format', env.format_actions)
|
||||
env.Alias('test', env.test_program)
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/false
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import glob
|
||||
|
||||
|
||||
Import('env')
|
||||
|
||||
dir_path = Dir('.').abspath
|
||||
|
||||
env.sources += sorted(glob.glob(dir_path + "/*.cpp"))
|
||||
env.headers += sorted(glob.glob(dir_path + "/*.h"))
|
||||
|
||||
env.add_source_files(env.objects, env.sources)
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import glob
|
||||
|
||||
Import('env')
|
||||
|
||||
dir_path = Dir('.').abspath
|
||||
env.test_sources = sorted(glob.glob(dir_path + "/*.cpp"))
|
||||
|
||||
env_test = env.Clone()
|
||||
env.test_objects = []
|
||||
env.test_sources.append(dir_path+'/suite/suite.cpp')
|
||||
|
||||
env.test_program = env_test.Program('#bin/test', [env.test_sources, env.objects])
|
|
@ -0,0 +1,108 @@
|
|||
#include "suite.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
namespace ent {
|
||||
namespace test {
|
||||
|
||||
TestCase* testCaseHead = nullptr;
|
||||
TestCase** testCaseTail = &testCaseHead;
|
||||
|
||||
TestCase::TestCase(const std::string& file_, uint line_, const std::string& description_):
|
||||
file{file_},
|
||||
line{line_},
|
||||
description{description_},
|
||||
matched_filter{false},
|
||||
next{nullptr},
|
||||
prev{testCaseTail}
|
||||
{
|
||||
*prev = this;
|
||||
testCaseTail = &next;
|
||||
}
|
||||
|
||||
TestCase::~TestCase(){
|
||||
*prev = next;
|
||||
if( next == nullptr ){
|
||||
testCaseTail = prev;
|
||||
}else{
|
||||
next->prev = prev;
|
||||
}
|
||||
}
|
||||
|
||||
class TestRunner {
|
||||
private:
|
||||
enum Colour {
|
||||
RED,
|
||||
GREEN,
|
||||
BLUE,
|
||||
WHITE
|
||||
};
|
||||
|
||||
void write(Colour colour, const std::string& front, const std::string& message){
|
||||
std::string start_col, end_col;
|
||||
switch(colour){
|
||||
case RED: start_col = "\033[0;1;31m"; break;
|
||||
case GREEN: start_col = "\033[0;1;32m"; break;
|
||||
case BLUE: start_col = "\033[0;1;34m"; break;
|
||||
case WHITE: start_col = "\033[0m"; break;
|
||||
}
|
||||
end_col = "\033[0m";
|
||||
std::cout<<start_col<<front<<end_col<<message<<'\n';
|
||||
}
|
||||
public:
|
||||
void allowAll(){
|
||||
for(TestCase* testCase = testCaseHead; testCase != nullptr; testCase = testCase->next){
|
||||
testCase->matched_filter = true;
|
||||
}
|
||||
}
|
||||
|
||||
int run() {
|
||||
size_t passed_count = 0;
|
||||
size_t failed_count = 0;
|
||||
|
||||
for(TestCase* testCase = testCaseHead; testCase != nullptr; testCase =testCase->next){
|
||||
if(testCase->matched_filter){
|
||||
std::string name = testCase->file + std::string(":") + std::to_string(testCase->line) + std::string(": ") + testCase->description;
|
||||
write(BLUE, "[ TEST ] ", name);
|
||||
bool failed = true;
|
||||
std::string fail_message;
|
||||
auto start_clock = std::chrono::steady_clock::now();
|
||||
try {
|
||||
testCase->run();
|
||||
failed = false;
|
||||
}catch(std::exception& e){
|
||||
fail_message = e.what();
|
||||
failed = true;
|
||||
}
|
||||
auto stop_clock = std::chrono::steady_clock::now();
|
||||
|
||||
auto runtime_duration_intern = stop_clock - start_clock;
|
||||
auto runtime_duration = std::chrono::duration_cast<std::chrono::microseconds>(runtime_duration_intern);
|
||||
std::string message = name + std::string(" (") + std::to_string(runtime_duration.count()) + std::string(" µs)");
|
||||
if(failed){
|
||||
write(RED, "[ FAIL ] ", message + " " + fail_message);
|
||||
++failed_count;
|
||||
}else{
|
||||
write(GREEN, "[ PASS ] ", message);
|
||||
++passed_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(passed_count>0) write(GREEN, std::to_string(passed_count) + std::string(" test(s) passed"), "");
|
||||
if(failed_count>0) write(RED, std::to_string(failed_count) + std::string(" test(s) failed"), "");
|
||||
return -failed_count;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
ent::test::TestRunner runner;
|
||||
runner.allowAll();
|
||||
int rv = runner.run();
|
||||
return rv<0?-1:0;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#define KEL_CONCAT_(x,y) x##y
|
||||
#define KEL_CONCAT(x,y) KEL_CONCAT_(x,y)
|
||||
#define KEL_UNIQUE_NAME(prefix) KEL_CONCAT(prefix, __LINE__)
|
||||
|
||||
namespace ent {
|
||||
namespace test {
|
||||
class TestRunner;
|
||||
class TestCase {
|
||||
private:
|
||||
std::string file;
|
||||
uint line;
|
||||
std::string description;
|
||||
bool matched_filter;
|
||||
TestCase* next;
|
||||
TestCase** prev;
|
||||
|
||||
friend class TestRunner;
|
||||
public:
|
||||
TestCase(const std::string& file_, uint line_, const std::string& description_);
|
||||
~TestCase();
|
||||
|
||||
virtual void run() = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
#define KEL_TEST(description) \
|
||||
class KEL_UNIQUE_NAME(TestCase) : public ::ent::test::TestCase { \
|
||||
public: \
|
||||
KEL_UNIQUE_NAME(TestCase)(): ::ent::test::TestCase(__FILE__,__LINE__,description) {} \
|
||||
void run() override; \
|
||||
}KEL_UNIQUE_NAME(testCase); \
|
||||
void KEL_UNIQUE_NAME(TestCase)::run()
|
||||
|
||||
#define KEL_EXPECT(expr, msg) \
|
||||
if( ! (expr) ){ \
|
||||
throw std::runtime_error{msg}; \
|
||||
}
|
Loading…
Reference in New Issue