EditorAutomationTestDialog.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <QHeaderView>
  9. #include <QItemSelectionModel>
  10. #include <QMainWindow>
  11. #include <QMenuBar>
  12. #include <QPushButton>
  13. #include <QScreen>
  14. #include <QTableView>
  15. #include <QVBoxLayout>
  16. #include <QWindow>
  17. #include <AzCore/Casting/numeric_cast.h>
  18. #include <GraphCanvas/Editor/Automation/AutomationUtils.h>
  19. #include <GraphCanvas/Widgets/NodePalette/NodePaletteDockWidget.h>
  20. #include <GraphCanvas/Widgets/NodePalette/NodePaletteWidget.h>
  21. #include <ScriptCanvas/Bus/RequestBus.h>
  22. #include <Editor/GraphCanvas/AutomationIds.h>
  23. #include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
  24. #include <EditorAutomationTestDialog.h>
  25. #include <EditorAutomationTests/EditorAutomationTests.h>
  26. #include <EditorAutomationTests/GraphCreationTests.h>
  27. #include <EditorAutomationTests/GroupTests.h>
  28. #include <EditorAutomationTests/InteractionTests.h>
  29. #include <EditorAutomationTests/NodeCreationTests.h>
  30. #include <EditorAutomationTests/VariableTests.h>
  31. namespace ScriptCanvas::Developer
  32. {
  33. //////////////////
  34. // TestListModel
  35. //////////////////
  36. TestListModel::TestListModel()
  37. : m_runningIcon("Icons/AssetBrowser/in_progress.gif")
  38. , m_passedIcon(":/ScriptCanvasEditorResources/Resources/valid_icon.png")
  39. , m_failedIcon(":/ScriptCanvasEditorResources/Resources/error_icon.png")
  40. {
  41. }
  42. TestListModel::~TestListModel()
  43. {
  44. for (EditorAutomationTest* test : m_actionTests)
  45. {
  46. delete test;
  47. }
  48. }
  49. QModelIndex TestListModel::index(int row, int column, const QModelIndex&) const
  50. {
  51. if (row < 0 || row >= m_actionTests.size())
  52. {
  53. return QModelIndex();
  54. }
  55. return createIndex(row, column, nullptr);
  56. }
  57. QModelIndex TestListModel::parent(const QModelIndex&) const
  58. {
  59. return QModelIndex();
  60. }
  61. int TestListModel::columnCount(const QModelIndex&) const
  62. {
  63. return ColumnIndex::Count;
  64. }
  65. int TestListModel::rowCount(const QModelIndex&) const
  66. {
  67. return static_cast<int>(m_actionTests.size());
  68. }
  69. QVariant TestListModel::headerData(int, Qt::Orientation, int) const
  70. {
  71. return QVariant();
  72. }
  73. QVariant TestListModel::data(const QModelIndex& index, int role) const
  74. {
  75. EditorAutomationTest* actionTest = FindTestForIndex(index);
  76. if (actionTest == nullptr)
  77. {
  78. return QVariant();
  79. }
  80. if (role == Qt::DisplayRole)
  81. {
  82. if (index.column() == ColumnIndex::TestName)
  83. {
  84. return actionTest->GetTestName();
  85. }
  86. }
  87. else if (role == Qt::DecorationRole)
  88. {
  89. if (index.column() == ColumnIndex::TestName)
  90. {
  91. if (actionTest->HasRun() || actionTest->IsRunning())
  92. {
  93. if (actionTest->IsRunning())
  94. {
  95. return m_runningIcon;
  96. }
  97. else if (actionTest->HasErrors())
  98. {
  99. return m_failedIcon;
  100. }
  101. else
  102. {
  103. return m_passedIcon;
  104. }
  105. }
  106. }
  107. }
  108. return QVariant();
  109. }
  110. void TestListModel::AddTest(EditorAutomationTest* actionTest)
  111. {
  112. m_actionTests.emplace_back(actionTest);
  113. }
  114. EditorAutomationTest* TestListModel::FindTestForIndex(const QModelIndex& index) const
  115. {
  116. if (index.isValid())
  117. {
  118. return FindTestForRow(index.row());
  119. }
  120. return nullptr;
  121. }
  122. EditorAutomationTest* TestListModel::FindTestForRow(int row) const
  123. {
  124. if (row >= 0 && row < m_actionTests.size())
  125. {
  126. return m_actionTests[row];
  127. }
  128. return nullptr;
  129. }
  130. int TestListModel::FindRowForTest(EditorAutomationTest* actionTest)
  131. {
  132. for (int i = 0; i < m_actionTests.size(); ++i)
  133. {
  134. if (m_actionTests[i] == actionTest)
  135. {
  136. return i;
  137. }
  138. }
  139. return -1;
  140. }
  141. void TestListModel::UpdateTestDisplay(EditorAutomationTest* actionTest)
  142. {
  143. int row = FindRowForTest(actionTest);
  144. if (row >= 0)
  145. {
  146. dataChanged(index(0, 0, QModelIndex()), index(0, ColumnIndex::Count - 1, QModelIndex()));
  147. }
  148. }
  149. ///////////////////////////////
  150. // EditorAutomationTestDialog
  151. ///////////////////////////////
  152. EditorAutomationTestDialog::EditorAutomationTestDialog(QMainWindow* mainWindow)
  153. : QDialog()
  154. , m_scriptCanvasWindow(mainWindow)
  155. {
  156. EditorAutomationTestDialogRequestBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
  157. QMenuBar* menuBar = mainWindow->menuBar();
  158. const QObjectList& objectList = menuBar->children();
  159. QMenu* targetMenu = nullptr;
  160. for (QObject* object : objectList)
  161. {
  162. QMenu* menu = qobject_cast<QMenu*>(object);
  163. if (menu && menu->title() == "Developer")
  164. {
  165. targetMenu = menu;
  166. }
  167. }
  168. QObject* object = GraphCanvas::AutomationUtils::FindObjectById<QObject>(ScriptCanvasEditor::AssetEditorId, ScriptCanvasEditor::AutomationIds::NodePaletteWidget);
  169. // Can't use qobject_cast, since it is accessing across DLLs
  170. GraphCanvas::NodePaletteWidget* nodePaletteWidget = static_cast<GraphCanvas::NodePaletteWidget*>(object);
  171. if (nodePaletteWidget == nullptr)
  172. {
  173. AZ_Error("Editor Automation", false, "Failed to find NodePaletteWidget");
  174. }
  175. QLineEdit* searchFilter = nodePaletteWidget->GetSearchFilter();
  176. setWindowFlag(Qt::WindowType::WindowCloseButtonHint);
  177. setAttribute(Qt::WA_DeleteOnClose);
  178. QLayout* layout = new QVBoxLayout();
  179. m_tableView = new QTableView();
  180. m_tableView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  181. m_tableView->setMinimumSize(QSize(250, 250));
  182. m_tableView->setAlternatingRowColors(true);
  183. m_tableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
  184. m_testListModel = new TestListModel();
  185. // Populate tests here
  186. // General Sanity Tests of the interactions
  187. m_testListModel->AddTest(aznew OpenMenuTest(targetMenu));
  188. m_testListModel->AddTest(aznew WriteTextToInput(searchFilter, "Multiply (*)"));
  189. m_testListModel->AddTest(aznew WriteTextToInput(searchFilter, "::Test::"));
  190. ////
  191. // General Test for Graph Creation
  192. m_testListModel->AddTest(aznew CreateGraphTest());
  193. m_testListModel->AddTest(aznew CreateFunctionTest());
  194. ////
  195. // General Tests for Node Creation
  196. m_testListModel->AddTest(aznew CreateNodeFromPaletteTest("Multiply (*)", nodePaletteWidget));
  197. m_testListModel->AddTest(aznew CreateNodeFromPaletteTest("Print", nodePaletteWidget));
  198. m_testListModel->AddTest(aznew CreateNodeFromContextMenuTest("Multiply (*)"));
  199. m_testListModel->AddTest(aznew CreateNodeFromContextMenuTest("Print"));
  200. m_testListModel->AddTest(aznew CreateHelloWorldFromPalette(nodePaletteWidget));
  201. m_testListModel->AddTest(aznew CreateHelloWorldFromContextMenu());
  202. m_testListModel->AddTest(aznew CreateExecutionSplicedNodeTest("Build String"));
  203. m_testListModel->AddTest(aznew CreateDragDropExecutionSpliceNodeTest(nodePaletteWidget, "Build String"));
  204. ////
  205. m_testListModel->AddTest(aznew AltClickDeleteTest());
  206. // Actual BAT tests
  207. m_testListModel->AddTest(aznew ManuallyCreateVariableTest(ScriptCanvas::Data::Type::Number(), CreateVariableAction::CreationType::AutoComplete));
  208. m_testListModel->AddTest(aznew ManuallyCreateVariableTest(ScriptCanvas::Data::Type::Number(), CreateVariableAction::CreationType::Palette));
  209. m_testListModel->AddTest(aznew ManuallyCreateVariableTest(ScriptCanvas::Data::Type::Number(), CreateVariableAction::CreationType::Programmatic));
  210. m_testListModel->AddTest(aznew ManuallyCreateVariableTest(ScriptCanvas::Data::Type::Vector3(), CreateVariableAction::CreationType::AutoComplete));
  211. m_testListModel->AddTest(aznew ManuallyCreateVariableTest(ScriptCanvas::Data::Type::Vector3(), CreateVariableAction::CreationType::Palette));
  212. m_testListModel->AddTest(aznew ManuallyCreateVariableTest(ScriptCanvas::Data::Type::Vector3(), CreateVariableAction::CreationType::Programmatic));
  213. m_testListModel->AddTest(aznew CreateNamedVariableTest(ScriptCanvas::Data::Type::EntityID(), "Caterpillar", CreateVariableAction::CreationType::AutoComplete));
  214. m_testListModel->AddTest(aznew DuplicateVariableNameTest(ScriptCanvas::Data::Type::Number(), ScriptCanvas::Data::Type::Number(), "SameType"));
  215. m_testListModel->AddTest(aznew DuplicateVariableNameTest(ScriptCanvas::Data::Type::Color(), ScriptCanvas::Data::Type::String(), "DifferentType"));
  216. m_testListModel->AddTest(aznew ModifyNumericInputTest(123.45));
  217. m_testListModel->AddTest(aznew ModifyStringInputTest("abcdefghijklmnopqrstuvwxyz"));
  218. m_testListModel->AddTest(aznew ToggleBoolInputTest());
  219. AZStd::vector< ScriptCanvas::Data::Type > primitiveTypes;
  220. ScriptCanvasEditor::VariableAutomationRequestBus::BroadcastResult(primitiveTypes, &ScriptCanvasEditor::VariableAutomationRequests::GetPrimitiveTypes);
  221. m_testListModel->AddTest(aznew VariableLifeCycleTest("Primitive Variable LifeCycle Test", primitiveTypes));
  222. AZStd::vector< ScriptCanvas::Data::Type > objectTypes;
  223. ScriptCanvasEditor::VariableAutomationRequestBus::BroadcastResult(objectTypes, &ScriptCanvasEditor::VariableAutomationRequests::GetBehaviorContextObjectTypes);
  224. m_testListModel->AddTest(aznew VariableLifeCycleTest("BCO Variable LifeCycle Test", objectTypes));
  225. AZStd::vector< ScriptCanvas::Data::Type > mapTypes;
  226. ScriptCanvasEditor::VariableAutomationRequestBus::BroadcastResult(mapTypes, &ScriptCanvasEditor::VariableAutomationRequests::GetMapTypes);
  227. {
  228. AZStd::vector< ScriptCanvas::Data::Type > tempTypes;
  229. for (int i = 0; i < AZStd::GetMin(aznumeric_cast<int>(mapTypes.size()), 10); ++i)
  230. {
  231. tempTypes.emplace_back((*mapTypes.begin()));
  232. mapTypes.erase(mapTypes.begin());
  233. }
  234. mapTypes.clear();
  235. mapTypes = tempTypes;
  236. }
  237. m_testListModel->AddTest(aznew VariableLifeCycleTest("Map Variable LifeCycle Test", mapTypes, CreateVariableAction::CreationType::Programmatic));
  238. AZStd::vector< ScriptCanvas::Data::Type > arrayTypes;
  239. ScriptCanvasEditor::VariableAutomationRequestBus::BroadcastResult(arrayTypes, &ScriptCanvasEditor::VariableAutomationRequests::GetArrayTypes);
  240. {
  241. AZStd::vector< ScriptCanvas::Data::Type > tempTypes;
  242. for (int i = 0; i < AZStd::GetMin(aznumeric_cast<int>(arrayTypes.size()), 10); ++i)
  243. {
  244. tempTypes.emplace_back((*arrayTypes.begin()));
  245. arrayTypes.erase(arrayTypes.begin());
  246. }
  247. arrayTypes.clear();
  248. arrayTypes = tempTypes;
  249. }
  250. m_testListModel->AddTest(aznew VariableLifeCycleTest("Array Variable LifeCycle Test", arrayTypes, CreateVariableAction::CreationType::Programmatic));
  251. m_testListModel->AddTest(aznew RapidVariableCreationDeletionTest());
  252. m_testListModel->AddTest(aznew CreateCategoryTest("Logic", nodePaletteWidget));
  253. m_testListModel->AddTest(aznew CreateGroupTest());
  254. m_testListModel->AddTest(aznew CreateGroupTest(CreateGroupAction::CreationType::Toolbar));
  255. m_testListModel->AddTest(aznew GroupManipulationTest(nodePaletteWidget));
  256. m_testListModel->AddTest(aznew CutCopyPasteDuplicateTest("On Tick"));
  257. m_testListModel->AddTest(aznew CutCopyPasteDuplicateTest("Multiply (*)"));
  258. m_testListModel->AddTest(aznew CutCopyPasteDuplicateTest("Print"));
  259. ////
  260. ////
  261. m_tableView->setModel(m_testListModel);
  262. m_tableView->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
  263. m_tableView->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
  264. m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Stretch);
  265. QPushButton* button = new QPushButton();
  266. button->setText("Run All Tests");
  267. layout->addWidget(button);
  268. m_runLabel = new QLabel();
  269. layout->addWidget(m_runLabel);
  270. layout->addWidget(m_tableView);
  271. m_errorTestLabel = new QLabel();
  272. m_errorTestLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
  273. layout->addWidget(m_errorTestLabel);
  274. setLayout(layout);
  275. setWindowTitle("Editor Automated Testing");
  276. QObject::connect(button, &QPushButton::clicked, this, &EditorAutomationTestDialog::RunAllTests);
  277. QObject::connect(m_tableView, &QTableView::doubleClicked, this, &EditorAutomationTestDialog::RunTest);
  278. QObject::connect(m_tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &EditorAutomationTestDialog::OnSelectionChanged);
  279. }
  280. EditorAutomationTestDialog::~EditorAutomationTestDialog()
  281. {
  282. EditorAutomationTestDialogRequestBus::Handler::BusDisconnect();;
  283. }
  284. void EditorAutomationTestDialog::RunAllTests()
  285. {
  286. for (int i = 0; i < m_testListModel->rowCount(); ++i)
  287. {
  288. EditorAutomationTest* test = m_testListModel->FindTestForRow(i);
  289. if (test)
  290. {
  291. m_testQueue.insert(test);
  292. }
  293. }
  294. if (!AZ::SystemTickBus::Handler::BusIsConnected())
  295. {
  296. StartNewTestRun();
  297. }
  298. }
  299. void EditorAutomationTestDialog::RunTest(QModelIndex modelIndex)
  300. {
  301. EditorAutomationTest* test = m_testListModel->FindTestForIndex(modelIndex);
  302. if (test)
  303. {
  304. m_testQueue.insert(test);
  305. if (!AZ::SystemTickBus::Handler::BusIsConnected())
  306. {
  307. StartNewTestRun();
  308. }
  309. }
  310. }
  311. void EditorAutomationTestDialog::OnSystemTick()
  312. {
  313. auto currentTime = AZStd::chrono::steady_clock::now();
  314. auto elapsedTimed = AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(currentTime - m_startTime);
  315. if (m_activeTest)
  316. {
  317. if (!m_activeTest->IsRunning())
  318. {
  319. if (!m_activeTest->HasErrors())
  320. {
  321. ++m_successCount;
  322. }
  323. m_testListModel->UpdateTestDisplay(m_activeTest);
  324. int row = m_testListModel->FindRowForTest(m_activeTest);
  325. if (m_tableView)
  326. {
  327. if (m_tableView->selectionModel()->currentIndex().row() == row)
  328. {
  329. DisplayErrorsForTest(m_activeTest);
  330. }
  331. }
  332. m_activeTest = nullptr;
  333. m_startTime = AZStd::chrono::steady_clock::now();
  334. UpdateRunLabel();
  335. }
  336. }
  337. else if (elapsedTimed >= AZStd::chrono::milliseconds(1000))
  338. {
  339. if (m_testQueue.empty())
  340. {
  341. FinishTestRun();
  342. // Force the entire layout to refresh for now.
  343. m_testListModel->layoutChanged();
  344. }
  345. else
  346. {
  347. m_scriptCanvasWindow->show();
  348. m_scriptCanvasWindow->raise();
  349. m_scriptCanvasWindow->activateWindow();
  350. m_scriptCanvasWindow->setFocus(Qt::FocusReason::MouseFocusReason);
  351. m_activeTest = (*m_testQueue.begin());
  352. m_testQueue.erase(m_testQueue.begin());
  353. m_runCount++;
  354. m_activeTest->StartTest();
  355. m_testListModel->UpdateTestDisplay(m_activeTest);
  356. }
  357. }
  358. }
  359. void EditorAutomationTestDialog::ShowTestDialog()
  360. {
  361. show();
  362. raise();
  363. activateWindow();
  364. setFocus(Qt::FocusReason::MouseFocusReason);
  365. }
  366. void EditorAutomationTestDialog::OnSelectionChanged(const QItemSelection& selected, const QItemSelection&)
  367. {
  368. m_errorTestLabel->clear();
  369. if (selected.size() == 1)
  370. {
  371. EditorAutomationTest* test = m_testListModel->FindTestForIndex(selected.front().indexes().front());
  372. DisplayErrorsForTest(test);
  373. }
  374. }
  375. void EditorAutomationTestDialog::StartNewTestRun()
  376. {
  377. m_runCount = 0;
  378. m_successCount = 0;
  379. AZ::SystemTickBus::Handler::BusConnect();
  380. m_startTime = AZStd::chrono::steady_clock::now();
  381. UpdateRunLabel();
  382. }
  383. void EditorAutomationTestDialog::FinishTestRun()
  384. {
  385. AZ::SystemTickBus::Handler::BusDisconnect();
  386. m_runLabel->setText(QString("%1 of %2 Test rans successfully").arg(m_successCount).arg(m_runCount));
  387. ShowTestDialog();
  388. }
  389. void EditorAutomationTestDialog::UpdateRunLabel()
  390. {
  391. m_runLabel->setText(QString("Running Tests.... %1 remaining.").arg(m_testQueue.size()));
  392. }
  393. void EditorAutomationTestDialog::DisplayErrorsForTest(EditorAutomationTest* actionTest)
  394. {
  395. AZStd::vector<AZStd::string> errorStrings = actionTest->GetErrors();
  396. QString displayString;
  397. for (const AZStd::string& errorString : errorStrings)
  398. {
  399. if (!displayString.isEmpty())
  400. {
  401. displayString.append("\n");
  402. }
  403. displayString.append(errorString.c_str());
  404. }
  405. m_errorTestLabel->setText(displayString);
  406. }
  407. #include <Editor/Source/moc_EditorAutomationTestDialog.cpp>
  408. }