Files
UnrealEngineUWP/Engine/Source/Developer/AutomationWindow/Private/SAutomationWindow.cpp
Nick Darnell 5c3f60e315 Copying //UE4/Dev-Automation to //UE4/Dev-Main (Source: //UE4/Dev-Automation @ 3376875)
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3340659 on 2017/03/09 by nick.bullard

	Submitting Template map with update to lighting. The direction of the light was not optimal for looking down X+ for screenshots, which is what the SSF actor does be default.

Change 3340686 on 2017/03/09 by Nick.Bullard

	Updating Animaton/SetVertexColor map to use Template map setup.

Change 3364139 on 2017/03/24 by Nick.Darnell

	Automation - Removing the option to disable taking screenshots, doesn't make sense to have that any more.  Screenshot comparison in an intergal part of the tests now.
	Automation - The system now waits for the asset registry to finish loading assets before running tests.

Change 3364149 on 2017/03/24 by Nick.Darnell

	Automation - Fixing a bug with the source control not reverting the state of screenshots before marking them for delete.

Change 3364588 on 2017/03/24 by Nick.Darnell

	Removing the allow screenshots option.

Change 3364591 on 2017/03/24 by Nick.Darnell

	Automation - Making the camera functional test actor respect the delay.

Change 3364598 on 2017/03/24 by Nick.Darnell

	Automation - Disabling Noisy rendering features now actually disables TAA, instead of enabling FXAA.

Change 3364723 on 2017/03/25 by Nick.Darnell

	Automation - Fixing the client functional test runner to pass object and package path so that in the editor we can reliably load a particular map with object path, but for actually opening the map we can use the package path when running the test since that is what works at editor and in a cooked game.

Change 3366600 on 2017/03/27 by Nick.Bullard

	Adding FuntionalTest map back as selection in New Map window

Change 3367590 on 2017/03/28 by Nick.Darnell

	Automation - Fixing a bug with initial tolerence levels.  They showed as 'Low' in the UI, but the values were in fact all set to 0.  So new screenshot tests had zero tolerence for pixel differences.

	Automation - Adding some documentation to Functional Test.

Change 3367602 on 2017/03/28 by Nick.Darnell

	Automation - Forcing more things to stream in before the screenshots are taken.

Change 3367604 on 2017/03/28 by Nick.Darnell

	Automation - Adding some flare to how disabled functional test actors appear in the editor.

Change 3368024 on 2017/03/28 by mason.seay

	Added new test to map.  Disabled since it hasn't ran at all

Change 3368109 on 2017/03/28 by mason.seay

	Updating authorship on test actors

Change 3369701 on 2017/03/29 by Nick.Bullard

	Removing old Ground Truth images. Investigating why new copies were made rather than replacing original.

Change 3373253 on 2017/03/30 by Samuel.Proctor

	Test updates for Containers. Adding coverage for native containers

Change 3373294 on 2017/03/30 by Nick.Darnell

	Automation - Fixing a bug with Tolerence levels set via blueprints, now properly configuring the tolerence amounts before the screenshot is taken.

Change 3374355 on 2017/03/31 by Samuel.Proctor

	Added testing scenarios to the test actors and fixed a few typos.

[CL 3376906 by Nick Darnell in Main branch]
2017-04-03 13:59:25 -04:00

