Merge mess...

This commit is contained in:
NissimHadar 2019-01-24 16:30:21 -08:00
parent 684172bf6b
commit c2ee8f7795
33 changed files with 4021 additions and 75 deletions

View file

@ -81,7 +81,7 @@ if (ANDROID)
set(GLES_OPTION ON)
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
else ()
set(PLATFORM_QT_COMPONENTS WebEngine Xml)
set(PLATFORM_QT_COMPONENTS WebEngine)
endif ()
if (USE_GLES AND (NOT ANDROID))

View file

@ -32,8 +32,8 @@ To produce an executable installer on Windows, the following are required:
1. [InetC Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0
1. Extract Zip
1. Copy `Plugins\x86-ansi\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
1. Copy `Plugins\x86-unicode\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
1. Copy `Plugin\x86-ansi\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-ansi\`
1. Copy `Plugin\x86-unicode\InetC.dll` to `C:\Program Files (x86)\NSIS\Plugins\x86-unicode\`
1. [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0
1. Extract Zip

View file

@ -1,36 +0,0 @@
#
# FixupNitpick.cmake
# cmake/macros
#
# Copyright 2019 High Fidelity, Inc.
# Created by Nissim Hadar on January 14th, 2016
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(fixup_nitpick)
if (APPLE)
string(REPLACE " " "\\ " ESCAPED_BUNDLE_NAME ${NITPICK_BUNDLE_NAME})
string(REPLACE " " "\\ " ESCAPED_INSTALL_PATH ${NITPICK_INSTALL_DIR})
set(_NITPICK_INSTALL_PATH "${ESCAPED_INSTALL_PATH}/${ESCAPED_BUNDLE_NAME}.app")
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH)
if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD))
message(FATAL_ERROR "Could not find macdeployqt at ${QT_DIR}/bin.\
It is required to produce a relocatable nitpick application.\
Check that the environment variable QT_DIR points to your Qt installation.\
")
endif ()
install(CODE "
execute_process(COMMAND ${MACDEPLOYQT_COMMAND}\
\${CMAKE_INSTALL_PREFIX}/${_NITPICK_INSTALL_PATH}/\
-verbose=2 -qmldir=${CMAKE_SOURCE_DIR}/interface/resources/qml/\
)"
COMPONENT ${CLIENT_COMPONENT}
)
endif ()
endmacro()

View file

@ -77,9 +77,6 @@ macro(SET_PACKAGING_PARAMETERS)
add_definitions(-DDEV_BUILD)
endif ()
set(NITPICK_BUNDLE_NAME "nitpick")
set(NITPICK_ICON_PREFIX "nitpick")
string(TIMESTAMP BUILD_TIME "%d/%m/%Y")
# if STABLE_BUILD is 1, PRODUCTION_BUILD must be 1 and
@ -143,9 +140,8 @@ macro(SET_PACKAGING_PARAMETERS)
set(DMG_SUBFOLDER_ICON "${HF_CMAKE_DIR}/installer/install-folder.rsrc")
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
if (CLIENT_ONLY)
set(CONSOLE_EXEC_NAME "Console.app")
@ -163,14 +159,11 @@ macro(SET_PACKAGING_PARAMETERS)
set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app")
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns")
set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.icns")
else ()
if (WIN32)
set(CONSOLE_INSTALL_DIR "server-console")
set(NITPICK_INSTALL_DIR "nitpick")
else ()
set(CONSOLE_INSTALL_DIR ".")
set(NITPICK_INSTALL_DIR ".")
endif ()
set(COMPONENT_INSTALL_DIR ".")
@ -180,7 +173,6 @@ macro(SET_PACKAGING_PARAMETERS)
if (WIN32)
set(INTERFACE_EXEC_PREFIX "interface")
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.ico")
set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.ico")
set(CONSOLE_EXEC_NAME "server-console.exe")

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

View file

@ -16,7 +16,7 @@
#include <QObject>
#include <QTextStream>
#include "BusyWindow.h"
#include "ui/BusyWindow.h"
#include "PythonInterface.h"

View file

@ -16,13 +16,13 @@
PythonInterface::PythonInterface() {
#ifdef Q_OS_WIN
if (QProcessEnvironment::systemEnvironment().contains("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);
QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH");
if (!QFile::exists(_pythonPath + "/" + _pythonExe)) {
QMessageBox::critical(0, _pythonExe, QString("Python executable not found in ") + _pythonPath);
exit(-1);
}
_pythonCommand = pythonPath + "/" + _pythonExe;
_pythonCommand = _pythonPath + "/" + _pythonExe;
} else {
QMessageBox::critical(0, "PYTHON_PATH not defined",
"Please set PYTHON_PATH to directory containing the Python executable");
@ -31,7 +31,7 @@ PythonInterface::PythonInterface() {
#elif defined Q_OS_MAC
_pythonCommand = "/usr/local/bin/python3";
if (!QFile::exists(_pythonCommand)) {
QMessageBox::critical(0, "python not found",
QMessageBox::critical(0, "PYTHON_PATH not defined",
"python3 not found at " + _pythonCommand);
exit(-1);
}

View file

@ -19,7 +19,7 @@
#include <quazip5/quazip.h>
#include <quazip5/JlCompress.h>
#include "Nitpick.h"
#include "ui/Nitpick.h"
extern Nitpick* nitpick;
#include <math.h>

View file

@ -18,7 +18,7 @@
#include "AWSInterface.h"
#include "ImageComparer.h"
#include "MismatchWindow.h"
#include "ui/MismatchWindow.h"
#include "TestRailInterface.h"
class Step {

View file

@ -11,10 +11,10 @@
#ifndef hifi_test_testrail_interface_h
#define hifi_test_testrail_interface_h
#include "BusyWindow.h"
#include "TestRailTestCasesSelectorWindow.h"
#include "TestRailRunSelectorWindow.h"
#include "TestRailResultsSelectorWindow.h"
#include "ui/BusyWindow.h"
#include "ui/TestRailTestCasesSelectorWindow.h"
#include "ui/TestRailRunSelectorWindow.h"
#include "ui/TestRailResultsSelectorWindow.h"
#include <QDirIterator>
#include <QtXml/QDomDocument>

View file

@ -1,7 +1,7 @@
//
// TestRunner.cpp
//
// Created by Nissim Hadar on 23 Jan 2019.
// Created by Nissim Hadar on 1 Sept 2018.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -9,9 +9,70 @@
//
#include "TestRunner.h"
#include <QFileDialog>
#include <QThread>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QFileDialog>
void TestRunner::setWorkingFolder(QLabel* workingFolderLabel) {
#include "ui/Nitpick.h"
extern Nitpick* nitpick;
#ifdef Q_OS_WIN
#include <windows.h>
#include <tlhelp32.h>
#endif
// TODO: for debug
#include <iostream>
TestRunner::TestRunner(std::vector<QCheckBox*> dayCheckboxes,
std::vector<QCheckBox*> timeEditCheckboxes,
std::vector<QTimeEdit*> timeEdits,
QLabel* workingFolderLabel,
QCheckBox* runServerless,
QCheckBox* runLatest,
QLineEdit* url,
QPushButton* runNow,
QObject* parent) :
QObject(parent) {
_dayCheckboxes = dayCheckboxes;
_timeEditCheckboxes = timeEditCheckboxes;
_timeEdits = timeEdits;
_workingFolderLabel = workingFolderLabel;
_runServerless = runServerless;
_runLatest = runLatest;
_url = url;
_runNow = runNow;
_installerThread = new QThread();
_installerWorker = new Worker();
_installerWorker->moveToThread(_installerThread);
_installerThread->start();
connect(this, SIGNAL(startInstaller()), _installerWorker, SLOT(runCommand()));
connect(_installerWorker, SIGNAL(commandComplete()), this, SLOT(installationComplete()));
_interfaceThread = new QThread();
_interfaceWorker = new Worker();
_interfaceThread->start();
_interfaceWorker->moveToThread(_interfaceThread);
connect(this, SIGNAL(startInterface()), _interfaceWorker, SLOT(runCommand()));
connect(_interfaceWorker, SIGNAL(commandComplete()), this, SLOT(interfaceExecutionComplete()));
}
TestRunner::~TestRunner() {
delete _installerThread;
delete _installerWorker;
delete _interfaceThread;
delete _interfaceWorker;
if (_timer) {
delete _timer;
}
}
void TestRunner::setWorkingFolder() {
// Everything will be written to this folder
QString previousSelection = _workingFolder;
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
@ -19,8 +80,8 @@ void TestRunner::setWorkingFolder(QLabel* workingFolderLabel) {
parent += "/";
}
_workingFolder = QFileDialog::getExistingDirectory(nullptr, "Please select a working folder for temporary files", parent,
QFileDialog::ShowDirsOnly);
_workingFolder = QFileDialog::getExistingDirectory(nullptr, "Please select a temporary folder for installation", parent,
QFileDialog::ShowDirsOnly);
// If user canceled then restore previous selection and return
if (_workingFolder == "") {
@ -28,7 +89,726 @@ void TestRunner::setWorkingFolder(QLabel* workingFolderLabel) {
return;
}
workingFolderLabel->setText(QDir::toNativeSeparators(_workingFolder));
#ifdef Q_OS_WIN
_installationFolder = _workingFolder + "/High Fidelity";
#elif defined Q_OS_MAC
_installationFolder = _workingFolder + "/High_Fidelity";
#endif
_logFile.setFileName(_workingFolder + "/log.txt");
nitpick->enableRunTabControls();
_workingFolderLabel->setText(QDir::toNativeSeparators(_workingFolder));
_timer = new QTimer(this);
connect(_timer, SIGNAL(timeout()), this, SLOT(checkTime()));
_timer->start(30 * 1000); //time specified in ms
#ifdef Q_OS_MAC
// Create MAC shell scripts
QFile script;
// This script waits for a process to start
script.setFileName(_workingFolder + "/waitForStart.sh");
if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
"Could not open 'waitForStart.sh'");
exit(-1);
}
script.write("#!/bin/sh\n\n");
script.write("PROCESS=\"$1\"\n");
script.write("until (pgrep -x $PROCESS >nul)\n");
script.write("do\n");
script.write("\techo waiting for \"$1\" to start\n");
script.write("\tsleep 2\n");
script.write("done\n");
script.write("echo \"$1\" \"started\"\n");
script.close();
script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
// The Mac shell command returns immediately. This little script waits for a process to finish
script.setFileName(_workingFolder + "/waitForFinish.sh");
if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
"Could not open 'waitForFinish.sh'");
exit(-1);
}
script.write("#!/bin/sh\n\n");
script.write("PROCESS=\"$1\"\n");
script.write("while (pgrep -x $PROCESS >nul)\n");
script.write("do\n");
script.write("\techo waiting for \"$1\" to finish\n");
script.write("\tsleep 2\n");
script.write("done\n");
script.write("echo \"$1\" \"finished\"\n");
script.close();
script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
// Create an AppleScript to resize Interface. This is needed so that snapshots taken
// with the primary camera will be the correct size.
// This will be run from a normal shell script
script.setFileName(_workingFolder + "/setInterfaceSizeAndPosition.scpt");
if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
"Could not open 'setInterfaceSizeAndPosition.scpt'");
exit(-1);
}
script.write("set width to 960\n");
script.write("set height to 540\n");
script.write("set x to 100\n");
script.write("set y to 100\n\n");
script.write("tell application \"System Events\" to tell application process \"interface\" to tell window 1 to set {size, position} to {{width, height}, {x, y}}\n");
script.close();
script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
script.setFileName(_workingFolder + "/setInterfaceSizeAndPosition.sh");
if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
"Could not open 'setInterfaceSizeAndPosition.sh'");
exit(-1);
}
script.write("#!/bin/sh\n\n");
script.write("echo resizing interface\n");
script.write(("osascript " + _workingFolder + "/setInterfaceSizeAndPosition.scpt\n").toStdString().c_str());
script.write("echo resize complete\n");
script.close();
script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
#endif
}
void TestRunner::run() {
_runNow->setEnabled(false);
_testStartDateTime = QDateTime::currentDateTime();
_automatedTestIsRunning = true;
// Initial setup
_branch = nitpick->getSelectedBranch();
_user = nitpick->getSelectedUser();
// This will be restored at the end of the tests
saveExistingHighFidelityAppDataFolder();
// Download the latest High Fidelity build XML.
// Note that this is not needed for PR builds (or whenever `Run Latest` is unchecked)
// It is still downloaded, to simplify the flow
QStringList urls;
QStringList filenames;
urls << DEV_BUILD_XML_URL;
filenames << DEV_BUILD_XML_FILENAME;
updateStatusLabel("Downloading Build XML");
buildXMLDownloaded = false;
nitpick->downloadFiles(urls, _workingFolder, filenames, (void*)this);
// `downloadComplete` will run after download has completed
}
void TestRunner::downloadComplete() {
if (!buildXMLDownloaded) {
// Download of Build XML has completed
buildXMLDownloaded = true;
// Download the High Fidelity installer
QStringList urls;
QStringList filenames;
if (_runLatest->isChecked()) {
parseBuildInformation();
_installerFilename = INSTALLER_FILENAME_LATEST;
urls << _buildInformation.url;
filenames << _installerFilename;
} else {
QString urlText = _url->text();
urls << urlText;
_installerFilename = getInstallerNameFromURL(urlText);
filenames << _installerFilename;
}
updateStatusLabel("Downloading installer");
nitpick->downloadFiles(urls, _workingFolder, filenames, (void*)this);
// `downloadComplete` will run again after download has completed
} else {
// Download of Installer has completed
appendLog(QString("Tests started at ") + QString::number(_testStartDateTime.time().hour()) + ":" +
QString("%1").arg(_testStartDateTime.time().minute(), 2, 10, QChar('0')) + ", on " +
_testStartDateTime.date().toString("ddd, MMM d, yyyy"));
updateStatusLabel("Installing");
// Kill any existing processes that would interfere with installation
killProcesses();
runInstaller();
}
}
void TestRunner::runInstaller() {
// Qt cannot start an installation process using QProcess::start (Qt Bug 9761)
// To allow installation, the installer is run using the `system` command
QStringList arguments{ QStringList() << QString("/S") << QString("/D=") + QDir::toNativeSeparators(_installationFolder) };
QString installerFullPath = _workingFolder + "/" + _installerFilename;
QString commandLine;
#ifdef Q_OS_WIN
commandLine = "\"" + QDir::toNativeSeparators(installerFullPath) + "\"" + " /S /D=" + QDir::toNativeSeparators(_installationFolder);
#elif defined Q_OS_MAC
// Create installation shell script
QFile script;
script.setFileName(_workingFolder + "/install_app.sh");
if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
"Could not open 'install_app.sh'");
exit(-1);
}
if (!QDir().exists(_installationFolder)) {
QDir().mkdir(_installationFolder);
}
// This script installs High Fidelity. It is run as "yes | install_app.sh... so "yes" is killed at the end
script.write("#!/bin/sh\n\n");
script.write("VOLUME=`hdiutil attach \"$1\" | grep Volumes | awk '{print $3}'`\n");
QString folderName {"High Fidelity"};
if (!_runLatest->isChecked()) {
folderName += QString(" - ") + getPRNumberFromURL(_url->text());
}
script.write((QString("cp -rf \"$VOLUME/") + folderName + "/interface.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str());
script.write((QString("cp -rf \"$VOLUME/") + folderName + "/Sandbox.app\" \"" + _workingFolder + "/High_Fidelity/\"\n").toStdString().c_str());
script.write("hdiutil detach \"$VOLUME\"\n");
script.write("killall yes\n");
script.close();
script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
commandLine = "yes | " + _workingFolder + "/install_app.sh " + installerFullPath;
#endif
appendLog(commandLine);
_installerWorker->setCommandLine(commandLine);
emit startInstaller();
}
void TestRunner::installationComplete() {
verifyInstallationSucceeded();
createSnapshotFolder();
updateStatusLabel("Running tests");
if (!_runServerless->isChecked()) {
startLocalServerProcesses();
}
runInterfaceWithTestScript();
}
void TestRunner::verifyInstallationSucceeded() {
// Exit if the executables are missing.
// On Windows, the reason is probably that UAC has blocked the installation. This is treated as a critical error
#ifdef Q_OS_WIN
QFileInfo interfaceExe(QDir::toNativeSeparators(_installationFolder) + "\\interface.exe");
QFileInfo assignmentClientExe(QDir::toNativeSeparators(_installationFolder) + "\\assignment-client.exe");
QFileInfo domainServerExe(QDir::toNativeSeparators(_installationFolder) + "\\domain-server.exe");
if (!interfaceExe.exists() || !assignmentClientExe.exists() || !domainServerExe.exists()) {
QMessageBox::critical(0, "Installation of High Fidelity has failed", "Please verify that UAC has been disabled");
exit(-1);
}
#endif
}
void TestRunner::saveExistingHighFidelityAppDataFolder() {
QString dataDirectory{ "NOT FOUND" };
#ifdef Q_OS_WIN
dataDirectory = qgetenv("USERPROFILE") + "\\AppData\\Roaming";
#elif defined Q_OS_MAC
dataDirectory = QDir::homePath() + "/Library/Application Support";
#endif
if (_runLatest->isChecked()) {
_appDataFolder = dataDirectory + "/High Fidelity";
} else {
// We are running a PR build
_appDataFolder = dataDirectory + "/High Fidelity - " + getPRNumberFromURL(_url->text());
}
_savedAppDataFolder = dataDirectory + "/" + UNIQUE_FOLDER_NAME;
if (QDir(_savedAppDataFolder).exists()) {
_savedAppDataFolder.removeRecursively();
}
if (_appDataFolder.exists()) {
// The original folder is saved in a unique name
_appDataFolder.rename(_appDataFolder.path(), _savedAppDataFolder.path());
}
// Copy an "empty" AppData folder (i.e. no entities)
copyFolder(QDir::currentPath() + "/AppDataHighFidelity", _appDataFolder.path());
}
void TestRunner::createSnapshotFolder() {
_snapshotFolder = _workingFolder + "/" + SNAPSHOT_FOLDER_NAME;
// Just delete all PNGs from the folder if it already exists
if (QDir(_snapshotFolder).exists()) {
// Note that we cannot use just a `png` filter, as the filenames include periods
// Also, delete any `jpg` and `txt` files
// The idea is to leave only previous zipped result folders
QDirIterator it(_snapshotFolder);
while (it.hasNext()) {
QString filename = it.next();
if (filename.right(4) == ".png" || filename.right(4) == ".jpg" || filename.right(4) == ".txt") {
QFile::remove(filename);
}
}
} else {
QDir().mkdir(_snapshotFolder);
}
}
void TestRunner::killProcesses() {
#ifdef Q_OS_WIN
try {
QStringList processesToKill = QStringList() << "interface.exe"
<< "assignment-client.exe"
<< "domain-server.exe"
<< "server-console.exe";
// Loop until all pending processes to kill have actually died
QStringList pendingProcessesToKill;
do {
pendingProcessesToKill.clear();
// Get list of running tasks
HANDLE processSnapHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (processSnapHandle == INVALID_HANDLE_VALUE) {
throw("Process snapshot creation failure");
}
PROCESSENTRY32 processEntry32;
processEntry32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(processSnapHandle, &processEntry32)) {
CloseHandle(processSnapHandle);
throw("Process32First failed");
}
// Kill any task in the list
do {
foreach (QString process, processesToKill)
if (QString(processEntry32.szExeFile) == process) {
QString commandLine = "taskkill /im " + process + " /f >nul";
system(commandLine.toStdString().c_str());
pendingProcessesToKill << process;
}
} while (Process32Next(processSnapHandle, &processEntry32));
QThread::sleep(2);
} while (!pendingProcessesToKill.isEmpty());
} catch (QString errorMessage) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage);
exit(-1);
} catch (...) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error");
exit(-1);
}
#elif defined Q_OS_MAC
QString commandLine;
commandLine = QString("killall interface") + "; " + _workingFolder +"/waitForFinish.sh interface";
system(commandLine.toStdString().c_str());
commandLine = QString("killall Sandbox") + "; " + _workingFolder +"/waitForFinish.sh Sandbox";
system(commandLine.toStdString().c_str());
commandLine = QString("killall Console") + "; " + _workingFolder +"/waitForFinish.sh Console";
system(commandLine.toStdString().c_str());
#endif
}
void TestRunner::startLocalServerProcesses() {
QString commandLine;
#ifdef Q_OS_WIN
commandLine =
"start \"domain-server.exe\" \"" + QDir::toNativeSeparators(_installationFolder) + "\\domain-server.exe\"";
system(commandLine.toStdString().c_str());
commandLine =
"start \"assignment-client.exe\" \"" + QDir::toNativeSeparators(_installationFolder) + "\\assignment-client.exe\" -n 6";
system(commandLine.toStdString().c_str());
#elif defined Q_OS_MAC
commandLine = "open \"" +_installationFolder + "/Sandbox.app\"";
system(commandLine.toStdString().c_str());
#endif
// Give server processes time to stabilize
QThread::sleep(20);
}
void TestRunner::runInterfaceWithTestScript() {
QString url = QString("hifi://localhost");
if (_runServerless->isChecked()) {
// Move to an empty area
url = "file:///~serverless/tutorial.json";
} else {
url = "hifi://localhost";
}
QString deleteScript =
QString("https://raw.githubusercontent.com/") + _user + "/hifi_tests/" + _branch + "/tests/utils/deleteNearbyEntities.js";
QString testScript =
QString("https://raw.githubusercontent.com/") + _user + "/hifi_tests/" + _branch + "/tests/testRecursive.js";
QString commandLine;
#ifdef Q_OS_WIN
QString exeFile;
// First, run script to delete any entities in test area
// Note that this will run to completion before continuing
exeFile = QString("\"") + QDir::toNativeSeparators(_installationFolder) + "\\interface.exe\"";
commandLine = "start /wait \"\" " + exeFile +
" --url " + url +
" --no-updater" +
" --no-login-suggestion"
" --testScript " + deleteScript + " quitWhenFinished";
system(commandLine.toStdString().c_str());
// Now run the test suite
exeFile = QString("\"") + QDir::toNativeSeparators(_installationFolder) + "\\interface.exe\"";
commandLine = exeFile +
" --url " + url +
" --no-updater" +
" --no-login-suggestion"
" --testScript " + testScript + " quitWhenFinished" +
" --testResultsLocation " + _snapshotFolder;
_interfaceWorker->setCommandLine(commandLine);
emit startInterface();
#elif defined Q_OS_MAC
QFile script;
script.setFileName(_workingFolder + "/runInterfaceTests.sh");
if (!script.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
"Could not open 'runInterfaceTests.sh'");
exit(-1);
}
script.write("#!/bin/sh\n\n");
// First, run script to delete any entities in test area
commandLine =
"open -W \"" +_installationFolder + "/interface.app\" --args" +
" --url " + url +
" --no-updater" +
" --no-login-suggestion"
" --testScript " + deleteScript + " quitWhenFinished\n";
script.write(commandLine.toStdString().c_str());
// On The Mac, we need to resize Interface. The Interface window opens a few seconds after the process
// has started.
// Before starting interface, start a process that will resize interface 10s after it opens
commandLine = _workingFolder +"/waitForStart.sh interface && sleep 10 && " + _workingFolder +"/setInterfaceSizeAndPosition.sh &\n";
script.write(commandLine.toStdString().c_str());
commandLine =
"open \"" +_installationFolder + "/interface.app\" --args" +
" --url " + url +
" --no-updater" +
" --no-login-suggestion"
" --testScript " + testScript + " quitWhenFinished" +
" --testResultsLocation " + _snapshotFolder +
" && " + _workingFolder +"/waitForFinish.sh interface\n";
script.write(commandLine.toStdString().c_str());
script.close();
script.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
commandLine = _workingFolder + "/runInterfaceTests.sh";
_interfaceWorker->setCommandLine(commandLine);
emit startInterface();
#endif
// Helpful for debugging
appendLog(commandLine);
}
void TestRunner::interfaceExecutionComplete() {
QFileInfo testCompleted(QDir::toNativeSeparators(_snapshotFolder) +"/tests_completed.txt");
if (!testCompleted.exists()) {
QMessageBox::critical(0, "Tests not completed", "Interface seems to have crashed before completion of the test scripts\nExisting images will be evaluated");
}
evaluateResults();
killProcesses();
// The High Fidelity AppData folder will be restored after evaluation has completed
}
void TestRunner::evaluateResults() {
updateStatusLabel("Evaluating results");
nitpick->startTestsEvaluation(false, true, _snapshotFolder, _branch, _user);
}
void TestRunner::automaticTestRunEvaluationComplete(QString zippedFolder, int numberOfFailures) {
addBuildNumberToResults(zippedFolder);
restoreHighFidelityAppDataFolder();
updateStatusLabel("Testing complete");
QDateTime currentDateTime = QDateTime::currentDateTime();
QString completionText = QString("Tests completed at ") + QString::number(currentDateTime.time().hour()) + ":" +
QString("%1").arg(currentDateTime.time().minute(), 2, 10, QChar('0')) + ", on " +
currentDateTime.date().toString("ddd, MMM d, yyyy");
if (numberOfFailures == 0) {
completionText += "; no failures";
} else if (numberOfFailures == 1) {
completionText += "; 1 failure";
} else {
completionText += QString("; ") + QString::number(numberOfFailures) + " failures";
}
appendLog(completionText);
_automatedTestIsRunning = false;
_runNow->setEnabled(true);
}
void TestRunner::addBuildNumberToResults(QString zippedFolderName) {
QString augmentedFilename;
if (!_runLatest->isChecked()) {
augmentedFilename = zippedFolderName.replace("local", getPRNumberFromURL(_url->text()));
} else {
augmentedFilename = zippedFolderName.replace("local", _buildInformation.build);
}
QFile::rename(zippedFolderName, augmentedFilename);
}
void TestRunner::restoreHighFidelityAppDataFolder() {
_appDataFolder.removeRecursively();
if (_savedAppDataFolder != QDir()) {
_appDataFolder.rename(_savedAppDataFolder.path(), _appDataFolder.path());
}
}
// Copies a folder recursively
void TestRunner::copyFolder(const QString& source, const QString& destination) {
try {
if (!QFileInfo(source).isDir()) {
// just a file copy
QFile::copy(source, destination);
} else {
QDir destinationDir(destination);
if (!destinationDir.cdUp()) {
throw("'source '" + source + "'seems to be a root folder");
}
if (!destinationDir.mkdir(QFileInfo(destination).fileName())) {
throw("Could not create destination folder '" + destination + "'");
}
QStringList fileNames =
QDir(source).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
foreach (const QString& fileName, fileNames) {
copyFolder(QString(source + "/" + fileName), QString(destination + "/" + fileName));
}
}
} catch (QString errorMessage) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage);
exit(-1);
} catch (...) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error");
exit(-1);
}
}
void TestRunner::checkTime() {
// No processing is done if a test is running
if (_automatedTestIsRunning) {
return;
}
QDateTime now = QDateTime::currentDateTime();
// Check day of week
if (!_dayCheckboxes.at(now.date().dayOfWeek() - 1)->isChecked()) {
return;
}
// Check the time
bool timeToRun{ false };
for (size_t i = 0; i < std::min(_timeEditCheckboxes.size(), _timeEdits.size()); ++i) {
if (_timeEditCheckboxes[i]->isChecked() && (_timeEdits[i]->time().hour() == now.time().hour()) &&
(_timeEdits[i]->time().minute() == now.time().minute())) {
timeToRun = true;
break;
}
}
if (timeToRun) {
run();
}
}
void TestRunner::updateStatusLabel(const QString& message) {
nitpick->updateStatusLabel(message);
}
void TestRunner::appendLog(const QString& message) {
if (!_logFile.open(QIODevice::Append | QIODevice::Text)) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
"Could not open the log file");
exit(-1);
}
_logFile.write(message.toStdString().c_str());
_logFile.write("\n");
_logFile.close();
nitpick->appendLogWindow(message);
}
QString TestRunner::getInstallerNameFromURL(const QString& url) {
// An example URL: https://deployment.highfidelity.com/jobs/pr-build/label%3Dwindows/13023/HighFidelity-Beta-Interface-PR14006-be76c43.exe
// On Mac, replace `exe` with `dmg`
try {
QStringList urlParts = url.split("/");
return urlParts[urlParts.size() - 1];
} catch (QString errorMessage) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage);
exit(-1);
} catch (...) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error");
exit(-1);
}
}
QString TestRunner::getPRNumberFromURL(const QString& url) {
try {
QStringList urlParts = url.split("/");
QStringList filenameParts = urlParts[urlParts.size() - 1].split("-");
if (filenameParts.size() <= 3) {
#ifdef Q_OS_WIN
throw "URL not in expected format, should look like `https://deployment.highfidelity.com/jobs/pr-build/label%3Dwindows/13023/HighFidelity-Beta-Interface-PR14006-be76c43.exe`";
#elif defined Q_OS_MAC
throw "URL not in expected format, should look like `https://deployment.highfidelity.com/jobs/pr-build/label%3Dwindows/13023/HighFidelity-Beta-Interface-PR14006-be76c43.dmg`";
#endif
}
return filenameParts[filenameParts.size() - 2];
} catch (QString errorMessage) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage);
exit(-1);
} catch (...) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error");
exit(-1);
}
}
void TestRunner::parseBuildInformation() {
try {
QDomDocument domDocument;
QString filename{ _workingFolder + "/" + DEV_BUILD_XML_FILENAME };
QFile file(filename);
if (!file.open(QIODevice::ReadOnly) || !domDocument.setContent(&file)) {
throw QString("Could not open " + filename);
}
QString platformOfInterest;
#ifdef Q_OS_WIN
platformOfInterest = "windows";
#elif defined(Q_OS_MAC)
platformOfInterest = "mac";
#endif
QDomElement element = domDocument.documentElement();
// Verify first element is "projects"
if (element.tagName() != "projects") {
throw("File seems to be in wrong format");
}
element = element.firstChild().toElement();
if (element.tagName() != "project") {
throw("File seems to be in wrong format");
}
if (element.attribute("name") != "interface") {
throw("File is not from 'interface' build");
}
// Now loop over the platforms, looking for ours
bool platformFound{ false };
element = element.firstChild().toElement();
while (!element.isNull()) {
if (element.attribute("name") == platformOfInterest) {
platformFound = true;
break;
}
element = element.nextSibling().toElement();
}
if (!platformFound) {
throw("File seems to be in wrong format - platform " + platformOfInterest + " not found");
}
element = element.firstChild().toElement();
if (element.tagName() != "build") {
throw("File seems to be in wrong format");
}
// Next element should be the version
element = element.firstChild().toElement();
if (element.tagName() != "version") {
throw("File seems to be in wrong format");
}
// Add the build number to the end of the filename
_buildInformation.build = element.text();
// First sibling should be stable_version
element = element.nextSibling().toElement();
if (element.tagName() != "stable_version") {
throw("File seems to be in wrong format");
}
// Next sibling should be url
element = element.nextSibling().toElement();
if (element.tagName() != "url") {
throw("File seems to be in wrong format");
}
_buildInformation.url = element.text();
} catch (QString errorMessage) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage);
exit(-1);
} catch (...) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error");
exit(-1);
}
}
void Worker::setCommandLine(const QString& commandLine) {
@ -40,4 +820,3 @@ int Worker::runCommand() {
emit commandComplete();
return result;
}

View file

@ -1,7 +1,7 @@
//
// TestRunner.h
//
// Created by Nissim Hadar on 23 Jan 2019.
// Created by Nissim Hadar on 1 Sept 2018.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -11,17 +11,133 @@
#ifndef hifi_testRunner_h
#define hifi_testRunner_h
#include <QCheckBox>
#include <QDir>
#include <QLabel>
#include <QLineEdit>
#include <QObject>
#include <QPushButton>
#include <QThread>
#include <QTimeEdit>
#include <QTimer>
class BuildInformation {
public:
QString build;
QString url;
};
class Worker;
class TestRunner {
class TestRunner : public QObject {
Q_OBJECT
public:
void setWorkingFolder(QLabel* workingFolderLabel);
explicit TestRunner(std::vector<QCheckBox*> dayCheckboxes,
std::vector<QCheckBox*> timeEditCheckboxes,
std::vector<QTimeEdit*> timeEdits,
QLabel* workingFolderLabel,
QCheckBox* runServerless,
QCheckBox* runLatest,
QLineEdit* url,
QPushButton* runNow,
QObject* parent = 0);
~TestRunner();
void setWorkingFolder();
void run();
void downloadComplete();
void runInstaller();
void verifyInstallationSucceeded();
void saveExistingHighFidelityAppDataFolder();
void restoreHighFidelityAppDataFolder();
void createSnapshotFolder();
void killProcesses();
void startLocalServerProcesses();
void runInterfaceWithTestScript();
void evaluateResults();
void automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures);
void addBuildNumberToResults(QString zippedFolderName);
void copyFolder(const QString& source, const QString& destination);
void updateStatusLabel(const QString& message);
void appendLog(const QString& message);
QString getInstallerNameFromURL(const QString& url);
QString getPRNumberFromURL(const QString& url);
void parseBuildInformation();
private slots:
void checkTime();
void installationComplete();
void interfaceExecutionComplete();
signals:
void startInstaller();
void startInterface();
void startResize();
private:
bool _automatedTestIsRunning{ false };
#ifdef Q_OS_WIN
const QString INSTALLER_FILENAME_LATEST{ "HighFidelity-Beta-latest-dev.exe" };
#elif defined(Q_OS_MAC)
const QString INSTALLER_FILENAME_LATEST{ "HighFidelity-Beta-latest-dev.dmg" };
#else
const QString INSTALLER_FILENAME_LATEST{ "" };
#endif
QString _installerURL;
QString _installerFilename;
const QString DEV_BUILD_XML_URL{ "https://highfidelity.com/dev-builds.xml" };
const QString DEV_BUILD_XML_FILENAME{ "dev-builds.xml" };
bool buildXMLDownloaded;
QDir _appDataFolder;
QDir _savedAppDataFolder;
protected:
QString _workingFolder;
QString _installationFolder;
QString _snapshotFolder;
const QString UNIQUE_FOLDER_NAME{ "fgadhcUDHSFaidsfh3478JJJFSDFIUSOEIrf" };
const QString SNAPSHOT_FOLDER_NAME{ "snapshots" };
QString _branch;
QString _user;
std::vector<QCheckBox*> _dayCheckboxes;
std::vector<QCheckBox*> _timeEditCheckboxes;
std::vector<QTimeEdit*> _timeEdits;
QLabel* _workingFolderLabel;
QCheckBox* _runServerless;
QCheckBox* _runLatest;
QLineEdit* _url;
QPushButton* _runNow;
QTimer* _timer;
QFile _logFile;
QDateTime _testStartDateTime;
QThread* _installerThread;
QThread* _interfaceThread;
Worker* _installerWorker;
Worker* _interfaceWorker;
BuildInformation _buildInformation;
};
class Worker : public QObject {
@ -34,9 +150,10 @@ public slots:
signals:
void commandComplete();
void startInstaller();
void startInterface();
private:
QString _commandLine;
};
#endif
#endif // hifi_testRunner_h

View file

@ -8,7 +8,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QtWidgets/QApplication>
#include "Nitpick.h"
#include "ui/Nitpick.h"
#include <iostream>

View file

@ -0,0 +1,14 @@
//
// BusyWindow.cpp
//
// Created by Nissim Hadar on 26 Jul 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "BusyWindow.h"
BusyWindow::BusyWindow(QWidget *parent) {
setupUi(this);
}

View file

@ -0,0 +1,22 @@
//
// BusyWindow.h
//
// Created by Nissim Hadar on 29 Jul 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_BusyWindow_h
#define hifi_BusyWindow_h
#include "ui_BusyWindow.h"
class BusyWindow : public QDialog, public Ui::BusyWindow {
Q_OBJECT
public:
BusyWindow(QWidget* parent = Q_NULLPTR);
};
#endif

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BusyWindow</class>
<widget class="QDialog" name="BusyWindow">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>542</width>
<height>189</height>
</rect>
</property>
<property name="windowTitle">
<string>Updating TestRail - please wait</string>
</property>
<widget class="QLabel" name="errorLabel">
<property name="geometry">
<rect>
<x>30</x>
<y>850</y>
<width>500</width>
<height>28</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>similarity</string>
</property>
</widget>
<widget class="QProgressBar" name="progressBar">
<property name="geometry">
<rect>
<x>40</x>
<y>40</y>
<width>481</width>
<height>101</height>
</rect>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>50</x>
<y>60</y>
<width>431</width>
<height>61</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string>Please wait for this window to close</string>
</property>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,101 @@
//
// MismatchWindow.cpp
//
// Created by Nissim Hadar on 9 Nov 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "MismatchWindow.h"
#include <QtCore/QFileInfo>
#include <cmath>
MismatchWindow::MismatchWindow(QWidget *parent) : QDialog(parent) {
setupUi(this);
expectedImage->setScaledContents(true);
resultImage->setScaledContents(true);
diffImage->setScaledContents(true);
}
QPixmap MismatchWindow::computeDiffPixmap(QImage expectedImage, QImage resultImage) {
// Create an empty difference image if the images differ in size
if (expectedImage.height() != resultImage.height() || expectedImage.width() != resultImage.width()) {
return QPixmap();
}
// This is an optimization, as QImage.setPixel() is embarrassingly slow
unsigned char* buffer = new unsigned char[expectedImage.height() * expectedImage.width() * 3];
// loop over each pixel
for (int y = 0; y < expectedImage.height(); ++y) {
for (int x = 0; x < expectedImage.width(); ++x) {
QRgb pixelP = expectedImage.pixel(QPoint(x, y));
QRgb pixelQ = resultImage.pixel(QPoint(x, y));
// Convert to luminance
double p = R_Y * qRed(pixelP) + G_Y * qGreen(pixelP) + B_Y * qBlue(pixelP);
double q = R_Y * qRed(pixelQ) + G_Y * qGreen(pixelQ) + B_Y * qBlue(pixelQ);
// The intensity value is modified to increase the brightness of the displayed image
double absoluteDifference = fabs(p - q) / 255.0;
double modifiedDifference = sqrt(absoluteDifference);
int difference = (int)(modifiedDifference * 255.0);
buffer[3 * (x + y * expectedImage.width()) + 0] = difference;
buffer[3 * (x + y * expectedImage.width()) + 1] = difference;
buffer[3 * (x + y * expectedImage.width()) + 2] = difference;
}
}
QImage diffImage(buffer, expectedImage.width(), expectedImage.height(), QImage::Format_RGB888);
QPixmap resultPixmap = QPixmap::fromImage(diffImage);
delete[] buffer;
return resultPixmap;
}
void MismatchWindow::setTestResult(TestResult testResult) {
errorLabel->setText("Similarity: " + QString::number(testResult._error));
imagePath->setText("Path to test: " + testResult._pathname);
expectedFilename->setText(testResult._expectedImageFilename);
resultFilename->setText(testResult._actualImageFilename);
QPixmap expectedPixmap = QPixmap(testResult._pathname + testResult._expectedImageFilename);
QPixmap actualPixmap = QPixmap(testResult._pathname + testResult._actualImageFilename);
_diffPixmap = computeDiffPixmap(
QImage(testResult._pathname + testResult._expectedImageFilename),
QImage(testResult._pathname + testResult._actualImageFilename)
);
expectedImage->setPixmap(expectedPixmap);
resultImage->setPixmap(actualPixmap);
diffImage->setPixmap(_diffPixmap);
}
void MismatchWindow::on_passTestButton_clicked() {
_userResponse = USER_RESPONSE_PASS;
close();
}
void MismatchWindow::on_failTestButton_clicked() {
_userResponse = USE_RESPONSE_FAIL;
close();
}
void MismatchWindow::on_abortTestsButton_clicked() {
_userResponse = USER_RESPONSE_ABORT;
close();
}
QPixmap MismatchWindow::getComparisonImage() {
return _diffPixmap;
}

View file

@ -0,0 +1,42 @@
//
// MismatchWindow.h
//
// Created by Nissim Hadar on 9 Nov 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_MismatchWindow_h
#define hifi_MismatchWindow_h
#include "ui_MismatchWindow.h"
#include "../common.h"
class MismatchWindow : public QDialog, public Ui::MismatchWindow {
Q_OBJECT
public:
MismatchWindow(QWidget *parent = Q_NULLPTR);
void setTestResult(TestResult testResult);
UserResponse getUserResponse() { return _userResponse; }
QPixmap computeDiffPixmap(QImage expectedImage, QImage resultImage);
QPixmap getComparisonImage();
private slots:
void on_passTestButton_clicked();
void on_failTestButton_clicked();
void on_abortTestsButton_clicked();
private:
UserResponse _userResponse{ USER_RESPONSE_INVALID };
QPixmap _diffPixmap;
};
#endif // hifi_MismatchWindow_h

View file

@ -0,0 +1,199 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MismatchWindow</class>
<widget class="QDialog" name="MismatchWindow">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1782</width>
<height>942</height>
</rect>
</property>
<property name="windowTitle">
<string>MismatchWindow</string>
</property>
<widget class="QLabel" name="expectedImage">
<property name="geometry">
<rect>
<x>10</x>
<y>25</y>
<width>800</width>
<height>450</height>
</rect>
</property>
<property name="text">
<string>expected image</string>
</property>
</widget>
<widget class="QLabel" name="resultImage">
<property name="geometry">
<rect>
<x>900</x>
<y>25</y>
<width>800</width>
<height>450</height>
</rect>
</property>
<property name="text">
<string>result image</string>
</property>
</widget>
<widget class="QLabel" name="diffImage">
<property name="geometry">
<rect>
<x>540</x>
<y>480</y>
<width>800</width>
<height>450</height>
</rect>
</property>
<property name="text">
<string>diff image</string>
</property>
</widget>
<widget class="QLabel" name="resultFilename">
<property name="geometry">
<rect>
<x>60</x>
<y>660</y>
<width>480</width>
<height>28</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>result image filename</string>
</property>
</widget>
<widget class="QLabel" name="expectedFilename">
<property name="geometry">
<rect>
<x>60</x>
<y>630</y>
<width>480</width>
<height>28</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>expected image filename</string>
</property>
</widget>
<widget class="QLabel" name="imagePath">
<property name="geometry">
<rect>
<x>20</x>
<y>600</y>
<width>1200</width>
<height>28</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>image path</string>
</property>
</widget>
<widget class="QPushButton" name="passTestButton">
<property name="geometry">
<rect>
<x>30</x>
<y>790</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Pass</string>
</property>
</widget>
<widget class="QPushButton" name="failTestButton">
<property name="geometry">
<rect>
<x>120</x>
<y>790</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Fail</string>
</property>
</widget>
<widget class="QPushButton" name="abortTestsButton">
<property name="geometry">
<rect>
<x>210</x>
<y>790</y>
<width>121</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Abort current test</string>
</property>
</widget>
<widget class="QLabel" name="errorLabel">
<property name="geometry">
<rect>
<x>30</x>
<y>850</y>
<width>500</width>
<height>28</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>similarity</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>30</x>
<y>5</y>
<width>151</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Expected Image</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>930</x>
<y>5</y>
<width>151</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Actual Image</string>
</property>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,314 @@
//
// Nitpick.cpp
// zone/ambientLightInheritence
//
// Created by Nissim Hadar on 2 Nov 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Nitpick.h"
#ifdef Q_OS_WIN
#include <windows.h>
#include <shellapi.h>
#endif
#include <QDesktopServices>
Nitpick::Nitpick(QWidget* parent) : QMainWindow(parent) {
_ui.setupUi(this);
_ui.checkBoxInteractiveMode->setChecked(true);
_ui.progressBar->setVisible(false);
_ui.tabWidget->setCurrentIndex(0);
_signalMapper = new QSignalMapper();
connect(_ui.actionClose, &QAction::triggered, this, &Nitpick::on_closeButton_clicked);
connect(_ui.actionAbout, &QAction::triggered, this, &Nitpick::about);
connect(_ui.actionContent, &QAction::triggered, this, &Nitpick::content);
// The second tab hides and shows the Windows task bar
#ifndef Q_OS_WIN
_ui.tabWidget->removeTab(1);
#endif
_ui.statusLabel->setText("");
_ui.plainTextEdit->setReadOnly(true);
setWindowTitle("Nitpick - v1.3.2");
}
Nitpick::~Nitpick() {
delete _signalMapper;
if (_test) {
delete _test;
}
if (_testRunner) {
delete _testRunner;
}
}
void Nitpick::setup() {
if (_test) {
delete _test;
}
_test = new Test(_ui.progressBar, _ui.checkBoxInteractiveMode);
std::vector<QCheckBox*> dayCheckboxes;
dayCheckboxes.emplace_back(_ui.mondayCheckBox);
dayCheckboxes.emplace_back(_ui.tuesdayCheckBox);
dayCheckboxes.emplace_back(_ui.wednesdayCheckBox);
dayCheckboxes.emplace_back(_ui.thursdayCheckBox);
dayCheckboxes.emplace_back(_ui.fridayCheckBox);
dayCheckboxes.emplace_back(_ui.saturdayCheckBox);
dayCheckboxes.emplace_back(_ui.sundayCheckBox);
std::vector<QCheckBox*> timeEditCheckboxes;
timeEditCheckboxes.emplace_back(_ui.timeEdit1checkBox);
timeEditCheckboxes.emplace_back(_ui.timeEdit2checkBox);
timeEditCheckboxes.emplace_back(_ui.timeEdit3checkBox);
timeEditCheckboxes.emplace_back(_ui.timeEdit4checkBox);
std::vector<QTimeEdit*> timeEdits;
timeEdits.emplace_back(_ui.timeEdit1);
timeEdits.emplace_back(_ui.timeEdit2);
timeEdits.emplace_back(_ui.timeEdit3);
timeEdits.emplace_back(_ui.timeEdit4);
if (_testRunner) {
delete _testRunner;
}
_testRunner = new TestRunner(dayCheckboxes, timeEditCheckboxes, timeEdits, _ui.workingFolderLabel, _ui.checkBoxServerless, _ui.checkBoxRunLatest, _ui.urlLineEdit, _ui.runNowButton);
}
void Nitpick::startTestsEvaluation(const bool isRunningFromCommandLine,
const bool isRunningInAutomaticTestRun,
const QString& snapshotDirectory,
const QString& branch,
const QString& user
) {
_test->startTestsEvaluation(isRunningFromCommandLine, isRunningInAutomaticTestRun, snapshotDirectory, branch, user);
}
void Nitpick::on_tabWidget_currentChanged(int index) {
// Enable the GitHub edit boxes as required
#ifdef Q_OS_WIN
if (index == 0 || index == 2 || index == 3) {
#else
if (index == 0 || index == 1 || index == 2) {
#endif
_ui.userLineEdit->setDisabled(false);
_ui.branchLineEdit->setDisabled(false);
} else {
_ui.userLineEdit->setDisabled(true);
_ui.branchLineEdit->setDisabled(true);
}
}
void Nitpick::on_evaluateTestsButton_clicked() {
_test->startTestsEvaluation(false, false);
}
void Nitpick::on_createRecursiveScriptButton_clicked() {
_test->createRecursiveScript();
}
void Nitpick::on_createAllRecursiveScriptsButton_clicked() {
_test->createAllRecursiveScripts();
}
void Nitpick::on_createTestsButton_clicked() {
_test->createTests();
}
void Nitpick::on_createMDFileButton_clicked() {
_test->createMDFile();
}
void Nitpick::on_createAllMDFilesButton_clicked() {
_test->createAllMDFiles();
}
void Nitpick::on_createTestAutoScriptButton_clicked() {
_test->createTestAutoScript();
}
void Nitpick::on_createAllTestAutoScriptsButton_clicked() {
_test->createAllTestAutoScripts();
}
void Nitpick::on_createTestsOutlineButton_clicked() {
_test->createTestsOutline();
}
void Nitpick::on_createTestRailTestCasesButton_clicked() {
_test->createTestRailTestCases();
}
void Nitpick::on_createTestRailRunButton_clicked() {
_test->createTestRailRun();
}
void Nitpick::on_setWorkingFolderButton_clicked() {
_testRunner->setWorkingFolder();
}
void Nitpick::enableRunTabControls() {
_ui.runNowButton->setEnabled(true);
_ui.daysGroupBox->setEnabled(true);
_ui.timesGroupBox->setEnabled(true);
}
void Nitpick::on_runNowButton_clicked() {
_testRunner->run();
}
void Nitpick::on_checkBoxRunLatest_clicked() {
_ui.urlLineEdit->setEnabled(!_ui.checkBoxRunLatest->isChecked());
}
void Nitpick::automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures) {
_testRunner->automaticTestRunEvaluationComplete(zippedFolderName, numberOfFailures);
}
void Nitpick::on_updateTestRailRunResultsButton_clicked() {
_test->updateTestRailRunResult();
}
// To toggle between show and hide
// if (uState & ABS_AUTOHIDE) on_showTaskbarButton_clicked();
// else on_hideTaskbarButton_clicked();
//
void Nitpick::on_hideTaskbarButton_clicked() {
#ifdef Q_OS_WIN
APPBARDATA abd = { sizeof abd };
UINT uState = (UINT)SHAppBarMessage(ABM_GETSTATE, &abd);
LPARAM param = uState & ABS_ALWAYSONTOP;
abd.lParam = ABS_AUTOHIDE | param;
SHAppBarMessage(ABM_SETSTATE, &abd);
#endif
}
void Nitpick::on_showTaskbarButton_clicked() {
#ifdef Q_OS_WIN
APPBARDATA abd = { sizeof abd };
UINT uState = (UINT)SHAppBarMessage(ABM_GETSTATE, &abd);
LPARAM param = uState & ABS_ALWAYSONTOP;
abd.lParam = param;
SHAppBarMessage(ABM_SETSTATE, &abd);
#endif
}
void Nitpick::on_closeButton_clicked() {
exit(0);
}
void Nitpick::on_createPythonScriptRadioButton_clicked() {
_test->setTestRailCreateMode(PYTHON);
}
void Nitpick::on_createXMLScriptRadioButton_clicked() {
_test->setTestRailCreateMode(XML);
}
void Nitpick::on_createWebPagePushButton_clicked() {
_test->createWebPage(_ui.updateAWSCheckBox, _ui.awsURLLineEdit);
}
void Nitpick::downloadFile(const QUrl& url) {
_downloaders.emplace_back(new Downloader(url, this));
connect(_downloaders[_index], SIGNAL(downloaded()), _signalMapper, SLOT(map()));
_signalMapper->setMapping(_downloaders[_index], _index);
++_index;
}
void Nitpick::downloadFiles(const QStringList& URLs, const QString& directoryName, const QStringList& filenames, void *caller) {
connect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveFile(int)));
_directoryName = directoryName;
_filenames = filenames;
_caller = caller;
_numberOfFilesToDownload = URLs.size();
_numberOfFilesDownloaded = 0;
_index = 0;
_ui.progressBar->setMinimum(0);
_ui.progressBar->setMaximum(_numberOfFilesToDownload - 1);
_ui.progressBar->setValue(0);
_ui.progressBar->setVisible(true);
foreach (auto downloader, _downloaders) {
delete downloader;
}
_downloaders.clear();
for (int i = 0; i < _numberOfFilesToDownload; ++i) {
downloadFile(URLs[i]);
}
}
void Nitpick::saveFile(int index) {
try {
QFile file(_directoryName + "/" + _filenames[index]);
file.open(QIODevice::WriteOnly);
file.write(_downloaders[index]->downloadedData());
file.close();
} catch (...) {
QMessageBox::information(0, "Test Aborted", "Failed to save file: " + _filenames[index]);
_ui.progressBar->setVisible(false);
return;
}
++_numberOfFilesDownloaded;
if (_numberOfFilesDownloaded == _numberOfFilesToDownload) {
disconnect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveFile(int)));
if (_caller == _test) {
_test->finishTestsEvaluation();
} else if (_caller == _testRunner) {
_testRunner->downloadComplete();
}
} else {
_ui.progressBar->setValue(_numberOfFilesDownloaded);
}
}
void Nitpick::about() {
QMessageBox::information(0, "About", QString("Built ") + __DATE__ + ", " + __TIME__);
}
void Nitpick::content() {
QDesktopServices::openUrl(QUrl("https://github.com/highfidelity/hifi/blob/master/tools/nitpick/README.md"));
}
void Nitpick::setUserText(const QString& user) {
_ui.userLineEdit->setText(user);
}
QString Nitpick::getSelectedUser() {
return _ui.userLineEdit->text();
}
void Nitpick::setBranchText(const QString& branch) {
_ui.branchLineEdit->setText(branch);
}
QString Nitpick::getSelectedBranch() {
return _ui.branchLineEdit->text();
}
void Nitpick::updateStatusLabel(const QString& status) {
_ui.statusLabel->setText(status);
}
void Nitpick::appendLogWindow(const QString& message) {
_ui.plainTextEdit->appendPlainText(message);
}

