You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown nick.penwarden ========================== MAJOR FEATURES + CHANGES ========================== Change 2806454 on 2015/12/16 by Bob.Tellez #UE4 Getting crash reporting working again on linux servers. Since -Unattended is now being passed BEFORE the target folder, the cmd line parsing code was failing so now it parses tokens and switches in a more general way. Also, diagnostics.txt had the incorrect case, since the d is supposed to be capitolized and the crash report processor is case sensitive. #rb Ben.Zeigler #codereview Dmitry.Rekman Change 2805502 on 2015/12/16 by Ben.Zeigler #UE4 Move ValidateEnumProperties into ValidateGeneratedClass, it was happening too early in the generation process so was being called at an invalid time. As a result of this ValidateEnumProperties will not be called correctly for compile on load blueprints, that issue is covered in UE-24569 #codereview mike.beach, bob.tellez Change 2805288 on 2015/12/16 by David.Nikdel #HTTP #HttpRetry - Add new Failed_ConnectionError code to EHttpRequestStatus to distinguish between connection errors and protocol errors. - Changed HTTP retry logic a little bit * If a response was received, retry on service-specific explicit HTTP codes (defaults to empty) * If a response was not received and we did not send a full request, automatically retry * If a response was not received and a request may have been sent, retry if the verb is GET or HEAD (should be idempotent) - Adjusted Curl/IOS/Mac/PS4/WinInet to try and distinguish Failed_ConnectionError where possible * Other systems will default to Failed which is ok (ConnectionError is an opportunistic categorization) * Opened a PS4 ticket to try to improve detection, but unfortunately there's no way (currently) to distinguish between send timeout, connection timeout, and receive timeout, the latter being the problematic case. - Removed the concept of global/default HTTP retry status codes. No system has enough knowledge to set those globally. * Individual requests still specify explicit "retryable" codes and McpServiceBase sets that on each request on a per-service basis #RB: Sam.Zamani #CodeReview: Sam.Zamani, Josh.Markiewicz, Alex.Fennell, Dmitry.Rekman, Sam.Spiro #Fixes: FORT-17804 Change 2803864 on 2015/12/15 by Bob.Tellez #UE4 Changed usage of !UE_SERVER to !IsRunningDedicatedServer in cases where we are preventing load attempts on UFonts. This is so running an editor build with -server works the same as running a cooked server. #rb Dmitry.Rekman #codereview Nick.Darnell Change 2803677 on 2015/12/15 by Billy.Bramer - Expose equality and inequality operators for gameplay attributes #rb Todd.Eckert Change 2802881 on 2015/12/14 by Bob.Tellez #UE4 InheritableComponentHandler no longer keeps records for components that we are no longer inheriting. #rb Phillip.Kavan, Maciej.Mroz #codereview Phillip.Kavan, Maciej.Mroz Change 2801636 on 2015/12/14 by Bob.Tellez #UE4 Returning package insert order for non-imports back to being after those of matching priorities unconditionally since this is what you want even when you are not using the asset registry to preload packages. #codereview Graeme.Thornton Change 2800400 on 2015/12/11 by Jonathan.Lindquist Submitting a new Pivot Painter Edition - now renders to textures - improved workflow - greater capabilities Change 2799579 on 2015/12/11 by John.Abercrombie [AUTOMERGE] Fixed EQS BP query wrappers getting GCed before wrapped query finishes #UE4 Fixes FORT-18649 - Patrols don't spawn consistently - The patrol blueprint was waiting (endlessly) for an EQS query to finish but because the wrapper could be GC-ed while the EQS query was running the delegate would never fire #rb me (this code was written by MieszkoZ) (removed code review for integration of Mieszko.Zielinski, Phil.Cole, Dominic.Barile) -------- Integrated using branch UE4-Fortnite-To-UE4-FortniteReleases/0.10 (reversed) of change#2799575 by John.Abercrombie on 2015/12/11 09:55:11. Change 2799018 on 2015/12/10 by Bob.Tellez #UE4 The asset registry tags stripped from cooked builds is now a blacklist by default that includes only the FiB tag. You can opt-in to using the whitelist by flipping the bUseAssetRegistryTagsWhitelistInsteadOfBlacklist flag. #rb Fred.Kimberley #codereview Peter.Knepley Change 2798926 on 2015/12/10 by Bob.Tellez #UE4 Removed some showflags from the list of "Fixed" showflags since they were actually in use at runtime in Fortnite in a scene capture. #jira FORT-18514 #codereview Martin.Mittring Change 2797758 on 2015/12/10 by Mark.Satterthwaite Defer calls to AUGraphUpdate into FCoreAudioDevice::UpdateHardware - this call will synchronise the calling thread with the CoreAudio thread/run-loop so that the CoreAudio graph is safe to modify and this may incur a significant stall. This means it is far more efficient to amortise the cost of all changes to the graph with a single call. To ensure correctness the audio format conversion components are cached and disposed of after the call to AUGraphUpdate so that any existing operations on the CoreAudio thread are completed prior to disposal. Change 2781204 on 2015/11/25 by Dmitry.Rekman Linux: use jemalloc by default if available. - Alleviates one of the reasons for player disconnect (FORT-18048), which was machines running OOM. #rb Bob.Tellez #codereview Bob.Tellez, Ben.Zeigler Change 2779398 on 2015/11/24 by Mark.Satterthwaite Switch the default graphics API on Mac back to OpenGL, but allow Metal to run with -metal (or -metalsm5 for experimental SM5 support).
798 lines
26 KiB
C++
798 lines
26 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AutomationControllerPrivatePCH.h"
|
|
|
|
#if WITH_EDITOR
|
|
#include "MessageLog.h"
|
|
#endif
|
|
|
|
namespace AutomationControllerConstants
|
|
{
|
|
const FString HistoryConfigSectionName = TEXT("AutomationController.History");
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::RequestAvailableWorkers( const FGuid& SessionId )
|
|
{
|
|
//invalidate previous tests
|
|
++ExecutionCount;
|
|
DeviceClusterManager.Reset();
|
|
|
|
ControllerResetDelegate.ExecuteIfBound();
|
|
|
|
// Don't allow reports to be exported
|
|
bTestResultsAvailable = false;
|
|
|
|
//store off active session ID to reject messages that come in from different sessions
|
|
ActiveSessionId = SessionId;
|
|
|
|
//TODO AUTOMATION - include change list, game, etc, or remove when launcher is integrated
|
|
int32 ChangelistNumber = 10000;
|
|
FString ProcessName = TEXT( "instance_name" );
|
|
|
|
MessageEndpoint->Publish(new FAutomationWorkerFindWorkers(ChangelistNumber, FApp::GetGameName(), ProcessName, SessionId), EMessageScope::Network);
|
|
|
|
// Reset the check test timers
|
|
LastTimeUpdateTicked = FPlatformTime::Seconds();
|
|
CheckTestTimer = 0.f;
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::RequestTests()
|
|
{
|
|
//invalidate incoming results
|
|
ExecutionCount++;
|
|
//reset the number of responses we have received
|
|
RefreshTestResponses = 0;
|
|
|
|
ReportManager.Empty();
|
|
|
|
for (int32 ClusterIndex = 0; ClusterIndex < DeviceClusterManager.GetNumClusters(); ++ClusterIndex)
|
|
{
|
|
int32 DevicesInCluster = DeviceClusterManager.GetNumDevicesInCluster(ClusterIndex);
|
|
if (DevicesInCluster > 0)
|
|
{
|
|
FMessageAddress MessageAddress = DeviceClusterManager.GetDeviceMessageAddress(ClusterIndex, 0);
|
|
|
|
ResetIntermediateTestData();
|
|
|
|
//issue tests on appropriate platforms
|
|
MessageEndpoint->Send(new FAutomationWorkerRequestTests(bDeveloperDirectoryIncluded, RequestedTestFlags), MessageAddress);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::RunTests( const bool bInIsLocalSession )
|
|
{
|
|
ExecutionCount++;
|
|
CurrentTestPass = 0;
|
|
ReportManager.SetCurrentTestPass(CurrentTestPass);
|
|
ClusterDistributionMask = 0;
|
|
bTestResultsAvailable = false;
|
|
TestRunningArray.Empty();
|
|
bIsLocalSession = bInIsLocalSession;
|
|
|
|
// Reset the check test timers
|
|
LastTimeUpdateTicked = FPlatformTime::Seconds();
|
|
CheckTestTimer = 0.f;
|
|
|
|
#if WITH_EDITOR
|
|
FMessageLog AutomationTestingLog("AutomationTestingLog");
|
|
FString NewPageName = FString::Printf(TEXT("-----Test Run %d----"), ExecutionCount);
|
|
FText NewPageNameText = FText::FromString(*NewPageName);
|
|
AutomationTestingLog.Open();
|
|
AutomationTestingLog.NewPage(NewPageNameText);
|
|
AutomationTestingLog.Info(NewPageNameText);
|
|
#endif
|
|
//reset all tests
|
|
ReportManager.ResetForExecution(NumTestPasses);
|
|
|
|
for (int32 ClusterIndex = 0; ClusterIndex < DeviceClusterManager.GetNumClusters(); ++ClusterIndex)
|
|
{
|
|
//enable each device cluster
|
|
ClusterDistributionMask |= (1<<ClusterIndex);
|
|
|
|
//for each device in this cluster
|
|
for (int32 DeviceIndex = 0; DeviceIndex < DeviceClusterManager.GetNumDevicesInCluster(ClusterIndex); ++DeviceIndex)
|
|
{
|
|
//mark the device as idle
|
|
DeviceClusterManager.SetTest(ClusterIndex, DeviceIndex, NULL);
|
|
|
|
// Send command to reset tests (delete local files, etc)
|
|
FMessageAddress MessageAddress = DeviceClusterManager.GetDeviceMessageAddress(ClusterIndex, DeviceIndex);
|
|
MessageEndpoint->Send(new FAutomationWorkerResetTests(), MessageAddress);
|
|
}
|
|
}
|
|
|
|
// Inform the UI we are running tests
|
|
if (ClusterDistributionMask != 0)
|
|
{
|
|
SetControllerStatus( EAutomationControllerModuleState::Running );
|
|
}
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::StopTests()
|
|
{
|
|
bTestResultsAvailable = false;
|
|
ClusterDistributionMask = 0;
|
|
|
|
ReportManager.StopRunningTests();
|
|
|
|
// Inform the UI we have stopped running tests
|
|
if (DeviceClusterManager.HasActiveDevice())
|
|
{
|
|
SetControllerStatus( EAutomationControllerModuleState::Ready );
|
|
}
|
|
else
|
|
{
|
|
SetControllerStatus( EAutomationControllerModuleState::Disabled );
|
|
}
|
|
|
|
TestRunningArray.Empty();
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::Init()
|
|
{
|
|
AutomationTestState = EAutomationControllerModuleState::Disabled;
|
|
bTestResultsAvailable = false;
|
|
bScreenshotsEnabled = true;
|
|
bRequestFullScreenScreenshots = false;
|
|
bSendAnalytics = FParse::Param(FCommandLine::Get(), TEXT("SendAutomationAnalytics"));
|
|
|
|
// Update the ini with the settings
|
|
bTrackHistory = false;
|
|
GConfig->GetBool(*AutomationControllerConstants::HistoryConfigSectionName, TEXT("bTrackHistory"), bTrackHistory, GEngineIni);
|
|
|
|
// Default num of items to track
|
|
NumberOfHistoryItemsTracked = 5;
|
|
GConfig->GetInt(*AutomationControllerConstants::HistoryConfigSectionName, TEXT("NumberOfHistoryItemsTracked"), NumberOfHistoryItemsTracked, GEngineIni);
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::RequestLoadAsset( const FString& InAssetName )
|
|
{
|
|
MessageEndpoint->Publish(new FAssetEditorRequestOpenAsset(InAssetName), EMessageScope::Process);
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::Tick()
|
|
{
|
|
ProcessAvailableTasks();
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::ProcessAvailableTasks()
|
|
{
|
|
// Distribute tasks
|
|
if( ClusterDistributionMask != 0)
|
|
{
|
|
// For each device cluster
|
|
for( int32 ClusterIndex = 0; ClusterIndex < DeviceClusterManager.GetNumClusters(); ++ClusterIndex )
|
|
{
|
|
bool bAllTestsComplete = true;
|
|
|
|
// If any of the devices were valid
|
|
if( ( ClusterDistributionMask & ( 1<< ClusterIndex ) ) && DeviceClusterManager.GetNumDevicesInCluster( ClusterIndex ) > 0 )
|
|
{
|
|
ExecuteNextTask( ClusterIndex, bAllTestsComplete );
|
|
}
|
|
|
|
//if we're all done running our tests
|
|
if ( bAllTestsComplete )
|
|
{
|
|
//we don't need to test this cluster anymore
|
|
ClusterDistributionMask &= ~( 1<<ClusterIndex );
|
|
|
|
if ( ClusterDistributionMask == 0 )
|
|
{
|
|
ProcessResults();
|
|
//Notify the graphical layout we are done processing results.
|
|
TestsCompleteDelegate.ExecuteIfBound();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bIsLocalSession == false)
|
|
{
|
|
// Update the test status for timeouts if this is not a local session
|
|
UpdateTests();
|
|
}
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::ExecuteNextTask( int32 ClusterIndex, OUT bool& bAllTestsCompleted )
|
|
{
|
|
bool bTestThatRequiresMultiplePraticipantsHadEnoughParticipants = false;
|
|
TArray< IAutomationReportPtr > TestsRunThisPass;
|
|
|
|
// For each device in this cluster
|
|
int32 NumDevicesInCluster = DeviceClusterManager.GetNumDevicesInCluster( ClusterIndex );
|
|
for( int32 DeviceIndex = 0; DeviceIndex < NumDevicesInCluster; ++DeviceIndex )
|
|
{
|
|
// If this device is idle
|
|
if( !DeviceClusterManager.GetTest( ClusterIndex, DeviceIndex ).IsValid() && DeviceClusterManager.DeviceEnabled( ClusterIndex, DeviceIndex ) )
|
|
{
|
|
// Get the next test that should be worked on
|
|
TSharedPtr< IAutomationReport > NextTest = ReportManager.GetNextReportToExecute( bAllTestsCompleted, ClusterIndex,CurrentTestPass, NumDevicesInCluster );
|
|
if( NextTest.IsValid() )
|
|
{
|
|
// Get the status of the test
|
|
EAutomationState::Type TestState = NextTest->GetState( ClusterIndex,CurrentTestPass );
|
|
if( TestState == EAutomationState::NotRun )
|
|
{
|
|
// Reserve this device for the test
|
|
DeviceClusterManager.SetTest( ClusterIndex, DeviceIndex, NextTest );
|
|
TestsRunThisPass.Add( NextTest );
|
|
|
|
// If we now have enough devices reserved for the test, run it!
|
|
TArray<FMessageAddress> DeviceAddresses = DeviceClusterManager.GetDevicesReservedForTest( ClusterIndex, NextTest );
|
|
if( DeviceAddresses.Num() == NextTest->GetNumParticipantsRequired() )
|
|
{
|
|
// Send it to each device
|
|
for (int32 AddressIndex = 0; AddressIndex < DeviceAddresses.Num(); ++AddressIndex)
|
|
{
|
|
FAutomationTestResults TestResults;
|
|
|
|
GLog->Logf(ELogVerbosity::Display, TEXT("Running Automation: '%s' (Class Name: '%s')"), *TestsRunThisPass[AddressIndex]->GetDisplayName(), *TestsRunThisPass[AddressIndex]->GetCommand());
|
|
TestResults.State = EAutomationState::InProcess;
|
|
|
|
TestResults.GameInstance = DeviceClusterManager.GetClusterDeviceName( ClusterIndex, DeviceIndex );
|
|
NextTest->SetResults( ClusterIndex,CurrentTestPass, TestResults );
|
|
NextTest->ResetNetworkCommandResponses();
|
|
|
|
// Mark the device as busy
|
|
FMessageAddress DeviceAddress = DeviceAddresses[AddressIndex];
|
|
|
|
// Send the test to the device for execution!
|
|
MessageEndpoint->Send(new FAutomationWorkerRunTests(ExecutionCount, AddressIndex, NextTest->GetCommand(), NextTest->GetDisplayName(), bScreenshotsEnabled, bRequestFullScreenScreenshots, bSendAnalytics), DeviceAddress);
|
|
|
|
// Add a test so we can check later if the device is still active
|
|
TestRunningArray.Add( FTestRunningInfo( DeviceAddress ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// At least one device is still working
|
|
bAllTestsCompleted = false;
|
|
}
|
|
}
|
|
|
|
// Ensure any tests we have attempted to run on this pass had enough participants to successfully run.
|
|
for( int32 TestIndex = 0; TestIndex < TestsRunThisPass.Num(); TestIndex++ )
|
|
{
|
|
IAutomationReportPtr CurrentTest = TestsRunThisPass[ TestIndex ];
|
|
|
|
if( CurrentTest->GetNumDevicesRunningTest() != CurrentTest->GetNumParticipantsRequired() )
|
|
{
|
|
if( GetNumDevicesInCluster( ClusterIndex ) < CurrentTest->GetNumParticipantsRequired() )
|
|
{
|
|
float EmptyDuration = 0.0f;
|
|
TArray<FString> EmptyStringArray;
|
|
TArray<FString> AutomationsWarnings;
|
|
AutomationsWarnings.Add( FString::Printf( TEXT( "Needed %d devices to participate, Only had %d available." ), CurrentTest->GetNumParticipantsRequired(), DeviceClusterManager.GetNumDevicesInCluster( ClusterIndex ) ) );
|
|
|
|
FAutomationTestResults TestResults;
|
|
TestResults.State = EAutomationState::NotEnoughParticipants;
|
|
TestResults.GameInstance = DeviceClusterManager.GetClusterDeviceName(ClusterIndex, 0);
|
|
TestResults.Warnings.Append( AutomationsWarnings );
|
|
|
|
CurrentTest->SetResults( ClusterIndex,CurrentTestPass, TestResults );
|
|
DeviceClusterManager.ResetAllDevicesRunningTest( ClusterIndex, CurrentTest );
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check to see if we finished a pass
|
|
if( bAllTestsCompleted && CurrentTestPass < NumTestPasses - 1 )
|
|
{
|
|
CurrentTestPass++;
|
|
ReportManager.SetCurrentTestPass(CurrentTestPass);
|
|
bAllTestsCompleted = false;
|
|
}
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::Startup()
|
|
{
|
|
MessageEndpoint = FMessageEndpoint::Builder("FAutomationControllerModule")
|
|
.Handling<FAutomationWorkerFindWorkersResponse>(this, &FAutomationControllerManager::HandleFindWorkersResponseMessage)
|
|
.Handling<FAutomationWorkerPong>(this, &FAutomationControllerManager::HandlePongMessage)
|
|
.Handling<FAutomationWorkerRequestNextNetworkCommand>(this, &FAutomationControllerManager::HandleRequestNextNetworkCommandMessage)
|
|
.Handling<FAutomationWorkerRequestTestsReply>(this, &FAutomationControllerManager::HandleRequestTestsReplyMessage)
|
|
.Handling<FAutomationWorkerRequestTestsReplyComplete>(this, &FAutomationControllerManager::HandleRequestTestsReplyCompleteMessage)
|
|
.Handling<FAutomationWorkerRunTestsReply>(this, &FAutomationControllerManager::HandleRunTestsReplyMessage)
|
|
.Handling<FAutomationWorkerScreenImage>(this, &FAutomationControllerManager::HandleReceivedScreenShot)
|
|
.Handling<FAutomationWorkerWorkerOffline>(this, &FAutomationControllerManager::HandleWorkerOfflineMessage);
|
|
|
|
if (MessageEndpoint.IsValid())
|
|
{
|
|
MessageEndpoint->Subscribe<FAutomationWorkerWorkerOffline>();
|
|
}
|
|
|
|
ClusterDistributionMask = 0;
|
|
ExecutionCount = 0;
|
|
bDeveloperDirectoryIncluded = false;
|
|
RequestedTestFlags = EAutomationTestFlags::SmokeFilter | EAutomationTestFlags::EngineFilter | EAutomationTestFlags::ProductFilter | EAutomationTestFlags::PerfFilter;
|
|
|
|
NumOfTestsToReceive = 0;
|
|
NumTestPasses = 1;
|
|
|
|
//Default to machine name
|
|
DeviceGroupFlags = 0;
|
|
ToggleDeviceGroupFlag(EAutomationDeviceGroupTypes::MachineName);
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::Shutdown()
|
|
{
|
|
MessageEndpoint.Reset();
|
|
ShutdownDelegate.ExecuteIfBound();
|
|
RemoveCallbacks();
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::RemoveCallbacks()
|
|
{
|
|
ShutdownDelegate.Unbind();
|
|
TestsAvailableDelegate.Unbind();
|
|
TestsRefreshedDelegate.Unbind();
|
|
TestsCompleteDelegate.Unbind();
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::SetTestNames( const FMessageAddress& AutomationWorkerAddress )
|
|
{
|
|
int32 DeviceClusterIndex = INDEX_NONE;
|
|
int32 DeviceIndex = INDEX_NONE;
|
|
|
|
// Find the device that requested these tests
|
|
if( DeviceClusterManager.FindDevice( AutomationWorkerAddress, DeviceClusterIndex, DeviceIndex ) )
|
|
{
|
|
// Sort tests by display name
|
|
struct FCompareAutomationTestInfo
|
|
{
|
|
FORCEINLINE bool operator()(const FAutomationTestInfo& A,const FAutomationTestInfo& B) const
|
|
{
|
|
return A.GetDisplayName() < B.GetDisplayName();
|
|
}
|
|
};
|
|
|
|
TestInfo.Sort(FCompareAutomationTestInfo());
|
|
|
|
// Add each test to the collection
|
|
for( int32 TestIndex = 0; TestIndex < TestInfo.Num(); ++TestIndex )
|
|
{
|
|
// Ensure our test exists. If not, add it
|
|
ReportManager.EnsureReportExists( TestInfo[TestIndex], DeviceClusterIndex, NumTestPasses );
|
|
}
|
|
|
|
// Clear any intermediate data we had associated with the tests whilst building the full list of tests
|
|
ResetIntermediateTestData();
|
|
}
|
|
else
|
|
{
|
|
//todo automation - make sure to report error if the device was not discovered correctly
|
|
}
|
|
|
|
// Note the response
|
|
RefreshTestResponses++;
|
|
|
|
// If we have received all the responses we expect to
|
|
if (RefreshTestResponses == DeviceClusterManager.GetNumClusters())
|
|
{
|
|
TestsRefreshedDelegate.ExecuteIfBound();
|
|
|
|
// Update the tests with tracking details
|
|
ReportManager.TrackHistory(bTrackHistory, NumberOfHistoryItemsTracked);
|
|
}
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::ProcessResults()
|
|
{
|
|
bHasErrors = false;
|
|
bHasWarning = false;
|
|
bHasLogs = false;
|
|
|
|
TArray< TSharedPtr< IAutomationReport > >& TestReports = GetReports();
|
|
|
|
if (TestReports.Num())
|
|
{
|
|
bTestResultsAvailable = true;
|
|
|
|
for (int32 Index = 0; Index < TestReports.Num(); Index++)
|
|
{
|
|
CheckChildResult(TestReports[Index ]);
|
|
}
|
|
}
|
|
|
|
SetControllerStatus( EAutomationControllerModuleState::Ready );
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::CheckChildResult(TSharedPtr<IAutomationReport> InReport)
|
|
{
|
|
TArray<TSharedPtr<IAutomationReport> >& ChildReports = InReport->GetChildReports();
|
|
|
|
if (ChildReports.Num() > 0)
|
|
{
|
|
for (int32 Index = 0; Index < ChildReports.Num(); Index++)
|
|
{
|
|
CheckChildResult(ChildReports[Index]);
|
|
}
|
|
}
|
|
else if ((bHasErrors && bHasWarning && bHasLogs ) == false && InReport->IsEnabled())
|
|
{
|
|
for (int32 ClusterIndex = 0; ClusterIndex < GetNumDeviceClusters(); ++ClusterIndex)
|
|
{
|
|
FAutomationTestResults TestResults = InReport->GetResults( ClusterIndex,CurrentTestPass );
|
|
|
|
if (TestResults.Errors.Num())
|
|
{
|
|
bHasErrors = true;
|
|
}
|
|
if (TestResults.Warnings.Num())
|
|
{
|
|
bHasWarning = true;
|
|
}
|
|
if (TestResults.Logs.Num())
|
|
{
|
|
bHasLogs = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::SetControllerStatus( EAutomationControllerModuleState::Type InAutomationTestState )
|
|
{
|
|
if (InAutomationTestState != AutomationTestState)
|
|
{
|
|
// Inform the UI if the test state has changed
|
|
AutomationTestState = InAutomationTestState;
|
|
TestsAvailableDelegate.ExecuteIfBound(AutomationTestState);
|
|
}
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::RemoveTestRunning( const FMessageAddress& TestAddressToRemove )
|
|
{
|
|
for ( int32 Index = 0; Index < TestRunningArray.Num(); Index++ )
|
|
{
|
|
if ( TestRunningArray[ Index ].OwnerMessageAddress == TestAddressToRemove )
|
|
{
|
|
TestRunningArray.RemoveAt( Index );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::AddPingResult( const FMessageAddress& ResponderAddress )
|
|
{
|
|
for ( int32 Index = 0; Index < TestRunningArray.Num(); Index++ )
|
|
{
|
|
if ( TestRunningArray[ Index ].OwnerMessageAddress == ResponderAddress )
|
|
{
|
|
TestRunningArray[ Index ].LastPingTime = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::UpdateTests( )
|
|
{
|
|
static const float CheckTestInterval = 1.0f;
|
|
static const float GameInstanceLostTimer = 200.0f;
|
|
|
|
CheckTestTimer += FPlatformTime::Seconds() - LastTimeUpdateTicked;
|
|
LastTimeUpdateTicked = FPlatformTime::Seconds();
|
|
if ( CheckTestTimer > CheckTestInterval )
|
|
{
|
|
for ( int32 Index = 0; Index < TestRunningArray.Num(); Index++ )
|
|
{
|
|
TestRunningArray[ Index ].LastPingTime += CheckTestTimer;
|
|
|
|
if ( TestRunningArray[ Index ].LastPingTime > GameInstanceLostTimer )
|
|
{
|
|
// Find the game session instance info
|
|
int32 ClusterIndex;
|
|
int32 DeviceIndex;
|
|
verify( DeviceClusterManager.FindDevice( TestRunningArray[ Index ].OwnerMessageAddress, ClusterIndex, DeviceIndex ) );
|
|
//verify this device thought it was busy
|
|
TSharedPtr <IAutomationReport> Report = DeviceClusterManager.GetTest(ClusterIndex, DeviceIndex);
|
|
check (Report.IsValid());
|
|
// A dummy array used to report the result
|
|
|
|
TArray<FString> EmptyStringArray;
|
|
TArray<FString> ErrorStringArray;
|
|
ErrorStringArray.Add( FString( TEXT( "Failed" ) ) );
|
|
bHasErrors = true;
|
|
GLog->Logf(ELogVerbosity::Display, TEXT("Timeout hit. Nooooooo."));
|
|
|
|
FAutomationTestResults TestResults;
|
|
TestResults.State = EAutomationState::Fail;
|
|
TestResults.GameInstance = DeviceClusterManager.GetClusterDeviceName(ClusterIndex, DeviceIndex);
|
|
|
|
// Set the results
|
|
Report->SetResults(ClusterIndex,CurrentTestPass, TestResults );
|
|
bTestResultsAvailable = true;
|
|
|
|
// Disable the device in the cluster so it is not used again
|
|
DeviceClusterManager.DisableDevice( ClusterIndex, DeviceIndex );
|
|
|
|
// Remove the running test
|
|
TestRunningArray.RemoveAt( Index-- );
|
|
|
|
// If there are no more devices, set the module state to disabled
|
|
if ( DeviceClusterManager.HasActiveDevice() == false )
|
|
{
|
|
GLog->Logf(ELogVerbosity::Display, TEXT("Module disabled"));
|
|
SetControllerStatus( EAutomationControllerModuleState::Disabled );
|
|
ClusterDistributionMask = 0;
|
|
}
|
|
else
|
|
{
|
|
GLog->Logf(ELogVerbosity::Display, TEXT("Module not disabled. Keep looking."));
|
|
// Remove the cluster from the mask if there are no active devices left
|
|
if ( DeviceClusterManager.GetNumActiveDevicesInCluster( ClusterIndex ) == 0 )
|
|
{
|
|
ClusterDistributionMask &= ~( 1<<ClusterIndex );
|
|
}
|
|
if ( TestRunningArray.Num() == 0 )
|
|
{
|
|
SetControllerStatus( EAutomationControllerModuleState::Ready );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MessageEndpoint->Send(new FAutomationWorkerPing(), TestRunningArray[Index].OwnerMessageAddress);
|
|
}
|
|
}
|
|
CheckTestTimer = 0.f;
|
|
}
|
|
}
|
|
|
|
|
|
const bool FAutomationControllerManager::ExportReport( uint32 FileExportTypeMask )
|
|
{
|
|
return ReportManager.ExportReport( FileExportTypeMask, GetNumDeviceClusters() );
|
|
}
|
|
|
|
|
|
bool FAutomationControllerManager::IsTestRunnable( IAutomationReportPtr InReport ) const
|
|
{
|
|
bool bIsRunnable = false;
|
|
|
|
for( int32 ClusterIndex = 0; ClusterIndex < GetNumDeviceClusters(); ++ClusterIndex )
|
|
{
|
|
if( InReport->IsSupported( ClusterIndex ) )
|
|
{
|
|
if( GetNumDevicesInCluster( ClusterIndex ) >= InReport->GetNumParticipantsRequired() )
|
|
{
|
|
bIsRunnable = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bIsRunnable;
|
|
}
|
|
|
|
/* FAutomationControllerModule callbacks
|
|
*****************************************************************************/
|
|
|
|
void FAutomationControllerManager::HandleFindWorkersResponseMessage( const FAutomationWorkerFindWorkersResponse& Message, const IMessageContextRef& Context )
|
|
{
|
|
if (Message.SessionId == ActiveSessionId)
|
|
{
|
|
DeviceClusterManager.AddDeviceFromMessage(Context->GetSender(), Message, DeviceGroupFlags);
|
|
}
|
|
|
|
RequestTests();
|
|
|
|
SetControllerStatus( EAutomationControllerModuleState::Ready );
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::HandlePongMessage( const FAutomationWorkerPong& Message, const IMessageContextRef& Context )
|
|
{
|
|
AddPingResult(Context->GetSender());
|
|
}
|
|
|
|
void FAutomationControllerManager::HandleReceivedScreenShot( const FAutomationWorkerScreenImage& Message, const IMessageContextRef& Context )
|
|
{
|
|
// Forward the screen shot on to listeners
|
|
FAutomationWorkerScreenImage* ImageMessage = new FAutomationWorkerScreenImage( Message );
|
|
MessageEndpoint->Publish(ImageMessage, EMessageScope::Network);
|
|
}
|
|
|
|
void FAutomationControllerManager::HandleRequestNextNetworkCommandMessage( const FAutomationWorkerRequestNextNetworkCommand& Message, const IMessageContextRef& Context )
|
|
{
|
|
// Harvest iteration of running the tests this result came from (stops stale results from being committed to subsequent runs)
|
|
if (Message.ExecutionCount == ExecutionCount)
|
|
{
|
|
// Find the device id for the address
|
|
int32 ClusterIndex;
|
|
int32 DeviceIndex;
|
|
|
|
verify(DeviceClusterManager.FindDevice(Context->GetSender(), ClusterIndex, DeviceIndex));
|
|
|
|
// Verify this device thought it was busy
|
|
TSharedPtr <IAutomationReport> Report = DeviceClusterManager.GetTest(ClusterIndex, DeviceIndex);
|
|
check (Report.IsValid());
|
|
|
|
// Increment network command responses
|
|
bool bAllResponsesReceived = Report->IncrementNetworkCommandResponses();
|
|
|
|
// Test if we've accumulated all responses AND this was the result for the round of test running AND we're still running tests
|
|
if (bAllResponsesReceived && (ClusterDistributionMask & (1<<ClusterIndex)))
|
|
{
|
|
// Reset the counter
|
|
Report->ResetNetworkCommandResponses();
|
|
|
|
// For every device in this networked test
|
|
TArray<FMessageAddress> DeviceAddresses = DeviceClusterManager.GetDevicesReservedForTest(ClusterIndex, Report);
|
|
check (DeviceAddresses.Num() == Report->GetNumParticipantsRequired());
|
|
|
|
// Send it to each device
|
|
for (int32 AddressIndex = 0; AddressIndex < DeviceAddresses.Num(); ++AddressIndex)
|
|
{
|
|
//send "next command message" to worker
|
|
MessageEndpoint->Send(new FAutomationWorkerNextNetworkCommandReply(), DeviceAddresses[AddressIndex]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::HandleRequestTestsReplyMessage(const FAutomationWorkerRequestTestsReply& Message, const IMessageContextRef& Context)
|
|
{
|
|
FAutomationTestInfo NewTest(Message.TestInfo);
|
|
TestInfo.Add(NewTest);
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::HandleRequestTestsReplyCompleteMessage(const FAutomationWorkerRequestTestsReplyComplete& Message, const IMessageContextRef& Context)
|
|
{
|
|
SetTestNames(Context->GetSender());
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::HandleRunTestsReplyMessage( const FAutomationWorkerRunTestsReply& Message, const IMessageContextRef& Context )
|
|
{
|
|
// If we should commit these results
|
|
if (Message.ExecutionCount == ExecutionCount)
|
|
{
|
|
FAutomationTestResults TestResults;
|
|
|
|
TestResults.State = Message.Success ? EAutomationState::Success : EAutomationState::Fail;
|
|
TestResults.Duration = Message.Duration;
|
|
|
|
// Mark device as back on the market
|
|
int32 ClusterIndex;
|
|
int32 DeviceIndex;
|
|
|
|
verify(DeviceClusterManager.FindDevice(Context->GetSender(), ClusterIndex, DeviceIndex));
|
|
|
|
TestResults.GameInstance = DeviceClusterManager.GetClusterDeviceName(ClusterIndex, DeviceIndex);
|
|
TestResults.Errors = Message.Errors;
|
|
TestResults.Logs = Message.Logs;
|
|
TestResults.Warnings = Message.Warnings;
|
|
|
|
// Verify this device thought it was busy
|
|
TSharedPtr <IAutomationReport> Report = DeviceClusterManager.GetTest(ClusterIndex, DeviceIndex);
|
|
check(Report.IsValid());
|
|
|
|
Report->SetResults(ClusterIndex,CurrentTestPass, TestResults);
|
|
|
|
#if WITH_EDITOR
|
|
FMessageLog AutomationTestingLog("AutomationTestingLog");
|
|
AutomationTestingLog.Open();
|
|
#endif
|
|
|
|
for (TArray<FString>::TConstIterator ErrorIter(Message.Errors); ErrorIter; ++ErrorIter)
|
|
{
|
|
GLog->Logf(ELogVerbosity::Error, TEXT("%s"), **ErrorIter);
|
|
#if WITH_EDITOR
|
|
AutomationTestingLog.Error(FText::FromString(*ErrorIter));
|
|
#endif
|
|
}
|
|
for (TArray<FString>::TConstIterator WarningIter(Message.Warnings); WarningIter; ++WarningIter)
|
|
{
|
|
GLog->Logf(ELogVerbosity::Warning, TEXT("%s"), **WarningIter);
|
|
#if WITH_EDITOR
|
|
AutomationTestingLog.Warning(FText::FromString(*WarningIter));
|
|
#endif
|
|
}
|
|
for (TArray<FString>::TConstIterator LogItemIter(Message.Logs); LogItemIter; ++LogItemIter)
|
|
{
|
|
GLog->Logf(ELogVerbosity::Log, TEXT("%s"), **LogItemIter);
|
|
#if WITH_EDITOR
|
|
AutomationTestingLog.Info(FText::FromString(*LogItemIter));
|
|
#endif
|
|
}
|
|
|
|
if (TestResults.State == EAutomationState::Success)
|
|
{
|
|
FString SuccessString = FString::Printf(TEXT("...Automation Test Succeeded (%s)"), *Report->GetDisplayName());
|
|
GLog->Logf(ELogVerbosity::Log, *SuccessString);
|
|
#if WITH_EDITOR
|
|
AutomationTestingLog.Info(FText::FromString(*SuccessString));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
FString FailureString = FString::Printf(TEXT("...Automation Test Failed (%s)"), *Report->GetDisplayName());
|
|
GLog->Logf(ELogVerbosity::Log, *FailureString);
|
|
#if WITH_EDITOR
|
|
AutomationTestingLog.Error(FText::FromString(*FailureString));
|
|
#endif
|
|
}
|
|
|
|
// Device is now good to go
|
|
DeviceClusterManager.SetTest(ClusterIndex, DeviceIndex, NULL);
|
|
}
|
|
|
|
// Remove the running test
|
|
RemoveTestRunning(Context->GetSender());
|
|
}
|
|
|
|
|
|
void FAutomationControllerManager::HandleWorkerOfflineMessage( const FAutomationWorkerWorkerOffline& Message, const IMessageContextRef& Context )
|
|
{
|
|
FMessageAddress DeviceMessageAddress = Context->GetSender();
|
|
DeviceClusterManager.Remove(DeviceMessageAddress);
|
|
}
|
|
|
|
bool FAutomationControllerManager::IsDeviceGroupFlagSet( EAutomationDeviceGroupTypes::Type InDeviceGroup ) const
|
|
{
|
|
const uint32 FlagMask = 1 << InDeviceGroup;
|
|
return (DeviceGroupFlags & FlagMask) > 0;
|
|
}
|
|
|
|
void FAutomationControllerManager::ToggleDeviceGroupFlag( EAutomationDeviceGroupTypes::Type InDeviceGroup )
|
|
{
|
|
const uint32 FlagMask = 1 << InDeviceGroup;
|
|
DeviceGroupFlags = DeviceGroupFlags ^ FlagMask;
|
|
}
|
|
|
|
void FAutomationControllerManager::UpdateDeviceGroups( )
|
|
{
|
|
DeviceClusterManager.ReGroupDevices( DeviceGroupFlags );
|
|
|
|
// Update the reports in case the number of clusters changed
|
|
int32 NumOfClusters = DeviceClusterManager.GetNumClusters();
|
|
ReportManager.ClustersUpdated(NumOfClusters);
|
|
}
|
|
|
|
void FAutomationControllerManager::TrackReportHistory(const bool bShouldTrack, const int32 NumReportsToTrack)
|
|
{
|
|
bTrackHistory = bShouldTrack;
|
|
NumberOfHistoryItemsTracked = NumReportsToTrack;
|
|
|
|
// Update the ini with the settings
|
|
GConfig->SetBool(*AutomationControllerConstants::HistoryConfigSectionName, TEXT("bTrackHistory"), bTrackHistory, GEngineIni);
|
|
GConfig->SetInt(*AutomationControllerConstants::HistoryConfigSectionName, TEXT("NumberOfHistoryItemsTracked"), NumberOfHistoryItemsTracked, GEngineIni);
|
|
|
|
ReportManager.TrackHistory(bTrackHistory, NumberOfHistoryItemsTracked);
|
|
}
|
|
|
|
const bool FAutomationControllerManager::IsTrackingHistory() const
|
|
{
|
|
return bTrackHistory;
|
|
}
|
|
|
|
const int32 FAutomationControllerManager::GetNumberHistoryItemsTracking() const
|
|
{
|
|
return NumberOfHistoryItemsTracked;
|
|
} |