From 91ec214eea09653c2a4684c390c30ac69dbaf794 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Tue, 4 Jun 2024 14:44:51 -0700 Subject: [PATCH] saving from a test works --- dDatabase/GameDatabase/CMakeLists.txt | 8 +- dDatabase/GameDatabase/Database.cpp | 5 + dDatabase/GameDatabase/Database.h | 4 + dDatabase/GameDatabase/TestSQL/CMakeLists.txt | 4 + .../GameDatabase/TestSQL/TestSQLDatabase.cpp | 290 ++++ .../GameDatabase/TestSQL/TestSQLDatabase.h | 91 ++ dGame/Character.cpp | 4 +- dGame/Character.h | 1 + tests/dGameTests/CMakeLists.txt | 1 + tests/dGameTests/GameDependencies.h | 4 + .../dComponentsTests/CMakeLists.txt | 6 + .../dComponentsTests/SavingTests.cpp | 31 + .../dComponentsTests/TestData/CMakeLists.txt | 10 + .../TestData/test_xml_data.xml | 1374 +++++++++++++++++ 14 files changed, 1830 insertions(+), 3 deletions(-) create mode 100644 dDatabase/GameDatabase/TestSQL/CMakeLists.txt create mode 100644 dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp create mode 100644 dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h create mode 100644 tests/dGameTests/dComponentsTests/SavingTests.cpp create mode 100644 tests/dGameTests/dComponentsTests/TestData/CMakeLists.txt create mode 100644 tests/dGameTests/dComponentsTests/TestData/test_xml_data.xml diff --git a/dDatabase/GameDatabase/CMakeLists.txt b/dDatabase/GameDatabase/CMakeLists.txt index 09ca7251..32fe414a 100644 --- a/dDatabase/GameDatabase/CMakeLists.txt +++ b/dDatabase/GameDatabase/CMakeLists.txt @@ -8,9 +8,15 @@ foreach(file ${DDATABSE_DATABSES_MYSQL_SOURCES}) set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "MySQL/${file}") endforeach() +add_subdirectory(TestSQL) + +foreach(file ${DDATABSE_DATABSES_TEST_SQL_SOURCES}) + set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "TestSQL/${file}") +endforeach() + add_library(dDatabaseGame STATIC ${DDATABASE_GAMEDATABASE_SOURCES}) target_include_directories(dDatabaseGame PUBLIC "." - "ITables" PRIVATE "MySQL" + "ITables" PRIVATE "MySQL" "TestSQL" "${PROJECT_SOURCE_DIR}/dCommon" "${PROJECT_SOURCE_DIR}/dCommon/dEnums" ) diff --git a/dDatabase/GameDatabase/Database.cpp b/dDatabase/GameDatabase/Database.cpp index cb4f989a..fef9ab39 100644 --- a/dDatabase/GameDatabase/Database.cpp +++ b/dDatabase/GameDatabase/Database.cpp @@ -38,3 +38,8 @@ void Database::Destroy(std::string source) { LOG("Trying to destroy database when it's not connected!"); } } + +void Database::_setDatabase(GameDatabase* const db) { + if (database) delete database; + database = db; +} diff --git a/dDatabase/GameDatabase/Database.h b/dDatabase/GameDatabase/Database.h index 3eb292d1..65b04722 100644 --- a/dDatabase/GameDatabase/Database.h +++ b/dDatabase/GameDatabase/Database.h @@ -9,4 +9,8 @@ namespace Database { void Connect(); GameDatabase* Get(); void Destroy(std::string source = ""); + + // Used for assigning a test database as the handler for database logic. + // Do not use in production code. + void _setDatabase(GameDatabase* const db); }; diff --git a/dDatabase/GameDatabase/TestSQL/CMakeLists.txt b/dDatabase/GameDatabase/TestSQL/CMakeLists.txt new file mode 100644 index 00000000..cf07f419 --- /dev/null +++ b/dDatabase/GameDatabase/TestSQL/CMakeLists.txt @@ -0,0 +1,4 @@ +SET(DDATABSE_DATABSES_TEST_SQL_SOURCES + "TestSQLDatabase.cpp" + PARENT_SCOPE +) diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp new file mode 100644 index 00000000..0c07adde --- /dev/null +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp @@ -0,0 +1,290 @@ +#include "TestSQLDatabase.h" + +void TestSQLDatabase::Connect() { + +} + +void TestSQLDatabase::Destroy(std::string source) { + +} + +sql::PreparedStatement* TestSQLDatabase::CreatePreppedStmt(const std::string& query) { + return nullptr; +} + +void TestSQLDatabase::Commit() { + +} + +bool TestSQLDatabase::GetAutoCommit() { + return {}; +} + +void TestSQLDatabase::SetAutoCommit(bool value) { + +} + +void TestSQLDatabase::ExecuteCustomQuery(const std::string_view query) { + +} + +std::optional TestSQLDatabase::GetMasterInfo() { + return {}; +} + +std::vector TestSQLDatabase::GetApprovedCharacterNames() { + return {}; +} + +std::vector TestSQLDatabase::GetFriendsList(uint32_t charID) { + return {}; +} + +std::optional TestSQLDatabase::GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) { + return {}; +} + +void TestSQLDatabase::SetBestFriendStatus(const uint32_t playerAccountId, const uint32_t friendAccountId, const uint32_t bestFriendStatus) { + +} + +void TestSQLDatabase::AddFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) { + +} + +void TestSQLDatabase::RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) { + +} + +void TestSQLDatabase::UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) { + +} + +void TestSQLDatabase::DeleteUgcModelData(const LWOOBJID& modelId) { + +} + +void TestSQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) { + +} + +std::vector TestSQLDatabase::GetAllUgcModels() { + return {}; +} + +void TestSQLDatabase::CreateMigrationHistoryTable() { + +} + +bool TestSQLDatabase::IsMigrationRun(const std::string_view str) { + return {}; +} + +void TestSQLDatabase::InsertMigration(const std::string_view str) { + +} + +std::optional TestSQLDatabase::GetCharacterInfo(const uint32_t charId) { + return {}; +} + +std::optional TestSQLDatabase::GetCharacterInfo(const std::string_view charId) { + return {}; +} + +std::string TestSQLDatabase::GetCharacterXml(const uint32_t accountId) { + return {}; +} + +void TestSQLDatabase::UpdateCharacterXml(const uint32_t characterId, const std::string_view lxfml) { + +} + +std::optional TestSQLDatabase::GetAccountInfo(const std::string_view username) { + return {}; +} + +void TestSQLDatabase::InsertNewCharacter(const ICharInfo::Info info) { + +} + +void TestSQLDatabase::InsertCharacterXml(const uint32_t accountId, const std::string_view lxfml) { + +} + +std::vector TestSQLDatabase::GetAccountCharacterIds(uint32_t accountId) { + return {}; +} + +void TestSQLDatabase::DeleteCharacter(const uint32_t characterId) { + +} + +void TestSQLDatabase::SetCharacterName(const uint32_t characterId, const std::string_view name) { + +} + +void TestSQLDatabase::SetPendingCharacterName(const uint32_t characterId, const std::string_view name) { + +} + +void TestSQLDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) { + +} + +void TestSQLDatabase::SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) { + +} + +std::optional TestSQLDatabase::GetPetNameInfo(const LWOOBJID& petId) { + return {}; +} + +std::optional TestSQLDatabase::GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) { + return {}; +} + +void TestSQLDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) { + +} + +void TestSQLDatabase::UpdatePropertyDetails(const IProperty::Info& info) { + +} + +void TestSQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) { + +} + +std::vector TestSQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) { + return {}; +} + +void TestSQLDatabase::RemoveUnreferencedUgcModels() { + +} + +void TestSQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) { + +} + +void TestSQLDatabase::UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) { + +} + +void TestSQLDatabase::RemoveModel(const LWOOBJID& modelId) { + +} + +void TestSQLDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) { + +} + +void TestSQLDatabase::InsertNewBugReport(const IBugReports::Info& info) { + +} + +void TestSQLDatabase::InsertCheatDetection(const IPlayerCheatDetections::Info& info) { + +} + +void TestSQLDatabase::InsertNewMail(const IMail::MailInfo& mail) { + +} + +void TestSQLDatabase::InsertNewUgcModel(std::istringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) { + +} + +std::vector TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) { + return {}; +} + +std::optional TestSQLDatabase::GetMail(const uint64_t mailId) { + return {}; +} + +uint32_t TestSQLDatabase::GetUnreadMailCount(const uint32_t characterId) { + return {}; +} + +void TestSQLDatabase::MarkMailRead(const uint64_t mailId) { + +} + +void TestSQLDatabase::DeleteMail(const uint64_t mailId) { + +} + +void TestSQLDatabase::ClaimMailItem(const uint64_t mailId) { + +} + +void TestSQLDatabase::InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) { + +} + +void TestSQLDatabase::UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) { + +} + +void TestSQLDatabase::UpdateAccountBan(const uint32_t accountId, const bool banned) { + +} + +void TestSQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) { + +} + +void TestSQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) { + +} + +void TestSQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) { + +} + +std::optional TestSQLDatabase::GetCurrentPersistentId() { + return {}; +} + +void TestSQLDatabase::InsertDefaultPersistentId() { + +} + +void TestSQLDatabase::UpdatePersistentId(const uint32_t id) { + +} + +std::optional TestSQLDatabase::GetDonationTotal(const uint32_t activityId) { + return {}; +} + +std::optional TestSQLDatabase::IsPlaykeyActive(const int32_t playkeyId) { + return {}; +} + +std::vector TestSQLDatabase::GetUgcModels(const LWOOBJID& propertyId) { + return {}; +} + +void TestSQLDatabase::AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) { + +} + +void TestSQLDatabase::RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) { + +} + +std::vector TestSQLDatabase::GetIgnoreList(const uint32_t playerId) { + return {}; +} + +void TestSQLDatabase::InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) { + +} + +std::vector TestSQLDatabase::GetRewardCodesByAccountID(const uint32_t account_id) { + return {}; +} + diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h new file mode 100644 index 00000000..085dc532 --- /dev/null +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h @@ -0,0 +1,91 @@ +#ifndef TESTSQLDATABASE_H +#define TESTSQLDATABASE_H + +#include "GameDatabase.h" + +class TestSQLDatabase : public GameDatabase { + void Connect() override; + void Destroy(std::string source = "") override; + + sql::PreparedStatement* CreatePreppedStmt(const std::string& query) override; + void Commit() override; + bool GetAutoCommit() override; + void SetAutoCommit(bool value) override; + void ExecuteCustomQuery(const std::string_view query) override; + + // Overloaded queries + std::optional GetMasterInfo() override; + + std::vector GetApprovedCharacterNames() override; + + std::vector GetFriendsList(uint32_t charID) override; + + std::optional GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) override; + void SetBestFriendStatus(const uint32_t playerAccountId, const uint32_t friendAccountId, const uint32_t bestFriendStatus) override; + void AddFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override; + void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override; + void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override; + void DeleteUgcModelData(const LWOOBJID& modelId) override; + void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override; + std::vector GetAllUgcModels() override; + void CreateMigrationHistoryTable() override; + bool IsMigrationRun(const std::string_view str) override; + void InsertMigration(const std::string_view str) override; + std::optional GetCharacterInfo(const uint32_t charId) override; + std::optional GetCharacterInfo(const std::string_view charId) override; + std::string GetCharacterXml(const uint32_t accountId) override; + void UpdateCharacterXml(const uint32_t characterId, const std::string_view lxfml) override; + std::optional GetAccountInfo(const std::string_view username) override; + void InsertNewCharacter(const ICharInfo::Info info) override; + void InsertCharacterXml(const uint32_t accountId, const std::string_view lxfml) override; + std::vector GetAccountCharacterIds(uint32_t accountId) override; + void DeleteCharacter(const uint32_t characterId) override; + void SetCharacterName(const uint32_t characterId, const std::string_view name) override; + void SetPendingCharacterName(const uint32_t characterId, const std::string_view name) override; + void UpdateLastLoggedInCharacter(const uint32_t characterId) override; + void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) override; + std::optional GetPetNameInfo(const LWOOBJID& petId) override; + std::optional GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override; + void UpdatePropertyModerationInfo(const IProperty::Info& info) override; + void UpdatePropertyDetails(const IProperty::Info& info) override; + void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override; + std::vector GetPropertyModels(const LWOOBJID& propertyId) override; + void RemoveUnreferencedUgcModels() override; + void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override; + void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) override; + void RemoveModel(const LWOOBJID& modelId) override; + void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; + void InsertNewBugReport(const IBugReports::Info& info) override; + void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; + void InsertNewMail(const IMail::MailInfo& mail) override; + void InsertNewUgcModel( + std::istringstream& sd0Data, + const uint32_t blueprintId, + const uint32_t accountId, + const uint32_t characterId) override; + std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override; + std::optional GetMail(const uint64_t mailId) override; + uint32_t GetUnreadMailCount(const uint32_t characterId) override; + void MarkMailRead(const uint64_t mailId) override; + void DeleteMail(const uint64_t mailId) override; + void ClaimMailItem(const uint64_t mailId) override; + void InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) override; + void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) override; + void UpdateAccountBan(const uint32_t accountId, const bool banned) override; + void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override; + void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override; + void SetMasterIp(const std::string_view ip, const uint32_t port) override; + std::optional GetCurrentPersistentId() override; + void InsertDefaultPersistentId() override; + void UpdatePersistentId(const uint32_t id) override; + std::optional GetDonationTotal(const uint32_t activityId) override; + std::optional IsPlaykeyActive(const int32_t playkeyId) override; + std::vector GetUgcModels(const LWOOBJID& propertyId) override; + void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override; + void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override; + std::vector GetIgnoreList(const uint32_t playerId) override; + void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override; + std::vector GetRewardCodesByAccountID(const uint32_t account_id) override; +}; + +#endif //!TESTSQLDATABASE_H diff --git a/dGame/Character.cpp b/dGame/Character.cpp index 57a951d9..3b76a5db 100644 --- a/dGame/Character.cpp +++ b/dGame/Character.cpp @@ -40,8 +40,8 @@ void Character::UpdateInfoFromDatabase() { auto charInfo = Database::Get()->GetCharacterInfo(m_ID); if (charInfo) { - m_Name = charInfo->name; - m_UnapprovedName = charInfo->pendingName; + m_Name = charInfo->name; + m_UnapprovedName = charInfo->pendingName; m_NameRejected = charInfo->needsRename; m_PropertyCloneID = charInfo->cloneId; m_PermissionMap = charInfo->permissionMap; diff --git a/dGame/Character.h b/dGame/Character.h index 7a83325b..4a86f64b 100644 --- a/dGame/Character.h +++ b/dGame/Character.h @@ -38,6 +38,7 @@ public: const std::string& GetXMLData() const { return m_XMLData; } const tinyxml2::XMLDocument& GetXMLDoc() const { return m_Doc; } + void _setXmlDoc(tinyxml2::XMLDocument& doc) { doc.DeepCopy(&m_Doc); } /** * Out of abundance of safety and clarity of what this saves, this is its own function. diff --git a/tests/dGameTests/CMakeLists.txt b/tests/dGameTests/CMakeLists.txt index 58f213e0..e1c29433 100644 --- a/tests/dGameTests/CMakeLists.txt +++ b/tests/dGameTests/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(dGameMessagesTests) list(APPEND DGAMETEST_SOURCES ${DGAMEMESSAGES_TESTS}) file(COPY ${GAMEMESSAGE_TESTBITSTREAMS} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +file(COPY ${COMPONENT_TEST_DATA} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) # Add the executable. Remember to add all tests above this! add_executable(dGameTests ${DGAMETEST_SOURCES}) diff --git a/tests/dGameTests/GameDependencies.h b/tests/dGameTests/GameDependencies.h index 52e3919a..e811446d 100644 --- a/tests/dGameTests/GameDependencies.h +++ b/tests/dGameTests/GameDependencies.h @@ -8,6 +8,7 @@ #include "EntityInfo.h" #include "EntityManager.h" #include "dConfig.h" +#include "dZoneManager.h" #include class dZoneManager; @@ -34,6 +35,8 @@ protected: Game::server = new dServerMock(); Game::config = new dConfig("worldconfig.ini"); Game::entityManager = new EntityManager(); + Game::zoneManager = new dZoneManager(); + Game::zoneManager->LoadZone(LWOZONEID(1, 0, 0)); // Create a CDClientManager instance and load from defaults CDClientManager::LoadValuesFromDefaults(); @@ -42,6 +45,7 @@ protected: void TearDownDependencies() { if (Game::server) delete Game::server; if (Game::entityManager) delete Game::entityManager; + if (Game::zoneManager) delete Game::zoneManager; if (Game::logger) { Game::logger->Flush(); delete Game::logger; diff --git a/tests/dGameTests/dComponentsTests/CMakeLists.txt b/tests/dGameTests/dComponentsTests/CMakeLists.txt index 374095af..f73f1214 100644 --- a/tests/dGameTests/dComponentsTests/CMakeLists.txt +++ b/tests/dGameTests/dComponentsTests/CMakeLists.txt @@ -2,6 +2,7 @@ set(DCOMPONENTS_TESTS "DestroyableComponentTests.cpp" "PetComponentTests.cpp" "SimplePhysicsComponentTests.cpp" + "SavingTests.cpp" ) # Get the folder name and prepend it to the files above @@ -10,3 +11,8 @@ list(TRANSFORM DCOMPONENTS_TESTS PREPEND "${thisFolderName}/") # Export to parent scope set(DCOMPONENTS_TESTS ${DCOMPONENTS_TESTS} PARENT_SCOPE) + +# Copy test files to testing directory +add_subdirectory(TestData) +list(TRANSFORM COMPONENT_TEST_DATA PREPEND "${thisFolderName}/") +set(COMPONENT_TEST_DATA ${COMPONENT_TEST_DATA} PARENT_SCOPE) diff --git a/tests/dGameTests/dComponentsTests/SavingTests.cpp b/tests/dGameTests/dComponentsTests/SavingTests.cpp new file mode 100644 index 00000000..97e7002d --- /dev/null +++ b/tests/dGameTests/dComponentsTests/SavingTests.cpp @@ -0,0 +1,31 @@ +#include "GameDependencies.h" +#include "Character.h" +#include "Entity.h" +#include "tinyxml2.h" + +class SavingTest : public GameDependenciesTest { +protected: + std::unique_ptr entity; + std::unique_ptr character; + tinyxml2::XMLDocument doc; + void SetUp() override { + SetUpDependencies(); + entity = std::make_unique(1, GameDependenciesTest::info); + character = std::make_unique(1, nullptr); + doc.LoadFile("./test_xml_data.xml"); + entity->SetCharacter(character.get()); + character->_setXmlDoc(doc); + character->SetEntity(entity.get()); + } + + void TearDown() override { + entity->SetCharacter(nullptr); + entity.reset(); + character.reset(); + TearDownDependencies(); + } +}; + +TEST_F(SavingTest, EntityLevelTest) { + character->SaveXMLToDatabase(); +} diff --git a/tests/dGameTests/dComponentsTests/TestData/CMakeLists.txt b/tests/dGameTests/dComponentsTests/TestData/CMakeLists.txt new file mode 100644 index 00000000..31e92542 --- /dev/null +++ b/tests/dGameTests/dComponentsTests/TestData/CMakeLists.txt @@ -0,0 +1,10 @@ +set(COMPONENT_TEST_DATA + "test_xml_data.xml" +) + +# Get the folder name and prepend it to the files above +get_filename_component(thisFolderName ${CMAKE_CURRENT_SOURCE_DIR} NAME) +list(TRANSFORM COMPONENT_TEST_DATA PREPEND "${thisFolderName}/") + +# Export our list of files +set(COMPONENT_TEST_DATA ${COMPONENT_TEST_DATA} PARENT_SCOPE) diff --git a/tests/dGameTests/dComponentsTests/TestData/test_xml_data.xml b/tests/dGameTests/dComponentsTests/TestData/test_xml_data.xml new file mode 100644 index 00000000..2c83d64a --- /dev/null +++ b/tests/dGameTests/dComponentsTests/TestData/test_xml_data.xml @@ -0,0 +1,1374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+

+ +