From 45ed92fdacc61ed7fbc1df11f82b1325cd3a0472 Mon Sep 17 00:00:00 2001 From: Lily Ballard Date: Wed, 16 Dec 2020 17:48:05 -0800 Subject: [PATCH] Add tests for the example KDL files (#1) * Add From impls for KdlNodeValue Most clients probably don't need to create these directly, but we may as well make it easy for them. * Add TryFrom impls to convert from KdlNodeValue to underlying types This includes converting from borrowed values, which will produce `&str` instead of `String`. * Add test for parsing examples/ci.kdl This tests that the file can parse, and that its contents match what we expect. This test currently fails as the file cannot be parsed. I may have gone a little overboard in the macro I wrote in order to generate the expected nodes. * Add test for parsing examples/cargo.kdl * Add test for examples/nuget.kdl Unlike the others, this one just validates that it parses, as the file is sufficiently large that I don't want to copy its contents into the test. --- tests/examples.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 tests/examples.rs diff --git a/tests/examples.rs b/tests/examples.rs new file mode 100644 index 0000000..58f4375 --- /dev/null +++ b/tests/examples.rs @@ -0,0 +1,136 @@ +//! Tests the kdl files in the examples directory. + +use kdl::*; +use std::collections::HashMap; + +/// Helper for constructing nodes. +/// +/// This takes input that's similar to KDL itself, but each node must be terminated with either +/// a semicolon or a braced block. Nodes whose name contains characters not valid in Rust +/// identifiers must be written as a string literal instead. +macro_rules! nodes { + ([$v:ident]:name) => {}; + ([$v:ident]:name $name:ident $($tt:tt)*) => { + nodes!([$v]:values stringify!($name); {} {} $($tt)*) + }; + ([$v:ident]:name $name:literal $($tt:tt)*) => { + nodes!([$v]:values $name; {} {} $($tt)*) + }; + ([$v:ident]:values $name:expr; {$($value:literal,)*} $props:tt $new_value:literal $($tt:tt)*) => { + nodes!([$v]:values $name; {$($value,)* $new_value,} $props $($tt)*) + }; + ([$v:ident]:values $name:expr; $values:tt {$($key:ident=$prop:literal,)*} $new_key:ident=$new_prop:literal $($tt:tt)*) => { + nodes!([$v]:values $name; $values {$($key=$prop,)* $new_key=$new_prop,} $($tt)*) + }; + ([$v:ident]:values $name:expr; $values:tt $props:tt $(; $($tt:tt)*)?) => { + nodes!([$v]:values $name; $values $props {} $($($tt)*)?) + }; + ([$v:ident]:values $name:expr; {$($value:literal,)*} {$($key:ident=$prop:literal,)*} {$($child:tt)*} $($tail:tt)*) => { + $v.push(KdlNode { + name: $name.to_owned(), + values: vec![$( $value.to_owned().into() ),*], + properties: { + #[allow(unused_mut)] + let mut map = HashMap::new(); + $( + map.insert(stringify!($key).to_owned(), $prop.to_owned().into()); + )* + map + }, + children: nodes!($($child)*), + }); + nodes!([$v]:name $($tail)*); + }; + // Explicitly match literal and ident at the start instead of $($tt:tt)* + // so we get better errors than "recursion limit exceeded" if we fail to match. + (:start $($tt:tt)+) => {{ + let mut v = Vec::new(); + nodes!([v]:name $($tt)+); + v + }}; + ($name:literal $($tt:tt)*) => { + nodes!(:start $name $($tt)*) + }; + ($name:ident $($tt:tt)*) => { + nodes!(:start $name $($tt)*) + }; + () => { vec![] } +} + +#[test] +fn test_ci() { + let doc = parse_document(include_str!("../examples/ci.kdl")); + let nodes = nodes! { + name "CI"; + on "push" "pull_request"; + env { + RUSTFLAGS "-Dwarnings" + } + jobs { + fmt_and_docs "Check fmt & build docs" { + "runs-on" "ubuntu-latest"; + steps { + step uses="actions/checkout@v1"; + step "Install Rust" uses="actions-rs/toolchain@v1" { + profile "minimal"; + toolchain "stable"; + components "rustfmt"; + override true; + } + step "rustfmt" run="cargo fmt --all -- --check"; + step "docs" run="cargo doc --no-deps"; + } + } + build_and_test "Build & Test" { + "runs-on" "${{ matrix.os }}"; + strategy { + matrix { + rust "1.46.0" "stable"; + os "ubuntu-latest" "macOS-latest" "windows-latest"; + } + } + + steps { + step uses="actions/checkout@v1"; + step "Install Rust" uses="actions-rs/toolchain@v1" { + profile "minimal"; + toolchain "${{ matrix.rust }}"; + components "clippy"; + override true; + } + step "Clippy" run="cargo clippy --all -- -D warnings"; + step "Run tests" run="cargo test --all --verbose"; + } + } + } + }; + assert_eq!(doc, Ok(nodes)); +} + +#[test] +fn test_cargo() { + let doc = parse_document(include_str!("../examples/cargo.kdl")); + let nodes = nodes! { + package { + name "kdl"; + version "0.0.0"; + description "kat's document language"; + authors "Kat Marchán "; + "license-file" "LICENSE.md"; + edition "2018"; + } + dependencies { + nom "6.0.1"; + thiserror "1.0.22"; + } + }; + assert_eq!(doc, Ok(nodes)); +} + +#[test] +fn test_nuget() { + let doc = parse_document(include_str!("../examples/nuget.kdl")); + // This file is particularly large. It would be nice to validate it, but for now + // I'm just going to settle for making sure it parses. + doc.expect("Parsing failed"); +}