max / makenotwork
5 files changed,
+346 insertions,
-10 deletions
| @@ -30,6 +30,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 30 | 30 | checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" | |
| 31 | 31 | ||
| 32 | 32 | [[package]] | |
| 33 | + | name = "bit-set" | |
| 34 | + | version = "0.8.0" | |
| 35 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 36 | + | checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" | |
| 37 | + | dependencies = [ | |
| 38 | + | "bit-vec", | |
| 39 | + | ] | |
| 40 | + | ||
| 41 | + | [[package]] | |
| 42 | + | name = "bit-vec" | |
| 43 | + | version = "0.8.0" | |
| 44 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 45 | + | checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" | |
| 46 | + | ||
| 47 | + | [[package]] | |
| 48 | + | name = "bitflags" | |
| 49 | + | version = "2.13.1" | |
| 50 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 51 | + | checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" | |
| 52 | + | ||
| 53 | + | [[package]] | |
| 33 | 54 | name = "bumpalo" | |
| 34 | 55 | version = "3.20.2" | |
| 35 | 56 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -173,6 +194,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 173 | 194 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" | |
| 174 | 195 | ||
| 175 | 196 | [[package]] | |
| 197 | + | name = "errno" | |
| 198 | + | version = "0.3.14" | |
| 199 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 200 | + | checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" | |
| 201 | + | dependencies = [ | |
| 202 | + | "libc", | |
| 203 | + | "windows-sys", | |
| 204 | + | ] | |
| 205 | + | ||
| 206 | + | [[package]] | |
| 207 | + | name = "fastrand" | |
| 208 | + | version = "2.5.0" | |
| 209 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 210 | + | checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" | |
| 211 | + | ||
| 212 | + | [[package]] | |
| 213 | + | name = "fnv" | |
| 214 | + | version = "1.0.7" | |
| 215 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 216 | + | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" | |
| 217 | + | ||
| 218 | + | [[package]] | |
| 219 | + | name = "getrandom" | |
| 220 | + | version = "0.3.4" | |
| 221 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 222 | + | checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" | |
| 223 | + | dependencies = [ | |
| 224 | + | "cfg-if", | |
| 225 | + | "libc", | |
| 226 | + | "r-efi 5.3.0", | |
| 227 | + | "wasip2", | |
| 228 | + | ] | |
| 229 | + | ||
| 230 | + | [[package]] | |
| 231 | + | name = "getrandom" | |
| 232 | + | version = "0.4.3" | |
| 233 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 234 | + | checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" | |
| 235 | + | dependencies = [ | |
| 236 | + | "cfg-if", | |
| 237 | + | "libc", | |
| 238 | + | "r-efi 6.0.0", | |
| 239 | + | ] | |
| 240 | + | ||
| 241 | + | [[package]] | |
| 176 | 242 | name = "half" | |
| 177 | 243 | version = "2.7.1" | |
| 178 | 244 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -232,6 +298,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 232 | 298 | checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" | |
| 233 | 299 | ||
| 234 | 300 | [[package]] | |
| 301 | + | name = "linux-raw-sys" | |
| 302 | + | version = "0.12.1" | |
| 303 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 304 | + | checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" | |
| 305 | + | ||
| 306 | + | [[package]] | |
| 235 | 307 | name = "memchr" | |
| 236 | 308 | version = "2.8.0" | |
| 237 | 309 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -287,6 +359,15 @@ dependencies = [ | |||
| 287 | 359 | ] | |
| 288 | 360 | ||
| 289 | 361 | [[package]] | |
| 362 | + | name = "ppv-lite86" | |
| 363 | + | version = "0.2.21" | |
| 364 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 365 | + | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" | |
| 366 | + | dependencies = [ | |
| 367 | + | "zerocopy", | |
| 368 | + | ] | |
| 369 | + | ||
| 370 | + | [[package]] | |
| 290 | 371 | name = "proc-macro2" | |
| 291 | 372 | version = "1.0.106" | |
| 292 | 373 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -296,6 +377,31 @@ dependencies = [ | |||
| 296 | 377 | ] | |
| 297 | 378 | ||
| 298 | 379 | [[package]] | |
| 380 | + | name = "proptest" | |
| 381 | + | version = "1.11.0" | |
| 382 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 383 | + | checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" | |
| 384 | + | dependencies = [ | |
| 385 | + | "bit-set", | |
| 386 | + | "bit-vec", | |
| 387 | + | "bitflags", | |
| 388 | + | "num-traits", | |
| 389 | + | "rand", | |
| 390 | + | "rand_chacha", | |
| 391 | + | "rand_xorshift", | |
| 392 | + | "regex-syntax", | |
| 393 | + | "rusty-fork", | |
| 394 | + | "tempfile", | |
| 395 | + | "unarray", | |
| 396 | + | ] | |
| 397 | + | ||
| 398 | + | [[package]] | |
| 399 | + | name = "quick-error" | |
| 400 | + | version = "1.2.3" | |
| 401 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 402 | + | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" | |
| 403 | + | ||
| 404 | + | [[package]] | |
| 299 | 405 | name = "quote" | |
| 300 | 406 | version = "1.0.45" | |
| 301 | 407 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -305,6 +411,56 @@ dependencies = [ | |||
| 305 | 411 | ] | |
| 306 | 412 | ||
| 307 | 413 | [[package]] | |
| 414 | + | name = "r-efi" | |
| 415 | + | version = "5.3.0" | |
| 416 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 417 | + | checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" | |
| 418 | + | ||
| 419 | + | [[package]] | |
| 420 | + | name = "r-efi" | |
| 421 | + | version = "6.0.0" | |
| 422 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 423 | + | checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" | |
| 424 | + | ||
| 425 | + | [[package]] | |
| 426 | + | name = "rand" | |
| 427 | + | version = "0.9.5" | |
| 428 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 429 | + | checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" | |
| 430 | + | dependencies = [ | |
| 431 | + | "rand_chacha", | |
| 432 | + | "rand_core", | |
| 433 | + | ] | |
| 434 | + | ||
| 435 | + | [[package]] | |
| 436 | + | name = "rand_chacha" | |
| 437 | + | version = "0.9.0" | |
| 438 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 439 | + | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" | |
| 440 | + | dependencies = [ | |
| 441 | + | "ppv-lite86", | |
| 442 | + | "rand_core", | |
| 443 | + | ] | |
| 444 | + | ||
| 445 | + | [[package]] | |
| 446 | + | name = "rand_core" | |
| 447 | + | version = "0.9.5" | |
| 448 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 449 | + | checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" | |
| 450 | + | dependencies = [ | |
| 451 | + | "getrandom 0.3.4", | |
| 452 | + | ] | |
| 453 | + | ||
| 454 | + | [[package]] | |
| 455 | + | name = "rand_xorshift" | |
| 456 | + | version = "0.4.0" | |
| 457 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 458 | + | checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" | |
| 459 | + | dependencies = [ | |
| 460 | + | "rand_core", | |
| 461 | + | ] | |
| 462 | + | ||
| 463 | + | [[package]] | |
| 308 | 464 | name = "rayon" | |
| 309 | 465 | version = "1.11.0" | |
| 310 | 466 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -354,12 +510,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 354 | 510 | checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" | |
| 355 | 511 | ||
| 356 | 512 | [[package]] | |
| 513 | + | name = "rustix" | |
| 514 | + | version = "1.1.4" | |
| 515 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 516 | + | checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" | |
| 517 | + | dependencies = [ | |
| 518 | + | "bitflags", | |
| 519 | + | "errno", | |
| 520 | + | "libc", | |
| 521 | + | "linux-raw-sys", | |
| 522 | + | "windows-sys", | |
| 523 | + | ] | |
| 524 | + | ||
| 525 | + | [[package]] | |
| 357 | 526 | name = "rustversion" | |
| 358 | 527 | version = "1.0.22" | |
| 359 | 528 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 360 | 529 | checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" | |
| 361 | 530 | ||
| 362 | 531 | [[package]] | |
| 532 | + | name = "rusty-fork" | |
| 533 | + | version = "0.3.1" | |
| 534 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 535 | + | checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" | |
| 536 | + | dependencies = [ | |
| 537 | + | "fnv", | |
| 538 | + | "quick-error", | |
| 539 | + | "tempfile", | |
| 540 | + | "wait-timeout", | |
| 541 | + | ] | |
| 542 | + | ||
| 543 | + | [[package]] | |
| 363 | 544 | name = "same-file" | |
| 364 | 545 | version = "1.0.6" | |
| 365 | 546 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -427,6 +608,20 @@ name = "tagtree" | |||
| 427 | 608 | version = "0.4.0" | |
| 428 | 609 | dependencies = [ | |
| 429 | 610 | "criterion", | |
| 611 | + | "proptest", | |
| 612 | + | ] | |
| 613 | + | ||
| 614 | + | [[package]] | |
| 615 | + | name = "tempfile" | |
| 616 | + | version = "3.27.0" | |
| 617 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 618 | + | checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" | |
| 619 | + | dependencies = [ | |
| 620 | + | "fastrand", | |
| 621 | + | "getrandom 0.4.3", | |
| 622 | + | "once_cell", | |
| 623 | + | "rustix", | |
| 624 | + | "windows-sys", | |
| 430 | 625 | ] | |
| 431 | 626 | ||
| 432 | 627 | [[package]] | |
| @@ -440,12 +635,27 @@ dependencies = [ | |||
| 440 | 635 | ] | |
| 441 | 636 | ||
| 442 | 637 | [[package]] | |
| 638 | + | name = "unarray" | |
| 639 | + | version = "0.1.4" | |
| 640 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 641 | + | checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" | |
| 642 | + | ||
| 643 | + | [[package]] | |
| 443 | 644 | name = "unicode-ident" | |
| 444 | 645 | version = "1.0.24" | |
| 445 | 646 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 446 | 647 | checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" | |
| 447 | 648 | ||
| 448 | 649 | [[package]] | |
| 650 | + | name = "wait-timeout" | |
| 651 | + | version = "0.2.1" | |
| 652 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 653 | + | checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" | |
| 654 | + | dependencies = [ | |
| 655 | + | "libc", | |
| 656 | + | ] | |
| 657 | + | ||
| 658 | + | [[package]] | |
| 449 | 659 | name = "walkdir" | |
| 450 | 660 | version = "2.5.0" | |
| 451 | 661 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -456,6 +666,15 @@ dependencies = [ | |||
| 456 | 666 | ] | |
| 457 | 667 | ||
| 458 | 668 | [[package]] | |
| 669 | + | name = "wasip2" | |
| 670 | + | version = "1.0.4+wasi-0.2.12" | |
| 671 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 672 | + | checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" | |
| 673 | + | dependencies = [ | |
| 674 | + | "wit-bindgen", | |
| 675 | + | ] | |
| 676 | + | ||
| 677 | + | [[package]] | |
| 459 | 678 | name = "wasm-bindgen" | |
| 460 | 679 | version = "0.2.114" | |
| 461 | 680 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| @@ -535,6 +754,12 @@ dependencies = [ | |||
| 535 | 754 | ] | |
| 536 | 755 | ||
| 537 | 756 | [[package]] | |
| 757 | + | name = "wit-bindgen" | |
| 758 | + | version = "0.57.1" | |
| 759 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 760 | + | checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" | |
| 761 | + | ||
| 762 | + | [[package]] | |
| 538 | 763 | name = "zerocopy" | |
| 539 | 764 | version = "0.8.47" | |
| 540 | 765 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -11,6 +11,7 @@ categories = ["data-structures", "text-processing"] | |||
| 11 | 11 | ||
| 12 | 12 | [dev-dependencies] | |
| 13 | 13 | criterion = { version = "0.5", features = ["html_reports"] } | |
| 14 | + | proptest = "1" | |
| 14 | 15 | ||
| 15 | 16 | [[bench]] | |
| 16 | 17 | name = "tagtree_bench" |
| @@ -92,7 +92,7 @@ assert!(results.contains(&"genre.electronic.house")); | |||
| 92 | 92 | ||
| 93 | 93 | ## Dependencies | |
| 94 | 94 | ||
| 95 | - | Zero runtime dependencies. `std`-only. Criterion is a dev-dependency for benchmarks. | |
| 95 | + | Zero runtime dependencies. `std`-only. Criterion (benchmarks) and proptest (property tests) are dev-dependencies. | |
| 96 | 96 | ||
| 97 | 97 | ## License | |
| 98 | 98 |
| @@ -333,15 +333,9 @@ pub fn common_ancestor<'a>(a: &'a str, b: &str) -> Option<&'a str> { | |||
| 333 | 333 | } | |
| 334 | 334 | // If one is a prefix of the other and ends at a separator boundary, | |
| 335 | 335 | // the shorter one is the common ancestor. | |
| 336 | - | let min_len = a.len().min(b.len()); | |
| 337 | - | if a.len() != b.len() | |
| 338 | - | && a.as_bytes()[..min_len] == b.as_bytes()[..min_len] | |
| 339 | - | && (min_len == a.len() || min_len == b.len()) | |
| 340 | - | { | |
| 341 | - | let longer = if a.len() > b.len() { a } else { b }; | |
| 342 | - | if longer.as_bytes()[min_len] == SEPARATOR_BYTE { | |
| 343 | - | return Some(&a[..min_len]); | |
| 344 | - | } | |
| 336 | + | let (shorter, longer) = if a.len() <= b.len() { (a, b) } else { (b, a) }; | |
| 337 | + | if is_ancestor_of(shorter, longer) { | |
| 338 | + | return Some(&a[..shorter.len()]); | |
| 345 | 339 | } | |
| 346 | 340 | last_sep.map(|pos| &a[..pos]) | |
| 347 | 341 | } |
| @@ -0,0 +1,116 @@ | |||
| 1 | + | //! Property tests for `validate_with` over random strings and random configs. | |
| 2 | + | //! | |
| 3 | + | //! The unit tests in `src/lib.rs` cover the named cases. These cover the space | |
| 4 | + | //! between them: arbitrary input must never panic, and an `Ok` verdict must | |
| 5 | + | //! imply every invariant the error set claims to enforce. | |
| 6 | + | ||
| 7 | + | use proptest::prelude::*; | |
| 8 | + | use tagtree::{TagConfig, TagError, depth, validate, validate_with}; | |
| 9 | + | ||
| 10 | + | /// Configs across the range apps actually use, plus the degenerate ends. | |
| 11 | + | fn any_config() -> impl Strategy<Value = TagConfig> { | |
| 12 | + | (0usize..=8, 0usize..=64, 0usize..=3).prop_map(|(max_depth, max_length, semantic_depth)| { | |
| 13 | + | TagConfig { max_depth, max_length, semantic_depth } | |
| 14 | + | }) | |
| 15 | + | } | |
| 16 | + | ||
| 17 | + | /// Well-formed tags: 1-5 segments drawn from the allowed charset. | |
| 18 | + | fn valid_tag() -> impl Strategy<Value = String> { | |
| 19 | + | proptest::collection::vec("[a-z0-9-]{1,6}", 1..=5).prop_map(|segs| segs.join(".")) | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | /// Tag-shaped input, weighted so that `Ok` verdicts stay common enough to test. | |
| 23 | + | /// Pure `.*` almost never validates, which starves the `Ok`-implies properties. | |
| 24 | + | fn tagish() -> impl Strategy<Value = String> { | |
| 25 | + | prop_oneof![ | |
| 26 | + | 4 => valid_tag(), | |
| 27 | + | 2 => "[a-z0-9.\\-]{0,24}", | |
| 28 | + | 1 => ".*", | |
| 29 | + | ] | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | fn is_allowed_char(ch: char) -> bool { | |
| 33 | + | ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '-' || ch == '.' | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | proptest! { | |
| 37 | + | /// Arbitrary bytes-as-text must not panic, however malformed. | |
| 38 | + | #[test] | |
| 39 | + | fn never_panics_on_arbitrary_input(tag in ".*", config in any_config()) { | |
| 40 | + | let _ = validate_with(&tag, &config); | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | /// `Ok` is a promise: every invariant the error set names must hold. | |
| 44 | + | #[test] | |
| 45 | + | fn ok_implies_all_invariants(tag in tagish(), config in any_config()) { | |
| 46 | + | prop_assume!(validate_with(&tag, &config).is_ok()); | |
| 47 | + | ||
| 48 | + | prop_assert!(!tag.is_empty()); | |
| 49 | + | prop_assert!(tag.chars().count() <= config.max_length); | |
| 50 | + | prop_assert!(!tag.starts_with('.') && !tag.ends_with('.')); | |
| 51 | + | prop_assert!(!tag.contains("..")); | |
| 52 | + | prop_assert!(tag.chars().all(is_allowed_char)); | |
| 53 | + | ||
| 54 | + | let d = depth(&tag); | |
| 55 | + | prop_assert!(d <= config.max_depth); | |
| 56 | + | if config.semantic_depth > 0 { | |
| 57 | + | prop_assert!(d > config.semantic_depth); | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | // No empty segments, and the segments reconstruct the tag. | |
| 61 | + | let segs: Vec<&str> = tag.split('.').collect(); | |
| 62 | + | prop_assert_eq!(segs.len(), d); | |
| 63 | + | prop_assert!(segs.iter().all(|s| !s.is_empty())); | |
| 64 | + | prop_assert_eq!(segs.join("."), tag); | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | /// `InvalidChar` must name a character that is really present and really illegal. | |
| 68 | + | #[test] | |
| 69 | + | fn invalid_char_is_accurate(tag in tagish(), config in any_config()) { | |
| 70 | + | if let Err(TagError::InvalidChar(ch)) = validate_with(&tag, &config) { | |
| 71 | + | prop_assert!(tag.contains(ch)); | |
| 72 | + | prop_assert!(!is_allowed_char(ch)); | |
| 73 | + | } | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | /// A well-formed tag validates under any config sized to admit it. | |
| 77 | + | #[test] | |
| 78 | + | fn well_formed_tags_pass_a_fitting_config(tag in valid_tag()) { | |
| 79 | + | let d = depth(&tag); | |
| 80 | + | for semantic_depth in 0..d { | |
| 81 | + | let config = TagConfig { | |
| 82 | + | max_depth: d, | |
| 83 | + | max_length: tag.chars().count(), | |
| 84 | + | semantic_depth, | |
| 85 | + | }; | |
| 86 | + | prop_assert_eq!(validate_with(&tag, &config), Ok(())); | |
| 87 | + | } | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | /// Tightening one bound at a time rejects, so each check is load-bearing. | |
| 91 | + | #[test] | |
| 92 | + | fn tightening_a_bound_rejects(tag in valid_tag()) { | |
| 93 | + | let d = depth(&tag); | |
| 94 | + | let len = tag.chars().count(); | |
| 95 | + | ||
| 96 | + | prop_assert_eq!( | |
| 97 | + | validate_with(&tag, &TagConfig { max_depth: d, max_length: len - 1, semantic_depth: 0 }), | |
| 98 | + | Err(TagError::TooLong { max: len - 1 }) | |
| 99 | + | ); | |
| 100 | + | prop_assert_eq!( | |
| 101 | + | validate_with(&tag, &TagConfig { max_depth: d - 1, max_length: len, semantic_depth: 0 }), | |
| 102 | + | Err(TagError::TooDeep { levels: d, max: d - 1 }) | |
| 103 | + | ); | |
| 104 | + | prop_assert_eq!( | |
| 105 | + | validate_with(&tag, &TagConfig { max_depth: d, max_length: len, semantic_depth: d }), | |
| 106 | + | Err(TagError::InsufficientDepth { min_segments: d + 1, semantic_depth: d }) | |
| 107 | + | ); | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | /// `validate` is exactly `validate_with` against the documented defaults. | |
| 111 | + | #[test] | |
| 112 | + | fn validate_matches_its_documented_defaults(tag in ".*") { | |
| 113 | + | const DEFAULT: TagConfig = TagConfig { max_depth: 5, max_length: 100, semantic_depth: 0 }; | |
| 114 | + | prop_assert_eq!(validate(&tag), validate_with(&tag, &DEFAULT)); | |
| 115 | + | } | |
| 116 | + | } |