aboutsummaryrefslogtreecommitdiff
path: root/simloop/test
diff options
context:
space:
mode:
Diffstat (limited to 'simloop/test')
-rw-r--r--simloop/test/simloop_test.c102
1 files changed, 54 insertions, 48 deletions
diff --git a/simloop/test/simloop_test.c b/simloop/test/simloop_test.c
index e8efd94..603a38c 100644
--- a/simloop/test/simloop_test.c
+++ b/simloop/test/simloop_test.c
@@ -1,18 +1,25 @@
1#include <simloop.h> 1#include <simloop.h>
2 2
3#include <test.h> 3#include <test.h>
4#include <timer.h>
5 4
6#include <stdint.h> 5#include <stdint.h>
7 6
8// ----------------------------------------------------------------------------- 7// -----------------------------------------------------------------------------
8// Time.
9
10static simloop_time_t time_delta_from_sec(double seconds) {
11 static constexpr double NANOS_PER_SEC = 1e9;
12 return (simloop_time_t)(seconds * NANOS_PER_SEC);
13}
14
15// -----------------------------------------------------------------------------
9// Randomness. 16// Randomness.
10 17
11typedef struct { 18typedef struct {
12 uint64_t a; 19 uint64_t a;
13} XorShift64State; 20} XorShift64State;
14 21
15uint64_t xorshift64(XorShift64State* state) { 22static uint64_t xorshift64(XorShift64State* state) {
16 uint64_t x = state->a; 23 uint64_t x = state->a;
17 x ^= x << 7; 24 x ^= x << 7;
18 x ^= x >> 9; 25 x ^= x >> 9;
@@ -26,12 +33,10 @@ uint64_t xorshift64(XorShift64State* state) {
26/// 1. An initial render is always triggered. 33/// 1. An initial render is always triggered.
27/// 2. No update is triggered (not enough time passed). 34/// 2. No update is triggered (not enough time passed).
28TEST_CASE(simloop_initial_render) { 35TEST_CASE(simloop_initial_render) {
29 Timer timer = {}; 36 Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = 10});
30 Simloop simloop =
31 simloop_make(&(SimloopArgs){.update_fps = 10, .timer = &timer});
32 SimloopOut simout; 37 SimloopOut simout;
33 38
34 simloop_update(&simloop, &simout); 39 simloop_update(&simloop, 0, &simout);
35 40
36 TEST_TRUE(simout.should_render); 41 TEST_TRUE(simout.should_render);
37 TEST_TRUE(!simout.should_update); 42 TEST_TRUE(!simout.should_update);
@@ -41,12 +46,11 @@ TEST_CASE(simloop_initial_render) {
41/// The initial render is not re-triggered if there is a render frame rate cap 46/// The initial render is not re-triggered if there is a render frame rate cap
42/// and time does not advance. 47/// and time does not advance.
43TEST_CASE(simloop_initial_render_not_retriggered) { 48TEST_CASE(simloop_initial_render_not_retriggered) {
44 Timer timer = {}; 49 Simloop simloop =
45 Simloop simloop = simloop_make( 50 simloop_make(&(SimloopArgs){.update_fps = 10, .max_render_fps = 10});
46 &(SimloopArgs){.update_fps = 10, .max_render_fps = 10, .timer = &timer});
47 SimloopOut simout; 51 SimloopOut simout;
48 52
49 simloop_update(&simloop, &simout); 53 simloop_update(&simloop, 0, &simout);
50 54
51 TEST_TRUE(simout.should_render); 55 TEST_TRUE(simout.should_render);
52 TEST_TRUE(!simout.should_update); 56 TEST_TRUE(!simout.should_update);
@@ -54,7 +58,7 @@ TEST_CASE(simloop_initial_render_not_retriggered) {
54 58
55 for (int i = 0; i < 10; i++) { 59 for (int i = 0; i < 10; i++) {
56 // Note that time does not advance. 60 // Note that time does not advance.
57 simloop_update(&simloop, &simout); 61 simloop_update(&simloop, 0, &simout);
58 TEST_TRUE(!simout.should_render); 62 TEST_TRUE(!simout.should_render);
59 TEST_TRUE(!simout.should_update); 63 TEST_TRUE(!simout.should_update);
60 TEST_EQUAL(simout.frame, 0); 64 TEST_EQUAL(simout.frame, 0);
@@ -65,27 +69,27 @@ TEST_CASE(simloop_initial_render_not_retriggered) {
65/// 1. Updates based on the desired update frame rate. 69/// 1. Updates based on the desired update frame rate.
66/// 2. Renders at every loop (provided there are updates). 70/// 2. Renders at every loop (provided there are updates).
67TEST_CASE(simloop_no_render_frame_cap) { 71TEST_CASE(simloop_no_render_frame_cap) {
68 constexpr int UPDATE_FPS = 10; // 100ms delta 72 constexpr int UPDATE_FPS = 10; // 100ms delta
69 const time_delta UPDATE_DDT = sec_to_time_delta(1.0 / (double)UPDATE_FPS); 73 const simloop_time_t UPDATE_DDT =
70 const time_delta STEP = sec_to_time_delta(1); 74 time_delta_from_sec(1.0 / (double)UPDATE_FPS);
71 const time_delta SIM_TIME_SEC = sec_to_time_delta(30); 75 const simloop_time_t STEP = time_delta_from_sec(1);
76 const simloop_time_t SIM_TIME_SEC = time_delta_from_sec(30);
72 77
73 // We need simulation time to be an exact multiple of the desired deltas for 78 // We need simulation time to be an exact multiple of the desired deltas for
74 // the modulo comparisons below. 79 // the modulo comparisons below.
75 TEST_TRUE((STEP % UPDATE_DDT) == 0); 80 TEST_TRUE((STEP % UPDATE_DDT) == 0);
76 81
77 Timer timer = {}; 82 simloop_time_t dt = 0;
78 Simloop simloop = 83 Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS});
79 simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS, .timer = &timer});
80 SimloopOut simout; 84 SimloopOut simout;
81 85
82 for (time_delta t = 0; t < SIM_TIME_SEC; t += STEP) { 86 for (simloop_time_t t = 0; t <= SIM_TIME_SEC; t += STEP) {
83 timer_advance(&timer, t); 87 simloop_update(&simloop, dt, &simout);
84 simloop_update(&simloop, &simout);
85 const bool expect_update = (t > 0) && ((t % UPDATE_DDT) == 0); 88 const bool expect_update = (t > 0) && ((t % UPDATE_DDT) == 0);
86 // A render is still expected at time 0. 89 // A render is still expected at time 0.
87 TEST_EQUAL(simout.should_render, (t == 0) || expect_update); 90 TEST_EQUAL(simout.should_render, (t == 0) || expect_update);
88 TEST_EQUAL(simout.should_update, expect_update); 91 TEST_EQUAL(simout.should_update, expect_update);
92 dt = STEP;
89 } 93 }
90} 94}
91 95
@@ -93,29 +97,31 @@ TEST_CASE(simloop_no_render_frame_cap) {
93/// 1. Updates based on the desired update frame rate. 97/// 1. Updates based on the desired update frame rate.
94/// 2. Renders based on the desired render frame rate. 98/// 2. Renders based on the desired render frame rate.
95TEST_CASE(simloop_with_render_frame_cap) { 99TEST_CASE(simloop_with_render_frame_cap) {
96 constexpr int UPDATE_FPS = 10; // 100ms delta 100 constexpr int UPDATE_FPS = 10; // 100ms delta
97 constexpr int RENDER_FPS = 5; // 200ms delta 101 constexpr int RENDER_FPS = 5; // 200ms delta
98 const time_delta UPDATE_DDT = sec_to_time_delta(1.0 / (double)UPDATE_FPS); 102 const simloop_time_t UPDATE_DDT =
99 const time_delta RENDER_DDT = sec_to_time_delta(1.0 / (double)RENDER_FPS); 103 time_delta_from_sec(1.0 / (double)UPDATE_FPS);
100 const time_delta STEP = sec_to_time_delta(0.1); // 100ms 104 const simloop_time_t RENDER_DDT =
101 const time_delta SIM_TIME_SEC = sec_to_time_delta(30); 105 time_delta_from_sec(1.0 / (double)RENDER_FPS);
106 const simloop_time_t STEP = time_delta_from_sec(0.1); // 100ms
107 const simloop_time_t SIM_TIME_SEC = time_delta_from_sec(30);
102 108
103 // We need simulation time to be an exact multiple of the desired deltas for 109 // We need simulation time to be an exact multiple of the desired deltas for
104 // the modulo comparisons below. 110 // the modulo comparisons below.
105 TEST_TRUE((UPDATE_DDT % STEP) == 0); 111 TEST_TRUE((UPDATE_DDT % STEP) == 0);
106 TEST_TRUE((RENDER_DDT % STEP) == 0); 112 TEST_TRUE((RENDER_DDT % STEP) == 0);
107 113
108 Timer timer = {}; 114 simloop_time_t dt = 0;
109 Simloop simloop = simloop_make(&(SimloopArgs){ 115 Simloop simloop = simloop_make(
110 .update_fps = UPDATE_FPS, .max_render_fps = RENDER_FPS, .timer = &timer}); 116 &(SimloopArgs){.update_fps = UPDATE_FPS, .max_render_fps = RENDER_FPS});
111 SimloopOut simout; 117 SimloopOut simout;
112 118
113 for (time_delta t = 0; t < SIM_TIME_SEC; t += STEP) { 119 for (simloop_time_t t = 0; t <= SIM_TIME_SEC; t += STEP) {
114 timer_advance(&timer, t); 120 simloop_update(&simloop, dt, &simout);
115 simloop_update(&simloop, &simout);
116 // A render is still expected at time 0. 121 // A render is still expected at time 0.
117 TEST_EQUAL(simout.should_render, (t % RENDER_DDT) == 0); 122 TEST_EQUAL(simout.should_render, (t % RENDER_DDT) == 0);
118 TEST_EQUAL(simout.should_update, (t > 0) && ((t % UPDATE_DDT) == 0)); 123 TEST_EQUAL(simout.should_update, (t > 0) && ((t % UPDATE_DDT) == 0));
124 dt = STEP;
119 } 125 }
120} 126}
121 127
@@ -130,16 +136,16 @@ TEST_CASE(simloop_with_render_frame_cap) {
130/// the client to decide whether to update just once or as many times as 136/// the client to decide whether to update just once or as many times as
131/// requested, depending on whether they want determinism or convergence. 137/// requested, depending on whether they want determinism or convergence.
132TEST_CASE(simloop_determinism) { 138TEST_CASE(simloop_determinism) {
133 constexpr int UPDATE_FPS = 100; // 10ms delta 139 constexpr int UPDATE_FPS = 100; // 10ms delta
134 const time_delta RANDOM_STEPS[] = { 140 const simloop_time_t RANDOM_STEPS[] = {
135 sec_to_time_delta(0.007), // 7ms 141 time_delta_from_sec(0.007), // 7ms
136 sec_to_time_delta(0.005), // 5ms 142 time_delta_from_sec(0.005), // 5ms
137 sec_to_time_delta(0.003), // 3ms 143 time_delta_from_sec(0.003), // 3ms
138 }; 144 };
139 constexpr uint64_t NUM_RANDOM_STEPS = 145 constexpr uint64_t NUM_RANDOM_STEPS =
140 sizeof(RANDOM_STEPS) / sizeof(RANDOM_STEPS[0]); 146 sizeof(RANDOM_STEPS) / sizeof(RANDOM_STEPS[0]);
141 const time_delta SIM_TIME_SEC = sec_to_time_delta(10); 147 const simloop_time_t SIM_TIME_SEC = time_delta_from_sec(10);
142 constexpr float ADD = 0.123f; 148 constexpr float ADD = 0.123f;
143 149
144 typedef struct Simulation { 150 typedef struct Simulation {
145 int iter_count; 151 int iter_count;
@@ -153,26 +159,26 @@ TEST_CASE(simloop_determinism) {
153 } 159 }
154 160
155 Simulation sim[2] = {0}; 161 Simulation sim[2] = {0};
156 XorShift64State xss = (XorShift64State){12069019817132197873}; 162 XorShift64State xss = (XorShift64State){12069019817132197873ULL};
157 163
158 // Perform two simulations with random clock-time steps. 164 // Perform two simulations with random clock-time steps.
159 for (int s = 0; s < 2; ++s) { 165 for (int s = 0; s < 2; ++s) {
160 Timer timer = {}; 166 simloop_time_t dt = 0;
161 Simloop simloop = 167 Simloop simloop = simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS});
162 simloop_make(&(SimloopArgs){.update_fps = UPDATE_FPS, .timer = &timer});
163 SimloopOut simout; 168 SimloopOut simout;
164 169
165 for (time_delta t = 0; t < SIM_TIME_SEC;) { 170 for (simloop_time_t t = 0; t <= SIM_TIME_SEC;) {
166 timer_advance(&timer, t); 171 simloop_update(&simloop, dt, &simout);
167 simloop_update(&simloop, &simout);
168 172
169 if (simout.should_update) { 173 if (simout.should_update) {
170 UPDATE_SIMULATION(sim[s]); 174 UPDATE_SIMULATION(sim[s]);
171 } 175 }
172 176
173 // Advance time with a random step. 177 // Advance time with a random step.
174 const time_delta step = RANDOM_STEPS[xorshift64(&xss) % NUM_RANDOM_STEPS]; 178 const simloop_time_t step =
179 RANDOM_STEPS[xorshift64(&xss) % NUM_RANDOM_STEPS];
175 t += step; 180 t += step;
181 dt = step;
176 } 182 }
177 } 183 }
178 184