Skip to content

Commit d27d7c9

Browse files
committed
Merge commit '72effb2' into add-github-workflows
2 parents e595f41 + 72effb2 commit d27d7c9

3 files changed

Lines changed: 177 additions & 0 deletions

File tree

src/grid.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,48 @@ impl Grid {
2121
self.cells[y * self.width + x] = v;
2222
}
2323
}
24+
25+
#[cfg(test)]
26+
mod tests {
27+
use super::*;
28+
29+
#[test]
30+
fn new_initializes_to_zero() {
31+
let grid = Grid::new(4, 3);
32+
assert_eq!(grid.width, 4);
33+
assert_eq!(grid.height, 3);
34+
assert!(grid.cells.iter().all(|&v| v == 0.0));
35+
}
36+
37+
#[test]
38+
fn new_allocates_correct_cell_count() {
39+
let grid = Grid::new(5, 7);
40+
assert_eq!(grid.cells.len(), 35);
41+
}
42+
43+
#[test]
44+
fn set_and_get_roundtrip() {
45+
let mut grid = Grid::new(4, 4);
46+
grid.set(2, 3, 1.5);
47+
assert_eq!(grid.get(2, 3), 1.5);
48+
}
49+
50+
#[test]
51+
fn set_uses_row_major_indexing() {
52+
let mut grid = Grid::new(4, 4);
53+
grid.set(1, 2, 9.9);
54+
// y * width + x = 2 * 4 + 1 = 9
55+
assert_eq!(grid.cells[9], 9.9);
56+
}
57+
58+
#[test]
59+
fn set_does_not_clobber_neighbors() {
60+
let mut grid = Grid::new(3, 3);
61+
grid.set(0, 0, 1.0);
62+
grid.set(1, 0, 2.0);
63+
grid.set(0, 1, 3.0);
64+
assert_eq!(grid.get(0, 0), 1.0);
65+
assert_eq!(grid.get(1, 0), 2.0);
66+
assert_eq!(grid.get(0, 1), 3.0);
67+
}
68+
}

src/renderer.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,46 @@ pub fn render(grid: &Grid, time: f32, stdout: &mut impl Write) -> std::io::Resul
4242

4343
Ok(())
4444
}
45+
46+
#[cfg(test)]
47+
mod tests {
48+
use super::*;
49+
50+
#[test]
51+
fn lava_color_zero_is_black() {
52+
let Color::Rgb { r, g, b } = lava_color(0.0) else {
53+
panic!("expected Rgb variant");
54+
};
55+
assert_eq!((r, g, b), (0, 0, 0));
56+
}
57+
58+
#[test]
59+
fn lava_color_clamps_large_input() {
60+
// v=100 * 0.35 = 35, clamped to 1.0 → same as v where v*0.35 == 1.0
61+
let Color::Rgb { r: r1, g: g1, b: b1 } = lava_color(100.0) else {
62+
panic!("expected Rgb variant");
63+
};
64+
let Color::Rgb { r: r2, g: g2, b: b2 } = lava_color(3.0) else {
65+
panic!("expected Rgb variant");
66+
};
67+
assert_eq!((r1, g1, b1), (r2, g2, b2));
68+
}
69+
70+
#[test]
71+
fn lava_color_red_dominates_at_low_values() {
72+
// At low (but nonzero) t, red > green > blue due to polynomial degrees
73+
let Color::Rgb { r, g, b } = lava_color(1.0) else {
74+
panic!("expected Rgb variant");
75+
};
76+
assert!(r >= g);
77+
assert!(g >= b);
78+
}
79+
80+
#[test]
81+
fn render_writes_output() {
82+
let grid = Grid::new(4, 4);
83+
let mut buf: Vec<u8> = Vec::new();
84+
render(&grid, 0.0, &mut buf).unwrap();
85+
assert!(!buf.is_empty());
86+
}
87+
}