View file

@ -0,0 +1,121 @@
//
// Nitpick.h
//
// Created by Nissim Hadar on 2 Nov 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Nitpick_h
#define hifi_Nitpick_h
#include <QtWidgets/QMainWindow>
#include <QSignalMapper>
#include <QTextEdit>
#include "ui_Nitpick.h"
#include "../Downloader.h"
#include "../Test.h"
#include "../TestRunner.h"
#include "../AWSInterface.h"
class Nitpick : public QMainWindow {
Q_OBJECT
public:
Nitpick(QWidget* parent = Q_NULLPTR);
~Nitpick();
void setup();
void startTestsEvaluation(const bool isRunningFromCommandLine,
const bool isRunningInAutomaticTestRun,
const QString& snapshotDirectory,
const QString& branch,
const QString& user);
void automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures);
void downloadFile(const QUrl& url);
void downloadFiles(const QStringList& URLs, const QString& directoryName, const QStringList& filenames, void* caller);
void setUserText(const QString& user);
QString getSelectedUser();
void setBranchText(const QString& branch);
QString getSelectedBranch();
void enableRunTabControls();
void updateStatusLabel(const QString& status);
void appendLogWindow(const QString& message);
private slots:
void on_tabWidget_currentChanged(int index);
void on_evaluateTestsButton_clicked();
void on_createRecursiveScriptButton_clicked();
void on_createAllRecursiveScriptsButton_clicked();
void on_createTestsButton_clicked();
void on_createMDFileButton_clicked();
void on_createAllMDFilesButton_clicked();
void on_createTestAutoScriptButton_clicked();
void on_createAllTestAutoScriptsButton_clicked();
void on_createTestsOutlineButton_clicked();
void on_createTestRailTestCasesButton_clicked();
void on_createTestRailRunButton_clicked();
void on_setWorkingFolderButton_clicked();
void on_runNowButton_clicked();
void on_checkBoxRunLatest_clicked();
void on_updateTestRailRunResultsButton_clicked();
void on_hideTaskbarButton_clicked();
void on_showTaskbarButton_clicked();
void on_createPythonScriptRadioButton_clicked();
void on_createXMLScriptRadioButton_clicked();
void on_createWebPagePushButton_clicked();
void on_closeButton_clicked();
void saveFile(int index);
void about();
void content();
private:
Ui::NitpickClass _ui;
Test* _test{ nullptr };
TestRunner* _testRunner{ nullptr };
AWSInterface _awsInterface;
std::vector<Downloader*> _downloaders;
// local storage for parameters - folder to store downloaded files in, and a list of their names
QString _directoryName;
QStringList _filenames;
// Used to enable passing a parameter to slots
QSignalMapper* _signalMapper;
int _numberOfFilesToDownload{ 0 };
int _numberOfFilesDownloaded{ 0 };
int _index{ 0 };
bool _isRunningFromCommandline{ false };
void* _caller;
};
#endif // hifi_Nitpick_h

