Merge branch 'workload-master' of github.com:samcake/hifi into workload-bis
|
@ -19,9 +19,9 @@
|
|||
android:allowBackup="true"
|
||||
android:screenOrientation="unspecified"
|
||||
android:theme="@style/NoSystemUI"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:launchMode="singleTop"
|
||||
android:roundIcon="@mipmap/ic_launcher_round">
|
||||
android:roundIcon="@drawable/ic_launcher">
|
||||
<activity android:name="io.highfidelity.hifiinterface.PermissionChecker">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
|
17
android/app/src/main/res/drawable/ic_launcher.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--suppress AndroidUnknownAttribute -->
|
||||
<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:viewportWidth="192"
|
||||
android:viewportHeight="192"
|
||||
android:width="192dp"
|
||||
android:height="192dp">
|
||||
<path
|
||||
android:pathData="M189.5 96.5A93.5 93.5 0 0 1 96 190 93.5 93.5 0 0 1 2.5 96.5 93.5 93.5 0 0 1 96 3 93.5 93.5 0 0 1 189.5 96.5Z"
|
||||
android:fillColor="#333333" />
|
||||
<path
|
||||
android:pathData="M96.2 173.1c-10.3 0 -20.4 -2.1 -29.8 -6 -9.2 -3.8 -17.3 -9.4 -24.3 -16.4 -7 -7 -12.6 -15.2 -16.4 -24.3 -4.1 -9.6 -6.2 -19.6 -6.2 -30 0 -10.3 2.1 -20.4 6 -29.8 3.8 -9.2 9.4 -17.3 16.4 -24.3 7 -7 15.2 -12.6 24.3 -16.4 9.5 -4 19.5 -6 29.8 -6 10.3 0 20.4 2.1 29.8 6 9.2 3.8 17.3 9.4 24.3 16.4 7 7 12.6 15.2 16.4 24.3 4 9.5 6 19.5 6 29.8 0 10.3 -2.1 20.4 -6 29.8 -3.8 9.2 -9.4 17.3 -16.4 24.3 -7 7 -15.2 12.6 -24.3 16.4 -9.2 4.1 -19.3 6.2 -29.6 6.2zm0 -145.3c-37.8 0 -68.6 30.8 -68.6 68.6 0 37.8 30.8 68.6 68.6 68.6 37.8 0 68.6 -30.8 68.6 -68.6 0 -37.8 -30.8 -68.6 -68.6 -68.6z"
|
||||
android:fillColor="#00b4f0" />
|
||||
<path
|
||||
android:pathData="M119.6 129l0 -53.8c3.4 -1.1 5.8 -4.3 5.8 -8 0 -4.6 -3.8 -8.4 -8.4 -8.4 -4.6 0 -8.4 3.8 -8.4 8.4 0 3.6 2.2 6.6 5.4 7.9l0 25L79 83.8 79 64c3.4 -1.1 5.8 -4.3 5.8 -8 0 -4.6 -3.8 -8.4 -8.4 -8.4 -4.6 0 -8.4 3.8 -8.4 8.4 0 3.6 2.2 6.6 5.4 7.9l0 54.1c-3.1 1.2 -5.4 4.3 -5.4 7.9 0 4.6 3.8 8.4 8.4 8.4 4.6 0 8.4 -3.8 8.4 -8.4 0 -3.7 -2.4 -6.9 -5.8 -8l0 -27.3 35 16.3 0 22.2c-3.1 1.2 -5.4 4.3 -5.4 7.9 0 4.6 3.8 8.4 8.4 8.4 4.6 0 8.4 -3.8 8.4 -8.4 0 -3.8 -2.4 -6.9 -5.8 -8z"
|
||||
android:fillColor="#00b4f0" />
|
||||
</vector>
|
Before Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 8.2 KiB |
|
@ -45,6 +45,9 @@
|
|||
#include "LocationBookmarks.h"
|
||||
#include "DeferredLightingEffect.h"
|
||||
|
||||
#include "AmbientOcclusionEffect.h"
|
||||
#include "RenderShadowTask.h"
|
||||
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
#include "SpeechRecognizer.h"
|
||||
#endif
|
||||
|
@ -361,18 +364,6 @@ Menu::Menu() {
|
|||
// Developer menu ----------------------------------
|
||||
MenuWrapper* developerMenu = addMenu("Developer", "Developer");
|
||||
|
||||
// Developer > Graphics
|
||||
MenuWrapper* graphicsOptionsMenu = developerMenu->addMenu("Render");
|
||||
action = addCheckableActionToQMenuAndActionHash(graphicsOptionsMenu, MenuOption::Shadows, 0, true);
|
||||
connect(action, &QAction::triggered, [action] {
|
||||
DependencyManager::get<DeferredLightingEffect>()->setShadowMapEnabled(action->isChecked());
|
||||
});
|
||||
|
||||
action = addCheckableActionToQMenuAndActionHash(graphicsOptionsMenu, MenuOption::AmbientOcclusion, 0, false);
|
||||
connect(action, &QAction::triggered, [action] {
|
||||
DependencyManager::get<DeferredLightingEffect>()->setAmbientOcclusionEnabled(action->isChecked());
|
||||
});
|
||||
|
||||
// Developer > UI >>>
|
||||
MenuWrapper* uiOptionsMenu = developerMenu->addMenu("UI");
|
||||
action = addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::DesktopTabletToToolbar, 0,
|
||||
|
@ -389,6 +380,36 @@ Menu::Menu() {
|
|||
|
||||
// Developer > Render >>>
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, true);
|
||||
connect(action, &QAction::triggered, [action] {
|
||||
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
|
||||
if (renderConfig) {
|
||||
auto mainViewShadowTaskConfig = renderConfig->getConfig<RenderShadowTask>("RenderMainView.RenderShadowTask");
|
||||
if (mainViewShadowTaskConfig) {
|
||||
if (action->isChecked()) {
|
||||
mainViewShadowTaskConfig->setPreset("Enabled");
|
||||
} else {
|
||||
mainViewShadowTaskConfig->setPreset("None");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion, 0, false);
|
||||
connect(action, &QAction::triggered, [action] {
|
||||
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
|
||||
if (renderConfig) {
|
||||
auto mainViewAmbientOcclusionConfig = renderConfig->getConfig<AmbientOcclusionEffect>("RenderMainView.AmbientOcclusion");
|
||||
if (mainViewAmbientOcclusionConfig) {
|
||||
if (action->isChecked()) {
|
||||
mainViewAmbientOcclusionConfig->setPreset("Enabled");
|
||||
} else {
|
||||
mainViewAmbientOcclusionConfig->setPreset("None");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DefaultSkybox, 0, true);
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ namespace MenuOption {
|
|||
const QString DesktopTabletToToolbar = "Desktop Tablet Becomes Toolbar";
|
||||
const QString HMDTabletToToolbar = "HMD Tablet Becomes Toolbar";
|
||||
const QString Shadows = "Shadows";
|
||||
const QString AmbientOcclusion = "AmbientOcclusion";
|
||||
const QString AmbientOcclusion = "Ambient Occlusion";
|
||||
}
|
||||
|
||||
#endif // hifi_Menu_h
|
||||
|
|
|
@ -1115,7 +1115,6 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) {
|
|||
|
||||
void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
||||
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
|
||||
_skeletonModel->setCanCastShadow(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
|
||||
|
@ -1468,7 +1467,6 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
int skeletonModelChangeCount = _skeletonModelChangeCount;
|
||||
Avatar::setSkeletonModelURL(skeletonModelURL);
|
||||
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
|
||||
_skeletonModel->setCanCastShadow(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
|
||||
_headBoneSet.clear();
|
||||
_cauterizationNeedsUpdate = true;
|
||||
|
||||
|
@ -2043,8 +2041,8 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
|
|||
_attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(),
|
||||
render::ItemKey::TAG_BITS_NONE, true);
|
||||
|
||||
_attachmentModels[i]->setCanCastShadow(shouldDrawHead, qApp->getMain3DScene(),
|
||||
render::ItemKey::TAG_BITS_NONE, true);
|
||||
_attachmentModels[i]->setCanCastShadow(shouldDrawHead, qApp->getMain3DScene(),
|
||||
render::ItemKey::TAG_BITS_NONE, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,6 @@
|
|||
#include "SnapshotAnimated.h"
|
||||
#include "UserActivityLogger.h"
|
||||
|
||||
#include "AmbientOcclusionEffect.h"
|
||||
#include "AntialiasingEffect.h"
|
||||
#include "RenderShadowTask.h"
|
||||
|
||||
void setupPreferences() {
|
||||
auto preferences = DependencyManager::get<Preferences>();
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -295,30 +291,6 @@ void setupPreferences() {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
{
|
||||
static const QString RENDER("Graphics");
|
||||
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
|
||||
if (renderConfig) {
|
||||
auto mainViewAmbientOcclusionConfig = renderConfig->getConfig<AmbientOcclusionEffect>("RenderMainView.AmbientOcclusion");
|
||||
if (mainViewAmbientOcclusionConfig) {
|
||||
auto getter = [mainViewAmbientOcclusionConfig]()->QString { return mainViewAmbientOcclusionConfig->getPreset(); };
|
||||
auto setter = [mainViewAmbientOcclusionConfig](QString preset) { mainViewAmbientOcclusionConfig->setPreset(preset); };
|
||||
auto preference = new ComboBoxPreference(RENDER, "Ambient occlusion", getter, setter);
|
||||
preference->setItems(mainViewAmbientOcclusionConfig->getPresetList());
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
auto mainViewShadowConfig = renderConfig->getConfig<RenderShadowTask>("RenderMainView.RenderShadowTask");
|
||||
if (mainViewShadowConfig) {
|
||||
auto getter = [mainViewShadowConfig]()->QString { return mainViewShadowConfig->getPreset(); };
|
||||
auto setter = [mainViewShadowConfig](QString preset) { mainViewShadowConfig->setPreset(preset); };
|
||||
auto preference = new ComboBoxPreference(RENDER, "Shadows", getter, setter);
|
||||
preference->setItems(mainViewShadowConfig->getPresetList());
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
static const QString NETWORKING("Networking");
|
||||
|
||||
|
|
|
@ -33,6 +33,10 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
|
|||
{
|
||||
// SkeletonModels, and by extention Avatars, use Dual Quaternion skinning.
|
||||
_useDualQuaternionSkinning = true;
|
||||
|
||||
// Avatars all cast shadow
|
||||
_canCastShadow = true;
|
||||
|
||||
assert(_owningAvatar);
|
||||
}
|
||||
|
||||
|
|
|
@ -313,7 +313,7 @@
|
|||
<label>ID:</label>
|
||||
<input type="text" id="property-id" readonly>
|
||||
</div>
|
||||
<div class="property checkbox">
|
||||
<div class="can-cast-shadow-section property checkbox">
|
||||
<input type="checkbox" id="property-can-cast-shadow">
|
||||
<label for="property-can-cast-shadow">Can cast shadow</label>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,7 @@ project(${TARGET_NAME})
|
|||
SET (CMAKE_AUTOUIC ON)
|
||||
SET (CMAKE_AUTOMOC ON)
|
||||
|
||||
setup_hifi_project (Core Widgets)
|
||||
setup_hifi_project (Core Widgets Network)
|
||||
link_hifi_libraries ()
|
||||
|
||||
# FIX: Qt was built with -reduce-relocations
|
||||
|
|
32
tools/auto-tester/src/Downloader.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Downloader.cpp
|
||||
//
|
||||
// Created by Nissim Hadar on 1 Mar 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 "Downloader.h"
|
||||
|
||||
Downloader::Downloader(QUrl imageUrl, QObject *parent) : QObject(parent) {
|
||||
connect(
|
||||
&_networkAccessManager, SIGNAL (finished(QNetworkReply*)),
|
||||
this, SLOT (fileDownloaded(QNetworkReply*))
|
||||
);
|
||||
|
||||
QNetworkRequest request(imageUrl);
|
||||
_networkAccessManager.get(request);
|
||||
}
|
||||
|
||||
void Downloader::fileDownloaded(QNetworkReply* reply) {
|
||||
_downloadedData = reply->readAll();
|
||||
|
||||
//emit a signal
|
||||
reply->deleteLater();
|
||||
emit downloaded();
|
||||
}
|
||||
|
||||
QByteArray Downloader::downloadedData() const {
|
||||
return _downloadedData;
|
||||
}
|
48
tools/auto-tester/src/Downloader.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// Downloader.h
|
||||
//
|
||||
// Created by Nissim Hadar on 1 Mar 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_downloader_h
|
||||
#define hifi_downloader_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QDebug>
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class Downloader : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Downloader(QUrl imageUrl, QObject *parent = 0);
|
||||
|
||||
QByteArray downloadedData() const;
|
||||
|
||||
signals:
|
||||
void downloaded();
|
||||
|
||||
private slots:
|
||||
void fileDownloaded(QNetworkReply* pReply);
|
||||
|
||||
private:
|
||||
QNetworkAccessManager _networkAccessManager;
|
||||
QByteArray _downloadedData;
|
||||
};
|
||||
|
||||
#endif // hifi_downloader_h
|
|
@ -17,13 +17,13 @@
|
|||
double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) const {
|
||||
// Make sure the image is 8 bits per colour
|
||||
QImage::Format format = expectedImage.format();
|
||||
if (format != QImage::Format::Format_RGB32) {
|
||||
if (format != QImage::Format::Format_ARGB32) {
|
||||
throw -1;
|
||||
}
|
||||
|
||||
const int L = 255; // (2^number of bits per pixel) - 1
|
||||
const double K1{ 0.01 };
|
||||
const double K2{ 0.03 };
|
||||
const double K1 { 0.01 };
|
||||
const double K2 { 0.03 };
|
||||
const double c1 = pow((K1 * L), 2);
|
||||
const double c2 = pow((K2 * L), 2);
|
||||
|
||||
|
|
|
@ -12,33 +12,32 @@
|
|||
#include <assert.h>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QDirIterator>
|
||||
#include <QImageReader>
|
||||
#include <QImageWriter>
|
||||
|
||||
#include <quazip5/quazip.h>
|
||||
#include <quazip5/JlCompress.h>
|
||||
|
||||
Test::Test() {
|
||||
snapshotFilenameFormat = QRegularExpression("hifi-snap-by-.*-on-\\d\\d\\d\\d-\\d\\d-\\d\\d_\\d\\d-\\d\\d-\\d\\d.jpg");
|
||||
#include "ui/AutoTester.h"
|
||||
extern AutoTester* autoTester;
|
||||
|
||||
expectedImageFilenameFormat = QRegularExpression("ExpectedImage_\\d+.jpg");
|
||||
#include <math.h>
|
||||
|
||||
Test::Test() {
|
||||
QString regex(EXPECTED_IMAGE_PREFIX + QString("\\\\d").repeated(NUM_DIGITS) + ".png");
|
||||
|
||||
expectedImageFilenameFormat = QRegularExpression(regex);
|
||||
|
||||
mismatchWindow.setModal(true);
|
||||
}
|
||||
|
||||
bool Test::createTestResultsFolderPathIfNeeded(QString directory) {
|
||||
// The test results folder is located in the root of the tests (i.e. for recursive test evaluation)
|
||||
if (testResultsFolderPath == "") {
|
||||
testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER;
|
||||
QDir testResultsFolder(testResultsFolderPath);
|
||||
bool Test::createTestResultsFolderPath(QString directory) {
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT);
|
||||
QDir testResultsFolder(testResultsFolderPath);
|
||||
|
||||
if (testResultsFolder.exists()) {
|
||||
testResultsFolder.removeRecursively();
|
||||
}
|
||||
|
||||
// Create a new test results folder
|
||||
return QDir().mkdir(testResultsFolderPath);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
// Create a new test results folder
|
||||
return QDir().mkdir(testResultsFolderPath);
|
||||
}
|
||||
|
||||
void Test::zipAndDeleteTestResultsFolder() {
|
||||
|
@ -60,9 +59,9 @@ void Test::zipAndDeleteTestResultsFolder() {
|
|||
index = 1;
|
||||
}
|
||||
|
||||
bool Test::compareImageLists(QStringList expectedImages, QStringList resultImages, QString testDirectory, bool interactiveMode, QProgressBar* progressBar) {
|
||||
bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar) {
|
||||
progressBar->setMinimum(0);
|
||||
progressBar->setMaximum(expectedImages.length() - 1);
|
||||
progressBar->setMaximum(expectedImagesFullFilenames.length() - 1);
|
||||
progressBar->setValue(0);
|
||||
progressBar->setVisible(true);
|
||||
|
||||
|
@ -71,12 +70,13 @@ bool Test::compareImageLists(QStringList expectedImages, QStringList resultImage
|
|||
const double THRESHOLD { 0.999 };
|
||||
bool success{ true };
|
||||
bool keepOn{ true };
|
||||
for (int i = 0; keepOn && i < expectedImages.length(); ++i) {
|
||||
for (int i = 0; keepOn && i < expectedImagesFullFilenames.length(); ++i) {
|
||||
// First check that images are the same size
|
||||
QImage resultImage(resultImages[i]);
|
||||
QImage expectedImage(expectedImages[i]);
|
||||
QImage resultImage(resultImagesFullFilenames[i]);
|
||||
QImage expectedImage(expectedImagesFullFilenames[i]);
|
||||
|
||||
if (resultImage.width() != expectedImage.width() || resultImage.height() != expectedImage.height()) {
|
||||
messageBox.critical(0, "Internal error", "Images are not the same size");
|
||||
messageBox.critical(0, "Internal error #1", "Images are not the same size");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
@ -84,21 +84,21 @@ bool Test::compareImageLists(QStringList expectedImages, QStringList resultImage
|
|||
try {
|
||||
similarityIndex = imageComparer.compareImages(resultImage, expectedImage);
|
||||
} catch (...) {
|
||||
messageBox.critical(0, "Internal error", "Image not in expected format");
|
||||
messageBox.critical(0, "Internal error #2", "Image not in expected format");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (similarityIndex < THRESHOLD) {
|
||||
TestFailure testFailure = TestFailure{
|
||||
(float)similarityIndex,
|
||||
expectedImages[i].left(expectedImages[i].lastIndexOf("/") + 1), // path to the test (including trailing /)
|
||||
QFileInfo(expectedImages[i].toStdString().c_str()).fileName(), // filename of expected image
|
||||
QFileInfo(resultImages[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);
|
||||
|
||||
if (!interactiveMode) {
|
||||
if (!isInteractiveMode) {
|
||||
appendTestResultsToFile(testResultsFolderPath, testFailure, mismatchWindow.getComparisonImage());
|
||||
success = false;
|
||||
} else {
|
||||
|
@ -131,20 +131,20 @@ bool Test::compareImageLists(QStringList expectedImages, QStringList resultImage
|
|||
|
||||
void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
|
||||
if (!QDir().exists(testResultsFolderPath)) {
|
||||
messageBox.critical(0, "Internal error", "Folder " + testResultsFolderPath + " not found");
|
||||
messageBox.critical(0, "Internal error #3", "Folder " + testResultsFolderPath + " not found");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QString failureFolderPath { testResultsFolderPath + "/" + "Failure_" + QString::number(index) };
|
||||
if (!QDir().mkdir(failureFolderPath)) {
|
||||
messageBox.critical(0, "Internal error", "Failed to create folder " + failureFolderPath);
|
||||
messageBox.critical(0, "Internal error #4", "Failed to create folder " + failureFolderPath);
|
||||
exit(-1);
|
||||
}
|
||||
++index;
|
||||
|
||||
QFile descriptionFile(failureFolderPath + "/" + TEST_RESULTS_FILENAME);
|
||||
if (!descriptionFile.open(QIODevice::ReadWrite)) {
|
||||
messageBox.critical(0, "Internal error", "Failed to create file " + TEST_RESULTS_FILENAME);
|
||||
messageBox.critical(0, "Internal error #5", "Failed to create file " + TEST_RESULTS_FILENAME);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
@ -164,60 +164,91 @@ void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure te
|
|||
sourceFile = testFailure._pathname + testFailure._expectedImageFilename;
|
||||
destinationFile = failureFolderPath + "/" + "Expected Image.jpg";
|
||||
if (!QFile::copy(sourceFile, destinationFile)) {
|
||||
messageBox.critical(0, "Internal error", "Failed to copy " + sourceFile + " to " + destinationFile);
|
||||
messageBox.critical(0, "Internal error #6", "Failed to copy " + sourceFile + " to " + destinationFile);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
sourceFile = testFailure._pathname + testFailure._actualImageFilename;
|
||||
destinationFile = failureFolderPath + "/" + "Actual Image.jpg";
|
||||
if (!QFile::copy(sourceFile, destinationFile)) {
|
||||
messageBox.critical(0, "Internal error", "Failed to copy " + sourceFile + " to " + destinationFile);
|
||||
messageBox.critical(0, "Internal error #7", "Failed to copy " + sourceFile + " to " + destinationFile);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
comparisonImage.save(failureFolderPath + "/" + "Difference Image.jpg");
|
||||
}
|
||||
|
||||
void Test::evaluateTests(bool interactiveMode, QProgressBar* progressBar) {
|
||||
void Test::startTestsEvaluation() {
|
||||
// Get list of JPEG images in folder, sorted by name
|
||||
QString pathToImageDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
|
||||
if (pathToImageDirectory == "") {
|
||||
pathToTestResultsDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
|
||||
if (pathToTestResultsDirectory == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Leave if test results folder could not be created
|
||||
if (!createTestResultsFolderPathIfNeeded(pathToImageDirectory)) {
|
||||
// Quit if test results folder could not be created
|
||||
if (!createTestResultsFolderPath(pathToTestResultsDirectory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList sortedImageFilenames = createListOfAllJPEGimagesInDirectory(pathToImageDirectory);
|
||||
// Before any processing - all images are converted to PNGs, as this is the format stored on GitHub
|
||||
QStringList sortedSnapshotFilenames = createListOfAll_imagesInDirectory("jpg", pathToTestResultsDirectory);
|
||||
foreach(QString filename, sortedSnapshotFilenames) {
|
||||
QStringList stringParts = filename.split(".");
|
||||
copyJPGtoPNG(
|
||||
pathToTestResultsDirectory + "/" + stringParts[0] + ".jpg",
|
||||
pathToTestResultsDirectory + "/" + stringParts[0] + ".png"
|
||||
);
|
||||
|
||||
// Separate images into two lists. The first is the expected images, the second is the test results
|
||||
QFile::remove(pathToTestResultsDirectory + "/" + stringParts[0] + ".jpg");
|
||||
}
|
||||
|
||||
// Create two lists. The first is the test results, the second is the expected images
|
||||
// The expected images are represented as a URL to enable download from GitHub
|
||||
// Images that are in the wrong format are ignored.
|
||||
QStringList expectedImages;
|
||||
QStringList resultImages;
|
||||
foreach(QString currentFilename, sortedImageFilenames) {
|
||||
QString fullCurrentFilename = pathToImageDirectory + "/" + currentFilename;
|
||||
if (isInExpectedImageFilenameFormat(currentFilename)) {
|
||||
expectedImages << fullCurrentFilename;
|
||||
} else if (isInSnapshotFilenameFormat(currentFilename)) {
|
||||
resultImages << fullCurrentFilename;
|
||||
|
||||
QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", pathToTestResultsDirectory);
|
||||
QStringList expectedImagesURLs;
|
||||
|
||||
const QString URLPrefix("https://raw.githubusercontent.com");
|
||||
const QString githubUser("NissimHadar");
|
||||
const QString testsRepo("hifi_tests");
|
||||
const QString branch("addRecursionToAutotester");
|
||||
|
||||
resultImagesFullFilenames.clear();
|
||||
expectedImagesFilenames.clear();
|
||||
expectedImagesFullFilenames.clear();
|
||||
|
||||
foreach(QString currentFilename, sortedTestResultsFilenames) {
|
||||
QString fullCurrentFilename = pathToTestResultsDirectory + "/" + currentFilename;
|
||||
if (isInSnapshotFilenameFormat("png", currentFilename)) {
|
||||
resultImagesFullFilenames << fullCurrentFilename;
|
||||
|
||||
QString expectedImagePartialSourceDirectory = getExpectedImagePartialSourceDirectory(currentFilename);
|
||||
|
||||
// Images are stored on GitHub as ExpectedImage_ddddd.png
|
||||
// Extract the digits at the end of the filename (exluding the file extension)
|
||||
QString expectedImageFilenameTail = currentFilename.left(currentFilename.length() - 4).right(NUM_DIGITS);
|
||||
QString expectedImageStoredFilename = EXPECTED_IMAGE_PREFIX + expectedImageFilenameTail + ".png";
|
||||
|
||||
QString imageURLString(URLPrefix + "/" + githubUser + "/" + testsRepo + "/" + branch + "/" +
|
||||
expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename);
|
||||
|
||||
expectedImagesURLs << imageURLString;
|
||||
|
||||
// The image retrieved from Github needs a unique name
|
||||
QString expectedImageFilename = currentFilename.replace("/", "_").replace(".", "_EI.");
|
||||
|
||||
expectedImagesFilenames << expectedImageFilename;
|
||||
expectedImagesFullFilenames << pathToTestResultsDirectory + "/" + expectedImageFilename;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of images in each list should be identical
|
||||
if (expectedImages.length() != resultImages.length()) {
|
||||
messageBox.critical(0,
|
||||
"Test failed",
|
||||
"Found " + QString::number(resultImages.length()) + " images in directory" +
|
||||
"\nExpected to find " + QString::number(expectedImages.length()) + " images"
|
||||
);
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
bool success = compareImageLists(expectedImages, resultImages, pathToImageDirectory, interactiveMode, progressBar);
|
||||
autoTester->downloadImages(expectedImagesURLs, pathToTestResultsDirectory, expectedImagesFilenames);
|
||||
}
|
||||
|
||||
void Test::finishTestsEvaluation(bool interactiveMode, QProgressBar* progressBar) {
|
||||
bool success = compareImageLists(interactiveMode, progressBar);
|
||||
|
||||
if (success) {
|
||||
messageBox.information(0, "Success", "All images are as expected");
|
||||
} else {
|
||||
|
@ -242,72 +273,25 @@ bool Test::isAValidDirectory(QString pathname) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Two criteria are used to decide if a folder contains valid test results.
|
||||
// 1) a 'test'js' file exists in the folder
|
||||
// 2) the folder has the same number of actual and expected images
|
||||
void Test::evaluateTestsRecursively(bool interactiveMode, QProgressBar* progressBar) {
|
||||
// Select folder to start recursing from
|
||||
QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", ".", QFileDialog::ShowDirsOnly);
|
||||
if (topLevelDirectory == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Leave if test results folder could not be created
|
||||
if (!createTestResultsFolderPathIfNeeded(topLevelDirectory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool success{ true };
|
||||
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
if (!isAValidDirectory(directory)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QString testPathname{ directory + "/" + TEST_FILENAME };
|
||||
QFileInfo fileInfo(testPathname);
|
||||
if (!fileInfo.exists()) {
|
||||
// Folder does not contain 'test.js'
|
||||
continue;
|
||||
}
|
||||
|
||||
QStringList sortedImageFilenames = createListOfAllJPEGimagesInDirectory(directory);
|
||||
|
||||
// Separate images into two lists. The first is the expected images, the second is the test results
|
||||
// Images that are in the wrong format are ignored.
|
||||
QStringList expectedImages;
|
||||
QStringList resultImages;
|
||||
foreach(QString currentFilename, sortedImageFilenames) {
|
||||
QString fullCurrentFilename = directory + "/" + currentFilename;
|
||||
if (isInExpectedImageFilenameFormat(currentFilename)) {
|
||||
expectedImages << fullCurrentFilename;
|
||||
} else if (isInSnapshotFilenameFormat(currentFilename)) {
|
||||
resultImages << fullCurrentFilename;
|
||||
}
|
||||
}
|
||||
|
||||
if (expectedImages.length() != resultImages.length()) {
|
||||
// Number of images doesn't match
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set success to false if any test has failed
|
||||
success &= compareImageLists(expectedImages, resultImages, directory, interactiveMode, progressBar);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
messageBox.information(0, "Success", "All images are as expected");
|
||||
} else {
|
||||
messageBox.information(0, "Failure", "One or more images are not as expected");
|
||||
}
|
||||
|
||||
zipAndDeleteTestResultsFolder();
|
||||
}
|
||||
|
||||
void Test::importTest(QTextStream& textStream, const QString& testPathname) {
|
||||
textStream << "Script.include(\"" << "file:///" << testPathname + "?raw=true\");" << endl;
|
||||
// `testPathname` includes the full path to the test. We need the portion below (and including) `tests`
|
||||
QStringList filenameParts = testPathname.split('/');
|
||||
int i{ 0 };
|
||||
while (i < filenameParts.length() && filenameParts[i] != "tests") {
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i == filenameParts.length()) {
|
||||
messageBox.critical(0, "Internal error #10", "Bad testPathname");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QString filename;
|
||||
for (int j = i; j < filenameParts.length(); ++j) {
|
||||
filename += "/" + filenameParts[j];
|
||||
}
|
||||
|
||||
textStream << "Script.include(\"" << "https://raw.githubusercontent.com/" << user << "/hifi_tests/" << branch << filename + "\");" << endl;
|
||||
}
|
||||
|
||||
// Creates a single script in a user-selected folder.
|
||||
|
@ -319,11 +303,58 @@ void Test::createRecursiveScript() {
|
|||
return;
|
||||
}
|
||||
|
||||
QFile allTestsFilename(topLevelDirectory + "/" + "allTests.js");
|
||||
createRecursiveScript(topLevelDirectory, true);
|
||||
}
|
||||
|
||||
// This method creates a `testRecursive.js` script in every sub-folder.
|
||||
void Test::createRecursiveScriptsRecursively() {
|
||||
// Select folder to start recursing from
|
||||
QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts", ".", QFileDialog::ShowDirsOnly);
|
||||
if (topLevelDirectory == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
createRecursiveScript(topLevelDirectory, false);
|
||||
|
||||
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
// Only process directories
|
||||
QDir dir;
|
||||
if (!isAValidDirectory(directory)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only process directories that have sub-directories
|
||||
bool hasNoSubDirectories{ true };
|
||||
QDirIterator it2(directory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it2.hasNext()) {
|
||||
QString directory2 = it2.next();
|
||||
|
||||
// Only process directories
|
||||
QDir dir;
|
||||
if (isAValidDirectory(directory2)) {
|
||||
hasNoSubDirectories = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasNoSubDirectories) {
|
||||
createRecursiveScript(directory, false);
|
||||
}
|
||||
}
|
||||
|
||||
messageBox.information(0, "Success", "Scripts have been created");
|
||||
}
|
||||
|
||||
void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode) {
|
||||
const QString recursiveTestsFilename("testRecursive.js");
|
||||
QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename);
|
||||
if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
messageBox.critical(0,
|
||||
"Internal Error",
|
||||
"Failed to create \"allTests.js\" in directory \"" + topLevelDirectory + "\""
|
||||
"Internal Error #8",
|
||||
"Failed to create \"" + recursiveTestsFilename + "\" in directory \"" + topLevelDirectory + "\""
|
||||
);
|
||||
|
||||
exit(-1);
|
||||
|
@ -332,12 +363,9 @@ void Test::createRecursiveScript() {
|
|||
QTextStream textStream(&allTestsFilename);
|
||||
textStream << "// This is an automatically generated file, created by auto-tester" << endl << endl;
|
||||
|
||||
textStream << "var autoTester = Script.require(\"https://github.com/highfidelity/hifi_tests/blob/master/tests/utils/autoTester.js?raw=true\");" << endl;
|
||||
textStream << "var autoTester = Script.require(\"https://raw.githubusercontent.com/" + user + "/hifi_tests/" + branch + "/tests/utils/autoTester.js\");" << endl;
|
||||
textStream << "autoTester.enableRecursive();" << endl << endl;
|
||||
|
||||
// The main will call each test after the previous test is completed
|
||||
// This is implemented with an interval timer that periodically tests if a
|
||||
// running test has increment a testNumber variable that it received as an input.
|
||||
QVector<QString> testPathnames;
|
||||
|
||||
// First test if top-level folder has a test.js file
|
||||
|
@ -360,7 +388,7 @@ void Test::createRecursiveScript() {
|
|||
continue;
|
||||
}
|
||||
|
||||
const QString testPathname{ directory + "/" + TEST_FILENAME };
|
||||
const QString testPathname { directory + "/" + TEST_FILENAME };
|
||||
QFileInfo fileInfo(testPathname);
|
||||
if (fileInfo.exists()) {
|
||||
// Current folder contains a test
|
||||
|
@ -370,8 +398,8 @@ void Test::createRecursiveScript() {
|
|||
}
|
||||
}
|
||||
|
||||
if (testPathnames.length() <= 0) {
|
||||
messageBox.information(0, "Failure", "No \"test.js\" files found");
|
||||
if (interactiveMode && testPathnames.length() <= 0) {
|
||||
messageBox.information(0, "Failure", "No \"" + TEST_FILENAME + "\" files found");
|
||||
allTestsFilename.close();
|
||||
return;
|
||||
}
|
||||
|
@ -380,50 +408,45 @@ void Test::createRecursiveScript() {
|
|||
textStream << "autoTester.runRecursive();" << endl;
|
||||
|
||||
allTestsFilename.close();
|
||||
messageBox.information(0, "Success", "Script has been created");
|
||||
|
||||
if (interactiveMode) {
|
||||
messageBox.information(0, "Success", "Script has been created");
|
||||
}
|
||||
}
|
||||
|
||||
void Test::createTest() {
|
||||
// Rename files sequentially, as ExpectedResult_1.jpeg, ExpectedResult_2.jpg and so on
|
||||
// Rename files sequentially, as ExpectedResult_00000.jpeg, ExpectedResult_00001.jpg and so on
|
||||
// Any existing expected result images will be deleted
|
||||
QString pathToImageDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
|
||||
if (pathToImageDirectory == "") {
|
||||
QString imageSourceDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
|
||||
if (imageSourceDirectory == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList sortedImageFilenames = createListOfAllJPEGimagesInDirectory(pathToImageDirectory);
|
||||
QString imageDestinationDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder to save the test images", ".", QFileDialog::ShowDirsOnly);
|
||||
if (imageDestinationDirectory == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("jpg", imageSourceDirectory);
|
||||
|
||||
int i = 1;
|
||||
const int maxImages = pow(10, NUM_DIGITS);
|
||||
foreach (QString currentFilename, sortedImageFilenames) {
|
||||
QString fullCurrentFilename = pathToImageDirectory + "/" + currentFilename;
|
||||
if (isInExpectedImageFilenameFormat(currentFilename)) {
|
||||
if (!QFile::remove(fullCurrentFilename)) {
|
||||
QString fullCurrentFilename = imageSourceDirectory + "/" + currentFilename;
|
||||
if (isInSnapshotFilenameFormat("jpg", currentFilename)) {
|
||||
if (i >= maxImages) {
|
||||
messageBox.critical(0, "Error", "More than " + QString::number(maxImages) + " images not supported");
|
||||
exit(-1);
|
||||
}
|
||||
QString newFilename = "ExpectedImage_" + QString::number(i - 1).rightJustified(5, '0') + ".png";
|
||||
QString fullNewFileName = imageDestinationDirectory + "/" + newFilename;
|
||||
|
||||
try {
|
||||
copyJPGtoPNG(fullCurrentFilename, fullNewFileName);
|
||||
} catch (...) {
|
||||
messageBox.critical(0, "Error", "Could not delete existing file: " + currentFilename + "\nTest creation aborted");
|
||||
exit(-1);
|
||||
}
|
||||
} else if (isInSnapshotFilenameFormat(currentFilename)) {
|
||||
const int MAX_IMAGES = 100000;
|
||||
if (i >= MAX_IMAGES) {
|
||||
messageBox.critical(0, "Error", "More than 100,000 images not supported");
|
||||
exit(-1);
|
||||
}
|
||||
QString newFilename = "ExpectedImage_" + QString::number(i-1).rightJustified(5, '0') + ".jpg";
|
||||
QString fullNewFileName = pathToImageDirectory + "/" + newFilename;
|
||||
|
||||
if (!imageDirectory.rename(fullCurrentFilename, newFilename)) {
|
||||
if (!QFile::exists(fullCurrentFilename)) {
|
||||
messageBox.critical(0, "Error", "Could not rename file: " + fullCurrentFilename + " to: " + newFilename + "\n"
|
||||
+ fullCurrentFilename + " not found"
|
||||
+ "\nTest creation aborted"
|
||||
);
|
||||
exit(-1);
|
||||
} else {
|
||||
messageBox.critical(0, "Error", "Could not rename file: " + fullCurrentFilename + " to: " + newFilename + "\n"
|
||||
+ "unknown error" + "\nTest creation aborted"
|
||||
);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
@ -431,54 +454,87 @@ void Test::createTest() {
|
|||
messageBox.information(0, "Success", "Test images have been created");
|
||||
}
|
||||
|
||||
void Test::deleteOldSnapshots() {
|
||||
// Select folder to start recursing from
|
||||
QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select root folder for snapshot deletion", ".", QFileDialog::ShowDirsOnly);
|
||||
if (topLevelDirectory == "") {
|
||||
return;
|
||||
}
|
||||
void Test::copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFullFilename) {
|
||||
QFile::remove(destinationPNGFullFilename);
|
||||
|
||||
// Recurse over folders
|
||||
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
QImageReader reader;
|
||||
reader.setFileName(sourceJPGFullFilename);
|
||||
|
||||
// Only process directories
|
||||
QDir dir(directory);
|
||||
if (!isAValidDirectory(directory)) {
|
||||
continue;
|
||||
}
|
||||
QImage image = reader.read();
|
||||
|
||||
QStringList sortedImageFilenames = createListOfAllJPEGimagesInDirectory(directory);
|
||||
|
||||
// Delete any file that is a snapshot (NOT the Expected Images)
|
||||
QStringList expectedImages;
|
||||
QStringList resultImages;
|
||||
foreach(QString currentFilename, sortedImageFilenames) {
|
||||
QString fullCurrentFilename = directory + "/" + currentFilename;
|
||||
if (isInSnapshotFilenameFormat(currentFilename)) {
|
||||
if (!QFile::remove(fullCurrentFilename)) {
|
||||
messageBox.critical(0, "Error", "Could not delete existing file: " + currentFilename + "\nSnapshot deletion aborted");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
QImageWriter writer;
|
||||
writer.setFileName(destinationPNGFullFilename);
|
||||
writer.write(image);
|
||||
}
|
||||
|
||||
QStringList Test::createListOfAllJPEGimagesInDirectory(QString pathToImageDirectory) {
|
||||
QStringList Test::createListOfAll_imagesInDirectory(QString imageFormat, QString pathToImageDirectory) {
|
||||
imageDirectory = QDir(pathToImageDirectory);
|
||||
QStringList nameFilters;
|
||||
nameFilters << "*.jpg";
|
||||
nameFilters << "*." + imageFormat;
|
||||
|
||||
return imageDirectory.entryList(nameFilters, QDir::Files, QDir::Name);
|
||||
}
|
||||
|
||||
// Use regular expressions to check if files are in specific format
|
||||
bool Test::isInSnapshotFilenameFormat(QString filename) {
|
||||
return (snapshotFilenameFormat.match(filename).hasMatch());
|
||||
// Snapshots are files in the following format:
|
||||
// Filename contains no periods (excluding period before exception
|
||||
// Filename (i.e. without extension) contains _tests_ (this is based on all test scripts being within the tests folder
|
||||
// Last 5 characters in filename are digits
|
||||
// Extension is jpg
|
||||
bool Test::isInSnapshotFilenameFormat(QString imageFormat, QString filename) {
|
||||
QStringList filenameParts = filename.split(".");
|
||||
|
||||
bool filnameHasNoPeriods = (filenameParts.size() == 2);
|
||||
bool contains_tests = filenameParts[0].contains("_tests_");
|
||||
|
||||
bool last5CharactersAreDigits;
|
||||
filenameParts[0].right(5).toInt(&last5CharactersAreDigits, 10);
|
||||
|
||||
bool extensionIsIMAGE_FORMAT = (filenameParts[1] == imageFormat);
|
||||
|
||||
return (filnameHasNoPeriods && contains_tests && last5CharactersAreDigits && extensionIsIMAGE_FORMAT);
|
||||
}
|
||||
|
||||
bool Test::isInExpectedImageFilenameFormat(QString filename) {
|
||||
return (expectedImageFilenameFormat.match(filename).hasMatch());
|
||||
}
|
||||
// For a file named "D_GitHub_hifi-tests_tests_content_entity_zone_create_0.jpg", the test directory is
|
||||
// D:/GitHub/hifi-tests/tests/content/entity/zone/create
|
||||
// This method assumes the filename is in the correct format
|
||||
QString Test::getExpectedImageDestinationDirectory(QString filename) {
|
||||
QString filenameWithoutExtension = filename.split(".")[0];
|
||||
QStringList filenameParts = filenameWithoutExtension.split("_");
|
||||
|
||||
QString result = filenameParts[0] + ":";
|
||||
|
||||
for (int i = 1; i < filenameParts.length() - 1; ++i) {
|
||||
result += "/" + filenameParts[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// For a file named "D_GitHub_hifi-tests_tests_content_entity_zone_create_0.jpg", the source directory on GitHub
|
||||
// is ...tests/content/entity/zone/create
|
||||
// This is used to create the full URL
|
||||
// This method assumes the filename is in the correct format
|
||||
QString Test::getExpectedImagePartialSourceDirectory(QString filename) {
|
||||
QString filenameWithoutExtension = filename.split(".")[0];
|
||||
QStringList filenameParts = filenameWithoutExtension.split("_");
|
||||
|
||||
// Note that the bottom-most "tests" folder is assumed to be the root
|
||||
// This is required because the tests folder is named hifi_tests
|
||||
int i { filenameParts.length() - 1 };
|
||||
while (i >= 0 && filenameParts[i] != "tests") {
|
||||
--i;
|
||||
}
|
||||
|
||||
if (i < 0) {
|
||||
messageBox.critical(0, "Internal error #9", "Bad filename");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QString result = filenameParts[i];
|
||||
|
||||
for (int j = i + 1; j < filenameParts.length() - 1; ++j) {
|
||||
result += "/" + filenameParts[j];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -23,28 +23,36 @@ class Test {
|
|||
public:
|
||||
Test();
|
||||
|
||||
void evaluateTests(bool interactiveMode, QProgressBar* progressBar);
|
||||
void evaluateTestsRecursively(bool interactiveMode, QProgressBar* progressBar);
|
||||
void startTestsEvaluation();
|
||||
void finishTestsEvaluation(bool interactiveMode, QProgressBar* progressBar);
|
||||
|
||||
void createRecursiveScript();
|
||||
void createRecursiveScriptsRecursively();
|
||||
void createRecursiveScript(QString topLevelDirectory, bool interactiveMode);
|
||||
|
||||
void createTest();
|
||||
void deleteOldSnapshots();
|
||||
|
||||
bool compareImageLists(QStringList expectedImages, QStringList resultImages, QString testDirectory, bool interactiveMode, QProgressBar* progressBar);
|
||||
bool compareImageLists(bool isInteractiveMode, QProgressBar* progressBar);
|
||||
|
||||
QStringList createListOfAllJPEGimagesInDirectory(QString pathToImageDirectory);
|
||||
QStringList createListOfAll_imagesInDirectory(QString imageFormat, QString pathToImageDirectory);
|
||||
|
||||
bool isInSnapshotFilenameFormat(QString filename);
|
||||
bool isInExpectedImageFilenameFormat(QString filename);
|
||||
bool isInSnapshotFilenameFormat(QString imageFormat, QString filename);
|
||||
|
||||
void importTest(QTextStream& textStream, const QString& testPathname);
|
||||
|
||||
void appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage);
|
||||
|
||||
bool createTestResultsFolderPathIfNeeded(QString directory);
|
||||
bool createTestResultsFolderPath(QString directory);
|
||||
void zipAndDeleteTestResultsFolder();
|
||||
|
||||
bool isAValidDirectory(QString pathname);
|
||||
|
||||
QString getExpectedImageDestinationDirectory(QString filename);
|
||||
QString getExpectedImagePartialSourceDirectory(QString filename);
|
||||
|
||||
void copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFullFilename);
|
||||
|
||||
private:
|
||||
const QString TEST_FILENAME { "test.js" };
|
||||
const QString TEST_RESULTS_FOLDER { "TestResults" };
|
||||
|
@ -54,16 +62,28 @@ private:
|
|||
|
||||
QDir imageDirectory;
|
||||
|
||||
QRegularExpression snapshotFilenameFormat;
|
||||
QRegularExpression expectedImageFilenameFormat;
|
||||
|
||||
MismatchWindow mismatchWindow;
|
||||
|
||||
ImageComparer imageComparer;
|
||||
|
||||
|
||||
QString testResultsFolderPath { "" };
|
||||
int index { 1 };
|
||||
|
||||
// Expected images are in the format ExpectedImage_dddd.jpg (d == decimal digit)
|
||||
const int NUM_DIGITS { 5 };
|
||||
const QString EXPECTED_IMAGE_PREFIX { "ExpectedImage_" };
|
||||
|
||||
QString pathToTestResultsDirectory;
|
||||
QStringList expectedImagesFilenames;
|
||||
QStringList expectedImagesFullFilenames;
|
||||
QStringList resultImagesFullFilenames;
|
||||
|
||||
// Used for accessing GitHub
|
||||
const QString user { "NissimHadar" };
|
||||
const QString branch { "addRecursionToAutotester" };
|
||||
const QString DATETIME_FORMAT { "yyyy-MM-dd_hh-mm-ss" };
|
||||
};
|
||||
|
||||
#endif // hifi_test_h
|
|
@ -10,11 +10,13 @@
|
|||
#include <QtWidgets/QApplication>
|
||||
#include "ui/AutoTester.h"
|
||||
|
||||
AutoTester* autoTester;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
QApplication application(argc, argv);
|
||||
|
||||
AutoTester autoTester;
|
||||
autoTester.show();
|
||||
autoTester = new AutoTester();
|
||||
autoTester->show();
|
||||
|
||||
return application.exec();
|
||||
}
|
||||
|
|
|
@ -12,32 +12,79 @@
|
|||
|
||||
AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
|
||||
ui.setupUi(this);
|
||||
|
||||
ui.checkBoxInteractiveMode->setChecked(true);
|
||||
|
||||
ui.progressBar->setVisible(false);
|
||||
|
||||
test = new Test();
|
||||
|
||||
signalMapper = new QSignalMapper();
|
||||
}
|
||||
|
||||
void AutoTester::on_evaluateTestsButton_clicked() {
|
||||
test.evaluateTests(ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
|
||||
}
|
||||
|
||||
void AutoTester::on_evaluateTestsRecursivelyButton_clicked() {
|
||||
test.evaluateTestsRecursively(ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
|
||||
test->startTestsEvaluation();
|
||||
}
|
||||
|
||||
void AutoTester::on_createRecursiveScriptButton_clicked() {
|
||||
test.createRecursiveScript();
|
||||
test->createRecursiveScript();
|
||||
}
|
||||
|
||||
void AutoTester::on_createRecursiveScriptsRecursivelyButton_clicked() {
|
||||
test->createRecursiveScriptsRecursively();
|
||||
}
|
||||
|
||||
void AutoTester::on_createTestButton_clicked() {
|
||||
test.createTest();
|
||||
}
|
||||
|
||||
void AutoTester::on_deleteOldSnapshotsButton_clicked() {
|
||||
test.deleteOldSnapshots();
|
||||
test->createTest();
|
||||
}
|
||||
|
||||
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()));
|
||||
|
||||
signalMapper->setMapping(downloaders[_index], _index);
|
||||
|
||||
++_index;
|
||||
}
|
||||
|
||||
void AutoTester::downloadImages(const QStringList& URLs, const QString& directoryName, const QStringList& filenames) {
|
||||
_directoryName = directoryName;
|
||||
_filenames = filenames;
|
||||
|
||||
_numberOfImagesToDownload = URLs.size();
|
||||
_numberOfImagesDownloaded = 0;
|
||||
_index = 0;
|
||||
|
||||
ui.progressBar->setMinimum(0);
|
||||
ui.progressBar->setMaximum(_numberOfImagesToDownload - 1);
|
||||
ui.progressBar->setValue(0);
|
||||
ui.progressBar->setVisible(true);
|
||||
|
||||
for (int i = 0; i < _numberOfImagesToDownload; ++i) {
|
||||
QUrl imageURL(URLs[i]);
|
||||
downloadImage(imageURL);
|
||||
}
|
||||
|
||||
connect(signalMapper, SIGNAL (mapped(int)), this, SLOT (saveImage(int)));
|
||||
}
|
||||
|
||||
void AutoTester::saveImage(int index) {
|
||||
QPixmap pixmap;
|
||||
pixmap.loadFromData(downloaders[index]->downloadedData());
|
||||
|
||||
QImage image = pixmap.toImage();
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
|
||||
QString fullPathname = _directoryName + "/" + _filenames[index];
|
||||
image.save(fullPathname, 0, 100);
|
||||
|
||||
++_numberOfImagesDownloaded;
|
||||
|
||||
if (_numberOfImagesDownloaded == _numberOfImagesToDownload) {
|
||||
test->finishTestsEvaluation(ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
|
||||
} else {
|
||||
ui.progressBar->setValue(_numberOfImagesDownloaded);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
#define hifi_AutoTester_h
|
||||
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QSignalMapper>
|
||||
#include "ui_AutoTester.h"
|
||||
|
||||
#include "../Downloader.h"
|
||||
#include "../Test.h"
|
||||
|
||||
class AutoTester : public QMainWindow {
|
||||
|
@ -19,19 +22,34 @@ class AutoTester : public QMainWindow {
|
|||
|
||||
public:
|
||||
AutoTester(QWidget *parent = Q_NULLPTR);
|
||||
void downloadImage(const QUrl& url);
|
||||
void downloadImages(const QStringList& URLs, const QString& directoryName, const QStringList& filenames);
|
||||
|
||||
private slots:
|
||||
void on_evaluateTestsButton_clicked();
|
||||
void on_evaluateTestsRecursivelyButton_clicked();
|
||||
void on_createRecursiveScriptButton_clicked();
|
||||
void on_createRecursiveScriptsRecursivelyButton_clicked();
|
||||
void on_createTestButton_clicked();
|
||||
void on_deleteOldSnapshotsButton_clicked();
|
||||
void on_closeButton_clicked();
|
||||
|
||||
void saveImage(int index);
|
||||
|
||||
private:
|
||||
Ui::AutoTesterClass ui;
|
||||
Test* test;
|
||||
|
||||
Test test;
|
||||
std::vector<Downloader*> downloaders;
|
||||
|
||||
// local storage for parameters - folder to store downloaded files in, and a list of their names
|
||||
QString _directoryName;
|
||||
QStringList _filenames;
|
||||
|
||||
// Used to enable passing a parameter to slots
|
||||
QSignalMapper* signalMapper;
|
||||
|
||||
int _numberOfImagesToDownload;
|
||||
int _numberOfImagesDownloaded;
|
||||
int _index;
|
||||
};
|
||||
|
||||
#endif // hifi_AutoTester_h
|
|
@ -17,7 +17,7 @@
|
|||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<x>20</x>
|
||||
<y>300</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
|
@ -30,8 +30,8 @@
|
|||
<widget class="QPushButton" name="createTestButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>360</x>
|
||||
<y>130</y>
|
||||
<x>20</x>
|
||||
<y>30</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
|
@ -44,7 +44,7 @@
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>75</y>
|
||||
<y>135</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
|
@ -66,24 +66,11 @@
|
|||
<string>Create Recursive Script</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="evaluateTestsRecursivelyButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>130</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Evaluate Tests Recursively</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="checkBoxInteractiveMode">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>23</x>
|
||||
<y>40</y>
|
||||
<y>100</y>
|
||||
<width>131</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
|
@ -108,17 +95,17 @@
|
|||
<number>24</number>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="deleteOldSnapshotsButton">
|
||||
<widget class="QPushButton" name="createRecursiveScriptsRecursivelyButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>360</x>
|
||||
<y>240</y>
|
||||
<y>140</y>
|
||||
<width>220</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete Old Snapshots</string>
|
||||
<string>Create Recursive Scripts Recursively</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
|
@ -145,4 +132,4 @@
|
|||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
</ui>
|
||||
|
|