1use std::f32::consts::TAU; 2 3use whiskers_pet::*; 4 5fn t(kind: TouchKind, x: f32, y: f32, at_ms: u64) -> Touch { 6 Touch { kind, x, y, at_ms } 7} 8 9#[test] 10fn eyes_follow_the_finger_and_return() { 11 let mut p = Pet::new(); 12 p.tick(0); 13 p.touch(t(TouchKind::Down, 0.8, 0.0, 10)); 14 let v = (1..=40).map(|i| p.tick(10 + i * 16)).last().unwrap(); 15 assert!(v.gaze.0 > 0.75, "{:?}", v.gaze); 16 p.touch(t(TouchKind::Up, 0.8, 0.0, 700)); 17 let v = (1..=80).map(|i| p.tick(700 + i * 16)).last().unwrap(); 18 assert!(v.gaze.0.abs() < 0.05); 19} 20 21#[test] 22fn a_finger_far_outside_still_only_turns_the_eyes_to_the_edge() { 23 let mut p = Pet::new(); 24 p.tick(0); 25 p.touch(t(TouchKind::Down, 5.0, 0.0, 1)); 26 let v = (1..=80).map(|i| p.tick(1 + i * 16)).last().unwrap(); 27 assert!(v.gaze.0 <= 1.0 && v.gaze.0 > 0.95); 28} 29 30#[test] 31fn a_quick_tap_on_the_body_tickles_and_counts_a_poke() { 32 let mut p = Pet::new(); 33 p.touch(t(TouchKind::Down, 0.1, 0.2, 1000)); 34 p.touch(t(TouchKind::Up, 0.1, 0.2, 1100)); 35 let v = p.tick(1200); 36 assert_eq!((v.mood, v.pokes), (Mood::Tickled, 1)); 37 assert_eq!(p.tick(1100 + 1600).mood, Mood::Idle); 38} 39 40#[test] 41fn a_tap_off_the_body_or_a_long_press_is_not_a_poke() { 42 let mut p = Pet::new(); 43 p.touch(t(TouchKind::Down, 2.0, 0.0, 0)); 44 p.touch(t(TouchKind::Up, 2.0, 0.0, 100)); 45 p.touch(t(TouchKind::Down, 0.0, 0.0, 500)); 46 p.touch(t(TouchKind::Up, 0.0, 0.0, 1500)); 47 assert_eq!(p.tick(1600).pokes, 0); 48} 49 50fn circle(p: &mut Pet, start: u64, turns: f32, per_turn_ms: u64) -> u64 { 51 let steps = (turns * 48.0) as u64; 52 p.touch(t(TouchKind::Down, 0.6, 0.0, start)); 53 for i in 1..=steps { 54 let a = TAU * i as f32 / 48.0; 55 p.touch(t(TouchKind::Move, 0.6 * a.cos(), 0.6 * a.sin(), start + i * per_turn_ms / 48)); 56 } 57 start + steps * per_turn_ms / 48 58} 59 60#[test] 61fn two_fast_circles_make_it_dizzy_then_it_recovers() { 62 let mut p = Pet::new(); 63 let end = circle(&mut p, 0, 2.5, 700); 64 let v = p.tick(end); 65 assert_eq!((v.mood, v.dizzy_spells), (Mood::Dizzy, 1)); 66 p.touch(t(TouchKind::Up, 0.6, 0.0, end + 10)); 67 assert_eq!(p.tick(end + 3100).mood, Mood::Idle); 68} 69 70#[test] 71fn slow_circling_is_not_dizzying_in_either_direction() { 72 let mut p = Pet::new(); 73 let end = circle(&mut p, 0, 2.5, 4000); 74 assert_ne!(p.tick(end).mood, Mood::Dizzy); 75 let mut q = Pet::new(); 76 q.touch(t(TouchKind::Down, 0.6, 0.0, 0)); 77 for i in 1..=120u64 { 78 let a = -TAU * i as f32 / 48.0; 79 q.touch(t(TouchKind::Move, 0.6 * a.cos(), 0.6 * a.sin(), i * 14)); 80 } 81 assert_eq!(q.tick(1700).mood, Mood::Dizzy, "fast the other way round is dizzy too"); 82} 83 84#[test] 85fn wobbling_across_the_middle_is_not_a_spin() { 86 let mut p = Pet::new(); 87 p.touch(t(TouchKind::Down, 0.6, 0.0, 0)); 88 for i in 1..=200u64 { 89 let x = if i % 2 == 0 { 0.6 } else { -0.6 }; 90 p.touch(t(TouchKind::Move, x, 0.02, i * 10)); 91 } 92 assert_ne!(p.tick(2000).mood, Mood::Dizzy); 93} 94 95#[test] 96fn the_conversation_sets_the_face_and_touch_outranks_it() { 97 let mut p = Pet::new(); 98 for (phase, mood) in [ 99 (Phase::Listening, Mood::Listening), 100 (Phase::Thinking, Mood::Thinking), 101 (Phase::Working, Mood::Working), 102 (Phase::Speaking, Mood::Speaking), 103 (Phase::Tangled, Mood::Tangled), 104 (Phase::Busy, Mood::Busy), 105 (Phase::Offline, Mood::Offline), 106 ] { 107 p.set_phase(phase, 0); 108 assert_eq!(p.tick(10).mood, mood); 109 } 110 p.touch(t(TouchKind::Down, 0.0, 0.0, 20)); 111 p.touch(t(TouchKind::Up, 0.0, 0.0, 60)); 112 assert_eq!(p.tick(100).mood, Mood::Tickled); 113} 114 115#[test] 116fn it_dozes_after_a_quiet_minute_and_a_half_and_wakes_on_touch() { 117 let mut p = Pet::new(); 118 assert_eq!(p.tick(89_000).mood, Mood::Idle); 119 assert_eq!(p.tick(90_000).mood, Mood::Sleeping); 120 p.touch(t(TouchKind::Down, 3.0, 0.0, 91_000)); 121 assert_eq!(p.tick(91_010).mood, Mood::Idle); 122} 123 124#[test] 125fn it_does_not_doze_mid_conversation() { 126 let mut p = Pet::new(); 127 p.set_phase(Phase::Thinking, 0); 128 assert_eq!(p.tick(500_000).mood, Mood::Thinking); 129} 130 131#[test] 132fn a_busy_or_offline_cat_never_dozes_and_is_never_the_idle_face() { 133 // "Idle" means ready to listen; anything else must look different. 134 for phase in [Phase::Busy, Phase::Offline, Phase::Tangled] { 135 let mut p = Pet::new(); 136 p.set_phase(phase, 0); 137 let v = p.tick(10_000_000); 138 assert_ne!(v.mood, Mood::Idle); 139 assert_ne!(v.mood, Mood::Sleeping); 140 } 141}