aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/buildfile5
-rw-r--r--tests/buildtab/buildfile9
-rw-r--r--tests/buildtab/driver.cxx48
-rw-r--r--tests/buildtab/testscript63
-rw-r--r--tests/manifest/buildfile9
-rw-r--r--tests/manifest/driver.cxx71
-rw-r--r--tests/manifest/machine.test157
-rw-r--r--tests/manifest/result-request.test92
-rw-r--r--tests/manifest/result.test230
-rw-r--r--tests/manifest/task-request.test96
-rw-r--r--tests/manifest/task-response.test114
-rw-r--r--tests/manifest/task.test260
-rw-r--r--tests/variable/buildfile9
-rw-r--r--tests/variable/driver.cxx61
-rw-r--r--tests/variable/testscript55
16 files changed, 1280 insertions, 0 deletions
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..10e73ec
--- /dev/null
+++ b/tests/buildfile
@@ -0,0 +1,5 @@
+# file : tests/buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+./: */
diff --git a/tests/buildtab/buildfile b/tests/buildtab/buildfile
new file mode 100644
index 0000000..f156dbf
--- /dev/null
+++ b/tests/buildtab/buildfile
@@ -0,0 +1,9 @@
+# file : tests/buildtab/buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+import libs += libbutl%lib{butl}
+
+exe{driver}: cxx{driver} ../../bbot/lib{bbot} $libs test{testscript}
+
+include ../../bbot/
diff --git a/tests/buildtab/driver.cxx b/tests/buildtab/driver.cxx
new file mode 100644
index 0000000..c3e3a60
--- /dev/null
+++ b/tests/buildtab/driver.cxx
@@ -0,0 +1,48 @@
+// file : tests/buildtab/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <ios> // ios::failbit, ios::badbit
+#include <cassert>
+#include <iostream>
+
+#include <butl/utility> // operator<<(ostream,exception)
+
+#include <bbot/build-config>
+
+using namespace std;
+using namespace butl;
+using namespace bbot;
+
+// Usage: argv[0]
+//
+// Read and parse buildtab from STDIN and serialize the resulted build
+// configuration to STDOUT.
+//
+int
+main ()
+try
+{
+ cin.exceptions (ios::failbit | ios::badbit);
+ cout.exceptions (ios::failbit | ios::badbit);
+
+ for (const auto& c: parse_buildtab (cin, "cin"))
+ {
+ cout << c.machine_pattern << ' ' << c.name;
+
+ if (c.target)
+ cout << ' ' << *c.target;
+
+ for (const auto& v: c.vars)
+ cout << ' ' << v;
+
+ cout << '\n';
+ }
+
+ return 0;
+}
+catch (const tab_parsing& e)
+{
+ cerr << e << endl;
+ return 1;
+}
diff --git a/tests/buildtab/testscript b/tests/buildtab/testscript
new file mode 100644
index 0000000..5bf6b24
--- /dev/null
+++ b/tests/buildtab/testscript
@@ -0,0 +1,63 @@
+# file : tests/buildtab/testscript
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+: valid
+:
+: Roundtrip buildtab.
+:
+{
+ : all-fileds-combinations
+ :
+ $* <<EOF >>EOF
+ windows*-vc_14* windows-vc_14
+ windows*-vc_14* windows-vc_14-32 i686-microsoft-win32-msvc14.0
+ windows*-vc_14* windows-vc_14debug config.cc.coptions=/Z7 config.cc.loptions=/DEBUG
+ windows*-vc_14* windows-vc_14-32-debug i686-microsoft-win32-msvc14.0 config.cc.coptions=/Z7 config.cc.loptions=/DEBUG
+ EOF
+
+ : empty-lines
+ :
+ $* <<EOI >>EOO
+
+ windows*-vc_14* windows-vc_14-32-debug
+ # abc
+ EOI
+ windows*-vc_14* windows-vc_14-32-debug
+ EOO
+}
+
+: parse-errors
+:
+{
+ : no-name
+ :
+ $* <<EOI 2>>EOE == 1
+ windows*-vc_14*
+ EOI
+ cin:1:16: error: no configuration name found
+ EOE
+
+ : invalid-target
+ :
+ $* <<EOI 2>>EOE == 1
+ windows*-vc_14* windows-vc_14-32 microsoft
+ EOI
+ cin:1:34: error: missing cpu
+ EOE
+
+ : invalid-var
+ :
+ $* <<EOI 2>>EOE == 1
+ windows*-vc_14* windows-vc_14-32 config.cc.coptions="/Z7
+ EOI
+ cin:1:57: error: unterminated quoted string
+ EOE
+
+ : dup-config-name
+ :
+ $* <<EOI 2>'cin:2:17: error: duplicate configuration name' == 1
+ windows*-vc_14* windows-vc_14-32
+ windows*-vc_14* windows-vc_14-32 i686-microsoft-win32-msvc14.0
+ EOI
+}
diff --git a/tests/manifest/buildfile b/tests/manifest/buildfile
new file mode 100644
index 0000000..002dbb0
--- /dev/null
+++ b/tests/manifest/buildfile
@@ -0,0 +1,9 @@
+# file : tests/manifest/buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+import libs += libbutl%lib{butl}
+
+exe{driver}: cxx{driver} ../../bbot/lib{bbot} $libs test{*}
+
+include ../../bbot/
diff --git a/tests/manifest/driver.cxx b/tests/manifest/driver.cxx
new file mode 100644
index 0000000..0f24c8a
--- /dev/null
+++ b/tests/manifest/driver.cxx
@@ -0,0 +1,71 @@
+// file : tests/manifest/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <ios> // ios_base::failbit, ios_base::badbit
+#include <string>
+#include <cassert>
+#include <iostream>
+
+#include <butl/utility> // operator<<(ostream,exception)
+#include <butl/manifest-parser>
+#include <butl/manifest-serializer>
+
+#include <bbot/manifest>
+
+using namespace std;
+using namespace butl;
+using namespace bbot;
+
+// Usage: argv[0] (-m|-t|-r|-tq|-ts|-rq)
+//
+// Read and parse manifest from STDIN and serialize it to STDOUT. The
+// following options specify the manifest type.
+//
+// -m parse machine manifest
+// -t parse task manifest
+// -r parse result manifest
+// -tq parse task request manifest
+// -ts parse task response manifest
+// -rq parse result request manifest
+//
+int
+main (int argc, char* argv[])
+try
+{
+ assert (argc == 2);
+ string opt (argv[1]);
+
+ cin.exceptions (ios_base::failbit | ios_base::badbit);
+ cout.exceptions (ios_base::failbit | ios_base::badbit);
+
+ manifest_parser p (cin, "stdin");
+ manifest_serializer s (cout, "stdout");
+
+ if (opt == "-m")
+ machine_manifest (p).serialize (s);
+ else if (opt == "-t")
+ task_manifest (p).serialize (s);
+ else if (opt == "-r")
+ result_manifest (p).serialize (s);
+ else if (opt == "-tq")
+ task_request_manifest (p).serialize (s);
+ else if (opt == "-ts")
+ task_response_manifest (p).serialize (s);
+ else if (opt == "-rq")
+ result_request_manifest (p).serialize (s);
+ else
+ assert (false);
+
+ return 0;
+}
+catch (const manifest_parsing& e)
+{
+ cerr << e << endl;
+ return 1;
+}
+catch (const manifest_serialization& e)
+{
+ cerr << e << endl;
+ return 1;
+}
diff --git a/tests/manifest/machine.test b/tests/manifest/machine.test
new file mode 100644
index 0000000..b775a79
--- /dev/null
+++ b/tests/manifest/machine.test
@@ -0,0 +1,157 @@
+# file : tests/manifest/machine.test
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+test.options += -m
+
+: valid
+:
+: Roundtrip the machine manifest.
+:
+{
+ : vm
+ :
+ $* <<EOF >>EOF
+ : 1
+ id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ name: windows_10-msvc_14
+ type: vm
+ summary: Windows 10 build 1607 with VC 14 update 3
+ EOF
+
+ : container
+ :
+ $* <<EOF >>EOF
+ : 1
+ id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ name: windows_10-msvc_14
+ type: container
+ summary: Windows 10 build 1607 with VC 14 update 3
+ EOF
+}
+
+: multiple
+:
+$* <<EOI 2>'stdin:6:1: error: single machine manifest expected' == 1
+: 1
+id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+name: windows_10-msvc_14
+type: vm
+summary: Windows 10 build 1607 with VC 14 update 3
+:
+EOI
+
+: redefinition
+:
+{
+ : id
+ :
+ $* <<EOI 2>'stdin:3:1: error: machine id redefinition' == 1
+ : 1
+ id: 123
+ id: 123
+ EOI
+
+ : name
+ :
+ $* <<EOI 2>'stdin:3:1: error: machine name redefinition' == 1
+ : 1
+ name: windows
+ name: windows
+ EOI
+
+ : type
+ :
+ $* <<EOI 2>'stdin:3:1: error: machine type redefinition' == 1
+ : 1
+ type: vm
+ type: vm
+ EOI
+
+ : summary
+ :
+ $* <<EOI 2>'stdin:3:1: error: machine summary redefinition' == 1
+ : 1
+ summary: Windows
+ summary: Windows
+ EOI
+}
+
+: empty
+:
+{
+ : id
+ :
+ $* <<EOI 2>'stdin:2:4: error: empty machine id' == 1
+ : 1
+ id:
+ EOI
+
+ : name
+ :
+ $* <<EOI 2>'stdin:2:6: error: empty machine name' == 1
+ : 1
+ name:
+ EOI
+
+ : summary
+ :
+ $* <<EOI 2>'stdin:2:9: error: empty machine summary' == 1
+ : 1
+ summary:
+ EOI
+}
+
+: invalid-type
+:
+$* <<EOI 2>'stdin:2:7: error: invalid machine type' == 1
+: 1
+type: unknown
+EOI
+
+: unknown-name
+:
+$* <<EOI 2>"stdin:2:1: error: unknown name 'x' in machine manifest" == 1
+: 1
+x:
+EOI
+
+: missed
+:
+{
+ : id
+ :
+ $* <<EOI 2>'stdin:5:1: error: no machine id specified' == 1
+ : 1
+ name: windows
+ type: vm
+ summary: Windows
+ EOI
+
+ : name
+ :
+ $* <<EOI 2>'stdin:5:1: error: no machine name specified' == 1
+ : 1
+ id: 123
+ type: vm
+ summary: Windows
+ EOI
+
+ : type
+ :
+ $* <<EOI 2>'stdin:5:1: error: no machine type specified' == 1
+ : 1
+ id: 123
+ name: windows
+ summary: Windows
+ EOI
+
+ : summary
+ :
+ $* <<EOI 2>'stdin:5:1: error: no machine summary specified' == 1
+ : 1
+ id: 123
+ name: windows
+ type: vm
+ EOI
+}
diff --git a/tests/manifest/result-request.test b/tests/manifest/result-request.test
new file mode 100644
index 0000000..8cf64b9
--- /dev/null
+++ b/tests/manifest/result-request.test
@@ -0,0 +1,92 @@
+# file : tests/manifest/result-request.test
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+test.options += -rq
+
+: valid
+:
+: Roundtrip the result request manifest.
+:
+{
+ $* <<EOF >>EOF
+ : 1
+ session: abcd
+ challenge: xyz
+ :
+ name: libfoo
+ version: 1.0
+ status: error
+ EOF
+}
+
+: redefinition
+:
+{
+ : session
+ :
+ $* <<EOI 2>'stdin:3:1: error: result request session redefinition' == 1
+ : 1
+ session: abcd
+ session: abcd
+ EOI
+
+ : challenge
+ :
+ $* <<EOI 2>'stdin:3:1: error: result request challenge redefinition' == 1
+ : 1
+ challenge: xyz
+ challenge: xyz
+ EOI
+}
+
+: empty
+:
+{
+ : session
+ :
+ $* <<EOI 2>'stdin:2:9: error: empty result request session' == 1
+ : 1
+ session:
+ EOI
+
+ : challenge
+ :
+ $* <<EOI 2>'stdin:2:11: error: empty result request challenge' == 1
+ : 1
+ challenge:
+ EOI
+}
+
+: unknown-name
+:
+$* <<EOI 2>"stdin:2:1: error: unknown name 'x' in result request manifest" == 1
+: 1
+x:
+EOI
+
+: missed
+:
+{
+ : session
+ :
+ $* <<EOI 2>'stdin:3:1: error: no result request session specified' == 1
+ : 1
+ challenge: xyz
+ EOI
+
+ : challenge
+ :
+ $* <<EOI 2>'stdin:3:1: error: no result request challenge specified' == 1
+ : 1
+ session: abc
+ EOI
+
+ : result
+ :
+ $* <<EOI 2>'stdin:4:1: error: result manifest expected' == 1
+ : 1
+ session: abc
+ challenge: xyz
+ EOI
+}
diff --git a/tests/manifest/result.test b/tests/manifest/result.test
new file mode 100644
index 0000000..52964e0
--- /dev/null
+++ b/tests/manifest/result.test
@@ -0,0 +1,230 @@
+# file : tests/manifest/result.test
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+test.options += -r
+
+: valid
+:
+: Roundtrip the result manifest.
+:
+{
+ : test-error
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 1.0
+ status: error
+ configure-status: success
+ update-status: warning
+ test-status: error
+ configure-log: \
+ conf line 1
+ conf line 2
+ \
+ update-log: \
+ update line 1
+ update line 2
+ \
+ test-log: \
+ test line 1
+ test line 2
+ \
+ EOF
+
+ : update-error
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 1.0
+ status: error
+ configure-status: warning
+ update-status: error
+ configure-log: \
+ conf line 1
+ conf line 2
+ \
+ update-log: \
+ update line 1
+ update line 2
+ \
+ EOF
+
+ : early-abort
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 1.0
+ status: abort
+ EOF
+}
+
+: redefinition
+:
+{
+ : name
+ :
+ $* <<EOI 2>'stdin:3:1: error: result package name redefinition' == 1
+ : 1
+ name: libfoo
+ name: libfoo
+ EOI
+
+ : version
+ :
+ $* <<EOI 2>'stdin:3:1: error: result package version redefinition' == 1
+ : 1
+ version: 1.0
+ version: 1.0
+ EOI
+
+ : status
+ :
+ $* <<EOI 2>'stdin:3:1: error: result status redefinition' == 1
+ : 1
+ status: success
+ status: error
+ EOI
+
+ : configure-status
+ :
+ $* <<EOI 2>'stdin:4:1: error: result configure-status redefinition' == 1
+ : 1
+ status: success
+ configure-status: success
+ configure-status: abnormal
+ EOI
+
+ : configure-log
+ :
+ $* <<EOI 2>'stdin:5:1: error: unexpected configure-log' == 1
+ : 1
+ status: success
+ configure-status: success
+ configure-log: configured
+ configure-log: configured
+ EOI
+}
+
+: invalid
+:
+{
+ : name-empty
+ :
+ $* <<EOI 2>'stdin:2:6: error: empty result package name' == 1
+ : 1
+ name:
+ EOI
+
+ : version
+ :
+ {
+ : empty
+ :
+ $* <<EOI 2>'stdin:2:9: error: invalid result package version: unexpected end' == 1
+ : 1
+ version:
+ EOI
+
+ : release
+ :
+ $* <<EOI 2>'stdin:2:10: error: invalid result package version release' == 1
+ : 1
+ version: 1.2.3-
+ EOI
+ }
+
+ : status
+ :
+ $* <<EOI 2>'stdin:2:9: error: invalid result status' == 1
+ : 1
+ status: alert
+ EOI
+
+ : configure-status
+ :
+ $* <<EOI 2>'stdin:3:19: error: invalid configure-status' == 1
+ : 1
+ status: abort
+ configure-status: alert
+ EOI
+
+ : order
+ :
+ {
+ : op-status-before-status
+ :
+ $* <<EOI 2>'stdin:2:1: error: result status must appear first' == 1
+ : 1
+ configure-status: success
+ EOI
+
+ : op-status-after-log
+ :
+ $* <<EOI 2>'stdin:5:1: error: update-status after operations logs' == 1
+ : 1
+ status: success
+ configure-status: success
+ configure-log: log
+ update-status: error
+ EOI
+
+ : wrong-op-log
+ :
+ $* <<EOI 2>'stdin:5:1: error: configure-log is expected' == 1
+ : 1
+ status: success
+ configure-status: success
+ update-status: error
+ update-log: log
+ EOI
+ }
+}
+
+: unknown-name
+:
+$* <<EOI 2>"stdin:2:1: error: unknown name 'full-logs' in result manifest" == 1
+: 1
+full-logs: log
+EOI
+
+: missed
+:
+{
+ : name
+ :
+ $* <<EOI 2>'stdin:4:1: error: no result package name specified' == 1
+ : 1
+ version: 1.0
+ status: success
+ EOI
+
+ : version
+ :
+ $* <<EOI 2>'stdin:4:1: error: no result package version specified' == 1
+ : 1
+ name: libfoo
+ status: success
+ EOI
+
+ : status
+ :
+ $* <<EOI 2>'stdin:4:1: error: no result status specified' == 1
+ : 1
+ name: libfoo
+ version: 1.0
+ EOI
+
+ : configure-log
+ :
+ $* <<EOI 2>'stdin:6:1: error: no result configure-log specified' == 1
+ : 1
+ name: libfoo
+ version: 1.0
+ status: error
+ configure-status: error
+ EOI
+}
diff --git a/tests/manifest/task-request.test b/tests/manifest/task-request.test
new file mode 100644
index 0000000..2e3fb75
--- /dev/null
+++ b/tests/manifest/task-request.test
@@ -0,0 +1,96 @@
+# file : tests/manifest/task-request.test
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+test.options += -tq
+
+: valid
+:
+: Roundtrip the task request manifest.
+:
+$* <<EOF >>EOF
+: 1
+agent: upsa
+fingerprint: 1105fb394ee870adb154b7abfbbae5755df7dcef6c81db34e8d1b68d2653734e
+:
+id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+name: windows_10-msvc_14
+summary: Windows 10 build 1607 with VC 14 update 3
+EOF
+
+: redefinition
+:
+{
+ : agent
+ :
+ $* <<EOI 2>'stdin:3:1: error: task request agent redefinition' == 1
+ : 1
+ agent: upsa
+ agent: upsa
+ EOI
+
+ : fingerprint
+ :
+ $* <<EOI 2>'stdin:3:1: error: task request fingerprint redefinition' == 1
+ : 1
+ fingerprint: 1105fb394ee870adb154b7abfbbae5755df7dcef6c81db34e8d1b68d2653734e
+ fingerprint: 1105fb394ee870adb154b7abfbbae5755df7dcef6c81db34e8d1b68d2653734e
+ EOI
+}
+
+: empty
+:
+{
+ : agent
+ :
+ $* <<EOI 2>'stdin:2:7: error: empty task request agent' == 1
+ : 1
+ agent:
+ EOI
+}
+
+: invalid-fingerprint
+:
+$* <<EOI 2>'stdin:2:14: error: invalid task request fingerprint' == 1
+: 1
+fingerprint: 123x
+EOI
+
+: missed
+:
+{
+ : agent
+ :
+ $* <<EOI 2>'stdin:3:1: error: no task request agent specified' == 1
+ : 1
+ fingerprint: 1105fb394ee870adb154b7abfbbae5755df7dcef6c81db34e8d1b68d2653734e
+ EOI
+
+ : fingerprint
+ :
+ $* <<EOI 2>'stdin:3:1: error: no task request fingerprint specified' == 1
+ : 1
+ agent: upsa
+ EOI
+}
+
+: no-machines
+:
+$* <<EOI 2>'stdin:4:1: error: no task request machines specified' == 1
+: 1
+agent: upsa
+fingerprint: 1105fb394ee870adb154b7abfbbae5755df7dcef6c81db34e8d1b68d2653734e
+EOI
+
+: type-not-allowed
+:
+$* <<EOI 2>'stdin:7:1: error: machine type not allowed' == 1
+: 1
+agent: upsa
+fingerprint: 1105fb394ee870adb154b7abfbbae5755df7dcef6c81db34e8d1b68d2653734e
+:
+id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+name: windows_10-msvc_14
+type: vm
+summary: Windows 10 build 1607 with VC 14 update 3
+EOI
diff --git a/tests/manifest/task-response.test b/tests/manifest/task-response.test
new file mode 100644
index 0000000..472cca7
--- /dev/null
+++ b/tests/manifest/task-response.test
@@ -0,0 +1,114 @@
+# file : tests/manifest/task-response.test
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+test.options += -ts
+
+: valid
+:
+: Roundtrip the task response manifest.
+:
+{
+ : session-not-empty
+ :
+ $* <<EOF >>EOF
+ : 1
+ session: abcd
+ challenge: xyz
+ :
+ name: libfoo
+ version: 1.0
+ repository: http://pkg.example.org/1/math
+ machine: windows_10-msvc_14
+ EOF
+
+ : session-empty
+ :
+ $* <<EOF >>EOF
+ : 1
+ session:
+ EOF
+}
+
+: redefinition
+:
+{
+ : session
+ :
+ $* <<EOI 2>'stdin:3:1: error: task response session redefinition' == 1
+ : 1
+ session: abcd
+ session: abcd
+ EOI
+
+ : challenge
+ :
+ $* <<EOI 2>'stdin:3:1: error: task response challenge redefinition' == 1
+ : 1
+ challenge: xyz
+ challenge: xyz
+ EOI
+}
+
+: invalid
+:
+{
+ : challenge
+ :
+ {
+ : empty
+ :
+ $* <<EOI 2>'stdin:2:11: error: empty task response challenge' == 1
+ : 1
+ challenge:
+ EOI
+
+ : redundant
+ :
+ $* <<EOI 2>'stdin:4:1: error: unexpected task response challenge' == 1
+ : 1
+ session:
+ challenge: abc
+ EOI
+ }
+
+ : task-unexpected
+ :
+ $* <<EOI 2>'stdin:3:1: error: single task response manifest expected' == 1
+ : 1
+ session:
+ :
+ EOI
+}
+
+: unknown-name
+:
+$* <<EOI 2>"stdin:2:1: error: unknown name 'x' in task response manifest" == 1
+: 1
+x:
+EOI
+
+: missed
+:
+{
+ : session
+ :
+ $* <<EOI 2>'stdin:2:1: error: no task response session specified' == 1
+ : 1
+ EOI
+
+ : challenge
+ :
+ $* <<EOI 2>'stdin:3:1: error: no task response challenge specified' == 1
+ : 1
+ session: abc
+ EOI
+
+ : task
+ :
+ $* <<EOI 2>'stdin:4:1: error: task manifest expected' == 1
+ : 1
+ session: abcd
+ challenge: xyz
+ EOI
+}
diff --git a/tests/manifest/task.test b/tests/manifest/task.test
new file mode 100644
index 0000000..09777b1
--- /dev/null
+++ b/tests/manifest/task.test
@@ -0,0 +1,260 @@
+# file : tests/manifest/task.test
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+test.options += -t
+
+: valid
+:
+: Roundtrip the task manifest.
+:
+{
+ : all-names
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 1.0
+ repository: http://pkg.example.org/1/math
+ machine: windows_10-msvc_14
+ target: x86_64-microsoft-win32-msvc14.0
+ config: config.cc.coptions=/Z7 config.cc.loptions=/DEBUG
+ EOF
+
+ : no-target
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 1.0
+ repository: http://pkg.example.org/1/math
+ machine: windows_10-msvc_14
+ config: config.cc.coptions=/Z7 config.cc.loptions=/DEBUG
+ EOF
+
+ : no-config
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 1.0
+ repository: http://pkg.example.org/1/math
+ machine: windows_10-msvc_14
+ target: x86_64-microsoft-win32-msvc14.0
+ EOF
+
+ : config
+ :
+ {
+ : empty-var-value
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 1.0
+ repository: http://pkg.example.org/1/math
+ machine: windows
+ config: abc=
+ EOF
+
+ : var-value-quoting
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 1.0
+ repository: http://pkg.example.org/1/math
+ machine: windows
+ config: abc='a "b '"d\e x y="
+ EOF
+ }
+}
+
+: redefinition
+:
+{
+ : name
+ :
+ $* <<EOI 2>'stdin:3:1: error: task package name redefinition' == 1
+ : 1
+ name: libfoo
+ name: libfoo
+ EOI
+
+ : version
+ :
+ $* <<EOI 2>'stdin:3:1: error: task package version redefinition' == 1
+ : 1
+ version: 1.0
+ version: 1.0
+ EOI
+
+ : repository
+ :
+ $* <<EOI 2>'stdin:3:1: error: task repository redefinition' == 1
+ : 1
+ repository: http://pkg.example.org/1/math
+ repository: http://pkg.example.org/1/math
+ EOI
+
+ : machine
+ :
+ $* <<EOI 2>'stdin:3:1: error: task machine redefinition' == 1
+ : 1
+ machine: windows_10-msvc_14
+ machine: windows_10-msvc_14
+ EOI
+
+ : target
+ :
+ $* <<EOI 2>'stdin:3:1: error: task target redefinition' == 1
+ : 1
+ target: x86_64-microsoft-win32-msvc14.0
+ target: x86_64-microsoft-win32-msvc14.0
+ EOI
+
+ : config
+ :
+ $* <<EOI 2>'stdin:3:1: error: task configuration redefinition' == 1
+ : 1
+ config: config.cc.coptions=/Z7
+ config: config.cc.loptions=/DEBUG
+ EOI
+}
+
+: invalid
+:
+{
+ : name-empty
+ :
+ $* <<EOI 2>'stdin:2:6: error: empty task package name' == 1
+ : 1
+ name:
+ EOI
+
+ : version
+ :
+ {
+ : empty
+ :
+ $* <<EOI 2>'stdin:2:9: error: invalid task package version: unexpected end' == 1
+ : 1
+ version:
+ EOI
+
+ : release
+ :
+ $* <<EOI 2>'stdin:2:10: error: invalid task package version release' == 1
+ : 1
+ version: 1.2.3-
+ EOI
+ }
+
+ : machine-empty
+ :
+ $* <<EOI 2>'stdin:2:9: error: empty task machine' == 1
+ : 1
+ machine:
+ EOI
+
+ : target-empty
+ :
+ $* <<EOI 2>'stdin:2:8: error: invalid task target: missing cpu' == 1
+ : 1
+ target:
+ EOI
+
+ : config
+ :
+ {
+ : empty
+ :
+ $* <<EOI 2>'stdin:2:8: error: empty task configuration' == 1
+ : 1
+ config:
+ EOI
+
+ : bad-field
+ :
+ $* <<EOI 2>'stdin:2:15: error: invalid task configuration: unterminated quoted string' == 1
+ : 1
+ config: 'abc=x
+ EOI
+
+ : bad-var
+ :
+ $* <<EOI 2>'stdin:2:12: error: invalid task configuration: no variable value' == 1
+ : 1
+ config: abc xyz=1
+ EOI
+
+ : multiline
+ :
+ {
+ : bad-field
+ :
+ $* <<EOI 2>'stdin:3:7: error: invalid task configuration: unterminated quoted string' == 1
+ : 1
+ config: \
+ 'abc=x
+ \
+ EOI
+
+ : bad-var
+ :
+ $* <<EOI 2>'stdin:3:4: error: invalid task configuration: no variable value' == 1
+ : 1
+ config: \
+ abc xyz=1
+ \
+ EOI
+ }
+ }
+}
+
+: unknown-name
+:
+$* <<EOI 2>"stdin:2:1: error: unknown name 'x' in task manifest" == 1
+: 1
+x:
+EOI
+
+: missed
+:
+{
+ : name
+ :
+ $* <<EOI 2>'stdin:5:1: error: no task package name specified' == 1
+ : 1
+ version: 1.0
+ repository: http://pkg.example.org/1/math
+ machine: windows_10-msvc_14
+ EOI
+
+ : version
+ :
+ $* <<EOI 2>'stdin:5:1: error: no task package version specified' == 1
+ : 1
+ name: libfoo
+ repository: http://pkg.example.org/1/math
+ machine: windows_10-msvc_14
+ EOI
+
+ : repository
+ :
+ $* <<EOI 2>'stdin:5:1: error: no task repository specified' == 1
+ : 1
+ name: libfoo
+ version: 1.0
+ machine: windows_10-msvc_14
+ EOI
+
+ : machine
+ :
+ $* <<EOI 2>'stdin:5:1: error: no task machine specified' == 1
+ : 1
+ name: libfoo
+ version: 1.0
+ repository: http://pkg.example.org/1/math
+ EOI
+}
diff --git a/tests/variable/buildfile b/tests/variable/buildfile
new file mode 100644
index 0000000..7fb5105
--- /dev/null
+++ b/tests/variable/buildfile
@@ -0,0 +1,9 @@
+# file : tests/variable/buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+import libs += libbutl%lib{butl}
+
+exe{driver}: cxx{driver} ../../bbot/lib{bbot} $libs test{testscript}
+
+include ../../bbot/
diff --git a/tests/variable/driver.cxx b/tests/variable/driver.cxx
new file mode 100644
index 0000000..88167ad
--- /dev/null
+++ b/tests/variable/driver.cxx
@@ -0,0 +1,61 @@
+// file : tests/variable/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <ios> // ios_base::failbit, ios_base::badbit
+#include <string>
+#include <cassert>
+#include <cstddef> // size_t
+#include <iostream>
+
+#include <butl/utility> // operator<<(ostream,exception)
+
+#include <bbot/variable>
+
+using namespace std;
+using namespace butl;
+using namespace bbot;
+
+// Usage: argv[0] [-u]
+//
+// Read variables from STDIN (one per line) and serialize them to STDOUT (also
+// one per line).
+//
+// -u output variables being unquoted beforehand
+//
+int
+main (int argc, char* argv[])
+{
+ assert (argc <= 2);
+ bool unquote (false);
+
+ if (argc == 2)
+ {
+ assert (argv[1] == string ("-u"));
+ unquote = true;
+ }
+
+ cin.exceptions (ios_base::badbit);
+ cout.exceptions (ios_base::failbit | ios_base::badbit);
+
+ string s;
+ for (size_t l (1); getline (cin, s); ++l)
+ {
+ try
+ {
+ variable v (move (s));
+
+ cout << (unquote
+ ? v.unquoted ()
+ : static_cast<const string&> (v))
+ << '\n';
+ }
+ catch (const invalid_variable& e)
+ {
+ cerr << l << ':' << 1 + e.pos << ": error: " << e << endl;
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/variable/testscript b/tests/variable/testscript
new file mode 100644
index 0000000..d3d9302
--- /dev/null
+++ b/tests/variable/testscript
@@ -0,0 +1,55 @@
+# file : tests/variable/testscript
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+test.options += -u
+
+: valid
+:
+{
+ $* <<EOI >> EOO
+ config.cc.coptions="-O3 -stdlib='libc++'"
+ ab'c="x y"'
+ var=xy
+ var=
+ EOI
+ config.cc.coptions=-O3 -stdlib='libc++'
+ abc="x y"
+ var=xy
+ var=
+ EOO
+}
+
+: invalid
+:
+{
+ : expected-assignment
+ :
+ $* <'v"a r=abc"' 2>'1:4: error: expected variable assignment' == 1
+
+ : unterminated-quoted-string
+ :
+ $* <'var="a b' 2>'1:9: error: unterminated quoted string' == 1
+
+ : no-value
+ :
+ $* <'var' 2>'1:4: error: no variable value' == 1
+}
+
+: unquoting
+:
+{
+ : single
+ :
+ $* <"var='a \" b'" >'var=a " b'
+
+ : double
+ :
+ $* <'var="a '"'"' b"' >"var=a ' b"
+
+ : mixed
+ :
+ $* <<EOI >'var=a bc e'
+ var='a b'"c e"
+ EOI
+}