From d316af61ab9d8d2b26e46ca34a95448334cd1574 Mon Sep 17 00:00:00 2001 From: Stefan de Bruijn Date: Fri, 4 Jun 2021 23:00:46 +0200 Subject: [PATCH] Added yaml tree builder tests. Fixed nasty bug in parser handler. --- Grbl_Esp32/src/Configuration/Parser.cpp | 1 + Grbl_Esp32/src/Configuration/ParserHandler.h | 62 ++++-- Grbl_Esp32/src/Logging.cpp | 2 +- .../test/Configuration/YamlTreeBuilder.cpp | 199 ++++++++++++++++++ UnitTests.vcxproj | 5 + UnitTests.vcxproj.filters | 19 +- 6 files changed, 265 insertions(+), 23 deletions(-) create mode 100644 Grbl_Esp32/test/Configuration/YamlTreeBuilder.cpp diff --git a/Grbl_Esp32/src/Configuration/Parser.cpp b/Grbl_Esp32/src/Configuration/Parser.cpp index 03c94368..29546abf 100644 --- a/Grbl_Esp32/src/Configuration/Parser.cpp +++ b/Grbl_Esp32/src/Configuration/Parser.cpp @@ -110,6 +110,7 @@ namespace Configuration { if (last == token_.indent_) { // Yes, the token continues where we left off: current_ = token_; + // Tokenize(); } else { current_ = TokenData(); current_.indent_ = last; diff --git a/Grbl_Esp32/src/Configuration/ParserHandler.h b/Grbl_Esp32/src/Configuration/ParserHandler.h index 16489850..cb456e7d 100644 --- a/Grbl_Esp32/src/Configuration/ParserHandler.h +++ b/Grbl_Esp32/src/Configuration/ParserHandler.h @@ -28,66 +28,86 @@ namespace Configuration { class ParserHandler : public Configuration::HandlerBase { private: - Configuration::Parser& parser_; + Configuration::Parser& _parser; + bool _previousIsLeave = false; + + void checkPreviousLeave() { + // When a section does a 'leave', the token becomes invalid, and we need to do a + // moveNext. See the tests for the yaml parser for what the parser expects. + // So, let's introduce that here: + if (_previousIsLeave) { + _parser.moveNext(); + } + _previousIsLeave = false; + } protected: void handleDetail(const char* name, Configuration::Configurable* value) override { - if (value != nullptr && parser_.is(name)) { + if (value != nullptr && _parser.is(name)) { log_debug("Parsing configurable " << name); + checkPreviousLeave(); - parser_.enter(); - for (; !parser_.isEndSection(); parser_.moveNext()) { + _parser.enter(); + for (; !_parser.isEndSection(); _parser.moveNext()) { value->handle(*this); } - parser_.leave(); + _parser.leave(); + _previousIsLeave = true; } } - bool matchesUninitialized(const char* name) override { return parser_.is(name); } + bool matchesUninitialized(const char* name) override { return _parser.is(name); } public: - ParserHandler(Configuration::Parser& parser) : parser_(parser) {} + ParserHandler(Configuration::Parser& parser) : _parser(parser) {} void handle(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override { - if (parser_.is(name)) { - value = parser_.intValue(); + if (_parser.is(name)) { + checkPreviousLeave(); + value = _parser.intValue(); } } void handle(const char* name, int& value, EnumItem* e) override { - if (parser_.is(name)) { - value = parser_.enumValue(e); + if (_parser.is(name)) { + checkPreviousLeave(); + value = _parser.enumValue(e); } } void handle(const char* name, bool& value) override { - if (parser_.is(name)) { - value = parser_.boolValue(); + if (_parser.is(name)) { + checkPreviousLeave(); + value = _parser.boolValue(); } } void handle(const char* name, float& value, float minValue, float maxValue) override { - if (parser_.is(name)) { - value = parser_.floatValue(); + if (_parser.is(name)) { + checkPreviousLeave(); + value = _parser.floatValue(); } } void handle(const char* name, StringRange& value, int minLength, int maxLength) override { - if (parser_.is(name)) { - value = parser_.stringValue(); + if (_parser.is(name)) { + checkPreviousLeave(); + value = _parser.stringValue(); } } void handle(const char* name, Pin& value) override { - if (parser_.is(name)) { - auto parsed = parser_.pinValue(); + if (_parser.is(name)) { + checkPreviousLeave(); + auto parsed = _parser.pinValue(); value.swap(parsed); } } void handle(const char* name, IPAddress& value) override { - if (parser_.is(name)) { - value = parser_.ipValue(); + if (_parser.is(name)) { + checkPreviousLeave(); + value = _parser.ipValue(); } } diff --git a/Grbl_Esp32/src/Logging.cpp b/Grbl_Esp32/src/Logging.cpp index 715f1c7c..52f31234 100644 --- a/Grbl_Esp32/src/Logging.cpp +++ b/Grbl_Esp32/src/Logging.cpp @@ -23,7 +23,7 @@ # include DebugStream::DebugStream(const char* name) { - std::cout << '[MSG:' << name << ": "; + std::cout << "[MSG:" << name << ": "; } void DebugStream::add(char c) { std::cout << c; diff --git a/Grbl_Esp32/test/Configuration/YamlTreeBuilder.cpp b/Grbl_Esp32/test/Configuration/YamlTreeBuilder.cpp new file mode 100644 index 00000000..ceee3397 --- /dev/null +++ b/Grbl_Esp32/test/Configuration/YamlTreeBuilder.cpp @@ -0,0 +1,199 @@ +#include "../TestFramework.h" + +#include + +#include +#include +#include +#include + +namespace Configuration { + class TestBasic : public Configurable { + public: + String a; + String b; + String c; + + void validate() const {} + void handle(HandlerBase& handler) { + handler.handle("a", a); + handler.handle("b", b); + handler.handle("c", c); + } + }; + + class TestBasic2 : public Configurable { + public: + String aap; + int banaan; + + void validate() const {} + void handle(HandlerBase& handler) { + handler.handle("aap", aap); + handler.handle("banaan", banaan); + } + }; + + class TestHierarchical : public Configurable { + public: + TestBasic* n1 = nullptr; + TestBasic2* n2 = nullptr; + int foo = 0; + + void validate() const {} + void handle(HandlerBase& handler) { + handler.handle("n1", n1); + handler.handle("n2", n2); + handler.handle("foo", foo); + } + }; + + struct Helper { + template + static inline void Parse(const char* config, T& test) { + Parser p(config, config + strlen(config)); + ParserHandler handler(p); + + test.handle(handler); + for (; !p.isEndSection(); p.moveNext()) { + test.handle(handler); + } + } + }; + + Test(YamlTreeBuilder, BasicProperties) { + const char* config = "a: aap\n" + "b: banaan\n" + "\n" + "c: chocolade"; + + TestBasic test; + Helper::Parse(config, test); + + Assert(test.a == "aap"); + Assert(test.b == "banaan"); + Assert(test.c == "chocolade"); + } + + Test(YamlTreeBuilder, BasicPropertiesInvert) { + const char* config = "c: chocolade\n" + "b: banaan\n" + "a: aap\n"; + + TestBasic test; + Helper::Parse(config, test); + + Assert(test.a == "aap"); + Assert(test.b == "banaan"); + Assert(test.c == "chocolade"); + } + + Test(YamlTreeBuilder, BasicProperties2) { + const char* config = "aap: aap\n" + "banaan: 2\n"; + + TestBasic2 test; + Helper::Parse(config, test); + + Assert(test.aap == "aap"); + Assert(test.banaan == 2); + } + + Test(YamlTreeBuilder, BasicPropertiesInvert2) { + const char* config = "banaan: 2\n" + "aap: aap\n"; + + TestBasic2 test; + Helper::Parse(config, test); + + Assert(test.aap == "aap"); + Assert(test.banaan == 2); + } + + Test(YamlTreeBuilder, Hierarchical1) { + const char* config = "n1:\n" + " a: aap\n" + " b: banaan\n" + " \n" + " c: chocolade\n" + "n2:\n" + " banaan: 2\n" + " aap: aap\n" + "foo: 2\n"; + + TestHierarchical test; + Helper::Parse(config, test); + + { + Assert(test.n1 != nullptr); + Assert(test.n1->a == "aap"); + Assert(test.n1->b == "banaan"); + Assert(test.n1->c == "chocolade"); + } + + { + Assert(test.n2 != nullptr); + Assert(test.n2->banaan == 2); + Assert(test.n2->aap == "aap"); + } + Assert(test.foo == 2); + } + + Test(YamlTreeBuilder, Hierarchical2) { + const char* config = "n2:\n" + " banaan: 2\n" + " aap: aap\n" + "n1:\n" + " a: aap\n" + " b: banaan\n" + " \n" + " c: chocolade\n" + "foo: 2\n"; + + TestHierarchical test; + Helper::Parse(config, test); + + { + Assert(test.n1 != nullptr); + Assert(test.n1->a == "aap"); + Assert(test.n1->b == "banaan"); + Assert(test.n1->c == "chocolade"); + } + + { + Assert(test.n2 != nullptr); + Assert(test.n2->banaan == 2); + Assert(test.n2->aap == "aap"); + } + Assert(test.foo == 2); + } + + Test(YamlTreeBuilder, Hierarchical3) { + const char* config = "foo: 2\n" + "n2:\n" + " banaan: 2\n" + " aap: aap\n" + "n1:\n" + " a: aap\n" + " b: banaan\n" + " \n" + " c: chocolade\n"; + + TestHierarchical test; + Helper::Parse(config, test); + + { + Assert(test.n1 != nullptr); + Assert(test.n1->a == "aap"); + Assert(test.n1->b == "banaan"); + Assert(test.n1->c == "chocolade"); + } + + { + Assert(test.n2 != nullptr); + Assert(test.n2->banaan == 2); + Assert(test.n2->aap == "aap"); + } + Assert(test.foo == 2); + } +} diff --git a/UnitTests.vcxproj b/UnitTests.vcxproj index a0fd95fc..7c7df0d7 100644 --- a/UnitTests.vcxproj +++ b/UnitTests.vcxproj @@ -44,6 +44,7 @@ + @@ -53,6 +54,7 @@ + @@ -65,6 +67,7 @@ + @@ -74,10 +77,12 @@ + + diff --git a/UnitTests.vcxproj.filters b/UnitTests.vcxproj.filters index 7979c92b..91d5202e 100644 --- a/UnitTests.vcxproj.filters +++ b/UnitTests.vcxproj.filters @@ -93,6 +93,12 @@ X86TestSupport + + src + + + src + @@ -170,7 +176,18 @@ src\Configuration - + + test\Configuration + + + test\Configuration + + + src + + + src +