View file

@ -0,0 +1,900 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NitpickClass</class>
<widget class="QMainWindow" name="NitpickClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<height>870</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Nitpick</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QPushButton" name="closeButton">
<property name="geometry">
<rect>
<x>470</x>
<y>750</y>
<width>100</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Close</string>
</property>
</widget>
<widget class="QTabWidget" name="tabWidget">
<property name="geometry">
<rect>
<x>45</x>
<y>140</y>
<width>630</width>
<height>580</height>
</rect>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab_1">
<attribute name="title">
<string>Create</string>
</attribute>
<widget class="QPushButton" name="createTestsButton">
<property name="geometry">
<rect>
<x>195</x>
<y>60</y>
<width>220</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Create Tests</string>
</property>
</widget>
<widget class="QPushButton" name="createMDFileButton">
<property name="geometry">
<rect>
<x>70</x>
<y>180</y>
<width>220</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Create MD file</string>
</property>
</widget>
<widget class="QPushButton" name="createAllMDFilesButton">
<property name="geometry">
<rect>
<x>320</x>
<y>180</y>
<width>220</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Create all MD files</string>
</property>
</widget>
<widget class="QPushButton" name="createTestsOutlineButton">
<property name="geometry">
<rect>
<x>195</x>
<y>120</y>
<width>220</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Create Tests Outline</string>
</property>
</widget>
<widget class="QPushButton" name="createRecursiveScriptButton">
<property name="geometry">
<rect>
<x>70</x>
<y>300</y>
<width>220</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Create Recursive Script</string>
</property>
</widget>
<widget class="QPushButton" name="createAllRecursiveScriptsButton">
<property name="geometry">
<rect>
<x>320</x>
<y>300</y>
<width>220</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Create all Recursive Scripts</string>
</property>
</widget>
<widget class="QPushButton" name="createTestAutoScriptButton">
<property name="geometry">
<rect>
<x>70</x>
<y>240</y>
<width>220</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Create testAuto script</string>
</property>
</widget>
<widget class="QPushButton" name="createAllTestAutoScriptsButton">
<property name="geometry">
<rect>
<x>320</x>
<y>240</y>
<width>220</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Create all testAuto scripts</string>
</property>
</widget>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>Windows</string>
</attribute>
<widget class="QPushButton" name="hideTaskbarButton">
<property name="geometry">
<rect>
<x>200</x>
<y>130</y>
<width>211</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Hide Windows Taskbar</string>
</property>
</widget>
<widget class="QPushButton" name="showTaskbarButton">
<property name="geometry">
<rect>
<x>200</x>
<y>200</y>
<width>211</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Show Windows Taskbar</string>
</property>
</widget>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Run</string>
</attribute>
<widget class="QPushButton" name="runNowButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>10</x>
<y>160</y>
<width>161</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>Run now</string>
</property>
</widget>
<widget class="QGroupBox" name="daysGroupBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>20</x>
<y>240</y>
<width>91</width>
<height>241</height>
</rect>
</property>
<property name="title">
<string>Days</string>
</property>
<widget class="QCheckBox" name="sundayCheckBox">
<property name="geometry">
<rect>
<x>10</x>
<y>210</y>
<width>80</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Sunday</string>
</property>
</widget>
<widget class="QCheckBox" name="wednesdayCheckBox">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>80</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Wednesday</string>
</property>
</widget>
<widget class="QCheckBox" name="tuesdayCheckBox">
<property name="geometry">
<rect>
<x>10</x>
<y>60</y>
<width>80</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Tuesday</string>
</property>
</widget>
<widget class="QCheckBox" name="thursdayCheckBox">
<property name="geometry">
<rect>
<x>10</x>
<y>120</y>
<width>80</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Thursday</string>
</property>
</widget>
<widget class="QCheckBox" name="fridayCheckBox">
<property name="geometry">
<rect>
<x>10</x>
<y>150</y>
<width>80</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Friday</string>
</property>
</widget>
<widget class="QCheckBox" name="saturdayCheckBox">
<property name="geometry">
<rect>
<x>10</x>
<y>180</y>
<width>80</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Saturday</string>
</property>
</widget>
<widget class="QCheckBox" name="mondayCheckBox">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>80</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Monday</string>
</property>
</widget>
</widget>
<widget class="QGroupBox" name="timesGroupBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>130</x>
<y>240</y>
<width>161</width>
<height>191</height>
</rect>
</property>
<property name="title">
<string>Times</string>
</property>
<widget class="QTimeEdit" name="timeEdit1">
<property name="geometry">
<rect>
<x>30</x>
<y>20</y>
<width>118</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QTimeEdit" name="timeEdit2">
<property name="geometry">
<rect>
<x>30</x>
<y>60</y>
<width>118</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QTimeEdit" name="timeEdit3">
<property name="geometry">
<rect>
<x>30</x>
<y>100</y>
<width>118</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QTimeEdit" name="timeEdit4">
<property name="geometry">
<rect>
<x>30</x>
<y>140</y>
<width>118</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QCheckBox" name="timeEdit1checkBox">
<property name="geometry">
<rect>
<x>10</x>
<y>23</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QCheckBox" name="timeEdit2checkBox">
<property name="geometry">
<rect>
<x>10</x>
<y>63</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QCheckBox" name="timeEdit3checkBox">
<property name="geometry">
<rect>
<x>10</x>
<y>103</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QCheckBox" name="timeEdit4checkBox">
<property name="geometry">
<rect>
<x>10</x>
<y>143</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
<widget class="QPushButton" name="setWorkingFolderButton">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>161</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Set Working Folder</string>
</property>
</widget>
<widget class="QLabel" name="workingFolderLabel">
<property name="geometry">
<rect>
<x>190</x>
<y>20</y>
<width>321</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>(not set...)</string>
</property>
</widget>
<widget class="QPlainTextEdit" name="plainTextEdit">
<property name="geometry">
<rect>
<x>300</x>
<y>210</y>
<width>311</width>
<height>331</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="workingFolderLabel_2">
<property name="geometry">
<rect>
<x>300</x>
<y>170</y>
<width>41</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>Status:</string>
</property>
</widget>
<widget class="QLabel" name="statusLabel">
<property name="geometry">
<rect>
<x>350</x>
<y>170</y>
<width>271</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>#######</string>
</property>
</widget>
<widget class="QCheckBox" name="checkBoxServerless">
<property name="geometry">
<rect>
<x>20</x>
<y>70</y>
<width>120</width>
<height>20</height>
</rect>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If unchecked, will not show results during evaluation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Server-less</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
<widget class="QCheckBox" name="checkBoxRunLatest">
<property name="geometry">
<rect>
<x>20</x>
<y>100</y>
<width>120</width>
<height>20</height>
</rect>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If unchecked, will not show results during evaluation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Run Latest</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="workingFolderLabel_3">
<property name="geometry">
<rect>
<x>128</x>
<y>95</y>
<width>31</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>URL</string>
</property>
</widget>
<widget class="QLineEdit" name="urlLineEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>170</x>
<y>100</y>
<width>451</width>
<height>21</height>
</rect>
</property>
</widget>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Evaluate</string>
</attribute>
<widget class="QCheckBox" name="checkBoxInteractiveMode">
<property name="geometry">
<rect>
<x>190</x>
<y>180</y>
<width>131</width>
<height>20</height>
</rect>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If unchecked, will not show results during evaluation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Interactive Mode</string>
</property>
</widget>
<widget class="QPushButton" name="evaluateTestsButton">
<property name="geometry">
<rect>
<x>330</x>
<y>170</y>
<width>181</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>Evaluate Test</string>
</property>
</widget>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Web Interface</string>
</attribute>
<widget class="QPushButton" name="updateTestRailRunResultsButton">
<property name="geometry">
<rect>
<x>240</x>
<y>220</y>
<width>160</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Update Run Results</string>
</property>
</widget>
<widget class="QRadioButton" name="createPythonScriptRadioButton">
<property name="geometry">
<rect>
<x>170</x>
<y>100</y>
<width>95</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Python</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QPushButton" name="createTestRailRunButton">
<property name="geometry">
<rect>
<x>240</x>
<y>160</y>
<width>160</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Create Run</string>
</property>
</widget>
<widget class="QPushButton" name="createTestRailTestCasesButton">
<property name="geometry">
<rect>
<x>240</x>
<y>100</y>
<width>160</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Create Test Cases</string>
</property>
</widget>
<widget class="QRadioButton" name="createXMLScriptRadioButton">
<property name="geometry">
<rect>
<x>170</x>
<y>120</y>
<width>95</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>XML</string>
</property>
</widget>
<widget class="QGroupBox" name="groupBox">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>601</width>
<height>300</height>
</rect>
</property>
<property name="title">
<string>TestRail</string>
</property>
</widget>
<widget class="QGroupBox" name="groupBox_2">
<property name="geometry">
<rect>
<x>10</x>
<y>350</y>
<width>601</width>
<height>151</height>
</rect>
</property>
<property name="title">
<string>Amazon Web Services</string>
</property>
<widget class="QPushButton" name="createWebPagePushButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>270</x>
<y>30</y>
<width>160</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>Create Web Page</string>
</property>
</widget>
<widget class="QCheckBox" name="updateAWSCheckBox">
<property name="geometry">
<rect>
<x>150</x>
<y>42</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Update AWS</string>
</property>
</widget>
<widget class="QLineEdit" name="awsURLLineEdit">
<property name="geometry">
<rect>
<x>20</x>
<y>90</y>
<width>561</width>
<height>21</height>
</rect>
</property>
</widget>
</widget>
<zorder>groupBox</zorder>
<zorder>updateTestRailRunResultsButton</zorder>
<zorder>createPythonScriptRadioButton</zorder>
<zorder>createTestRailRunButton</zorder>
<zorder>createTestRailTestCasesButton</zorder>
<zorder>createXMLScriptRadioButton</zorder>
<zorder>groupBox_2</zorder>
</widget>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>120</x>
<y>80</y>
<width>110</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>GitHub Branch</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>120</x>
<y>40</y>
<width>110</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>GitHub User</string>
</property>
</widget>
<widget class="QProgressBar" name="progressBar">
<property name="geometry">
<rect>
<x>80</x>
<y>760</y>
<width>255</width>
<height>23</height>
</rect>
</property>
<property name="value">
<number>24</number>
</property>
</widget>
<widget class="QLineEdit" name="userLineEdit">
<property name="geometry">
<rect>
<x>220</x>
<y>40</y>
<width>161</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="branchLineEdit">
<property name="geometry">
<rect>
<x>220</x>
<y>80</y>
<width>161</width>
<height>21</height>
</rect>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionClose"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout"/>
<addaction name="actionContent"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionClose">
<property name="text">
<string>Close</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>About</string>
</property>
</action>
<action name="actionContent">
<property name="text">
<string>Online readme</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
<tabstop>userLineEdit</tabstop>
<tabstop>branchLineEdit</tabstop>
<tabstop>createTestsButton</tabstop>
<tabstop>createMDFileButton</tabstop>
<tabstop>createAllMDFilesButton</tabstop>
<tabstop>createTestsOutlineButton</tabstop>
<tabstop>createRecursiveScriptButton</tabstop>
<tabstop>createAllRecursiveScriptsButton</tabstop>
<tabstop>createTestAutoScriptButton</tabstop>
<tabstop>createAllTestAutoScriptsButton</tabstop>
<tabstop>hideTaskbarButton</tabstop>
<tabstop>showTaskbarButton</tabstop>
<tabstop>runNowButton</tabstop>
<tabstop>sundayCheckBox</tabstop>
<tabstop>wednesdayCheckBox</tabstop>
<tabstop>tuesdayCheckBox</tabstop>
<tabstop>thursdayCheckBox</tabstop>
<tabstop>fridayCheckBox</tabstop>
<tabstop>saturdayCheckBox</tabstop>
<tabstop>mondayCheckBox</tabstop>
<tabstop>timeEdit1</tabstop>
<tabstop>timeEdit2</tabstop>
<tabstop>timeEdit3</tabstop>
<tabstop>timeEdit4</tabstop>
<tabstop>timeEdit1checkBox</tabstop>
<tabstop>timeEdit2checkBox</tabstop>
<tabstop>timeEdit3checkBox</tabstop>
<tabstop>timeEdit4checkBox</tabstop>
<tabstop>setWorkingFolderButton</tabstop>
<tabstop>plainTextEdit</tabstop>
<tabstop>checkBoxServerless</tabstop>
<tabstop>checkBoxRunLatest</tabstop>
<tabstop>urlLineEdit</tabstop>
<tabstop>checkBoxInteractiveMode</tabstop>
<tabstop>evaluateTestsButton</tabstop>
<tabstop>updateTestRailRunResultsButton</tabstop>
<tabstop>createPythonScriptRadioButton</tabstop>
<tabstop>createTestRailRunButton</tabstop>
<tabstop>createTestRailTestCasesButton</tabstop>
<tabstop>createXMLScriptRadioButton</tabstop>
<tabstop>createWebPagePushButton</tabstop>
<tabstop>updateAWSCheckBox</tabstop>
<tabstop>awsURLLineEdit</tabstop>
<tabstop>closeButton</tabstop>
<tabstop>tabWidget</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,106 @@
//
// TestRailResultsSelectorWindow.cpp
//
// Created by Nissim Hadar on 2 Aug 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "TestRailResultsSelectorWindow.h"
#include <QtCore/QFileInfo>
#include <cmath>
TestRailResultsSelectorWindow::TestRailResultsSelectorWindow(QWidget *parent) {
setupUi(this);
projectIDLineEdit->setValidator(new QIntValidator(1, 999, this));
}
void TestRailResultsSelectorWindow::reset() {
urlLineEdit->setDisabled(false);
userLineEdit->setDisabled(false);
passwordLineEdit->setDisabled(false);
projectIDLineEdit->setDisabled(false);
suiteIDLineEdit->setDisabled(false);
OKButton->setDisabled(true);
runsLabel->setDisabled(true);
runsComboBox->setDisabled(true);
}
void TestRailResultsSelectorWindow::on_acceptButton_clicked() {
urlLineEdit->setDisabled(true);
userLineEdit->setDisabled(true);
passwordLineEdit->setDisabled(true);
projectIDLineEdit->setDisabled(true);
suiteIDLineEdit->setDisabled(true);
OKButton->setDisabled(false);
runsLabel->setDisabled(false);
runsComboBox->setDisabled(false);
close();
}
void TestRailResultsSelectorWindow::on_OKButton_clicked() {
userCancelled = false;
close();
}
void TestRailResultsSelectorWindow::on_cancelButton_clicked() {
userCancelled = true;
close();
}
bool TestRailResultsSelectorWindow::getUserCancelled() {
return userCancelled;
}
void TestRailResultsSelectorWindow::setURL(const QString& user) {
urlLineEdit->setText(user);
}
QString TestRailResultsSelectorWindow::getURL() {
return urlLineEdit->text();
}
void TestRailResultsSelectorWindow::setUser(const QString& user) {
userLineEdit->setText(user);
}
QString TestRailResultsSelectorWindow::getUser() {
return userLineEdit->text();
}
QString TestRailResultsSelectorWindow::getPassword() {
return passwordLineEdit->text();
}
void TestRailResultsSelectorWindow::setProjectID(const int project) {
projectIDLineEdit->setText(QString::number(project));
}
int TestRailResultsSelectorWindow::getProjectID() {
return projectIDLineEdit->text().toInt();
}
void TestRailResultsSelectorWindow::setSuiteID(const int project) {
suiteIDLineEdit->setText(QString::number(project));
}
int TestRailResultsSelectorWindow::getSuiteID() {
return suiteIDLineEdit->text().toInt();
}
void TestRailResultsSelectorWindow::updateRunsComboBoxData(QStringList data) {
runsComboBox->insertItems(0, data);
}
int TestRailResultsSelectorWindow::getRunID() {
return runsComboBox->currentIndex();
}

