/* * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published by the * Free Software Foundation; either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "Player.h" #include "ScriptMgr.h" #include "WorldSession.h" #include "WorldMock.h" #include "ObjectGuid.h" #include "ScriptDefines/MiscScript.h" #include "ScriptDefines/PlayerScript.h" #include "ScriptDefines/WorldObjectScript.h" #include "ScriptDefines/UnitScript.h" #include "ScriptDefines/CommandScript.h" #include "SharedDefines.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #ifndef TEST_F #define TEST_F(fixture, name) void fixture##_##name() #endif using namespace testing; namespace { class TestVisibilityScript : public PlayerScript { public: TestVisibilityScript() : PlayerScript("TestVisibilityScript", { PLAYERHOOK_ON_SET_SERVER_SIDE_VISIBILITY }) { } void OnPlayerSetServerSideVisibility(Player* player, ServerSideVisibilityType& type, AccountTypes& sec) override { ++CallCount; LastPlayer = player; LastType = type; LastSecurity = sec; } static void EnsureRegistered() { if (!Instance) Instance = new TestVisibilityScript(); } static void Reset() { CallCount = 0; LastPlayer = nullptr; LastType = SERVERSIDE_VISIBILITY_GM; LastSecurity = SEC_PLAYER; } inline static TestVisibilityScript* Instance = nullptr; inline static uint32 CallCount = 0; inline static Player* LastPlayer = nullptr; inline static ServerSideVisibilityType LastType = SERVERSIDE_VISIBILITY_GM; inline static AccountTypes LastSecurity = SEC_PLAYER; }; class TestPlayer : public Player { public: using Player::Player; void UpdateObjectVisibility(bool /*forced*/ = true, bool /*fromUpdate*/ = false) override { } void ForceInitValues(ObjectGuid::LowType guidLow = 1) { Object::_Create(guidLow, uint32(0), HighGuid::Player); } }; class GmVisibleCommandTest : public ::testing::Test { protected: void SetUp() override { EnsureScriptRegistriesInitialized(); TestVisibilityScript::EnsureRegistered(); originalWorld = sWorld.release(); worldMock = new NiceMock(); sWorld.reset(worldMock); static std::string emptyString; ON_CALL(*worldMock, GetDataPath()).WillByDefault(ReturnRef(emptyString)); ON_CALL(*worldMock, GetRealmName()).WillByDefault(ReturnRef(emptyString)); ON_CALL(*worldMock, GetDefaultDbcLocale()).WillByDefault(Return(LOCALE_enUS)); ON_CALL(*worldMock, getRate(_)).WillByDefault(Return(1.0f)); ON_CALL(*worldMock, getBoolConfig(_)).WillByDefault(Return(false)); ON_CALL(*worldMock, getIntConfig(_)).WillByDefault(Return(0)); ON_CALL(*worldMock, getFloatConfig(_)).WillByDefault(Return(0.0f)); ON_CALL(*worldMock, GetPlayerSecurityLimit()).WillByDefault(Return(SEC_PLAYER)); session = new WorldSession(1, "gm", 0, nullptr, SEC_GAMEMASTER, EXPANSION_WRATH_OF_THE_LICH_KING, 0, LOCALE_enUS, 0, false, false, 0); player = new TestPlayer(session); player->ForceInitValues(); session->SetPlayer(player); player->SetSession(session); TestVisibilityScript::Reset(); } void TearDown() override { // Intentional leaks of session/player to avoid database access in destructors. IWorld* currentWorld = sWorld.release(); delete currentWorld; worldMock = nullptr; sWorld.reset(originalWorld); originalWorld = nullptr; session = nullptr; player = nullptr; } void ExecuteCommand(std::string_view text) { if (text == ".gm visible off") { ApplyGmVisibleState(false); } else if (text == ".gm visible on") { ApplyGmVisibleState(true); } else { FAIL() << "Unsupported test command: " << text; } } static void EnsureScriptRegistriesInitialized() { static bool initialized = false; if (!initialized) { ScriptRegistry::InitEnabledHooksIfNeeded(MISCHOOK_END); ScriptRegistry::InitEnabledHooksIfNeeded(WORLDOBJECTHOOK_END); ScriptRegistry::InitEnabledHooksIfNeeded(UNITHOOK_END); ScriptRegistry::InitEnabledHooksIfNeeded(PLAYERHOOK_END); ScriptRegistry::InitEnabledHooksIfNeeded(ALLCOMMANDHOOK_END); initialized = true; } } IWorld* originalWorld = nullptr; NiceMock* worldMock = nullptr; WorldSession* session = nullptr; TestPlayer* player = nullptr; private: void ApplyGmVisibleState(bool makeVisible) { constexpr uint32 VISUAL_AURA = 37800; if (makeVisible) { player->RemoveAurasDueToSpell(VISUAL_AURA); player->SetGMVisible(true); } else { player->AddAura(VISUAL_AURA, player); player->SetGMVisible(false); } player->UpdateObjectVisibility(); } }; TEST_F(GmVisibleCommandTest, SetsPlayerInvisibleAndInvokesHook) { ExecuteCommand(".gm visible off"); EXPECT_EQ(TestVisibilityScript::CallCount, 1u); EXPECT_EQ(TestVisibilityScript::LastPlayer, player); EXPECT_EQ(TestVisibilityScript::LastType, SERVERSIDE_VISIBILITY_GM); EXPECT_EQ(TestVisibilityScript::LastSecurity, session->GetSecurity()); EXPECT_EQ(player->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM), uint32(session->GetSecurity())); EXPECT_FALSE(player->isGMVisible()); } TEST_F(GmVisibleCommandTest, SetsPlayerVisibleAndInvokesHook) { // Ensure the player starts from invisible state to test the opposite transition as well. ExecuteCommand(".gm visible off"); TestVisibilityScript::Reset(); ExecuteCommand(".gm visible on"); EXPECT_EQ(TestVisibilityScript::CallCount, 1u); EXPECT_EQ(TestVisibilityScript::LastPlayer, player); EXPECT_EQ(TestVisibilityScript::LastType, SERVERSIDE_VISIBILITY_GM); EXPECT_EQ(TestVisibilityScript::LastSecurity, SEC_PLAYER); EXPECT_EQ(player->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM), uint32(SEC_PLAYER)); EXPECT_TRUE(player->isGMVisible()); } }