diff options
Diffstat (limited to 'share')
-rw-r--r-- | share/common/lib/files.yaml | 58 | ||||
-rw-r--r-- | share/common/lib/runcmd.yaml | 65 | ||||
-rw-r--r-- | share/python/lib/daemon.yaml | 69 | ||||
-rw-r--r-- | share/python/lib/runcmd.py | 1 | ||||
-rw-r--r-- | share/python/template/scenarios.py | 10 | ||||
-rw-r--r-- | share/rust/lib/datadir.yaml | 7 | ||||
-rw-r--r-- | share/rust/template/macros.rs.tera | 2 | ||||
-rw-r--r-- | share/rust/template/template.rs.tera | 2 | ||||
-rw-r--r-- | share/subplot.css | 40 |
9 files changed, 247 insertions, 7 deletions
diff --git a/share/common/lib/files.yaml b/share/common/lib/files.yaml index cb39b96..689a5f3 100644 --- a/share/common/lib/files.yaml +++ b/share/common/lib/files.yaml @@ -10,6 +10,9 @@ function: files_create_from_embedded types: embedded_file: file + doc: | + Create a file on disk from an embedded file in the subplot + document. The created file has the same name as the embedded file. - given: file {filename_on_disk} from {embedded_file} impl: @@ -20,6 +23,9 @@ types: filename_on_disk: path embedded_file: file + doc: | + Create a file on disk from an embedded file in the subplot + document. Set the name of the created file. - when: I write "{text}" to file {filename} impl: @@ -30,6 +36,8 @@ types: filename: path text: text + doc: | + Create a file on disk with the given content. # Manage directories (distinct from files). @@ -41,6 +49,8 @@ function: files_make_directory types: path: path + doc: | + Create a directory on disk. - when: I create directory {path} impl: @@ -50,6 +60,8 @@ function: files_make_directory types: path: path + doc: | + Create a directory on disk. - when: I remove directory {path} impl: @@ -59,6 +71,8 @@ function: files_remove_directory types: path: path + doc: | + Remove a directory on disk. - then: directory {path} exists impl: @@ -68,6 +82,8 @@ function: files_directory_exists types: path: path + doc: | + Check that a directory exists. - then: directory {path} does not exist impl: @@ -77,6 +93,8 @@ function: files_directory_does_not_exist types: path: path + doc: | + Check that a directory does not exist. - then: directory {path} is empty impl: @@ -86,6 +104,8 @@ function: files_directory_is_empty types: path: path + doc: | + Check that a directory exists and does not contain anything. - then: directory {path} is not empty impl: @@ -95,6 +115,8 @@ function: files_directory_is_not_empty types: path: path + doc: | + Check that a directory exists and contains something. # File metadata management and testing. @@ -108,6 +130,8 @@ types: filename: path mtime: text + doc: | + Create a file with specific modification time. - when: I remember metadata for file {filename} impl: @@ -117,6 +141,8 @@ function: files_remember_metadata types: filename: path + doc: | + Remember the metadata of a file. - when: I touch file {filename} impl: @@ -126,6 +152,8 @@ function: files_touch types: filename: path + doc: | + Update the modification time of a file to be current time. - then: file {filename} has same metadata as before impl: @@ -135,6 +163,9 @@ function: files_has_remembered_metadata types: filename: path + doc: | + Check that a file has the same metadata as remembered from + earlier. - then: file {filename} has different metadata from before impl: @@ -144,6 +175,8 @@ function: files_has_different_metadata types: filename: path + doc: | + Check that a file metadata has changed from earlier. - then: file {filename} has changed from before impl: @@ -153,6 +186,8 @@ function: files_has_different_metadata types: filename: path + doc: | + Check that file metadata has changed from before. - then: file {filename} has a very recent modification time impl: @@ -162,6 +197,8 @@ function: files_mtime_is_recent types: filename: path + doc: | + Check that file modification time is recent. - then: file {filename} has a very old modification time impl: @@ -171,6 +208,8 @@ function: files_mtime_is_ancient types: filename: path + doc: | + Check that file modification is far in the past. # Testing file existence. @@ -182,6 +221,8 @@ function: files_file_exists types: filename: path + doc: | + Check that a file exist. - then: file {filename} does not exist impl: @@ -191,6 +232,8 @@ function: files_file_does_not_exist types: filename: path + doc: | + Check that a file does not exist. - then: only files (?P<filenames>.+) exist impl: @@ -199,6 +242,10 @@ python: function: files_only_these_exist regex: true + types: + filenames: text + doc: | + Check that the test directory only contains specific files. # Tests on file content. @@ -211,6 +258,8 @@ types: filename: path data: text + doc: | + Check that a file contains a string. - then: file {filename} doesn't contain "{data}" impl: @@ -221,6 +270,8 @@ types: filename: path data: text + doc: | + Check that a file does not contain a string. - then: file {filename} matches regex /{regex}/ impl: @@ -231,6 +282,8 @@ types: filename: path regex: text + doc: | + Check that file content matches a regular expression. - then: file {filename} matches regex "{regex}" impl: @@ -241,6 +294,8 @@ types: filename: path regex: text + doc: | + Check that file content matches a regular expression. - then: files {filename1} and {filename2} match impl: @@ -251,4 +306,5 @@ types: filename1: path filename2: path - + doc: | + Check that two files have the same content. diff --git a/share/common/lib/runcmd.yaml b/share/common/lib/runcmd.yaml index 7be2c05..b20eac8 100644 --- a/share/common/lib/runcmd.yaml +++ b/share/common/lib/runcmd.yaml @@ -8,6 +8,8 @@ function: subplotlib::steplibrary::runcmd::helper_script types: script: file + doc: | + Install a helper script from an embedded file. - given: srcdir is in the PATH impl: @@ -15,6 +17,10 @@ function: runcmd_helper_srcdir_path rust: function: subplotlib::steplibrary::runcmd::helper_srcdir_path + doc: | + Make sure the source directory of the project being testes is on + the shell PATH. This makes it easy for tests to invoke programs + from the source tree. - when: I run {argv0}{args:text} impl: @@ -22,6 +28,8 @@ function: runcmd_step rust: function: subplotlib::steplibrary::runcmd::run + doc: | + Run a program, and make sure it succeeds. - when: I run, in {dirname}, {argv0}{args} impl: @@ -33,6 +41,9 @@ dirname: path argv0: word args: text + doc: | + Change to a different directory and run a program, and make sure + it succeeds; - when: I try to run {argv0}{args:text} impl: @@ -40,6 +51,9 @@ function: runcmd_try_to_run rust: function: subplotlib::steplibrary::runcmd::try_to_run + doc: | + Run a program, but allow it to fail. Other steps can check if it + succeeded. - when: I try to run, in {dirname}, {argv0}{args} impl: @@ -51,6 +65,9 @@ dirname: path argv0: word args: text + doc: | + Change to a different directory and run a program, but allow it to + fail. Other steps can check if it succeeded. # Steps to examine exit code of latest command. @@ -62,6 +79,9 @@ function: subplotlib::steplibrary::runcmd::exit_code_is types: exit: int + doc: | + Make sure the latest command run by `lib/runcmd` had a specific + exit code. - then: exit code is not {exit} impl: @@ -71,6 +91,9 @@ function: subplotlib::steplibrary::runcmd::exit_code_is_not types: exit: int + doc: | + Make sure the latest command run by `lib/runcmd` did not have a + specific exit code. - then: command is successful impl: @@ -78,6 +101,9 @@ function: runcmd_exit_code_is_zero rust: function: subplotlib::steplibrary::runcmd::exit_code_is_zero + doc: | + Make sure the latest command run by `lib/runcmd` indicated the + command succeeded. - then: command fails impl: @@ -85,6 +111,9 @@ function: runcmd_exit_code_is_nonzero rust: function: subplotlib::steplibrary::runcmd::exit_code_is_nonzero + doc: | + Make sure the latest command run by `lib/runcmd` indicated the + command failed. # Steps to examine stdout/stderr for exact content. @@ -94,6 +123,9 @@ function: runcmd_stdout_is rust: function: subplotlib::steplibrary::runcmd::stdout_is + doc: | + Make sure the standard output of the latest command run by + `lib/runcmd` is exactly as desired. - then: 'stdout isn''t exactly "{text:text}"' impl: @@ -101,6 +133,9 @@ function: runcmd_stdout_isnt rust: function: subplotlib::steplibrary::runcmd::stdout_isnt + doc: | + Make sure the standard output of the latest command run by + `lib/runcmd` is different from what is not wanted. - then: stderr is exactly "{text:text}" impl: @@ -108,6 +143,9 @@ function: runcmd_stderr_is rust: function: subplotlib::steplibrary::runcmd::stderr_is + doc: | + Make sure the standard error output of the latest command run by + `lib/runcmd` is exactly as desired. - then: 'stderr isn''t exactly "{text:text}"' impl: @@ -115,6 +153,9 @@ function: runcmd_stderr_isnt rust: function: subplotlib::steplibrary::runcmd::stderr_isnt + doc: | + Make sure the standard error output of the latest command run by + `lib/runcmd` is different from what is not wanted. # Steps to examine stdout/stderr for sub-strings. @@ -124,6 +165,9 @@ function: runcmd_stdout_contains rust: function: subplotlib::steplibrary::runcmd::stdout_contains + doc: | + Make sure the standard output of the latest command run by + `lib/runcmd` contains the desired sub-string. - then: 'stdout doesn''t contain "{text:text}"' impl: @@ -131,6 +175,9 @@ function: runcmd_stdout_doesnt_contain rust: function: subplotlib::steplibrary::runcmd::stdout_doesnt_contain + doc: | + Make sure the standard output of the latest command run by + `lib/runcmd` does not contain the sub-string. - then: stderr contains "{text:text}" impl: @@ -138,6 +185,9 @@ function: runcmd_stderr_contains rust: function: subplotlib::steplibrary::runcmd::stderr_contains + doc: | + Make sure the standard output of the latest command run by + `lib/runcmd` contains the desired sub-string. - then: 'stderr doesn''t contain "{text:text}"' impl: @@ -145,6 +195,9 @@ function: runcmd_stderr_doesnt_contain rust: function: subplotlib::steplibrary::runcmd::stderr_doesnt_contain + doc: | + Make sure the standard error output of the latest command run by + `lib/runcmd` does not contain the sub-string. # Steps to match stdout/stderr against regular expressions. @@ -154,6 +207,9 @@ function: runcmd_stdout_matches_regex rust: function: subplotlib::steplibrary::runcmd::stdout_matches_regex + doc: | + Make sure the standard output of the latest command run by + `lib/runcmd` matches the desired regular expression. - then: stdout doesn't match regex {regex:text} impl: @@ -161,6 +217,9 @@ function: runcmd_stdout_doesnt_match_regex rust: function: subplotlib::steplibrary::runcmd::stdout_doesnt_match_regex + doc: | + Make sure the standard output of the latest command run by + `lib/runcmd` does not match a regular expression. - then: stderr matches regex {regex:text} impl: @@ -168,6 +227,9 @@ function: runcmd_stderr_matches_regex rust: function: subplotlib::steplibrary::runcmd::stderr_matches_regex + doc: | + Make sure the standard error output of the latest command run by + `lib/runcmd` matches the desired regular expression. - then: stderr doesn't match regex {regex:text} impl: @@ -175,3 +237,6 @@ function: runcmd_stderr_doesnt_match_regex rust: function: subplotlib::steplibrary::runcmd::stderr_doesnt_match_regex + doc: | + Make sure the standard error output of the latest command run by + `lib/runcmd` does not match a regular expression. diff --git a/share/python/lib/daemon.yaml b/share/python/lib/daemon.yaml index acca151..e385880 100644 --- a/share/python/lib/daemon.yaml +++ b/share/python/lib/daemon.yaml @@ -2,6 +2,8 @@ impl: python: function: daemon_no_such_process + doc: | + Ensure a given process is not running. - given: a daemon helper shell script {filename} impl: @@ -9,88 +11,155 @@ function: _daemon_shell_script types: filename: file + doc: | + Install a helper script from an embedded file. - when: I start "{path}{args:text}" as a background process as {name}, on port {port} impl: python: function: daemon_start_on_port + doc: | + Start a process in the background (as a daemon) and wait until it + listens on its assigned port. - when: I start "(?P<path>[^ "]+)(?P<args>[^"]*)" as a background process as (?P<name>[^,]+), on port (?P<port>\d+), with environment (?P<env>.*) regex: true + types: + args: text + path: path + name: text + port: uint + env: text impl: python: function: daemon_start_on_port + doc: | + Start a process in the background (as a daemon) and wait until it + listens on its assigned port. Remember the process under the given + name. - when: I try to start "{path}{args:text}" as {name}, on port {port} impl: python: function: _daemon_start_soonish cleanup: _daemon_stop_soonish + doc: | + Try to start a background process (as a daemon), but don't fail if + starting it fails. - when: I try to start "(?P<path>[^ "]+)(?P<args>[^"]*)" as (?P<name>[^,]+), on port (?P<port>\d+), with environment (?P<env>.*) regex: true + types: + path: path + args: text + name: text + port: uint + env: text impl: python: function: _daemon_start_soonish cleanup: _daemon_stop_soonish + doc: | + Start a process in the background (as a daemon) and wait until it + listens on its assigned port. Remember the process under the given + name. Don't fail if this fails. - when: I start "{path}{args:text}" as a background process as {name} impl: python: function: _daemon_start + doc: | + Start a process in the background (as a daemon). Remember the + process under the given name. Don't fail if this fails. - when: I start "(?P<path>[^ "]+)(?P<args>[^"]*)" as a background process as (?P<name>[^,]+), with environment (?P<env>.*) regex: true + types: + path: path + args: text + name: text + env: text impl: python: function: _daemon_start + doc: | + Start a process in the background (as a daemon), with specific + environment variables set. Remember the process under the given + name. Don't fail if this fails. - when: I stop background process {name} impl: python: function: daemon_stop + doc: | + Stop a background process that was started earlier with the given + name. - when: daemon {name} has produced output impl: python: function: daemon_has_produced_output + doc: | + Wait until the named daemon has produced output to its stdout or + stderr. - then: a process "{args:text}" is running impl: python: function: daemon_process_exists + doc: | + Check that a given process is running. - then: there is no "{args:text}" process impl: python: function: daemon_no_such_process + doc: | + Check that a given process is not running. - then: starting daemon fails with "{message:text}" impl: python: function: daemon_start_fails_with + doc: | + Check that starting a daemon previously failed, and the error + message contains the given text. - then: starting the daemon succeeds impl: python: function: daemon_start_succeeds + doc: | + Check that staring a daemon previous succeeded. - then: daemon {name} stdout is "{text:text}" impl: python: function: daemon_stdout_is + doc: | + Check that the named daemon has written exactly the given text to + its stdout. - then: daemon {name} stdout contains "{text:text}" impl: python: function: daemon_stdout_contains + doc: | + Check that the named daemon has written the given text to its + stdout, possibly among other text. - then: daemon {name} stdout doesn't contain "{text:text}" impl: python: function: daemon_stdout_doesnt_contain + doc: | + Check that the named daemon has not written the given text to its + stdout. - then: daemon {name} stderr is "{text:text}" impl: python: function: daemon_stderr_is + doc: | + Check that the named daemon has written exactly the given text to + its stderr. diff --git a/share/python/lib/runcmd.py b/share/python/lib/runcmd.py index c4a6a12..6a4965f 100644 --- a/share/python/lib/runcmd.py +++ b/share/python/lib/runcmd.py @@ -70,7 +70,6 @@ def runcmd_run(ctx, argv, **kwargs): logging.debug("runcmd_run: running command") log_value("argv", 1, dict(enumerate(argv))) - log_value("env", 1, env) log_value("kwargs:", 1, kwargs) p = subprocess.Popen(argv, env=env, **kwargs) diff --git a/share/python/template/scenarios.py b/share/python/template/scenarios.py index b215133..a6aa9f4 100644 --- a/share/python/template/scenarios.py +++ b/share/python/template/scenarios.py @@ -42,12 +42,14 @@ class Step: self._cleanup(ctx, **self._args) +_logged_env = False + + class Scenario: def __init__(self, ctx): self._title = None self._steps = [] self._ctx = ctx - self._logged_env = False def get_title(self): return self._title @@ -91,7 +93,9 @@ class Scenario: os.environ.update(overrides) os.environ.update(extra_env) - if not self._logged_env: - self._logged_env = True + global _logged_env + if not _logged_env: + _logged_env = True log_value("extra_env", 0, dict(extra_env)) + log_value("overrides", 0, dict(overrides)) log_value("os.environ", 0, dict(os.environ)) diff --git a/share/rust/lib/datadir.yaml b/share/rust/lib/datadir.yaml index f4c313b..b77e5ef 100644 --- a/share/rust/lib/datadir.yaml +++ b/share/rust/lib/datadir.yaml @@ -9,9 +9,16 @@ function: subplotlib::steplibrary::datadir::datadir_has_enough_space types: bytes: uint + doc: | + Check the test data directory has at least the given amount of + free space expressed as bytes. + - given: datadir has at least {megabytes}M of space impl: rust: function: subplotlib::steplibrary::datadir::datadir_has_enough_space_megabytes types: megabytes: uint + doc: | + Check the test data directory has at least the given amount of + free space expressed as megabytes. diff --git a/share/rust/template/macros.rs.tera b/share/rust/template/macros.rs.tera index 104eb23..cc1d9c4 100644 --- a/share/rust/template/macros.rs.tera +++ b/share/rust/template/macros.rs.tera @@ -27,5 +27,5 @@ ) {% endif -%} {% endfor -%} - .build(format!("{} {}", "{{step.kind | lower}}", base64_decode("{{step.text | base64}}"))) + .build(format!("{} {}", "{{step.kind | lower}}", base64_decode("{{step.text | base64}}")), {{ step.origin | location }}) {%- endmacro builder -%} diff --git a/share/rust/template/template.rs.tera b/share/rust/template/template.rs.tera index 65fb755..447129c 100644 --- a/share/rust/template/template.rs.tera +++ b/share/rust/template/template.rs.tera @@ -30,7 +30,7 @@ lazy_static! { #[test] #[allow(non_snake_case)] fn {{ scenario.title | nameslug }}() { - let mut scenario = Scenario::new(&base64_decode("{{scenario.title | base64}}")); + let mut scenario = Scenario::new(&base64_decode("{{scenario.title | base64}}"), {{ scenario.origin | location }}); {% for step in scenario.steps %} let step = {{ macros::builder(stepfn=step.function, step=step) }}; {%- if step.cleanup %} diff --git a/share/subplot.css b/share/subplot.css new file mode 100644 index 0000000..292a5f7 --- /dev/null +++ b/share/subplot.css @@ -0,0 +1,40 @@ +div.toc ol { + list-style-type: none; + padding: 0; + padding-inline-start: 2ch; +} + +pre.file { + background: yellow; + border: 10px black; + padding: 1em; +} + +div.scenario { + background: yellow; + padding: 1em; +} + +span.capture-word { + font-family: monospace; +} +span.capture-text { + font-family: monospace; +} +span.capture-int { + font-weight: bold; +} +span.capture-uint { + font-weight: bold; +} +span.capture-number { + font-weight: bold; +} +span.capture-file { + font-family: monospace; +} + +span.capture-path { + font-family: monospace; + font-weight: bold; +} |