From 15a4c40852cca4693c8b3b483bc0280efb03cd42 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Tue, 28 Sep 2021 21:54:18 +0100 Subject: direnv: Add basic flake and direnv setup Signed-off-by: Daniel Silverstone --- .envrc | 1 + flake.lock | 40 ++++++++++++++++++++++++++++++++++++++++ flake.nix | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..38de242 --- /dev/null +++ b/flake.lock @@ -0,0 +1,40 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1631561581, + "narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1632639184, + "narHash": "sha256-fRLxre+gPxIkjFVj17O68pyAWU1cxT20XFOiulIWzRw=", + "path": "/nix/store/mz8vpyg587llb4c802w96m956icc39vm-source", + "rev": "fd8a7fd07da0f3fc0e27575891f45c2f88e5dd44", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..c19f3bc --- /dev/null +++ b/flake.nix @@ -0,0 +1,37 @@ +# This is a nix "flake" which is used both by `direnv` and to offer +# a quick way to acquire `subplot` in a nix/nixos environment. +# If you have `direnv` support, just allow this tree and you should +# be able to develop `subplot`. If you use this as a package flake +# then the `subplot` package derivation is available in the usual way + +{ + inputs = { flake-utils.url = "github:numtide/flake-utils"; }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + test-python-packages = python-packages: + with python-packages; + [ requests ]; + + in { + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + stdenv + graphviz + plantuml + pandoc + texlive.combined.scheme-medium + daemonize + librsvg + (python3.withPackages test-python-packages) + ]; + shellHook = '' + export SUBPLOT_DOT_PATH=${pkgs.graphviz}/bin/dot + export SUBPLOT_JAVA_PATH=${pkgs.jre}/bin/java + export SUBPLOT_PLANTUML_JAR_PATH=${pkgs.plantuml}/lib/plantuml.jar + ''; + }; + }); +} -- cgit v1.2.1 From 09983e35984755339ce379e41d6f9dcd5587f7bf Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 9 Oct 2021 10:25:30 +0100 Subject: check: Add support for newer pandoc without pandoc-citeproc Signed-off-by: Daniel Silverstone --- check | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/check b/check index 601f900..b7c5164 100755 --- a/check +++ b/check @@ -90,6 +90,11 @@ class Runcmd: p = self.runcmd_unchecked(["which", name], stdout=DEVNULL) return p.returncode == 0 + def pandoc_is_newer(self): + """Is pandoc new enough for --citeproc""" + p = self.runcmd(["pandoc", "--help"], stdout=PIPE) + return "--citeproc" in p.stdout.decode("UTF-8") + def cargo(self, args, **kwargs): """Run cargo with arguments.""" self.runcmd(["cargo"] + args, **kwargs) @@ -329,6 +334,10 @@ def check_tooling(r): ] for command in commands: if not r.got_command(command): + if command == "pandoc-citeproc": + if r.pandoc_is_newer(): + r.msg(" Fortunately pandoc is new enough for --citeproc, no need for pandoc-citeproc") + continue sys.exit(f"can't find {command}, which is needed for test suite") if not r.got_command("daemonize") and not r.got_command("/usr/sbin/daemonize"): -- cgit v1.2.1 From 08aa8ad930c1488285263145f01c7e8a9dca59dc Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Fri, 8 Oct 2021 19:29:14 +0100 Subject: templates: Inherit environment for the most part We inherit all the environment except we override a few variables in order to provide some level of consistency. Signed-off-by: Daniel Silverstone --- share/bash/template/template.sh.tera | 14 +------------- share/python/template/scenarios.py | 6 ++---- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/share/bash/template/template.sh.tera b/share/bash/template/template.sh.tera index da748be..12d72ea 100644 --- a/share/bash/template/template.sh.tera +++ b/share/bash/template/template.sh.tera @@ -163,20 +163,8 @@ scenario_{{ loop.index }}() { {% endfor %} ############################################################################# -# Make the environment minimal. - -# Write to stdout the names of all environment variables, one per -# line. Handle also cases where the value of an environment variable -# contains newlines. -envnames() -{ - env -0 | sed -z 's/^\([^=]\+\)=.*$/\1/' | tr '\0' '\n' -} +# Update the environment -# Unset all environment variables. At the beginning of each scenario, -# some additional ones will be set to the per-scenario directory. -unset $(envnames) -export PATH=/bin:/usr/bin export SHELL=/bin/sh # Include any configured environment variables diff --git a/share/python/template/scenarios.py b/share/python/template/scenarios.py index 96dcc69..b215133 100644 --- a/share/python/template/scenarios.py +++ b/share/python/template/scenarios.py @@ -83,15 +83,13 @@ class Scenario: def _set_environment_variables_to(self, scendir, extra_env): log_value = globals()["log_value"] - minimal = { - "PATH": "/bin:/usr/bin", + overrides = { "SHELL": "/bin/sh", "HOME": scendir, "TMPDIR": scendir, } - os.environ.clear() - os.environ.update(minimal) + os.environ.update(overrides) os.environ.update(extra_env) if not self._logged_env: self._logged_env = True -- cgit v1.2.1 From 6e72eae061dd88ff6d871a6c9d657569b03888fa Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Fri, 8 Oct 2021 20:57:33 +0100 Subject: subplotlib: Do not override environment entirely As per the Python template, do not override the full environment Signed-off-by: Daniel Silverstone --- subplotlib/src/steplibrary/runcmd.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/subplotlib/src/steplibrary/runcmd.rs b/subplotlib/src/steplibrary/runcmd.rs index 7075b68..2bf8c56 100644 --- a/subplotlib/src/steplibrary/runcmd.rs +++ b/subplotlib/src/steplibrary/runcmd.rs @@ -46,8 +46,13 @@ impl ContextElement for Runcmd { self.env.drain(); self.paths.drain(..); self.env.insert("SHELL".into(), "/bin/sh".into()); - self.env - .insert("PATH".into(), env::join_paths(DEFAULT_PATHS.iter())?); + self.env.insert( + "PATH".into(), + env::var_os("PATH") + .map(Ok) + .unwrap_or_else(|| env::join_paths(DEFAULT_PATHS.iter()))?, + ); + // Having assembled the 'default' environment, override it with injected // content from the calling environment. for (k, v) in env::vars_os() { @@ -125,7 +130,6 @@ pub fn try_to_run_in(context: &ScenarioContext, dirname: &str, argv0: &str, args let mut proc = Command::new(argv0); proc.args(&shell_words::split(args)?); proc.current_dir(&datadir); - proc.env_clear(); proc.env("HOME", &datadir); proc.env("TMPDIR", &datadir); -- cgit v1.2.1 From 630c7ffc4605f7c166d4e86d4147f9e3747e8cbd Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Fri, 8 Oct 2021 20:57:53 +0100 Subject: daemon: Support finding the binary on the PATH Signed-off-by: Daniel Silverstone --- share/python/lib/daemon.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/share/python/lib/daemon.py b/share/python/lib/daemon.py index 9f1dfc0..1d0d443 100644 --- a/share/python/lib/daemon.py +++ b/share/python/lib/daemon.py @@ -25,7 +25,7 @@ def daemon_start_on_port(ctx, path=None, args=None, name=None, port=None, env=No # Start a daemon after a little wait. This is used only for testing the # port-waiting code. def _daemon_start_soonish(ctx, path=None, args=None, name=None, port=None, env=None): - _daemon_start(ctx, path=os.path.abspath(path), args=args, name=name, env=env) + _daemon_start(ctx, path=path, args=args, name=name, env=env) daemon = ctx.declare("_daemon") # Store the PID of the process we just started so that _daemon_stop_soonish @@ -53,6 +53,18 @@ def _daemon_stop_soonish(ctx, path=None, args=None, name=None, port=None, env=No logging.warning("Process did not actually exist (anymore?)") +# Find a binary akin to the `which` or `whereis` command +def _daemon_whereis(path): + if "/" in path: + logging.debug(f"Not using PATH for daemon {path}") + return path + for prefix in os.environ["PATH"].split(":"): + absolute = os.path.join(prefix, path) + logging.debug(f"Checking for {absolute}") + if os.access(absolute, os.X_OK) and os.path.isfile(absolute): + return absolute + return path + # Start a daeamon, get its PID. Don't wait for a port or anything. This is # meant for background processes that don't have port. Useful for testing the # lib/daemon library of Subplot, but not much else. @@ -63,6 +75,7 @@ def _daemon_start(ctx, path=None, args=None, name=None, env=None): runcmd_get_stderr = globals()["runcmd_get_stderr"] runcmd_prepend_to_path = globals()["runcmd_prepend_to_path"] + path = _daemon_whereis(path) path = os.path.abspath(path) argv = [path] + args.split() -- cgit v1.2.1 From bcf9975fa19050e07d6724992a75ebde4b0156c5 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Fri, 8 Oct 2021 20:58:22 +0100 Subject: tests: Fix up use of absolute paths for false/true/echo etc. Signed-off-by: Daniel Silverstone --- subplotlib/runcmd.md | 24 ++++++++++++------------ subplotlib/tests/runcmd.rs | 40 ++++++++++++++++++++-------------------- tests/python/daemon.md | 18 +++++++++--------- tests/python/runcmd.md | 24 ++++++++++++------------ 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/subplotlib/runcmd.md b/subplotlib/runcmd.md index d6210fb..50e41f6 100644 --- a/subplotlib/runcmd.md +++ b/subplotlib/runcmd.md @@ -22,8 +22,8 @@ they're verified. It uses the steps and functions from the command, then examine the exit code, standard output (stdout for short), or standard error output (stderr) of the command. -The scenarios use the Unix commands `/bin/true` and `/bin/false` to -generate exit codes, and `/bin/echo` to produce stdout. To generate +The scenarios use the Unix commands `true` and `false` to +generate exit codes, and `echo` to produce stdout. To generate stderr, they use the little helper script below. ```{#err.sh .file .sh .numberLines} @@ -40,7 +40,7 @@ variations. ## Successful execution ```scenario -when I run /bin/true +when I run true then exit code is 0 and command is successful ``` @@ -49,7 +49,7 @@ and command is successful ```scenario given a directory xyzzy -when I run, in xyzzy, /bin/pwd +when I run, in xyzzy, pwd then exit code is 0 then command is successful then stdout contains "/xyzzy" @@ -58,7 +58,7 @@ then stdout contains "/xyzzy" ## Failed execution ```scenario -when I try to run /bin/false +when I try to run false then exit code is not 0 and command fails ``` @@ -67,7 +67,7 @@ and command fails ```scenario given a directory xyzzy -when I try to run, in xyzzy, /bin/false +when I try to run, in xyzzy, false then exit code is not 0 and command fails ``` @@ -84,7 +84,7 @@ to the reader what's inside. Also, C-style string escapes are understood. ```scenario -when I run /bin/echo hello, world +when I run echo hello, world then stdout is exactly "hello, world\n" ``` @@ -102,7 +102,7 @@ Exact string comparisons are not always enough, so we can verify a sub-string is in output. ```scenario -when I run /bin/echo hello, world +when I run echo hello, world then stdout contains "world\n" and exit code is 0 ``` @@ -122,7 +122,7 @@ regular expression. Note that the regular expression is not delimited and does not get any C-style string escaped decoded. ```scenario -when I run /bin/echo hello, world +when I run echo hello, world then stdout matches regex world$ ``` @@ -142,7 +142,7 @@ have something we want to avoid. ## Check stdout is not exactly something ```scenario -when I run /bin/echo hi +when I run echo hi then stdout isn't exactly "hello, world\n" ``` @@ -157,7 +157,7 @@ then stderr isn't exactly "hello, world\n" ## Check stdout doesn't contain sub-string ```scenario -when I run /bin/echo hi +when I run echo hi then stdout doesn't contain "world" ``` @@ -172,7 +172,7 @@ then stderr doesn't contain "world" ## Check stdout doesn't match regular expression ```scenario -when I run /bin/echo hi +when I run echo hi then stdout doesn't match regex world$ ``` diff --git a/subplotlib/tests/runcmd.rs b/subplotlib/tests/runcmd.rs index f18f463..0c677ba 100644 --- a/subplotlib/tests/runcmd.rs +++ b/subplotlib/tests/runcmd.rs @@ -18,8 +18,8 @@ fn successful_execution() { let step = subplotlib::steplibrary::runcmd::run::Builder::default() .argv0( - // "/bin/true" - &base64_decode("L2Jpbi90cnVl"), + // "true" + &base64_decode("dHJ1ZQ=="), ) .args( // "" @@ -62,8 +62,8 @@ fn successful_execution_in_a_sub_directory() { &base64_decode("eHl6enk="), ) .argv0( - // "/bin/pwd" - &base64_decode("L2Jpbi9wd2Q="), + // "pwd" + &base64_decode("cHdk"), ) .args( // "" @@ -100,8 +100,8 @@ fn failed_execution() { let step = subplotlib::steplibrary::runcmd::try_to_run::Builder::default() .argv0( - // "/bin/false" - &base64_decode("L2Jpbi9mYWxzZQ=="), + // "false" + &base64_decode("ZmFsc2U="), ) .args( // "" @@ -144,8 +144,8 @@ fn failed_execution_in_a_sub_directory() { &base64_decode("eHl6enk="), ) .argv0( - // "/bin/false" - &base64_decode("L2Jpbi9mYWxzZQ=="), + // "false" + &base64_decode("ZmFsc2U="), ) .args( // "" @@ -176,8 +176,8 @@ fn check_stdout_is_exactly_as_wanted() { let step = subplotlib::steplibrary::runcmd::run::Builder::default() .argv0( - // "/bin/echo" - &base64_decode("L2Jpbi9lY2hv"), + // "echo" + &base64_decode("ZWNobw=="), ) .args( // " hello, world" @@ -254,8 +254,8 @@ fn check_stdout_using_sub_string_search() { let step = subplotlib::steplibrary::runcmd::run::Builder::default() .argv0( - // "/bin/echo" - &base64_decode("L2Jpbi9lY2hv"), + // "echo" + &base64_decode("ZWNobw=="), ) .args( // " hello, world" @@ -337,8 +337,8 @@ fn check_stdout_using_regular_expressions() { let step = subplotlib::steplibrary::runcmd::run::Builder::default() .argv0( - // "/bin/echo" - &base64_decode("L2Jpbi9lY2hv"), + // "echo" + &base64_decode("ZWNobw=="), ) .args( // " hello, world" @@ -415,8 +415,8 @@ fn check_stdout_is_not_exactly_something() { let step = subplotlib::steplibrary::runcmd::run::Builder::default() .argv0( - // "/bin/echo" - &base64_decode("L2Jpbi9lY2hv"), + // "echo" + &base64_decode("ZWNobw=="), ) .args( // " hi" @@ -493,8 +493,8 @@ fn check_stdout_doesn_t_contain_sub_string() { let step = subplotlib::steplibrary::runcmd::run::Builder::default() .argv0( - // "/bin/echo" - &base64_decode("L2Jpbi9lY2hv"), + // "echo" + &base64_decode("ZWNobw=="), ) .args( // " hi" @@ -571,8 +571,8 @@ fn check_stdout_doesn_t_match_regular_expression() { let step = subplotlib::steplibrary::runcmd::run::Builder::default() .argv0( - // "/bin/echo" - &base64_decode("L2Jpbi9lY2hv"), + // "echo" + &base64_decode("ZWNobw=="), ) .args( // " hi" diff --git a/tests/python/daemon.md b/tests/python/daemon.md index 285f9f8..6d9b8f4 100644 --- a/tests/python/daemon.md +++ b/tests/python/daemon.md @@ -17,11 +17,11 @@ This scenario starts a background process, verifies it's started, and verifies it's terminated after the scenario ends. ~~~scenario -given there is no "/bin/sleep 12765" process -when I start "/bin/sleep 12765" as a background process as sleepyhead -then a process "/bin/sleep 12765" is running +given there is no "sleep 12765" process +when I start "sleep 12765" as a background process as sleepyhead +then a process "sleep 12765" is running when I stop background process sleepyhead -then there is no "/bin/sleep 12765" process +then there is no "sleep 12765" process ~~~ @@ -68,12 +68,12 @@ This scenario verifies that if the background process never starts listening on its port, the daemon library handles that correctly. ~~~scenario -given there is no "/bin/sleep 12765" process -when I try to start "/bin/sleep 12765" as sleepyhead, on port 8888 +given there is no "sleep 12765" process +when I try to start "sleep 12765" as sleepyhead, on port 8888 then starting daemon fails with "ConnectionRefusedError" -then a process "/bin/sleep 12765" is running +then a process "sleep 12765" is running when I stop background process sleepyhead -then there is no "/bin/sleep 12765" process +then there is no "sleep 12765" process ~~~ @@ -101,7 +101,7 @@ have had time to produce it yet. ~~~{#chatty-daemon.sh .file .sh .numberLines} -#!/bin/bash +#!/usr/bin/env bash set -euo pipefail diff --git a/tests/python/runcmd.md b/tests/python/runcmd.md index 68465a8..7b88dd3 100644 --- a/tests/python/runcmd.md +++ b/tests/python/runcmd.md @@ -14,8 +14,8 @@ they're verified. It uses the steps and functions from the command, then examine the exit code, standard output (stdout for short), or standard error output (stderr) of the command. -The scenarios use the Unix commands `/bin/true` and `/bin/false` to -generate exit codes, and `/bin/echo` to produce stdout. To generate +The scenarios use the Unix commands `true` and `false` to +generate exit codes, and `echo` to produce stdout. To generate stderr, they use the little helper script below. ~~~{#err.sh .file .sh .numberLines} @@ -32,7 +32,7 @@ variations. ## Successful execution ~~~scenario -when I run /bin/true +when I run true then exit code is 0 and command is successful ~~~ @@ -41,7 +41,7 @@ and command is successful ~~~scenario given a directory xyzzy -when I run, in xyzzy, /bin/pwd +when I run, in xyzzy, pwd then exit code is 0 then command is successful then stdout contains "/xyzzy" @@ -50,7 +50,7 @@ then stdout contains "/xyzzy" ## Failed execution ~~~scenario -when I try to run /bin/false +when I try to run false then exit code is not 0 and command fails ~~~ @@ -59,7 +59,7 @@ and command fails ~~~scenario given a directory xyzzy -when I try to run, in xyzzy, /bin/false +when I try to run, in xyzzy, false then exit code is not 0 and command fails ~~~ @@ -97,7 +97,7 @@ to the reader what's inside. Also, C-style string escapes are understood. ~~~scenario -when I run /bin/echo hello, world +when I run echo hello, world then stdout is exactly "hello, world\n" ~~~ @@ -115,7 +115,7 @@ Exact string comparisons are not always enough, so we can verify a sub-string is in output. ~~~scenario -when I run /bin/echo hello, world +when I run echo hello, world then stdout contains "world\n" and exit code is 0 ~~~ @@ -135,7 +135,7 @@ regular expression. Note that the regular expression is not delimited and does not get any C-style string escaped decoded. ~~~scenario -when I run /bin/echo hello, world +when I run echo hello, world then stdout matches regex world$ ~~~ @@ -155,7 +155,7 @@ have something we want to avoid. ## Check stdout is not exactly something ~~~scenario -when I run /bin/echo hi +when I run echo hi then stdout isn't exactly "hello, world\n" ~~~ @@ -170,7 +170,7 @@ then stderr isn't exactly "hello, world\n" ## Check stdout doesn't contain sub-string ~~~scenario -when I run /bin/echo hi +when I run echo hi then stdout doesn't contain "world" ~~~ @@ -185,7 +185,7 @@ then stderr doesn't contain "world" ## Check stdout doesn't match regular expression ~~~scenario -when I run /bin/echo hi +when I run echo hi then stdout doesn't match regex world$ ~~~ -- cgit v1.2.1 From b61f72f4bbe8fa9d1371b26a0a86738c6e62a8c3 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Fri, 8 Oct 2021 20:58:41 +0100 Subject: examples: Fix up absolute paths on echo example Signed-off-by: Daniel Silverstone --- examples/echo/echo.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/echo/echo.md b/examples/echo/echo.md index 8bf5405..f7a43b3 100644 --- a/examples/echo/echo.md +++ b/examples/echo/echo.md @@ -12,14 +12,14 @@ Introduction **echo**(1) is a Unix command line tool, which writes its command line arguments to the standard output. This is a simple acceptance test -suite for the `/bin/echo` implementation. +suite for the `echo` implementation. For more information, see [@foo2020]. No arguments ============================================================================= -Run `/bin/echo` without arguments. +Run `echo` without arguments. ```scenario when user runs echo without arguments @@ -31,7 +31,7 @@ then standard error is empty Hello, world ============================================================================= -This scenario runs `/bin/echo` to produce the output "hello, world". +This scenario runs `echo` to produce the output "hello, world". ```scenario when user runs echo with arguments hello, world -- cgit v1.2.1 From dde8f03212e7605b693d75868733979f9bbc425d Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Tue, 12 Oct 2021 19:52:54 +0100 Subject: DECISIONS: Record environment clear decision Signed-off-by: Daniel Silverstone --- DECISIONS.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/DECISIONS.md b/DECISIONS.md index 00a41fb..fe67b1b 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -25,6 +25,18 @@ MSRV forward as and when our client projects move forward. Who: Daniel, Lars +## Do not clear/override all environment variables + +Date: 2021-10-08 + +What: We decided that the environment being "clean" is more the +resposibility of the caller of the test suite than of the test +suite itself. As such, we have decided to only set SHELL, HOME, +and TMPDIR. Importantly we do not override PATH etc. so that +things will work on NixOS etc. + +Who: Daniel, Lars. + ## Start decision log Date: 2021-09-11 -- cgit v1.2.1