Files
UnrealEngine/Engine/Source/Developer/AutomationWindow/Private/SAutomationGraphicalResultBox.cpp
2025-05-18 13:04:45 +08:00

352 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SAutomationGraphicalResultBox.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/SOverlay.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Layout/SGridPanel.h"
#include "Styling/AppStyle.h"
#include "AutomationWindowStyle.h"
#define LOCTEXT_NAMESPACE "SAutomationGraphicalResultBox"
/* SAutomationGraphicalResultBox interface
*****************************************************************************/
void SAutomationGraphicalResultBox::Construct( const FArguments& InArgs, const IAutomationControllerManagerRef& InAutomationController )
{
AutomationController = InAutomationController;
DisplayType = EAutomationGrapicalDisplayType::DisplayName;
RootBox = SNew(SVerticalBox);
ChildSlot
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(1.0f)
[
RootBox.ToSharedRef()
]
];
ClearResults();
AutomationController->OnTestsComplete().AddRaw(this, &SAutomationGraphicalResultBox::OnFillResults);
}
SAutomationGraphicalResultBox::~SAutomationGraphicalResultBox()
{
AutomationController->OnTestsComplete().RemoveAll(this);
}
void SAutomationGraphicalResultBox::ClearResults()
{
TotalTestDuration = 0.f;
ClusterResults.Empty();
RootBox->ClearChildren();
}
void SAutomationGraphicalResultBox::GetEnabledReports(TSharedPtr<IAutomationReport> InReport, TArray< TSharedPtr< IAutomationReport > >& outReports)
{
TArray<TSharedPtr<IAutomationReport> >& ChildReports = InReport->GetChildReports();
if (ChildReports.Num() > 0)
{
for (int32 Index = 0; Index < ChildReports.Num(); Index++)
{
GetEnabledReports(ChildReports[Index], outReports);
}
}
else if ( InReport->IsEnabled() )
{
outReports.Add(InReport);
}
}
void SAutomationGraphicalResultBox::PopulateData()
{
//Get the report list
TArray< TSharedPtr< IAutomationReport > > AllReports;
AllReports = AutomationController->GetEnabledReports();
//Find only the enabled tests
TArray< TSharedPtr< IAutomationReport > > EnabledReports;
for( int32 i=0; i<AllReports.Num(); ++i )
{
GetEnabledReports(AllReports[i],EnabledReports);
}
uint32 LastPassIndex = AutomationController->GetNumPasses() - 1;
//Pull the data out of the reports
for(int i = 0; i < AutomationController->GetNumDeviceClusters(); ++i)
{
ClusterResults.Add(FClusterResults(AutomationController->GetClusterGroupName(i)));
FClusterResults* ClusterIt = &ClusterResults[i];
ClusterIt->TotalNumTests = EnabledReports.Num();
for(int j = 0; j < AutomationController->GetNumDevicesInCluster(i); ++j)
{
ClusterIt->Devices.Add(FDeviceResults(AutomationController->GetGameInstanceId(i, j), AutomationController->GetGameInstanceName(i, j)));
FDeviceResults* DeviceIt = &ClusterIt->Devices[j];
for( int k = 0; k < EnabledReports.Num(); ++k )
{
FAutomationTestResults TestResults = EnabledReports[k]->GetResults( i, LastPassIndex );
if (TestResults.GameInstance == DeviceIt->GameInstanceId)
{
uint32 TestIndex = DeviceIt->Tests.Add(FTestResults(EnabledReports[k]->GetDisplayNameWithDecoration()));
FTestResults* TestIt = &DeviceIt->Tests[TestIndex];
TestIt->Duration = TestResults.Duration;
TestIt->TestState = TestResults.State;
TestIt->bHasWarnings = TestResults.GetWarningTotal() > 0;
DeviceIt->TotalTime += TestIt->Duration;
ClusterIt->TotalTime += TestIt->Duration;
if( TestResults.State == EAutomationState::Success )
{
DeviceIt->TotalTestSuccesses++;
ClusterIt->TotalTestSuccesses++;
}
else if (TestResults.State == EAutomationState::Skipped)
{
DeviceIt->TotalTestSkips++;
ClusterIt->TotalTestSkips++;
}
}
}
//See if this is the new longest running device for this cluster
if( DeviceIt->TotalTime > ClusterIt->ParallelTime )
{
ClusterIt->ParallelTime = DeviceIt->TotalTime;
}
}
//The total test duration is the time of the longest cluster.
if( ClusterIt->ParallelTime > TotalTestDuration )
{
TotalTestDuration = ClusterIt->ParallelTime;
}
}
}
void SAutomationGraphicalResultBox::OnFillResults()
{
ClearResults();
PopulateData();
CreateWidgets();
}
void SAutomationGraphicalResultBox::CreateWidgets()
{
//Note: I am using 10 columns here to hack around a sizing bug. The Grid calculates the width for items that span multiple
// columns by just dividing the size by the number of columns. This causes problems if the columns are different sizes.
// By having a bunch of extra columns here it will calculate a smaller per column size for our header.
TSharedRef<SGridPanel> GridContainer = SNew(SGridPanel).FillColumn(1,1);
uint32 RowCounter = 0;
for( int32 ClusterIndex = 0; ClusterIndex < ClusterResults.Num(); ++ClusterIndex )
{
FClusterResults* ClusterIt = &ClusterResults[ClusterIndex];
FFormatNamedArguments ClusterArgs;
ClusterArgs.Add(TEXT("Name"), FText::FromString(ClusterIt->ClusterName));
ClusterArgs.Add(TEXT("NumTests"), ClusterIt->TotalNumTests);
ClusterArgs.Add(TEXT("NumSkips"), ClusterIt->TotalTestSkips);
ClusterArgs.Add(TEXT("NumFails"), ClusterIt->TotalNumTests - ClusterIt->TotalTestSuccesses - ClusterIt->TotalTestSkips);
ClusterArgs.Add(TEXT("TotalTime"), ClusterIt->TotalTime);
ClusterArgs.Add(TEXT("ParallelTime"), ClusterIt->ParallelTime);
//Add Cluster Header
GridContainer->AddSlot(0,RowCounter)
.ColumnSpan(10)
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
.Padding( FMargin(1,3) )
[
SNew(STextBlock)
.TextStyle(FAutomationWindowStyle::Get(), "Automation.ReportHeader" )
.Text(FText::Format(LOCTEXT("AutomationGraphicalClusterHeader", "{Name} - {NumTests} Tests / {NumFails} Fails / {NumSkips} Skips / {TotalTime} Seconds (Total) / {ParallelTime} Seconds (Parallel)"), ClusterArgs))
];
RowCounter++;
for( int32 DeviceIndex = 0; DeviceIndex < ClusterIt->Devices.Num(); ++DeviceIndex )
{
FDeviceResults* DeviceIt = &ClusterIt->Devices[DeviceIndex];
const int32 NumTests = DeviceIt->Tests.Num();
//Add Device Header
FFormatNamedArguments DeviceArgs;
DeviceArgs.Add(TEXT("NumTests"), NumTests);
DeviceArgs.Add(TEXT("NumSkips"), DeviceIt->TotalTestSkips);
DeviceArgs.Add(TEXT("NumFails"), NumTests - DeviceIt->TotalTestSuccesses - DeviceIt->TotalTestSkips);
DeviceArgs.Add(TEXT("TotalTime"), DeviceIt->TotalTime);
GridContainer->AddSlot(0,RowCounter)
.HAlign(HAlign_Left)
.Padding(0,0,3,0)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
[
SNew(STextBlock)
.Text(FText::FromString(DeviceIt->GameInstanceName))
]
+SVerticalBox::Slot()
[
SNew(STextBlock)
.Text(FText::Format(LOCTEXT("AutomationGraphicalDeviceHeader", "{NumTests} Tests / {NumFails} Fails / {NumSkips} Skips / {TotalTime} Seconds"), DeviceArgs))
]
];
TSharedRef<SHorizontalBox> TestContainer = SNew(SHorizontalBox);
// If there are too many tests then result blocks may become invisible because they get too tiny
// and margins are covering them up. Hence reducing the margins with relation to the number of tests.
int32 TestsNum = DeviceIt->Tests.Num();
float HorizontalMargin = TestsNum < 30 ? 1.0 : (TestsNum < 100 ? 0.5 : (TestsNum < 1000 ? 0.25 : 0.1));
for( int32 TestIndex = 0; TestIndex < DeviceIt->Tests.Num(); ++TestIndex)
{
FTestResults* TestIt = &DeviceIt->Tests[TestIndex];
FFormatNamedArguments TestArgs;
TestArgs.Add(TEXT("Duration"), TestIt->Duration);
TestArgs.Add(TEXT("Name"), FText::FromString(TestIt->TestName));
TestContainer->AddSlot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding( FMargin(HorizontalMargin, 5) )
.FillWidth(TestIt->Duration)
[
SNew(SOverlay)
.ToolTipText(FText::Format(LOCTEXT("AutomationGraphicalToolTip", "{Name} \nDuration: {Duration}s"), TestArgs))
+SOverlay::Slot()
[
SNew(SBorder)
.BorderImage( FAppStyle::GetBrush("ErrorReporting.Box") )
.BorderBackgroundColor(this,&SAutomationGraphicalResultBox::GetColorForTestState, TestIt->TestState, TestIt->bHasWarnings)
]
+ SOverlay::Slot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(this,&SAutomationGraphicalResultBox::GetTestDisplayText,TestIt->TestName,TestIt->Duration)//FText::Format(LOCTEXT("AutomationGraphicalDuration", "{Duration}s"), TestArgs))
.ColorAndOpacity(FLinearColor(0,0,0,1))
]
];
}
//Fill in the end with the remaining time
if( DeviceIt->TotalTime < TotalTestDuration )
{
const float RemainingTime = TotalTestDuration - DeviceIt->TotalTime;
TestContainer->AddSlot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding( FMargin(1,5) )
.FillWidth( RemainingTime )
[
SNew(SBorder)
.BorderImage( FAppStyle::GetBrush("ErrorReporting.Box") )
.BorderBackgroundColor(FLinearColor(0,0,0,0))
];
}
RowCounter++;
GridContainer->AddSlot(0,RowCounter)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Center)
.Padding( FMargin(1,0) )
.ColumnSpan(11)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.FillWidth(1.f)
[
TestContainer
]
];
RowCounter++;
}
}
RootBox->AddSlot()
[
GridContainer
];
}
FSlateColor SAutomationGraphicalResultBox::GetColorForTestState(const EAutomationState TestState, const bool bHasWarnings) const
{
switch(TestState)
{
case EAutomationState::Success:
if (bHasWarnings)
{
return FSlateColor( FLinearColor( 1.0f, 0.5f, 0.0f ) );
}
else
{
return FSlateColor( FLinearColor( 0.0f, 0.5f, 0.0f ) );
}
case EAutomationState::Fail:
default: //Default to fail!
break;
}
return FSlateColor( FLinearColor( 0.5f, 0.0f, 0.0f ) );
}
FText SAutomationGraphicalResultBox::GetTestDisplayText(const FString TestName, const float TestTime) const
{
if(DisplayType == EAutomationGrapicalDisplayType::DisplayName)
{
return FText::FromString(TestName);
}
else
{
FFormatNamedArguments TestArgs;
TestArgs.Add(TEXT("Duration"), TestTime);
return FText::Format(LOCTEXT("AutomationGraphicalDuration", "{Duration}s"), TestArgs);
}
}
EAutomationGrapicalDisplayType::Type SAutomationGraphicalResultBox::GetDisplayType() const
{
return DisplayType;
}
void SAutomationGraphicalResultBox::SetDisplayType(EAutomationGrapicalDisplayType::Type NewDisplayType)
{
DisplayType = NewDisplayType;
}
bool SAutomationGraphicalResultBox::HasResults() const
{
return ClusterResults.Num() > 0;
}
#undef LOCTEXT_NAMESPACE