//////////////////////////////////////////////////////////////////////////////// // (c) Copyright, Ubisoft Entertainment Inc. || Created by Harbour // //////////////////////////////////////////////////////////////////////////////// // TrafficStatistics cannot be accessed in DLL. #include "tests/config.h" #include "tests/fixture/memoryCheckFixture.h" #include "testsUnit/mock/mockHttpEngine.h" #include #include #include #include #include // WARNING: This includes "configpriv.h" #include #include #include #include #include #include namespace US_NS {; US_DEFINE_TEST(TestJobHttpRequest); }; #define DUMMY_URL "dummy" #if !defined(US_DLL) namespace US_NS {; typedef LeakChecker CORE_JOB_HTTP_REQUEST_MOCK; namespace TestJobHttpRequest_BF { void waitForRequestCreation(HttpRequestInternal* request) { const uint32 MAX_SLEEP_ITERATIONS = 50; uint32 currentSleepIteration = 0; while (request == NULL && currentSleepIteration < MAX_SLEEP_ITERATIONS) { Helper::sleep(100); currentSleepIteration++; } EXPECT_LT(currentSleepIteration, MAX_SLEEP_ITERATIONS) << "The request was not created"; } } TEST_F(CORE_JOB_HTTP_REQUEST_MOCK, LOCAL_RequestSuccess) { using namespace testing; MockHttpEngineImpl* engineImpl = MockHttpEngine_BF::getMockHttpEngineInit(1); HttpEngine* engine = US_NEW(HttpEngine, engineImpl); InstancesManager::getInstance()->replaceHttpEngine(engine); HttpConfig httpConfig; engine->initialize(httpConfig); DummyRequest* dummyRequest = nullptr; EXPECT_CALL(*engineImpl, createRequest(_)).WillOnce(MockActionHttpEngine_BF::CreateRequestSetPtr(&dummyRequest)); AsyncResultInternal response(US_WHERE()); SmartPtr stats = US_NEW(TrafficStatistics); SmartPtr facade = US_NEW(FacadeInternal); // No need to retrieve a "real" facadeInternal here response.startTask(US_NEW(JobHttpRequest, response, *facade, HttpGet(DUMMY_URL), HttpRetryHandler(HttpRetryConfig(0u)), stats)); response.wait(250); // Ensure the waiting step has been reached TestJobHttpRequest_BF::waitForRequestCreation(dummyRequest); if (dummyRequest != NULL) { dummyRequest->setToSuccess(); response.wait(); US_EXPECT_SUCCESS(response); US_EXPECT_PROPERTY_WAIT(engine, HttpEngine::getNumberOfRequests, static_cast(0u), US_WAIT_MOCK_PROPERTY_DEFAULT_TIME); } } TEST_F(CORE_JOB_HTTP_REQUEST_MOCK, LOCAL_RequestFailure) { using namespace testing; MockHttpEngineImpl* engineImpl = MockHttpEngine_BF::getMockHttpEngineInit(1); HttpEngine* engine = US_NEW(HttpEngine, engineImpl); InstancesManager::getInstance()->replaceHttpEngine(engine); // Disable retry feature HttpConfig httpConfig; //httpConfig.setMaxRequestRetry(0); engine->initialize(httpConfig); HttpRetryConfig retryConfig(0); DummyRequest* dummyRequest = NULL; EXPECT_CALL(*engineImpl, createRequest(_)).WillOnce(MockActionHttpEngine_BF::CreateRequestSetPtr(&dummyRequest)); AsyncResultInternal response(US_WHERE()); SmartPtr stats = US_NEW(TrafficStatistics); SmartPtr facade = US_NEW(FacadeInternal); // No need to retrieve a "real" facadeInternal here response.startTask(US_NEW(JobHttpRequest, response, *facade, HttpGet(DUMMY_URL), HttpRetryHandler(retryConfig), stats)); response.wait(250); // Ensure the waiting step has been reached TestJobHttpRequest_BF::waitForRequestCreation(dummyRequest); if (dummyRequest != NULL) { HttpRequestError error(Errors::HTTP_REQUEST_TRANSFER_FAILURE, "generic failure"); dummyRequest->setToError(error); response.wait(); US_EXPECT_FAILURE(response, Errors::HTTP_REQUEST_TRANSFER_FAILURE); US_EXPECT_PROPERTY_WAIT(engine, HttpEngine::getNumberOfRequests, static_cast(0u), US_WAIT_MOCK_PROPERTY_DEFAULT_TIME); } } TEST_F(CORE_JOB_HTTP_REQUEST_MOCK, LOCAL_RequestCancel) { using namespace testing; MockHttpEngineImpl* engineImpl = MockHttpEngine_BF::getMockHttpEngineInit(1); HttpEngine* engine = US_NEW(HttpEngine, engineImpl); InstancesManager::getInstance()->replaceHttpEngine(engine); HttpConfig httpConfig; engine->initialize(httpConfig); DummyRequest* dummyRequest = NULL; EXPECT_CALL(*engineImpl, createRequest(_)).WillOnce(MockActionHttpEngine_BF::CreateRequestSetPtr(&dummyRequest)); AsyncResultInternal response(US_WHERE()); SmartPtr stats = US_NEW(TrafficStatistics); SmartPtr facade = US_NEW(FacadeInternal); // No need to retrieve a "real" facadeInternal here response.startTask(US_NEW(JobHttpRequest, response, *facade, HttpGet(DUMMY_URL), HttpRetryHandler(HttpRetryConfig(0u)), stats)); response.wait(250); // Ensure the waiting step has been reached TestJobHttpRequest_BF::waitForRequestCreation(dummyRequest); if (dummyRequest != NULL) { // The context shall be retrieved before the call to cancel // in order to increase its number of reference on the context and avoid its deletion during the cancel. HttpRequestContext requestContext = dummyRequest->getRequestContext(); response.cancel(); US_EXPECT_PROPERTY_WAIT(requestContext, HttpRequestContext::isCanceled, true, US_WAIT_MOCK_PROPERTY_DEFAULT_TIME); US_EXPECT_FAILURE(response, Errors::CANCELED); US_EXPECT_PROPERTY_WAIT(engine, HttpEngine::getNumberOfRequests, static_cast(0u), US_WAIT_MOCK_PROPERTY_DEFAULT_TIME); } } #if !defined(US_TARGET_ANDROID) ///@test Validate that cancelling a request isn't locking the thread if the httpEngine lags somewhere it it's processing. TEST_F(CORE_JOB_HTTP_REQUEST_MOCK, LOCAL_RequestCancelNonBlockingCallingThread) { using namespace testing; class Local { public: Local() : m_invokeCalled(false) {} HttpEngineComponent::State invokeProcessRequestImpl(uint32 contextHandle, HttpRequestContext& request) { US_UNUSED(contextHandle); US_UNUSED(request); m_invokeCalled = true; Helper::sleep(200); return HttpEngineComponent::Finish; } bool isInvokedCalled() const { return m_invokeCalled; } private: volatile bool m_invokeCalled; }; Local testInvoker; MockHttpEngineImpl* engineImpl = MockHttpEngine_BF::getMockHttpEngineInitComponentTest(); HttpEngine* engine = US_NEW(HttpEngine, engineImpl); MockHttpEngineComponent* mockComponent = US_NEW(MockHttpEngineComponent, HttpEngineComponent::Controller); EXPECT_CALL(*engineImpl, configureEngineComponents(_)).WillOnce(MockActionHttpEngine_BF::SetHttpComponent(mockComponent)); // This validate the state of the request, when the components are notified Expectation expectCreate = EXPECT_CALL(*mockComponent, onCreateImpl(Property(&HttpRequestContext::getRequestState, HttpRequestState::Init))).Times(1); // When the mock component return HttpEngineComponent::Finish the request should be completed since this is the only component Expectation expectProcess = EXPECT_CALL(*mockComponent, processRequestImpl(_, Property(&HttpRequestContext::getRequestState, HttpRequestState::Processing))).WillOnce(Invoke(&testInvoker, &Local::invokeProcessRequestImpl)); HttpConfig httpConfig; engine->initialize(httpConfig); InstancesManager::getInstance()->replaceHttpEngine(engine); { const StringUrl dummyUrl = "http://dummy.dummy.dm"; const String dummyBody = "DummyBody DummyBody DummyBody"; SmartPtr stats = US_NEW(TrafficStatistics); AsyncResultInternal response(US_WHERE()); SmartPtr facade = US_NEW(FacadeInternal); // No need to retrieve a "real" facadeInternal here response.startTask(US_NEW(JobHttpRequest, response, *facade, HttpPost(dummyUrl, dummyBody), HttpRetryHandler(HttpRetryConfig(0u)), stats)); US_EXPECT_PROPERTY_WAIT(static_cast(response), AsyncResultBase::isProcessing, true, US_WAIT_MOCK_PROPERTY_DEFAULT_TIME); US_EXPECT_PROPERTY_WAIT(testInvoker, Local::isInvokedCalled, true, US_WAIT_MOCK_PROPERTY_DEFAULT_TIME); HighResolutionChrono chrono; response.cancel(); const ClockSteady::TTimeNano elapsed = chrono.getElapsed(); EXPECT_LT(elapsed, ClockSteady::getNanoFromMilli(1)) << "The cancel of the request should not block the main thread."; } } #endif TEST_F(CORE_JOB_HTTP_REQUEST_MOCK, LOCAL_RequestStatUpdates) { using namespace testing; MockHttpEngineImpl* engineImpl = MockHttpEngine_BF::getMockHttpEngineInit(1); HttpEngine* engine = US_NEW(HttpEngine, engineImpl); InstancesManager::getInstance()->replaceHttpEngine(engine); HttpConfig httpConfig; engine->initialize(httpConfig); DummyRequest* dummyRequest = NULL; EXPECT_CALL(*engineImpl, createRequest(_)).WillOnce(MockActionHttpEngine_BF::CreateRequestSetPtr(&dummyRequest)); AsyncResultInternal response(US_WHERE()); SmartPtr stats = US_NEW(TrafficStatistics); SmartPtr facade = US_NEW(FacadeInternal); // No need to retrieve a "real" facadeInternal here const StringUrl dummyUrl = "http://dummy.dummy.dm"; const String dummyBody = "DummyBody DummyBody DummyBody"; response.startTask(US_NEW(JobHttpRequest, response, *facade, HttpPost(dummyUrl, dummyBody), HttpRetryHandler(HttpRetryConfig(0u)), stats)); response.wait(250); // Ensure the waiting step has been reached TestJobHttpRequest_BF::waitForRequestCreation(dummyRequest); if (dummyRequest != NULL) { HttpRequestContext requestContext = dummyRequest->getRequestContext(); requestContext.getStats().incrementByteSent(dummyBody.getLength()); const uint32 RECEIVED_BODY_LENGTH = 5u; requestContext.getStats().incrementByteReceived(RECEIVED_BODY_LENGTH); dummyRequest->setToSuccess(); response.wait(); US_EXPECT_SUCCESS(response); US_EXPECT_PROPERTY_WAIT(engine, HttpEngine::getNumberOfRequests, static_cast(0u), US_WAIT_MOCK_PROPERTY_DEFAULT_TIME); EXPECT_EQ(1u, stats->getNbRequests(static_cast(HttpTrafficWay::Upload), static_cast(HttpTrafficType::Http))); EXPECT_EQ(dummyUrl.getLength(), stats->getStatistic(static_cast(HttpTrafficWay::Upload), static_cast(HttpTrafficType::Http), static_cast(HttpTrafficStatistic::UrlSize))); EXPECT_EQ(dummyBody.getLength(), stats->getStatistic(static_cast(HttpTrafficWay::Upload), static_cast(HttpTrafficType::Http), static_cast(HttpTrafficStatistic::BodySize))); EXPECT_EQ(RECEIVED_BODY_LENGTH, stats->getStatistic(static_cast(HttpTrafficWay::Download), static_cast(HttpTrafficType::Http), static_cast(HttpTrafficStatistic::BodySize))); } } }; #endif