summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2020-04-22 10:25:58 +0300
committerLars Wirzenius <liw@liw.fi>2020-04-22 10:50:01 +0300
commit18d2dd3b0094d373a4632c9c444d7a2267cc9164 (patch)
tree09487a98c0c7a8afacc053fec63fb3e627e26019
parente50ca0c984173ce8d84d6a4092096753512ccf26 (diff)
downloadsubplot-18d2dd3b0094d373a4632c9c444d7a2267cc9164.tar.gz
Add: a bash template and change echo.md to use it
-rw-r--r--echo.md3
-rw-r--r--echo.py28
-rw-r--r--echo.sh48
-rw-r--r--templates/bash/template.sh195
-rw-r--r--templates/bash/template.yaml2
5 files changed, 247 insertions, 29 deletions
diff --git a/echo.md b/echo.md
index 083eb8f..c335f53 100644
--- a/echo.md
+++ b/echo.md
@@ -1,8 +1,9 @@
---
title: "**echo**(1) acceptance tests"
author: The Subplot project
+template: bash
bindings: echo.yaml
-functions: echo.py
+functions: echo.sh
bibliography: echo.bib
...
diff --git a/echo.py b/echo.py
deleted file mode 100644
index a7ea7a8..0000000
--- a/echo.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import subprocess
-
-def _runcmd(ctx, argv):
- p = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- stdout, stderr = p.communicate("")
- ctx['stdout'] = stdout
- ctx['stderr'] = stderr
- ctx['exit'] = p.returncode
-
-def run_echo_without_args(ctx):
- _runcmd(ctx, ['echo'])
-
-def run_echo_with_args(ctx, args=None):
- _runcmd(ctx, ['echo', args])
-
-def exit_code_is(ctx, exit_code=None):
- assert_eq(ctx['exit'], int(exit_code))
-
-def stdout_is_a_newline(ctx):
- assert_eq(ctx['stdout'], b'\n')
-
-def stdout_is_text(ctx, text=None):
- text += '\n'
- text = text.encode()
- assert_eq(ctx['stdout'], text)
-
-def stderr_is_empty(ctx):
- assert_eq(ctx['stderr'], b'')
diff --git a/echo.sh b/echo.sh
new file mode 100644
index 0000000..44f272a
--- /dev/null
+++ b/echo.sh
@@ -0,0 +1,48 @@
+_run()
+{
+ if "$@" < /dev/null > stdout 2> stderr
+ then
+ ctx_set exit 0
+ else
+ ctx_set exit "$?"
+ fi
+ ctx_set stdout "$(cat stdout)"
+ ctx_set stderr "$(cat stderr)"
+}
+
+run_echo_without_args()
+{
+ _run echo
+}
+
+run_echo_with_args()
+{
+ args="$(cap_get args)"
+ _run echo "$args"
+}
+
+exit_code_is()
+{
+ actual_exit="$(ctx_get exit)"
+ wanted_exit="$(cap_get exit_code)"
+ assert_eq "$actual_exit" "$wanted_exit"
+}
+
+stdout_is_a_newline()
+{
+ stdout="$(ctx_get stdout)"
+ assert_eq "$stdout" "$(printf '\n')"
+}
+
+stdout_is_text()
+{
+ stdout="$(ctx_get stdout)"
+ text="$(cap_get text)"
+ assert_contains "$stdout" "$text"
+}
+
+stderr_is_empty()
+{
+ stderr="$(ctx_get stderr)"
+ assert_eq "$stderr" ""
+}
diff --git a/templates/bash/template.sh b/templates/bash/template.sh
new file mode 100644
index 0000000..92cb1b7
--- /dev/null
+++ b/templates/bash/template.sh
@@ -0,0 +1,195 @@
+#!/bin/bash
+
+set -eu -o pipefail
+
+#############################################################################
+# Functions that implement steps. From {{ functions_filename }}.
+
+{{ functions }}
+
+
+#############################################################################
+# Helper code generated by Subplot.
+
+
+# Simple dictionary abstraction. All values are stored in files so
+# they can more easily be inspected.
+
+dict_new() {
+ local name="$1"
+ rm -rf "$name"
+ mkdir "$name"
+}
+
+dict_has() {
+ local name="$1"
+ local key="$2"
+ local f="$name/$key"
+ test -e "$f"
+}
+
+dict_get() {
+ local name="$1"
+ local key="$2"
+ local f="$name/$key"
+ cat "$f"
+}
+
+dict_get_default() {
+ local name="$1"
+ local key="$2"
+ local default="$3"
+ local f="$name/$key"
+ if [ -e "$f" ]
+ then
+ cat "$f"
+ else
+ echo "$default"
+ fi
+}
+
+dict_set() {
+ local name="$1"
+ local key="$2"
+ local value="$3"
+ local f="$name/$key"
+ echo "$value" > "$f"
+}
+
+dict_keys() {
+ local name="$1"
+ ls -1 "$name"
+}
+
+
+# A context abstraction using dictionaries.
+
+ctx_new() {
+ dict_new _ctx
+}
+
+ctx_set()
+{
+ dict_set _ctx "$1" "$2"
+}
+
+ctx_get()
+{
+ dict_get _ctx "$1"
+}
+
+
+# Store step captures for calling the corresponding functions.
+
+cap_new() {
+ dict_new _cap
+}
+
+cap_set()
+{
+ dict_set _cap "$1" "$2"
+}
+
+cap_get()
+{
+ dict_get _cap "$1"
+}
+
+
+# Store files embedded in the markdown input.
+
+files_new() {
+ dict_new _files
+}
+
+files_set() {
+ dict_set _files "$1" "$2"
+}
+
+files_get() {
+ dict_get _files "$1"
+}
+
+
+# Decode a base64 encoded string.
+
+decode_base64() {
+ echo "$1" | base64 -d
+}
+
+# Check two values for equality and give error if they are not equal
+assert_eq() {
+ if ! diff -u <(echo "$1") <(echo "$2")
+ then
+ echo "expected values to be identical, but they're not"
+ exit 1
+ fi
+}
+
+# Check first value contains second value.
+assert_contains() {
+ if ! echo "$1" | grep -F "$2" > /dev/null
+ then
+ echo "expected first value to contain second value"
+ exit 1
+ fi
+}
+
+# Remember where we started from. The step functions may need to refer
+# to files there.
+srcdir="$(pwd)"
+echo "srcdir $srcdir"
+
+# Create a new temporary directory and chdir there. This allows step
+# functions to create new files in the current working directory
+# without having to be so careful.
+_datadir="$(mktemp -d)"
+echo "datadir $_datadir"
+cd "$_datadir"
+
+
+# Store test data files that were embedded in the source document.
+# Base64 encoding is used to allow arbitrary data.
+
+files_new
+{% for file in files %}
+# {{ file.filename }}
+filename="$(decode_base64 '{{ file.filename | base64 }}')"
+contents="$(decode_base64 '{{ file.contents | base64 }}')"
+files_set "$filename" "$contents"
+{% endfor %}
+
+
+#############################################################################
+# Code to implement the scenarios.
+
+{% for scenario in scenarios %}
+######################################
+# Scenario: {{ scenario.title }}
+title="$(decode_base64 '{{ scenario.title | base64 }}')"
+echo "scenario: $title"
+_scendir="$(mktemp -d -p "$_datadir")"
+cd "$_scendir"
+ctx_new
+{% for step in scenario.steps %}
+# Step: {{ step.text }}
+step="$(decode_base64 '{{ step.text | base64 }}')"
+echo " step: {{ step.kind | lower }} $step"
+
+cap_new
+{% for part in step.parts %}{% if part.CapturedText is defined -%}
+name="$(decode_base64 '{{ part.CapturedText.name | base64 }}')"
+text="$(decode_base64 '{{ part.CapturedText.text | base64 }}')"
+cap_set "$name" "$text"
+{% endif -%}
+{% endfor -%}
+{{ step.function }}
+{% endfor %}
+{% endfor %}
+
+
+#############################################################################
+# Clean up temporary directory and report success.
+
+rm -rf "$_datadir"
+echo "OK, all scenarios finished successfully"
diff --git a/templates/bash/template.yaml b/templates/bash/template.yaml
new file mode 100644
index 0000000..b542a06
--- /dev/null
+++ b/templates/bash/template.yaml
@@ -0,0 +1,2 @@
+template: template.sh
+run: bash