src/simulation.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,92 @@ pub fn update_grid(grid: &mut Grid, balls: &[Metaball]) {
5050
}
5151
}
5252
}
53+
54+
#[cfg(test)]
55+
mod tests {
56+
use super::*;
57+
58+
fn make_ball(x: f32, y: f32, vx: f32, vy: f32, radius: f32) -> Metaball {
59+
Metaball { x, y, vx, vy, radius }
60+
}
61+
62+
// clamp_and_bounce tests (tested via update_balls)
63+
64+
#[test]
65+
fn ball_moves_by_velocity() {
66+
let mut balls = vec![make_ball(10.0, 10.0, 2.0, -3.0, 1.0)];
67+
update_balls(&mut balls, 100.0, 100.0);
68+
assert_eq!(balls[0].x, 12.0);
69+
assert_eq!(balls[0].y, 7.0);
70+
}
71+
72+
#[test]
73+
fn ball_bounces_off_right_wall() {
74+
// Place ball near right wall with positive vx so it would exceed max
75+
let mut balls = vec![make_ball(98.0, 50.0, 5.0, 0.0, 2.0)];
76+
update_balls(&mut balls, 100.0, 100.0);
77+
// After update: x = 103 > 98 (100 - radius), so clamp to 98 and reverse vx
78+
assert_eq!(balls[0].x, 98.0);
79+
assert!(balls[0].vx < 0.0);
80+
}
81+
82+
#[test]
83+
fn ball_bounces_off_left_wall() {
84+
let mut balls = vec![make_ball(3.0, 50.0, -5.0, 0.0, 4.0)];
85+
update_balls(&mut balls, 100.0, 100.0);
86+
// After update: x = -2 < 4 (radius), clamp to 4 and flip vx positive
87+
assert_eq!(balls[0].x, 4.0);
88+
assert!(balls[0].vx > 0.0);
89+
}
90+
91+
#[test]
92+
fn ball_bounces_off_bottom_wall() {
93+
let mut balls = vec![make_ball(50.0, 97.0, 0.0, 5.0, 3.0)];
94+
update_balls(&mut balls, 100.0, 100.0);
95+
assert_eq!(balls[0].y, 97.0);
96+
assert!(balls[0].vy < 0.0);
97+
}
98+
99+
#[test]
100+
fn ball_bounces_off_top_wall() {
101+
let mut balls = vec![make_ball(50.0, 2.0, 0.0, -5.0, 3.0)];
102+
update_balls(&mut balls, 100.0, 100.0);
103+
assert_eq!(balls[0].y, 3.0);
104+
assert!(balls[0].vy > 0.0);
105+
}
106+
107+
#[test]
108+
fn field_value_is_higher_near_ball_center() {
109+
let balls = vec![make_ball(5.0, 5.0, 0.0, 0.0, 10.0)];
110+
// Cell directly on ball center should have high field value
111+
let near = field_value(5.0, 5.0, &balls);
112+
// Cell far from ball
113+
let far = field_value(50.0, 50.0, &balls);
114+
assert!(near > far);
115+
}
116+
117+
#[test]
118+
fn field_value_no_balls_returns_negative_threshold() {
119+
let value = field_value(0.0, 0.0, &[]);
120+
assert_eq!(value, -0.8);
121+
}
122+
123+
#[test]
124+
fn update_grid_populates_all_cells() {
125+
let mut grid = Grid::new(5, 5);
126+
let balls = vec![make_ball(2.0, 2.0, 0.0, 0.0, 5.0)];
127+
update_grid(&mut grid, &balls);
128+
// At least some cells should be non-zero
129+
assert!(grid.cells.iter().any(|&v| v != 0.0));
130+
}
131+
132+
#[test]
133+
fn update_grid_center_cell_higher_than_edge() {
134+
let mut grid = Grid::new(10, 10);
135+
let balls = vec![make_ball(5.0, 5.0, 0.0, 0.0, 10.0)];
136+
update_grid(&mut grid, &balls);
137+
let center = grid.get(5, 5);
138+
let edge = grid.get(0, 0);
139+
assert!(center > edge);
140+
}
141+
}

0 commit comments

Comments
 (0)