aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2016-09-29 21:54:14 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2016-09-29 23:28:03 +0300
commit25a9484378ddaae9602ec54532cdc03b1f1924ef (patch)
tree7aafb613337eb6c6aee4fef78b8345405c4d7f70 /tests
parentf4f6d906733027a7bd802e035b3e9852db7be967 (diff)
Add manifest_parser and manifest_serializer
Diffstat (limited to 'tests')
-rw-r--r--tests/buildfile3
-rw-r--r--tests/manifest-parser/buildfile7
-rw-r--r--tests/manifest-parser/driver.cxx207
-rw-r--r--tests/manifest-roundtrip/buildfile8
-rw-r--r--tests/manifest-roundtrip/driver.cxx52
-rw-r--r--tests/manifest-roundtrip/manifest32
-rw-r--r--tests/manifest-serializer/buildfile7
-rw-r--r--tests/manifest-serializer/driver.cxx245
8 files changed, 560 insertions, 1 deletions
diff --git a/tests/buildfile b/tests/buildfile
index fd2589a..84a17aa 100644
--- a/tests/buildfile
+++ b/tests/buildfile
@@ -2,7 +2,8 @@
# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
-d = base64/ cpfile/ dir-iterator/ fdstream/ link/ pager/ path/ prefix-map/ \
+d = base64/ cpfile/ dir-iterator/ fdstream/ link/ manifest-parser/ \
+ manifest-serializer/ manifest-roundtrip/ pager/ path/ prefix-map/ \
process/ sha256/ strcase/ timestamp/ triplet/
./: $d
diff --git a/tests/manifest-parser/buildfile b/tests/manifest-parser/buildfile
new file mode 100644
index 0000000..9173bdb
--- /dev/null
+++ b/tests/manifest-parser/buildfile
@@ -0,0 +1,7 @@
+# file : tests/manifest-parser/buildfile
+# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+exe{driver}: cxx{driver} ../../butl/lib{butl}
+
+include ../../butl/
diff --git a/tests/manifest-parser/driver.cxx b/tests/manifest-parser/driver.cxx
new file mode 100644
index 0000000..bab60a8
--- /dev/null
+++ b/tests/manifest-parser/driver.cxx
@@ -0,0 +1,207 @@
+// file : tests/manifest-parser/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <vector>
+#include <string>
+#include <utility> // pair
+#include <cassert>
+#include <sstream>
+#include <iostream>
+
+#include <butl/manifest-parser>
+
+using namespace std;
+using namespace butl;
+
+using pairs = vector<pair<string, string>>;
+
+static bool
+test (const char* manifest, const pairs& expected);
+
+static bool
+fail (const char* manifest);
+
+int
+main ()
+{
+ // Whitespaces and comments.
+ //
+ assert (test (" \t", {{"",""}}));
+ assert (test (" \t\n \n\n", {{"",""}}));
+ assert (test ("# one\n #two", {{"",""}}));
+
+ // Test encountering eos at various points.
+ //
+ assert (test ("", {{"",""}}));
+ assert (test (" ", {{"",""}}));
+ assert (test ("\n", {{"",""}}));
+ assert (fail ("a"));
+ assert (test (":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 (test (":1", {{"","1"},{"",""},{"",""}}));
+ assert (test (" \t :1", {{"","1"},{"",""},{"",""}}));
+ assert (test (" \t : 1", {{"","1"},{"",""},{"",""}}));
+ assert (test (" \t : 1 ", {{"","1"},{"",""},{"",""}}));
+ assert (test (":1\n", {{"","1"},{"",""},{"",""}}));
+ assert (test (":1 \n", {{"","1"},{"",""},{"",""}}));
+
+ // Single manifest.
+ //
+ assert (test (":1\na:x", {{"","1"},{"a", "x"},{"",""},{"",""}}));
+ assert (test (":1\na:x\n", {{"","1"},{"a","x"},{"",""},{"",""}}));
+ assert (test (":1\na:x\nb:y",
+ {{"","1"},{"a","x"},{"b","y"},{"",""},{"",""}}));
+ assert (test (":1\na:x\n\tb : y\n #comment",
+ {{"","1"},{"a","x"},{"b","y"},{"",""},{"",""}}));
+
+ // Multiple manifests.
+ //
+ assert (test (":1\na:x\n:\nb:y",
+ {{"","1"},{"a", "x"},{"",""},
+ {"","1"},{"b", "y"},{"",""},{"",""}}));
+ assert (test (":1\na:x\n:1\nb:y",
+ {{"","1"},{"a", "x"},{"",""},
+ {"","1"},{"b", "y"},{"",""},{"",""}}));
+ assert (test (":1\na:x\n:\nb:y\n:\nc:z\n",
+ {{"","1"},{"a", "x"},{"",""},
+ {"","1"},{"b", "y"},{"",""},
+ {"","1"},{"c", "z"},{"",""},{"",""}}));
+
+ // Name parsing.
+ //
+ assert (test (":1\nabc:", {{"","1"},{"abc",""},{"",""},{"",""}}));
+ assert (test (":1\nabc :", {{"","1"},{"abc",""},{"",""},{"",""}}));
+ assert (test (":1\nabc\t:", {{"","1"},{"abc",""},{"",""},{"",""}}));
+
+ // Simple value parsing.
+ //
+ assert (test (":1\na: \t xyz \t ", {{"","1"},{"a","xyz"},{"",""},{"",""}}));
+
+ // Simple value escaping.
+ //
+ assert (test (":1\na:x\\", {{"","1"},{"a","x"},{"",""},{"",""}}));
+ assert (test (":1\na:x\\\ny", {{"","1"},{"a","xy"},{"",""},{"",""}}));
+ assert (test (":1\na:x\\\\\nb:",
+ {{"","1"},{"a","x\\"},{"b",""},{"",""},{"",""}}));
+ assert (test (":1\na:x\\\\\\\nb:",
+ {{"","1"},{"a","x\\\\"},{"b",""},{"",""},{"",""}}));
+
+ // Simple value literal newline.
+ //
+ assert (test (":1\na:x\\\n\\", {{"","1"},{"a","x\n"},{"",""},{"",""}}));
+ assert (test (":1\na:x\\\n\\\ny", {{"","1"},{"a","x\ny"},{"",""},{"",""}}));
+ assert (test (":1\na:x\\\n\\\ny\\\n\\\nz",
+ {{"","1"},{"a","x\ny\nz"},{"",""},{"",""}}));
+
+ // Multi-line value parsing.
+ //
+ assert (test (":1\na:\\", {{"","1"},{"a", ""},{"",""},{"",""}}));
+ assert (test (":1\na:\\\n", {{"","1"},{"a", ""},{"",""},{"",""}}));
+ assert (test (":1\na:\\x", {{"","1"},{"a", "\\x"},{"",""},{"",""}}));
+ assert (test (":1\na:\\\n\\", {{"","1"},{"a", ""},{"",""},{"",""}}));
+ assert (test (":1\na:\\\n\\\n", {{"","1"},{"a", ""},{"",""},{"",""}}));
+ assert (test (":1\na:\\\n\\x\n\\",
+ {{"","1"},{"a", "\\x"},{"",""},{"",""}}));
+ assert (test (":1\na:\\\nx\ny", {{"","1"},{"a", "x\ny"},{"",""},{"",""}}));
+ assert (test (":1\na:\\\n \n#\t\n\\",
+ {{"","1"},{"a", " \n#\t"},{"",""},{"",""}}));
+ assert (test (":1\na:\\\n\n\n\\", {{"","1"},{"a", "\n"},{"",""},{"",""}}));
+
+ // Multi-line value escaping.
+ //
+ assert (test (":1\na:\\\nx\\", {{"","1"},{"a","x"},{"",""},{"",""}}));
+ assert (test (":1\na:\\\nx\\\ny\n\\",
+ {{"","1"},{"a","xy"},{"",""},{"",""}}));
+ assert (test (":1\na:\\\nx\\\\\n\\\nb:",
+ {{"","1"},{"a","x\\"},{"b",""},{"",""},{"",""}}));
+ assert (test (":1\na:\\\nx\\\\\\\n\\\nb:",
+ {{"","1"},{"a","x\\\\"},{"b",""},{"",""},{"",""}}));
+}
+
+static ostream&
+operator<< (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; )
+ {
+ manifest_name_value nv (p.next ());
+
+ if (nv.empty ()) // End pair.
+ {
+ eos = eom;
+ eom = true;
+ }
+ else
+ eom = false;
+
+ r.emplace_back (nv.name, nv.value); // move
+ }
+
+ return r;
+}
+
+static bool
+test (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;
+}
diff --git a/tests/manifest-roundtrip/buildfile b/tests/manifest-roundtrip/buildfile
new file mode 100644
index 0000000..78e5a08
--- /dev/null
+++ b/tests/manifest-roundtrip/buildfile
@@ -0,0 +1,8 @@
+# file : tests/manifest-roundtrip/buildfile
+# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+exe{driver}: cxx{driver} ../../butl/lib{butl}
+exe{driver}: test.roundtrip = manifest
+
+include ../../butl/
diff --git a/tests/manifest-roundtrip/driver.cxx b/tests/manifest-roundtrip/driver.cxx
new file mode 100644
index 0000000..e1ce5b8
--- /dev/null
+++ b/tests/manifest-roundtrip/driver.cxx
@@ -0,0 +1,52 @@
+// file : tests/manifest-roundtrip/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <cassert>
+#include <iostream>
+
+#include <butl/fdstream>
+#include <butl/manifest-parser>
+#include <butl/manifest-serializer>
+
+using namespace std;
+using namespace butl;
+
+int
+main (int argc, char* argv[])
+{
+ if (argc != 2)
+ {
+ cerr << "usage: " << argv[0] << " <file>" << endl;
+ return 1;
+ }
+
+ try
+ {
+ ifdstream ifs (argv[1]);
+ manifest_parser p (ifs, argv[1]);
+
+ stdout_fdmode (fdstream_mode::binary); // Write in binary mode.
+ manifest_serializer s (cout, "stdout");
+
+ for (bool eom (true), eos (false); !eos; )
+ {
+ manifest_name_value nv (p.next ());
+
+ if (nv.empty ()) // End pair.
+ {
+ eos = eom;
+ eom = true;
+ }
+ else
+ eom = false;
+
+ s.next (nv.name, nv.value);
+ }
+ }
+ catch (const exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/tests/manifest-roundtrip/manifest b/tests/manifest-roundtrip/manifest
new file mode 100644
index 0000000..23c2730
--- /dev/null
+++ b/tests/manifest-roundtrip/manifest
@@ -0,0 +1,32 @@
+: 1
+name: libbpkg
+version: 1.0.1
+summary: build2 package manager library
+license: MIT
+tags: c++, package, manager, bpkg
+description: A very very very very very very very very very very very very\
+ very very very very very very very very very very very very very very very\
+ very very long description.
+changes: \
+1.0.1
+ - Fixed a very very very very very very very very very very very very very\
+ very annoying bug.
+1.0.0
+ - Firts public release
+ - Lots of really cool features
+\
+url: http://www.codesynthesis.com/projects/libstudxml/
+email: build-users@codesynthesis.com; Public mailing list, posts by\
+ non-members are allowed but moderated.
+package-email: boris@codesynthesis.com; Direct email to the author.
+depends: libbutl
+depends: * build2
+depends: ?* bpkg
+requires: ?* linux | windows
+requires: c++11
+:
+path: c:\windows\\
+path: \
+
+c:\windows\\
+\
diff --git a/tests/manifest-serializer/buildfile b/tests/manifest-serializer/buildfile
new file mode 100644
index 0000000..0325323
--- /dev/null
+++ b/tests/manifest-serializer/buildfile
@@ -0,0 +1,7 @@
+# file : tests/manifest-serializer/buildfile
+# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+exe{driver}: cxx{driver} ../../butl/lib{butl}
+
+include ../../butl/
diff --git a/tests/manifest-serializer/driver.cxx b/tests/manifest-serializer/driver.cxx
new file mode 100644
index 0000000..250272d
--- /dev/null
+++ b/tests/manifest-serializer/driver.cxx
@@ -0,0 +1,245 @@
+// file : tests/manifest-serializer/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <vector>
+#include <string>
+#include <utility> // pair
+#include <cassert>
+#include <sstream>
+#include <iostream>
+
+#include <butl/manifest-serializer>
+
+using namespace std;
+using namespace butl;
+
+using pairs = vector<pair<string, string>>;
+
+static bool
+test (const pairs& manifest, const string& expected);
+
+static bool
+fail (const pairs& manifest);
+
+int
+main ()
+{
+ // Comments.
+ //
+ assert (test ({{"#", ""}}, "#\n"));
+ assert (test ({{"#", "x"}}, "# x\n"));
+ assert (test ({{"#", "x"},{"#", "y"},{"#", ""}}, "# x\n# y\n#\n"));
+ assert (fail ({{"",""},{"#", "x"}})); // serialization after eos
+
+ // Empty manifest stream.
+ //
+ assert (test ({}, ""));
+ assert (test ({{"",""}}, ""));
+
+ // Empty manifest.
+ //
+ assert (test ({{"","1"},{"",""},{"",""}}, ": 1\n"));
+ assert (test ({{"","1"},{"",""},{"","1"},{"",""},{"",""}}, ": 1\n:\n"));
+
+ // Invalid manifests.
+ //
+ assert (fail ({{"a",""}})); // format version pair expected
+ assert (fail ({{"","1"},{"",""},{"a",""}})); // format version pair expected
+ assert (fail ({{"","9"}})); // unsupported format version 9
+ assert (fail ({{"","1"},{"","x"}})); // non-empty value in end pair
+ assert (fail ({{"",""},{"","1"}})); // serialization after eos
+
+ // Single manifest.
+ //
+ assert (test ({{"","1"},{"a","x"},{"",""},{"",""}}, ": 1\na: x\n"));
+ assert (test ({{"","1"},{"a","x"},{"b","y"},{"",""},{"",""}},
+ ": 1\na: x\nb: y\n"));
+ assert (test ({{"","1"},{"#","c"},{"a","x"},{"",""},{"",""}},
+ ": 1\n# c\na: x\n"));
+
+ // Multiple manifests.
+ //
+ assert (test ({{"","1"},{"a","x"},{"",""},
+ {"","1"},{"b","y"},{"",""},{"",""}}, ": 1\na: x\n:\nb: y\n"));
+ assert (test ({{"","1"},{"a","x"},{"",""},
+ {"","1"},{"b","y"},{"",""},
+ {"","1"},{"c","z"},{"",""},{"",""}},
+ ": 1\na: x\n:\nb: y\n:\nc: z\n"));
+
+ // Invalid name.
+ //
+ assert (fail ({{"","1"},{"#a",""}}));
+ assert (fail ({{"","1"},{"a:b",""}}));
+ assert (fail ({{"","1"},{"a b",""}}));
+ assert (fail ({{"","1"},{"a\tb",""}}));
+ assert (fail ({{"","1"},{"a\n",""}}));
+
+ // Simple value.
+ //
+ assert (test ({{"","1"},{"a",""},{"",""},{"",""}}, ": 1\na:\n"));
+ assert (test ({{"","1"},{"a","x y z"},{"",""},{"",""}}, ": 1\na: x y z\n"));
+
+ // Long simple value (newline escaping).
+ //
+
+ // "Solid" text/hard break.
+ //
+ string l1 ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ "Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");
+
+ string e1 ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\\\n"
+ "Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\\\n"
+ "Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");
+
+ // Space too early/hard break.
+ //
+ string l2 ("x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "Yyyyyyyyyyyyyyyyy yyyyyyyyyyyyyyyyyyy"
+ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ "Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz z"
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");
+
+ string e2 ("x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\\\n"
+ "Yyyyyyyyyyyyyyyyy yyyyyyyyyyyyyyyyyyy"
+ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\\\n"
+ "Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz z"
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");
+
+ // Space/soft break.
+ //
+ string l3 ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxx"
+ " Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ " Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");
+
+ string e3 ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxx\\\n"
+ " Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyy\\\n"
+ " Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");
+
+ // Space with a better one/soft break.
+ //
+ string l4 ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxx xxxxxxxxx"
+ " Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ "yyyyyyyyyyyyyyyyyy yyyyyyyyyy"
+ " Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");
+
+ string e4 ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxx xxxxxxxxx\\\n"
+ " Yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
+ "yyyyyyyyyyyyyyyyyy yyyyyyyyyy\\\n"
+ " Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
+ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");
+
+ assert (test ({{"","1"},{"a",l1},{"",""},{"",""}}, ": 1\na: " + e1 + "\n"));
+ assert (test ({{"","1"},{"a",l2},{"",""},{"",""}}, ": 1\na: " + e2 + "\n"));
+ assert (test ({{"","1"},{"a",l3},{"",""},{"",""}}, ": 1\na: " + e3 + "\n"));
+ assert (test ({{"","1"},{"a",l4},{"",""},{"",""}}, ": 1\na: " + e4 + "\n"));
+
+
+ // Multi-line value.
+ //
+ string n ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+ assert (test ({{"","1"},{n,"x"},{"",""},{"",""}},
+ ": 1\n" + n + ": \\\nx\n\\\n"));
+ assert (test ({{"","1"},{"a","\n"},{"",""},{"",""}},
+ ": 1\na: \\\n\n\n\\\n"));
+ assert (test ({{"","1"},{"a","\n\n"},{"",""},{"",""}},
+ ": 1\na: \\\n\n\n\n\\\n"));
+ assert (test ({{"","1"},{"a","\nx\n"},{"",""},{"",""}},
+ ": 1\na: \\\n\nx\n\n\\\n"));
+ assert (test ({{"","1"},{"a","x\ny\nz"},{"",""},{"",""}},
+ ": 1\na: \\\nx\ny\nz\n\\\n"));
+ assert (test ({{"","1"},{"a"," x"},{"",""},{"",""}},
+ ": 1\na: \\\n x\n\\\n"));
+ assert (test ({{"","1"},{"a","x "},{"",""},{"",""}},
+ ": 1\na: \\\nx \n\\\n"));
+ assert (test ({{"","1"},{"a"," x "},{"",""},{"",""}},
+ ": 1\na: \\\n x \n\\\n"));
+
+ // Extra three x's are for the leading name part ("a: ") that we
+ // don't have.
+ //
+ assert (test ({{"","1"},{"a","\nxxx" + l1},{"",""},{"",""}},
+ ": 1\na: \\\n\nxxx" + e1 + "\n\\\n"));
+ assert (test ({{"","1"},{"a","\nxxx" + l2},{"",""},{"",""}},
+ ": 1\na: \\\n\nxxx" + e2 + "\n\\\n"));
+ assert (test ({{"","1"},{"a","\nxxx" + l3},{"",""},{"",""}},
+ ": 1\na: \\\n\nxxx" + e3 + "\n\\\n"));
+ assert (test ({{"","1"},{"a","\nxxx" + l4},{"",""},{"",""}},
+ ": 1\na: \\\n\nxxx" + e4 + "\n\\\n"));
+
+ // Backslash escaping (simple and multi-line).
+ //
+ assert (test ({{"","1"},{"a","c:\\"},{"",""},{"",""}},
+ ": 1\na: c:\\\\\n"));
+ assert (test ({{"","1"},{"a","c:\\\nd:\\"},{"",""},{"",""}},
+ ": 1\na: \\\nc:\\\\\nd:\\\\\n\\\n"));
+}
+
+static string
+serialize (const pairs& m)
+{
+ ostringstream os;
+ os.exceptions (istream::failbit | istream::badbit);
+ manifest_serializer s (os, "");
+
+ for (const auto& p: m)
+ {
+ if (p.first != "#")
+ s.next (p.first, p.second);
+ else
+ s.comment (p.second);
+ }
+
+ return os.str ();
+}
+
+static bool
+test (const pairs& m, const string& e)
+{
+ string r (serialize (m));
+
+ if (r != e)
+ {
+ cerr << "actual:" << endl << "'" << r << "'"<< endl
+ << "expect:" << endl << "'" << e << "'"<< endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+fail (const pairs& m)
+{
+ try
+ {
+ string r (serialize (m));
+ cerr << "nofail: " << r << endl;
+ return false;
+ }
+ catch (const manifest_serialization& e)
+ {
+ //cerr << e.what () << endl;
+ }
+
+ return true;
+}