name = "unicode-normalization"
language = "rust"
strategies = ["proptest", "quickcheck", "crabcheck", "hegel"]
crate = "unicode-normalization"
base_commit = "fec9327b7ba018ac4dfbe74282ad2943e19bb3f3"

[[tasks]]
mutations = ["composition_table_astral_99d6e46_1"]

[tasks.source]
repo = "https://github.com/unicode-rs/unicode-normalization"
commits = ["99d6e46cfbe1b9bc84249a84f9e58b7619c106e0"]
commit_subjects = ["Include astral-plane compositions in the generated table"]
summary = "The composition-table generator emitted `compose(c1, c2) -> c3` entries only when all three codepoints were astral, so astral+BMP canonical compositions silently fell through to `None`. The fix widens the generator to emit the mixed-plane entries as well."

[tasks.injection]
kind = "marauders"
files = ["src/tables.rs"]

[tasks.bug]
short_name = "composition_table_astral"
invariant = "`compose(c1, c2)` returns the expected canonical composition whenever the Unicode data defines one, including across the BMP/astral-plane boundary."
how_triggered = "The generator's astral-gating predicate suppressed mixed-plane canonical compositions; the mutation reinstates the over-strict guard so astral+BMP pairs that ought to compose return `None`."

[[tasks.tasks]]
property = "ComposeTable"
witnesses = [
  { test_fn = "witness_compose_table_case_astral_105d2" },
  { test_fn = "witness_compose_table_case_astral_105da" },
]

[[tasks]]
mutations = ["hangul_compose_lvt_e4fd0e1_1"]

[tasks.source]
repo = "https://github.com/unicode-rs/unicode-normalization"
commits = ["e4fd0e1b21f39e7d8ff35da53eb11d0624bbf6cc"]
commit_subjects = ["Do not compose LVT syllables with trailing jamo"]
summary = "`char::compose` had a match arm that accepted any `S_BASE..=S_LAST` LV or LVT syllable and unconditionally offset into another LVT cell. For LVT syllables (which already carry a trailing consonant), composing with another T-jamo silently produced a different LVT syllable instead of `None`. The fix restricts the arm to actual LV syllables."

[tasks.injection]
kind = "patch"
files = ["src/normalize.rs"]
patch = "patches/hangul_compose_lvt_e4fd0e1_1.patch"

[tasks.bug]
short_name = "hangul_compose_lvt"
invariant = "Composing an LVT syllable with any T-jamo yields `None` — LVT syllables already carry a trailing consonant and cannot absorb another."
how_triggered = "The mutation widens the Hangul-compose match arm back to `S_BASE..=S_LAST`, so LVT syllables are treated as composable and offset into a different LVT cell instead of rejected."

[[tasks.tasks]]
property = "HangulComposeLvt"
witnesses = [
  { test_fn = "witness_hangul_compose_lvt_case_ac01_11a8" },
  { test_fn = "witness_hangul_compose_lvt_case_ac02_11a9" },
]

[[tasks]]
mutations = ["hangul_compose_tbase_bd3770c_1"]

[tasks.source]
repo = "https://github.com/unicode-rs/unicode-normalization"
commits = ["bd3770c5852d7e01fa74635c52100f0e4f4c34f4"]
commit_subjects = ["Reject T_BASE sentinel in Hangul compose"]
summary = "`char::compose` did not guard against the T-base sentinel codepoint `U+11A7` — the slot between T-jamos, not an actual trailing consonant. Composing any LV syllable with `U+11A7` incorrectly produced an LVT syllable. The fix rejects the T-base sentinel and requires the T-jamo to be strictly `> T_BASE`."

[tasks.injection]
kind = "patch"
files = ["src/normalize.rs"]
patch = "patches/hangul_compose_tbase_bd3770c_1.patch"

[tasks.bug]
short_name = "hangul_compose_tbase"
invariant = "Composing an LV syllable with the T-base sentinel codepoint `U+11A7` yields `None` — the slot between T-jamos is not a real trailing consonant."
how_triggered = "The mutation restores the `>= T_BASE` bound on the trailing jamo, accepting `U+11A7` and offsetting into the LVT syllable space instead of rejecting the sentinel."

[[tasks.tasks]]
property = "HangulComposeTbase"
witnesses = [
  { test_fn = "witness_hangul_compose_tbase_case_ac00_11a7" },
  { test_fn = "witness_hangul_compose_tbase_case_ac1c_11a7" },
]

[[tasks]]
mutations = ["streamsafe_drop_buffer_47906fa_1"]

[tasks.source]
repo = "https://github.com/unicode-rs/unicode-normalization"
commits = ["47906fa3ab0baea6baacec756c77e9a94c1a7675"]
commit_subjects = ["Preserve all input characters in StreamSafe"]
summary = "`StreamSafe::next` dropped the internal pending-non-starter buffer when the run crossed the 30-non-starter boundary, so input characters were silently lost from the output even though the stream-safe transform is required to be a CGJ-insertion (never a deletion)."

[tasks.injection]
kind = "patch"
files = ["src/stream_safe.rs"]
patch = "patches/streamsafe_drop_buffer_47906fa_1.patch"

[tasks.bug]
short_name = "streamsafe_drop_buffer"
invariant = "The stream-safe transform may only insert CGJ (U+034F); the multiset of non-CGJ input characters equals the multiset of non-CGJ output characters."
how_triggered = "The mutation reinstates the pre-fix logic that clears the pending-non-starter buffer on a 30-non-starter overflow, so non-starters that should have been flushed to the output after the inserted CGJ are silently discarded."

[[tasks.tasks]]
property = "StreamsafePreservesInput"
witnesses = [
  { test_fn = "witness_streamsafe_preserves_input_case_31_nonstarters" },
  { test_fn = "witness_streamsafe_preserves_input_case_40_nonstarters" },
]
