add unit tests

This commit is contained in:
2026-08-01 00:59:18 -07:00
parent 06ade6a4b7
commit fca6bd0945
6 changed files with 191 additions and 27 deletions
+12 -2
View File
@@ -36,6 +36,15 @@ Character_Data :: struct {
height: int,
}
parse_char_def :: proc(file_data: []u8) -> (Char_Def, bool) {
def: Char_Def
if err := json.unmarshal(file_data, &def); err != nil {
fmt.eprintfln("json unmarshal failed: %v", err)
return {}, false
}
return def, true
}
load_character_data :: proc(app: ^App, character_json_path: string) -> (Character_Data, bool) {
file_data, read_err := os.read_entire_file(character_json_path, context.allocator)
if read_err != nil {
@@ -46,8 +55,9 @@ load_character_data :: proc(app: ^App, character_json_path: string) -> (Characte
defer delete(file_data)
out: Character_Data
if err := json.unmarshal(file_data, &out.def); err != nil {
fmt.eprintfln("json unmarshal failed: %v", err)
ok: bool
out.def, ok = parse_char_def(file_data)
if !ok {
return {}, false
}
+83
View File
@@ -0,0 +1,83 @@
package engine
import "core:testing"
@(test)
parse_char_def_happy_path :: proc(t: ^testing.T) {
src := transmute([]u8)string(
`{"version":1,"id":"toad","atlas":"toad.atlas.png","pixels_per_unit":160.0,"pivot":[0.5,1.0],"clips":{"idle":{"loop":true,"fps":8.0,"frames":[{"rect":[1,2,3,4],"source_size":[3,4],"trim_offset":[0,0]}]}}}`,
)
def, ok := parse_char_def(src)
defer destroy_char_def(&def)
testing.expect(t, ok)
testing.expect_value(t, def.id, "toad")
testing.expect_value(t, def.atlas, "toad.atlas.png")
testing.expect_value(t, def.version, 1)
testing.expect_value(t, def.pixels_per_unit, f32(160.0))
testing.expect_value(t, def.pivot[0], f32(0.5))
testing.expect_value(t, def.pivot[1], f32(1.0))
idle, found := def.clips["idle"]
testing.expect(t, found)
testing.expect(t, idle.loop)
testing.expect_value(t, idle.fps, f32(8.0))
testing.expect_value(t, len(idle.frames), 1)
testing.expect_value(t, idle.frames[0].rect, [4]int{1, 2, 3, 4})
}
@(test)
parse_char_def_bad_json :: proc(t: ^testing.T) {
_, ok := parse_char_def(transmute([]u8)string("{ not json"))
testing.expect(t, !ok)
}
@(test)
character_frame_rect_hit_and_miss :: proc(t: ^testing.T) {
data: Character_Data
data.def.clips = make(map[string]Clip_Def)
defer delete(data.def.clips)
frames := make([]Frame_Def, 2)
frames[0] = {rect = {10, 20, 30, 40}}
frames[1] = {rect = {50, 60, 70, 80}}
defer delete(frames)
data.def.clips["idle"] = Clip_Def {
loop = true,
fps = 10,
frames = frames,
}
rect, ok := character_frame_rect(&data, "idle", 1)
testing.expect(t, ok)
testing.expect_value(t, rect, [4]int{50, 60, 70, 80})
_, ok = character_frame_rect(&data, "missing", 0)
testing.expect(t, !ok)
_, ok = character_frame_rect(&data, "idle", -1)
testing.expect(t, !ok)
_, ok = character_frame_rect(&data, "idle", 2)
testing.expect(t, !ok)
}
destroy_char_def :: proc(def: ^Char_Def) {
if def == nil do return
keys := make([dynamic]string, context.temp_allocator)
for key, clip in def.clips {
delete(clip.frames)
append(&keys, key)
}
for key in keys {
delete_key(&def.clips, key)
delete(key)
}
delete(def.clips)
delete(def.id)
delete(def.atlas)
def^ = {}
}
+27 -24
View File
@@ -4,43 +4,46 @@ import "core:fmt"
import "core:os"
import sdl "vendor:sdl3"
choose_shader_runtime :: proc(device: ^sdl.GPUDevice) -> (Shader_Runtime, bool) {
supported := sdl.GetGPUShaderFormats(device)
choose_shader_runtime_from_formats :: proc(
supported: sdl.GPUShaderFormat,
) -> (
Shader_Runtime,
bool,
) {
if .MSL in supported {
return {
backend = .Metal_MSL,
format = {.MSL},
shader_dir = "shaders/metal",
entrypoint = "main",
},
true
backend = .Metal_MSL,
format = {.MSL},
shader_dir = "shaders/metal",
entrypoint = "main",
}, true
}
if .DXIL in supported {
return {
backend = .DSD12_DXIL,
format = {.DXIL},
shader_dir = "shaders/d3d12",
entrypoint = "main",
},
true
backend = .DSD12_DXIL,
format = {.DXIL},
shader_dir = "shaders/d3d12",
entrypoint = "main",
}, true
}
if .SPIRV in supported {
return {
backend = .Vulkan_SPIRV,
format = {.SPIRV},
shader_dir = "shaders/vulkan",
entrypoint = "main",
},
true
backend = .Vulkan_SPIRV,
format = {.SPIRV},
shader_dir = "shaders/vulkan",
entrypoint = "main",
}, true
}
fmt.eprintfln("No supported shader format from device {got %v}", supported)
fmt.eprintfln("No supported shader format from device (got %v)", supported)
return {}, false
}
choose_shader_runtime :: proc(device: ^sdl.GPUDevice) -> (Shader_Runtime, bool) {
supported := sdl.GetGPUShaderFormats(device)
return choose_shader_runtime_from_formats(supported)
}
load_shader_blob :: proc(path: string) -> ([]u8, bool) {
data, err := os.read_entire_file(path, context.allocator)
if err != nil {
+43
View File
@@ -0,0 +1,43 @@
package engine
import "core:testing"
import sdl "vendor:sdl3"
@(test)
shader_filenames_per_backend :: proc(t: ^testing.T) {
vert, frag := shader_filenames(.Vulkan_SPIRV)
testing.expect_value(t, vert, "sprite.vert.spv")
testing.expect_value(t, frag, "sprite.frag.spv")
vert, frag = shader_filenames(.DSD12_DXIL)
testing.expect_value(t, vert, "sprite.vert.dxil")
testing.expect_value(t, frag, "sprite.frag.dxil")
vert, frag = shader_filenames(.Metal_MSL)
testing.expect_value(t, vert, "sprite.vert.msl")
testing.expect_value(t, frag, "sprite.frag.msl")
}
@(test)
choose_shader_runtime_prefers_msl :: proc(t: ^testing.T) {
rt, ok := choose_shader_runtime_from_formats({.MSL, .SPIRV, .DXIL})
testing.expect(t, ok)
testing.expect_value(t, rt.backend, Shader_Backend.Metal_MSL)
testing.expect_value(t, rt.shader_dir, "shaders/metal")
testing.expect_value(t, rt.format, sdl.GPUShaderFormat{.MSL})
}
@(test)
choose_shader_runtime_spirv_only :: proc(t: ^testing.T) {
rt, ok := choose_shader_runtime_from_formats({.SPIRV})
testing.expect(t, ok)
testing.expect_value(t, rt.backend, Shader_Backend.Vulkan_SPIRV)
testing.expect_value(t, rt.shader_dir, "shaders/vulkan")
testing.expect_value(t, rt.format, sdl.GPUShaderFormat{.SPIRV})
}
@(test)
choose_shader_runtime_empty_fails :: proc(t: ^testing.T) {
_, ok := choose_shader_runtime_from_formats({})
testing.expect(t, !ok)
}
+21
View File
@@ -0,0 +1,21 @@
package engine
import "core:testing"
@(test)
to_clip_corners :: proc(t: ^testing.T) {
// top-left pixel (0,0) -> clip (-1, 1) with y-flip
p := to_clip(0, 0, 800, 600)
testing.expect_value(t, p[0], f32(-1))
testing.expect_value(t, p[1], f32(1))
// bottom-right pixel (sw, sh) -> clip (1, -1)
p = to_clip(800, 600, 800, 600)
testing.expect_value(t, p[0], f32(1))
testing.expect_value(t, p[1], f32(-1))
// center
p = to_clip(400, 300, 800, 600)
testing.expect_value(t, p[0], f32(0))
testing.expect_value(t, p[1], f32(0))
}