View file

@ -0,0 +1,50 @@
//
// TestRailResultsSelectorWindow.h
//
// Created by Nissim Hadar on 2 Aug 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_TestRailResultsSelectorWindow_h
#define hifi_TestRailResultsSelectorWindow_h
#include "ui_TestRailResultsSelectorWindow.h"
class TestRailResultsSelectorWindow : public QDialog, public Ui::TestRailResultsSelectorWindow {
Q_OBJECT
public:
TestRailResultsSelectorWindow(QWidget* parent = Q_NULLPTR);
void reset();
bool getUserCancelled();
void setURL(const QString& user);
QString getURL();
void setUser(const QString& user);
QString getUser();
QString getPassword();
void setProjectID(const int project);
int getProjectID();
void setSuiteID(const int project);
int getSuiteID();
bool userCancelled{ false };
void updateRunsComboBoxData(QStringList data);
int getRunID();
private slots:
void on_acceptButton_clicked();
void on_OKButton_clicked();
void on_cancelButton_clicked();
};
#endif

View file

@ -0,0 +1,280 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TestRailResultsSelectorWindow</class>
<widget class="QDialog" name="TestRailResultsSelectorWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>533</width>
<height>474</height>
</rect>
</property>
<property name="windowTitle">
<string>TestRail Test Case Selector Window</string>
</property>
<widget class="QLabel" name="errorLabel">
<property name="geometry">
<rect>
<x>30</x>
<y>850</y>
<width>500</width>
<height>28</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>similarity</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>70</x>
<y>125</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Password</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>70</x>
<y>25</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail URL</string>
</property>
</widget>
<widget class="QPushButton" name="OKButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>120</x>
<y>420</y>
<width>93</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>OK</string>
</property>
</widget>
<widget class="QPushButton" name="cancelButton">
<property name="geometry">
<rect>
<x>280</x>
<y>420</y>
<width>93</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
<widget class="QLineEdit" name="passwordLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>120</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>70</x>
<y>75</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail User</string>
</property>
</widget>
<widget class="QLineEdit" name="projectIDLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>170</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>70</x>
<y>175</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Project ID</string>
</property>
</widget>
<widget class="QPushButton" name="acceptButton">
<property name="geometry">
<rect>
<x>200</x>
<y>270</y>
<width>231</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Accept</string>
</property>
</widget>
<widget class="QComboBox" name="runsComboBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>160</x>
<y>350</y>
<width>271</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="runsLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>80</x>
<y>350</y>
<width>71</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Run</string>
</property>
</widget>
<widget class="QLineEdit" name="urlLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>20</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLineEdit" name="userLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>70</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLineEdit" name="suiteIDLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>215</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>70</x>
<y>220</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Suite ID</string>
</property>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
<tabstop>urlLineEdit</tabstop>
<tabstop>userLineEdit</tabstop>
<tabstop>passwordLineEdit</tabstop>
<tabstop>projectIDLineEdit</tabstop>
<tabstop>suiteIDLineEdit</tabstop>
<tabstop>acceptButton</tabstop>
<tabstop>runsComboBox</tabstop>
<tabstop>OKButton</tabstop>
<tabstop>cancelButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,101 @@
//
// TestRailRunSelectorWindow.cpp
//
// Created by Nissim Hadar on 31 Jul 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "TestRailRunSelectorWindow.h"
#include <QtCore/QFileInfo>
#include <cmath>
TestRailRunSelectorWindow::TestRailRunSelectorWindow(QWidget *parent) {
setupUi(this);
projectIDLineEdit->setValidator(new QIntValidator(1, 999, this));
}
void TestRailRunSelectorWindow::reset() {
urlLineEdit->setDisabled(false);
userLineEdit->setDisabled(false);
passwordLineEdit->setDisabled(false);
projectIDLineEdit->setDisabled(false);
suiteIDLineEdit->setDisabled(false);
OKButton->setDisabled(true);
sectionsComboBox->setDisabled(true);
}
void TestRailRunSelectorWindow::on_acceptButton_clicked() {
urlLineEdit->setDisabled(true);
userLineEdit->setDisabled(true);
passwordLineEdit->setDisabled(true);
projectIDLineEdit->setDisabled(true);
suiteIDLineEdit->setDisabled(true);
OKButton->setDisabled(false);
sectionsComboBox->setDisabled(false);
close();
}
void TestRailRunSelectorWindow::on_OKButton_clicked() {
userCancelled = false;
close();
}
void TestRailRunSelectorWindow::on_cancelButton_clicked() {
userCancelled = true;
close();
}
bool TestRailRunSelectorWindow::getUserCancelled() {
return userCancelled;
}
void TestRailRunSelectorWindow::setURL(const QString& user) {
urlLineEdit->setText(user);
}
QString TestRailRunSelectorWindow::getURL() {
return urlLineEdit->text();
}
void TestRailRunSelectorWindow::setUser(const QString& user) {
userLineEdit->setText(user);
}
QString TestRailRunSelectorWindow::getUser() {
return userLineEdit->text();
}
QString TestRailRunSelectorWindow::getPassword() {
return passwordLineEdit->text();
}
void TestRailRunSelectorWindow::setProjectID(const int project) {
projectIDLineEdit->setText(QString::number(project));
}
int TestRailRunSelectorWindow::getProjectID() {
return projectIDLineEdit->text().toInt();
}
void TestRailRunSelectorWindow::setSuiteID(const int project) {
suiteIDLineEdit->setText(QString::number(project));
}
int TestRailRunSelectorWindow::getSuiteID() {
return suiteIDLineEdit->text().toInt();
}
void TestRailRunSelectorWindow::updateSectionsComboBoxData(QStringList data) {
sectionsComboBox->insertItems(0, data);
}
int TestRailRunSelectorWindow::getSectionID() {
return sectionsComboBox->currentIndex();
}

