396 lines
14 KiB
C++
396 lines
14 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ToolMenus.h"
|
|
|
|
#include "TestHarness.h"
|
|
|
|
TEST_CASE("Developer::ToolMenus::Can create menu", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
UToolMenu* ToolMenu = UToolMenus::Get()->RegisterMenu("ToolMenusTest_MyMenu");
|
|
|
|
CHECK(ToolMenu);
|
|
}
|
|
|
|
TEST_CASE("Developer::ToolMenus::Non-registered menu is not registered", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
CHECK_FALSE(UToolMenus::Get()->IsMenuRegistered("ToolMenusTest_MyMenu"));
|
|
}
|
|
|
|
TEST_CASE("Developer::ToolMenus::Removed menu is not registered", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
const FName MenuName("ToolMenusTest_MyMenu");
|
|
|
|
UToolMenus::Get()->RegisterMenu(MenuName);
|
|
|
|
CHECK(UToolMenus::Get()->IsMenuRegistered(MenuName));
|
|
|
|
UToolMenus::Get()->RemoveMenu(MenuName);
|
|
|
|
CHECK_FALSE(UToolMenus::Get()->IsMenuRegistered(MenuName));
|
|
}
|
|
|
|
TEST_CASE("Developer::ToolMenus::GenerateWidget calls dynamic section lambdas", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
const FName MenuName("ToolMenusTest_MyMenu");
|
|
|
|
UToolMenu* ToolMenu = UToolMenus::Get()->RegisterMenu(MenuName);
|
|
|
|
REQUIRE(ToolMenu);
|
|
|
|
bool bWasLambdaCalled = false;
|
|
ToolMenu->AddDynamicSection("MySection", FNewToolMenuDelegateLegacy::CreateLambda([&bWasLambdaCalled](FMenuBuilder&, UToolMenu*) {
|
|
bWasLambdaCalled = true;
|
|
}));
|
|
|
|
UToolMenus::Get()->GenerateWidget(MenuName, FToolMenuContext());
|
|
|
|
CHECK(bWasLambdaCalled);
|
|
}
|
|
|
|
TEST_CASE("Developer::ToolMenus::Can add and remove sections to menu", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
UToolMenu* ToolMenu = UToolMenus::Get()->RegisterMenu("ToolMenusTest_MyMenu");
|
|
|
|
REQUIRE(ToolMenu);
|
|
|
|
// Add a section to the menu
|
|
const FName SectionName("MySection");
|
|
|
|
ToolMenu->AddSection(SectionName);
|
|
CHECK(ToolMenu->Sections.Num() == 1);
|
|
|
|
// Check if the section we added is correct
|
|
FToolMenuSection* Section = ToolMenu->FindSection(SectionName);
|
|
REQUIRE(Section);
|
|
CHECK(Section->Name == SectionName);
|
|
|
|
// Check if we are able to remove the section
|
|
ToolMenu->RemoveSection(SectionName);
|
|
CHECK(ToolMenu->Sections.Num() == 0);
|
|
}
|
|
|
|
TEST_CASE("Developer::ToolMenus::Can add entries to menu section", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
UToolMenu* ToolMenu = UToolMenus::Get()->RegisterMenu("ToolMenusTest_MyMenu");
|
|
|
|
REQUIRE(ToolMenu);
|
|
|
|
FToolMenuSection& Section = ToolMenu->AddSection("MySection");
|
|
|
|
// Add the first entry to the menu
|
|
const FName Entry1Name("MyEntry1");
|
|
Section.AddMenuEntry(Entry1Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
CHECK(Section.Blocks.Num() == 1);
|
|
|
|
// Check if the first entry is correct
|
|
FToolMenuEntry* Entry1 = Section.FindEntry(Entry1Name);
|
|
REQUIRE(Entry1);
|
|
CHECK(Entry1->Name == Entry1Name);
|
|
|
|
// Add the second entry to the menu
|
|
const FName Entry2Name("MyEntry2");
|
|
Section.AddMenuEntry(Entry2Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
CHECK(Section.Blocks.Num() == 2);
|
|
|
|
// Check if the second entry is correct
|
|
FToolMenuEntry* Entry2 = Section.FindEntry(Entry2Name);
|
|
REQUIRE(Entry2);
|
|
CHECK(Entry2->Name == Entry2Name);
|
|
}
|
|
|
|
TEST_CASE("Developer::ToolMenus::Can add runtime menu customization", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
const FName MenuName("MyMenu");
|
|
|
|
UToolMenus::Get()->RegisterMenu(MenuName);
|
|
|
|
// Add the customization to the menu
|
|
UToolMenus::Get()->AddRuntimeMenuCustomization(MenuName);
|
|
|
|
// Try and find the customization
|
|
FCustomizedToolMenu* CustomizedToolMenu = UToolMenus::Get()->FindRuntimeMenuCustomization(MenuName);
|
|
CHECK(CustomizedToolMenu);
|
|
}
|
|
|
|
|
|
TEST_CASE("Developer::ToolMenus::Can customize menu using runtime menu customization", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
const FName MenuName("MyMenu");
|
|
|
|
UToolMenu* ToolMenu = UToolMenus::Get()->RegisterMenu(MenuName);
|
|
|
|
REQUIRE(ToolMenu);
|
|
|
|
// Add test entries 1, 2 and 3
|
|
const FName SectionName("MySection");
|
|
const FName Entry1Name("MyEntry1");
|
|
const FName Entry2Name("MyEntry2");
|
|
const FName Entry3Name("MyEntry3");
|
|
|
|
FToolMenuSection& Section = ToolMenu->AddSection(SectionName);
|
|
Section.AddMenuEntry(Entry1Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
Section.AddMenuEntry(Entry2Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
Section.AddMenuEntry(Entry3Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
|
|
// Add the customization to the menu
|
|
UToolMenus::Get()->AddRuntimeMenuCustomization(MenuName);
|
|
FCustomizedToolMenu* CustomizedToolMenu = UToolMenus::Get()->FindRuntimeMenuCustomization(MenuName);
|
|
REQUIRE(CustomizedToolMenu);
|
|
|
|
// Test the customization by denying entry 2
|
|
const FName OwnerName("ToolMenusTest");
|
|
|
|
CustomizedToolMenu->MenuPermissions.AddDenyListItem(OwnerName, Entry2Name);
|
|
|
|
UToolMenu* GeneratedMenu = UToolMenus::Get()->GenerateMenu(MenuName, FToolMenuContext());
|
|
REQUIRE(GeneratedMenu);
|
|
|
|
FToolMenuSection* GeneratedMenuSection = GeneratedMenu->FindSection(SectionName);
|
|
REQUIRE(GeneratedMenuSection);
|
|
|
|
// We should only have 2 entries: Entry1 and Entry3 since Entry2 is denied
|
|
CHECK(GeneratedMenuSection->Blocks.Num() == 2);
|
|
CHECK(GeneratedMenuSection->FindEntry(Entry1Name));
|
|
CHECK_FALSE(GeneratedMenuSection->FindEntry(Entry2Name));
|
|
CHECK(GeneratedMenuSection->FindEntry(Entry3Name));
|
|
}
|
|
|
|
TEST_CASE("Developer::ToolMenus::Can add runtime menu profile", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
const FName MenuName("MyMenu");
|
|
|
|
UToolMenus::Get()->RegisterMenu(MenuName);
|
|
|
|
// Add the customization to the menu
|
|
const FName ProfileName("MyProfile1");
|
|
UToolMenus::Get()->AddRuntimeMenuProfile(MenuName, ProfileName);
|
|
|
|
// Try and find the profile
|
|
FToolMenuProfile* MenuProfile = UToolMenus::Get()->FindRuntimeMenuProfile(MenuName, ProfileName);
|
|
CHECK(MenuProfile);
|
|
}
|
|
|
|
TEST_CASE("Developer::ToolMenus::Can customize menu using runtime menu profile", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
const FName MenuName("MyMenu");
|
|
|
|
UToolMenu* ToolMenu = UToolMenus::Get()->RegisterMenu(MenuName);
|
|
|
|
REQUIRE(ToolMenu);
|
|
|
|
// Add test entries 1, 2 and 3 to the menu
|
|
const FName SectionName("MySection");
|
|
const FName Entry1Name("MyEntry1");
|
|
const FName Entry2Name("MyEntry2");
|
|
const FName Entry3Name("MyEntry3");
|
|
|
|
FToolMenuSection& Section = ToolMenu->AddSection(SectionName);
|
|
Section.AddMenuEntry(Entry1Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
Section.AddMenuEntry(Entry2Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
Section.AddMenuEntry(Entry3Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
|
|
// Add a profile to the menu
|
|
const FName Profile1Name("MyProfile1");
|
|
|
|
UToolMenus::Get()->AddRuntimeMenuProfile(MenuName, Profile1Name);
|
|
|
|
// Check if the profile was added
|
|
FToolMenuProfile* MenuProfile = UToolMenus::Get()->FindRuntimeMenuProfile(MenuName, Profile1Name);
|
|
REQUIRE(MenuProfile);
|
|
|
|
// Deny Entry 2 in the profile
|
|
const FName OwnerName("ToolMenusTest");
|
|
|
|
MenuProfile->MenuPermissions.AddDenyListItem(OwnerName, Entry2Name);
|
|
|
|
SECTION("Generating menu without profile should show all entries")
|
|
{
|
|
UToolMenu* GeneratedMenuWithoutProfile = UToolMenus::Get()->GenerateMenu(MenuName, FToolMenuContext());
|
|
REQUIRE(GeneratedMenuWithoutProfile);
|
|
|
|
FToolMenuSection* GeneratedMenuSection = GeneratedMenuWithoutProfile->FindSection(SectionName);
|
|
REQUIRE(GeneratedMenuSection);
|
|
|
|
CHECK(GeneratedMenuSection->Blocks.Num() == 3);
|
|
CHECK(GeneratedMenuSection->FindEntry(Entry1Name));
|
|
CHECK(GeneratedMenuSection->FindEntry(Entry2Name));
|
|
CHECK(GeneratedMenuSection->FindEntry(Entry3Name));
|
|
}
|
|
|
|
SECTION("Generating menu with profile should hide entry 2")
|
|
{
|
|
FToolMenuContext MenuContext;
|
|
|
|
UToolMenuProfileContext* ProfileContext = NewObject<UToolMenuProfileContext>();
|
|
ProfileContext->ActiveProfiles.Add(Profile1Name);
|
|
MenuContext.AddObject(ProfileContext);
|
|
|
|
UToolMenu* GeneratedMenuWithProfile = UToolMenus::Get()->GenerateMenu(MenuName, MenuContext);
|
|
REQUIRE(GeneratedMenuWithProfile);
|
|
|
|
FToolMenuSection* GeneratedMenuSection = GeneratedMenuWithProfile->FindSection(SectionName);
|
|
REQUIRE(GeneratedMenuSection);
|
|
|
|
CHECK(GeneratedMenuSection->Blocks.Num() == 2);
|
|
CHECK(GeneratedMenuSection->FindEntry(Entry1Name));
|
|
CHECK_FALSE(GeneratedMenuSection->FindEntry(Entry2Name));
|
|
CHECK(GeneratedMenuSection->FindEntry(Entry3Name));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Developer::ToolMenus::Can combine customization and multiple profiles", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
const FName MenuName("MyMenu");
|
|
|
|
UToolMenu* ToolMenu = UToolMenus::Get()->RegisterMenu(MenuName);
|
|
|
|
REQUIRE(ToolMenu);
|
|
|
|
// Add the section and entries
|
|
const FName SectionName("MySection");
|
|
const FName Entry1Name("MyEntry1");
|
|
const FName Entry2Name("MyEntry2");
|
|
const FName Entry3Name("MyEntry3");
|
|
|
|
FToolMenuSection& Section = ToolMenu->AddSection(SectionName);
|
|
Section.AddMenuEntry(Entry1Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
Section.AddMenuEntry(Entry2Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
Section.AddMenuEntry(Entry3Name, FText::GetEmpty(), FText::GetEmpty(), FSlateIcon(), FToolUIActionChoice());
|
|
|
|
// The menu customization denies entry 1
|
|
const FName OwnerName("ToolMenusTest");
|
|
|
|
UToolMenus::Get()->AddRuntimeMenuCustomization(MenuName);
|
|
FCustomizedToolMenu* CustomizedToolMenu = UToolMenus::Get()->FindRuntimeMenuCustomization(MenuName);
|
|
REQUIRE(CustomizedToolMenu);
|
|
CustomizedToolMenu->MenuPermissions.AddDenyListItem(OwnerName, Entry2Name);
|
|
|
|
// Add Profile 1 which denies entry 1 and 2
|
|
const FName Profile1Name("MyProfile1");
|
|
|
|
UToolMenus::Get()->AddRuntimeMenuProfile(MenuName, Profile1Name);
|
|
FToolMenuProfile* MenuProfile = UToolMenus::Get()->FindRuntimeMenuProfile(MenuName, Profile1Name);
|
|
REQUIRE(MenuProfile);
|
|
MenuProfile->MenuPermissions.AddDenyListItem(OwnerName, Entry1Name);
|
|
MenuProfile->MenuPermissions.AddDenyListItem(OwnerName, Entry2Name);
|
|
|
|
// Add Profile 2 which ONLY allows entry 1 (i.e denies ALL other entries)
|
|
const FName Profile2Name("MyProfile2");
|
|
|
|
UToolMenus::Get()->AddRuntimeMenuProfile(MenuName, Profile2Name);
|
|
FToolMenuProfile* MenuProfile2 = UToolMenus::Get()->FindRuntimeMenuProfile(MenuName, Profile2Name);
|
|
REQUIRE(MenuProfile2);
|
|
MenuProfile2->MenuPermissions.AddAllowListItem(OwnerName, Entry1Name);
|
|
|
|
|
|
SECTION("Generating menu with customization + profile 1 should only show entry 3")
|
|
{
|
|
FToolMenuContext MenuContext;
|
|
|
|
UToolMenuProfileContext* ProfileContext = NewObject<UToolMenuProfileContext>();
|
|
ProfileContext->ActiveProfiles.Add(Profile1Name);
|
|
MenuContext.AddObject(ProfileContext);
|
|
|
|
UToolMenu* GeneratedMenuWithProfile = UToolMenus::Get()->GenerateMenu(MenuName, MenuContext);
|
|
REQUIRE(GeneratedMenuWithProfile);
|
|
|
|
FToolMenuSection* GeneratedMenuSection = GeneratedMenuWithProfile->FindSection(SectionName);
|
|
REQUIRE(GeneratedMenuSection);
|
|
|
|
CHECK(GeneratedMenuSection->Blocks.Num() == 1);
|
|
CHECK_FALSE(GeneratedMenuSection->FindEntry(Entry1Name));
|
|
CHECK_FALSE(GeneratedMenuSection->FindEntry(Entry2Name));
|
|
CHECK(GeneratedMenuSection->FindEntry(Entry3Name));
|
|
}
|
|
|
|
SECTION("Generating menu with customization + profile 2 should only show entry 1")
|
|
{
|
|
FToolMenuContext MenuContext;
|
|
|
|
UToolMenuProfileContext* ProfileContext = NewObject<UToolMenuProfileContext>();
|
|
ProfileContext->ActiveProfiles.Add(Profile2Name);
|
|
MenuContext.AddObject(ProfileContext);
|
|
|
|
UToolMenu* GeneratedMenuWithProfile = UToolMenus::Get()->GenerateMenu(MenuName, MenuContext);
|
|
REQUIRE(GeneratedMenuWithProfile);
|
|
|
|
FToolMenuSection* GeneratedMenuSection = GeneratedMenuWithProfile->FindSection(SectionName);
|
|
REQUIRE(GeneratedMenuSection);
|
|
|
|
CHECK(GeneratedMenuSection->Blocks.Num() == 1);
|
|
|
|
CHECK(GeneratedMenuSection->FindEntry(Entry1Name));
|
|
CHECK_FALSE(GeneratedMenuSection->FindEntry(Entry2Name));
|
|
CHECK_FALSE(GeneratedMenuSection->FindEntry(Entry3Name));
|
|
}
|
|
|
|
SECTION("Generating menu with customization + profile 1 + profile 2 should only no entries")
|
|
{
|
|
FToolMenuContext MenuContext;
|
|
|
|
UToolMenuProfileContext* ProfileContext = NewObject<UToolMenuProfileContext>();
|
|
ProfileContext->ActiveProfiles.Add(Profile1Name);
|
|
ProfileContext->ActiveProfiles.Add(Profile2Name);
|
|
MenuContext.AddObject(ProfileContext);
|
|
|
|
UToolMenu* GeneratedMenuWithProfile = UToolMenus::Get()->GenerateMenu(MenuName, MenuContext);
|
|
REQUIRE(GeneratedMenuWithProfile);
|
|
|
|
FToolMenuSection* GeneratedMenuSection = GeneratedMenuWithProfile->FindSection(SectionName);
|
|
REQUIRE(GeneratedMenuSection);
|
|
|
|
CHECK(GeneratedMenuSection->Blocks.Num() == 0);
|
|
CHECK_FALSE(GeneratedMenuSection->FindEntry(Entry1Name));
|
|
CHECK_FALSE(GeneratedMenuSection->FindEntry(Entry2Name));
|
|
CHECK_FALSE(GeneratedMenuSection->FindEntry(Entry3Name));
|
|
}
|
|
}
|
|
|
|
// Repro of UE-201151.
|
|
TEST_CASE("Developer::ToolMenus::GenerateWidget can handle simultaneous AddReferencedObjects calls in legacy dynamic sections", "[ToolMenus]")
|
|
{
|
|
UE::ToolMenus::FToolMenuTestInstanceScoped Scope;
|
|
|
|
UToolMenu* ToolMenu = UToolMenus::Get()->RegisterMenu("ToolMenusTest_MyMenu");
|
|
|
|
REQUIRE(ToolMenu);
|
|
|
|
// This simulates the crash of UE-201151 that occurred in UToolMenus::GenerateWidget(UToolMenu*) after a complex delegate
|
|
// triggered a call to UToolMenus::AddReferencedObjects while the delegate was still executing.
|
|
ToolMenu->AddDynamicSection(
|
|
"MyDynamicLegacySection",
|
|
FNewToolMenuDelegateLegacy::CreateLambda(
|
|
[](FMenuBuilder&, UToolMenu*)
|
|
{
|
|
TArray<UObject*> Array;
|
|
FReferenceFinder Finder(Array);
|
|
UToolMenus::AddReferencedObjects(UToolMenus::Get(), Finder);
|
|
}
|
|
)
|
|
);
|
|
|
|
UToolMenus::Get()->GenerateWidget("ToolMenusTest_MyMenu", FToolMenuContext());
|
|
}
|