diff options
-rw-r--r-- | project2/Jamfile.jam | 1 | ||||
-rw-r--r-- | project2/files/Jamfile.jam | 4 | ||||
-rw-r--r-- | project2/files/configFlexLexer.cpp | 28 | ||||
-rw-r--r-- | project2/files/configFlexLexer.h | 27 | ||||
-rw-r--r-- | project2/files/configuration.ll | 64 | ||||
-rw-r--r-- | project2/files/optionsSource.cpp | 47 | ||||
-rw-r--r-- | project2/files/unittests/Jamfile.jam | 18 | ||||
-rw-r--r-- | project2/files/unittests/test.config | 20 | ||||
-rw-r--r-- | project2/files/unittests/testConfig.cpp | 65 |
9 files changed, 235 insertions, 39 deletions
diff --git a/project2/Jamfile.jam b/project2/Jamfile.jam index 07681d1..cb99d35 100644 --- a/project2/Jamfile.jam +++ b/project2/Jamfile.jam @@ -40,6 +40,7 @@ build-project basics//unittests ; build-project ice//unittests ; build-project sql//unittests ; build-project xml//unittests ; +build-project files//unittests ; explicit install ; package.install install : : finalbin : finallib ; diff --git a/project2/files/Jamfile.jam b/project2/files/Jamfile.jam index 91272e0..23fd351 100644 --- a/project2/files/Jamfile.jam +++ b/project2/files/Jamfile.jam @@ -1,3 +1,5 @@ +import lex ; + alias glibmm : : : : <cflags>"`pkg-config --cflags glibmm-2.4`" <linkflags>"`pkg-config --libs glibmm-2.4`" @@ -12,7 +14,7 @@ cpp-pch pch : pch.hpp : ; lib p2files : pch - [ glob-tree *.cpp ] + [ glob-tree *.cpp *.ll : unittests bin ] : <include>. <include>../libmisc diff --git a/project2/files/configFlexLexer.cpp b/project2/files/configFlexLexer.cpp new file mode 100644 index 0000000..fe44b0c --- /dev/null +++ b/project2/files/configFlexLexer.cpp @@ -0,0 +1,28 @@ +#include "FlexLexer.h" +#include "configFlexLexer.h" +#include <stdexcept> + +configFlexLexer::configFlexLexer(std::istream & in, const ConfigConsumer & c, const Options::CurrentPlatform & cp) : + yyFlexLexer(&in, NULL), + consume(c), + currentPlatform(cp) +{ +} + +void +configFlexLexer::process() const +{ + consume(domain.empty() ? option : domain + "." + option, platform, value, currentPlatform); +} + +void +configFlexLexer::LexerOutput(const char *, int) +{ +} + +void +configFlexLexer::LexerError(const char * msg) +{ + throw std::runtime_error(msg); +} + diff --git a/project2/files/configFlexLexer.h b/project2/files/configFlexLexer.h new file mode 100644 index 0000000..177f11c --- /dev/null +++ b/project2/files/configFlexLexer.h @@ -0,0 +1,27 @@ +#ifndef CONFIGFLEXLEXER_H +#define CONFIGFLEXLEXER_H + +#include <glibmm/ustring.h> +#include <optionsSource.h> + +class configFlexLexer : public yyFlexLexer { + public: + configFlexLexer(std::istream & in, const ConfigConsumer & consume, const Options::CurrentPlatform &); + int yylex(); + + void LexerOutput(const char *, int) override; + void LexerError(const char * msg) override; + + private: + void process() const; + const ConfigConsumer & consume; + const Options::CurrentPlatform & currentPlatform; + + Glib::ustring domain; + Glib::ustring platform; + Glib::ustring option; + Glib::ustring value; +}; + +#endif + diff --git a/project2/files/configuration.ll b/project2/files/configuration.ll new file mode 100644 index 0000000..b3c8de7 --- /dev/null +++ b/project2/files/configuration.ll @@ -0,0 +1,64 @@ +%option batch +%option c++ +%option noyywrap +%option yyclass="configFlexLexer" + +%{ +#include "configFlexLexer.h" +%} + +element [a-zA-Z][a-zA-Z0-9_-]* +identifier {element}("."{element})* +begindomain "[" +enddomain "]" +beginplatform "/" +eq "=" +value [^ ][^\r\n]* + +%x DOMAIN +%x PLATFORM +%x POSTOPT +%x VALUE +%x EQUAL + +%% + +<INITIAL>{begindomain} { + domain.clear(); + yy_push_state(DOMAIN); +} + +<DOMAIN>{identifier} { + domain = YYText(); +} + +<DOMAIN>{enddomain} { + yy_pop_state(); +} + +<INITIAL>{identifier} { + option = YYText(); + platform.clear(); + value.clear(); + BEGIN(POSTOPT); +} + +<POSTOPT>{beginplatform} { + BEGIN(PLATFORM); +} + +<POSTOPT,EQUAL>{eq} { + BEGIN(VALUE); +} + +<PLATFORM>{identifier} { + platform = YYText(); + BEGIN(EQUAL); +} + +<VALUE>{value} { + value = YYText(); + process(); + BEGIN(INITIAL); +} + diff --git a/project2/files/optionsSource.cpp b/project2/files/optionsSource.cpp index c09132a..2ebebdb 100644 --- a/project2/files/optionsSource.cpp +++ b/project2/files/optionsSource.cpp @@ -1,9 +1,10 @@ #include <pch.hpp> #include <boost/filesystem/convenience.hpp> #include "optionsSource.h" -#include <glibmm/iochannel.h> -#include <glibmm/fileutils.h> -#include <boost/algorithm/string/trim.hpp> +#include <FlexLexer.h> +#include "configFlexLexer.h" +#include <boost/shared_ptr.hpp> +#include <fstream> FileOptions::FileOptions(const boost::filesystem::path & f) : file(boost::filesystem::absolute(f)) @@ -14,42 +15,12 @@ void FileOptions::loadInto(const ConfigConsumer & consume, const Options::CurrentPlatform & platform) const { if (boost::filesystem::exists(file)) { - Glib::RefPtr<Glib::IOChannel> cfg = Glib::IOChannel::create_from_file(file.string(), "r"); - Glib::ustring line; - Glib::ustring prefix; - while (cfg->read_line(line) != Glib::IO_STATUS_EOF) { - switch (line[0]) { - case '#': - continue; - case '[': - prefix = line.substr(1, line.find(']', 2) - 1); - boost::algorithm::trim_if(prefix, Glib::Unicode::isspace); - if (!prefix.empty()) { - prefix += '.'; - } - break; - default: - Glib::ustring name, plat; - Glib::ustring::size_type eq = line.find('='); - Glib::ustring::size_type sl = line.find('/'); - if (sl < eq && sl != (Glib::ustring::size_type)-1) { - name = line.substr(0, sl); - plat = line.substr(sl + 1, eq - sl - 1); - } - else { - name = line.substr(0, eq); - } - boost::algorithm::trim_if(name, Glib::Unicode::isspace); - if (name.empty()) { - continue; - } - Glib::ustring val = line.substr(eq + 1); - boost::algorithm::trim_if(plat, Glib::Unicode::isspace); - boost::algorithm::trim_if(val, Glib::Unicode::isspace); - consume(prefix + name, plat, val, platform); - break; - } + std::ifstream in(file.string()); + if (!in.good()) { + throw std::runtime_error("Couldn't open configuration file " + file.string()); } + configFlexLexer flex(in, consume, platform); + while (flex.yylex()) ; } } diff --git a/project2/files/unittests/Jamfile.jam b/project2/files/unittests/Jamfile.jam new file mode 100644 index 0000000..236938d --- /dev/null +++ b/project2/files/unittests/Jamfile.jam @@ -0,0 +1,18 @@ +import testing ; + +lib boost_system ; +lib boost_filesystem ; + +path-constant me : . ; + +run + testConfig.cpp + : : : + <library>..//p2files + <library>../../ut//p2ut + <library>../../common//p2common + <library>boost_filesystem + <define>ROOT=\"$(me)\" + <dependency>test.config + : testConfig ; + diff --git a/project2/files/unittests/test.config b/project2/files/unittests/test.config new file mode 100644 index 0000000..aefcce9 --- /dev/null +++ b/project2/files/unittests/test.config @@ -0,0 +1,20 @@ +noscope1 = 1 +noscope2 = two +noscope3and4 = 3 & four + +some.scope = scoped +some.more.scope = more scoped + +[domain] +subscope = 1 +sub.scope = 2 + +[ other.domain ] +subscope = 1 +sub.scope = 2 + +[] +noscope5 = 5 +platform / test = testonly +platform = all + diff --git a/project2/files/unittests/testConfig.cpp b/project2/files/unittests/testConfig.cpp new file mode 100644 index 0000000..45fafac --- /dev/null +++ b/project2/files/unittests/testConfig.cpp @@ -0,0 +1,65 @@ +#define BOOST_TEST_MODULE TestFilesConfig +#include <boost/test/unit_test.hpp> + +#include <definedDirs.h> +#include <boost/tuple/tuple.hpp> +#include <boost/tuple/tuple_comparison.hpp> +#include "../optionsSource.h" +#include <safeMapFind.h> + +class TestConfigConsumer : public ConfigConsumer { + public: + typedef boost::tuple<Glib::ustring, Glib::ustring> OptionPlatform; + typedef std::map<OptionPlatform, Glib::ustring> OptionsMap; + class NotFound : public std::runtime_error { + public: + NotFound(const OptionPlatform & op) : std::runtime_error("NotFound: " + op.get<0>() + " / " + op.get<1>()) { } + }; + void operator()(const Glib::ustring & option, const Glib::ustring & platform, const Glib::ustring & value, const Options::CurrentPlatform &) const override + { + options[OptionPlatform(option, platform)] = value; + } + + const Glib::ustring & operator()(const Glib::ustring & name, const Glib::ustring & platform) const + { + return safeMapLookup<NotFound>(options, {name, platform}); + } + const Options::Option * get(const Glib::ustring &) const override { return NULL; } + mutable OptionsMap options; +}; + +BOOST_AUTO_TEST_CASE( readMissingFile ) +{ + TestConfigConsumer options; + OptionsSourcePtr file = OptionsSourcePtr(new FileOptions(RootDir / "nothere.config")); + file->loadInto(options, []() { return Glib::ustring(); }); + BOOST_REQUIRE(options.options.empty()); +} + +BOOST_AUTO_TEST_CASE( readConfigFile ) +{ + TestConfigConsumer options; + BOOST_REQUIRE(boost::filesystem::exists(RootDir / "test.config")); + OptionsSourcePtr file = OptionsSourcePtr(new FileOptions(RootDir / "test.config")); + file->loadInto(options, []() { return Glib::ustring(); }); + // Count + BOOST_REQUIRE_EQUAL(12, options.options.size()); + // Simple + BOOST_REQUIRE_EQUAL("1", options("noscope1", "")); + BOOST_REQUIRE_EQUAL("two", options("noscope2", "")); + BOOST_REQUIRE_EQUAL("3 & four", options("noscope3and4", "")); + BOOST_REQUIRE_EQUAL("scoped", options("some.scope", "")); + BOOST_REQUIRE_EQUAL("more scoped", options("some.more.scope", "")); + // Domain + BOOST_REQUIRE_EQUAL("1", options("domain.subscope", "")); + BOOST_REQUIRE_EQUAL("2", options("domain.sub.scope", "")); + // Other Domain + BOOST_REQUIRE_EQUAL("1", options("other.domain.subscope", "")); + BOOST_REQUIRE_EQUAL("2", options("other.domain.sub.scope", "")); + // No domain + BOOST_REQUIRE_EQUAL("5", options("noscope5", "")); + // Platform + BOOST_REQUIRE_EQUAL("testonly", options("platform", "test")); + BOOST_REQUIRE_EQUAL("all", options("platform", "")); +} + |