From 3402d7836a37f04bf437e3209c439176923af698 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 31 Jul 2018 11:17:53 -0700 Subject: [PATCH] Added milestones to TestRail cases. --- tools/auto-tester/src/TestRailInterface.cpp | 163 +++++++++++++++--- tools/auto-tester/src/TestRailInterface.h | 20 ++- .../src/ui/TestRailSelectorWindow.cpp | 27 ++- .../src/ui/TestRailSelectorWindow.h | 4 + .../src/ui/TestRailSelectorWindow.ui | 112 +++++++++--- 5 files changed, 261 insertions(+), 65 deletions(-) diff --git a/tools/auto-tester/src/TestRailInterface.cpp b/tools/auto-tester/src/TestRailInterface.cpp index d8784e5a86..1640918c47 100644 --- a/tools/auto-tester/src/TestRailInterface.cpp +++ b/tools/auto-tester/src/TestRailInterface.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include TestRailInterface::TestRailInterface() { @@ -39,8 +38,8 @@ QString TestRailInterface::getObject(const QString& path) { // Creates the testrail.py script // This is the file linked to from http://docs.gurock.com/testrail-api2/bindings-python -void TestRailInterface::createTestRailDotPyScript(const QString& outputDirectory) { - QFile file(outputDirectory + "/testrail.py"); +void TestRailInterface::createTestRailDotPyScript() { + QFile file(_outputDirectory + "/testrail.py"); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Could not create 'testrail.py'"); @@ -145,8 +144,13 @@ void TestRailInterface::createTestRailDotPyScript(const QString& outputDirectory } // Creates a Stack class -void TestRailInterface::createStackDotPyScript(const QString& outputDirectory) { - QFile file(outputDirectory + "/stack.py"); +void TestRailInterface::createStackDotPyScript() { + QString filename = _outputDirectory + "/stack.py"; + if (QFile::exists(filename)) { + QFile::remove(filename); + } + QFile file(filename); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Could not create 'stack.py'"); @@ -184,7 +188,7 @@ void TestRailInterface::createStackDotPyScript(const QString& outputDirectory) { file.close(); } -void TestRailInterface::requestDataFromUser() { +void TestRailInterface::requestTestRailDataFromUser() { _testRailSelectorWindow.exec(); if (_testRailSelectorWindow.getUserCancelled()) { @@ -249,10 +253,15 @@ void TestRailInterface::processDirectoryPython(const QString& directory, // Therefore, the tree is built top-down, using a stack to store the IDs of each node // void TestRailInterface::createAddTestCasesPythonScript(const QString& testDirectory, - const QString& outputDirectory, - const QString& userGitHub, - const QString& branchGitHub) { - QFile file(outputDirectory + "/addTestCases.py"); + const QString& userGitHub, + const QString& branchGitHub) { + + QString filename = _outputDirectory + "/addTestCases.py"; + if (QFile::exists(filename)) { + QFile::remove(filename); + } + QFile file(filename); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Could not create 'addTestCases.py'"); @@ -283,48 +292,141 @@ void TestRailInterface::createAddTestCasesPythonScript(const QString& testDirect file.close(); if (QMessageBox::Yes == QMessageBox(QMessageBox::Information, "Python script has been created", "Do you want to run the script and update TestRail?", QMessageBox::Yes | QMessageBox::No).exec()) { - QString command(_pythonPath + "/" + pythonExe); - QStringList parameters = QStringList() << outputDirectory + "/addTestCases.py"; QProcess* process = new QProcess(); - connect( - process, &QProcess::started, - this, [=]() { + connect(process, &QProcess::started, this, + [=]() { _busyWindow.exec(); } ); - connect( - process, static_cast(&QProcess::finished), - this, [=](int exitCode, QProcess::ExitStatus exitStatus) { + connect(process, static_cast(&QProcess::finished), this, + [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); } ); - process->start(command, parameters); + QStringList parameters = QStringList() << _outputDirectory + "/addTestCases.py"; + process->start(_pythonCommand, parameters); } } +void TestRailInterface::updateMilestonesComboData(int exitCode, QProcess::ExitStatus exitStatus) { + // Check if process completed successfully + if (exitStatus != QProcess::NormalExit) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not get milestones from TestRail"); + exit(-1); + } + + // Create map of milestones from the file created by the process + _milestoneNames.clear(); + + QString filename = _outputDirectory + "/milestones.txt"; + if (!QFile::exists(filename)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not find milestones.txt in " + _outputDirectory); + exit(-1); + } + + QFile file(filename); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open " + _outputDirectory + "/milestones.txt"); + exit(-1); + } + + QTextStream in(&file); + QString line = in.readLine(); + while (!line.isNull()) { + QStringList words = line.split(' '); + _milestones[words[0]] = words[1].toInt(); + _milestoneNames << words[0]; + + line = in.readLine(); + } + + file.close(); + + // Update the combo + _testRailSelectorWindow.updateMilestoneComboBoxData(_milestoneNames); + + _testRailSelectorWindow.exec(); + + if (_testRailSelectorWindow.getUserCancelled()) { + return; + } + + createAddTestCasesPythonScript(_testDirectory, _userGitHub, _branchGitHub); +} + +void TestRailInterface::getMilestonesFromTestRail() { + QString filename = _outputDirectory + "/getMilestones.py"; + if (QFile::exists(filename)) { + QFile::remove(filename); + } + QFile file(filename); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not create 'getMilestones.py'"); + exit(-1); + } + + QTextStream stream(&file); + + // Code to access TestRail + stream << "from testrail import *\n"; + stream << "client = APIClient('" << _url.toStdString().c_str() << "')\n"; + stream << "client.user = '" << _user << "'\n"; + stream << "client.password = '" << _password << "'\n\n"; + + // Print the list of uncompleted milestones + stream << "file = open('" + _outputDirectory + "/milestones.txt', 'w')\n\n"; + stream << "milestones = client.send_get('get_milestones/" + _project + "')\n"; + stream << "for milestone in milestones:\n"; + stream << "\tif milestone['is_completed'] == False:\n"; + stream << "\t\tfile.write(milestone['name'] + ' ' + str(milestone['id']) + '\\n')\n\n"; + stream << "file.close()\n"; + + file.close(); + + QProcess* process = new QProcess(); + connect(process, static_cast(&QProcess::finished), this, + [=](int exitCode, QProcess::ExitStatus exitStatus) { + updateMilestonesComboData(exitCode, exitStatus); + } + ); + + QStringList parameters = QStringList() << _outputDirectory + "/getMilestones.py "; + process->start(_pythonCommand, parameters); +} + void TestRailInterface::createTestSuitePython(const QString& testDirectory, const QString& outputDirectory, const QString& userGitHub, const QString& branchGitHub) { - + + _testDirectory = testDirectory; + _outputDirectory = outputDirectory; + _userGitHub = userGitHub; + _branchGitHub = branchGitHub; + // First check that Python is available - QProcessEnvironment e = QProcessEnvironment::systemEnvironment(); - QStringList sl = e.toStringList(); if (QProcessEnvironment::systemEnvironment().contains("PYTHON_PATH")) { - _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH"); + QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH"); if (!QFile::exists(_pythonPath + "/" + pythonExe)) { QMessageBox::critical(0, pythonExe, QString("Python executable not found in ") + _pythonPath); } + _pythonCommand = _pythonPath + "/" + pythonExe; } else { QMessageBox::critical(0, "PYTHON_PATH not defined", "Please set PYTHON_PATH to directory containing the Python executable"); + return; } - createTestRailDotPyScript(outputDirectory); - createStackDotPyScript(outputDirectory); - requestDataFromUser(); - createAddTestCasesPythonScript(testDirectory, outputDirectory, userGitHub, branchGitHub); + requestTestRailDataFromUser(); + getMilestonesFromTestRail(); + createTestRailDotPyScript(); + createStackDotPyScript(); } void TestRailInterface::createTestSuiteXML(const QString& testDirectory, @@ -332,6 +434,8 @@ void TestRailInterface::createTestSuitePython(const QString& testDirectory, const QString& userGitHub, const QString& branchGitHub) { + _outputDirectory = outputDirectory; + QDomProcessingInstruction instruction = _document.createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'"); _document.appendChild(instruction); @@ -353,7 +457,7 @@ void TestRailInterface::createTestSuitePython(const QString& testDirectory, root.appendChild(topLevelSection); // Write to file - const QString testRailsFilename{ outputDirectory + "/TestRailSuite.xml" }; + const QString testRailsFilename{ _outputDirectory + "/TestRailSuite.xml" }; QFile file(testRailsFilename); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Could not create XML file"); @@ -559,9 +663,12 @@ void TestRailInterface::processTestPython(const QString& fullDirectory, QString testContent = QString("Execute instructions in [THIS TEST](") + testMDName + ")"; QString testExpected = QString("Refer to the expected result in the linked description."); + int milestone_id = _milestones[_milestoneNames[_testRailSelectorWindow.getMilestoneID()]]; + stream << "data = {\n\t" << "'title': '" << title << "',\n\t" << "'template_id': 2,\n\t" + << "'milestone_id': " << milestone_id << ",\n\t" << "'custom_preconds': " << "'Tester is in an empty region of a domain in which they have edit rights\\n\\n*Note: Press \\'n\\' to advance test script',\n\t" << "'custom_steps_separated': " << "[\n\t\t{\n\t\t\t'content': '" << testContent << "',\n\t\t\t'expected': '" << testExpected << "'\n\t\t}\n\t]\n" << "}\n"; diff --git a/tools/auto-tester/src/TestRailInterface.h b/tools/auto-tester/src/TestRailInterface.h index 4c094a27f3..f7fe131416 100644 --- a/tools/auto-tester/src/TestRailInterface.h +++ b/tools/auto-tester/src/TestRailInterface.h @@ -15,6 +15,7 @@ #include "ui/TestRailSelectorWindow.h" #include #include +#include #include class TestRailInterface : public QObject{ @@ -49,11 +50,12 @@ public: const QString& userGitHub, const QString& branchGitHub); - void createTestRailDotPyScript(const QString& outputDirectory); - void createStackDotPyScript(const QString& outputDirectory); - void requestDataFromUser(); + void getMilestonesFromTestRail(); + void createTestRailDotPyScript(); + void createStackDotPyScript(); + void requestTestRailDataFromUser(); + void requestMilestoneFromUser(); void createAddTestCasesPythonScript(const QString& testDirectory, - const QString& outputDirectory, const QString& userGitHub, const QString& branchGitHub); @@ -65,6 +67,8 @@ public: bool isAValidTestDirectory(const QString& directory); QString getObject(const QString& path); + void updateMilestonesComboData(int exitCode, QProcess::ExitStatus exitStatus); + private: QDomDocument _document; @@ -76,9 +80,15 @@ private: QString _password; QString _project; - QString _pythonPath; + QString _testDirectory; + QString _outputDirectory; + QString _userGitHub; + QString _branchGitHub; const QString pythonExe{ "python.exe" }; + QString _pythonCommand; + std::map _milestones; + QStringList _milestoneNames; }; #endif \ No newline at end of file diff --git a/tools/auto-tester/src/ui/TestRailSelectorWindow.cpp b/tools/auto-tester/src/ui/TestRailSelectorWindow.cpp index b2555ae4ad..9f07c25f3a 100644 --- a/tools/auto-tester/src/ui/TestRailSelectorWindow.cpp +++ b/tools/auto-tester/src/ui/TestRailSelectorWindow.cpp @@ -19,6 +19,17 @@ TestRailSelectorWindow::TestRailSelectorWindow(QWidget *parent) { projectLineEdit->setValidator(new QIntValidator(1, 999, this)); } +void TestRailSelectorWindow::on_acceptButton_clicked() { + urlLineEdit->setDisabled(true); + userLineEdit->setDisabled(true); + passwordLineEdit->setDisabled(true); + projectLineEdit->setDisabled(true); + + OKButton->setDisabled(false); + milestoneComboBox->setDisabled(false); + close(); +} + void TestRailSelectorWindow::on_OKButton_clicked() { userCancelled = false; close(); @@ -34,19 +45,19 @@ bool TestRailSelectorWindow::getUserCancelled() { } void TestRailSelectorWindow::setURL(const QString& user) { - URLTextEdit->setText(user); + urlLineEdit->setText(user); } QString TestRailSelectorWindow::getURL() { - return URLTextEdit->toPlainText(); + return urlLineEdit->text(); } void TestRailSelectorWindow::setUser(const QString& user) { - userTextEdit->setText(user); + userLineEdit->setText(user); } QString TestRailSelectorWindow::getUser() { - return userTextEdit->toPlainText(); + return userLineEdit->text(); } QString TestRailSelectorWindow::getPassword() { @@ -60,3 +71,11 @@ void TestRailSelectorWindow::setProject(const int project) { int TestRailSelectorWindow::getProject() { return projectLineEdit->text().toInt(); } + +void TestRailSelectorWindow::updateMilestoneComboBoxData(QStringList data) { + milestoneComboBox->insertItems(0, data); +} + +int TestRailSelectorWindow::getMilestoneID() { + return milestoneComboBox->currentIndex(); +} \ No newline at end of file diff --git a/tools/auto-tester/src/ui/TestRailSelectorWindow.h b/tools/auto-tester/src/ui/TestRailSelectorWindow.h index 821102b6bc..7072c1cd46 100644 --- a/tools/auto-tester/src/ui/TestRailSelectorWindow.h +++ b/tools/auto-tester/src/ui/TestRailSelectorWindow.h @@ -33,7 +33,11 @@ public: bool userCancelled{ false }; + void updateMilestoneComboBoxData(QStringList data); + int getMilestoneID(); + private slots: + void on_acceptButton_clicked(); void on_OKButton_clicked(); void on_cancelButton_clicked(); }; diff --git a/tools/auto-tester/src/ui/TestRailSelectorWindow.ui b/tools/auto-tester/src/ui/TestRailSelectorWindow.ui index dfc477deb0..98c52f3194 100644 --- a/tools/auto-tester/src/ui/TestRailSelectorWindow.ui +++ b/tools/auto-tester/src/ui/TestRailSelectorWindow.ui @@ -7,7 +7,7 @@ 0 0 489 - 312 + 415 @@ -35,7 +35,7 @@ 70 - 115 + 125 121 20 @@ -67,21 +67,14 @@ TestRail URL - - - - 200 - 25 - 231 - 24 - - - + + false + 120 - 240 + 350 93 28 @@ -94,7 +87,7 @@ 280 - 240 + 350 93 28 @@ -107,7 +100,7 @@ 200 - 115 + 120 231 24 @@ -116,21 +109,11 @@ QLineEdit::Password - - - - 200 - 70 - 231 - 24 - - - 70 - 70 + 75 121 20 @@ -148,7 +131,7 @@ 200 - 160 + 170 231 24 @@ -161,7 +144,7 @@ 70 - 160 + 175 121 20 @@ -175,6 +158,79 @@ TestRail Project + + + + 200 + 220 + 231 + 28 + + + + Accept + + + + + false + + + + 270 + 280 + 161 + 22 + + + + + + true + + + + 140 + 280 + 121 + 20 + + + + + 10 + + + + TestRail Milestone + + + + + + 200 + 20 + 231 + 24 + + + + QLineEdit::Normal + + + + + + 200 + 70 + 231 + 24 + + + + QLineEdit::Normal + +