From 72648921ec28903615698a61aeff4799e1ca9a7d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 9 Jun 2015 19:50:58 +0200 Subject: Implement low-level manifest parser --- tests/.gitignore | 1 + tests/buildfile | 7 ++ tests/manifest-parser/buildfile | 7 ++ tests/manifest-parser/driver.cxx | 210 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 tests/.gitignore create mode 100644 tests/buildfile create mode 100644 tests/manifest-parser/buildfile create mode 100644 tests/manifest-parser/driver.cxx (limited to 'tests') diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..e54525b --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +driver diff --git a/tests/buildfile b/tests/buildfile new file mode 100644 index 0000000..8832040 --- /dev/null +++ b/tests/buildfile @@ -0,0 +1,7 @@ +# file : tests/buildfile +# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +d = manifest-parser/ +.: $d +include $d diff --git a/tests/manifest-parser/buildfile b/tests/manifest-parser/buildfile new file mode 100644 index 0000000..4fcf735 --- /dev/null +++ b/tests/manifest-parser/buildfile @@ -0,0 +1,7 @@ +# file : tests/manifest-parser/buildfile +# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +exe{driver}: cxx{driver} ../../bpkg/lib{bpkg} + +include ../../bpkg/ diff --git a/tests/manifest-parser/driver.cxx b/tests/manifest-parser/driver.cxx new file mode 100644 index 0000000..b86b806 --- /dev/null +++ b/tests/manifest-parser/driver.cxx @@ -0,0 +1,210 @@ +// file : tests/manifest-parser/driver.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include +#include +#include // pair +#include +#include +#include + +#include + +using namespace std; +using namespace bpkg; + +using pairs = vector>; + +static bool +parse (const char* manifest, const pairs& expected); + +static bool +fail (const char* manifest); + +int +main () +{ + // Whitespaces and comments. + // + assert (parse (" \t", {{"",""}})); + assert (parse (" \t\n \n\n", {{"",""}})); + assert (parse ("# one\n #two", {{"",""}})); + + // Test encountering eos at various points. + // + assert (parse ("", {{"",""}})); + assert (parse (" ", {{"",""}})); + assert (parse ("\n", {{"",""}})); + assert (fail ("a")); + assert (parse (":1\na:", {{"","1"},{"a", ""},{"",""},{"",""}})); + + // Invalid manifests. + // + assert (fail ("a:")); // format version pair expected + assert (fail (":")); // format version value expected + assert (fail (":9")); // unsupported format version + assert (fail ("a")); // ':' expected after name + assert (fail ("a b")); // ':' expected after name + assert (fail ("a\tb")); // ':' expected after name + assert (fail ("a\nb")); // ':' expected after name + assert (fail (":1\na:b\n:9")); // unsupported format version + + // Empty manifest. + // + assert (parse (":1", {{"","1"},{"",""},{"",""}})); + assert (parse (" \t :1", {{"","1"},{"",""},{"",""}})); + assert (parse (" \t : 1", {{"","1"},{"",""},{"",""}})); + assert (parse (" \t : 1 ", {{"","1"},{"",""},{"",""}})); + assert (parse (":1\n", {{"","1"},{"",""},{"",""}})); + assert (parse (":1 \n", {{"","1"},{"",""},{"",""}})); + + // Single manifest. + // + assert (parse (":1\na:x", {{"","1"},{"a", "x"},{"",""},{"",""}})); + assert (parse (":1\na:x\n", {{"","1"},{"a","x"},{"",""},{"",""}})); + assert (parse (":1\na:x\nb:y", + {{"","1"},{"a","x"},{"b","y"},{"",""},{"",""}})); + assert (parse (":1\na:x\n\tb : y\n #comment", + {{"","1"},{"a","x"},{"b","y"},{"",""},{"",""}})); + + // Multiple manifests. + // + assert (parse (":1\na:x\n:\nb:y", + {{"","1"},{"a", "x"},{"",""}, + {"","1"},{"b", "y"},{"",""},{"",""}})); + assert (parse (":1\na:x\n:1\nb:y", + {{"","1"},{"a", "x"},{"",""}, + {"","1"},{"b", "y"},{"",""},{"",""}})); + assert (parse (":1\na:x\n:\nb:y\n:\nc:z\n", + {{"","1"},{"a", "x"},{"",""}, + {"","1"},{"b", "y"},{"",""}, + {"","1"},{"c", "z"},{"",""},{"",""}})); + + // Name parsing. + // + assert (parse (":1\nabc:", {{"","1"},{"abc",""},{"",""},{"",""}})); + assert (parse (":1\nabc :", {{"","1"},{"abc",""},{"",""},{"",""}})); + assert (parse (":1\nabc\t:", {{"","1"},{"abc",""},{"",""},{"",""}})); + + // Simple value parsing. + // + assert (parse (":1\na: \t xyz \t ", {{"","1"},{"a","xyz"},{"",""},{"",""}})); + + // Simple value escaping. + // + assert (parse (":1\na:x\\", {{"","1"},{"a","x"},{"",""},{"",""}})); + assert (parse (":1\na:x\\\ny", {{"","1"},{"a","xy"},{"",""},{"",""}})); + assert (parse (":1\na:x\\\\\nb:", + {{"","1"},{"a","x\\"},{"b",""},{"",""},{"",""}})); + assert (parse (":1\na:x\\\\\\\nb:", + {{"","1"},{"a","x\\\\"},{"b",""},{"",""},{"",""}})); + + // Simple value literal newline. + // + assert (parse (":1\na:x\\\n\\", + {{"","1"},{"a","x\n"},{"",""},{"",""}})); + assert (parse (":1\na:x\\\n\\\ny", + {{"","1"},{"a","x\ny"},{"",""},{"",""}})); + assert (parse (":1\na:x\\\n\\\ny\\\n\\\nz", + {{"","1"},{"a","x\ny\nz"},{"",""},{"",""}})); + + // Multi-line value parsing. + // + assert (parse (":1\na:\\", {{"","1"},{"a", ""},{"",""},{"",""}})); + assert (parse (":1\na:\\\n", {{"","1"},{"a", ""},{"",""},{"",""}})); + assert (parse (":1\na:\\x", {{"","1"},{"a", "\\x"},{"",""},{"",""}})); + assert (parse (":1\na:\\\n\\", {{"","1"},{"a", ""},{"",""},{"",""}})); + assert (parse (":1\na:\\\n\\\n", {{"","1"},{"a", ""},{"",""},{"",""}})); + assert (parse (":1\na:\\\n\\x\n\\", + {{"","1"},{"a", "\\x"},{"",""},{"",""}})); + assert (parse (":1\na:\\\nx\ny", {{"","1"},{"a", "x\ny"},{"",""},{"",""}})); + assert (parse (":1\na:\\\n \n#\t\n\\", + {{"","1"},{"a", " \n#\t"},{"",""},{"",""}})); + assert (parse (":1\na:\\\n\n\n\\", {{"","1"},{"a", "\n"},{"",""},{"",""}})); + + // Multi-line value escaping. + // + assert (parse (":1\na:\\\nx\\", + {{"","1"},{"a","x"},{"",""},{"",""}})); + assert (parse (":1\na:\\\nx\\\ny\n\\", + {{"","1"},{"a","xy"},{"",""},{"",""}})); + assert (parse (":1\na:\\\nx\\\\\n\\\nb:", + {{"","1"},{"a","x\\"},{"b",""},{"",""},{"",""}})); + assert (parse (":1\na:\\\nx\\\\\\\n\\\nb:", + {{"","1"},{"a","x\\\\"},{"b",""},{"",""},{"",""}})); +} + +static std::ostream& +operator<< (std::ostream& os, const pairs& ps) +{ + os << '{'; + + bool f (true); + for (const auto& p: ps) + os << (f ? (f = false, "") : ",") + << '{' << p.first << ',' << p.second << '}'; + + os << '}'; + return os; +} + +static pairs +parse (const char* m) +{ + istringstream is (m); + is.exceptions (istream::failbit | istream::badbit); + manifest_parser p (is, ""); + + pairs r; + + for (bool eom (true), eos (false); !eos; ) + { + auto nv (p.next ()); + + if (nv.name.empty () && nv.value.empty ()) // End pair. + { + eos = eom; + eom = true; + } + else + eom = false; + + r.emplace_back (nv.name, nv.value); // move + } + + return r; +} + +static bool +parse (const char* m, const pairs& e) +{ + pairs r (parse (m)); + + if (r != e) + { + cerr << "actual: " << r << endl + << "expect: " << e << endl; + + return false; + } + + return true; +} + +static bool +fail (const char* m) +{ + try + { + pairs r (parse (m)); + cerr << "nofail: " << r << endl; + return false; + } + catch (const manifest_parsing& e) + { + cerr << e.what () << endl; + } + + return true; +} -- cgit v1.1