| 922 |
922 |
|
}
|
| 923 |
923 |
|
prev[b.len()]
|
| 924 |
924 |
|
}
|
|
925 |
+ |
|
|
926 |
+ |
#[cfg(test)]
|
|
927 |
+ |
mod tests {
|
|
928 |
+ |
//! Unit tests for the oracle's own helpers.
|
|
929 |
+ |
//!
|
|
930 |
+ |
//! The oracle asserts things about the crate; nothing asserted anything about
|
|
931 |
+ |
//! the oracle. `split_query` and `split_rename` parse the fuzz script's
|
|
932 |
+ |
//! operands, and `model_valid` is the independent reimplementation that
|
|
933 |
+ |
//! `validate_with` is checked against, so a wrong answer here does not fail a
|
|
934 |
+ |
//! fuzz run: it makes one check less than it claims. Deleting this module
|
|
935 |
+ |
//! would return all eleven mutants these kill.
|
|
936 |
+ |
//!
|
|
937 |
+ |
//! Inputs avoid 0, 1, "" and "xyzzy" wherever an operator or a constant is
|
|
938 |
+ |
//! under test, because those are the values the mutants themselves return and
|
|
939 |
+ |
//! a test that picks one cannot disagree with the mutant. See
|
|
940 |
+ |
//! `_private/docs/meta/test_style.md`, "Tests that cannot disagree".
|
|
941 |
+ |
|
|
942 |
+ |
use super::{model_valid, split_query, split_rename};
|
|
943 |
+ |
use crate::TagConfig;
|
|
944 |
+ |
|
|
945 |
+ |
// ── split_query: `input limit`, splitting on the LAST space ──
|
|
946 |
+ |
|
|
947 |
+ |
#[test]
|
|
948 |
+ |
fn split_query_reads_the_limit_after_the_last_space() {
|
|
949 |
+ |
// 5 rather than 0 or 1: the four mutants return ("", 0), ("", 1),
|
|
950 |
+ |
// ("xyzzy", 0) and ("xyzzy", 1), so both halves have to differ from all
|
|
951 |
+ |
// of those for one assertion to kill all four.
|
|
952 |
+ |
assert_eq!(split_query("rock 5"), ("rock", 5));
|
|
953 |
+ |
}
|
|
954 |
+ |
|
|
955 |
+ |
#[test]
|
|
956 |
+ |
fn split_query_defaults_to_ten_with_no_space() {
|
|
957 |
+ |
assert_eq!(split_query("rock"), ("rock", 10));
|
|
958 |
+ |
}
|
|
959 |
+ |
|
|
960 |
+ |
#[test]
|
|
961 |
+ |
fn split_query_keeps_the_whole_string_when_the_limit_does_not_parse() {
|
|
962 |
+ |
// The Err arm returns `rest`, not `input`, so the trailing word stays
|
|
963 |
+ |
// part of the query rather than being eaten as a bad limit.
|
|
964 |
+ |
assert_eq!(split_query("rock jazz"), ("rock jazz", 10));
|
|
965 |
+ |
}
|
|
966 |
+ |
|
|
967 |
+ |
#[test]
|
|
968 |
+ |
fn split_query_clamps_the_limit_to_512() {
|
|
969 |
+ |
assert_eq!(split_query("rock 9999"), ("rock", 512));
|
|
970 |
+ |
}
|
|
971 |
+ |
|
|
972 |
+ |
#[test]
|
|
973 |
+ |
fn split_query_splits_on_the_last_space_not_the_first() {
|
|
974 |
+ |
// Distinguishes rsplit_once from split_once, which agree on every
|
|
975 |
+ |
// single-space input.
|
|
976 |
+ |
assert_eq!(split_query("deep rock 7"), ("deep rock", 7));
|
|
977 |
+ |
}
|
|
978 |
+ |
|
|
979 |
+ |
// ── split_rename: `old>new`, splitting on the FIRST `>` ──
|
|
980 |
+ |
|
|
981 |
+ |
#[test]
|
|
982 |
+ |
fn split_rename_splits_on_the_arrow() {
|
|
983 |
+ |
assert_eq!(split_rename("rock>metal"), ("rock", "metal"));
|
|
984 |
+ |
}
|
|
985 |
+ |
|
|
986 |
+ |
#[test]
|
|
987 |
+ |
fn split_rename_strips_one_leading_space() {
|
|
988 |
+ |
assert_eq!(split_rename(" rock>metal"), ("rock", "metal"));
|
|
989 |
+ |
}
|
|
990 |
+ |
|
|
991 |
+ |
#[test]
|
|
992 |
+ |
fn split_rename_with_no_arrow_returns_the_input_twice() {
|
|
993 |
+ |
// The unwrap_or arm. Both halves equal and non-empty, so it also fails
|
|
994 |
+ |
// under every one of the four constant-tuple mutants.
|
|
995 |
+ |
assert_eq!(split_rename("rock"), ("rock", "rock"));
|
|
996 |
+ |
}
|
|
997 |
+ |
|
|
998 |
+ |
#[test]
|
|
999 |
+ |
fn split_rename_splits_on_the_first_arrow_not_the_last() {
|
|
1000 |
+ |
// split_once vs rsplit_once agree on any single-arrow input.
|
|
1001 |
+ |
assert_eq!(split_rename("a>b>c"), ("a", "b>c"));
|
|
1002 |
+ |
}
|
|
1003 |
+ |
|
|
1004 |
+ |
// ── model_valid: the boundaries, asserted from both sides ──
|
|
1005 |
+ |
|
|
1006 |
+ |
fn cfg(max_depth: usize, max_length: usize) -> TagConfig {
|
|
1007 |
+ |
TagConfig {
|
|
1008 |
+ |
max_depth,
|
|
1009 |
+ |
max_length,
|
|
1010 |
+ |
semantic_depth: 0,
|
|
1011 |
+ |
}
|
|
1012 |
+ |
}
|
|
1013 |
+ |
|
|
1014 |
+ |
#[test]
|
|
1015 |
+ |
fn model_valid_accepts_a_tag_of_exactly_max_length() {
|
|
1016 |
+ |
// `count > max_length` vs `>=`: the two disagree only at equality, so a
|
|
1017 |
+ |
// tag one character shorter would pass under either.
|
|
1018 |
+ |
assert!(
|
|
1019 |
+ |
model_valid("abcde", &cfg(3, 5)),
|
|
1020 |
+ |
"five characters is at the limit, not over it"
|
|
1021 |
+ |
);
|
|
1022 |
+ |
}
|
|
1023 |
+ |
|
|
1024 |
+ |
#[test]
|
|
1025 |
+ |
fn model_valid_rejects_a_tag_one_character_over_max_length() {
|
|
1026 |
+ |
assert!(!model_valid("abcdef", &cfg(3, 5)));
|
|
1027 |
+ |
}
|
|
1028 |
+ |
|
|
1029 |
+ |
#[test]
|
|
1030 |
+ |
fn model_valid_accepts_a_tag_at_exactly_max_depth() {
|
|
1031 |
+ |
// `segs.len() > max_depth` vs `>=`, same boundary argument.
|
|
1032 |
+ |
assert!(
|
|
1033 |
+ |
model_valid("a.b.c", &cfg(3, 60)),
|
|
1034 |
+ |
"three segments is at the depth limit, not over it"
|
|
1035 |
+ |
);
|
|
1036 |
+ |
}
|
|
1037 |
+ |
|
|
1038 |
+ |
#[test]
|
|
1039 |
+ |
fn model_valid_rejects_a_tag_one_segment_over_max_depth() {
|
|
1040 |
+ |
assert!(!model_valid("a.b.c.d", &cfg(3, 60)));
|
|
1041 |
+ |
}
|
|
1042 |
+ |
|
|
1043 |
+ |
#[test]
|
|
1044 |
+ |
fn model_valid_rejects_leading_and_trailing_separators() {
|
|
1045 |
+ |
// Kept for the behaviour rather than for a mutant: the `||` at the
|
|
1046 |
+ |
// leading/trailing guard is an equivalent mutant, because a tag with a
|
|
1047 |
+ |
// separator at either end also has an empty segment and the empty-segment
|
|
1048 |
+ |
// check below returns false for it either way. Recorded in
|
|
1049 |
+ |
// .cargo/mutants.toml.
|
|
1050 |
+ |
assert!(!model_valid(".rock", &cfg(3, 60)));
|
|
1051 |
+ |
assert!(!model_valid("rock.", &cfg(3, 60)));
|
|
1052 |
+ |
}
|
|
1053 |
+ |
|
|
1054 |
+ |
#[test]
|
|
1055 |
+ |
fn model_valid_rejects_the_empty_tag_and_uppercase() {
|
|
1056 |
+ |
assert!(!model_valid("", &cfg(3, 60)));
|
|
1057 |
+ |
assert!(
|
|
1058 |
+ |
!model_valid("Rock", &cfg(3, 60)),
|
|
1059 |
+ |
"uppercase is outside the allowed set"
|
|
1060 |
+ |
);
|
|
1061 |
+ |
}
|
|
1062 |
+ |
|
|
1063 |
+ |
#[test]
|
|
1064 |
+ |
fn model_valid_enforces_the_semantic_depth_floor() {
|
|
1065 |
+ |
let c = TagConfig {
|
|
1066 |
+ |
max_depth: 5,
|
|
1067 |
+ |
max_length: 60,
|
|
1068 |
+ |
semantic_depth: 2,
|
|
1069 |
+ |
};
|
|
1070 |
+ |
assert!(
|
|
1071 |
+ |
!model_valid("genre.rock", &c),
|
|
1072 |
+ |
"two segments is one short of the floor"
|
|
1073 |
+ |
);
|
|
1074 |
+ |
assert!(model_valid("genre.rock.metal", &c));
|
|
1075 |
+ |
}
|
|
1076 |
+ |
}
|