// Copyright Epic Games, Inc. All Rights Reserved. #include "Misc/AutomationTest.h" #include "Tests/TestHelpers.h" #include "Tests/Fake/HttpManager.fake.h" #include "Tests/Mock/FileSystem.mock.h" #include "Tests/Mock/DownloadServiceStat.mock.h" #include "Tests/Mock/InstallerAnalytics.mock.h" #include "Installer/DownloadService.h" #include "Containers/Ticker.h" #include "BuildPatchHash.h" #if WITH_DEV_AUTOMATION_TESTS BEGIN_DEFINE_SPEC(FDownloadServiceSpec, "BuildPatchServices.Unit", EAutomationTestFlags::ProductFilter | EAutomationTestFlags_ApplicationContextMask) // Unit. TUniquePtr DownloadService; // Mock/Fake. FTSTicker Ticker; TUniquePtr FakeHttpModule; TUniquePtr MockFileSystem; TUniquePtr MockDownloadServiceStat; TUniquePtr MockInstallerAnalytics; // Data. BuildPatchServices::FDownloadProgressDelegate DownloadProgress; BuildPatchServices::FDownloadCompleteDelegate DownloadComplete; TArray> RxDownloadProgress; TArray> RxDownloadComplete; FString HttpFileUrl; FString HttpsFileUrl; FString NetworkFileUrl; int32 MadeRequestId; // Ticker helpers. void DoTick(); void DoTicksUntilCreated(int32 TickCount = 50, int32 CreateCount = 1, float Sleep = 0.0f); void DoTicksUntilComplete(int32 TickCount = 50, int32 CompleteCount = 1, float Sleep = 0.0f); END_DEFINE_SPEC(FDownloadServiceSpec) void FDownloadServiceSpec::Define() { using namespace BuildPatchServices; // Data setup. FRollingHashConst::Init(); DownloadProgress.BindLambda([this](int32 RequestId, int32 BytesSoFar) { RxDownloadProgress.Emplace(FStatsCollector::GetSeconds(), RequestId, BytesSoFar); }); DownloadComplete.BindLambda([this](int32 RequestId, const FDownloadRef& Download) { RxDownloadComplete.Emplace(FStatsCollector::GetSeconds(), RequestId, Download); }); HttpFileUrl = TEXT("http://download.tests.com/file.dat"); HttpsFileUrl = TEXT("https://download.tests.com/file.dat"); NetworkFileUrl = TEXT("\\\\somenetwork\\somefolder\\file.dat"); // Specs. BeforeEach([this]() { Ticker.Reset(); FakeHttpModule.Reset(new FFakeHttpManager(Ticker)); MockFileSystem.Reset(new FMockFileSystem()); MockDownloadServiceStat.Reset(new FMockDownloadServiceStat()); MockInstallerAnalytics.Reset(new FMockInstallerAnalytics()); DownloadService.Reset(FDownloadServiceFactory::Create( FakeHttpModule.Get(), MockFileSystem.Get(), MockDownloadServiceStat.Get(), MockInstallerAnalytics.Get())); }); xDescribe("DownloadService", [this]() { Describe("RequestFile", [this]() { Describe("when given a FileUri starting with http", [this]() { It("should use the http module to process the request.", [this]() { DownloadService->RequestFile(HttpFileUrl, DownloadComplete, DownloadProgress); DoTicksUntilComplete(); TEST_EQUAL(FakeHttpModule->RxCreateRequest, 1); TEST_EQUAL(MockFileSystem->RxCreateFileReader.Num(), 0); }); }); Describe("when given a FileUri starting with https", [this]() { It("should use the http module to process the request.", [this]() { DownloadService->RequestFile(HttpsFileUrl, DownloadComplete, DownloadProgress); DoTicksUntilComplete(); TEST_EQUAL(FakeHttpModule->RxCreateRequest, 1); TEST_EQUAL(MockFileSystem->RxCreateFileReader.Num(), 0); }); }); Describe("when given a FileUri not starting with http", [this]() { xIt("should use the file manager to process the request.", [this]() { DownloadService->RequestFile(NetworkFileUrl, DownloadComplete, DownloadProgress); DoTicksUntilComplete(50, 1); TEST_EQUAL(FakeHttpModule->RxCreateRequest, 0); TEST_EQUAL(MockFileSystem->RxCreateFileReader.Num(), 1); }); }); Describe("when the http request results in success", [this]() { BeforeEach([this]() { FakeHttpModule->DataServed.Add(HttpFileUrl, {1,2,3,4,5,6,7,8,9,10}); }); It("should provide an IDownload with access to success status.", [this]() { DownloadService->RequestFile(HttpFileUrl, DownloadComplete, DownloadProgress); DoTicksUntilComplete(); TEST_EQUAL(RxDownloadComplete.Num(), 1); if (RxDownloadComplete.Num() == 1) { const FDownloadRef& Download = RxDownloadComplete[0].Get<2>(); TEST_TRUE(Download->ResponseSuccessful()); TEST_EQUAL(Download->GetResponseCode(), EHttpResponseCodes::Ok); TEST_EQUAL(Download->GetData(), FakeHttpModule->DataServed[HttpFileUrl]); } }); }); Describe("when the file request results in success", [this]() { BeforeEach([this]() { MockFileSystem->ReadFile = {1,2,3,4,5,6,7,8,9,10}; }); xIt("should provide an IDownload with access to success status.", [this]() { DownloadService->RequestFile(NetworkFileUrl, DownloadComplete, DownloadProgress); DoTicksUntilComplete(); TEST_EQUAL(RxDownloadComplete.Num(), 1); if (RxDownloadComplete.Num() == 1) { const FDownloadRef& Download = RxDownloadComplete[0].Get<2>(); TEST_TRUE(Download->ResponseSuccessful()); TEST_EQUAL(Download->GetResponseCode(), EHttpResponseCodes::Ok); TEST_EQUAL(Download->GetData(), MockFileSystem->ReadFile); } }); }); }); Describe("RequestCancel", [this]() { Describe("when a file request was made", [this]() { BeforeEach([this]() { MockFileSystem->ReadFile.AddUninitialized(1024 * 1024 * 50); MadeRequestId = DownloadService->RequestFile(NetworkFileUrl, DownloadComplete, DownloadProgress); }); It("should cancel if it was not started yet.", [this]() { DownloadService->RequestCancel(MadeRequestId); DoTicksUntilComplete(); TEST_EQUAL(FakeHttpModule->RxCreateRequest, 0); TEST_EQUAL(MockFileSystem->RxCreateFileReader.Num(), 0); TEST_EQUAL(RxDownloadComplete.Num(), 1); if (RxDownloadComplete.Num() == 1) { const FDownloadRef& Download = RxDownloadComplete[0].Get<2>(); TEST_FALSE(Download->ResponseSuccessful()); } }); xIt("should cancel if it had already started.", [this]() { DoTicksUntilCreated(); DownloadService->RequestCancel(MadeRequestId); DoTicksUntilComplete(50, 1, 0.1f); TEST_EQUAL(FakeHttpModule->RxCreateRequest, 0); TEST_EQUAL(MockFileSystem->RxCreateFileReader.Num(), 1); TEST_EQUAL(RxDownloadComplete.Num(), 1); if (RxDownloadComplete.Num() == 1) { const FDownloadRef& Download = RxDownloadComplete[0].Get<2>(); TEST_FALSE(Download->ResponseSuccessful()); } }); }); Describe("when a HTTP request was made", [this]() { BeforeEach([this]() { FakeHttpModule->DataServed.FindOrAdd(HttpFileUrl).AddUninitialized(1024*1024*50); MadeRequestId = DownloadService->RequestFile(HttpFileUrl, DownloadComplete, DownloadProgress); }); It("should cancel if it was not started yet.", [this]() { DownloadService->RequestCancel(MadeRequestId); DoTicksUntilComplete(); TEST_EQUAL(FakeHttpModule->RxCreateRequest, 0); TEST_EQUAL(MockFileSystem->RxCreateFileReader.Num(), 0); TEST_EQUAL(RxDownloadComplete.Num(), 1); if (RxDownloadComplete.Num() == 1) { const FDownloadRef& Download = RxDownloadComplete[0].Get<2>(); TEST_FALSE(Download->ResponseSuccessful()); } }); It("should cancel if it had already started.", [this]() { DoTicksUntilCreated(); DownloadService->RequestCancel(MadeRequestId); DoTicksUntilComplete(); TEST_EQUAL(FakeHttpModule->RxCreateRequest, 1); TEST_EQUAL(MockFileSystem->RxCreateFileReader.Num(), 0); TEST_EQUAL(RxDownloadComplete.Num(), 1); if (RxDownloadComplete.Num() == 1) { const FDownloadRef& Download = RxDownloadComplete[0].Get<2>(); TEST_FALSE(Download->ResponseSuccessful()); } }); }); }); Describe("Destructor", [this]() { Describe("when there are currently active http requests", [this]() { It("should cancel the request.", [this]() { int32 RequestId = DownloadService->RequestFile(HttpFileUrl, DownloadComplete, DownloadProgress); DoTicksUntilCreated(); DownloadService.Reset(); DoTicksUntilComplete(); TEST_EQUAL(FakeHttpModule->RxCreateRequest, 1); TEST_EQUAL(MockFileSystem->RxCreateFileReader.Num(), 0); TEST_EQUAL(RxDownloadComplete.Num(), 1); if (RxDownloadComplete.Num() == 1) { const FDownloadRef& Download = RxDownloadComplete[0].Get<2>(); TEST_FALSE(Download->ResponseSuccessful()); } }); }); }); }); AfterEach([this]() { DownloadService.Reset(); RxDownloadProgress.Reset(); RxDownloadComplete.Reset(); FakeHttpModule.Reset(); MockFileSystem.Reset(); MockDownloadServiceStat.Reset(); MockInstallerAnalytics.Reset(); }); } void FDownloadServiceSpec::DoTick() { Ticker.Tick(0.1f); } void FDownloadServiceSpec::DoTicksUntilComplete(int32 TickCount, int32 CompleteCount, float Sleep) { while (TickCount > 0 && RxDownloadComplete.Num() < CompleteCount) { DoTick(); --TickCount; FPlatformProcess::Sleep(Sleep); } } void FDownloadServiceSpec::DoTicksUntilCreated(int32 TickCount, int32 CreateCount, float Sleep) { MockFileSystem->ThreadLock.Lock(); while (TickCount > 0 && (FakeHttpModule->RxCreateRequest + MockFileSystem->RxCreateFileReader.Num()) < CreateCount) { MockFileSystem->ThreadLock.Unlock(); DoTick(); --TickCount; FPlatformProcess::Sleep(Sleep); MockFileSystem->ThreadLock.Lock(); } MockFileSystem->ThreadLock.Unlock(); } #endif //WITH_DEV_AUTOMATION_TESTS