#include #include "BuddyLogic.h" static int32_t fixed_rng(int32_t lo, int32_t hi) { (void)hi; return lo; } void setUp() {} void tearDown() {} // ── initBuddy ────────────────────────────────────────────────────────────── void test_initBuddy_sets_open_blink() { BuddyState b{}; initBuddy(b, 0); TEST_ASSERT_EQUAL(BLINK_OPEN, b.blinkState); TEST_ASSERT_EQUAL(EYE_RY, b.blinkRy); } void test_initBuddy_sets_normal_mood() { BuddyState b{}; initBuddy(b, 0); TEST_ASSERT_EQUAL(MOOD_NORMAL, b.mood); } void test_initBuddy_timestamps_relative_to_now() { BuddyState b{}; initBuddy(b, 1000); TEST_ASSERT_EQUAL(1000u, b.lastEvent); TEST_ASSERT_EQUAL(1000u + 3000, b.nextBlink); TEST_ASSERT_EQUAL(1000u + 2000, b.nextLook); } // ── setBuddyMood ─────────────────────────────────────────────────────────── void test_setBuddyMood_no_duration_sets_revertAt_zero() { BuddyState b{}; initBuddy(b, 0); setBuddyMood(b, MOOD_HAPPY, 1000, 0); TEST_ASSERT_EQUAL(0u, b.revertAt); } void test_setBuddyMood_with_duration_sets_revertAt() { BuddyState b{}; initBuddy(b, 0); setBuddyMood(b, MOOD_HAPPY, 1000, 4000); TEST_ASSERT_EQUAL(5000u, b.revertAt); } void test_setBuddyMood_updates_lastEvent() { BuddyState b{}; initBuddy(b, 0); setBuddyMood(b, MOOD_HAPPY, 5000); TEST_ASSERT_EQUAL(5000u, b.lastEvent); } void test_setBuddyMood_happy_sets_pupil_target_up() { BuddyState b{}; initBuddy(b, 0); setBuddyMood(b, MOOD_HAPPY, 0); TEST_ASSERT_EQUAL( 0, b.pupilTargetDx); TEST_ASSERT_EQUAL(-2, b.pupilTargetDy); } void test_setBuddyMood_sad_sets_pupil_target_down() { BuddyState b{}; initBuddy(b, 0); setBuddyMood(b, MOOD_SAD, 0); TEST_ASSERT_EQUAL(0, b.pupilTargetDx); TEST_ASSERT_EQUAL(4, b.pupilTargetDy); } // ── updateBuddyAnim — mood revert ───────────────────────────────────────── void test_updateBuddyAnim_reverts_mood_when_revertAt_passed() { BuddyState b{}; initBuddy(b, 0); setBuddyMood(b, MOOD_HAPPY, 0, 4000); // revertAt = 4000 updateBuddyAnim(b, 5000, fixed_rng); // now > revertAt TEST_ASSERT_EQUAL(MOOD_NORMAL, b.mood); TEST_ASSERT_EQUAL(0u, b.revertAt); } void test_updateBuddyAnim_does_not_revert_before_revertAt() { BuddyState b{}; initBuddy(b, 0); setBuddyMood(b, MOOD_HAPPY, 0, 4000); updateBuddyAnim(b, 3999, fixed_rng); TEST_ASSERT_EQUAL(MOOD_HAPPY, b.mood); } void test_updateBuddyAnim_goes_sleepy_after_idle() { BuddyState b{}; initBuddy(b, 0); // lastEvent = 0, mood = NORMAL updateBuddyAnim(b, 300001, fixed_rng); TEST_ASSERT_EQUAL(MOOD_SLEEPY, b.mood); } void test_updateBuddyAnim_no_sleepy_before_idle() { BuddyState b{}; initBuddy(b, 0); updateBuddyAnim(b, 299999, fixed_rng); TEST_ASSERT_EQUAL(MOOD_NORMAL, b.mood); } // ── Blink state machine ──────────────────────────────────────────────────── void test_blink_open_to_closing_when_nextBlink_passed() { BuddyState b{}; initBuddy(b, 0); // nextBlink = 3000 updateBuddyAnim(b, 3001, fixed_rng); TEST_ASSERT_EQUAL(BLINK_CLOSING, b.blinkState); } void test_blink_closing_decrements_blinkRy() { BuddyState b{}; initBuddy(b, 0); b.blinkState = BLINK_CLOSING; b.blinkRy = 15; updateBuddyAnim(b, 0, fixed_rng); TEST_ASSERT_EQUAL(11, b.blinkRy); TEST_ASSERT_EQUAL(BLINK_CLOSING, b.blinkState); } void test_blink_closing_reaches_closed_when_small() { BuddyState b{}; initBuddy(b, 0); b.blinkState = BLINK_CLOSING; b.blinkRy = 3; updateBuddyAnim(b, 0, fixed_rng); TEST_ASSERT_EQUAL(BLINK_CLOSED, b.blinkState); TEST_ASSERT_EQUAL(1, b.blinkRy); } void test_blink_closed_ticks_to_opening() { BuddyState b{}; initBuddy(b, 0); b.blinkState = BLINK_CLOSED; b.closedTicks = 2; updateBuddyAnim(b, 0, fixed_rng); TEST_ASSERT_EQUAL(BLINK_OPENING, b.blinkState); } void test_blink_opening_increments_blinkRy() { BuddyState b{}; initBuddy(b, 0); b.blinkState = BLINK_OPENING; b.blinkRy = 7; // 7+4=11 < EYE_RY(15), stays OPENING updateBuddyAnim(b, 0, fixed_rng); TEST_ASSERT_EQUAL(11, b.blinkRy); TEST_ASSERT_EQUAL(BLINK_OPENING, b.blinkState); } void test_blink_opening_returns_to_open_at_eye_ry() { BuddyState b{}; initBuddy(b, 0); b.blinkState = BLINK_OPENING; b.blinkRy = 13; // 13+4=17 >= EYE_RY(15) → OPEN updateBuddyAnim(b, 0, fixed_rng); TEST_ASSERT_EQUAL(BLINK_OPEN, b.blinkState); TEST_ASSERT_EQUAL(EYE_RY, b.blinkRy); } void test_surprised_does_not_blink() { BuddyState b{}; initBuddy(b, 0); setBuddyMood(b, MOOD_SURPRISED, 0); b.nextBlink = 0; // past-due updateBuddyAnim(b, 1000, fixed_rng); TEST_ASSERT_EQUAL(BLINK_OPEN, b.blinkState); // no transition TEST_ASSERT_EQUAL(EYE_RY, b.blinkRy); // stays full } // ── Pupil saccade ────────────────────────────────────────────────────────── void test_pupil_moves_fast_when_far() { BuddyState b{}; initBuddy(b, 0); b.pupilDx = 0; b.pupilTargetDx = 8; // |delta| = 8 >= 4 → step 3 b.pupilDy = b.pupilTargetDy = 0; updateBuddyAnim(b, 0, fixed_rng); TEST_ASSERT_EQUAL(3, b.pupilDx); } void test_pupil_moves_slow_when_close() { BuddyState b{}; initBuddy(b, 0); b.pupilDx = 0; b.pupilTargetDx = 2; // |delta| = 2 < 4 → step 1 b.pupilDy = b.pupilTargetDy = 0; updateBuddyAnim(b, 0, fixed_rng); TEST_ASSERT_EQUAL(1, b.pupilDx); } // ── main ────────────────────────────────────────────────────────────────── int main() { UNITY_BEGIN(); RUN_TEST(test_initBuddy_sets_open_blink); RUN_TEST(test_initBuddy_sets_normal_mood); RUN_TEST(test_initBuddy_timestamps_relative_to_now); RUN_TEST(test_setBuddyMood_no_duration_sets_revertAt_zero); RUN_TEST(test_setBuddyMood_with_duration_sets_revertAt); RUN_TEST(test_setBuddyMood_updates_lastEvent); RUN_TEST(test_setBuddyMood_happy_sets_pupil_target_up); RUN_TEST(test_setBuddyMood_sad_sets_pupil_target_down); RUN_TEST(test_updateBuddyAnim_reverts_mood_when_revertAt_passed); RUN_TEST(test_updateBuddyAnim_does_not_revert_before_revertAt); RUN_TEST(test_updateBuddyAnim_goes_sleepy_after_idle); RUN_TEST(test_updateBuddyAnim_no_sleepy_before_idle); RUN_TEST(test_blink_open_to_closing_when_nextBlink_passed); RUN_TEST(test_blink_closing_decrements_blinkRy); RUN_TEST(test_blink_closing_reaches_closed_when_small); RUN_TEST(test_blink_closed_ticks_to_opening); RUN_TEST(test_blink_opening_increments_blinkRy); RUN_TEST(test_blink_opening_returns_to_open_at_eye_ry); RUN_TEST(test_surprised_does_not_blink); RUN_TEST(test_pupil_moves_fast_when_far); RUN_TEST(test_pupil_moves_slow_when_close); return UNITY_END(); }