//////////////////////////////////////////////////////////////////////////////// // (c) Copyright, Ubisoft Entertainment Inc. || Created by Harbour // //////////////////////////////////////////////////////////////////////////////// #include "tests/config.h" #include "tests/common/configs.h" #include "tests/common/helpers.h" #include "tests/fixture/authenticationFixture.h" #include "tests/fixture/loginMockFixture.h" #include "tests/fixture/memoryCheckFixture.h" #include "tests/mock/parametersMockHelper.h" #include "tests/mock/mockResultHelper.h" #include "tests/mock/MockHttpClientImpl.h" #include "tests/mock/mockLogDevice.h" #include "tests/mock/parametersMockHelper.h" #include "testsUnit/helpers/ubiservicesSdkStateManagerProxy.h" #include "testsIntegration/event/eventsConfigMockHelper.h" #if !defined(US_SDK_LIGHT_MOBILE) #include "testsIntegration/notificationHelper.h" #endif #if defined(US_SDK_FULL) #include "testsIntegration/storeHelper.h" #endif #include #include <../private/ubiservices/core/debug/assert.h> #include #include <../private/ubiservices/core/chrono/chronometer.h> #include <../private/ubiservices/core/configs/environmentHelper.h> #include <../private/ubiservices/core/configs/platformName.h> #include <../private/ubiservices/core/extensions/enumeration.h> #include #include #include #include #include <../private/ubiservices/platformHelperProxy.h> #include #include <../private/ubiservices/services/cacheManager.h> #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(US_DLL) #include <../private/ubiservices/services/notification/notificationClientProxy.h> #endif #include #include #include <../private/ubiservices/services/parameters/parametersClientProxy.h> #include <../private/ubiservices/services/parameters/parametersInfo.h> #include #include #include #if defined(US_TARGET_PS4) || defined(US_TARGET_XONE) || defined(US_TARGET_GGP) || defined(US_TARGET_KILI) #include #include #endif #include #include #include #include <../private/ubiservices/services/secondaryStore/sessionManagerStoreProxy.h> #include <../private/ubiservices/ubiServicesSdkStateInternal.h> #include #if defined(US_TARGET_XONE) #if defined(US_XONE_USE_XSAPI_CPP) #include #else #include "ubiservices/core/tasks/xone/xboxLiveContext.h" #endif #endif #if defined(US_TARGET_SWITCH) #include #endif #include #include namespace US_NS {; US_DEFINE_TEST(TestSuspendedMode); #define UPLAYPC_DUMMY_DLCID "dummyUplayPcItemId" #define UPLAYPC_DUMMY_CONSUMABLEID "dummyUplayPcConsumableId" #if defined(US_TARGET_PS4) || defined(US_TARGET_KILI) const char* const APP_ID_SHORT_EXPIRATION = "d2943775-5ae7-46bd-b871-819f01ae64eb"; //Short Expiration PS4 #elif !defined(US_TARGET_OSX) && !defined(US_TARGET_ANDROID_VS) && !defined(US_TARGET_LINUX) && !defined(US_TARGET_GGP) const char* const APP_ID_SHORT_EXPIRATION = "8700c8f7-c538-4ee4-8dfb-1d5650bd66ed"; //Short Expiration PC #endif #if !defined(US_DLL) namespace TestPrimaryStoreClient_BF { String renderPrimaryStoreMapping(); #if defined(US_PRIMARY_STORE_WITHOUT_API) && !defined(US_DLL) Vector getInventoryWithItemsForRetrySyncTrigger(uint32 balance); #endif }; namespace TestPrimarySecondaryStoreSync_BF { #if !defined(US_SDK_LIGHT_MOBILE) && !defined(US_DLL) bool waitForNotification(ListenerHandler& handler, SecondaryStoreNotificationType eventNotificationType, const DurationNano& waitTimeMax); Vector getSyncSuccessNotifications(ListenerHandler& handler, const DurationNano& waitTimeMax); #endif }; namespace TestSuspendedMode_EX { bool areAllCachesInvalid(FacadeInternal& facade) { return #if defined(US_ENABLE_CLUB) !CacheManager::isCacheValid(facade, CacheUnit::ClubBadges) && !CacheManager::isCacheValid(facade, CacheUnit::ClubActions) && !CacheManager::isCacheValid(facade, CacheUnit::ClubRewards) && //!CacheManager::isCacheValid(facade, CacheUnit::ClubChallenges) && // TODO: Is this cache missing? #endif !CacheManager::isCacheValid(facade, CacheUnit::Friends) && !CacheManager::isCacheValid(facade, CacheUnit::ProfileApplicationUsed) && // ProfilePopulations gets filled because there is call to population in JobResume[...], so cache IS valid ! //!CacheManager::isCacheValid(facade, CacheUnit::ProfilePopulations) && #if defined(US_SDK_FULL) !CacheManager::isCacheValid(facade, CacheUnit::StoreAttributesAll) && !CacheManager::isCacheValid(facade, CacheUnit::StoreRulesAll) && !CacheManager::isCacheValid(facade, CacheUnit::StoreListsOfAttributesAll) && #endif !CacheManager::isCacheValid(facade, CacheUnit::NewsProfile) && !CacheManager::isCacheValid(facade, CacheUnit::NewsSpace); } void spinDelay(const uint32 delayMsec) { HighResolutionChrono chrono; const uint64 delayNSec = ClockSteady::getNanoFromMilli(delayMsec); while (chrono.getElapsed() < delayNSec) { // do nothing !!! #if defined(US_TARGET_SWITCH) // TODO_NNX (The test fails otherwise... thread starving?) Helper::sleep(1); #endif } } }; #endif typedef LeakCheckerNoInit SUSPENDEDMODE_NOINIT; typedef AuthenticationFixture<1> SUSPENDEDMODE; typedef LoginMockFixture SUSPENDEDMODE_MOCK_LOGIN; typedef LoginMockFixture SUSPENDEDMODE_MOCK_NOLOGIN; namespace TestSuspendedMode_BF { const DurationMilli MOCK_PERIOD_JOB_WAIT_TIME = 3500; bool waitForEventNotification(ListenerHandler& handler, EventNotificationType eventNotificationType, bool mockWait) { class Condition : public US_TEST_NS::Helper::ConditionTemplate { public: Condition(ListenerHandler& handler, EventNotificationType eventNotificationType) : m_notification(AuthenticationNotificationType::None) , m_listener(handler) , m_eventNotificationType(eventNotificationType) { } bool operator()() const { return (m_listener.isNotificationAvailable() && m_listener.popNotification().m_type == m_eventNotificationType); } mutable AuthenticationNotificationType m_notification; private: //US_CLASS_COPY_NOT_ALLOWED(Condition) Condition(const Condition& dummy) : m_listener(dummy.m_listener), m_eventNotificationType(dummy.m_eventNotificationType) {} Condition& operator=(Condition&) { return *this; } ListenerHandler& m_listener; const EventNotificationType m_eventNotificationType; } condition(handler, eventNotificationType); US_ASSERT(eventNotificationType != EventNotificationType::None, "Impossible expectation by design"); const ClockSteady::TTimeMilli waitTimeMax = mockWait ? MOCK_PERIOD_JOB_WAIT_TIME * 4 : NS_COMMON::DEFAULT_WAIT_TIME; return US_TEST_NS::Helper::WaitFor(condition, 5ull, waitTimeMax, "event notification"); } AuthenticationNotificationType waitForExtendSessionNotification(ListenerHandler& listener, DurationNano waitTime) { class Condition : public US_TEST_NS::Helper::ConditionTemplate { public: Condition(ListenerHandler& listener) : m_notification(AuthenticationNotificationType::None) , m_listener(listener) { } bool operator()() const { if (m_listener.isNotificationAvailable()) { const AuthenticationNotificationType notif = m_listener.popNotification().m_type; if (notif == AuthenticationNotificationType::SessionInfoChanged || notif == AuthenticationNotificationType::SessionExtensionFailed) { m_notification = notif; return true; } } return false; } mutable AuthenticationNotificationType m_notification; private: //US_CLASS_COPY_NOT_ALLOWED(Condition) Condition(const Condition& dummy) { US_UNUSED(dummy); } Condition& operator=(Condition&) { return *this; } mutable ListenerHandler m_listener; } condition(listener); return US_TEST_NS::Helper::WaitFor(condition, 5ull, waitTime, "extend session") ? condition.m_notification : AuthenticationNotificationType::None; } struct Local { // Polls the notification client until an incoming message be available (or getting out of time) // then test that the message has the specified type, body and space. static ::testing::AssertionResult expectNotification( NotificationSource::ListenerHandler& handler, const StateNotificationType& type) { US_ASSERT(type != StateNotificationType::None, "Impossible expectation by design"); const ClockSteady::TDuration::TRep MAX_WAIT_TIME = 30*1000*1000; // microseconds StateNotification incoming; HighResolutionChrono chrono; uint32 messageCount = 0; bool done = false; do { if (handler.isNotificationAvailable()) { incoming = handler.popNotification(); done = true; } else if (++messageCount > 1) { //return ::testing::AssertionFailure() << "Two messages received within the timeframe..."; } } // Waits 10 seconds max (arbitrary resilience) while (!done && chrono.getElapsed() / 1000 < MAX_WAIT_TIME); if (!done) { return ::testing::AssertionFailure() << "No incoming data available after " << MAX_WAIT_TIME / 1000 << " ms"; } if (type != incoming.m_type) { return ::testing::AssertionFailure() << "Incoming message type unexpected " << static_cast(incoming.m_type) << " " "instead of " << static_cast(type); } return ::testing::AssertionSuccess(); } }; AsyncResultBatch changeState(PlatformHelper::State toState) { #if defined(US_TARGET_PS4) || defined(US_TARGET_XONE) || defined(US_TARGET_SWITCH) || defined(US_TARGET_KILI) || defined(US_TARGET_MANJARO) return PlatformHelperProxy::changeState(toState); #else return PlatformHelper::changeState(toState); #endif } String renderEmptyResult(const String& label) { JsonWriter emptyResult; emptyResult[label] = JsonWriter(JsonWriterType::Array).getJson(); return emptyResult.renderContent(); } String renderPrimaryStoreMappingOnlyDLCs() { JsonWriter mappingDlc; #if defined(US_PRIMARY_STORE_WITHOUT_API) mappingDlc["primaryStoreItemId"] = String(UPLAYPC_DUMMY_DLCID); #elif defined(US_TARGET_PS4) || defined(US_TARGET_KILI) mappingDlc["primaryStoreItemId"] = "UBISERVICESDLC01"; #elif defined(US_TARGET_XONE) mappingDlc["primaryStoreItemId"] = "6df724cf-af33-4767-842f-dcd7cf86b5ac"; //DLC-Skin #endif mappingDlc["items"] = JsonWriter(JsonWriterType::Array).getJson(); JsonWriter mappingElements(JsonWriterType::Array); mappingElements.addItemToArray(mappingDlc.getJson()); JsonWriter mapping; mapping["mappings"] = mappingElements.getJson(); return mapping.renderContent(); } String renderPrimaryStoreUpdateResult(bool hasResults = true, bool hasErrors = true, bool hasAdditionalDetails=true) { JsonWriter updateResult; ///mock results if (hasResults) { JsonWriter inventoryItem; inventoryItem["itemId"] = "11111111-2222-3333-4444-555566667777"; inventoryItem["quantity"] = 42; inventoryItem["quantityBefore"] = 42; inventoryItem["lastModified"] = DateTimeHelper::formatDateISO8601(DateTime(2015, 01, 10, 14, 03, 58)); inventoryItem["expirationDate"] = DateTimeHelper::formatDateISO8601(DateTime(2016, 02, 10, 14, 03, 58)); inventoryItem["expirationDetails"].setToNull(); JsonWriter transactionWriter; transactionWriter["transactionId"] = "ABABABAB-CCCC-DDDD-EEEE-112233445566"; transactionWriter["items"].addItemToArray(inventoryItem.getJson()); updateResult["results"].addItemToArray(transactionWriter.getJson()); } else { updateResult["results"] = JsonWriter(JsonWriterType::Array).getJson(); } if (hasErrors) // mock errors { JsonWriter errorWriter; errorWriter["errorMessage"] = "MOCKEDERROR"; errorWriter["primaryStoreItemId"] = "MOCKEDID"; updateResult["errors"].addItemToArray(errorWriter.getJson()); } else { updateResult["errors"] = JsonWriter(JsonWriterType::Array).getJson(); } if (hasAdditionalDetails) { JsonWriter additionalDetailsItem; additionalDetailsItem["primaryStoreItemId"] = "11111111-2222-3333-4444-555566664444"; additionalDetailsItem["code"] = 0; additionalDetailsItem["description"] = "Test."; updateResult["additionalDetails"].addItemToArray(additionalDetailsItem.getJson()); } else { updateResult["additionalDetails"] = JsonWriter(JsonWriterType::Array).getJson(); } return updateResult.renderContent(); } String renderEmptyOffersResult(const String& label = "offers") { JsonWriter offersResult; offersResult[label] = JsonWriter(JsonWriterType::Array).getJson(); return offersResult.renderContent(); } void setupMockExpectationsForPrimaryStoreAfterSuspendedMode(MockHttpClientImpl& httpClientImpl, const MockAsyncResult& result) { #if defined(US_PRIMARY_STORE_WITH_API) { // When going back to foreground, the primary store sync will be triggered US_EXPECT_CALL_HTTP(httpClientImpl, HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .Times(testing::AtMost(1)) .WillRepeatedly(testing::Return(result)) .RetiresOnSaturation(); } #else US_UNUSED(httpClientImpl); US_UNUSED(result); #endif } // This simulate a spaceId change (like being assigned to a Staging spaceId) class ParametersApplicationMockHelperWithDifferentSpaceId : public ParametersApplicationMockHelper { private: Json buildParameterGroupStaging() const override { JsonWriter fields; fields["spaceId"] = "00000000-1111-2222-3333-444444444444"; return renderParameterGroup(fields.getJson()); } Json buildFeatureSwitchesParameterGroup() const override { JsonWriter fields; fields["ThisIsANewFeatureSwitchForParametersApplicationMockHelperWithDifferentSpaceId"] = true; return renderParameterGroup(fields.getJson()); } }; class ParametersSpaceMockHelperUpdatedParams : public ParametersSpaceMockHelper { public: ParametersSpaceMockHelperUpdatedParams(uint32 maxCount = 20) :m_maxCount(maxCount) { } private: Json buildParameterGroupSettingsHttpInternal() const override { JsonWriter httpParamJson; httpParamJson["maxCount"] = m_maxCount; httpParamJson["retryIncrementFactorMsec"] = 3000; httpParamJson["retryRandomDelayMsec"] = 4000; httpParamJson["retryInitialDelayMsec"] = 5000; httpParamJson["timeoutIncrementFactorMsec"] = 6000; httpParamJson["timeoutInitialDelayMsec"] = 7000; return renderParameterGroup(httpParamJson.getJson()); } uint32 m_maxCount; }; bool waitForParametersNotification(ListenerHandler& handler, ParametersNotificationType eventNotificationType, const DurationNano& waitTimeMax) { HighResolutionChrono chrono; while (chrono.getElapsed() < waitTimeMax) { if (handler.isNotificationAvailable()) { ParametersNotificationType type = handler.popNotification().m_type; if (type == eventNotificationType) { PRINT_LINE("Expected ParametersNotification received Type: %d", type); return true; } else { PRINT_LINE("Unexpected ParametersNotification received Type: %d", type); } } Helper::sleep(100); } return false; } bool waitForAuthentificationNotification(ListenerHandler& handler, AuthenticationNotificationType eventNotificationType, const DurationNano& waitTimeMax) { HighResolutionChrono chrono; while (chrono.getElapsed() < waitTimeMax) { if (handler.isNotificationAvailable()) { AuthenticationNotificationType type = handler.popNotification().m_type; if (type == eventNotificationType) { PRINT_LINE("Expected AuthenticationNotification received Type: %d", type); return true; } else { PRINT_LINE("Unexpected AuthenticationNotification received Type: %d", type); } } Helper::sleep(100); } return false; } class ParametersApplicationMockHelperDefaultPopulations : public ParametersApplicationMockHelper { public: ParametersApplicationMockHelperDefaultPopulations(const String& name = String(), const String& subject = String(), const SpaceId& spaceId = SpaceId(MOCKED_GUID_ID1)) : m_name(name) , m_subject(subject) , m_spaceId(spaceId) { } private: Json buildParameterGroupStaging() const override { JsonWriter fields; fields["spaceId"] = m_spaceId; return renderParameterGroup(fields.getJson()); } Json buildRelatedPopulationsCategory() const override { JsonWriter population; { JsonWriter writer; writer["name"] = m_name; writer["subject"] = m_subject; population["relatedPopulation"] = writer.getJson(); } return population.getJson(); } const String m_name; const String m_subject; const SpaceId m_spaceId; }; } namespace TestPopulations_BF { extern const char* POPULATION_SUBJECT_DEFAULT; extern const char* POPULATION_NAME_DEFAULT; // The expected subject-population name extern const char* POPULATION_SUBJECT_AFTER_PUT_POPULATION; extern const char* POPULATION_NAME_AFTER_PUT_POPULATION; // The expected subject-population name that makes SpaceId changes extern const char* POPULATION_SUBJECT_WITH_NEW_SPACEID; extern const char* POPULATION_NAME_WITH_NEW_SPACEID; // The expected subject-population name when in new SpaceId extern const char* POPULATION_SUBJECT_ON_NEW_SPACEID; extern const char* POPULATION_NAME_ON_NEW_SPACEID; // Test SpaceId created for Dynamic Population test // Used by all platforms extern const char* UBISERVICES_STAGING_TEST_PC_RETAIL_SPACEID; extern const char* UBISERVICES_STAGING_TEST_PC_STAGING_SPACEID; extern const char* SPACE_ID_FOR_NEW_POPULATION; } // TODO: This test does not validation much of what is stated... /// @test Exercises the code path when there is no session created before going /// to suspended mode (there is therefore no need to create a new session and /// process the other related tasks for resuming). TEST_F(SUSPENDEDMODE, US_DeleteSessionBeforeSuspended) { ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().deleteSession())); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); } /// @test Ensures that existing job are canceled when the platform goes to suspended mode TEST_F(SUSPENDEDMODE, US_CancelDuringSuspended) { HttpClient& client = getFacade().getHttpClient(); const StringUrl url = TestHelpers_BF::getHttpTestUrl("get", String::formatText("delay=%i", 1)); // 1 second const AsyncResult result = client.sendRequest(HttpGet(url, TestHelpers_BF::getHttpBoxHeader())); EXPECT_TRUE(result.isProcessing()); // The test is not that much efficient: // The HTTP request is canceled before reaching the HTTP engine thread (verified on PS4). ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); US_TEST_NS::Helper::sleep(2000); // Should be sufficient for the sendRequest task to be finished when the SDK is not suspended. EXPECT_TRUE(result.isProcessing()); EXPECT_FALSE(result.isCanceled()); #if defined(US_SDK_TEST_CHINA) // Will need to be investigated !!! Slow China network speed make this test fails sometime... ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground), 60000)); #else ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); #endif US_EXPECT_FAILURE(result, Errors::CANCELED_SUSPENDED_MODE); EXPECT_TRUE(result.isCanceled()); } #if !defined(US_SDK_LIGHT_MOBILE) /// @test Ensures that new service calls that are launched while the platform is suspended return a PLATFORM_SUSPENDED error TEST_F(SUSPENDEDMODE, US_NewCallsForbiddenDuringSuspendedMode) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); #if defined(US_ENABLE_CLUB_TEST) EXPECT_TRUE(AsyncHelper::waitFailure(getFacade().getClubClient().requestActionsAll(), Errors::PLATFORM_SUSPENDED)); #else EXPECT_TRUE(AsyncHelper::waitFailure(getFacade().getNewsClient().requestProfileNews(), Errors::PLATFORM_SUSPENDED)); #endif ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))) << "Resuming is required to ensure next tests will not run in suspended mode"; } /// @test Ensures that service calls are correctly running once the platform is back from suspended mode #if defined(US_TARGET_IOS) && !defined(US_UPLAY_LOGIN_FOR_IOS) || defined(US_TARGET_SWITCH) || defined(US_SDK_TEST_CHINA) TEST_F(SUSPENDEDMODE, DISABLED_US_CallsSucceedAfterSuspendedMode) // News Services does not support Game Center tickets #else TEST_F(SUSPENDEDMODE, US_CallsSucceedAfterSuspendedMode) #endif { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getNewsClient().requestProfileNews(SpaceId(US_TEST_NS::SPACE_ID_US_SDK_PC)))); } #endif // !defined(US_SDK_LIGHT_MOBILE) // UbiServicesSdkStateManagerProxy is not in the public API, and thus is not available in DLL #if !defined(US_DLL) ///////////////////////////////////////////////////////////////// /// There are 21 possible transitions in the SDK : /// 1) FG->FG US_TransitionForegroundToForeground /// 2) FG->BG US_TransitionBackgroundToBackground /// 3) FG->SUSP US_TransitionSuspendedToSuspended /// 4) BG->BGFG US_TransitionBackgroundToForeground /// 5) BG->BG US_TransitionBackgroundToBackground /// 6) BG->SUSP US_TransitionBackgroundToSuspended /// 7) SUSP->SUSPFG US_TransitionSuspendedToForeground /// 8) SUSP->SUSP(BG) US_TransitionSuspendedToBackgroundError /// 9) SUSP->SUSP(SUSP) US_TransitionSuspendedToSuspended /// 10) BGFG->BGFG US_TransitionBackgroundToForeground /// 11) BGFG->BG US_TransitionBackgroundToForegroundToBackground /// 12) BGFG->SUSP US_TransitionBackgroundToForegroundToSuspended /// 13) BGFG->FG US_TransitionBackgroundToForeground /// 14) SUSPFG->SUSPFG US_TransitionSuspendedToForeground /// 15) SUSPFG->SUSPFGBG US_TransitionSuspendedToForegroundToBackground /// 16) SUSPFG->SUSP US_TransitionSuspendedToForegroundToSuspended /// 17) SUSPFG->FG US_TransitionSuspendedToForeground /// 18) SUSPFGBG->SUSPFG US_TransitionSuspendedToForegroundToBackgroundToForeground /// 19) SUSPFGBG->SUSPFGBG US_TransitionSuspendedToForegroundToBackgroundToBackground /// 20) SUSPFGBG->SUSP US_TransitionSuspendedToForegroundToBackgroundToSuspended /// 21) SUSPFGBG->BG US_TransitionSuspendedToForegroundToBackground /// @test Validates the transition 1) Foreground to Foreground TEST_F(SUSPENDEDMODE, US_TransitionForegroundToForeground) { EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); } /// @test Validates the transition 2) Foreground to Background /// and 5) Background to Background TEST_F(SUSPENDEDMODE, US_TransitionBackgroundToBackground) { EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); } /// @test Validates the transition 3) Foreground to Suspended /// and 9) Suspended to Suspended from a call to transitionTo(Suspended) TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToSuspended) { EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); // Go back to foreground in order to be able to finish the test cleanly ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); } /// @test Validates the transition 8) Suspended to Suspended from a call to transitionTo(Background) TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToBackgroundError) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitFailure(TestSuspendedMode_BF::changeState(PlatformHelper::Background), Errors::RESUME_INVALID_STATE)); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); // Go back to foreground in order to be able to finish the test cleanly ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); } /// @test Validates the transition 4) Background to BackgroundToForeground /// 10) BackgroundToForeground to BackgroundToForeground /// 13) BackgroundToForeground to Foreground TEST_F(SUSPENDEDMODE, US_TransitionBackgroundToForeground) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); const AsyncResultBatch resultBgToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::BackgroundToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultBgToFg.isProcessing()); const AsyncResultBatch resultNewBgToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_TRUE(resultNewBgToFg.isProcessing()); EXPECT_TRUE(resultNewBgToFg == resultBgToFg); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::BackgroundToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(resultBgToFg)); EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); } class TestSuspendedModeHelper { public: static int32 getRefCount(const Facade& facade); static int32 getRefCount(const FacadeInternal& facade); static void copyRef(SmartPtr& target, const Facade& source); static const HttpHeader& getRemoteLoggerSdkHeaders(FacadeInternal& facade); }; /// @test Validates that a Facade can be deleted just after a transition from background to foreground. /// The transition to foreground starts the JobResumeFocusMaster, that requires Facade(s) to be valid during its execution. /// A SmartPtr in the Facade keeps the FacadeInternal alive while JobResumeFocusMaster runs. /// The Facade(s) will be deleted once the JobResumeFocusMaster is done. TEST_F(SUSPENDEDMODE, US_TransitionBackgroundToForegroundWithDeletedFacade) { // Note: // There is already an existing Facade when this test starts (created by AuthentificationFixture) SmartPtr facadeKeep; { Facade facade2; // Facade has 1 // FacadesManager has 1 EXPECT_EQ(2, TestSuspendedModeHelper::getRefCount(facade2)); // Keep the newly created facade so we can validate its refcount once the JobResumeFocusMaster is done. TestSuspendedModeHelper::copyRef(facadeKeep, facade2); //facadeKeep = facade2.m_facadeInternal; EXPECT_EQ(3, TestSuspendedModeHelper::getRefCount(facade2)); // Go to background and wait until done ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); // Back to foreground, no waiting! const AsyncResultBatch result = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::BackgroundToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(result)); // Wait for transition to complete } // Facade2 is now deleted // FacadesManager has also release the FacadeInternal US_TEST_NS::Helper::sleep(1000); EXPECT_EQ(1, TestSuspendedModeHelper::getRefCount(*facadeKeep)); } /// @test Validates that a Facade can be deleted by a Game just after /// a transition from suspended to foreground. /// The transition to foreground starts the JobResumeFromSuspended, that requires Facade(s) to be valid during its execution. /// A SmartPtr in the Facade keep the FacadeInternal alive while JobResumeFromSuspended runs. /// The Facade(s) will be deleted once the JobResumeFromSuspended is done. TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToForegroundWithDeletedFacade) { SmartPtr facadeTemp; AsyncResultBatch result; { // Note: // There is already one existing Facade before this test starts // It was created in the AuthentificationFixture Facade facade2; // Facade has 1 // FacadesManager has 1 EXPECT_EQ(2, TestSuspendedModeHelper::getRefCount(facade2)); // Keep the newly created facade so we can validate its refcount once the JobResumeFocusMaster is done. TestSuspendedModeHelper::copyRef(facadeTemp, facade2); //facadeTemp = facade2.m_facadeInternal; EXPECT_EQ(3, TestSuspendedModeHelper::getRefCount(facade2)); // Go to Suspended and wait until done ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); // Back to foreground, no waiting! result = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); } // Facade2 is now deleted // FacadesManager has also release the FacadeInternal // Only JobResumeFocusMaster and this test keep the facade alive. // Wait for transition ASSERT_TRUE(AsyncHelper::waitSuccess(result)); // The facadeTemp SmartPtr is the only one keeping the Facade alive, so refcount should be 1 EXPECT_EQ(1, TestSuspendedModeHelper::getRefCount(*facadeTemp)); } /// @test Validates that a Facade can be deleted by a Game just after /// a transition from background to foreground. /// The transition to foreground starts the JobResumeFocusMaster, that requires Facade(s) to be valid during its execution. /// A SmartPtr keep the Facade alive while JobResumeFocusMaster runs. /// The Facade(s) will be deleted once the JobResumeFocusMaster is done. /// This test is different from the one above because there is no delay. /// The test will crash if something is wrong ! TEST_F(SUSPENDEDMODE, US_TransitionBackgroundToForegroundWithDeletedFacadeSuperFast) { setupDebugLogging(); uint32 ITERATION_COUNT = 5; PrepareLoggingMock([ITERATION_COUNT](US_TEST_NS::MockLogDevice& device) { US_EXPECT_LOG("UbiServices SDK resuming from background mode.", LogLevel::Info, Times(ITERATION_COUNT)); }); for (uint32 i = 0; i < ITERATION_COUNT; i++) { PRINT_PROGRESS(i + 1, ITERATION_COUNT, "Iteration %d/%d - State %d", i + 1, ITERATION_COUNT, PlatformHelper::getState()); { // Note: // There is already one existing Facade before this test starts // It was created in the AuthentificationFixture Facade facade2; // Go to background and wait until done ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); // Back to foreground, no waiting ! AsyncResultBatch resultBgToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::BackgroundToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); } // Facade2 is now deleted // FacadesManager has also release the FacadeInternal // Only JobResumeFocusMaster keep the facade alive. // Before the use of the SmartPtr in the Facade, // the JobResumeFocusMaster would have crash here // because the facade would have been deleted. } } /// @test Validates that a Facade can be deleted by a Game just after /// a transition from suspended to foreground. /// The transition to foreground starts the JobResumeFromSuspended, that requires Facade(s) to be valid during its execution. /// A SmartPtr keep the Facade alive while JobResumeFromSuspended runs. /// The Facade(s) will be deleted once the JobResumeFromSuspended is done. /// This test is different from the one above because there is no delay. /// The test will crash if something is wrong ! TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToForegroundWithDeletedFacadeSuperFast) { setupDebugLogging(); uint32 ITERATION_COUNT = 5; PrepareLoggingMock([ITERATION_COUNT](US_TEST_NS::MockLogDevice& device) { US_EXPECT_LOG("UbiServices SDK resuming from suspended mode.", LogLevel::Info, Times(ITERATION_COUNT)); }); AsyncResultBatch result; for (uint32 i = 0; i < ITERATION_COUNT; i++) { PRINT_PROGRESS(i + 1, ITERATION_COUNT, "Iteration %d/%d - State %d", i + 1, ITERATION_COUNT, PlatformHelper::getState()); { // Note: // There is already one existing Facade before this test starts // It was created in the AuthentificationFixture Facade facade2; // Go to Suspended and wait until done ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); // Back to foreground, no waiting ! result = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); } // Facade2 is now deleted // FacadesManager has also release the FacadeInternal // Only JobResumeFromSuspended keep the facade alive. // Before the use of the SmartPtr in the Facade, // the JobResumeFromSuspended would have crash here // because the facade would have been deleted. } // Wait for state to be foreground // This is required because the FacadeInternal::deleteSession() // will raise an error is the current state is Suspended // So we need to wait for Foreground AsyncHelper::waitSuccess(result); } /// @test Validates that a Facade can be deleted by a Game just after /// a transition from background to foreground. /// The transition to foreground starts the JobResumeFocusMaster, that requires Facade(s) to be valid during its execution. /// A SmartPtr keep the Facade alive while JobResumeFocusMaster runs. /// The Facade(s) will be deleted once the JobResumeFocusMaster is done. /// This test is different from the one above because we increment sleep time at each loop /// The test will crash if something is wrong ! TEST_F(SUSPENDEDMODE, US_TransitionBackgroundToForegroundWithDeletedFacadeWithSleep) { setupDebugLogging(); uint32 ITERATION_COUNT = 50; PrepareLoggingMock([ITERATION_COUNT](US_TEST_NS::MockLogDevice& device) { US_EXPECT_LOG("UbiServices SDK resuming from background mode.", LogLevel::Info, Times(ITERATION_COUNT)); }); for (uint32 i = 0; i < ITERATION_COUNT; i++) { PRINT_PROGRESS(i + 1, ITERATION_COUNT, "Iteration %d/%d - State %d", i + 1, ITERATION_COUNT, PlatformHelper::getState()); { // Note: // There is already one existing Facade before this test starts // It was created in the AuthentificationFixture Facade facade2; // Go to background and wait until done ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); // Back to foreground, no waiting! const AsyncResultBatch resultBgToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::BackgroundToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); TestSuspendedMode_EX::spinDelay(i * 10); // milliseconds } // The test needs to wait the transition completion to foreground...? // while (PlatformHelper::getState() != PlatformHelper::Foreground) // { // PRINT_LINE("Waiting foreground"); // Helper::sleep(1); // } // Facade2 is now deleted // FacadesManager has also release the FacadeInternal // JobResumeFocusMaster keeps the facade alive. // Before the use of the SmartPtr in the Facade, // the JobResumeFocusMaster would have crash here // because the facade would have been deleted. } } /// @test Validates that a Facade can be deleted by a game just after /// a transition from suspended to foreground. /// The transition to foreground starts the JobResumeFromSuspended, /// that requires Facade(s) to be valid during its execution. /// A SmartPtr keeps the Facade alive while JobResumeFromSuspended runs. /// The Facade(s) will be deleted once the JobResumeFromSuspended is done. /// This test is different from the one above because we increment sleep time at each loop /// The test crashes if something is wrong! TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToForegroundWithDeletedFacadeWithSleep) { setupDebugLogging(); uint32 ITERATION_COUNT = 50; PrepareLoggingMock([ITERATION_COUNT](US_TEST_NS::MockLogDevice& device) { US_EXPECT_LOG("UbiServices SDK resuming from suspended mode.", LogLevel::Info, Times(ITERATION_COUNT)); }); for (uint32 i = 0; i < ITERATION_COUNT; i++) { PRINT_PROGRESS(i + 1, ITERATION_COUNT, "Iteration %d/%d - State %d", i + 1, ITERATION_COUNT, PlatformHelper::getState()); AsyncResultBatch resultBgToFg; { // Note: // There is already one existing Facade before this test starts // It was created in the AuthentificationFixture Facade facade2; // Go to background and wait until done ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); // Back to foreground, no waiting ! resultBgToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); TestSuspendedMode_EX::spinDelay(i*10); // milliseconds } // Facade2 is now deleted // FacadesManager has also release the FacadeInternal // Only JobResumeFromSuspended keep the facade alive. // Before the use of the SmartPtr in the Facade, // the JobResumeFromSuspended would have crash here // because the facade would have been deleted. // Wait for the resume to foreground to finish in order to avoid concurrency/race conditions on the jobs // that handle the state change during the next loop. ASSERT_TRUE(AsyncHelper::waitSuccess(resultBgToFg)); } } /// @test Validates that a Facade can be deleted by a Game just after /// a transition from background to foreground. /// The transition to foreground starts the JobResumeFocusMaster, that requires Facade(s) to be valid during its execution. /// A SmartPtr keep the Facade alive while JobResumeFocusMaster runs. /// The Facade(s) will be deleted once the JobResumeFocusMaster is done. TEST_F(SUSPENDEDMODE, DISABLED_MANUAL_US_TransitionBackgroundToForegroundWithDeletedFacade) { uint32 loopCount = 1; do { PRINT_DEBUG("Loop #%d", loopCount); PRINT_DEBUG("Waiting for Foreground State"); while (PlatformHelper::getState() != PlatformHelper::Foreground) { Helper::sleep(1); } PRINT_DEBUG("State is now Foreground"); { // Note: // There is already one existing Facade before this test starts // It was created in the AuthentificationFixture Facade facade2; PRINT_DEBUG("Waiting for Background State"); while (PlatformHelper::getState() != PlatformHelper::Background) { Helper::sleep(1); } PRINT_DEBUG("State is now Background"); PRINT_DEBUG("Waiting for Foreground State"); while (PlatformHelper::getState() != PlatformHelper::Foreground) { Helper::sleep(1); } PRINT_DEBUG("State is now Foreground"); } // Facade2 is now deleted // FacadesManager has also release the FacadeInternal // Only JobResumeFocusMaster keep the facade alive. // Before the use of the SmartPtr in the Facade, // the JobResumeFocusMaster would have crash here // because the facade would have been deleted. } while (++loopCount < 5); } /// @test Validates the transition events TEST_F(SUSPENDEDMODE, US_ValidatePlatformTransitionStateEvent) { InstancesManager* instancesManager = InstancesManager::getInstance(); ASSERT_TRUE(instancesManager != nullptr); NotificationSource::ListenerHandler handler = instancesManager->createNotificationListener(); /////////////////////////////// // To Background ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); // Pop incoming notifications EXPECT_TRUE(TestSuspendedMode_BF::Local::expectNotification(handler, StateNotificationType::Background)); /////////////////////////////// // To Foreground ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); // Pop incoming notifications EXPECT_TRUE(TestSuspendedMode_BF::Local::expectNotification(handler, StateNotificationType::Foreground)); /////////////////////////////// // To Suspended ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); // Pop incoming notifications EXPECT_TRUE(TestSuspendedMode_BF::Local::expectNotification(handler, StateNotificationType::Suspended)); /////////////////////////////// // To Foreground // Go back to foreground in order to be able to finish the test cleanly ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); // Pop incoming notifications EXPECT_TRUE(TestSuspendedMode_BF::Local::expectNotification(handler, StateNotificationType::Foreground)); } /// @test Validates the transition 6) Background to Suspended TEST_F(SUSPENDEDMODE, US_TransitionBackgroundToSuspended) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); // Go back to foreground in order to be able to finish the test cleanly ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); } /// @test Validates the transition 7) Suspended to SuspendedToForeground /// 14) SuspendedToForeground to SuspendedToForeground /// 17) SuspendedToForeground to Foreground TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToForeground) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); const AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultSuspToFg.isProcessing()); const AsyncResultBatch resultNewSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_TRUE(resultNewSuspToFg.isProcessing()); EXPECT_TRUE(resultNewSuspToFg == resultSuspToFg); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(resultSuspToFg)); EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); } #if defined(US_SDK_FULL) // Test that the notification types are correctly updated in the connection when the TGO Portal parameters change during suspendedMode // and then the SDK transition from suspended to foreground.. // Steps: // 1. precondition: OnlineNotificationType1 is configured in the portal and OnlineNotificationTypeAfterParamUpdate is not configured. // 2. check that sendNotificationInSequence works with OnlineNotificationType1 and does not work with OnlineNotificationTypeAfterParamUpdate. // 3. while in suspended mode place a breakpoint, then update the portal: rename OnlineNotificationType1 as OnlineNotificationTypeAfterParamUpdate. // When performing the update in the portal don't enable the checkbox "Update for all players connected..". // 4. check that sendNotificationInSequence works with OnlineNotificationTypeAfterParamUpdate and does not work with OnlineNotificationType1. // 5. in the portal rename OnlineNotificationTypeAfterParamUpdate back to OnlineNotificationType1. TEST_F(SUSPENDEDMODE, DISABLED_MANUAL_TransitionSuspendedToForegroundToCheckNotificationTypes) { ASSERT_TRUE(US_TEST_NS::Helper::waitForWebsocketInit(getFacade())); // confirm that OnlineNotificationType1 is configured in the portal and OnlineNotificationTypeAfterParamUpdate is not configured const String body = "ABCDE"; ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getNotificationClient().sendNotificationInSequence( { getFacade().getAuthenticationClient().getSessionInfo().getProfileId() }, NotificationOutgoing("OnlineNotificationType1", body)))); ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getNotificationClient().sendNotificationInSequence( { getFacade().getAuthenticationClient().getSessionInfo().getProfileId() }, NotificationOutgoing("OnlineNotificationTypeAfterParamUpdate", body)), Errors::NOTIFICATION_SEND_FAILED)); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); // add breakpoint here. while in breakpoint rename OnlineNotificationType1 to OnlineNotificationTypeAfterParamUpdate, // then run test until the end const AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(resultSuspToFg)); EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(US_TEST_NS::Helper::waitForWebsocketInit(getFacade())); // check that the update was done in the connection ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getNotificationClient().sendNotificationInSequence( { getFacade().getAuthenticationClient().getSessionInfo().getProfileId() }, NotificationOutgoing("OnlineNotificationTypeAfterParamUpdate", "body after the connection was updated")))); ASSERT_TRUE(AsyncHelper::waitFailure(getFacade().getNotificationClient().sendNotificationInSequence( { getFacade().getAuthenticationClient().getSessionInfo().getProfileId() }, NotificationOutgoing("OnlineNotificationType1", "body after the connection was updated")), Errors::NOTIFICATION_SEND_FAILED)); // in the portal rename OnlineNotificationTypeAfterParamUpdate back to OnlineNotificationType1 } #endif /// @test Validates the transition 11) BackgroundToForeground to Background TEST_F(SUSPENDEDMODE, US_TransitionBackgroundToForegroundToBackground) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); const AsyncResultBatch resultBgToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::BackgroundToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultBgToFg.isProcessing()); const AsyncResultBatch resultToBg = TestSuspendedMode_BF::changeState(PlatformHelper::Background); EXPECT_FALSE(resultToBg == resultBgToFg); EXPECT_TRUE(resultBgToFg.isCanceled()); ASSERT_TRUE(AsyncHelper::waitSuccess(resultToBg)); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); } /// @test Validates the transition 12) BackgroundToForeground to Suspended TEST_F(SUSPENDEDMODE, US_TransitionBackgroundToForegroundToSuspended) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); const AsyncResultBatch resultBgToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::BackgroundToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultBgToFg.isProcessing()); const AsyncResultBatch resultToSusp = TestSuspendedMode_BF::changeState(PlatformHelper::Suspended); EXPECT_FALSE(resultToSusp == resultBgToFg); ASSERT_TRUE(AsyncHelper::waitSuccess(resultToSusp)); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultBgToFg.isProcessing()); // Go back to foreground in order to be able to finish the test cleanly ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); } /// @test Validates the transition 15) SuspendedToForeground to SuspendedToForegroundToBackground /// and 21) SuspendedToForegroundToBackground to Background TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToForegroundToBackground) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); const AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultSuspToFg.isProcessing()); const AsyncResultBatch resultSuspToFgToBg = TestSuspendedMode_BF::changeState(PlatformHelper::Background); EXPECT_TRUE(resultSuspToFgToBg == resultSuspToFg); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForegroundToBackground, UbiServicesSdkStateManagerProxy::getSdkState()); ASSERT_TRUE(AsyncHelper::waitSuccess(resultSuspToFg)); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); } /// @test Validates the transition 16) SuspendedToForeground to Suspended TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToForegroundToSuspended) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); const AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultSuspToFg.isProcessing()); const AsyncResultBatch resultSuspToFgToSusp = TestSuspendedMode_BF::changeState(PlatformHelper::Suspended); EXPECT_FALSE(resultSuspToFgToSusp == resultSuspToFg); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultSuspToFg.isProcessing()); // Go back to foreground in order to be able to finish the test cleanly ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); } /// @test Validates the transition 18) SuspendedToForegroundToBackground to SuspendedToForeground TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToForegroundToBackgroundToForeground) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); const AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); const AsyncResultBatch resultSuspToFgToBg = TestSuspendedMode_BF::changeState(PlatformHelper::Background); EXPECT_TRUE(resultSuspToFgToBg == resultSuspToFg); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForegroundToBackground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultSuspToFg.isProcessing()); const AsyncResultBatch resultSuspToFgToBgToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_TRUE(resultSuspToFgToBgToFg == resultSuspToFgToBg); EXPECT_TRUE(resultSuspToFgToBgToFg == resultSuspToFg); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultSuspToFg.isProcessing()); ASSERT_TRUE(AsyncHelper::waitSuccess(resultSuspToFg)); EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); } /// @test Validates the transition 19) SuspendedToForegroundToBackground to SuspendedToForegroundToBackground TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToForegroundToBackgroundToBackground) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); const AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); const AsyncResultBatch resultSuspToFgToBg = TestSuspendedMode_BF::changeState(PlatformHelper::Background); EXPECT_TRUE(resultSuspToFgToBg == resultSuspToFg); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForegroundToBackground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultSuspToFg.isProcessing()); const AsyncResultBatch resultSuspToFgToBgToBg = TestSuspendedMode_BF::changeState(PlatformHelper::Background); EXPECT_TRUE(resultSuspToFgToBgToBg == resultSuspToFgToBg); EXPECT_TRUE(resultSuspToFgToBgToBg == resultSuspToFg); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForegroundToBackground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultSuspToFg.isProcessing()); ASSERT_TRUE(AsyncHelper::waitSuccess(resultSuspToFg)); EXPECT_EQ(PlatformHelper::Background, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Background, UbiServicesSdkStateManagerProxy::getSdkState()); } /// @test Validates the transition 20) SuspendedToForegroundToBackground to Suspended TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToForegroundToBackgroundToSuspended) { ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); const AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForeground, UbiServicesSdkStateManagerProxy::getSdkState()); const AsyncResultBatch resultSuspToFgToBg = TestSuspendedMode_BF::changeState(PlatformHelper::Background); EXPECT_TRUE(resultSuspToFgToBg == resultSuspToFg); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::SuspendedToForegroundToBackground, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultSuspToFg.isProcessing()); const AsyncResultBatch resultSuspToFgToBgToBg = TestSuspendedMode_BF::changeState(PlatformHelper::Suspended); EXPECT_FALSE(resultSuspToFgToBgToBg == resultSuspToFgToBg); EXPECT_FALSE(resultSuspToFgToBgToBg == resultSuspToFg); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); EXPECT_TRUE(resultSuspToFg.isProcessing()); // Go back to foreground in order to be able to finish the test cleanly ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); } #endif // US_DLL #if defined(US_TARGET_XONE) && !defined(US_DLL) /// @test Validates that when resuming to foreground from suspended state, it is possible to assign an XboxLiveContext TEST_F(SUSPENDEDMODE, US_TransitionSuspendedToForegroundUpdateXboxLiveContext) { /////////////////////////////// // To Suspended ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); EXPECT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Suspended, UbiServicesSdkStateManagerProxy::getSdkState()); /////////////////////////////// // To Foreground ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); EXPECT_EQ(PlatformHelper::Foreground, PlatformHelper::getState()); EXPECT_EQ(UbiServicesSdkStateInternal::Foreground, UbiServicesSdkStateManagerProxy::getSdkState()); // There must be at least one user online on the console ASSERT_GT(Windows::Xbox::System::User::Users->Size, 0u); Windows::Xbox::System::User^ user = Windows::Xbox::System::User::Users->GetAt(0); Platform::String^ xboxId = user->XboxUserId; #if defined(US_XONE_USE_XSAPI_CPP) std::shared_ptr xboxLiveContext(new xbox::services::xbox_live_context(user)); PlatformHelper::updateXboxLiveContext(xboxLiveContext); ASSERT_EQ(xboxLiveContext.get(), InstancesManager::getInstance()->getXboxLiveContext(user).get()); #else XboxLiveContext context; context.m_context = ref new Microsoft::Xbox::Services::XboxLiveContext(user); PlatformHelper::updateXboxLiveContext(&context); ASSERT_EQ(context.m_context, InstancesManager::getInstance()->getXboxLiveContext(user).m_context); #endif } #endif // US_TARGET_XONE && !US_DLL #if !defined(US_DLL) #if defined(US_TARGET_PS4) || defined(US_TARGET_XONE) || defined(US_TARGET_IOS) || defined(US_TARGET_KILI) /// @test Checks that when coming back from suspendedMode we request a new console ticket to create a session. /// expiration is 2 minutes on this application. TEST_F(SUSPENDEDMODE_NOINIT, US_LONG_PS4CreateNewSessionAfterSuspendedMode) { const US_TEST_NS::ScopedInit autoInit; { configureSdk(GameConfig(ApplicationId(APP_ID_SHORT_EXPIRATION), NS_COMMON::Options::getApplicationBuildId().c_str() #if defined(US_SDK_S2S) , NS_COMMON::Options::environment() #endif , OnlineAccessContext::Standard #if !defined(US_SDK_S2S) , GameConfigEvent(US_SDK_VERSION_CONST_CHAR, US_TEST_NS::US_SDK_DUMMY_GAME_SKU, Vector()) #endif #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 )); #if !defined(US_SDK_S2S) EXPECT_TRUE(InstancesHelper::SetBackdoorEnvironment(NS_COMMON::Options::environment())); #endif const std::unique_ptr credentials(NS_COMMON::AuthenticationHelper::createPlayerCredentials()); Facade facade; const AsyncResult resultLogin = facade.createSession(*NS_COMMON::AuthenticationHelper::createCredentials(), {}, {}); EXPECT_TRUE(AsyncHelper::waitSuccess(resultLogin)); const String ticket1 = facade.getAuthenticationClient().getSessionInfo().getTicket(); const String remoteLoggerSdkTicket1 = TestSuspendedModeHelper::getRemoteLoggerSdkHeaders(getFacadeInternal(facade))["Authorization"]; PRINT_LINE("Entering Suspended Mode"); EXPECT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); US_TEST_NS::Helper::sleep(60 * 1000 * 3); PRINT_LINE("Sleep finished"); EXPECT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); const String ticket2 = facade.getAuthenticationClient().getSessionInfo().getTicket(); const String remoteLoggerSdkTicket2 = TestSuspendedModeHelper::getRemoteLoggerSdkHeaders(getFacadeInternal(facade))["Authorization"]; EXPECT_NE(ticket1, ticket2); EXPECT_NE(remoteLoggerSdkTicket1, remoteLoggerSdkTicket2); EXPECT_TRUE(AsyncHelper::waitSuccess(facade.deleteSession())); } } #elif defined(US_TARGET_WINDOWS) && !defined(US_SDK_TEST_CHINA) // AppId not supported in China context /// @test Checks that when coming back from suspendedMode we request a new console ticket to create a session. /// expiration is 2 minutes on this application. TEST_F(SUSPENDEDMODE_NOINIT, US_LONG_CreateNewSessionAfterSuspendedMode) { const US_TEST_NS::ScopedInit autoInit; { configureSdk(GameConfig(ApplicationId(APP_ID_SHORT_EXPIRATION), NS_COMMON::Options::getApplicationBuildId().c_str() #if defined(US_SDK_S2S) , NS_COMMON::Options::environment() #endif , OnlineAccessContext::Standard #if !defined(US_SDK_S2S) , GameConfigEvent(US_SDK_VERSION_CONST_CHAR, US_TEST_NS::US_SDK_DUMMY_GAME_SKU, Vector()) #endif #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 )); Facade facade; const AsyncResult resultLogin = facade.createSession(*NS_COMMON::AuthenticationHelper::createCredentials(), {}, {}); EXPECT_TRUE(AsyncHelper::waitSuccess(resultLogin)); const String ticket1 = facade.getAuthenticationClient().getSessionInfo().getTicket(); const String remoteLoggerSdkTicket1 = TestSuspendedModeHelper::getRemoteLoggerSdkHeaders(getFacadeInternal(facade))["Authorization"]; PRINT_LINE("Entering Suspended Mode"); EXPECT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); US_TEST_NS::Helper::sleep(60 * 1000 * 3); PRINT_LINE("Sleep finished"); EXPECT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); const String ticket2 = facade.getAuthenticationClient().getSessionInfo().getTicket(); const String remoteLoggerSdkTicket2 = TestSuspendedModeHelper::getRemoteLoggerSdkHeaders(getFacadeInternal(facade))["Authorization"]; EXPECT_NE(ticket1, ticket2); EXPECT_NE(remoteLoggerSdkTicket1, remoteLoggerSdkTicket2); EXPECT_TRUE(AsyncHelper::waitSuccess(facade.deleteSession())); } } #endif #endif // US_DLL #if !defined(US_SDK_LIGHT_MOBILE) && !defined(US_DLL) /// @test Ensures that the notification periodic job is correctly restarted after suspended mode /// @note This test is based on MESSAGING.US_SendRetrieveSelfMessageUsingProfileId #if defined(US_TARGET_SWITCH) TEST_F(SUSPENDEDMODE, DISABLED_US_NotificationAfterSuspendedMode) #else TEST_F(SUSPENDEDMODE, US_NotificationAfterSuspendedMode) #endif { ASSERT_TRUE(US_TEST_NS::Helper::waitForWebsocketInit(getFacade())); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); ASSERT_TRUE(US_TEST_NS::Helper::waitForWebsocketInit(getFacade())); Vector facades = Vector({ &getFacade() }); const Vector types = getWebSocketContentTypes(); NotificationHelper::MultiConnection multi(facades, types, getWebSocketJsonData()); AsyncHelper::waitSuccess(NotificationClientProxy::updateNotificationTypes(getFacade().getNotificationClient(), types)); multi.checkConnection(true); { String out; multi.sendMessage(0, { 0u }, NotificationHelper::MultiConnection::SendType::UsingProfileId, &out); //PRINT_STREAM("**********" << out << "**********"); } { String out; multi.waitForMessageAndPop(0, 1, &out); //PRINT_STREAM("**********" << out << "**********"); } } #endif // !defined(US_SDK_LIGHT_MOBILE) && !defined(US_DLL) // Use the User edit page in the portal to unlink & to remove the TOS acceptance // and then link/accept TOS once inside the test TEST_F(SUSPENDEDMODE, DISABLED_MANUAL_SimulateResumeFocus) { ASSERT_TRUE(getFacade().getAuthenticationClient().getSessionInfo().hasValidSession()); const bool isLinked = getFacade().getAuthenticationClient().getSessionInfo().hasUserAccountLinked(); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_EQ(PlatformHelper::Suspended, PlatformHelper::getState()); PRINT_LINE("Time to suspend and set back legal opt-ins and link"); US_TEST_NS::Helper::sleep(20000); // Increase the sleep time to test on platforms without suspended mode (Win32) ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); ASSERT_TRUE(getFacade().getAuthenticationClient().getSessionInfo().hasValidSession()); if(!isLinked) { EXPECT_TRUE(getFacade().getAuthenticationClient().getSessionInfo().hasUserAccountLinked()); } } #if !defined(US_SDK_LIGHT_MOBILE) #if !defined(US_DLL) #if defined(US_TARGET_IOS) && !defined(US_UPLAY_LOGIN_FOR_IOS) || defined(US_SDK_TEST_CHINA) TEST_F(SUSPENDEDMODE, DISABLED_US_CachesAreInvalidatedOnResumeFromSuspended) // News Services does not support Game Center tickets #else TEST_F(SUSPENDEDMODE, US_CachesAreInvalidatedOnResumeFromSuspended) #endif { // Fill another cache ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getNewsClient().requestProfileNews(SpaceId(US_TEST_NS::SPACE_ID_US_SDK_PC)))); EXPECT_TRUE(CacheManager::isCacheValid(getFacadeInternal(), CacheUnit::NewsProfile)); EXPECT_FALSE(TestSuspendedMode_EX::areAllCachesInvalid(getFacadeInternal())); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); EXPECT_TRUE(TestSuspendedMode_EX::areAllCachesInvalid(getFacadeInternal())); } #if defined(US_TARGET_IOS) && !defined(US_UPLAY_LOGIN_FOR_IOS) || defined(US_SDK_TEST_CHINA) TEST_F(SUSPENDEDMODE, DISABLED_US_CachesAreInvalidatedOnResumeFromBackground) // News Services does not support Game Center tickets #else TEST_F(SUSPENDEDMODE, US_CachesAreInvalidatedOnResumeFromBackground) #endif { // Fill another cache ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getNewsClient().requestProfileNews(SpaceId(US_TEST_NS::SPACE_ID_US_SDK_PC)))); EXPECT_TRUE(CacheManager::isCacheValid(getFacadeInternal(), CacheUnit::NewsProfile)); EXPECT_FALSE(TestSuspendedMode_EX::areAllCachesInvalid(getFacadeInternal())); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); EXPECT_TRUE(TestSuspendedMode_EX::areAllCachesInvalid(getFacadeInternal())); } #endif // !defined(US_DLL) #if defined(US_TARGET_WINDOWS) && !defined(US_SDK_TEST_CHINA) // AppId not supported in China context /// @test Ensures that a new session is created and service calls are working /// when the suspension time is above the expiration time /// @note use ApplicationId 8700c8f7-c538-4ee4-8dfb-1d5650bd66ed (Short expiration PC) /// or d2943775-5ae7-46bd-b871-819f01ae64eb (Short expiration PS4) to launch this test, /// expiration is 2 minutes on this application. TEST_F(SUSPENDEDMODE_NOINIT, LONG_US_CreateNewSessionAfterSuspendedMode) { const US_TEST_NS::ScopedInit autoInit; { // Forcing applicationId. server make check that it is the same as in the ticket's claims configureSdk(GameConfig(ApplicationId(APP_ID_SHORT_EXPIRATION), String() #if defined(US_SDK_S2S) , NS_COMMON::Options::environment() #endif , OnlineAccessContext::Standard #if !defined(US_SDK_S2S) , GameConfigEvent(US_SDK_VERSION_CONST_CHAR, US_TEST_NS::US_SDK_DUMMY_GAME_SKU, Vector()) #endif ), SystemConfig()); Facade facade; EXPECT_TRUE(AsyncHelper::waitSuccess(facade.createSession(*NS_COMMON::AuthenticationHelper::createCredentials(), {}, {}))); EXPECT_TRUE(AsyncHelper::waitSuccess(PlatformHelper::changeState(PlatformHelper::Suspended))); US_TEST_NS::Helper::sleep(150000); EXPECT_TRUE(AsyncHelper::waitSuccess(PlatformHelper::changeState(PlatformHelper::Foreground))); EXPECT_TRUE(AsyncHelper::waitSuccess(facade.getProfileClient().requestUplayProfiles({ String("ussdk001") }))); } } #endif #endif // !defined(US_SDK_LIGHT_MOBILE) #if defined(US_TARGET_PS4) || defined(US_TARGET_XONE) || defined(US_TARGET_GGP) || defined(US_TARGET_KILI) /// Exercises the suspend mode auto-detection on Xbox One. /// This test ends as soon as the test goes into "constraint" mode, /// that is, pressing the "HOME" button on the controller /// (or pressing the POWER button of the console). TEST_F(SUSPENDEDMODE_NOINIT, DISABLED_MANUAL_US_AutoDetectSuspendedMode) { const US_TEST_NS::ScopedInit autoInit; configureSdk(GameConfig(ApplicationId(NS_COMMON::Options::getApplicationId().c_str()), NS_COMMON::Options::getApplicationBuildId().c_str() #if defined(US_SDK_S2S) , NS_COMMON::Options::environment() #endif , OnlineAccessContext::Standard #if !defined(US_SDK_S2S) , GameConfigEvent(US_SDK_VERSION_CONST_CHAR, US_TEST_NS::US_SDK_DUMMY_GAME_SKU, Vector()) #endif #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 ), SystemConfig()); #if !defined(US_SDK_S2S) EXPECT_TRUE(InstancesHelper::SetBackdoorEnvironment(NS_COMMON::Options::environment())); #endif { Facade facade; #if defined(US_TARGET_XONE) // There must be at least one user online on the console ASSERT_GT(Windows::Xbox::System::User::Users->Size, 0u); Windows::Xbox::System::User^ user = Windows::Xbox::System::User::Users->GetAt(0); Platform::String^ xboxId = user->XboxUserId; #if defined(US_XONE_USE_XSAPI_CPP) std::shared_ptr xboxLiveContext(new xbox::services::xbox_live_context(user)); const PlayerCredentials credentials(xboxLiveContext); #else Microsoft::Xbox::Services::XboxLiveContext^ xblContext = ref new Microsoft::Xbox::Services::XboxLiveContext(user); const PlayerCredentials credentials(xblContext); #endif ASSERT_TRUE(AsyncHelper::waitSuccess(facade.createSession(credentials, {}, {}))); #endif #if defined(US_TARGET_GGP) ASSERT_TRUE(AsyncHelper::waitSuccess(facade.createSession(PlayerCredentials(ggp::GetPrimaryPlayerId()), {}, {}))); #endif //while(PlatformHelper::getState() != PlatformHelper::Background) { uint32 count = 0; facade.getRemoteLogClient().sendRemoteLog(RemoteLogClient::Warning, "TestCategory", RemoteLog(String("TEST_AUTODETECT BEGIN"))); while(PlatformHelper::getState() != PlatformHelper::Suspended) { US_TEST_NS::Helper::sleep(100); if (count++ > 10) { facade.getRemoteLogClient().sendRemoteLog(RemoteLogClient::Warning, "TestCategory", RemoteLog(String("TEST_AUTODETECT TICK #1"))); count = 0; } } count = 0; facade.getRemoteLogClient().sendRemoteLog(RemoteLogClient::Warning, "TestCategory", RemoteLog(String("TEST_AUTODETECT FROM SUSPENDED"))); while(PlatformHelper::getState() != PlatformHelper::Foreground) { US_TEST_NS::Helper::sleep(100); if (count++ > 10) { facade.getRemoteLogClient().sendRemoteLog(RemoteLogClient::Warning, "TestCategory", RemoteLog(String("TEST_AUTODETECT TICK #2"))); count = 0; } } facade.getRemoteLogClient().sendRemoteLog(RemoteLogClient::Warning, "TestCategory", RemoteLog(String("TEST_AUTODETECT END"))); } Helper::sleep(30000); } } #endif #if !defined(US_DLL) /// @test Ensures that events periodic job is relaunched after suspended mode TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_SendingEventsAfterSuspendedMode) { { const MockAsyncResult eventConfigMock = MockResultHelper::createSuccessResult( EventsConfigMockHelper::getBody(2).renderContent()); // SEND_PERIOD_SEC = 2 setupWebsocketMock(); mockFullLogin(nullptr, nullptr, nullptr, nullptr, &eventConfigMock); US_EXPECT_CALL(US_TEST_NS::Action::SendEvents) .Times(2) .WillRepeatedly(testing::Return(MockResultHelper::createSuccessResult())); } ListenerHandler handler = getFacade().getEventClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); waitForPostLoginCompletion(); // Ensure the periodic job is started before going to suspended mode ASSERT_TRUE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::EventConfigSuccess, true)); ASSERT_TRUE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::SendingSuccessful, true)) << "The game.start and player.start should be sent."; { // PS4 and XONE mocked suspended test will always do the put because of the consumable balance US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); TestSuspendedMode_BF::setupMockExpectationsForPrimaryStoreAfterSuspendedMode(getMockHttpClientImpl(), getMockedNotifyPrimaryStoreUpdate()); } { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); } ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); const AsyncResultBatch result = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); ASSERT_FALSE(handler.isNotificationAvailable()) << "The event notifications should be empty at this moment."; ASSERT_TRUE(AsyncHelper::waitSuccess(result)); getFacade().getEventClient().pushEvent(EventInfoCustom(CUSTOM_EVENT_NAME)); ASSERT_TRUE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::SendingSuccessful, true)) << "The events pushed after suspended should be sent."; } #endif #if !defined(US_DLL) /// @test Ensures that events periodic job is relaunched after suspended mode, /// even if the resume is call two times simultaneously. Without protection, /// the first resume would cancel the event periodic job, and the second one /// would not restart the periodic job since it shall be restarted only if it /// was running before going to suspended mode. TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_SendingEventsAfterTwoParallelTransitionFromSuspendedMode) { { const MockAsyncResult eventConfigMock = MockResultHelper::createSuccessResult( EventsConfigMockHelper::getBody(2).renderContent()); // SEND_PERIOD_SEC = 2 setupWebsocketMock(); mockFullLogin(nullptr, nullptr, nullptr, nullptr, &eventConfigMock); US_EXPECT_CALL(US_TEST_NS::Action::SendEvents) .Times(2) .WillRepeatedly(testing::Return(MockResultHelper::createSuccessResult())); } ListenerHandler handler = getFacade().getEventClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); waitForPostLoginCompletion(); // Ensure the periodic job is started before going to suspended mode ASSERT_TRUE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::SendingSuccessful, true)) << "The game.start and player.start should be sent."; { // PS4 and XONE mocked suspended test will always do the put because of the consumable balance US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); TestSuspendedMode_BF::setupMockExpectationsForPrimaryStoreAfterSuspendedMode(getMockHttpClientImpl(), getMockedNotifyPrimaryStoreUpdate()); } { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); } ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); // Do NOT wait intendedly for the async result to finish here. // This test checks the behavior when two transition are done in parallel const AsyncResultBatch resultFirstResume = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); const AsyncResultBatch resultSecondResume = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); ASSERT_FALSE(handler.isNotificationAvailable()) << "The event notifications should be empty at this moment."; ASSERT_TRUE(AsyncHelper::waitSuccess(resultSecondResume)); // Wait here instead, to ensure the resume is finished for the rest of the test ASSERT_TRUE(AsyncHelper::waitSuccess(resultFirstResume)); getFacade().getEventClient().pushEvent(EventInfoCustom(CUSTOM_EVENT_NAME)); ASSERT_TRUE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::SendingSuccessful, true)) << "The events pushed after suspended should be sent."; } #endif #if defined(US_ENABLE_UPLAY_LOGIN) && !defined(US_SDK_TEST_CHINA) /// A crash occurred in the websocket engine when resuming from suspended mode. /// Both websockets on the two facades try to connect simultaneously, but when the second connects, /// it erases the first one from the websocket engine, because it is not connected yet, and therefore /// detected as disconnected. When trying to access the first websocket again, the crash occurs. TEST_F(SUSPENDEDMODE, US_SuspendedForegroundWithTwoFacades) { setupDebugLogging(); const int ITERATION_COUNT = 10; PrepareLoggingMockWithLogsPrinted([ITERATION_COUNT](US_TEST_NS::MockLogDevice& device) { US_EXPECT_LOG("UbiServices SDK resuming from suspended mode.", LogLevel::Info, Times(ITERATION_COUNT)); US_EXPECT_LOG("Connection already closed.", LogLevel::Warning, Times(ITERATION_COUNT*2)); }); Facade facade2; ASSERT_TRUE(AsyncHelper::waitSuccess(facade2.createSession(PlayerCredentials("ussdk002@ubisoft.com", "a1234567"), {}, {}))); US_TEST_NS::Helper::sleep(2500); for (uint32 i = 0; i < ITERATION_COUNT; i++) { PRINT_PROGRESS(i + 1, ITERATION_COUNT, "Iteration %d/%d", i + 1, ITERATION_COUNT); // Try several times, because the crash depended on timing issues EXPECT_TRUE(AsyncHelper::waitSuccess(PlatformHelper::changeState(PlatformHelper::Suspended))); US_TEST_NS::Helper::sleep(2000); EXPECT_TRUE(AsyncHelper::waitSuccess(PlatformHelper::changeState(PlatformHelper::Foreground))); US_TEST_NS::Helper::sleep(2000); } } #endif #if defined(US_TARGET_SWITCH) /// @test Test that we remain in the same session after being in a suspended state for a long time /// The steps are the following: /// 1- Start the test /// 2- Wait in DNA-LiveStream for the session to start on Short Expiration PC /// 3- Upon session being on DNA-LiveStream, put the console on suspended physically(go back to home is sufficient) /// 4- Wait for the ticket to expire(3+ minutes) /// 5- Come back from suspended by pressing the home button or selecting the test in the home screen /// 6- Wait for event to be push and verify in DNA-LiveStream that the Testing event has been sent in the same session /// 7- Verify the test passed TEST_F(SUSPENDEDMODE_NOINIT, DISABLED_MANUAL_US_EventPeriodicAfterExpiration) { const US_TEST_NS::ScopedInit autoInit; { ApplicationId appId(APP_ID_SHORT_EXPIRATION); configureSdk(GameConfig(appId, NS_COMMON::Options::getApplicationBuildId().c_str() #if defined(US_SDK_S2S) , NS_COMMON::Options::environment() #endif , OnlineAccessContext::Standard #if !defined(US_SDK_S2S) , GameConfigEvent(US_SDK_VERSION_CONST_CHAR, US_TEST_NS::US_SDK_DUMMY_GAME_SKU, Vector()) #endif #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 )); #if !defined(US_SDK_S2S) EXPECT_TRUE(InstancesHelper::SetBackdoorEnvironment(NS_COMMON::Options::environment())); #endif Facade facade; const AsyncResult resultLogin = facade.createSession(*NS_COMMON::AuthenticationHelper::createCredentials(), {}, {}); EXPECT_TRUE(AsyncHelper::waitSuccess(resultLogin)); const String ticket1 = facade.getAuthenticationClient().getSessionInfo().getTicket(); //Put in suspended once the session has started on DNA-LiveStream nn::oe::SetResumeNotificationEnabled(true); nn::oe::Message message; while (!nn::oe::TryPopNotificationMessage(&message)) { US_TEST_NS::Helper::sleep(150); } PRINT_LINE("Entering Suspended Mode"); EXPECT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); PRINT_LINE("Back To Foreground"); EXPECT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); auto listener = facade.getEventClient().createNotificationListener(); facade.getEventClient().pushEvent(EventInfoCustom(CUSTOM_EVENT_NAME)); TestSuspendedMode_BF::waitForEventNotification(listener, EventNotificationType::SendingSuccessful, false); const String ticket2 = facade.getAuthenticationClient().getSessionInfo().getTicket(); EXPECT_NE(ticket1, ticket2); EXPECT_TRUE(AsyncHelper::waitSuccess(facade.deleteSession())); } } #endif #if !defined(US_DLL) /// @test Ensures that events periodic job is relaunched after suspended mode TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_EventPeriodicJobNotRestartedAfterSuspendedMode) { PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device) { US_EXPECT_LOG("Status code: 404", LogLevel::Error, Times(1)); }); { const MockAsyncResult eventConfigMock = MockResultHelper::createSuccessResult( EventsConfigMockHelper::getBody(3).renderContent()); // SEND_PERIOD_SEC = 3 setupWebsocketMock(); mockLogin(getMockedLoginResponse(), getMockedParametersApplication(), getMockedParametersSpace()); US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/mock-config-events", eventConfigMock); US_EXPECT_CALL(US_TEST_NS::Action::SendEvents) .WillOnce(testing::Return(MockResultHelper::createRestErrorResult(RestStatusCode::Error404, 1000u))); { const char* const JSON_POP = // "\"opaque field\": 123" "{" "\"populations\": [" "{" "\"name\": \"dummy Name 1\"," "\"obj\": {}," "\"subject\": \"dummy Subject 1\"," "\"spaceId\": \"" DUMMY_GUID_ID1 "\"," "\"assignmentTime\": \"2012-10-15T15:46:32\"" "}," "{" "\"name\": \"dummy Name 2\"," "\"obj\": {}," "\"subject\": \"dummy Subject 2\"," "\"spaceId\": \"" DUMMY_GUID_ID1 "\"" "}," "{" "\"name\": \"dummy Name 3\"," "\"obj\": {}," "\"subject\": \"dummy Subject 3\"," "\"spaceId\": \"" DUMMY_GUID_ID1 "\"" "}" "]" "}"; US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/data", MockResultHelper::createSuccessResult(JSON_POP)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/mock-spaces-primarystoremapping") .Times(testing::AnyNumber()) .WillRepeatedly(testing::Return(getMockedSecondaryPrimaryStoreMapping())); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .Times(testing::AnyNumber()) .WillRepeatedly(testing::Return(getMockedNotifyPrimaryStoreUpdate())); } } ListenerHandler handler = getFacade().getEventClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); // Waiting for events to be sent there's no blocking popUnsentEvent waitForStartEventSessionSuccess(); // Ensure the periodic job is started before going to suspended mode ASSERT_TRUE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::EventConfigSuccess, true)); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getEventClient().popUnsentEvents(String()))) << "This will stop the periodic jobs."; ASSERT_TRUE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::SendingFailure, true)) << "Failing a event request will stop the periodic job."; { // PS4 and XONE mocked suspended test will always do the put because of the consumable balance US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); } { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); } ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); ASSERT_FALSE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::SendingSuccessful, true)) << "No events should be sent. The periodic job shouldn't be restarted."; } #endif //!defined(US_DLL) #if !defined(US_DLL) /// @test Ensures the extend session job restarts after a suspended state /// @note This test checks the case where the user has a uplay account TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_ExtendSessionAfterSuspendedModeUplayUser) { const uint8 SHORT_SESSION_DURATION_SECONDS = 10; { const MockAsyncResult sessionResult = getMockedLoginShortSession(SHORT_SESSION_DURATION_SECONDS, true); mockFullLogin(&sessionResult); } HighResolutionChrono sessionTime; ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); waitForPostLoginCompletion(); { const uint8 LONG_SESSION_DURATION_SECONDS = 50; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/sessions") // Set a longer session here to avoid flakiness at the end of the test // (basically, no new extend session should be triggered) .WillOnce(testing::Return(getMockedLoginShortSession(LONG_SESSION_DURATION_SECONDS, true))); } { // PS4 and XONE mocked suspended test will always do the put US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); TestSuspendedMode_BF::setupMockExpectationsForPrimaryStoreAfterSuspendedMode(getMockHttpClientImpl(), getMockedNotifyPrimaryStoreUpdate()); } { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); } ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); // Sleep to resume "just before" the end of the session (85% of the session validity) (i.e. slightly after the moment where the // automatic extend session would have been triggered if not suspended which is at 80% of the session duration), // but with some time to proceed to the resume and the renew session before expiration const uint64 sessionDurationMs = ClockSteady::getMilliFromNano(sessionTime.getElapsed()); const int64 sleepDuration = SHORT_SESSION_DURATION_SECONDS * 1000 * 85 / 100 - sessionDurationMs; if (sleepDuration > 0) { US_TEST_NS::Helper::sleep(sleepDuration); } // Create listener before triggering state change, so we can catch notification ListenerHandler notificationListener = getFacade().getAuthenticationClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); const AuthenticationNotificationType notif = TestSuspendedMode_BF::waitForExtendSessionNotification(notificationListener, Duration<>(15ull)); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notif) << "We didn't get the expected client notification."; // This sleep is to catch the unwanted call to renew session in case the renew session expiration is not well calculated // (if the waiting times are kept from the ones computed during the resume process, when relaunching the extendSession job) // 4 seconds to be slightly above the worst case : expiration is in 10 - 8 = 2 second. US_TEST_NS::Helper::sleep(2000); } /// @test Ensures the extend session job restarts after a suspended state /// @note This test checks the case where the user does not have a uplay account (meaning an additional extend session) TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_ExtendSessionAfterSuspendedMode) { { const uint8 SHORT_SESSION_DURATION_SECONDS = 10; const uint8 LONG_SESSION_DURATION_SECONDS = 50; const MockAsyncResult sessionResult = getMockedLoginShortSession(SHORT_SESSION_DURATION_SECONDS, false); mockFullLogin(&sessionResult); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/sessions") // Extend session from the DetectLinkedUser step .WillOnce(testing::Return(getMockedLoginShortSession(SHORT_SESSION_DURATION_SECONDS, false))) // Extend session from the scheduling job. // Set a longer session here to avoid flakiness at the end of the test // (basically, no new extend session should be triggered) .WillOnce(testing::Return(getMockedLoginShortSession(LONG_SESSION_DURATION_SECONDS, false))); } ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); waitForPostLoginCompletion(); { // PS4 and XONE mocked suspended test will always do the put because of the consumable balance US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); TestSuspendedMode_BF::setupMockExpectationsForPrimaryStoreAfterSuspendedMode(getMockHttpClientImpl(), getMockedNotifyPrimaryStoreUpdate()); } // Create listener before triggering state change, so we can catch notification ListenerHandler notificationListener = getFacade().getAuthenticationClient().createNotificationListener(); { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); } ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); // Sleep 4 seconds to resume "just before" the end of the session (i.e. slightly after the moment where the // automatic extend session would have been triggered if not suspended which is at 80% of the session duration), // but with some time to proceed to the resume and the renew session before expiration US_TEST_NS::Helper::sleep(4000); AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); { // Extend session from the DetectLinkedUser step const AuthenticationNotificationType notif = TestSuspendedMode_BF::waitForExtendSessionNotification(notificationListener, 15ull * 1000ull * 1000ull * 1000ull); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notif) << "We didn't get the expected client notification."; } ASSERT_TRUE(AsyncHelper::waitSuccess(resultSuspToFg)); { // Extend session from the scheduling job const AuthenticationNotificationType notif = TestSuspendedMode_BF::waitForExtendSessionNotification(notificationListener, 15ull * 1000ull * 1000ull * 1000ull); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notif) << "We didn't get the expected client notification."; } // This sleep is to catch the unwanted call to renew session in case the renew session expiration is not well calculated // (if the waiting times are kept from the ones computed during the resume process, when relaunching the extendSession job) // 2 seconds to be slightly above the worst case : expiration is in 10 - 8 = 2 second. US_TEST_NS::Helper::sleep(4000); } /// @test Ensures the extend session periodic job is still there even when an extend session has been triggered and failed /// @note The extend session is actually triggered during the resume from background, because the user has no uplay account TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_ExtendSessionAfterBackgroundMode) { PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device) { US_EXPECT_LOG("Status code: 404", LogLevel::Error, Times(1)); }); { const uint8 SHORT_SESSION_DURATION_SECONDS = 5; const uint8 LONG_SESSION_DURATION_SECONDS = 50; const MockAsyncResult sessionResult = getMockedLoginShortSession(SHORT_SESSION_DURATION_SECONDS, false); mockFullLogin(&sessionResult); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/sessions") // Extend session from the DetectLinkedUser step .WillOnce(testing::Return(MockResultHelper::createRestErrorResult(RestStatusCode::Error404, 1000))) // Extend session from the scheduling job. // Set a longer session here to avoid flakiness at the end of the test // (basically, no new extend session should be triggered) .WillOnce(testing::Return(getMockedLoginShortSession(LONG_SESSION_DURATION_SECONDS, false))); } ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); #if defined(US_PRIMARY_STORE_WITH_API) { // PS4 and XONE mocked suspended test will always do the put because of the consumable balance US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); const MockAsyncResult mockResultNotification = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderPrimaryStoreUpdateResult(true, false)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillRepeatedly(testing::Return(mockResultNotification)); } #endif ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); // Create listener before triggering state change, so we can catch notification ListenerHandler authentificationNotifListener = getFacade().getAuthenticationClient().createNotificationListener(); #if defined(US_PRIMARY_STORE_WITH_API) ListenerHandler secondaryStoreNotifListener = getFacade().getSecondaryStoreClient().createNotificationListener(); #endif ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); { // Extend session from the scheduling job const AuthenticationNotificationType notif = TestSuspendedMode_BF::waitForExtendSessionNotification(authentificationNotifListener, 15ull * 1000ull * 1000ull * 1000ull); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notif) << "We didn't get the expected client notification."; } #if defined(US_PRIMARY_STORE_WITH_API) //After background the notification should be received ASSERT_TRUE(TestPrimarySecondaryStoreSync_BF::waitForNotification(secondaryStoreNotifListener, SecondaryStoreNotificationType::InventorySyncUpdate, TestSuspendedMode_BF::MOCK_PERIOD_JOB_WAIT_TIME)); #endif } #endif #if defined(US_ENABLE_UPLAY_LOGIN) || defined(US_TARGET_PS4) || defined(US_TARGET_XONE) || defined(US_TARGET_KILI) /// @test Ensures that the a new session is created when coming back from suspended and the session has expired TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_CreateNewSessionWhenResumingFromSuspendedModeAfterSessionHasExpired) { { const uint8 SHORT_SESSION_DURATION_SECONDS = 5; const MockAsyncResult shortSessionResult = getMockedLoginShortSession(SHORT_SESSION_DURATION_SECONDS, true); mockFullLogin(&shortSessionResult); } //Start creation of session ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); } ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); // Sleep 7 seconds to resume after the end of the session (i.e. slightly after the moment where the // automatic creation of a new session will be triggered from JobDetectLink US_TEST_NS::Helper::sleep(7000); { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/profiles/sessions") // Create new session from the jobDetectLink .WillOnce(testing::Return(getMockedLoginResponse())); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); const MockAsyncResult mockResultNotification = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderPrimaryStoreUpdateResult(true, false)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .Times(testing::AtMost(1)) .WillOnce(testing::Return(mockResultNotification)); } // Create listener before triggering state change, so we can catch notification ListenerHandler notificationListener = getFacade().getAuthenticationClient().createNotificationListener(); const AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); { // Wait for notification for change of sessionInfo since we have a new login. const AuthenticationNotificationType notif = TestSuspendedMode_BF::waitForExtendSessionNotification(notificationListener, 15ull * 1000ull * 1000ull * 1000ull); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notif) << "We didn't get the expected client notification."; } ASSERT_TRUE(AsyncHelper::waitSuccess(resultSuspToFg)); } #endif #if defined(US_ENABLE_UPLAY_LOGIN) /// @test Ensures that the a new session is created when coming back from suspended and the session has expired with a RememberMe ticket TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_CreateNewSessionWhenResumingFromSuspendedModeAfterSessionHasExpiredWithRememberMeTicket) { { const uint8 SHORT_SESSION_DURATION_SECONDS = 5; const MockAsyncResult shortSessionResult = getMockedLoginShortSession(SHORT_SESSION_DURATION_SECONDS, true); mockFullLogin(&shortSessionResult); } const PlayerCredentials credentials = PlayerCredentials(NS_COMMON::Options::email().c_str(), NS_COMMON::Options::password().c_str(), RememberMe::Enabled); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(credentials, {}, {}))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); // Sleep 7 seconds to resume after the end of the session (i.e. slightly after the moment where the // automatic creation of a new session will be triggered from JobDetectLink US_TEST_NS::Helper::sleep(7000); // SuspdendedMode expects { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); US_EXPECT_CALL_HTTP_SIMPLE(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data", getMockedPopulationsResponse()); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/profiles/sessions") // Create new session from the jobDetectLink .WillOnce(testing::Return(getMockedLoginResponse())); } // Create listener before triggering state change, so we can catch notification ListenerHandler notificationListener = getFacade().getAuthenticationClient().createNotificationListener(); AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); { // Wait for notification for change of sessionInfo since we have a new login. const AuthenticationNotificationType notif = TestSuspendedMode_BF::waitForExtendSessionNotification(notificationListener, 15ull * 1000ull * 1000ull * 1000ull); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notif) << "We didn't get the expected client notification."; } ASSERT_TRUE(AsyncHelper::waitSuccess(resultSuspToFg)); } #endif #if defined(US_ENABLE_UPLAY_LOGIN) || defined(US_TARGET_PS4) || defined(US_TARGET_XONE) || defined(US_TARGET_KILI) /// @test Ensures that there is no new playerSessionId when coming back from suspended despite a new UbiServices /// session has been created. TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_CreateNewSessionWhenResumingFromSuspendedModeAfterSessionHasExpiredEventContext) { HttpPost httpRequestBeforeSuspend(StringUrl(""), String()); HttpPost httpRequestAfterSuspend(StringUrl(""), String()); { const MockAsyncResult shortSessionResult = getMockedLoginShortSession(5, true); // SHORT_SESSION_DURATION_SECONDS = 5 const MockAsyncResult eventConfigMock = MockResultHelper::createSuccessResult(EventsConfigMockHelper::getBody(2).renderContent()); // SEND_PERIOD_SEC = 2 mockFullLogin(&shortSessionResult, nullptr, nullptr, nullptr, &eventConfigMock); US_EXPECT_CALL(US_TEST_NS::Action::SendEvents) .WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast(&httpRequestBeforeSuspend)), testing::Return(MockResultHelper::createSuccessResult()))) .WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast(&httpRequestAfterSuspend)), testing::Return(MockResultHelper::createSuccessResult()))); } ListenerHandler handler = getFacade().getEventClient().createNotificationListener(); getFacade().getEventClient().pushEvent(EventInfoContextStart("myContextType1", "MyContextName1")); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); // Ensure the periodic job is started before going to suspended mode ASSERT_TRUE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::EventConfigSuccess, true)); ASSERT_TRUE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::SendingSuccessful, true)); { // PS4 and XONE mocked suspended test will always do the put because of the consumable balance US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); TestSuspendedMode_BF::setupMockExpectationsForPrimaryStoreAfterSuspendedMode(getMockHttpClientImpl(), getMockedNotifyPrimaryStoreUpdate()); } { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); } ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); // Sleep 7 seconds to resume after the end of the session (i.e. slightly after the moment where the // automatic creation of a new session will be triggered from JobDetectLink US_TEST_NS::Helper::sleep(7000); { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/profiles/sessions") // Create new session from the jobDetectLink .WillOnce(testing::Return(getMockedLoginResponse())); } // Create listener before triggering state change, so we can catch notification ListenerHandler notificationListener = getFacade().getAuthenticationClient().createNotificationListener(); const AsyncResultBatch resultSuspToFg = TestSuspendedMode_BF::changeState(PlatformHelper::Foreground); getFacade().getEventClient().pushEvent(EventInfoContextStop("myContextType1", "MyContextName1")); { // Wait for notification for change of sessionInfo since we have a new login. const AuthenticationNotificationType notif = TestSuspendedMode_BF::waitForExtendSessionNotification(notificationListener, 15ull * 1000ull * 1000ull * 1000ull); EXPECT_EQ(AuthenticationNotificationType::SessionInfoChanged, notif) << "We didn't get the expected client notification."; } ASSERT_TRUE(AsyncHelper::waitSuccess(resultSuspToFg)); ASSERT_TRUE(TestSuspendedMode_BF::waitForEventNotification(handler, EventNotificationType::SendingSuccessful, true)); // Expect the playerSessionId to be the same in the two requests const String gameSessionIdBeforeSuspended = Json(httpRequestBeforeSuspend.getBodyAsString())["gameSessionId"].getValueString(); const String gameSessionIdAfterSuspended = Json(httpRequestAfterSuspend.getBodyAsString())["gameSessionId"].getValueString(); const String playerSessionIdBeforeSuspended = Json(httpRequestBeforeSuspend.getBodyAsString())["playerSessionId"].getValueString(); const String playerSessionIdAfterSuspended = Json(httpRequestAfterSuspend.getBodyAsString())["playerSessionId"].getValueString(); EXPECT_FALSE(gameSessionIdBeforeSuspended.isEmpty()); EXPECT_FALSE(gameSessionIdAfterSuspended.isEmpty()); EXPECT_EQ(gameSessionIdAfterSuspended, gameSessionIdBeforeSuspended); EXPECT_FALSE(playerSessionIdBeforeSuspended.isEmpty()); EXPECT_FALSE(playerSessionIdAfterSuspended.isEmpty()); EXPECT_EQ(playerSessionIdAfterSuspended, playerSessionIdBeforeSuspended); } #endif #if !defined(US_SDK_LIGHT_MOBILE) #if defined(US_PRIMARY_STORE_ENABLED) && !defined(US_TARGET_SWITCH) #define US_PLATFORM_WITH_PRIMARYSTORE_NOTIFICATION #endif /// @test Ensures the notification to the secondary store service is sent on the first call to the inventory /// when going back from background #if defined(US_PLATFORM_WITH_PRIMARYSTORE_NOTIFICATION) TEST_F(SUSPENDEDMODE_MOCK_LOGIN, USNOT_SecondaryStoreUpdateNotificationAfterBackgroundMode) #else /// @disabled With notification not supported, this test is just like requesting the inventory "normally" TEST_F(SUSPENDEDMODE_MOCK_LOGIN, DISABLED_USNOT_SecondaryStoreUpdateNotificationAfterBackgroundMode) #endif { { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .WillRepeatedly(testing::Return(getMockedPopulationsResponse())); const MockAsyncResult mockResultMapping = MockResultHelper::createSuccessResult(StoreHelper::renderPrimaryStoreMapping()); StringStream expectedUrlMapping; expectedUrlMapping << "/" << getFacade().getWorkingSpaceId() << "/mock-spaces-primarystoremapping"; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrlMapping.getContent()) .WillRepeatedly(testing::Return(mockResultMapping)); const MockAsyncResult mockResultNotification = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderPrimaryStoreUpdateResult(true, false)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillOnce(testing::Return(mockResultNotification)) .WillRepeatedly(testing::Return(mockResultNotification)); const MockAsyncResult mockResultInventory = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderEmptyResult("items")); StringStream expectedUrlInventory; expectedUrlInventory << "/" << getFacade().getAuthenticationClient().getSessionInfo().getProfileId() << "/mock-profiles-inventory"; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrlInventory.getContent()) .WillOnce(testing::Return(mockResultInventory)) .WillOnce(testing::Return(mockResultInventory)); } #if defined(US_PRIMARY_STORE_WITHOUT_API) { PrimaryStoreInventoryItem dlc; dlc.m_id = UPLAYPC_DUMMY_DLCID; dlc.m_type = PrimaryStoreItemType::Durable; dlc.m_state = PrimaryStoreInventoryItem::InventoryItemState::ReadyToUse; dlc.m_balance = 1; ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getPrimaryStoreClient().updatePrimaryStoreProducts({ dlc }))); } #endif ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getSecondaryStoreClient().requestInventory())); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); #if defined(US_PRIMARY_STORE_WITHOUT_API) { PrimaryStoreInventoryItem consumable; consumable.m_id = UPLAYPC_DUMMY_CONSUMABLEID; consumable.m_type = PrimaryStoreItemType::Consumable; consumable.m_state = PrimaryStoreInventoryItem::InventoryItemState::ReadyToUse; consumable.m_balance = 8; ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getPrimaryStoreClient().updatePrimaryStoreProducts({ consumable }))); } #endif ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getSecondaryStoreClient().requestInventory())); } /// @test Ensures the notification to the secondary store service is sent on the first call to the inventory /// when going back from background #if defined(US_PLATFORM_WITH_PRIMARYSTORE_NOTIFICATION) TEST_F(SUSPENDEDMODE_MOCK_LOGIN, USNOT_SecondaryStoreUpdateNotificationAfterSuspendedMode) #else /// @disabled With notification not supported, this test is just like requesting the inventory "normally" TEST_F(SUSPENDEDMODE_MOCK_LOGIN, DISABLED_USNOT_SecondaryStoreUpdateNotificationAfterSuspendedMode) #endif { #if defined(US_LOG_ENABLED) && defined(DEBUGGING) LoggingHelper::enableVerbose(); LoggingHelper::enableMultiLines(); #endif { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .WillRepeatedly(testing::Return(getMockedPopulationsResponse())); const MockAsyncResult mockResultMapping = MockResultHelper::createSuccessResult(StoreHelper::renderPrimaryStoreMapping()); StringStream expectedUrlMapping; expectedUrlMapping << "/" << getFacade().getWorkingSpaceId() << "/mock-spaces-primarystoremapping"; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrlMapping.getContent()) .WillRepeatedly(testing::Return(mockResultMapping)); const MockAsyncResult mockResultNotification = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderPrimaryStoreUpdateResult(true, false)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillOnce(testing::Return(mockResultNotification)) .WillRepeatedly(testing::Return(mockResultNotification)); const MockAsyncResult mockResultInventory = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderEmptyResult("items")); StringStream expectedUrlInventory; expectedUrlInventory << "/" << getFacade().getAuthenticationClient().getSessionInfo().getProfileId() << "/mock-profiles-inventory"; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrlInventory.getContent()) .WillOnce(testing::Return(mockResultInventory)) .WillOnce(testing::Return(mockResultInventory)); } #if defined(US_PRIMARY_STORE_WITHOUT_API) { PrimaryStoreInventoryItem dlc; dlc.m_id = UPLAYPC_DUMMY_DLCID; dlc.m_type = PrimaryStoreItemType::Durable; dlc.m_state = PrimaryStoreInventoryItem::InventoryItemState::ReadyToUse; dlc.m_balance = 1; ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getPrimaryStoreClient().updatePrimaryStoreProducts({ dlc }))); } #endif ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getSecondaryStoreClient().requestInventory())); { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); } ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); #if defined(US_PRIMARY_STORE_WITHOUT_API) { PrimaryStoreInventoryItem consumable; consumable.m_id = UPLAYPC_DUMMY_CONSUMABLEID; consumable.m_type = PrimaryStoreItemType::Consumable; consumable.m_state = PrimaryStoreInventoryItem::InventoryItemState::ReadyToUse; consumable.m_balance = 8; ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getPrimaryStoreClient().updatePrimaryStoreProducts({ consumable }))); } #endif ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getSecondaryStoreClient().requestInventory())); } /// @test Ensures the notification to the secondary store service is NOT sent on the first call to the inventory /// when going back from background if no update occurred #if defined(US_PLATFORM_WITH_PRIMARYSTORE_NOTIFICATION) TEST_F(SUSPENDEDMODE_MOCK_LOGIN, USNOT_SecondaryStoreUpdateNotificationAfterBackgroundModeNoUpdate) #else /// @disabled With notification not supported, this test is just like requesting the inventory "normally" TEST_F(SUSPENDEDMODE_MOCK_LOGIN, DISABLED_USNOT_SecondaryStoreUpdateNotificationAfterBackgroundModeNoUpdate) #endif { { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .WillRepeatedly(testing::Return(getMockedPopulationsResponse())); const MockAsyncResult mockResultMapping = MockResultHelper::createSuccessResult(StoreHelper::renderPrimaryStoreMapping()); StringStream expectedUrlMapping; expectedUrlMapping << "/" << getFacade().getWorkingSpaceId() << "/mock-spaces-primarystoremapping"; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrlMapping.getContent()) .WillRepeatedly(testing::Return(mockResultMapping)); const MockAsyncResult mockResultNotification = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderPrimaryStoreUpdateResult(true, false)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillOnce(testing::Return(mockResultNotification)) .WillRepeatedly(testing::Return(mockResultNotification)); const MockAsyncResult mockResultInventory = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderEmptyResult("items")); StringStream expectedUrlInventory; expectedUrlInventory << "/" << getFacade().getAuthenticationClient().getSessionInfo().getProfileId() << "/mock-profiles-inventory"; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrlInventory.getContent()) .WillOnce(testing::Return(mockResultInventory)) .WillOnce(testing::Return(mockResultInventory)); } #if defined(US_PRIMARY_STORE_WITHOUT_API) { PrimaryStoreInventoryItem dlc; dlc.m_id = UPLAYPC_DUMMY_DLCID; dlc.m_type = PrimaryStoreItemType::Durable; dlc.m_state = PrimaryStoreInventoryItem::InventoryItemState::ReadyToUse; dlc.m_balance = 1; ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getPrimaryStoreClient().updatePrimaryStoreProducts({ dlc }))); } #endif SecondaryStoreClient& clientSecondaryStore = getFacade().getSecondaryStoreClient(); ASSERT_TRUE(AsyncHelper::waitSuccess(clientSecondaryStore.requestInventory())); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); ASSERT_TRUE(AsyncHelper::waitSuccess(clientSecondaryStore.requestInventory())); } #if !defined(US_DLL) /// Checks that notification is sent when returning from background /// triggered by primary store sync. #if defined(US_PRIMARY_STORE_WITH_API) TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, LOCAL_CheckPrimaryStoreSyncNotificationWhenReturningFromBackground) #else /// @disabled With notification not supported, this test is just like requesting the inventory "normally" TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, DISABLED_LOCAL_CheckPrimaryStoreSyncNotificationWhenReturningFromBackground) #endif { { mockFullLogin(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); waitForPostLoginCompletion(); } HttpPost requestNotification(StringUrl(""), ""); { const MockAsyncResult mockResultNotification = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderPrimaryStoreUpdateResult(true, false)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast(&requestNotification)), testing::Return(mockResultNotification))); const MockAsyncResult mockResultInventory = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderEmptyOffersResult("items")); StringStream expectedUrlInventory; expectedUrlInventory << "/" << getFacade().getAuthenticationClient().getSessionInfo().getProfileId() << "/mock-profiles-inventory"; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrlInventory.getContent()) .WillOnce(testing::Return(mockResultInventory)); } { // PS4 and XONE mocked suspended test will always do the put because of the consumable balance US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); } // Go to background and back to foreground to trigger Sync notification for primary store ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); // Call to secondary store to ensure that primary store sync has been completed SecondaryStoreClient& clientSecondaryStore = getFacade().getSecondaryStoreClient(); ASSERT_TRUE(AsyncHelper::waitSuccess(clientSecondaryStore.requestInventoryItems({ StoreItemId("11111111-2222-3333-4444-555566667777") }))); const SpaceId expectedSpaceId = getFacade().getWorkingSpaceId(); // Check parameters for the notification request ASSERT_NE(size_t(0), requestNotification.getBodyAsString().getLength()); EXPECT_NE(String::npos, requestNotification.getBodyAsString().findSubstringCase(expectedSpaceId)); #if defined(US_TARGET_PS4) || defined(US_TARGET_KILI) // Check that psn parameters are present in the notification const Json body(requestNotification.getBodyAsString()); EXPECT_TRUE(body.isValid()); if (body.isValid()) { EXPECT_TRUE(body["psn.issuerid"].isTypeNumber()); EXPECT_FALSE(body["psn.code"].getValueString().isEmpty()); EXPECT_FALSE(body["psn.clientId"].getValueString().isEmpty()); } #endif } /// Checks that notification is sent when returning from suspended mode /// triggered by primary store sync. #if defined(US_PRIMARY_STORE_WITH_API) && !defined(US_TARGET_SWITCH) TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, LOCAL_CheckPrimaryStoreSyncNotificationWhenReturningFromSuspendedMode) #else /// @disabled With notification not supported, this test is just like requesting the inventory "normally" TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, DISABLED_LOCAL_CheckPrimaryStoreSyncNotificationWhenReturningFromSuspendedMode) #endif { { mockFullLogin(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); waitForPostLoginCompletion(); } HttpPost requestNotification(StringUrl(""), ""); { const MockAsyncResult mockResultNotification = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderPrimaryStoreUpdateResult(true, false)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillOnce(testing::DoAll(testing::SaveArg<0>(static_cast(&requestNotification)), testing::Return(mockResultNotification))); const MockAsyncResult mockResultInventory = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderEmptyOffersResult("items")); StringStream expectedUrlInventory; expectedUrlInventory << "/" << getFacade().getAuthenticationClient().getSessionInfo().getProfileId() << "/mock-profiles-inventory"; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrlInventory.getContent()) .WillOnce(testing::Return(mockResultInventory)); } { // PS4 and XONE mocked suspended test will always do the put because of the consumable balance US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); } { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); } // Go to background and back to foreground to trigger Sync notification for primary store ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); // Call to secondary store to ensure that primary store sync has been completed SecondaryStoreClient& clientSecondaryStore = getFacade().getSecondaryStoreClient(); ASSERT_TRUE(AsyncHelper::waitSuccess(clientSecondaryStore.requestInventoryItems({ StoreItemId("11111111-2222-3333-4444-555566667777") }))); const SpaceId expectedSpaceId = getFacade().getWorkingSpaceId(); // Check parameters for the notification request EXPECT_NE(String::npos, requestNotification.getBodyAsString().findSubstringCase(expectedSpaceId)); #if defined(US_TARGET_PS4) || defined(US_TARGET_KILI) // Check that psn parameters are present in the notification const Json body(requestNotification.getBodyAsString()); EXPECT_TRUE(body.isValid()); if (body.isValid()) { EXPECT_TRUE(body["psn.issuerid"].isTypeNumber()); EXPECT_FALSE(body["psn.code"].getValueString().isEmpty()); EXPECT_FALSE(body["psn.clientId"].getValueString().isEmpty()); } #endif } #if defined(US_PLATFORM_WITH_PRIMARYSTORE_NOTIFICATION) #if defined(US_PRIMARY_STORE_WITHOUT_API) ///@test Checks that, when calling initialize/update primaryStore and then the sdk goes in background mode, the retry sync is still running. TEST_F(SUSPENDEDMODE_MOCK_LOGIN, USNOT_InitializePrimaryStoreBeforeBackgroundModeDoesNotStopPrimaryStoreSyncRetries) { waitForPostLoginCompletion(); { // Secondary Primary Store Mapping const MockAsyncResult mappingResult = MockResultHelper::createSuccessResult(TestPrimaryStoreClient_BF::renderPrimaryStoreMapping()); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/mock-spaces-primarystoremapping") .WillRepeatedly(testing::Return(mappingResult)); const MockAsyncResult mockErrorRestResult = MockResultHelper::createRestErrorResult(RestStatusCode::Error301, 1003, ""); // arbitrary error const MockAsyncResult mockResultPartialSuccess = MockResultHelper::createSuccessResult(StoreHelper::renderPrimaryStoreUpdateResult()); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillOnce(testing::Return(mockResultPartialSuccess)) .WillOnce(testing::Return(mockErrorRestResult)).RetiresOnSaturation(); } ListenerHandler handler = getFacade().getSecondaryStoreClient().createNotificationListener(); PrimaryStoreClient& client = getFacade().getPrimaryStoreClient(); ASSERT_TRUE(AsyncHelper::waitSuccess(client.initializePrimaryStoreProducts(TestPrimaryStoreClient_BF::getInventoryWithItemsForRetrySyncTrigger(10)))); //arbitrary value ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); //After background the notification should be received ASSERT_TRUE(TestPrimarySecondaryStoreSync_BF::waitForNotification(handler, SecondaryStoreNotificationType::InventorySyncUpdate, TestSuspendedMode_BF::MOCK_PERIOD_JOB_WAIT_TIME)); //After background the job retry sync should still be running const SpaceId parametersSpaceId = getFacade().getWorkingSpaceId(); ASSERT_TRUE(SessionManagerStoreProxy::hasPrimaryStorePeriodicSyncResult(getFacadeInternal(), parametersSpaceId)); ASSERT_TRUE(SessionManagerStoreProxy::getPeriodicSyncResult(getFacadeInternal(), parametersSpaceId).isProcessing()); ASSERT_TRUE(TestPrimarySecondaryStoreSync_BF::waitForNotification(handler, SecondaryStoreNotificationType::InventorySyncError, TestSuspendedMode_BF::MOCK_PERIOD_JOB_WAIT_TIME)); ASSERT_FALSE(SessionManagerStoreProxy::hasPrimaryStorePeriodicSyncResult(getFacadeInternal(), parametersSpaceId)); } #else ///@test Checks that, when calling primaryStore sync and then the sdk goes in background mode, the retry sync is still running. TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_PrimaryStoreBeforeBackgroundModeDoesNotStopPrimaryStoreSyncRetries) { { mockLogin(getMockedLoginResponse(), getMockedParametersApplication(), getMockedParametersSpace()); setupWebsocketMock(); MockEventConfigHelper::mockEventConfig(getMockHttpClientImpl()); // Secondary Primary Store Mapping const MockAsyncResult mappingResult = getMockedSecondaryPrimaryStoreMapping(); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/mock-spaces-primarystoremapping") .WillRepeatedly(testing::Return(mappingResult)); const MockAsyncResult mockErrorRestResult = MockResultHelper::createRestErrorResult(RestStatusCode::Error301, 1003, ""); const MockAsyncResult mockResultPartialSuccess = MockResultHelper::createSuccessResult(StoreHelper::renderPrimaryStoreUpdateResult()); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillOnce(testing::Return(mockResultPartialSuccess)) .WillOnce(testing::Return(mockErrorRestResult)).RetiresOnSaturation(); } ListenerHandler handler = getFacade().getSecondaryStoreClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(*NS_COMMON::AuthenticationHelper::createCredentials(), {}, {}))); waitForPostLoginCompletion(); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); //After background the notification should be received and the ASSERT_TRUE(TestPrimarySecondaryStoreSync_BF::waitForNotification(handler, SecondaryStoreNotificationType::InventorySyncUpdate, TestSuspendedMode_BF::MOCK_PERIOD_JOB_WAIT_TIME)); //After background the job retry sync should still be running const SpaceId parametersSpaceId = getFacade().getWorkingSpaceId(); ASSERT_TRUE(SessionManagerStoreProxy::hasPrimaryStorePeriodicSyncResult(getFacadeInternal(), parametersSpaceId)); ASSERT_TRUE(SessionManagerStoreProxy::getPeriodicSyncResult(getFacadeInternal(), parametersSpaceId).isProcessing()); ASSERT_TRUE(TestPrimarySecondaryStoreSync_BF::waitForNotification(handler, SecondaryStoreNotificationType::InventorySyncError, TestSuspendedMode_BF::MOCK_PERIOD_JOB_WAIT_TIME)); ASSERT_FALSE(SessionManagerStoreProxy::hasPrimaryStorePeriodicSyncResult(getFacadeInternal(), parametersSpaceId)); } ///@test Checks that, when calling primaryStore sync and then the sdk goes in suspended mode, the retry sync gets canceled. TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_PrimaryStoreSyncBeforeSuspendedModeGetsCanceledAfterSuspended) { { mockLogin(getMockedLoginResponse(), getMockedParametersApplication(), getMockedParametersSpace()); setupWebsocketMock(); MockEventConfigHelper::mockEventConfig(getMockHttpClientImpl()); // Secondary Primary Store Mapping const MockAsyncResult mappingResult = getMockedSecondaryPrimaryStoreMapping(); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/mock-spaces-primarystoremapping") .WillRepeatedly(testing::Return(mappingResult)); const MockAsyncResult mockErrorRestResult = MockResultHelper::createRestErrorResult(RestStatusCode::Error301, 1003, ""); const MockAsyncResult mockResultPartialSuccess = MockResultHelper::createSuccessResult(StoreHelper::renderPrimaryStoreUpdateResult()); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillOnce(testing::Return(mockResultPartialSuccess)) .WillOnce(testing::Return(mockErrorRestResult)).RetiresOnSaturation(); } ListenerHandler handler = getFacade().getSecondaryStoreClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(*NS_COMMON::AuthenticationHelper::createCredentials(), {}, {}))); waitForPostLoginCompletion(); ASSERT_TRUE(TestPrimarySecondaryStoreSync_BF::waitForNotification(handler, SecondaryStoreNotificationType::InventorySyncUpdate, TestSuspendedMode_BF::MOCK_PERIOD_JOB_WAIT_TIME)); { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "parameters") .WillOnce(testing::Return(getMockedParametersApplication())) .WillOnce(testing::Return(getMockedParametersSpace())) .RetiresOnSaturation(); } ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); const SpaceId parametersSpaceId = getFacade().getWorkingSpaceId(); ASSERT_TRUE(SessionManagerStoreProxy::hasPrimaryStorePeriodicSyncResult(getFacadeInternal(), parametersSpaceId)); ASSERT_FALSE(SessionManagerStoreProxy::getPeriodicSyncResult(getFacadeInternal(), parametersSpaceId).isProcessing()); ASSERT_TRUE(TestPrimarySecondaryStoreSync_BF::waitForNotification(handler, SecondaryStoreNotificationType::InventorySyncError, TestSuspendedMode_BF::MOCK_PERIOD_JOB_WAIT_TIME)); } #endif #endif // defined(US_PLATFORM_WITH_PRIMARYSTORE_NOTIFICATION) #endif #endif // !defined(US_SDK_LIGHT_MOBILE) TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, LOCAL_SuspendedModeWithCreateSessionFeatureSwitchOffDoesNotCreateSessionWhenComingBackFromSuspended) { { const MockAsyncResult parametersSpaceMock = MockResultHelper::createSuccessResult( ParametersSpaceMockHelper().changeFeatureSwitch(FeatureSwitchId::CreateSession, FeatureSwitchState::Disabled).getParameters().renderContent()); const MockAsyncResult parametersApplicationMock = getMockedParametersApplication(); mockLogin(getMockedLoginResponse(), parametersApplicationMock, parametersSpaceMock); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(1); US_EXPECT_CALL(US_TEST_NS::Action::GetConfigEvent).Times(0); US_EXPECT_CALL(US_TEST_NS::Action::SendEvents).Times(0); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/mock-spaces-primarystoremapping").Times(0); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore").Times(0); } ASSERT_TRUE(AsyncHelper::waitFailureResponse(getFacade().createSession(*NS_COMMON::AuthenticationHelper::createCredentials(), {}, {}), Errors::SKIPPED_FEATURESWITCH)); // Force a pop to validate that there isn't any call to the events service. ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getEventClient().popUnsentEvents(""))); ASSERT_FALSE(getFacade().getAuthenticationClient().getSessionInfo().hasValidSession()); { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .Times(testing::AtMost(1)) .WillOnce(testing::Return(getMockedPopulationsResponse())); } // Go to background and back to foreground to trigger Sync notification for primary store ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); ASSERT_FALSE(getFacade().getAuthenticationClient().getSessionInfo().hasValidSession()); // Force a pop to validate that there isn't any call to the events service. ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().getEventClient().popUnsentEvents(""))); } #if !defined(US_DLL) /// @test Validates config is retrieved and updated after suspended - foreground transition /// FeatureSwitchId::TerminateSessionOnConfigurationUpdateMismatchSpaceId is OFF /// Actual and retrieved SpaceID are the same TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_ValidateConfigRetrievedAndUpdatedAfterSuspendedForegroundTransition) { setupDebugLogging(); { Map featureSwitches; featureSwitches[FeatureSwitchId::TerminateSessionOnConfigurationUpdateMismatchSpaceId] = FeatureSwitchState::Disabled; const auto parametersSpaceMock = MockResultHelper::createSuccessResult(ParametersSpaceMockHelper().changeFeatureSwitches(featureSwitches) .getParameters().renderContent()); setupWebsocketMock(); mockFullLogin(nullptr, nullptr, ¶metersSpaceMock); } ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); waitForPostLoginCompletion(); const SpaceId currentAppParametersSpaceId = getFacade().getWorkingSpaceId(); { const ParametersInfo& parametersInfo = ParametersClientProxy::getParametersInfo(getFacadeInternal()); EXPECT_EQ(3, parametersInfo.m_paramsSettingsHttpInternal.m_maxCount); } // SuspendedMode Expects { US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/mock-parameters") .WillOnce(testing::Return(MockResultHelper::createSuccessResult(ParametersApplicationMockHelper().getParameters().renderContent()))) .WillOnce(testing::Return(MockResultHelper::createSuccessResult(TestSuspendedMode_BF::ParametersSpaceMockHelperUpdatedParams(20).getParameters().renderContent()))); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .WillRepeatedly(testing::Return(getMockedPopulationsResponse())); StringStream expectedUrlMapping; expectedUrlMapping << "/" << getFacade().getWorkingSpaceId() << "/mock-spaces-primarystoremapping"; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrlMapping.getContent()) .WillRepeatedly(testing::Return(getMockedSecondaryPrimaryStoreMapping())); const MockAsyncResult mockResultNotification = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderPrimaryStoreUpdateResult(true, false)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillRepeatedly(testing::Return(mockResultNotification)); } NotificationSource::ListenerHandler handler = getFacade().getParametersClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); // We expect latest parameters to partially apply, since spaceId changed and feature switch is set to accept that case ASSERT_TRUE(TestSuspendedMode_BF::waitForParametersNotification(handler, ParametersNotificationType::ApplicationParametersUpdated, DurationMilli(5000))); const SpaceId newParametersSpaceId = getFacade().getWorkingSpaceId(); // New config, but keep original spaceId EXPECT_EQ(currentAppParametersSpaceId, newParametersSpaceId); { const ParametersInfo& parametersInfo = ParametersClientProxy::getParametersInfo(getFacadeInternal()); EXPECT_EQ(20, parametersInfo.m_paramsSettingsHttpInternal.m_maxCount); } } /// @test Validates config is retrieved and partially updated after suspended - foreground transition and SpaceId is different /// Partially updated = force new config to use original SpaceId /// FeatureSwitchId::TerminateSessionOnConfigurationUpdateMismatchSpaceId is OFF /// Actual and retrieved SpaceID are NOT the same TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_ValidateConfigRetrievedPartiallyUpdatedAfterSuspendedForegroundTransitionAndMismatchSpaceId) { setupDebugLogging(); { Map featureSwitches; featureSwitches[FeatureSwitchId::TerminateSessionOnConfigurationUpdateMismatchSpaceId] = FeatureSwitchState::Disabled; const auto parametersSpaceMock = MockResultHelper::createSuccessResult(ParametersSpaceMockHelper().changeFeatureSwitches(featureSwitches) .getParameters().renderContent()); setupWebsocketMock(); mockFullLogin(NULL, NULL, ¶metersSpaceMock); } ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); waitForPostLoginCompletion(); const SpaceId currentAppParametersSpaceId = getFacade().getWorkingSpaceId(); { const ParametersInfo& parametersInfo = ParametersClientProxy::getParametersInfo(getFacadeInternal()); EXPECT_EQ(3, parametersInfo.m_paramsSettingsHttpInternal.m_maxCount); EXPECT_EQ(parametersInfo.m_featuresSwitches.m_parameters.end(), parametersInfo.m_featuresSwitches.m_parameters.find("ThisIsANewFeatureSwitchForParametersApplicationMockHelperWithDifferentSpaceId")); } { const Json applicationParameters = TestSuspendedMode_BF::ParametersApplicationMockHelperWithDifferentSpaceId().getParameters(); const Json spaceParameters = TestSuspendedMode_BF::ParametersSpaceMockHelperUpdatedParams(10).getParameters(); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/mock-parameters") .WillOnce(testing::Return(MockResultHelper::createSuccessResult(applicationParameters.renderContent()))) .WillOnce(testing::Return(MockResultHelper::createSuccessResult(spaceParameters.renderContent()))); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .WillRepeatedly(testing::Return(getMockedPopulationsResponse())); StringStream expectedUrlMapping; expectedUrlMapping << "/" << getFacade().getWorkingSpaceId() << "/mock-spaces-primarystoremapping"; US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, expectedUrlMapping.getContent()) .WillRepeatedly(testing::Return(getMockedSecondaryPrimaryStoreMapping())); const MockAsyncResult mockResultNotification = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::renderPrimaryStoreUpdateResult(true, false)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-profiles-inventory-primarystore") .WillRepeatedly(testing::Return(mockResultNotification)); } NotificationSource::ListenerHandler handler = getFacade().getParametersClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); // We expect latest config to partially apply, since spaceId changed and feature switch is set to accept that case ASSERT_TRUE(TestSuspendedMode_BF::waitForParametersNotification(handler, ParametersNotificationType::ApplicationParametersUpdatedPartially, DurationMilli(5000))); const SpaceId newParametersSpaceId = getFacade().getWorkingSpaceId(); // New config, but keep original spaceId EXPECT_EQ(currentAppParametersSpaceId, newParametersSpaceId); { const ParametersInfo& parametersInfo = ParametersClientProxy::getParametersInfo(getFacadeInternal()); EXPECT_EQ(10, parametersInfo.m_paramsSettingsHttpInternal.m_maxCount); EXPECT_EQ(true, parametersInfo.m_featuresSwitches.m_parameters.find("ThisIsANewFeatureSwitchForParametersApplicationMockHelperWithDifferentSpaceId")->second); } } /// @test Validates config is retrieved, not updated and current session is terminated, after suspended - foreground transition and SpaceId is different /// FeatureSwitchId::TerminateSessionOnConfigurationUpdateMismatchSpaceId is ON /// Actual and retrieved SpaceID are NOT the same TEST_F(SUSPENDEDMODE_MOCK_NOLOGIN, USNOT_ValidateConfigRetrievedAndSessionTerminatedAfterSuspendedForegroundTransitionAndMismatchSpaceId) { setupDebugLogging(); { Map featureSwitches; featureSwitches[FeatureSwitchId::TerminateSessionOnConfigurationUpdateMismatchSpaceId] = FeatureSwitchState::Enabled; const auto parametersSpaceMock = MockResultHelper::createSuccessResult(ParametersSpaceMockHelper().changeFeatureSwitches(featureSwitches) .getParameters().renderContent()); setupWebsocketMock(); mockFullLogin(NULL, NULL, ¶metersSpaceMock); } ASSERT_TRUE(AsyncHelper::waitSuccess(getFacade().createSession(getPlayerCredentials(), {}, {}))); waitForPostLoginCompletion(); const SpaceId currentAppParametersSpaceId = getFacade().getWorkingSpaceId(); { const ParametersInfo& parametersInfo = ParametersClientProxy::getParametersInfo(getFacadeInternal()); EXPECT_EQ(3, parametersInfo.m_paramsSettingsHttpInternal.m_maxCount); } { const auto parametersApplicationResult = MockResultHelper::createSuccessResult(TestSuspendedMode_BF::ParametersApplicationMockHelperDefaultPopulations (TestPopulations_BF::POPULATION_NAME_DEFAULT, TestPopulations_BF::POPULATION_SUBJECT_DEFAULT, SpaceId(TestPopulations_BF::SPACE_ID_FOR_NEW_POPULATION)).getParameters().renderContent()); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_GET, "/mock-parameters") .WillOnce(testing::Return(parametersApplicationResult)); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_PUT, "/profiles/me/mock-populations/data") .WillRepeatedly(testing::Return(getMockedPopulationsResponse())); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_DELETE, "/profiles/sessions") .WillOnce(testing::Return(MockResultHelper::createSuccessResult("{}"))); US_EXPECT_CALL_HTTP(getMockHttpClientImpl(), HttpMethod::HTTP_POST, "/mock-events") .WillOnce(testing::Return(MockResultHelper::createSuccessResult("{}"))) .RetiresOnSaturation(); } NotificationSource::ListenerHandler paramsNotifListener = getFacade().getParametersClient().createNotificationListener(); NotificationSource::ListenerHandler authNotifListener = getFacade().getAuthenticationClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); // We DO NOT expect latest config to be apply, since spaceId changed and feature switch is set to NOT accept that case ASSERT_FALSE(TestSuspendedMode_BF::waitForParametersNotification(paramsNotifListener, ParametersNotificationType::ApplicationParametersUpdatedPartially, DurationMilli(5000))); ASSERT_TRUE(TestSuspendedMode_BF::waitForAuthentificationNotification(authNotifListener, AuthenticationNotificationType::SessionConfigurationUpdateMismatchSpaceIDSessionTerminated, DurationMilli(5000))); const SpaceId newParametersSpaceId= getFacade().getWorkingSpaceId(); // Same config, same spaceId EXPECT_EQ(currentAppParametersSpaceId, newParametersSpaceId); { const ParametersInfo& parametersInfo = ParametersClientProxy::getParametersInfo(getFacadeInternal()); EXPECT_EQ(3, parametersInfo.m_paramsSettingsHttpInternal.m_maxCount); } // Session is terminated EXPECT_FALSE(getFacade().getAuthenticationClient().getSessionInfo().hasValidSession()); #if !defined(US_SDK_LIGHT_MOBILE) SecondaryStoreClient& clientSecondaryStore = getFacade().getSecondaryStoreClient(); const AsyncResult > result = clientSecondaryStore.requestInventory(); EXPECT_FALSE(AsyncHelper::waitSuccess(clientSecondaryStore.requestInventory())); #endif } /// @test Validates config is retrieved and updated after suspended - foreground transition /// FeatureSwitchId::TerminateSessionOnConfigurationUpdateMismatchSpaceId is ON /// Actual and retrieved SpaceID are the same #if defined(US_TARGET_SWITCH) TEST_F(SUSPENDEDMODE, DISABLED_US_ValidateConfigRetrievedAndUpdatedAfterSuspendedForegroundTransition) #else TEST_F(SUSPENDEDMODE, US_ValidateConfigRetrievedAndUpdatedAfterSuspendedForegroundTransition) #endif { setupDebugLogging(); #if defined(US_LOG_ENABLED) && defined(DEBUGGING) PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device) { US_EXPECT_LOG("Application and Space paramaters were updated after coming back from suspended.", LogLevel::Info, Times(1)); }); #endif NotificationSource::ListenerHandler paramsNotifListener = getFacade().getParametersClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Suspended))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); ASSERT_TRUE(TestSuspendedMode_BF::waitForParametersNotification(paramsNotifListener, ParametersNotificationType::ApplicationParametersUpdated, DurationMilli(5000))); } /// @test Validates config is NOT retrieved and updated after background - foreground transition #if defined(US_TARGET_SWITCH) TEST_F(SUSPENDEDMODE, DISABLED_US_ValidateConfigNotRetrievedAndUpdatedAfterBackgroundForegroundTransition) #else TEST_F(SUSPENDEDMODE, US_ValidateConfigNotRetrievedAndUpdatedAfterBackgroundForegroundTransition) #endif { setupDebugLogging(); #if defined(US_LOG_ENABLED) && defined(DEBUGGING) PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device) { US_EXPECT_LOG("Application and Space paramaters were updated after coming back from suspended.", LogLevel::Info, Times(0)); }); #endif NotificationSource::ListenerHandler paramsNotifListener = getFacade().getParametersClient().createNotificationListener(); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Background))); ASSERT_TRUE(AsyncHelper::waitSuccess(TestSuspendedMode_BF::changeState(PlatformHelper::Foreground))); ASSERT_FALSE(TestSuspendedMode_BF::waitForParametersNotification(paramsNotifListener, ParametersNotificationType::ApplicationParametersUpdated, DurationMilli(5000))); } #endif #if defined(US_TARGET_SWITCH) /// @test Validate that Foreground to Suspended is deprecated through PlatfromHelper::changeState TEST_F(SUSPENDEDMODE_MOCK_LOGIN, USNOT_ForegroundSuspendedTransitionDeprecated) { ASSERT_TRUE(PlatformHelper::getState() == PlatformHelper::Foreground); auto foregroundToSuspendedAsync = PlatformHelper::changeState(PlatformHelper::Suspended); ASSERT_TRUE(AsyncHelper::waitSuccess(foregroundToSuspendedAsync)); ASSERT_TRUE(foregroundToSuspendedAsync.getError().m_description == "Switch SDK uses automatic suspended/foreground detection"); } /// @test Validate that Background to Suspended is deprecated through PlatfromHelper::changeState TEST_F(SUSPENDEDMODE_MOCK_LOGIN, USNOT_BackgroundSuspendedTransitionDeprecated) { ASSERT_TRUE(AsyncHelper::waitSuccess(PlatformHelper::changeState(PlatformHelper::Background))); ASSERT_TRUE(PlatformHelper::getState() == PlatformHelper::Background); auto foregroundToSuspendedAsync = PlatformHelper::changeState(PlatformHelper::Suspended); ASSERT_TRUE(AsyncHelper::waitSuccess(foregroundToSuspendedAsync)); ASSERT_TRUE(foregroundToSuspendedAsync.getError().m_description == "Switch SDK uses automatic suspended/foreground detection"); } /// @test Validate that Foreground to Background and Background to Forefround is supported through PlatfromHelper::changeState TEST_F(SUSPENDEDMODE_MOCK_LOGIN, USNOT_BackgroundForegroundTransitionAndForegroundBackgroundTransition) { ASSERT_TRUE(PlatformHelper::getState() == PlatformHelper::Foreground); ASSERT_TRUE(AsyncHelper::waitSuccess(PlatformHelper::changeState(PlatformHelper::Background))); ASSERT_TRUE(PlatformHelper::getState() == PlatformHelper::Background); ASSERT_TRUE(AsyncHelper::waitSuccess(PlatformHelper::changeState(PlatformHelper::Foreground))); ASSERT_TRUE(PlatformHelper::getState() == PlatformHelper::Foreground); } #endif #undef US_PLATFORM_WITH_PRIMARYSTORE_NOTIFICATION #undef UPLAYPC_DUMMY_DLCID #undef UPLAYPC_DUMMY_CONSUMABLEID };