Mutation pins for run.rs, context.rs, encode.rs (cut C) #90
7 changed files with 193 additions and 0 deletions
|
|
@ -759,6 +759,9 @@ impl Prompt {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mutants;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
14
crates/weksa/src/context/mutants.rs
Normal file
14
crates/weksa/src/context/mutants.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//! Pins for private reach of `context.rs` that no run can produce.
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A corpus with no live Train item of the panel's languages holds an empty
|
||||
/// train. (A run refuses a panel without Train items at open, so only the
|
||||
/// builder shows it.)
|
||||
#[test]
|
||||
fn no_train_item_of_the_panel_is_an_empty_train() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let corpus = CorpusStore::open(&dir.path().join("c.cc")).unwrap();
|
||||
let train = TrainItems::from_corpus(&corpus, &["en-US".to_string()]).unwrap();
|
||||
assert_eq!((train.len(), train.is_empty()), (0, true));
|
||||
}
|
||||
|
|
@ -566,6 +566,9 @@ pub fn packet_id(item: &CorpusItemId, rev: u32, encoder: &EncoderId) -> PacketId
|
|||
PacketId::parse(&format!("packet:{}", hex16(digest))).expect("a packet id is well formed")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mutants;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
13
crates/weksa/src/encode/mutants.rs
Normal file
13
crates/weksa/src/encode/mutants.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//! Pins for `unmark` that the import door only reaches through a whole work.
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A form that opens another markup is malformed, though its entity id is
|
||||
/// sound: the model wrote a nested or unclosed mark, not a name.
|
||||
#[test]
|
||||
fn a_form_that_opens_a_mark_is_malformed() {
|
||||
assert_eq!(unmark("[[entity:orsa|Or[[sa]] came"), None);
|
||||
// The same line without the nested opener unmarks.
|
||||
let (plain, names) = unmark("[[entity:orsa|Orsa]] came").unwrap();
|
||||
assert_eq!((plain.as_str(), names.len()), ("Orsa came", 1));
|
||||
}
|
||||
|
|
@ -3112,6 +3112,9 @@ fn discard_inventory(dir: &Path) {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mutants;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
21
crates/weksa/src/run/mutants.rs
Normal file
21
crates/weksa/src/run/mutants.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//! Pins for private functions of `run.rs` that the doors reach only through a
|
||||
//! whole run.
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A line is probed only when it has at least four words, and then it is
|
||||
/// split in two near its byte middle, the pieces trimmed.
|
||||
#[test]
|
||||
fn a_recall_line_splits_from_four_words_and_not_before() {
|
||||
assert_eq!(recall_split("one two three"), None);
|
||||
assert_eq!(recall_split(""), None);
|
||||
assert_eq!(
|
||||
recall_split("one two three four"),
|
||||
Some(("one two".to_string(), "three four".to_string()))
|
||||
);
|
||||
// No spaces: each ideograph is a word, the split falls at the middle.
|
||||
assert_eq!(
|
||||
recall_split("天地不仁以万物为刍狗"),
|
||||
Some(("天地不仁以".to_string(), "万物为刍狗".to_string()))
|
||||
);
|
||||
}
|
||||
136
crates/weksa/tests/mutants_c.rs
Normal file
136
crates/weksa/tests/mutants_c.rs
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
//! Pins for behaviour of `context.rs`, `encode.rs` and `run.rs` found unpinned
|
||||
//! by mutation testing. Each test states what the code does in domain terms.
|
||||
|
||||
mod common;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use weksa::context::Prompt;
|
||||
use weksa::entity::{Entity, EntityOrigin, LanguageForms};
|
||||
use weksa::packet::{EntityId, proof_packet};
|
||||
use weksa::{CorpusItemId, CorpusStore, EntityForms, EntityStore, InventoryStore, PacketStore};
|
||||
|
||||
fn data(sub: &str) -> PathBuf {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../data")
|
||||
.join(sub)
|
||||
}
|
||||
|
||||
struct Held(Vec<Entity>);
|
||||
|
||||
impl EntityForms for Held {
|
||||
fn entity(&self, id: &EntityId) -> anyhow::Result<Option<Entity>> {
|
||||
Ok(self.0.iter().find(|e| &e.id == id).cloned())
|
||||
}
|
||||
}
|
||||
|
||||
fn spoken(slug: &str, form: &str) -> Entity {
|
||||
Entity {
|
||||
id: EntityId::parse(&format!("entity:{slug}")).unwrap(),
|
||||
forms: vec![LanguageForms {
|
||||
language: "en-US".into(),
|
||||
forms: vec![form.into()],
|
||||
pronunciation: None,
|
||||
}],
|
||||
origin: EntityOrigin::Cast {
|
||||
persona: slug.into(),
|
||||
fixture: "f.yaml".into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// A speaker whom no referent cites contributes no entry to the lowering
|
||||
/// prompt, even when another entity is cited: only the cited entity is listed.
|
||||
#[test]
|
||||
fn an_uncited_speaker_is_absent_from_the_lowering_prompt() {
|
||||
let cast_inv = InventoryStore::open(&data("interlingua/inventory.cc")).unwrap();
|
||||
let entities = EntityStore::open(&data("entity/entity.cc")).unwrap();
|
||||
let corpus = CorpusStore::open(&data("corpus/corpus.cc")).unwrap();
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let view = PacketStore::open(&dir.path().join("p.cc"))
|
||||
.unwrap()
|
||||
.admit(proof_packet(), &cast_inv, &corpus, &entities)
|
||||
.unwrap();
|
||||
let state = weksa::SpeakerStateStore::open(&dir.path().join("s.cc")).unwrap();
|
||||
let view = view
|
||||
.with_scene(&cast_inv.inventory().unwrap(), &state, &corpus)
|
||||
.unwrap();
|
||||
let held = Held(vec![
|
||||
spoken("nibu", "Nibu"),
|
||||
spoken("other", "SPEAKERCANARY"),
|
||||
]);
|
||||
let item = CorpusItemId::parse("item:nibu:1").unwrap();
|
||||
let speaker = EntityId::parse("entity:other").unwrap();
|
||||
let prompt = Prompt::lower(
|
||||
&view,
|
||||
&item,
|
||||
&speaker,
|
||||
"en-US",
|
||||
&cast_inv.inventory().unwrap(),
|
||||
&held,
|
||||
&weksa::Ontology::empty(),
|
||||
)
|
||||
.unwrap();
|
||||
let text = prompt.text();
|
||||
assert!(
|
||||
text.contains("\n\nentities: {\"entity:nibu\":[\"Nibu\"]}\n\n"),
|
||||
"{text}"
|
||||
);
|
||||
assert!(!text.contains("SPEAKERCANARY"), "{text}");
|
||||
}
|
||||
|
||||
/// A run refuses a panel language that lacks a live Train or a live Validation
|
||||
/// item: one no item carries, one with only a Test item, one with only Train.
|
||||
#[test]
|
||||
fn a_panel_language_no_item_carries_is_refused_at_open() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let d = dir.path();
|
||||
// Builds the corpus, entity and ontology stores the run reads.
|
||||
drop(common::run_over(d));
|
||||
let entities = common::entities_in(d);
|
||||
let mut corpus = CorpusStore::open(&d.join("c.cc")).unwrap();
|
||||
for (key, language, bucket) in [("item:jp:1", "ja-JP", 90), ("item:pt:1", "pt-BR", 10)] {
|
||||
let mut item = common::item_in(
|
||||
key,
|
||||
&format!("line of {key}"),
|
||||
&common::group_in_bucket(bucket),
|
||||
common::cast(),
|
||||
);
|
||||
item.language = language.into();
|
||||
corpus.add(item, &entities).unwrap();
|
||||
}
|
||||
drop(corpus);
|
||||
let create = |panel: &[&str]| {
|
||||
weksa::run::RunStores::create(
|
||||
&d.join(format!("run-{}", panel.join("-"))),
|
||||
"t",
|
||||
weksa::run::RunPaths {
|
||||
corpus: &d.join("c.cc"),
|
||||
inventory: &d.join("i.cc"),
|
||||
entities: &d.join("entities.cc"),
|
||||
ontology: &d.join("ontology.cc"),
|
||||
speaker_state: &common::speaker_state_beside(&d.join("ontology.cc")),
|
||||
},
|
||||
"fake-model",
|
||||
"bag",
|
||||
weksa::run::LowererReach::ToolLessRelay,
|
||||
weksa::corpus::ModelFamily::Unrecorded,
|
||||
&panel.iter().map(|t| (*t).to_string()).collect::<Vec<_>>(),
|
||||
)
|
||||
};
|
||||
assert!(create(&["en-US"]).is_ok());
|
||||
for panel in [&["ja-JP"][..], &["pt-BR"], &["xx-XX"], &["en-US", "ja-JP"]] {
|
||||
assert!(
|
||||
matches!(create(panel), Err(weksa::run::RunError::Panel)),
|
||||
"{panel:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A run keeps its recall probes in `recall` under its own root.
|
||||
#[test]
|
||||
fn a_run_keeps_its_recall_under_its_root() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let run = common::run_over(dir.path());
|
||||
assert_eq!(run.recall_dir(), dir.path().join("run").join("recall"));
|
||||
}
|
||||
Loading…
Reference in a new issue