JD2022-TU1/main/extern/UbiServices/custom/testsUnit/services/event/testEventQueue.cpp

1048 lines
54 KiB
C++

////////////////////////////////////////////////////////////////////////////////
// (c) Copyright, Ubisoft Entertainment Inc. || Created by Harbour //
////////////////////////////////////////////////////////////////////////////////
#include <tests/config.h>
#include <tests/common/badEvents.h>
#include <tests/fixture/memoryCheckFixture.h>
#include <tests/mock/mockLogDevice.h>
#include <testsIntegration/event/eventsConfigMockHelper.h>
#include <ubiservices/loggingHelper.h>
#include <ubiservices/core/chrono/chronometer.h>
#include <ubiservices/core/chrono/clockSystem.h>
#include <ubiservices/core/JSON/jsonWriter.h>
#include <ubiservices/core/types/dateTimeHelper.h>
#include <ubiservices/core/types/string.h>
#include <ubiservices/services/event/eventQueue.h>
#include <ubiservices/services/event/eventRequest.h>
#include <ubiservices/services/event/eventConfigInfo.h>
#include <ubiservices/services/event/pushEventResult.h>
#include <ubiservices/services/event/types/eventInfoBaseProxy.h>
#if !defined(US_SDK_S2S)
#include <ubiservices/services/event/types/eventInfoCustom.h>
#endif
#include <ubiservices/services/event/types/eventInfoService.h>
#if !defined(US_SDK_S2S)
#include <ubiservices/services/event/types/eventInfoContextStart.h>
#include <ubiservices/services/event/types/eventInfoContextStop.h>
#include <ubiservices/services/event/types/eventInfoPlayerPosition.h>
#endif
namespace US_NS {;
US_DEFINE_TEST(TestEventQueue);
#if !defined(US_DLL)
typedef LeakChecker SERVICES_EVENT_QUEUE;
namespace TestEventQueue_BF
{
#if !defined(US_SDK_S2S)
static const char* s_mockContextType = "dummyContextType";
static const char* s_mockContextName = "dummyContextName";
static const char* s_currentSession = "4d9061de-7c20-4cc9-9f55-b8d6228da6ae";
static const char* s_currentSpaceId = "4d9061de-7c20-4cc9-9f55-b8d6228da6af";
#endif
testing::AssertionResult validateEvents(const Vector<String>& ecxpectedEvents, const Json& outputRequest)
{
const Vector<Json> resultEvents = outputRequest["events"].getItems();
if (resultEvents.size() != ecxpectedEvents.size())
{
return testing::AssertionFailure() << "We don't have the expected amount of events.";
}
Vector<String>::const_iterator expectedEventIt = ecxpectedEvents.begin();
for (Vector<Json>::const_iterator resultEventIt = resultEvents.begin(); resultEventIt != resultEvents.end(); ++resultEventIt)
{
if ((*resultEventIt)["type"].getValueString() != *expectedEventIt)
{
return testing::AssertionFailure() << "The events result doesn't match the expected events.";
}
++expectedEventIt;
}
return testing::AssertionSuccess();
}
#if !defined(US_SDK_S2S)
const ClockSteady::TTimeMilli s_PLAYTIME_WAIT_ONESECOND = 1100u; //extra 100 ms to remove flakiness...
#endif
}
/// @test Validates that the timestamp is correctly set at push time.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventTimeStamp)
{
#if !defined(US_SDK_S2S)
EventInfoCustom customEvent("Test"); //Can't do the getTimeStamp on the mock.
#else
EventInfoService customEvent = TestHelpers_BF::getServiceEvent("Test");
#endif
{
ASSERT_EQ(0u, EventInfoBaseProxy::getTimeStamp(customEvent)) << "The timestamp should not be computed at construction time.";
#if !defined(US_SDK_S2S)
EventInfoCustom copy = customEvent;
#else
EventInfoService copy = customEvent;
#endif
ASSERT_EQ(0u, EventInfoBaseProxy::getTimeStamp(copy)) << "The timestamp should linger on copy.";
}
EventQueue eventQueue;
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(customEvent));
ASSERT_EQ(0, EventInfoBaseProxy::getTimeStamp(customEvent)) << "We shouldn't be modifying the prod event since they could reuse their event.";
#if defined(US_SDK_S2S)
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(EventConfigInfo());
#else
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
#endif
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
#if defined(US_SDK_S2S)
const String requestResult = requests[0]->outputHttpBodyFormat(eventConfig).front();
#else
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSpaceId, eventConfig).front();
#endif
const Json resultJson(requestResult);
#if defined(US_SDK_S2S)
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "service.Test" }, resultJson));
const String createdDate = resultJson["events"].getItems().at(0u)["createdDate"].getValueString();
#else
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start", "custom.Test" }, resultJson));
const String createdDate = resultJson["events"].getItems().at(1u)["createdDate"].getValueString();
#endif
// Since the events were never sent and oEventRequest::outputUnsentFormat() was never called, the date has a place holder instead of a real date.
ASSERT_EQ(createdDate, "OFFLINE_TIMESTAMP_PLACEHOLDER");
}
#if !defined(US_SDK_S2S)
/// @test Validates that the context.start and context.stop have the same id when context.stop is pushed.
/// @test Verifies that the Id increments appropriately after other context push.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventContextCheckId)
{
EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
ASSERT_EQ(-1, EventInfoBaseProxy::getContextId(contextStartEvent));
ASSERT_EQ(-1, EventInfoBaseProxy::getContextId(contextStopEvent));
EventQueue eventQueue;
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
ASSERT_EQ(-1, EventInfoBaseProxy::getContextId(contextStartEvent)) << "We shouldn't be modifying the prod event since they could reuse their event.";
ASSERT_EQ(-1, EventInfoBaseProxy::getContextId(contextStopEvent));
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start",
String::formatText("context.start.%s", TestEventQueue_BF::s_mockContextType),
String::formatText("context.stop.%s", TestEventQueue_BF::s_mockContextType) }, resultJson));
ASSERT_EQ(0u, resultJson["events"].getItems().at(0)["contexts"].getItemsCount()) << "game.start shouldn't have any context set.";
ASSERT_EQ(0u, resultJson["events"].getItems().at(1)["typeData"]["contextId"].getValueInteger());
ASSERT_EQ(0u, resultJson["events"].getItems().at(2)["typeData"]["contextId"].getValueInteger());
}
/// @test Verifies that the Id increments appropriately after multiple context push.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventContextIdIncrements)
{
const EventInfoContextStart contextStartEvent1(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const String otherName = "OtherType";
const EventInfoContextStart contextStartEvent2(otherName, TestEventQueue_BF::s_mockContextName);
EventQueue eventQueue;
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent1));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent2));
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start",
String::formatText("context.start.%s", TestEventQueue_BF::s_mockContextType),
String::formatText("context.start.%s", otherName.getUtf8()) }, resultJson));
ASSERT_EQ(0u, resultJson["events"].getItems().at(0)["contexts"].getItemsCount()) << "game.start shouldn't have any context set.";
ASSERT_EQ(0u, resultJson["events"].getItems().at(1)["typeData"]["contextId"].getValueInteger());
ASSERT_EQ(1u, resultJson["events"].getItems().at(2)["typeData"]["contextId"].getValueInteger());
}
/// @test Validates that the context stop takes the accumulates chrono in the context.start at push time
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventContextCheckDuration)
{
EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
EventQueue eventQueue;
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
const ClockSystem::TTimeMilli CONTEXT_DURATION = 100;
#if defined(US_TARGET_IOS)
const ClockSteady::TTimeNano ref = ClockSteady::getTimeNano();
while ((ClockSteady::getTimeNano() - ref) < CONTEXT_DURATION * 1000000ul)
{
}
#else
US_TEST_NS::Helper::sleep(CONTEXT_DURATION);
#endif
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start",
String::formatText("context.start.%s", TestEventQueue_BF::s_mockContextType),
String::formatText("context.stop.%s", TestEventQueue_BF::s_mockContextType) }, resultJson));
ASSERT_GE(resultJson["events"].getItems().at(2)["typeData"]["contextDuration"].getValueInteger(), (CONTEXT_DURATION / 2)) << "Event Result : " << requestResult.getUtf8();
ASSERT_LT(resultJson["events"].getItems().at(2)["typeData"]["contextDuration"].getValueInteger(), (CONTEXT_DURATION / 2) + CONTEXT_DURATION);
}
/// @test Validates that adding a context is non blocking for other context
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventContextNonBlocking)
{
const String anotherContextType = String("Another") + TestEventQueue_BF::s_mockContextType;
const String anotherContextName = String("Another") + TestEventQueue_BF::s_mockContextType;
const EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStart contextStartEventAnotherTypeAndName(anotherContextType, anotherContextName);
const EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStop contextStopEventAnotherTypeAndName(anotherContextType, anotherContextName);
EventQueue eventQueue;
{
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEventAnotherTypeAndName));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEventAnotherTypeAndName));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
}
}
/// @test Validates that we can close the context in any order
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventContextAnyOrder)
{
const String anotherContextType = String("Another") + TestEventQueue_BF::s_mockContextType;
const String anotherContextName = String("Another") + TestEventQueue_BF::s_mockContextType;
const EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStart contextStartEventAnotherType(anotherContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStop contextStopEventAnotherType(anotherContextType, TestEventQueue_BF::s_mockContextName);
EventQueue eventQueue;
{
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEventAnotherType));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEventAnotherType));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEventAnotherType));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEventAnotherType));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEventAnotherType));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEventAnotherType));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
}
}
/// @test Validates that we get a error on push on a malformed context.start
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventContextStartFailureInvalid)
{
const EventInfoContextStart contextStartEventInvalid1(String(), TestEventQueue_BF::s_mockContextName);
const EventInfoContextStart contextStartEventInvalid2(TestEventQueue_BF::s_mockContextType, String());
const EventInfoContextStart contextStartEventInvalid3((String()), String());
EventQueue eventQueue;
{
EXPECT_EQ(PushEventResult::ContextCreationInvalid, eventQueue.pushEvent(contextStartEventInvalid1));
EXPECT_EQ(PushEventResult::ContextCreationInvalid, eventQueue.pushEvent(contextStartEventInvalid2));
EXPECT_EQ(PushEventResult::ContextCreationInvalid, eventQueue.pushEvent(contextStartEventInvalid3));
}
}
/// @test Validates that we get a error on push when the context.start is already present
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventContextStartFailurePresent)
{
const EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStart contextStartEventAnotherName(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName + String("AnotherName"));
EventQueue eventQueue;
{
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
EXPECT_EQ(PushEventResult::ContextCreationButAlreadyExist, eventQueue.pushEvent(contextStartEvent));
EXPECT_EQ(PushEventResult::ContextCreationButAlreadyExist, eventQueue.pushEvent(contextStartEventAnotherName));
}
}
/// @test Validates that we cannot stop a context if it is not started or if it is already stopped.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventContextStopFailure)
{
const EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
EventQueue eventQueue;
{
EXPECT_EQ(PushEventResult::ContextDeletionButDoesNotExist, eventQueue.pushEvent(contextStopEvent));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
EXPECT_EQ(PushEventResult::ContextDeletionButDoesNotExist, eventQueue.pushEvent(contextStopEvent));
}
}
/// @test Validates that a events accumulates the current context Ids in its inner list.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventWithContext)
{
struct Local
{
static Vector<int32> getContextFromEvent(Json event)
{
Vector<int32> contexts;
const Vector<Json> contextsNode = event["contexts"].getItems();
for (Vector<Json>::const_iterator contextIt = contextsNode.begin(); contextIt != contextsNode.end(); ++contextIt)
{
contexts.push_back(contextIt->getValueInteger());
}
return contexts;
}
};
const String anotherContextType = String("Another") + TestEventQueue_BF::s_mockContextType;
const String anotherContextName = String("Another") + TestEventQueue_BF::s_mockContextType;
const EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStart contextStartEventAnotherType(anotherContextType, anotherContextName);
const EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStop contextStopEventAnotherType(anotherContextType, anotherContextName);
EventQueue eventQueue;
{
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom("Test")));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(TestHelpers_BF::getServiceEvent("TestService")));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEventAnotherType));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom("Test")));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEventAnotherType));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(TestHelpers_BF::getServiceEvent("TestService")));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
EXPECT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom("Test")));
}
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
const Vector<String> eventNames
{
"game.start",
"custom.Test",
String::formatText("context.start.%s", TestEventQueue_BF::s_mockContextType),
"service.TestService",
String::formatText("context.start.%s", anotherContextType.getUtf8()),
"custom.Test",
String::formatText("context.stop.%s", anotherContextType.getUtf8()),
"service.TestService",
String::formatText("context.stop.%s", TestEventQueue_BF::s_mockContextType),
"custom.Test"
};
ASSERT_TRUE(TestEventQueue_BF::validateEvents(eventNames, resultJson));
const Vector<int32> contextStart = { 0u };
const Vector<int32> contextStartBoth = { 0u ,1u };
{
EXPECT_THAT(Local::getContextFromEvent(resultJson["events"].getItems().at(0)), Vector<int32>());
EXPECT_THAT(Local::getContextFromEvent(resultJson["events"].getItems().at(1)), Vector<int32>());
EXPECT_THAT(Local::getContextFromEvent(resultJson["events"].getItems().at(2)), contextStart);
EXPECT_THAT(Local::getContextFromEvent(resultJson["events"].getItems().at(3)), contextStart);
EXPECT_THAT(Local::getContextFromEvent(resultJson["events"].getItems().at(4)), contextStartBoth);
EXPECT_THAT(Local::getContextFromEvent(resultJson["events"].getItems().at(5)), contextStartBoth);
EXPECT_THAT(Local::getContextFromEvent(resultJson["events"].getItems().at(6)), contextStart);
EXPECT_THAT(Local::getContextFromEvent(resultJson["events"].getItems().at(7)), contextStart);
EXPECT_THAT(Local::getContextFromEvent(resultJson["events"].getItems().at(8)), Vector<int32>());
}
}
/// @test Validates that the context duration are accumulating the suspended time.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_ContextDurationSuspendedMode)
{
const EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const ClockSystem::TTimeMilli SLEEP_SUSPEND_TIME = 300;
const uint32 SUSPENDED_COUNT = 4;
const ClockSystem::TTimeMilli SLEEP_CONTEXT_TIME = 50;
EventQueue eventQueue;
eventQueue.pushEvent(contextStartEvent);
// Need to know precisely the context time. The sleep duration cannot be trusted
// because of the sleep resolution on some platforms, e.g 15 ms on XboxOne
HighResolutionChrono chronoContextSleep;
US_TEST_NS::Helper::sleep(SLEEP_CONTEXT_TIME);
const uint64 expectedContextDuration = ClockSteady::getMilliFromNano(chronoContextSleep.getElapsed());
for (uint32 i = 0; i < SUSPENDED_COUNT; i++)
{
// Need to know precisely the suspend time. The sleep duration cannot be trusted
// because of the sleep resolution on some platforms, e.g 15 ms on XboxOne
HighResolutionChrono chronoSuspend;
US_TEST_NS::Helper::sleep(SLEEP_SUSPEND_TIME);
eventQueue.notifySuspendedAwake(chronoSuspend.getElapsed());
}
eventQueue.pushEvent(contextStopEvent);
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
Vector<String> expectedEvents;
expectedEvents.push_back("game.start");
expectedEvents.push_back(String::formatText("context.start.%s", TestEventQueue_BF::s_mockContextType));
expectedEvents.push_back(String::formatText("context.stop.%s", TestEventQueue_BF::s_mockContextType));
ASSERT_TRUE(TestEventQueue_BF::validateEvents(expectedEvents, resultJson));
EXPECT_GT((*resultJson["events"].getItems().rbegin())["typeData"]["contextDuration"].getValueInteger(), expectedContextDuration / 2);
// The EXPECT_LT has a higher margin than EXPECT_GT due to the different processing time that are not measured in the test
// e.g the time taken by the for loop, or to put the events in the queue
EXPECT_LT((*resultJson["events"].getItems().rbegin())["typeData"]["contextDuration"].getValueInteger(), expectedContextDuration * 2);
}
#endif
/// @test Validates that we filter an event when the event tag is not in the event config
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PopRequestEventFilter)
{
EventQueue eventQueue;
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
#if !defined(US_SDK_S2S)
ASSERT_EQ(1, eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig).size()) << "First pop should purge the game.start and player.start.";
ASSERT_EQ(0, eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig).size()) << "There should be no events left in the queue at this point.";
{
ASSERT_TRUE(eventConfig.validateEventTag("custom.Testing")) << "The event should be in the config";
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom(CUSTOM_EVENT_NAME)));
ASSERT_EQ(1, eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig).size());
}
ASSERT_EQ(0, eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig).size()) << "There should be no events left in the queue at this point.";
{
ASSERT_FALSE(eventConfig.validateEventTag("custom.NotPushableEvent")) << "The event should not be in the config";
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom("NotPushableEvent")));
ASSERT_EQ(0, eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig).size());
}
ASSERT_EQ(0, eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig).size()) << "There should be no events left in the queue at this point.";
#else
ASSERT_EQ(0, eventQueue.popEventRequests(eventConfig).size()) << "There should be no events left in the queue at this point.";
{
ASSERT_TRUE(eventConfig.validateEventTag("service.Testing")) << "The event should be in the config";
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(TestHelpers_BF::getServiceEvent()));
ASSERT_EQ(1, eventQueue.popEventRequests(eventConfig).size());
}
ASSERT_EQ(0, eventQueue.popEventRequests(eventConfig).size()) << "There should be no events left in the queue at this point.";
#endif
}
#if defined(US_SDK_S2S)
/// @test Validates that a log is sent if number of events is higher than the maxBatchSize set in the config
/// TODO : Update this test once the maxBatchSize will be implemented, i.e. when the SDK will send several requests if
/// more than maxBatchSize events are in the queue
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventMaxBatchSize)
{
setupDebugLogging();
PrepareLoggingMock([](US_TEST_NS::MockLogDevice& device)
{
US_EXPECT_LOG("higher than the maxBatchSize", LogLevel::Warning, Times(1));
});
const uint32 MAX_BATCH_SIZE = 3;
EventQueue eventQueue;
for (uint32 i = 0; i < MAX_BATCH_SIZE + 1; i++)
{
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(TestHelpers_BF::getServiceEvent()));
}
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBodyServer(MAX_BATCH_SIZE), eventConfig));
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(eventConfig);
}
#endif
#if !defined(US_SDK_S2S)
/// @test Validates that we have an appropriate sequence id on request creation.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PushEventSequenceId)
{
EventQueue eventQueue;
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom("Test")));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom("Test")));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom("Test")));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom("Test")));
{
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
const Vector<String> expectedEvents
{
"game.start",
"custom.Test",
"custom.Test",
"custom.Test",
"custom.Test",
};
ASSERT_TRUE(TestEventQueue_BF::validateEvents(expectedEvents, resultJson));
const String eventStr(resultJson["events"].getItems().at(0).renderContent());
ASSERT_EQ(0u, resultJson["events"].getItems().at(0)["seqId"].getValueInteger());
ASSERT_EQ(1, resultJson["events"].getItems().at(1)["seqId"].getValueInteger());
ASSERT_EQ(2, resultJson["events"].getItems().at(2)["seqId"].getValueInteger());
ASSERT_EQ(3, resultJson["events"].getItems().at(3)["seqId"].getValueInteger());
ASSERT_EQ(4, resultJson["events"].getItems().at(4)["seqId"].getValueInteger());
}
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom("Test")));
//We should still have a increment after a request creation
{
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "custom.Test" }, resultJson));
ASSERT_EQ(5, resultJson["events"].getItems().at(0)["seqId"].getValueInteger());
}
}
/// @test Validates that no request is created if there are only a few non critical events like player.position
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_NoRequestWhenOnlyFewPlayerPosition)
{
EventQueue eventQueue;
// Pop a first request to get rid of events like game.start
{
eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
}
const uint32 MAX_NON_CRITICAL_EVENTS = 30;
// Until MAX_NON_CRITICAL_EVENTS player.position events, the request should not be sent
for (uint32 i = 0; i < MAX_NON_CRITICAL_EVENTS - 1; i++)
{
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoPlayerPosition(Json().renderContent())));
}
{
ASSERT_EQ(0, eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo()).size());
}
// Reaching MAX_NON_CRITICAL_EVENTS -> The request should be sent
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoPlayerPosition(Json().renderContent())));
{
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
ASSERT_EQ(1, requests.size());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
EXPECT_EQ(MAX_NON_CRITICAL_EVENTS, resultJson["events"].getItems().size());
}
}
/// @test Validates that a request is created if there are only a few non critical events like player.position, but at least one "critical" event is present
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_RequestCreatedWhenAtLeastOneCriticalEventIsInQueue)
{
EventQueue eventQueue;
// Pop a first request to get rid of events like game.start
{
eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
}
// Only a few player.position events -> no request
for (uint32 i = 0; i < 5; i++)
{
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoPlayerPosition(Json().renderContent())));
}
{
ASSERT_EQ(0, eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo()).size());
}
// Adding a "critical" event
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(TestHelpers_BF::getServiceEvent("TestingService")));
{
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
ASSERT_EQ(1, requests.size());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
EXPECT_EQ(6, resultJson["events"].getItems().size());
}
}
/// @test Validates that we can get play time even if it has never been set
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_GetPlayTimeNotSet)
{
ASSERT_EQ(ClockSteady::getNanoFromSeconds(0u), EventQueue().m_playTimeAbsoluteTimePoint)
<< "Play time should be 0 if it has never been set.";
}
/// @test Validates that we can set play time to a given value
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimeSet)
{
const TimeSeconds ABSOLUTE_PLAYTIME = 10ul;
EventQueue eventQueue;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME));
ASSERT_EQ(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME), eventQueue.m_playTimeAbsoluteTimePoint)
<< "Play time should be equal to the value set.";
}
/// @test Validates that the play time clock can be set several times consecutively
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimeReSet)
{
EventQueue eventQueue;
{
const TimeSeconds ABSOLUTE_PLAYTIME_1 = 0ul;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_1));
}
{
const TimeSeconds ABSOLUTE_PLAYTIME_2 = 10ul;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_2));
ASSERT_EQ(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_2), eventQueue.m_playTimeAbsoluteTimePoint);
}
{
const TimeSeconds ABSOLUTE_PLAYTIME_3 = 100ul;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_3));
ASSERT_EQ(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_3), eventQueue.m_playTimeAbsoluteTimePoint);
}
{
const TimeSeconds ABSOLUTE_PLAYTIME_4 = 1000ul;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_4));
ASSERT_EQ(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_4), eventQueue.m_playTimeAbsoluteTimePoint);
}
{
const TimeSeconds ABSOLUTE_PLAYTIME_5 = 10000ul;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_5));
ASSERT_EQ(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_5), eventQueue.m_playTimeAbsoluteTimePoint);
}
{
// Validating that we can set the play time backwards without issue.
const TimeSeconds ABSOLUTE_PLAYTIME_6 = 1000ul;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_6));
ASSERT_EQ(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_6), eventQueue.m_playTimeAbsoluteTimePoint);
}
}
/// @test Validates that the play time clock does not change over time unless directly changed.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimeStableOverTime)
{
const TimeSeconds ABSOLUTE_PLAYTIME = 2000ul;
EventQueue eventQueue;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME));
// Arbitrary but sufficient amount of time for possible instabilities to affect set play time.
US_TEST_NS::Helper::sleep(5000);
ASSERT_EQ(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME), eventQueue.m_playTimeAbsoluteTimePoint)
<< "Play time should remain unchanged and stable over time if it has net been intentionally directly set";
}
/// @test Validates that the play time is stable and accurate over several sets and events.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimePushAbsolute)
{
const TimeSeconds ABSOLUTE_PLAYTIME_1 = 100ul;
const TimeSeconds ABSOLUTE_PLAYTIME_2 = 200ul;
const TimeSeconds ABSOLUTE_PLAYTIME_3 = 200ul;
EventQueue eventQueue;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_1));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom(CUSTOM_EVENT_NAME)));
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
//Play time should not change after request creation
{
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig);
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start", "custom.Testing" }, resultJson));
ASSERT_EQ(ABSOLUTE_PLAYTIME_1, static_cast<TimeSeconds>(resultJson["events"].getItems().at(1)["absolutePlaytime"].getValueInteger()));
}
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_2));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom(CUSTOM_EVENT_NAME)));
{
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig);
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "custom.Testing" }, resultJson));
const TimeSeconds eventPlayTime = static_cast<TimeSeconds>(resultJson["events"].getItems().at(0)["absolutePlaytime"].getValueInteger());
ASSERT_EQ(ABSOLUTE_PLAYTIME_2, eventPlayTime);
}
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_3));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(TestHelpers_BF::getServiceEvent("TestingService")));
{
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig);
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "service.TestingService" }, resultJson));
const TimeSeconds eventPlayTime = static_cast<TimeSeconds>(resultJson["events"].getItems().at(0)["absolutePlaytime"].getValueInteger());
ASSERT_EQ(ABSOLUTE_PLAYTIME_3, eventPlayTime) << "ABSOLUTE_PLAYTIME_3 time should have passed.";
}
}
/// @test Validates that the events pushed have the correct session playTime
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimePush)
{
const TimeSeconds ABSOLUTE_PLAYTIME_1 = 100ul;
const TimeSeconds ABSOLUTE_PLAYTIME_2 = 200ul;
const TimeSeconds ABSOLUTE_PLAYTIME_3 = 300ul;
EventQueue eventQueue;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_1));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom(CUSTOM_EVENT_NAME)));
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
//Play time should not change after request creation
{
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig);
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start", "custom.Testing" }, resultJson));
ASSERT_EQ(0ul, static_cast<TimeSeconds>(resultJson["events"].getItems().at(1)["relativePlaytime"].getValueInteger()));
}
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_2));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoCustom(CUSTOM_EVENT_NAME)));
{
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig);
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "custom.Testing" }, resultJson));
const TimeSeconds eventPlaytime = static_cast<TimeSeconds>(resultJson["events"].getItems().at(0)["relativePlaytime"].getValueInteger());
ASSERT_EQ(ABSOLUTE_PLAYTIME_2 - ABSOLUTE_PLAYTIME_1, eventPlaytime);
}
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_3));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(TestHelpers_BF::getServiceEvent("TestingService")));
{
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, eventConfig);
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "service.TestingService" }, resultJson));
const TimeSeconds eventPlaytime = static_cast<TimeSeconds>(resultJson["events"].getItems().at(0)["relativePlaytime"].getValueInteger());
ASSERT_EQ(ABSOLUTE_PLAYTIME_3 - ABSOLUTE_PLAYTIME_1, eventPlaytime) << "ABSOLUTE_PLAYTIME_2 time should have passed.";
}
}
/// @test Validates that playTime clock doesn't accumulate ticks when returning from the application suspended state.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimeSuspended)
{
const TimeSeconds ABSOLUTE_PLAYTIME_START = 100ul;
const TimeSeconds SUSPENDED_PLAYTIME = 1ul;
const TimeSeconds SESSION_PLAYTIME = 1ul;
EventQueue eventQueue;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_START));
US_TEST_NS::Helper::sleep(SUSPENDED_PLAYTIME * TestEventQueue_BF::s_PLAYTIME_WAIT_ONESECOND);
eventQueue.notifySuspendedAwake(ClockSteady::getNanoFromSeconds(SUSPENDED_PLAYTIME));
ASSERT_EQ(ABSOLUTE_PLAYTIME_START, ClockSteady::getSecondsFromNano(eventQueue.m_playTimeAbsoluteTimePoint))
<< "Play time should have been affected by suspend.";
US_TEST_NS::Helper::sleep((SUSPENDED_PLAYTIME + SESSION_PLAYTIME) * TestEventQueue_BF::s_PLAYTIME_WAIT_ONESECOND);
eventQueue.notifySuspendedAwake(ClockSteady::getNanoFromSeconds(SUSPENDED_PLAYTIME));
ASSERT_EQ(ABSOLUTE_PLAYTIME_START, ClockSteady::getSecondsFromNano(eventQueue.m_playTimeAbsoluteTimePoint))
<< "Awaken. Play time should not be affected.";
}
/// @test Validates the context event has the right playTime duration value.
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimeContextDuration)
{
const EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const TimeSeconds ABSOLUTE_PLAYTIME_1 = 100ul;
const TimeSeconds CONTEXT_DURATION = 3ul;
EventQueue eventQueue;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_1));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
// Arbitrary sleep time value, only to ensure that the playtime duration is not impacted by the actual time.
US_TEST_NS::Helper::sleep(10ul);
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_1 + CONTEXT_DURATION));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start",
String::formatText("context.start.%s", TestEventQueue_BF::s_mockContextType),
String::formatText("context.stop.%s", TestEventQueue_BF::s_mockContextType) }, resultJson));
ASSERT_EQ(CONTEXT_DURATION, resultJson["events"].getItems().at(2)["typeData"]["contextDurationInPlaytime"].getValueInteger())
<< "Contexts starting and stopping should not affect the play time.";
}
/// @test Validates that if the playtime is decremented, the context duration is 0 if the context.start is detected
/// later than the context.stop. (No "negative" value)
/// Note : In this test, the time shift is not sufficient to go back before the absolute playtime start
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimeContextDurationWithLowTimeShift)
{
EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const TimeSeconds ABSOLUTE_PLAYTIME_START = 100ul;
const TimeSeconds TIME_BEFORE_CONTEXT = 10ul;
const TimeSeconds TIME_SHIFT_DURING_CONTEXT = 5ul; // Should be lower than TIME_BEFORE_CONTEXT
EventQueue eventQueue;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_START));
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_START + TIME_BEFORE_CONTEXT));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_START + TIME_BEFORE_CONTEXT - TIME_SHIFT_DURING_CONTEXT));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start",
String::formatText("context.start.%s", TestEventQueue_BF::s_mockContextType),
String::formatText("context.stop.%s", TestEventQueue_BF::s_mockContextType) }, resultJson));
ASSERT_EQ(0u, resultJson["events"].getItems().at(2)["typeData"]["contextDurationInPlaytime"].getValueInteger())
<< "The context playTime duration should be set to 0 if the playtime is decremented during the context.";
ASSERT_EQ(TIME_BEFORE_CONTEXT - TIME_SHIFT_DURING_CONTEXT, resultJson["events"].getItems().at(2)["relativePlaytime"].getValueInteger())
<< "The context.stop relative playTime should still be calculated based on the ABSOLUTE_PLAYTIME_START value.";
}
/// @test Validates that if the playtime is decremented, the context duration is 0 if the context.start is detected
/// later than the context.stop. (No "negative" value)
/// Note : In this test, the time shift is not sufficient to go back before the absolute playtime start
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimeContextDurationWithHighTimeShift)
{
EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const TimeSeconds ABSOLUTE_PLAYTIME_START = 100ul;
const TimeSeconds TIME_BEFORE_CONTEXT = 10ul;
const TimeSeconds TIME_SHIFT_DURING_CONTEXT = 20ul; // Should be higher than TIME_BEFORE_CONTEXT
EventQueue eventQueue;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_START));
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_START + TIME_BEFORE_CONTEXT));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_START + TIME_BEFORE_CONTEXT - TIME_SHIFT_DURING_CONTEXT));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start",
String::formatText("context.start.%s", TestEventQueue_BF::s_mockContextType),
String::formatText("context.stop.%s", TestEventQueue_BF::s_mockContextType) }, resultJson));
ASSERT_EQ(0u, resultJson["events"].getItems().at(2)["typeData"]["contextDurationInPlaytime"].getValueInteger())
<< "The context playTime duration should be set to 0 if the playtime is decremented during the context.";
ASSERT_EQ(0u, resultJson["events"].getItems().at(2)["relativePlaytime"].getValueInteger())
<< "The context playTime duration should be set to 0 if the playtime is decremented to a value below ABSOLUTE_PLAYTIME_START during the context.";
}
/// @test Validates the context playTime duration value doesn't accumulate time while suspended
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimeContextDurationSuspended)
{
EventInfoContextStart contextStartEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
EventInfoContextStop contextStopEvent(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
const TimeSeconds ABSOLUTE_PLAYTIME_1 = 100ul;
const TimeSeconds SUSPENDED_PLAYTIME = 1ul;
const TimeSeconds CONTEXT_PLAYTIME = 20ul;
EventQueue eventQueue;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_1));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent));
eventQueue.notifySuspendedAwake(ClockSteady::getNanoFromSeconds(SUSPENDED_PLAYTIME));
// Arbitrary sleep time value, only to ensure that the playtime duration is not impacted by the actual time.
US_TEST_NS::Helper::sleep(10ul);
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_1 + CONTEXT_PLAYTIME));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStopEvent));
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start",
String::formatText("context.start.%s", TestEventQueue_BF::s_mockContextType),
String::formatText("context.stop.%s", TestEventQueue_BF::s_mockContextType) }, resultJson));
ASSERT_EQ(CONTEXT_PLAYTIME, resultJson["events"].getItems().at(2)["typeData"]["contextDurationInPlaytime"].getValueInteger())
<< "Contexts starting and stopping should affect play time even when they are suspended.";
}
/// @test Validates that pushing an event while the playtime clock isn't set doesn't output
TEST_F(SERVICES_EVENT_QUEUE, LOCAL_PlayTimePushEventNoTimeplay)
{
EventQueue eventQueue;
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoContextStart(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName)));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(EventInfoContextStop(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName)));
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
ASSERT_TRUE(TestEventQueue_BF::validateEvents({ "game.start",
String::formatText("context.start.%s", TestEventQueue_BF::s_mockContextType),
String::formatText("context.stop.%s", TestEventQueue_BF::s_mockContextType) }, resultJson));
const Json contextStop = resultJson["events"].getItems().at(0);
ASSERT_FALSE(contextStop["absolutePlaytime"].isTypeNumber());
ASSERT_FALSE(contextStop["relativePlaytime"].isTypeNumber());
ASSERT_FALSE(contextStop["typeData"]["contextDurationInPlaytime"].isTypeNumber());
}
TEST_F(SERVICES_EVENT_QUEUE, US_TestAbsoluteAndRelativePlaytimeDifferences)
{
const int32 ABSOLUTE_PLAYTIME_REFERENCE_VALUE_1 = 1000u;
const int32 ABSOLUTE_PLAYTIME_REFERENCE_VALUE_2 = 1100u;
const int32 RELATIVE_PLAYTIME_REFERENCE_VALUE = ABSOLUTE_PLAYTIME_REFERENCE_VALUE_2 - ABSOLUTE_PLAYTIME_REFERENCE_VALUE_1;
EventQueue eventQueue;
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_REFERENCE_VALUE_1));
EventInfoContextStart contextStartEvent1(TestEventQueue_BF::s_mockContextType, TestEventQueue_BF::s_mockContextName);
EventInfoContextStart contextStartEvent2("OtherType", TestEventQueue_BF::s_mockContextName);
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent1));
eventQueue.setPlayTime(ClockSteady::getNanoFromSeconds(ABSOLUTE_PLAYTIME_REFERENCE_VALUE_2));
ASSERT_EQ(PushEventResult::Success, eventQueue.pushEvent(contextStartEvent2));
const Vector<EventRequestPtr> requests = eventQueue.popEventRequests(TestEventQueue_BF::s_currentSession, EventConfigInfo());
EventConfigInfo eventConfig;
ASSERT_TRUE(EventConfigInfoPrivate::extractData(EventsConfigMockHelper::getBody(), eventConfig));
const String requestResult = requests[0]->outputHttpBodyFormat(TestEventQueue_BF::s_currentSession, eventConfig).front();
const Json resultJson(requestResult);
if (resultJson["events"].getItemsCount() > 0)
{
EXPECT_EQ(ABSOLUTE_PLAYTIME_REFERENCE_VALUE_1, resultJson["events"].getItems().at(1)["absolutePlaytime"].getValueInteger());
EXPECT_EQ(0u, resultJson["events"].getItems().at(1)["relativePlaytime"].getValueInteger());
EXPECT_EQ(ABSOLUTE_PLAYTIME_REFERENCE_VALUE_2, resultJson["events"].getItems().at(2)["absolutePlaytime"].getValueInteger());
EXPECT_EQ(RELATIVE_PLAYTIME_REFERENCE_VALUE, resultJson["events"].getItems().at(2)["relativePlaytime"].getValueInteger());
}
else
{
ADD_FAILURE() << "Expected a event request valid body: " << requestResult.getUtf8();
}
}
#endif // !defined(US_SDK_S2S)
#endif // US_DLL
};