proptest reveals that dsl breaks at invalid char boundaries

This commit is contained in:
🪞👃🪞 2025-01-27 20:02:27 +01:00
parent 2e18ca96fd
commit e8e0f5646d
11 changed files with 145 additions and 51 deletions

13
Cargo.lock generated
View file

@ -952,6 +952,17 @@ dependencies = [
"unarray", "unarray",
] ]
[[package]]
name = "proptest-derive"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "quanta" name = "quanta"
version = "0.12.5" version = "0.12.5"
@ -1496,6 +1507,7 @@ dependencies = [
"clojure-reader", "clojure-reader",
"itertools 0.14.0", "itertools 0.14.0",
"konst", "konst",
"proptest",
"tek_tui", "tek_tui",
] ]
@ -1530,6 +1542,7 @@ name = "tek_output"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"proptest", "proptest",
"proptest-derive",
"tek_edn", "tek_edn",
"tek_tui", "tek_tui",
] ]

View file

@ -13,3 +13,4 @@ default = []
[dev-dependencies] [dev-dependencies]
tek_tui = { path = "../tui" } tek_tui = { path = "../tui" }
proptest = "^1.6.0"

View file

@ -0,0 +1,7 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc bbb90b16e6106f17dbb5a4f57594f451360a2ea7e3e20c28adeb8babc98d39df # shrinks to source = "(𰀀"

View file

@ -0,0 +1,7 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 5fb814b74ae035bdecb536817090cfb473f0a874e9acf9aaa136a4794cdb367f # shrinks to source = "", start = 10336420442936153584, length = 8110323630773398032

View file

@ -108,3 +108,20 @@ pub const fn to_digit (c: char) -> Result<usize, ParseError> {
_ => return Result::Err(Unexpected(c)) _ => return Result::Err(Unexpected(c))
}) })
} }
#[cfg(test)] mod test_token_iter {
use proptest::prelude::*;
proptest! {
#[test] fn proptest_source_iter (
source in "\\PC*"
) {
let mut iter = crate::SourceIter::new(&source);
let _ = iter.next();
}
#[test] fn proptest_token_iter (
source in "\\PC*"
) {
let mut iter = crate::TokenIter::new(&source);
let _ = iter.next();
}
}
}

View file

@ -35,45 +35,6 @@ pub(crate) use std::fmt::{Debug, Display, Formatter, Result as FormatResult};
} }
} }
} }
#[cfg(test)] #[test] fn test_token () -> Result<(), Box<dyn std::error::Error>> {
let source = ":f00";
let mut token = Token { source, start: 0, length: 1, value: Sym(":") };
token = token.grow_sym();
assert_eq!(token, Token { source, start: 0, length: 2, value: Sym(":f") });
token = token.grow_sym();
assert_eq!(token, Token { source, start: 0, length: 3, value: Sym(":f0") });
token = token.grow_sym();
assert_eq!(token, Token { source, start: 0, length: 4, value: Sym(":f00") });
let src = "";
assert_eq!(None, SourceIter(src).next());
let src = " \n \r \t ";
assert_eq!(None, SourceIter(src).next());
let src = "7";
assert_eq!(Num(7), SourceIter(src).next().unwrap().0.value);
let src = " 100 ";
assert_eq!(Num(100), SourceIter(src).next().unwrap().0.value);
let src = " 9a ";
assert_eq!(Err(Unexpected('a')), SourceIter(src).next().unwrap().0.value);
let src = " :123foo ";
assert_eq!(Sym(":123foo"), SourceIter(src).next().unwrap().0.value);
let src = " \r\r\r\n\n\n@bar456\t\t\t\t\t\t";
assert_eq!(Sym("@bar456"), SourceIter(src).next().unwrap().0.value);
let src = "foo123";
assert_eq!(Key("foo123"), SourceIter(src).next().unwrap().0.value);
let src = "foo/bar";
assert_eq!(Key("foo/bar"), SourceIter(src).next().unwrap().0.value);
Ok(())
}
//#[cfg(test)] #[test] fn test_examples () -> Result<(), ParseError> { //#[cfg(test)] #[test] fn test_examples () -> Result<(), ParseError> {
//// Let's pretend to render some view. //// Let's pretend to render some view.
//let source = include_str!("../../tek/src/view_arranger.edn"); //let source = include_str!("../../tek/src/view_arranger.edn");

