-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_simulation.py
More file actions
268 lines (198 loc) · 7.82 KB
/
test_simulation.py
File metadata and controls
268 lines (198 loc) · 7.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
from pyglm import glm
from simulation import Simulation
from body import Body
class TestSimulationInitialization:
"""Test Simulation class initialization"""
def test_simulation_creation(self):
"""Test creating a simulation with N bodies"""
N = 50
sim = Simulation(N)
assert sim._N == N
# Bodies not created until setup_simulation
assert len(sim._bodies) == 0
def test_simulation_properties_empty(self):
"""Test that properties are empty before setup"""
sim = Simulation(10)
assert len(sim.positions) == 0
assert len(sim.sizes) == 0
assert len(sim.colors) == 0
class TestSimulationSetup:
"""Test simulation setup"""
def test_setup_simulation_creates_bodies(self):
"""Test that setup creates the correct number of bodies"""
N = 25
sim = Simulation(N)
sim.setup_simulation()
assert len(sim._bodies) == N
def test_setup_simulation_bodies_in_bounds(self):
"""Test that all bodies are initialized within bounds
(OpenGL NDC coordinates range from -1 to 1)"""
N = 50
sim = Simulation(N)
sim.setup_simulation()
for body in sim._bodies:
assert -1.0 <= body.pos.x <= 1.0
assert -1.0 <= body.pos.y <= 1.0
def test_setup_simulation_bodies_have_velocity(self):
"""Test that bodies are initialized with velocity"""
N = 20
sim = Simulation(N)
sim.setup_simulation()
for body in sim._bodies:
assert -0.2 <= body.vel.x <= 0.2
assert -0.2 <= body.vel.y <= 0.2
def test_setup_simulation_bodies_have_mass(self):
"""Test that bodies have mass in expected range"""
N = 15
sim = Simulation(N)
sim.setup_simulation()
for body in sim._bodies:
assert body.mass
def test_setup_simulation_bodies_have_colors(self):
"""Test that bodies have valid colors (OpenGL doesnt use 0-255 scale, instead
it uses 0.0-1.0 scale for each channel)"""
N = 10
sim = Simulation(N)
sim.setup_simulation()
for body in sim._bodies:
assert 0 <= body.visibleColor.x <= 1
assert 0 <= body.visibleColor.y <= 1
assert 0 <= body.visibleColor.z <= 1
def test_setup_simulation_randomness(self):
"""Test that setup creates different configurations"""
sim1 = Simulation(5)
sim1.setup_simulation()
sim2 = Simulation(5)
sim2.setup_simulation()
# At least one body should have different position
positions_different = False
for i in range(5):
if sim1._bodies[i].pos != sim2._bodies[i].pos:
positions_different = True
break
assert positions_different
class TestSimulationUpdate:
"""Test simulation update mechanics"""
def test_update_changes_positions(self):
"""Test that update changes body positions"""
sim = Simulation(5)
sim.setup_simulation()
initial_positions = [glm.vec2(body.pos.x, body.pos.y) for body in sim._bodies]
sim.update(0.1)
positions_changed = False
for i, body in enumerate(sim._bodies):
if body.pos != initial_positions[i]:
positions_changed = True
break
assert positions_changed
def test_update_updates_positions_list(self):
"""Test that update refreshes the positions property"""
sim = Simulation(5)
sim.setup_simulation()
sim.update(0.1)
assert len(sim.positions) == 5
for i, pos in enumerate(sim.positions):
assert pos == sim._bodies[i].pos
def test_update_updates_colors_list(self):
"""Test that update refreshes the colors property"""
sim = Simulation(5)
sim.setup_simulation()
sim.update(0.1)
assert len(sim.colors) == 5
for i, color in enumerate(sim.colors):
assert color == sim._bodies[i].visibleColor
def test_update_multiple_steps(self):
"""Test multiple consecutive updates"""
sim = Simulation(3)
sim.setup_simulation()
for _ in range(10):
sim.update(0.01)
assert len(sim.positions) == 3
assert len(sim.colors) == 3
class TestCollisionHandling:
"""Test collision detection and resolution during simulation.
The problem with testing collisions is that they are random due to random initialization.
Moreover, best way to check this is by what we see while running a simulation."""
def check_colision():
"""Test that collisions are detected and resolved"""
bA = Body(glm.vec2(0, 0), glm.vec2(0, 0), 0.1, 1.0, glm.vec3(1, 0, 0))
bB = Body(glm.vec2(0.05, 0), glm.vec2(0, 0), 0.1, 1.0, glm.vec3(0, 1, 0))
assert Body.is_colliding(bA, bB)
Body.resolve_collision(bA, bB)
assert bA.vel.x < 0
assert bB.vel.x > 0
def test_multiple_body_collisions(self):
"""Test simulation with multiple potential collisions"""
sim = Simulation(10)
sim.setup_simulation()
# Run simulation for several steps
for _ in range(20):
sim.update(0.01)
# Should complete without errors
assert len(sim._bodies) == 10
assert len(sim.positions) == 10
class TestSimulationProperties:
"""Test simulation property access"""
def test_positions_property_returns_correct_type(self):
"""Test that positions property returns list of vec2"""
sim = Simulation(5)
sim.setup_simulation()
sim.update(0.01)
positions = sim.positions
assert isinstance(positions, list)
assert len(positions) == 5
for pos in positions:
assert isinstance(pos, glm.vec2)
def test_sizes_property_returns_correct_type(self):
"""Test that sizes property returns list of floats"""
sim = Simulation(5)
sim.setup_simulation()
sizes = sim.sizes
assert isinstance(sizes, list)
assert len(sizes) == 5
for size in sizes:
assert isinstance(size, float)
def test_colors_property_returns_correct_type(self):
"""Test that colors property returns list of vec3"""
sim = Simulation(5)
sim.setup_simulation()
sim.update(0.01)
colors = sim.colors
assert isinstance(colors, list)
assert len(colors) == 5
for color in colors:
assert isinstance(color, glm.vec3)
class TestSimulationEdgeCases:
"""Test edge cases and boundary conditions"""
def test_simulation_with_one_body(self):
"""Test simulation with single body"""
sim = Simulation(1)
sim.setup_simulation()
sim.update(0.1)
assert len(sim._bodies) == 1
assert len(sim.positions) == 1
def test_simulation_with_zero_bodies(self):
"""Test simulation with no bodies"""
sim = Simulation(0)
sim.setup_simulation()
sim.update(0.1)
assert len(sim._bodies) == 0
assert len(sim.positions) == 0
def test_large_deltatime(self):
"""Test simulation with large time step"""
sim = Simulation(5)
sim.setup_simulation()
sim.update(1.0) # Large time step
# Should complete without errors, though physics might be wonky
assert len(sim.positions) == 5
def test_simulation_stability_over_time(self):
"""Test that simulation remains stable over many updates"""
sim = Simulation(20)
sim.setup_simulation()
for _ in range(100):
sim.update(0.01)
# All bodies should still exist and have valid positions
assert len(sim._bodies) == 20
for body in sim._bodies:
assert -1.0 <= body.pos.x <= 1.0
assert -1.0 <= body.pos.y <= 1.0