View file

@ -0,0 +1,50 @@
//
// TestRailRunSelectorWindow.h
//
// Created by Nissim Hadar on 31 Jul 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_TestRailRunSelectorWindow_h
#define hifi_TestRailRunSelectorWindow_h
#include "ui_TestRailRunSelectorWindow.h"
class TestRailRunSelectorWindow : public QDialog, public Ui::TestRailRunSelectorWindow {
Q_OBJECT
public:
TestRailRunSelectorWindow(QWidget* parent = Q_NULLPTR);
void reset();
bool getUserCancelled();
void setURL(const QString& user);
QString getURL();
void setUser(const QString& user);
QString getUser();
QString getPassword();
void setProjectID(const int project);
int getProjectID();
void setSuiteID(const int project);
int getSuiteID();
bool userCancelled{ false };
void updateSectionsComboBoxData(QStringList data);
int getSectionID();
private slots:
void on_acceptButton_clicked();
void on_OKButton_clicked();
void on_cancelButton_clicked();
};
#endif

View file

@ -0,0 +1,283 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TestRailRunSelectorWindow</class>
<widget class="QDialog" name="TestRailRunSelectorWindow">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>489</width>
<height>474</height>
</rect>
</property>
<property name="windowTitle">
<string>TestRail Run Selector Window</string>
</property>
<widget class="QLabel" name="errorLabel">
<property name="geometry">
<rect>
<x>30</x>
<y>850</y>
<width>500</width>
<height>28</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>similarity</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>70</x>
<y>125</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Password</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>70</x>
<y>25</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail URL</string>
</property>
</widget>
<widget class="QPushButton" name="OKButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>120</x>
<y>420</y>
<width>93</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>OK</string>
</property>
</widget>
<widget class="QPushButton" name="cancelButton">
<property name="geometry">
<rect>
<x>280</x>
<y>420</y>
<width>93</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
<widget class="QLineEdit" name="passwordLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>120</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>70</x>
<y>75</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail User</string>
</property>
</widget>
<widget class="QLineEdit" name="projectIDLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>170</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>70</x>
<y>175</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Project ID</string>
</property>
</widget>
<widget class="QPushButton" name="acceptButton">
<property name="geometry">
<rect>
<x>200</x>
<y>270</y>
<width>231</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Accept</string>
</property>
</widget>
<widget class="QComboBox" name="sectionsComboBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>140</x>
<y>350</y>
<width>311</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="milestoneLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>20</x>
<y>350</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Sections</string>
</property>
</widget>
<widget class="QLineEdit" name="urlLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>20</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLineEdit" name="userLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>70</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLineEdit" name="suiteIDLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>215</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>70</x>
<y>220</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Suite ID</string>
</property>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
<tabstop>urlLineEdit</tabstop>
<tabstop>userLineEdit</tabstop>
<tabstop>passwordLineEdit</tabstop>
<tabstop>projectIDLineEdit</tabstop>
<tabstop>suiteIDLineEdit</tabstop>
<tabstop>acceptButton</tabstop>
<tabstop>sectionsComboBox</tabstop>
<tabstop>OKButton</tabstop>
<tabstop>cancelButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,106 @@
//
// TestRailTestCasesSelectorWindow.cpp
//
// Created by Nissim Hadar on 26 Jul 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "TestRailTestCasesSelectorWindow.h"
#include <QtCore/QFileInfo>
#include <cmath>
TestRailTestCasesSelectorWindow::TestRailTestCasesSelectorWindow(QWidget *parent) {
setupUi(this);
projectIDLineEdit->setValidator(new QIntValidator(1, 999, this));
}
void TestRailTestCasesSelectorWindow::reset() {
urlLineEdit->setDisabled(false);
userLineEdit->setDisabled(false);
passwordLineEdit->setDisabled(false);
projectIDLineEdit->setDisabled(false);
suiteIDLineEdit->setDisabled(false);
OKButton->setDisabled(true);
releasesLabel->setDisabled(true);
releasesComboBox->setDisabled(true);
}
void TestRailTestCasesSelectorWindow::on_acceptButton_clicked() {
urlLineEdit->setDisabled(true);
userLineEdit->setDisabled(true);
passwordLineEdit->setDisabled(true);
projectIDLineEdit->setDisabled(true);
suiteIDLineEdit->setDisabled(true);
OKButton->setDisabled(false);
releasesLabel->setDisabled(false);
releasesComboBox->setDisabled(false);
close();
}
void TestRailTestCasesSelectorWindow::on_OKButton_clicked() {
userCancelled = false;
close();
}
void TestRailTestCasesSelectorWindow::on_cancelButton_clicked() {
userCancelled = true;
close();
}
bool TestRailTestCasesSelectorWindow::getUserCancelled() {
return userCancelled;
}
void TestRailTestCasesSelectorWindow::setURL(const QString& user) {
urlLineEdit->setText(user);
}
QString TestRailTestCasesSelectorWindow::getURL() {
return urlLineEdit->text();
}
void TestRailTestCasesSelectorWindow::setUser(const QString& user) {
userLineEdit->setText(user);
}
QString TestRailTestCasesSelectorWindow::getUser() {
return userLineEdit->text();
}
QString TestRailTestCasesSelectorWindow::getPassword() {
return passwordLineEdit->text();
}
void TestRailTestCasesSelectorWindow::setProjectID(const int project) {
projectIDLineEdit->setText(QString::number(project));
}
int TestRailTestCasesSelectorWindow::getProjectID() {
return projectIDLineEdit->text().toInt();
}
void TestRailTestCasesSelectorWindow::setSuiteID(const int project) {
suiteIDLineEdit->setText(QString::number(project));
}
int TestRailTestCasesSelectorWindow::getSuiteID() {
return suiteIDLineEdit->text().toInt();
}
void TestRailTestCasesSelectorWindow::updateReleasesComboBoxData(QStringList data) {
releasesComboBox->insertItems(0, data);
}
int TestRailTestCasesSelectorWindow::getReleaseID() {
return releasesComboBox->currentIndex();
}

