From 765b2e1d4d94b2274de28d4efd24bfe77e8d93ac Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sat, 3 Sep 2022 13:47:16 +0300 Subject: feat! read document metadata from a YAML file This is a huge change all in one commit, sorry. However, as it changes a fundamental part of the command line interface (namely, what constitutes as the input file), there doesn't seem a way to break this into a tidy series of small commits. Most of the diff is in subplot.md, where every scenario that invokes Subplot needs multiple changes, thus touching much of the file. The overall change is that where we previously had document metadata in embedded YAML in the Markdown file, we now have it in a separate YAML file. The Markdown file is named in the YAML file. We still parse the Markdown with Pandoc for everything, except codegen. Switching from Pandoc to pulldown_cmark for parsing will be another big change that I didn't want to include in this huge change set. Sponsored-by: author --- check | 32 +- examples/seq/build.rs | 4 +- examples/seq/seq.subplot | 10 + src/ast.rs | 38 +-- src/bin/subplot.rs | 2 +- src/doc.rs | 51 ++- src/error.rs | 4 + src/metadata.rs | 26 +- subplot-build/src/lib.rs | 1 + subplot.md | 623 +++++++++++++++++++++-------------- subplot.subplot | 18 + subplotlib/build.rs | 14 +- subplotlib/subplotlib.subplot | 10 + tests/subplots/common/files.md | 11 - tests/subplots/common/files.subplot | 10 + tests/subplots/common/runcmd.md | 17 - tests/subplots/common/runcmd.subplot | 15 + 17 files changed, 538 insertions(+), 348 deletions(-) create mode 100644 examples/seq/seq.subplot create mode 100644 subplot.subplot create mode 100644 subplotlib/subplotlib.subplot create mode 100644 tests/subplots/common/files.subplot create mode 100644 tests/subplots/common/runcmd.subplot diff --git a/check b/check index f9b331c..755a483 100755 --- a/check +++ b/check @@ -267,20 +267,20 @@ def check_subplots(r): output = os.path.abspath("test-outputs") os.makedirs(output, exist_ok=True) - mds = find_files( - "**/*.md", + subplots = find_files( + "**/*.subplot", lambda f: f == f.lower() and "subplotlib" not in f and "test-outputs" not in f, ) - for md0 in mds: - r.title(f"checking subplot {md0}") + for subplot0 in subplots: + r.title(f"checking subplot {subplot0}") - dirname = os.path.dirname(md0) or "." - md = os.path.basename(md0) - base, _ = os.path.splitext(md) + dirname = os.path.dirname(subplot0) or "." + subplot = os.path.basename(subplot0) + base, _ = os.path.splitext(subplot) doc_template = None - for template in r.get_templates(md0): + for template in r.get_templates(subplot0): if doc_template is None: doc_template = template if template == "python": @@ -293,7 +293,7 @@ def check_subplots(r): bindir = get_bin_dir(r) - r.codegen(md, "python", test_py, cwd=dirname) + r.codegen(subplot, "python", test_py, cwd=dirname) p = r.runcmd_unchecked( [ "python3", @@ -310,18 +310,18 @@ def check_subplots(r): sys.exit(1) elif template == "bash": test_sh = os.path.join(output, f"test-{base}.sh") - r.codegen(md, "bash", test_sh, cwd=dirname) + r.codegen(subplot, "bash", test_sh, cwd=dirname) r.runcmd(["bash", "-x", test_sh], cwd=dirname) elif template == "rust": - r.msg(f"Ignoring Rust template in {md0}") + r.msg(f"Ignoring Rust template in {subplot0}") else: - sys.exit(f"unknown template {template} in {md0}") + sys.exit(f"unknown template {template} in {subplot0}") - base = os.path.basename(md) - base, _ = os.path.splitext(md) + base = os.path.basename(subplot) + base, _ = os.path.splitext(subplot) base = os.path.join(output, base) - r.docgen(md, doc_template, base + ".pdf", cwd=dirname) - r.docgen(md, doc_template, base + ".html", cwd=dirname) + r.docgen(subplot, doc_template, base + ".pdf", cwd=dirname) + r.docgen(subplot, doc_template, base + ".html", cwd=dirname) def tail(filename, numlines=100): diff --git a/examples/seq/build.rs b/examples/seq/build.rs index 398ec90..d730645 100644 --- a/examples/seq/build.rs +++ b/examples/seq/build.rs @@ -1,6 +1,6 @@ //! Subplot seq example build script. fn main() { - println!("cargo:rerun-if-changed=seq.md"); - subplot_build::codegen("seq.md").expect("failed to generate code with Subplot"); + println!("cargo:rerun-if-changed=seq.subplot"); + subplot_build::codegen("seq.subplot").expect("failed to generate code with Subplot"); } diff --git a/examples/seq/seq.subplot b/examples/seq/seq.subplot new file mode 100644 index 0000000..a6a6219 --- /dev/null +++ b/examples/seq/seq.subplot @@ -0,0 +1,10 @@ +title: "**seq**(1) acceptance tests" +author: The Subplot project +markdowns: + - seq.md +bindings: + - lib/runcmd.yaml + - seq-extras.yaml +impls: + rust: + - seq-extras.rs diff --git a/src/ast.rs b/src/ast.rs index f02fdd1..7efe836 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -279,7 +279,7 @@ pub enum Error { /// block we can work with, in any input file. By being strict here we /// make it easier to tell the user when a metadata block has, say, a /// misspelled field. -#[derive(Debug, Default, Deserialize)] +#[derive(Debug, Default, Clone, Deserialize)] #[serde(deny_unknown_fields)] pub struct YamlMetadata { title: String, @@ -288,6 +288,7 @@ pub struct YamlMetadata { date: Option, classes: Option>, bibliography: Option>, + markdowns: Vec, bindings: Option>, documentclass: Option, #[serde(default)] @@ -301,7 +302,13 @@ impl YamlMetadata { Ok(meta) } - fn to_map(&self) -> Map { + /// Name of file with the Markdown for the subplot document. + pub fn markdown(&self) -> &Path { + &self.markdowns[0] + } + + /// Convert into a pandoc_ast::Map. + pub fn to_map(&self) -> Map { trace!("Creating metadata map from parsed YAML"); let mut map: Map = Map::new(); @@ -367,8 +374,8 @@ fn meta_path_bufs(v: &[PathBuf]) -> MetaValue { #[cfg(test)] mod test { - use super::{extract_metadata, parse_code_block_attrs, AbstractSyntaxTree, YamlMetadata}; - use std::path::PathBuf; + use super::{parse_code_block_attrs, YamlMetadata}; + use std::path::{Path, PathBuf}; #[test] fn code_block_attrs() { @@ -391,26 +398,6 @@ mod test { ); } - #[test] - fn parses_leading_meta() { - let markdown = "\n\n---\ntitle: Foo Bar\n...\nfoobar\n"; - let (meta, markdown) = extract_metadata(markdown).unwrap(); - let ast = AbstractSyntaxTree::new(meta, markdown); - let doc = ast.to_pandoc(); - let keys: Vec = doc.meta.keys().cloned().collect(); - assert_eq!(keys, ["title"]); - } - - #[test] - fn parses_trailing_meta() { - let markdown = "foobar\n---\ntitle: Foo Bar\n...\n\n\n"; - let (meta, markdown) = extract_metadata(markdown).unwrap(); - let ast = AbstractSyntaxTree::new(meta, markdown); - let doc = ast.to_pandoc(); - let keys: Vec = doc.meta.keys().cloned().collect(); - assert_eq!(keys, ["title"]); - } - #[test] fn full_meta() { let meta = YamlMetadata::new( @@ -425,6 +412,8 @@ impls: bibliography: - foo.bib - bar.bib +markdowns: +- test.md bindings: - foo.yaml - bar.yaml @@ -438,6 +427,7 @@ bindings: meta.bibliography.unwrap(), &[path("foo.bib"), path("bar.bib")] ); + assert_eq!(meta.markdowns, vec![Path::new("test.md")]); assert_eq!( meta.bindings.unwrap(), &[path("foo.yaml"), path("bar.yaml")] diff --git a/src/bin/subplot.rs b/src/bin/subplot.rs index b618ac9..907e616 100644 --- a/src/bin/subplot.rs +++ b/src/bin/subplot.rs @@ -278,7 +278,7 @@ impl Docgen { } else if let Some(date) = doc.meta().date() { date.to_string() } else { - Self::mtime_formatted(Self::mtime(&self.input)?) + Self::mtime_formatted(Self::mtime(doc.meta().markdown_filename())?) }; pandoc.add_option(pandoc::PandocOption::Meta("date".to_string(), Some(date))); pandoc.add_option(pandoc::PandocOption::TableOfContents); diff --git a/src/doc.rs b/src/doc.rs index 38e14b5..2ed3ef5 100644 --- a/src/doc.rs +++ b/src/doc.rs @@ -12,12 +12,14 @@ use crate::Scenario; use crate::ScenarioStep; use crate::Style; use crate::SubplotError; +use crate::YamlMetadata; use crate::{bindings::CaptureType, parser::parse_scenario_snippet}; use crate::{Warning, Warnings}; use std::collections::HashSet; use std::default::Default; use std::fmt::Debug; +use std::fs::read; use std::ops::Deref; use std::path::{Path, PathBuf}; @@ -58,7 +60,7 @@ static KNOWN_PANDOC_CLASSES: &[&str] = &["numberLines", "noNumberLines"]; /// # Example /// /// fix this example; -/// ~~~~ +/// ~~~~ignored /// let markdown = "\ /// --- /// title: Test Title @@ -114,6 +116,7 @@ impl Document { fn from_ast

( basedir: P, markdowns: Vec, + yamlmeta: &ast::YamlMetadata, mut ast: Pandoc, style: Style, template: Option<&str>, @@ -121,7 +124,7 @@ impl Document { where P: AsRef + Debug, { - let meta = Metadata::new(basedir, &ast, template)?; + let meta = Metadata::new(basedir, yamlmeta, template)?; let mut linter = LintingVisitor::default(); trace!("Walking AST for linting..."); linter.walk_pandoc(&mut ast); @@ -151,10 +154,15 @@ impl Document { basedir.display(), filename.display() ); - let markdowns = vec![filename.to_path_buf()]; + + let meta = load_metadata_from_yaml_file(filename)?; + + let mdfile = meta.markdown(); + let mdfile = basedir.join(mdfile); + let markdowns = vec![mdfile.clone()]; let mut pandoc = pandoc::new(); - pandoc.add_input(&filename); + pandoc.add_input(&mdfile); pandoc.set_input_format( pandoc::InputFormat::Markdown, vec![pandoc::MarkdownExtension::Citations], @@ -167,7 +175,7 @@ impl Document { trace!( "Invoking Pandoc to parse document {:?} into AST as JSON", - filename + mdfile, ); let json = match pandoc.execute().map_err(SubplotError::Pandoc)? { pandoc::PandocOutput::ToBuffer(o) => o, @@ -176,8 +184,9 @@ impl Document { trace!("Pandoc was happy"); trace!("Parsing document AST as JSON..."); - let ast: Pandoc = serde_json::from_str(&json).map_err(SubplotError::AstJson)?; - let doc = Self::from_ast(basedir, markdowns, ast, style, template)?; + let mut ast: Pandoc = serde_json::from_str(&json).map_err(SubplotError::AstJson)?; + ast.meta = meta.to_map(); + let doc = Self::from_ast(basedir, markdowns, &meta, ast, style, template)?; trace!("Loaded document OK"); Ok(doc) @@ -195,14 +204,22 @@ impl Document { template: Option<&str>, ) -> Result { trace!("Parsing document with pullmark-cmark from {:?}", filename); - let filename = filename.to_path_buf(); - let markdown = std::fs::read_to_string(&filename) - .map_err(|err| SubplotError::ReadFile(filename.clone(), err))?; - let (meta, markdown) = ast::extract_metadata(&markdown)?; - let ast = ast::AbstractSyntaxTree::new(meta, markdown); + let meta = load_metadata_from_yaml_file(filename)?; + let mdfile = meta.markdown(); + let mdfile = basedir.join(mdfile); + let markdown = std::fs::read_to_string(&mdfile) + .map_err(|err| SubplotError::ReadFile(mdfile.clone(), err))?; + let ast = ast::AbstractSyntaxTree::new(meta.clone(), &markdown); trace!("Parsed document OK"); - Self::from_ast(basedir, vec![filename], ast.to_pandoc(), style, template) + Self::from_ast( + basedir, + vec![filename.into()], + &meta, + ast.to_pandoc(), + style, + template, + ) } /// Return the AST of a Document, serialized as JSON. @@ -481,6 +498,14 @@ impl Document { } } +fn load_metadata_from_yaml_file(filename: &Path) -> Result { + let yaml = read(filename).map_err(|e| SubplotError::ReadFile(filename.into(), e))?; + trace!("Parsing YAML metadata from {}", filename.display()); + let meta: ast::YamlMetadata = serde_yaml::from_slice(&yaml) + .map_err(|e| SubplotError::MetadataFile(filename.into(), e))?; + Ok(meta) +} + /// Load a `Document` from a file. /// /// This version uses Pandoc to parse the Markdown. diff --git a/src/error.rs b/src/error.rs index 17870c5..53eccad 100644 --- a/src/error.rs +++ b/src/error.rs @@ -310,6 +310,10 @@ pub enum SubplotError { #[error(transparent)] Ast(#[from] crate::ast::Error), + /// UTF8 conversion error. + #[error("failed to parse UTF8 in file {0}")] + FileUtf8(PathBuf, #[source] std::string::FromUtf8Error), + /// UTF8 conversion error. #[error(transparent)] Utf8Error(#[from] std::str::Utf8Error), diff --git a/src/metadata.rs b/src/metadata.rs index 5f5e183..dee0b50 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -1,11 +1,11 @@ -use crate::{Bindings, SubplotError, TemplateSpec}; +use crate::{Bindings, SubplotError, TemplateSpec, YamlMetadata}; use std::collections::HashMap; use std::fmt::Debug; use std::ops::Deref; use std::path::{Path, PathBuf}; -use pandoc_ast::{Inline, Map, MetaValue, Pandoc}; +use pandoc_ast::{Inline, Map, MetaValue}; use log::trace; @@ -14,6 +14,7 @@ use log::trace; pub struct Metadata { title: String, date: Option, + markdown_filename: PathBuf, bindings_filenames: Vec, bindings: Bindings, impls: HashMap, @@ -32,22 +33,23 @@ impl Metadata { /// Construct a Metadata from a Document, if possible. pub fn new

( basedir: P, - doc: &Pandoc, + meta: &YamlMetadata, template: Option<&str>, ) -> Result where P: AsRef + Debug, { - let title = get_title(&doc.meta); - let date = get_date(&doc.meta); - let bindings_filenames = get_bindings_filenames(&doc.meta); - let bibliographies = get_bibliographies(basedir.as_ref(), &doc.meta); - let classes = get_classes(&doc.meta); + let map = meta.to_map(); + let title = get_title(&map); + let date = get_date(&map); + let bindings_filenames = get_bindings_filenames(&map); + let bibliographies = get_bibliographies(basedir.as_ref(), &map); + let classes = get_classes(&map); trace!("Loaded basic metadata"); let mut impls = HashMap::new(); - if let Some(raw_impls) = doc.meta.get("impls") { + if let Some(raw_impls) = map.get("impls") { match raw_impls { MetaValue::MetaMap(raw_impls) => { for (impl_name, functions_filenames) in raw_impls.iter() { @@ -74,6 +76,7 @@ impl Metadata { Ok(Metadata { title, date, + markdown_filename: meta.markdown().into(), bindings_filenames, bindings, impls, @@ -92,6 +95,11 @@ impl Metadata { self.date.as_deref() } + /// Return filename of the markdown file. + pub fn markdown_filename(&self) -> &Path { + &self.markdown_filename + } + /// Return filename where bindings are specified. pub fn bindings_filenames(&self) -> Vec<&Path> { self.bindings_filenames.iter().map(|f| f.as_ref()).collect() diff --git a/subplot-build/src/lib.rs b/subplot-build/src/lib.rs index f8ed074..7f194c5 100644 --- a/subplot-build/src/lib.rs +++ b/subplot-build/src/lib.rs @@ -54,6 +54,7 @@ where // re-running. let base_path = get_basedir_from(filename); let meta = output.doc.meta(); + buildrs_deps(&base_path, Some(meta.markdown_filename())); buildrs_deps(&base_path, meta.bindings_filenames()); let docimpl = output .doc diff --git a/subplot.md b/subplot.md index ca791d5..8ae5014 100644 --- a/subplot.md +++ b/subplot.md @@ -1,22 +1,3 @@ ---- -title: "Subplot" -author: The Subplot project -bindings: -- subplot.yaml -- lib/runcmd.yaml -- lib/files.yaml -impls: - python: - - subplot.py - - lib/files.py - - lib/runcmd.py - rust: - - subplotlib/subplot-rust-support.rs -classes: -- json -... - - # Introduction Subplot is software to help capture and communicate acceptance @@ -815,24 +796,26 @@ Here is an example of a binding from one of those libraries: ### Embedded file name didn't match ```scenario +given file badfilename.subplot given file badfilename.md and file b.yaml and file f.py and an installed subplot -when I run subplot docgen --merciful badfilename.md -o foo.pdf +when I run subplot docgen --merciful badfilename.subplot -o foo.pdf then file foo.pdf exists when I try to run subplot codegen --run badfilename.md -o test.py then command fails ``` -~~~{#badfilename.md .file .markdown .numberLines} ---- +~~~{#badfilename.subplot .file .yaml .numberLines} title: Bad filenames in matched steps do not permit codegen +markdowns: [badfilename.md] bindings: [b.yaml] impls: python: [f.py] -... +~~~ +~~~{#badfilename.md .file .markdown .numberLines} # Bad filename ```scenario @@ -1010,14 +993,16 @@ They're separate from the scenarios so that the scenarios are shorter and clearer, but also so that the input files do not need to be duplicated for each scenario. -~~~~{#simple.md .file .markdown .numberLines} ---- +~~~~{#simple.subplot .file .yaml .numberLines} title: Test scenario +markdowns: +- simple.md bindings: [b.yaml] impls: python: [f.py] -... +~~~~ +~~~~{#simple.md .file .markdown .numberLines} # Simple This is the simplest possible test scenario @@ -1094,15 +1079,16 @@ generating the test program from an input file, running the test program, and examining the output. ~~~scenario +given file simple.subplot given file simple.md and file b.yaml and file f.py and an installed subplot -when I run subplot docgen simple.md -o simple.pdf +when I run subplot docgen simple.subplot -o simple.pdf then file simple.pdf exists -when I run subplot docgen simple.md -o simple.html +when I run subplot docgen simple.subplot -o simple.html then file simple.html exists -when I run subplot codegen --run simple.md -o test.py +when I run subplot codegen --run simple.subplot -o test.py then scenario "Simple" was run and step "given precondition foo" was run and step "when I do bar" was run @@ -1116,19 +1102,21 @@ If you attempt to `subplot codegen` on a document which contains no scenarios, t tool will fail to execute with a reasonable error message. ~~~scenario +given file noscenarios.subplot given file noscenarios.md and an installed subplot -when I try to run subplot codegen noscenarios.md -o test.py +when I try to run subplot codegen noscenarios.subplot -o test.py then command fails and stderr contains "no scenarios were found" ~~~ -~~~{#noscenarios.md .file .markdown .numberLines} ---- +~~~{#noscenarios.subplot .file .yaml .numberLines} title: No scenarios in here +markdowns: [noscenarios.md] impls: { python: [] } -... +~~~ +~~~{#noscenarios.md .file .markdown .numberLines} # This is a title But there are no scenarios in this file, and thus nothing can be generated in a test suite. @@ -1146,13 +1134,14 @@ combinations. ### All the keywords ~~~scenario +given file allkeywords.subplot given file allkeywords.md and file b.yaml and file f.py and an installed subplot -when I run subplot docgen allkeywords.md -o foo.pdf +when I run subplot docgen allkeywords.subplot -o foo.pdf then file foo.pdf exists -when I run subplot codegen --run allkeywords.md -o test.py +when I run subplot codegen --run allkeywords.subplot -o test.py then scenario "All keywords" was run and step "given precondition foo" was run and step "when I do bar" was run @@ -1160,14 +1149,16 @@ and step "then bar was done" was run and command is successful ~~~ -~~~{#allkeywords.md .file .markdown .numberLines} ---- +~~~{#allkeywords.subplot .file .yaml .numberLines} title: All the keywords scenario +markdowns: +- allkeywords.md bindings: [b.yaml] impls: python: [f.py] -... +~~~ +~~~{#allkeywords.md .file .markdown .numberLines} # All keywords This uses all the keywords. @@ -1188,11 +1179,12 @@ scenarios in output so they are used when they are allowed. This scenario verifies that this happens. ~~~scenario +given file aliases.subplot given file aliases.md given file b.yaml given file f.py given an installed subplot -when I run subplot docgen --merciful aliases.md -o aliases.html +when I run subplot docgen --merciful aliases.subplot -o aliases.html then command is successful then file aliases.html matches regex /given<[^>]*> precondition foo/ then file aliases.html matches regex /when<[^>]*> I do bar/ @@ -1201,13 +1193,16 @@ then file aliases.html matches regex /then<[^>]*> bar was done/ then file aliases.html matches regex /and<[^>]*> foobar was done/ ~~~ -~~~{#aliases.md .file .markdown .numberLines} ---- +~~~{#aliases.subplot .file .yaml .numberLines} title: Keyword aliases +markdowns: +- aliases.md bindings: [b.yaml] -functions: [f.py] -... +impls: + python: [f.py] +~~~ +~~~{#aliases.md .file .markdown .numberLines} # Aliases ```scenario @@ -1227,24 +1222,27 @@ subplot will be unable to determine what kind of keyword they are meant to be continuing. ~~~scenario +given file continuationmisuse.subplot given file continuationmisuse.md and file b.yaml and file f.py and an installed subplot -when I run subplot docgen continuationmisuse.md -o foo.pdf +when I run subplot docgen continuationmisuse.subplot -o foo.pdf then file foo.pdf exists -when I try to run subplot codegen --run continuationmisuse.md -o test.py +when I try to run subplot codegen --run continuationmisuse.subplot -o test.py then command fails ~~~ -~~~{#continuationmisuse.md .file .markdown .numberLines} ---- +~~~{#continuationmisuse.subplot .file .yaml .numberLines} title: Continuation keyword misuse +markdowns: +- continuationmisuse.subplot bindings: [b.yaml] impls: python: [f.py] -... +~~~ +~~~{#continuationmisuse.md .file .markdown .numberLines} # Continuation keyword misuse This scenario should fail to parse because we misuse a @@ -1264,36 +1262,39 @@ It is OK to use markup in document titles, in the YAML metadata section. This scenario verifies that all markup works. ~~~scenario +given file title-markup.subplot given file title-markup.md given an installed subplot -when I run subplot docgen title-markup.md -o foo.pdf +when I run subplot docgen title-markup.subplot -o foo.pdf then file foo.pdf exists ~~~ -~~~~{#title-markup.md .file .markdown .numberLines} ---- +~~~~{#title-markup.subplot .file .yaml .numberLines} title: This _uses_ ~~all~~ **most** inline `markup` subtitle: H~2~O is not 2^10^ +markdowns: [title-markup.md] impls: { python: [] } -... +~~~~ +~~~~{#title-markup.md .file .markdown .numberLines} # Introduction ~~~~ ## Empty lines in scenarios -This scenario verifies that empty lines in scenarios are ignored. +This scenario verifies that empty lines in scenarios are OK. ~~~scenario +given file emptylines.subplot given file emptylines.md and file b.yaml and file f.py and an installed subplot -when I run subplot docgen emptylines.md -o emptylines.pdf +when I run subplot docgen emptylines.subplot -o emptylines.pdf then file emptylines.pdf exists -when I run subplot docgen emptylines.md -o emptylines.html +when I run subplot docgen emptylines.subplot -o emptylines.html then file emptylines.html exists -when I run subplot codegen --run emptylines.md -o test.py +when I run subplot codegen --run emptylines.subplot -o test.py then scenario "Simple" was run and step "given precondition foo" was run and step "when I do bar" was run @@ -1301,14 +1302,16 @@ and step "then bar was done" was run and command is successful ~~~ -~~~~{#emptylines.md .file .markdown .numberLines} ---- +~~~~{#emptylines.subplot .file .yaml .numberLines} title: Test scenario +markdowns: +- emptylines.md bindings: [b.yaml] impls: python: [f.py] -... +~~~~ +~~~~{#emptylines.md .file .markdown .numberLines} # Simple This is the simplest possible test scenario @@ -1397,11 +1400,12 @@ failure_cleanup() { ### Cleanup functions gets called on success (Python) ~~~scenario +given file cleanup-success-python.subplot given file cleanup-success-python.md and file cleanup.yaml and file cleanup.py and an installed subplot -when I run subplot codegen --run cleanup-success-python.md -o test.py +when I run subplot codegen --run cleanup-success-python.subplot -o test.py then scenario "Cleanup" was run and step "given foo" was run, and then step "given bar" and cleanup for "given bar" was run, and then for "given foo" @@ -1409,14 +1413,17 @@ and command is successful ~~~ -~~~~~{#cleanup-success-python.md .file .markdown .numberLines} ---- +~~~~~{#cleanup-success-python.subplot .file .yaml .numberLines} title: Cleanup +markdowns: +- cleanup-success-python.md bindings: [cleanup.yaml] impls: python: [cleanup.py] -... +~~~~~ + +~~~~~{#cleanup-success-python.md .file .markdown .numberLines} # Cleanup ~~~scenario @@ -1429,11 +1436,12 @@ given bar ### Cleanup functions get called on failure (Python) ~~~scenario +given file cleanup-fail-python.subplot given file cleanup-fail-python.md and file cleanup.yaml and file cleanup.py and an installed subplot -when I try to run subplot codegen --run cleanup-fail-python.md -o test.py +when I try to run subplot codegen --run cleanup-fail-python.subplot -o test.py then scenario "Cleanup" was run and step "given foo" was run, and then step "given bar" and cleanup for "given bar" was run, and then for "given foo" @@ -1441,14 +1449,16 @@ and cleanup for "given failure" was not run and command fails ~~~ -~~~~~{#cleanup-fail-python.md .file .markdown .numberLines} ---- +~~~~~{#cleanup-fail-python.subplot .file .yaml .numberLines} title: Cleanup +markdowns: +- cleanup-fail-python.md bindings: [cleanup.yaml] impls: python: [cleanup.py] -... +~~~~~ +~~~~~{#cleanup-fail-python.md .file .markdown .numberLines} # Cleanup ~~~scenario @@ -1462,17 +1472,27 @@ given failure ### Cleanup functions gets called on success (Bash) ~~~scenario +given file cleanup-success-bash.subplot given file cleanup-success-bash.md and file cleanup.yaml and file cleanup.sh and an installed subplot -when I run subplot codegen --run cleanup-success-bash.md -o test.sh +when I run subplot codegen --run cleanup-success-bash.subplot -o test.sh then scenario "Cleanup" was run and step "given foo" was run, and then step "given bar" and cleanup for "given bar" was run, and then for "given foo" and command is successful ~~~ +~~~~~{#cleanup-success-bash.subplot .file .yaml .numberLines} +title: Cleanup +markdowns: +- cleanup-success-bash.md +bindings: [cleanup.yaml] +impls: + bash: [cleanup.sh] +~~~~~ + ~~~~~{#cleanup-success-bash.md .file .markdown .numberLines} --- title: Cleanup @@ -1496,11 +1516,12 @@ If a step fails, all the cleanups for the preceding steps are still called, in reverse order. ~~~scenario +given file cleanup-fail-bash.subplot given file cleanup-fail-bash.md and file cleanup.yaml and file cleanup.sh and an installed subplot -when I try to run subplot codegen --run cleanup-fail-bash.md -o test.sh +when I try to run subplot codegen --run cleanup-fail-bash.subplot -o test.sh then scenario "Cleanup" was run and step "given foo" was run, and then step "given bar" and cleanup for "given bar" was run, and then for "given foo" @@ -1508,14 +1529,16 @@ and cleanup for "given failure" was not run and command fails ~~~ -~~~~~{#cleanup-fail-bash.md .file .markdown .numberLines} ---- +~~~~~{#cleanup-fail-bash.subplot .file .yaml .numberLines} title: Cleanup +markdowns: +- cleanup-fail-bash.md bindings: [cleanup.yaml] impls: bash: [cleanup.sh] -... +~~~~~ +~~~~~{#cleanup-fail-bash.md .file .markdown .numberLines} # Cleanup ~~~scenario @@ -1538,24 +1561,26 @@ the `TMPDIR` environment variable to point at the data directory. This scenario verifies that it happens. ~~~scenario +given file tmpdir.subplot given file tmpdir.md and file tmpdir.yaml and file tmpdir.py and an installed subplot -when I run subplot codegen --run tmpdir.md -o test.py +when I run subplot codegen --run tmpdir.subplot -o test.py then command is successful and scenario "TMPDIR" was run and step "then TMPDIR is set" was run ~~~ -~~~~{#tmpdir.md .file .markdown .numberLines} ---- +~~~~{#tmpdir.subplot .file .yaml .numberLines} title: TMPDIR +markdowns: [tmpdir.md] bindings: [tmpdir.yaml] impls: python: [tmpdir.py] -... +~~~~ +~~~~{#tmpdir.md .file .markdown .numberLines} # TMPDIR ~~~scenario @@ -1589,25 +1614,28 @@ can be done using regular expressions or "simple patterns". ### Capture using simple patterns ~~~scenario +given file simplepattern.subplot given file simplepattern.md and file simplepattern.yaml and file capture.py and an installed subplot -when I run subplot codegen --run simplepattern.md -o test.py +when I run subplot codegen --run simplepattern.subplot -o test.py then scenario "Simple pattern" was run and step "given I am Tomjon" was run and stdout contains "function got argument name as Tomjon" and command is successful ~~~ -~~~~{#simplepattern.md .file .markdown .numberLines} ---- +~~~~{#simplepattern.subplot .file .yaml .numberLines} title: Simple pattern capture +markdowns: +- simplepattern.md bindings: [simplepattern.yaml] impls: python: [capture.py] -... +~~~~ +~~~~{#simplepattern.md .file .markdown .numberLines} # Simple pattern ~~~scenario @@ -1635,23 +1663,26 @@ expression meta characters unless the rule is explicitly marked as not being a regular expression pattern. ~~~scenario +given file confusedpattern.subplot given file confusedpattern.md and file confusedpattern.yaml and file capture.py and an installed subplot -when I try to run subplot codegen --run confusedpattern.md -o test.py +when I try to run subplot codegen --run confusedpattern.subplot -o test.py then command fails and stderr contains "simple pattern contains regex" ~~~ -~~~~{#confusedpattern.md .file .markdown .numberLines} ---- +~~~~{#confusedpattern.subplot .file .yaml .numberLines} title: Simple pattern capture +markdowns: +- confusedpattern.md bindings: [confusedpattern.yaml] impls: python: [capture.py] -... +~~~~ +~~~~{#confusedpattern.md .file .markdown .numberLines} # Simple pattern ~~~scenario @@ -1669,22 +1700,25 @@ given I* am Tomjon ### Simple patterns with regex metacharacters: allowed case ~~~scenario +given file confusedbutok.subplot given file confusedbutok.md and file confusedbutok.yaml and file capture.py and an installed subplot -when I run subplot codegen --run confusedbutok.md -o test.py +when I run subplot codegen --run confusedbutok.subplot -o test.py then command is successful ~~~ -~~~~{#confusedbutok.md .file .markdown .numberLines} ---- +~~~~{#confusedbutok.subplot .file .yaml .numberLines} title: Simple pattern capture +markdowns: +- confusedbutok.md bindings: [confusedbutok.yaml] impls: python: [capture.py] -... +~~~~ +~~~~{#confusedbutok.md .file .markdown .numberLines} # Simple pattern ~~~scenario @@ -1703,25 +1737,28 @@ given I* am Tomjon ### Capture using regular expressions ~~~scenario +given file regex.subplot given file regex.md and file regex.yaml and file capture.py and an installed subplot -when I run subplot codegen --run regex.md -o test.py +when I run subplot codegen --run regex.subplot -o test.py then scenario "Regex" was run and step "given I am Tomjon" was run and stdout contains "function got argument name as Tomjon" and command is successful ~~~ -~~~~{#regex.md .file .markdown .numberLines} ---- +~~~~{#regex.subplot .file .yaml .numberLines} title: Regex capture +markdowns: +- regex.md bindings: [regex.yaml] impls: python: [capture.py] -... +~~~~ +~~~~{#regex.md .file .markdown .numberLines} # Regex ~~~scenario @@ -1753,31 +1790,32 @@ expansions don't accidentally refer to values meant for another purpose. ~~~scenario +given file values.subplot given file values.md and file values.yaml and file values.py and an installed subplot -when I run subplot codegen values.md -o test.py +when I run subplot codegen values.subplot -o test.py when I run python3 test.py then command is successful ~~~ -~~~~~~{#values.md .file .markdown .numberLines} ---- +~~~~~~{#values.subplot .file .yaml .numberLines} title: Values +markdowns: +- values.md bindings: [values.yaml] impls: python: [values.py] -... - +~~~~~~ +~~~~~~{#values.md .file .markdown .numberLines} # Values ~~~scenario when I remember foo as bar then expanded "${foo}" is bar ~~~ - ~~~~~~ ~~~{#values.yaml .file .yaml .numberLines} @@ -1823,11 +1861,12 @@ There is currently no equivalent functionality for the generated Bash test program. Patches for that are welcome. ~~~scenario +given file env.subplot given file env.md and file env.yaml and file env.py and an installed subplot -when I run subplot codegen env.md -o test.py +when I run subplot codegen env.subplot -o test.py when I try to run python3 test.py then command fails when I try to run python3 test.py --env FOO=foo @@ -1836,14 +1875,16 @@ when I try to run python3 test.py --env FOO=bar then command is successful ~~~ -~~~~~~{#env.md .file .markdown .numberLines} ---- +~~~~~~{#env.subplot .file .yaml .numberLines} title: Environment variables +markdowns: +- env.md bindings: [env.yaml] impls: python: [env.py] -... +~~~~~~ +~~~~~~{#env.md .file .markdown .numberLines} # Test ~~~scenario then environment variable FOO is set to "bar" @@ -1876,64 +1917,68 @@ the output file, including its timestamp. This avoids triggering programs that monitor the output file for changes. ~~~scenario +given file simple.subplot given file simple.md and file b.yaml and file f.py and an installed subplot -when I run subplot docgen simple.md -o simple.pdf +when I run subplot docgen simple.subplot -o simple.pdf then file simple.pdf exists when I remember metadata for file simple.pdf and I wait until 1 second has passed -and I run subplot docgen simple.md -o simple.pdf +and I run subplot docgen simple.subplot -o simple.pdf then file simple.pdf has same metadata as before -and only files simple.md, b.yaml, f.py, simple.pdf exist +and only files simple.subplot, simple.md, b.yaml, f.py, simple.pdf exist ~~~ ### Do typeset if output is older than markdown ~~~scenario +given file simple.subplot given file simple.md and file b.yaml and file f.py and an installed subplot -when I run subplot docgen simple.md -o simple.pdf +when I run subplot docgen simple.subplot -o simple.pdf then file simple.pdf exists when I remember metadata for file simple.pdf and I wait until 1 second has passed and I touch file simple.md -and I run subplot docgen simple.md -o simple.pdf +and I run subplot docgen simple.subplot -o simple.pdf then file simple.pdf has changed from before ~~~ ### Do typeset if output is older than functions ~~~scenario +given file simple.subplot given file simple.md and file b.yaml and file f.py and an installed subplot -when I run subplot docgen simple.md -o simple.pdf +when I run subplot docgen simple.subplot -o simple.pdf then file simple.pdf exists when I remember metadata for file simple.pdf and I wait until 1 second has passed and I touch file f.py -and I run subplot docgen simple.md -o simple.pdf +and I run subplot docgen simple.subplot -o simple.pdf then file simple.pdf has changed from before ~~~ ### Do typeset if output is older than bindings ~~~scenario +given file simple.subplot given file simple.md and file b.yaml and file f.py and an installed subplot -when I run subplot docgen simple.md -o simple.pdf +when I run subplot docgen simple.subplot -o simple.pdf then file simple.pdf exists when I remember metadata for file simple.pdf and I wait until 1 second has passed and I touch file b.yaml -and I run subplot docgen simple.md -o simple.pdf +and I run subplot docgen simple.subplot -o simple.pdf then file simple.pdf has changed from before ~~~ @@ -1949,24 +1994,26 @@ higher level starts a new scenario. ### Lowest level heading is name of scenario ~~~scenario +given file scenarioislowest.subplot given file scenarioislowest.md and file b.yaml and file f.py and an installed subplot -when I run subplot codegen --run scenarioislowest.md -o test.py +when I run subplot codegen --run scenarioislowest.subplot -o test.py then scenario "heading 1.1.1" was run and command is successful ~~~ -~~~~{#scenarioislowest.md .file .markdown .numberLines} - ---- +~~~~{#scenarioislowest.subplot .file .yaml .numberLines} title: Test scenario +markdowns: +- scenarioislowest.md bindings: [b.yaml] impls: python: [f.py] -... +~~~~ +~~~~{#scenarioislowest.md .file .markdown .numberLines} # heading 1 ## heading 1.1 ### heading 1.1.1 @@ -1979,24 +2026,26 @@ given precondition foo ### Subheadings don't start new scenario ~~~scenario +given file subisnotnewscenario.subplot given file subisnotnewscenario.md and file b.yaml and file f.py and an installed subplot -when I run subplot codegen --run subisnotnewscenario.md -o test.py +when I run subplot codegen --run subisnotnewscenario.subplot -o test.py then scenario "heading 1.1a" was run and command is successful ~~~ -~~~~{#subisnotnewscenario.md .file .markdown .numberLines} - ---- +~~~~{#subisnotnewscenario.subplot .file .yaml .numberLines} title: Test scenario +markdowns: +- subisnotnewscenario.md bindings: [b.yaml] impls: python: [f.py] -... +~~~~ +~~~~{#subisnotnewscenario.md .file .markdown .numberLines} # heading 1 ## heading 1.1a @@ -2006,31 +2055,33 @@ given precondition foo ### heading 1.1.1 ### heading 1.1.2 - ~~~~ ### Next heading at same level starts new scenario ~~~scenario +given file samelevelisnewscenario.subplot given file samelevelisnewscenario.md and file b.yaml and file f.py and an installed subplot -when I run subplot codegen --run samelevelisnewscenario.md -o test.py +when I run subplot codegen --run samelevelisnewscenario.subplot -o test.py then scenario "heading 1.1.1" was run and scenario "heading 1.1.2" was run and command is successful ~~~ -~~~~{#samelevelisnewscenario.md .file .markdown .numberLines} - ---- +~~~~{#samelevelisnewscenario.subplot .file .yaml .numberLines} title: Test scenario +markdowns: +- samelevelisnewscenario.md bindings: [b.yaml] impls: python: [f.py] -... +~~~~ + +~~~~{#samelevelisnewscenario.md .file .markdown .numberLines} # heading 1 ## heading 1.1 ### heading 1.1.1 @@ -2049,25 +2100,27 @@ given precondition foo ### Next heading at higher level starts new scenario ~~~scenario +given file higherisnewscenario.subplot given file higherisnewscenario.md and file b.yaml and file f.py and an installed subplot -when I run subplot codegen --run higherisnewscenario.md -o test.py +when I run subplot codegen --run higherisnewscenario.subplot -o test.py then scenario "heading 1.1.1" was run and scenario "heading 1.2" was run and command is successful ~~~ -~~~~{#higherisnewscenario.md .file .markdown .numberLines} - ---- +~~~~{#higherisnewscenario.subplot .file .yaml .numberLines} title: Test scenario +markdowns: +- higherisnewscenario.md bindings: [b.yaml] impls: python: [f.py] -... +~~~~ +~~~~{#higherisnewscenario.md .file .markdown .numberLines} # heading 1 ## heading 1.1 ### heading 1.1.1 @@ -2094,23 +2147,24 @@ ikiwiki, and ikiwiki has a different way of specifying page titles. #### Document generator gives an error if input document lacks title ~~~scenario +given file notitle.subplot given file notitle.md and an installed subplot -when I try to run subplot docgen notitle.md -o foo.md +when I try to run subplot docgen notitle.subplot -o foo.md then command fails ~~~ -~~~{#notitle.md .file .markdown .numberLines} ---- +~~~{#notitle.subplot .file .yaml .numberLines} +markdowns: +- notitle.md bindings: [b.yaml] functions: [f.py] -... - +~~~ +~~~{#notitle.md .file .markdown .numberLines} # Introduction -This is a very simple Markdown file without a YAML metadata block, -and thus also no document title. +This is a very simple Markdown file without a document title. ```scenario given precondition foo @@ -2121,9 +2175,10 @@ then bar was done #### Code generator gives an error if input document lacks title ~~~scenario +given file notitle.subplot given file notitle.md and an installed subplot -when I try to run subplot codegen --run notitle.md -o test.py +when I try to run subplot codegen --run notitle.subplot -o test.py then command fails ~~~ @@ -2134,25 +2189,27 @@ Markdown allows using any inline markup in document titles and chapter and section headings. Verify that Subplot accepts them. ~~~scenario +given file fancytitle.subplot given file fancytitle.md and file b.yaml and file f.py and an installed subplot -when I try to run subplot docgen fancytitle.md -o foo.md +when I try to run subplot docgen fancytitle.subplot -o foo.md then command is successful -when I try to run subplot codegen fancytitle.md -o foo.md +when I try to run subplot codegen fancytitle.subplot -o foo.md then command is successful ~~~ -~~~~~~{#fancytitle.md .file .markdown .numberLines} ---- +~~~~~~{#fancytitle.subplot .file .yaml .numberLines} title: Plain *emph* **strong** ~~strikeout~~ superscript^10^ subscript~10~ +markdowns: +- fancytitle.md bindings: [b.yaml] impls: python: [f.py] -... - +~~~~~~ +~~~~~~{#fancytitle.md .file .markdown .numberLines} # `code` [smallcaps]{.smallcaps} $$2^10$$ ## "double quoted" @@ -2172,7 +2229,6 @@ To satisfy codegen, we *MUST* have a scenario here when I do bar then bar was done ~~~~ - ~~~~~~ @@ -2188,25 +2244,28 @@ This verifies that the generated Python test program can run only chosen scenarios. ~~~scenario +given file twoscenarios-python.subplot given file twoscenarios-python.md and file b.yaml and file f.py and an installed subplot -when I run subplot codegen twoscenarios-python.md -o test.py +when I run subplot codegen twoscenarios-python.subplot -o test.py and I run python3 test.py on then scenario "One" was run and scenario "Two" was not run and command is successful ~~~ -~~~{#twoscenarios-python.md .file .markdown .numberLines} ---- +~~~{#twoscenarios-python.subplot .file .yaml .numberLines} title: Test scenario +markdowns: +- twoscenarios-python.md bindings: [b.yaml] impls: python: [f.py] -... +~~~ +~~~{#twoscenarios-python.md .file .markdown .numberLines} # One ```scenario @@ -2230,11 +2289,12 @@ This verifies that the generated Bash test program can run only chosen scenarios. ~~~scenario +given file twoscenarios-bash.subplot given file twoscenarios-bash.md and file b.yaml and file f.sh and an installed subplot -when I run subplot codegen twoscenarios-bash.md -o test.sh +when I run subplot codegen twoscenarios-bash.subplot -o test.sh and I run bash test.sh on then scenario "One" was run and scenario "Two" was not run @@ -2242,14 +2302,16 @@ and command is successful ~~~ -~~~{#twoscenarios-bash.md .file .markdown .numberLines} ---- +~~~{#twoscenarios-bash.subplot .file .yaml .numberLines} title: Test scenario +markdowns: +- twoscenarios-bash.md bindings: [b.yaml] impls: bash: [f.sh] -... +~~~ +~~~{#twoscenarios-bash.md .file .markdown .numberLines} # One ```scenario @@ -2322,9 +2384,11 @@ This scenario tests that the `date` field in metadata is used if specified. ~~~scenario +given file metadate.subplot given file metadate.md and an installed subplot -when I run subplot docgen metadate.md -o metadate.html +when I run subplot docgen metadate.subplot -o metadate.html +when I run cat metadate.html then file metadate.html exists and file metadate.html contains "The Fabulous Title" and file metadate.html contains "Alfred Pennyworth" @@ -2332,14 +2396,15 @@ and file metadate.html contains "Geoffrey Butler" and file metadate.html contains "WIP" ~~~ -~~~{#metadate.md .file .markdown .numberLines} ---- +~~~{#metadate.subplot .file .yaml .numberLines} title: The Fabulous Title -author: -- Alfred Pennyworth -- Geoffrey Butler +author: Alfred Pennyworth and Geoffrey Butler date: WIP -... +markdowns: +- metadate.md +~~~ + +~~~{#metadate.md .file .markdown .numberLines} # Introduction This is a test document. That's all. ~~~ @@ -2349,9 +2414,10 @@ This is a test document. That's all. This scenario tests that the `--date` command line option is used. ~~~scenario +given file dateless.subplot given file dateless.md and an installed subplot -when I run subplot docgen dateless.md -o dateoption.html --date=FANCYDATE +when I run subplot docgen dateless.subplot -o dateoption.html --date=FANCYDATE then file dateoption.html exists and file dateoption.html contains "The Fabulous Title" and file dateoption.html contains "Alfred Pennyworth" @@ -2359,13 +2425,14 @@ and file dateoption.html contains "Geoffrey Butler" and file dateoption.html contains "FANCYDATE" ~~~ -~~~{#dateless.md .file .markdown .numberLines} ---- +~~~{#dateless.subplot .file .yaml .numberLines} title: The Fabulous Title -author: -- Alfred Pennyworth -- Geoffrey Butler -... +author: Alfred Pennyworth and Geoffrey Butler +markdowns: +- dateless.md +~~~ + +~~~{#dateless.md .file .markdown .numberLines} # Introduction This is a test document. It has no date metadata. ~~~ @@ -2378,10 +2445,11 @@ modification time of the input file, and shall have the date in ISO 8601 format, with time to the minute. ~~~scenario +given file dateless.subplot given file dateless.md and file dateless.md has modification time 2020-02-26 07:53:17 and an installed subplot -when I run subplot docgen dateless.md -o mtime.html +when I run subplot docgen dateless.subplot -o mtime.html then file mtime.html exists and file mtime.html contains "The Fabulous Title" and file mtime.html contains "Alfred Pennyworth" @@ -2395,19 +2463,23 @@ If a bindings file is missing, the error message should name the missing file. ~~~scenario +given file missing-binding.subplot given file missing-binding.md and an installed subplot -when I try to run subplot docgen missing-binding.md -o foo.html +when I try to run subplot docgen missing-binding.subplot -o foo.html then command fails and stderr contains "could not be found" and stderr contains "missing-binding.yaml" ~~~ -~~~{#missing-binding.md .file .markdown .numberLines} ---- +~~~{#missing-binding.subplot .file .yaml .numberLines} title: Missing binding +markdowns: +- missing-binding.md bindings: [missing-binding.yaml] -... +~~~ + +~~~{#missing-binding.md .file .markdown .numberLines} ~~~ ### Missing functions file @@ -2416,24 +2488,30 @@ If a functions file is missing, the error message should name the missing file. ~~~scenario +given file missing-functions.subplot given file missing-functions.md and file b.yaml and an installed subplot -when I try to run subplot codegen --run missing-functions.md -o foo.py +when I try to run subplot codegen --run missing-functions.subplot -o foo.py then command fails and stderr contains "could not be found" and stderr contains "missing-functions.py" ~~~ -~~~{#missing-functions.md .file .markdown .numberLines} +~~~{#missing-functions.subplot .file .yaml .numberLines} --- title: Missing functions +markdowns: +- missing-functions.md bindings: [b.yaml] impls: python: [missing-functions.py] ... ~~~ +~~~{#missing-functions.md .file .markdown .numberLines} +~~~ + ### Extracting metadata from a document The **subplot metadata** program extracts metadata from a document. It is @@ -2454,6 +2532,7 @@ This scenario check subplot metadata works. Note that it requires the bindings or functions files. ~~~scenario +given file images.subplot given file images.md and file b.yaml and file other.yaml @@ -2463,7 +2542,7 @@ and file foo.bib and file bar.bib and file expected.json and an installed subplot -when I run subplot metadata images.md +when I run subplot metadata images.subplot then stdout contains "source: images.md" and stdout contains "source: b.yaml" and stdout contains "source: other.yaml" @@ -2475,14 +2554,15 @@ and stdout contains "source: image.gif" and stdout contains "bindings: b.yaml" and stdout contains "bindings: other.yaml" and stdout contains "functions[python]: f.py" -when I run subplot metadata images.md -o json +when I run subplot metadata images.subplot -o json then JSON output matches expected.json ~~~ -~~~{#images.md .file .markdown .numberLines} ---- +~~~{#images.subplot .file .yaml .numberLines} title: Document refers to external images +markdowns: +- images.md bindings: - b.yaml - other.yaml @@ -2491,8 +2571,9 @@ impls: - f.py - other.py bibliography: [foo.bib, bar.bib] -... +~~~ +~~~{#images.md .file .markdown .numberLines} ![alt text](image.gif) ~~~ @@ -2582,18 +2663,21 @@ This scenario checks that an embedded file can be extracted, and used in a subplot. ~~~scenario +given file embedded.subplot given file embedded.md and an installed subplot -when I run subplot docgen --merciful embedded.md -o foo.html +when I run subplot docgen --merciful embedded.subplot -o foo.html then file foo.html exists and file foo.html matches regex /embedded\.txt/ ~~~ -~~~~~~~{#embedded.md .file .markdown .numberLines} ---- +~~~~~~~{#embedded.subplot .file .yaml .numberLines} title: One embedded file -... +markdowns: +- embedded.md +~~~~~~~ +~~~~~~~{#embedded.md .file .markdown .numberLines} ~~~{#embedded.txt .file} This is the embedded file. ~~~ @@ -2763,19 +2847,22 @@ This scenario checks that we get warnings, when using a subplot with embedded files that aren't used. ~~~scenario +given file unusedfile.subplot given file unusedfile.md and an installed subplot -when I try to run subplot docgen --merciful unusedfile.md -o unusedfile.html +when I try to run subplot docgen --merciful unusedfile.subplot -o unusedfile.html then command is successful and file unusedfile.html exists and stderr contains "thisisnotused.txt" ~~~ -~~~~{#unusedfile.md .file .markdown .numberLines} ---- +~~~~{#unusedfile.subplot .file .yaml .numberLines} title: Embedded file is not used by a scenario -... +markdowns: +- unusedfile.md +~~~~ +~~~~{#unusedfile.md .file .markdown .numberLines} ```{#thisisnotused.txt .file} This is the embedded file. ``` @@ -2791,19 +2878,22 @@ subject to the same naming constraints (caseless uniqueness). ### Examples may be unused ~~~scenario +given file unusedexample.subplot given file unusedexample.md and an installed subplot -when I try to run subplot docgen --merciful unusedexample.md -o unusedexample.html +when I try to run subplot docgen --merciful unusedexample.subplot -o unusedexample.html then command is successful and file unusedexample.html exists and stderr doesn't contain "thisisnotused.txt" ~~~ -~~~{#unusedexample.md .file .markdown .numberLines} ---- +~~~{#unusedexample.subplot .file .yaml .numberLines} title: Example is not an embedded file -... +markdowns: +- unusedexample.md +~~~ +~~~{#unusedexample.md .file .markdown .numberLines} ```{#thisisnotused.txt .example} This is the embedded example. ``` @@ -2812,20 +2902,24 @@ This is the embedded example. ### Examples are not files ~~~scenario +given file examplesnotfiles.subplot given file examplesnotfiles.md and an installed subplot -when I try to run subplot codegen examplesnotfiles.md -t python -o examplesnotfiles.html +when I try to run subplot codegen examplesnotfiles.subplot -t python -o examplesnotfiles.html then command fails and file examplesnotfiles.html does not exist and stderr contains "thisisanexample.txt" ~~~ -~~~{#examplesnotfiles.md .file .markdown .numberLines} ---- +~~~{#examplesnotfiles.subplot .file .yaml .numberLines} title: Examples are not files +markdowns: +- examplesnotfiles.md impls: python: [] -... +~~~ + +~~~{#examplesnotfiles.md .file .markdown .numberLines} # Try and use an example as a file @@ -2836,7 +2930,6 @@ given file thisisanexample.txt ```{#thisisanexample.txt .example} This is an embedded example ``` - ~~~ ## Steps must match bindings @@ -2865,12 +2958,15 @@ binding. ### Steps which do not match bindings do not work -~~~~{#nobinding.md .file .markdown} ---- +~~~~{#nobinding.subplot .file .yaml} title: No bindings available +markdowns: +- nobinding.md bindings: - badbindings.yaml -... +~~~~ + +~~~~{#nobinding.md .file .markdown} # Broken scenario because step has no binding ```scenario @@ -2880,22 +2976,26 @@ then nothing works ~~~~ ```scenario +given file nobinding.subplot given file nobinding.md and file badbindings.yaml and an installed subplot -when I try to run subplot codegen --run nobinding.md -o test.py +when I try to run subplot codegen --run nobinding.subplot -o test.py then command fails ``` ### Steps which do not case-sensitively match sensitive bindings do not work -~~~~{#casemismatch.md .file .markdown} ---- +~~~~{#casemismatch.subplot .file .yaml} title: Case sensitivity mismatch +markdowns: +- casemismatch.md impls: { python: [] } bindings: - badbindings.yaml -... +~~~~ + +~~~~{#casemismatch.md .file .markdown} # Broken scenario because step has a case mismatch with sensitive binding ```scenario @@ -2904,23 +3004,27 @@ given a capitalised binding ~~~~ ```scenario +given file casemismatch.subplot given file casemismatch.md and file badbindings.yaml and an installed subplot -when I try to run subplot codegen --run casemismatch.md -o test.py +when I try to run subplot codegen --run casemismatch.subplot -o test.py then command fails ``` ### Steps which match more than one binding do not work -~~~~{#twobindings.md .file .markdown} ---- +~~~~{#twobindings.subplot .file .yaml} title: Two bindings match +markdowns: +- twobindings.md bindings: - twobindings.yaml impls: python: [a_function.py] -... +~~~~ + +~~~~{#twobindings.md .file .markdown} # Broken scenario because step has two possible bindings ```scenario @@ -2945,11 +3049,12 @@ def a_function(ctx): ~~~ ```scenario +given file twobindings.subplot given file twobindings.md and file twobindings.yaml given file a_function.py and an installed subplot -when I try to run subplot codegen --run twobindings.md -o test.py +when I try to run subplot codegen --run twobindings.subplot -o test.py then command fails then stderr contains "xyzzy" then stderr contains "plugh" @@ -2961,18 +3066,21 @@ then stderr contains "plugh" The `subplot metadata` command lists embedded files in its output. ~~~scenario +given file two-embedded.subplot given file two-embedded.md and an installed subplot -when I run subplot metadata --merciful two-embedded.md +when I run subplot metadata --merciful two-embedded.subplot then stdout contains "foo.txt" and stdout contains "bar.yaml" ~~~ -~~~~~~{#two-embedded.md .file .markdown .numberLines} ---- +~~~~~~{#two-embedded.subplot .file .yaml .numberLines} title: Two embedded files -... +markdowns: +- two-embedded.md +~~~~~~ +~~~~~~{#two-embedded.md .file .markdown .numberLines} ~~~{#foo.txt .file} ~~~ @@ -3005,9 +3113,10 @@ The scenario checks that a diagram is generated and embedded into the HTML outpu and is not referenced as an external image. ~~~scenario +given file pikchr.subplot given file pikchr.md and an installed subplot -when I run subplot docgen pikchr.md -o pikchr.html +when I run subplot docgen pikchr.subplot -o pikchr.html then file pikchr.html matches regex /src="data:image/svg\+xml;base64,/ ~~~ @@ -3015,9 +3124,6 @@ The sample input file **pikchr.md**: ~~~~~~~~{#pikchr.md .file .markdown .numberLines} --- -title: Pikchr test -... - This is an example markdown file that embeds a simple Pikchr diagram. ~~~pikchr @@ -3030,6 +3136,12 @@ box same "Pikchr" "Formatter" "(docs.rs/pikchr)" fit ~~~~~~~~ +~~~~~~~~{#pikchr.subplot .file .yaml .numberLines} +title: Pikchr test +markdowns: +- pikchr.md +~~~~~~~~ + ### Dot [Graphviz]: http://www.graphviz.org/ @@ -3047,20 +3159,17 @@ The scenario checks that a diagram is generated and embedded into the HTML output, not referenced as an external image. ~~~scenario +given file dot.subplot given file dot.md and file b.yaml and an installed subplot -when I run subplot docgen dot.md -o dot.html +when I run subplot docgen dot.subplot -o dot.html then file dot.html matches regex /src="data:image/svg\+xml;base64,/ ~~~ The sample input file **dot.md**: ~~~~~~~~{#dot.md .file .markdown .numberLines} ---- -title: Dot test -... - This is an example Markdown file, which embeds a diagram using dot markup. ~~~dot @@ -3070,6 +3179,12 @@ thing -> other ~~~ ~~~~~~~~ +~~~~~~~~{#dot.subplot .file .yaml .numberLines} +title: Dot test +markdowns: +- dot.md +~~~~~~~~ + ### PlantUML @@ -3093,20 +3208,17 @@ The scenario below checks that a diagram is generated and embedded into the HTML output, not referenced as an external image. ~~~scenario +given file plantuml.subplot given file plantuml.md and file b.yaml and an installed subplot -when I run subplot docgen plantuml.md -o plantuml.html +when I run subplot docgen plantuml.subplot -o plantuml.html then file plantuml.html matches regex /src="data:image/svg\+xml;base64,/ ~~~ The sample input file **plantuml.md**: ~~~~~~~~{#plantuml.md .file .markdown .numberLines} ---- -title: Plantuml test -... - This is an example Markdown file, which embeds a diagram using PlantUML markup. @@ -3121,6 +3233,12 @@ Alice <-- Bob: Another authentication Response ~~~ ~~~~~~~~ +~~~~~~~~{#plantuml.subplot .file .yaml .numberLines} +title: Plantuml test +markdowns: +- plantuml.md +~~~~~~~~ + ### Roadmap @@ -3178,20 +3296,17 @@ This scenario checks that a diagram is generated and embedded into the HTML output, not referenced as an external image. ~~~scenario +given file roadmap.subplot given file roadmap.md and file b.yaml and an installed subplot -when I run subplot docgen roadmap.md -o roadmap.html +when I run subplot docgen roadmap.subplot -o roadmap.html then file roadmap.html matches regex /src="data:image/svg\+xml;base64,/ ~~~ The sample input file **roadmap.md**: ~~~~~~~~{#roadmap.md .file .markdown .numberLines} ---- -title: Roadmap test -... - This is an example Markdown file, which embeds a roadmap. ~~~roadmap @@ -3238,6 +3353,12 @@ blocked: ~~~ ~~~~~~~~ +~~~~~~~~{#roadmap.subplot .file .yaml .numberLines} +title: Roadmap test +markdowns: +- roadmap.md +~~~~~~~~ + ### Class name validation @@ -3250,40 +3371,44 @@ include a `classes` list in the document metadata which subplot will treat as valid. ~~~scenario +given file unknown-class-name.subplot given file unknown-class-name.md +and file known-class-name.subplot and file known-class-name.md and file b.yaml and an installed subplot -when I try to run subplot docgen unknown-class-name.md -o unknown-class-name.html +when I try to run subplot docgen unknown-class-name.subplot -o unknown-class-name.html then command fails and file unknown-class-name.html does not exist and stderr contains "Unknown classes found in the document: foobar" -when I run subplot docgen known-class-name.md -o known-class-name.html +when I run subplot docgen known-class-name.subplot -o known-class-name.html then file known-class-name.html exists ~~~ -~~~~~~~~{#unknown-class-name.md .file .markdown .numberLines} ---- +~~~~~~~~{#unknown-class-name.subplot .file .yaml .numberLines} title: A document with an unknown class name -... +markdowns: +- unknown-class-name.md +~~~~~~~~ +~~~~~~~~{#unknown-class-name.md .file .markdown .numberLines} ```foobar This content is foobarish ``` - ~~~~~~~~ -~~~~~~~~{#known-class-name.md .file .markdown .numberLines} ---- +~~~~~~~~{#known-class-name.subplot .file .yaml .numberLines} title: A document with a previously unknown class name +markdowns: +- known-class-name.md classes: - foobar -... +~~~~~~~~ +~~~~~~~~{#known-class-name.md .file .markdown .numberLines} ```foobar This content is foobarish ``` - ~~~~~~~~ ## Extract embedded files @@ -3291,22 +3416,24 @@ This content is foobarish `subplot extract` extracts embedded files from a subplot file. ~~~scenario +given file embedded-file.subplot given file embedded-file.md and file expected.txt and an installed subplot -when I run subplot extract --merciful embedded-file.md foo.txt -d . +when I run subplot extract --merciful embedded-file.subplot foo.txt -d . then files foo.txt and expected.txt match ~~~ -~~~~~~{#embedded-file.md .file .markdown .numberLines} ---- +~~~~~~{#embedded-file.subplot .file .yaml .numberLines} title: Embedded file -... +markdowns: +- embedded-file.md +~~~~~~ +~~~~~~{#embedded-file.md .file .markdown .numberLines} ~~~{#foo.txt .file} This is a test file. ~~~ - ~~~~~~ ~~~{#expected.txt .file} diff --git a/subplot.subplot b/subplot.subplot new file mode 100644 index 0000000..61ecd4f --- /dev/null +++ b/subplot.subplot @@ -0,0 +1,18 @@ +title: "Subplot" +author: The Subplot project +markdowns: + - subplot.md +bindings: +- subplot.yaml +- lib/runcmd.yaml +- lib/files.yaml +impls: + python: + - subplot.py + - lib/files.py + - lib/runcmd.py + rust: + - subplotlib/subplot-rust-support.rs +classes: +- json +- ignored diff --git a/subplotlib/build.rs b/subplotlib/build.rs index f22a50c..e5f8917 100644 --- a/subplotlib/build.rs +++ b/subplotlib/build.rs @@ -2,8 +2,8 @@ // // We use the `subplot_build` crate to generate a Rust test code file // with functions for the scenarios for each subplot file -// (subplot/*.md in the source tree). The generated file is written to -// the Cargo target directory. For each subplot foo.md there should be +// (subplot/*.subplot in the source tree). The generated file is written to +// the Cargo target directory. For each subplot foo.subplot there should be // a tests/foo.rs in the source tree, which includes the generated // file from the Cargo target tree. The source file should look like this: // @@ -15,11 +15,11 @@ use glob::glob; use std::{fs, path::Path}; fn gen_tests() { - let subplots = glob("*.md").expect("failed to find subplots in subplotlib"); + let subplots = glob("*.subplot").expect("failed to find subplots in subplotlib"); let tests = Path::new("tests"); - let subplots = subplots.chain(Some(Ok("../subplot.md".into()))); + let subplots = subplots.chain(Some(Ok("../subplot.subplot".into()))); let subplots = subplots - .chain(glob("../tests/subplots/common/*.md").expect("failed to find common subplots")); + .chain(glob("../tests/subplots/common/*.subplot").expect("failed to find common subplots")); for entry in subplots { let entry = entry.expect("failed to get subplot dir entry in subplotlib"); let mut inc = tests.join(&entry.file_name().unwrap()); @@ -35,8 +35,8 @@ fn gen_tests() { fn main() { // Because we cannot generate tests if we're not fully inside the main subplot tree - // we only generate them if we can see ../subplot.md which is a good indicator. - if fs::metadata("../subplot.md").is_ok() { + // we only generate them if we can see ../subplot.subplot which is a good indicator. + if fs::metadata("../subplot.subplot").is_ok() { gen_tests(); } } diff --git a/subplotlib/subplotlib.subplot b/subplotlib/subplotlib.subplot new file mode 100644 index 0000000..e12804e --- /dev/null +++ b/subplotlib/subplotlib.subplot @@ -0,0 +1,10 @@ +title: Testing the Rust crate "subplotlib" +markdowns: + - subplotlib.md +bindings: + - subplotlib.yaml + - lib/datadir.yaml +impls: + rust: + - helpers/subplotlib_context.rs + - helpers/subplotlib_impl.rs diff --git a/tests/subplots/common/files.md b/tests/subplots/common/files.md index d8d598b..521dd28 100644 --- a/tests/subplots/common/files.md +++ b/tests/subplots/common/files.md @@ -95,14 +95,3 @@ and directory second/third is not empty when I remove directory second then directory second does not exist ``` - ---- -title: Acceptance criteria for the files Subplot library -author: The Subplot project -bindings: -- lib/files.yaml -impls: - python: - - lib/files.py - rust: [] -... diff --git a/tests/subplots/common/files.subplot b/tests/subplots/common/files.subplot new file mode 100644 index 0000000..edf7a26 --- /dev/null +++ b/tests/subplots/common/files.subplot @@ -0,0 +1,10 @@ +title: Acceptance criteria for the files Subplot library +author: The Subplot project +markdowns: +- files.md +bindings: +- lib/files.yaml +impls: + python: + - lib/files.py + rust: [] diff --git a/tests/subplots/common/runcmd.md b/tests/subplots/common/runcmd.md index 4f66685..4fdb3f3 100644 --- a/tests/subplots/common/runcmd.md +++ b/tests/subplots/common/runcmd.md @@ -197,20 +197,3 @@ given helper script err.sh for runcmd when I run sh err.sh hi then stderr doesn't match regex world$ ~~~ - - ---- -title: Acceptance criteria for the lib/runcmd Subplot library -author: The Subplot project -bindings: -- lib/runcmd.yaml -- runcmd_test.yaml -- lib/files.yaml -impls: - python: - - lib/runcmd.py - - runcmd_test.py - - lib/files.py - rust: - - runcmd_test.rs -... diff --git a/tests/subplots/common/runcmd.subplot b/tests/subplots/common/runcmd.subplot new file mode 100644 index 0000000..e30e5a8 --- /dev/null +++ b/tests/subplots/common/runcmd.subplot @@ -0,0 +1,15 @@ +title: Acceptance criteria for the lib/runcmd Subplot library +author: The Subplot project +markdowns: +- runcmd.md +bindings: +- lib/runcmd.yaml +- runcmd_test.yaml +- lib/files.yaml +impls: + python: + - lib/runcmd.py + - runcmd_test.py + - lib/files.py + rust: + - runcmd_test.rs -- cgit v1.2.1