|
|
@@ -1,219 +0,0 @@
|
|
|
-#include <array>
|
|
|
-#include <cstddef>
|
|
|
-#include <cstdint>
|
|
|
-#include <cstdio>
|
|
|
-#include <cstring>
|
|
|
-#include <span>
|
|
|
-
|
|
|
-#include "middleware/content/packages/tables/definition_index_table.h"
|
|
|
-#include "middleware/content/packages/tables/items.h"
|
|
|
-#include "middleware/datagen/character_record/appearance/internal.h"
|
|
|
-#include "middleware/queuez/queuez_update.h"
|
|
|
-#include "middleware/web_service/messages/opcode1901.h"
|
|
|
-#include "server/bap/encrypted/queuez/queuez_state_validation.h"
|
|
|
-#include "server/web_service/web_service_runtime.h"
|
|
|
-#include "state/build_data/inventory/buckets/inventory_bucket_catalog.h"
|
|
|
-#include "state/build_data/table.h"
|
|
|
-
|
|
|
-namespace {
|
|
|
-
|
|
|
-int failures{};
|
|
|
-
|
|
|
-void expect(bool value, const char* label) {
|
|
|
- if (!value) {
|
|
|
- std::fprintf(stderr, "FAIL %s\n", label);
|
|
|
- ++failures;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-template <typename Value>
|
|
|
-void write_at(std::span<std::byte> bytes, std::size_t offset, Value value) {
|
|
|
- std::memcpy(bytes.data() + offset, &value, sizeof value);
|
|
|
-}
|
|
|
-
|
|
|
-void test_opcode1901_fixture() {
|
|
|
- using namespace sunrise::middleware::web_service;
|
|
|
- using namespace sunrise::middleware::web_service::messages;
|
|
|
- constexpr std::array<std::uint8_t, 24> wire{
|
|
|
- 0x19, 0xA8, 0x28, 0x56, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00,
|
|
|
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
|
|
|
- };
|
|
|
- Message message{
|
|
|
- opcode1901::kOpcode, 7, {reinterpret_cast<const std::byte*>(wire.data()), wire.size()}};
|
|
|
- opcode1901::Request decoded{};
|
|
|
- expect(opcode1901::parse_request(message, decoded), "opcode1901 captured fixture parses");
|
|
|
- expect(decoded.plugDefinitionIndex == 6786, "opcode1901 plug index");
|
|
|
- expect(decoded.canonicalSocketKind == 5 && decoded.modelSocketKind == 0,
|
|
|
- "opcode1901 socket kinds");
|
|
|
- expect(decoded.socketIndex == 5 && decoded.auxiliary == 0, "opcode1901 socket descriptor");
|
|
|
- expect(decoded.equipmentSelector == 8, "opcode1901 equipment selector");
|
|
|
-
|
|
|
- auto malformed = wire;
|
|
|
- malformed[0] = 0;
|
|
|
- message.payload = {reinterpret_cast<const std::byte*>(malformed.data()), malformed.size()};
|
|
|
- expect(!opcode1901::parse_request(message, decoded), "opcode1901 rejects wrong count");
|
|
|
- expect(decoded.plugDefinitionIndex == 0 && decoded.equipmentSelector == 0,
|
|
|
- "opcode1901 clears rejected output");
|
|
|
-}
|
|
|
-
|
|
|
-void test_class_art_selection() {
|
|
|
- namespace appearance = sunrise::middleware::datagen::character_record::appearance;
|
|
|
- namespace details = sunrise::state::build_data::items::details;
|
|
|
- details::Definition definition{};
|
|
|
- definition.artArrangementIndices = {100, 101, details::kUnavailableArtIndex, 103};
|
|
|
- expect(appearance::select_art_arrangement(definition, sunrise::state::CharacterClass::titan)
|
|
|
- == 101,
|
|
|
- "Titan art selects class row");
|
|
|
- expect(appearance::select_art_arrangement(definition, sunrise::state::CharacterClass::hunter)
|
|
|
- == 100,
|
|
|
- "missing Hunter art falls back to generic");
|
|
|
- expect(appearance::select_art_arrangement(definition, sunrise::state::CharacterClass::warlock)
|
|
|
- == 103,
|
|
|
- "Warlock art selects class row");
|
|
|
-}
|
|
|
-
|
|
|
-void test_class_art_extraction() {
|
|
|
- namespace tables = sunrise::middleware::content::packages::tables;
|
|
|
- namespace items = tables::items;
|
|
|
- std::array<std::byte, 384> blob{};
|
|
|
- constexpr std::size_t art = 192;
|
|
|
- constexpr std::size_t header = 304;
|
|
|
- constexpr std::size_t data = header + 16;
|
|
|
- write_at(
|
|
|
- blob, tables::kArtBlockOffset, static_cast<std::int64_t>(art - tables::kArtBlockOffset));
|
|
|
- write_at(blob, art, std::uint64_t{3});
|
|
|
- write_at(blob, art + 8, static_cast<std::int64_t>(header - (art + 8)));
|
|
|
- write_at(blob, header - 4, std::uint32_t{0x80800000});
|
|
|
- write_at(blob, header, std::uint64_t{3});
|
|
|
- write_at(blob, header + 8, tables::kArtRowClass);
|
|
|
- write_at(blob, art + 88, std::uint16_t{77});
|
|
|
- write_at(blob, data, std::int8_t{-1});
|
|
|
- write_at(blob, data + 2, std::uint16_t{100});
|
|
|
- write_at(blob, data + 4, std::int8_t{0});
|
|
|
- write_at(blob, data + 6, std::uint16_t{101});
|
|
|
- write_at(blob, data + 8, std::int8_t{2});
|
|
|
- write_at(blob, data + 10, std::uint16_t{103});
|
|
|
-
|
|
|
- items::Row row{};
|
|
|
- items::read_appearance(blob, row);
|
|
|
- expect(row.gearArtIndex == 77, "appearance extraction reads gear art");
|
|
|
- expect(row.artArrangementIndices[0] == 100 && row.artArrangementIndices[1] == 101
|
|
|
- && row.artArrangementIndices[2] == items::kUnavailableArtIndex
|
|
|
- && row.artArrangementIndices[3] == 103,
|
|
|
- "appearance extraction keeps generic and class rows");
|
|
|
-}
|
|
|
-
|
|
|
-void test_table_replacement() {
|
|
|
- struct Row {
|
|
|
- std::uint32_t value{};
|
|
|
- };
|
|
|
- sunrise::state::build_data::Table<Row, 8> table{};
|
|
|
- constexpr std::array<Row, 4> first{{{1}, {2}, {3}, {4}}};
|
|
|
- constexpr std::array<Row, 2> second{{{9}, {8}}};
|
|
|
- expect(table.replace(first), "table accepts first replacement");
|
|
|
- expect(table.replace(second), "table accepts shorter replacement");
|
|
|
- std::array<Row, 8> snapshot{};
|
|
|
- std::size_t count = 0;
|
|
|
- expect(table.snapshot(snapshot, count) && count == second.size(), "table publishes new count");
|
|
|
- expect(snapshot[0].value == second[0].value && snapshot[1].value == second[1].value,
|
|
|
- "table publishes new prefix");
|
|
|
- expect(table.reset(3).size() == 3 && table.count() == 3, "table reset resizes safely");
|
|
|
- table.clear();
|
|
|
- expect(table.count() == 0, "table clear publishes empty state");
|
|
|
-}
|
|
|
-
|
|
|
-void test_bucket_equipment_mapping_validation() {
|
|
|
- namespace buckets = sunrise::state::build_data::inventory::buckets;
|
|
|
- constexpr std::array<buckets::Descriptor, 2> mapped{{
|
|
|
- {0, buckets::ArraySelector::character, 0, 10, 7, 0},
|
|
|
- {13, buckets::ArraySelector::profile, 75, 50, buckets::kUnavailableEquipmentSlot, 0},
|
|
|
- }};
|
|
|
- expect(buckets::valid(mapped), "bucket table accepts a unique extracted equipment slot");
|
|
|
-
|
|
|
- auto unmapped = mapped;
|
|
|
- unmapped[0].equipmentSlot = buckets::kUnavailableEquipmentSlot;
|
|
|
- expect(!buckets::valid(unmapped), "bucket table rejects an all-unmapped cache domain");
|
|
|
-
|
|
|
- auto duplicate = mapped;
|
|
|
- duplicate[1].equipmentSlot = mapped[0].equipmentSlot;
|
|
|
- expect(!buckets::valid(duplicate), "bucket table rejects duplicate equipment slots");
|
|
|
-}
|
|
|
-
|
|
|
-void test_family4_resync() {
|
|
|
- namespace queuez = sunrise::server::bap::encrypted::queuez;
|
|
|
- namespace wire = sunrise::middleware::queuez;
|
|
|
- queuez::SessionState before{};
|
|
|
- before.family4RootSoid = 0x100;
|
|
|
- before.family4Version = 3;
|
|
|
- before.family4ResidentCount = 2;
|
|
|
- before.family4Active = true;
|
|
|
- before.family4Residents[0] = {0x100, 10};
|
|
|
- before.family4Residents[1] = {0x101, 11};
|
|
|
- expect(queuez::valid(before), "Family4 before state is valid");
|
|
|
-
|
|
|
- constexpr std::array<wire::Object, 3> objects{{
|
|
|
- {10, 0x100, wire::Encoding::oodle, {}},
|
|
|
- {11, 0x101, wire::Encoding::oodle, {}},
|
|
|
- {12, 0x202, wire::Encoding::oodle, {}},
|
|
|
- }};
|
|
|
- const wire::Family refresh{
|
|
|
- queuez::kAccountFamilyType, 0x100, 4, wire::kFullSnapshotFlag, objects};
|
|
|
- queuez::SessionState after{};
|
|
|
- expect(queuez::stage_family4_refresh(before, refresh, after),
|
|
|
- "Family4 next-version refresh stages");
|
|
|
- expect(after.family4Version == 4 && after.family4ResidentCount == 3,
|
|
|
- "Family4 refresh advances version and manifest");
|
|
|
- expect(after.family4Residents[2].objectSoid == 0x202, "Family4 refresh records added resident");
|
|
|
-
|
|
|
- wire::Family stale = refresh;
|
|
|
- stale.version = before.family4Version;
|
|
|
- queuez::SessionState rejected{};
|
|
|
- expect(!queuez::stage_family4_refresh(before, stale, rejected),
|
|
|
- "Family4 rejects stale refresh version");
|
|
|
- expect(rejected.family4Version == before.family4Version
|
|
|
- && rejected.family4ResidentCount == before.family4ResidentCount,
|
|
|
- "rejected refresh leaves exact before image");
|
|
|
-
|
|
|
- auto duplicateObjects = objects;
|
|
|
- duplicateObjects[2].version = duplicateObjects[1].version;
|
|
|
- wire::Family duplicate = refresh;
|
|
|
- duplicate.objects = duplicateObjects;
|
|
|
- expect(!queuez::stage_family4_refresh(before, duplicate, rejected),
|
|
|
- "Family4 rejects duplicate resident identity");
|
|
|
-}
|
|
|
-
|
|
|
-void test_outcome_exclusivity() {
|
|
|
- using namespace sunrise;
|
|
|
- server::web_service::Outcome outcome{};
|
|
|
- state::PendingItemState itemState{};
|
|
|
- itemState.prepared = true;
|
|
|
- outcome.mutation = itemState;
|
|
|
- expect(server::web_service::mutation_if<state::PendingItemState>(outcome) != nullptr,
|
|
|
- "Outcome stores item-state transaction");
|
|
|
- expect(server::web_service::mutation_if<state::PendingSocketPlug>(outcome) == nullptr,
|
|
|
- "Outcome exposes only one transaction alternative");
|
|
|
- state::PendingSocketPlug socket{};
|
|
|
- socket.prepared = true;
|
|
|
- outcome.mutation = socket;
|
|
|
- expect(server::web_service::mutation_if<state::PendingItemState>(outcome) == nullptr
|
|
|
- && server::web_service::mutation_if<state::PendingSocketPlug>(outcome) != nullptr,
|
|
|
- "Outcome replacement cannot retain a second mutation");
|
|
|
-}
|
|
|
-
|
|
|
-} // namespace
|
|
|
-
|
|
|
-int main() {
|
|
|
- test_opcode1901_fixture();
|
|
|
- test_class_art_selection();
|
|
|
- test_class_art_extraction();
|
|
|
- test_table_replacement();
|
|
|
- test_bucket_equipment_mapping_validation();
|
|
|
- test_family4_resync();
|
|
|
- test_outcome_exclusivity();
|
|
|
- if (failures != 0) {
|
|
|
- std::fprintf(stderr, "%d regression test(s) failed\n", failures);
|
|
|
- return 1;
|
|
|
- }
|
|
|
- std::puts("All Sunrise regression tests passed");
|
|
|
- return 0;
|
|
|
-}
|