gemba 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/THIRD_PARTY_NOTICES +113 -0
  3. data/assets/JetBrainsMonoNL-Regular.ttf +0 -0
  4. data/assets/ark-pixel-12px-monospaced-ja.ttf +0 -0
  5. data/bin/gemba +14 -0
  6. data/ext/gemba/extconf.rb +185 -0
  7. data/ext/gemba/gemba_ext.c +1051 -0
  8. data/ext/gemba/gemba_ext.h +15 -0
  9. data/gemba.gemspec +38 -0
  10. data/lib/gemba/child_window.rb +62 -0
  11. data/lib/gemba/cli.rb +384 -0
  12. data/lib/gemba/config.rb +621 -0
  13. data/lib/gemba/core.rb +121 -0
  14. data/lib/gemba/headless.rb +12 -0
  15. data/lib/gemba/headless_player.rb +206 -0
  16. data/lib/gemba/hotkey_map.rb +202 -0
  17. data/lib/gemba/input_mappings.rb +214 -0
  18. data/lib/gemba/locale.rb +92 -0
  19. data/lib/gemba/locales/en.yml +157 -0
  20. data/lib/gemba/locales/ja.yml +157 -0
  21. data/lib/gemba/method_coverage_service.rb +265 -0
  22. data/lib/gemba/overlay_renderer.rb +109 -0
  23. data/lib/gemba/player.rb +1515 -0
  24. data/lib/gemba/recorder.rb +156 -0
  25. data/lib/gemba/recorder_decoder.rb +325 -0
  26. data/lib/gemba/rom_info_window.rb +346 -0
  27. data/lib/gemba/rom_loader.rb +100 -0
  28. data/lib/gemba/runtime.rb +39 -0
  29. data/lib/gemba/save_state_manager.rb +155 -0
  30. data/lib/gemba/save_state_picker.rb +199 -0
  31. data/lib/gemba/settings_window.rb +1173 -0
  32. data/lib/gemba/tip_service.rb +133 -0
  33. data/lib/gemba/toast_overlay.rb +128 -0
  34. data/lib/gemba/version.rb +5 -0
  35. data/lib/gemba.rb +17 -0
  36. data/test/fixtures/test.gba +0 -0
  37. data/test/fixtures/test.sav +0 -0
  38. data/test/shared/screenshot_helper.rb +113 -0
  39. data/test/shared/simplecov_config.rb +59 -0
  40. data/test/shared/teek_test_worker.rb +388 -0
  41. data/test/shared/tk_test_helper.rb +354 -0
  42. data/test/support/input_mocks.rb +61 -0
  43. data/test/support/player_helpers.rb +77 -0
  44. data/test/test_cli.rb +281 -0
  45. data/test/test_config.rb +897 -0
  46. data/test/test_core.rb +401 -0
  47. data/test/test_gamepad_map.rb +116 -0
  48. data/test/test_headless_player.rb +205 -0
  49. data/test/test_helper.rb +19 -0
  50. data/test/test_hotkey_map.rb +396 -0
  51. data/test/test_keyboard_map.rb +108 -0
  52. data/test/test_locale.rb +159 -0
  53. data/test/test_mgba.rb +26 -0
  54. data/test/test_overlay_renderer.rb +199 -0
  55. data/test/test_player.rb +903 -0
  56. data/test/test_recorder.rb +180 -0
  57. data/test/test_rom_loader.rb +149 -0
  58. data/test/test_save_state_manager.rb +289 -0
  59. data/test/test_settings_hotkeys.rb +434 -0
  60. data/test/test_settings_window.rb +1039 -0
  61. data/test/test_tip_service.rb +138 -0
  62. data/test/test_toast_overlay.rb +216 -0
  63. data/test/test_virtual_keyboard.rb +39 -0
  64. data/test/test_xor_delta.rb +61 -0
  65. metadata +234 -0
