summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2020-05-20 10:14:00 +0300
committerLars Wirzenius <liw@liw.fi>2020-05-20 10:59:54 +0300
commit0a073fa0b2a7e456d3bff9c9bfe930e8b3ee7449 (patch)
treeec50e222e1c9b139d08a0eae6911cbe87e0fb485
parent6ca305d000360de74e686d801c638086c876d08a (diff)
downloadsubplot-0a073fa0b2a7e456d3bff9c9bfe930e8b3ee7449.tar.gz
refactor: move things from subplot.{py,yaml} to separate files
files.{py,yaml} contain re-usable steps for handling files. runcmd.{py,yaml} contain re-usable steps for running commands and inspecting results.
-rw-r--r--files.py81
-rw-r--r--files.yaml36
-rw-r--r--runcmd.py84
-rw-r--r--runcmd.yaml14
-rw-r--r--subplot.md2
-rw-r--r--subplot.py145
-rw-r--r--subplot.yaml51
7 files changed, 230 insertions, 183 deletions
diff --git a/files.py b/files.py
new file mode 100644
index 0000000..9ee1739
--- /dev/null
+++ b/files.py
@@ -0,0 +1,81 @@
+# Some generic file handling step implementations.
+
+import os
+import re
+import time
+
+
+# Create a file on disk from an embedded file.
+def create_file(ctx, filename=None):
+ with open(filename, "wb") as f:
+ f.write(get_file(filename))
+
+
+# Update mtime of a file in datadir to a specific time.
+def touch_file(ctx, filename=None, y=None, mon=None, d=None, h=None, min=None, s=None):
+ t = (int(y), int(mon), int(d), int(h), int(min), int(s), -1, -1, -1)
+ ts = time.mktime(t)
+ os.utime(filename, times=(ts, ts))
+
+
+# Update mtime of a file in datadir to current time.
+def update_mtime(ctx, filename=None):
+ os.utime(filename)
+
+
+# Check that a file exists.
+def file_exists(ctx, filename=None):
+ assert_eq(os.path.exists(filename), True)
+
+
+# Check that a file does not exist.
+def file_does_not_exist(ctx, filename=None):
+ assert_eq(os.path.exists(filename), False)
+
+
+# Check that only specific files exist in the current working directory.
+def only_these_files_exist(ctx, filenames=None):
+ filenames = filenames.replace(",", "").split()
+ assert_eq(set(os.listdir(".")), set(filenames))
+
+
+# Check that a file contents match a regex.
+def file_matches(ctx, filename=None, regex=None):
+ with open(filename) as f:
+ content = f.read()
+ m = re.search(regex, content)
+ if m is None:
+ print("content:", repr(content))
+ print("regex:", repr(regex))
+ assert_eq(bool(m), True)
+
+
+# Check that a file contains a fixed string.
+def file_contains(ctx, filename=None, pattern=None):
+ with open(filename) as f:
+ content = f.read()
+ assert_eq(pattern in content, True)
+
+
+# Retrieve metadata for a file as a struct so it can be easily inspected.
+def _get_metadata(filename):
+ st = os.lstat(filename)
+ keys = ["st_dev", "st_gid", "st_ino", "st_mode", "st_mtime", "st_size", "st_uid"]
+ return {key: getattr(st, key) for key in keys}
+
+
+# Store in the context current metdata for a file.
+def remember_metadata(ctx, filename=None):
+ meta = ctx.get("remembered-metadata", {})
+ meta[filename] = _get_metadata(filename)
+ ctx["remembered-metadata"] = meta
+
+
+# Check that current metadata of a file is as stored in the context.
+def has_same_metadata_as_remembered(ctx, filename=None):
+ assert_eq(ctx["remembered-metadata"][filename], _get_metadata(filename))
+
+
+# Check that current metadata of a file is different than stored in the context.
+def has_diff_metadata_than_remembered(ctx, filename=None):
+ assert_ne(ctx["remembered-metadata"][filename], _get_metadata(filename))
diff --git a/files.yaml b/files.yaml
new file mode 100644
index 0000000..2cc53e1
--- /dev/null
+++ b/files.yaml
@@ -0,0 +1,36 @@
+- given: file {filename}
+ function: create_file
+
+- given: file (?P<filename>\S+) has modification time (?P<y>\d+)-(?P<mon>\d+)-(?P<d>\d+) (?P<h>\d+):(?P<min>\d+):(?P<s>\d+)
+ function: touch_file
+ regex: true
+
+- when: I remember the metadata for {filename}
+ function: remember_metadata
+
+- when: I touch {filename}
+ function: update_mtime
+
+- then: file {filename} exists
+ function: file_exists
+
+- then: file {filename} does not exist
+ function: file_does_not_exist
+
+- then: file (?P<filename>\S+) matches /(?P<regex>.+)/
+ function: file_matches
+ regex: true
+
+- then: file (?P<filename>\S+) contains "(?P<pattern>.+)"
+ function: file_contains
+ regex: true
+
+- then: "{filename} has the same metadata as before"
+ function: has_same_metadata_as_remembered
+
+- then: "{filename} has changed from before"
+ function: has_diff_metadata_than_remembered
+
+- then: only files (?P<filenames>.+) exist
+ function: only_these_files_exist
+ regex: true
diff --git a/runcmd.py b/runcmd.py
index e69de29..b67c6bb 100644
--- a/runcmd.py
+++ b/runcmd.py
@@ -0,0 +1,84 @@
+# Some step implementations for running commands and capturing the result.
+
+import subprocess
+
+
+# Run a command, capture its stdout, stderr, and exit code in context.
+def runcmd(ctx, argv, **kwargs):
+ p = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
+ stdout, stderr = p.communicate("")
+ ctx["argv"] = argv
+ ctx["stdout"] = stdout.decode("utf-8")
+ ctx["stderr"] = stderr.decode("utf-8")
+ ctx["exit"] = p.returncode
+
+
+# Check that latest exit code captured by runcmd was a specific one.
+def exit_code_is(ctx, wanted):
+ if ctx.get("exit") != wanted:
+ print("context:", ctx.as_dict())
+ assert_eq(ctx.get("exit"), wanted)
+
+
+# Check that latest exit code captured by runcmd was not a specific one.
+def exit_code_is_not(ctx, unwanted):
+ if ctx.get("exit") == wanted:
+ print("context:", ctx.as_dict())
+ assert_ne(ctx.get("exit"), wanted)
+
+
+# Check that latest exit code captured by runcmd was not a specific one.
+def exit_code_is_not(ctx, unwanted):
+ if ctx.get("exit") == unwanted:
+ print("context:", ctx.as_dict())
+ assert_ne(ctx.get("exit"), unwanted)
+
+
+# Check that latest exit code captured by runcmd was zero.
+def exit_code_zero(ctx):
+ exit_code_is(ctx, 0)
+
+
+# Check that latest exit code captured by runcmd was not zero.
+def exit_code_nonzero(ctx):
+ exit_code_is_not(ctx, 0)
+
+
+# Check that stdout of latest runcmd contains a specific string.
+def stdout_contains(ctx, pattern=None):
+ stdout = ctx.get("stdout", "")
+ if pattern not in stdout:
+ print("pattern:", repr(pattern))
+ print("stdout:", repr(stdout))
+ print("ctx:", ctx.as_dict())
+ assert_eq(pattern in stdout, True)
+
+
+# Check that stdout of latest runcmd does not contain a specific string.
+def stdout_does_not_contain(ctx, pattern=None):
+ stdout = ctx.get("stdout", "")
+ if pattern in stdout:
+ print("pattern:", repr(pattern))
+ print("stdout:", repr(stdout))
+ print("ctx:", ctx.as_dict())
+ assert_eq(pattern not in stdout, True)
+
+
+# Check that stderr of latest runcmd does contains a specific string.
+def stderr_contains(ctx, pattern=None):
+ stderr = ctx.get("stderr", "")
+ if pattern not in stderr:
+ print("pattern:", repr(pattern))
+ print("stderr:", repr(stderr))
+ print("ctx:", ctx.as_dict())
+ assert_eq(pattern in stderr, True)
+
+
+# Check that stderr of latest runcmd does not contain a specific string.
+def stderr_does_not_contain(ctx, pattern=None):
+ stderr = ctx.get("stderr", "")
+ if pattern not in stderr:
+ print("pattern:", repr(pattern))
+ print("stderr:", repr(stderr))
+ print("ctx:", ctx.as_dict())
+ assert_eq(pattern not in stderr, True)
diff --git a/runcmd.yaml b/runcmd.yaml
index fe51488..02e5ee1 100644
--- a/runcmd.yaml
+++ b/runcmd.yaml
@@ -1 +1,13 @@
-[]
+- then: exit code is non-zero
+ function: exit_code_nonzero
+
+- then: output matches /(?P<pattern>.+)/
+ function: stdout_contains
+ regex: true
+
+- then: stderr matches /(?P<pattern>.+)/
+ function: stderr_contains
+ regex: true
+
+- then: program finished successfully
+ function: exit_code_zero
diff --git a/subplot.md b/subplot.md
index 6459e5f..ff1d702 100644
--- a/subplot.md
+++ b/subplot.md
@@ -1846,7 +1846,9 @@ date: work in progress
bindings:
- subplot.yaml
- runcmd.yaml
+- files.yaml
functions:
- subplot.py
- runcmd.py
+- files.py
...
diff --git a/subplot.py b/subplot.py
index e586b53..9e16168 100644
--- a/subplot.py
+++ b/subplot.py
@@ -4,21 +4,6 @@ import subprocess
import time
-def create_file(ctx, filename=None):
- with open(filename, "wb") as f:
- f.write(get_file(filename))
-
-
-def touch_file(ctx, filename=None, y=None, mon=None, d=None, h=None, min=None, s=None):
- t = (int(y), int(mon), int(d), int(h), int(min), int(s), -1, -1, -1)
- ts = time.mktime(t)
- os.utime(filename, times=(ts, ts))
-
-
-def update_mtime(ctx, filename=None):
- os.utime(filename)
-
-
def try_docgen(ctx, md=None, output=None):
docgen = binary("sp-docgen")
runcmd(ctx, [docgen, md, "-o", output])
@@ -26,13 +11,13 @@ def try_docgen(ctx, md=None, output=None):
def run_docgen(ctx, md=None, output=None):
try_docgen(ctx, md=md, output=output)
- exit_code_zero(ctx)
+ exit_code_is(ctx, 0)
def run_docgen_with_date(ctx, md=None, output=None, date=None):
docgen = binary("sp-docgen")
runcmd(ctx, [docgen, md, "-o", output, "--date", date])
- exit_code_zero(ctx)
+ exit_code_is(ctx, 0)
def try_codegen_and_program(ctx, filename=None, testprog=None):
@@ -43,76 +28,52 @@ def try_codegen_and_program(ctx, filename=None, testprog=None):
def run_codegen_and_program(ctx, filename=None, testprog=None):
try_codegen_and_program(ctx, filename=filename, testprog=testprog)
- exit_code_zero(ctx)
+ exit_code_is(ctx, 0)
def run_codegen(ctx, filename=None, testprog=None):
codegen = binary("sp-codegen")
tmpldir = os.path.join(srcdir, "templates")
runcmd(ctx, [codegen, filename, "-o", testprog, "--templates", tmpldir])
- exit_code_zero(ctx)
+ exit_code_is(ctx, 0)
def run_python_test_program(ctx, testprog=None, pattern=None):
runcmd(ctx, ["python3", testprog, pattern])
- exit_code_zero(ctx)
+ exit_code_is(ctx, 0)
def run_bash_test_program(ctx, testprog=None, pattern=None):
runcmd(ctx, ["bash", testprog, pattern])
- exit_code_zero(ctx)
+ exit_code_is(ctx, 0)
def run_meta(ctx, filename=None):
meta = binary("sp-meta")
runcmd(ctx, [meta, filename])
- exit_code_zero(ctx)
+ exit_code_is(ctx, 0)
def run_pandoc_with_filter(ctx, filename=None, output=None):
sp_filter = binary("sp-filter")
runcmd(ctx, ["pandoc", "--filter", sp_filter, filename, "-o", output])
- exit_code_zero(ctx)
-
-
-def file_exists(ctx, filename=None):
- assert_eq(os.path.exists(filename), True)
-
-
-def file_does_not_exist(ctx, filename=None):
- assert_eq(os.path.exists(filename), False)
-
-
-def file_matches(ctx, filename=None, regex=None):
- with open(filename) as f:
- content = f.read()
- m = re.search(regex, content)
- if m is None:
- print("content:", repr(content))
- print("regex:", repr(regex))
- assert_eq(bool(m), True)
-
-
-def file_contains(ctx, filename=None, pattern=None):
- with open(filename) as f:
- content = f.read()
- assert_eq(pattern in content, True)
+ exit_code_is(ctx, 0)
def scenario_was_run(ctx, name=None):
- stdout_matches(ctx, pattern="\nscenario: {}\n".format(name))
+ stdout_contains(ctx, pattern="\nscenario: {}\n".format(name))
def scenario_was_not_run(ctx, name=None):
- stdout_does_not_match(ctx, pattern="\nscenario: {}\n".format(name))
+ stdout_does_not_contain(ctx, pattern="\nscenario: {}\n".format(name))
def step_was_run(ctx, keyword=None, name=None):
- stdout_matches(ctx, pattern="\n step: {} {}\n".format(keyword, name))
+ stdout_contains(ctx, pattern="\n step: {} {}\n".format(keyword, name))
def step_was_run_and_then(ctx, keyword1=None, name1=None, keyword2=None, name2=None):
- stdout_matches(
+ stdout_contains(
ctx,
pattern="\n step: {} {}\n step: {} {}".format(
keyword1, name1, keyword2, name2
@@ -121,7 +82,7 @@ def step_was_run_and_then(ctx, keyword1=None, name1=None, keyword2=None, name2=N
def cleanup_was_run(ctx, keyword1=None, name1=None, keyword2=None, name2=None):
- stdout_matches(
+ stdout_contains(
ctx,
pattern="\n cleanup: {} {}\n cleanup: {} {}\n".format(
keyword1, name1, keyword2, name2
@@ -130,86 +91,8 @@ def cleanup_was_run(ctx, keyword1=None, name1=None, keyword2=None, name2=None):
def cleanup_was_not_run(ctx, keyword=None, name=None):
- stdout_does_not_match(ctx, pattern="\n cleanup: {} {}\n".format(keyword, name))
-
-
-def exit_code_zero(ctx):
- if ctx.get("exit") != 0:
- print("context:", ctx.as_dict())
- assert_eq(ctx.get("exit"), 0)
-
-
-def exit_code_nonzero(ctx):
- assert_ne(ctx.get("exit"), 0)
+ stdout_does_not_contain(ctx, pattern="\n cleanup: {} {}\n".format(keyword, name))
def binary(basename):
return os.path.join(srcdir, "target", "debug", basename)
-
-
-def stdout_matches(ctx, pattern=None):
- stdout = ctx.get("stdout", "")
- if pattern not in stdout:
- print("pattern:", repr(pattern))
- print("stdout:", repr(stdout))
- print("ctx:", ctx.as_dict())
- assert_eq(pattern in stdout, True)
-
-
-def stdout_does_not_match(ctx, pattern=None):
- stdout = ctx.get("stdout", "")
- if pattern in stdout:
- print("pattern:", repr(pattern))
- print("stdout:", repr(stdout))
- print("ctx:", ctx.as_dict())
- assert_eq(pattern not in stdout, True)
-
-
-def stderr_matches(ctx, pattern=None):
- stderr = ctx.get("stderr", "")
- if pattern not in stderr:
- print("pattern:", repr(pattern))
- print("stderr:", repr(stderr))
- print("ctx:", ctx.as_dict())
- assert_eq(pattern in stderr, True)
-
-
-def stderr_does_not_match(ctx, pattern=None):
- stderr = ctx.get("stderr", "")
- if pattern not in stderr:
- print("pattern:", repr(pattern))
- print("stderr:", repr(stderr))
- print("ctx:", ctx.as_dict())
- assert_eq(pattern not in stderr, True)
-
-
-def _get_metadata(filename):
- st = os.lstat(filename)
- keys = ["st_dev", "st_gid", "st_ino", "st_mode", "st_mtime", "st_size", "st_uid"]
- return {key: getattr(st, key) for key in keys}
-
-
-def remember_metadata(ctx, filename=None):
- ctx["remembered-metadata"] = _get_metadata(filename)
-
-
-def has_same_metadata_as_remembered(ctx, filename=None):
- assert_eq(ctx["remembered-metadata"], _get_metadata(filename))
-
-
-def has_diff_metadata_than_remembered(ctx, filename=None):
- assert_ne(ctx["remembered-metadata"], _get_metadata(filename))
-
-
-def only_these_files_exist(ctx, filenames=None):
- filenames = filenames.replace(",", "").split()
- assert_eq(set(os.listdir(".")), set(filenames))
-
-
-def runcmd(ctx, argv):
- p = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- stdout, stderr = p.communicate("")
- ctx["argv"] = argv
- ctx["stdout"] = stdout.decode("utf-8")
- ctx["stderr"] = stderr.decode("utf-8")
- ctx["exit"] = p.returncode
diff --git a/subplot.yaml b/subplot.yaml
index 8d4187f..f35cdef 100644
--- a/subplot.yaml
+++ b/subplot.yaml
@@ -1,13 +1,3 @@
-- given: file {filename}
- function: create_file
-
-- given: file (?P<filename>\S+) has modification time (?P<y>\d+)-(?P<mon>\d+)-(?P<d>\d+) (?P<h>\d+):(?P<min>\d+):(?P<s>\d+)
- function: touch_file
- regex: true
-
-- when: I remember the metadata for {filename}
- function: remember_metadata
-
- when: I run sp-docgen {md} -o {output}
function: run_docgen
@@ -35,43 +25,9 @@
- when: I run sp-meta {filename}
function: run_meta
-- when: I touch {filename}
- function: update_mtime
-
- when: I run pandoc --filter sp-filter {filename} -o {output}
function: run_pandoc_with_filter
-- then: exit code is non-zero
- function: exit_code_nonzero
-
-- then: file {filename} exists
- function: file_exists
-
-- then: file {filename} does not exist
- function: file_does_not_exist
-
-- then: file (?P<filename>\S+) matches /(?P<regex>.+)/
- function: file_matches
- regex: true
-
-- then: file (?P<filename>\S+) contains "(?P<pattern>.+)"
- function: file_contains
- regex: true
-
-- then: "{filename} has the same metadata as before"
- function: has_same_metadata_as_remembered
-
-- then: "{filename} has changed from before"
- function: has_diff_metadata_than_remembered
-
-- then: output matches /(?P<pattern>.+)/
- function: stdout_matches
- regex: true
-
-- then: stderr matches /(?P<pattern>.+)/
- function: stderr_matches
- regex: true
-
- then: scenario "(?P<name>.+)" was run
function: scenario_was_run
regex: true
@@ -95,10 +51,3 @@
- then: cleanup for "(?P<keyword>given|when|then) (?P<name>.+)" was not run
function: cleanup_was_not_run
regex: true
-
-- then: program finished successfully
- function: exit_code_zero
-
-- then: only files (?P<filenames>.+) exist
- function: only_these_files_exist
- regex: true