View file

@ -0,0 +1,50 @@
//
// TestRailTestCasesSelectorWindow.h
//
// Created by Nissim Hadar on 26 Jul 2017.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_TestRailTestCasesSelectorWindow_h
#define hifi_TestRailTestCasesSelectorWindow_h
#include "ui_TestRailTestCasesSelectorWindow.h"
class TestRailTestCasesSelectorWindow : public QDialog, public Ui::TestRailTestCasesSelectorWindow {
Q_OBJECT
public:
TestRailTestCasesSelectorWindow(QWidget* parent = Q_NULLPTR);
void reset();
bool getUserCancelled();
void setURL(const QString& user);
QString getURL();
void setUser(const QString& user);
QString getUser();
QString getPassword();
void setProjectID(const int project);
int getProjectID();
void setSuiteID(const int project);
int getSuiteID();
bool userCancelled{ false };
void updateReleasesComboBoxData(QStringList data);
int getReleaseID();
private slots:
void on_acceptButton_clicked();
void on_OKButton_clicked();
void on_cancelButton_clicked();
};
#endif

View file

@ -0,0 +1,280 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TestRailTestCasesSelectorWindow</class>
<widget class="QDialog" name="TestRailTestCasesSelectorWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>489</width>
<height>474</height>
</rect>
</property>
<property name="windowTitle">
<string>TestRail Test Case Selector Window</string>
</property>
<widget class="QLabel" name="errorLabel">
<property name="geometry">
<rect>
<x>30</x>
<y>850</y>
<width>500</width>
<height>28</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>similarity</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>70</x>
<y>125</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Password</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>70</x>
<y>25</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail URL</string>
</property>
</widget>
<widget class="QPushButton" name="OKButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>120</x>
<y>420</y>
<width>93</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>OK</string>
</property>
</widget>
<widget class="QPushButton" name="cancelButton">
<property name="geometry">
<rect>
<x>280</x>
<y>420</y>
<width>93</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
<widget class="QLineEdit" name="passwordLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>120</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>70</x>
<y>75</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail User</string>
</property>
</widget>
<widget class="QLineEdit" name="projectIDLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>170</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>70</x>
<y>175</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Project ID</string>
</property>
</widget>
<widget class="QPushButton" name="acceptButton">
<property name="geometry">
<rect>
<x>200</x>
<y>270</y>
<width>231</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Accept</string>
</property>
</widget>
<widget class="QComboBox" name="releasesComboBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>270</x>
<y>350</y>
<width>161</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="releasesLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>80</x>
<y>350</y>
<width>181</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Added for Release</string>
</property>
</widget>
<widget class="QLineEdit" name="urlLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>20</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLineEdit" name="userLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>70</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLineEdit" name="suiteIDLineEdit">
<property name="geometry">
<rect>
<x>200</x>
<y>215</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="echoMode">
<enum>QLineEdit::Normal</enum>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>70</x>
<y>220</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>TestRail Suite ID</string>
</property>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
<tabstop>urlLineEdit</tabstop>
<tabstop>userLineEdit</tabstop>
<tabstop>passwordLineEdit</tabstop>
<tabstop>projectIDLineEdit</tabstop>
<tabstop>suiteIDLineEdit</tabstop>
<tabstop>acceptButton</tabstop>
<tabstop>releasesComboBox</tabstop>
<tabstop>OKButton</tabstop>
<tabstop>cancelButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>