aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-10-24 18:00:05 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-11-04 09:26:35 +0200
commit79a83d6dd0f312a5e390f5627f68cc96c4427d33 (patch)
treef66a0a33ea9d18bf1e85d7c4503fc923028edc80
parent2c0a2b0d688b4450c72cde12ecedaa3fc3c9662a (diff)
Add support for setup/teardown commands
-rw-r--r--build2/test/script/lexer3
-rw-r--r--build2/test/script/lexer.cxx57
-rw-r--r--build2/test/script/parser2
-rw-r--r--build2/test/script/parser.cxx64
-rw-r--r--build2/test/script/script2
-rw-r--r--build2/test/script/token3
-rw-r--r--build2/test/script/token.cxx3
-rw-r--r--doc/testscript.cli32
-rw-r--r--tests/test/script/buildfile2
-rw-r--r--tests/test/script/integration/bootstrap.build4
-rw-r--r--tests/test/script/integration/buildfile7
-rw-r--r--tests/test/script/integration/testscript27
-rw-r--r--unit-tests/test/script/lexer/buildfile2
-rw-r--r--unit-tests/test/script/lexer/driver.cxx6
-rw-r--r--unit-tests/test/script/lexer/first-token.test (renamed from unit-tests/test/script/lexer/assign-line.test)2
-rw-r--r--unit-tests/test/script/lexer/second-token.test8
-rw-r--r--unit-tests/test/script/parser/driver.cxx2
-rw-r--r--unit-tests/test/script/parser/here-document.test16
18 files changed, 198 insertions, 44 deletions
diff --git a/build2/test/script/lexer b/build2/test/script/lexer
index 80e6036..53b88be 100644
--- a/build2/test/script/lexer
+++ b/build2/test/script/lexer
@@ -25,7 +25,8 @@ namespace build2
enum
{
script_line = base_type::value_next,
- assign_line, // Auto-expires at the end of the token.
+ first_token, // Auto-expires at the end of the token.
+ second_token, // Auto-expires at the end of the token.
variable_line, // Auto-expires at the end of the line.
command_line,
here_line
diff --git a/build2/test/script/lexer.cxx b/build2/test/script/lexer.cxx
index ea906a1..7ced2b9 100644
--- a/build2/test/script/lexer.cxx
+++ b/build2/test/script/lexer.cxx
@@ -30,14 +30,31 @@ namespace build2
s2 = " == ";
break;
}
- case lexer_mode::assign_line:
+ case lexer_mode::first_token:
{
- // As script_line but with variable assignments.
+ // First token on the script line. Like script_line but recognizes
+ // leading plus/minus and variable assignments as separators.
+ //
+ // Note that to recognize only leading plus/minus we shouldn't add
+ // them to the separator strings.
//
s1 = ";=+!|&<> $(#\t\n";
s2 = " == ";
break;
}
+ case lexer_mode::second_token:
+ {
+ // Second token on the script line. Like script_line but
+ // recognizes leading variable assignments.
+ //
+ // Note that to recognize only leading assignments we shouldn't
+ // add them to the separator strings (so this is identical to
+ // script_line).
+ //
+ s1 = ";=!|&<> $(#\t\n";
+ s2 = " == ";
+ break;
+ }
case lexer_mode::variable_line:
{
// Like value except we recognize ';' and don't recognize '{'.
@@ -87,7 +104,8 @@ namespace build2
switch (state_.top ().mode)
{
case lexer_mode::script_line:
- case lexer_mode::assign_line:
+ case lexer_mode::first_token:
+ case lexer_mode::second_token:
case lexer_mode::variable_line:
case lexer_mode::command_line:
case lexer_mode::here_line: r = next_line (); break;
@@ -116,13 +134,13 @@ namespace build2
if (eos (c))
return make_token (type::eos);
- state st (state_.top ()); // Make copy (see assign_line).
+ state st (state_.top ()); // Make copy (see first/second_token).
lexer_mode m (st.mode);
- // Expire the assign mode at the end of the token. Do it early in case
+ // Expire certain modes at the end of the token. Do it early in case
// we push any new mode (e.g., double quote).
//
- if (m == lexer_mode::assign_line)
+ if (m == lexer_mode::first_token || m == lexer_mode::second_token)
state_.pop ();
// NOTE: remember to update mode() if adding new special characters.
@@ -161,8 +179,9 @@ namespace build2
// Line separators.
//
- if (m == lexer_mode::script_line ||
- m == lexer_mode::assign_line ||
+ if (m == lexer_mode::script_line ||
+ m == lexer_mode::first_token ||
+ m == lexer_mode::second_token ||
m == lexer_mode::variable_line)
{
switch (c)
@@ -173,7 +192,9 @@ namespace build2
// Command line operator/separators.
//
- if (m == lexer_mode::script_line || m == lexer_mode::assign_line)
+ if (m == lexer_mode::script_line ||
+ m == lexer_mode::first_token ||
+ m == lexer_mode::second_token)
{
switch (c)
{
@@ -193,8 +214,9 @@ namespace build2
// Command operators/separators.
//
- if (m == lexer_mode::script_line ||
- m == lexer_mode::assign_line ||
+ if (m == lexer_mode::script_line ||
+ m == lexer_mode::first_token ||
+ m == lexer_mode::second_token ||
m == lexer_mode::command_line)
{
switch (c)
@@ -291,9 +313,20 @@ namespace build2
}
}
+ // Plus/minus.
+ //
+ if (m == lexer_mode::first_token)
+ {
+ switch (c)
+ {
+ case '+': return make_token (type::plus);
+ case '-': return make_token (type::minus);
+ }
+ }
+
// Variable assignment (=, +=, =+).
//
- if (m == lexer_mode::assign_line)
+ if (m == lexer_mode::second_token)
{
switch (c)
{
diff --git a/build2/test/script/parser b/build2/test/script/parser
index ef65c1a..2e8a9b1 100644
--- a/build2/test/script/parser
+++ b/build2/test/script/parser
@@ -64,7 +64,7 @@ namespace build2
parse_variable_line (token&, token_type&);
bool
- parse_test_line (token&, token_type&, size_t);
+ parse_command_line (token&, token_type&, size_t);
command_exit
parse_command_exit (token&, token_type&);
diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx
index 2e5e9df..f8b414f 100644
--- a/build2/test/script/parser.cxx
+++ b/build2/test/script/parser.cxx
@@ -72,10 +72,9 @@ namespace build2
//
replay_save ();
- // We need to start lexing each line in the assign mode in order to
- // recognize assignment operators as separators.
+ // Start lexing each line recognizing leading plus/minus.
//
- mode (lexer_mode::assign_line);
+ mode (lexer_mode::first_token);
next (t, tt);
if (group_->start_loc_.empty ())
@@ -111,11 +110,36 @@ namespace build2
// semicolon and we should add this one to the same place.
//
if (lines_ != nullptr)
+ {
+ switch (lt.first)
+ {
+ case line_type::setup: fail (ll) << "setup command in test";
+ case line_type::tdown: fail (ll) << "teardown command in test";
+ default: break;
+ }
+
ls = lines_;
+ }
else
{
switch (lt.first)
{
+ case line_type::setup:
+ {
+ if (!group_->scopes.empty ())
+ fail (ll) << "setup command after tests";
+
+ if (!group_->tdown_.empty ())
+ fail (ll) << "setup command after teardown";
+
+ ls = &group_->setup_;
+ break;
+ }
+ case line_type::tdown:
+ {
+ ls = &group_->tdown_;
+ break;
+ }
case line_type::variable:
{
// If there is a semicolon after the variable then we assume
@@ -142,10 +166,10 @@ namespace build2
// This will detect things like variable assignments between
// tests.
//
- // @@ Can the teardown line be from a different file?
- //
if (!group_->tdown_.empty ())
{
+ // @@ Can the teardown line be from a different file?
+ //
location tl (
get_location (
group_->tdown_.back ().tokens.front ().token));
@@ -249,11 +273,11 @@ namespace build2
//
if (tt == type::word && !t.quoted)
{
- // Switch recognition of variable assignments for one more token.
- // This is safe to do because we know we cannot be in the quoted
- // mode (since the current token is not quoted).
+ // Switch recognition of leading variable assignments for the next
+ // token. This is safe to do because we know we cannot be in the
+ // quoted mode (since the current token is not quoted).
//
- mode (lexer_mode::assign_line);
+ mode (lexer_mode::second_token);
type p (peek ());
if (p == type::assign || p == type::prepend || p == type::append)
@@ -263,7 +287,14 @@ namespace build2
}
}
- return make_pair (line_type::test, parse_test_line (t, tt, 0));
+ line_type lt (tt == type::plus ? line_type::setup :
+ tt == type::minus ? line_type::tdown :
+ line_type::test);
+
+ if (lt != line_type::test)
+ next (t, tt);
+
+ return make_pair (lt, parse_command_line (t, tt, 0));
}
void parser::
@@ -272,7 +303,10 @@ namespace build2
switch (lt)
{
case line_type::variable: parse_variable_line (t, tt); break;
- case line_type::test: parse_test_line (t, tt, li); break;
+
+ case line_type::setup:
+ case line_type::tdown: next (t, tt); // Skip plus/minus fallthrough.
+ case line_type::test: parse_command_line (t, tt, li); break;
}
}
@@ -363,7 +397,7 @@ namespace build2
}
bool parser::
- parse_test_line (token& t, type& tt, size_t li)
+ parse_command_line (token& t, type& tt, size_t li)
{
command c;
@@ -941,9 +975,11 @@ namespace build2
break;
}
- // Expand the line.
+ // Expand the line (can be blank).
//
- names ns (parse_names (t, tt, false, "here-document line", nullptr));
+ names ns (tt != type::newline
+ ? parse_names (t, tt, false, "here-document line", nullptr)
+ : names ());
if (!pre_parse_)
{
diff --git a/build2/test/script/script b/build2/test/script/script
index 8b6c3c0..2dbf7ce 100644
--- a/build2/test/script/script
+++ b/build2/test/script/script
@@ -26,7 +26,7 @@ namespace build2
// Pre-parse representation.
//
- enum class line_type {variable, test};
+ enum class line_type {variable, setup, tdown, test};
struct line
{
diff --git a/build2/test/script/token b/build2/test/script/token
index 1d4f2e4..f9352d2 100644
--- a/build2/test/script/token
+++ b/build2/test/script/token
@@ -26,6 +26,9 @@ namespace build2
semi = base_type::value_next, // ;
+ plus, // +
+ minus, // -
+
pipe, // |
clean, // &
log_and, // &&
diff --git a/build2/test/script/token.cxx b/build2/test/script/token.cxx
index 2e86e29..549fe13 100644
--- a/build2/test/script/token.cxx
+++ b/build2/test/script/token.cxx
@@ -23,6 +23,9 @@ namespace build2
{
case token_type::semi: os << q << ';' << q; break;
+ case token_type::plus: os << q << '+' << q; break;
+ case token_type::minus: os << q << '-' << q; break;
+
case token_type::pipe: os << q << '|' << q; break;
case token_type::clean: os << q << '&' << q; break;
case token_type::log_and: os << q << "&&" << q; break;
diff --git a/doc/testscript.cli b/doc/testscript.cli
index 0a4bd8f..9a137eb 100644
--- a/doc/testscript.cli
+++ b/doc/testscript.cli
@@ -704,20 +704,34 @@ here-document fragments.
\
script:
- *script-line
+ scope-body
-script-line:
- variable-line|test-line
+scope-body:
+ *setup
+ *(scope|test)
+ *teardown
+
+setup:
+ variable-line|setup-line
+
+teardown:
+ variable-line|teardown-line
+
+scope:
+ '{'
+ scope-body
+ '}'
+
+test:
+ *((variable-line|test-line) ';')
+ test-line
variable-line: <variable> ('='|'+='|'=+') value-attributes? <value>
value-attributes: '[' <key-value-pairs> ']'
-test-line:
- *((variable-line|command-line) ';')
- command-line
-
-command-line:
- command
+setup-line: '+' command
+teardown-line: '-' command
+test-line: command
command: <path>(' '+(<arg>|redirect))* command-exit?
*here-document
diff --git a/tests/test/script/buildfile b/tests/test/script/buildfile
index 2bc4780..e613013 100644
--- a/tests/test/script/buildfile
+++ b/tests/test/script/buildfile
@@ -2,6 +2,6 @@
# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
-d = runner/
+d = runner/ integration/
./: $d
include $d
diff --git a/tests/test/script/integration/bootstrap.build b/tests/test/script/integration/bootstrap.build
new file mode 100644
index 0000000..876be09
--- /dev/null
+++ b/tests/test/script/integration/bootstrap.build
@@ -0,0 +1,4 @@
+project = test
+amalgamation =
+
+using test
diff --git a/tests/test/script/integration/buildfile b/tests/test/script/integration/buildfile
new file mode 100644
index 0000000..9b7b906
--- /dev/null
+++ b/tests/test/script/integration/buildfile
@@ -0,0 +1,7 @@
+# file : tests/test/script/integration/buildfile
+# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+./: test{testscript}
+
+test{*}: test = $build.driver
diff --git a/tests/test/script/integration/testscript b/tests/test/script/integration/testscript
new file mode 100644
index 0000000..4c8c380
--- /dev/null
+++ b/tests/test/script/integration/testscript
@@ -0,0 +1,27 @@
+# file : tests/test/script/integration/testscript
+# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+test.options += --buildfile -
+test.arguments = test
+
++mkdir build
+#+cat <<EOI >>>build/boostrap.build
+#project = test
+#amalgamation =
+#
+#using test
+#EOI
++cp $src_base/bootstrap.build build/
+
+touch testscript foo.test;
+$* <<EOI 2>>EOE != 0;
+./: test{testscript foo}
+EOI
+error: both 'testscript' and other names specified for dir{./}
+info: while testing dir{./}
+EOE
+rm -f testscript foo.test
+
+-rm -f build/bootstrap.build
+-rmdir build/
diff --git a/unit-tests/test/script/lexer/buildfile b/unit-tests/test/script/lexer/buildfile
index 856d35d..fab61b6 100644
--- a/unit-tests/test/script/lexer/buildfile
+++ b/unit-tests/test/script/lexer/buildfile
@@ -8,6 +8,6 @@ import libs = libbutl%lib{butl}
src = token lexer diagnostics utility variable name test/script/{token lexer}
exe{driver}: cxx{driver} ../../../../build2/cxx{$src} $libs \
-test{script-line assign-line variable-line variable comment}
+test{script-line first-token second-token variable-line variable comment}
include ../../../../build2/
diff --git a/unit-tests/test/script/lexer/driver.cxx b/unit-tests/test/script/lexer/driver.cxx
index e37e29d..bda21e4 100644
--- a/unit-tests/test/script/lexer/driver.cxx
+++ b/unit-tests/test/script/lexer/driver.cxx
@@ -30,7 +30,8 @@ namespace build2
string s (argv[1]);
if (s == "script-line") m = lexer_mode::script_line;
- else if (s == "assign-line") m = lexer_mode::assign_line;
+ else if (s == "first-token") m = lexer_mode::second_token;
+ else if (s == "second-token") m = lexer_mode::first_token;
else if (s == "variable-line") m = lexer_mode::variable_line;
else if (s == "command-line") m = lexer_mode::command_line;
else if (s == "here-line") m = lexer_mode::here_line;
@@ -44,7 +45,8 @@ namespace build2
// Some modes auto-expire so we need something underneath.
//
- bool u (m == lexer_mode::assign_line ||
+ bool u (m == lexer_mode::first_token ||
+ m == lexer_mode::second_token ||
m == lexer_mode::variable_line ||
m == lexer_mode::variable);
diff --git a/unit-tests/test/script/lexer/assign-line.test b/unit-tests/test/script/lexer/first-token.test
index ce3e8a1..a665fed 100644
--- a/unit-tests/test/script/lexer/assign-line.test
+++ b/unit-tests/test/script/lexer/first-token.test
@@ -1,6 +1,6 @@
# Note: this mode auto-expires after each token.
#
-test.arguments += assign-line
+test.arguments += first-token
$* <";" >>EOO # semi-only
;
diff --git a/unit-tests/test/script/lexer/second-token.test b/unit-tests/test/script/lexer/second-token.test
new file mode 100644
index 0000000..a665fed
--- /dev/null
+++ b/unit-tests/test/script/lexer/second-token.test
@@ -0,0 +1,8 @@
+# Note: this mode auto-expires after each token.
+#
+test.arguments += first-token
+
+$* <";" >>EOO # semi-only
+;
+<newline>
+EOO
diff --git a/unit-tests/test/script/parser/driver.cxx b/unit-tests/test/script/parser/driver.cxx
index 4fd0489..6e3fed5 100644
--- a/unit-tests/test/script/parser/driver.cxx
+++ b/unit-tests/test/script/parser/driver.cxx
@@ -70,7 +70,7 @@ namespace build2
{
tracer trace ("main");
- init (1); // Default verbosity.
+ init ("false", 1); // No build system driver, default verbosity.
reset (strings ()); // No command line variables.
bool scope (false);
diff --git a/unit-tests/test/script/parser/here-document.test b/unit-tests/test/script/parser/here-document.test
index 13b070a..d6b21fd 100644
--- a/unit-tests/test/script/parser/here-document.test
+++ b/unit-tests/test/script/parser/here-document.test
@@ -1,4 +1,20 @@
+$* <<EOI >>EOO # blank-lines
+cmd <<EOF
+foo
+
+bar
+
+EOF
+EOI
+cmd <<EOF
+foo
+
+bar
+
+EOF
+EOO
+
# quote
#
# Note: they are still recognized in eval contexts.