gemba 0.1.1 → 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.
- checksums.yaml +4 -4
- data/THIRD_PARTY_NOTICES +37 -2
- data/assets/placeholder_boxart.png +0 -0
- data/bin/gemba +2 -2
- data/ext/gemba/extconf.rb +23 -1
- data/ext/gemba/gemba_ext.c +436 -2
- data/ext/gemba/gemba_ext.h +2 -0
- data/gemba.gemspec +5 -3
- data/lib/gemba/achievements/achievement.rb +23 -0
- data/lib/gemba/achievements/backend.rb +186 -0
- data/lib/gemba/achievements/cache.rb +70 -0
- data/lib/gemba/achievements/credentials_presenter.rb +142 -0
- data/lib/gemba/achievements/fake_backend.rb +205 -0
- data/lib/gemba/achievements/null_backend.rb +11 -0
- data/lib/gemba/achievements/offline_backend.rb +168 -0
- data/lib/gemba/achievements/retro_achievements/backend.rb +453 -0
- data/lib/gemba/achievements/retro_achievements/cli_sync_requester.rb +64 -0
- data/lib/gemba/achievements/retro_achievements/ping_worker.rb +27 -0
- data/lib/gemba/achievements.rb +19 -0
- data/lib/gemba/achievements_window.rb +556 -0
- data/lib/gemba/app_controller.rb +1015 -0
- data/lib/gemba/bios.rb +54 -0
- data/lib/gemba/boxart_fetcher/libretro_backend.rb +39 -0
- data/lib/gemba/boxart_fetcher/null_backend.rb +12 -0
- data/lib/gemba/boxart_fetcher.rb +79 -0
- data/lib/gemba/bus_emitter.rb +13 -0
- data/lib/gemba/child_window.rb +24 -1
- data/lib/gemba/cli/commands/config_cmd.rb +83 -0
- data/lib/gemba/cli/commands/decode.rb +154 -0
- data/lib/gemba/cli/commands/patch.rb +78 -0
- data/lib/gemba/cli/commands/play.rb +78 -0
- data/lib/gemba/cli/commands/record.rb +114 -0
- data/lib/gemba/cli/commands/replay.rb +161 -0
- data/lib/gemba/cli/commands/retro_achievements.rb +213 -0
- data/lib/gemba/cli/commands/version.rb +22 -0
- data/lib/gemba/cli.rb +52 -364
- data/lib/gemba/config.rb +134 -1
- data/lib/gemba/data/gb_games.json +1 -0
- data/lib/gemba/data/gb_md5.json +1 -0
- data/lib/gemba/data/gba_games.json +1 -0
- data/lib/gemba/data/gba_md5.json +1 -0
- data/lib/gemba/data/gbc_games.json +1 -0
- data/lib/gemba/data/gbc_md5.json +1 -0
- data/lib/gemba/emulator_frame.rb +1060 -0
- data/lib/gemba/event_bus.rb +48 -0
- data/lib/gemba/frame_stack.rb +60 -0
- data/lib/gemba/game_index.rb +84 -0
- data/lib/gemba/game_picker_frame.rb +268 -0
- data/lib/gemba/gamepad_map.rb +103 -0
- data/lib/gemba/headless.rb +6 -5
- data/lib/gemba/headless_player.rb +33 -3
- data/lib/gemba/help_window.rb +61 -0
- data/lib/gemba/hotkey_map.rb +3 -1
- data/lib/gemba/input_recorder.rb +107 -0
- data/lib/gemba/input_replayer.rb +119 -0
- data/lib/gemba/keyboard_map.rb +90 -0
- data/lib/gemba/locales/en.yml +97 -5
- data/lib/gemba/locales/ja.yml +97 -5
- data/lib/gemba/main_window.rb +56 -0
- data/lib/gemba/modal_stack.rb +81 -0
- data/lib/gemba/patcher_window.rb +223 -0
- data/lib/gemba/platform/gb.rb +21 -0
- data/lib/gemba/platform/gba.rb +21 -0
- data/lib/gemba/platform/gbc.rb +23 -0
- data/lib/gemba/platform.rb +20 -0
- data/lib/gemba/platform_open.rb +19 -0
- data/lib/gemba/recorder.rb +4 -3
- data/lib/gemba/replay_player.rb +691 -0
- data/lib/gemba/rom_info.rb +57 -0
- data/lib/gemba/rom_info_window.rb +16 -3
- data/lib/gemba/rom_library.rb +106 -0
- data/lib/gemba/rom_overrides.rb +47 -0
- data/lib/gemba/rom_patcher/bps.rb +161 -0
- data/lib/gemba/rom_patcher/ips.rb +101 -0
- data/lib/gemba/rom_patcher/ups.rb +118 -0
- data/lib/gemba/rom_patcher.rb +109 -0
- data/lib/gemba/{rom_loader.rb → rom_resolver.rb} +7 -6
- data/lib/gemba/runtime.rb +59 -26
- data/lib/gemba/save_state_manager.rb +4 -7
- data/lib/gemba/save_state_picker.rb +17 -4
- data/lib/gemba/session_logger.rb +64 -0
- data/lib/gemba/settings/audio_tab.rb +77 -0
- data/lib/gemba/settings/gamepad_tab.rb +351 -0
- data/lib/gemba/settings/hotkeys_tab.rb +259 -0
- data/lib/gemba/settings/paths.rb +11 -0
- data/lib/gemba/settings/recording_tab.rb +83 -0
- data/lib/gemba/settings/save_states_tab.rb +91 -0
- data/lib/gemba/settings/system_tab.rb +362 -0
- data/lib/gemba/settings/video_tab.rb +318 -0
- data/lib/gemba/settings_window.rb +162 -1036
- data/lib/gemba/version.rb +1 -1
- data/lib/gemba/virtual_keyboard.rb +19 -0
- data/lib/gemba.rb +2 -12
- data/test/achievements_window/test_bulk_sync.rb +218 -0
- data/test/achievements_window/test_bus_events.rb +125 -0
- data/test/achievements_window/test_close_confirmation.rb +201 -0
- data/test/achievements_window/test_initial_state.rb +164 -0
- data/test/achievements_window/test_sorting.rb +227 -0
- data/test/achievements_window/test_tree_rendering.rb +133 -0
- data/test/fixtures/fake_bios.bin +0 -0
- data/test/fixtures/pong.gba +0 -0
- data/test/fixtures/test.gb +0 -0
- data/test/fixtures/test.gbc +0 -0
- data/test/fixtures/test_quicksave.ss +0 -0
- data/test/screenshots/no_focus.png +0 -0
- data/test/shared/teek_test_worker.rb +17 -1
- data/test/shared/tk_test_helper.rb +91 -4
- data/test/support/achievements_window_helpers.rb +18 -0
- data/test/support/fake_core.rb +25 -0
- data/test/support/fake_ra_runtime.rb +74 -0
- data/test/support/fake_requester.rb +68 -0
- data/test/support/player_helpers.rb +20 -5
- data/test/test_achievement.rb +32 -0
- data/test/{test_player.rb → test_app_controller.rb} +353 -85
- data/test/test_bios.rb +123 -0
- data/test/test_boxart_fetcher.rb +150 -0
- data/test/test_cli.rb +17 -265
- data/test/test_cli_config.rb +64 -0
- data/test/test_cli_decode.rb +97 -0
- data/test/test_cli_patch.rb +58 -0
- data/test/test_cli_play.rb +213 -0
- data/test/test_cli_ra.rb +175 -0
- data/test/test_cli_record.rb +69 -0
- data/test/test_cli_replay.rb +72 -0
- data/test/test_cli_sync_requester.rb +152 -0
- data/test/test_cli_version.rb +27 -0
- data/test/test_config.rb +2 -3
- data/test/test_config_ra.rb +69 -0
- data/test/test_core.rb +62 -1
- data/test/test_credentials_presenter.rb +192 -0
- data/test/test_event_bus.rb +100 -0
- data/test/test_fake_backend_achievements.rb +130 -0
- data/test/test_fake_backend_auth.rb +68 -0
- data/test/test_game_index.rb +77 -0
- data/test/test_game_picker_frame.rb +310 -0
- data/test/test_gamepad_map.rb +1 -3
- data/test/test_headless_player.rb +17 -3
- data/test/test_help_window.rb +82 -0
- data/test/test_hotkey_map.rb +22 -1
- data/test/test_input_recorder.rb +179 -0
- data/test/test_input_replay_determinism.rb +113 -0
- data/test/test_input_replayer.rb +162 -0
- data/test/test_keyboard_map.rb +1 -3
- data/test/test_libretro_backend.rb +41 -0
- data/test/test_locale.rb +1 -1
- data/test/test_logging.rb +123 -0
- data/test/test_null_backend.rb +42 -0
- data/test/test_offline_backend.rb +116 -0
- data/test/test_overlay_renderer.rb +1 -1
- data/test/test_platform.rb +149 -0
- data/test/test_ra_backend.rb +313 -0
- data/test/test_ra_backend_unlock_gate.rb +56 -0
- data/test/test_recorder.rb +0 -3
- data/test/test_replay_player.rb +316 -0
- data/test/test_rom_info.rb +149 -0
- data/test/test_rom_overrides.rb +86 -0
- data/test/test_rom_patcher.rb +382 -0
- data/test/{test_rom_loader.rb → test_rom_resolver.rb} +25 -26
- data/test/test_save_state_manager.rb +2 -4
- data/test/test_settings_audio.rb +107 -0
- data/test/test_settings_hotkeys.rb +83 -66
- data/test/test_settings_recording.rb +49 -0
- data/test/test_settings_save_states.rb +97 -0
- data/test/test_settings_system.rb +133 -0
- data/test/test_settings_video.rb +450 -0
- data/test/test_settings_window.rb +76 -507
- data/test/test_tip_service.rb +6 -6
- data/test/test_toast_overlay.rb +1 -1
- data/test/test_virtual_events.rb +156 -0
- data/test/test_virtual_keyboard.rb +1 -1
- data/vendor/rcheevos/CHANGELOG.md +495 -0
- data/vendor/rcheevos/LICENSE +21 -0
- data/vendor/rcheevos/Package.swift +33 -0
- data/vendor/rcheevos/README.md +67 -0
- data/vendor/rcheevos/include/module.modulemap +70 -0
- data/vendor/rcheevos/include/rc_api_editor.h +296 -0
- data/vendor/rcheevos/include/rc_api_info.h +280 -0
- data/vendor/rcheevos/include/rc_api_request.h +77 -0
- data/vendor/rcheevos/include/rc_api_runtime.h +417 -0
- data/vendor/rcheevos/include/rc_api_user.h +262 -0
- data/vendor/rcheevos/include/rc_client.h +877 -0
- data/vendor/rcheevos/include/rc_client_raintegration.h +101 -0
- data/vendor/rcheevos/include/rc_consoles.h +138 -0
- data/vendor/rcheevos/include/rc_error.h +59 -0
- data/vendor/rcheevos/include/rc_export.h +100 -0
- data/vendor/rcheevos/include/rc_hash.h +200 -0
- data/vendor/rcheevos/include/rc_runtime.h +148 -0
- data/vendor/rcheevos/include/rc_runtime_types.h +452 -0
- data/vendor/rcheevos/include/rc_util.h +51 -0
- data/vendor/rcheevos/include/rcheevos.h +8 -0
- data/vendor/rcheevos/src/rapi/rc_api_common.c +1379 -0
- data/vendor/rcheevos/src/rapi/rc_api_common.h +88 -0
- data/vendor/rcheevos/src/rapi/rc_api_editor.c +625 -0
- data/vendor/rcheevos/src/rapi/rc_api_info.c +587 -0
- data/vendor/rcheevos/src/rapi/rc_api_runtime.c +901 -0
- data/vendor/rcheevos/src/rapi/rc_api_user.c +483 -0
- data/vendor/rcheevos/src/rc_client.c +6941 -0
- data/vendor/rcheevos/src/rc_client_external.c +281 -0
- data/vendor/rcheevos/src/rc_client_external.h +177 -0
- data/vendor/rcheevos/src/rc_client_external_versions.h +171 -0
- data/vendor/rcheevos/src/rc_client_internal.h +409 -0
- data/vendor/rcheevos/src/rc_client_raintegration.c +566 -0
- data/vendor/rcheevos/src/rc_client_raintegration_internal.h +61 -0
- data/vendor/rcheevos/src/rc_client_types.natvis +396 -0
- data/vendor/rcheevos/src/rc_compat.c +251 -0
- data/vendor/rcheevos/src/rc_compat.h +121 -0
- data/vendor/rcheevos/src/rc_libretro.c +915 -0
- data/vendor/rcheevos/src/rc_libretro.h +98 -0
- data/vendor/rcheevos/src/rc_util.c +199 -0
- data/vendor/rcheevos/src/rc_version.c +11 -0
- data/vendor/rcheevos/src/rc_version.h +32 -0
- data/vendor/rcheevos/src/rcheevos/alloc.c +312 -0
- data/vendor/rcheevos/src/rcheevos/condition.c +754 -0
- data/vendor/rcheevos/src/rcheevos/condset.c +777 -0
- data/vendor/rcheevos/src/rcheevos/consoleinfo.c +1215 -0
- data/vendor/rcheevos/src/rcheevos/format.c +330 -0
- data/vendor/rcheevos/src/rcheevos/lboard.c +287 -0
- data/vendor/rcheevos/src/rcheevos/memref.c +805 -0
- data/vendor/rcheevos/src/rcheevos/operand.c +607 -0
- data/vendor/rcheevos/src/rcheevos/rc_internal.h +390 -0
- data/vendor/rcheevos/src/rcheevos/rc_runtime_types.natvis +541 -0
- data/vendor/rcheevos/src/rcheevos/rc_validate.c +1406 -0
- data/vendor/rcheevos/src/rcheevos/rc_validate.h +18 -0
- data/vendor/rcheevos/src/rcheevos/richpresence.c +922 -0
- data/vendor/rcheevos/src/rcheevos/runtime.c +852 -0
- data/vendor/rcheevos/src/rcheevos/runtime_progress.c +1073 -0
- data/vendor/rcheevos/src/rcheevos/trigger.c +344 -0
- data/vendor/rcheevos/src/rcheevos/value.c +935 -0
- data/vendor/rcheevos/src/rhash/aes.c +480 -0
- data/vendor/rcheevos/src/rhash/aes.h +49 -0
- data/vendor/rcheevos/src/rhash/cdreader.c +838 -0
- data/vendor/rcheevos/src/rhash/hash.c +1402 -0
- data/vendor/rcheevos/src/rhash/hash_disc.c +1340 -0
- data/vendor/rcheevos/src/rhash/hash_encrypted.c +566 -0
- data/vendor/rcheevos/src/rhash/hash_rom.c +426 -0
- data/vendor/rcheevos/src/rhash/hash_zip.c +460 -0
- data/vendor/rcheevos/src/rhash/md5.c +382 -0
- data/vendor/rcheevos/src/rhash/md5.h +91 -0
- data/vendor/rcheevos/src/rhash/rc_hash_internal.h +116 -0
- data/vendor/rcheevos/test/libretro.h +205 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_common.c +941 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_editor.c +931 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_info.c +545 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_runtime.c +2213 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_user.c +998 -0
- data/vendor/rcheevos/test/rcheevos/mock_memory.h +32 -0
- data/vendor/rcheevos/test/rcheevos/test_condition.c +570 -0
- data/vendor/rcheevos/test/rcheevos/test_condset.c +5170 -0
- data/vendor/rcheevos/test/rcheevos/test_consoleinfo.c +203 -0
- data/vendor/rcheevos/test/rcheevos/test_format.c +112 -0
- data/vendor/rcheevos/test/rcheevos/test_lboard.c +746 -0
- data/vendor/rcheevos/test/rcheevos/test_memref.c +520 -0
- data/vendor/rcheevos/test/rcheevos/test_operand.c +692 -0
- data/vendor/rcheevos/test/rcheevos/test_rc_validate.c +502 -0
- data/vendor/rcheevos/test/rcheevos/test_richpresence.c +1564 -0
- data/vendor/rcheevos/test/rcheevos/test_runtime.c +1667 -0
- data/vendor/rcheevos/test/rcheevos/test_runtime_progress.c +1821 -0
- data/vendor/rcheevos/test/rcheevos/test_timing.c +166 -0
- data/vendor/rcheevos/test/rcheevos/test_trigger.c +2521 -0
- data/vendor/rcheevos/test/rcheevos/test_value.c +870 -0
- data/vendor/rcheevos/test/rcheevos-test.sln +46 -0
- data/vendor/rcheevos/test/rcheevos-test.vcxproj +239 -0
- data/vendor/rcheevos/test/rcheevos-test.vcxproj.filters +335 -0
- data/vendor/rcheevos/test/rhash/data.c +657 -0
- data/vendor/rcheevos/test/rhash/data.h +32 -0
- data/vendor/rcheevos/test/rhash/mock_filereader.c +236 -0
- data/vendor/rcheevos/test/rhash/mock_filereader.h +31 -0
- data/vendor/rcheevos/test/rhash/test_cdreader.c +920 -0
- data/vendor/rcheevos/test/rhash/test_hash.c +310 -0
- data/vendor/rcheevos/test/rhash/test_hash_disc.c +1450 -0
- data/vendor/rcheevos/test/rhash/test_hash_rom.c +899 -0
- data/vendor/rcheevos/test/rhash/test_hash_zip.c +551 -0
- data/vendor/rcheevos/test/test.c +113 -0
- data/vendor/rcheevos/test/test_framework.h +205 -0
- data/vendor/rcheevos/test/test_rc_client.c +10509 -0
- data/vendor/rcheevos/test/test_rc_client_external.c +2197 -0
- data/vendor/rcheevos/test/test_rc_client_raintegration.c +441 -0
- data/vendor/rcheevos/test/test_rc_libretro.c +952 -0
- data/vendor/rcheevos/test/test_types.natvis +9 -0
- data/vendor/rcheevos/validator/validator.c +658 -0
- data/vendor/rcheevos/validator/validator.vcxproj +152 -0
- data/vendor/rcheevos/validator/validator.vcxproj.filters +82 -0
- metadata +274 -11
- data/lib/gemba/input_mappings.rb +0 -214
- data/lib/gemba/player.rb +0 -1525
|
@@ -0,0 +1,2197 @@
|
|
|
1
|
+
#include "rc_client.h"
|
|
2
|
+
|
|
3
|
+
#include "../src/rc_client_external_versions.h"
|
|
4
|
+
#include "../src/rc_client_internal.h"
|
|
5
|
+
#include "../src/rc_version.h"
|
|
6
|
+
#include "rc_consoles.h"
|
|
7
|
+
#include "rhash/data.h"
|
|
8
|
+
|
|
9
|
+
#include "test_framework.h"
|
|
10
|
+
|
|
11
|
+
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
|
12
|
+
|
|
13
|
+
static rc_client_t* g_client;
|
|
14
|
+
static const char* g_external_event;
|
|
15
|
+
static int g_external_int = 0;
|
|
16
|
+
static void* g_callback_userdata = &g_client; /* dummy object to use for callback userdata validation */
|
|
17
|
+
|
|
18
|
+
/* begin from test_rc_client.c */
|
|
19
|
+
|
|
20
|
+
extern void rc_client_server_call(const rc_api_request_t* request, rc_client_server_callback_t callback, void* callback_data, rc_client_t* client);
|
|
21
|
+
extern void reset_mock_api_handlers(void);
|
|
22
|
+
extern void mock_api_response(const char* request_params, const char* response_body);
|
|
23
|
+
extern void mock_api_error(const char* request_params, const char* response_body, int http_status_code);
|
|
24
|
+
|
|
25
|
+
/* end from test_rc_client.c */
|
|
26
|
+
|
|
27
|
+
#define RC_OFFSETOF(s,f) ((uint32_t)((uint8_t*)&(((s*)(0))->f) - ((uint8_t*)0)))
|
|
28
|
+
|
|
29
|
+
#define ASSERT_FIELD_OFFSET(struct1, struct2, field) { \
|
|
30
|
+
const uint32_t __o1 = RC_OFFSETOF(struct1, field); \
|
|
31
|
+
const uint32_t __o2 = RC_OFFSETOF(struct2, field); \
|
|
32
|
+
if (__o1 != __o2) { \
|
|
33
|
+
ASSERT_FAIL("Expected: " #struct1 "." #field " at offset %u (%s:%d)\n Found: " #struct2 "." #field " at offset %u", \
|
|
34
|
+
__o1, test_framework_basename(__FILE__), __LINE__, __o2); \
|
|
35
|
+
} \
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static uint32_t rc_client_read_memory(uint32_t address, uint8_t* buffer, uint32_t num_bytes, rc_client_t* client)
|
|
39
|
+
{
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static rc_client_t* mock_client_with_external()
|
|
44
|
+
{
|
|
45
|
+
rc_client_t* client = rc_client_create(rc_client_read_memory, rc_client_server_call);
|
|
46
|
+
client->state.external_client = (rc_client_external_t*)
|
|
47
|
+
rc_buffer_alloc(&client->state.buffer, sizeof(*client->state.external_client));
|
|
48
|
+
memset(client->state.external_client, 0, sizeof(*client->state.external_client));
|
|
49
|
+
|
|
50
|
+
rc_api_set_host(NULL);
|
|
51
|
+
reset_mock_api_handlers();
|
|
52
|
+
g_external_event = "none";
|
|
53
|
+
g_external_int = 0;
|
|
54
|
+
|
|
55
|
+
return client;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static void rc_client_callback_expect_success(int result, const char* error_message, rc_client_t* client, void* callback_userdata)
|
|
59
|
+
{
|
|
60
|
+
ASSERT_NUM_EQUALS(result, RC_OK);
|
|
61
|
+
ASSERT_PTR_NULL(error_message);
|
|
62
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
63
|
+
ASSERT_PTR_EQUALS(callback_userdata, g_callback_userdata);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static void rc_client_external_unload_game(void)
|
|
67
|
+
{
|
|
68
|
+
g_external_event = "unload_game";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* ----- settings ----- */
|
|
72
|
+
|
|
73
|
+
static int rc_client_external_get_int(void)
|
|
74
|
+
{
|
|
75
|
+
return g_external_int;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static void rc_client_external_set_int(int value)
|
|
79
|
+
{
|
|
80
|
+
g_external_int = value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static void test_hardcore_enabled(void)
|
|
84
|
+
{
|
|
85
|
+
g_client = mock_client_with_external();
|
|
86
|
+
g_client->state.external_client->get_hardcore_enabled = rc_client_external_get_int;
|
|
87
|
+
g_client->state.external_client->set_hardcore_enabled = rc_client_external_set_int;
|
|
88
|
+
|
|
89
|
+
g_external_int = 0;
|
|
90
|
+
ASSERT_NUM_EQUALS(rc_client_get_hardcore_enabled(g_client), 0);
|
|
91
|
+
|
|
92
|
+
g_external_int = 1;
|
|
93
|
+
ASSERT_NUM_EQUALS(rc_client_get_hardcore_enabled(g_client), 1);
|
|
94
|
+
|
|
95
|
+
rc_client_set_hardcore_enabled(g_client, 0);
|
|
96
|
+
ASSERT_NUM_EQUALS(g_external_int, 0);
|
|
97
|
+
|
|
98
|
+
rc_client_set_hardcore_enabled(g_client, 1);
|
|
99
|
+
ASSERT_NUM_EQUALS(g_external_int, 1);
|
|
100
|
+
|
|
101
|
+
rc_client_destroy(g_client);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static void test_unofficial_enabled(void)
|
|
105
|
+
{
|
|
106
|
+
g_client = mock_client_with_external();
|
|
107
|
+
g_client->state.external_client->get_unofficial_enabled = rc_client_external_get_int;
|
|
108
|
+
g_client->state.external_client->set_unofficial_enabled = rc_client_external_set_int;
|
|
109
|
+
|
|
110
|
+
g_external_int = 0;
|
|
111
|
+
ASSERT_NUM_EQUALS(rc_client_get_unofficial_enabled(g_client), 0);
|
|
112
|
+
|
|
113
|
+
g_external_int = 1;
|
|
114
|
+
ASSERT_NUM_EQUALS(rc_client_get_unofficial_enabled(g_client), 1);
|
|
115
|
+
|
|
116
|
+
rc_client_set_unofficial_enabled(g_client, 0);
|
|
117
|
+
ASSERT_NUM_EQUALS(g_external_int, 0);
|
|
118
|
+
|
|
119
|
+
rc_client_set_unofficial_enabled(g_client, 1);
|
|
120
|
+
ASSERT_NUM_EQUALS(g_external_int, 1);
|
|
121
|
+
|
|
122
|
+
rc_client_destroy(g_client);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static void test_encore_mode_enabled(void)
|
|
126
|
+
{
|
|
127
|
+
g_client = mock_client_with_external();
|
|
128
|
+
g_client->state.external_client->get_encore_mode_enabled = rc_client_external_get_int;
|
|
129
|
+
g_client->state.external_client->set_encore_mode_enabled = rc_client_external_set_int;
|
|
130
|
+
|
|
131
|
+
g_external_int = 0;
|
|
132
|
+
ASSERT_NUM_EQUALS(rc_client_get_encore_mode_enabled(g_client), 0);
|
|
133
|
+
|
|
134
|
+
g_external_int = 1;
|
|
135
|
+
ASSERT_NUM_EQUALS(rc_client_get_encore_mode_enabled(g_client), 1);
|
|
136
|
+
|
|
137
|
+
rc_client_set_encore_mode_enabled(g_client, 0);
|
|
138
|
+
ASSERT_NUM_EQUALS(g_external_int, 0);
|
|
139
|
+
|
|
140
|
+
rc_client_set_encore_mode_enabled(g_client, 1);
|
|
141
|
+
ASSERT_NUM_EQUALS(g_external_int, 1);
|
|
142
|
+
|
|
143
|
+
rc_client_destroy(g_client);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static void test_spectator_mode_enabled(void)
|
|
147
|
+
{
|
|
148
|
+
g_client = mock_client_with_external();
|
|
149
|
+
g_client->state.external_client->get_spectator_mode_enabled = rc_client_external_get_int;
|
|
150
|
+
g_client->state.external_client->set_spectator_mode_enabled = rc_client_external_set_int;
|
|
151
|
+
|
|
152
|
+
g_external_int = 0;
|
|
153
|
+
ASSERT_NUM_EQUALS(rc_client_get_spectator_mode_enabled(g_client), 0);
|
|
154
|
+
|
|
155
|
+
g_external_int = 1;
|
|
156
|
+
ASSERT_NUM_EQUALS(rc_client_get_spectator_mode_enabled(g_client), 1);
|
|
157
|
+
|
|
158
|
+
rc_client_set_spectator_mode_enabled(g_client, 0);
|
|
159
|
+
ASSERT_NUM_EQUALS(g_external_int, 0);
|
|
160
|
+
|
|
161
|
+
rc_client_set_spectator_mode_enabled(g_client, 1);
|
|
162
|
+
ASSERT_NUM_EQUALS(g_external_int, 1);
|
|
163
|
+
|
|
164
|
+
rc_client_destroy(g_client);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static void rc_client_external_log_message(const char* message, const rc_client_t* client)
|
|
168
|
+
{
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static void rc_client_external_enable_logging(rc_client_t* client, int level, rc_client_message_callback_t callback)
|
|
172
|
+
{
|
|
173
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
174
|
+
ASSERT_NUM_EQUALS(level, RC_CLIENT_LOG_LEVEL_INFO);
|
|
175
|
+
ASSERT_PTR_EQUALS(callback, rc_client_external_log_message);
|
|
176
|
+
|
|
177
|
+
g_external_event = "enable_logging";
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
static void test_enable_logging(void)
|
|
181
|
+
{
|
|
182
|
+
g_client = mock_client_with_external();
|
|
183
|
+
g_client->state.external_client->enable_logging = rc_client_external_enable_logging;
|
|
184
|
+
|
|
185
|
+
rc_client_enable_logging(g_client, RC_CLIENT_LOG_LEVEL_INFO, rc_client_external_log_message);
|
|
186
|
+
|
|
187
|
+
ASSERT_STR_EQUALS(g_external_event, "enable_logging");
|
|
188
|
+
|
|
189
|
+
rc_client_destroy(g_client);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
static void rc_client_external_event_handler(const rc_client_event_t* event, rc_client_t* client)
|
|
193
|
+
{
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
static void rc_client_external_set_event_handler(rc_client_t* client, rc_client_event_handler_t handler)
|
|
197
|
+
{
|
|
198
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
199
|
+
ASSERT_PTR_EQUALS(handler, rc_client_external_event_handler);
|
|
200
|
+
|
|
201
|
+
g_external_event = "event_handler";
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static void test_event_handler(void)
|
|
205
|
+
{
|
|
206
|
+
g_client = mock_client_with_external();
|
|
207
|
+
g_client->state.external_client->set_event_handler = rc_client_external_set_event_handler;
|
|
208
|
+
|
|
209
|
+
rc_client_set_event_handler(g_client, rc_client_external_event_handler);
|
|
210
|
+
|
|
211
|
+
ASSERT_STR_EQUALS(g_external_event, "event_handler");
|
|
212
|
+
|
|
213
|
+
rc_client_destroy(g_client);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static uint32_t rc_client_external_read_memory(uint32_t address, uint8_t* buffer, uint32_t num_bytes, rc_client_t* client)
|
|
217
|
+
{
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
static void rc_client_external_set_read_memory_function(rc_client_t* client, rc_client_read_memory_func_t handler)
|
|
222
|
+
{
|
|
223
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
224
|
+
ASSERT_PTR_EQUALS(handler, rc_client_external_read_memory);
|
|
225
|
+
|
|
226
|
+
g_external_event = "read_memory";
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
static void test_read_memory(void)
|
|
230
|
+
{
|
|
231
|
+
g_client = mock_client_with_external();
|
|
232
|
+
g_client->state.external_client->set_read_memory = rc_client_external_set_read_memory_function;
|
|
233
|
+
|
|
234
|
+
rc_client_set_read_memory_function(g_client, rc_client_external_read_memory);
|
|
235
|
+
|
|
236
|
+
ASSERT_STR_EQUALS(g_external_event, "read_memory");
|
|
237
|
+
|
|
238
|
+
rc_client_destroy(g_client);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
static rc_clock_t rc_client_external_now_millisecs(const rc_client_t* client)
|
|
242
|
+
{
|
|
243
|
+
return (rc_clock_t)12345678;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
static void rc_client_external_set_get_time_millisecs(rc_client_t* client, rc_get_time_millisecs_func_t handler)
|
|
247
|
+
{
|
|
248
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
249
|
+
ASSERT_PTR_EQUALS(handler, rc_client_external_now_millisecs);
|
|
250
|
+
|
|
251
|
+
g_external_event = "set_milli";
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static void test_get_time_millisecs(void)
|
|
255
|
+
{
|
|
256
|
+
g_client = mock_client_with_external();
|
|
257
|
+
g_client->state.external_client->set_get_time_millisecs = rc_client_external_set_get_time_millisecs;
|
|
258
|
+
|
|
259
|
+
rc_client_set_get_time_millisecs_function(g_client, rc_client_external_now_millisecs);
|
|
260
|
+
|
|
261
|
+
ASSERT_STR_EQUALS(g_external_event, "set_milli");
|
|
262
|
+
|
|
263
|
+
rc_client_destroy(g_client);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
static void rc_client_external_set_host(const char* hostname)
|
|
267
|
+
{
|
|
268
|
+
ASSERT_STR_EQUALS(hostname, "localhost");
|
|
269
|
+
|
|
270
|
+
g_external_event = "set_host";
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
static void test_set_host(void)
|
|
274
|
+
{
|
|
275
|
+
g_client = mock_client_with_external();
|
|
276
|
+
g_client->state.external_client->set_host = rc_client_external_set_host;
|
|
277
|
+
|
|
278
|
+
rc_client_set_host(g_client, "localhost");
|
|
279
|
+
|
|
280
|
+
ASSERT_STR_EQUALS(g_external_event, "set_host");
|
|
281
|
+
|
|
282
|
+
rc_api_set_host(NULL);
|
|
283
|
+
rc_api_set_image_host(NULL);
|
|
284
|
+
|
|
285
|
+
rc_client_destroy(g_client);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
static size_t rc_client_external_get_user_agent_clause(char buffer[], size_t buffer_size)
|
|
289
|
+
{
|
|
290
|
+
return snprintf(buffer, buffer_size, "external/2.1");
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
static void test_get_user_agent_clause(void)
|
|
294
|
+
{
|
|
295
|
+
char expected_clause[] = "external/2.1 rc_client/" RCHEEVOS_VERSION_STRING;
|
|
296
|
+
char buffer[64];
|
|
297
|
+
|
|
298
|
+
g_client = mock_client_with_external();
|
|
299
|
+
g_client->state.external_client->get_user_agent_clause = rc_client_external_get_user_agent_clause;
|
|
300
|
+
|
|
301
|
+
ASSERT_NUM_EQUALS(rc_client_get_user_agent_clause(g_client, buffer, sizeof(buffer)), sizeof(expected_clause) - 1);
|
|
302
|
+
ASSERT_STR_EQUALS(buffer, expected_clause);
|
|
303
|
+
|
|
304
|
+
/* snprintf will return the number of characters it wants, even if the buffer is too small,
|
|
305
|
+
* but will only fill as much of the buffer is available */
|
|
306
|
+
ASSERT_NUM_EQUALS(rc_client_get_user_agent_clause(g_client, buffer, 8), sizeof(expected_clause) - 1);
|
|
307
|
+
ASSERT_STR_EQUALS(buffer, "externa");
|
|
308
|
+
|
|
309
|
+
ASSERT_NUM_EQUALS(rc_client_get_user_agent_clause(g_client, buffer, 20), sizeof(expected_clause) - 1);
|
|
310
|
+
ASSERT_STR_EQUALS(buffer, "external/2.1 rc_cli");
|
|
311
|
+
|
|
312
|
+
rc_client_destroy(g_client);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
static void rc_client_external_add_game_hash(const char* hash, uint32_t game_id)
|
|
316
|
+
{
|
|
317
|
+
ASSERT_STR_EQUALS(hash, "6a2305a2b6675a97ff792709be1ca857");
|
|
318
|
+
ASSERT_NUM_EQUALS(game_id, 1234);
|
|
319
|
+
|
|
320
|
+
g_external_event = "add_game_hash";
|
|
321
|
+
g_external_int = game_id;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
static void test_add_game_hash(void)
|
|
325
|
+
{
|
|
326
|
+
g_client = mock_client_with_external();
|
|
327
|
+
g_client->state.external_client->add_game_hash = rc_client_external_add_game_hash;
|
|
328
|
+
|
|
329
|
+
rc_client_add_game_hash(g_client, "6a2305a2b6675a97ff792709be1ca857", 1234);
|
|
330
|
+
|
|
331
|
+
/* hash should be loaded in both local client and external client */
|
|
332
|
+
ASSERT_PTR_NOT_NULL(g_client->hashes);
|
|
333
|
+
ASSERT_STR_EQUALS(g_client->hashes->hash, "6a2305a2b6675a97ff792709be1ca857");
|
|
334
|
+
ASSERT_NUM_EQUALS(g_client->hashes->game_id, 1234);
|
|
335
|
+
|
|
336
|
+
ASSERT_STR_EQUALS(g_external_event, "add_game_hash");
|
|
337
|
+
|
|
338
|
+
rc_client_destroy(g_client);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
static void test_set_allow_background_memory_reads(void)
|
|
342
|
+
{
|
|
343
|
+
g_client = mock_client_with_external();
|
|
344
|
+
g_client->state.external_client->set_allow_background_memory_reads = rc_client_external_set_int;
|
|
345
|
+
|
|
346
|
+
rc_client_set_allow_background_memory_reads(g_client, 0);
|
|
347
|
+
ASSERT_NUM_EQUALS(g_external_int, 0);
|
|
348
|
+
|
|
349
|
+
rc_client_set_allow_background_memory_reads(g_client, 1);
|
|
350
|
+
ASSERT_NUM_EQUALS(g_external_int, 1);
|
|
351
|
+
|
|
352
|
+
rc_client_destroy(g_client);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/* ----- login ----- */
|
|
356
|
+
|
|
357
|
+
static void test_v1_user_field_offsets(void)
|
|
358
|
+
{
|
|
359
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v1_rc_client_user_t, display_name);
|
|
360
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v1_rc_client_user_t, username);
|
|
361
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v1_rc_client_user_t, token);
|
|
362
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v1_rc_client_user_t, score);
|
|
363
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v1_rc_client_user_t, score_softcore);
|
|
364
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v1_rc_client_user_t, num_unread_messages);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
static void test_v3_user_field_offsets(void)
|
|
368
|
+
{
|
|
369
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v3_rc_client_user_t, display_name);
|
|
370
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v3_rc_client_user_t, username);
|
|
371
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v3_rc_client_user_t, token);
|
|
372
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v3_rc_client_user_t, score);
|
|
373
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v3_rc_client_user_t, score_softcore);
|
|
374
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v3_rc_client_user_t, num_unread_messages);
|
|
375
|
+
ASSERT_FIELD_OFFSET(rc_client_user_t, v3_rc_client_user_t, avatar_url);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
static void assert_login_with_password(rc_client_t* client, const char* username, const char* password)
|
|
379
|
+
{
|
|
380
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
381
|
+
|
|
382
|
+
ASSERT_STR_EQUALS(username, "User");
|
|
383
|
+
ASSERT_STR_EQUALS(password, "Pa$$word");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
static rc_client_async_handle_t* rc_client_external_login_with_password(rc_client_t* client,
|
|
387
|
+
const char* username, const char* password, rc_client_callback_t callback, void* callback_userdata)
|
|
388
|
+
{
|
|
389
|
+
assert_login_with_password(client, username, password);
|
|
390
|
+
|
|
391
|
+
g_external_event = "login";
|
|
392
|
+
|
|
393
|
+
callback(RC_OK, NULL, client, callback_userdata);
|
|
394
|
+
return NULL;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
static const rc_client_user_t* rc_client_external_get_user_info_v1(void)
|
|
398
|
+
{
|
|
399
|
+
v1_rc_client_user_t* user = (v1_rc_client_user_t*)
|
|
400
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v1_rc_client_user_t));
|
|
401
|
+
|
|
402
|
+
memset(user, 0, sizeof(*user));
|
|
403
|
+
user->display_name = "User";
|
|
404
|
+
user->username = "User";
|
|
405
|
+
user->token = "ApiToken";
|
|
406
|
+
user->score = 12345;
|
|
407
|
+
user->score_softcore = 123;
|
|
408
|
+
user->num_unread_messages = 2;
|
|
409
|
+
|
|
410
|
+
return (rc_client_user_t*)user;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
static const rc_client_user_t* rc_client_external_get_user_info_v3(void)
|
|
414
|
+
{
|
|
415
|
+
v3_rc_client_user_t* user = (v3_rc_client_user_t*)
|
|
416
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v3_rc_client_user_t));
|
|
417
|
+
|
|
418
|
+
memset(user, 0, sizeof(*user));
|
|
419
|
+
user->display_name = "User";
|
|
420
|
+
user->username = "User";
|
|
421
|
+
user->token = "ApiToken";
|
|
422
|
+
user->score = 12345;
|
|
423
|
+
user->score_softcore = 123;
|
|
424
|
+
user->num_unread_messages = 2;
|
|
425
|
+
user->avatar_url = "/UserPic/User.png";
|
|
426
|
+
|
|
427
|
+
return (rc_client_user_t*)user;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
static void test_login_with_password(void)
|
|
431
|
+
{
|
|
432
|
+
const rc_client_user_t* user;
|
|
433
|
+
|
|
434
|
+
g_client = mock_client_with_external();
|
|
435
|
+
g_client->state.external_client->begin_login_with_password = rc_client_external_login_with_password;
|
|
436
|
+
g_client->state.external_client->get_user_info = rc_client_external_get_user_info_v1;
|
|
437
|
+
g_client->state.external_client->get_user_info_v3 = rc_client_external_get_user_info_v3;
|
|
438
|
+
|
|
439
|
+
rc_client_begin_login_with_password(g_client, "User", "Pa$$word", rc_client_callback_expect_success, g_callback_userdata);
|
|
440
|
+
|
|
441
|
+
ASSERT_STR_EQUALS(g_external_event, "login");
|
|
442
|
+
|
|
443
|
+
/* user data should come from external client. validate structure */
|
|
444
|
+
user = rc_client_get_user_info(g_client);
|
|
445
|
+
ASSERT_PTR_NOT_NULL(user);
|
|
446
|
+
ASSERT_STR_EQUALS(user->username, "User");
|
|
447
|
+
ASSERT_STR_EQUALS(user->display_name, "User");
|
|
448
|
+
ASSERT_STR_EQUALS(user->token, "ApiToken");
|
|
449
|
+
ASSERT_NUM_EQUALS(user->score, 12345);
|
|
450
|
+
ASSERT_NUM_EQUALS(user->score_softcore, 123);
|
|
451
|
+
ASSERT_NUM_EQUALS(user->num_unread_messages, 2);
|
|
452
|
+
ASSERT_STR_EQUALS(user->avatar_url, "/UserPic/User.png");
|
|
453
|
+
|
|
454
|
+
/* ensure non-external client user was not initialized */
|
|
455
|
+
ASSERT_PTR_NULL(g_client->user.username);
|
|
456
|
+
|
|
457
|
+
rc_client_destroy(g_client);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
static void assert_login_with_token(rc_client_t* client, const char* username, const char* token)
|
|
461
|
+
{
|
|
462
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
463
|
+
|
|
464
|
+
ASSERT_STR_EQUALS(username, "User");
|
|
465
|
+
ASSERT_STR_EQUALS(token, "ApiToken");
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
static rc_client_async_handle_t* rc_client_external_login_with_token(rc_client_t* client,
|
|
469
|
+
const char* username, const char* token, rc_client_callback_t callback, void* callback_userdata)
|
|
470
|
+
{
|
|
471
|
+
assert_login_with_token(client, username, token);
|
|
472
|
+
|
|
473
|
+
g_external_event = "login";
|
|
474
|
+
|
|
475
|
+
callback(RC_OK, NULL, client, callback_userdata);
|
|
476
|
+
return NULL;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
static void test_login_with_token_v1(void)
|
|
480
|
+
{
|
|
481
|
+
const rc_client_user_t* user;
|
|
482
|
+
|
|
483
|
+
g_client = mock_client_with_external();
|
|
484
|
+
g_client->state.external_client->begin_login_with_token = rc_client_external_login_with_token;
|
|
485
|
+
g_client->state.external_client->get_user_info = rc_client_external_get_user_info_v1;
|
|
486
|
+
|
|
487
|
+
rc_client_begin_login_with_token(g_client, "User", "ApiToken", rc_client_callback_expect_success, g_callback_userdata);
|
|
488
|
+
|
|
489
|
+
ASSERT_STR_EQUALS(g_external_event, "login");
|
|
490
|
+
|
|
491
|
+
/* user data should come from external client. validate structure */
|
|
492
|
+
user = rc_client_get_user_info(g_client);
|
|
493
|
+
ASSERT_PTR_NOT_NULL(user);
|
|
494
|
+
ASSERT_STR_EQUALS(user->username, "User");
|
|
495
|
+
ASSERT_STR_EQUALS(user->display_name, "User");
|
|
496
|
+
ASSERT_STR_EQUALS(user->token, "ApiToken");
|
|
497
|
+
ASSERT_NUM_EQUALS(user->score, 12345);
|
|
498
|
+
ASSERT_NUM_EQUALS(user->score_softcore, 123);
|
|
499
|
+
ASSERT_NUM_EQUALS(user->num_unread_messages, 2);
|
|
500
|
+
ASSERT_STR_EQUALS(user->avatar_url, "https://media.retroachievements.org/UserPic/User.png");
|
|
501
|
+
|
|
502
|
+
/* ensure non-external client user was not initialized */
|
|
503
|
+
ASSERT_PTR_NULL(g_client->user.username);
|
|
504
|
+
|
|
505
|
+
rc_client_destroy(g_client);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
static const rc_client_user_t* rc_client_external_get_user_info_v1_long_name(void)
|
|
509
|
+
{
|
|
510
|
+
v1_rc_client_user_t* user = (v1_rc_client_user_t*)
|
|
511
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v1_rc_client_user_t));
|
|
512
|
+
|
|
513
|
+
memset(user, 0, sizeof(*user));
|
|
514
|
+
user->display_name = "TwentyCharUserNameXX";
|
|
515
|
+
user->username = "TwentyCharUserNameXX";
|
|
516
|
+
user->token = "ApiToken";
|
|
517
|
+
user->score = 12345;
|
|
518
|
+
user->score_softcore = 123;
|
|
519
|
+
user->num_unread_messages = 2;
|
|
520
|
+
|
|
521
|
+
return (rc_client_user_t*)user;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
static rc_client_async_handle_t* rc_client_external_login_with_token_long_name(rc_client_t* client,
|
|
525
|
+
const char* username, const char* token, rc_client_callback_t callback, void* callback_userdata)
|
|
526
|
+
{
|
|
527
|
+
g_external_event = "login";
|
|
528
|
+
|
|
529
|
+
callback(RC_OK, NULL, client, callback_userdata);
|
|
530
|
+
return NULL;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
static void test_login_with_token_v1_long_username(void)
|
|
534
|
+
{
|
|
535
|
+
const rc_client_user_t* user;
|
|
536
|
+
|
|
537
|
+
g_client = mock_client_with_external();
|
|
538
|
+
g_client->state.external_client->begin_login_with_token = rc_client_external_login_with_token_long_name;
|
|
539
|
+
g_client->state.external_client->get_user_info = rc_client_external_get_user_info_v1_long_name;
|
|
540
|
+
|
|
541
|
+
rc_client_begin_login_with_token(g_client, "TwentyCharUserNameXX", "ApiToken", rc_client_callback_expect_success, g_callback_userdata);
|
|
542
|
+
|
|
543
|
+
ASSERT_STR_EQUALS(g_external_event, "login");
|
|
544
|
+
|
|
545
|
+
/* user data should come from external client. validate structure */
|
|
546
|
+
user = rc_client_get_user_info(g_client);
|
|
547
|
+
ASSERT_PTR_NOT_NULL(user);
|
|
548
|
+
ASSERT_STR_EQUALS(user->username, "TwentyCharUserNameXX");
|
|
549
|
+
ASSERT_STR_EQUALS(user->display_name, "TwentyCharUserNameXX");
|
|
550
|
+
ASSERT_STR_EQUALS(user->token, "ApiToken");
|
|
551
|
+
ASSERT_NUM_EQUALS(user->score, 12345);
|
|
552
|
+
ASSERT_NUM_EQUALS(user->score_softcore, 123);
|
|
553
|
+
ASSERT_NUM_EQUALS(user->num_unread_messages, 2);
|
|
554
|
+
ASSERT_STR_EQUALS(user->avatar_url, "https://media.retroachievements.org/UserPic/TwentyCharUserNameXX.png");
|
|
555
|
+
|
|
556
|
+
/* ensure non-external client user was not initialized */
|
|
557
|
+
ASSERT_PTR_NULL(g_client->user.username);
|
|
558
|
+
|
|
559
|
+
rc_client_destroy(g_client);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
static const rc_client_user_t* rc_client_external_get_user_info_v1_too_long_name(void)
|
|
563
|
+
{
|
|
564
|
+
v1_rc_client_user_t* user = (v1_rc_client_user_t*)
|
|
565
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v1_rc_client_user_t));
|
|
566
|
+
|
|
567
|
+
memset(user, 0, sizeof(*user));
|
|
568
|
+
user->display_name = "ThisUserNameIsTooLongToFitIntoTheUserAvatarBufferWithoutOverflowing";
|
|
569
|
+
user->username = "ThisUserNameIsTooLongToFitIntoTheUserAvatarBufferWithoutOverflowing";
|
|
570
|
+
user->token = "ApiToken";
|
|
571
|
+
user->score = 12345;
|
|
572
|
+
user->score_softcore = 123;
|
|
573
|
+
user->num_unread_messages = 2;
|
|
574
|
+
|
|
575
|
+
return (rc_client_user_t*)user;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
static void test_login_with_token_v1_too_long_username(void)
|
|
579
|
+
{
|
|
580
|
+
const rc_client_user_t* user;
|
|
581
|
+
|
|
582
|
+
g_client = mock_client_with_external();
|
|
583
|
+
g_client->state.external_client->begin_login_with_token = rc_client_external_login_with_token_long_name;
|
|
584
|
+
g_client->state.external_client->get_user_info = rc_client_external_get_user_info_v1_too_long_name;
|
|
585
|
+
|
|
586
|
+
rc_client_begin_login_with_token(g_client, "ThisUserNameIsTooLongToFitIntoTheUserAvatarBufferWithoutOverflowing", "ApiToken", rc_client_callback_expect_success, g_callback_userdata);
|
|
587
|
+
|
|
588
|
+
ASSERT_STR_EQUALS(g_external_event, "login");
|
|
589
|
+
|
|
590
|
+
/* user data should come from external client. validate structure */
|
|
591
|
+
user = rc_client_get_user_info(g_client);
|
|
592
|
+
ASSERT_PTR_NOT_NULL(user);
|
|
593
|
+
ASSERT_STR_EQUALS(user->username, "ThisUserNameIsTooLongToFitIntoTheUserAvatarBufferWithoutOverflowing");
|
|
594
|
+
ASSERT_STR_EQUALS(user->display_name, "ThisUserNameIsTooLongToFitIntoTheUserAvatarBufferWithoutOverflowing");
|
|
595
|
+
ASSERT_STR_EQUALS(user->token, "ApiToken");
|
|
596
|
+
ASSERT_NUM_EQUALS(user->score, 12345);
|
|
597
|
+
ASSERT_NUM_EQUALS(user->score_softcore, 123);
|
|
598
|
+
ASSERT_NUM_EQUALS(user->num_unread_messages, 2);
|
|
599
|
+
/* overly long URL will be truncated, but should not cause an exception.
|
|
600
|
+
* test_login_with_token_v1_long_username validates the longest allowed username, so this shouldn't occur anyway */
|
|
601
|
+
ASSERT_STR_EQUALS(user->avatar_url, "https://media.retroachievements.org/UserPic/ThisUserNameIsTooLongToFitIntoTheUs");
|
|
602
|
+
|
|
603
|
+
/* ensure non-external client user was not initialized */
|
|
604
|
+
ASSERT_PTR_NULL(g_client->user.username);
|
|
605
|
+
|
|
606
|
+
rc_client_destroy(g_client);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
static void test_login_with_token(void)
|
|
610
|
+
{
|
|
611
|
+
const rc_client_user_t* user;
|
|
612
|
+
|
|
613
|
+
g_client = mock_client_with_external();
|
|
614
|
+
g_client->state.external_client->begin_login_with_token = rc_client_external_login_with_token;
|
|
615
|
+
g_client->state.external_client->get_user_info_v3 = rc_client_external_get_user_info_v3;
|
|
616
|
+
|
|
617
|
+
rc_client_begin_login_with_token(g_client, "User", "ApiToken", rc_client_callback_expect_success, g_callback_userdata);
|
|
618
|
+
|
|
619
|
+
ASSERT_STR_EQUALS(g_external_event, "login");
|
|
620
|
+
|
|
621
|
+
/* user data should come from external client. validate structure */
|
|
622
|
+
user = rc_client_get_user_info(g_client);
|
|
623
|
+
ASSERT_PTR_NOT_NULL(user);
|
|
624
|
+
ASSERT_STR_EQUALS(user->username, "User");
|
|
625
|
+
ASSERT_STR_EQUALS(user->display_name, "User");
|
|
626
|
+
ASSERT_STR_EQUALS(user->token, "ApiToken");
|
|
627
|
+
ASSERT_NUM_EQUALS(user->score, 12345);
|
|
628
|
+
ASSERT_NUM_EQUALS(user->score_softcore, 123);
|
|
629
|
+
ASSERT_NUM_EQUALS(user->num_unread_messages, 2);
|
|
630
|
+
ASSERT_STR_EQUALS(user->avatar_url, "/UserPic/User.png");
|
|
631
|
+
|
|
632
|
+
/* ensure non-external client user was not initialized */
|
|
633
|
+
ASSERT_PTR_NULL(g_client->user.username);
|
|
634
|
+
|
|
635
|
+
rc_client_destroy(g_client);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
static void rc_client_external_logout(void)
|
|
639
|
+
{
|
|
640
|
+
g_external_event = "logout";
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
static void test_logout(void)
|
|
644
|
+
{
|
|
645
|
+
g_client = mock_client_with_external();
|
|
646
|
+
g_client->state.external_client->logout = rc_client_external_logout;
|
|
647
|
+
|
|
648
|
+
/* external client should maintain its own state, but use the singular instance*/
|
|
649
|
+
g_client->state.user = RC_CLIENT_USER_STATE_LOGGED_IN;
|
|
650
|
+
|
|
651
|
+
rc_client_logout(g_client);
|
|
652
|
+
ASSERT_STR_EQUALS(g_external_event, "logout");
|
|
653
|
+
|
|
654
|
+
/* ensure non-external client user was not modified */
|
|
655
|
+
ASSERT_NUM_EQUALS(g_client->state.user, RC_CLIENT_USER_STATE_LOGGED_IN);
|
|
656
|
+
|
|
657
|
+
rc_client_destroy(g_client);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/* ----- load game ----- */
|
|
661
|
+
|
|
662
|
+
static void test_v1_game_field_offsets(void)
|
|
663
|
+
{
|
|
664
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v1_rc_client_game_t, id);
|
|
665
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v1_rc_client_game_t, console_id);
|
|
666
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v1_rc_client_game_t, title);
|
|
667
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v1_rc_client_game_t, hash);
|
|
668
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v1_rc_client_game_t, badge_name);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
static void test_v3_game_field_offsets(void)
|
|
672
|
+
{
|
|
673
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v3_rc_client_game_t, id);
|
|
674
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v3_rc_client_game_t, console_id);
|
|
675
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v3_rc_client_game_t, title);
|
|
676
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v3_rc_client_game_t, hash);
|
|
677
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v3_rc_client_game_t, badge_name);
|
|
678
|
+
ASSERT_FIELD_OFFSET(rc_client_game_t, v3_rc_client_game_t, badge_url);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
static const rc_client_game_t* rc_client_external_get_game_info_v1(void)
|
|
682
|
+
{
|
|
683
|
+
v1_rc_client_game_t* game = (v1_rc_client_game_t*)
|
|
684
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v1_rc_client_game_t));
|
|
685
|
+
|
|
686
|
+
memset(game, 0, sizeof(*game));
|
|
687
|
+
game->id = 1234;
|
|
688
|
+
game->console_id = RC_CONSOLE_PLAYSTATION;
|
|
689
|
+
game->title = "Game Title";
|
|
690
|
+
game->hash = "GAME_HASH";
|
|
691
|
+
game->badge_name = "BDG001";
|
|
692
|
+
|
|
693
|
+
return (const rc_client_game_t*)game;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
static const rc_client_game_t* rc_client_external_get_game_info_v1_not_found(void)
|
|
697
|
+
{
|
|
698
|
+
return NULL;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
static const rc_client_game_t* rc_client_external_get_game_info_v3(void)
|
|
702
|
+
{
|
|
703
|
+
v3_rc_client_game_t* game = (v3_rc_client_game_t*)
|
|
704
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v3_rc_client_game_t));
|
|
705
|
+
|
|
706
|
+
memset(game, 0, sizeof(*game));
|
|
707
|
+
game->id = 1234;
|
|
708
|
+
game->console_id = RC_CONSOLE_PLAYSTATION;
|
|
709
|
+
game->title = "Game Title";
|
|
710
|
+
game->hash = "GAME_HASH";
|
|
711
|
+
game->badge_name = "BDG001";
|
|
712
|
+
game->badge_url = "/Badge/BDG001.png";
|
|
713
|
+
|
|
714
|
+
return (const rc_client_game_t*)game;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
#ifdef RC_CLIENT_SUPPORTS_HASH
|
|
718
|
+
|
|
719
|
+
static void assert_identify_and_load_game(rc_client_t* client,
|
|
720
|
+
uint32_t console_id, const char* file_path, const uint8_t* data, size_t data_size)
|
|
721
|
+
{
|
|
722
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
723
|
+
|
|
724
|
+
ASSERT_NUM_EQUALS(console_id, RC_CONSOLE_GAMEBOY);
|
|
725
|
+
ASSERT_STR_EQUALS(file_path, "foo.zip#foo.gb");
|
|
726
|
+
ASSERT_PTR_NOT_NULL(data);
|
|
727
|
+
ASSERT_NUM_EQUALS(32768, data_size);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
static rc_client_async_handle_t* rc_client_external_identify_and_load_game(rc_client_t* client,
|
|
731
|
+
uint32_t console_id, const char* file_path, const uint8_t* data, size_t data_size,
|
|
732
|
+
rc_client_callback_t callback, void* callback_userdata)
|
|
733
|
+
{
|
|
734
|
+
assert_identify_and_load_game(client, console_id, file_path, data, data_size);
|
|
735
|
+
|
|
736
|
+
g_external_event = "load_game";
|
|
737
|
+
|
|
738
|
+
callback(RC_OK, NULL, client, callback_userdata);
|
|
739
|
+
return NULL;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
static void test_identify_and_load_game_v1(void)
|
|
743
|
+
{
|
|
744
|
+
const size_t image_size = 32768;
|
|
745
|
+
uint8_t* image = generate_generic_file(image_size);
|
|
746
|
+
const rc_client_game_t* game;
|
|
747
|
+
|
|
748
|
+
g_client = mock_client_with_external();
|
|
749
|
+
g_client->state.external_client->begin_identify_and_load_game = rc_client_external_identify_and_load_game;
|
|
750
|
+
g_client->state.external_client->get_game_info = rc_client_external_get_game_info_v1;
|
|
751
|
+
|
|
752
|
+
rc_client_begin_identify_and_load_game(g_client, RC_CONSOLE_GAMEBOY, "foo.zip#foo.gb",
|
|
753
|
+
image, image_size, rc_client_callback_expect_success, g_callback_userdata);
|
|
754
|
+
|
|
755
|
+
ASSERT_STR_EQUALS(g_external_event, "load_game");
|
|
756
|
+
|
|
757
|
+
/* user data should come from external client. validate structure */
|
|
758
|
+
game = rc_client_get_game_info(g_client);
|
|
759
|
+
ASSERT_PTR_NOT_NULL(game);
|
|
760
|
+
ASSERT_NUM_EQUALS(game->id, 1234);
|
|
761
|
+
ASSERT_NUM_EQUALS(game->console_id, RC_CONSOLE_PLAYSTATION);
|
|
762
|
+
ASSERT_STR_EQUALS(game->title, "Game Title");
|
|
763
|
+
ASSERT_STR_EQUALS(game->hash, "GAME_HASH");
|
|
764
|
+
ASSERT_STR_EQUALS(game->badge_name, "BDG001");
|
|
765
|
+
ASSERT_STR_EQUALS(game->badge_url, "https://media.retroachievements.org/Images/BDG001.png");
|
|
766
|
+
|
|
767
|
+
/* ensure non-external client game was not initialized */
|
|
768
|
+
ASSERT_PTR_NULL(g_client->game);
|
|
769
|
+
|
|
770
|
+
rc_client_destroy(g_client);
|
|
771
|
+
free(image);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
static void test_identify_and_load_game(void)
|
|
775
|
+
{
|
|
776
|
+
const size_t image_size = 32768;
|
|
777
|
+
uint8_t* image = generate_generic_file(image_size);
|
|
778
|
+
const rc_client_game_t* game;
|
|
779
|
+
|
|
780
|
+
g_client = mock_client_with_external();
|
|
781
|
+
g_client->state.external_client->begin_identify_and_load_game = rc_client_external_identify_and_load_game;
|
|
782
|
+
g_client->state.external_client->get_game_info = rc_client_external_get_game_info_v1;
|
|
783
|
+
g_client->state.external_client->get_game_info_v3 = rc_client_external_get_game_info_v3;
|
|
784
|
+
|
|
785
|
+
rc_client_begin_identify_and_load_game(g_client, RC_CONSOLE_GAMEBOY, "foo.zip#foo.gb",
|
|
786
|
+
image, image_size, rc_client_callback_expect_success, g_callback_userdata);
|
|
787
|
+
|
|
788
|
+
ASSERT_STR_EQUALS(g_external_event, "load_game");
|
|
789
|
+
|
|
790
|
+
/* user data should come from external client. validate structure */
|
|
791
|
+
game = rc_client_get_game_info(g_client);
|
|
792
|
+
ASSERT_PTR_NOT_NULL(game);
|
|
793
|
+
ASSERT_NUM_EQUALS(game->id, 1234);
|
|
794
|
+
ASSERT_NUM_EQUALS(game->console_id, RC_CONSOLE_PLAYSTATION);
|
|
795
|
+
ASSERT_STR_EQUALS(game->title, "Game Title");
|
|
796
|
+
ASSERT_STR_EQUALS(game->hash, "GAME_HASH");
|
|
797
|
+
ASSERT_STR_EQUALS(game->badge_name, "BDG001");
|
|
798
|
+
ASSERT_STR_EQUALS(game->badge_url, "/Badge/BDG001.png");
|
|
799
|
+
/* ensure non-external client game was not initialized */
|
|
800
|
+
ASSERT_PTR_NULL(g_client->game);
|
|
801
|
+
|
|
802
|
+
rc_client_destroy(g_client);
|
|
803
|
+
free(image);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
static void test_identify_and_reload_game(void)
|
|
807
|
+
{
|
|
808
|
+
const size_t image_size = 32768;
|
|
809
|
+
uint8_t* image = generate_generic_file(image_size);
|
|
810
|
+
const rc_client_game_t* game;
|
|
811
|
+
|
|
812
|
+
g_client = mock_client_with_external();
|
|
813
|
+
g_client->state.external_client->begin_identify_and_load_game = rc_client_external_identify_and_load_game;
|
|
814
|
+
g_client->state.external_client->get_game_info = rc_client_external_get_game_info_v1;
|
|
815
|
+
g_client->state.external_client->get_game_info_v3 = rc_client_external_get_game_info_v3;
|
|
816
|
+
g_client->state.external_client->unload_game = rc_client_external_unload_game;
|
|
817
|
+
|
|
818
|
+
rc_client_begin_identify_and_load_game(g_client, RC_CONSOLE_GAMEBOY, "foo.zip#foo.gb",
|
|
819
|
+
image, image_size, rc_client_callback_expect_success, g_callback_userdata);
|
|
820
|
+
|
|
821
|
+
ASSERT_STR_EQUALS(g_external_event, "load_game");
|
|
822
|
+
|
|
823
|
+
/* user data should come from external client. validate structure */
|
|
824
|
+
game = rc_client_get_game_info(g_client);
|
|
825
|
+
ASSERT_PTR_NOT_NULL(game);
|
|
826
|
+
ASSERT_NUM_EQUALS(game->id, 1234);
|
|
827
|
+
ASSERT_NUM_EQUALS(game->console_id, RC_CONSOLE_PLAYSTATION);
|
|
828
|
+
ASSERT_STR_EQUALS(game->title, "Game Title");
|
|
829
|
+
ASSERT_STR_EQUALS(game->hash, "GAME_HASH");
|
|
830
|
+
ASSERT_STR_EQUALS(game->badge_name, "BDG001");
|
|
831
|
+
ASSERT_STR_EQUALS(game->badge_url, "/Badge/BDG001.png");
|
|
832
|
+
/* ensure non-external client game was not initialized */
|
|
833
|
+
ASSERT_PTR_NULL(g_client->game);
|
|
834
|
+
|
|
835
|
+
rc_client_unload_game(g_client);
|
|
836
|
+
|
|
837
|
+
rc_client_begin_identify_and_load_game(g_client, RC_CONSOLE_GAMEBOY, "foo.zip#foo.gb",
|
|
838
|
+
image, image_size, rc_client_callback_expect_success, g_callback_userdata);
|
|
839
|
+
|
|
840
|
+
ASSERT_STR_EQUALS(g_external_event, "load_game");
|
|
841
|
+
|
|
842
|
+
/* user data should come from external client. validate structure */
|
|
843
|
+
game = rc_client_get_game_info(g_client);
|
|
844
|
+
ASSERT_PTR_NOT_NULL(game);
|
|
845
|
+
ASSERT_NUM_EQUALS(game->id, 1234);
|
|
846
|
+
ASSERT_NUM_EQUALS(game->console_id, RC_CONSOLE_PLAYSTATION);
|
|
847
|
+
ASSERT_STR_EQUALS(game->title, "Game Title");
|
|
848
|
+
ASSERT_STR_EQUALS(game->hash, "GAME_HASH");
|
|
849
|
+
ASSERT_STR_EQUALS(game->badge_name, "BDG001");
|
|
850
|
+
ASSERT_STR_EQUALS(game->badge_url, "/Badge/BDG001.png");
|
|
851
|
+
/* ensure non-external client game was not initialized */
|
|
852
|
+
ASSERT_PTR_NULL(g_client->game);
|
|
853
|
+
|
|
854
|
+
rc_client_destroy(g_client);
|
|
855
|
+
free(image);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
#endif /* RC_CLIENT_SUPPORTS_HASH */
|
|
859
|
+
|
|
860
|
+
static void assert_load_game(rc_client_t* client, const char* hash)
|
|
861
|
+
{
|
|
862
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
863
|
+
|
|
864
|
+
ASSERT_STR_EQUALS(hash, "6a2305a2b6675a97ff792709be1ca857");
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
static rc_client_async_handle_t* rc_client_external_load_game(rc_client_t* client,
|
|
868
|
+
const char* hash, rc_client_callback_t callback, void* callback_userdata)
|
|
869
|
+
{
|
|
870
|
+
assert_load_game(client, hash);
|
|
871
|
+
|
|
872
|
+
g_external_event = "load_game";
|
|
873
|
+
|
|
874
|
+
callback(RC_OK, NULL, client, callback_userdata);
|
|
875
|
+
return NULL;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
static void test_load_game_v1(void)
|
|
879
|
+
{
|
|
880
|
+
const rc_client_game_t* game;
|
|
881
|
+
|
|
882
|
+
g_client = mock_client_with_external();
|
|
883
|
+
g_client->state.external_client->begin_load_game = rc_client_external_load_game;
|
|
884
|
+
g_client->state.external_client->get_game_info = rc_client_external_get_game_info_v1;
|
|
885
|
+
|
|
886
|
+
rc_client_begin_load_game(g_client, "6a2305a2b6675a97ff792709be1ca857", rc_client_callback_expect_success, g_callback_userdata);
|
|
887
|
+
|
|
888
|
+
ASSERT_STR_EQUALS(g_external_event, "load_game");
|
|
889
|
+
|
|
890
|
+
/* game data should come from external client. validate structure */
|
|
891
|
+
game = rc_client_get_game_info(g_client);
|
|
892
|
+
ASSERT_PTR_NOT_NULL(game);
|
|
893
|
+
ASSERT_NUM_EQUALS(game->id, 1234);
|
|
894
|
+
ASSERT_NUM_EQUALS(game->console_id, RC_CONSOLE_PLAYSTATION);
|
|
895
|
+
ASSERT_STR_EQUALS(game->title, "Game Title");
|
|
896
|
+
ASSERT_STR_EQUALS(game->hash, "GAME_HASH");
|
|
897
|
+
ASSERT_STR_EQUALS(game->badge_name, "BDG001");
|
|
898
|
+
ASSERT_STR_EQUALS(game->badge_url, "https://media.retroachievements.org/Images/BDG001.png");
|
|
899
|
+
|
|
900
|
+
/* ensure non-external client user was not initialized */
|
|
901
|
+
ASSERT_PTR_NULL(g_client->game);
|
|
902
|
+
|
|
903
|
+
rc_client_destroy(g_client);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
static void test_load_game(void)
|
|
907
|
+
{
|
|
908
|
+
const rc_client_game_t* game;
|
|
909
|
+
|
|
910
|
+
g_client = mock_client_with_external();
|
|
911
|
+
g_client->state.external_client->begin_load_game = rc_client_external_load_game;
|
|
912
|
+
g_client->state.external_client->get_game_info = rc_client_external_get_game_info_v1;
|
|
913
|
+
g_client->state.external_client->get_game_info_v3 = rc_client_external_get_game_info_v3;
|
|
914
|
+
|
|
915
|
+
rc_client_begin_load_game(g_client, "6a2305a2b6675a97ff792709be1ca857", rc_client_callback_expect_success, g_callback_userdata);
|
|
916
|
+
|
|
917
|
+
ASSERT_STR_EQUALS(g_external_event, "load_game");
|
|
918
|
+
|
|
919
|
+
/* game data should come from external client. validate structure */
|
|
920
|
+
game = rc_client_get_game_info(g_client);
|
|
921
|
+
ASSERT_PTR_NOT_NULL(game);
|
|
922
|
+
ASSERT_NUM_EQUALS(game->id, 1234);
|
|
923
|
+
ASSERT_NUM_EQUALS(game->console_id, RC_CONSOLE_PLAYSTATION);
|
|
924
|
+
ASSERT_STR_EQUALS(game->title, "Game Title");
|
|
925
|
+
ASSERT_STR_EQUALS(game->hash, "GAME_HASH");
|
|
926
|
+
ASSERT_STR_EQUALS(game->badge_name, "BDG001");
|
|
927
|
+
ASSERT_STR_EQUALS(game->badge_url, "/Badge/BDG001.png");
|
|
928
|
+
|
|
929
|
+
/* ensure non-external client user was not initialized */
|
|
930
|
+
ASSERT_PTR_NULL(g_client->game);
|
|
931
|
+
|
|
932
|
+
rc_client_destroy(g_client);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
static void test_get_game_info_v1_no_game_loaded(void)
|
|
936
|
+
{
|
|
937
|
+
const rc_client_game_t* game;
|
|
938
|
+
|
|
939
|
+
g_client = mock_client_with_external();
|
|
940
|
+
g_client->state.external_client->get_game_info = rc_client_external_get_game_info_v1_not_found;
|
|
941
|
+
|
|
942
|
+
/* game data should come from external client. validate structure */
|
|
943
|
+
game = rc_client_get_game_info(g_client);
|
|
944
|
+
ASSERT_PTR_NULL(game);
|
|
945
|
+
|
|
946
|
+
rc_client_destroy(g_client);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
static void test_v1_user_game_summary_field_offsets(void)
|
|
950
|
+
{
|
|
951
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v1_rc_client_user_game_summary_t, num_core_achievements);
|
|
952
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v1_rc_client_user_game_summary_t, num_unofficial_achievements);
|
|
953
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v1_rc_client_user_game_summary_t, num_unlocked_achievements);
|
|
954
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v1_rc_client_user_game_summary_t, num_unsupported_achievements);
|
|
955
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v1_rc_client_user_game_summary_t, points_core);
|
|
956
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v1_rc_client_user_game_summary_t, points_unlocked);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
static void test_v5_user_game_summary_field_offsets(void)
|
|
960
|
+
{
|
|
961
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v5_rc_client_user_game_summary_t, num_core_achievements);
|
|
962
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v5_rc_client_user_game_summary_t, num_unofficial_achievements);
|
|
963
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v5_rc_client_user_game_summary_t, num_unlocked_achievements);
|
|
964
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v5_rc_client_user_game_summary_t, num_unsupported_achievements);
|
|
965
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v5_rc_client_user_game_summary_t, points_core);
|
|
966
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v5_rc_client_user_game_summary_t, points_unlocked);
|
|
967
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v5_rc_client_user_game_summary_t, beaten_time);
|
|
968
|
+
ASSERT_FIELD_OFFSET(rc_client_user_game_summary_t, v5_rc_client_user_game_summary_t, completed_time);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
static void rc_client_external_get_user_game_summary(rc_client_user_game_summary_t* summary)
|
|
972
|
+
{
|
|
973
|
+
summary->num_core_achievements = 20;
|
|
974
|
+
summary->num_unlocked_achievements = 6;
|
|
975
|
+
summary->num_unofficial_achievements = 3;
|
|
976
|
+
summary->num_unsupported_achievements = 1;
|
|
977
|
+
summary->points_core = 100;
|
|
978
|
+
summary->points_unlocked = 23;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
static void rc_client_external_get_user_game_summary_v5(rc_client_user_game_summary_t* summary)
|
|
982
|
+
{
|
|
983
|
+
summary->num_core_achievements = 20;
|
|
984
|
+
summary->num_unlocked_achievements = 6;
|
|
985
|
+
summary->num_unofficial_achievements = 3;
|
|
986
|
+
summary->num_unsupported_achievements = 1;
|
|
987
|
+
summary->points_core = 100;
|
|
988
|
+
summary->points_unlocked = 23;
|
|
989
|
+
summary->beaten_time = 1234567890;
|
|
990
|
+
summary->completed_time = 1234598760;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
static void test_get_user_game_summary(void)
|
|
994
|
+
{
|
|
995
|
+
rc_client_user_game_summary_t summary;
|
|
996
|
+
|
|
997
|
+
g_client = mock_client_with_external();
|
|
998
|
+
g_client->state.external_client->get_user_game_summary = rc_client_external_get_user_game_summary;
|
|
999
|
+
|
|
1000
|
+
rc_client_get_user_game_summary(g_client, &summary);
|
|
1001
|
+
|
|
1002
|
+
ASSERT_NUM_EQUALS(summary.num_core_achievements, 20);
|
|
1003
|
+
ASSERT_NUM_EQUALS(summary.num_unlocked_achievements, 6);
|
|
1004
|
+
ASSERT_NUM_EQUALS(summary.num_unofficial_achievements, 3);
|
|
1005
|
+
ASSERT_NUM_EQUALS(summary.num_unsupported_achievements, 1);
|
|
1006
|
+
ASSERT_NUM_EQUALS(summary.points_core, 100);
|
|
1007
|
+
ASSERT_NUM_EQUALS(summary.points_unlocked, 23);
|
|
1008
|
+
ASSERT_NUM_EQUALS(summary.beaten_time, 0);
|
|
1009
|
+
ASSERT_NUM_EQUALS(summary.completed_time, 0);
|
|
1010
|
+
|
|
1011
|
+
rc_client_destroy(g_client);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
static void test_get_user_game_summary_v5(void)
|
|
1015
|
+
{
|
|
1016
|
+
rc_client_user_game_summary_t summary;
|
|
1017
|
+
|
|
1018
|
+
g_client = mock_client_with_external();
|
|
1019
|
+
g_client->state.external_client->get_user_game_summary_v5 = rc_client_external_get_user_game_summary_v5;
|
|
1020
|
+
|
|
1021
|
+
rc_client_get_user_game_summary(g_client, &summary);
|
|
1022
|
+
|
|
1023
|
+
ASSERT_NUM_EQUALS(summary.num_core_achievements, 20);
|
|
1024
|
+
ASSERT_NUM_EQUALS(summary.num_unlocked_achievements, 6);
|
|
1025
|
+
ASSERT_NUM_EQUALS(summary.num_unofficial_achievements, 3);
|
|
1026
|
+
ASSERT_NUM_EQUALS(summary.num_unsupported_achievements, 1);
|
|
1027
|
+
ASSERT_NUM_EQUALS(summary.points_core, 100);
|
|
1028
|
+
ASSERT_NUM_EQUALS(summary.points_unlocked, 23);
|
|
1029
|
+
ASSERT_NUM_EQUALS(summary.beaten_time, 1234567890);
|
|
1030
|
+
ASSERT_NUM_EQUALS(summary.completed_time, 1234598760);
|
|
1031
|
+
|
|
1032
|
+
rc_client_destroy(g_client);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
static void rc_client_external_get_user_subset_summary(uint32_t subset_id, rc_client_user_game_summary_t* summary)
|
|
1036
|
+
{
|
|
1037
|
+
if (subset_id == 6) {
|
|
1038
|
+
summary->num_core_achievements = 20;
|
|
1039
|
+
summary->num_unlocked_achievements = 6;
|
|
1040
|
+
summary->num_unofficial_achievements = 3;
|
|
1041
|
+
summary->num_unsupported_achievements = 1;
|
|
1042
|
+
summary->points_core = 100;
|
|
1043
|
+
summary->points_unlocked = 23;
|
|
1044
|
+
summary->beaten_time = 1234567890;
|
|
1045
|
+
summary->completed_time = 1234598760;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
static void test_get_user_subset_summary(void)
|
|
1050
|
+
{
|
|
1051
|
+
rc_client_user_game_summary_t summary;
|
|
1052
|
+
|
|
1053
|
+
g_client = mock_client_with_external();
|
|
1054
|
+
g_client->state.external_client->get_user_subset_summary = rc_client_external_get_user_subset_summary;
|
|
1055
|
+
|
|
1056
|
+
rc_client_get_user_subset_summary(g_client, 1, &summary);
|
|
1057
|
+
|
|
1058
|
+
ASSERT_NUM_EQUALS(summary.num_core_achievements, 0);
|
|
1059
|
+
ASSERT_NUM_EQUALS(summary.num_unlocked_achievements, 0);
|
|
1060
|
+
ASSERT_NUM_EQUALS(summary.num_unofficial_achievements, 0);
|
|
1061
|
+
ASSERT_NUM_EQUALS(summary.num_unsupported_achievements, 0);
|
|
1062
|
+
ASSERT_NUM_EQUALS(summary.points_core, 0);
|
|
1063
|
+
ASSERT_NUM_EQUALS(summary.points_unlocked, 0);
|
|
1064
|
+
ASSERT_NUM_EQUALS(summary.beaten_time, 0);
|
|
1065
|
+
ASSERT_NUM_EQUALS(summary.completed_time, 0);
|
|
1066
|
+
|
|
1067
|
+
rc_client_get_user_subset_summary(g_client, 6, &summary);
|
|
1068
|
+
|
|
1069
|
+
ASSERT_NUM_EQUALS(summary.num_core_achievements, 20);
|
|
1070
|
+
ASSERT_NUM_EQUALS(summary.num_unlocked_achievements, 6);
|
|
1071
|
+
ASSERT_NUM_EQUALS(summary.num_unofficial_achievements, 3);
|
|
1072
|
+
ASSERT_NUM_EQUALS(summary.num_unsupported_achievements, 1);
|
|
1073
|
+
ASSERT_NUM_EQUALS(summary.points_core, 100);
|
|
1074
|
+
ASSERT_NUM_EQUALS(summary.points_unlocked, 23);
|
|
1075
|
+
ASSERT_NUM_EQUALS(summary.beaten_time, 1234567890);
|
|
1076
|
+
ASSERT_NUM_EQUALS(summary.completed_time, 1234598760);
|
|
1077
|
+
|
|
1078
|
+
rc_client_destroy(g_client);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
#ifdef RC_CLIENT_SUPPORTS_HASH
|
|
1082
|
+
|
|
1083
|
+
static void test_identify_and_load_game_external_hash(void)
|
|
1084
|
+
{
|
|
1085
|
+
const size_t image_size = 32768;
|
|
1086
|
+
uint8_t* image = generate_generic_file(image_size);
|
|
1087
|
+
const rc_client_game_t* game;
|
|
1088
|
+
|
|
1089
|
+
g_client = mock_client_with_external();
|
|
1090
|
+
g_client->state.external_client->add_game_hash = rc_client_external_add_game_hash;
|
|
1091
|
+
g_client->state.external_client->begin_load_game = rc_client_external_load_game;
|
|
1092
|
+
g_client->state.external_client->get_game_info_v3 = rc_client_external_get_game_info_v3;
|
|
1093
|
+
|
|
1094
|
+
mock_api_response("r=gameid&m=6a2305a2b6675a97ff792709be1ca857", "{\"Success\":true,\"GameID\":1234}");
|
|
1095
|
+
g_external_int = 0;
|
|
1096
|
+
|
|
1097
|
+
rc_client_begin_identify_and_load_game(g_client, RC_CONSOLE_GAMEBOY, "foo.zip#foo.gb",
|
|
1098
|
+
image, image_size, rc_client_callback_expect_success, g_callback_userdata);
|
|
1099
|
+
|
|
1100
|
+
ASSERT_STR_EQUALS(g_external_event, "load_game"); /* begin_load_game called */
|
|
1101
|
+
ASSERT_NUM_EQUALS(g_external_int, 1234); /* add_game_hash called */
|
|
1102
|
+
ASSERT_PTR_NULL(g_client->state.load);
|
|
1103
|
+
|
|
1104
|
+
/* user data should come from external client. validate structure */
|
|
1105
|
+
game = rc_client_get_game_info(g_client);
|
|
1106
|
+
ASSERT_PTR_NOT_NULL(game);
|
|
1107
|
+
ASSERT_NUM_EQUALS(game->id, 1234);
|
|
1108
|
+
ASSERT_NUM_EQUALS(game->console_id, RC_CONSOLE_PLAYSTATION);
|
|
1109
|
+
ASSERT_STR_EQUALS(game->title, "Game Title");
|
|
1110
|
+
ASSERT_STR_EQUALS(game->hash, "GAME_HASH");
|
|
1111
|
+
ASSERT_STR_EQUALS(game->badge_name, "BDG001");
|
|
1112
|
+
ASSERT_STR_EQUALS(game->badge_url, "/Badge/BDG001.png");
|
|
1113
|
+
|
|
1114
|
+
/* ensure internal client game was initialized to hold media hashes */
|
|
1115
|
+
ASSERT_PTR_NOT_NULL(g_client->game);
|
|
1116
|
+
|
|
1117
|
+
rc_client_destroy(g_client);
|
|
1118
|
+
free(image);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
static void test_identify_and_reload_game_external_hash(void)
|
|
1122
|
+
{
|
|
1123
|
+
const size_t image_size = 32768;
|
|
1124
|
+
uint8_t* image = generate_generic_file(image_size);
|
|
1125
|
+
const rc_client_game_t* game;
|
|
1126
|
+
|
|
1127
|
+
g_client = mock_client_with_external();
|
|
1128
|
+
g_client->state.external_client->add_game_hash = rc_client_external_add_game_hash;
|
|
1129
|
+
g_client->state.external_client->begin_load_game = rc_client_external_load_game;
|
|
1130
|
+
g_client->state.external_client->get_game_info_v3 = rc_client_external_get_game_info_v3;
|
|
1131
|
+
g_client->state.external_client->unload_game = rc_client_external_unload_game;
|
|
1132
|
+
|
|
1133
|
+
mock_api_response("r=gameid&m=6a2305a2b6675a97ff792709be1ca857", "{\"Success\":true,\"GameID\":1234}");
|
|
1134
|
+
g_external_int = 0;
|
|
1135
|
+
|
|
1136
|
+
rc_client_begin_identify_and_load_game(g_client, RC_CONSOLE_GAMEBOY, "foo.zip#foo.gb",
|
|
1137
|
+
image, image_size, rc_client_callback_expect_success, g_callback_userdata);
|
|
1138
|
+
|
|
1139
|
+
ASSERT_STR_EQUALS(g_external_event, "load_game"); /* begin_load_game called */
|
|
1140
|
+
ASSERT_NUM_EQUALS(g_external_int, 1234); /* add_game_hash called */
|
|
1141
|
+
ASSERT_PTR_NULL(g_client->state.load);
|
|
1142
|
+
|
|
1143
|
+
/* user data should come from external client. validate structure */
|
|
1144
|
+
game = rc_client_get_game_info(g_client);
|
|
1145
|
+
ASSERT_PTR_NOT_NULL(game);
|
|
1146
|
+
ASSERT_NUM_EQUALS(game->id, 1234);
|
|
1147
|
+
ASSERT_NUM_EQUALS(game->console_id, RC_CONSOLE_PLAYSTATION);
|
|
1148
|
+
ASSERT_STR_EQUALS(game->title, "Game Title");
|
|
1149
|
+
ASSERT_STR_EQUALS(game->hash, "GAME_HASH");
|
|
1150
|
+
ASSERT_STR_EQUALS(game->badge_name, "BDG001");
|
|
1151
|
+
ASSERT_STR_EQUALS(game->badge_url, "/Badge/BDG001.png");
|
|
1152
|
+
|
|
1153
|
+
/* ensure internal client game was initialized to hold media hashes */
|
|
1154
|
+
ASSERT_PTR_NOT_NULL(g_client->game);
|
|
1155
|
+
|
|
1156
|
+
rc_client_unload_game(g_client);
|
|
1157
|
+
|
|
1158
|
+
rc_client_begin_identify_and_load_game(g_client, RC_CONSOLE_GAMEBOY, "foo.zip#foo.gb",
|
|
1159
|
+
image, image_size, rc_client_callback_expect_success, g_callback_userdata);
|
|
1160
|
+
|
|
1161
|
+
ASSERT_STR_EQUALS(g_external_event, "load_game"); /* begin_load_game called */
|
|
1162
|
+
ASSERT_NUM_EQUALS(g_external_int, 1234); /* add_game_hash called */
|
|
1163
|
+
ASSERT_PTR_NULL(g_client->state.load);
|
|
1164
|
+
|
|
1165
|
+
/* user data should come from external client. validate structure */
|
|
1166
|
+
game = rc_client_get_game_info(g_client);
|
|
1167
|
+
ASSERT_PTR_NOT_NULL(game);
|
|
1168
|
+
ASSERT_NUM_EQUALS(game->id, 1234);
|
|
1169
|
+
ASSERT_NUM_EQUALS(game->console_id, RC_CONSOLE_PLAYSTATION);
|
|
1170
|
+
ASSERT_STR_EQUALS(game->title, "Game Title");
|
|
1171
|
+
ASSERT_STR_EQUALS(game->hash, "GAME_HASH");
|
|
1172
|
+
ASSERT_STR_EQUALS(game->badge_name, "BDG001");
|
|
1173
|
+
ASSERT_STR_EQUALS(game->badge_url, "/Badge/BDG001.png");
|
|
1174
|
+
|
|
1175
|
+
/* ensure internal client game was initialized to hold media hashes */
|
|
1176
|
+
ASSERT_PTR_NOT_NULL(g_client->game);
|
|
1177
|
+
|
|
1178
|
+
rc_client_destroy(g_client);
|
|
1179
|
+
free(image);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
static void assert_change_media(rc_client_t* client, const char* file_path, const uint8_t* data, size_t data_size)
|
|
1183
|
+
{
|
|
1184
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
1185
|
+
ASSERT_STR_EQUALS(file_path, "foo.zip#foo.gb");
|
|
1186
|
+
ASSERT_PTR_NOT_NULL(data);
|
|
1187
|
+
ASSERT_NUM_EQUALS(data_size, 32768);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
static rc_client_async_handle_t* rc_client_external_begin_identify_and_change_media(rc_client_t* client, const char* file_path,
|
|
1191
|
+
const uint8_t* data, size_t data_size, rc_client_callback_t callback, void* callback_userdata)
|
|
1192
|
+
{
|
|
1193
|
+
assert_change_media(client, file_path, data, data_size);
|
|
1194
|
+
|
|
1195
|
+
g_external_event = "change_media";
|
|
1196
|
+
|
|
1197
|
+
callback(RC_OK, NULL, client, callback_userdata);
|
|
1198
|
+
return NULL;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
static void test_change_media(void)
|
|
1202
|
+
{
|
|
1203
|
+
const size_t image_size = 32768;
|
|
1204
|
+
uint8_t* image = generate_generic_file(image_size);
|
|
1205
|
+
|
|
1206
|
+
g_client = mock_client_with_external();
|
|
1207
|
+
g_client->state.external_client->begin_identify_and_change_media = rc_client_external_begin_identify_and_change_media;
|
|
1208
|
+
|
|
1209
|
+
rc_client_begin_identify_and_change_media(g_client, "foo.zip#foo.gb", image, image_size, rc_client_callback_expect_success, g_callback_userdata);
|
|
1210
|
+
|
|
1211
|
+
ASSERT_STR_EQUALS(g_external_event, "change_media");
|
|
1212
|
+
|
|
1213
|
+
rc_client_destroy(g_client);
|
|
1214
|
+
free(image);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
#endif
|
|
1218
|
+
|
|
1219
|
+
static void assert_change_media_from_hash(rc_client_t* client, const char* hash)
|
|
1220
|
+
{
|
|
1221
|
+
ASSERT_PTR_EQUALS(client, g_client);
|
|
1222
|
+
ASSERT_STR_EQUALS(hash, "6a2305a2b6675a97ff792709be1ca857");
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
static rc_client_async_handle_t* rc_client_external_begin_change_media(rc_client_t* client, const char* hash,
|
|
1226
|
+
rc_client_callback_t callback, void* callback_userdata)
|
|
1227
|
+
{
|
|
1228
|
+
assert_change_media_from_hash(client, hash);
|
|
1229
|
+
|
|
1230
|
+
g_external_event = "change_media_from_hash";
|
|
1231
|
+
|
|
1232
|
+
callback(RC_OK, NULL, client, callback_userdata);
|
|
1233
|
+
return NULL;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
static void test_change_media_from_hash(void)
|
|
1237
|
+
{
|
|
1238
|
+
g_client = mock_client_with_external();
|
|
1239
|
+
g_client->state.external_client->begin_change_media = rc_client_external_begin_change_media;
|
|
1240
|
+
|
|
1241
|
+
rc_client_begin_change_media(g_client, "6a2305a2b6675a97ff792709be1ca857", rc_client_callback_expect_success, g_callback_userdata);
|
|
1242
|
+
|
|
1243
|
+
ASSERT_STR_EQUALS(g_external_event, "change_media_from_hash");
|
|
1244
|
+
|
|
1245
|
+
rc_client_destroy(g_client);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
static const rc_client_subset_t* rc_client_external_get_subset_info_v1(uint32_t subset_id)
|
|
1249
|
+
{
|
|
1250
|
+
v1_rc_client_subset_t* subset = (v1_rc_client_subset_t*)
|
|
1251
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v1_rc_client_subset_t));
|
|
1252
|
+
|
|
1253
|
+
memset(subset, 0, sizeof(*subset));
|
|
1254
|
+
subset->id = subset_id;
|
|
1255
|
+
subset->title = "Subset Title";
|
|
1256
|
+
snprintf(subset->badge_name, sizeof(subset->badge_name), "%s", "BDG001");
|
|
1257
|
+
subset->num_achievements = 2;
|
|
1258
|
+
subset->num_leaderboards = 1;
|
|
1259
|
+
|
|
1260
|
+
return (const rc_client_subset_t*)subset;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
static const rc_client_subset_t* rc_client_external_get_subset_info_v3(uint32_t subset_id)
|
|
1264
|
+
{
|
|
1265
|
+
v3_rc_client_subset_t* subset = (v3_rc_client_subset_t*)
|
|
1266
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v3_rc_client_subset_t));
|
|
1267
|
+
|
|
1268
|
+
memset(subset, 0, sizeof(*subset));
|
|
1269
|
+
subset->id = subset_id;
|
|
1270
|
+
subset->title = "Subset Title";
|
|
1271
|
+
snprintf(subset->badge_name, sizeof(subset->badge_name), "%s", "BDG001");
|
|
1272
|
+
subset->num_achievements = 2;
|
|
1273
|
+
subset->num_leaderboards = 1;
|
|
1274
|
+
subset->badge_url = "/Badge/BDG001.png";
|
|
1275
|
+
|
|
1276
|
+
return (const rc_client_subset_t*)subset;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
static void test_v1_subset_field_offsets(void)
|
|
1280
|
+
{
|
|
1281
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v1_rc_client_subset_t, id);
|
|
1282
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v1_rc_client_subset_t, title);
|
|
1283
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v1_rc_client_subset_t, badge_name);
|
|
1284
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v1_rc_client_subset_t, num_achievements);
|
|
1285
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v1_rc_client_subset_t, num_leaderboards);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
static void test_v3_subset_field_offsets(void)
|
|
1289
|
+
{
|
|
1290
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v3_rc_client_subset_t, id);
|
|
1291
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v3_rc_client_subset_t, title);
|
|
1292
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v3_rc_client_subset_t, badge_name);
|
|
1293
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v3_rc_client_subset_t, num_achievements);
|
|
1294
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v3_rc_client_subset_t, num_leaderboards);
|
|
1295
|
+
ASSERT_FIELD_OFFSET(rc_client_subset_t, v3_rc_client_subset_t, badge_url);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
static void test_get_subset_info_v1(void)
|
|
1299
|
+
{
|
|
1300
|
+
const rc_client_subset_t* subset;
|
|
1301
|
+
|
|
1302
|
+
g_client = mock_client_with_external();
|
|
1303
|
+
g_client->state.external_client->get_subset_info = rc_client_external_get_subset_info_v1;
|
|
1304
|
+
|
|
1305
|
+
/* subset data should come from external client. validate structure */
|
|
1306
|
+
subset = rc_client_get_subset_info(g_client, 1234);
|
|
1307
|
+
ASSERT_PTR_NOT_NULL(subset);
|
|
1308
|
+
ASSERT_NUM_EQUALS(subset->id, 1234);
|
|
1309
|
+
ASSERT_STR_EQUALS(subset->title, "Subset Title");
|
|
1310
|
+
ASSERT_STR_EQUALS(subset->badge_name, "BDG001");
|
|
1311
|
+
ASSERT_NUM_EQUALS(subset->num_achievements, 2);
|
|
1312
|
+
ASSERT_NUM_EQUALS(subset->num_leaderboards, 1);
|
|
1313
|
+
ASSERT_STR_EQUALS(subset->badge_url, "https://media.retroachievements.org/Images/BDG001.png");
|
|
1314
|
+
|
|
1315
|
+
rc_client_destroy(g_client);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
static void test_get_subset_info(void)
|
|
1319
|
+
{
|
|
1320
|
+
const rc_client_subset_t* subset;
|
|
1321
|
+
|
|
1322
|
+
g_client = mock_client_with_external();
|
|
1323
|
+
g_client->state.external_client->get_subset_info_v3 = rc_client_external_get_subset_info_v3;
|
|
1324
|
+
|
|
1325
|
+
/* subset data should come from external client. validate structure */
|
|
1326
|
+
subset = rc_client_get_subset_info(g_client, 1234);
|
|
1327
|
+
ASSERT_PTR_NOT_NULL(subset);
|
|
1328
|
+
ASSERT_NUM_EQUALS(subset->id, 1234);
|
|
1329
|
+
ASSERT_STR_EQUALS(subset->title, "Subset Title");
|
|
1330
|
+
ASSERT_STR_EQUALS(subset->badge_name, "BDG001");
|
|
1331
|
+
ASSERT_NUM_EQUALS(subset->num_achievements, 2);
|
|
1332
|
+
ASSERT_NUM_EQUALS(subset->num_leaderboards, 1);
|
|
1333
|
+
ASSERT_STR_EQUALS(subset->badge_url, "/Badge/BDG001.png");
|
|
1334
|
+
|
|
1335
|
+
rc_client_destroy(g_client);
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
static void rc_client_external_destroy_subset_list(rc_client_subset_list_info_t* list)
|
|
1339
|
+
{
|
|
1340
|
+
g_external_event = "destroyed";
|
|
1341
|
+
free(list);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
static rc_client_subset_list_info_t* rc_client_external_create_subset_list_v6()
|
|
1345
|
+
{
|
|
1346
|
+
rc_client_subset_list_info_t* list;
|
|
1347
|
+
|
|
1348
|
+
list = (rc_client_subset_list_info_t*)calloc(1, sizeof(*list) + sizeof(rc_client_subset_t*) * 2);
|
|
1349
|
+
if (list) {
|
|
1350
|
+
const rc_client_subset_t** subset;
|
|
1351
|
+
rc_client_subset_t* mutable_subset;
|
|
1352
|
+
list->public_.num_subsets = 2;
|
|
1353
|
+
list->public_.subsets = subset = (const rc_client_subset_t**)((uint8_t*)list + sizeof(*list));
|
|
1354
|
+
*subset++ = rc_client_external_get_subset_info_v3(1111);
|
|
1355
|
+
*subset = rc_client_external_get_subset_info_v3(2345);
|
|
1356
|
+
mutable_subset = (rc_client_subset_t*)*subset;
|
|
1357
|
+
mutable_subset->title = "Bonus";
|
|
1358
|
+
mutable_subset->num_achievements = 1;
|
|
1359
|
+
mutable_subset->num_leaderboards = 0;
|
|
1360
|
+
|
|
1361
|
+
list->destroy_func = rc_client_external_destroy_subset_list;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
return list;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
static void test_create_subset_list(void)
|
|
1368
|
+
{
|
|
1369
|
+
rc_client_subset_list_t* list;
|
|
1370
|
+
|
|
1371
|
+
g_client = mock_client_with_external();
|
|
1372
|
+
g_client->state.external_client->create_subset_list = rc_client_external_create_subset_list_v6;
|
|
1373
|
+
|
|
1374
|
+
list = rc_client_create_subset_list(g_client);
|
|
1375
|
+
ASSERT_PTR_NOT_NULL(list);
|
|
1376
|
+
ASSERT_NUM_EQUALS(list->num_subsets, 2);
|
|
1377
|
+
ASSERT_PTR_NOT_NULL(list->subsets);
|
|
1378
|
+
ASSERT_NUM_EQUALS(list->subsets[0]->id, 1111);
|
|
1379
|
+
ASSERT_STR_EQUALS(list->subsets[0]->title, "Subset Title");
|
|
1380
|
+
ASSERT_STR_EQUALS(list->subsets[0]->badge_name, "BDG001");
|
|
1381
|
+
ASSERT_NUM_EQUALS(list->subsets[0]->num_achievements, 2);
|
|
1382
|
+
ASSERT_NUM_EQUALS(list->subsets[0]->num_leaderboards, 1);
|
|
1383
|
+
ASSERT_STR_EQUALS(list->subsets[0]->badge_url, "/Badge/BDG001.png");
|
|
1384
|
+
ASSERT_NUM_EQUALS(list->subsets[1]->id, 2345);
|
|
1385
|
+
ASSERT_STR_EQUALS(list->subsets[1]->title, "Bonus");
|
|
1386
|
+
ASSERT_STR_EQUALS(list->subsets[1]->badge_name, "BDG001");
|
|
1387
|
+
ASSERT_NUM_EQUALS(list->subsets[1]->num_achievements, 1);
|
|
1388
|
+
ASSERT_NUM_EQUALS(list->subsets[1]->num_leaderboards, 0);
|
|
1389
|
+
ASSERT_STR_EQUALS(list->subsets[1]->badge_url, "/Badge/BDG001.png");
|
|
1390
|
+
|
|
1391
|
+
rc_client_destroy_subset_list(list);
|
|
1392
|
+
|
|
1393
|
+
ASSERT_STR_EQUALS(g_external_event, "destroyed");
|
|
1394
|
+
|
|
1395
|
+
rc_client_destroy(g_client);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
static void test_unload_game(void)
|
|
1399
|
+
{
|
|
1400
|
+
g_client = mock_client_with_external();
|
|
1401
|
+
g_client->state.external_client->unload_game = rc_client_external_unload_game;
|
|
1402
|
+
|
|
1403
|
+
rc_client_unload_game(g_client);
|
|
1404
|
+
|
|
1405
|
+
ASSERT_STR_EQUALS(g_external_event, "unload_game");
|
|
1406
|
+
|
|
1407
|
+
rc_client_destroy(g_client);
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
/* ----- achievements ----- */
|
|
1411
|
+
|
|
1412
|
+
static void test_v1_achievement_field_offsets(void)
|
|
1413
|
+
{
|
|
1414
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, id);
|
|
1415
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, description);
|
|
1416
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, badge_name);
|
|
1417
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, measured_progress);
|
|
1418
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, measured_percent);
|
|
1419
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, id);
|
|
1420
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, points);
|
|
1421
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, unlock_time);
|
|
1422
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, state);
|
|
1423
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, category);
|
|
1424
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, bucket);
|
|
1425
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, unlocked);
|
|
1426
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, rarity);
|
|
1427
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, rarity_hardcore);
|
|
1428
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v1_rc_client_achievement_t, type);
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
static void test_v3_achievement_field_offsets(void)
|
|
1432
|
+
{
|
|
1433
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, id);
|
|
1434
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, description);
|
|
1435
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, badge_name);
|
|
1436
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, measured_progress);
|
|
1437
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, measured_percent);
|
|
1438
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, id);
|
|
1439
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, points);
|
|
1440
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, unlock_time);
|
|
1441
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, state);
|
|
1442
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, category);
|
|
1443
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, bucket);
|
|
1444
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, unlocked);
|
|
1445
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, rarity);
|
|
1446
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, rarity_hardcore);
|
|
1447
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, type);
|
|
1448
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, badge_url);
|
|
1449
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_t, v3_rc_client_achievement_t, badge_locked_url);
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
static const rc_client_achievement_t* rc_client_external_get_achievement_info_v1(uint32_t id)
|
|
1453
|
+
{
|
|
1454
|
+
v1_rc_client_achievement_t* achievement = (v1_rc_client_achievement_t*)
|
|
1455
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v1_rc_client_achievement_t));
|
|
1456
|
+
|
|
1457
|
+
memset(achievement, 0, sizeof(*achievement));
|
|
1458
|
+
achievement->id = id;
|
|
1459
|
+
achievement->title = "Achievement Title";
|
|
1460
|
+
achievement->description = "Do something cool";
|
|
1461
|
+
memcpy(achievement->badge_name, "BDG1234", 8);
|
|
1462
|
+
achievement->measured_percent = 33.5;
|
|
1463
|
+
achievement->points = 5;
|
|
1464
|
+
achievement->state = RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE;
|
|
1465
|
+
achievement->category = RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE;
|
|
1466
|
+
achievement->bucket = RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED;
|
|
1467
|
+
achievement->unlocked = RC_CLIENT_ACHIEVEMENT_UNLOCKED_NONE;
|
|
1468
|
+
achievement->rarity = 75.0f;
|
|
1469
|
+
achievement->rarity_hardcore = 66.66f;
|
|
1470
|
+
achievement->type = RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE;
|
|
1471
|
+
|
|
1472
|
+
return (const rc_client_achievement_t*)achievement;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
static const rc_client_achievement_t* rc_client_external_get_achievement_info_v1_not_found(uint32_t id)
|
|
1476
|
+
{
|
|
1477
|
+
return NULL;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
static const rc_client_achievement_t* rc_client_external_get_achievement_info_v3(uint32_t id)
|
|
1481
|
+
{
|
|
1482
|
+
v3_rc_client_achievement_t* achievement = (v3_rc_client_achievement_t*)
|
|
1483
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v3_rc_client_achievement_t));
|
|
1484
|
+
|
|
1485
|
+
memset(achievement, 0, sizeof(*achievement));
|
|
1486
|
+
achievement->id = id;
|
|
1487
|
+
achievement->title = "Achievement Title";
|
|
1488
|
+
achievement->description = "Do something cool";
|
|
1489
|
+
memcpy(achievement->badge_name, "BDG1234", 8);
|
|
1490
|
+
achievement->measured_percent = 33.5;
|
|
1491
|
+
achievement->points = 5;
|
|
1492
|
+
achievement->state = RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE;
|
|
1493
|
+
achievement->category = RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE;
|
|
1494
|
+
achievement->bucket = RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED;
|
|
1495
|
+
achievement->unlocked = RC_CLIENT_ACHIEVEMENT_UNLOCKED_NONE;
|
|
1496
|
+
achievement->rarity = 75.0f;
|
|
1497
|
+
achievement->rarity_hardcore = 66.66f;
|
|
1498
|
+
achievement->type = RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE;
|
|
1499
|
+
achievement->badge_url = "/Badge/000234.png";
|
|
1500
|
+
achievement->badge_locked_url = "/Badge/000234_locked.png";
|
|
1501
|
+
return (const rc_client_achievement_t*)achievement;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
static void test_has_achievements(void)
|
|
1505
|
+
{
|
|
1506
|
+
g_client = mock_client_with_external();
|
|
1507
|
+
g_client->state.external_client->has_achievements = rc_client_external_get_int;
|
|
1508
|
+
|
|
1509
|
+
g_external_int = 0;
|
|
1510
|
+
ASSERT_NUM_EQUALS(rc_client_has_achievements(g_client), 0);
|
|
1511
|
+
|
|
1512
|
+
g_external_int = 1;
|
|
1513
|
+
ASSERT_NUM_EQUALS(rc_client_has_achievements(g_client), 1);
|
|
1514
|
+
|
|
1515
|
+
rc_client_destroy(g_client);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
static void test_get_achievement_info_v1(void)
|
|
1519
|
+
{
|
|
1520
|
+
const rc_client_achievement_t* achievement;
|
|
1521
|
+
|
|
1522
|
+
g_client = mock_client_with_external();
|
|
1523
|
+
g_client->state.external_client->get_achievement_info = rc_client_external_get_achievement_info_v1;
|
|
1524
|
+
|
|
1525
|
+
achievement = rc_client_get_achievement_info(g_client, 4);
|
|
1526
|
+
ASSERT_PTR_NOT_NULL(achievement);
|
|
1527
|
+
ASSERT_NUM_EQUALS(achievement->id, 4);
|
|
1528
|
+
ASSERT_STR_EQUALS(achievement->title, "Achievement Title");
|
|
1529
|
+
ASSERT_STR_EQUALS(achievement->description, "Do something cool");
|
|
1530
|
+
ASSERT_STR_EQUALS(achievement->badge_name, "BDG1234");
|
|
1531
|
+
ASSERT_FLOAT_EQUALS(achievement->measured_percent, 33.5);
|
|
1532
|
+
ASSERT_NUM_EQUALS(achievement->points, 5);
|
|
1533
|
+
ASSERT_NUM_EQUALS(achievement->state, RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE);
|
|
1534
|
+
ASSERT_NUM_EQUALS(achievement->category, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE);
|
|
1535
|
+
ASSERT_NUM_EQUALS(achievement->bucket, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED);
|
|
1536
|
+
ASSERT_NUM_EQUALS(achievement->unlocked, RC_CLIENT_ACHIEVEMENT_UNLOCKED_NONE);
|
|
1537
|
+
ASSERT_FLOAT_EQUALS(achievement->rarity, 75.0f);
|
|
1538
|
+
ASSERT_FLOAT_EQUALS(achievement->rarity_hardcore, 66.66f);
|
|
1539
|
+
ASSERT_NUM_EQUALS(achievement->type, RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE);
|
|
1540
|
+
ASSERT_STR_EQUALS(achievement->badge_url, "https://media.retroachievements.org/Badge/BDG1234.png");
|
|
1541
|
+
ASSERT_STR_EQUALS(achievement->badge_locked_url, "https://media.retroachievements.org/Badge/BDG1234_lock.png");
|
|
1542
|
+
|
|
1543
|
+
rc_client_destroy(g_client);
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
static void test_get_achievement_info_v1_not_found(void)
|
|
1547
|
+
{
|
|
1548
|
+
const rc_client_achievement_t* achievement;
|
|
1549
|
+
|
|
1550
|
+
g_client = mock_client_with_external();
|
|
1551
|
+
g_client->state.external_client->get_achievement_info = rc_client_external_get_achievement_info_v1_not_found;
|
|
1552
|
+
|
|
1553
|
+
achievement = rc_client_get_achievement_info(g_client, 4);
|
|
1554
|
+
ASSERT_PTR_NULL(achievement);
|
|
1555
|
+
|
|
1556
|
+
rc_client_destroy(g_client);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
static void test_get_achievement_info(void)
|
|
1560
|
+
{
|
|
1561
|
+
const rc_client_achievement_t* achievement;
|
|
1562
|
+
|
|
1563
|
+
g_client = mock_client_with_external();
|
|
1564
|
+
g_client->state.external_client->get_achievement_info = rc_client_external_get_achievement_info_v1;
|
|
1565
|
+
g_client->state.external_client->get_achievement_info_v3 = rc_client_external_get_achievement_info_v3;
|
|
1566
|
+
|
|
1567
|
+
achievement = rc_client_get_achievement_info(g_client, 4);
|
|
1568
|
+
ASSERT_PTR_NOT_NULL(achievement);
|
|
1569
|
+
ASSERT_NUM_EQUALS(achievement->id, 4);
|
|
1570
|
+
ASSERT_STR_EQUALS(achievement->title, "Achievement Title");
|
|
1571
|
+
ASSERT_STR_EQUALS(achievement->description, "Do something cool");
|
|
1572
|
+
ASSERT_STR_EQUALS(achievement->badge_name, "BDG1234");
|
|
1573
|
+
ASSERT_FLOAT_EQUALS(achievement->measured_percent, 33.5);
|
|
1574
|
+
ASSERT_NUM_EQUALS(achievement->points, 5);
|
|
1575
|
+
ASSERT_NUM_EQUALS(achievement->state, RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE);
|
|
1576
|
+
ASSERT_NUM_EQUALS(achievement->category, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE);
|
|
1577
|
+
ASSERT_NUM_EQUALS(achievement->bucket, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED);
|
|
1578
|
+
ASSERT_NUM_EQUALS(achievement->unlocked, RC_CLIENT_ACHIEVEMENT_UNLOCKED_NONE);
|
|
1579
|
+
ASSERT_FLOAT_EQUALS(achievement->rarity, 75.0f);
|
|
1580
|
+
ASSERT_FLOAT_EQUALS(achievement->rarity_hardcore, 66.66f);
|
|
1581
|
+
ASSERT_NUM_EQUALS(achievement->type, RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE);
|
|
1582
|
+
ASSERT_STR_EQUALS(achievement->badge_url, "/Badge/000234.png");
|
|
1583
|
+
ASSERT_STR_EQUALS(achievement->badge_locked_url, "/Badge/000234_locked.png");
|
|
1584
|
+
|
|
1585
|
+
rc_client_destroy(g_client);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
static const rc_client_achievement_t* rc_client_external_get_next_achievement_info(uint32_t id, int grouping)
|
|
1589
|
+
{
|
|
1590
|
+
if (grouping != RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED)
|
|
1591
|
+
return NULL;
|
|
1592
|
+
|
|
1593
|
+
return rc_client_external_get_achievement_info_v3((id + 1) * 4);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
static void test_get_next_achievement_info(void)
|
|
1597
|
+
{
|
|
1598
|
+
const rc_client_achievement_t* achievement;
|
|
1599
|
+
|
|
1600
|
+
g_client = mock_client_with_external();
|
|
1601
|
+
g_client->state.external_client->get_next_achievement_info = rc_client_external_get_next_achievement_info;
|
|
1602
|
+
|
|
1603
|
+
achievement = rc_client_get_next_achievement_info(g_client, NULL, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED);
|
|
1604
|
+
ASSERT_PTR_NOT_NULL(achievement);
|
|
1605
|
+
ASSERT_NUM_EQUALS(achievement->id, 4);
|
|
1606
|
+
|
|
1607
|
+
achievement = rc_client_get_next_achievement_info(g_client, achievement, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED);
|
|
1608
|
+
ASSERT_PTR_NOT_NULL(achievement);
|
|
1609
|
+
ASSERT_NUM_EQUALS(achievement->id, 20);
|
|
1610
|
+
|
|
1611
|
+
rc_client_destroy(g_client);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
static void test_v1_achievement_list_field_offsets(void)
|
|
1615
|
+
{
|
|
1616
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_list_info_t, v1_rc_client_achievement_list_info_t, public_);
|
|
1617
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_list_info_t, v1_rc_client_achievement_list_info_t, destroy_func);
|
|
1618
|
+
|
|
1619
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_list_t, v1_rc_client_achievement_list_t, buckets);
|
|
1620
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_list_t, v1_rc_client_achievement_list_t, num_buckets);
|
|
1621
|
+
|
|
1622
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_bucket_t, v1_rc_client_achievement_bucket_t, achievements);
|
|
1623
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_bucket_t, v1_rc_client_achievement_bucket_t, num_achievements);
|
|
1624
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_bucket_t, v1_rc_client_achievement_bucket_t, label);
|
|
1625
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_bucket_t, v1_rc_client_achievement_bucket_t, subset_id);
|
|
1626
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_bucket_t, v1_rc_client_achievement_bucket_t, bucket_type);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
static void test_v3_achievement_list_field_offsets(void)
|
|
1630
|
+
{
|
|
1631
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_list_info_t, v3_rc_client_achievement_list_info_t, public_);
|
|
1632
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_list_info_t, v3_rc_client_achievement_list_info_t, destroy_func);
|
|
1633
|
+
|
|
1634
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_list_t, v3_rc_client_achievement_list_t, buckets);
|
|
1635
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_list_t, v3_rc_client_achievement_list_t, num_buckets);
|
|
1636
|
+
|
|
1637
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_bucket_t, v3_rc_client_achievement_bucket_t, achievements);
|
|
1638
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_bucket_t, v3_rc_client_achievement_bucket_t, num_achievements);
|
|
1639
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_bucket_t, v3_rc_client_achievement_bucket_t, label);
|
|
1640
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_bucket_t, v3_rc_client_achievement_bucket_t, subset_id);
|
|
1641
|
+
ASSERT_FIELD_OFFSET(rc_client_achievement_bucket_t, v3_rc_client_achievement_bucket_t, bucket_type);
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
static void assert_achievement_list_category_grouping(int category, int grouping)
|
|
1645
|
+
{
|
|
1646
|
+
ASSERT_NUM_EQUALS(category, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE);
|
|
1647
|
+
ASSERT_NUM_EQUALS(grouping, RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_PROGRESS);
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
static void rc_client_external_destroy_achievement_list(rc_client_achievement_list_info_t* list)
|
|
1651
|
+
{
|
|
1652
|
+
g_external_event = "destroyed";
|
|
1653
|
+
free(list);
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
static rc_client_achievement_list_info_t* rc_client_external_create_achievement_list_v1(int category, int grouping)
|
|
1657
|
+
{
|
|
1658
|
+
v1_rc_client_achievement_list_info_t* list;
|
|
1659
|
+
|
|
1660
|
+
assert_achievement_list_category_grouping(category, grouping);
|
|
1661
|
+
|
|
1662
|
+
list = (v1_rc_client_achievement_list_info_t*)calloc(1, sizeof(*list) + sizeof(v1_rc_client_achievement_bucket_t) + sizeof(v1_rc_client_achievement_t*) * 2);
|
|
1663
|
+
if (list) {
|
|
1664
|
+
list->public_.num_buckets = 1;
|
|
1665
|
+
list->public_.buckets = (v1_rc_client_achievement_bucket_t*)((uint8_t*)list + sizeof(*list));
|
|
1666
|
+
list->public_.buckets[0].achievements = (v1_rc_client_achievement_t**)((uint8_t*)list->public_.buckets + sizeof(*list->public_.buckets));
|
|
1667
|
+
list->public_.buckets[0].achievements[0] = (v1_rc_client_achievement_t*)rc_client_external_get_achievement_info_v1(1234);
|
|
1668
|
+
list->public_.buckets[0].achievements[1] = (v1_rc_client_achievement_t*)rc_client_external_get_achievement_info_v1(1235);
|
|
1669
|
+
list->public_.buckets[0].num_achievements = 2;
|
|
1670
|
+
list->public_.buckets[0].bucket_type = RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED;
|
|
1671
|
+
list->public_.buckets[0].label = "Locked";
|
|
1672
|
+
list->public_.buckets[0].subset_id = 1234;
|
|
1673
|
+
|
|
1674
|
+
list->destroy_func = rc_client_external_destroy_achievement_list;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
return (rc_client_achievement_list_info_t*)list;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
static rc_client_achievement_list_info_t* rc_client_external_create_achievement_list_v3(int category, int grouping)
|
|
1681
|
+
{
|
|
1682
|
+
v3_rc_client_achievement_list_info_t* list;
|
|
1683
|
+
|
|
1684
|
+
assert_achievement_list_category_grouping(category, grouping);
|
|
1685
|
+
|
|
1686
|
+
list = (v3_rc_client_achievement_list_info_t*)calloc(1, sizeof(*list) + sizeof(v3_rc_client_achievement_bucket_t) + sizeof(v3_rc_client_achievement_t*) * 2);
|
|
1687
|
+
if (list) {
|
|
1688
|
+
v3_rc_client_achievement_bucket_t* bucket;
|
|
1689
|
+
list->public_.num_buckets = 1;
|
|
1690
|
+
list->public_.buckets = bucket = (v3_rc_client_achievement_bucket_t*)((uint8_t*)list + sizeof(*list));
|
|
1691
|
+
bucket->achievements = (const v3_rc_client_achievement_t**)((uint8_t*)list->public_.buckets + sizeof(*list->public_.buckets));
|
|
1692
|
+
bucket->achievements[0] = (const v3_rc_client_achievement_t*)rc_client_external_get_achievement_info_v3(1234);
|
|
1693
|
+
bucket->achievements[1] = (const v3_rc_client_achievement_t*)rc_client_external_get_achievement_info_v3(1235);
|
|
1694
|
+
bucket->num_achievements = 2;
|
|
1695
|
+
bucket->bucket_type = RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED;
|
|
1696
|
+
bucket->label = "Locked";
|
|
1697
|
+
bucket->subset_id = 1234;
|
|
1698
|
+
|
|
1699
|
+
list->destroy_func = rc_client_external_destroy_achievement_list;
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
return (rc_client_achievement_list_info_t*)list;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
static void test_create_achievement_list_v1(void)
|
|
1706
|
+
{
|
|
1707
|
+
rc_client_achievement_list_t* list;
|
|
1708
|
+
|
|
1709
|
+
g_client = mock_client_with_external();
|
|
1710
|
+
g_client->state.external_client->create_achievement_list = rc_client_external_create_achievement_list_v1;
|
|
1711
|
+
|
|
1712
|
+
list = rc_client_create_achievement_list(g_client, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE, RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_PROGRESS);
|
|
1713
|
+
ASSERT_PTR_NOT_NULL(list);
|
|
1714
|
+
ASSERT_NUM_EQUALS(list->num_buckets, 1);
|
|
1715
|
+
ASSERT_PTR_NOT_NULL(list->buckets);
|
|
1716
|
+
ASSERT_NUM_EQUALS(list->buckets[0].num_achievements, 2);
|
|
1717
|
+
ASSERT_NUM_EQUALS(list->buckets[0].achievements[0]->id, 1234);
|
|
1718
|
+
ASSERT_NUM_EQUALS(list->buckets[0].achievements[1]->id, 1235);
|
|
1719
|
+
ASSERT_NUM_EQUALS(list->buckets[0].bucket_type, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED);
|
|
1720
|
+
ASSERT_NUM_EQUALS(list->buckets[0].subset_id, 1234);
|
|
1721
|
+
ASSERT_STR_EQUALS(list->buckets[0].label, "Locked");
|
|
1722
|
+
|
|
1723
|
+
ASSERT_STR_EQUALS(list->buckets[0].achievements[0]->badge_url, "https://media.retroachievements.org/Badge/BDG1234.png");
|
|
1724
|
+
ASSERT_STR_EQUALS(list->buckets[0].achievements[0]->badge_locked_url, "https://media.retroachievements.org/Badge/BDG1234_lock.png");
|
|
1725
|
+
|
|
1726
|
+
rc_client_destroy_achievement_list(list);
|
|
1727
|
+
|
|
1728
|
+
ASSERT_STR_EQUALS(g_external_event, "destroyed");
|
|
1729
|
+
|
|
1730
|
+
rc_client_destroy(g_client);
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
static void test_create_achievement_list(void)
|
|
1734
|
+
{
|
|
1735
|
+
rc_client_achievement_list_t* list;
|
|
1736
|
+
|
|
1737
|
+
g_client = mock_client_with_external();
|
|
1738
|
+
g_client->state.external_client->create_achievement_list = rc_client_external_create_achievement_list_v1;
|
|
1739
|
+
g_client->state.external_client->create_achievement_list_v3 = rc_client_external_create_achievement_list_v3;
|
|
1740
|
+
|
|
1741
|
+
list = rc_client_create_achievement_list(g_client, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE, RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_PROGRESS);
|
|
1742
|
+
ASSERT_PTR_NOT_NULL(list);
|
|
1743
|
+
ASSERT_NUM_EQUALS(list->num_buckets, 1);
|
|
1744
|
+
ASSERT_PTR_NOT_NULL(list->buckets);
|
|
1745
|
+
ASSERT_NUM_EQUALS(list->buckets[0].num_achievements, 2);
|
|
1746
|
+
ASSERT_NUM_EQUALS(list->buckets[0].achievements[0]->id, 1234);
|
|
1747
|
+
ASSERT_NUM_EQUALS(list->buckets[0].achievements[1]->id, 1235);
|
|
1748
|
+
ASSERT_NUM_EQUALS(list->buckets[0].bucket_type, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED);
|
|
1749
|
+
ASSERT_NUM_EQUALS(list->buckets[0].subset_id, 1234);
|
|
1750
|
+
ASSERT_STR_EQUALS(list->buckets[0].label, "Locked");
|
|
1751
|
+
|
|
1752
|
+
// only difference between v1 and v3 create_achievement_list is the badge_url/badge_unlocked_url fields on each achievement
|
|
1753
|
+
ASSERT_STR_EQUALS(list->buckets[0].achievements[0]->badge_url, "/Badge/000234.png");
|
|
1754
|
+
ASSERT_STR_EQUALS(list->buckets[0].achievements[0]->badge_locked_url, "/Badge/000234_locked.png");
|
|
1755
|
+
|
|
1756
|
+
rc_client_destroy_achievement_list(list);
|
|
1757
|
+
|
|
1758
|
+
ASSERT_STR_EQUALS(g_external_event, "destroyed");
|
|
1759
|
+
|
|
1760
|
+
rc_client_destroy(g_client);
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
/* ----- leaderboards ----- */
|
|
1764
|
+
|
|
1765
|
+
typedef struct v1_rc_client_leaderboard_t {
|
|
1766
|
+
const char* title;
|
|
1767
|
+
const char* description;
|
|
1768
|
+
const char* tracker_value;
|
|
1769
|
+
uint32_t id;
|
|
1770
|
+
uint8_t state;
|
|
1771
|
+
uint8_t format;
|
|
1772
|
+
uint8_t lower_is_better;
|
|
1773
|
+
} v1_rc_client_leaderboard_t;
|
|
1774
|
+
|
|
1775
|
+
static const rc_client_leaderboard_t* rc_client_external_get_leaderboard_info(uint32_t id)
|
|
1776
|
+
{
|
|
1777
|
+
v1_rc_client_leaderboard_t* leaderboard = (v1_rc_client_leaderboard_t*)
|
|
1778
|
+
rc_buffer_alloc(&g_client->state.buffer, sizeof(v1_rc_client_leaderboard_t));
|
|
1779
|
+
|
|
1780
|
+
memset(leaderboard, 0, sizeof(*leaderboard));
|
|
1781
|
+
leaderboard->id = 1234;
|
|
1782
|
+
leaderboard->title = "Leaderboard Title";
|
|
1783
|
+
leaderboard->description = "Do something cool";
|
|
1784
|
+
leaderboard->tracker_value = "000250";
|
|
1785
|
+
leaderboard->state = RC_CLIENT_LEADERBOARD_STATE_ACTIVE;
|
|
1786
|
+
leaderboard->format = RC_CLIENT_LEADERBOARD_FORMAT_SCORE;
|
|
1787
|
+
leaderboard->lower_is_better = 1;
|
|
1788
|
+
|
|
1789
|
+
return (const rc_client_leaderboard_t*)leaderboard;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
typedef struct v1_rc_client_leaderboard_bucket_t {
|
|
1793
|
+
rc_client_leaderboard_t** leaderboards;
|
|
1794
|
+
uint32_t num_leaderboards;
|
|
1795
|
+
|
|
1796
|
+
const char* label;
|
|
1797
|
+
uint32_t subset_id;
|
|
1798
|
+
uint8_t bucket_type;
|
|
1799
|
+
} v1_rc_client_leaderboard_bucket_t;
|
|
1800
|
+
|
|
1801
|
+
typedef struct v1_rc_client_leaderboard_list_t {
|
|
1802
|
+
v1_rc_client_leaderboard_bucket_t* buckets;
|
|
1803
|
+
uint32_t num_buckets;
|
|
1804
|
+
} v1_rc_client_leaderboard_list_t;
|
|
1805
|
+
|
|
1806
|
+
typedef struct v1_rc_client_leaderboard_list_info_t {
|
|
1807
|
+
v1_rc_client_leaderboard_list_t public_;
|
|
1808
|
+
rc_client_destroy_leaderboard_list_func_t destroy_func;
|
|
1809
|
+
} v1_rc_client_leaderboard_list_info_t;
|
|
1810
|
+
|
|
1811
|
+
static void assert_leaderboard_list_grouping(int grouping)
|
|
1812
|
+
{
|
|
1813
|
+
ASSERT_NUM_EQUALS(grouping, RC_CLIENT_LEADERBOARD_LIST_GROUPING_TRACKING);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
static void rc_client_external_destroy_leaderboard_list(rc_client_leaderboard_list_info_t* list)
|
|
1817
|
+
{
|
|
1818
|
+
g_external_event = "destroyed";
|
|
1819
|
+
free(list);
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
static rc_client_leaderboard_list_info_t* rc_client_external_create_leaderboard_list(int grouping)
|
|
1823
|
+
{
|
|
1824
|
+
v1_rc_client_leaderboard_list_info_t* list;
|
|
1825
|
+
|
|
1826
|
+
assert_leaderboard_list_grouping(grouping);
|
|
1827
|
+
|
|
1828
|
+
list = (v1_rc_client_leaderboard_list_info_t*)calloc(1, sizeof(*list) + sizeof(v1_rc_client_leaderboard_bucket_t));
|
|
1829
|
+
if (list) {
|
|
1830
|
+
list->public_.num_buckets = 1;
|
|
1831
|
+
list->public_.buckets = (v1_rc_client_leaderboard_bucket_t*)((uint8_t*)list + sizeof(*list));
|
|
1832
|
+
list->public_.buckets[0].num_leaderboards = 2; /* didn't actually allocate these */
|
|
1833
|
+
list->public_.buckets[0].bucket_type = RC_CLIENT_LEADERBOARD_BUCKET_INACTIVE;
|
|
1834
|
+
list->public_.buckets[0].label = "Inactive";
|
|
1835
|
+
list->public_.buckets[0].subset_id = 1234;
|
|
1836
|
+
|
|
1837
|
+
list->destroy_func = rc_client_external_destroy_leaderboard_list;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
return (rc_client_leaderboard_list_info_t*)list;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
static void test_create_leaderboard_list(void)
|
|
1844
|
+
{
|
|
1845
|
+
rc_client_leaderboard_list_t* list;
|
|
1846
|
+
|
|
1847
|
+
g_client = mock_client_with_external();
|
|
1848
|
+
g_client->state.external_client->create_leaderboard_list = rc_client_external_create_leaderboard_list;
|
|
1849
|
+
|
|
1850
|
+
list = rc_client_create_leaderboard_list(g_client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_TRACKING);
|
|
1851
|
+
ASSERT_PTR_NOT_NULL(list);
|
|
1852
|
+
ASSERT_NUM_EQUALS(list->num_buckets, 1);
|
|
1853
|
+
ASSERT_PTR_NOT_NULL(list->buckets);
|
|
1854
|
+
ASSERT_NUM_EQUALS(list->buckets[0].num_leaderboards, 2);
|
|
1855
|
+
ASSERT_NUM_EQUALS(list->buckets[0].bucket_type, RC_CLIENT_LEADERBOARD_BUCKET_INACTIVE);
|
|
1856
|
+
ASSERT_NUM_EQUALS(list->buckets[0].subset_id, 1234);
|
|
1857
|
+
ASSERT_STR_EQUALS(list->buckets[0].label, "Inactive");
|
|
1858
|
+
|
|
1859
|
+
rc_client_destroy_leaderboard_list(list);
|
|
1860
|
+
|
|
1861
|
+
rc_client_destroy(g_client);
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
static void test_has_leaderboards(void)
|
|
1865
|
+
{
|
|
1866
|
+
g_client = mock_client_with_external();
|
|
1867
|
+
g_client->state.external_client->has_leaderboards = rc_client_external_get_int;
|
|
1868
|
+
|
|
1869
|
+
g_external_int = 0;
|
|
1870
|
+
ASSERT_NUM_EQUALS(rc_client_has_leaderboards(g_client), 0);
|
|
1871
|
+
|
|
1872
|
+
g_external_int = 1;
|
|
1873
|
+
ASSERT_NUM_EQUALS(rc_client_has_leaderboards(g_client), 1);
|
|
1874
|
+
|
|
1875
|
+
rc_client_destroy(g_client);
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
static void test_get_leaderboard_info(void)
|
|
1879
|
+
{
|
|
1880
|
+
const rc_client_leaderboard_t* leaderboard;
|
|
1881
|
+
|
|
1882
|
+
g_client = mock_client_with_external();
|
|
1883
|
+
g_client->state.external_client->get_leaderboard_info = rc_client_external_get_leaderboard_info;
|
|
1884
|
+
|
|
1885
|
+
leaderboard = rc_client_get_leaderboard_info(g_client, 4);
|
|
1886
|
+
ASSERT_PTR_NOT_NULL(leaderboard);
|
|
1887
|
+
ASSERT_NUM_EQUALS(leaderboard->id, 1234);
|
|
1888
|
+
ASSERT_STR_EQUALS(leaderboard->title, "Leaderboard Title");
|
|
1889
|
+
ASSERT_STR_EQUALS(leaderboard->description, "Do something cool");
|
|
1890
|
+
ASSERT_STR_EQUALS(leaderboard->tracker_value, "000250");
|
|
1891
|
+
ASSERT_NUM_EQUALS(leaderboard->state, RC_CLIENT_LEADERBOARD_STATE_ACTIVE);
|
|
1892
|
+
ASSERT_NUM_EQUALS(leaderboard->format, RC_CLIENT_LEADERBOARD_FORMAT_SCORE);
|
|
1893
|
+
ASSERT_NUM_EQUALS(leaderboard->lower_is_better, 1);
|
|
1894
|
+
|
|
1895
|
+
rc_client_destroy(g_client);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
/* ----- rich presence ----- */
|
|
1899
|
+
|
|
1900
|
+
static size_t rc_client_external_get_rich_presence_message(char buffer[], size_t buffer_size)
|
|
1901
|
+
{
|
|
1902
|
+
size_t result = snprintf(buffer, buffer_size, "Playing Game Title");
|
|
1903
|
+
if (result >= buffer_size)
|
|
1904
|
+
return (buffer_size - 1);
|
|
1905
|
+
return result;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
static void test_get_rich_presence_message(void)
|
|
1909
|
+
{
|
|
1910
|
+
char buffer[16];
|
|
1911
|
+
size_t result;
|
|
1912
|
+
|
|
1913
|
+
g_client = mock_client_with_external();
|
|
1914
|
+
g_client->state.external_client->get_rich_presence_message = rc_client_external_get_rich_presence_message;
|
|
1915
|
+
|
|
1916
|
+
result = rc_client_get_rich_presence_message(g_client, buffer, sizeof(buffer));
|
|
1917
|
+
|
|
1918
|
+
ASSERT_STR_EQUALS(buffer, "Playing Game Ti");
|
|
1919
|
+
ASSERT_NUM_EQUALS(result, 15);
|
|
1920
|
+
|
|
1921
|
+
rc_client_destroy(g_client);
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
static void test_has_rich_presence(void)
|
|
1925
|
+
{
|
|
1926
|
+
g_client = mock_client_with_external();
|
|
1927
|
+
g_client->state.external_client->has_rich_presence = rc_client_external_get_int;
|
|
1928
|
+
|
|
1929
|
+
g_external_int = 0;
|
|
1930
|
+
ASSERT_NUM_EQUALS(rc_client_has_rich_presence(g_client), 0);
|
|
1931
|
+
|
|
1932
|
+
g_external_int = 1;
|
|
1933
|
+
ASSERT_NUM_EQUALS(rc_client_has_rich_presence(g_client), 1);
|
|
1934
|
+
|
|
1935
|
+
rc_client_destroy(g_client);
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
/* ----- processing ----- */
|
|
1939
|
+
|
|
1940
|
+
static void test_is_processing_required(void)
|
|
1941
|
+
{
|
|
1942
|
+
g_client = mock_client_with_external();
|
|
1943
|
+
g_client->state.external_client->is_processing_required = rc_client_external_get_int;
|
|
1944
|
+
|
|
1945
|
+
g_external_int = 0;
|
|
1946
|
+
ASSERT_NUM_EQUALS(rc_client_is_processing_required(g_client), 0);
|
|
1947
|
+
|
|
1948
|
+
g_external_int = 1;
|
|
1949
|
+
ASSERT_NUM_EQUALS(rc_client_is_processing_required(g_client), 1);
|
|
1950
|
+
|
|
1951
|
+
rc_client_destroy(g_client);
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
static void rc_client_external_do_frame(void)
|
|
1955
|
+
{
|
|
1956
|
+
g_external_event = "do_frame";
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
static void test_do_frame(void)
|
|
1960
|
+
{
|
|
1961
|
+
g_client = mock_client_with_external();
|
|
1962
|
+
g_client->state.external_client->do_frame = rc_client_external_do_frame;
|
|
1963
|
+
|
|
1964
|
+
rc_client_do_frame(g_client);
|
|
1965
|
+
|
|
1966
|
+
ASSERT_STR_EQUALS(g_external_event, "do_frame");
|
|
1967
|
+
|
|
1968
|
+
rc_client_destroy(g_client);
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
static void rc_client_external_idle(void)
|
|
1972
|
+
{
|
|
1973
|
+
g_external_event = "idle";
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
static void test_idle(void)
|
|
1977
|
+
{
|
|
1978
|
+
g_client = mock_client_with_external();
|
|
1979
|
+
g_client->state.external_client->idle = rc_client_external_idle;
|
|
1980
|
+
|
|
1981
|
+
rc_client_idle(g_client);
|
|
1982
|
+
|
|
1983
|
+
ASSERT_STR_EQUALS(g_external_event, "idle");
|
|
1984
|
+
|
|
1985
|
+
rc_client_destroy(g_client);
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
static void rc_client_external_reset(void)
|
|
1989
|
+
{
|
|
1990
|
+
g_external_event = "reset";
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
static int rc_client_external_can_pause(uint32_t* frames_remaining)
|
|
1994
|
+
{
|
|
1995
|
+
*frames_remaining = g_external_int ? 0 : 10;
|
|
1996
|
+
|
|
1997
|
+
return g_external_int;
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
static void test_can_pause(void)
|
|
2001
|
+
{
|
|
2002
|
+
uint32_t frames_remaining;
|
|
2003
|
+
g_client = mock_client_with_external();
|
|
2004
|
+
g_client->state.external_client->can_pause = rc_client_external_can_pause;
|
|
2005
|
+
|
|
2006
|
+
g_external_int = 0;
|
|
2007
|
+
ASSERT_NUM_EQUALS(rc_client_can_pause(g_client, &frames_remaining), 0);
|
|
2008
|
+
ASSERT_NUM_EQUALS(frames_remaining, 10);
|
|
2009
|
+
|
|
2010
|
+
g_external_int = 1;
|
|
2011
|
+
ASSERT_NUM_EQUALS(rc_client_can_pause(g_client, &frames_remaining), 1);
|
|
2012
|
+
ASSERT_NUM_EQUALS(frames_remaining, 0);
|
|
2013
|
+
|
|
2014
|
+
rc_client_destroy(g_client);
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
static void test_reset(void)
|
|
2018
|
+
{
|
|
2019
|
+
g_client = mock_client_with_external();
|
|
2020
|
+
g_client->state.external_client->reset = rc_client_external_reset;
|
|
2021
|
+
|
|
2022
|
+
rc_client_reset(g_client);
|
|
2023
|
+
|
|
2024
|
+
ASSERT_STR_EQUALS(g_external_event, "reset");
|
|
2025
|
+
|
|
2026
|
+
rc_client_destroy(g_client);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
/* ----- progress ----- */
|
|
2030
|
+
|
|
2031
|
+
static size_t rc_client_external_progress_size(void)
|
|
2032
|
+
{
|
|
2033
|
+
return 12345678;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
static void test_progress_size(void)
|
|
2037
|
+
{
|
|
2038
|
+
g_client = mock_client_with_external();
|
|
2039
|
+
g_client->state.external_client->progress_size = rc_client_external_progress_size;
|
|
2040
|
+
|
|
2041
|
+
ASSERT_NUM_EQUALS(rc_client_progress_size(g_client), 12345678);
|
|
2042
|
+
|
|
2043
|
+
rc_client_destroy(g_client);
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
static int rc_client_external_serialize_progress(uint8_t* buffer, size_t size)
|
|
2047
|
+
{
|
|
2048
|
+
memcpy(buffer, "SAVED", 6);
|
|
2049
|
+
|
|
2050
|
+
g_external_event = "serialize_progress";
|
|
2051
|
+
|
|
2052
|
+
return RC_OK;
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
static void test_serialize_progress(void)
|
|
2056
|
+
{
|
|
2057
|
+
int result;
|
|
2058
|
+
uint8_t buffer[8] = { 0 };
|
|
2059
|
+
|
|
2060
|
+
g_client = mock_client_with_external();
|
|
2061
|
+
g_client->state.external_client->serialize_progress = rc_client_external_serialize_progress;
|
|
2062
|
+
|
|
2063
|
+
result = rc_client_serialize_progress(g_client, buffer);
|
|
2064
|
+
|
|
2065
|
+
ASSERT_STR_EQUALS(g_external_event, "serialize_progress");
|
|
2066
|
+
ASSERT_STR_EQUALS(buffer, "SAVED");
|
|
2067
|
+
ASSERT_NUM_EQUALS(result, RC_OK);
|
|
2068
|
+
|
|
2069
|
+
rc_client_destroy(g_client);
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
static int rc_client_external_deserialize_progress(const uint8_t* buffer, size_t size)
|
|
2073
|
+
{
|
|
2074
|
+
if (memcmp(buffer, "SAVE", 5) == 0)
|
|
2075
|
+
g_external_event = "deserialize_progress";
|
|
2076
|
+
|
|
2077
|
+
return RC_OK;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
static void test_deserialize_progress(void)
|
|
2081
|
+
{
|
|
2082
|
+
int result;
|
|
2083
|
+
uint8_t buffer[8] = {'S', 'A', 'V', 'E'};
|
|
2084
|
+
|
|
2085
|
+
g_client = mock_client_with_external();
|
|
2086
|
+
g_client->state.external_client->deserialize_progress = rc_client_external_deserialize_progress;
|
|
2087
|
+
|
|
2088
|
+
result = rc_client_deserialize_progress(g_client, buffer);
|
|
2089
|
+
|
|
2090
|
+
ASSERT_STR_EQUALS(g_external_event, "deserialize_progress");
|
|
2091
|
+
ASSERT_NUM_EQUALS(result, RC_OK);
|
|
2092
|
+
|
|
2093
|
+
rc_client_destroy(g_client);
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
/* ----- harness ----- */
|
|
2097
|
+
|
|
2098
|
+
void test_client_external(void) {
|
|
2099
|
+
TEST_SUITE_BEGIN();
|
|
2100
|
+
|
|
2101
|
+
/* settings */
|
|
2102
|
+
TEST(test_hardcore_enabled);
|
|
2103
|
+
TEST(test_unofficial_enabled);
|
|
2104
|
+
TEST(test_encore_mode_enabled);
|
|
2105
|
+
TEST(test_spectator_mode_enabled);
|
|
2106
|
+
TEST(test_enable_logging);
|
|
2107
|
+
TEST(test_event_handler);
|
|
2108
|
+
TEST(test_read_memory);
|
|
2109
|
+
TEST(test_get_time_millisecs);
|
|
2110
|
+
TEST(test_set_host);
|
|
2111
|
+
TEST(test_get_user_agent_clause);
|
|
2112
|
+
TEST(test_set_allow_background_memory_reads);
|
|
2113
|
+
|
|
2114
|
+
/* login */
|
|
2115
|
+
TEST(test_v1_user_field_offsets);
|
|
2116
|
+
TEST(test_v3_user_field_offsets);
|
|
2117
|
+
TEST(test_login_with_password);
|
|
2118
|
+
TEST(test_login_with_token_v1);
|
|
2119
|
+
TEST(test_login_with_token_v1_long_username);
|
|
2120
|
+
TEST(test_login_with_token_v1_too_long_username);
|
|
2121
|
+
TEST(test_login_with_token);
|
|
2122
|
+
|
|
2123
|
+
TEST(test_logout);
|
|
2124
|
+
|
|
2125
|
+
/* load game */
|
|
2126
|
+
TEST(test_v1_game_field_offsets);
|
|
2127
|
+
TEST(test_v3_game_field_offsets);
|
|
2128
|
+
|
|
2129
|
+
#ifdef RC_CLIENT_SUPPORTS_HASH
|
|
2130
|
+
TEST(test_identify_and_load_game_v1);
|
|
2131
|
+
TEST(test_identify_and_load_game);
|
|
2132
|
+
TEST(test_identify_and_reload_game);
|
|
2133
|
+
#endif
|
|
2134
|
+
TEST(test_load_game_v1);
|
|
2135
|
+
TEST(test_load_game);
|
|
2136
|
+
TEST(test_get_game_info_v1_no_game_loaded);
|
|
2137
|
+
TEST(test_v1_user_game_summary_field_offsets);
|
|
2138
|
+
TEST(test_v5_user_game_summary_field_offsets);
|
|
2139
|
+
TEST(test_get_user_game_summary);
|
|
2140
|
+
TEST(test_get_user_game_summary_v5);
|
|
2141
|
+
TEST(test_get_user_subset_summary);
|
|
2142
|
+
#ifdef RC_CLIENT_SUPPORTS_HASH
|
|
2143
|
+
TEST(test_identify_and_load_game_external_hash);
|
|
2144
|
+
TEST(test_identify_and_reload_game_external_hash);
|
|
2145
|
+
TEST(test_change_media);
|
|
2146
|
+
#endif
|
|
2147
|
+
TEST(test_change_media_from_hash);
|
|
2148
|
+
TEST(test_add_game_hash);
|
|
2149
|
+
|
|
2150
|
+
TEST(test_unload_game);
|
|
2151
|
+
|
|
2152
|
+
/* subsets */
|
|
2153
|
+
TEST(test_v1_subset_field_offsets);
|
|
2154
|
+
TEST(test_v3_subset_field_offsets);
|
|
2155
|
+
TEST(test_get_subset_info_v1);
|
|
2156
|
+
TEST(test_get_subset_info);
|
|
2157
|
+
TEST(test_create_subset_list);
|
|
2158
|
+
|
|
2159
|
+
/* achievements */
|
|
2160
|
+
TEST(test_v1_achievement_field_offsets);
|
|
2161
|
+
TEST(test_v3_achievement_field_offsets);
|
|
2162
|
+
TEST(test_has_achievements);
|
|
2163
|
+
TEST(test_get_achievement_info_v1);
|
|
2164
|
+
TEST(test_get_achievement_info_v1_not_found);
|
|
2165
|
+
TEST(test_get_achievement_info);
|
|
2166
|
+
TEST(test_get_next_achievement_info);
|
|
2167
|
+
|
|
2168
|
+
TEST(test_v1_achievement_list_field_offsets);
|
|
2169
|
+
TEST(test_v3_achievement_list_field_offsets);
|
|
2170
|
+
TEST(test_create_achievement_list_v1);
|
|
2171
|
+
TEST(test_create_achievement_list);
|
|
2172
|
+
|
|
2173
|
+
/* leaderboards */
|
|
2174
|
+
TEST(test_create_leaderboard_list);
|
|
2175
|
+
TEST(test_has_leaderboards);
|
|
2176
|
+
TEST(test_get_leaderboard_info);
|
|
2177
|
+
|
|
2178
|
+
/* rich presence */
|
|
2179
|
+
TEST(test_get_rich_presence_message);
|
|
2180
|
+
TEST(test_has_rich_presence);
|
|
2181
|
+
|
|
2182
|
+
/* processing */
|
|
2183
|
+
TEST(test_is_processing_required);
|
|
2184
|
+
TEST(test_do_frame);
|
|
2185
|
+
TEST(test_idle);
|
|
2186
|
+
TEST(test_can_pause);
|
|
2187
|
+
TEST(test_reset);
|
|
2188
|
+
|
|
2189
|
+
/* progress */
|
|
2190
|
+
TEST(test_progress_size);
|
|
2191
|
+
TEST(test_serialize_progress);
|
|
2192
|
+
TEST(test_deserialize_progress);
|
|
2193
|
+
|
|
2194
|
+
TEST_SUITE_END();
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
#endif /* RC_CLIENT_SUPPORTS_EXTERNAL */
|