gemba 0.1.0 → 0.2.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 (285) hide show
  1. checksums.yaml +4 -4
  2. data/THIRD_PARTY_NOTICES +37 -2
  3. data/assets/placeholder_boxart.png +0 -0
  4. data/bin/gemba +2 -2
  5. data/ext/gemba/extconf.rb +24 -1
  6. data/ext/gemba/gemba_ext.c +436 -2
  7. data/ext/gemba/gemba_ext.h +2 -0
  8. data/gemba.gemspec +5 -3
  9. data/lib/gemba/achievements/achievement.rb +23 -0
  10. data/lib/gemba/achievements/backend.rb +186 -0
  11. data/lib/gemba/achievements/cache.rb +70 -0
  12. data/lib/gemba/achievements/credentials_presenter.rb +142 -0
  13. data/lib/gemba/achievements/fake_backend.rb +205 -0
  14. data/lib/gemba/achievements/null_backend.rb +11 -0
  15. data/lib/gemba/achievements/offline_backend.rb +168 -0
  16. data/lib/gemba/achievements/retro_achievements/backend.rb +453 -0
  17. data/lib/gemba/achievements/retro_achievements/cli_sync_requester.rb +64 -0
  18. data/lib/gemba/achievements/retro_achievements/ping_worker.rb +27 -0
  19. data/lib/gemba/achievements.rb +19 -0
  20. data/lib/gemba/achievements_window.rb +556 -0
  21. data/lib/gemba/app_controller.rb +1015 -0
  22. data/lib/gemba/bios.rb +54 -0
  23. data/lib/gemba/boxart_fetcher/libretro_backend.rb +39 -0
  24. data/lib/gemba/boxart_fetcher/null_backend.rb +12 -0
  25. data/lib/gemba/boxart_fetcher.rb +79 -0
  26. data/lib/gemba/bus_emitter.rb +13 -0
  27. data/lib/gemba/child_window.rb +24 -1
  28. data/lib/gemba/cli/commands/config_cmd.rb +83 -0
  29. data/lib/gemba/cli/commands/decode.rb +154 -0
  30. data/lib/gemba/cli/commands/patch.rb +78 -0
  31. data/lib/gemba/cli/commands/play.rb +78 -0
  32. data/lib/gemba/cli/commands/record.rb +114 -0
  33. data/lib/gemba/cli/commands/replay.rb +161 -0
  34. data/lib/gemba/cli/commands/retro_achievements.rb +213 -0
  35. data/lib/gemba/cli/commands/version.rb +22 -0
  36. data/lib/gemba/cli.rb +52 -364
  37. data/lib/gemba/config.rb +135 -1
  38. data/lib/gemba/data/gb_games.json +1 -0
  39. data/lib/gemba/data/gb_md5.json +1 -0
  40. data/lib/gemba/data/gba_games.json +1 -0
  41. data/lib/gemba/data/gba_md5.json +1 -0
  42. data/lib/gemba/data/gbc_games.json +1 -0
  43. data/lib/gemba/data/gbc_md5.json +1 -0
  44. data/lib/gemba/emulator_frame.rb +1060 -0
  45. data/lib/gemba/event_bus.rb +48 -0
  46. data/lib/gemba/frame_stack.rb +60 -0
  47. data/lib/gemba/game_index.rb +84 -0
  48. data/lib/gemba/game_picker_frame.rb +268 -0
  49. data/lib/gemba/gamepad_map.rb +103 -0
  50. data/lib/gemba/headless.rb +6 -5
  51. data/lib/gemba/headless_player.rb +33 -3
  52. data/lib/gemba/help_window.rb +61 -0
  53. data/lib/gemba/hotkey_map.rb +3 -1
  54. data/lib/gemba/input_recorder.rb +107 -0
  55. data/lib/gemba/input_replayer.rb +119 -0
  56. data/lib/gemba/keyboard_map.rb +90 -0
  57. data/lib/gemba/locales/en.yml +97 -5
  58. data/lib/gemba/locales/ja.yml +97 -5
  59. data/lib/gemba/main_window.rb +56 -0
  60. data/lib/gemba/modal_stack.rb +81 -0
  61. data/lib/gemba/patcher_window.rb +223 -0
  62. data/lib/gemba/platform/gb.rb +21 -0
  63. data/lib/gemba/platform/gba.rb +21 -0
  64. data/lib/gemba/platform/gbc.rb +23 -0
  65. data/lib/gemba/platform.rb +20 -0
  66. data/lib/gemba/platform_open.rb +19 -0
  67. data/lib/gemba/recorder.rb +4 -3
  68. data/lib/gemba/replay_player.rb +691 -0
  69. data/lib/gemba/rom_info.rb +57 -0
  70. data/lib/gemba/rom_info_window.rb +16 -3
  71. data/lib/gemba/rom_library.rb +106 -0
  72. data/lib/gemba/rom_overrides.rb +47 -0
  73. data/lib/gemba/rom_patcher/bps.rb +161 -0
  74. data/lib/gemba/rom_patcher/ips.rb +101 -0
  75. data/lib/gemba/rom_patcher/ups.rb +118 -0
  76. data/lib/gemba/rom_patcher.rb +109 -0
  77. data/lib/gemba/{rom_loader.rb → rom_resolver.rb} +7 -6
  78. data/lib/gemba/runtime.rb +59 -26
  79. data/lib/gemba/save_state_manager.rb +4 -7
  80. data/lib/gemba/save_state_picker.rb +17 -4
  81. data/lib/gemba/session_logger.rb +64 -0
  82. data/lib/gemba/settings/audio_tab.rb +77 -0
  83. data/lib/gemba/settings/gamepad_tab.rb +351 -0
  84. data/lib/gemba/settings/hotkeys_tab.rb +259 -0
  85. data/lib/gemba/settings/paths.rb +11 -0
  86. data/lib/gemba/settings/recording_tab.rb +83 -0
  87. data/lib/gemba/settings/save_states_tab.rb +91 -0
  88. data/lib/gemba/settings/system_tab.rb +362 -0
  89. data/lib/gemba/settings/video_tab.rb +318 -0
  90. data/lib/gemba/settings_window.rb +162 -1036
  91. data/lib/gemba/version.rb +1 -1
  92. data/lib/gemba/virtual_keyboard.rb +19 -0
  93. data/lib/gemba.rb +2 -12
  94. data/test/achievements_window/test_bulk_sync.rb +218 -0
  95. data/test/achievements_window/test_bus_events.rb +125 -0
  96. data/test/achievements_window/test_close_confirmation.rb +201 -0
  97. data/test/achievements_window/test_initial_state.rb +164 -0
  98. data/test/achievements_window/test_sorting.rb +227 -0
  99. data/test/achievements_window/test_tree_rendering.rb +133 -0
  100. data/test/fixtures/fake_bios.bin +0 -0
  101. data/test/fixtures/pong.gba +0 -0
  102. data/test/fixtures/test.gb +0 -0
  103. data/test/fixtures/test.gbc +0 -0
  104. data/test/fixtures/test_quicksave.ss +0 -0
  105. data/test/screenshots/no_focus.png +0 -0
  106. data/test/shared/teek_test_worker.rb +17 -1
  107. data/test/shared/tk_test_helper.rb +91 -4
  108. data/test/support/achievements_window_helpers.rb +18 -0
  109. data/test/support/fake_core.rb +25 -0
  110. data/test/support/fake_ra_runtime.rb +74 -0
  111. data/test/support/fake_requester.rb +68 -0
  112. data/test/support/player_helpers.rb +20 -5
  113. data/test/test_achievement.rb +32 -0
  114. data/test/{test_player.rb → test_app_controller.rb} +353 -85
  115. data/test/test_bios.rb +123 -0
  116. data/test/test_boxart_fetcher.rb +150 -0
  117. data/test/test_cli.rb +17 -265
  118. data/test/test_cli_config.rb +64 -0
  119. data/test/test_cli_decode.rb +97 -0
  120. data/test/test_cli_patch.rb +58 -0
  121. data/test/test_cli_play.rb +213 -0
  122. data/test/test_cli_ra.rb +175 -0
  123. data/test/test_cli_record.rb +69 -0
  124. data/test/test_cli_replay.rb +72 -0
  125. data/test/test_cli_sync_requester.rb +152 -0
  126. data/test/test_cli_version.rb +27 -0
  127. data/test/test_config.rb +2 -3
  128. data/test/test_config_ra.rb +69 -0
  129. data/test/test_core.rb +62 -1
  130. data/test/test_credentials_presenter.rb +192 -0
  131. data/test/test_event_bus.rb +100 -0
  132. data/test/test_fake_backend_achievements.rb +130 -0
  133. data/test/test_fake_backend_auth.rb +68 -0
  134. data/test/test_game_index.rb +77 -0
  135. data/test/test_game_picker_frame.rb +310 -0
  136. data/test/test_gamepad_map.rb +1 -3
  137. data/test/test_headless_player.rb +17 -3
  138. data/test/test_help_window.rb +82 -0
  139. data/test/test_hotkey_map.rb +22 -1
  140. data/test/test_input_recorder.rb +179 -0
  141. data/test/test_input_replay_determinism.rb +113 -0
  142. data/test/test_input_replayer.rb +162 -0
  143. data/test/test_keyboard_map.rb +1 -3
  144. data/test/test_libretro_backend.rb +41 -0
  145. data/test/test_locale.rb +1 -1
  146. data/test/test_logging.rb +123 -0
  147. data/test/test_null_backend.rb +42 -0
  148. data/test/test_offline_backend.rb +116 -0
  149. data/test/test_overlay_renderer.rb +1 -1
  150. data/test/test_platform.rb +149 -0
  151. data/test/test_ra_backend.rb +313 -0
  152. data/test/test_ra_backend_unlock_gate.rb +56 -0
  153. data/test/test_recorder.rb +0 -3
  154. data/test/test_replay_player.rb +316 -0
  155. data/test/test_rom_info.rb +149 -0
  156. data/test/test_rom_overrides.rb +86 -0
  157. data/test/test_rom_patcher.rb +382 -0
  158. data/test/{test_rom_loader.rb → test_rom_resolver.rb} +25 -26
  159. data/test/test_save_state_manager.rb +2 -4
  160. data/test/test_settings_audio.rb +107 -0
  161. data/test/test_settings_hotkeys.rb +83 -66
  162. data/test/test_settings_recording.rb +49 -0
  163. data/test/test_settings_save_states.rb +97 -0
  164. data/test/test_settings_system.rb +133 -0
  165. data/test/test_settings_video.rb +450 -0
  166. data/test/test_settings_window.rb +76 -507
  167. data/test/test_tip_service.rb +6 -6
  168. data/test/test_toast_overlay.rb +1 -1
  169. data/test/test_virtual_events.rb +156 -0
  170. data/test/test_virtual_keyboard.rb +1 -1
  171. data/vendor/rcheevos/CHANGELOG.md +495 -0
  172. data/vendor/rcheevos/LICENSE +21 -0
  173. data/vendor/rcheevos/Package.swift +33 -0
  174. data/vendor/rcheevos/README.md +67 -0
  175. data/vendor/rcheevos/include/module.modulemap +70 -0
  176. data/vendor/rcheevos/include/rc_api_editor.h +296 -0
  177. data/vendor/rcheevos/include/rc_api_info.h +280 -0
  178. data/vendor/rcheevos/include/rc_api_request.h +77 -0
  179. data/vendor/rcheevos/include/rc_api_runtime.h +417 -0
  180. data/vendor/rcheevos/include/rc_api_user.h +262 -0
  181. data/vendor/rcheevos/include/rc_client.h +877 -0
  182. data/vendor/rcheevos/include/rc_client_raintegration.h +101 -0
  183. data/vendor/rcheevos/include/rc_consoles.h +138 -0
  184. data/vendor/rcheevos/include/rc_error.h +59 -0
  185. data/vendor/rcheevos/include/rc_export.h +100 -0
  186. data/vendor/rcheevos/include/rc_hash.h +200 -0
  187. data/vendor/rcheevos/include/rc_runtime.h +148 -0
  188. data/vendor/rcheevos/include/rc_runtime_types.h +452 -0
  189. data/vendor/rcheevos/include/rc_util.h +51 -0
  190. data/vendor/rcheevos/include/rcheevos.h +8 -0
  191. data/vendor/rcheevos/src/rapi/rc_api_common.c +1379 -0
  192. data/vendor/rcheevos/src/rapi/rc_api_common.h +88 -0
  193. data/vendor/rcheevos/src/rapi/rc_api_editor.c +625 -0
  194. data/vendor/rcheevos/src/rapi/rc_api_info.c +587 -0
  195. data/vendor/rcheevos/src/rapi/rc_api_runtime.c +901 -0
  196. data/vendor/rcheevos/src/rapi/rc_api_user.c +483 -0
  197. data/vendor/rcheevos/src/rc_client.c +6941 -0
  198. data/vendor/rcheevos/src/rc_client_external.c +281 -0
  199. data/vendor/rcheevos/src/rc_client_external.h +177 -0
  200. data/vendor/rcheevos/src/rc_client_external_versions.h +171 -0
  201. data/vendor/rcheevos/src/rc_client_internal.h +409 -0
  202. data/vendor/rcheevos/src/rc_client_raintegration.c +566 -0
  203. data/vendor/rcheevos/src/rc_client_raintegration_internal.h +61 -0
  204. data/vendor/rcheevos/src/rc_client_types.natvis +396 -0
  205. data/vendor/rcheevos/src/rc_compat.c +251 -0
  206. data/vendor/rcheevos/src/rc_compat.h +121 -0
  207. data/vendor/rcheevos/src/rc_libretro.c +915 -0
  208. data/vendor/rcheevos/src/rc_libretro.h +98 -0
  209. data/vendor/rcheevos/src/rc_util.c +199 -0
  210. data/vendor/rcheevos/src/rc_version.c +11 -0
  211. data/vendor/rcheevos/src/rc_version.h +32 -0
  212. data/vendor/rcheevos/src/rcheevos/alloc.c +312 -0
  213. data/vendor/rcheevos/src/rcheevos/condition.c +754 -0
  214. data/vendor/rcheevos/src/rcheevos/condset.c +777 -0
  215. data/vendor/rcheevos/src/rcheevos/consoleinfo.c +1215 -0
  216. data/vendor/rcheevos/src/rcheevos/format.c +330 -0
  217. data/vendor/rcheevos/src/rcheevos/lboard.c +287 -0
  218. data/vendor/rcheevos/src/rcheevos/memref.c +805 -0
  219. data/vendor/rcheevos/src/rcheevos/operand.c +607 -0
  220. data/vendor/rcheevos/src/rcheevos/rc_internal.h +390 -0
  221. data/vendor/rcheevos/src/rcheevos/rc_runtime_types.natvis +541 -0
  222. data/vendor/rcheevos/src/rcheevos/rc_validate.c +1406 -0
  223. data/vendor/rcheevos/src/rcheevos/rc_validate.h +18 -0
  224. data/vendor/rcheevos/src/rcheevos/richpresence.c +922 -0
  225. data/vendor/rcheevos/src/rcheevos/runtime.c +852 -0
  226. data/vendor/rcheevos/src/rcheevos/runtime_progress.c +1073 -0
  227. data/vendor/rcheevos/src/rcheevos/trigger.c +344 -0
  228. data/vendor/rcheevos/src/rcheevos/value.c +935 -0
  229. data/vendor/rcheevos/src/rhash/aes.c +480 -0
  230. data/vendor/rcheevos/src/rhash/aes.h +49 -0
  231. data/vendor/rcheevos/src/rhash/cdreader.c +838 -0
  232. data/vendor/rcheevos/src/rhash/hash.c +1402 -0
  233. data/vendor/rcheevos/src/rhash/hash_disc.c +1340 -0
  234. data/vendor/rcheevos/src/rhash/hash_encrypted.c +566 -0
  235. data/vendor/rcheevos/src/rhash/hash_rom.c +426 -0
  236. data/vendor/rcheevos/src/rhash/hash_zip.c +460 -0
  237. data/vendor/rcheevos/src/rhash/md5.c +382 -0
  238. data/vendor/rcheevos/src/rhash/md5.h +91 -0
  239. data/vendor/rcheevos/src/rhash/rc_hash_internal.h +116 -0
  240. data/vendor/rcheevos/test/libretro.h +205 -0
  241. data/vendor/rcheevos/test/rapi/test_rc_api_common.c +941 -0
  242. data/vendor/rcheevos/test/rapi/test_rc_api_editor.c +931 -0
  243. data/vendor/rcheevos/test/rapi/test_rc_api_info.c +545 -0
  244. data/vendor/rcheevos/test/rapi/test_rc_api_runtime.c +2213 -0
  245. data/vendor/rcheevos/test/rapi/test_rc_api_user.c +998 -0
  246. data/vendor/rcheevos/test/rcheevos/mock_memory.h +32 -0
  247. data/vendor/rcheevos/test/rcheevos/test_condition.c +570 -0
  248. data/vendor/rcheevos/test/rcheevos/test_condset.c +5170 -0
  249. data/vendor/rcheevos/test/rcheevos/test_consoleinfo.c +203 -0
  250. data/vendor/rcheevos/test/rcheevos/test_format.c +112 -0
  251. data/vendor/rcheevos/test/rcheevos/test_lboard.c +746 -0
  252. data/vendor/rcheevos/test/rcheevos/test_memref.c +520 -0
  253. data/vendor/rcheevos/test/rcheevos/test_operand.c +692 -0
  254. data/vendor/rcheevos/test/rcheevos/test_rc_validate.c +502 -0
  255. data/vendor/rcheevos/test/rcheevos/test_richpresence.c +1564 -0
  256. data/vendor/rcheevos/test/rcheevos/test_runtime.c +1667 -0
  257. data/vendor/rcheevos/test/rcheevos/test_runtime_progress.c +1821 -0
  258. data/vendor/rcheevos/test/rcheevos/test_timing.c +166 -0
  259. data/vendor/rcheevos/test/rcheevos/test_trigger.c +2521 -0
  260. data/vendor/rcheevos/test/rcheevos/test_value.c +870 -0
  261. data/vendor/rcheevos/test/rcheevos-test.sln +46 -0
  262. data/vendor/rcheevos/test/rcheevos-test.vcxproj +239 -0
  263. data/vendor/rcheevos/test/rcheevos-test.vcxproj.filters +335 -0
  264. data/vendor/rcheevos/test/rhash/data.c +657 -0
  265. data/vendor/rcheevos/test/rhash/data.h +32 -0
  266. data/vendor/rcheevos/test/rhash/mock_filereader.c +236 -0
  267. data/vendor/rcheevos/test/rhash/mock_filereader.h +31 -0
  268. data/vendor/rcheevos/test/rhash/test_cdreader.c +920 -0
  269. data/vendor/rcheevos/test/rhash/test_hash.c +310 -0
  270. data/vendor/rcheevos/test/rhash/test_hash_disc.c +1450 -0
  271. data/vendor/rcheevos/test/rhash/test_hash_rom.c +899 -0
  272. data/vendor/rcheevos/test/rhash/test_hash_zip.c +551 -0
  273. data/vendor/rcheevos/test/test.c +113 -0
  274. data/vendor/rcheevos/test/test_framework.h +205 -0
  275. data/vendor/rcheevos/test/test_rc_client.c +10509 -0
  276. data/vendor/rcheevos/test/test_rc_client_external.c +2197 -0
  277. data/vendor/rcheevos/test/test_rc_client_raintegration.c +441 -0
  278. data/vendor/rcheevos/test/test_rc_libretro.c +952 -0
  279. data/vendor/rcheevos/test/test_types.natvis +9 -0
  280. data/vendor/rcheevos/validator/validator.c +658 -0
  281. data/vendor/rcheevos/validator/validator.vcxproj +152 -0
  282. data/vendor/rcheevos/validator/validator.vcxproj.filters +82 -0
  283. metadata +274 -11
  284. data/lib/gemba/input_mappings.rb +0 -214
  285. data/lib/gemba/player.rb +0 -1515
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require_relative "shared/tk_test_helper"
5
+
6
+ class TestSettingsSaveStatesTab < Minitest::Test
7
+ include TeekTestHelper
8
+
9
+ def test_save_states_tab_exists
10
+ assert_tk_app("save states tab exists in notebook") do
11
+ require "gemba/headless"
12
+ sw = Gemba::SettingsWindow.new(app)
13
+ sw.show
14
+ app.update
15
+
16
+ tabs = app.command(Gemba::Settings::Paths::NB, 'tabs')
17
+ assert_includes tabs, Gemba::Settings::SaveStatesTab::FRAME
18
+ end
19
+ end
20
+
21
+ def test_quick_slot_defaults_to_1
22
+ assert_tk_app("quick slot defaults to 1") do
23
+ require "gemba/headless"
24
+ sw = Gemba::SettingsWindow.new(app)
25
+ sw.show
26
+ app.update
27
+
28
+ assert_equal '1', app.get_variable(Gemba::Settings::SaveStatesTab::VAR_QUICK_SLOT)
29
+ end
30
+ end
31
+
32
+ def test_selecting_slot_fires_callback
33
+ assert_tk_app("selecting slot fires on_quick_slot_change") do
34
+ require "gemba/headless"
35
+ received = nil
36
+ Gemba.bus = Gemba::EventBus.new
37
+ Gemba.bus.on(:quick_slot_changed) { |v| received = v }
38
+ sw = Gemba::SettingsWindow.new(app)
39
+ sw.show
40
+ app.update
41
+
42
+ app.set_variable(Gemba::Settings::SaveStatesTab::VAR_QUICK_SLOT, '5')
43
+ app.command(:event, 'generate', Gemba::Settings::SaveStatesTab::SLOT_COMBO, '<<ComboboxSelected>>')
44
+ app.update
45
+
46
+ assert_equal 5, received
47
+ end
48
+ end
49
+
50
+ def test_backup_defaults_to_on
51
+ assert_tk_app("backup checkbox defaults to on") do
52
+ require "gemba/headless"
53
+ sw = Gemba::SettingsWindow.new(app)
54
+ sw.show
55
+ app.update
56
+
57
+ assert_equal '1', app.get_variable(Gemba::Settings::SaveStatesTab::VAR_BACKUP)
58
+ end
59
+ end
60
+
61
+ def test_clicking_backup_fires_callback
62
+ assert_tk_app("clicking backup fires on_backup_change") do
63
+ require "gemba/headless"
64
+ received = nil
65
+ Gemba.bus = Gemba::EventBus.new
66
+ Gemba.bus.on(:backup_changed) { |v| received = v }
67
+ sw = Gemba::SettingsWindow.new(app)
68
+ sw.show
69
+ app.update
70
+
71
+ app.command(Gemba::Settings::SaveStatesTab::BACKUP_CHECK, 'invoke')
72
+ app.update
73
+
74
+ assert_equal false, received
75
+ end
76
+ end
77
+
78
+ def test_clicking_backup_twice_re_enables
79
+ assert_tk_app("clicking backup twice re-enables") do
80
+ require "gemba/headless"
81
+ received = nil
82
+ Gemba.bus = Gemba::EventBus.new
83
+ Gemba.bus.on(:backup_changed) { |v| received = v }
84
+ sw = Gemba::SettingsWindow.new(app)
85
+ sw.show
86
+ app.update
87
+
88
+ app.command(Gemba::Settings::SaveStatesTab::BACKUP_CHECK, 'invoke')
89
+ app.update
90
+ assert_equal false, received
91
+
92
+ app.command(Gemba::Settings::SaveStatesTab::BACKUP_CHECK, 'invoke')
93
+ app.update
94
+ assert_equal true, received
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require_relative "shared/tk_test_helper"
5
+
6
+ class TestSettingsSystem < Minitest::Test
7
+ include TeekTestHelper
8
+
9
+ FAKE_BIOS = File.expand_path("fixtures/fake_bios.bin", __dir__)
10
+
11
+ # -- tab presence -----------------------------------------------------------
12
+
13
+ def test_system_tab_exists_in_notebook
14
+ assert_tk_app("system tab exists in notebook") do
15
+ require "gemba/headless"
16
+ Gemba.bus = Gemba::EventBus.new
17
+ sw = Gemba::SettingsWindow.new(app)
18
+ sw.show
19
+ app.update
20
+
21
+ tabs = app.command(Gemba::SettingsWindow::NB, 'tabs').split
22
+ assert_includes tabs, Gemba::SettingsWindow::SYSTEM_TAB
23
+ end
24
+ end
25
+
26
+ # -- initial state ----------------------------------------------------------
27
+
28
+ def test_bios_path_empty_initially
29
+ assert_tk_app("bios path var is empty initially") do
30
+ require "gemba/headless"
31
+ Gemba.bus = Gemba::EventBus.new
32
+ sw = Gemba::SettingsWindow.new(app)
33
+ sw.show
34
+ app.update
35
+
36
+ val = app.get_variable(Gemba::SettingsWindow::VAR_BIOS_PATH)
37
+ assert_equal '', val
38
+ end
39
+ end
40
+
41
+ def test_skip_bios_unchecked_initially
42
+ assert_tk_app("skip bios checkbox is unchecked initially") do
43
+ require "gemba/headless"
44
+ Gemba.bus = Gemba::EventBus.new
45
+ sw = Gemba::SettingsWindow.new(app)
46
+ sw.show
47
+ app.update
48
+
49
+ val = app.get_variable(Gemba::SettingsWindow::VAR_SKIP_BIOS)
50
+ assert_equal '0', val
51
+ end
52
+ end
53
+
54
+ # -- skip_bios checkbox marks dirty ----------------------------------------
55
+
56
+ def test_skip_bios_toggle_marks_save_dirty
57
+ assert_tk_app("toggling skip bios enables the save button") do
58
+ require "gemba/headless"
59
+ Gemba.bus = Gemba::EventBus.new
60
+ sw = Gemba::SettingsWindow.new(app)
61
+ sw.show
62
+
63
+ # Navigate to system tab
64
+ app.command(Gemba::SettingsWindow::NB, 'select', Gemba::SettingsWindow::SYSTEM_TAB)
65
+ app.update
66
+
67
+ app.command(Gemba::Settings::SystemTab::SKIP_BIOS_CHECK, 'invoke')
68
+ app.update
69
+
70
+ state = app.command(Gemba::SettingsWindow::SAVE_BTN, :cget, '-state').to_s
71
+ assert_equal 'normal', state
72
+ end
73
+ end
74
+
75
+ # -- clear button -----------------------------------------------------------
76
+
77
+ def test_clear_button_empties_bios_path
78
+ assert_tk_app("clear button empties the bios path variable") do
79
+ require "gemba/headless"
80
+ Gemba.bus = Gemba::EventBus.new
81
+ sw = Gemba::SettingsWindow.new(app)
82
+ sw.show
83
+ app.update
84
+
85
+ # Seed a value
86
+ app.set_variable(Gemba::SettingsWindow::VAR_BIOS_PATH, 'gba_bios.bin')
87
+ app.command(Gemba::Settings::SystemTab::BIOS_CLEAR, 'invoke')
88
+ app.update
89
+
90
+ assert_equal '', app.get_variable(Gemba::SettingsWindow::VAR_BIOS_PATH)
91
+ end
92
+ end
93
+
94
+ def test_clear_button_marks_save_dirty
95
+ assert_tk_app("clear button enables the save button") do
96
+ require "gemba/headless"
97
+ Gemba.bus = Gemba::EventBus.new
98
+ sw = Gemba::SettingsWindow.new(app)
99
+ sw.show
100
+ app.update
101
+
102
+ app.command(Gemba::Settings::SystemTab::BIOS_CLEAR, 'invoke')
103
+ app.update
104
+
105
+ state = app.command(Gemba::SettingsWindow::SAVE_BTN, :cget, '-state').to_s
106
+ assert_equal 'normal', state
107
+ end
108
+ end
109
+
110
+ def test_clear_resets_status_label
111
+ assert_tk_app("clear button resets status label to not-set text") do
112
+ require "gemba/headless"
113
+ Gemba::Locale.load('en')
114
+ Gemba.bus = Gemba::EventBus.new
115
+ sw = Gemba::SettingsWindow.new(app)
116
+ sw.show
117
+ app.update
118
+
119
+ app.command(Gemba::Settings::SystemTab::BIOS_CLEAR, 'invoke')
120
+ app.update
121
+
122
+ text = app.command(Gemba::Settings::SystemTab::BIOS_STATUS, :cget, '-text').to_s
123
+ assert_includes text, 'Not set'
124
+ end
125
+ end
126
+
127
+ # -- system tab is per-game eligible ----------------------------------------
128
+
129
+ def test_system_tab_in_per_game_tabs
130
+ require "gemba/headless"
131
+ assert Gemba::SettingsWindow::PER_GAME_TABS.include?(Gemba::SettingsWindow::SYSTEM_TAB)
132
+ end
133
+ end
@@ -0,0 +1,450 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require_relative "shared/tk_test_helper"
5
+
6
+ class TestSettingsVideoTab < Minitest::Test
7
+ include TeekTestHelper
8
+
9
+ def test_video_tab_exists
10
+ assert_tk_app("video tab exists in notebook") do
11
+ require "gemba/headless"
12
+ sw = Gemba::SettingsWindow.new(app)
13
+ sw.show
14
+ app.update
15
+
16
+ tabs = app.command(Gemba::Settings::Paths::NB, 'tabs')
17
+ assert_includes tabs, Gemba::Settings::VideoTab::FRAME
18
+ end
19
+ end
20
+
21
+ # -- Scale combobox --------------------------------------------------------
22
+
23
+ def test_scale_defaults_to_3x
24
+ assert_tk_app("scale combobox defaults to 3x") do
25
+ require "gemba/headless"
26
+ sw = Gemba::SettingsWindow.new(app)
27
+ sw.show
28
+ app.update
29
+
30
+ assert_equal '3x', app.get_variable(Gemba::Settings::VideoTab::VAR_SCALE)
31
+ end
32
+ end
33
+
34
+ def test_selecting_2x_scale_fires_callback
35
+ assert_tk_app("selecting 2x scale fires on_scale_change") do
36
+ require "gemba/headless"
37
+ received = nil
38
+ Gemba.bus = Gemba::EventBus.new
39
+ Gemba.bus.on(:scale_changed) { |s| received = s }
40
+ sw = Gemba::SettingsWindow.new(app)
41
+ sw.show
42
+ app.update
43
+
44
+ app.set_variable(Gemba::Settings::VideoTab::VAR_SCALE, '2x')
45
+ app.command(:event, 'generate', Gemba::Settings::VideoTab::SCALE_COMBO, '<<ComboboxSelected>>')
46
+ app.update
47
+
48
+ assert_equal 2, received
49
+ end
50
+ end
51
+
52
+ def test_selecting_4x_scale_fires_callback
53
+ assert_tk_app("selecting 4x scale fires on_scale_change") do
54
+ require "gemba/headless"
55
+ received = nil
56
+ Gemba.bus = Gemba::EventBus.new
57
+ Gemba.bus.on(:scale_changed) { |s| received = s }
58
+ sw = Gemba::SettingsWindow.new(app)
59
+ sw.show
60
+ app.update
61
+
62
+ app.set_variable(Gemba::Settings::VideoTab::VAR_SCALE, '4x')
63
+ app.command(:event, 'generate', Gemba::Settings::VideoTab::SCALE_COMBO, '<<ComboboxSelected>>')
64
+ app.update
65
+
66
+ assert_equal 4, received
67
+ end
68
+ end
69
+
70
+ # -- Turbo speed combobox --------------------------------------------------
71
+
72
+ def test_turbo_defaults_to_2x
73
+ assert_tk_app("turbo speed defaults to 2x") do
74
+ require "gemba/headless"
75
+ sw = Gemba::SettingsWindow.new(app)
76
+ sw.show
77
+ app.update
78
+
79
+ assert_equal '2x', app.get_variable(Gemba::Settings::VideoTab::VAR_TURBO)
80
+ end
81
+ end
82
+
83
+ def test_selecting_4x_turbo_fires_callback
84
+ assert_tk_app("selecting 4x turbo fires on_turbo_speed_change") do
85
+ require "gemba/headless"
86
+ received = nil
87
+ Gemba.bus = Gemba::EventBus.new
88
+ Gemba.bus.on(:turbo_speed_changed) { |s| received = s }
89
+ sw = Gemba::SettingsWindow.new(app)
90
+ sw.show
91
+ app.update
92
+
93
+ app.set_variable(Gemba::Settings::VideoTab::VAR_TURBO, '4x')
94
+ app.command(:event, 'generate', Gemba::Settings::VideoTab::TURBO_COMBO, '<<ComboboxSelected>>')
95
+ app.update
96
+
97
+ assert_equal 4, received
98
+ end
99
+ end
100
+
101
+ # -- Aspect ratio checkbox -------------------------------------------------
102
+
103
+ def test_aspect_ratio_defaults_to_on
104
+ assert_tk_app("aspect ratio checkbox defaults to on") do
105
+ require "gemba/headless"
106
+ sw = Gemba::SettingsWindow.new(app)
107
+ sw.show
108
+ app.update
109
+
110
+ assert_equal '1', app.get_variable(Gemba::Settings::VideoTab::VAR_ASPECT_RATIO)
111
+ end
112
+ end
113
+
114
+ def test_unchecking_aspect_ratio_fires_callback
115
+ assert_tk_app("unchecking aspect ratio fires on_aspect_ratio_change") do
116
+ require "gemba/headless"
117
+ received = nil
118
+ Gemba.bus = Gemba::EventBus.new
119
+ Gemba.bus.on(:aspect_ratio_changed) { |keep| received = keep }
120
+ sw = Gemba::SettingsWindow.new(app)
121
+ sw.show
122
+ app.update
123
+
124
+ app.command(Gemba::Settings::VideoTab::ASPECT_CHECK, 'invoke')
125
+ app.update
126
+
127
+ assert_equal false, received
128
+ end
129
+ end
130
+
131
+ def test_checking_aspect_ratio_fires_callback
132
+ assert_tk_app("re-checking aspect ratio fires callback with true") do
133
+ require "gemba/headless"
134
+ received = nil
135
+ Gemba.bus = Gemba::EventBus.new
136
+ Gemba.bus.on(:aspect_ratio_changed) { |keep| received = keep }
137
+ sw = Gemba::SettingsWindow.new(app)
138
+ sw.show
139
+ app.update
140
+
141
+ app.command(Gemba::Settings::VideoTab::ASPECT_CHECK, 'invoke')
142
+ app.update
143
+ app.command(Gemba::Settings::VideoTab::ASPECT_CHECK, 'invoke')
144
+ app.update
145
+
146
+ assert_equal true, received
147
+ end
148
+ end
149
+
150
+ # -- Show FPS checkbox -----------------------------------------------------
151
+
152
+ def test_show_fps_defaults_to_on
153
+ assert_tk_app("show fps checkbox defaults to on") do
154
+ require "gemba/headless"
155
+ sw = Gemba::SettingsWindow.new(app)
156
+ sw.show
157
+ app.update
158
+
159
+ assert_equal '1', app.get_variable(Gemba::Settings::VideoTab::VAR_SHOW_FPS)
160
+ end
161
+ end
162
+
163
+ def test_unchecking_show_fps_fires_callback
164
+ assert_tk_app("unchecking show fps fires on_show_fps_change") do
165
+ require "gemba/headless"
166
+ received = nil
167
+ Gemba.bus = Gemba::EventBus.new
168
+ Gemba.bus.on(:show_fps_changed) { |show| received = show }
169
+ sw = Gemba::SettingsWindow.new(app)
170
+ sw.show
171
+ app.update
172
+
173
+ app.command(Gemba::Settings::VideoTab::SHOW_FPS_CHECK, 'invoke')
174
+ app.update
175
+
176
+ assert_equal false, received
177
+ end
178
+ end
179
+
180
+ # -- Pause on focus loss checkbox ------------------------------------------
181
+
182
+ def test_pause_focus_defaults_to_on
183
+ assert_tk_app("pause on focus loss defaults to on") do
184
+ require "gemba/headless"
185
+ sw = Gemba::SettingsWindow.new(app)
186
+ sw.show
187
+ app.update
188
+
189
+ assert_equal '1', app.get_variable(Gemba::Settings::VideoTab::VAR_PAUSE_FOCUS)
190
+ end
191
+ end
192
+
193
+ def test_unchecking_pause_focus_fires_callback
194
+ assert_tk_app("unchecking pause on focus loss fires callback") do
195
+ require "gemba/headless"
196
+ received = nil
197
+ Gemba.bus = Gemba::EventBus.new
198
+ Gemba.bus.on(:pause_on_focus_loss_changed) { |v| received = v }
199
+ sw = Gemba::SettingsWindow.new(app)
200
+ sw.show
201
+ app.update
202
+
203
+ # The pause focus checkbox path
204
+ pause_check = "#{Gemba::Settings::VideoTab::FRAME}.pause_focus_row.check"
205
+ app.command(pause_check, 'invoke')
206
+ app.update
207
+
208
+ assert_equal false, received
209
+ end
210
+ end
211
+
212
+ # -- Toast duration combobox -----------------------------------------------
213
+
214
+ def test_toast_duration_defaults_to_1_5s
215
+ assert_tk_app("toast duration defaults to 1.5s") do
216
+ require "gemba/headless"
217
+ sw = Gemba::SettingsWindow.new(app)
218
+ sw.show
219
+ app.update
220
+
221
+ assert_equal '1.5s', app.get_variable(Gemba::Settings::VideoTab::VAR_TOAST_DURATION)
222
+ end
223
+ end
224
+
225
+ def test_selecting_3s_toast_fires_callback
226
+ assert_tk_app("selecting 3s toast fires on_toast_duration_change") do
227
+ require "gemba/headless"
228
+ received = nil
229
+ Gemba.bus = Gemba::EventBus.new
230
+ Gemba.bus.on(:toast_duration_changed) { |s| received = s }
231
+ sw = Gemba::SettingsWindow.new(app)
232
+ sw.show
233
+ app.update
234
+
235
+ app.set_variable(Gemba::Settings::VideoTab::VAR_TOAST_DURATION, '3s')
236
+ app.command(:event, 'generate', Gemba::Settings::VideoTab::TOAST_COMBO, '<<ComboboxSelected>>')
237
+ app.update
238
+
239
+ assert_in_delta 3.0, received
240
+ end
241
+ end
242
+
243
+ # -- Pixel filter combobox -------------------------------------------------
244
+
245
+ def test_pixel_filter_defaults_to_nearest
246
+ assert_tk_app("pixel filter defaults to Nearest Neighbor") do
247
+ require "gemba/headless"
248
+ sw = Gemba::SettingsWindow.new(app)
249
+ sw.show
250
+ app.update
251
+
252
+ assert_equal 'Nearest Neighbor', app.get_variable(Gemba::Settings::VideoTab::VAR_FILTER)
253
+ end
254
+ end
255
+
256
+ def test_selecting_bilinear_fires_callback
257
+ assert_tk_app("selecting Bilinear fires on_filter_change") do
258
+ require "gemba/headless"
259
+ received = nil
260
+ Gemba.bus = Gemba::EventBus.new
261
+ Gemba.bus.on(:filter_changed) { |f| received = f }
262
+ sw = Gemba::SettingsWindow.new(app)
263
+ sw.show
264
+ app.update
265
+
266
+ app.set_variable(Gemba::Settings::VideoTab::VAR_FILTER, 'Bilinear')
267
+ app.command(:event, 'generate', Gemba::Settings::VideoTab::FILTER_COMBO, '<<ComboboxSelected>>')
268
+ app.update
269
+
270
+ assert_equal 'linear', received
271
+ end
272
+ end
273
+
274
+ # -- Integer scaling checkbox ----------------------------------------------
275
+
276
+ def test_integer_scale_defaults_to_off
277
+ assert_tk_app("integer scale checkbox defaults to off") do
278
+ require "gemba/headless"
279
+ sw = Gemba::SettingsWindow.new(app)
280
+ sw.show
281
+ app.update
282
+
283
+ assert_equal '0', app.get_variable(Gemba::Settings::VideoTab::VAR_INTEGER_SCALE)
284
+ end
285
+ end
286
+
287
+ def test_clicking_integer_scale_fires_callback
288
+ assert_tk_app("clicking integer scale fires on_integer_scale_change") do
289
+ require "gemba/headless"
290
+ received = nil
291
+ Gemba.bus = Gemba::EventBus.new
292
+ Gemba.bus.on(:integer_scale_changed) { |v| received = v }
293
+ sw = Gemba::SettingsWindow.new(app)
294
+ sw.show
295
+ app.update
296
+
297
+ app.command(Gemba::Settings::VideoTab::INTEGER_SCALE_CHECK, 'invoke')
298
+ app.update
299
+
300
+ assert_equal true, received
301
+ end
302
+ end
303
+
304
+ # -- Color correction checkbox ---------------------------------------------
305
+
306
+ def test_color_correction_defaults_to_off
307
+ assert_tk_app("color correction checkbox defaults to off") do
308
+ require "gemba/headless"
309
+ sw = Gemba::SettingsWindow.new(app)
310
+ sw.show
311
+ app.update
312
+
313
+ assert_equal '0', app.get_variable(Gemba::Settings::VideoTab::VAR_COLOR_CORRECTION)
314
+ end
315
+ end
316
+
317
+ def test_clicking_color_correction_fires_callback
318
+ assert_tk_app("clicking color correction fires on_color_correction_change") do
319
+ require "gemba/headless"
320
+ received = nil
321
+ Gemba.bus = Gemba::EventBus.new
322
+ Gemba.bus.on(:color_correction_changed) { |v| received = v }
323
+ sw = Gemba::SettingsWindow.new(app)
324
+ sw.show
325
+ app.update
326
+
327
+ app.command(Gemba::Settings::VideoTab::COLOR_CORRECTION_CHECK, 'invoke')
328
+ app.update
329
+
330
+ assert_equal true, received
331
+ end
332
+ end
333
+
334
+ def test_unchecking_color_correction_fires_false
335
+ assert_tk_app("unchecking color correction fires callback with false") do
336
+ require "gemba/headless"
337
+ received = nil
338
+ Gemba.bus = Gemba::EventBus.new
339
+ Gemba.bus.on(:color_correction_changed) { |v| received = v }
340
+ sw = Gemba::SettingsWindow.new(app)
341
+ sw.show
342
+ app.update
343
+
344
+ app.command(Gemba::Settings::VideoTab::COLOR_CORRECTION_CHECK, 'invoke')
345
+ app.update
346
+ app.command(Gemba::Settings::VideoTab::COLOR_CORRECTION_CHECK, 'invoke')
347
+ app.update
348
+
349
+ assert_equal false, received
350
+ end
351
+ end
352
+
353
+ # -- Frame blending checkbox -----------------------------------------------
354
+
355
+ def test_frame_blending_defaults_to_off
356
+ assert_tk_app("frame blending checkbox defaults to off") do
357
+ require "gemba/headless"
358
+ sw = Gemba::SettingsWindow.new(app)
359
+ sw.show
360
+ app.update
361
+
362
+ assert_equal '0', app.get_variable(Gemba::Settings::VideoTab::VAR_FRAME_BLENDING)
363
+ end
364
+ end
365
+
366
+ def test_clicking_frame_blending_fires_callback
367
+ assert_tk_app("clicking frame blending fires on_frame_blending_change") do
368
+ require "gemba/headless"
369
+ received = nil
370
+ Gemba.bus = Gemba::EventBus.new
371
+ Gemba.bus.on(:frame_blending_changed) { |v| received = v }
372
+ sw = Gemba::SettingsWindow.new(app)
373
+ sw.show
374
+ app.update
375
+
376
+ app.command(Gemba::Settings::VideoTab::FRAME_BLENDING_CHECK, 'invoke')
377
+ app.update
378
+
379
+ assert_equal true, received
380
+ end
381
+ end
382
+
383
+ def test_unchecking_frame_blending_fires_false
384
+ assert_tk_app("unchecking frame blending fires callback with false") do
385
+ require "gemba/headless"
386
+ received = nil
387
+ Gemba.bus = Gemba::EventBus.new
388
+ Gemba.bus.on(:frame_blending_changed) { |v| received = v }
389
+ sw = Gemba::SettingsWindow.new(app)
390
+ sw.show
391
+ app.update
392
+
393
+ app.command(Gemba::Settings::VideoTab::FRAME_BLENDING_CHECK, 'invoke')
394
+ app.update
395
+ app.command(Gemba::Settings::VideoTab::FRAME_BLENDING_CHECK, 'invoke')
396
+ app.update
397
+
398
+ assert_equal false, received
399
+ end
400
+ end
401
+
402
+ # -- Rewind checkbox -------------------------------------------------------
403
+
404
+ def test_rewind_defaults_to_on
405
+ assert_tk_app("rewind checkbox defaults to on") do
406
+ require "gemba/headless"
407
+ sw = Gemba::SettingsWindow.new(app)
408
+ sw.show
409
+ app.update
410
+
411
+ assert_equal '1', app.get_variable(Gemba::Settings::VideoTab::VAR_REWIND_ENABLED)
412
+ end
413
+ end
414
+
415
+ def test_clicking_rewind_fires_callback
416
+ assert_tk_app("clicking rewind fires on_rewind_toggle") do
417
+ require "gemba/headless"
418
+ received = nil
419
+ Gemba.bus = Gemba::EventBus.new
420
+ Gemba.bus.on(:rewind_toggled) { |v| received = v }
421
+ sw = Gemba::SettingsWindow.new(app)
422
+ sw.show
423
+ app.update
424
+
425
+ app.command(Gemba::Settings::VideoTab::REWIND_CHECK, 'invoke')
426
+ app.update
427
+
428
+ assert_equal false, received
429
+ end
430
+ end
431
+
432
+ def test_rechecking_rewind_fires_true
433
+ assert_tk_app("re-checking rewind fires callback with true") do
434
+ require "gemba/headless"
435
+ received = nil
436
+ Gemba.bus = Gemba::EventBus.new
437
+ Gemba.bus.on(:rewind_toggled) { |v| received = v }
438
+ sw = Gemba::SettingsWindow.new(app)
439
+ sw.show
440
+ app.update
441
+
442
+ app.command(Gemba::Settings::VideoTab::REWIND_CHECK, 'invoke')
443
+ app.update
444
+ app.command(Gemba::Settings::VideoTab::REWIND_CHECK, 'invoke')
445
+ app.update
446
+
447
+ assert_equal true, received
448
+ end
449
+ end
450
+ end