2259 lines
67 KiB
C++

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "SAutomationWindow.h"
#include "HAL/PlatformProcess.h"
#include "Misc/MessageDialog.h"
#include "Misc/TextFilter.h"
#include "Misc/FilterCollection.h"
#include "Widgets/Layout/SSplitter.h"
#include "SlateOptMacros.h"
#include "Textures/SlateIcon.h"
#include "Framework/Commands/InputChord.h"
#include "Framework/Commands/UIAction.h"
#include "Framework/Commands/Commands.h"
#include "Framework/Commands/UICommandList.h"
#include "Widgets/Images/SImage.h"
#include "Framework/MultiBox/MultiBoxDefs.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Input/SSpinBox.h"
#include "EditorStyleSet.h"
#include "SAutomationWindowCommandBar.h"
#include "AutomationFilter.h"
#include "AutomationPresetManager.h"
#include "SAutomationTestItemContextMenu.h"
#include "SAutomationTestItem.h"
#if WITH_EDITOR
#include "Engine/World.h"
#include "FileHelpers.h"
#include "AssetRegistryModule.h"
#endif
#include "Widgets/Input/SSearchBox.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Widgets/Images/SThrobber.h"
#include "Widgets/Input/SHyperlink.h"
#include "Internationalization/Regex.h"
#define LOCTEXT_NAMESPACE "AutomationTest"
//////////////////////////////////////////////////////////////////////////
// FAutomationWindowCommands
class FAutomationWindowCommands : public TCommands<FAutomationWindowCommands>
{
public:
FAutomationWindowCommands()
: TCommands<FAutomationWindowCommands>(
TEXT("AutomationWindow"),
NSLOCTEXT("Contexts", "AutomationWindow", "Automation Window"),
NAME_None, FEditorStyle::GetStyleSetName()
)
{
}
virtual void RegisterCommands() override
{
UI_COMMAND( RefreshTests, "Refresh Tests", "Refresh Tests", EUserInterfaceActionType::Button, FInputChord() );
UI_COMMAND( FindWorkers, "Find Workers", "Find Workers", EUserInterfaceActionType::Button, FInputChord() );
UI_COMMAND( ErrorFilter, "Errors", "Toggle Error Filter", EUserInterfaceActionType::ToggleButton, FInputChord() );
UI_COMMAND( WarningFilter, "Warnings", "Toggle Warning Filter", EUserInterfaceActionType::ToggleButton, FInputChord() );
UI_COMMAND( DeveloperDirectoryContent, "Dev Content", "Developer Directory Content Filter (when enabled, developer directories are also included)", EUserInterfaceActionType::ToggleButton, FInputChord() );
#if WITH_EDITOR
// Added button for running the currently open level test.
UI_COMMAND(RunLevelTest, "Run Level Test", "Run Level Test", EUserInterfaceActionType::Button, FInputGesture());
#endif
}
public:
TSharedPtr<FUICommandInfo> RefreshTests;
TSharedPtr<FUICommandInfo> FindWorkers;
TSharedPtr<FUICommandInfo> ErrorFilter;
TSharedPtr<FUICommandInfo> WarningFilter;
TSharedPtr<FUICommandInfo> DeveloperDirectoryContent;
#if WITH_EDITOR
TSharedPtr<FUICommandInfo> RunLevelTest;
#endif
};
//////////////////////////////////////////////////////////////////////////
// SAutomationWindow
SAutomationWindow::SAutomationWindow()
: ColumnWidth(50.0f)
{
}
SAutomationWindow::~SAutomationWindow()
{
// @todo PeterMcW: is there an actual delegate missing here?
//give the controller a way to indicate it requires a UI update
//AutomationController->SetRefreshTestCallback(FOnAutomationControllerTestsRefreshed());
// Remove ourselves from the session manager
if( SessionManager.IsValid( ) )
{
SessionManager->OnCanSelectSession().RemoveAll(this);
SessionManager->OnSelectedSessionChanged().RemoveAll(this);
SessionManager->OnSessionInstanceUpdated().RemoveAll(this);
}
if (AutomationController.IsValid())
{
AutomationController->RemoveCallbacks();
AutomationController->OnControllerReset().RemoveAll(this);
AutomationController->OnTestsRefreshed().RemoveAll(this);
AutomationController->OnTestsAvailable().RemoveAll(this);
AutomationController->OnTestsComplete().RemoveAll(this);
}
#if WITH_EDITOR
if ( FModuleManager::Get().IsModuleLoaded(TEXT("AssetRegistry")) )
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::GetModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().OnFileLoadProgressUpdated().RemoveAll(this);
}
#endif
TSharedPtr<IAutomationReport> PreviousSelectionLock = PreviousSelection.Pin();
if ( PreviousSelectionLock.IsValid() )
{
PreviousSelectionLock->OnSetResults.Unbind();
}
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SAutomationWindow::Construct( const FArguments& InArgs, const IAutomationControllerManagerRef& InAutomationController, const TSharedRef<ISessionManager>& InSessionManager )
{
FAutomationWindowCommands::Register();
CreateCommands();
#if WITH_EDITOR
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().OnFileLoadProgressUpdated().AddSP(this, &SAutomationWindow::OnAssetRegistryFileLoadProgress);
#endif
TestPresetManager = MakeShareable(new FAutomationTestPresetManager());
TestPresetManager->LoadPresets();
bAddingTestPreset = false;
bHasChildTestSelected = false;
SessionManager = InSessionManager;
AutomationController = InAutomationController;
AutomationController->OnControllerReset().AddSP(this, &SAutomationWindow::OnRefreshTestCallback);
AutomationController->OnTestsRefreshed().AddSP(this, &SAutomationWindow::OnRefreshTestCallback);
AutomationController->OnTestsAvailable().AddSP(this, &SAutomationWindow::OnTestAvailableCallback);
AutomationController->OnTestsComplete().AddSP(this, &SAutomationWindow::OnTestsCompleteCallback);
AutomationControllerState = AutomationController->GetTestState();
//cache off reference to filtered reports
TArray <TSharedPtr <IAutomationReport> >& TestReports = AutomationController->GetReports();
// Create the search filter and set criteria
AutomationTextFilter = MakeShareable( new AutomationReportTextFilter( AutomationReportTextFilter::FItemToStringArray::CreateSP( this, &SAutomationWindow::PopulateReportSearchStrings ) ) );
AutomationGeneralFilter = MakeShareable( new FAutomationFilter() );
AutomationFilters = MakeShareable( new AutomationFilterCollection() );
AutomationFilters->Add( AutomationTextFilter );
AutomationFilters->Add( AutomationGeneralFilter );
bIsRequestingTests = false;
//make the widget for platforms
PlatformsHBox = SNew (SHorizontalBox);
TestTable = SNew(SAutomationTestTreeView< TSharedPtr< IAutomationReport > >)
.SelectionMode(ESelectionMode::Multi)
.TreeItemsSource( &TestReports )
// Generates the actual widget for a tree item
.OnGenerateRow( this, &SAutomationWindow::OnGenerateWidgetForTest )
// Gets children
.OnGetChildren(this, &SAutomationWindow::OnGetChildren)
// on recursive expansion (shift + click)
.OnSetExpansionRecursive(this, &SAutomationWindow::OnTestExpansionRecursive)
//on selection
.OnSelectionChanged(this, &SAutomationWindow::OnTestSelectionChanged)
// Allow for some spacing between items with a larger item height.
.ItemHeight(20.0f)
#if WITH_EDITOR
// If in editor - add a context menu for opening assets when in editor
.OnContextMenuOpening(this, &SAutomationWindow::HandleAutomationListContextMenuOpening)
#endif
.HeaderRow
(
SAssignNew(TestTableHeaderRow,SHeaderRow)
+ SHeaderRow::Column( AutomationTestWindowConstants::Title )
.FillWidth(0.80f)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Center)
[
//global enable/disable check box
SAssignNew(HeaderCheckbox, SCheckBox)
.OnCheckStateChanged( this, &SAutomationWindow::HeaderCheckboxStateChange)
.ToolTipText( LOCTEXT( "Enable Disable Test", "Enable / Disable Test" ) )
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew( STextBlock )
.Text( LOCTEXT("TestName_Header", "Test Name") )
]
]
+ SHeaderRow::Column( AutomationTestWindowConstants::SmokeTest )
.FixedWidth( 50.0f )
.HAlignHeader(HAlign_Center)
.VAlignHeader(VAlign_Center)
.HAlignCell(HAlign_Center)
.VAlignCell(VAlign_Center)
[
//icon for the smoke test column
SNew(SImage)
.ColorAndOpacity(FLinearColor(1.0f, 1.0f, 1.0f, 0.4f))
.ToolTipText( LOCTEXT( "Smoke Test", "Smoke Test" ) )
.Image(FEditorStyle::GetBrush("Automation.SmokeTest"))
]
+ SHeaderRow::Column( AutomationTestWindowConstants::RequiredDeviceCount )
.FixedWidth(50.0f)
.HAlignHeader(HAlign_Center)
.VAlignHeader(VAlign_Center)
.HAlignCell(HAlign_Center)
.VAlignCell(VAlign_Center)
[
SNew( SImage )
.Image( FEditorStyle::GetBrush("Automation.ParticipantsWarning") )
.ToolTipText( LOCTEXT( "RequiredDeviceCountWarningToolTip", "Number of devices required." ) )
]
+ SHeaderRow::Column(AutomationTestWindowConstants::Timing)
.FixedWidth(100.0f)
.DefaultLabel(LOCTEXT("TestDurationRange", "Duration"))
+ SHeaderRow::Column( AutomationTestWindowConstants::Status )
.FillWidth(0.20f)
[
//platform header placeholder
PlatformsHBox.ToSharedRef()
]
);
RequestedFilterComboList.Empty();
RequestedFilterComboList.Add(MakeShareable(new FString(TEXT("All Tests"))));
RequestedFilterComboList.Add(MakeShareable(new FString(TEXT("Smoke Tests"))));
RequestedFilterComboList.Add(MakeShareable(new FString(TEXT("Engine Tests"))));
RequestedFilterComboList.Add(MakeShareable(new FString(TEXT("Product Tests"))));
RequestedFilterComboList.Add(MakeShareable(new FString(TEXT("Performance Tests"))));
RequestedFilterComboList.Add(MakeShareable(new FString(TEXT("Stress Tests"))));
RequestedFilterComboList.Add(MakeShareable(new FString(TEXT("Standard Tests"))));
TSharedRef<SNotificationList> NotificationList = SNew(SNotificationList) .Visibility( EVisibility::HitTestInvisible );
//build the actual guts of the window
this->ChildSlot
[
SNew(SOverlay)
+ SOverlay::Slot()
[
SNew( SSplitter )
.IsEnabled(this, &SAutomationWindow::HandleMainContentIsEnabled)
.Orientation(Orient_Vertical)
+ SSplitter::Slot()
.Value(0.66f)
[
//automation test panel
SAssignNew( MenuBar, SVerticalBox )
//ACTIONS
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew( SHorizontalBox )
+ SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
SAutomationWindow::MakeAutomationWindowToolBar( AutomationWindowActions.ToSharedRef(), SharedThis(this) )
]
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
.Padding(0.0f, 4.0f, 0.0f, 0.0f)
[
SNew(SOverlay)
+ SOverlay::Slot()
[
SNew(SBorder)
.BorderImage(this, &SAutomationWindow::GetTestBackgroundBorderImage)
.Padding(3)
[
SNew(SBox)
.Padding(4)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 0.0f, 0.0f, 4.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew(SBox)
.MinDesiredWidth(130.0f)
[
SAssignNew(RequestedFilterComboBox, SComboBox< TSharedPtr<FString> >)
.OptionsSource(&RequestedFilterComboList)
.InitiallySelectedItem(RequestedFilterComboList[6])
.OnGenerateWidget(this, &SAutomationWindow::GenerateRequestedFilterComboItem)
.OnSelectionChanged(this, &SAutomationWindow::HandleRequesteFilterChanged)
.ContentPadding(FMargin(4.0, 1.0f))
[
SNew(STextBlock)
.Text(this, &SAutomationWindow::GetRequestedFilterComboText)
]
]
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.VAlign(VAlign_Center)
.Padding(2.0f, 0, 0, 0)
[
SAssignNew(AutomationSearchBox, SSearchBox)
.ToolTipText(LOCTEXT("Search Tests", "Search Tests"))
.OnTextChanged(this, &SAutomationWindow::OnFilterTextChanged)
.IsEnabled(this, &SAutomationWindow::IsAutomationControllerIdle)
]
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
//the actual table full of tests
TestTable.ToSharedRef()
]
]
]
]
+ SOverlay::Slot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(SThrobber)
.Visibility(this, &SAutomationWindow::GetTestsUpdatingThrobberVisibility)
]
]
]
+ SSplitter::Slot()
.Value(0.33f)
[
SNew(SOverlay)
+ SOverlay::Slot()
[
SNew(SBox)
.Visibility(this, &SAutomationWindow::GetTestGraphVisibility)
[
//Graphical Results Panel
SNew( SVerticalBox )
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.HAlign(HAlign_Left)
[
SNew(STextBlock)
.Text( LOCTEXT("AutomationTest_GraphicalResults", "Automation Test Graphical Results:"))
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
[
SNew(STextBlock)
.Text( LOCTEXT("AutomationTest_Display", "Display:"))
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
[
SNew(SCheckBox)
.Style(FCoreStyle::Get(), "RadioButton")
.IsChecked(this, &SAutomationWindow::HandleResultDisplayTypeIsChecked, EAutomationGrapicalDisplayType::DisplayName)
.OnCheckStateChanged(this, &SAutomationWindow::HandleResultDisplayTypeStateChanged, EAutomationGrapicalDisplayType::DisplayName)
[
SNew(STextBlock)
.Text( LOCTEXT("AutomationTest_GraphicalResultsDisplayName", "Name"))
]
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
[
SNew(SCheckBox)
.Style(FCoreStyle::Get(), "RadioButton")
.IsChecked(this, &SAutomationWindow::HandleResultDisplayTypeIsChecked, EAutomationGrapicalDisplayType::DisplayTime)
.OnCheckStateChanged(this, &SAutomationWindow::HandleResultDisplayTypeStateChanged, EAutomationGrapicalDisplayType::DisplayTime)
[
SNew(STextBlock)
.Text( LOCTEXT("AutomationTest_GraphicalResultsDisplayTime", "Time"))
]
]
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SBorder)
[
SNew(SScrollBox)
+ SScrollBox::Slot()
[
SAssignNew(GraphicalResultBox, SAutomationGraphicalResultBox, InAutomationController)
]
]
]
]
]
+ SOverlay::Slot()
[
SNew(SBox)
.Visibility(this, &SAutomationWindow::GetTestLogVisibility)
[
//results panel
SNew( SVerticalBox )
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew( STextBlock )
.Text( LOCTEXT("AutomationTest_Results", "Automation Test Results:") )
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
.Padding(0.0f, 4.0f, 0.0f, 0.0f)
[
//list of results for the selected test
SNew(SBorder)
.BorderImage(FEditorStyle::GetBrush("MessageLog.ListBorder"))
[
SAssignNew(LogListView, SListView<TSharedPtr<FAutomationOutputMessage> >)
.ItemHeight(18)
.ListItemsSource(&LogMessages)
.SelectionMode(ESelectionMode::Multi)
.OnGenerateRow(this, &SAutomationWindow::OnGenerateWidgetForLog)
.OnSelectionChanged(this, &SAutomationWindow::HandleLogListSelectionChanged)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 4.0f, 0.0f, 0.0f)
[
SNew(SBorder)
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
.Padding(FMargin(8.0f, 6.0f))
[
// Add the command bar
SAssignNew(CommandBar, SAutomationWindowCommandBar, NotificationList)
.OnCopyLogClicked(this, &SAutomationWindow::HandleCommandBarCopyLogClicked)
]
]
]
]
]
]
+ SOverlay::Slot()
.HAlign( HAlign_Center )
.VAlign( VAlign_Center )
.Padding( 15.0f )
[
NotificationList
]
+ SOverlay::Slot()
.HAlign( HAlign_Center )
.VAlign( VAlign_Center )
.Padding( 15.0f )
[
SNew(SBorder)
.BorderImage(FEditorStyle::GetBrush("NotificationList.ItemBackground"))
.Padding(8.0f)
.Visibility(this, &SAutomationWindow::HandleSelectSessionOverlayVisibility)
[
SNew(STextBlock)
.Text(LOCTEXT("SelectSessionOverlayText", "Please select at least one instance from the Session Browser"))
]
]
];
SessionManager->OnCanSelectSession().AddSP( this, &SAutomationWindow::HandleSessionManagerCanSelectSession );
SessionManager->OnSelectedSessionChanged().AddSP( this, &SAutomationWindow::HandleSessionManagerSelectionChanged );
SessionManager->OnSessionInstanceUpdated().AddSP( this, &SAutomationWindow::HandleSessionManagerInstanceChanged );
FindWorkers();
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SAutomationWindow::HandleResultDisplayTypeStateChanged( ECheckBoxState NewRadioState, EAutomationGrapicalDisplayType::Type NewDisplayType)
{
if (NewRadioState == ECheckBoxState::Checked)
{
GraphicalResultBox->SetDisplayType(NewDisplayType);
}
}
ECheckBoxState SAutomationWindow::HandleResultDisplayTypeIsChecked( EAutomationGrapicalDisplayType::Type InDisplayType ) const
{
return (GraphicalResultBox->GetDisplayType() == InDisplayType)
? ECheckBoxState::Checked
: ECheckBoxState::Unchecked;
}
const FSlateBrush* SAutomationWindow::GetTestBackgroundBorderImage() const
{
switch(TestBackgroundType)
{
case EAutomationTestBackgroundStyle::Game:
return FEditorStyle::GetBrush("AutomationWindow.GameGroupBorder");
case EAutomationTestBackgroundStyle::Editor:
return FEditorStyle::GetBrush("AutomationWindow.EditorGroupBorder");
case EAutomationTestBackgroundStyle::Unknown:
default:
return FEditorStyle::GetBrush("ToolPanel.GroupBorder");
}
}
void SAutomationWindow::CreateCommands()
{
check(!AutomationWindowActions.IsValid());
AutomationWindowActions = MakeShareable(new FUICommandList);
const FAutomationWindowCommands& Commands = FAutomationWindowCommands::Get();
FUICommandList& ActionList = *AutomationWindowActions;
ActionList.MapAction( Commands.RefreshTests,
FExecuteAction::CreateRaw( this, &SAutomationWindow::ListTests ),
FCanExecuteAction::CreateRaw( this, &SAutomationWindow::IsAutomationControllerIdle )
);
ActionList.MapAction( Commands.FindWorkers,
FExecuteAction::CreateRaw( this, &SAutomationWindow::FindWorkers ),
FCanExecuteAction::CreateRaw( this, &SAutomationWindow::IsAutomationControllerIdle )
);
ActionList.MapAction( Commands.ErrorFilter,
FExecuteAction::CreateRaw( this, &SAutomationWindow::OnToggleErrorFilter ),
FCanExecuteAction::CreateRaw( this, &SAutomationWindow::IsAutomationControllerIdle ),
FIsActionChecked::CreateRaw( this, &SAutomationWindow::IsErrorFilterOn )
);
ActionList.MapAction( Commands.WarningFilter,
FExecuteAction::CreateRaw( this, &SAutomationWindow::OnToggleWarningFilter ),
FCanExecuteAction::CreateRaw( this, &SAutomationWindow::IsAutomationControllerIdle ),
FIsActionChecked::CreateRaw( this, &SAutomationWindow::IsWarningFilterOn )
);
ActionList.MapAction( Commands.DeveloperDirectoryContent,
FExecuteAction::CreateRaw( this, &SAutomationWindow::OnToggleDeveloperDirectoryIncluded ),
FCanExecuteAction::CreateRaw( this, &SAutomationWindow::IsAutomationControllerIdle ),
FIsActionChecked::CreateRaw( this, &SAutomationWindow::IsDeveloperDirectoryIncluded )
);
// Added button for running the currently open level test.
#if WITH_EDITOR
ActionList.MapAction(Commands.RunLevelTest,
FExecuteAction::CreateRaw(this, &SAutomationWindow::OnRunLevelTest),
FCanExecuteAction::CreateRaw(this, &SAutomationWindow::CanExecuteRunLevelTest)
);
#endif // WITH_EDITOR
}
TSharedRef< SWidget > SAutomationWindow::MakeAutomationWindowToolBar( const TSharedRef<FUICommandList>& InCommandList, TSharedPtr<class SAutomationWindow> InAutomationWindow )
{
return InAutomationWindow->MakeAutomationWindowToolBar(InCommandList);
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef< SWidget > SAutomationWindow::MakeAutomationWindowToolBar( const TSharedRef<FUICommandList>& InCommandList )
{
struct Local
{
static void FillToolbar(FToolBarBuilder& ToolbarBuilder, TSharedRef<SWidget> RunTests, TSharedRef<SWidget> PresetBox, TWeakPtr<class SAutomationWindow> InAutomationWindow)
{
ToolbarBuilder.BeginSection("Automation");
{
ToolbarBuilder.AddWidget( RunTests );
FUIAction DefaultAction;
ToolbarBuilder.AddComboButton(
DefaultAction,
FOnGetContent::CreateStatic( &SAutomationWindow::GenerateTestsOptionsMenuContent, InAutomationWindow ),
LOCTEXT( "TestOptions_Label", "Test Options" ),
LOCTEXT( "TestOptionsToolTip", "Test Options" ),
FSlateIcon(FEditorStyle::GetStyleSetName(), "AutomationWindow.TestOptions"),
true);
// Added button for running the currently open level test.
#if WITH_EDITOR
ToolbarBuilder.AddToolBarButton(
FAutomationWindowCommands::Get().RunLevelTest,
NAME_None,
TAttribute<FText>(),
LOCTEXT("RunLevelTest_ToolTip", "If the currently loaded editor level is a test map, click this to select the test and run it immediately."),
FSlateIcon(FEditorStyle::GetStyleSetName(), "AutomationWindow.RunTests"));
#endif
ToolbarBuilder.AddToolBarButton( FAutomationWindowCommands::Get().RefreshTests );
ToolbarBuilder.AddToolBarButton( FAutomationWindowCommands::Get().FindWorkers );
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection("Filters");
{
ToolbarBuilder.AddToolBarButton( FAutomationWindowCommands::Get().ErrorFilter );
ToolbarBuilder.AddToolBarButton( FAutomationWindowCommands::Get().WarningFilter );
ToolbarBuilder.AddToolBarButton( FAutomationWindowCommands::Get().DeveloperDirectoryContent );
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection("GroupFlags");
{
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateStatic( &SAutomationWindow::GenerateGroupOptionsMenuContent, InAutomationWindow ),
LOCTEXT( "GroupOptions_Label", "Device Groups" ),
LOCTEXT( "GroupOptionsToolTip", "Device Group Options" ),
FSlateIcon(FEditorStyle::GetStyleSetName(), "AutomationWindow.GroupSettings"));
}
ToolbarBuilder.EndSection();
ToolbarBuilder.BeginSection("Presets");
{
ToolbarBuilder.AddWidget( PresetBox );
}
ToolbarBuilder.EndSection();
}
};
TSharedRef<SWidget> RunTests =
SNew( SButton )
.ButtonStyle( FEditorStyle::Get(), "ToggleButton" )
.ToolTipText( LOCTEXT( "StartStop Tests", "Start / Stop tests" ) )
.OnClicked( this, &SAutomationWindow::RunTests )
.IsEnabled( this, &SAutomationWindow::IsAutomationRunButtonEnabled )
.ContentPadding(0)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1)
.VAlign(VAlign_Center)
[
SNew( SVerticalBox )
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign( HAlign_Center )
[
SNew( SOverlay )
+SOverlay::Slot()
[
SNew( SImage )
.Image( this, &SAutomationWindow::GetRunAutomationIcon )
]
+SOverlay::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Bottom)
[
SNew( STextBlock )
.Text( this, &SAutomationWindow::OnGetNumEnabledTestsString )
.ColorAndOpacity( FLinearColor::White )
.ShadowOffset( FVector2D::UnitVector )
.Font( FEditorStyle::GetFontStyle( FName( "ToggleButton.LabelFont" ) ) )
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign( HAlign_Center )
[
SNew( STextBlock )
.Visibility( this, &SAutomationWindow::GetLargeToolBarVisibility )
.Text( this, &SAutomationWindow::GetRunAutomationLabel )
.Font( FEditorStyle::GetFontStyle( FName( "ToggleButton.LabelFont" ) ) )
.ColorAndOpacity(FLinearColor::White)
.ShadowOffset( FVector2D::UnitVector )
]
]
];
TSharedRef<SWidget> TestPresets =
SNew( SVerticalBox )
+SVerticalBox::Slot()
.FillHeight(0.75f)
.VAlign(VAlign_Bottom)
.HAlign(HAlign_Left)
[
SNew( SHorizontalBox )
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(2.0f)
.VAlign(VAlign_Bottom)
[
SNew( STextBlock )
.Text( LOCTEXT("AutomationPresetLabel", "Preset:") )
.IsEnabled( this, &SAutomationWindow::IsAutomationControllerIdle )
]
]
+SVerticalBox::Slot()
.AutoHeight()
.VAlign(VAlign_Bottom)
[
SNew( SHorizontalBox )
+SHorizontalBox::Slot()
.AutoWidth()
[
//Preset Combo / Text
SNew(SOverlay)
+SOverlay::Slot()
[
SNew(SHorizontalBox)
.Visibility(this,&SAutomationWindow::HandlePresetComboVisibility)
+SHorizontalBox::Slot()
.FillWidth(1.0)
[
SAssignNew( PresetComboBox, SComboBox< TSharedPtr<FAutomationTestPreset> > )
.OptionsSource( &TestPresetManager->GetAllPresets() )
.OnGenerateWidget( this, &SAutomationWindow::GeneratePresetComboItem )
.OnSelectionChanged( this, &SAutomationWindow::HandlePresetChanged )
.IsEnabled(this, &SAutomationWindow::IsAutomationControllerIdle)
[
SNew( STextBlock )
.Text( this, &SAutomationWindow::GetPresetComboText )
]
]
]
+SOverlay::Slot()
[
SNew(SHorizontalBox)
.Visibility(this,&SAutomationWindow::HandlePresetTextVisibility)
+SHorizontalBox::Slot()
.FillWidth(1.0)
[
SAssignNew(PresetTextBox, SEditableTextBox)
.OnTextCommitted(this, &SAutomationWindow::HandlePresetTextCommited)
.IsEnabled(this, &SAutomationWindow::IsAutomationControllerIdle)
]
]
]
//New button
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew( SButton )
.ButtonStyle( FEditorStyle::Get(), "NoBorder" )
.OnClicked( this, &SAutomationWindow::HandleNewPresetClicked )
.ToolTipText( LOCTEXT("AutomationPresetNewButtonTooltip", "Create a new preset") )
.IsEnabled(this, &SAutomationWindow::IsAddButtonEnabled)
.Content()
[
SNew(SImage)
.Image(FEditorStyle::Get().GetBrush("AutomationWindow.PresetNew"))
]
]
//Save button
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew( SButton )
.ButtonStyle( FEditorStyle::Get(), "NoBorder" )
.OnClicked( this, &SAutomationWindow::HandleSavePresetClicked )
.ToolTipText( LOCTEXT("AutomationPresetSaveButtonTooltip", "Save the current test list") )
.IsEnabled(this, &SAutomationWindow::IsSaveButtonEnabled)
.Content()
[
SNew(SImage)
.Image(FEditorStyle::Get().GetBrush("AutomationWindow.PresetSave"))
]
]
//Remove button
+ SHorizontalBox::Slot()
.AutoWidth()
[
// remove button
SNew(SButton)
.ButtonStyle( FEditorStyle::Get(), "NoBorder" )
.OnClicked( this, &SAutomationWindow::HandleRemovePresetClicked )
.ToolTipText(LOCTEXT("AutomationPresetRemoveButtonTooltip", "Remove the selected preset"))
.IsEnabled(this, &SAutomationWindow::IsRemoveButtonEnabled)
.Content()
[
SNew(SImage)
.Image(FEditorStyle::Get().GetBrush("AutomationWindow.PresetRemove"))
]
]
];
FToolBarBuilder ToolbarBuilder( InCommandList, FMultiBoxCustomization::None );
TWeakPtr<SAutomationWindow> AutomationWindow = SharedThis(this);
Local::FillToolbar(ToolbarBuilder, RunTests, TestPresets, AutomationWindow);
// Create the tool bar!
return
SNew( SHorizontalBox )
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew( SBorder )
.Padding(0)
.BorderImage( FEditorStyle::GetBrush("NoBorder") )
.IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() )
[
ToolbarBuilder.MakeWidget()
]
];
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
EVisibility SAutomationWindow::HandlePresetComboVisibility( ) const
{
return bAddingTestPreset ? EVisibility::Hidden : EVisibility::Visible;
}
EVisibility SAutomationWindow::HandlePresetTextVisibility( ) const
{
return bAddingTestPreset ? EVisibility::Visible : EVisibility::Hidden;
}
bool SAutomationWindow::IsAddButtonEnabled() const
{
return !bAddingTestPreset && IsAutomationControllerIdle();
}
bool SAutomationWindow::IsSaveButtonEnabled() const
{
return (!bAddingTestPreset && SelectedPreset.IsValid() && IsAutomationControllerIdle());
}
bool SAutomationWindow::IsRemoveButtonEnabled() const
{
return (!bAddingTestPreset && SelectedPreset.IsValid() && IsAutomationControllerIdle());
}
void SAutomationWindow::HandlePresetTextCommited( const FText& CommittedText, ETextCommit::Type CommitType )
{
if( CommitType == ETextCommit::OnEnter )
{
bAddingTestPreset = false;
if ( CommittedText.IsEmpty() )
{
return;
}
TArray<FString> EnabledTests;
AutomationController->GetEnabledTestNames(EnabledTests);
AutomationPresetPtr NewPreset = TestPresetManager->AddNewPreset(CommittedText, EnabledTests);
if ( NewPreset.IsValid() )
{
PresetComboBox->SetSelectedItem(NewPreset);
SelectedPreset = NewPreset;
PresetTextBox->SetText(FText());
}
}
else if( CommitType == ETextCommit::OnCleared || CommitType == ETextCommit::OnUserMovedFocus )
{
if( bAddingTestPreset )
{
bAddingTestPreset = false;
SelectedPreset = nullptr;
PresetComboBox->ClearSelection();
PresetTextBox->SetText(FText());
}
}
}
void SAutomationWindow::HandlePresetChanged( TSharedPtr<FAutomationTestPreset> Item, ESelectInfo::Type SelectInfo )
{
if( Item.IsValid() )
{
SelectedPreset = Item;
AutomationController->SetEnabledTests(Item->GetEnabledTests());
TestTable->RequestTreeRefresh();
//Expand selected items
TestTable->ClearExpandedItems();
TArray< TSharedPtr< IAutomationReport > >& TestReports = AutomationController->GetReports();
for( int32 Index = 0; Index < TestReports.Num(); Index++ )
{
ExpandEnabledTests(TestReports[Index]);
}
}
else
{
SelectedPreset.Reset();
TArray<FString> EnabledTests;
AutomationController->SetEnabledTests(EnabledTests);
TestTable->ClearExpandedItems();
TestTable->RequestTreeRefresh();
}
}
void SAutomationWindow::HandleRequesteFilterChanged(TSharedPtr<FString> Item, ESelectInfo::Type SelectInfo)
{
const int32 EntryIndex = RequestedFilterComboList.Find(Item);
uint32 NewRequestedFlags = EAutomationTestFlags::SmokeFilter;
switch (EntryIndex)
{
case 0: // "All Tests"
NewRequestedFlags = EAutomationTestFlags::FilterMask;
break;
case 1: // "Smoke Tests"
NewRequestedFlags = EAutomationTestFlags::SmokeFilter;
break;
case 2: // "Engine Tests"
NewRequestedFlags = EAutomationTestFlags::EngineFilter;
break;
case 3: // "Product Tests"
NewRequestedFlags = EAutomationTestFlags::ProductFilter;
break;
case 4: // "Performance Tests"
NewRequestedFlags = EAutomationTestFlags::PerfFilter;
break;
case 5: // "Stress Tests"
NewRequestedFlags = EAutomationTestFlags::StressFilter;
break;
case 6: // "Standard Tests"
NewRequestedFlags = EAutomationTestFlags::SmokeFilter | EAutomationTestFlags::EngineFilter | EAutomationTestFlags::ProductFilter | EAutomationTestFlags::PerfFilter;
break;
}
AutomationController->SetRequestedTestFlags(NewRequestedFlags);
}
void SAutomationWindow::ExpandEnabledTests( TSharedPtr< IAutomationReport > InReport )
{
// Expand node if the report is enabled or contains an enabled test
TestTable->SetItemExpansion( InReport, InReport->IsEnabled() || InReport->GetEnabledTestsNum() > 0 );
// Iterate through the child nodes to see if they should be expanded
TArray<TSharedPtr< IAutomationReport > > Reports = InReport->GetFilteredChildren();
for ( int32 ChildItem = 0; ChildItem < Reports.Num(); ChildItem++ )
{
ExpandEnabledTests( Reports[ ChildItem ] );
}
}
FReply SAutomationWindow::HandleNewPresetClicked()
{
bAddingTestPreset = true;
return FReply::Handled().SetUserFocus(PresetTextBox.ToSharedRef(), EFocusCause::SetDirectly);
}
FReply SAutomationWindow::HandleSavePresetClicked()
{
if(SelectedPreset.IsValid())
{
TArray<FString> EnabledTests;
AutomationController->GetEnabledTestNames(EnabledTests);
SelectedPreset->SetEnabledTests(EnabledTests);
TestPresetManager->SavePreset(SelectedPreset.ToSharedRef());
}
return FReply::Handled();
}
FReply SAutomationWindow::HandleRemovePresetClicked()
{
if(SelectedPreset.IsValid())
{
TestPresetManager->RemovePreset(SelectedPreset.ToSharedRef());
SelectedPreset = nullptr;
PresetComboBox->ClearSelection();
}
return FReply::Handled();
}
FText SAutomationWindow::GetPresetComboText() const
{
if ( SelectedPreset.IsValid() )
{
return SelectedPreset->GetName();
}
else
{
return LOCTEXT("AutomationPresetComboLabel", "None");
}
}
FText SAutomationWindow::GetRequestedFilterComboText() const
{
if (RequestedFilterComboBox->GetSelectedItem().IsValid())
{
return FText::FromString(*RequestedFilterComboBox->GetSelectedItem());
}
else
{
return LOCTEXT("AutomationRequestedFilterComboLabel", "All Tests");
}
}
TSharedRef<SWidget> SAutomationWindow::GeneratePresetComboItem(TSharedPtr<FAutomationTestPreset> InItem)
{
if ( InItem.IsValid() )
{
return SNew(STextBlock)
.Text(InItem->GetName());
}
else
{
return SNew(STextBlock)
.Text(LOCTEXT("AutomationPreset_None", "None"));
}
}
TSharedRef<SWidget> SAutomationWindow::GenerateRequestedFilterComboItem(TSharedPtr<FString> InItem)
{
return SNew(STextBlock)
.Text(FText::FromString(*InItem));
}
TSharedRef< SWidget > SAutomationWindow::GenerateGroupOptionsMenuContent( TWeakPtr<class SAutomationWindow> InAutomationWindow )
{
TSharedPtr<SAutomationWindow> AutomationWindow(InAutomationWindow.Pin());
if( AutomationWindow.IsValid() )
{
return AutomationWindow->GenerateGroupOptionsMenuContent();
}
//Return empty menu
FMenuBuilder MenuBuilder( true, nullptr );
MenuBuilder.BeginSection("AutomationWindowGroupOptions", LOCTEXT("DeviceGroupOptions", "Device Group Options"));
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
TSharedRef< SWidget > SAutomationWindow::GenerateGroupOptionsMenuContent( )
{
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, AutomationWindowActions );
const uint32 NumFlags = EAutomationDeviceGroupTypes::Max;
TSharedPtr<SWidget> FlagWidgets[NumFlags];
for( int32 i=0; i<NumFlags; i++ )
{
FlagWidgets[i] =
SNew(SCheckBox)
.IsChecked(this, &SAutomationWindow::IsDeviceGroupCheckBoxIsChecked, i)
.OnCheckStateChanged(this, &SAutomationWindow::HandleDeviceGroupCheckStateChanged, i)
.Padding(FMargin(4.0f, 0.0f))
.ToolTipText(EAutomationDeviceGroupTypes::ToDescription((EAutomationDeviceGroupTypes::Type)i))
.IsEnabled( this, &SAutomationWindow::IsAutomationControllerIdle )
.Content()
[
SNew(STextBlock)
.Text(EAutomationDeviceGroupTypes::ToName((EAutomationDeviceGroupTypes::Type)i))
];
}
MenuBuilder.BeginSection("AutomationWindowGroupDevices", LOCTEXT("GroupTypeOptions", "Group Types"));
{
for( int32 i=0; i<NumFlags;i++ )
{
MenuBuilder.AddWidget(FlagWidgets[i].ToSharedRef(),FText::GetEmpty());
}
}
return MenuBuilder.MakeWidget();
}
/** Returns if full size screen shots are enabled */
ECheckBoxState SAutomationWindow::IsDeviceGroupCheckBoxIsChecked(const int32 DeviceGroupFlag) const
{
return AutomationController->IsDeviceGroupFlagSet((EAutomationDeviceGroupTypes::Type)DeviceGroupFlag) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
/** Toggles if we are collecting full size screenshots */
void SAutomationWindow::HandleDeviceGroupCheckStateChanged(ECheckBoxState CheckBoxState, const int32 DeviceGroupFlag)
{
//Update the device groups
AutomationController->ToggleDeviceGroupFlag((EAutomationDeviceGroupTypes::Type)DeviceGroupFlag);
AutomationController->UpdateDeviceGroups();
//Update header
RebuildPlatformIcons();
//Need to force the tree to do a full refresh here because the reports have changed but the tree will keep using cached data.
TestTable->ReCreateTreeView();
}
TSharedRef< SWidget > SAutomationWindow::GenerateTestsOptionsMenuContent( TWeakPtr<class SAutomationWindow> InAutomationWindow )
{
TSharedPtr<SAutomationWindow> AutomationWindow(InAutomationWindow.Pin());
if( AutomationWindow.IsValid() )
{
return AutomationWindow->GenerateTestsOptionsMenuContent();
}
//Return empty menu
FMenuBuilder MenuBuilder( true, nullptr );
MenuBuilder.BeginSection("AutomationWindowRunTest", LOCTEXT("RunTestOptions", "Advanced Settings"));
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
TSharedRef< SWidget > SAutomationWindow::GenerateTestsOptionsMenuContent( )
{
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, AutomationWindowActions );
TSharedRef<SWidget> NumTests =
SNew(SBox)
.WidthOverride( 200.0f )
[
SNew( SHorizontalBox )
+SHorizontalBox::Slot()
.Padding(0.0f,0.0f,4.0f, 0.0f)
.AutoWidth()
[
SNew( STextBlock )
.Text( LOCTEXT("NumTestsToolTip", "Number of runs:") )
]
+SHorizontalBox::Slot()
.FillWidth(1.f)
[
SNew(SSpinBox<int32>)
.MinValue(1)
.MaxValue(1000)
.MinSliderValue(1)
.MaxSliderValue(1000)
.Value(this,&SAutomationWindow::GetRepeatCount)
.OnValueChanged(this,&SAutomationWindow::OnChangeRepeatCount)
.IsEnabled( this, &SAutomationWindow::IsAutomationControllerIdle )
]
];
TSharedRef<SWidget> SendAnalyticsWidget =
SNew(SCheckBox)
.IsChecked(this, &SAutomationWindow::IsSendAnalyticsCheckBoxChecked)
.OnCheckStateChanged(this, &SAutomationWindow::HandleSendAnalyticsBoxCheckStateChanged)
.Padding(FMargin(4.0f, 0.0f))
.ToolTipText(LOCTEXT("AutomationSendAnalyticsTip", "If checked, tests send analytics results to the backend"))
.IsEnabled(this, &SAutomationWindow::IsAutomationControllerIdle)
.Content()
[
SNew(STextBlock)
.Text(LOCTEXT("AutomationSendAnalyticsText", "Enable analytics"))
];
MenuBuilder.BeginSection("AutomationWindowRunTest", LOCTEXT("RunTestOptions", "Advanced Settings"));
{
MenuBuilder.AddWidget(NumTests, FText::GetEmpty());
MenuBuilder.AddWidget(SendAnalyticsWidget, FText::GetEmpty());
}
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
ECheckBoxState SAutomationWindow::IsSendAnalyticsCheckBoxChecked() const
{
return AutomationController->IsSendAnalytics() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
void SAutomationWindow::HandleSendAnalyticsBoxCheckStateChanged(ECheckBoxState CheckBoxState)
{
AutomationController->SetSendAnalytics(CheckBoxState == ECheckBoxState::Checked);
}
TArray<FString> SAutomationWindow::SaveExpandedTestNames(TSet<TSharedPtr<IAutomationReport>> ExpandedItems)
{
TArray<FString> ExpandedItemsNames;
for ( TSharedPtr<IAutomationReport> ExpandedItem : ExpandedItems )
{
ExpandedItemsNames.Add(ExpandedItem->GetDisplayNameWithDecoration());
}
return ExpandedItemsNames;
}
// Expanded the given item if its name is in the array of strings given.
void SAutomationWindow::ExpandItemsInList(TSharedPtr<SAutomationTestTreeView<TSharedPtr<IAutomationReport>>> InTestTable, TSharedPtr<IAutomationReport> InReport, TArray<FString> ItemsToExpand)
{
InTestTable->SetItemExpansion(InReport, ItemsToExpand.Contains(InReport->GetDisplayNameWithDecoration()));
TArray<TSharedPtr<IAutomationReport>> ChildReports = InReport->GetFilteredChildren();
for ( int32 Index = 0; Index < ChildReports.Num(); Index++ )
{
ExpandItemsInList(InTestTable, ChildReports[Index], ItemsToExpand);
}
}
// Only valid in the editor
#if WITH_EDITOR
TSharedPtr<SWidget> SAutomationWindow::HandleAutomationListContextMenuOpening()
{
TArray< TSharedPtr<IAutomationReport> >SelectedReport = TestTable->GetSelectedItems();
TArray<FString> AssetNames;
for (int32 ReportIndex = 0; ReportIndex < SelectedReport.Num(); ++ReportIndex)
{
// TODO This is super sketch, we were interpreting the parameter always as the asset, this is no good.
if (SelectedReport[ReportIndex].IsValid() && (SelectedReport[ReportIndex]->GetTestParameter().Len() > 0))
{
AssetNames.Add(SelectedReport[ReportIndex]->GetTestParameter());
}
}
if (AssetNames.Num())
{
return SNew(SAutomationTestItemContextMenu, AssetNames);
}
return nullptr;
}
void SAutomationWindow::RunSelectedTests()
{
AutomationController->SetVisibleTestsEnabled(false);
SetAllSelectedTestsChecked(true);
RunTests();
}
namespace
{
bool MakeMapPathUrl(FString& InPath)
{
if ( FPaths::MakePathRelativeTo(InPath, *FPaths::GameContentDir()) )
{
InPath.InsertAt(0, TEXT("/Game/"));
InPath.RemoveFromEnd(TEXT(".umap"));
return true;
}
return false;
}
/**
* Kind of a hack - this requires that we know we group all the map tests coming from blueprints under "Functional Tests"
*/
TSharedPtr<IAutomationReport> GetFunctionalTestsReport(const TArray< TSharedPtr< IAutomationReport > >& TestReports)
{
for ( auto& Report : TestReports )
{
if ( Report->GetDisplayName() == TEXT("Functional Tests") )
{
return Report;
}
auto FoundInChild = GetFunctionalTestsReport(Report->GetChildReports());
if ( FoundInChild.IsValid() )
{
return FoundInChild;
}
}
return TSharedPtr<IAutomationReport>();
}
void FindReportByGameRelativeAssetPath(const TSharedPtr<IAutomationReport>& RootReport, const FString& AssetRelativePath, TArray<TSharedPtr<IAutomationReport>>& OutLevelReports)
{
FString TestAssetRelativePath(RootReport->GetTestParameter());
if ( TestAssetRelativePath.StartsWith(AssetRelativePath) )
{
OutLevelReports.Add(RootReport);
}
else
{
// Branch node
for ( auto ChildReport : RootReport->GetChildReports() )
{
FindReportByGameRelativeAssetPath(ChildReport, AssetRelativePath, OutLevelReports);
}
}
}
} // namespace
void SAutomationWindow::FindTestReportsForCurrentEditorLevel(TArray<TSharedPtr<IAutomationReport>>& OutLevelReports)
{
// Find the current map path
if ( GWorld && GWorld->GetCurrentLevel() )
{
FString MapUrl(FEditorFileUtils::GetFilename(GWorld->GetCurrentLevel()));
if ( MakeMapPathUrl(MapUrl) )
{
auto FunctionTestsReport = GetFunctionalTestsReport(AutomationController->GetReports());
if ( FunctionTestsReport.IsValid() )
{
FindReportByGameRelativeAssetPath(FunctionTestsReport, MapUrl, OutLevelReports);
}
}
}
}
bool SAutomationWindow::CanExecuteRunLevelTest()
{
return IsAutomationControllerIdle();
}
void SAutomationWindow::OnRunLevelTest()
{
TArray<TSharedPtr<IAutomationReport>> LevelReports;
FindTestReportsForCurrentEditorLevel(LevelReports);
if ( LevelReports.Num() > 0 )
{
TestTable->ClearSelection();
for ( auto& LevelReport : LevelReports )
{
TestTable->SetItemSelection(LevelReport, true);
}
ScrollToTest(LevelReports[0]);
RunSelectedTests();
}
}
void SAutomationWindow::ScrollToTest(TSharedPtr<IAutomationReport> InReport)
{
auto& RootReports = AutomationController->GetReports();
for ( auto ChildReport : RootReports )
{
auto ShouldExpand = ExpandToTest(ChildReport, InReport);
TestTable->SetItemExpansion(ChildReport, ShouldExpand);
}
TestTable->RequestScrollIntoView(InReport);
}
bool SAutomationWindow::ExpandToTest(TSharedPtr<IAutomationReport> InRoot, TSharedPtr<IAutomationReport> InReport)
{
if ( InRoot == InReport )
return true;
bool WasExpanded = false;
for ( auto ChildReport : InRoot->GetChildReports() )
{
auto ShouldExpand = ExpandToTest(ChildReport, InReport);
TestTable->SetItemExpansion(ChildReport, ShouldExpand);
if ( ShouldExpand )
{
// Here we could just return true, but we want to collapse all the other reports
// so we keep going and just remember that we found the test.
WasExpanded = true;
}
}
return WasExpanded;
}
#endif
void SAutomationWindow::PopulateReportSearchStrings( const TSharedPtr< IAutomationReport >& Report, OUT TArray< FString >& OutSearchStrings ) const
{
if( !Report.IsValid() )
{
return;
}
OutSearchStrings.Add( Report->GetDisplayName() );
OutSearchStrings.Add( Report->GetFullTestPath() );
}
void SAutomationWindow::OnGetChildren(TSharedPtr<IAutomationReport> InItem, TArray<TSharedPtr<IAutomationReport> >& OutItems)
{
OutItems = InItem->GetFilteredChildren();
}
void SAutomationWindow::OnTestExpansionRecursive(TSharedPtr<IAutomationReport> InAutomationReport, bool bInIsItemExpanded)
{
if ( InAutomationReport.IsValid() )
{
TArray<TSharedPtr<IAutomationReport> >& FilteredChildren = InAutomationReport->GetFilteredChildren();
TestTable->SetItemExpansion(InAutomationReport, bInIsItemExpanded);
for ( TSharedPtr<IAutomationReport>& Child : FilteredChildren )
{
OnTestExpansionRecursive(Child, bInIsItemExpanded);
}
}
}
void SAutomationWindow::OnTestSelectionChanged(TSharedPtr<IAutomationReport> Selection, ESelectInfo::Type /*SelectInfo*/)
{
TSharedPtr<IAutomationReport> PreviousSelectionLock = PreviousSelection.Pin();
if ( PreviousSelectionLock.IsValid() )
{
PreviousSelectionLock->OnSetResults.Unbind();
}
bHasChildTestSelected = false;
UpdateTestLog(Selection);
if ( Selection.IsValid() )
{
Selection->OnSetResults.BindRaw(this, &SAutomationWindow::UpdateTestLog);
PreviousSelection = Selection;
if ( Selection->GetTotalNumChildren() == 0 )
{
bHasChildTestSelected = true;
}
}
}
void SAutomationWindow::UpdateTestLog(TSharedPtr<IAutomationReport> Selection)
{
//empty the previous log
LogMessages.Empty();
if (Selection.IsValid())
{
//accumulate results for each device cluster that supports the test
int32 NumClusters = AutomationController->GetNumDeviceClusters();
for (int32 ClusterIndex = 0; ClusterIndex < NumClusters; ++ClusterIndex)
{
//no sense displaying device name if only one is available
if (NumClusters > 1)
{
FString DeviceTypeName = AutomationController->GetClusterGroupName(ClusterIndex) + TEXT(" - ") + Selection->GetGameInstanceName(ClusterIndex);
LogMessages.Add(MakeShareable(new FAutomationOutputMessage(DeviceTypeName, TEXT("Automation.Header"))));
}
const int32 NumOfPasses = Selection->GetNumResults(ClusterIndex);
for( int32 PassIndex = 0; PassIndex < NumOfPasses; ++PassIndex )
{
//get strings out of the report and populate the Log Messages
FAutomationTestResults TestResults = Selection->GetResults(ClusterIndex,PassIndex);
//no sense displaying device name if only one is available
if (NumOfPasses > 1)
{
FString PassHeader = LOCTEXT("TestPassHeader", "Pass:").ToString();
PassHeader += FString::Printf(TEXT("%i"),PassIndex+1);
LogMessages.Add(MakeShareable(new FAutomationOutputMessage(PassHeader, TEXT("Automation.Header"))));
}
for (const FAutomationEvent& Event : TestResults.GetEvents())
{
switch ( Event.Type )
{
case EAutomationEventType::Info:
LogMessages.Add(MakeShareable(new FAutomationOutputMessage(Event.ToString(), TEXT("Automation.Normal"))));
break;
case EAutomationEventType::Warning:
LogMessages.Add(MakeShareable(new FAutomationOutputMessage(Event.ToString(), TEXT("Automation.Warning"))));
break;
case EAutomationEventType::Error:
LogMessages.Add(MakeShareable(new FAutomationOutputMessage(Event.ToString(), TEXT("Automation.Error"))));
break;
}
}
if ( ( TestResults.GetWarningTotal() == 0 ) && ( TestResults.GetErrorTotal() == 0 ) && ( Selection->GetState(ClusterIndex, PassIndex) == EAutomationState::Success ) )
{
LogMessages.Add(MakeShareable(new FAutomationOutputMessage(LOCTEXT("AutomationTest_SuccessMessage", "Success").ToString(), TEXT("Automation.Normal"))));
}
LogMessages.Add(MakeShareable(new FAutomationOutputMessage(TEXT(""), TEXT("Log.Normal"))));
}
}
}
//rebuild UI
LogListView->RequestListRefresh();
}
EVisibility SAutomationWindow::GetTestLogVisibility( ) const
{
return (GetTestGraphVisibility() == EVisibility::Visible) ? EVisibility::Hidden : EVisibility::Visible;
}
EVisibility SAutomationWindow::GetTestGraphVisibility( ) const
{
//Show the graphical window if we don't have a child test selected and we have results to view
return (!bHasChildTestSelected && GraphicalResultBox->HasResults()) ? EVisibility::Visible : EVisibility::Hidden;
}
void SAutomationWindow::HeaderCheckboxStateChange(ECheckBoxState InCheckboxState)
{
const bool bState = (InCheckboxState == ECheckBoxState::Checked)? true : false;
AutomationController->SetVisibleTestsEnabled(bState);
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SAutomationWindow::RebuildPlatformIcons()
{
//empty header UI
PlatformsHBox->ClearChildren();
//for each device type
int32 NumClusters = AutomationController->GetNumDeviceClusters();
for (int32 ClusterIndex = 0; ClusterIndex < NumClusters; ++ClusterIndex)
{
//find the right platform icon
FString DeviceImageName = TEXT("Launcher.Platform_");
FString DeviceTypeName = AutomationController->GetDeviceTypeName(ClusterIndex);
DeviceImageName += DeviceTypeName;
const FSlateBrush* ImageToUse = FEditorStyle::GetBrush(*DeviceImageName);
PlatformsHBox->AddSlot()
.AutoWidth()
.MaxWidth(ColumnWidth)
[
SNew( SOverlay )
+ SOverlay::Slot()
[
SNew(SBorder)
.BorderImage( FEditorStyle::GetBrush("ErrorReporting.Box") )
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Padding( FMargin(3,0) )
.BorderBackgroundColor( FSlateColor( FLinearColor( 1.0f, 0.0f, 1.0f, 0.0f ) ) )
.ToolTipText( CreateDeviceTooltip( ClusterIndex ) )
[
SNew(SImage)
.Image(ImageToUse)
]
]
+ SOverlay::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Bottom)
[
//Overlay how many devices are in the cluster
SNew( STextBlock )
.Text( this, &SAutomationWindow::OnGetNumDevicesInClusterString, ClusterIndex )
]
];
}
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
FText SAutomationWindow::CreateDeviceTooltip(int32 ClusterIndex)
{
FTextBuilder ReportBuilder;
const int32 NumClusters = AutomationController->GetNumDeviceClusters();
if( NumClusters > 1 )
{
ReportBuilder.AppendLine(LOCTEXT("ToolTipClusterName", "Cluster Name:"));
ReportBuilder.AppendLine(AutomationController->GetClusterGroupName(ClusterIndex));
}
ReportBuilder.AppendLine(LOCTEXT("ToolTipGameInstances", "Game Instances:"));
int32 NumDevices = AutomationController->GetNumDevicesInCluster( ClusterIndex );
for ( int32 DeviceIndex = 0; DeviceIndex < NumDevices; ++DeviceIndex )
{
ReportBuilder.AppendLine(AutomationController->GetGameInstanceName(ClusterIndex, DeviceIndex).LeftPad(2));
}
return ReportBuilder.ToText();
}
void SAutomationWindow::ClearAutomationUI ()
{
// Clear results from the automation controller
AutomationController->ClearAutomationReports();
TestTable->RequestTreeRefresh();
// Clear the platform icons
if (PlatformsHBox.IsValid())
{
PlatformsHBox->ClearChildren();
}
// Clear the log
LogMessages.Empty();
LogListView->RequestListRefresh();
}
TSharedRef<ITableRow> SAutomationWindow::OnGenerateWidgetForTest( TSharedPtr<IAutomationReport> InItem, const TSharedRef<STableViewBase>& OwnerTable )
{
bIsRequestingTests = false;
return SNew( SAutomationTestItem, OwnerTable )
.TestStatus( InItem )
.ColumnWidth( ColumnWidth )
.HighlightText(this, &SAutomationWindow::HandleAutomationHighlightText)
.OnCheckedStateChanged(this, &SAutomationWindow::HandleItemCheckBoxCheckedStateChanged);
}
TSharedRef<ITableRow> SAutomationWindow::OnGenerateWidgetForLog(TSharedPtr<FAutomationOutputMessage> Message, const TSharedRef<STableViewBase>& OwnerTable)
{
check(Message.IsValid());
// ^((?:[\w]\:|\\)(?:(?:\\[a-z_\-\s0-9\.]+)+)\.(?:cpp|h))\((\d+)\)
// https://regex101.com/r/vV4cV7/1
FRegexPattern FileAndLinePattern(TEXT("^((?:[\\w]\\:|\\\\)(?:(?:\\\\[a-z_\\-\\s0-9\\.]+)+)\\.(?:cpp|h))\\((\\d+)\\)"));
FRegexMatcher FileAndLineRegexMatcher(FileAndLinePattern, Message->Text);
TSharedRef<SWidget> SourceLink = SNullWidget::NullWidget;
FString MessageString = Message->Text;
if ( FileAndLineRegexMatcher.FindNext() )
{
FString FileName = FileAndLineRegexMatcher.GetCaptureGroup(1);
int32 LineNumber = FCString::Atoi(*FileAndLineRegexMatcher.GetCaptureGroup(2));
// Remove the hyperlink from the message, since we're splitting it into its own string.
MessageString = MessageString.RightChop(FileAndLineRegexMatcher.GetMatchEnding());
SourceLink = SNew(SHyperlink)
.Style(FEditorStyle::Get(), "Common.GotoNativeCodeHyperlink")
.TextStyle(FEditorStyle::Get(), Message->Style)
.OnNavigate_Lambda([=] { FSlateApplication::Get().GotoLineInSource(FileName, LineNumber); })
.Text(FText::FromString(FileAndLineRegexMatcher.GetCaptureGroup(0)));
}
return SNew(STableRow<TSharedPtr<FAutomationOutputMessage> >, OwnerTable)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0)
[
SourceLink
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0)
[
SNew(STextBlock)
.TextStyle( FEditorStyle::Get(), Message->Style )
.Text(FText::FromString(MessageString))
]
];
}
FText SAutomationWindow::OnGetNumEnabledTestsString() const
{
int32 NumPasses = AutomationController->GetNumPasses();
if( NumPasses > 1 )
{
return FText::Format(LOCTEXT("NumEnabledTestsFmt", "{0} x{1}"), FText::AsNumber(AutomationController->GetEnabledTestsNum()), FText::AsNumber(NumPasses));
}
else
{
return FText::AsNumber(AutomationController->GetEnabledTestsNum());
}
}
FText SAutomationWindow::OnGetNumDevicesInClusterString(const int32 ClusterIndex) const
{
return FText::AsNumber(AutomationController->GetNumDevicesInCluster(ClusterIndex));
}
void SAutomationWindow::OnRefreshTestCallback()
{
//if the window hasn't been created yet
if (!PlatformsHBox.IsValid())
{
return;
}
//rebuild the platform header
RebuildPlatformIcons();
//filter the tests that are shown
AutomationController->SetFilter( AutomationFilters );
// Only expand the child nodes if we have a text filter
bool ExpandChildren = !AutomationTextFilter->GetRawFilterText().IsEmpty();
TArray< TSharedPtr< IAutomationReport > >& TestReports = AutomationController->GetReports();
for( int32 Index = 0; Index < TestReports.Num(); Index++ )
{
ExpandTreeView( TestReports[ Index ], ExpandChildren );
// Expand any items that where expanded before refresh tests was pressed.
if( !ExpandChildren )
{
ExpandItemsInList( TestTable, TestReports[Index], SavedExpandedItems );
}
}
// Check Tests that where checked before refresh tests was pressed.
AutomationController->SetEnabledTests(SavedEnabledTests);
SavedEnabledTests.Empty();
SavedExpandedItems.Empty();
//rebuild the UI
TestTable->RequestTreeRefresh();
//update the background style
UpdateTestListBackgroundStyle();
}
void SAutomationWindow::OnTestAvailableCallback( EAutomationControllerModuleState::Type InAutomationControllerState )
{
AutomationControllerState = InAutomationControllerState;
// Only list tests on opening the Window if the asset registry isn't in the middle of loading tests.
if ( InAutomationControllerState == EAutomationControllerModuleState::Ready && AutomationController->GetReports().Num() == 0 )
{
#if WITH_EDITOR
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
if ( !AssetRegistryModule.Get().IsLoadingAssets() )
{
ListTests();
}
#else
ListTests();
#endif
}
}
void SAutomationWindow::OnTestsCompleteCallback()
{
// Simulate selection again after testing finishes.
if ( TestTable->GetNumItemsSelected() > 0 )
{
OnTestSelectionChanged(TestTable->GetSelectedItems()[0], ESelectInfo::Direct);
}
}
void SAutomationWindow::ExpandTreeView( TSharedPtr< IAutomationReport > InReport, const bool ShouldExpand )
{
// Expand node if the report is flagged
TestTable->SetItemExpansion( InReport, ShouldExpand && InReport->ExpandInUI() );
// Iterate through the child nodes to see if they should be expanded
TArray<TSharedPtr< IAutomationReport > > Reports = InReport->GetFilteredChildren();
for ( int32 ChildItem = 0; ChildItem < Reports.Num(); ChildItem++ )
{
ExpandTreeView( Reports[ ChildItem ], ShouldExpand );
}
}
//TODO AUTOMATION - remove
/** Updates list of all the tests */
void SAutomationWindow::ListTests( )
{
// Save Expanded and Enabled Test Names
AutomationController->GetEnabledTestNames(SavedEnabledTests);
TSet<TSharedPtr<IAutomationReport>> ExpandedItems;
TestTable->GetExpandedItems(ExpandedItems);
SavedExpandedItems = SaveExpandedTestNames(ExpandedItems);
AutomationController->RequestTests();
}
//TODO AUTOMATION - remove
/** Finds available workers */
void SAutomationWindow::FindWorkers()
{
ActiveSession = SessionManager->GetSelectedSession();
bool SessionIsValid = ActiveSession.IsValid() && (ActiveSession->GetSessionOwner() == FPlatformProcess::UserName(false));
if (SessionIsValid)
{
bIsRequestingTests = true;
AutomationController->RequestAvailableWorkers(ActiveSession->GetSessionId());
RebuildPlatformIcons();
}
else
{
bIsRequestingTests = false;
// Clear UI if the session is invalid
ClearAutomationUI();
}
MenuBar->SetEnabled( SessionIsValid );
}
void SAutomationWindow::HandleSessionManagerInstanceChanged()
{
UpdateTestListBackgroundStyle();
}
void SAutomationWindow::UpdateTestListBackgroundStyle()
{
TArray<TSharedPtr<ISessionInstanceInfo>> OutInstances;
if( ActiveSession.IsValid() )
{
ActiveSession->GetInstances(OutInstances);
}
TestBackgroundType = EAutomationTestBackgroundStyle::Unknown;
if( OutInstances.Num() > 0 )
{
FString FirstInstanceType = OutInstances[0]->GetInstanceType();
if( FirstInstanceType.Contains(TEXT("Editor")) )
{
TestBackgroundType = EAutomationTestBackgroundStyle::Editor;
}
else if( FirstInstanceType.Contains(TEXT("Game")) )
{
TestBackgroundType = EAutomationTestBackgroundStyle::Game;
}
}
}
FReply SAutomationWindow::RunTests()
{
if( AutomationControllerState == EAutomationControllerModuleState::Running )
{
AutomationController->StopTests();
}
else
{
// Prompt to save current map when running a test.
#if WITH_EDITOR
if ( !GIsDemoMode )
{
// If there are any unsaved changes to the current level, see if the user wants to save those first.
const bool bPromptUserToSave = true;
const bool bSaveMapPackages = true;
const bool bSaveContentPackages = true;
if ( FEditorFileUtils::SaveDirtyPackages(bPromptUserToSave, bSaveMapPackages, bSaveContentPackages) == false )
{
// something went wrong or the user pressed cancel. Return to the editor so the user doesn't lose their changes
return FReply::Handled();
}
}
#endif
AutomationController->RunTests( ActiveSession->IsStandalone() );
}
LogMessages.Empty();
LogListView->RequestListRefresh();
//Clear old results
GraphicalResultBox->ClearResults();
return FReply::Handled();
}
/** Filtering */
void SAutomationWindow::OnFilterTextChanged( const FText& InFilterText )
{
AutomationTextFilter->SetRawFilterText( InFilterText );
AutomationSearchBox->SetError( AutomationTextFilter->GetFilterErrorText() );
//update the widget
OnRefreshTestCallback();
}
bool SAutomationWindow::IsDeveloperDirectoryIncluded() const
{
return AutomationController->IsDeveloperDirectoryIncluded();
}
void SAutomationWindow::OnToggleDeveloperDirectoryIncluded()
{
//Change controller filter
AutomationController->SetDeveloperDirectoryIncluded(!IsDeveloperDirectoryIncluded());
// need to call this to request update
ListTests();
}
bool SAutomationWindow::IsSmokeTestFilterOn() const
{
return AutomationGeneralFilter->OnlyShowSmokeTests();
}
void SAutomationWindow::OnToggleSmokeTestFilter()
{
AutomationGeneralFilter->SetOnlyShowSmokeTests( !IsSmokeTestFilterOn() );
OnRefreshTestCallback();
}
bool SAutomationWindow::IsWarningFilterOn() const
{
return AutomationGeneralFilter->ShouldShowWarnings();
}
void SAutomationWindow::OnToggleWarningFilter()
{
AutomationGeneralFilter->SetShowWarnings( !IsWarningFilterOn() );
OnRefreshTestCallback();
}
bool SAutomationWindow::IsErrorFilterOn() const
{
return AutomationGeneralFilter->ShouldShowErrors();
}
void SAutomationWindow::OnToggleErrorFilter()
{
AutomationGeneralFilter->SetShowErrors( !IsErrorFilterOn() );
OnRefreshTestCallback();
}
void SAutomationWindow::OnChangeRepeatCount(int32 InNewValue)
{
AutomationController->SetNumPasses(InNewValue);
}
int32 SAutomationWindow::GetRepeatCount() const
{
return AutomationController->GetNumPasses();
}
FString SAutomationWindow::GetSmallIconExtension() const
{
FString Brush;
if (FMultiBoxSettings::UseSmallToolBarIcons.Get())
{
Brush += TEXT( ".Small" );
}
return Brush;
}
EVisibility SAutomationWindow::GetLargeToolBarVisibility() const
{
return FMultiBoxSettings::UseSmallToolBarIcons.Get() ? EVisibility::Collapsed : EVisibility::Visible;
}
const FSlateBrush* SAutomationWindow::GetRunAutomationIcon() const
{
FString Brush = TEXT( "AutomationWindow" );
if( AutomationControllerState == EAutomationControllerModuleState::Running )
{
Brush += TEXT( ".StopTests" ); // Temporary brush type for stop tests
}
else
{
Brush += TEXT( ".RunTests" );
}
Brush += GetSmallIconExtension();
return FEditorStyle::GetBrush( *Brush );
}
FText SAutomationWindow::GetRunAutomationLabel() const
{
if( AutomationControllerState == EAutomationControllerModuleState::Running )
{
return LOCTEXT( "RunStopTestsLabel", "Stop Tests" );
}
else
{
return LOCTEXT( "RunStartTestsLabel", "Start Tests" );
}
}
FText SAutomationWindow::HandleAutomationHighlightText( ) const
{
if ( AutomationSearchBox.IsValid() )
{
return AutomationSearchBox->GetText();
}
return FText();
}
EVisibility SAutomationWindow::HandleSelectSessionOverlayVisibility( ) const
{
if (SessionManager->GetSelectedInstances().Num() > 0)
{
return EVisibility::Hidden;
}
return EVisibility::Visible;
}
void SAutomationWindow::HandleSessionManagerCanSelectSession( const TSharedPtr<ISessionInfo>& Session, bool& CanSelect )
{
if (ActiveSession.IsValid() && AutomationController->CheckTestResultsAvailable())
{
EAppReturnType::Type Result = FMessageDialog::Open(EAppMsgType::YesNo, LOCTEXT("ChangeSessionDialog", "Are you sure you want to change sessions?\nAll automation results data will be lost"));
CanSelect = Result == EAppReturnType::Yes ? true : false;
}
}
void SAutomationWindow::HandleSessionManagerSelectionChanged( const TSharedPtr<ISessionInfo>& SelectedSession )
{
FindWorkers();
}
bool SAutomationWindow::IsAutomationControllerIdle() const
{
return AutomationControllerState != EAutomationControllerModuleState::Running;
}
bool SAutomationWindow::IsAutomationRunButtonEnabled() const
{
return AutomationControllerState != EAutomationControllerModuleState::Disabled;
}
void SAutomationWindow::CopyLog( )
{
TArray<TSharedPtr<FAutomationOutputMessage> > SelectedItems = LogListView->GetSelectedItems();
if (SelectedItems.Num() > 0)
{
FString SelectedText;
for( int32 Index = 0; Index < SelectedItems.Num(); ++Index )
{
SelectedText += SelectedItems[Index]->Text;
SelectedText += LINE_TERMINATOR;
}
FPlatformMisc::ClipboardCopy( *SelectedText );
}
}
FReply SAutomationWindow::HandleCommandBarCopyLogClicked( )
{
CopyLog();
return FReply::Handled();
}
void SAutomationWindow::HandleLogListSelectionChanged( TSharedPtr<FAutomationOutputMessage> InItem, ESelectInfo::Type SelectInfo )
{
CommandBar->SetNumLogMessages(LogListView->GetNumItemsSelected());
}
void SAutomationWindow::ChangeTheSelectionToThisRow(TSharedPtr< IAutomationReport > ThisRow)
{
TestTable->SetSelection(ThisRow, ESelectInfo::Direct);
}
bool SAutomationWindow::IsRowSelected(TSharedPtr< IAutomationReport > ThisRow)
{
TArray< TSharedPtr<IAutomationReport> >SelectedReport = TestTable->GetSelectedItems();
bool ThisRowIsInTheSelectedSet = false;
for (int i = 0; i<SelectedReport.Num();++i)
{
if (SelectedReport[i] == ThisRow)
{
ThisRowIsInTheSelectedSet = true;
}
}
return ThisRowIsInTheSelectedSet;
}
void SAutomationWindow::SetAllSelectedTestsChecked( bool InChecked )
{
TArray< TSharedPtr<IAutomationReport> >SelectedReport = TestTable->GetSelectedItems();
for (int i = 0; i<SelectedReport.Num();++i)
{
if (SelectedReport[i].IsValid())
{
SelectedReport[i]->SetEnabled(InChecked);
}
}
}
bool SAutomationWindow::IsAnySelectedRowEnabled()
{
TArray< TSharedPtr<IAutomationReport> >SelectedReport = TestTable->GetSelectedItems();
//Do check or uncheck selected rows based on current settings
bool bFoundCheckedRow = false;
bool bFoundNotCheckedRow = false;
bool bRowCheckedValue = true;
//Check all the rows if there is a mixture of checked and unchecked then we set all checked, otherwise set to opposite of current values
for (int i = 0; i<SelectedReport.Num();++i)
{
if (SelectedReport[i].IsValid())
{
if (SelectedReport[i]->IsEnabled())
{
bFoundCheckedRow = true;
}
else
{
bFoundNotCheckedRow = true;
}
}
//break when all rows checked or different values found
if (bFoundCheckedRow && bFoundNotCheckedRow)
{
break;
}
}
//if rows were all checked set to unchecked otherwise we can set to checked
if (bFoundCheckedRow && !bFoundNotCheckedRow)
{
bRowCheckedValue = false;
}
return bRowCheckedValue;
}
/* SWidget implementation
*****************************************************************************/
FReply SAutomationWindow::OnKeyUp( const FGeometry& InGeometry, const FKeyEvent& InKeyEvent )
{
if (InKeyEvent.GetKey() == EKeys::SpaceBar)
{
SetAllSelectedTestsChecked(IsAnySelectedRowEnabled());
return FReply::Handled();
}
return FReply::Unhandled();
}
FReply SAutomationWindow::OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent )
{
if (InKeyEvent.IsControlDown())
{
if (InKeyEvent.GetKey() == EKeys::C)
{
CopyLog();
return FReply::Handled();
}
}
return FReply::Unhandled();
}
/* SAutomationWindow callbacks
*****************************************************************************/
void SAutomationWindow::HandleItemCheckBoxCheckedStateChanged( TSharedPtr< IAutomationReport > TestStatus )
{
//If multiple rows selected then handle all the rows
if (AreMultipleRowsSelected())
{
//if current row is not in the selected list select that row
if(IsRowSelected(TestStatus))
{
//Just set them all to the opposite of the row just clicked.
SetAllSelectedTestsChecked(!TestStatus->IsEnabled());
}
else
{
//Change the selection to this row rather than keep other rows selected unrelated to the ticked/unticked item
ChangeTheSelectionToThisRow(TestStatus);
TestStatus->SetEnabled( !TestStatus->IsEnabled() );
}
}
else
{
TestStatus->SetEnabled( !TestStatus->IsEnabled() );
}
}
bool SAutomationWindow::HandleItemCheckBoxIsEnabled( ) const
{
return IsAutomationControllerIdle();
}
bool SAutomationWindow::HandleMainContentIsEnabled() const
{
return (SessionManager->GetSelectedInstances().Num() > 0);
}
#if WITH_EDITOR
// React to asset registry finishing updating.
// We only want to do this if there are no tests already listed, otherwise this fires every time you save a map for example.
void SAutomationWindow::OnAssetRegistryFileLoadProgress(const IAssetRegistry::FFileLoadProgressUpdateData& ProgressUpdateData)
{
if ( ProgressUpdateData.NumAssetsProcessedByAssetRegistry == ProgressUpdateData.NumTotalAssets && IsAutomationControllerIdle() && AutomationController->GetReports().Num() == 0 )
{
ListTests();
}
}
#endif
#undef LOCTEXT_NAMESPACE