/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace EMotionFX { class CanUseFileMenuUIFixture : public MenuUIFixture { public: void SetUp() override { MenuUIFixture::SetUp(); GetEMotionFX().InitAssetFolderPaths(); m_animGraphPlugin = static_cast(EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::AnimGraphPlugin::CLASS_ID)); ASSERT_TRUE(m_animGraphPlugin); EMStudio::GetManager()->SetSkipSourceControlCommands(true); } void TearDown() override { QDir(GetAssetSaveFolder()).removeRecursively(); MenuUIFixture::TearDown(); } QString GetAssetSaveFolder() const { QString dataDir = GetEMotionFX().GetAssetSourceFolder().c_str(); dataDir += "/TmpTestAssets"; if (!QDir(dataDir).exists()) { QDir().mkdir(dataDir); } return dataDir; } QString GenerateTempAssetFile(const QString& filenamebase, const QString& extension) const { bool foundFilename = false; int fileIndex = 0; QString baseDir = GetAssetSaveFolder(); while (!foundFilename) { const QString filename = QString("%1_%2").arg(filenamebase).arg(fileIndex); const QString filepath = QString("%1/%2.%3").arg(baseDir, filename, extension); if (!QFile::exists(filepath)) { return filepath; } fileIndex++; } return nullptr; } QString GenerateTempAnimGraphFilename() const { return GenerateTempAssetFile("tmpanimgraph", "animgraph"); } QString GenerateTempWorkspaceFilename() const { return GenerateTempAssetFile("tmpworkspace", "emfxworkspace"); } QString GenerateTempActorFilename() const { return GenerateTempAssetFile("tmpactor", "actor"); } QString GenerateTempMotionSetFilename() const { return GenerateTempAssetFile("tmpmotionset", "motionset"); } QString GenerateTempMotionFilename() const { return GenerateTempAssetFile("tmpmotion", "motion"); } void CreateAnimGraph() { if (!AnimGraphExists()) { m_animGraphPlugin->GetViewWidget()->OnCreateAnimGraph(); ASSERT_TRUE(m_animGraphPlugin->GetActiveAnimGraph()) << "Failed to create AnimGraph."; } } void CreateActor() { if (EMotionFX::GetActorManager().GetNumActorInstances() == 0) { AutoRegisteredActor actor = ActorFactory::CreateAndInit(2, "CanAddSimulatedObjectWithJointsActor"); ActorInstance* actorInstance = ActorInstance::Create(actor.get()); EXPECT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 1) << "Failed to create actor set for reset test."; } } void CreateAndSaveActor(const char* filename) const { if (EMotionFX::GetActorManager().GetNumActorInstances() == 0) { AutoRegisteredActor actor = ActorFactory::CreateAndInit(2, "CanAddSimulatedObjectWithJointsActor"); ActorInstance* actorInstance = ActorInstance::Create(actor.get()); EXPECT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 1) << "Failed to create actor set for reset test."; actor->SetFileName(filename); AZStd::string stringFilename = filename; ExporterLib::SaveActor(stringFilename, actor.get(), MCore::Endian::ENDIAN_LITTLE); } } void LoadActor(const char* filename, const bool replaceScene) { ModalPopupHandler saveDirtyPopupHandler; saveDirtyPopupHandler.WaitForPopupPressDialogButton(QDialogButtonBox::Ok); EMStudio::GetMainWindow()->LoadActor(filename, replaceScene); } void CreateMotionSet() { if (EMotionFX::GetMotionManager().GetNumMotionSets() == 0) { EMStudio::MotionSetManagementWindow* managementWindow = GetMotionSetManagementWindow(); ASSERT_TRUE(managementWindow); managementWindow->OnCreateMotionSet(); ASSERT_EQ(EMotionFX::GetMotionManager().GetNumMotionSets(), 1) << "Failed to create motion set for reset test."; } } void CreateMotion() { if (EMotionFX::GetMotionManager().GetNumMotions() == 0) { LoadTestMotion(); ASSERT_EQ(EMotionFX::GetMotionManager().GetNumMotions(), 1) << "Failed to create motion for reset test."; } } void DeleteAnimGraph() { EMotionFX::AnimGraph* animGraph = m_animGraphPlugin->GetActiveAnimGraph(); GetEventManager().OnDeleteAnimGraph(animGraph); } bool AnimGraphExists() const { EMotionFX::AnimGraph* animGraph = m_animGraphPlugin->GetActiveAnimGraph(); return animGraph; } void SaveCurrentAnimGraph(const QString& filename) { // Set the save filename to avoid a file select dialog. EMotionFX::AnimGraph* animGraph = m_animGraphPlugin->GetActiveAnimGraph(); animGraph->SetFileName(filename.toUtf8().constData()); m_animGraphPlugin->OnFileSave(); ASSERT_TRUE(QFile::exists(filename)) << "Failed to save AnimGraph."; } void TestActorMenus(QMenu* fileMenu) { // Open Actor // We can't use the Open Actor menu item as it would involve interacting with a system requestor, so just do what the menu option does internally. //Clear any existing actors before we start. QAction* resetAction = GetResetMenuAction(fileMenu); ASSERT_TRUE(resetAction) << "Reset menu item not found"; TestResetMenuItem(resetAction, "EMFX.ResetSettingsDialog.Actors"); ASSERT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 0) << "Failed to reset Actors."; // Create an actor and save it so we can reload it for the merge step. const QString actorFilename = GenerateTempActorFilename(); CreateAndSaveActor(actorFilename.toUtf8().data()); // Clear out the existing actors so we can tell whether the load works. TestResetMenuItem(resetAction, "EMFX.ResetSettingsDialog.Actors"); ASSERT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 0) << "Failed to reset Actors."; // Load the actor we just saved, with replaceScene set to true to represent a load. LoadActor(actorFilename.toUtf8().data(), true); ASSERT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 1) << "Failed to load Actor."; // Do it again to verify that number of actors stays the same when replaceScene is true. LoadActor(actorFilename.toUtf8().data(), true); ASSERT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 1) << "Failed to load Actor."; // Now load again, but with replaceScene set to false, as the merge code does. LoadActor(actorFilename.toUtf8().data(), false); ASSERT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 2) << "Failed to merge Actor."; // We can't test Save Selected Actor as we would it would involve mocking fbx scene handling. // Add the filename to the recent actorsa anyway, so we can test that functionality. EMStudio::GetMainWindow()->AddRecentActorFile(actorFilename); // Check for the file saved in the Save test to be listed in the recent actors submenu. const QList recentMenus = fileMenu->findChildren("EMFX.MainWindow.RecentFilesMenu"); QMenu* recentActorsMenu = nullptr; for (QMenu* recentMenu : recentMenus) { if (recentMenu->title() == "Recent Actors") { recentActorsMenu = recentMenu; } } ASSERT_TRUE(recentActorsMenu) << "Unable to find recent actors menu."; const QList actions = recentActorsMenu->findChildren(); QAction* recentAction = nullptr; for (QAction* action : actions) { if (IsActionRecentlySavedActor(action->text(), actorFilename)) { recentAction = action; } } ASSERT_TRUE(recentAction) << "Recent action for last saved actor not found."; recentAction->trigger(); ASSERT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 1) << "Failed to load recent Actor."; QAction* resetRecentAction = recentActorsMenu->findChild("EMFX.RecentFiles.ResetRecentFilesAction"); ASSERT_TRUE(resetRecentAction) << "Reset recent actors action not found."; resetRecentAction->trigger(); const QList actionsAfterReset = recentActorsMenu->findChildren(); ASSERT_EQ(actionsAfterReset.size(), 1) << "Failed to reset recent items menu."; } bool IsActionRecentlySavedActor(const QString& actionTitle, const QString& actorFilename) const { if (actionTitle.isEmpty()) { return false; } QFileInfo fileInfo(actionTitle); QString fileName = fileInfo.fileName(); // Remove the shortcut at the start. const int shortcutLen = fileName.indexOf(" "); fileName = fileName.mid(shortcutLen + 1); return actorFilename.endsWith(fileName); } void TestSaveWorkspaceMenuOption(QMenu* fileMenu, const QString& workspaceFilename) { EMStudio::Workspace* workspace = EMStudio::GetManager()->GetWorkspace(); ASSERT_TRUE(workspace) << "Current workspace not found"; // Create an anim graph so that there is unsaved data CreateAnimGraph(); // The workspace needs to have a file to save to, as we can't interact with the SaveAs dialog. workspace->SetFilename(workspaceFilename.toUtf8().constData()); // If we try to save now, we'll be asked to select a save file for the anim graph, we need to provide one to avoid that. const QString animGraphFilename = GenerateTempAnimGraphFilename(); SaveCurrentAnimGraph(animGraphFilename); // Prepare a watcher to press the ok button when the SaveDirtySettingsWindow appears. ModalPopupHandler saveDirtyPopupHandler; saveDirtyPopupHandler.WaitForPopupPressDialogButton(QDialogButtonBox::Ok); EMStudio::GetMainWindow()->OnFileSaveWorkspace(); ASSERT_TRUE(saveDirtyPopupHandler.GetSeenTargetWidget()) << "Expected SaveDirtySettingsWindow not found."; ASSERT_TRUE(QFile::exists(workspaceFilename)) << "Workspace save failed."; } bool IsActionRecentlySavedWorkspace(const QString& actionTitle) const { if (actionTitle.isEmpty()) { return false; } QFileInfo fileInfo(actionTitle); QString fileName = fileInfo.fileName(); // Remove the shortcut at the start. const int shortcutLen = fileName.indexOf(" "); fileName = fileName.mid(shortcutLen + 1); return m_lastSavedWorkspaceFilename.endsWith(fileName); } void TestNewWorkspaceMenuOption(QMenu* fileMenu) { // Create an anim graph so that there is unsaved data CreateAnimGraph(); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Test 1. Select New Workspace, press cancel in the SaveDirtySettingsWindow. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Start waiting for the "do you want to save" dialog, then press Cancel. ModalPopupHandler saveDirtyPopupHandler; saveDirtyPopupHandler.WaitForPopupPressDialogButton< EMStudio::SaveDirtySettingsWindow*>(QDialogButtonBox::Cancel); // Trigger the new workspace menu option QAction* newWorkspaceAction = MenuUIFixture::FindMenuActionWithObjectName(fileMenu, "EMFX.MainWindow.NewWorkspaceAction", "EMFX.MainWindow.FileMenu"); ASSERT_TRUE(newWorkspaceAction); newWorkspaceAction->trigger(); // If the dialog does not appear, we might get here before the callback is triggered, so check that it was. ASSERT_TRUE(saveDirtyPopupHandler.GetSeenTargetWidget()) << "Expected SaveDirtySettingsWindow not found."; // The dialog should now be gone but the anim graph we made should still exist. ASSERT_FALSE(QApplication::activeModalWidget()) << "SaveDirtySettingsWindow failed to close."; ASSERT_TRUE(AnimGraphExists()) << "AnimGraph not found."; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Test 2. Select New Workspace, press Discard in the SaveDirtySettingsWindow. // Then press No in the new workspace confirmation dialog. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Start waiting for the "do you want to save" dialog, then press Discard. saveDirtyPopupHandler.WaitForPopupPressDialogButton< EMStudio::SaveDirtySettingsWindow*>(QDialogButtonBox::Discard); // Start waiting for the "really make new workspace?" confirmation popup, then press No. ModalPopupHandler messageBoxPopupHandler; messageBoxPopupHandler.WaitForPopupPressDialogButton(QDialogButtonBox::No); newWorkspaceAction->trigger(); saveDirtyPopupHandler.WaitForCompletion(); messageBoxPopupHandler.WaitForCompletion(); // If the dialog does not appear, we might get here before the callback is triggered, so check that it was. ASSERT_TRUE(saveDirtyPopupHandler.GetSeenTargetWidget()) << "Expected SaveDirtySettingsWindow not found."; ASSERT_TRUE(messageBoxPopupHandler.GetSeenTargetWidget()) << "Expected QMessageBox not found."; // The dialog should now be gone and the anim graph we made should still be there. ASSERT_FALSE(QApplication::activeModalWidget()) << "SaveDirtySettingsWindow failed to close."; ASSERT_TRUE(AnimGraphExists()) << "AnimGraph not found."; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Test 3. Select New Workspace, press discard in the SaveDirtySettingsWindow. // Then press Yes in the new workspace confirmation dialog. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Start waiting for the "do you want to save" dialog, then press Discard. saveDirtyPopupHandler.WaitForPopupPressDialogButton< EMStudio::SaveDirtySettingsWindow*>(QDialogButtonBox::Discard); // Start waiting for the "really make new workspace?" confirmation popup, then press Yes. messageBoxPopupHandler.WaitForPopupPressDialogButton(QDialogButtonBox::Yes); newWorkspaceAction->trigger(); // If the dialog does not appear, we might get here before the callback is triggered, so check that it did. ASSERT_TRUE(saveDirtyPopupHandler.GetSeenTargetWidget()) << "Expected SaveDirtySettingsWindow not found."; ASSERT_TRUE(messageBoxPopupHandler.GetSeenTargetWidget()) << "Expected QMessageBox not found."; // There should be no active popup. ASSERT_FALSE(QApplication::activeModalWidget()) << "SaveDirtySettingsWindow failed to close."; // The AnimGraph should be removed. ASSERT_FALSE(AnimGraphExists()) << "AnimGraph not removed."; } void TestRecentWorkspacesMenuOption(QMenu* fileMenu) { // Remove the AnimGraph so that we can tell the workspace has been reloaded correctly. DeleteAnimGraph(); ASSERT_FALSE(AnimGraphExists()) << "AnimGraph not removed."; // Check for the file saved in the Save test to be listed. const QList recentMenus = fileMenu->findChildren("EMFX.MainWindow.RecentFilesMenu"); QMenu* recentWorkspacesMenu = nullptr; for (QMenu* recentMenu : recentMenus) { if (recentMenu->title() == "Recent Workspaces") { recentWorkspacesMenu = recentMenu; } } ASSERT_TRUE(recentWorkspacesMenu) << "Unable to find recent workspaces menu."; const QList actions = recentWorkspacesMenu->findChildren(); QAction* recentAction = nullptr; for (QAction* action : actions) { if (IsActionRecentlySavedWorkspace(action->text())) { recentAction = action; } } ASSERT_TRUE(recentAction) << "Recent action for last saved workspace not found."; // Now try to use the action to reload the workspace. // As we've deleted the AnimGraph, we'll be asked about saving changes, discard them. ModalPopupHandler saveDirtyPopupHandler; saveDirtyPopupHandler.WaitForPopupPressDialogButton< EMStudio::SaveDirtySettingsWindow*>(QDialogButtonBox::Discard); recentAction->trigger(); ASSERT_TRUE(AnimGraphExists()) << "AnimGraph not found after reloading recent workspace."; // Test the clear recent items action. QAction* resetRecentAction = recentWorkspacesMenu->findChild("EMFX.RecentFiles.ResetRecentFilesAction"); ASSERT_TRUE(resetRecentAction) << "Reset recent workspaces action not found."; resetRecentAction->trigger(); const QList actionsAfterReset = recentWorkspacesMenu->findChildren(); ASSERT_EQ(actionsAfterReset.size(), 1) << "Failed to reset workspaces items menu."; } void CreateDataForResetTest() { CreateActor(); CreateMotionSet(); CreateMotion(); CreateAnimGraph(); } void TestResetMenuItem(QAction* resetMenuAction, const QString& resetItemName) { // Set up a callback to set the correct checkbox states when the ResetSettings dialog appears. WidgetActiveCallback resetSettingsCallback = [resetItemName](QWidget* widget) { ASSERT_TRUE(widget) << "Failed to find Reset widget."; if (resetItemName == "*") { // Set all checkboxes QListcheckBoxes = widget->findChildren(); for (QCheckBox *checkBox : checkBoxes) { checkBox->setChecked(true); } } else { // Reset all checkboxes QListcheckBoxes = widget->findChildren(); for (QCheckBox *checkBox : checkBoxes) { QString on = checkBox->objectName(); checkBox->setChecked(false); } // Just tick the one we want QCheckBox* checkBox = widget->findChild(resetItemName); ASSERT_TRUE(checkBox) << "Failed to find reset item checkbox "; checkBox->setChecked(true); } // Press the Ok button QDialogButtonBox* buttonBox = widget->findChild< QDialogButtonBox*>(); ASSERT_TRUE(buttonBox) << "Unable to find button box in ResetSettingsDialog"; QAbstractButton* button = buttonBox->button(QDialogButtonBox::Ok); ASSERT_TRUE(button) << "Unable to find Ok button in ResetSettingsDialog"; QTest::mouseClick(button, Qt::LeftButton); }; // Setup a watcher to handle the save dirty settings dialog by pressing Discard. ModalPopupHandler saveDirtyPopupHandler; saveDirtyPopupHandler.WaitForPopupPressDialogButton< EMStudio::SaveDirtySettingsWindow*>(QDialogButtonBox::Discard); // Setup a handler to select the type to reset and press ok. ModalPopupHandler resetSettingsHandler; resetSettingsHandler.WaitForPopup(resetSettingsCallback); resetMenuAction->trigger(); saveDirtyPopupHandler.WaitForCompletion(); resetSettingsHandler.WaitForCompletion(); } QString GetTestMotionFileName() const { QString assets = GetEMotionFX().GetAssetSourceFolder().c_str(); return assets + "Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_Idle.motion"; } void LoadTestMotion() { const QString testFile = GetTestMotionFileName(); ASSERT_TRUE(QFile::exists(testFile)) << "Failed to find motion file asset."; AZStd::vector motionFilenames; motionFilenames.push_back(testFile.toUtf8().data()); CommandSystem::LoadMotionsCommand(motionFilenames); } QAction* GetResetMenuAction(const QMenu* fileMenu) const { return MenuUIFixture::FindMenuActionWithObjectName(fileMenu, "EMFX.MainWindow.ResetAction", fileMenu->objectName()); } void TestResetMenuItem(QMenu* fileMenu) { QAction* resetAction = GetResetMenuAction(fileMenu); ASSERT_TRUE(resetAction) << "Reset menu item not found"; ASSERT_TRUE(resetAction->isEnabled()) << "Reset menu action is disabled."; // Make one of everything to reset CreateDataForResetTest(); // Reset them one at a time: { // Actors TestResetMenuItem(resetAction, "EMFX.ResetSettingsDialog.Actors"); ASSERT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 0) << "Failed to reset Actors."; // Motions TestResetMenuItem(resetAction, "EMFX.ResetSettingsDialog.Motions"); ASSERT_EQ(EMotionFX::GetMotionManager().GetNumMotions(), 0) << "Failed to reset Motions."; // Motion Sets TestResetMenuItem(resetAction, "EMFX.ResetSettingsDialog.MotionSets"); ASSERT_EQ(EMotionFX::GetMotionManager().GetNumMotionSets(), 0) << "Failed to reset MotionSets."; // AnimGraphs TestResetMenuItem(resetAction, "EMFX.ResetSettingsDialog.AnimGraphs"); ASSERT_FALSE(m_animGraphPlugin->GetActiveAnimGraph()) << "Failed to reset AnimGraphs."; } // The reset menu item should be disabled as there is nothing to reset. ASSERT_FALSE(resetAction->isEnabled()) << "Reset menu action is enabled after resetting all items."; // Recreate test data CreateDataForResetTest(); // Reset them all at once { TestResetMenuItem(resetAction, "*"); ASSERT_EQ(EMotionFX::GetActorManager().GetNumActorInstances(), 0) << "Failed to reset Actors."; ASSERT_EQ(EMotionFX::GetMotionManager().GetNumMotions(), 0) << "Failed to reset Motions."; ASSERT_EQ(EMotionFX::GetMotionManager().GetNumMotionSets(), 0) << "Failed to reset MotionSets."; ASSERT_FALSE(m_animGraphPlugin->GetActiveAnimGraph()) << "Failed to reset AnimGraphs."; } } void TestSaveAllMenuItem(QMenu* fileMenu) { // Use the test reset menu item to ensure everything is cleared out. QAction* resetAction = GetResetMenuAction(fileMenu); ASSERT_TRUE(resetAction) << "Reset menu item not found"; TestResetMenuItem(resetAction, "*"); // Make new sets of all data types, give them a unique filename and set them as dirty. CreateAnimGraph(); const QString animGraphFilename = GenerateTempAnimGraphFilename(); EMotionFX::AnimGraph* animGraph = m_animGraphPlugin->GetActiveAnimGraph(); animGraph->SetFileName(animGraphFilename.toUtf8().constData()); animGraph->SetDirtyFlag(true); CreateMotionSet(); const QString motionsetFilename = GenerateTempMotionSetFilename(); EMotionFX::MotionSet *motionSet = EMotionFX::GetMotionManager().GetMotionSet(0); motionSet->SetFilename(motionsetFilename.toUtf8().constData()); motionSet->SetDirtyFlag(true); // Don't create an actor or motion as we can't save that due to fbx scene requirements. EMStudio::Workspace* workspace = EMStudio::GetManager()->GetWorkspace(); const QString workspaceFilename = GenerateTempWorkspaceFilename(); workspace->SetFilename(workspaceFilename.toUtf8().data()); workspace->SetDirtyFlag(true); ModalPopupHandler saveDirtyPopupHandler; saveDirtyPopupHandler.WaitForPopupPressDialogButton< EMStudio::SaveDirtySettingsWindow*>(QDialogButtonBox::Ok); QAction* saveAllAction = MenuUIFixture::FindMenuActionWithObjectName(fileMenu, "EMFX.MainWindow.SaveAllAction", fileMenu->objectName()); ASSERT_TRUE(saveAllAction) << "Save All menu item not found"; ASSERT_TRUE(saveAllAction->isEnabled()); saveAllAction->trigger(); ASSERT_TRUE(QFile::exists(animGraphFilename)) << "Failed to save AnimGraph in SaveAll action."; ASSERT_TRUE(QFile::exists(motionsetFilename)) << "Failed to save MotionSet in SaveAll action."; ASSERT_TRUE(QFile::exists(workspaceFilename)) << "Failed to save Workspace in SaveAll action."; } void TestWorkspaceMenuItems(QMenu* fileMenu) { m_lastSavedWorkspaceFilename = GenerateTempWorkspaceFilename(); TestNewWorkspaceMenuOption(fileMenu); // We can't test Open Workspace as it requires interacting with a system file dialog. TestSaveWorkspaceMenuOption(fileMenu, m_lastSavedWorkspaceFilename); // We can't test Save As as it requires interacting with a system file dialog. TestRecentWorkspacesMenuOption(fileMenu); } private: EMStudio::AnimGraphPlugin* m_animGraphPlugin = nullptr; QString m_lastSavedWorkspaceFilename; }; TEST_F(CanUseFileMenuUIFixture, CanUseFileMenu) { RecordProperty("test_case_id", "C1698601"); RecordProperty("test_case_id", "C16302183"); RecordProperty("test_case_id", "C1698617"); // Find the File menu. QMenu* fileMenu = MenuUIFixture::FindMainMenuWithName("EMFX.MainWindow.FileMenu"); ASSERT_TRUE(fileMenu) << "Unable to find file menu."; TestWorkspaceMenuItems(fileMenu); TestResetMenuItem(fileMenu); TestActorMenus(fileMenu); TestSaveAllMenuItem(fileMenu); } } // namespace EMotionFX