diff options
author | Lars Wirzenius <liw@liw.fi> | 2021-09-13 09:44:39 +0000 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2021-09-13 09:44:39 +0000 |
commit | a06ef1f46c8603b8b5e5a44e6521b10238331891 (patch) | |
tree | 995392da4d56597be9ac71bba4669df299506ea7 /subplot.md | |
parent | 217d920d72d9bf56174e0694329ef9c558160b69 (diff) | |
parent | 66bedaf70e7b652f791dd0e2fcbd39db3cbec6f9 (diff) | |
download | subplot-a06ef1f46c8603b8b5e5a44e6521b10238331891.tar.gz |
Merge branch 'multi-lang-docs' into 'main'
Implement polyglot bindings
See merge request subplot/subplot!210
Diffstat (limited to 'subplot.md')
-rw-r--r-- | subplot.md | 191 |
1 files changed, 152 insertions, 39 deletions
@@ -646,27 +646,49 @@ The bindings file binds scenario steps to code functions that implement the steps. The YAML file is a list of objects (also known as dicts or hashmaps or key/value pairs), specifying a step kind (given, when, then), a pattern matching the text of the step and -optionally capturing interesting parts of the text, and the name of a -function that implements the step. - -Patterns can be simple or full-blown Perl-compatible regular -expresssions ([PCRE][]). +optionally capturing interesting parts of the text. Each binding may contain +a type map which tells subplot the types of the captures in the patterns so +that they can be validated to some extent, and a binding will list some number +of implementations, each of which is specified by the name of the language +(template) it is for, and then the name of a function that implements the step, +optionally with the name of a function to call to clean up a scenario which +includes that step. + +There are some flexibilities in bindings, futher details can be found below: + +1. Patterns can be simple or full-blown Perl-compatible regular + expresssions ([PCRE][]). +2. Bindings _may_ have type maps. Without a type map, all captures are + considered to be short strings (words). +3. Bindings _may_ have as many or as few implementations as needed. A zero + `impl` binding will work for `docgen` but will fail to `codegen`. This can + permit document authors to prepare bindings without knowing how an engineer + might implement it. ~~~{.yaml .numberLines} - given: "a standard setup" - function: create_standard_setup + impl: + python: + function: create_standard_setup - when: "{thing} happens" - function: make_thing_happen + impl: + python: + function: make_thing_happen types: thing: word - when: "I say (?P<sentence>.+) with a smile" regex: true - function: speak + impl: + python: + function: speak - then: "everything is OK" - function: check_everything_is_ok + impl: + python: + function: check_everything_is_ok ~~~ -In the example above, there are four bindings: +In the example above, there are four bindings and they all provide Python +implementation functions: * A binding for a "given a standard setup" step. The binding captures no part of the text, and causes the `create_standard_setup` function @@ -682,7 +704,7 @@ In the example above, there are four bindings: * A binding for a "then everything is OK" step, which captures nothing, and calls the `check_everything_is_ok` function. -## Simple patterns +### Simple patterns The simple patterns are of the form `{name}` and match a single word consisting of printable characters. This can be varied by adding a @@ -703,7 +725,7 @@ Subplot complains if typical regular expression characters are used, when simple patterns are expected, unless `regex` is explicitly set to false. -## Regular expression patterns +### Regular expression patterns Regular expression patterns are used only if the binding `regex` field is set to `true`. @@ -716,9 +738,9 @@ as arguments, when it's called. [PCRE]: https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions [regex]: https://crates.io/crates/regex -## The type map +### The type map -Patterns may also contain a type map. This is a dictionary called `types` +Bindings may also contain a type map. This is a dictionary called `types` and contains a key-value mapping from capture name to the type of the capture. Valid types are listed above in the simple patterns section. In addition to simple patterns, the type map can be used for regular expression bindings as @@ -734,6 +756,32 @@ permits the generated test suites to use native language types directly. The `file` type, if used, must refer to an embedded file in the document; subplot docgen will emit a warning if the file is not found, and subplot codegen will emit an error. +### The implementation map + +Bindings can contain an `impl` map which connects the binding with zero or more +language templates. If a binding has no `impl` entries then it can still be +used to `docgen` a PDF or HTML document from a subplot document. This permits a +workflow where requirements owners / architects design the validations for a +project and then engineers implement the step functions to permit the +validations to work. + +Shipped with subplot are a number of libraries such as `files` or `runcmd` and +these libraries are polyglot in that they provide bindings for all supported +templates provided by subplot. + +Here is an example of a binding from one of those libraries: + +```yaml +- given: file {embedded_file} + impl: + rust: + function: subplotlib::steplibrary::files::create_from_embedded + python: + function: files_create_from_embedded + types: + embedded_file: file +``` + ### Embedded file name didn't match ```scenario @@ -925,17 +973,41 @@ then bar was done ~~~{#b.yaml .file .yaml .numberLines} - given: precondition foo - function: precond_foo + impl: + python: + function: precond_foo + bash: + function: precond_foo - when: I do bar - function: do_bar + impl: + python: + function: do_bar + bash: + function: do_bar - when: I do foobar - function: do_foobar + impl: + python: + function: do_foobar + bash: + function: do_foobar - then: bar was done - function: bar_was_done + impl: + python: + function: bar_was_done + bash: + function: bar_was_done - then: foobar was done - function: foobar_was_done + impl: + python: + function: foobar_was_done + bash: + function: foobar_was_done - given: file {filename} - function: provide_file + impl: + python: + function: provide_file + bash: + function: provide_file types: filename: file ~~~ @@ -1174,14 +1246,29 @@ test this for every language template we support. ~~~{#cleanup.yaml .file .yaml .numberLines} - given: foo - function: foo - cleanup: foo_cleanup + impl: + python: + function: foo + cleanup: foo_cleanup + bash: + function: foo + cleanup: foo_cleanup - given: bar - function: bar - cleanup: bar_cleanup + impl: + python: + function: bar + cleanup: bar_cleanup + bash: + function: bar + cleanup: bar_cleanup - given: failure - function: failure - cleanup: failure_cleanup + impl: + python: + function: failure + cleanup: failure_cleanup + bash: + function: failure + cleanup: failure_cleanup ~~~ ~~~{#cleanup.py .file .python .numberLines} @@ -1392,7 +1479,9 @@ then TMPDIR is set ~~~{#tmpdir.yaml .file .yaml .numberLines} - then: TMPDIR is set - function: tmpdir_is_set + impl: + python: + function: tmpdir_is_set ~~~ ~~~{#tmpdir.py .file .python .numberLines} @@ -1442,7 +1531,9 @@ given I am Tomjon ~~~{#simplepattern.yaml .file .yaml .numberLines} - given: I am {name} - function: func + impl: + python: + function: func ~~~ ~~~{#capture.py .file .python .numberLines} @@ -1484,7 +1575,9 @@ given I* am Tomjon ~~~{#confusedpattern.yaml .file .yaml .numberLines} - given: I* am {name} - function: func + impl: + python: + function: func ~~~ ### Simple patterns with regex metacharacters: allowed case @@ -1515,7 +1608,9 @@ given I* am Tomjon ~~~{#confusedbutok.yaml .file .yaml .numberLines} - given: I* am {name} - function: func + impl: + python: + function: func regex: false ~~~ @@ -1550,7 +1645,9 @@ given I am Tomjon ~~~{#regex.yaml .file .yaml .numberLines} - given: I am (?P<name>\S+) - function: func + impl: + python: + function: func regex: true ~~~ @@ -1599,10 +1696,14 @@ then expanded "${foo}" is bar ~~~{#values.yaml .file .yaml .numberLines} - when: I remember {name} as {value} - function: remember + impl: + python: + function: remember - then: expanded "{actual}" is {expected} - function: check + impl: + python: + function: check ~~~ ~~~{#values.py .file .python .numberLines} @@ -1665,7 +1766,9 @@ then environment variable FOO is set to "bar" ~~~{#env.yaml .file .yaml .numberLines} - then: environment variable {name} is set to "{value:text}" - function: is_set_to + impl: + python: + function: is_set_to ~~~ ~~~{#env.py .file .python .numberLines} @@ -2563,12 +2666,18 @@ binding. ```{#badbindings.yaml .file .yaml} - given: a binding - function: a_binding + impl: + python: + function: a_binding - given: a (?:broken)? binding - function: a_broken_binding + impl: + python: + function: a_broken_binding regex: true - given: a capitalised Binding - function: os.getcwd + impl: + python: + function: os.getcwd case_sensitive: true ``` @@ -2640,9 +2749,13 @@ given a binding ~~~{#twobindings.yaml .file .yaml} - given: a {xyzzy} - function: a_function + impl: + python: + function: a_function - given: a {plugh} - function: a_function + impl: + python: + function: a_function ~~~ ~~~{#a_function.py .file .python} |