View file

@ -41,7 +41,7 @@ impl<'a> Token<'a> {
Self { source, start, length, value } Self { source, start, length, value }
} }
pub const fn end (&self) -> usize { pub const fn end (&self) -> usize {
self.start + self.length self.start.saturating_add(self.length)
} }
pub const fn slice (&'a self) -> &'a str { pub const fn slice (&'a self) -> &'a str {
self.slice_source(self.source) self.slice_source(self.source)
@ -51,7 +51,7 @@ impl<'a> Token<'a> {
str_range(source, self.start, self.end()) str_range(source, self.start, self.end())
} }
pub const fn slice_source_exp <'b> (&'a self, source: &'b str) -> &'b str { pub const fn slice_source_exp <'b> (&'a self, source: &'b str) -> &'b str {
str_range(source, self.start + 1, self.end()) str_range(source, self.start.saturating_add(1), self.end())
} }
pub const fn value (&self) -> Value { pub const fn value (&self) -> Value {
self.value self.value
@ -60,7 +60,7 @@ impl<'a> Token<'a> {
Self { value: Value::Err(error), ..self } Self { value: Value::Err(error), ..self }
} }
pub const fn grow (self) -> Self { pub const fn grow (self) -> Self {
Self { length: self.length + 1, ..self } Self { length: self.length.saturating_add(1), ..self }
} }
pub const fn grow_num (self, m: usize, c: char) -> Self { pub const fn grow_num (self, m: usize, c: char) -> Self {
match to_digit(c) { match to_digit(c) {
@ -90,7 +90,7 @@ impl<'a> Token<'a> {
pub const fn grow_in (self) -> Self { pub const fn grow_in (self) -> Self {
let mut token = self.grow_exp(); let mut token = self.grow_exp();
if let Value::Exp(depth, source) = token.value { if let Value::Exp(depth, source) = token.value {
token.value = Value::Exp(depth + 1, source) token.value = Value::Exp(depth.saturating_add(1), source)
} else { } else {
unreachable!() unreachable!()
} }
@ -110,3 +110,60 @@ impl<'a> Token<'a> {
token token
} }
} }
#[cfg(test)] mod test_token_prop {
use proptest::prelude::*;
proptest! {
#[test] fn test_token_prop (
source in "\\PC*",
start in usize::MIN..usize::MAX,
length in usize::MIN..usize::MAX,
) {
let token = crate::Token {
source: &source,
start,
length,
value: crate::Value::Nil
};
let _ = token.slice();
}
}
}
#[cfg(test)] #[test] fn test_token () -> Result<(), Box<dyn std::error::Error>> {
let source = ":f00";
let mut token = Token { source, start: 0, length: 1, value: Sym(":") };
token = token.grow_sym();
assert_eq!(token, Token { source, start: 0, length: 2, value: Sym(":f") });
token = token.grow_sym();
assert_eq!(token, Token { source, start: 0, length: 3, value: Sym(":f0") });
token = token.grow_sym();
assert_eq!(token, Token { source, start: 0, length: 4, value: Sym(":f00") });
let src = "";
assert_eq!(None, SourceIter(src).next());
let src = " \n \r \t ";
assert_eq!(None, SourceIter(src).next());
let src = "7";
assert_eq!(Num(7), SourceIter(src).next().unwrap().0.value);
let src = " 100 ";
assert_eq!(Num(100), SourceIter(src).next().unwrap().0.value);
let src = " 9a ";
assert_eq!(Err(Unexpected('a')), SourceIter(src).next().unwrap().0.value);
let src = " :123foo ";
assert_eq!(Sym(":123foo"), SourceIter(src).next().unwrap().0.value);
let src = " \r\r\r\n\n\n@bar456\t\t\t\t\t\t";
assert_eq!(Sym("@bar456"), SourceIter(src).next().unwrap().0.value);
let src = "foo123";
assert_eq!(Key("foo123"), SourceIter(src).next().unwrap().0.value);
let src = "foo/bar";
assert_eq!(Key("foo/bar"), SourceIter(src).next().unwrap().0.value);
Ok(())
}

View file

@ -8,4 +8,5 @@ tek_edn = { path = "../edn" }
[dev-dependencies] [dev-dependencies]
tek_tui = { path = "../tui" } tek_tui = { path = "../tui" }
proptest = "^1.6.0" proptest = "^1"
proptest-derive = "^0.5.1"

View file

@ -96,7 +96,7 @@ impl<N: Coordinate> Area<N> for [N;4] {
use super::*; use super::*;
use proptest::prelude::*; use proptest::prelude::*;
proptest! { proptest! {
#[test] fn test_area ( #[test] fn test_area_prop (
x in u16::MIN..u16::MAX, x in u16::MIN..u16::MAX,
y in u16::MIN..u16::MAX, y in u16::MIN..u16::MAX,
w in u16::MIN..u16::MAX, w in u16::MIN..u16::MAX,

View file

@ -1,6 +1,8 @@
use crate::*; use crate::*;
#[cfg(test)] use proptest_derive::Arbitrary;
/// A cardinal direction. /// A cardinal direction.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(test, derive(Arbitrary))]
pub enum Direction { North, South, East, West, Above, Below } pub enum Direction { North, South, East, West, Above, Below }
impl Direction { impl Direction {
pub fn split_fixed <N: Coordinate> (self, area: impl Area<N>, a: N) -> ([N;4],[N;4]) { pub fn split_fixed <N: Coordinate> (self, area: impl Area<N>, a: N) -> ([N;4],[N;4]) {
@ -14,20 +16,23 @@ impl Direction {
} }
} }
} }
#[cfg(test)] mod test_op_transform { #[cfg(test)] mod test {
use super::*; use super::*;
use proptest::prelude::*; use proptest::prelude::*;
proptest! { proptest! {
#[test] fn test_direction ( #[test] fn proptest_direction (
d in prop_oneof![
Just(North), Just(South),
Just(East), Just(West),
Just(Above), Just(Below)
],
x in u16::MIN..u16::MAX, x in u16::MIN..u16::MAX,
y in u16::MIN..u16::MAX, y in u16::MIN..u16::MAX,
w in u16::MIN..u16::MAX, w in u16::MIN..u16::MAX,
h in u16::MIN..u16::MAX, h in u16::MIN..u16::MAX,
a in u16::MIN..u16::MAX, a in u16::MIN..u16::MAX,
) { ) {
for d in [North, South, East, West, Above, Below].iter() { let _ = d.split_fixed([x, y, w, h], a);
let _ = d.split_fixed([x, y, w, h], a);
}
} }
} }
} }

View file

@ -116,3 +116,28 @@ impl<E: Output, A: Content<E>, B: Content<E>> BspAreas<E, A, B> for Bsp<A, B> {
#[macro_export] macro_rules! row { #[macro_export] macro_rules! row {
($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }}; ($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }};
} }
#[cfg(test)] mod test {
use super::*;
use proptest::prelude::*;
proptest! {
#[test] fn proptest_op_bsp (
d in prop_oneof![
Just(North), Just(South),
Just(East), Just(West),
Just(Above), Just(Below)
],
a in "\\PC*",
b in "\\PC*",
x in u16::MIN..u16::MAX,
y in u16::MIN..u16::MAX,
w in u16::MIN..u16::MAX,
h in u16::MIN..u16::MAX,
) {
let bsp = Bsp(d, a, b);
assert_eq!(
Content::layout(&bsp, [x, y, w, h]),
Render::layout(&bsp, [x, y, w, h]),
);
}
}
}