diff --git a/src/game/input_mapper_tests.zig b/src/game/input_mapper_tests.zig new file mode 100644 index 00000000..356349f3 --- /dev/null +++ b/src/game/input_mapper_tests.zig @@ -0,0 +1,143 @@ +const std = @import("std"); +const testing = std.testing; +const input_mapper_pkg = @import("input_mapper.zig"); +const InputBinding = input_mapper_pkg.InputBinding; +const ActionBinding = input_mapper_pkg.ActionBinding; +const InputMapper = input_mapper_pkg.InputMapper; +const GameAction = input_mapper_pkg.GameAction; +const Key = @import("../engine/core/interfaces.zig").Key; +const MouseButton = @import("../engine/core/interfaces.zig").MouseButton; + +test "InputBinding.getName for key" { + const binding = InputBinding{ .key = .w }; + try testing.expectEqualStrings("W", binding.getName()); + + const binding2 = InputBinding{ .key = .space }; + try testing.expectEqualStrings("Space", binding2.getName()); + + const binding3 = InputBinding{ .key = .f1 }; + try testing.expectEqualStrings("F1", binding3.getName()); +} + +test "InputBinding.getName for key_alt" { + const binding = InputBinding{ .key_alt = .a }; + try testing.expectEqualStrings("A", binding.getName()); +} + +test "InputBinding.getName for mouse_button" { + const binding = InputBinding{ .mouse_button = .left }; + try testing.expectEqualStrings("Left Click", binding.getName()); + + const binding2 = InputBinding{ .mouse_button = .right }; + try testing.expectEqualStrings("Right Click", binding2.getName()); + + const binding3 = InputBinding{ .mouse_button = .middle }; + try testing.expectEqualStrings("Middle Click", binding3.getName()); +} + +test "InputBinding.getName for none" { + const binding = InputBinding{ .none = {} }; + try testing.expectEqualStrings("Unbound", binding.getName()); +} + +test "InputBinding.getName for numpad keys" { + const binding = InputBinding{ .key = .kp_plus }; + try testing.expectEqualStrings("Numpad +", binding.getName()); + + const binding2 = InputBinding{ .key = .kp_minus }; + try testing.expectEqualStrings("Numpad -", binding2.getName()); +} + +test "InputBinding.eql with .none values" { + const a = InputBinding{ .none = {} }; + const b = InputBinding{ .none = {} }; + try testing.expect(a.eql(b)); + try testing.expect(b.eql(a)); +} + +test "InputBinding.eql key vs key_alt same key" { + const a = InputBinding{ .key = .w }; + const b = InputBinding{ .key_alt = .w }; + try testing.expect(a.eql(b)); + try testing.expect(b.eql(a)); +} + +test "InputBinding.eql different key types" { + const a = InputBinding{ .key = .w }; + const b = InputBinding{ .mouse_button = .left }; + try testing.expect(!a.eql(b)); +} + +test "InputBinding.eql none vs key" { + const a = InputBinding{ .none = {} }; + const b = InputBinding{ .key = .w }; + try testing.expect(!a.eql(b)); +} + +test "ActionBinding.init sets primary and none alternate" { + const binding = ActionBinding.init(.{ .key = .w }); + try testing.expect(binding.primary.eql(InputBinding{ .key = .w })); + try testing.expect(binding.alternate.eql(InputBinding{ .none = {} })); +} + +test "ActionBinding.initWithAlt sets both bindings" { + const binding = ActionBinding.initWithAlt(.{ .key = .w }, .{ .key_alt = .space }); + try testing.expect(binding.primary.eql(InputBinding{ .key = .w })); + try testing.expect(binding.alternate.eql(InputBinding{ .key_alt = .space })); +} + +test "InputMapper.init sets all default bindings" { + const mapper = InputMapper.init(); + try testing.expect(mapper.getBinding(.move_forward).primary.eql(InputBinding{ .key = .w })); + try testing.expect(mapper.getBinding(.jump).primary.eql(InputBinding{ .key = .space })); + try testing.expect(mapper.getBinding(.interact_primary).primary.eql(InputBinding{ .mouse_button = .left })); +} + +test "InputMapper.resetToDefaults restores all bindings" { + var mapper = InputMapper.init(); + + mapper.setBinding(.move_forward, .{ .key = .up }); + mapper.setBinding(.jump, .{ .mouse_button = .right }); + try testing.expect(!mapper.getBinding(.move_forward).primary.eql(InputBinding{ .key = .w })); + + mapper.resetToDefaults(); + + try testing.expect(mapper.getBinding(.move_forward).primary.eql(InputBinding{ .key = .w })); + try testing.expect(mapper.getBinding(.jump).primary.eql(InputBinding{ .key = .space })); +} + +test "InputMapper.setBinding updates primary binding" { + var mapper = InputMapper.init(); + try testing.expect(mapper.getBinding(.move_forward).primary.eql(InputBinding{ .key = .w })); + + mapper.setBinding(.move_forward, .{ .key = .up }); + try testing.expect(mapper.getBinding(.move_forward).primary.eql(InputBinding{ .key = .up })); +} + +test "InputMapper.setAlternateBinding updates alternate binding" { + var mapper = InputMapper.init(); + try testing.expect(mapper.getBinding(.move_forward).alternate.eql(InputBinding{ .none = {} })); + + mapper.setAlternateBinding(.move_forward, .{ .key_alt = .kp_plus }); + try testing.expect(mapper.getBinding(.move_forward).alternate.eql(InputBinding{ .key_alt = .kp_plus })); +} + +test "InputMapper.resetActionToDefault restores single action" { + var mapper = InputMapper.init(); + + mapper.setBinding(.move_forward, .{ .key = .up }); + try testing.expect(!mapper.getBinding(.move_forward).primary.eql(InputBinding{ .key = .w })); + + mapper.resetActionToDefault(.move_forward); + try testing.expect(mapper.getBinding(.move_forward).primary.eql(InputBinding{ .key = .w })); + + try testing.expect(mapper.getBinding(.jump).primary.eql(InputBinding{ .key = .space })); +} + +test "InputMapper interface.getBinding returns correct binding" { + const mapper = InputMapper.init(); + const iface = mapper.interface(); + + const binding = iface.getBinding(.jump); + try testing.expect(binding.primary.eql(InputBinding{ .key = .space })); +} diff --git a/src/game/settings/persistence_tests.zig b/src/game/settings/persistence_tests.zig new file mode 100644 index 00000000..04bcc50b --- /dev/null +++ b/src/game/settings/persistence_tests.zig @@ -0,0 +1,146 @@ +const std = @import("std"); +const testing = std.testing; +const persistence = @import("persistence.zig"); +const data = @import("data.zig"); +const Settings = data.Settings; + +test "setTexturePack returns early when same value" { + const allocator = testing.allocator; + var settings = Settings{ .texture_pack = "default" }; + + try persistence.setTexturePack(&settings, allocator, "default"); + try testing.expectEqualStrings("default", settings.texture_pack); +} + +test "setTexturePack changes value" { + const allocator = testing.allocator; + var settings = Settings{ .texture_pack = "default" }; + + try persistence.setTexturePack(&settings, allocator, "mypack"); + try testing.expectEqualStrings("mypack", settings.texture_pack); + persistence.deinit(&settings, allocator); +} + +test "setTexturePack handles multiple changes" { + const allocator = testing.allocator; + var settings = Settings{ .texture_pack = "default" }; + + try persistence.setTexturePack(&settings, allocator, "pack1"); + try testing.expectEqualStrings("pack1", settings.texture_pack); + + try persistence.setTexturePack(&settings, allocator, "pack2"); + try testing.expectEqualStrings("pack2", settings.texture_pack); + persistence.deinit(&settings, allocator); +} + +test "setEnvironmentMap returns early when same value" { + const allocator = testing.allocator; + var settings = Settings{ .texture_pack = "default", .environment_map = "default" }; + + try persistence.setEnvironmentMap(&settings, allocator, "default"); + try testing.expectEqualStrings("default", settings.environment_map); +} + +test "setEnvironmentMap changes value" { + const allocator = testing.allocator; + var settings = Settings{ .texture_pack = "default", .environment_map = "default" }; + + try persistence.setEnvironmentMap(&settings, allocator, "sunset.exr"); + try testing.expectEqualStrings("sunset.exr", settings.environment_map); + persistence.deinit(&settings, allocator); +} + +test "Settings.getShadowResolution returns correct resolution" { + var settings = Settings{}; + + settings.shadow_quality = 0; + try testing.expectEqual(@as(u32, 1024), settings.getShadowResolution()); + + settings.shadow_quality = 1; + try testing.expectEqual(@as(u32, 1536), settings.getShadowResolution()); + + settings.shadow_quality = 2; + try testing.expectEqual(@as(u32, 2048), settings.getShadowResolution()); + + settings.shadow_quality = 3; + try testing.expectEqual(@as(u32, 4096), settings.getShadowResolution()); +} + +test "Settings.getShadowResolution clamps out-of-bounds" { + var settings = Settings{}; + settings.shadow_quality = 99; + try testing.expectEqual(@as(u32, 2048), settings.getShadowResolution()); +} + +test "Settings.getResolutionIndex finds matching resolution" { + var settings = Settings{ .window_width = 1920, .window_height = 1080 }; + try testing.expectEqual(@as(usize, 2), settings.getResolutionIndex()); +} + +test "Settings.getResolutionIndex returns default for unknown" { + var settings = Settings{ .window_width = 9999, .window_height = 9999 }; + try testing.expectEqual(@as(usize, 2), settings.getResolutionIndex()); +} + +test "Settings.setResolutionByIndex updates dimensions" { + var settings = Settings{}; + settings.setResolutionByIndex(0); + try testing.expectEqual(@as(u32, 1280), settings.window_width); + try testing.expectEqual(@as(u32, 720), settings.window_height); +} + +test "Settings.setResolutionByIndex ignores invalid index" { + var settings = Settings{ .window_width = 1920, .window_height = 1080 }; + settings.setResolutionByIndex(99); + try testing.expectEqual(@as(u32, 1920), settings.window_width); + try testing.expectEqual(@as(u32, 1080), settings.window_height); +} + +test "Settings default values" { + const settings = Settings{}; + try testing.expectEqual(@as(i32, 15), settings.render_distance); + try testing.expectEqual(@as(f32, 50.0), settings.mouse_sensitivity); + try testing.expectEqual(true, settings.vsync); + try testing.expectEqual(@as(f32, 45.0), settings.fov); + try testing.expectEqual(true, settings.textures_enabled); + try testing.expectEqual(false, settings.wireframe_enabled); +} + +test "SHADOW_QUALITIES array has correct values" { + try testing.expectEqual(@as(u32, 1024), data.SHADOW_QUALITIES[0].resolution); + try testing.expectEqualStrings("LOW", data.SHADOW_QUALITIES[0].label); + + try testing.expectEqual(@as(u32, 1536), data.SHADOW_QUALITIES[1].resolution); + try testing.expectEqualStrings("MEDIUM", data.SHADOW_QUALITIES[1].label); + + try testing.expectEqual(@as(u32, 2048), data.SHADOW_QUALITIES[2].resolution); + try testing.expectEqualStrings("HIGH", data.SHADOW_QUALITIES[2].label); + + try testing.expectEqual(@as(u32, 4096), data.SHADOW_QUALITIES[3].resolution); + try testing.expectEqualStrings("ULTRA", data.SHADOW_QUALITIES[3].label); +} + +test "RESOLUTIONS array has expected entries" { + try testing.expectEqual(@as(u32, 1920), data.RESOLUTIONS[2].width); + try testing.expectEqual(@as(u32, 1080), data.RESOLUTIONS[2].height); + try testing.expectEqualStrings("1920X1080", data.RESOLUTIONS[2].label); +} + +test "RESOLUTIONS covers standard resolutions" { + try testing.expectEqual(@as(u32, 7), data.RESOLUTIONS.len); + + try testing.expectEqual(@as(u32, 1280), data.RESOLUTIONS[0].width); + try testing.expectEqual(@as(u32, 720), data.RESOLUTIONS[0].height); + + try testing.expectEqual(@as(u32, 1600), data.RESOLUTIONS[1].width); + try testing.expectEqual(@as(u32, 900), data.RESOLUTIONS[1].height); +} + +test "Settings resolution roundtrip" { + var settings = Settings{}; + + for (0..data.RESOLUTIONS.len) |i| { + settings.setResolutionByIndex(i); + try testing.expectEqual(@as(usize, i), settings.getResolutionIndex()); + } +} diff --git a/src/tests.zig b/src/tests.zig index 83d8d951..45a19dd7 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -93,6 +93,8 @@ test { _ = @import("game/screen_tests.zig"); _ = @import("game/session_tests.zig"); _ = @import("game/world_list_tests.zig"); + _ = @import("game/input_mapper_tests.zig"); + _ = @import("game/settings/persistence_tests.zig"); _ = @import("world/persistence/region_file.zig"); _ = @import("world/persistence/chunk_serializer.zig"); _ = @import("world/persistence/level_data.zig");