//////////////////////////////////////////////////////////////////////////////// // (c) Copyright, Ubisoft Entertainment Inc. || Created by Harbour // //////////////////////////////////////////////////////////////////////////////// #include "tests/common/helpers.h" #include "tests/common/badEvents.h" #include "tests/config.h" #include "tests/fixture/memoryCheckFixture.h" #include <../private/ubiservices/core/chrono/chronometer.h> #include <../private/ubiservices/core/helpers/instancesManager.h> #include #include #include #include #include #include #if defined(US_SDK_S2S) #include "ubiservices/services/event/types/eventTypeInfo.h" #endif #include #include #include <../private/ubiservices/services/preflightEndpoint.h> #include #include #include #if defined(US_SDK_S2S) #include #endif #if defined(US_PROFILE) #include #include #include #include #include #include #define FAPROFILER_ALLOCATED_MEMORY 16*1024*1024 #define FAPROFILER_FILE_NAME "UbiservicesProfile" #endif #if defined(US_TARGET_XONE) #include #include namespace harbour_printf //ubiservices_hack { int internalVPrintf(const char* format, va_list& argList); int internalPrintf(const char* format, ...); }; #elif defined(US_TARGET_SWITCH) #include "usCommon/nnx/firstPartyInit.h" #elif defined(US_TARGET_PS4) #include "usCommon/ps4/firstPartyInit.h" #elif defined(US_TARGET_KILI) #include "usCommon/kili/firstPartyInit.h" #elif defined(US_TARGET_GGP) #include "usCommon/ggp/firstPartyInit.h" #elif defined(US_TARGET_SWITCH) #define NintendoApplicationIdForUbiservices "01009d800a1d0000" #endif namespace US_NS {; #if defined(US_USE_LIBCURL) && !defined(US_DLL) extern bool g_skipEalMemForLibcurl; #endif extern US_DLL_API EventLog* g_testEventLog; // Only needed for test purposes #if !defined(US_SDK_S2S) namespace LeakChecker_BF { Vector getDefaultEventTypesForSaveGame() { US_TEST_STATIC_ASSERT(static_cast(EventType::ENUM_END) == 18u, "getDefaultEventTypesForSaveGame() needs to be updated"); // Default config accepts all event types (Note : This would be a wrong integration in games) return Vector { EventTypeInfo(EventType::PlayerAchievement), EventTypeInfo(EventType::GameStart), EventTypeInfo(EventType::GameSuspendedStart), EventTypeInfo(EventType::GameSuspendedStop), EventTypeInfo(EventType::PlayerStart), EventTypeInfo(EventType::ContextStart, "myContextType0"), EventTypeInfo(EventType::ContextStart, "myContextType1"), EventTypeInfo(EventType::ContextStart, "myContextType2"), EventTypeInfo(EventType::ContextStart, "myContextType3"), EventTypeInfo(EventType::ContextStop, "myContextType0"), EventTypeInfo(EventType::ContextStop, "myContextType1"), EventTypeInfo(EventType::ContextStop, "myContextType2"), EventTypeInfo(EventType::ContextStop, "myContextType3"), EventTypeInfo(EventType::GameLocalization), EventTypeInfo(EventType::HardwareSpecs), EventTypeInfo(EventType::PlayerProgression, "Mission"), EventTypeInfo(EventType::PlayerProgression, "myProgression"), EventTypeInfo(EventType::PlayerPosition), EventTypeInfo(EventType::PlayerPrimaryStore), EventTypeInfo(EventType::PlayerNewsAction), EventTypeInfo(EventType::PlayerNewsImpression), EventTypeInfo(EventType::PlayerStatsPlayTime), EventTypeInfo(EventType::Custom, CUSTOM_EVENT_NAME), EventTypeInfo(EventType::Service, TestHelpers_BF::SERVICE_EVENT_NAME), EventTypeInfo(EventType::Service, "TestingService"), }; } } #endif #if !defined(US_SDK_S2S) ::testing::AssertionResult WebsocketHelper::waitForWebsocketInit(Facade& facade) { class Condition : public US_TEST_NS::Helper::ConditionTemplate { public: Condition(Facade& facade) : m_facade(facade) , m_listener(facade.getAuthenticationClient().createNotificationListener()) { } bool operator()() const { return (m_facade.getNotificationClient().isConnectionActive() && !m_facade.getNotificationClient().getOwnConnectionInfo().m_contactUrl.isEmpty()); } private: //US_CLASS_COPY_NOT_ALLOWED(Condition) Condition(const Condition& dummy) : m_facade(dummy.m_facade) {} Condition& operator=(Condition&) { return *this; } Facade& m_facade; mutable ListenerHandler m_listener; } condition(facade); return US_TEST_NS::Helper::WaitFor(condition, 1ull, 10*1000ull, "websocket init") ? ::testing::AssertionSuccess() : ::testing::AssertionFailure() << "Timed out waiting for connection to be active"; } #endif #if !defined(US_RETAIL) extern bool US_DLL_API s_forceNonSecure; extern bool US_DLL_API g_forcePreflightURL[7]; #endif LeakChecker::LeakChecker(FixtureType type) : m_mightLeak(false) , m_willLeak(false) , m_type(type) , m_skipEalMemForLibcurl(false) #if defined(US_ENABLE_ALLOC_STATS) , m_currentTestAllocStatistics(g_stats) #endif { #if !defined(US_RETAIL) s_forceNonSecure = NS_COMMON::Options::forceUnsecure(); g_forcePreflightURL[PreflightEndpoint::Applications] = NS_COMMON::Options::preflightURL(PreflightEndpoint::Applications); g_forcePreflightURL[PreflightEndpoint::Users] = NS_COMMON::Options::preflightURL(PreflightEndpoint::Users); g_forcePreflightURL[PreflightEndpoint::Policies] = NS_COMMON::Options::preflightURL(PreflightEndpoint::Policies); g_forcePreflightURL[PreflightEndpoint::ProfilesExternal] = NS_COMMON::Options::preflightURL(PreflightEndpoint::ProfilesExternal); g_forcePreflightURL[PreflightEndpoint::Sessions] = NS_COMMON::Options::preflightURL(PreflightEndpoint::Sessions); #endif } LeakChecker::~LeakChecker() { #if !defined(US_RETAIL) s_forceNonSecure = false; #endif } void LeakChecker::SetUp() { #if defined(US_USE_LIBCURL) && !defined(US_DLL) && !defined(US_TARGET_SWITCH) g_skipEalMemForLibcurl = m_skipEalMemForLibcurl; #endif BadEvents::createInstance(); // Wait at least 1 ms to make sure that all subsequent memory allocations // will not have the same timer value than m_testStartTime. Otherwise // getAllocatedMemoryInfo would not display those allocations. Helper::sleep(1); #if defined(US_PROFILE) FAProfiler::Init(FAPROFILER_ALLOCATED_MEMORY); FAProfilerDisk::Init(); FAProfiler::Enable(); EalPerfLibInit(); // This fake a frame for the whole test // Not required but it can be helpful FAProfiler::BeginNewFrame(); #endif US_PERF_SET_CURRENT_THREADNAME("Main"); if (m_type == AutoExecuteInit || m_type == InitNoScheduler) { initDefaultSDK(); } if (m_type == AutoExecuteInit) { resetLogLevel(); #if defined(US_TARGET_XONE) // The following internal device allows to redirect UbiServices internal // logging into the Google test logging framework. This may be useful // to redirect all the logging sequence into a single output file. // This solution however does not work for unit tests explicitly // initializing the SDK. class LogDeviceGtest : public LogDevice { public: void output(const String& logString, LogLevel logLevel, const String& file, uint32 line) override { US_UNUSED(logLevel); US_UNUSED(file); US_UNUSED(line); harbour_printf::internalPrintf("%s", logString.getUtf8()); } }; InstancesManager::getInstance()->getEventLog().setLogDevice(US_NEW(LogDeviceGtest)); #endif } } void LeakChecker::TearDown() { if (m_type == AutoExecuteInit || m_type == InitNoScheduler) { uninitDefaultSDK(); } EXPECT_TRUE(!m_willLeak || SIMPL_NS::g_memoryLeakAtShutdown) << "[ US_TEST_ERROR_FAILED ] Expected memory leaks did not occurred in this test."; EXPECT_TRUE(m_willLeak || m_mightLeak || !SIMPL_NS::g_memoryLeakAtShutdown) << "[ US_TEST_ERROR_FAILED ] Memory leaks occurred during this test."; EXPECT_FALSE(SIMPL_NS::g_memoryCorruption) << "[ US_TEST_ERROR_FAILED ] Memory Corruption occurred during this test."; #if defined(US_PROFILE) // Profiler needs to be terminated after Ubiservices or it might crash during unitiliazeSDK() EalPerfLibShutdown(); FAProfiler::Disable(); FAProfilerDisk::Serialize(getTestName()); FAProfilerDisk::Shutdown(); FAProfiler::Shutdown(); #endif #if defined(US_ENABLE_ALLOC_STATS) { m_currentTestAllocStatistics.m_allocCountCurrent = g_stats.m_allocCountCurrent - m_currentTestAllocStatistics.m_allocCountCurrent; m_currentTestAllocStatistics.m_allocCountTotal = g_stats.m_allocCountTotal - m_currentTestAllocStatistics.m_allocCountTotal; m_currentTestAllocStatistics.m_allocSizeTotal = g_stats.m_allocSizeTotal - m_currentTestAllocStatistics.m_allocSizeTotal; m_currentTestAllocStatistics.m_allocArrayCountCurrent = g_stats.m_allocArrayCountCurrent - m_currentTestAllocStatistics.m_allocArrayCountCurrent; m_currentTestAllocStatistics.m_allocArrayCountTotal = g_stats.m_allocArrayCountTotal - m_currentTestAllocStatistics.m_allocArrayCountTotal; m_currentTestAllocStatistics.m_allocArraySizeTotal = g_stats.m_allocArraySizeTotal - m_currentTestAllocStatistics.m_allocArraySizeTotal; m_currentTestAllocStatistics.m_newArrayWithZero = g_stats.m_newArrayWithZero - m_currentTestAllocStatistics.m_newArrayWithZero; m_currentTestAllocStatistics.m_deleteWithNull = g_stats.m_deleteWithNull - m_currentTestAllocStatistics.m_deleteWithNull; m_currentTestAllocStatistics.m_deleteArrayWithNull = g_stats.m_deleteArrayWithNull - m_currentTestAllocStatistics.m_deleteArrayWithNull; } // Printing with tabs for easier adaptation to excel sheet US_NS::Log::print("\n"); US_NS::Log::print("--- Memory allocation statistics ---\n"); US_NS::Log::print("Stats: Test_name\tallocCountCurrent\tallocCountTotal\tallocSizeTotal\tallocArrayCountCurrent\t"); US_NS::Log::print("allocArrayCountTotal\tallocArraySizeTotal\tnewArrayWithZero\tdeleteWithNull\tdeleteArrayWithNull\n"); US_NS::Log::print("Stats: %s.%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", getTestCaseName(), getTestName(), m_currentTestAllocStatistics.m_allocCountCurrent , m_currentTestAllocStatistics.m_allocCountTotal, m_currentTestAllocStatistics.m_allocSizeTotal, m_currentTestAllocStatistics.m_allocArrayCountCurrent , m_currentTestAllocStatistics.m_allocArrayCountTotal, m_currentTestAllocStatistics.m_allocArraySizeTotal, m_currentTestAllocStatistics.m_newArrayWithZero , m_currentTestAllocStatistics.m_deleteWithNull, m_currentTestAllocStatistics.m_deleteArrayWithNull); US_NS::Log::print("-----------------------------------\n"); #endif // NB : This line needs to be placed AFTER the EalPerfLisShutdown in the US_PROFILE part. EXPECT_TRUE(g_AllocCounter == NULL) << "[ US_TEST_ERROR_FAILED ] Memory manager shouldn't be initialized anymore at the end of the test. " "Please check in this test that the number of SDK initialize matches the number of SDK uninitialize"; BadEvents::deleteInstance(); #if defined(US_USE_LIBCURL) && !defined(US_DLL) && !defined(US_TARGET_SWITCH) g_skipEalMemForLibcurl = false; #endif } FacadeInternal& LeakChecker::getFacadeInternal(Facade& facade) { return *facade.m_facadeInternal; } const FacadeInternal& LeakChecker::getFacadeInternal(const Facade& facade) { return *facade.m_facadeInternal; } EventLog* LeakChecker::getEventLog() { if (g_testEventLog == NULL) { g_testEventLog = US_NEW(EventLog); } return g_testEventLog; } void LeakChecker::outputLog(LogLevel level, LogCategory category, const char* string, const char* file, uint32 line) { if (g_testEventLog == NULL) { g_testEventLog = US_NEW(EventLog); } if (g_testEventLog->isLogEnabled(level, category)) { g_testEventLog->outputLog(level, category, string, file, line); } } void LeakChecker::setLogLevel(LogLevel logLevel) { if (g_testEventLog == NULL) { g_testEventLog = US_NEW(EventLog); } g_testEventLog->setLogLevel(logLevel); g_testEventLog->getOutputFormat().setShowTag(OutputFormat::Time, true); // Forces level to warning in case the previously executed test set it to an other value. if(InstancesManager::getInstance() != NULL) { EventLog& eventLog = InstancesManager::getInstance()->getEventLog(); eventLog.setLogLevel(logLevel); } } void LeakChecker::resetLogLevel() { setLogLevel(LogLevel::Warning); } void LeakChecker::setMultiLinesLog(bool state) { if (g_testEventLog == NULL) { g_testEventLog = US_NEW(EventLog); } g_testEventLog->enableMultiLines(state); // Setup for US_TEST_LOG EventLog #if defined(US_LOG_ENABLED) LoggingHelper::enableMultiLines(state); // Setup for US_LOG EventLog #endif } void LeakChecker::setupDebugLogging() { #if defined(US_LOG_ENABLED) LoggingHelper::enableMultiLines(true); LoggingHelper::enableVerbose(true); #endif setLogLevel(LogLevel::Debug); } void LeakChecker::disableLogCategories() { #if defined(US_LOG_ENABLED) for (uint32 i = static_cast(LogCategory::ENUM_BEGIN); i != static_cast(LogCategory::ENUM_END); ++i) { LoggingHelper::disableCategory(static_cast(i)); } #endif } void LeakChecker::enableLogCategories(const Vector& categories) { #if defined(US_LOG_ENABLED) disableLogCategories(); for (LogCategory category : categories) { LoggingHelper::enableCategory(category); } #else US_UNUSED(categories); #endif } HttpConfig LeakChecker::getHttpConfig() const { return HttpConfig(); } const char* LeakChecker::getTestName() const { const ::testing::TestInfo* testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); return testInfo->name(); } const char* US_NS::LeakChecker::getTestCaseName() const { const ::testing::TestInfo* testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); return testInfo->test_case_name(); } void LeakChecker::initDefaultSDK() { initializeSdk(); if (m_type != InitNoScheduler) { #if defined(ENABLE_PROXY_SETTINGS_TEST) US_NS::HttpConfig httpConfig; httpConfig.m_proxy.m_host = "127.0.0.1"; // Fiddler proxy port httpConfig.m_proxy.m_port = 8888; // Fiddler proxy port httpConfig.m_proxy.m_username = "username"; httpConfig.m_proxy.m_username = "password"; httpConfig.m_proxy.m_proxyType = HttpProxyConfig::Type::Enabled; #elif defined(US_TARGET_SWITCH) US_NS::HttpConfig httpConfig; //httpConfig.m_proxy.m_host = "127.0.0.1"; // Fiddler proxy port //httpConfig.m_proxy.m_port = 8888; // Fiddler proxy port //httpConfig.m_proxy.m_username = "username"; //httpConfig.m_proxy.m_username = "password"; httpConfig.m_proxy.m_proxyType = HttpProxyConfig::Type::AutoProxy; #else const US_NS::HttpConfig httpConfig = getHttpConfig(); #endif const GameConfig gameConfig(ApplicationId(NS_COMMON::Options::getApplicationId().c_str()) #if defined(US_SDK_TEST_CHINA) , String("CHINA_IS_PROD") #else , NS_COMMON::Options::getApplicationBuildId().c_str() #endif #if defined(US_SDK_S2S) , NS_COMMON::Options::environment() #endif #if defined(US_SDK_TEST_CHINA) , OnlineAccessContext::China #else , OnlineAccessContext::Standard #endif #if !defined(US_SDK_S2S) , GameConfigEvent(US_SDK_VERSION_CONST_CHAR , US_TEST_NS::US_SDK_DUMMY_GAME_SKU , LeakChecker_BF::getDefaultEventTypesForSaveGame() , StringJson("{}") , NS_COMMON::Options::getApplicationSecretKey().c_str()) #endif //!defined(US_SDK_S2S) #if defined(US_TARGET_PS4) || defined(US_TARGET_SWITCH) || defined(US_TARGET_XONE) || defined(US_TARGET_GGP) || defined(US_TARGET_KILI) , GameConfigConsole(NS_COMMON::FIRSTPARTY_APP_ID) #endif ); const SystemConfig systemConfig(US_NS::ThreadingConfig(), httpConfig); const US_NS::ConfigureResult result = configureSdk(gameConfig, systemConfig); if (result != ConfigureResult::Success) { PRINT_DEBUG("configureSdk FAILED with error : %d", result); } EXPECT_EQ(result, ConfigureResult::Success); #if !defined(US_SDK_S2S) EXPECT_TRUE(InstancesHelper::SetBackdoorEnvironment(NS_COMMON::Options::environment())); #endif } } void LeakChecker::uninitDefaultSDK() { US_TEST_NS::Helper::uninitializeSdk(); } #if defined(US_USE_LIBCURL) && !defined(US_DLL) && !defined(US_TARGET_SWITCH) void LeakCheckerNoLibcurlEalMem::SetUp() { g_skipEalMemForLibcurl = true; LeakChecker::SetUp(); } #endif LeakCheckerScopedInit::LeakCheckerScopedInit(LeakChecker& testInstance) : m_testInstance(testInstance) { m_testInstance.LeakChecker::initDefaultSDK(); } LeakCheckerScopedInit::~LeakCheckerScopedInit() { m_testInstance.LeakChecker::uninitDefaultSDK(); } };