aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/test/script/lexer.cxx4
-rw-r--r--build2/test/script/parser.cxx14
-rw-r--r--build2/test/script/runner4
-rw-r--r--build2/test/script/runner.cxx2
-rw-r--r--build2/test/script/script50
-rw-r--r--build2/test/script/script.cxx73
-rw-r--r--doc/testscript.cli2
-rw-r--r--unit-tests/test/script/lexer/driver.cxx4
-rw-r--r--unit-tests/test/script/parser/buildfile2
-rw-r--r--unit-tests/test/script/parser/driver.cxx14
-rw-r--r--unit-tests/test/script/parser/scope.test15
11 files changed, 136 insertions, 48 deletions
diff --git a/build2/test/script/lexer.cxx b/build2/test/script/lexer.cxx
index 01c93a4..5061a1d 100644
--- a/build2/test/script/lexer.cxx
+++ b/build2/test/script/lexer.cxx
@@ -284,14 +284,14 @@ namespace build2
lexer_mode m (st.mode);
// Customized implementation that handles special variable names ($*,
- // $~, $NNN).
+ // $NN, $~, $@).
//
if (m != lexer_mode::variable)
return base_lexer::word (st, sep);
xchar c (peek ());
- if (c != '*' && c != '~' && !digit (c))
+ if (c != '*' && c != '~' && c != '@' && !digit (c))
return base_lexer::word (st, sep);
uint64_t ln (c.line), cn (c.column);
diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx
index 756761f..a3860c4 100644
--- a/build2/test/script/parser.cxx
+++ b/build2/test/script/parser.cxx
@@ -79,6 +79,7 @@ namespace build2
if (tt == type::eos)
break;
+ const location ll (get_location (t));
line_type lt (pre_parse_script_line (t, tt));
assert (tt == type::newline);
@@ -98,11 +99,11 @@ namespace build2
}
case line_type::test:
{
- // Create implicit test scope.
+ // Create implicit test scope. Use line number as the scope id.
//
group_->scopes.push_back (
unique_ptr<test> (
- (test_ = new test (*group_))));
+ (test_ = new test (to_string (ll.line), *group_))));
ls = &test_->tests;
@@ -740,7 +741,7 @@ namespace build2
// Now that we have all the pieces, run the command.
//
if (!pre_parse_)
- runner_->run (c, li, ll);
+ runner_->run (*scope_, c, li, ll);
}
command_exit parser::
@@ -847,7 +848,8 @@ namespace build2
if (!qual.empty ())
fail (loc) << "qualified variable name";
- // @@ MT: will need RW mutex on var_pool.
+ // @@ MT: will need RW mutex on var_pool. Or maybe if it's not there
+ // then it can't possibly be found? Still will be setting variables.
//
if (name != "*" && !digits (name))
return scope_->find (script_->var_pool.insert (move (name)));
@@ -861,6 +863,10 @@ namespace build2
// we don't know which $NN vars will be looked up from inside.
// Could we collect all the variable names during the pre-parse
// stage? They could be computed.
+ //
+ // Or we could set all the non-NULL $NN (i.e., based on the number
+ // of elements in $*).
+ //
// In both cases first thing we do is lookup $*. It should always be
// defined since we set it on the script's root scope.
diff --git a/build2/test/script/runner b/build2/test/script/runner
index 57e506f..e2cffcf 100644
--- a/build2/test/script/runner
+++ b/build2/test/script/runner
@@ -31,14 +31,14 @@ namespace build2
// It can be used in diagnostics.
//
virtual void
- run (const command&, size_t index, const location&) = 0;
+ run (const scope&, const command&, size_t index, const location&) = 0;
};
class concurrent_runner: public runner
{
public:
virtual void
- run (const command&, size_t, const location&) override;
+ run (const scope&, const command&, size_t, const location&) override;
};
}
}
diff --git a/build2/test/script/runner.cxx b/build2/test/script/runner.cxx
index abd1ef3..7844119 100644
--- a/build2/test/script/runner.cxx
+++ b/build2/test/script/runner.cxx
@@ -250,7 +250,7 @@ namespace build2
}
void concurrent_runner::
- run (const command& c, size_t ci, const location& cl)
+ run (const scope&, const command& c, size_t ci, const location& cl)
{
if (verb >= 3)
text << c;
diff --git a/build2/test/script/script b/build2/test/script/script
index 860e5d4..7fbe949 100644
--- a/build2/test/script/script
+++ b/build2/test/script/script
@@ -103,10 +103,21 @@ namespace build2
ostream&
operator<< (ostream&, const command&);
+ class script;
+
class scope
{
public:
- scope* parent; // NULL for the root (script) scope.
+ scope* const parent; // NULL for the root (script) scope.
+ script* const root; // Self for the root (script) scope.
+
+ // Note that if we pass the variable name as a string, then it will
+ // be looked up in the wrong pool.
+ //
+ variable_map vars;
+
+ const path& id_path; // Id path ($@, relative in POSIX form).
+ const dir_path& wd_path; // Working dir ($~, absolute and normalized).
lines setup;
lines tdown;
@@ -114,11 +125,6 @@ namespace build2
// Variables.
//
public:
- // Note that if we pass the variable name as a string, then it will
- // be looked up in the wrong pool.
- //
- variable_map vars;
-
// Lookup the variable starting from this scope, continuing with outer
// scopes, then the target being tested, then the testscript target,
// and then outer buildfile scopes (including testscript-type/pattern
@@ -148,8 +154,7 @@ namespace build2
~scope () = default;
protected:
- scope (scope* p): parent (p) {}
- scope (): parent (nullptr) {} // For the root (script) scope.
+ scope (const string& id, scope* parent);
};
class group: public scope
@@ -158,10 +163,10 @@ namespace build2
vector<unique_ptr<scope>> scopes;
public:
- group (group& p): scope (&p) {}
+ group (const string& id, group& p): scope (id, &p) {}
protected:
- group (): scope (nullptr) {} // For the root (script) scope.
+ group (const string& id): scope (id, nullptr) {} // For root.
};
class test: public scope
@@ -170,17 +175,13 @@ namespace build2
lines tests;
public:
- test (group& p): scope (&p) {}
+ test (const string& id, group& p): scope (id, &p) {}
};
- class script: public group
+ class script_base // Make sure certain things are initialized early.
{
- public:
- script (target& test_target, testscript& script_target);
-
- public:
- target& test_target; // Target we are testing.
- testscript& script_target; // Target of the testscript file.
+ protected:
+ script_base ();
public:
variable_pool var_pool;
@@ -190,7 +191,18 @@ namespace build2
const variable& args_var; // test.arguments
const variable& cmd_var; // $*
- const variable& cwd_var; // $~
+ const variable& wd_var; // $~
+ const variable& id_var; // $@
+ };
+
+ class script: public script_base, public group
+ {
+ public:
+ script (target& test_target, testscript& script_target);
+
+ public:
+ target& test_target; // Target we are testing.
+ testscript& script_target; // Target of the testscript file.
};
}
}
diff --git a/build2/test/script/script.cxx b/build2/test/script/script.cxx
index 5931d3f..6602518 100644
--- a/build2/test/script/script.cxx
+++ b/build2/test/script/script.cxx
@@ -102,11 +102,38 @@ namespace build2
}
}
- script::
- script (target& tt, testscript& st)
- : test_target (tt), script_target (st),
+ scope::
+ scope (const string& id, scope* p)
+ : parent (p),
+ root (p != nullptr ? p->root : static_cast<script*> (this)),
+ id_path (cast<path> (assign (root->id_var) = path ())),
+ wd_path (cast<dir_path> (assign (root->wd_var) = dir_path ()))
+
+ {
+ // Construct the id_path as a string to ensure POSIX form. In fact,
+ // the only reason we keep it as a path is to be able to easily get id
+ // by calling leaf().
+ //
+ {
+ string s (p != nullptr ? p->id_path.string () : string ());
+
+ if (!s.empty () && !id.empty ())
+ s += '/';
- // Enter the test* variables with the same variable types as in
+ s += id;
+ const_cast<path&> (id_path) = path (move (s));
+ }
+
+ // Calculate the working directory path unless this is the root scope
+ // (handled in an ad hoc way).
+ //
+ if (p != nullptr)
+ const_cast<dir_path&> (wd_path) = dir_path (p->wd_path) /= id;
+ }
+
+ script_base::
+ script_base ()
+ : // Enter the test* variables with the same variable types as in
// buildfiles.
//
test_var (var_pool.insert<path> ("test")),
@@ -114,8 +141,38 @@ namespace build2
args_var (var_pool.insert<strings> ("test.arguments")),
cmd_var (var_pool.insert<strings> ("*")),
- cwd_var (var_pool.insert<dir_path> ("~"))
+ wd_var (var_pool.insert<dir_path> ("~")),
+ id_var (var_pool.insert<path> ("@")) {}
+
+ static inline string
+ script_id (const path& p)
+ {
+ string r (p.leaf ().string ());
+
+ if (r == "testscript")
+ return string ();
+
+ size_t n (path::traits::find_extension (r));
+ assert (n != string::npos);
+ r.resize (n);
+ return r;
+ }
+
+ script::
+ script (target& tt, testscript& st)
+ : group (script_id (st.path ())),
+ test_target (tt), script_target (st)
{
+ // Set the script working dir ($~) to $out_base/test/<id> (id_path
+ // for root is just the id).
+ //
+ {
+ auto& wd (const_cast<dir_path&> (wd_path));
+ wd = tt.out_dir ();
+ wd /= "test";
+ wd /= id_path.string ();
+ }
+
// Unless we have the test variable set on the test or script target,
// set it at the script level to the test target's path.
//
@@ -138,12 +195,6 @@ namespace build2
// on first access.
//
assign (cmd_var) = nullptr;
-
- // Set the script CWD ($~) which is the $out_base/<script-name>.
- //
- // @@ This will conflict for 'testscript'!
- //
- assign (cwd_var) = dir_path (tt.out_dir ()) /= st.name;
}
lookup scope::
diff --git a/doc/testscript.cli b/doc/testscript.cli
index 48f9e1e..027fb64 100644
--- a/doc/testscript.cli
+++ b/doc/testscript.cli
@@ -399,6 +399,8 @@ EOO
@@ how to preserve test output
+@@ term: 'test working directory'
+
\h1#integration|Build System Integration|
The \c{build2} \c{test} module provides the ability to run an executable
diff --git a/unit-tests/test/script/lexer/driver.cxx b/unit-tests/test/script/lexer/driver.cxx
index 6d9bfb7..cd7110f 100644
--- a/unit-tests/test/script/lexer/driver.cxx
+++ b/unit-tests/test/script/lexer/driver.cxx
@@ -19,11 +19,11 @@ namespace build2
{
namespace script
{
+ // Usage: argv[0] <lexer-mode>
+ //
int
main (int argc, char* argv[])
{
- // Usage: argv[0] <lexer-mode>
- //
lexer_mode m;
{
assert (argc == 2);
diff --git a/unit-tests/test/script/parser/buildfile b/unit-tests/test/script/parser/buildfile
index af602b1..a6be1f6 100644
--- a/unit-tests/test/script/parser/buildfile
+++ b/unit-tests/test/script/parser/buildfile
@@ -11,6 +11,6 @@ filesystem config/{utility init operation} dump types-parsers \
test/{target script/{token lexer parser script}}
exe{driver}: cxx{driver} ../../../../build2/cxx{$src} $libs \
-test{pre-parse expansion here-document command-re-parse}
+test{pre-parse expansion here-document command-re-parse scope}
include ../../../../build2/
diff --git a/unit-tests/test/script/parser/driver.cxx b/unit-tests/test/script/parser/driver.cxx
index 148a081..db253eb 100644
--- a/unit-tests/test/script/parser/driver.cxx
+++ b/unit-tests/test/script/parser/driver.cxx
@@ -29,7 +29,7 @@ namespace build2
{
public:
virtual void
- run (const command& t, size_t, const location&) override
+ run (const scope&, const command& t, size_t, const location&) override
{
// Here we assume we are running serially.
//
@@ -37,8 +37,10 @@ namespace build2
}
};
+ // Usage: argv[0] [<testscript-name>]
+ //
int
- main ()
+ main (int argc, char* argv[])
{
tracer trace ("main");
@@ -47,7 +49,7 @@ namespace build2
try
{
- path name ("testscript");
+ path name (argc > 1 ? argv[1] : "testscript");
cin.exceptions (istream::failbit | istream::badbit);
// Enter mock targets. Use fixed names and paths so that we can use
@@ -70,7 +72,7 @@ namespace build2
trace));
tt.path (path ("driver"));
- st.path (path ("testscript"));
+ st.path (name);
// Parse and run.
//
@@ -93,7 +95,7 @@ namespace build2
}
int
-main ()
+main (int argc, char* argv[])
{
- return build2::test::script::main ();
+ return build2::test::script::main (argc, argv);
}
diff --git a/unit-tests/test/script/parser/scope.test b/unit-tests/test/script/parser/scope.test
new file mode 100644
index 0000000..a2c6d9f
--- /dev/null
+++ b/unit-tests/test/script/parser/scope.test
@@ -0,0 +1,15 @@
+$* testscript <'cmd $@' >"cmd 1" # id-testscript
+$* foo.test <'cmd $@' >"cmd foo/1" # id
+
+wd = [dir_path] $build.work
+wd += test
+wd += 1
+$* testscript <'cmd $~' >"cmd $wd" # wd-testscript
+
+# @@ TMP wd1
+#
+wd1 = [dir_path] $build.work
+wd1 += test
+wd1 += foo
+wd1 += 1
+$* foo.test <'cmd $~' >"cmd $wd1" # wd