The drawing of a small domain of its own, a change to be reviewed. lmjtfy holds the drawing of its network to the picture it served before the drawing moved here.
5use rete::{Domain, Known, Network, Rule, Test, Then};
7use super::*; 8 9#[derive(Clone, Copy, Debug, PartialEq, Eq)] 10struct Change; 11 12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 13enum F { 14 Code, 15 Tests, 16 Reviewed, 17} 18 19#[derive(Clone, Copy, Debug, PartialEq, Eq)] 20struct Review; 21 22impl Domain for Change { 23 type Fact = F; 24 type Value = bool; 25 type Effect = Review; 26 type End = (); 27 type Note = (); 28 29 fn facts() -> &'static [F] { 30 &[F::Code, F::Tests, F::Reviewed] 31 } 32 fn fact_name(fact: F) -> &'static str { 33 match fact { 34 F::Code => "touches code", 35 F::Tests => "touches tests", 36 F::Reviewed => "reviewed", 37 } 38 } 39 fn values(_: F) -> &'static [bool] { 40 &[true, false] 41 } 42 fn value_name(value: bool) -> &'static str { 43 if value { "yes" } else { "no" } 44 } 45 fn asked_for(fact: F) -> bool { 46 fact != F::Reviewed 47 } 48 fn teaches(_: Review) -> F { 49 F::Reviewed 50 } 51 fn effect_name(_: Review) -> String { 52 "review".to_owned() 53 } 54 fn end_name(_: ()) -> String { 55 String::new() 56 } 57 fn note_name(_: ()) -> String { 58 String::new() 59 } 60} 61 62const RULES: &[Rule<'static, Change>] = &[ 63 Rule { 64 name: "tests missing", 65 when: &[Test::Is(F::Code, true), Test::Is(F::Tests, false)], 66 then: Then::Note(()), 67 }, 68 Rule { name: "look at it", when: &[Test::Is(F::Code, true), Test::Is(F::Tests, true)], then: Then::Do(Review) }, 69 Rule { name: "approved", when: &[Test::Is(F::Reviewed, true)], then: Then::Note(()) }, 70]; 71 72fn legend() -> Legend<Change> { 73 Legend { 74 frames: vec![("source · 2 questions".to_owned(), vec![F::Code, F::Tests]), ("review · 1 call".to_owned(), vec![F::Reviewed])], 75 then: |then| match then { 76 Then::Do(_) => "ask a reviewer".to_owned(), 77 _ => "say so".to_owned(), 78 }, 79 } 80} 81 82fn none(_: F) -> Option<String> { 83 None 84} 85 86fn unlinked(_: Clicked<Change>) -> Option<String> { 87 None 88} 89 90#[test] 91fn nothing_known_draws_every_node_waiting() { 92 let network = Network::compile(RULES); 93 let svg = rete(&network, &Known::default(), &[], &legend(), none, unlinked).into_string(); 94 assert_eq!(svg.matches(r#"<g class="alpha waits">"#).count(), network.alphas.len()); 95 assert_eq!(svg.matches(r#"<g class="rule waits">"#).count(), 3); 96 assert_eq!(svg.matches(r#"<g class="fact waits">"#).count(), 3); 97 assert_eq!(svg.matches(r#"<g class="frame">"#).count(), 2); 98 assert!(svg.contains("source · 2 questions") && svg.contains("tests missing → say so")); 99 assert!(!svg.contains("holds") && !svg.contains("spare") && !svg.contains("fired")); 100} 101 102#[test] 103fn a_rule_that_holds_is_lit_and_what_it_did_not_need_is_outlined() { 104 let network = Network::compile(RULES); 105 let mut known = Known::default(); 106 known.learn(F::Code, true); 107 known.learn(F::Tests, false); 108 let svg = rete(&network, &known, &[], &legend(), none, unlinked).into_string(); 109 assert_eq!(svg.matches(r#"<g class="rule holds">"#).count(), 1); 110 assert_eq!(svg.matches(r#"<g class="fact holds">"#).count(), 2); 111 // "look at it" fails, and its test on `touches tests` is the failed alpha. 112 assert!(svg.contains(r#"<g class="alpha fails">"#)); 113} 114 115#[test] 116fn the_rules_that_decided_are_numbered() { 117 let network = Network::compile(RULES); 118 let mut known = Known::default(); 119 known.learn(F::Code, true); 120 known.learn(F::Tests, true); 121 let (by, _) = network.decide(&known); 122 let fired: Vec<usize> = by.into_iter().collect(); 123 let svg = rete(&network, &known, &fired, &legend(), none, unlinked).into_string(); 124 assert_eq!(sequence(&network, &fired), "1 look at it"); 125 assert_eq!(svg.matches(r#" fired">"#).count(), 1); 126 assert!(svg.contains(r#"<text class="nth""#)); 127} 128 129#[test] 130fn facts_and_tests_are_links_when_they_are_given_one() { 131 let network = Network::compile(RULES); 132 let link = |clicked: Clicked<Change>| { 133 Some(match clicked { 134 Clicked::Fact(fact) => format!("/fact?{}", Change::fact_name(fact)), 135 Clicked::Test(test) => format!("/test?{}", Change::fact_name(test.fact())), 136 }) 137 }; 138 let svg = rete(&network, &Known::default(), &[], &legend(), none, link).into_string(); 139 assert_eq!(svg.matches(r#"<a href="/test?"#).count(), network.alphas.len()); 140 assert_eq!(svg.matches(r#"<a href="/fact?"#).count(), 3); 141} 142 143#[test] 144fn a_fact_shows_what_the_host_says_when_there_is_something() { 145 let mut known = Known::default(); 146 known.learn(F::Code, true); 147 known.learn(F::Tests, true); 148 let shown = |fact: F| (fact == F::Code).then(|| "0.97".to_owned()); 149 let svg = rete(&Network::compile(RULES), &known, &[], &legend(), shown, unlinked).into_string(); 150 assert!(svg.contains(">0.97</text>")); 151 assert!(svg.contains(">yes</text>"), "a fact without one shows its value's name"); 152} 153 154#[test] 155fn every_rule_with_an_effect_has_a_line_back_to_the_fact_it_teaches() { 156 let svg = rete(&Network::compile(RULES), &Known::default(), &[], &legend(), none, unlinked).into_string(); 157 assert_eq!(svg.matches(r#"<g class="back "#).count(), 1); 158} 159 160#[test] 161fn the_picture_stands_alone_as_an_svg_file_with_its_styles() { 162 let svg = standalone(&Network::compile(RULES), &legend(), "g.holds rect { fill: pink; }"); 163 assert!(svg.starts_with(r#"<svg xmlns="http://www.w3.org/2000/svg" "#), "{}", &svg[..60]); 164 assert!(svg.ends_with("</svg>")); 165 assert!(svg.contains("<style>g.holds rect { fill: pink; }</style>")); 166 assert!(!svg.contains("<a "), "nothing in it links"); 167} 168 169#[test] 170fn no_two_nodes_in_a_column_share_a_place() { 171 let mut taken = BTreeSet::new(); 172 assert_eq!(place(&mut taken, 20), 20); 173 assert_eq!(place(&mut taken, 20), 34); 174 assert_eq!(place(&mut taken, 20), 48); 175}