From 79a83d6dd0f312a5e390f5627f68cc96c4427d33 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 24 Oct 2016 18:00:05 +0200 Subject: Add support for setup/teardown commands --- build2/test/script/lexer | 3 +- build2/test/script/lexer.cxx | 57 ++++++++++++++++----- build2/test/script/parser | 2 +- build2/test/script/parser.cxx | 64 ++++++++++++++++++------ build2/test/script/script | 2 +- build2/test/script/token | 3 ++ build2/test/script/token.cxx | 3 ++ doc/testscript.cli | 32 ++++++++---- tests/test/script/buildfile | 2 +- tests/test/script/integration/bootstrap.build | 4 ++ tests/test/script/integration/buildfile | 7 +++ tests/test/script/integration/testscript | 27 ++++++++++ unit-tests/test/script/lexer/assign-line.test | 8 --- unit-tests/test/script/lexer/buildfile | 2 +- unit-tests/test/script/lexer/driver.cxx | 6 ++- unit-tests/test/script/lexer/first-token.test | 8 +++ unit-tests/test/script/lexer/second-token.test | 8 +++ unit-tests/test/script/parser/driver.cxx | 2 +- unit-tests/test/script/parser/here-document.test | 16 ++++++ 19 files changed, 205 insertions(+), 51 deletions(-) create mode 100644 tests/test/script/integration/bootstrap.build create mode 100644 tests/test/script/integration/buildfile create mode 100644 tests/test/script/integration/testscript delete mode 100644 unit-tests/test/script/lexer/assign-line.test create mode 100644 unit-tests/test/script/lexer/first-token.test create mode 100644 unit-tests/test/script/lexer/second-token.test 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: ('='|'+='|'=+') value-attributes? value-attributes: '[' ']' -test-line: - *((variable-line|command-line) ';') - command-line - -command-line: - command +setup-line: '+' command +teardown-line: '-' command +test-line: command command: (' '+(|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 <>>build/boostrap.build +#project = test +#amalgamation = +# +#using test +#EOI ++cp $src_base/bootstrap.build build/ + +touch testscript foo.test; +$* <>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/assign-line.test b/unit-tests/test/script/lexer/assign-line.test deleted file mode 100644 index ce3e8a1..0000000 --- a/unit-tests/test/script/lexer/assign-line.test +++ /dev/null @@ -1,8 +0,0 @@ -# Note: this mode auto-expires after each token. -# -test.arguments += assign-line - -$* <";" >>EOO # semi-only -; - -EOO 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/first-token.test b/unit-tests/test/script/lexer/first-token.test new file mode 100644 index 0000000..a665fed --- /dev/null +++ b/unit-tests/test/script/lexer/first-token.test @@ -0,0 +1,8 @@ +# Note: this mode auto-expires after each token. +# +test.arguments += first-token + +$* <";" >>EOO # semi-only +; + +EOO 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 +; + +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 @@ +$* <>EOO # blank-lines +cmd <