data/test/test_cli.rb ADDED
@@ -0,0 +1,281 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require "gemba"
5
+ require_relative "../lib/gemba/version"
6
+ require_relative "../lib/gemba/cli"
7
+
8
+ class TestCLI < Minitest::Test
9
+ def parse(args)
10
+ Gemba::CLI.parse(args)
11
+ end
12
+
13
+ # -- ROM argument --
14
+
15
+ def test_rom_path
16
+ opts = parse(["game.gba"])
17
+ assert_equal File.expand_path("game.gba"), opts[:rom]
18
+ end
19
+
20
+ def test_rom_path_expands_tilde
21
+ opts = parse(["~/game.gba"])
22
+ assert_equal File.join(Dir.home, "game.gba"), opts[:rom]
23
+ end
24
+
25
+ def test_no_rom
26
+ opts = parse([])
27
+ assert_nil opts[:rom]
28
+ end
29
+
30
+ # -- player flags --
31
+
32
+ def test_scale
33
+ opts = parse(["--scale", "2"])
34
+ assert_equal 2, opts[:scale]
35
+ end
36
+
37
+ def test_scale_short
38
+ opts = parse(["-s", "3"])
39
+ assert_equal 3, opts[:scale]
40
+ end
41
+
42
+ def test_scale_clamps_high
43
+ opts = parse(["--scale", "10"])
44
+ assert_equal 4, opts[:scale]
45
+ end
46
+
47
+ def test_scale_clamps_low
48
+ opts = parse(["--scale", "0"])
49
+ assert_equal 1, opts[:scale]
50
+ end
51
+
52
+ def test_volume
53
+ opts = parse(["--volume", "50"])
54
+ assert_equal 50, opts[:volume]
55
+ end
56
+
57
+ def test_volume_short
58
+ opts = parse(["-v", "75"])
59
+ assert_equal 75, opts[:volume]
60
+ end
61
+
62
+ def test_volume_clamps
63
+ opts = parse(["--volume", "200"])
64
+ assert_equal 100, opts[:volume]
65
+ end
66
+
67
+ def test_mute
68
+ opts = parse(["--mute"])
69
+ assert opts[:mute]
70
+ end
71
+
72
+ def test_mute_short
73
+ opts = parse(["-m"])
74
+ assert opts[:mute]
75
+ end
76
+
77
+ def test_no_sound
78
+ opts = parse(["--no-sound"])
79
+ assert_equal false, opts[:sound]
80
+ end
81
+
82
+ def test_fullscreen
83
+ opts = parse(["--fullscreen"])
84
+ assert opts[:fullscreen]
85
+ end
86
+
87
+ def test_fullscreen_short
88
+ opts = parse(["-f"])
89
+ assert opts[:fullscreen]
90
+ end
91
+
92
+ def test_show_fps
93
+ opts = parse(["--show-fps"])
94
+ assert opts[:show_fps]
95
+ end
96
+
97
+ def test_turbo_speed
98
+ opts = parse(["--turbo-speed", "3"])
99
+ assert_equal 3, opts[:turbo_speed]
100
+ end
101
+
102
+ def test_turbo_speed_clamps
103
+ opts = parse(["--turbo-speed", "99"])
104
+ assert_equal 4, opts[:turbo_speed]
105
+ end
106
+
107
+ def test_locale
108
+ opts = parse(["--locale", "ja"])
109
+ assert_equal "ja", opts[:locale]
110
+ end
111
+
112
+ def test_version_flag
113
+ opts = parse(["--version"])
114
+ assert opts[:version]
115
+ end
116
+
117
+ def test_help_flag
118
+ opts = parse(["--help"])
119
+ assert opts[:help]
120
+ end
121
+
122
+ # -- combinations --
123
+
124
+ def test_flags_with_rom
125
+ opts = parse(["-s", "2", "--mute", "pokemon.gba"])
126
+ assert_equal 2, opts[:scale]
127
+ assert opts[:mute]
128
+ assert_equal File.expand_path("pokemon.gba"), opts[:rom]
129
+ end
130
+
131
+ def test_rom_before_flags
132
+ opts = parse(["game.gba", "--scale", "4"])
133
+ assert_equal File.expand_path("game.gba"), opts[:rom]
134
+ assert_equal 4, opts[:scale]
135
+ end
136
+
137
+ # -- parser included for help output --
138
+
139
+ def test_parser_present
140
+ opts = parse([])
141
+ assert_kind_of OptionParser, opts[:parser]
142
+ end
143
+
144
+ def test_help_output_includes_banner
145
+ opts = parse([])
146
+ help = opts[:parser].to_s
147
+ assert_includes help, "Usage: gemba"
148
+ assert_includes help, "GBA emulator"
149
+ end
150
+
151
+ def test_help_lists_subcommands
152
+ opts = parse([])
153
+ help = opts[:parser].to_s
154
+ assert_includes help, "record"
155
+ assert_includes help, "decode"
156
+ assert_includes help, "info"
157
+ end
158
+
159
+ # -- apply --
160
+
161
+ class MockConfig
162
+ attr_accessor :scale, :volume, :muted, :show_fps, :turbo_speed, :locale
163
+
164
+ def initialize
165
+ @scale = 3
166
+ @volume = 100
167
+ @muted = false
168
+ @show_fps = false
169
+ @turbo_speed = 0
170
+ @locale = 'auto'
171
+ end
172
+ end
173
+
174
+ def test_apply_overrides_config
175
+ config = MockConfig.new
176
+ Gemba::CLI.apply(config, { scale: 2, volume: 50, mute: true, show_fps: true })
177
+ assert_equal 2, config.scale
178
+ assert_equal 50, config.volume
179
+ assert config.muted
180
+ assert config.show_fps
181
+ end
182
+
183
+ def test_apply_skips_unset_options
184
+ config = MockConfig.new
185
+ Gemba::CLI.apply(config, {})
186
+ assert_equal 3, config.scale
187
+ assert_equal 100, config.volume
188
+ refute config.muted
189
+ end
190
+
191
+ def test_apply_locale
192
+ config = MockConfig.new
193
+ Gemba::CLI.apply(config, { locale: 'ja' })
194
+ assert_equal 'ja', config.locale
195
+ end
196
+
197
+ def test_apply_turbo_speed
198
+ config = MockConfig.new
199
+ Gemba::CLI.apply(config, { turbo_speed: 3 })
200
+ assert_equal 3, config.turbo_speed
201
+ end
202
+
203
+ # -- subcommand dispatch --
204
+
205
+ def test_subcommands_constant
206
+ assert_includes Gemba::CLI::SUBCOMMANDS, 'record'
207
+ assert_includes Gemba::CLI::SUBCOMMANDS, 'decode'
208
+ assert_includes Gemba::CLI::SUBCOMMANDS, 'info'
209
+ end
210
+
211
+ # -- record subcommand parsing --
212
+
213
+ def test_parse_record_frames_and_rom
214
+ opts = Gemba::CLI.parse_record(["--frames", "100", "game.gba"])
215
+ assert_equal 100, opts[:frames]
216
+ assert_equal File.expand_path("game.gba"), opts[:rom]
217
+ end
218
+
219
+ def test_parse_record_output
220
+ opts = Gemba::CLI.parse_record(["-o", "out.grec", "--frames", "10", "game.gba"])
221
+ assert_equal "out.grec", opts[:output]
222
+ end
223
+
224
+ def test_parse_record_help
225
+ opts = Gemba::CLI.parse_record(["--help"])
226
+ assert opts[:help]
227
+ end
228
+
229
+ # -- decode subcommand parsing --
230
+
231
+ def test_parse_decode_trec
232
+ opts = Gemba::CLI.parse_decode(["recording.grec"])
233
+ assert_equal "recording.grec", opts[:trec]
234
+ end
235
+
236
+ def test_parse_decode_output
237
+ opts = Gemba::CLI.parse_decode(["-o", "out.mkv", "recording.grec"])
238
+ assert_equal "out.mkv", opts[:output]
239
+ end
240
+
241
+ def test_parse_decode_codecs
242
+ opts = Gemba::CLI.parse_decode(["--video-codec", "libx265", "--audio-codec", "opus", "r.grec"])
243
+ assert_equal "libx265", opts[:video_codec]
244
+ assert_equal "opus", opts[:audio_codec]
245
+ end
246
+
247
+ # -- misc --
248
+
249
+ def test_reset_config_flag
250
+ opts = parse(["--reset-config"])
251
+ assert opts[:reset_config]
252
+ end
253
+
254
+ def test_yes_flag
255
+ opts = parse(["-y"])
256
+ assert opts[:yes]
257
+ end
258
+
259
+ def test_frames
260
+ opts = parse(["--frames", "100", "game.gba"])
261
+ assert_equal 100, opts[:frames]
262
+ end
263
+
264
+ def test_frames_without_value_raises
265
+ assert_raises(OptionParser::MissingArgument) { parse(["--frames"]) }
266
+ end
267
+
268
+ def test_frames_without_rom_exits
269
+ assert_raises(SystemExit) do
270
+ Gemba::CLI.run(["--frames", "100"])
271
+ end
272
+ end
273
+
274
+ def test_unknown_flag_raises
275
+ assert_raises(OptionParser::InvalidOption) { parse(["--bogus"]) }
276
+ end
277
+
278
+ def test_missing_scale_value_raises
279
+ assert_raises(OptionParser::MissingArgument) { parse(["--scale"]) }
280
+ end
281
+ end