// This file Copyright © Mnemosyne LLC. // It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. #include #include #include #include #include #include "test-fixtures.h" using ApiCompatTest = ::libtransmission::test::TransmissionTest; namespace { constexpr std::string_view LegacySessionGetJson = R"json({ "method": "session-get", "tag": 0 })json"; constexpr std::string_view CurrentSessionGetJson = R"json({ "id": 0, "jsonrpc": "2.0", "method": "session_get" })json"; constexpr std::string_view LegacySessionGetResponseJson = R"json({ "arguments": { "alt-speed-down": 50, "alt-speed-enabled": false, "alt-speed-time-begin": 540, "alt-speed-time-day": 127, "alt-speed-time-enabled": false, "alt-speed-time-end": 1020, "alt-speed-up": 50, "anti-brute-force-enabled": false, "anti-brute-force-threshold": 100, "blocklist-enabled": false, "blocklist-size": 0, "blocklist-url": "http://www.example.com/blocklist", "cache-size-mb": 4, "config-dir": "/home/user/.config/transmission", "default-trackers": "", "dht-enabled": true, "download-dir": "/home/user/Downloads", "download-dir-free-space": 2199023255552, "download-queue-enabled": true, "download-queue-size": 4, "encryption": "preferred", "idle-seeding-limit": 30, "idle-seeding-limit-enabled": false, "incomplete-dir": "/home/user/Downloads", "incomplete-dir-enabled": false, "lpd-enabled": false, "peer-limit-global": 200, "peer-limit-per-torrent": 50, "peer-port": 51413, "peer-port-random-on-start": false, "pex-enabled": true, "port-forwarding-enabled": true, "preferred_transports": [ "utp", "tcp" ], "queue-stalled-enabled": true, "queue-stalled-minutes": 30, "rename-partial-files": true, "reqq": 2000, "rpc-version": 18, "rpc-version-minimum": 14, "rpc-version-semver": "6.0.0", "script-torrent-added-enabled": false, "script-torrent-added-filename": "", "script-torrent-done-enabled": false, "script-torrent-done-filename": "/home/user/scripts/script.sh", "script-torrent-done-seeding-enabled": false, "script-torrent-done-seeding-filename": "", "seed-queue-enabled": false, "seed-queue-size": 10, "seedRatioLimit": 2.0, "seedRatioLimited": false, "sequential_download": false, "session-id": "pdvuklydohaohwwluzpmpmllkaopzzlzzpvupkpuavhjhlzhyjfwoly", "speed-limit-down": 100.0, "speed-limit-down-enabled": false, "speed-limit-up": 100.0, "speed-limit-up-enabled": false, "start-added-torrents": true, "tcp-enabled": true, "trash-original-torrent-files": false, "units": { "memory-bytes": 1024, "memory-units": [ "B", "KiB", "MiB", "GiB", "TiB" ], "size-bytes": 1000, "size-units": [ "B", "kB", "MB", "GB", "TB" ], "speed-bytes": 1000, "speed-units": [ "B/s", "kB/s", "MB/s", "GB/s", "TB/s" ] }, "utp-enabled": true, "version": "4.1.0-beta.4 (b11bfd9712)" }, "result": "success", "tag": 40 })json"; constexpr std::string_view CurrentSessionGetResponseJson = R"json({ "id": 40, "jsonrpc": "2.0", "result": { "alt_speed_down": 50, "alt_speed_enabled": false, "alt_speed_time_begin": 540, "alt_speed_time_day": 127, "alt_speed_time_enabled": false, "alt_speed_time_end": 1020, "alt_speed_up": 50, "anti_brute_force_enabled": false, "anti_brute_force_threshold": 100, "blocklist_enabled": false, "blocklist_size": 0, "blocklist_url": "http://www.example.com/blocklist", "cache_size_mib": 4, "config_dir": "/home/user/.config/transmission", "default_trackers": "", "dht_enabled": true, "download_dir": "/home/user/Downloads", "download_dir_free_space": 2199023255552, "download_queue_enabled": true, "download_queue_size": 4, "encryption": "preferred", "idle_seeding_limit": 30, "idle_seeding_limit_enabled": false, "incomplete_dir": "/home/user/Downloads", "incomplete_dir_enabled": false, "lpd_enabled": false, "peer_limit_global": 200, "peer_limit_per_torrent": 50, "peer_port": 51413, "peer_port_random_on_start": false, "pex_enabled": true, "port_forwarding_enabled": true, "preferred_transports": [ "utp", "tcp" ], "queue_stalled_enabled": true, "queue_stalled_minutes": 30, "rename_partial_files": true, "reqq": 2000, "rpc_version": 18, "rpc_version_minimum": 14, "rpc_version_semver": "6.0.0", "script_torrent_added_enabled": false, "script_torrent_added_filename": "", "script_torrent_done_enabled": false, "script_torrent_done_filename": "/home/user/scripts/script.sh", "script_torrent_done_seeding_enabled": false, "script_torrent_done_seeding_filename": "", "seed_queue_enabled": false, "seed_queue_size": 10, "seed_ratio_limit": 2.0, "seed_ratio_limited": false, "sequential_download": false, "session_id": "pdvuklydohaohwwluzpmpmllkaopzzlzzpvupkpuavhjhlzhyjfwoly", "speed_limit_down": 100.0, "speed_limit_down_enabled": false, "speed_limit_up": 100.0, "speed_limit_up_enabled": false, "start_added_torrents": true, "tcp_enabled": true, "trash_original_torrent_files": false, "units": { "memory_bytes": 1024, "memory_units": [ "B", "KiB", "MiB", "GiB", "TiB" ], "size_bytes": 1000, "size_units": [ "B", "kB", "MB", "GB", "TB" ], "speed_bytes": 1000, "speed_units": [ "B/s", "kB/s", "MB/s", "GB/s", "TB/s" ] }, "utp_enabled": true, "version": "4.1.0-beta.4 (b11bfd9712)" } })json"; constexpr std::string_view LegacyTorrentGetJson = R"json({ "arguments": { "fields": [ "downloadDir", "downloadedEver", "editDate", "error", "errorString", "eta", "haveUnchecked", "haveValid", "id", "isFinished", "leftUntilDone", "manualAnnounceTime", "metadataPercentComplete", "name", "peersConnected", "peersGettingFromUs", "peersSendingToUs", "percentDone", "queuePosition", "rateDownload", "rateUpload", "recheckProgress", "seedRatioLimit", "seedRatioMode", "sizeWhenDone", "status", "uploadRatio", "uploadedEver", "webseedsSendingToUs" ], "ids": "recently-active" }, "method": "torrent-get", "tag": 6 })json"; constexpr std::string_view CurrentTorrentGetJson = R"json({ "id": 6, "jsonrpc": "2.0", "method": "torrent_get", "params": { "fields": [ "download_dir", "downloaded_ever", "edit_date", "error", "error_string", "eta", "have_unchecked", "have_valid", "id", "is_finished", "left_until_done", "manual_announce_time", "metadata_percent_complete", "name", "peers_connected", "peers_getting_from_us", "peers_sending_to_us", "percent_done", "queue_position", "rate_download", "rate_upload", "recheck_progress", "seed_ratio_limit", "seed_ratio_mode", "size_when_done", "status", "upload_ratio", "uploaded_ever", "webseeds_sending_to_us" ], "ids": "recently_active" } })json"; constexpr std::string_view CurrentFilesWantedResponseObjectJson = R"json({ "id": 6, "jsonrpc": "2.0", "result": { "torrents": [ { "wanted": [ false, true, true, false ] }, { "wanted": [ true, false, true, false, true ] } ] } })json"; constexpr std::string_view LegacyFilesWantedResponseObjectJson = R"json({ "arguments": { "torrents": [ { "wanted": [ 0, 1, 1, 0 ] }, { "wanted": [ 1, 0, 1, 0, 1 ] } ] }, "result": "success", "tag": 6 })json"; constexpr std::string_view CurrentFilesWantedResponseArrayJson = R"json({ "id": 6, "jsonrpc": "2.0", "result": { "torrents": [ [ "comment", "wanted", "id" ], [ "id 1", [ false, true, true, false ], 1 ], [ "id 2", [ true, false, true, false, true ], 2 ] ] } })json"; constexpr std::string_view LegacyFilesWantedResponseArrayJson = R"json({ "arguments": { "torrents": [ [ "comment", "wanted", "id" ], [ "id 1", [ 0, 1, 1, 0 ], 1 ], [ "id 2", [ 1, 0, 1, 0, 1 ], 2 ] ] }, "result": "success", "tag": 6 })json"; constexpr std::string_view CurrentPortTestErrorResponse = R"json({ "error": { "code": 8, "data": { "error_string": "Couldn't test port: No Response (0)", "result": { "ip_protocol": "ipv6" } }, "message": "HTTP error from backend service" }, "id": 9, "jsonrpc": "2.0" })json"; constexpr std::string_view LegacyPortTestErrorResponse = R"json({ "arguments": { "ip_protocol": "ipv6" }, "result": "Couldn't test port: No Response (0)", "tag": 9 })json"; constexpr std::string_view CurrentPreferEncryptionResponse = R"json({ "id": 6, "jsonrpc": "2.0", "result": { "encryption": "preferred" } })json"; constexpr std::string_view LegacyPreferEncryptionResponse = R"json({ "arguments": { "encryption": "preferred" }, "result": "success", "tag": 6 })json"; constexpr std::string_view CurrentRequireEncryptionResponse = R"json({ "id": 6, "jsonrpc": "2.0", "result": { "encryption": "required" } })json"; constexpr std::string_view LegacyRequireEncryptionResponse = R"json({ "arguments": { "encryption": "required" }, "result": "success", "tag": 6 })json"; constexpr std::string_view CurrentPreferClearResponse = R"json({ "id": 6, "jsonrpc": "2.0", "result": { "encryption": "allowed" } })json"; constexpr std::string_view LegacyPreferClearResponse = R"json({ "arguments": { "encryption": "tolerated" }, "result": "success", "tag": 6 })json"; constexpr std::string_view CurrentPreferEncryptionRequest = R"json({ "id": 6, "jsonrpc": "2.0", "method": "session_set", "params": { "encryption": "preferred" } })json"; constexpr std::string_view LegacyPreferEncryptionRequest = R"json({ "arguments": { "encryption": "preferred" }, "method": "session-set", "tag": 6 })json"; constexpr std::string_view CurrentRequireEncryptionRequest = R"json({ "id": 6, "jsonrpc": "2.0", "method": "session_set", "params": { "encryption": "required" } })json"; constexpr std::string_view LegacyRequireEncryptionRequest = R"json({ "arguments": { "encryption": "required" }, "method": "session-set", "tag": 6 })json"; constexpr std::string_view CurrentPreferClearRequest = R"json({ "id": 6, "jsonrpc": "2.0", "method": "session_set", "params": { "encryption": "allowed" } })json"; constexpr std::string_view LegacyPreferClearRequest = R"json({ "arguments": { "encryption": "tolerated" }, "method": "session-set", "tag": 6 })json"; constexpr std::string_view LegacyStatsJson = R"json({ "downloaded-bytes": 12, "files-added": 34, "seconds-active": 56, "session-count": 78, "uploaded-bytes": 90 })json"; constexpr std::string_view CurrentStatsJson = R"json({ "downloaded_bytes": 12, "files_added": 34, "seconds_active": 56, "session_count": 78, "uploaded_bytes": 90 })json"; constexpr std::string_view LegacySettingsJson = R"json({ "alt-speed-down": 50, "alt-speed-enabled": false, "alt-speed-time-begin": 540, "alt-speed-time-day": 127, "alt-speed-time-enabled": false, "alt-speed-time-end": 1020, "alt-speed-up": 50, "blocklist-date": 0, "blocklist-enabled": false, "blocklist-updates-enabled": true, "blocklist-url": "http://www.example.com/blocklist", "compact-view": false, "default-trackers": "", "dht-enabled": true, "download-dir": "/home/user/Downloads", "download-queue-enabled": true, "download-queue-size": 5, "encryption": 1, "filter-mode": "show-all", "filter-trackers": "", "idle-seeding-limit": 30, "idle-seeding-limit-enabled": false, "incomplete-dir": "/home/user/Downloads", "incomplete-dir-enabled": false, "inhibit-desktop-hibernation": false, "lpd-enabled": true, "main-window-height": 500, "main-window-layout-order": "menu,toolbar,filter,list,statusbar", "main-window-width": 650, "main-window-x": 3840, "main-window-y": 0, "message-level": 4, "open-dialog-dir": "/home/user", "peer-limit-global": 200, "peer-limit-per-torrent": 50, "peer-port": 51413, "peer-port-random-high": 65535, "peer-port-random-low": 49152, "peer-port-random-on-start": false, "peer-socket-tos": "le", "pex-enabled": true, "port-forwarding-enabled": true, "preallocation": 1, "prompt-before-exit": true, "queue-stalled-minutes": 30, "ratio-limit": 2.0, "ratio-limit-enabled": false, "read-clipboard": false, "remote-session-enabled": false, "remote-session-host": "localhost", "remote-session-https": false, "remote-session-password": "", "remote-session-port": 9091, "remote-session-requres-authentication": false, "remote-session-username": "", "rename-partial-files": true, "rpc-authentication-required": false, "rpc-enabled": false, "rpc-password": "", "rpc-port": 9091, "rpc-username": "", "rpc-whitelist": "127.0.0.1,::1", "rpc-whitelist-enabled": true, "script-torrent-done-enabled": false, "script-torrent-done-filename": "", "script-torrent-done-seeding-enabled": false, "script-torrent-done-seeding-filename": "", "show-backup-trackers": false, "show-filterbar": true, "show-notification-area-icon": false, "show-options-window": true, "show-statusbar": true, "show-toolbar": true, "show-tracker-scrapes": false, "sleep-per-seconds-during-verify": 100, "sort-mode": "sort-by-name", "sort-reversed": false, "speed-limit-down": 100, "speed-limit-down-enabled": false, "speed-limit-up": 100, "speed-limit-up-enabled": false, "start-added-torrents": true, "start-minimized": false, "statusbar-stats": "total-ratio", "torrent-added-notification-enabled": true, "torrent-complete-notification-enabled": true, "torrent-complete-sound-command": [ "canberra-gtk-play", "-i", "complete-download", "-d", "transmission torrent downloaded" ], "torrent-complete-sound-enabled": true, "trash-original-torrent-files": false, "upload-slots-per-torrent": 8, "utp-enabled": true, "watch-dir": "/home/user/Downloads", "watch-dir-enabled": false })json"; constexpr std::string_view CurrentSettingsJson = R"json({ "alt_speed_down": 50, "alt_speed_enabled": false, "alt_speed_time_begin": 540, "alt_speed_time_day": 127, "alt_speed_time_enabled": false, "alt_speed_time_end": 1020, "alt_speed_up": 50, "blocklist_date": 0, "blocklist_enabled": false, "blocklist_updates_enabled": true, "blocklist_url": "http://www.example.com/blocklist", "compact_view": false, "default_trackers": "", "dht_enabled": true, "download_dir": "/home/user/Downloads", "download_queue_enabled": true, "download_queue_size": 5, "encryption": "preferred", "filter_mode": "show_all", "filter_trackers": "", "idle_seeding_limit": 30, "idle_seeding_limit_enabled": false, "incomplete_dir": "/home/user/Downloads", "incomplete_dir_enabled": false, "inhibit_desktop_hibernation": false, "lpd_enabled": true, "main_window_height": 500, "main_window_layout_order": "menu,toolbar,filter,list,statusbar", "main_window_width": 650, "main_window_x": 3840, "main_window_y": 0, "message_level": 4, "open_dialog_dir": "/home/user", "peer_limit_global": 200, "peer_limit_per_torrent": 50, "peer_port": 51413, "peer_port_random_high": 65535, "peer_port_random_low": 49152, "peer_port_random_on_start": false, "peer_socket_diffserv": "le", "pex_enabled": true, "port_forwarding_enabled": true, "preallocation": 1, "prompt_before_exit": true, "queue_stalled_minutes": 30, "ratio_limit": 2.0, "ratio_limit_enabled": false, "read_clipboard": false, "remote_session_enabled": false, "remote_session_host": "localhost", "remote_session_https": false, "remote_session_password": "", "remote_session_port": 9091, "remote_session_requires_authentication": false, "remote_session_username": "", "rename_partial_files": true, "rpc_authentication_required": false, "rpc_enabled": false, "rpc_password": "", "rpc_port": 9091, "rpc_username": "", "rpc_whitelist": "127.0.0.1,::1", "rpc_whitelist_enabled": true, "script_torrent_done_enabled": false, "script_torrent_done_filename": "", "script_torrent_done_seeding_enabled": false, "script_torrent_done_seeding_filename": "", "show_backup_trackers": false, "show_filterbar": true, "show_notification_area_icon": false, "show_options_window": true, "show_statusbar": true, "show_toolbar": true, "show_tracker_scrapes": false, "sleep_per_seconds_during_verify": 100, "sort_mode": "sort_by_name", "sort_reversed": false, "speed_limit_down": 100, "speed_limit_down_enabled": false, "speed_limit_up": 100, "speed_limit_up_enabled": false, "start_added_torrents": true, "start_minimized": false, "statusbar_stats": "total-ratio", "torrent_added_notification_enabled": true, "torrent_complete_notification_enabled": true, "torrent_complete_sound_command": [ "canberra-gtk-play", "-i", "complete-download", "-d", "transmission torrent downloaded" ], "torrent_complete_sound_enabled": true, "trash_original_torrent_files": false, "upload_slots_per_torrent": 8, "utp_enabled": true, "watch_dir": "/home/user/Downloads", "watch_dir_enabled": false })json"; constexpr std::string_view LegacyPreferClearJson = R"json({ "encryption": 0 })json"; constexpr std::string_view CurrentPreferClearJson = R"json({ "encryption": "allowed" })json"; constexpr std::string_view LegacyPreferEncryptionJson = R"json({ "encryption": 1 })json"; constexpr std::string_view CurrentPreferEncryptionJson = R"json({ "encryption": "preferred" })json"; constexpr std::string_view LegacyRequireEncryptionJson = R"json({ "encryption": 2 })json"; constexpr std::string_view CurrentRequireEncryptionJson = R"json({ "encryption": "required" })json"; constexpr std::string_view BadFreeSpaceRequest = R"json({ "id": 39693, "jsonrpc": "2.0", "method": "free_space", "params": { "path": "this/path/is/not/absolute" } })json"; constexpr std::string_view BadFreeSpaceRequestLegacy = R"json({ "arguments": { "path": "this/path/is/not/absolute" }, "method": "free-space", "tag": 39693 })json"; constexpr std::string_view BadFreeSpaceResponse = R"json({ "error": { "code": 3, "data": { "error_string": "directory path is not absolute" }, "message": "path is not absolute" }, "id": 39693, "jsonrpc": "2.0" })json"; constexpr std::string_view BadFreeSpaceResponseLegacy = R"json({ "arguments": {}, "result": "directory path is not absolute", "tag": 39693 })json"; constexpr std::string_view WellFormedFreeSpaceRequest = R"json({ "id": 41414, "jsonrpc": "2.0", "method": "free_space", "params": { "path": "/this/path/does/not/exist" } })json"; constexpr std::string_view WellFormedFreeSpaceResponse = R"json({ "id": 41414, "jsonrpc": "2.0", "result": { "path": "/this/path/does/not/exist", "size_bytes": -1, "total_size": -1 } })json"; constexpr std::string_view WellFormedFreeSpaceLegacyRequest = R"json({ "arguments": { "path": "/this/path/does/not/exist" }, "method": "free-space", "tag": 41414 })json"; constexpr std::string_view WellFormedFreeSpaceLegacyResponse = R"json({ "arguments": { "path": "/this/path/does/not/exist", "size-bytes": -1, "total_size": -1 }, "result": "success", "tag": 41414 })json"; constexpr std::string_view BadMethodNameResponse = R"json({ "error": { "code": -32601, "message": "Method not found" }, "id": 39693, "jsonrpc": "2.0" })json"; constexpr std::string_view BadMethodNameLegacyResponse = R"json({ "arguments": {}, "result": "no method name", "tag": 39693 })json"; constexpr std::string_view UnrecognisedInfoResponse = R"json({ "error": { "code": 4, "message": "unrecognized info" }, "id": 10, "jsonrpc": "2.0" })json"; constexpr std::string_view UnrecognisedInfoLegacyResponse = R"json({ "arguments": {}, "result": "unrecognized info", "tag": 10 })json"; constexpr std::string_view LegacyNonIntTagRequest = R"json({ "method": "session-get", "tag": "0" })json"; constexpr std::string_view LegacyNonIntTagRequestResult = R"json({ "jsonrpc": "2.0", "method": "session_get" })json"; // clang-format off constexpr std::string_view LegacyResumeBenc = "d" "13:activity-date" "i1765724117e" "10:added-date" "i1756689559e" "18:bandwidth-priority" "i0e" "7:corrupt" "i0e" "11:destination" "30:/data/trackers/untracked/Books" "3:dnd" "l" "i0e" "i0e" "i0e" "i0e" "i0e" "i0e" "i0e" "e" "9:done-date" "i1756689845e" "10:downloaded" "i4830420542e" "24:downloading-time-seconds" "i286e" "5:files" "l" "102:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v01 - Oz - Baum - The Wonderful Wizard of Oz (1990).epub" "100:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v02 - Oz - Baum - The Marvelous Land of Oz (1904).epub" "86:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v03 - Oz - Baum - Ozma of Oz (1907).epub" "104:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v04 - Oz - Baum - Dorothy and the Wizard in Oz (1908).epub" "90:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v05 - Oz - Baum - The Road to Oz (1909).epub" "98:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v06 - Oz - Baum - The Emerald City of Oz (1910).epub" "100:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v07 - Oz - Baum - The Patchwork Girl of Oz (1913).epub" "e" "5:group" "0:" "10:idle-limit" "d" "10:idle-limit" "i30e" "9:idle-mode" "i0e" "e" "6:labels" "l" "e" "9:max-peers" "i20e" "4:name" "45:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7" "6:paused" "i0e" "6:peers2" "l" "d" "5:flags" "i0e" "14:socket_address" "6:\x80\x3b\xac\x8f\x3b\x1c" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xe2\xa1\xe3\x25\x2c\xfa" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xf2\x50\x82\xab\xed\x08" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xeb\xb2\x8c\xa1\x1e\xc6" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xe8\x92\x9e\x87\xd1\xb4" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\x0a\xca\x51\xdd\x61\x52" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\x3d\xf0\x9c\x23\x55\x20" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\x4d\x9f\x2e\xd9\x40\x9e" "e" "d" "5:flags" "i12e" "14:socket_address" "6:\x83\xa6\xd7\x7f\xa3\x4c" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xcd\x4a\xdf\x95\xc8\xaa" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\x78\x5b\x6c\x9b\xa8\x38" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\x60\xc6\xe0\x11\xc5\x76" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xd4\xfa\x37\x77\x0f\xe4" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xfb\x28\x6c\x4d\xc3\x02" "e" "e" "8:priority" "l" "i0e" "i0e" "i0e" "i0e" "i0e" "i0e" "i0e" "e" "8:progress" "d" "6:blocks" "3:all" "6:mtimes" "l" "i1756689844e" "i1756689800e" "i1756689836e" "i1756689812e" "i1756689839e" "i1756689844e" "i1756689804e" "e" "6:pieces" "3:all" "e" "11:ratio-limit" "d" "11:ratio-limit" "8:2.000000" "10:ratio-mode" "i0e" "e" "20:seeding-time-seconds" "i7373039e" "19:sequential_download" "i0e" "30:sequential_download_from_piece" "i0e" "16:speed-limit-down" "d" "9:speed-Bps" "i2000000e" "22:use-global-speed-limit" "i1e" "15:use-speed-limit" "i0e" "e" "14:speed-limit-up" "d" "9:speed-Bps" "i5000000e" "22:use-global-speed-limit" "i1e" "15:use-speed-limit" "i0e" "e" "8:uploaded" "i98667375637e" "e"; constexpr std::string_view ResumeBenc = "d" "13:activity_date" "i1765724117e" "10:added_date" "i1756689559e" "18:bandwidth_priority" "i0e" "7:corrupt" "i0e" "11:destination" "30:/data/trackers/untracked/Books" "3:dnd" "l" "i0e" "i0e" "i0e" "i0e" "i0e" "i0e" "i0e" "e" "9:done_date" "i1756689845e" "10:downloaded" "i4830420542e" "24:downloading_time_seconds" "i286e" "5:files" "l" "102:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v01 - Oz - Baum - The Wonderful Wizard of Oz (1990).epub" "100:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v02 - Oz - Baum - The Marvelous Land of Oz (1904).epub" "86:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v03 - Oz - Baum - Ozma of Oz (1907).epub" "104:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v04 - Oz - Baum - Dorothy and the Wizard in Oz (1908).epub" "90:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v05 - Oz - Baum - The Road to Oz (1909).epub" "98:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v06 - Oz - Baum - The Emerald City of Oz (1910).epub" "100:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7/v07 - Oz - Baum - The Patchwork Girl of Oz (1913).epub" "e" "5:group" "0:" "10:idle_limit" "d" "10:idle_limit" "i30e" "9:idle_mode" "i0e" "e" "6:labels" "l" "e" "9:max_peers" "i20e" "4:name" "45:Oz Series - Frank L Baum [PUBLIC DOMAIN] v1-7" "6:paused" "i0e" "6:peers2" "l" "d" "5:flags" "i0e" "14:socket_address" "6:\x80\x3b\xac\x8f\x3b\x1c" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xe2\xa1\xe3\x25\x2c\xfa" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xf2\x50\x82\xab\xed\x08" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xeb\xb2\x8c\xa1\x1e\xc6" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xe8\x92\x9e\x87\xd1\xb4" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\x0a\xca\x51\xdd\x61\x52" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\x3d\xf0\x9c\x23\x55\x20" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\x4d\x9f\x2e\xd9\x40\x9e" "e" "d" "5:flags" "i12e" "14:socket_address" "6:\x83\xa6\xd7\x7f\xa3\x4c" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xcd\x4a\xdf\x95\xc8\xaa" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\x78\x5b\x6c\x9b\xa8\x38" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\x60\xc6\xe0\x11\xc5\x76" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xd4\xfa\x37\x77\x0f\xe4" "e" "d" "5:flags" "i0e" "14:socket_address" "6:\xfb\x28\x6c\x4d\xc3\x02" "e" "e" "8:priority" "l" "i0e" "i0e" "i0e" "i0e" "i0e" "i0e" "i0e" "e" "8:progress" "d" "6:blocks" "3:all" "6:mtimes" "l" "i1756689844e" "i1756689800e" "i1756689836e" "i1756689812e" "i1756689839e" "i1756689844e" "i1756689804e" "e" "6:pieces" "3:all" "e" "11:ratio_limit" "d" "11:ratio_limit" "8:2.000000" "10:ratio_mode" "i0e" "e" "20:seeding_time_seconds" "i7373039e" "19:sequential_download" "i0e" "30:sequential_download_from_piece" "i0e" "16:speed_limit_down" "d" "9:speed_Bps" "i2000000e" "22:use_global_speed_limit" "i1e" "15:use_speed_limit" "i0e" "e" "14:speed_limit_up" "d" "9:speed_Bps" "i5000000e" "22:use_global_speed_limit" "i1e" "15:use_speed_limit" "i0e" "e" "8:uploaded" "i98667375637e" "e"; // clang-format on } // namespace TEST_F(ApiCompatTest, canConvertRpc) { using Style = libtransmission::api_compat::Style; using TestCase = std::tuple; // clang-format off static auto constexpr TestCases = std::array{ { { "free_space tr5 -> tr5", BadFreeSpaceRequest, Style::Tr5, BadFreeSpaceRequest }, { "free_space tr5 -> tr4", BadFreeSpaceRequest, Style::Tr4, BadFreeSpaceRequestLegacy }, { "free_space tr4 -> tr5", BadFreeSpaceRequestLegacy, Style::Tr5, BadFreeSpaceRequest }, { "free_space tr4 -> tr4", BadFreeSpaceRequestLegacy, Style::Tr4, BadFreeSpaceRequestLegacy }, { "free_space error response tr5 -> tr5", BadFreeSpaceResponse, Style::Tr5, BadFreeSpaceResponse }, { "free_space error response tr5 -> tr4", BadFreeSpaceResponse, Style::Tr4, BadFreeSpaceResponseLegacy }, { "free_space error response tr4 -> tr5", BadFreeSpaceResponseLegacy, Style::Tr5, BadFreeSpaceResponse }, { "free_space error response tr4 -> tr4", BadFreeSpaceResponseLegacy, Style::Tr4, BadFreeSpaceResponseLegacy }, { "free_space req tr5 -> tr5", WellFormedFreeSpaceRequest, Style::Tr5, WellFormedFreeSpaceRequest }, { "free_space req tr5 -> tr4", WellFormedFreeSpaceRequest, Style::Tr4, WellFormedFreeSpaceLegacyRequest }, { "free_space req tr4 -> tr5", WellFormedFreeSpaceLegacyRequest, Style::Tr5, WellFormedFreeSpaceRequest }, { "free_space req tr4 -> tr4", WellFormedFreeSpaceLegacyRequest, Style::Tr4, WellFormedFreeSpaceLegacyRequest }, { "free_space response tr5 -> tr5", WellFormedFreeSpaceResponse, Style::Tr5, WellFormedFreeSpaceResponse }, { "free_space response tr5 -> tr4", WellFormedFreeSpaceResponse, Style::Tr4, WellFormedFreeSpaceLegacyResponse }, { "free_space response tr4 -> tr5", WellFormedFreeSpaceLegacyResponse, Style::Tr5, WellFormedFreeSpaceResponse }, { "free_space response tr4 -> tr4", WellFormedFreeSpaceLegacyResponse, Style::Tr4, WellFormedFreeSpaceLegacyResponse }, { "session_get tr5 -> tr5", CurrentSessionGetJson, Style::Tr5, CurrentSessionGetJson }, { "session_get tr5 -> tr4", CurrentSessionGetJson, Style::Tr4, LegacySessionGetJson }, { "session_get tr4 -> tr5", LegacySessionGetJson, Style::Tr5, CurrentSessionGetJson }, { "session_get tr4 -> tr4", LegacySessionGetJson, Style::Tr4, LegacySessionGetJson }, { "session_get response tr5 -> tr5", CurrentSessionGetResponseJson, Style::Tr5, CurrentSessionGetResponseJson }, { "session_get response tr5 -> tr4", CurrentSessionGetResponseJson, Style::Tr4, LegacySessionGetResponseJson }, { "session_get response tr4 -> tr5", LegacySessionGetResponseJson, Style::Tr5, CurrentSessionGetResponseJson }, { "session_get response tr4 -> tr4", LegacySessionGetResponseJson, Style::Tr4, LegacySessionGetResponseJson }, { "torrent_get tr5 -> tr5", CurrentTorrentGetJson, Style::Tr5, CurrentTorrentGetJson }, { "torrent_get tr5 -> tr4", CurrentTorrentGetJson, Style::Tr4, LegacyTorrentGetJson }, { "torrent_get tr4 -> tr5", LegacyTorrentGetJson, Style::Tr5, CurrentTorrentGetJson }, { "torrent_get tr4 -> tr4", LegacyTorrentGetJson, Style::Tr4, LegacyTorrentGetJson }, { "port_test error response tr5 -> tr5", CurrentPortTestErrorResponse, Style::Tr5, CurrentPortTestErrorResponse }, { "port_test error response tr5 -> tr4", CurrentPortTestErrorResponse, Style::Tr4, LegacyPortTestErrorResponse }, { "port_test error response tr4 -> tr5", LegacyPortTestErrorResponse, Style::Tr5, CurrentPortTestErrorResponse }, { "port_test error response tr4 -> tr4", LegacyPortTestErrorResponse, Style::Tr4, LegacyPortTestErrorResponse }, { "bad method name tr5 -> tr5", BadMethodNameResponse, Style::Tr5, BadMethodNameResponse }, { "bad method name tr5 -> tr4", BadMethodNameResponse, Style::Tr4, BadMethodNameLegacyResponse }, { "bad method name tr4 -> tr5", BadMethodNameLegacyResponse, Style::Tr5, BadMethodNameResponse }, { "bad method name tr4 -> tr4", BadMethodNameLegacyResponse, Style::Tr4, BadMethodNameLegacyResponse }, { "unrecognised info tr5 -> tr5", UnrecognisedInfoResponse, Style::Tr5, UnrecognisedInfoResponse}, { "unrecognised info tr5 -> tr4", UnrecognisedInfoResponse, Style::Tr4, UnrecognisedInfoLegacyResponse}, { "unrecognised info tr4 -> tr5", UnrecognisedInfoLegacyResponse, Style::Tr5, UnrecognisedInfoResponse}, { "unrecognised info tr4 -> tr4", UnrecognisedInfoLegacyResponse, Style::Tr4, UnrecognisedInfoLegacyResponse}, { "non-int tag tr4 -> tr5", LegacyNonIntTagRequest, Style::Tr5, LegacyNonIntTagRequestResult }, { "non-int tag tr4 -> tr4", LegacyNonIntTagRequest, Style::Tr4, LegacyNonIntTagRequest }, { "files wanted response object tr5 -> tr5", CurrentFilesWantedResponseObjectJson, Style::Tr5, CurrentFilesWantedResponseObjectJson }, { "files wanted response object tr5 -> tr4", CurrentFilesWantedResponseObjectJson, Style::Tr4, LegacyFilesWantedResponseObjectJson }, { "files wanted response object tr4 -> tr5", LegacyFilesWantedResponseObjectJson, Style::Tr5, CurrentFilesWantedResponseObjectJson }, { "files wanted response object tr5 -> tr4", LegacyFilesWantedResponseObjectJson, Style::Tr4, LegacyFilesWantedResponseObjectJson }, { "files wanted response array tr5 -> tr5", CurrentFilesWantedResponseArrayJson, Style::Tr5, CurrentFilesWantedResponseArrayJson }, { "files wanted response array tr5 -> tr4", CurrentFilesWantedResponseArrayJson, Style::Tr4, LegacyFilesWantedResponseArrayJson }, { "files wanted response array tr4 -> tr5", LegacyFilesWantedResponseArrayJson, Style::Tr5, CurrentFilesWantedResponseArrayJson }, { "files wanted response array tr5 -> tr4", LegacyFilesWantedResponseArrayJson, Style::Tr4, LegacyFilesWantedResponseArrayJson }, { "prefer encryption response tr5 -> tr5", CurrentPreferEncryptionResponse, Style::Tr5, CurrentPreferEncryptionResponse }, { "prefer encryption response tr5 -> tr4", CurrentPreferEncryptionResponse, Style::Tr4, LegacyPreferEncryptionResponse }, { "prefer encryption response tr4 -> tr5", LegacyPreferEncryptionResponse, Style::Tr5, CurrentPreferEncryptionResponse }, { "prefer encryption response tr5 -> tr4", LegacyPreferEncryptionResponse, Style::Tr4, LegacyPreferEncryptionResponse }, { "require encryption response tr5 -> tr5", CurrentRequireEncryptionResponse, Style::Tr5, CurrentRequireEncryptionResponse }, { "require encryption response tr5 -> tr4", CurrentRequireEncryptionResponse, Style::Tr4, LegacyRequireEncryptionResponse }, { "require encryption response tr4 -> tr5", LegacyRequireEncryptionResponse, Style::Tr5, CurrentRequireEncryptionResponse }, { "require encryption response tr5 -> tr4", LegacyRequireEncryptionResponse, Style::Tr4, LegacyRequireEncryptionResponse }, { "prefer clear response tr5 -> tr5", CurrentPreferClearResponse, Style::Tr5, CurrentPreferClearResponse }, { "prefer clear response tr5 -> tr4", CurrentPreferClearResponse, Style::Tr4, LegacyPreferClearResponse }, { "prefer clear response tr4 -> tr5", LegacyPreferClearResponse, Style::Tr5, CurrentPreferClearResponse }, { "prefer clear response tr5 -> tr4", LegacyPreferClearResponse, Style::Tr4, LegacyPreferClearResponse }, { "prefer encryption request tr5 -> tr5", CurrentPreferEncryptionRequest, Style::Tr5, CurrentPreferEncryptionRequest }, { "prefer encryption request tr5 -> tr4", CurrentPreferEncryptionRequest, Style::Tr4, LegacyPreferEncryptionRequest }, { "prefer encryption request tr4 -> tr5", LegacyPreferEncryptionRequest, Style::Tr5, CurrentPreferEncryptionRequest }, { "prefer encryption request tr5 -> tr4", LegacyPreferEncryptionRequest, Style::Tr4, LegacyPreferEncryptionRequest }, { "require encryption request tr5 -> tr5", CurrentRequireEncryptionRequest, Style::Tr5, CurrentRequireEncryptionRequest }, { "require encryption request tr5 -> tr4", CurrentRequireEncryptionRequest, Style::Tr4, LegacyRequireEncryptionRequest }, { "require encryption request tr4 -> tr5", LegacyRequireEncryptionRequest, Style::Tr5, CurrentRequireEncryptionRequest }, { "require encryption request tr5 -> tr4", LegacyRequireEncryptionRequest, Style::Tr4, LegacyRequireEncryptionRequest }, { "prefer clear request tr5 -> tr5", CurrentPreferClearRequest, Style::Tr5, CurrentPreferClearRequest }, { "prefer clear request tr5 -> tr4", CurrentPreferClearRequest, Style::Tr4, LegacyPreferClearRequest }, { "prefer clear request tr4 -> tr5", LegacyPreferClearRequest, Style::Tr5, CurrentPreferClearRequest }, { "prefer clear request tr5 -> tr4", LegacyPreferClearRequest, Style::Tr4, LegacyPreferClearRequest }, // TODO(ckerr): torrent-get with 'table' } }; // clang-format on for (auto const& [name, src, tgt_style, expected] : TestCases) { auto serde = tr_variant_serde::json(); auto parsed = serde.parse(src); ASSERT_TRUE(parsed.has_value()) << name << ": " << serde.error_; libtransmission::api_compat::convert(*parsed, tgt_style); EXPECT_EQ(expected, serde.to_string(*parsed)) << name; } } TEST_F(ApiCompatTest, canConvertJsonDataFiles) { using Style = libtransmission::api_compat::Style; using TestCase = std::tuple; static auto constexpr TestCases = std::array{ { { "settings tr5 -> tr5", CurrentSettingsJson, Style::Tr5, CurrentSettingsJson }, { "settings tr5 -> tr4", CurrentSettingsJson, Style::Tr4, LegacySettingsJson }, { "settings tr4 -> tr5", LegacySettingsJson, Style::Tr5, CurrentSettingsJson }, { "settings tr4 -> tr4", LegacySettingsJson, Style::Tr4, LegacySettingsJson }, { "prefer clear tr5 -> tr5", CurrentPreferClearJson, Style::Tr5, CurrentPreferClearJson }, { "prefer clear tr5 -> tr4", CurrentPreferClearJson, Style::Tr4, LegacyPreferClearJson }, { "prefer clear tr4 -> tr5", LegacyPreferClearJson, Style::Tr5, CurrentPreferClearJson }, { "prefer clear tr4 -> tr4", LegacyPreferClearJson, Style::Tr4, LegacyPreferClearJson }, { "prefer encryption tr5 -> tr5", CurrentPreferEncryptionJson, Style::Tr5, CurrentPreferEncryptionJson }, { "prefer encryption tr5 -> tr4", CurrentPreferEncryptionJson, Style::Tr4, LegacyPreferEncryptionJson }, { "prefer encryption tr4 -> tr5", LegacyPreferEncryptionJson, Style::Tr5, CurrentPreferEncryptionJson }, { "prefer encryption tr4 -> tr4", LegacyPreferEncryptionJson, Style::Tr4, LegacyPreferEncryptionJson }, { "require encryption tr5 -> tr5", CurrentRequireEncryptionJson, Style::Tr5, CurrentRequireEncryptionJson }, { "require encryption tr5 -> tr4", CurrentRequireEncryptionJson, Style::Tr4, LegacyRequireEncryptionJson }, { "require encryption tr4 -> tr5", LegacyRequireEncryptionJson, Style::Tr5, CurrentRequireEncryptionJson }, { "require encryption tr4 -> tr4", LegacyRequireEncryptionJson, Style::Tr4, LegacyRequireEncryptionJson }, { "stats tr5 -> tr5", CurrentStatsJson, Style::Tr5, CurrentStatsJson }, { "stats tr5 -> tr4", CurrentStatsJson, Style::Tr4, LegacyStatsJson }, { "stats tr4 -> tr5", LegacyStatsJson, Style::Tr5, CurrentStatsJson }, { "stats tr4 -> tr4", LegacyStatsJson, Style::Tr4, LegacyStatsJson }, } }; for (auto const& [name, src, tgt_style, expected] : TestCases) { auto serde = tr_variant_serde::json(); serde.inplace(); auto parsed = serde.parse(src); ASSERT_TRUE(parsed.has_value()); libtransmission::api_compat::convert(*parsed, tgt_style); EXPECT_EQ(expected, serde.to_string(*parsed)) << name; } } TEST_F(ApiCompatTest, canConvertBencDataFiles) { using Style = libtransmission::api_compat::Style; using TestCase = std::tuple; static auto constexpr TestCases = std::array{ { { "resume tr5 -> tr5", ResumeBenc, Style::Tr5, ResumeBenc }, { "resume tr5 -> tr4", ResumeBenc, Style::Tr4, LegacyResumeBenc }, { "resume tr4 -> tr5", LegacyResumeBenc, Style::Tr5, ResumeBenc }, { "resume tr4 -> tr4", LegacyResumeBenc, Style::Tr4, LegacyResumeBenc }, } }; for (auto const& [name, src, tgt_style, expected] : TestCases) { auto serde = tr_variant_serde::benc(); serde.inplace(); auto parsed = serde.parse(src); ASSERT_TRUE(parsed.has_value()) << name; libtransmission::api_compat::convert(*parsed, tgt_style); EXPECT_EQ(expected, serde.to_string(*parsed)) << name; } }