summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2020-07-13 07:10:39 +0000
committerLars Wirzenius <liw@liw.fi>2020-07-13 07:10:39 +0000
commita5cf2de561dd75686528b4c83a55a9f756b077bc (patch)
tree066cf2cfec5abf4c9e12463935021b12e642c7f3
parent35aa4d6710389e5b5b364109ac5a0cb375112d87 (diff)
parent4a745bd12a52ac12d58303f57e2660f3a051606e (diff)
downloadsubplot-a5cf2de561dd75686528b4c83a55a9f756b077bc.tar.gz
Merge branch 'fix-12' into 'master'
Case insensitivity support Closes #12 See merge request larswirzenius/subplot!54
-rw-r--r--Cargo.lock13
-rw-r--r--Cargo.toml1
-rw-r--r--src/bindings.rs131
-rw-r--r--src/steps.rs6
-rw-r--r--subplot.md25
5 files changed, 144 insertions, 32 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7dabe73..3f75316 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -557,6 +557,17 @@ dependencies = [
]
[[package]]
+name = "serde-aux"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "serde_derive"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -645,6 +656,7 @@ dependencies = [
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"roadmap 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde-aux 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -958,6 +970,7 @@ dependencies = [
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
+"checksum serde-aux 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae50f53d4b01e854319c1f5b854cd59471f054ea7e554988850d3f36ca1dc852"
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7"
"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
diff --git a/Cargo.toml b/Cargo.toml
index 333217f..1b88a76 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,6 +25,7 @@ regex = "1"
serde_yaml = "0.8.11"
serde_json = "1.0"
serde = { version = "1.0.101", features = ["derive"] }
+serde-aux = "0.6.1"
chrono = "0.4"
thiserror = "1"
anyhow = "1"
diff --git a/src/bindings.rs b/src/bindings.rs
index 40ef0b4..9d80394 100644
--- a/src/bindings.rs
+++ b/src/bindings.rs
@@ -5,12 +5,13 @@ use super::StepKind;
use crate::{Result, SubplotError};
use serde::Deserialize;
+use serde_aux::prelude::*;
use std::fs::File;
use std::io::Read;
use std::path::Path;
-use regex::{escape, Regex};
+use regex::{escape, Regex, RegexBuilder};
/// A binding of a scenario step to its implementation.
///
@@ -33,11 +34,15 @@ impl Binding {
pattern: &str,
function: &str,
cleanup: Option<&str>,
+ case_sensitive: bool,
) -> Result<Binding> {
+ let regex = RegexBuilder::new(&format!("^{}$", pattern))
+ .case_insensitive(!case_sensitive)
+ .build()?;
Ok(Binding {
kind,
pattern: pattern.to_owned(),
- regex: Regex::new(&format!("^{}$", pattern))?,
+ regex,
function: function.to_string(),
cleanup: cleanup.map(String::from),
})
@@ -149,7 +154,7 @@ mod test_binding {
#[test]
fn creates_new_without_cleanup() {
- let b = Binding::new(StepKind::Given, "I am Tomjon", "set_name", None).unwrap();
+ let b = Binding::new(StepKind::Given, "I am Tomjon", "set_name", None, false).unwrap();
assert_eq!(b.kind(), StepKind::Given);
assert!(b.regex().is_match("I am Tomjon"));
assert!(!b.regex().is_match("I am Tomjon of Lancre"));
@@ -165,6 +170,7 @@ mod test_binding {
"I am Tomjon",
"set_name",
Some("unset_name"),
+ false,
)
.unwrap();
assert_eq!(b.kind(), StepKind::Given);
@@ -177,36 +183,57 @@ mod test_binding {
#[test]
fn equal() {
- let a = Binding::new(StepKind::Given, "I am Tomjon", "set_name", Some("unset")).unwrap();
- let b = Binding::new(StepKind::Given, "I am Tomjon", "set_name", Some("unset")).unwrap();
+ let a = Binding::new(
+ StepKind::Given,
+ "I am Tomjon",
+ "set_name",
+ Some("unset"),
+ false,
+ )
+ .unwrap();
+ let b = Binding::new(
+ StepKind::Given,
+ "I am Tomjon",
+ "set_name",
+ Some("unset"),
+ false,
+ )
+ .unwrap();
assert_eq!(a, b);
}
#[test]
fn not_equal() {
- let a = Binding::new(StepKind::Given, "I am Tomjon", "set_name", None).unwrap();
- let b = Binding::new(StepKind::Given, "I am Tomjon of Lancre", "set_name", None).unwrap();
+ let a = Binding::new(StepKind::Given, "I am Tomjon", "set_name", None, false).unwrap();
+ let b = Binding::new(
+ StepKind::Given,
+ "I am Tomjon of Lancre",
+ "set_name",
+ None,
+ false,
+ )
+ .unwrap();
assert_ne!(a, b);
}
#[test]
fn does_not_match_with_wrong_kind() {
let step = ScenarioStep::new(StepKind::Given, "given", "yo");
- let b = Binding::new(StepKind::When, "yo", "do_yo", None).unwrap();
+ let b = Binding::new(StepKind::When, "yo", "do_yo", None, false).unwrap();
assert!(b.match_with_step(&step).is_none());
}
#[test]
fn does_not_match_with_wrong_text() {
let step = ScenarioStep::new(StepKind::Given, "given", "foo");
- let b = Binding::new(StepKind::Given, "bar", "yo", None).unwrap();
+ let b = Binding::new(StepKind::Given, "bar", "yo", None, false).unwrap();
assert!(b.match_with_step(&step).is_none());
}
#[test]
fn match_with_fixed_pattern() {
let step = ScenarioStep::new(StepKind::Given, "given", "foo");
- let b = Binding::new(StepKind::Given, "foo", "do_foo", None).unwrap();
+ let b = Binding::new(StepKind::Given, "foo", "do_foo", None, false).unwrap();
let m = b.match_with_step(&step).unwrap();
assert_eq!(m.kind(), StepKind::Given);
let mut parts = m.parts();
@@ -223,6 +250,7 @@ mod test_binding {
r"I am (?P<who>\S+), I am",
"set_name",
None,
+ false,
)
.unwrap();
let m = b.match_with_step(&step).unwrap();
@@ -233,6 +261,15 @@ mod test_binding {
assert_eq!(parts.next().unwrap(), &PartialStep::uncaptured(", I am"));
assert_eq!(parts.next(), None);
}
+
+ #[test]
+ fn case_sensitive_mismatch() {
+ let step = ScenarioStep::new(StepKind::Given, "given", "I am Tomjon");
+ let b = Binding::new(StepKind::Given, r"i am tomjon", "set_name", None, false).unwrap();
+ assert!(b.match_with_step(&step).is_some());
+ let b = Binding::new(StepKind::Given, r"i am tomjon", "set_name", None, true).unwrap();
+ assert!(b.match_with_step(&step).is_none());
+ }
}
/// Set of all known bindings.
@@ -255,6 +292,15 @@ struct ParsedBinding {
function: String,
cleanup: Option<String>,
regex: Option<bool>,
+ #[serde(default)]
+ case_sensitive: bool,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(transparent)]
+struct ParsedBindingWrapper {
+ #[serde(deserialize_with = "deserialize_struct_case_insensitive")]
+ binding: ParsedBinding,
}
impl Bindings {
@@ -280,9 +326,9 @@ impl Bindings {
/// Add bindings from a YAML string
pub fn add_from_yaml(&mut self, yaml: &str) -> Result<()> {
- let bindings: Vec<ParsedBinding> = serde_yaml::from_str(yaml)?;
- for b in bindings {
- self.add(from_hashmap(&b)?);
+ let bindings: Vec<ParsedBindingWrapper> = serde_yaml::from_str(yaml)?;
+ for wrapper in bindings {
+ self.add(from_hashmap(&wrapper.binding)?);
}
Ok(())
}
@@ -372,6 +418,7 @@ fn from_hashmap(parsed: &ParsedBinding) -> Result<Binding> {
None => None,
Some(ref s) => Some(s),
},
+ parsed.case_sensitive,
)?)
}
@@ -392,8 +439,14 @@ mod test_bindings {
#[test]
fn adds_binding() {
- let binding =
- Binding::new(StepKind::Given, r"I am (?P<name>\S+)", "set_name", None).unwrap();
+ let binding = Binding::new(
+ StepKind::Given,
+ r"I am (?P<name>\S+)",
+ "set_name",
+ None,
+ false,
+ )
+ .unwrap();
let mut bindings = Bindings::new();
bindings.add(binding.clone());
assert_eq!(bindings.bindings(), &[binding]);
@@ -402,12 +455,15 @@ mod test_bindings {
#[test]
fn adds_from_yaml() {
let yaml = "
-- given: I am Tomjon
+- GIVEN: I am Tomjon
function: set_name
- when: I declare myself king
- function: declare_king
-- then: there is applause
+ Function: declare_king
+- tHEn: there is applause
function: check_for_applause
+- given: you are alice
+ function: other_name
+ case_sensitive: true
";
let mut bindings = Bindings::new();
bindings.add_from_yaml(&yaml).unwrap();
@@ -415,15 +471,17 @@ mod test_bindings {
assert!(bindings.has(StepKind::Given, "I am Tomjon"));
assert!(bindings.has(StepKind::When, "I declare myself king"));
assert!(bindings.has(StepKind::Then, "there is applause"));
- assert_eq!(bindings.len(), 3);
+ assert!(bindings.has(StepKind::Given, "you are alice"));
+ assert!(!bindings.has(StepKind::Given, "you are Alice"));
+ assert_eq!(bindings.len(), 4);
}
#[test]
fn add_from_yaml_notices_multiple_keywords() {
let yaml = "
-- given: I am Tomjon
- when: I am indeed Tomjon
- function: set_name
+- Given: I am Tomjon
+ wheN: I am indeed Tomjon
+ FUNCTION: set_name
";
match Bindings::new().add_from_yaml(&yaml) {
Ok(_) => unreachable!(),
@@ -435,7 +493,7 @@ mod test_bindings {
#[test]
fn does_not_find_match_for_unmatching_kind() {
let step = ScenarioStep::new(StepKind::Given, "given", "I am Tomjon");
- let binding = Binding::new(StepKind::When, r"I am Tomjon", "set_foo", None).unwrap();
+ let binding = Binding::new(StepKind::When, r"I am Tomjon", "set_foo", None, false).unwrap();
let mut bindings = Bindings::new();
bindings.add(binding);
assert!(match bindings.find(&step) {
@@ -447,8 +505,14 @@ mod test_bindings {
#[test]
fn does_not_find_match_for_unmatching_pattern() {
let step = ScenarioStep::new(StepKind::Given, "given", "I am Tomjon");
- let binding =
- Binding::new(StepKind::Given, r"I am Tomjon of Lancre", "set_foo", None).unwrap();
+ let binding = Binding::new(
+ StepKind::Given,
+ r"I am Tomjon of Lancre",
+ "set_foo",
+ None,
+ false,
+ )
+ .unwrap();
let mut bindings = Bindings::new();
bindings.add(binding);
assert!(match bindings.find(&step) {
@@ -461,13 +525,15 @@ mod test_bindings {
fn two_matching_bindings() {
let step = ScenarioStep::new(StepKind::Given, "given", "I am Tomjon");
let mut bindings = Bindings::default();
- bindings.add(Binding::new(StepKind::Given, r"I am Tomjon", "set_foo", None).unwrap());
+ bindings
+ .add(Binding::new(StepKind::Given, r"I am Tomjon", "set_foo", None, false).unwrap());
bindings.add(
Binding::new(
StepKind::Given,
&super::regex_from_simple_pattern(r"I am {name}", false).unwrap(),
"set_foo",
None,
+ false,
)
.unwrap(),
);
@@ -480,7 +546,8 @@ mod test_bindings {
#[test]
fn finds_match_for_fixed_string_pattern() {
let step = ScenarioStep::new(StepKind::Given, "given", "I am Tomjon");
- let binding = Binding::new(StepKind::Given, r"I am Tomjon", "set_name", None).unwrap();
+ let binding =
+ Binding::new(StepKind::Given, r"I am Tomjon", "set_name", None, false).unwrap();
let mut bindings = Bindings::new();
bindings.add(binding);
let m = bindings.find(&step).unwrap();
@@ -497,8 +564,14 @@ mod test_bindings {
#[test]
fn finds_match_for_regexp_pattern() {
let step = ScenarioStep::new(StepKind::Given, "given", "I am Tomjon");
- let binding =
- Binding::new(StepKind::Given, r"I am (?P<name>\S+)", "set_name", None).unwrap();
+ let binding = Binding::new(
+ StepKind::Given,
+ r"I am (?P<name>\S+)",
+ "set_name",
+ None,
+ false,
+ )
+ .unwrap();
let mut bindings = Bindings::new();
bindings.add(binding);
let m = bindings.find(&step).unwrap();
diff --git a/src/steps.rs b/src/steps.rs
index 1cbdfe5..9af526c 100644
--- a/src/steps.rs
+++ b/src/steps.rs
@@ -55,7 +55,7 @@ impl ScenarioStep {
_ => return Err(SubplotError::NoStepKeyword(text.to_string())),
};
- let kind = match keyword {
+ let kind = match keyword.to_ascii_lowercase().as_str() {
"given" => StepKind::Given,
"when" => StepKind::When,
"then" => StepKind::Then,
@@ -116,7 +116,7 @@ mod test {
#[test]
fn parses_given() {
- let step = ScenarioStep::new_from_str("given I am Tomjon", None).unwrap();
+ let step = ScenarioStep::new_from_str("GIVEN I am Tomjon", None).unwrap();
assert_eq!(step.kind(), StepKind::Given);
assert_eq!(step.text(), "I am Tomjon");
}
@@ -137,7 +137,7 @@ mod test {
#[test]
fn parses_then() {
- let step = ScenarioStep::new_from_str("then everyone accepts it", None).unwrap();
+ let step = ScenarioStep::new_from_str("thEN everyone accepts it", None).unwrap();
assert_eq!(step.kind(), StepKind::Then);
assert_eq!(step.text(), "everyone accepts it");
}
diff --git a/subplot.md b/subplot.md
index a73b805..c340763 100644
--- a/subplot.md
+++ b/subplot.md
@@ -1825,6 +1825,9 @@ binding.
- given: a (?:broken)? binding
function: a_broken_binding
regex: true
+- given: a capitalised Binding
+ function: os.getcwd
+ case_sensitive: true
```
### Steps which do not match bindings do not work
@@ -1850,6 +1853,28 @@ when I try to run sp-codegen --run nobinding.md -o test.py
then exit code is non-zero
```
+### Steps which do not case-sensitively match sensitive bindings do not work
+
+~~~~{#casemismatch.md .file .markdown}
+---
+title: Case sensitivity mismatch
+bindings:
+- badbindings.yaml
+...
+# Broken scenario because step has a case mismatch with sensitive binding
+
+```scenario
+given a capitalised binding
+```
+~~~~
+
+```scenario
+given file casemismatch.md
+and file badbindings.yaml
+when I try to run sp-codegen --run casemismatch.md -o test.py
+then exit code is non-zero
+```
+
### Steps which match more than one binding do not work
~~~~{#twobindings.md .file .markdown}