Files
UmbraAudio/tests/test_speaker_confidence.py
2026-10-18 19:01:04 -07:00

125 lines
5.2 KiB
Python

# Copyright 2026 Umbra Tools (umbratools.com)
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
from umbraaudio.analysis.speaker_confidence import (
assess_speakers,
name_quality_issues,
)
def _spk(name: str, **kw) -> dict:
base = {
"primary_name": name,
"aliases": [],
"demographics": {"type": "Human", "gender": "Male"},
"vocal_traits": ["deep"],
"description": "A character.",
"alias_evidence": [],
}
base.update(kw)
return base
def _stats(name: str, n: int = 10, flagged: int = 0) -> dict:
return {name: {"utterances": n, "needs_review": flagged}}
class TestNameQuality:
def test_narration_verb_prefix(self) -> None:
issues = name_quality_issues("Walking tallest")
assert any("narration verb" in i for i in issues)
def test_scene_suffix(self) -> None:
issues = name_quality_issues("Unidentified Speaker (Scene 2)")
assert any("Scene" in i for i in issues)
def test_not_capitalised(self) -> None:
issues = name_quality_issues("scum leader")
assert any("capitalised" in i for i in issues)
def test_generic_only_name(self) -> None:
issues = name_quality_issues("The tallest")
assert any("role/comparative" in i for i in issues)
def test_clean_name_has_no_issues(self) -> None:
assert name_quality_issues("Kainda Walters Foster Murphy") == []
assert name_quality_issues("Tobias") == []
def test_narrator_is_clean(self) -> None:
assert name_quality_issues("Narrator") == []
class TestDesignation:
def test_healthy_speaker_is_clear(self) -> None:
res = assess_speakers([_spk("Tobias")], _stats("Tobias"))
assert res[0].status == "clear"
assert res[0].confidence >= 0.75
def test_unidentified_is_needs_review(self) -> None:
spk = _spk("Lieutenant", unidentified=True, unidentified_reason="never named")
res = assess_speakers([spk], _stats("Lieutenant"))
assert res[0].status == "needs_review"
assert any("unidentified" in r for r in res[0].reasons)
def test_polluted_name_forces_review_even_if_otherwise_healthy(self) -> None:
# Everything else is perfect, but the name has a narration verb glued on.
spk = _spk("Walking tallest")
res = assess_speakers([spk], _stats("Walking tallest", n=40))
assert res[0].status == "needs_review"
assert any("narration verb" in r for r in res[0].reasons)
def test_no_dialogue_forces_review(self) -> None:
res = assess_speakers([_spk("Ghost Speaker")], _stats("Ghost Speaker", n=0))
assert res[0].status == "needs_review"
assert any("no dialogue" in r for r in res[0].reasons)
def test_duplicate_without_proper_name_is_flagged(self) -> None:
spk = [_spk("The Girl"), _spk("The Girl helping")]
res = assess_speakers(spk, _stats("The Girl") | _stats("The Girl helping"))
by_name = {a.name: a for a in res}
assert by_name["The Girl"].status == "needs_review"
assert any("same person" in r for r in by_name["The Girl"].reasons)
def test_generic_plus_surname_is_NOT_a_duplicate(self) -> None:
# "Lieutenant" and "Lieutenant Stonesplitter" are different people.
spk = [_spk("Lieutenant"), _spk("Lieutenant Stonesplitter")]
res = assess_speakers(spk, _stats("Lieutenant") | _stats("Lieutenant Stonesplitter"))
by_name = {a.name: a for a in res}
assert not any(
"same person" in r for r in by_name["Lieutenant Stonesplitter"].reasons
)
assert not any("same person" in r for r in by_name["Lieutenant"].reasons)
def test_comparatives_in_same_group_are_not_duplicates(self) -> None:
# "Walking oldest" and "Walking Tallest" are two different people.
spk = [_spk("Walking oldest"), _spk("Walking Tallest")]
res = assess_speakers(spk, _stats("Walking oldest") | _stats("Walking Tallest"))
for a in res:
assert not any("same person" in r for r in a.reasons)
def test_typo_duplicate_is_flagged(self) -> None:
spk = [_spk("Rielan"), _spk("Rieland")]
res = assess_speakers(spk, _stats("Rielan") | _stats("Rieland"))
by_name = {a.name: a for a in res}
assert any("same person" in r for r in by_name["Rielan"].reasons)
def test_high_flagged_share_lowers_confidence(self) -> None:
clean = assess_speakers([_spk("A")], _stats("A", n=20, flagged=0))[0]
dirty = assess_speakers([_spk("A")], _stats("A", n=20, flagged=16))[0]
assert dirty.confidence < clean.confidence
assert dirty.status == "needs_review"
def test_every_speaker_gets_a_designation(self) -> None:
spk = [_spk("A"), _spk("B"), _spk("C")]
res = assess_speakers(spk, None)
assert len(res) == 3
assert all(a.status in ("clear", "needs_review") for a in res)
def test_needs_review_sorted_first(self) -> None:
spk = [_spk("Clean"), _spk("Walking oldest"), _spk("Walking tallest")]
res = assess_speakers(spk, _stats("Clean") | _stats("Walking oldest") | _stats("Walking tallest"))
assert res[0].status == "needs_review"
assert res[-1].name == "Clean"