mirror of
https://github.com/lubosz/overte.git
synced 2025-04-10 03:42:45 +02:00
Merge remote-tracking branch 'upstream/master' into bloom
This commit is contained in:
commit
a0c3c09b05
72 changed files with 2659 additions and 678 deletions
|
@ -368,7 +368,6 @@ void Agent::executeScript() {
|
|||
// give scripts access to the Users object
|
||||
_scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
|
||||
|
||||
auto player = DependencyManager::get<recording::Deck>();
|
||||
connect(player.data(), &recording::Deck::playbackStateChanged, [=] {
|
||||
if (player->isPlaying()) {
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "AssignmentClientApp.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QtCore/QCommandLineParser>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QStandardPaths>
|
||||
|
@ -42,9 +44,8 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
// parse command-line
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Assignment Client");
|
||||
parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
const QCommandLineOption versionOption = parser.addVersionOption();
|
||||
|
||||
QString typeDescription = "run single assignment client of given type\n# | Type\n============================";
|
||||
for (Assignment::Type type = Assignment::FirstType;
|
||||
|
@ -97,11 +98,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
parser.addOption(parentPIDOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qCritical() << parser.errorText() << endl;
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
parser.showVersion();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(helpOption)) {
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <iostream>
|
||||
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
|
@ -69,6 +70,14 @@ const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com";
|
|||
const QString ICE_SERVER_DEFAULT_HOSTNAME = "dev-ice.highfidelity.com";
|
||||
#endif
|
||||
|
||||
QString DomainServer::_iceServerAddr { ICE_SERVER_DEFAULT_HOSTNAME };
|
||||
int DomainServer::_iceServerPort { ICE_SERVER_DEFAULT_PORT };
|
||||
bool DomainServer::_overrideDomainID { false };
|
||||
QUuid DomainServer::_overridingDomainID;
|
||||
bool DomainServer::_getTempName { false };
|
||||
QString DomainServer::_userConfigFilename;
|
||||
int DomainServer::_parentPID { -1 };
|
||||
|
||||
bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
||||
const QString& metaversePath,
|
||||
const QString& requestSubobjectKey,
|
||||
|
@ -148,24 +157,13 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_gatekeeper(this),
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||
_allAssignments(),
|
||||
_unfulfilledAssignments(),
|
||||
_isUsingDTLS(false),
|
||||
_oauthProviderURL(),
|
||||
_oauthClientID(),
|
||||
_hostname(),
|
||||
_ephemeralACScripts(),
|
||||
_webAuthenticationStateSet(),
|
||||
_cookieSessionHash(),
|
||||
_automaticNetworkingSetting(),
|
||||
_settingsManager(),
|
||||
_iceServerAddr(ICE_SERVER_DEFAULT_HOSTNAME),
|
||||
_iceServerPort(ICE_SERVER_DEFAULT_PORT)
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this)
|
||||
{
|
||||
PathUtils::removeTemporaryApplicationDirs();
|
||||
if (_parentPID != -1) {
|
||||
watchParentProcess(_parentPID);
|
||||
}
|
||||
|
||||
parseCommandLine();
|
||||
PathUtils::removeTemporaryApplicationDirs();
|
||||
|
||||
DependencyManager::set<tracing::Tracer>();
|
||||
DependencyManager::set<StatTracker>();
|
||||
|
@ -185,9 +183,16 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
// (need this since domain-server can restart itself and maintain static variables)
|
||||
DependencyManager::set<AccountManager>();
|
||||
|
||||
auto args = arguments();
|
||||
|
||||
_settingsManager.setupConfigMap(args);
|
||||
// load the user config
|
||||
QString userConfigFilename;
|
||||
if (!_userConfigFilename.isEmpty()) {
|
||||
userConfigFilename = _userConfigFilename;
|
||||
} else {
|
||||
// we weren't passed a user config path
|
||||
static const QString USER_CONFIG_FILE_NAME = "config.json";
|
||||
userConfigFilename = PathUtils::getAppDataFilePath(USER_CONFIG_FILE_NAME);
|
||||
}
|
||||
_settingsManager.setupConfigMap(userConfigFilename);
|
||||
|
||||
// setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us
|
||||
#ifdef _WIN32
|
||||
|
@ -246,8 +251,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
}
|
||||
|
||||
// check for the temporary name parameter
|
||||
const QString GET_TEMPORARY_NAME_SWITCH = "--get-temp-name";
|
||||
if (args.contains(GET_TEMPORARY_NAME_SWITCH)) {
|
||||
if (_getTempName) {
|
||||
getTemporaryName();
|
||||
}
|
||||
|
||||
|
@ -316,28 +320,45 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
connect(_contentManager.get(), &DomainContentBackupManager::recoveryCompleted, this, &DomainServer::restart);
|
||||
}
|
||||
|
||||
void DomainServer::parseCommandLine() {
|
||||
void DomainServer::parseCommandLine(int argc, char* argv[]) {
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Domain Server");
|
||||
parser.addHelpOption();
|
||||
const QCommandLineOption versionOption = parser.addVersionOption();
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption iceServerAddressOption("i", "ice-server address", "IP:PORT or HOSTNAME:PORT");
|
||||
parser.addOption(iceServerAddressOption);
|
||||
|
||||
const QCommandLineOption domainIDOption("d", "domain-server uuid");
|
||||
const QCommandLineOption domainIDOption("d", "domain-server uuid", "uuid");
|
||||
parser.addOption(domainIDOption);
|
||||
|
||||
const QCommandLineOption getTempNameOption("get-temp-name", "Request a temporary domain-name");
|
||||
parser.addOption(getTempNameOption);
|
||||
|
||||
const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option");
|
||||
parser.addOption(masterConfigOption);
|
||||
const QCommandLineOption userConfigOption("user-config", "Pass user config file pass", "path");
|
||||
parser.addOption(userConfigOption);
|
||||
|
||||
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
|
||||
parser.addOption(parentPIDOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qWarning() << parser.errorText() << endl;
|
||||
|
||||
QStringList arguments;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
arguments << argv[i];
|
||||
}
|
||||
if (!parser.parse(arguments)) {
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
parser.showVersion();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
if (parser.isSet(helpOption)) {
|
||||
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -354,7 +375,7 @@ void DomainServer::parseCommandLine() {
|
|||
|
||||
if (_iceServerAddr.isEmpty()) {
|
||||
qWarning() << "Could not parse an IP address and port combination from" << hostnamePortString;
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,14 +385,21 @@ void DomainServer::parseCommandLine() {
|
|||
qDebug() << "domain-server ID is" << _overridingDomainID;
|
||||
}
|
||||
|
||||
if (parser.isSet(getTempNameOption)) {
|
||||
_getTempName = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(userConfigOption)) {
|
||||
_userConfigFilename = parser.value(userConfigOption);
|
||||
}
|
||||
|
||||
if (parser.isSet(parentPIDOption)) {
|
||||
bool ok = false;
|
||||
int parentPID = parser.value(parentPIDOption).toInt(&ok);
|
||||
|
||||
if (ok) {
|
||||
qDebug() << "Parent process PID is" << parentPID;
|
||||
watchParentProcess(parentPID);
|
||||
_parentPID = parentPID;
|
||||
qDebug() << "Parent process PID is" << _parentPID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
DomainServer(int argc, char* argv[]);
|
||||
~DomainServer();
|
||||
|
||||
static void parseCommandLine(int argc, char* argv[]);
|
||||
|
||||
enum DomainType {
|
||||
NonMetaverse,
|
||||
MetaverseDomain,
|
||||
|
@ -138,7 +140,6 @@ signals:
|
|||
|
||||
private:
|
||||
QUuid getID();
|
||||
void parseCommandLine();
|
||||
|
||||
QString getContentBackupDir();
|
||||
QString getEntitiesDirPath();
|
||||
|
@ -228,7 +229,7 @@ private:
|
|||
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
|
||||
TransactionHash _pendingAssignmentCredits;
|
||||
|
||||
bool _isUsingDTLS;
|
||||
bool _isUsingDTLS { false };
|
||||
|
||||
QUrl _oauthProviderURL;
|
||||
QString _oauthClientID;
|
||||
|
@ -265,10 +266,13 @@ private:
|
|||
friend class DomainGatekeeper;
|
||||
friend class DomainMetadata;
|
||||
|
||||
QString _iceServerAddr;
|
||||
int _iceServerPort;
|
||||
bool _overrideDomainID { false }; // should we override the domain-id from settings?
|
||||
QUuid _overridingDomainID { QUuid() }; // what should we override it with?
|
||||
static QString _iceServerAddr;
|
||||
static int _iceServerPort;
|
||||
static bool _overrideDomainID; // should we override the domain-id from settings?
|
||||
static QUuid _overridingDomainID; // what should we override it with?
|
||||
static bool _getTempName;
|
||||
static QString _userConfigFilename;
|
||||
static int _parentPID;
|
||||
|
||||
bool _sendICEServerAddressToMetaverseAPIInProgress { false };
|
||||
bool _sendICEServerAddressToMetaverseAPIRedo { false };
|
||||
|
|
|
@ -191,13 +191,12 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<Re
|
|||
nodeList->sendPacketList(std::move(packetList), message->getSenderSockAddr());
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||
void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilename) {
|
||||
// since we're called from the DomainServerSettingsManager constructor, we don't take a write lock here
|
||||
// even though we change the underlying config map
|
||||
|
||||
_argumentList = argumentList;
|
||||
|
||||
_configMap.loadConfig(_argumentList);
|
||||
_configMap.setUserConfigFilename(userConfigFilename);
|
||||
_configMap.loadConfig();
|
||||
|
||||
static const auto VERSION_SETTINGS_KEYPATH = "version";
|
||||
QVariant* versionVariant = _configMap.valueForKeyPath(VERSION_SETTINGS_KEYPATH);
|
||||
|
@ -1736,7 +1735,7 @@ void DomainServerSettingsManager::persistToFile() {
|
|||
// failed to write, reload whatever the current config state is
|
||||
// with a write lock since we're about to overwrite the config map
|
||||
QWriteLocker locker(&_settingsLock);
|
||||
_configMap.loadConfig(_argumentList);
|
||||
_configMap.loadConfig();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
DomainServerSettingsManager();
|
||||
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||
|
||||
void setupConfigMap(const QStringList& argumentList);
|
||||
void setupConfigMap(const QString& userConfigFilename);
|
||||
|
||||
// each of the three methods in this group takes a read lock of _settingsLock
|
||||
// and cannot be called when the a write lock is held by the same thread
|
||||
|
@ -144,8 +144,6 @@ private slots:
|
|||
void processUsernameFromIDRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
private:
|
||||
QStringList _argumentList;
|
||||
|
||||
QJsonArray filteredDescriptionArray(bool isContentSettings);
|
||||
void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
|
||||
const QJsonObject& settingDescription);
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
int main(int argc, char* argv[]) {
|
||||
setupHifiApplication(BuildInfo::DOMAIN_SERVER_NAME);
|
||||
|
||||
DomainServer::parseCommandLine(argc, argv);
|
||||
|
||||
Setting::init();
|
||||
|
||||
int currentExitCode = 0;
|
||||
|
|
|
@ -42,6 +42,48 @@ extern "C" {
|
|||
int main(int argc, const char* argv[]) {
|
||||
setupHifiApplication(BuildInfo::INTERFACE_NAME);
|
||||
|
||||
QStringList arguments;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
arguments << argv[i];
|
||||
}
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Interface");
|
||||
QCommandLineOption versionOption = parser.addVersionOption();
|
||||
QCommandLineOption helpOption = parser.addHelpOption();
|
||||
|
||||
QCommandLineOption urlOption("url", "", "value");
|
||||
QCommandLineOption noUpdaterOption("no-updater", "Do not show auto-updater");
|
||||
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
|
||||
QCommandLineOption runServerOption("runServer", "Whether to run the server");
|
||||
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
|
||||
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
|
||||
QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir");
|
||||
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
|
||||
|
||||
parser.addOption(urlOption);
|
||||
parser.addOption(noUpdaterOption);
|
||||
parser.addOption(checkMinSpecOption);
|
||||
parser.addOption(runServerOption);
|
||||
parser.addOption(serverContentPathOption);
|
||||
parser.addOption(overrideAppLocalDataPathOption);
|
||||
parser.addOption(overrideScriptsPathOption);
|
||||
parser.addOption(allowMultipleInstancesOption);
|
||||
|
||||
if (!parser.parse(arguments)) {
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
parser.showVersion();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
if (parser.isSet(helpOption)) {
|
||||
QCoreApplication mockApp(argc, const_cast<char**>(argv)); // required for call to showHelp()
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
// Early check for --traceFile argument
|
||||
auto tracer = DependencyManager::set<tracing::Tracer>();
|
||||
const char * traceFile = nullptr;
|
||||
|
@ -95,30 +137,6 @@ int main(int argc, const char* argv[]) {
|
|||
qDebug() << "Crash handler started:" << crashHandlerStarted;
|
||||
}
|
||||
|
||||
QStringList arguments;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
arguments << argv[i];
|
||||
}
|
||||
|
||||
QCommandLineParser parser;
|
||||
QCommandLineOption urlOption("url", "", "value");
|
||||
QCommandLineOption noUpdaterOption("no-updater", "Do not show auto-updater");
|
||||
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
|
||||
QCommandLineOption runServerOption("runServer", "Whether to run the server");
|
||||
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
|
||||
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
|
||||
QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir");
|
||||
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
|
||||
parser.addOption(urlOption);
|
||||
parser.addOption(noUpdaterOption);
|
||||
parser.addOption(checkMinSpecOption);
|
||||
parser.addOption(runServerOption);
|
||||
parser.addOption(serverContentPathOption);
|
||||
parser.addOption(overrideAppLocalDataPathOption);
|
||||
parser.addOption(overrideScriptsPathOption);
|
||||
parser.addOption(allowMultipleInstancesOption);
|
||||
parser.parse(arguments);
|
||||
|
||||
|
||||
const QString& applicationName = getInterfaceSharedMemoryName();
|
||||
bool instanceMightBeRunning = true;
|
||||
|
|
|
@ -112,11 +112,14 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) {
|
|||
}
|
||||
|
||||
batch.setModelTransform(renderTransform);
|
||||
drawMaterial->setTextureTransforms(textureTransform);
|
||||
|
||||
// bind the material
|
||||
RenderPipelines::bindMaterial(drawMaterial, batch, args->_enableTexturing);
|
||||
args->_details._materialSwitches++;
|
||||
if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
|
||||
drawMaterial->setTextureTransforms(textureTransform);
|
||||
|
||||
// bind the material
|
||||
RenderPipelines::bindMaterial(drawMaterial, batch, args->_enableTexturing);
|
||||
args->_details._materialSwitches++;
|
||||
}
|
||||
|
||||
// Draw!
|
||||
DependencyManager::get<GeometryCache>()->renderSphere(batch);
|
||||
|
|
|
@ -35,9 +35,7 @@ ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Pare
|
|||
_procedural._vertexSource = gpu::Shader::getVertexShaderSource(shader::render_utils::vertex::simple);
|
||||
// FIXME: Setup proper uniform slots and use correct pipelines for forward rendering
|
||||
_procedural._opaquefragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple);
|
||||
// FIXME: Transparent procedural entities only seem to work if they use the opaque pipelines
|
||||
//_procedural._transparentfragmentSource = simple_transparent_frag::getSource();
|
||||
_procedural._transparentfragmentSource = _procedural._opaquefragmentSource;
|
||||
_procedural._transparentfragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple_transparent);
|
||||
_procedural._opaqueState->setCullMode(gpu::State::CULL_NONE);
|
||||
_procedural._opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
PrepareStencil::testMaskDrawShape(*_procedural._opaqueState);
|
||||
|
@ -268,8 +266,10 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline);
|
||||
}
|
||||
} else {
|
||||
RenderPipelines::bindMaterial(mat, batch, args->_enableTexturing);
|
||||
args->_details._materialSwitches++;
|
||||
if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
|
||||
RenderPipelines::bindMaterial(mat, batch, args->_enableTexturing);
|
||||
args->_details._materialSwitches++;
|
||||
}
|
||||
|
||||
geometryCache->renderShape(batch, geometryShape);
|
||||
}
|
||||
|
|
|
@ -212,8 +212,9 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
|
|||
_hazeStage->_currentFrame.pushHaze(_hazeIndex);
|
||||
}
|
||||
|
||||
// Bloom only if the mode is not inherit, as the model deals with on/off
|
||||
if (_bloomMode != COMPONENT_MODE_INHERIT) {
|
||||
if (_bloomMode == COMPONENT_MODE_DISABLED) {
|
||||
_bloomStage->_currentFrame.pushBloom(INVALID_INDEX);
|
||||
} else if (_bloomMode == COMPONENT_MODE_ENABLED) {
|
||||
_bloomStage->_currentFrame.pushBloom(_bloomIndex);
|
||||
}
|
||||
}
|
||||
|
@ -426,8 +427,6 @@ void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity)
|
|||
|
||||
const auto& bloom = editBloom();
|
||||
|
||||
const uint32_t bloomMode = entity->getBloomMode();
|
||||
bloom->setBloomActive(bloomMode == COMPONENT_MODE_ENABLED);
|
||||
bloom->setBloomIntensity(_bloomProperties.getBloomIntensity());
|
||||
bloom->setBloomThreshold(_bloomProperties.getBloomThreshold());
|
||||
bloom->setBloomSize(_bloomProperties.getBloomSize());
|
||||
|
|
|
@ -149,7 +149,7 @@ void main(void) {
|
|||
vec3 UP = vec3(0, 1, 0);
|
||||
vec3 modelUpWorld;
|
||||
<$transformModelToWorldDir(cam, obj, UP, modelUpWorld)$>
|
||||
vec3 upWorld = mix(UP, normalize(modelUpWorld), particle.rotateWithEntity);
|
||||
vec3 upWorld = mix(UP, normalize(modelUpWorld), float(particle.rotateWithEntity));
|
||||
vec3 upEye = normalize(view3 * upWorld);
|
||||
vec3 FORWARD = vec3(0, 0, -1);
|
||||
vec3 particleRight = normalize(cross(FORWARD, upEye));
|
||||
|
|
|
@ -43,14 +43,6 @@ bool recommendedSparseTextures = (QThread::idealThreadCount() >= MIN_CORES_FOR_I
|
|||
|
||||
std::atomic<bool> Texture::_enableSparseTextures { recommendedSparseTextures };
|
||||
|
||||
struct ReportTextureState {
|
||||
ReportTextureState() {
|
||||
qCDebug(gpulogging) << "[TEXTURE TRANSFER SUPPORT]"
|
||||
<< "\n\tidealThreadCount:" << QThread::idealThreadCount()
|
||||
<< "\n\tRECOMMENDED enableSparseTextures:" << recommendedSparseTextures;
|
||||
}
|
||||
} report;
|
||||
|
||||
void Texture::setEnableSparseTextures(bool enabled) {
|
||||
#ifdef Q_OS_WIN
|
||||
qCDebug(gpulogging) << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Sparse Textures and Dynamic Texture Management:" << enabled;
|
||||
|
|
|
@ -26,15 +26,12 @@ namespace graphics {
|
|||
void setBloomIntensity(const float bloomIntensity) { _bloomIntensity = bloomIntensity; }
|
||||
void setBloomThreshold(const float bloomThreshold) { _bloomThreshold = bloomThreshold; }
|
||||
void setBloomSize(const float bloomSize) { _bloomSize = bloomSize; }
|
||||
void setBloomActive(const bool isBloomActive) { _isBloomActive = isBloomActive; }
|
||||
|
||||
float getBloomIntensity() { return _bloomIntensity; }
|
||||
float getBloomThreshold() { return _bloomThreshold; }
|
||||
float getBloomSize() { return _bloomSize; }
|
||||
bool getBloomActive() { return _isBloomActive; }
|
||||
|
||||
private:
|
||||
bool _isBloomActive { false };
|
||||
float _bloomIntensity { INITIAL_BLOOM_INTENSITY };
|
||||
float _bloomThreshold {INITIAL_BLOOM_THRESHOLD };
|
||||
float _bloomSize { INITIAL_BLOOM_SIZE };
|
||||
|
|
|
@ -269,7 +269,14 @@ vec3 fetchLightmapMap(vec2 uv) {
|
|||
|
||||
<@func discardTransparent(opacity)@>
|
||||
{
|
||||
if (<$opacity$> < 1e-6) {
|
||||
if (<$opacity$> < 1.0) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
<@endfunc@>
|
||||
<@func discardInvisible(opacity)@>
|
||||
{
|
||||
if (<$opacity$> < 1.e-6) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,4 @@ void main(void) {
|
|||
// Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
|
||||
color = pow(color, vec3(2.2));
|
||||
_fragColor = vec4(color, 0.0);
|
||||
|
||||
// FIXME: scribe does not yet scrub out else statements
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -65,12 +65,13 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons
|
|||
|
||||
glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y };
|
||||
|
||||
if (!bloom || !bloom->getBloomActive()) {
|
||||
if (!bloom) {
|
||||
outputs = _outputBuffer;
|
||||
return;
|
||||
}
|
||||
|
||||
_parameters.edit()._threshold = bloom->getBloomThreshold();
|
||||
qDebug() << "boop1" << bloom->getBloomIntensity() << bloom->getBloomThreshold() << bloom->getBloomSize();
|
||||
|
||||
//{
|
||||
// std::string blurName { "BloomBlurN" };
|
||||
|
@ -136,10 +137,12 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In
|
|||
const auto bloom = inputs.get4();
|
||||
const glm::ivec4 viewport{ 0, 0, framebufferSize.x, framebufferSize.y };
|
||||
|
||||
if (!bloom || !bloom->getBloomActive()) {
|
||||
if (!bloom) {
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "boop2" << bloom->getBloomIntensity() << bloom->getBloomThreshold() << bloom->getBloomSize();
|
||||
|
||||
const auto newIntensity = bloom->getBloomIntensity() / 3.0f;
|
||||
auto& parameters = _parameters.edit();
|
||||
parameters._intensities.x = newIntensity;
|
||||
|
@ -172,6 +175,13 @@ void BloomDraw::run(const render::RenderContextPointer& renderContext, const Inp
|
|||
|
||||
const auto frameBuffer = inputs.get0();
|
||||
const auto bloomFrameBuffer = inputs.get1();
|
||||
const auto bloom = inputs.get2();
|
||||
|
||||
if (!bloom) {
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "boop3" << bloom->getBloomIntensity() << bloom->getBloomThreshold() << bloom->getBloomSize();
|
||||
|
||||
if (frameBuffer && bloomFrameBuffer) {
|
||||
const auto framebufferSize = frameBuffer->getSize();
|
||||
|
@ -290,7 +300,7 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In
|
|||
BloomEffect::BloomEffect() {}
|
||||
|
||||
void BloomEffect::configure(const Config& config) {
|
||||
std::string blurName{ "BloomBlurN" };
|
||||
std::string blurName { "BloomBlurN" };
|
||||
|
||||
for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) {
|
||||
blurName.back() = '0' + i;
|
||||
|
@ -312,10 +322,10 @@ void BloomEffect::build(JobModel& task, const render::Varying& inputs, render::V
|
|||
const auto& frameBuffer = input[1];
|
||||
|
||||
// Mix all blur levels at quarter resolution
|
||||
const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2, input.get2()).asVarying();
|
||||
const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2, input[2]).asVarying();
|
||||
task.addJob<BloomApply>("BloomApply", applyInput);
|
||||
// And then blend result in additive manner on top of final color buffer
|
||||
const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer).asVarying();
|
||||
const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer, input[2]).asVarying();
|
||||
task.addJob<BloomDraw>("BloomDraw", drawInput);
|
||||
|
||||
const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2, bloomInputBuffer).asVarying();
|
||||
|
|
|
@ -73,7 +73,7 @@ private:
|
|||
|
||||
class BloomDraw {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using Inputs = render::VaryingSet3<gpu::FramebufferPointer, gpu::FramebufferPointer, graphics::BloomPointer>;
|
||||
using JobModel = render::Job::ModelI<BloomDraw, Inputs>;
|
||||
|
||||
BloomDraw() {}
|
||||
|
|
|
@ -24,7 +24,6 @@ void FetchBloomStage::configure(const Config& config) {
|
|||
_bloom->setBloomIntensity(config.bloomIntensity);
|
||||
_bloom->setBloomThreshold(config.bloomThreshold);
|
||||
_bloom->setBloomSize(config.bloomSize);
|
||||
_bloom->setBloomActive(config.isBloomActive);
|
||||
}
|
||||
|
||||
BloomStage::Index BloomStage::findBloom(const BloomPointer& bloom) const {
|
||||
|
|
|
@ -85,7 +85,6 @@ class FetchBloomConfig : public render::Job::Config {
|
|||
Q_PROPERTY(float bloomIntensity MEMBER bloomIntensity WRITE setBloomIntensity NOTIFY dirty);
|
||||
Q_PROPERTY(float bloomThreshold MEMBER bloomThreshold WRITE setBloomThreshold NOTIFY dirty);
|
||||
Q_PROPERTY(float bloomSize MEMBER bloomSize WRITE setBloomSize NOTIFY dirty);
|
||||
Q_PROPERTY(bool isBloomActive MEMBER isBloomActive WRITE setBloomActive NOTIFY dirty);
|
||||
|
||||
public:
|
||||
FetchBloomConfig() : render::Job::Config() {}
|
||||
|
@ -94,13 +93,10 @@ public:
|
|||
float bloomThreshold { graphics::Bloom::INITIAL_BLOOM_THRESHOLD };
|
||||
float bloomSize { graphics::Bloom::INITIAL_BLOOM_SIZE };
|
||||
|
||||
bool isBloomActive { false };
|
||||
|
||||
public slots:
|
||||
void setBloomIntensity(const float value) { bloomIntensity = value; emit dirty(); }
|
||||
void setBloomThreshold(const float value) { bloomThreshold = value; emit dirty(); }
|
||||
void setBloomSize(const float value) { bloomSize = value; emit dirty(); }
|
||||
void setBloomActive(const bool active) { isBloomActive = active; emit dirty(); }
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
|
|
|
@ -157,8 +157,10 @@ void MeshPartPayload::render(RenderArgs* args) {
|
|||
bindMesh(batch);
|
||||
|
||||
// apply material properties
|
||||
RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing);
|
||||
args->_details._materialSwitches++;
|
||||
if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
|
||||
RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing);
|
||||
args->_details._materialSwitches++;
|
||||
}
|
||||
|
||||
// Draw!
|
||||
{
|
||||
|
@ -417,8 +419,10 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
|
|||
bindMesh(batch);
|
||||
|
||||
// apply material properties
|
||||
RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing);
|
||||
args->_details._materialSwitches++;
|
||||
if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
|
||||
RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing);
|
||||
args->_details._materialSwitches++;
|
||||
}
|
||||
|
||||
// Draw!
|
||||
{
|
||||
|
|
|
@ -89,7 +89,7 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs)
|
|||
lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight());
|
||||
backgroundStage->_currentFrame.pushBackground(0);
|
||||
hazeStage->_currentFrame.pushHaze(0);
|
||||
bloomStage->_currentFrame.pushBloom(0);
|
||||
bloomStage->_currentFrame.pushBloom(INVALID_INDEX);
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() {
|
||||
|
|
|
@ -40,7 +40,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _color.a;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
<$discardInvisible(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -51,9 +51,9 @@ void main(void) {
|
|||
#ifdef PROCEDURAL
|
||||
|
||||
#ifdef PROCEDURAL_V1
|
||||
specular = getProceduralColor().rgb;
|
||||
diffuse = getProceduralColor().rgb;
|
||||
// Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
|
||||
//specular = pow(specular, vec3(2.2));
|
||||
//diffuse = pow(diffuse, vec3(2.2));
|
||||
emissiveAmount = 1.0;
|
||||
#else
|
||||
emissiveAmount = getProceduralColors(diffuse, specular, shininess);
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// forward_simple_transparent.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 9/15/14.
|
||||
// Copyright 2014 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 DefaultMaterials.slh@>
|
||||
|
||||
<@include ForwardGlobalLight.slh@>
|
||||
<$declareEvalGlobalLightingAlphaBlended()$>
|
||||
|
||||
// the interpolated normal
|
||||
layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS;
|
||||
layout(location=RENDER_UTILS_ATTR_NORMAL_MS) in vec3 _normalMS;
|
||||
layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color;
|
||||
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01;
|
||||
#define _texCoord0 _texCoord01.xy
|
||||
#define _texCoord1 _texCoord01.zw
|
||||
layout(location=RENDER_UTILS_ATTR_POSITION_MS) in vec4 _positionMS;
|
||||
layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES;
|
||||
|
||||
// For retro-compatibility
|
||||
#define _normal _normalWS
|
||||
#define _modelNormal _normalMS
|
||||
#define _position _positionMS
|
||||
#define _eyePosition _positionES
|
||||
|
||||
layout(location=0) out vec4 _fragColor0;
|
||||
|
||||
//PROCEDURAL_COMMON_BLOCK
|
||||
|
||||
#line 1001
|
||||
//PROCEDURAL_BLOCK
|
||||
|
||||
#line 2030
|
||||
void main(void) {
|
||||
vec3 normal = normalize(_normalWS.xyz);
|
||||
vec3 diffuse = _color.rgb;
|
||||
vec3 specular = DEFAULT_SPECULAR;
|
||||
float shininess = DEFAULT_SHININESS;
|
||||
float emissiveAmount = 0.0;
|
||||
|
||||
#ifdef PROCEDURAL
|
||||
|
||||
#ifdef PROCEDURAL_V1
|
||||
specular = getProceduralColor().rgb;
|
||||
// Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
|
||||
//specular = pow(specular, vec3(2.2));
|
||||
emissiveAmount = 1.0;
|
||||
#else
|
||||
emissiveAmount = getProceduralColors(diffuse, specular, shininess);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
|
||||
if (emissiveAmount > 0.0) {
|
||||
_fragColor0 = vec4(evalGlobalLightingAlphaBlendedWithHaze(
|
||||
cam._viewInverse,
|
||||
1.0,
|
||||
DEFAULT_OCCLUSION,
|
||||
fragPosition,
|
||||
normal,
|
||||
specular,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_METALLIC,
|
||||
DEFAULT_EMISSIVE,
|
||||
DEFAULT_ROUGHNESS, _color.a),
|
||||
_color.a);
|
||||
} else {
|
||||
_fragColor0 = vec4(evalGlobalLightingAlphaBlendedWithHaze(
|
||||
cam._viewInverse,
|
||||
1.0,
|
||||
DEFAULT_OCCLUSION,
|
||||
fragPosition,
|
||||
normal,
|
||||
diffuse,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_METALLIC,
|
||||
DEFAULT_EMISSIVE,
|
||||
DEFAULT_ROUGHNESS, _color.a),
|
||||
_color.a);
|
||||
}
|
||||
}
|
|
@ -44,7 +44,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _color.a;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
<$discardInvisible(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -47,7 +47,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _color.a;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
<$discardInvisible(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -57,7 +57,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _color.a;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
<$discardInvisible(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -34,7 +34,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _color.a;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
<$discardInvisible(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -44,7 +44,7 @@ void main(void) {
|
|||
|
||||
float opacity = getMaterialOpacity(mat) * _color.a;
|
||||
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
|
||||
<$discardTransparent(opacity)$>;
|
||||
<$discardInvisible(opacity)$>;
|
||||
|
||||
vec3 albedo = getMaterialAlbedo(mat);
|
||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<@include gpu/Transform.slh@>
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
layout(std140, binding=0) uniform parabolaData {
|
||||
struct ParabolaData {
|
||||
vec3 velocity;
|
||||
float parabolicDistance;
|
||||
vec3 acceleration;
|
||||
|
@ -20,34 +20,38 @@ layout(std140, binding=0) uniform parabolaData {
|
|||
vec4 color;
|
||||
int numSections;
|
||||
ivec3 spare;
|
||||
}
|
||||
|
||||
layout(std140, binding=0) uniform parabolaData {
|
||||
ParabolaData _parabolaData;
|
||||
};
|
||||
|
||||
layout(location=0) out vec4 _color;
|
||||
|
||||
void main(void) {
|
||||
_color = color;
|
||||
_color = _parabolaData.color;
|
||||
|
||||
float t = parabolicDistance * (float(gl_VertexID / 2) / float(numSections));
|
||||
float t = _parabolaData.parabolicDistance * (float(gl_VertexID / 2) / float(_parabolaData.numSections));
|
||||
|
||||
vec4 pos = vec4(velocity * t + 0.5 * acceleration * t * t, 1);
|
||||
vec4 pos = vec4(_parabolaData.velocity * t + 0.5 * _parabolaData.acceleration * t * t, 1);
|
||||
const float EPSILON = 0.00001;
|
||||
vec4 normal;
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
if (dot(acceleration, acceleration) < EPSILON) {
|
||||
if (dot(_parabolaData.acceleration, _parabolaData.acceleration) < EPSILON) {
|
||||
// Handle case where acceleration == (0, 0, 0)
|
||||
vec3 eyeUp = vec3(0, 1, 0);
|
||||
vec3 worldUp;
|
||||
<$transformEyeToWorldDir(cam, eyeUp, worldUp)$>
|
||||
normal = vec4(normalize(cross(velocity, worldUp)), 0);
|
||||
normal = vec4(normalize(cross(_parabolaData.velocity, worldUp)), 0);
|
||||
} else {
|
||||
normal = vec4(normalize(cross(velocity, acceleration)), 0);
|
||||
normal = vec4(normalize(cross(_parabolaData.velocity, _parabolaData.acceleration)), 0);
|
||||
}
|
||||
if (gl_VertexID % 2 == 0) {
|
||||
pos += 0.5 * width * normal;
|
||||
pos += 0.5 * _parabolaData.width * normal;
|
||||
} else {
|
||||
pos -= 0.5 * width * normal;
|
||||
pos -= 0.5 * _parabolaData.width * normal;
|
||||
}
|
||||
|
||||
<$transformModelToClipPos(cam, obj, pos, gl_Position)$>
|
||||
|
|
|
@ -48,9 +48,9 @@ void main(void) {
|
|||
#ifdef PROCEDURAL
|
||||
|
||||
#ifdef PROCEDURAL_V1
|
||||
specular = getProceduralColor().rgb;
|
||||
diffuse = getProceduralColor().rgb;
|
||||
// Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
|
||||
//specular = pow(specular, vec3(2.2));
|
||||
//diffuse = pow(diffuse, vec3(2.2));
|
||||
emissiveAmount = 1.0;
|
||||
#else
|
||||
emissiveAmount = getProceduralColors(diffuse, specular, shininess);
|
||||
|
|
|
@ -11,8 +11,10 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
<@include DefaultMaterials.slh@>
|
||||
|
||||
<@include DeferredBufferWrite.slh@>
|
||||
<@include DeferredGlobalLight.slh@>
|
||||
<$declareEvalGlobalLightingAlphaBlendedWithHaze()$>
|
||||
|
||||
<@include render-utils/ShaderConstants.h@>
|
||||
|
||||
|
@ -26,6 +28,8 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01;
|
|||
layout(location=RENDER_UTILS_ATTR_POSITION_MS) in vec4 _positionMS;
|
||||
layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES;
|
||||
|
||||
layout(location=0) out vec4 _fragColor0;
|
||||
|
||||
// For retro-compatibility
|
||||
#define _normal _normalWS
|
||||
#define _modelNormal _normalMS
|
||||
|
@ -48,9 +52,9 @@ void main(void) {
|
|||
#ifdef PROCEDURAL
|
||||
|
||||
#ifdef PROCEDURAL_V1
|
||||
specular = getProceduralColor().rgb;
|
||||
diffuse = getProceduralColor().rgb;
|
||||
// Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
|
||||
//specular = pow(specular, vec3(2.2));
|
||||
//diffuse = pow(diffuse, vec3(2.2));
|
||||
emissiveAmount = 1.0;
|
||||
#else
|
||||
emissiveAmount = getProceduralColors(diffuse, specular, shininess);
|
||||
|
@ -58,19 +62,23 @@ void main(void) {
|
|||
|
||||
#endif
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
vec3 fragPosition = _positionES.xyz;
|
||||
|
||||
if (emissiveAmount > 0.0) {
|
||||
packDeferredFragmentTranslucent(
|
||||
normal,
|
||||
_color.a,
|
||||
specular,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_ROUGHNESS);
|
||||
_fragColor0 = vec4(diffuse, _color.a);
|
||||
} else {
|
||||
packDeferredFragmentTranslucent(
|
||||
_fragColor0 = vec4(evalGlobalLightingAlphaBlendedWithHaze(
|
||||
cam._viewInverse,
|
||||
1.0,
|
||||
DEFAULT_OCCLUSION,
|
||||
fragPosition,
|
||||
normal,
|
||||
_color.a,
|
||||
diffuse,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_ROUGHNESS);
|
||||
length(specular),
|
||||
DEFAULT_EMISSIVE,
|
||||
max(0.0, 1.0 - shininess / 128.0), _color.a),
|
||||
_color.a);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,8 @@
|
|||
|
||||
#include "Args.h"
|
||||
|
||||
|
||||
using namespace render;
|
||||
|
||||
|
||||
const gpu::PipelinePointer DrawSceneOctree::getDrawCellBoundsPipeline() {
|
||||
if (!_drawCellBoundsPipeline) {
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render::program::drawCellBounds);
|
||||
|
@ -71,7 +69,6 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS
|
|||
std::static_pointer_cast<Config>(renderContext->jobConfig)->numAllocatedCells = (int)scene->getSpatialTree().getNumAllocatedCells();
|
||||
std::static_pointer_cast<Config>(renderContext->jobConfig)->numFreeCells = (int)scene->getSpatialTree().getNumFreeCells();
|
||||
|
||||
|
||||
gpu::doInBatch("DrawSceneOctree::run", args->_context, [&](gpu::Batch& batch) {
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
|
@ -86,44 +83,30 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS
|
|||
// bind the one gpu::Pipeline we need
|
||||
batch.setPipeline(getDrawCellBoundsPipeline());
|
||||
|
||||
if (_showVisibleCells) {
|
||||
|
||||
for (const auto& cellID : inSelection.cellSelection.insideCells) {
|
||||
auto drawCellBounds = [this, &scene, &batch](const std::vector<gpu::Stamp>& cells) {
|
||||
for (const auto& cellID : cells) {
|
||||
auto cell = scene->getSpatialTree().getConcreteCell(cellID);
|
||||
auto cellLoc = cell.getlocation();
|
||||
glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth);
|
||||
|
||||
bool doDraw = true;
|
||||
if (cell.isBrickEmpty() || !cell.hasBrick()) {
|
||||
bool empty = cell.isBrickEmpty() || !cell.hasBrick();
|
||||
if (empty) {
|
||||
if (!_showEmptyCells) {
|
||||
doDraw = false;
|
||||
continue;
|
||||
}
|
||||
cellLocation.w *= -1;
|
||||
cellLocation.w *= -1.0;
|
||||
} else if (!empty && !_showVisibleCells) {
|
||||
continue;
|
||||
}
|
||||
if (doDraw) {
|
||||
batch._glUniform4iv(gpu::slot::uniform::Extra0, 1, ((const int*)(&cellLocation)));
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& cellID : inSelection.cellSelection.partialCells) {
|
||||
auto cell = scene->getSpatialTree().getConcreteCell(cellID);
|
||||
auto cellLoc = cell.getlocation();
|
||||
glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth);
|
||||
|
||||
bool doDraw = true;
|
||||
if (cell.isBrickEmpty() || !cell.hasBrick()) {
|
||||
if (!_showEmptyCells) {
|
||||
doDraw = false;
|
||||
}
|
||||
cellLocation.w *= -1;
|
||||
}
|
||||
if (doDraw) {
|
||||
batch._glUniform4iv(gpu::slot::uniform::Extra0, 1, ((const int*)(&cellLocation)));
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
}
|
||||
batch._glUniform4iv(gpu::slot::uniform::Extra0, 1, ((const int*)(&cellLocation)));
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
drawCellBounds(inSelection.cellSelection.insideCells);
|
||||
drawCellBounds(inSelection.cellSelection.partialCells);
|
||||
|
||||
// Draw the LOD Reticle
|
||||
{
|
||||
float angle = glm::degrees(getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
|
||||
|
@ -142,10 +125,6 @@ const gpu::PipelinePointer DrawItemSelection::getDrawItemBoundPipeline() {
|
|||
if (!_drawItemBoundPipeline) {
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render::program::drawItemBounds);
|
||||
|
||||
//_drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos");
|
||||
//_drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim");
|
||||
//_drawCellLocationLoc = program->getUniforms().findLocation("inCellLocation");
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
|
@ -173,6 +152,19 @@ void DrawItemSelection::run(const RenderContextPointer& renderContext, const Ite
|
|||
RenderArgs* args = renderContext->args;
|
||||
auto& scene = renderContext->_scene;
|
||||
|
||||
if (!_boundsBufferInside) {
|
||||
_boundsBufferInside = std::make_shared<gpu::Buffer>(sizeof(render::ItemBound));
|
||||
}
|
||||
if (!_boundsBufferInsideSubcell) {
|
||||
_boundsBufferInsideSubcell = std::make_shared<gpu::Buffer>(sizeof(render::ItemBound));
|
||||
}
|
||||
if (!_boundsBufferPartial) {
|
||||
_boundsBufferPartial = std::make_shared<gpu::Buffer>(sizeof(render::ItemBound));
|
||||
}
|
||||
if (!_boundsBufferPartialSubcell) {
|
||||
_boundsBufferPartialSubcell = std::make_shared<gpu::Buffer>(sizeof(render::ItemBound));
|
||||
}
|
||||
|
||||
gpu::doInBatch("DrawItemSelection::run", args->_context, [&](gpu::Batch& batch) {
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
|
@ -187,63 +179,35 @@ void DrawItemSelection::run(const RenderContextPointer& renderContext, const Ite
|
|||
// bind the one gpu::Pipeline we need
|
||||
batch.setPipeline(getDrawItemBoundPipeline());
|
||||
|
||||
auto drawItemBounds = [&](const render::ItemIDs itemIDs, const gpu::BufferPointer buffer) {
|
||||
render::ItemBounds itemBounds;
|
||||
for (const auto& itemID : itemIDs) {
|
||||
auto& item = scene->getItem(itemID);
|
||||
auto itemBound = item.getBound();
|
||||
if (!itemBound.isInvalid()) {
|
||||
itemBounds.emplace_back(itemID, itemBound);
|
||||
}
|
||||
}
|
||||
|
||||
if (itemBounds.size() > 0) {
|
||||
buffer->setData(itemBounds.size() * sizeof(render::ItemBound), (const gpu::Byte*) itemBounds.data());
|
||||
batch.setResourceBuffer(0, buffer);
|
||||
batch.draw(gpu::LINES, (gpu::uint32) itemBounds.size() * 24, 0);
|
||||
}
|
||||
};
|
||||
|
||||
if (_showInsideItems) {
|
||||
for (const auto& itemID : inSelection.insideItems) {
|
||||
auto& item = scene->getItem(itemID);
|
||||
auto itemBound = item.getBound();
|
||||
auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell());
|
||||
glm::ivec4 cellLocation(0, 0, 0, itemCell.depth);
|
||||
|
||||
//batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation)));
|
||||
//batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner()));
|
||||
//batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale()));
|
||||
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
}
|
||||
drawItemBounds(inSelection.insideItems, _boundsBufferInside);
|
||||
}
|
||||
|
||||
if (_showInsideSubcellItems) {
|
||||
for (const auto& itemID : inSelection.insideSubcellItems) {
|
||||
auto& item = scene->getItem(itemID);
|
||||
auto itemBound = item.getBound();
|
||||
auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell());
|
||||
glm::ivec4 cellLocation(0, 0, 1, itemCell.depth);
|
||||
|
||||
//batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation)));
|
||||
//batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner()));
|
||||
//batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale()));
|
||||
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
}
|
||||
drawItemBounds(inSelection.insideSubcellItems, _boundsBufferInsideSubcell);
|
||||
}
|
||||
|
||||
if (_showPartialItems) {
|
||||
for (const auto& itemID : inSelection.partialItems) {
|
||||
auto& item = scene->getItem(itemID);
|
||||
auto itemBound = item.getBound();
|
||||
auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell());
|
||||
glm::ivec4 cellLocation(0, 0, 0, itemCell.depth);
|
||||
|
||||
//batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation)));
|
||||
//batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner()));
|
||||
//batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale()));
|
||||
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
}
|
||||
drawItemBounds(inSelection.partialItems, _boundsBufferPartial);
|
||||
}
|
||||
|
||||
if (_showPartialSubcellItems) {
|
||||
for (const auto& itemID : inSelection.partialSubcellItems) {
|
||||
auto& item = scene->getItem(itemID);
|
||||
auto itemBound = item.getBound();
|
||||
auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell());
|
||||
glm::ivec4 cellLocation(0, 0, 1, itemCell.depth);
|
||||
//batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation)));
|
||||
//batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner()));
|
||||
//batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale()));
|
||||
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
}
|
||||
drawItemBounds(inSelection.partialSubcellItems, _boundsBufferPartialSubcell);
|
||||
}
|
||||
batch.setResourceBuffer(0, 0);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -52,7 +52,6 @@ namespace render {
|
|||
|
||||
class DrawSceneOctree {
|
||||
gpu::PipelinePointer _drawCellBoundsPipeline;
|
||||
gpu::BufferPointer _cells;
|
||||
gpu::PipelinePointer _drawLODReticlePipeline;
|
||||
gpu::PipelinePointer _drawItemBoundPipeline;
|
||||
|
||||
|
@ -107,6 +106,10 @@ namespace render {
|
|||
|
||||
class DrawItemSelection {
|
||||
gpu::PipelinePointer _drawItemBoundPipeline;
|
||||
gpu::BufferPointer _boundsBufferInside;
|
||||
gpu::BufferPointer _boundsBufferInsideSubcell;
|
||||
gpu::BufferPointer _boundsBufferPartial;
|
||||
gpu::BufferPointer _boundsBufferPartialSubcell;
|
||||
|
||||
bool _showInsideItems; // initialized by Config
|
||||
bool _showInsideSubcellItems; // initialized by Config
|
||||
|
|
|
@ -33,9 +33,6 @@ void DrawStatusConfig::dirtyHelper() {
|
|||
const gpu::PipelinePointer DrawStatus::getDrawItemBoundsPipeline() {
|
||||
if (!_drawItemBoundsPipeline) {
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render::program::drawItemBounds);
|
||||
//_drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos");
|
||||
//_drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim");
|
||||
//_drawItemCellLocLoc = program->getUniforms().findLocation("inCellLocation");
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
|
||||
|
@ -54,11 +51,7 @@ const gpu::PipelinePointer DrawStatus::getDrawItemBoundsPipeline() {
|
|||
|
||||
const gpu::PipelinePointer DrawStatus::getDrawItemStatusPipeline() {
|
||||
if (!_drawItemStatusPipeline) {
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render::program::blurGaussianDepthAwareV);
|
||||
//_drawItemStatusPosLoc = program->getUniforms().findLocation("");
|
||||
//_drawItemStatusDimLoc = program->getUniforms().findLocation("");
|
||||
//_drawItemStatusValue0Loc = program->getUniforms().findLocation("");
|
||||
//_drawItemStatusValue1Loc = program->getUniforms().findLocation("");
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render::program::drawItemStatus);
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
|
||||
|
@ -99,36 +92,30 @@ void DrawStatus::run(const RenderContextPointer& renderContext, const Input& inp
|
|||
const auto& inItems = input.get0();
|
||||
const auto jitter = input.get1();
|
||||
|
||||
// FIrst thing, we collect the bound and the status for all the items we want to render
|
||||
// First thing, we collect the bound and the status for all the items we want to render
|
||||
int nbItems = 0;
|
||||
render::ItemBounds itemBounds;
|
||||
std::vector<std::pair<glm::ivec4, glm::ivec4>> itemStatus;
|
||||
{
|
||||
_itemBounds.resize(inItems.size());
|
||||
_itemStatus.resize(inItems.size());
|
||||
_itemCells.resize(inItems.size());
|
||||
|
||||
// AABox* itemAABox = reinterpret_cast<AABox*> (_itemBounds->editData());
|
||||
// glm::ivec4* itemStatus = reinterpret_cast<glm::ivec4*> (_itemStatus->editData());
|
||||
// Octree::Location* itemCell = reinterpret_cast<Octree::Location*> (_itemCells->editData());
|
||||
for (size_t i = 0; i < inItems.size(); ++i) {
|
||||
const auto& item = inItems[i];
|
||||
if (!item.bound.isInvalid()) {
|
||||
if (!item.bound.isNull()) {
|
||||
_itemBounds[i] = item.bound;
|
||||
itemBounds.emplace_back(render::ItemBound(item.id, item.bound));
|
||||
} else {
|
||||
_itemBounds[i].setBox(item.bound.getCorner(), 0.1f);
|
||||
itemBounds.emplace_back(item.id, AABox(item.bound.getCorner(), 0.1f));
|
||||
}
|
||||
|
||||
|
||||
auto& itemScene = scene->getItem(item.id);
|
||||
_itemCells[i] = scene->getSpatialTree().getCellLocation(itemScene.getCell());
|
||||
|
||||
auto itemStatusPointer = itemScene.getStatus();
|
||||
if (itemStatusPointer) {
|
||||
itemStatus.push_back(std::pair<glm::ivec4, glm::ivec4>());
|
||||
// Query the current status values, this is where the statusGetter lambda get called
|
||||
auto&& currentStatusValues = itemStatusPointer->getCurrentValues();
|
||||
int valueNum = 0;
|
||||
for (int vec4Num = 0; vec4Num < NUM_STATUS_VEC4_PER_ITEM; vec4Num++) {
|
||||
auto& value = (vec4Num ? _itemStatus[i].first : _itemStatus[i].second);
|
||||
auto& value = (vec4Num ? itemStatus[nbItems].first : itemStatus[nbItems].second);
|
||||
value = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
|
||||
for (int component = 0; component < VEC4_LENGTH; component++) {
|
||||
valueNum = vec4Num * VEC4_LENGTH + component;
|
||||
|
@ -138,7 +125,8 @@ void DrawStatus::run(const RenderContextPointer& renderContext, const Input& inp
|
|||
}
|
||||
}
|
||||
} else {
|
||||
_itemStatus[i].first = _itemStatus[i].second = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
|
||||
auto invalid = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
|
||||
itemStatus.emplace_back(invalid, invalid);
|
||||
}
|
||||
nbItems++;
|
||||
}
|
||||
|
@ -149,7 +137,11 @@ void DrawStatus::run(const RenderContextPointer& renderContext, const Input& inp
|
|||
return;
|
||||
}
|
||||
|
||||
// Allright, something to render let's do it
|
||||
if (!_boundsBuffer) {
|
||||
_boundsBuffer = std::make_shared<gpu::Buffer>(sizeof(render::ItemBound));
|
||||
}
|
||||
|
||||
// Alright, something to render let's do it
|
||||
gpu::doInBatch("DrawStatus::run", args->_context, [&](gpu::Batch& batch) {
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
|
@ -165,32 +157,24 @@ void DrawStatus::run(const RenderContextPointer& renderContext, const Input& inp
|
|||
// bind the one gpu::Pipeline we need
|
||||
batch.setPipeline(getDrawItemBoundsPipeline());
|
||||
|
||||
//AABox* itemAABox = reinterpret_cast<AABox*> (_itemBounds->editData());
|
||||
//glm::ivec4* itemStatus = reinterpret_cast<glm::ivec4*> (_itemStatus->editData());
|
||||
//Octree::Location* itemCell = reinterpret_cast<Octree::Location*> (_itemCells->editData());
|
||||
|
||||
const unsigned int VEC3_ADRESS_OFFSET = 3;
|
||||
_boundsBuffer->setData(itemBounds.size() * sizeof(render::ItemBound), (const gpu::Byte*) itemBounds.data());
|
||||
|
||||
if (_showDisplay) {
|
||||
for (int i = 0; i < nbItems; i++) {
|
||||
//batch._glUniform3fv(gpu::slot::uniform::Extra0, 1, (const float*)&(_itemBounds[i]));
|
||||
//batch._glUniform3fv(gpu::slot::uniform::Extra1, 1, ((const float*)&(_itemBounds[i])) + VEC3_ADRESS_OFFSET);
|
||||
//glm::ivec4 cellLocation(_itemCells[i].pos, _itemCells[i].depth);
|
||||
//batch._glUniform4iv(_drawItemCellLocLoc, 1, ((const int*)(&cellLocation)));
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
}
|
||||
batch.setResourceBuffer(0, _boundsBuffer);
|
||||
batch.draw(gpu::LINES, (gpu::uint32) itemBounds.size() * 24, 0);
|
||||
}
|
||||
batch.setResourceBuffer(0, 0);
|
||||
|
||||
batch.setResourceTexture(0, gpu::TextureView(getStatusIconMap(), 0));
|
||||
|
||||
batch.setPipeline(getDrawItemStatusPipeline());
|
||||
|
||||
if (_showNetwork) {
|
||||
for (int i = 0; i < nbItems; i++) {
|
||||
batch._glUniform3fv(gpu::slot::uniform::Extra0, 1, (const float*)&(_itemBounds[i]));
|
||||
batch._glUniform3fv(gpu::slot::uniform::Extra1, 1, ((const float*)&(_itemBounds[i])) + VEC3_ADRESS_OFFSET);
|
||||
batch._glUniform4iv(gpu::slot::uniform::Extra2, 1, (const int*)&(_itemStatus[i].first));
|
||||
batch._glUniform4iv(gpu::slot::uniform::Extra3, 1, (const int*)&(_itemStatus[i].second));
|
||||
for (size_t i = 0; i < itemBounds.size(); i++) {
|
||||
batch._glUniform3fv(gpu::slot::uniform::Extra0, 1, (const float*)&itemBounds[i].bound.getCorner());
|
||||
batch._glUniform3fv(gpu::slot::uniform::Extra1, 1, ((const float*)&itemBounds[i].bound.getScale()));
|
||||
batch._glUniform4iv(gpu::slot::uniform::Extra2, 1, (const int*)&(itemStatus[i].first));
|
||||
batch._glUniform4iv(gpu::slot::uniform::Extra3, 1, (const int*)&(itemStatus[i].second));
|
||||
batch.draw(gpu::TRIANGLES, 24 * NUM_STATUS_VEC4_PER_ITEM, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,12 +62,7 @@ namespace render {
|
|||
gpu::PipelinePointer _drawItemBoundsPipeline;
|
||||
gpu::PipelinePointer _drawItemStatusPipeline;
|
||||
|
||||
std::vector<AABox> _itemBounds;
|
||||
std::vector<std::pair<glm::ivec4, glm::ivec4>> _itemStatus;
|
||||
std::vector<Octree::Location> _itemCells;
|
||||
//gpu::BufferPointer _itemBounds;
|
||||
//gpu::BufferPointer _itemCells;
|
||||
//gpu::BufferPointer _itemStatus;
|
||||
gpu::BufferPointer _boundsBuffer;
|
||||
gpu::TexturePointer _statusIconMap;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -321,6 +321,7 @@ inline QDebug operator<<(QDebug debug, const ItemFilter& me) {
|
|||
// Handy type to just pass the ID and the bound of an item
|
||||
class ItemBound {
|
||||
public:
|
||||
ItemBound() {}
|
||||
ItemBound(ItemID id) : id(id) { }
|
||||
ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { }
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ layout(location=GPU_UNIFORM_EXTRA0) uniform ivec4 inCellLocation;
|
|||
|
||||
layout(location=0) out vec4 varColor;
|
||||
|
||||
|
||||
void main(void) {
|
||||
const vec4 UNIT_BOX[8] = vec4[8](
|
||||
vec4(0.0, 0.0, 0.0, 1.0),
|
||||
|
|
|
@ -20,10 +20,8 @@
|
|||
<@include gpu/Color.slh@>
|
||||
<$declareColorWheel()$>
|
||||
|
||||
|
||||
layout(location=GPU_UNIFORM_COLOR) uniform vec4 inColor;
|
||||
|
||||
|
||||
struct ItemBound {
|
||||
vec4 id_boundPos;
|
||||
vec4 boundDim_s;
|
||||
|
@ -48,8 +46,6 @@ ItemBound getItemBound(int i) {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
layout(location=0) out vec4 varColor;
|
||||
layout(location=1) out vec2 varTexcoord;
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
#include "WebSocketClass.h"
|
||||
#include "RecordingScriptingInterface.h"
|
||||
#include "ScriptEngines.h"
|
||||
#include "StackTestScriptingInterface.h"
|
||||
#include "ModelScriptingInterface.h"
|
||||
|
||||
|
||||
|
@ -762,6 +763,10 @@ void ScriptEngine::init() {
|
|||
qScriptRegisterMetaType(this, meshesToScriptValue, meshesFromScriptValue);
|
||||
|
||||
registerGlobalObject("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
|
||||
|
||||
#if DEV_BUILD || PR_BUILD
|
||||
registerGlobalObject("StackTest", new StackTestScriptingInterface(this));
|
||||
#endif
|
||||
}
|
||||
|
||||
void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
|
||||
|
|
31
libraries/script-engine/src/StackTestScriptingInterface.cpp
Normal file
31
libraries/script-engine/src/StackTestScriptingInterface.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// StackTestScriptingInterface.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Clement Brisset on 7/25/18.
|
||||
// Copyright 2018 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 "StackTestScriptingInterface.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <QCoreApplication>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(stackTest)
|
||||
Q_LOGGING_CATEGORY(stackTest, "hifi.tools.stack-test")
|
||||
|
||||
void StackTestScriptingInterface::pass(QString message) {
|
||||
qCInfo(stackTest) << "PASS" << qPrintable(message);
|
||||
}
|
||||
|
||||
void StackTestScriptingInterface::fail(QString message) {
|
||||
qCInfo(stackTest) << "FAIL" << qPrintable(message);
|
||||
}
|
||||
|
||||
void StackTestScriptingInterface::exit(QString message) {
|
||||
qCInfo(stackTest) << "COMPLETE" << qPrintable(message);
|
||||
qApp->exit();
|
||||
}
|
31
libraries/script-engine/src/StackTestScriptingInterface.h
Normal file
31
libraries/script-engine/src/StackTestScriptingInterface.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// StackTestScriptingInterface.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Clement Brisset on 7/25/18.
|
||||
// Copyright 2018 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef hifi_StackTestScriptingInterface_h
|
||||
#define hifi_StackTestScriptingInterface_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class StackTestScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StackTestScriptingInterface(QObject* parent = nullptr) : QObject(parent) {}
|
||||
|
||||
Q_INVOKABLE void pass(QString message = QString());
|
||||
Q_INVOKABLE void fail(QString message = QString());
|
||||
|
||||
Q_INVOKABLE void exit(QString message = QString());
|
||||
};
|
||||
|
||||
#endif // hifi_StackTestScriptingInterface_h
|
|
@ -294,7 +294,19 @@ glm::vec3 safeEulerAngles(const glm::quat& q) {
|
|||
|
||||
// Helper function returns the positive angle (in radians) between two 3D vectors
|
||||
float angleBetween(const glm::vec3& v1, const glm::vec3& v2) {
|
||||
return acosf((glm::dot(v1, v2)) / (glm::length(v1) * glm::length(v2)));
|
||||
float lengthFactor = glm::length(v1) * glm::length(v2);
|
||||
|
||||
if (lengthFactor < EPSILON) {
|
||||
qWarning() << "DANGER: don't supply zero-length vec3's as arguments";
|
||||
}
|
||||
|
||||
float cosAngle = glm::dot(v1, v2) / lengthFactor;
|
||||
// If v1 and v2 are colinear, then floating point rounding errors might cause
|
||||
// cosAngle to be slightly higher than 1 or slightly lower than -1
|
||||
// which is are values for which acos is not defined and result in a NaN
|
||||
// So we clamp the value to insure the value is in the correct range
|
||||
cosAngle = glm::clamp(cosAngle, -1.0f, 1.0f);
|
||||
return acosf(cosAngle);
|
||||
}
|
||||
|
||||
// Helper function return the rotation from the first vector onto the second
|
||||
|
|
|
@ -91,58 +91,7 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL
|
|||
return mergedMap;
|
||||
}
|
||||
|
||||
void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) {
|
||||
// load the user config
|
||||
const QString USER_CONFIG_FILE_OPTION = "--user-config";
|
||||
static const QString USER_CONFIG_FILE_NAME = "config.json";
|
||||
|
||||
int userConfigIndex = argumentList.indexOf(USER_CONFIG_FILE_OPTION);
|
||||
if (userConfigIndex != -1) {
|
||||
_userConfigFilename = argumentList[userConfigIndex + 1];
|
||||
} else {
|
||||
// we weren't passed a user config path
|
||||
_userConfigFilename = PathUtils::getAppDataFilePath(USER_CONFIG_FILE_NAME);
|
||||
|
||||
// as of 1/19/2016 this path was moved so we attempt a migration for first run post migration here
|
||||
|
||||
// figure out what the old path was
|
||||
|
||||
// if our build version is "dev" we should migrate from a different organization folder
|
||||
|
||||
auto oldConfigFilename = QString("%1/%2/%3/%4").arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation),
|
||||
QCoreApplication::organizationName(),
|
||||
QCoreApplication::applicationName(),
|
||||
USER_CONFIG_FILE_NAME);
|
||||
|
||||
oldConfigFilename = oldConfigFilename.replace("High Fidelity - dev", "High Fidelity");
|
||||
|
||||
|
||||
// check if there's already a config file at the new path
|
||||
QFile newConfigFile { _userConfigFilename };
|
||||
if (!newConfigFile.exists()) {
|
||||
|
||||
QFile oldConfigFile { oldConfigFilename };
|
||||
|
||||
if (oldConfigFile.exists()) {
|
||||
// we have the old file and not the new file - time to copy the file
|
||||
|
||||
// make the destination directory if it doesn't exist
|
||||
auto dataDirectory = PathUtils::getAppDataPath();
|
||||
if (QDir().mkpath(dataDirectory)) {
|
||||
if (oldConfigFile.copy(_userConfigFilename)) {
|
||||
qCDebug(shared) << "Migrated config file from" << oldConfigFilename << "to" << _userConfigFilename;
|
||||
} else {
|
||||
qCWarning(shared) << "Could not copy previous config file from" << oldConfigFilename << "to" << _userConfigFilename
|
||||
<< "- please try to copy manually and restart.";
|
||||
}
|
||||
} else {
|
||||
qCWarning(shared) << "Could not create application data directory" << dataDirectory << "- unable to migrate previous config file.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void HifiConfigVariantMap::loadConfig() {
|
||||
loadMapFromJSONFile(_userConfig, _userConfigFilename);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ class HifiConfigVariantMap {
|
|||
public:
|
||||
static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList);
|
||||
|
||||
void loadConfig(const QStringList& argumentList);
|
||||
void loadConfig();
|
||||
|
||||
const QVariant value(const QString& key) const { return _userConfig.value(key); }
|
||||
QVariant* valueForKeyPath(const QString& keyPath, bool shouldCreateIfMissing = false)
|
||||
|
@ -30,6 +30,7 @@ public:
|
|||
QVariantMap& getConfig() { return _userConfig; }
|
||||
|
||||
const QString& getUserConfigFilename() const { return _userConfigFilename; }
|
||||
void setUserConfigFilename(const QString& filename) { _userConfigFilename = filename; }
|
||||
private:
|
||||
QString _userConfigFilename;
|
||||
|
||||
|
|
|
@ -33,17 +33,12 @@ LogHandler& LogHandler::getInstance() {
|
|||
}
|
||||
|
||||
LogHandler::LogHandler() {
|
||||
// when the log handler is first setup we should print our timezone
|
||||
QString timezoneString = "Time zone: " + QDateTime::currentDateTime().toString("t");
|
||||
printMessage(LogMsgType::LogInfo, QMessageLogContext(), timezoneString);
|
||||
|
||||
// make sure we setup the repeated message flusher, but do it on the LogHandler thread
|
||||
QMetaObject::invokeMethod(this, "setupRepeatedMessageFlusher");
|
||||
}
|
||||
|
||||
LogHandler::~LogHandler() {
|
||||
flushRepeatedMessages();
|
||||
printMessage(LogMsgType::LogDebug, QMessageLogContext(), "LogHandler shutdown.");
|
||||
}
|
||||
|
||||
const char* stringForLogType(LogMsgType msgType) {
|
||||
|
|
|
@ -90,7 +90,6 @@ const QString& PathUtils::resourcesPath() {
|
|||
staticResourcePath = projectRootPath() + "/interface/resources/";
|
||||
}
|
||||
#endif
|
||||
qDebug() << "Resource path resolved to " << staticResourcePath;
|
||||
});
|
||||
return staticResourcePath;
|
||||
}
|
||||
|
@ -105,7 +104,6 @@ const QString& PathUtils::resourcesUrl() {
|
|||
staticResourcePath = QUrl::fromLocalFile(projectRootPath() + "/interface/resources/").toString();
|
||||
}
|
||||
#endif
|
||||
qDebug() << "Resource url resolved to " << staticResourcePath;
|
||||
});
|
||||
return staticResourcePath;
|
||||
}
|
||||
|
|
|
@ -14,20 +14,11 @@ import "configSlider"
|
|||
|
||||
Item {
|
||||
id: root
|
||||
property var config: Render.getConfig("RenderMainView.Bloom")
|
||||
property var configThreshold: Render.getConfig("RenderMainView.BloomThreshold")
|
||||
property var configDebug: Render.getConfig("RenderMainView.DebugBloom")
|
||||
|
||||
Column {
|
||||
spacing: 8
|
||||
|
||||
CheckBox {
|
||||
text: "Enable"
|
||||
checked: root.config["enabled"]
|
||||
onCheckedChanged: {
|
||||
root.config["enabled"] = checked;
|
||||
}
|
||||
}
|
||||
GroupBox {
|
||||
title: "Debug"
|
||||
Row {
|
||||
|
@ -88,35 +79,5 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
ConfigSlider {
|
||||
label: "Intensity"
|
||||
integral: false
|
||||
config: root.config
|
||||
property: "intensity"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
height:38
|
||||
}
|
||||
ConfigSlider {
|
||||
label: "Size"
|
||||
integral: false
|
||||
config: root.config
|
||||
property: "size"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
height:38
|
||||
}
|
||||
ConfigSlider {
|
||||
label: "Threshold"
|
||||
integral: false
|
||||
config: root.configThreshold
|
||||
property: "threshold"
|
||||
max: 2.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
height:38
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ Column {
|
|||
|
||||
Component.onCompleted: {
|
||||
sceneOctree.enabled = true;
|
||||
itemSelection.enabled = true;
|
||||
itemSelection.enabled = true;
|
||||
sceneOctree.showVisibleCells = false;
|
||||
sceneOctree.showEmptyCells = false;
|
||||
itemSelection.showInsideItems = false;
|
||||
|
@ -29,9 +29,9 @@ Column {
|
|||
}
|
||||
Component.onDestruction: {
|
||||
sceneOctree.enabled = false;
|
||||
itemSelection.enabled = false;
|
||||
itemSelection.enabled = false;
|
||||
Render.getConfig("RenderMainView.FetchSceneSelection").freezeFrustum = false;
|
||||
Render.getConfig("RenderMainView.CullSceneSelection").freezeFrustum = false;
|
||||
Render.getConfig("RenderMainView.CullSceneSelection").freezeFrustum = false;
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
|
@ -44,7 +44,7 @@ Column {
|
|||
CheckBox {
|
||||
text: "Freeze Culling Frustum"
|
||||
checked: false
|
||||
onCheckedChanged: {
|
||||
onCheckedChanged: {
|
||||
Render.getConfig("RenderMainView.FetchSceneSelection").freezeFrustum = checked;
|
||||
Render.getConfig("RenderMainView.CullSceneSelection").freezeFrustum = checked;
|
||||
}
|
||||
|
@ -88,15 +88,19 @@ Column {
|
|||
text: "Partial Sub-cell Items"
|
||||
checked: false
|
||||
onCheckedChanged: { root.itemSelection.showPartialSubcellItems = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
title: "Render Items"
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
Column{
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
Repeater {
|
||||
model: [ "Opaque:RenderMainView.DrawOpaqueDeferred", "Transparent:RenderMainView.DrawTransparentDeferred", "Light:RenderMainView.DrawLight",
|
||||
"Opaque Overlays:RenderMainView.DrawOverlay3DOpaque", "Transparent Overlays:RenderMainView.DrawOverlay3DTransparent" ]
|
||||
|
|
|
@ -15,6 +15,6 @@ var window = new OverlayWindow({
|
|||
title: 'Bloom',
|
||||
source: qml,
|
||||
width: 285,
|
||||
height: 210,
|
||||
height: 40,
|
||||
});
|
||||
window.closed.connect(function() { Script.stop(); });
|
|
@ -38,30 +38,34 @@ getGrabPointSphereOffset = function(handController, ignoreSensorToWorldScale) {
|
|||
getControllerWorldLocation = function (handController, doOffset) {
|
||||
var orientation;
|
||||
var position;
|
||||
var pose = Controller.getPoseValue(handController);
|
||||
var valid = pose.valid;
|
||||
var controllerJointIndex;
|
||||
if (pose.valid) {
|
||||
if (handController === Controller.Standard.RightHand) {
|
||||
controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND");
|
||||
} else {
|
||||
controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
|
||||
}
|
||||
orientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(controllerJointIndex));
|
||||
position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(controllerJointIndex)));
|
||||
var valid = false;
|
||||
|
||||
if (handController >= 0) {
|
||||
var pose = Controller.getPoseValue(handController);
|
||||
valid = pose.valid;
|
||||
var controllerJointIndex;
|
||||
if (pose.valid) {
|
||||
if (handController === Controller.Standard.RightHand) {
|
||||
controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND");
|
||||
} else {
|
||||
controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
|
||||
}
|
||||
orientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(controllerJointIndex));
|
||||
position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(controllerJointIndex)));
|
||||
|
||||
// add to the real position so the grab-point is out in front of the hand, a bit
|
||||
if (doOffset) {
|
||||
var offset = getGrabPointSphereOffset(handController);
|
||||
position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, offset));
|
||||
}
|
||||
// add to the real position so the grab-point is out in front of the hand, a bit
|
||||
if (doOffset) {
|
||||
var offset = getGrabPointSphereOffset(handController);
|
||||
position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, offset));
|
||||
}
|
||||
|
||||
} else if (!HMD.isHandControllerAvailable()) {
|
||||
// NOTE: keep this offset in sync with scripts/system/controllers/handControllerPointer.js:493
|
||||
var VERTICAL_HEAD_LASER_OFFSET = 0.1 * MyAvatar.sensorToWorldScale;
|
||||
position = Vec3.sum(Camera.position, Vec3.multiplyQbyV(Camera.orientation, {x: 0, y: VERTICAL_HEAD_LASER_OFFSET, z: 0}));
|
||||
orientation = Quat.multiply(Camera.orientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 }));
|
||||
valid = true;
|
||||
} else if (!HMD.isHandControllerAvailable()) {
|
||||
// NOTE: keep this offset in sync with scripts/system/controllers/handControllerPointer.js:493
|
||||
var VERTICAL_HEAD_LASER_OFFSET = 0.1 * MyAvatar.sensorToWorldScale;
|
||||
position = Vec3.sum(Camera.position, Vec3.multiplyQbyV(Camera.orientation, {x: 0, y: VERTICAL_HEAD_LASER_OFFSET, z: 0}));
|
||||
orientation = Quat.multiply(Camera.orientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 }));
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {position: position,
|
||||
|
|
|
@ -5,7 +5,7 @@ project(${TARGET_NAME})
|
|||
SET (CMAKE_AUTOUIC ON)
|
||||
SET (CMAKE_AUTOMOC ON)
|
||||
|
||||
setup_hifi_project (Core Widgets Network)
|
||||
setup_hifi_project (Core Widgets Network Xml)
|
||||
link_hifi_libraries ()
|
||||
|
||||
# FIX: Qt was built with -reduce-relocations
|
||||
|
@ -18,7 +18,7 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR})
|
|||
include_directories (${Qt5Core_INCLUDE_DIRS})
|
||||
include_directories (${Qt5Widgets_INCLUDE_DIRS})
|
||||
|
||||
set (QT_LIBRARIES Qt5::Core Qt5::Widgets)
|
||||
set (QT_LIBRARIES Qt5::Core Qt5::Widgets QT::Gui Qt5::Xml)
|
||||
|
||||
if (WIN32)
|
||||
# Do not show Console
|
||||
|
|
|
@ -24,7 +24,7 @@ extern AutoTester* autoTester;
|
|||
#include <math.h>
|
||||
|
||||
Test::Test() {
|
||||
mismatchWindow.setModal(true);
|
||||
_mismatchWindow.setModal(true);
|
||||
|
||||
if (autoTester) {
|
||||
autoTester->setUserText("highfidelity");
|
||||
|
@ -34,35 +34,35 @@ Test::Test() {
|
|||
|
||||
bool Test::createTestResultsFolderPath(const QString& directory) {
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT);
|
||||
QDir testResultsFolder(testResultsFolderPath);
|
||||
_testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT);
|
||||
QDir testResultsFolder(_testResultsFolderPath);
|
||||
|
||||
// Create a new test results folder
|
||||
return QDir().mkdir(testResultsFolderPath);
|
||||
return QDir().mkdir(_testResultsFolderPath);
|
||||
}
|
||||
|
||||
void Test::zipAndDeleteTestResultsFolder() {
|
||||
QString zippedResultsFileName { testResultsFolderPath + ".zip" };
|
||||
QString zippedResultsFileName { _testResultsFolderPath + ".zip" };
|
||||
QFileInfo fileInfo(zippedResultsFileName);
|
||||
if (!fileInfo.exists()) {
|
||||
QFile::remove(zippedResultsFileName);
|
||||
}
|
||||
|
||||
QDir testResultsFolder(testResultsFolderPath);
|
||||
QDir testResultsFolder(_testResultsFolderPath);
|
||||
if (!testResultsFolder.isEmpty()) {
|
||||
JlCompress::compressDir(testResultsFolderPath + ".zip", testResultsFolderPath);
|
||||
JlCompress::compressDir(_testResultsFolderPath + ".zip", _testResultsFolderPath);
|
||||
}
|
||||
|
||||
testResultsFolder.removeRecursively();
|
||||
|
||||
//In all cases, for the next evaluation
|
||||
testResultsFolderPath = "";
|
||||
index = 1;
|
||||
_testResultsFolderPath = "";
|
||||
_index = 1;
|
||||
}
|
||||
|
||||
bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar) {
|
||||
progressBar->setMinimum(0);
|
||||
progressBar->setMaximum(expectedImagesFullFilenames.length() - 1);
|
||||
progressBar->setMaximum(_expectedImagesFullFilenames.length() - 1);
|
||||
progressBar->setValue(0);
|
||||
progressBar->setVisible(true);
|
||||
|
||||
|
@ -70,10 +70,10 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
|
|||
// Quit loop if user has aborted due to a failed test.
|
||||
bool success{ true };
|
||||
bool keepOn{ true };
|
||||
for (int i = 0; keepOn && i < expectedImagesFullFilenames.length(); ++i) {
|
||||
for (int i = 0; keepOn && i < _expectedImagesFullFilenames.length(); ++i) {
|
||||
// First check that images are the same size
|
||||
QImage resultImage(resultImagesFullFilenames[i]);
|
||||
QImage expectedImage(expectedImagesFullFilenames[i]);
|
||||
QImage resultImage(_resultImagesFullFilenames[i]);
|
||||
QImage expectedImage(_expectedImagesFullFilenames[i]);
|
||||
|
||||
double similarityIndex; // in [-1.0 .. 1.0], where 1.0 means images are identical
|
||||
|
||||
|
@ -82,30 +82,30 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
|
|||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Images are not the same size");
|
||||
similarityIndex = -100.0;
|
||||
} else {
|
||||
similarityIndex = imageComparer.compareImages(resultImage, expectedImage);
|
||||
similarityIndex = _imageComparer.compareImages(resultImage, expectedImage);
|
||||
}
|
||||
|
||||
if (similarityIndex < THRESHOLD) {
|
||||
TestFailure testFailure = TestFailure{
|
||||
(float)similarityIndex,
|
||||
expectedImagesFullFilenames[i].left(expectedImagesFullFilenames[i].lastIndexOf("/") + 1), // path to the test (including trailing /)
|
||||
QFileInfo(expectedImagesFullFilenames[i].toStdString().c_str()).fileName(), // filename of expected image
|
||||
QFileInfo(resultImagesFullFilenames[i].toStdString().c_str()).fileName() // filename of result image
|
||||
_expectedImagesFullFilenames[i].left(_expectedImagesFullFilenames[i].lastIndexOf("/") + 1), // path to the test (including trailing /)
|
||||
QFileInfo(_expectedImagesFullFilenames[i].toStdString().c_str()).fileName(), // filename of expected image
|
||||
QFileInfo(_resultImagesFullFilenames[i].toStdString().c_str()).fileName() // filename of result image
|
||||
};
|
||||
|
||||
mismatchWindow.setTestFailure(testFailure);
|
||||
_mismatchWindow.setTestFailure(testFailure);
|
||||
|
||||
if (!isInteractiveMode) {
|
||||
appendTestResultsToFile(testResultsFolderPath, testFailure, mismatchWindow.getComparisonImage());
|
||||
appendTestResultsToFile(_testResultsFolderPath, testFailure, _mismatchWindow.getComparisonImage());
|
||||
success = false;
|
||||
} else {
|
||||
mismatchWindow.exec();
|
||||
_mismatchWindow.exec();
|
||||
|
||||
switch (mismatchWindow.getUserResponse()) {
|
||||
switch (_mismatchWindow.getUserResponse()) {
|
||||
case USER_RESPONSE_PASS:
|
||||
break;
|
||||
case USE_RESPONSE_FAIL:
|
||||
appendTestResultsToFile(testResultsFolderPath, testFailure, mismatchWindow.getComparisonImage());
|
||||
appendTestResultsToFile(_testResultsFolderPath, testFailure, _mismatchWindow.getComparisonImage());
|
||||
success = false;
|
||||
break;
|
||||
case USER_RESPONSE_ABORT:
|
||||
|
@ -126,20 +126,20 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
|
|||
return success;
|
||||
}
|
||||
|
||||
void Test::appendTestResultsToFile(const QString& testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
|
||||
if (!QDir().exists(testResultsFolderPath)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + testResultsFolderPath + " not found");
|
||||
void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
|
||||
if (!QDir().exists(_testResultsFolderPath)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + _testResultsFolderPath + " not found");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QString err = QString::number(testFailure._error).left(6);
|
||||
|
||||
QString failureFolderPath { testResultsFolderPath + "/" + err + "-Failure_" + QString::number(index) + "--" + testFailure._actualImageFilename.left(testFailure._actualImageFilename.length() - 4) };
|
||||
QString failureFolderPath { _testResultsFolderPath + "/" + err + "-Failure_" + QString::number(_index) + "--" + testFailure._actualImageFilename.left(testFailure._actualImageFilename.length() - 4) };
|
||||
if (!QDir().mkdir(failureFolderPath)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create folder " + failureFolderPath);
|
||||
exit(-1);
|
||||
}
|
||||
++index;
|
||||
++_index;
|
||||
|
||||
QFile descriptionFile(failureFolderPath + "/" + TEST_RESULTS_FILENAME);
|
||||
if (!descriptionFile.open(QIODevice::ReadWrite)) {
|
||||
|
@ -152,7 +152,7 @@ void Test::appendTestResultsToFile(const QString& testResultsFolderPath, TestFai
|
|||
stream << "Test failed in folder " << testFailure._pathname.left(testFailure._pathname.length() - 1) << endl; // remove trailing '/'
|
||||
stream << "Expected image was " << testFailure._expectedImageFilename << endl;
|
||||
stream << "Actual image was " << testFailure._actualImageFilename << endl;
|
||||
stream << "Similarity index was " << testFailure._error << endl;
|
||||
stream << "Similarity _index was " << testFailure._error << endl;
|
||||
|
||||
descriptionFile.close();
|
||||
|
||||
|
@ -180,26 +180,26 @@ void Test::appendTestResultsToFile(const QString& testResultsFolderPath, TestFai
|
|||
void Test::startTestsEvaluation(const QString& testFolder, const QString& branchFromCommandLine, const QString& userFromCommandLine) {
|
||||
if (testFolder.isNull()) {
|
||||
// Get list of JPEG images in folder, sorted by name
|
||||
QString previousSelection = snapshotDirectory;
|
||||
QString previousSelection = _snapshotDirectory;
|
||||
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
|
||||
if (!parent.isNull() && parent.right(1) != "/") {
|
||||
parent += "/";
|
||||
}
|
||||
snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", parent,
|
||||
_snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", parent,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (snapshotDirectory == "") {
|
||||
snapshotDirectory = previousSelection;
|
||||
if (_snapshotDirectory == "") {
|
||||
_snapshotDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
snapshotDirectory = testFolder;
|
||||
exitWhenComplete = true;
|
||||
_snapshotDirectory = testFolder;
|
||||
_exitWhenComplete = true;
|
||||
}
|
||||
|
||||
// Quit if test results folder could not be created
|
||||
if (!createTestResultsFolderPath(snapshotDirectory)) {
|
||||
if (!createTestResultsFolderPath(_snapshotDirectory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -207,20 +207,20 @@ void Test::startTestsEvaluation(const QString& testFolder, const QString& branch
|
|||
// The expected images are represented as a URL to enable download from GitHub
|
||||
// Images that are in the wrong format are ignored.
|
||||
|
||||
QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", snapshotDirectory);
|
||||
QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", _snapshotDirectory);
|
||||
QStringList expectedImagesURLs;
|
||||
|
||||
resultImagesFullFilenames.clear();
|
||||
expectedImagesFilenames.clear();
|
||||
expectedImagesFullFilenames.clear();
|
||||
_resultImagesFullFilenames.clear();
|
||||
_expectedImagesFilenames.clear();
|
||||
_expectedImagesFullFilenames.clear();
|
||||
|
||||
QString branch = (branchFromCommandLine.isNull()) ? autoTester->getSelectedBranch() : branchFromCommandLine;
|
||||
QString user = (userFromCommandLine.isNull()) ? autoTester->getSelectedUser() : userFromCommandLine;
|
||||
|
||||
foreach(QString currentFilename, sortedTestResultsFilenames) {
|
||||
QString fullCurrentFilename = snapshotDirectory + "/" + currentFilename;
|
||||
QString fullCurrentFilename = _snapshotDirectory + "/" + currentFilename;
|
||||
if (isInSnapshotFilenameFormat("png", currentFilename)) {
|
||||
resultImagesFullFilenames << fullCurrentFilename;
|
||||
_resultImagesFullFilenames << fullCurrentFilename;
|
||||
|
||||
QString expectedImagePartialSourceDirectory = getExpectedImagePartialSourceDirectory(currentFilename);
|
||||
|
||||
|
@ -237,12 +237,12 @@ void Test::startTestsEvaluation(const QString& testFolder, const QString& branch
|
|||
// The image retrieved from GitHub needs a unique name
|
||||
QString expectedImageFilename = currentFilename.replace("/", "_").replace(".png", "_EI.png");
|
||||
|
||||
expectedImagesFilenames << expectedImageFilename;
|
||||
expectedImagesFullFilenames << snapshotDirectory + "/" + expectedImageFilename;
|
||||
_expectedImagesFilenames << expectedImageFilename;
|
||||
_expectedImagesFullFilenames << _snapshotDirectory + "/" + expectedImageFilename;
|
||||
}
|
||||
}
|
||||
|
||||
autoTester->downloadImages(expectedImagesURLs, snapshotDirectory, expectedImagesFilenames);
|
||||
autoTester->downloadImages(expectedImagesURLs, _snapshotDirectory, _expectedImagesFilenames);
|
||||
}
|
||||
|
||||
void Test::finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar) {
|
||||
|
@ -258,7 +258,7 @@ void Test::finishTestsEvaluation(bool isRunningFromCommandline, bool interactive
|
|||
|
||||
zipAndDeleteTestResultsFolder();
|
||||
|
||||
if (exitWhenComplete) {
|
||||
if (_exitWhenComplete) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
@ -310,46 +310,46 @@ void Test::includeTest(QTextStream& textStream, const QString& testPathname) {
|
|||
// This script will run all text.js scripts in every applicable sub-folder
|
||||
void Test::createRecursiveScript() {
|
||||
// Select folder to start recursing from
|
||||
QString previousSelection = testDirectory;
|
||||
QString previousSelection = _testDirectory;
|
||||
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
|
||||
if (!parent.isNull() && parent.right(1) != "/") {
|
||||
parent += "/";
|
||||
}
|
||||
|
||||
testDirectory =
|
||||
_testDirectory =
|
||||
QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", parent,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testDirectory == "") {
|
||||
testDirectory = previousSelection;
|
||||
if (_testDirectory == "") {
|
||||
_testDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
createRecursiveScript(testDirectory, true);
|
||||
createRecursiveScript(_testDirectory, true);
|
||||
}
|
||||
|
||||
// This method creates a `testRecursive.js` script in every sub-folder.
|
||||
void Test::createAllRecursiveScripts() {
|
||||
// Select folder to start recursing from
|
||||
QString previousSelection = testsRootDirectory;
|
||||
QString previousSelection = _testsRootDirectory;
|
||||
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
|
||||
if (!parent.isNull() && parent.right(1) != "/") {
|
||||
parent += "/";
|
||||
}
|
||||
|
||||
testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts",
|
||||
_testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts",
|
||||
parent, QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testsRootDirectory == "") {
|
||||
testsRootDirectory = previousSelection;
|
||||
if (_testsRootDirectory == "") {
|
||||
_testsRootDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
createRecursiveScript(testsRootDirectory, false);
|
||||
createRecursiveScript(_testsRootDirectory, false);
|
||||
|
||||
QDirIterator it(testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
@ -477,42 +477,42 @@ void Test::createRecursiveScript(const QString& topLevelDirectory, bool interact
|
|||
void Test::createTests() {
|
||||
// Rename files sequentially, as ExpectedResult_00000.jpeg, ExpectedResult_00001.jpg and so on
|
||||
// Any existing expected result images will be deleted
|
||||
QString previousSelection = snapshotDirectory;
|
||||
QString previousSelection = _snapshotDirectory;
|
||||
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
|
||||
if (!parent.isNull() && parent.right(1) != "/") {
|
||||
parent += "/";
|
||||
}
|
||||
|
||||
snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", parent,
|
||||
_snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", parent,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (snapshotDirectory == "") {
|
||||
snapshotDirectory = previousSelection;
|
||||
if (_snapshotDirectory == "") {
|
||||
_snapshotDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
previousSelection = testsRootDirectory;
|
||||
previousSelection = _testsRootDirectory;
|
||||
parent = previousSelection.left(previousSelection.lastIndexOf('/'));
|
||||
if (!parent.isNull() && parent.right(1) != "/") {
|
||||
parent += "/";
|
||||
}
|
||||
|
||||
testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select test root folder", parent,
|
||||
_testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select test root folder", parent,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testsRootDirectory == "") {
|
||||
testsRootDirectory = previousSelection;
|
||||
if (_testsRootDirectory == "") {
|
||||
_testsRootDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("png", snapshotDirectory);
|
||||
QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("png", _snapshotDirectory);
|
||||
|
||||
int i = 1;
|
||||
const int maxImages = pow(10, NUM_DIGITS);
|
||||
foreach (QString currentFilename, sortedImageFilenames) {
|
||||
QString fullCurrentFilename = snapshotDirectory + "/" + currentFilename;
|
||||
QString fullCurrentFilename = _snapshotDirectory + "/" + currentFilename;
|
||||
if (isInSnapshotFilenameFormat("png", currentFilename)) {
|
||||
if (i >= maxImages) {
|
||||
QMessageBox::critical(0, "Error", "More than " + QString::number(maxImages) + " images not supported");
|
||||
|
@ -522,17 +522,17 @@ void Test::createTests() {
|
|||
// Path to test is extracted from the file name
|
||||
// Example:
|
||||
// filename is tests.engine.interaction.pointer.laser.distanceScaleEnd.00000.jpg
|
||||
// path is <testDirectory>/engine/interaction/pointer/laser/distanceScaleEnd
|
||||
// path is <_testDirectory>/engine/interaction/pointer/laser/distanceScaleEnd
|
||||
//
|
||||
// Note: we don't use the first part and the last 2 parts of the filename at this stage
|
||||
//
|
||||
QStringList pathParts = currentFilename.split(".");
|
||||
QString fullNewFileName = testsRootDirectory;
|
||||
QString fullNewFileName = _testsRootDirectory;
|
||||
for (int j = 1; j < pathParts.size() - 2; ++j) {
|
||||
fullNewFileName += "/" + pathParts[j];
|
||||
}
|
||||
|
||||
// The image index is the penultimate component of the path parts (the last being the file extension)
|
||||
// The image _index is the penultimate component of the path parts (the last being the file extension)
|
||||
QString newFilename = "ExpectedImage_" + pathParts[pathParts.size() - 2].rightJustified(5, '0') + ".png";
|
||||
fullNewFileName += "/" + newFilename;
|
||||
|
||||
|
@ -621,51 +621,51 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
|
|||
// The folder selected must contain a script named "test.js", the file produced is named "test.md"
|
||||
void Test::createMDFile() {
|
||||
// Folder selection
|
||||
QString previousSelection = testDirectory;
|
||||
QString previousSelection = _testDirectory;
|
||||
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
|
||||
if (!parent.isNull() && parent.right(1) != "/") {
|
||||
parent += "/";
|
||||
}
|
||||
|
||||
testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test", parent,
|
||||
_testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test", parent,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testDirectory == "") {
|
||||
testDirectory = previousSelection;
|
||||
if (_testDirectory == "") {
|
||||
_testDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
createMDFile(testDirectory);
|
||||
createMDFile(_testDirectory);
|
||||
|
||||
QMessageBox::information(0, "Success", "MD file has been created");
|
||||
}
|
||||
|
||||
void Test::createAllMDFiles() {
|
||||
// Select folder to start recursing from
|
||||
QString previousSelection = testsRootDirectory;
|
||||
QString previousSelection = _testsRootDirectory;
|
||||
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
|
||||
if (!parent.isNull() && parent.right(1) != "/") {
|
||||
parent += "/";
|
||||
}
|
||||
|
||||
testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the MD files", parent,
|
||||
_testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the MD files", parent,
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testsRootDirectory == "") {
|
||||
testsRootDirectory = previousSelection;
|
||||
if (_testsRootDirectory == "") {
|
||||
_testsRootDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
// First test if top-level folder has a test.js file
|
||||
const QString testPathname{ testsRootDirectory + "/" + TEST_FILENAME };
|
||||
const QString testPathname{ _testsRootDirectory + "/" + TEST_FILENAME };
|
||||
QFileInfo fileInfo(testPathname);
|
||||
if (fileInfo.exists()) {
|
||||
createMDFile(testsRootDirectory);
|
||||
createMDFile(_testsRootDirectory);
|
||||
}
|
||||
|
||||
QDirIterator it(testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
@ -685,9 +685,9 @@ void Test::createAllMDFiles() {
|
|||
QMessageBox::information(0, "Success", "MD files have been created");
|
||||
}
|
||||
|
||||
void Test::createMDFile(const QString& testDirectory) {
|
||||
void Test::createMDFile(const QString& _testDirectory) {
|
||||
// Verify folder contains test.js file
|
||||
QString testFileName(testDirectory + "/" + TEST_FILENAME);
|
||||
QString testFileName(_testDirectory + "/" + TEST_FILENAME);
|
||||
QFileInfo testFileInfo(testFileName);
|
||||
if (!testFileInfo.exists()) {
|
||||
QMessageBox::critical(0, "Error", "Could not find file: " + TEST_FILENAME);
|
||||
|
@ -696,7 +696,7 @@ void Test::createMDFile(const QString& testDirectory) {
|
|||
|
||||
ExtractedText testScriptLines = getTestScriptLines(testFileName);
|
||||
|
||||
QString mdFilename(testDirectory + "/" + "test.md");
|
||||
QString mdFilename(_testDirectory + "/" + "test.md");
|
||||
QFile mdFile(mdFilename);
|
||||
if (!mdFile.open(QIODevice::WriteOnly)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
|
||||
|
@ -710,7 +710,7 @@ void Test::createMDFile(const QString& testDirectory) {
|
|||
stream << "# " << testName << "\n";
|
||||
|
||||
// Find the relevant part of the path to the test (i.e. from "tests" down
|
||||
QString partialPath = extractPathFromTestsDown(testDirectory);
|
||||
QString partialPath = extractPathFromTestsDown(_testDirectory);
|
||||
|
||||
stream << "## Run this script URL: [Manual](./test.js?raw=true) [Auto](./testAuto.js?raw=true)(from menu/Edit/Open and Run scripts from URL...)." << "\n\n";
|
||||
|
||||
|
@ -734,23 +734,23 @@ void Test::createMDFile(const QString& testDirectory) {
|
|||
}
|
||||
|
||||
void Test::createTestsOutline() {
|
||||
QString previousSelection = testDirectory;
|
||||
QString previousSelection = _testDirectory;
|
||||
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
|
||||
if (!parent.isNull() && parent.right(1) != "/") {
|
||||
parent += "/";
|
||||
}
|
||||
|
||||
testDirectory =
|
||||
_testDirectory =
|
||||
QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", parent, QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (testDirectory == "") {
|
||||
testDirectory = previousSelection;
|
||||
if (_testDirectory == "") {
|
||||
_testDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
const QString testsOutlineFilename { "testsOutline.md" };
|
||||
QString mdFilename(testDirectory + "/" + testsOutlineFilename);
|
||||
QString mdFilename(_testDirectory + "/" + testsOutlineFilename);
|
||||
QFile mdFile(mdFilename);
|
||||
if (!mdFile.open(QIODevice::WriteOnly)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
|
||||
|
@ -764,10 +764,10 @@ void Test::createTestsOutline() {
|
|||
stream << "Directories with an appended (*) have an automatic test\n\n";
|
||||
|
||||
// We need to know our current depth, as this isn't given by QDirIterator
|
||||
int rootDepth { testDirectory.count('/') };
|
||||
int rootDepth { _testDirectory.count('/') };
|
||||
|
||||
// Each test is shown as the folder name linking to the matching GitHub URL, and the path to the associated test.md file
|
||||
QDirIterator it(testDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(_testDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
@ -821,12 +821,51 @@ void Test::createTestsOutline() {
|
|||
QMessageBox::information(0, "Success", "Test outline file " + testsOutlineFilename + " has been created");
|
||||
}
|
||||
|
||||
void Test::createTestRailTestCases() {
|
||||
QString previousSelection = _testDirectory;
|
||||
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
|
||||
if (!parent.isNull() && parent.right(1) != "/") {
|
||||
parent += "/";
|
||||
}
|
||||
|
||||
_testDirectory =
|
||||
QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", parent, QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then restore previous selection and return
|
||||
if (_testDirectory == "") {
|
||||
_testDirectory = previousSelection;
|
||||
return;
|
||||
}
|
||||
|
||||
QString outputDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select a folder to store generated files in",
|
||||
nullptr, QFileDialog::ShowDirsOnly);
|
||||
|
||||
// If user cancelled then return
|
||||
if (outputDirectory == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_testRailCreateMode == PYTHON) {
|
||||
_testRailInterface.createTestSuitePython(_testDirectory, outputDirectory, autoTester->getSelectedUser(),
|
||||
autoTester->getSelectedBranch());
|
||||
} else {
|
||||
_testRailInterface.createTestSuiteXML(_testDirectory, outputDirectory, autoTester->getSelectedUser(),
|
||||
autoTester->getSelectedBranch());
|
||||
}
|
||||
}
|
||||
|
||||
void Test::createTestRailRun() {
|
||||
QString outputDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select a folder to store generated files in",
|
||||
nullptr, QFileDialog::ShowDirsOnly);
|
||||
_testRailInterface.createTestRailRun(outputDirectory);
|
||||
}
|
||||
|
||||
QStringList Test::createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory) {
|
||||
imageDirectory = QDir(pathToImageDirectory);
|
||||
_imageDirectory = QDir(pathToImageDirectory);
|
||||
QStringList nameFilters;
|
||||
nameFilters << "*." + imageFormat;
|
||||
|
||||
return imageDirectory.entryList(nameFilters, QDir::Files, QDir::Name);
|
||||
return _imageDirectory.entryList(nameFilters, QDir::Files, QDir::Name);
|
||||
}
|
||||
|
||||
// Snapshots are files in the following format:
|
||||
|
@ -889,3 +928,7 @@ QString Test::getExpectedImagePartialSourceDirectory(const QString& filename) {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Test::setTestRailCreateMode(TestRailCreateMode testRailCreateMode) {
|
||||
_testRailCreateMode = testRailCreateMode;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "ImageComparer.h"
|
||||
#include "ui/MismatchWindow.h"
|
||||
#include "TestRailInterface.h"
|
||||
|
||||
class Step {
|
||||
public:
|
||||
|
@ -33,6 +34,11 @@ public:
|
|||
StepList stepList;
|
||||
};
|
||||
|
||||
enum TestRailCreateMode {
|
||||
PYTHON,
|
||||
XML
|
||||
};
|
||||
|
||||
class Test {
|
||||
public:
|
||||
Test();
|
||||
|
@ -51,6 +57,9 @@ public:
|
|||
|
||||
void createTestsOutline();
|
||||
|
||||
void createTestRailTestCases();
|
||||
void createTestRailRun();
|
||||
|
||||
bool compareImageLists(bool isInteractiveMode, QProgressBar* progressBar);
|
||||
|
||||
QStringList createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory);
|
||||
|
@ -64,11 +73,15 @@ public:
|
|||
bool createTestResultsFolderPath(const QString& directory);
|
||||
void zipAndDeleteTestResultsFolder();
|
||||
|
||||
bool isAValidDirectory(const QString& pathname);
|
||||
static bool isAValidDirectory(const QString& pathname);
|
||||
QString extractPathFromTestsDown(const QString& fullPath);
|
||||
QString getExpectedImageDestinationDirectory(const QString& filename);
|
||||
QString getExpectedImagePartialSourceDirectory(const QString& filename);
|
||||
|
||||
ExtractedText getTestScriptLines(QString testFileName);
|
||||
|
||||
void setTestRailCreateMode(TestRailCreateMode testRailCreateMode);
|
||||
|
||||
private:
|
||||
const QString TEST_FILENAME { "test.js" };
|
||||
const QString TEST_RESULTS_FOLDER { "TestResults" };
|
||||
|
@ -76,14 +89,14 @@ private:
|
|||
|
||||
const double THRESHOLD{ 0.96 };
|
||||
|
||||
QDir imageDirectory;
|
||||
QDir _imageDirectory;
|
||||
|
||||
MismatchWindow mismatchWindow;
|
||||
MismatchWindow _mismatchWindow;
|
||||
|
||||
ImageComparer imageComparer;
|
||||
ImageComparer _imageComparer;
|
||||
|
||||
QString testResultsFolderPath;
|
||||
int index { 1 };
|
||||
QString _testResultsFolderPath;
|
||||
int _index { 1 };
|
||||
|
||||
// Expected images are in the format ExpectedImage_dddd.jpg (d == decimal digit)
|
||||
const int NUM_DIGITS { 5 };
|
||||
|
@ -93,28 +106,30 @@ private:
|
|||
// The first is the directory containing the test we are working with
|
||||
// The second is the root directory of all tests
|
||||
// The third contains the snapshots taken for test runs that need to be evaluated
|
||||
QString testDirectory;
|
||||
QString testsRootDirectory;
|
||||
QString snapshotDirectory;
|
||||
QString _testDirectory;
|
||||
QString _testsRootDirectory;
|
||||
QString _snapshotDirectory;
|
||||
|
||||
QStringList expectedImagesFilenames;
|
||||
QStringList expectedImagesFullFilenames;
|
||||
QStringList resultImagesFullFilenames;
|
||||
QStringList _expectedImagesFilenames;
|
||||
QStringList _expectedImagesFullFilenames;
|
||||
QStringList _resultImagesFullFilenames;
|
||||
|
||||
// Used for accessing GitHub
|
||||
const QString GIT_HUB_REPOSITORY{ "hifi_tests" };
|
||||
|
||||
const QString DATETIME_FORMAT{ "yyyy-MM-dd_hh-mm-ss" };
|
||||
|
||||
ExtractedText getTestScriptLines(QString testFileName);
|
||||
|
||||
// NOTE: these need to match the appropriate var's in autoTester.js
|
||||
// var advanceKey = "n";
|
||||
// var pathSeparator = ".";
|
||||
const QString ADVANCE_KEY{ "n" };
|
||||
const QString PATH_SEPARATOR{ "." };
|
||||
|
||||
bool exitWhenComplete{ false };
|
||||
bool _exitWhenComplete{ false };
|
||||
|
||||
TestRailInterface _testRailInterface;
|
||||
|
||||
TestRailCreateMode _testRailCreateMode { PYTHON };
|
||||
};
|
||||
|
||||
#endif // hifi_test_h
|
837
tools/auto-tester/src/TestRailInterface.cpp
Normal file
837
tools/auto-tester/src/TestRailInterface.cpp
Normal file
|
@ -0,0 +1,837 @@
|
|||
//
|
||||
// TestRailInterface.cpp
|
||||
//
|
||||
// Created by Nissim Hadar on 6 Jul 2018.
|
||||
// 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 "TestRailInterface.h"
|
||||
#include "Test.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
#include <QTextStream>
|
||||
|
||||
TestRailInterface::TestRailInterface() {
|
||||
_testRailTestCasesSelectorWindow.setURL("https://highfidelity.testrail.net");
|
||||
////_testRailTestCasesSelectorWindow.setURL("https://nissimhadar.testrail.io");
|
||||
_testRailTestCasesSelectorWindow.setUser("@highfidelity.io");
|
||||
////_testRailSelectorWindow.setUser("nissim.hadar@gmail.com");
|
||||
|
||||
_testRailTestCasesSelectorWindow.setProjectID(INTERFACE_PROJECT_ID);
|
||||
////_testRailSelectorWindow.setProject(1);
|
||||
|
||||
_testRailTestCasesSelectorWindow.setSuiteID(INTERFACE_SUITE_ID);
|
||||
|
||||
_testRailRunSelectorWindow.setURL("https://highfidelity.testrail.net");
|
||||
////_testRailRunSelectorWindow.setURL("https://nissimhadar.testrail.io");
|
||||
_testRailRunSelectorWindow.setUser("@highfidelity.io");
|
||||
////_testRailSelectorWindow.setUser("nissim.hadar@gmail.com");
|
||||
|
||||
_testRailRunSelectorWindow.setProjectID(INTERFACE_PROJECT_ID);
|
||||
////_testRailSelectorWindow.setProject(1);
|
||||
|
||||
_testRailRunSelectorWindow.setSuiteID(INTERFACE_SUITE_ID);
|
||||
}
|
||||
|
||||
QString TestRailInterface::getObject(const QString& path) {
|
||||
return path.right(path.length() - path.lastIndexOf("/") - 1);
|
||||
}
|
||||
|
||||
|
||||
bool TestRailInterface::setPythonCommand() {
|
||||
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);
|
||||
}
|
||||
_pythonCommand = _pythonPath + "/" + pythonExe;
|
||||
return true;
|
||||
} else {
|
||||
QMessageBox::critical(0, "PYTHON_PATH not defined",
|
||||
"Please set PYTHON_PATH to directory containing the Python executable");
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Creates the testrail.py script
|
||||
// This is the file linked to from http://docs.gurock.com/testrail-api2/bindings-python
|
||||
void TestRailInterface::createTestRailDotPyScript() {
|
||||
QFile file(_outputDirectory + "/testrail.py");
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not create 'testrail.py'");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
|
||||
stream << "#\n";
|
||||
stream << "# TestRail API binding for Python 3.x (API v2, available since \n";
|
||||
stream << "# TestRail 3.0)\n";
|
||||
stream << "#\n";
|
||||
stream << "# Learn more:\n";
|
||||
stream << "#\n";
|
||||
stream << "# http://docs.gurock.com/testrail-api2/start\n";
|
||||
stream << "# http://docs.gurock.com/testrail-api2/accessing\n";
|
||||
stream << "#\n";
|
||||
stream << "# Copyright Gurock Software GmbH. See license.md for details.\n";
|
||||
stream << "#\n";
|
||||
stream << "\n";
|
||||
stream << "import urllib.request, urllib.error\n";
|
||||
stream << "import json, base64\n";
|
||||
stream << "\n";
|
||||
stream << "class APIClient:\n";
|
||||
stream << "\tdef __init__(self, base_url):\n";
|
||||
stream << "\t\tself.user = ''\n";
|
||||
stream << "\t\tself.password = ''\n";
|
||||
stream << "\t\tif not base_url.endswith('/'):\n";
|
||||
stream << "\t\t\tbase_url += '/'\n";
|
||||
stream << "\t\tself.__url = base_url + 'index.php?/api/v2/'\n";
|
||||
stream << "\n";
|
||||
stream << "\t#\n";
|
||||
stream << "\t# Send Get\n";
|
||||
stream << "\t#\n";
|
||||
stream << "\t# Issues a GET request (read) against the API and returns the result\n";
|
||||
stream << "\t# (as Python dict).\n";
|
||||
stream << "\t#\n";
|
||||
stream << "\t# Arguments:\n";
|
||||
stream << "\t#\n";
|
||||
stream << "\t# uri The API method to call including parameters\n";
|
||||
stream << "\t# (e.g. get_case/1)\n";
|
||||
stream << "\t#\n";
|
||||
stream << "\tdef send_get(self, uri):\n";
|
||||
stream << "\t\treturn self.__send_request('GET', uri, None)\n";
|
||||
stream << "\n";
|
||||
stream << "\t#\n";
|
||||
stream << "\t# Send POST\n";
|
||||
stream << "\t#\n";
|
||||
stream << "\t# Issues a POST request (write) against the API and returns the result\n";
|
||||
stream << "\t# (as Python dict).\n";
|
||||
stream << "\t#\n";
|
||||
stream << "\t# Arguments:\n";
|
||||
stream << "\t#\n";
|
||||
stream << "\t# uri The API method to call including parameters\n";
|
||||
stream << "\t# (e.g. add_case/1)\n";
|
||||
stream << "\t# data The data to submit as part of the request (as\n";
|
||||
stream << "\t# Python dict, strings must be UTF-8 encoded)\n";
|
||||
stream << "\t#\n";
|
||||
stream << "\tdef send_post(self, uri, data):\n";
|
||||
stream << "\t\treturn self.__send_request('POST', uri, data)\n";
|
||||
stream << "\n";
|
||||
stream << "\tdef __send_request(self, method, uri, data):\n";
|
||||
stream << "\t\turl = self.__url + uri\n";
|
||||
stream << "\t\trequest = urllib.request.Request(url)\n";
|
||||
stream << "\t\tif (method == 'POST'):\n";
|
||||
stream << "\t\t\trequest.data = bytes(json.dumps(data), 'utf-8')\n";
|
||||
stream << "\t\tauth = str(\n";
|
||||
stream << "\t\t\tbase64.b64encode(\n";
|
||||
stream << "\t\t\t\tbytes('%s:%s' % (self.user, self.password), 'utf-8')\n";
|
||||
stream << "\t\t\t),\n";
|
||||
stream << "\t\t\t'ascii'\n";
|
||||
stream << "\t\t).strip()\n";
|
||||
stream << "\t\trequest.add_header('Authorization', 'Basic %s' % auth)\n";
|
||||
stream << "\t\trequest.add_header('Content-Type', 'application/json')\n";
|
||||
stream << "\n";
|
||||
stream << "\t\te = None\n";
|
||||
stream << "\t\ttry:\n";
|
||||
stream << "\t\t\tresponse = urllib.request.urlopen(request).read()\n";
|
||||
stream << "\t\texcept urllib.error.HTTPError as ex:\n";
|
||||
stream << "\t\t\tresponse = ex.read()\n";
|
||||
stream << "\t\t\te = ex\n";
|
||||
stream << "\n";
|
||||
stream << "\t\tif response:\n";
|
||||
stream << "\t\t\tresult = json.loads(response.decode())\n";
|
||||
stream << "\t\telse:\n";
|
||||
stream << "\t\t\tresult = {}\n";
|
||||
stream << "\n";
|
||||
stream << "\t\tif e != None:\n";
|
||||
stream << "\t\t\tif result and 'error' in result:\n";
|
||||
stream << "\t\t\t\terror = '\"' + result['error'] + '\"'\n";
|
||||
stream << "\t\t\telse:\n";
|
||||
stream << "\t\t\t\terror = 'No additional error message received'\n";
|
||||
stream << "\t\t\traise APIError('TestRail API returned HTTP %s (%s)' % \n";
|
||||
stream << "\t\t\t\t(e.code, error))\n";
|
||||
stream << "\n";
|
||||
stream << "\t\treturn result\n";
|
||||
stream << "\n";
|
||||
stream << "class APIError(Exception):\n";
|
||||
stream << "\tpass\n";
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Creates a Stack class
|
||||
void TestRailInterface::createStackDotPyScript() {
|
||||
QString filename = _outputDirectory + "/stack.py";
|
||||
if (QFile::exists(filename)) {
|
||||
QFile::remove(filename);
|
||||
}
|
||||
QFile file(filename);
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not create 'stack.py'");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
|
||||
stream << "class Stack:\n";
|
||||
|
||||
stream << "\tdef __init__(self):\n";
|
||||
stream << "\t\tself.items = []\n";
|
||||
stream << "\n";
|
||||
|
||||
stream << "\tdef is_empty(self):\n";
|
||||
stream << "\t\treturn self.items == []\n";
|
||||
stream << "\n";
|
||||
|
||||
stream << "\tdef push(self, item):\n";
|
||||
stream << "\t\tself.items.append(item)\n";
|
||||
stream << "\n";
|
||||
|
||||
stream << "\tdef pop(self):\n";
|
||||
stream << "\t\treturn self.items.pop()\n";
|
||||
stream << "\n";
|
||||
|
||||
stream << "\tdef peek(self):\n";
|
||||
stream << "\t\treturn self.items[len(self.items)-1]\n";
|
||||
stream << "\n";
|
||||
|
||||
stream << "\tdef size(self):\n";
|
||||
stream << "\t\treturn len(self.items)\n";
|
||||
stream << "\n";
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
void TestRailInterface::requestTestRailTestCasesDataFromUser() {
|
||||
// Make sure correct fields are enabled before calling
|
||||
_testRailTestCasesSelectorWindow.reset();
|
||||
_testRailTestCasesSelectorWindow.exec();
|
||||
|
||||
if (_testRailTestCasesSelectorWindow.getUserCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_url = _testRailTestCasesSelectorWindow.getURL() + "/";
|
||||
_user = _testRailTestCasesSelectorWindow.getUser();
|
||||
_password = _testRailTestCasesSelectorWindow.getPassword();
|
||||
////_password = "tutKA76";
|
||||
_projectID = QString::number(_testRailTestCasesSelectorWindow.getProjectID());
|
||||
_suiteID = QString::number(_testRailTestCasesSelectorWindow.getSuiteID());
|
||||
}
|
||||
|
||||
bool TestRailInterface::isAValidTestDirectory(const QString& directory) {
|
||||
if (Test::isAValidDirectory(directory)) {
|
||||
// Ignore the utils and preformance directories
|
||||
if (directory.right(QString("utils").length()) == "utils" ||
|
||||
directory.right(QString("performance").length()) == "performance") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TestRailInterface::processDirectoryPython(const QString& directory,
|
||||
QTextStream& stream,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub) {
|
||||
// Loop over all entries in directory
|
||||
QDirIterator it(directory.toStdString().c_str());
|
||||
while (it.hasNext()) {
|
||||
QString nextDirectory = it.next();
|
||||
|
||||
QString objectName = getObject(nextDirectory);
|
||||
|
||||
if (isAValidTestDirectory(nextDirectory)) {
|
||||
// The name of the section is the directory at the end of the path
|
||||
stream << "parent_id = parent_ids.peek()\n";
|
||||
stream << "data = { 'name': '" << objectName << "', 'suite_id': " + _suiteID + ", 'parent_id': parent_id }\n";
|
||||
|
||||
stream << "section = client.send_post('add_section/' + str(" << _projectID << "), data)\n";
|
||||
|
||||
// Now we push the parent_id, and recursively process each directory
|
||||
stream << "parent_ids.push(section['id'])\n\n";
|
||||
processDirectoryPython(nextDirectory, stream, userGitHub, branchGitHub);
|
||||
} else if (objectName == "test.js") {
|
||||
processTestPython(nextDirectory, stream, userGitHub, branchGitHub);
|
||||
}
|
||||
}
|
||||
|
||||
// pop the parent directory before leaving
|
||||
stream << "parent_ids.pop()\n\n";
|
||||
}
|
||||
|
||||
// A suite of TestRail test cases contains trees.
|
||||
// The nodes of the trees are sections
|
||||
// The leaves are the test cases
|
||||
//
|
||||
// Each node and leaf have an ID and a parent ID.
|
||||
// Therefore, the tree is built top-down, using a stack to store the IDs of each node
|
||||
//
|
||||
void TestRailInterface::createAddTestCasesPythonScript(const QString& testDirectory,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub) {
|
||||
QString filename = _outputDirectory + "/addTestCases.py";
|
||||
if (QFile::exists(filename)) {
|
||||
QFile::remove(filename);
|
||||
}
|
||||
QFile file(filename);
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not create 'addTestCases.py'");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
|
||||
// Code to access TestRail
|
||||
stream << "from testrail import *\n";
|
||||
stream << "client = APIClient('" << _url.toStdString().c_str() << "')\n";
|
||||
stream << "client.user = '" << _user << "'\n";
|
||||
stream << "client.password = '" << _password << "'\n\n";
|
||||
|
||||
stream << "from stack import *\n";
|
||||
stream << "parent_ids = Stack()\n\n";
|
||||
|
||||
// top-level section
|
||||
stream << "data = { 'name': '"
|
||||
<< "Test Suite - " << QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm") + "', "
|
||||
<< "'suite_id': " + _suiteID + "}\n";
|
||||
|
||||
stream << "section = client.send_post('add_section/' + str(" << _projectID << "), data)\n";
|
||||
|
||||
// Now we push the parent_id, and recursively process each directory
|
||||
stream << "parent_ids.push(section['id'])\n\n";
|
||||
processDirectoryPython(testDirectory, stream, userGitHub, branchGitHub);
|
||||
|
||||
file.close();
|
||||
|
||||
if (QMessageBox::Yes == QMessageBox(QMessageBox::Information, "Python script has been created",
|
||||
"Do you want to run the script and update TestRail?",
|
||||
QMessageBox::Yes | QMessageBox::No)
|
||||
.exec()) {
|
||||
QProcess* process = new QProcess();
|
||||
connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); });
|
||||
|
||||
connect(process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this,
|
||||
[=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); });
|
||||
|
||||
QStringList parameters = QStringList() << _outputDirectory + "/addTestCases.py";
|
||||
process->start(_pythonCommand, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
void TestRailInterface::updateMilestonesComboData(int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
// Quit if user has previously cancelled
|
||||
if (_testRailTestCasesSelectorWindow.getUserCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if process completed successfully
|
||||
if (exitStatus != QProcess::NormalExit) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not get milestones from TestRail");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Create map of milestones from the file created by the process
|
||||
_milestoneNames.clear();
|
||||
|
||||
QString filename = _outputDirectory + "/milestones.txt";
|
||||
if (!QFile::exists(filename)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not find milestones.txt in " + _outputDirectory);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not open " + _outputDirectory + "/milestones.txt");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
QString line = in.readLine();
|
||||
while (!line.isNull()) {
|
||||
QStringList words = line.split(' ');
|
||||
_milestones[words[0]] = words[1].toInt();
|
||||
_milestoneNames << words[0];
|
||||
|
||||
line = in.readLine();
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
// Update the combo
|
||||
_testRailTestCasesSelectorWindow.updateMilestonesComboBoxData(_milestoneNames);
|
||||
|
||||
_testRailTestCasesSelectorWindow.exec();
|
||||
|
||||
if (_testRailTestCasesSelectorWindow.getUserCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
createAddTestCasesPythonScript(_testDirectory, _userGitHub, _branchGitHub);
|
||||
}
|
||||
|
||||
void TestRailInterface::updateSectionsComboData(int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
// Quit if user has previously cancelled
|
||||
if (_testRailRunSelectorWindow.getUserCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if process completed successfully
|
||||
if (exitStatus != QProcess::NormalExit) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not get sections from TestRail");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Create map of sections from the file created by the process
|
||||
_sectionNames.clear();
|
||||
|
||||
QString filename = _outputDirectory + "/sections.txt";
|
||||
if (!QFile::exists(filename)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not find sections.txt in " + _outputDirectory);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not open " + _outputDirectory + "/sections.txt");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
QString line = in.readLine();
|
||||
while (!line.isNull()) {
|
||||
// The section name is all the words except for the last
|
||||
// The id is the last word
|
||||
QString section = line.left(line.lastIndexOf(" "));
|
||||
QString id = line.right(line.length() - line.lastIndexOf(" ") - 1);
|
||||
|
||||
_sections[section] = id.toInt();
|
||||
_sectionNames << section;
|
||||
|
||||
line = in.readLine();
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
// Update the combo
|
||||
_testRailRunSelectorWindow.updateSectionsComboBoxData(_sectionNames);
|
||||
|
||||
_testRailRunSelectorWindow.exec();
|
||||
|
||||
if (_testRailRunSelectorWindow.getUserCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
////createAddTestCasesPythonScript(_testDirectory, _userGitHub, _branchGitHub);
|
||||
}
|
||||
|
||||
void TestRailInterface::getMilestonesFromTestRail() {
|
||||
QString filename = _outputDirectory + "/getMilestones.py";
|
||||
if (QFile::exists(filename)) {
|
||||
QFile::remove(filename);
|
||||
}
|
||||
QFile file(filename);
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not create 'getMilestones.py'");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
|
||||
// Code to access TestRail
|
||||
stream << "from testrail import *\n";
|
||||
stream << "client = APIClient('" << _url.toStdString().c_str() << "')\n";
|
||||
stream << "client.user = '" << _user << "'\n";
|
||||
stream << "client.password = '" << _password << "'\n\n";
|
||||
|
||||
// Print the list of uncompleted milestones
|
||||
stream << "file = open('" + _outputDirectory + "/milestones.txt', 'w')\n\n";
|
||||
stream << "milestones = client.send_get('get_milestones/" + _projectID + "')\n";
|
||||
stream << "for milestone in milestones:\n";
|
||||
stream << "\tif milestone['is_completed'] == False:\n";
|
||||
stream << "\t\tfile.write(milestone['name'] + ' ' + str(milestone['id']) + '\\n')\n\n";
|
||||
stream << "file.close()\n";
|
||||
|
||||
file.close();
|
||||
|
||||
QProcess* process = new QProcess();
|
||||
connect(process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this,
|
||||
[=](int exitCode, QProcess::ExitStatus exitStatus) { updateMilestonesComboData(exitCode, exitStatus); });
|
||||
|
||||
QStringList parameters = QStringList() << _outputDirectory + "/getMilestones.py ";
|
||||
process->start(_pythonCommand, parameters);
|
||||
}
|
||||
|
||||
void TestRailInterface::createTestSuitePython(const QString& testDirectory,
|
||||
const QString& outputDirectory,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub) {
|
||||
_testDirectory = testDirectory;
|
||||
_outputDirectory = outputDirectory;
|
||||
_userGitHub = userGitHub;
|
||||
_branchGitHub = branchGitHub;
|
||||
|
||||
if (!setPythonCommand()) {
|
||||
return;
|
||||
}
|
||||
|
||||
requestTestRailTestCasesDataFromUser();
|
||||
createTestRailDotPyScript();
|
||||
createStackDotPyScript();
|
||||
|
||||
// TestRail will be updated after the process initiated by getMilestonesFromTestRail has completed
|
||||
getMilestonesFromTestRail();
|
||||
}
|
||||
|
||||
void TestRailInterface::createTestSuiteXML(const QString& testDirectory,
|
||||
const QString& outputDirectory,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub) {
|
||||
_outputDirectory = outputDirectory;
|
||||
|
||||
QDomProcessingInstruction instruction = _document.createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
|
||||
_document.appendChild(instruction);
|
||||
|
||||
// We create a single section, within sections
|
||||
QDomElement root = _document.createElement("sections");
|
||||
_document.appendChild(root);
|
||||
|
||||
QDomElement topLevelSection = _document.createElement("section");
|
||||
|
||||
QDomElement suiteName = _document.createElement("name");
|
||||
suiteName.appendChild(
|
||||
_document.createTextNode("Test Suite - " + QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm")));
|
||||
topLevelSection.appendChild(suiteName);
|
||||
|
||||
// This is the first call to 'process'. This is then called recursively to build the full XML tree
|
||||
QDomElement secondLevelSections = _document.createElement("sections");
|
||||
topLevelSection.appendChild(processDirectoryXML(testDirectory, userGitHub, branchGitHub, secondLevelSections));
|
||||
|
||||
topLevelSection.appendChild(secondLevelSections);
|
||||
root.appendChild(topLevelSection);
|
||||
|
||||
// Write to file
|
||||
const QString testRailsFilename{ _outputDirectory + "/TestRailSuite.xml" };
|
||||
QFile file(testRailsFilename);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not create XML file");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
stream << _document.toString();
|
||||
|
||||
file.close();
|
||||
|
||||
QMessageBox::information(0, "Success", "TestRail XML file has been created");
|
||||
}
|
||||
|
||||
QDomElement TestRailInterface::processDirectoryXML(const QString& directory,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub,
|
||||
const QDomElement& element) {
|
||||
QDomElement result = element;
|
||||
|
||||
// Loop over all entries in directory
|
||||
QDirIterator it(directory.toStdString().c_str());
|
||||
while (it.hasNext()) {
|
||||
QString nextDirectory = it.next();
|
||||
|
||||
// The object name appears after the last slash (we are assured there is at least 1).
|
||||
QString objectName = getObject(nextDirectory);
|
||||
|
||||
// Only process directories
|
||||
if (isAValidTestDirectory(nextDirectory)) {
|
||||
// Create a section and process it
|
||||
QDomElement sectionElement = _document.createElement("section");
|
||||
|
||||
QDomElement sectionElementName = _document.createElement("name");
|
||||
sectionElementName.appendChild(_document.createTextNode(objectName));
|
||||
sectionElement.appendChild(sectionElementName);
|
||||
|
||||
QDomElement testsElement = _document.createElement("sections");
|
||||
sectionElement.appendChild(processDirectoryXML(nextDirectory, userGitHub, branchGitHub, testsElement));
|
||||
|
||||
result.appendChild(sectionElement);
|
||||
} else if (objectName == "test.js" || objectName == "testStory.js") {
|
||||
QDomElement sectionElement = _document.createElement("section");
|
||||
QDomElement sectionElementName = _document.createElement("name");
|
||||
sectionElementName.appendChild(_document.createTextNode("all"));
|
||||
sectionElement.appendChild(sectionElementName);
|
||||
sectionElement.appendChild(
|
||||
processTestXML(nextDirectory, objectName, userGitHub, branchGitHub, _document.createElement("cases")));
|
||||
|
||||
result.appendChild(sectionElement);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QDomElement TestRailInterface::processTestXML(const QString& fullDirectory,
|
||||
const QString& test,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub,
|
||||
const QDomElement& element) {
|
||||
QDomElement result = element;
|
||||
|
||||
QDomElement caseElement = _document.createElement("case");
|
||||
|
||||
caseElement.appendChild(_document.createElement("id"));
|
||||
|
||||
// The name of the test is derived from the full path.
|
||||
// The first term is the first word after "tests"
|
||||
// The last word is the penultimate word
|
||||
QStringList words = fullDirectory.split('/');
|
||||
int i = 0;
|
||||
while (words[i] != "tests") {
|
||||
++i;
|
||||
if (i >= words.length() - 1) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Folder \"tests\" not found in " + fullDirectory);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
QString title{ words[i] };
|
||||
for (++i; i < words.length() - 1; ++i) {
|
||||
title += " / " + words[i];
|
||||
}
|
||||
|
||||
QDomElement titleElement = _document.createElement("title");
|
||||
titleElement.appendChild(_document.createTextNode(title));
|
||||
caseElement.appendChild(titleElement);
|
||||
|
||||
QDomElement templateElement = _document.createElement("template");
|
||||
templateElement.appendChild(_document.createTextNode("Test Case (Steps)"));
|
||||
caseElement.appendChild(templateElement);
|
||||
|
||||
QDomElement typeElement = _document.createElement("type");
|
||||
typeElement.appendChild(_document.createTextNode("3 - Regression"));
|
||||
caseElement.appendChild(typeElement);
|
||||
|
||||
QDomElement priorityElement = _document.createElement("priority");
|
||||
priorityElement.appendChild(_document.createTextNode("Medium"));
|
||||
caseElement.appendChild(priorityElement);
|
||||
|
||||
QDomElement estimateElementName = _document.createElement("estimate");
|
||||
estimateElementName.appendChild(_document.createTextNode("60"));
|
||||
caseElement.appendChild(estimateElementName);
|
||||
|
||||
caseElement.appendChild(_document.createElement("references"));
|
||||
|
||||
QDomElement customElement = _document.createElement("custom");
|
||||
|
||||
QDomElement tester_countElement = _document.createElement("tester_count");
|
||||
tester_countElement.appendChild(_document.createTextNode("1"));
|
||||
customElement.appendChild(tester_countElement);
|
||||
|
||||
QDomElement domain_bot_loadElement = _document.createElement("domain_bot_load");
|
||||
QDomElement domain_bot_loadElementId = _document.createElement("id");
|
||||
domain_bot_loadElementId.appendChild(_document.createTextNode("1"));
|
||||
domain_bot_loadElement.appendChild(domain_bot_loadElementId);
|
||||
QDomElement domain_bot_loadElementValue = _document.createElement("value");
|
||||
domain_bot_loadElementValue.appendChild(
|
||||
_document.createTextNode(" Without Bots (hifiqa-rc / hifi-qa-stable / hifiqa-master)"));
|
||||
domain_bot_loadElement.appendChild(domain_bot_loadElementValue);
|
||||
customElement.appendChild(domain_bot_loadElement);
|
||||
|
||||
QDomElement automation_typeElement = _document.createElement("automation_type");
|
||||
QDomElement automation_typeElementId = _document.createElement("id");
|
||||
automation_typeElementId.appendChild(_document.createTextNode("0"));
|
||||
automation_typeElement.appendChild(automation_typeElementId);
|
||||
QDomElement automation_typeElementValue = _document.createElement("value");
|
||||
automation_typeElementValue.appendChild(_document.createTextNode("None"));
|
||||
automation_typeElement.appendChild(automation_typeElementValue);
|
||||
customElement.appendChild(automation_typeElement);
|
||||
|
||||
QDomElement added_to_releaseElement = _document.createElement("added_to_release");
|
||||
QDomElement added_to_releaseElementId = _document.createElement("id");
|
||||
added_to_releaseElementId.appendChild(_document.createTextNode("4"));
|
||||
added_to_releaseElement.appendChild(added_to_releaseElementId);
|
||||
QDomElement added_to_releaseElementValue = _document.createElement("value");
|
||||
added_to_releaseElementValue.appendChild(_document.createTextNode(branchGitHub));
|
||||
added_to_releaseElement.appendChild(added_to_releaseElementValue);
|
||||
customElement.appendChild(added_to_releaseElement);
|
||||
|
||||
QDomElement precondsElement = _document.createElement("preconds");
|
||||
precondsElement.appendChild(_document.createTextNode(
|
||||
"Tester is in an empty region of a domain in which they have edit rights\n\n*Note: Press 'n' to advance test script"));
|
||||
customElement.appendChild(precondsElement);
|
||||
|
||||
QString testMDName = QString("https://github.com/") + userGitHub + "/hifi_tests/blob/" + branchGitHub +
|
||||
"/tests/content/entity/light/point/create/test.md";
|
||||
|
||||
QDomElement steps_seperatedElement = _document.createElement("steps_separated");
|
||||
QDomElement stepElement = _document.createElement("step");
|
||||
QDomElement stepIndexElement = _document.createElement("index");
|
||||
stepIndexElement.appendChild(_document.createTextNode("1"));
|
||||
stepElement.appendChild(stepIndexElement);
|
||||
QDomElement stepContentElement = _document.createElement("content");
|
||||
stepContentElement.appendChild(
|
||||
_document.createTextNode(QString("Execute instructions in [THIS TEST](") + testMDName + ")"));
|
||||
stepElement.appendChild(stepContentElement);
|
||||
QDomElement stepExpectedElement = _document.createElement("expected");
|
||||
stepExpectedElement.appendChild(_document.createTextNode("Refer to the expected result in the linked description."));
|
||||
stepElement.appendChild(stepExpectedElement);
|
||||
steps_seperatedElement.appendChild(stepElement);
|
||||
customElement.appendChild(steps_seperatedElement);
|
||||
|
||||
QDomElement notesElement = _document.createElement("notes");
|
||||
notesElement.appendChild(_document.createTextNode(testMDName));
|
||||
customElement.appendChild(notesElement);
|
||||
|
||||
caseElement.appendChild(customElement);
|
||||
|
||||
result.appendChild(caseElement);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TestRailInterface::processTestPython(const QString& fullDirectory,
|
||||
QTextStream& stream,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub) {
|
||||
// The name of the test is derived from the full path.
|
||||
// The first term is the first word after "tests"
|
||||
// The last word is the penultimate word
|
||||
QStringList words = fullDirectory.split('/');
|
||||
int i = 0;
|
||||
while (words[i] != "tests") {
|
||||
++i;
|
||||
if (i >= words.length() - 1) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Folder \"tests\" not found in " + fullDirectory);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
QString title{ words[i] };
|
||||
for (++i; i < words.length() - 1; ++i) {
|
||||
title += " / " + words[i];
|
||||
}
|
||||
|
||||
// To create the path to test.md, prefix by tests, and remove blanks
|
||||
QString pathToTestMD = QString("/tests/") + title.remove(" ");
|
||||
|
||||
stream << "section_id = parent_ids.peek()\n";
|
||||
|
||||
QString testMDName =
|
||||
QString("https://github.com/") + userGitHub + "/hifi_tests/blob/" + branchGitHub + pathToTestMD + "/test.md ";
|
||||
|
||||
QString testContent = QString("Execute instructions in [THIS TEST](") + testMDName + ")";
|
||||
QString testExpected = QString("Refer to the expected result in the linked description.");
|
||||
|
||||
int milestone_id = _milestones[_milestoneNames[_testRailTestCasesSelectorWindow.getMilestoneID()]];
|
||||
|
||||
stream << "data = {\n"
|
||||
<< "\t'title': '" << title << "',\n"
|
||||
<< "\t'template_id': 2,\n"
|
||||
<< "\t'milestone_id': " << milestone_id << ",\n"
|
||||
<< "\t'custom_tester_count': 1,\n"
|
||||
<< "\t'custom_domain_bot_load': 1,\n"
|
||||
<< "\t'custom_added_to_release': 4,\n"
|
||||
<< "\t'custom_preconds': "
|
||||
<< "'Tester is in an empty region of a domain in which they have edit rights\\n\\n*Note: Press \\'n\\' to advance "
|
||||
"test script',\n"
|
||||
<< "\t'custom_steps_separated': "
|
||||
<< "[\n\t\t{\n\t\t\t'content': '" << testContent << "',\n\t\t\t'expected': '" << testExpected << "'\n\t\t}\n\t]\n"
|
||||
<< "}\n";
|
||||
|
||||
stream << "case = client.send_post('add_case/' + str(section_id), data)\n";
|
||||
}
|
||||
|
||||
void TestRailInterface::requestTestRailRunDataFromUser() {
|
||||
_testRailRunSelectorWindow.reset();
|
||||
_testRailRunSelectorWindow.exec();
|
||||
|
||||
if (_testRailRunSelectorWindow.getUserCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_url = _testRailRunSelectorWindow.getURL() + "/";
|
||||
_user = _testRailRunSelectorWindow.getUser();
|
||||
_password = _testRailRunSelectorWindow.getPassword();
|
||||
////_password = "tutKA76";
|
||||
_projectID = QString::number(_testRailRunSelectorWindow.getProjectID());
|
||||
_suiteID = QString::number(_testRailRunSelectorWindow.getSuiteID());
|
||||
}
|
||||
|
||||
void TestRailInterface::getTestSectionsFromTestRail() {
|
||||
QString filename = _outputDirectory + "/getSections.py";
|
||||
if (QFile::exists(filename)) {
|
||||
QFile::remove(filename);
|
||||
}
|
||||
QFile file(filename);
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Could not create 'getSections.py'");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
|
||||
// Code to access TestRail
|
||||
stream << "from testrail import *\n";
|
||||
stream << "client = APIClient('" << _url.toStdString().c_str() << "')\n";
|
||||
stream << "client.user = '" << _user << "'\n";
|
||||
stream << "client.password = '" << _password << "'\n\n";
|
||||
|
||||
// Print the list of sections without parents
|
||||
stream << "sections = client.send_get('get_sections/" + _projectID + "&suite_id=" + _suiteID + "')\n\n";
|
||||
stream << "file = open('" + _outputDirectory + "/sections.txt', 'w')\n\n";
|
||||
stream << "for section in sections:\n";
|
||||
stream << "\tif section['parent_id'] == None:\n";
|
||||
stream << "\t\tfile.write(section['name'] + ' ' + str(section['id']) + '\\n')\n\n";
|
||||
stream << "file.close()\n";
|
||||
|
||||
file.close();
|
||||
|
||||
QProcess* process = new QProcess();
|
||||
connect(process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this,
|
||||
[=](int exitCode, QProcess::ExitStatus exitStatus) { updateSectionsComboData(exitCode, exitStatus); });
|
||||
|
||||
QStringList parameters = QStringList() << _outputDirectory + "/getSections.py ";
|
||||
process->start(_pythonCommand, parameters);
|
||||
}
|
||||
|
||||
void TestRailInterface::createTestRailRun(const QString& outputDirectory) {
|
||||
_outputDirectory = outputDirectory;
|
||||
|
||||
if (!setPythonCommand()) {
|
||||
return;
|
||||
}
|
||||
|
||||
requestTestRailRunDataFromUser();
|
||||
createTestRailDotPyScript();
|
||||
createStackDotPyScript();
|
||||
|
||||
// TestRail will be updated after the process initiated by getTestCasesFromTestRail has completed
|
||||
getTestSectionsFromTestRail();
|
||||
}
|
119
tools/auto-tester/src/TestRailInterface.h
Normal file
119
tools/auto-tester/src/TestRailInterface.h
Normal file
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// TestRailInterface.h
|
||||
//
|
||||
// Created by Nissim Hadar on 6 Jul 2018.
|
||||
// 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_test_testrail_interface_h
|
||||
#define hifi_test_testrail_interface_h
|
||||
|
||||
#include "ui/BusyWindow.h"
|
||||
|
||||
#include "ui/TestRailTestCasesSelectorWindow.h"
|
||||
#include "ui/TestRailRunSelectorWindow.h"
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QtXml/QDomDocument>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
|
||||
class TestRailInterface : public QObject{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestRailInterface();
|
||||
|
||||
void createTestSuiteXML(const QString& testDirectory,
|
||||
const QString& outputDirectory,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub);
|
||||
|
||||
void createTestSuitePython(const QString& testDirectory,
|
||||
const QString& outputDirectory,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub);
|
||||
|
||||
QDomElement processDirectoryXML(const QString& directory,
|
||||
const QString& useGitHubr,
|
||||
const QString& branchGitHub,
|
||||
const QDomElement& element);
|
||||
|
||||
QDomElement processTestXML(const QString& fullDirectory,
|
||||
const QString& test,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub,
|
||||
const QDomElement& element);
|
||||
|
||||
void processTestPython(const QString& fullDirectory,
|
||||
QTextStream& stream,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub);
|
||||
|
||||
void getMilestonesFromTestRail();
|
||||
void getTestSectionsFromTestRail();
|
||||
|
||||
void createTestRailDotPyScript();
|
||||
void createStackDotPyScript();
|
||||
|
||||
void requestTestRailTestCasesDataFromUser();
|
||||
void requestTestRailRunDataFromUser();
|
||||
|
||||
void createAddTestCasesPythonScript(const QString& testDirectory,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub);
|
||||
|
||||
void processDirectoryPython(const QString& directory,
|
||||
QTextStream& stream,
|
||||
const QString& userGitHub,
|
||||
const QString& branchGitHub);
|
||||
|
||||
bool isAValidTestDirectory(const QString& directory);
|
||||
|
||||
QString getObject(const QString& path);
|
||||
|
||||
void updateMilestonesComboData(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void updateSectionsComboData(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
void createTestRailRun(const QString& outputDirectory);
|
||||
|
||||
bool setPythonCommand();
|
||||
|
||||
private:
|
||||
// HighFidelity Interface project ID in TestRail
|
||||
const int INTERFACE_PROJECT_ID{ 24 };
|
||||
|
||||
// Rendering suite ID
|
||||
const int INTERFACE_SUITE_ID{ 1147 };
|
||||
|
||||
QDomDocument _document;
|
||||
|
||||
BusyWindow _busyWindow;
|
||||
TestRailTestCasesSelectorWindow _testRailTestCasesSelectorWindow;
|
||||
TestRailRunSelectorWindow _testRailRunSelectorWindow;
|
||||
|
||||
QString _url;
|
||||
QString _user;
|
||||
QString _password;
|
||||
QString _projectID;
|
||||
QString _suiteID;
|
||||
|
||||
QString _testDirectory;
|
||||
QString _outputDirectory;
|
||||
QString _userGitHub;
|
||||
QString _branchGitHub;
|
||||
|
||||
const QString pythonExe{ "python.exe" };
|
||||
QString _pythonCommand;
|
||||
|
||||
std::map<QString, int> _milestones;
|
||||
QStringList _milestoneNames;
|
||||
|
||||
std::map<QString, int> _sections;
|
||||
QStringList _sectionNames;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -16,56 +16,64 @@
|
|||
#endif
|
||||
|
||||
AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
|
||||
ui.setupUi(this);
|
||||
ui.checkBoxInteractiveMode->setChecked(true);
|
||||
ui.progressBar->setVisible(false);
|
||||
_ui.setupUi(this);
|
||||
_ui.checkBoxInteractiveMode->setChecked(true);
|
||||
_ui.progressBar->setVisible(false);
|
||||
|
||||
signalMapper = new QSignalMapper();
|
||||
_signalMapper = new QSignalMapper();
|
||||
|
||||
connect(ui.actionClose, &QAction::triggered, this, &AutoTester::on_closeButton_clicked);
|
||||
connect(ui.actionAbout, &QAction::triggered, this, &AutoTester::about);
|
||||
connect(_ui.actionClose, &QAction::triggered, this, &AutoTester::on_closeButton_clicked);
|
||||
connect(_ui.actionAbout, &QAction::triggered, this, &AutoTester::about);
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
ui.hideTaskbarButton->setVisible(false);
|
||||
ui.showTaskbarButton->setVisible(false);
|
||||
_ui.hideTaskbarButton->setVisible(false);
|
||||
_ui.showTaskbarButton->setVisible(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AutoTester::setup() {
|
||||
test = new Test();
|
||||
_test = new Test();
|
||||
}
|
||||
|
||||
void AutoTester::runFromCommandLine(const QString& testFolder, const QString& branch, const QString& user) {
|
||||
isRunningFromCommandline = true;
|
||||
test->startTestsEvaluation(testFolder, branch, user);
|
||||
_isRunningFromCommandline = true;
|
||||
_test->startTestsEvaluation(testFolder, branch, user);
|
||||
}
|
||||
|
||||
void AutoTester::on_evaluateTestsButton_clicked() {
|
||||
test->startTestsEvaluation();
|
||||
_test->startTestsEvaluation();
|
||||
}
|
||||
|
||||
void AutoTester::on_createRecursiveScriptButton_clicked() {
|
||||
test->createRecursiveScript();
|
||||
_test->createRecursiveScript();
|
||||
}
|
||||
|
||||
void AutoTester::on_createAllRecursiveScriptsButton_clicked() {
|
||||
test->createAllRecursiveScripts();
|
||||
_test->createAllRecursiveScripts();
|
||||
}
|
||||
|
||||
void AutoTester::on_createTestsButton_clicked() {
|
||||
test->createTests();
|
||||
_test->createTests();
|
||||
}
|
||||
|
||||
void AutoTester::on_createMDFileButton_clicked() {
|
||||
test->createMDFile();
|
||||
_test->createMDFile();
|
||||
}
|
||||
|
||||
void AutoTester::on_createAllMDFilesButton_clicked() {
|
||||
test->createAllMDFiles();
|
||||
_test->createAllMDFiles();
|
||||
}
|
||||
|
||||
void AutoTester::on_createTestsOutlineButton_clicked() {
|
||||
test->createTestsOutline();
|
||||
_test->createTestsOutline();
|
||||
}
|
||||
|
||||
void AutoTester::on_createTestRailTestCasesButton_clicked() {
|
||||
_test->createTestRailTestCases();
|
||||
}
|
||||
|
||||
void AutoTester::on_createTestRailRunButton_clicked() {
|
||||
_test->createTestRailRun();
|
||||
}
|
||||
|
||||
// To toggle between show and hide
|
||||
|
@ -96,11 +104,19 @@ void AutoTester::on_closeButton_clicked() {
|
|||
exit(0);
|
||||
}
|
||||
|
||||
void AutoTester::downloadImage(const QUrl& url) {
|
||||
downloaders.emplace_back(new Downloader(url, this));
|
||||
connect(downloaders[_index], SIGNAL (downloaded()), signalMapper, SLOT (map()));
|
||||
void AutoTester::on_createPythonScriptRadioButton_clicked() {
|
||||
_test->setTestRailCreateMode(PYTHON);
|
||||
}
|
||||
|
||||
signalMapper->setMapping(downloaders[_index], _index);
|
||||
void AutoTester::on_createXMLScriptRadioButton_clicked() {
|
||||
_test->setTestRailCreateMode(XML);
|
||||
}
|
||||
|
||||
void AutoTester::downloadImage(const QUrl& url) {
|
||||
_downloaders.emplace_back(new Downloader(url, this));
|
||||
connect(_downloaders[_index], SIGNAL (downloaded()), _signalMapper, SLOT (map()));
|
||||
|
||||
_signalMapper->setMapping(_downloaders[_index], _index);
|
||||
|
||||
++_index;
|
||||
}
|
||||
|
@ -113,39 +129,39 @@ void AutoTester::downloadImages(const QStringList& URLs, const QString& director
|
|||
_numberOfImagesDownloaded = 0;
|
||||
_index = 0;
|
||||
|
||||
ui.progressBar->setMinimum(0);
|
||||
ui.progressBar->setMaximum(_numberOfImagesToDownload - 1);
|
||||
ui.progressBar->setValue(0);
|
||||
ui.progressBar->setVisible(true);
|
||||
_ui.progressBar->setMinimum(0);
|
||||
_ui.progressBar->setMaximum(_numberOfImagesToDownload - 1);
|
||||
_ui.progressBar->setValue(0);
|
||||
_ui.progressBar->setVisible(true);
|
||||
|
||||
downloaders.clear();
|
||||
_downloaders.clear();
|
||||
for (int i = 0; i < _numberOfImagesToDownload; ++i) {
|
||||
QUrl imageURL(URLs[i]);
|
||||
downloadImage(imageURL);
|
||||
}
|
||||
|
||||
connect(signalMapper, SIGNAL (mapped(int)), this, SLOT (saveImage(int)));
|
||||
connect(_signalMapper, SIGNAL (mapped(int)), this, SLOT (saveImage(int)));
|
||||
}
|
||||
|
||||
void AutoTester::saveImage(int index) {
|
||||
try {
|
||||
QFile file(_directoryName + "/" + _filenames[index]);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(downloaders[index]->downloadedData());
|
||||
file.write(_downloaders[index]->downloadedData());
|
||||
file.close();
|
||||
} catch (...) {
|
||||
QMessageBox::information(0, "Test Aborted", "Failed to save image: " + _filenames[index]);
|
||||
ui.progressBar->setVisible(false);
|
||||
_ui.progressBar->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
++_numberOfImagesDownloaded;
|
||||
|
||||
if (_numberOfImagesDownloaded == _numberOfImagesToDownload) {
|
||||
disconnect(signalMapper, SIGNAL (mapped(int)), this, SLOT (saveImage(int)));
|
||||
test->finishTestsEvaluation(isRunningFromCommandline, ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
|
||||
disconnect(_signalMapper, SIGNAL (mapped(int)), this, SLOT (saveImage(int)));
|
||||
_test->finishTestsEvaluation(_isRunningFromCommandline, _ui.checkBoxInteractiveMode->isChecked(), _ui.progressBar);
|
||||
} else {
|
||||
ui.progressBar->setValue(_numberOfImagesDownloaded);
|
||||
_ui.progressBar->setValue(_numberOfImagesDownloaded);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,18 +170,18 @@ void AutoTester::about() {
|
|||
}
|
||||
|
||||
void AutoTester::setUserText(const QString& user) {
|
||||
ui.userTextEdit->setText(user);
|
||||
_ui.userTextEdit->setText(user);
|
||||
}
|
||||
|
||||
QString AutoTester::getSelectedUser()
|
||||
{
|
||||
return ui.userTextEdit->toPlainText();
|
||||
return _ui.userTextEdit->toPlainText();
|
||||
}
|
||||
|
||||
void AutoTester::setBranchText(const QString& branch) {
|
||||
ui.branchTextEdit->setText(branch);
|
||||
_ui.branchTextEdit->setText(branch);
|
||||
}
|
||||
|
||||
QString AutoTester::getSelectedBranch() {
|
||||
return ui.branchTextEdit->toPlainText();
|
||||
return _ui.branchTextEdit->toPlainText();
|
||||
}
|
||||
|
|
|
@ -45,10 +45,15 @@ private slots:
|
|||
void on_createMDFileButton_clicked();
|
||||
void on_createAllMDFilesButton_clicked();
|
||||
void on_createTestsOutlineButton_clicked();
|
||||
void on_createTestRailTestCasesButton_clicked();
|
||||
void on_createTestRailRunButton_clicked();
|
||||
|
||||
void on_hideTaskbarButton_clicked();
|
||||
void on_showTaskbarButton_clicked();
|
||||
|
||||
void on_createPythonScriptRadioButton_clicked();
|
||||
void on_createXMLScriptRadioButton_clicked();
|
||||
|
||||
void on_closeButton_clicked();
|
||||
|
||||
void saveImage(int index);
|
||||
|
@ -56,23 +61,23 @@ private slots:
|
|||
void about();
|
||||
|
||||
private:
|
||||
Ui::AutoTesterClass ui;
|
||||
Test* test;
|
||||
Ui::AutoTesterClass _ui;
|
||||
Test* _test;
|
||||
|
||||
std::vector<Downloader*> downloaders;
|
||||
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;
|
||||
QSignalMapper* _signalMapper;
|
||||
|
||||
int _numberOfImagesToDownload { 0 };
|
||||
int _numberOfImagesDownloaded { 0 };
|
||||
int _index { 0 };
|
||||
|
||||
bool isRunningFromCommandline { false };
|
||||
bool _isRunningFromCommandline { false };
|
||||
};
|
||||
|
||||
#endif // hifi_AutoTester_h
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>612</width>
|
||||
<height>537</height>
|
||||
<width>645</width>
|
||||
<height>814</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -18,7 +18,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>380</x>
|
||||
<y>430</y>
|
||||
<y>620</y>
|
||||
<width>101</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
|
@ -44,7 +44,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>430</x>
|
||||
<y>270</y>
|
||||
<y>490</y>
|
||||
<width>101</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
|
@ -57,7 +57,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>330</x>
|
||||
<y>110</y>
|
||||
<y>340</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
|
@ -70,7 +70,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>320</x>
|
||||
<y>280</y>
|
||||
<y>500</y>
|
||||
<width>131</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
|
@ -86,7 +86,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>320</x>
|
||||
<y>330</y>
|
||||
<y>550</y>
|
||||
<width>255</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
|
@ -99,7 +99,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>330</x>
|
||||
<y>170</y>
|
||||
<y>400</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
|
@ -229,13 +229,68 @@
|
|||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="createTestRailTestCasesButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>410</x>
|
||||
<y>100</y>
|
||||
<width>140</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create TestRail Test Cases</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QRadioButton" name="createPythonScriptRadioButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</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="QRadioButton" name="createXMLScriptRadioButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>120</y>
|
||||
<width>95</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>XML</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="createTestRailRunButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>410</x>
|
||||
<y>180</y>
|
||||
<width>140</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create TestRail Run</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>612</width>
|
||||
<width>645</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
|
18
tools/auto-tester/src/ui/BusyWindow.cpp
Normal file
18
tools/auto-tester/src/ui/BusyWindow.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// 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"
|
||||
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
BusyWindow::BusyWindow(QWidget *parent) {
|
||||
setupUi(this);
|
||||
}
|
22
tools/auto-tester/src/ui/BusyWindow.h
Normal file
22
tools/auto-tester/src/ui/BusyWindow.h
Normal 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
|
75
tools/auto-tester/src/ui/BusyWindow.ui
Normal file
75
tools/auto-tester/src/ui/BusyWindow.ui
Normal 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>
|
|
@ -66,14 +66,14 @@ void MismatchWindow::setTestFailure(TestFailure testFailure) {
|
|||
QPixmap expectedPixmap = QPixmap(testFailure._pathname + testFailure._expectedImageFilename);
|
||||
QPixmap actualPixmap = QPixmap(testFailure._pathname + testFailure._actualImageFilename);
|
||||
|
||||
diffPixmap = computeDiffPixmap(
|
||||
_diffPixmap = computeDiffPixmap(
|
||||
QImage(testFailure._pathname + testFailure._expectedImageFilename),
|
||||
QImage(testFailure._pathname + testFailure._actualImageFilename)
|
||||
);
|
||||
|
||||
expectedImage->setPixmap(expectedPixmap);
|
||||
resultImage->setPixmap(actualPixmap);
|
||||
diffImage->setPixmap(diffPixmap);
|
||||
diffImage->setPixmap(_diffPixmap);
|
||||
}
|
||||
|
||||
void MismatchWindow::on_passTestButton_clicked() {
|
||||
|
@ -92,5 +92,5 @@ void MismatchWindow::on_abortTestsButton_clicked() {
|
|||
}
|
||||
|
||||
QPixmap MismatchWindow::getComparisonImage() {
|
||||
return diffPixmap;
|
||||
return _diffPixmap;
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@
|
|||
|
||||
#include "../common.h"
|
||||
|
||||
class MismatchWindow : public QDialog, public Ui::MismatchWindow
|
||||
{
|
||||
class MismatchWindow : public QDialog, public Ui::MismatchWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -36,7 +35,7 @@ private slots:
|
|||
private:
|
||||
UserResponse _userResponse{ USER_RESPONSE_INVALID };
|
||||
|
||||
QPixmap diffPixmap;
|
||||
QPixmap _diffPixmap;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
<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>
|
||||
|
@ -193,4 +196,4 @@
|
|||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
</ui>
|
||||
|
|
101
tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp
Normal file
101
tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp
Normal 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);
|
||||
|
||||
OKButton->setDisabled(true);
|
||||
sectionsComboBox->setDisabled(true);
|
||||
}
|
||||
|
||||
void TestRailRunSelectorWindow::on_acceptButton_clicked() {
|
||||
urlLineEdit->setDisabled(true);
|
||||
userLineEdit->setDisabled(true);
|
||||
passwordLineEdit->setDisabled(true);
|
||||
projectIDLineEdit->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 0;
|
||||
sectionsComboBox->currentIndex();
|
||||
}
|
50
tools/auto-tester/src/ui/TestRailRunSelectorWindow.h
Normal file
50
tools/auto-tester/src/ui/TestRailRunSelectorWindow.h
Normal 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
|
283
tools/auto-tester/src/ui/TestRailRunSelectorWindow.ui
Normal file
283
tools/auto-tester/src/ui/TestRailRunSelectorWindow.ui
Normal 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>
|
100
tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp
Normal file
100
tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// 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);
|
||||
|
||||
OKButton->setDisabled(true);
|
||||
milestonesComboBox->setDisabled(true);
|
||||
}
|
||||
|
||||
void TestRailTestCasesSelectorWindow::on_acceptButton_clicked() {
|
||||
urlLineEdit->setDisabled(true);
|
||||
userLineEdit->setDisabled(true);
|
||||
passwordLineEdit->setDisabled(true);
|
||||
projectIDLineEdit->setDisabled(true);
|
||||
|
||||
OKButton->setDisabled(false);
|
||||
milestonesComboBox->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::updateMilestonesComboBoxData(QStringList data) {
|
||||
milestonesComboBox->insertItems(0, data);
|
||||
}
|
||||
|
||||
int TestRailTestCasesSelectorWindow::getMilestoneID() {
|
||||
return milestonesComboBox->currentIndex();
|
||||
}
|
50
tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.h
Normal file
50
tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.h
Normal 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 updateMilestonesComboBoxData(QStringList data);
|
||||
int getMilestoneID();
|
||||
|
||||
private slots:
|
||||
void on_acceptButton_clicked();
|
||||
void on_OKButton_clicked();
|
||||
void on_cancelButton_clicked();
|
||||
};
|
||||
|
||||
#endif
|
280
tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.ui
Normal file
280
tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.ui
Normal 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="milestonesComboBox">
|
||||
<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="milestoneLabel">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>140</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 Milestone</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>milestonesComboBox</tabstop>
|
||||
<tabstop>OKButton</tabstop>
|
||||
<tabstop>cancelButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
Reference in a new issue