parent
e1d32797d7
commit
37fb197a34
@ -0,0 +1,18 @@ |
||||
all: build test |
||||
|
||||
reset: |
||||
powershell -executionpolicy bypass .\scripts\reset_build.ps1
|
||||
|
||||
build: |
||||
meson compile -j 4 -C builddir
|
||||
|
||||
test: build |
||||
./builddir/runtests
|
||||
|
||||
install: build test |
||||
powershell "cp ./builddir/subprojects/libgit2-1.8.1/liblibgit2package.dll ."
|
||||
powershell "cp ./builddir/subprojects/efsw/libefsw.dll ."
|
||||
powershell "cp builddir/escape_turings_tarpit.exe ."
|
||||
|
||||
clean: |
||||
meson compile --clean -C builddir
|
@ -0,0 +1,40 @@ |
||||
#include "dbc.hpp" |
||||
|
||||
void dbc::log(const string &message) { |
||||
fmt::print("{}\n", message); |
||||
} |
||||
|
||||
void dbc::sentinel(const string &message) { |
||||
string err = fmt::format("[SENTINEL!] {}\n", message); |
||||
throw dbc::SentinelError{err}; |
||||
} |
||||
|
||||
void dbc::pre(const string &message, bool test) { |
||||
if(!test) { |
||||
string err = fmt::format("[PRE!] {}\n", message); |
||||
throw dbc::PreCondError{err}; |
||||
} |
||||
} |
||||
|
||||
void dbc::pre(const string &message, std::function<bool()> tester) { |
||||
dbc::pre(message, tester()); |
||||
} |
||||
|
||||
void dbc::post(const string &message, bool test) { |
||||
if(!test) { |
||||
string err = fmt::format("[POST!] {}\n", message); |
||||
throw dbc::PostCondError{err}; |
||||
} |
||||
} |
||||
|
||||
void dbc::post(const string &message, std::function<bool()> tester) { |
||||
dbc::post(message, tester()); |
||||
} |
||||
|
||||
void dbc::check(bool test, const string &message) { |
||||
if(!test) { |
||||
string err = fmt::format("[CHECK!] {}\n", message); |
||||
fmt::println("{}", err); |
||||
throw dbc::CheckError{err}; |
||||
} |
||||
} |
@ -0,0 +1,29 @@ |
||||
#pragma once |
||||
|
||||
#include <string> |
||||
#include <fmt/core.h> |
||||
#include <functional> |
||||
|
||||
using std::string; |
||||
|
||||
namespace dbc { |
||||
class Error { |
||||
public: |
||||
const string message; |
||||
Error(string m) : message{m} {} |
||||
Error(const char *m) : message{m} {} |
||||
}; |
||||
|
||||
class CheckError : public Error {}; |
||||
class SentinelError : public Error {}; |
||||
class PreCondError : public Error {}; |
||||
class PostCondError : public Error {}; |
||||
|
||||
void log(const string &message); |
||||
void sentinel(const string &message); |
||||
void pre(const string &message, bool test); |
||||
void pre(const string &message, std::function<bool()> tester); |
||||
void post(const string &message, bool test); |
||||
void post(const string &message, std::function<bool()> tester); |
||||
void check(bool test, const string &message); |
||||
} |
@ -0,0 +1,28 @@ |
||||
#pragma once |
||||
|
||||
#include <fmt/core.h> |
||||
|
||||
#ifndef FSM_DEBUG |
||||
#define FSM_STATE(C, S, E, ...) case C::S: S(E, ##__VA_ARGS__); break |
||||
#else |
||||
#define FSM_STATE(C, S, E, ...) case C::S: fmt::println(">> " #C " " #S " event={}, state={}", int(E), int(_state)); S(E, ##__VA_ARGS__); fmt::println("<< " #C " state={}", int(_state)); break |
||||
#endif |
||||
|
||||
template<typename S, typename E> |
||||
class DeadSimpleFSM { |
||||
protected: |
||||
// BUG: don't put this in your class because state() won't work
|
||||
S _state = S::START; |
||||
|
||||
public: |
||||
template<typename... Types> |
||||
void event(E event, Types... args); |
||||
|
||||
void state(S next_state) { |
||||
_state = next_state; |
||||
} |
||||
|
||||
bool in_state(S state) { |
||||
return _state == state; |
||||
} |
||||
}; |
@ -0,0 +1,14 @@ |
||||
project('distributary', 'cpp', |
||||
default_options: ['cpp_std=c++20']) |
||||
|
||||
catch2 = dependency('catch2-with-main') |
||||
fmt = dependency('fmt') |
||||
|
||||
runtests = executable('runtests', [ |
||||
'dbc.cpp', |
||||
'tests/fsm.cpp', |
||||
'tests/dbc.cpp', |
||||
], |
||||
dependencies: [catch2, fmt]) |
||||
|
||||
test('tests', runtests) |
@ -0,0 +1,11 @@ |
||||
mv .\subprojects\packagecache . |
||||
rm -recurse -force .\subprojects\,.\builddir\ |
||||
mkdir subprojects |
||||
mv .\packagecache .\subprojects\ |
||||
cp *.wrap subprojects |
||||
mkdir builddir |
||||
meson wrap install fmt |
||||
meson wrap install catch2 |
||||
# $env:CC="clang" |
||||
# $env:CXX="clang++" |
||||
meson setup --default-library=static --prefer-static builddir |
@ -0,0 +1,12 @@ |
||||
#!/usr/bin/env bash |
||||
set -e |
||||
|
||||
mv -f ./subprojects/packagecache . |
||||
rm -rf subprojects builddir |
||||
mkdir subprojects |
||||
mv packagecache ./subprojects/ |
||||
mkdir builddir |
||||
cp *.wrap subprojects |
||||
meson wrap install fmt |
||||
meson wrap install catch2 |
||||
meson setup builddir |
@ -0,0 +1,39 @@ |
||||
#include <catch2/catch_test_macros.hpp> |
||||
#include "dbc.hpp" |
||||
|
||||
using namespace dbc; |
||||
|
||||
TEST_CASE("basic feature tests", "[utils]") { |
||||
log("Logging a message."); |
||||
|
||||
try { |
||||
sentinel("This shouldn't happen."); |
||||
} catch(SentinelError) { |
||||
log("Sentinel happened."); |
||||
} |
||||
|
||||
pre("confirm positive cases work", 1 == 1); |
||||
pre("confirm positive lambda", [&]{ return 1 == 1;}); |
||||
post("confirm positive post", 1 == 1); |
||||
post("confirm postitive post with lamdba", [&]{ return 1 == 1;}); |
||||
|
||||
check(1 == 1, "one equals 1"); |
||||
|
||||
try { |
||||
check(1 == 2, "this should fail"); |
||||
} catch(CheckError err) { |
||||
log("check fail worked"); |
||||
} |
||||
|
||||
try { |
||||
pre("failing pre", 1 == 3); |
||||
} catch(PreCondError err) { |
||||
log("pre fail worked"); |
||||
} |
||||
|
||||
try { |
||||
post("failing post", 1 == 4); |
||||
} catch(PostCondError err) { |
||||
log("post faile worked"); |
||||
} |
||||
} |
@ -0,0 +1,67 @@ |
||||
#include <catch2/catch_test_macros.hpp> |
||||
#include <fmt/core.h> |
||||
#include <string> |
||||
#include "../fsm.hpp" |
||||
|
||||
using namespace fmt; |
||||
using std::string; |
||||
|
||||
enum class MyState { |
||||
START, RUNNING, END |
||||
}; |
||||
|
||||
enum class MyEvent { |
||||
STARTED, PUSH, QUIT |
||||
}; |
||||
|
||||
class MyFSM : public DeadSimpleFSM<MyState, MyEvent> { |
||||
public: |
||||
void event(MyEvent ev, string data="") { |
||||
switch(_state) { |
||||
FSM_STATE(MyState, START, ev); |
||||
FSM_STATE(MyState, RUNNING, ev, data); |
||||
FSM_STATE(MyState, END, ev); |
||||
} |
||||
} |
||||
|
||||
void START(MyEvent ev) { |
||||
println("<<< START"); |
||||
state(MyState::RUNNING); |
||||
} |
||||
|
||||
void RUNNING(MyEvent ev, string &data) { |
||||
if(ev == MyEvent::QUIT) { |
||||
println("<<< QUITTING {}", data); |
||||
state(MyState::END); |
||||
} else { |
||||
println("<<< RUN: {}", data); |
||||
state(MyState::RUNNING); |
||||
} |
||||
} |
||||
|
||||
void END(MyEvent ev) { |
||||
println("<<< STOP"); |
||||
state(MyState::END); |
||||
} |
||||
}; |
||||
|
||||
TEST_CASE("confirm fsm works with optional data", "[utils]") { |
||||
MyFSM fsm; |
||||
|
||||
REQUIRE(fsm.in_state(MyState::START)); |
||||
|
||||
fsm.event(MyEvent::STARTED); |
||||
REQUIRE(fsm.in_state(MyState::RUNNING)); |
||||
|
||||
fsm.event(MyEvent::PUSH); |
||||
REQUIRE(fsm.in_state(MyState::RUNNING)); |
||||
|
||||
fsm.event(MyEvent::PUSH); |
||||
REQUIRE(fsm.in_state(MyState::RUNNING)); |
||||
|
||||
fsm.event(MyEvent::PUSH); |
||||
REQUIRE(fsm.in_state(MyState::RUNNING)); |
||||
|
||||
fsm.event(MyEvent::QUIT, "DONE!"); |
||||
REQUIRE(fsm.in_state(MyState::END)); |
||||
} |
Loading…
Reference in new issue