Merge branch 'master' into menu-updates

This commit is contained in:
vladest 2018-05-10 10:36:38 +02:00
commit 62aa2df836
40 changed files with 421 additions and 313 deletions

View file

@ -84,11 +84,9 @@ Item {
height: undefined // invalidate so that the image's size sets the height
focus: true
style: OriginalStyles.ButtonStyle {
background: Image {
id: buttonImage
source: "../../images/steam-sign-in.png"
}
background: Image {
id: buttonImage
source: "../../images/steam-sign-in.png"
}
onClicked: signInBody.login()
}

View file

@ -787,7 +787,7 @@ Rectangle {
}
lightboxPopup.button2text = "CONFIRM";
lightboxPopup.button2method = function() {
Commerce.replaceContentSet(root.itemHref);
Commerce.replaceContentSet(root.itemHref, root.certificateId);
lightboxPopup.visible = false;
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();

View file

@ -145,16 +145,6 @@
#include <avatars-renderer/ScriptAvatar.h>
#include <RenderableEntityItem.h>
#include <AnimationLogging.h>
#include <AvatarLogging.h>
#include <ScriptEngineLogging.h>
#include <ModelFormatLogging.h>
#include <controllers/Logging.h>
#include <NetworkLogging.h>
#include <shared/StorageLogging.h>
#include <ScriptEngineLogging.h>
#include <ui/Logging.h>
#include "AudioClient.h"
#include "audio/AudioScope.h"
#include "avatar/AvatarManager.h"
@ -743,6 +733,11 @@ extern DisplayPluginList getDisplayPlugins();
extern InputPluginList getInputPlugins();
extern void saveInputPluginSettings(const InputPluginList& plugins);
// Parameters used for running tests from teh command line
const QString TEST_SCRIPT_COMMAND { "--testScript" };
const QString TEST_QUIT_WHEN_FINISHED_OPTION { "quitWhenFinished" };
const QString TEST_SNAPSHOT_LOCATION_COMMAND { "--testSnapshotLocation" };
bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
const char** constArgv = const_cast<const char**>(argv);
@ -777,7 +772,22 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
// Ignore any previous crashes if running from command line with a test script.
bool inTestMode { false };
for (int i = 0; i < argc; ++i) {
QString parameter(argv[i]);
if (parameter == TEST_SCRIPT_COMMAND) {
inTestMode = true;
break;
}
}
bool previousSessionCrashed { false };
if (!inTestMode) {
previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
}
// get dir to use for cache
static const auto CACHE_SWITCH = "--cache";
QString cacheDir = getCmdOption(argc, const_cast<const char**>(argv), CACHE_SWITCH);
@ -996,13 +1006,30 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
setProperty(hifi::properties::CRASHED, _previousSessionCrashed);
{
const QString TEST_SCRIPT = "--testScript";
const QStringList args = arguments();
for (int i = 0; i < args.size() - 1; ++i) {
if (args.at(i) == TEST_SCRIPT) {
if (args.at(i) == TEST_SCRIPT_COMMAND && (i + 1) < args.size()) {
QString testScriptPath = args.at(i + 1);
if (QFileInfo(testScriptPath).exists()) {
// If the URL scheme is http(s) or ftp, then use as is, else - treat it as a local file
// This is done so as not break previous command line scripts
if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) {
setProperty(hifi::properties::TEST, QUrl::fromUserInput(testScriptPath));
} else if (QFileInfo(testScriptPath).exists()) {
setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath));
}
// quite when finished parameter must directly follow the test script
if ((i + 2) < args.size() && args.at(i + 2) == TEST_QUIT_WHEN_FINISHED_OPTION) {
quitWhenFinished = true;
}
} else if (args.at(i) == TEST_SNAPSHOT_LOCATION_COMMAND) {
// Set test snapshot location only if it is a writeable directory
QString pathname(args.at(i + 1));
QFileInfo fileInfo(pathname);
if (fileInfo.isDir() && fileInfo.isWritable()) {
testSnapshotLocation = pathname;
}
}
}
@ -1335,8 +1362,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Needs to happen AFTER the render engine initialization to access its configuration
initializeUi();
updateVerboseLogging();
init();
qCDebug(interfaceapp, "init() complete.");
@ -1676,6 +1701,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
loadSettings();
updateVerboseLogging();
// Now that we've loaded the menu and thus switched to the previous display plugin
// we can unlock the desktop repositioning code, since all the positions will be
// relative to the desktop size for this plugin
@ -2128,14 +2155,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
return entityServerNode && !isPhysicsEnabled();
});
_snapshotSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/snap.wav"));
_snapshotSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/snapshot/snap.wav"));
QVariant testProperty = property(hifi::properties::TEST);
qDebug() << testProperty;
if (testProperty.isValid()) {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
const auto testScript = property(hifi::properties::TEST).toUrl();
scriptEngines->loadScript(testScript, false);
// Set last parameter to exit interface when the test script finishes, if so requested
scriptEngines->loadScript(testScript, false, false, false, false, quitWhenFinished);
// This is done so we don't get a "connection time-out" message when we haven't passed in a URL.
if (arguments().contains("--url")) {
auto reply = SandboxUtils::getStatus();
connect(reply, &QNetworkReply::finished, this, [=] {
handleSandboxStatus(reply);
});
}
} else {
PROFILE_RANGE(render, "GetSandboxStatus");
auto reply = SandboxUtils::getStatus();
@ -2210,43 +2247,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
}
void Application::updateVerboseLogging() {
bool enable = Menu::getInstance()->isOptionChecked(MenuOption::VerboseLogging);
auto menu = Menu::getInstance();
if (!menu) {
return;
}
bool enable = menu->isOptionChecked(MenuOption::VerboseLogging);
const_cast<QLoggingCategory*>(&animation())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&animation())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&avatars())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&avatars())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&scriptengine())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&scriptengine())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&modelformat())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&modelformat())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&controllers())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&controllers())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&resourceLog())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&resourceLog())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&asset_client())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&asset_client())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&messages_client())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&messages_client())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&storagelogging())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&storagelogging())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&uiLogging())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&uiLogging())->setEnabled(QtInfoMsg, enable);
const_cast<QLoggingCategory*>(&glLogging())->setEnabled(QtDebugMsg, enable);
const_cast<QLoggingCategory*>(&glLogging())->setEnabled(QtInfoMsg, enable);
QString rules = "*.debug=%1\n"
"*.info=%1";
rules = rules.arg(enable ? "true" : "false");
QLoggingCategory::setFilterRules(rules);
}
void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
@ -3175,7 +3185,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
qCDebug(interfaceapp) << "First run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("default location") : addressLookupString);
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
#else
showHelp();
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
#endif
@ -3637,7 +3646,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} else {
showCursor(Cursor::Icon::DEFAULT);
}
} else {
} else if (!event->isAutoRepeat()){
resetSensors(true);
}
break;
@ -7460,7 +7469,7 @@ void Application::loadAvatarBrowser() const {
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
// Get a screenshot and save it
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename);
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, testSnapshotLocation);
// If we're not doing an animated snapshot as well...
if (!includeAnimated) {
// Tell the dependency manager that the capture of the still snapshot has taken place.
@ -7474,7 +7483,7 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
void Application::takeSecondaryCameraSnapshot(const QString& filename) {
postLambdaEvent([filename, this] {
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename);
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, testSnapshotLocation);
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true);
});
}

View file

@ -750,5 +750,8 @@ private:
std::atomic<bool> _pendingIdleEvent { true };
std::atomic<bool> _pendingRenderEvent { true };
QString testSnapshotLocation;
bool quitWhenFinished { false };
};
#endif // hifi_Application_h

View file

@ -74,9 +74,9 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
return data;
}
QString Snapshot::saveSnapshot(QImage image, const QString& filename) {
QString Snapshot::saveSnapshot(QImage image, const QString& filename, const QString& pathname) {
QFile* snapshotFile = savedFileForSnapshot(image, false, filename);
QFile* snapshotFile = savedFileForSnapshot(image, false, filename, pathname);
// we don't need the snapshot file, so close it, grab its filename and delete it
snapshotFile->close();
@ -93,7 +93,7 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
return static_cast<QTemporaryFile*>(savedFileForSnapshot(image, true));
}
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename) {
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename, const QString& userSelectedPathname) {
// adding URL to snapshot
QUrl currentURL = DependencyManager::get<AddressManager>()->currentPublicAddress();
@ -118,7 +118,13 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
const int IMAGE_QUALITY = 100;
if (!isTemporary) {
QString snapshotFullPath = snapshotsLocation.get();
// If user has requested specific path then use it, else use the application value
QString snapshotFullPath;
if (!userSelectedPathname.isNull()) {
snapshotFullPath = userSelectedPathname;
} else {
snapshotFullPath = snapshotsLocation.get();
}
if (snapshotFullPath.isEmpty()) {
snapshotFullPath = OffscreenUi::getExistingDirectory(nullptr, "Choose Snapshots Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));

View file

@ -38,7 +38,7 @@ class Snapshot : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
static QString saveSnapshot(QImage image, const QString& filename);
static QString saveSnapshot(QImage image, const QString& filename, const QString& pathname = QString());
static QTemporaryFile* saveTempSnapshot(QImage image);
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
@ -52,7 +52,10 @@ public slots:
Q_INVOKABLE QString getSnapshotsLocation();
Q_INVOKABLE void setSnapshotsLocation(const QString& location);
private:
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary, const QString& userSelectedFilename = QString());
static QFile* savedFileForSnapshot(QImage& image,
bool isTemporary,
const QString& userSelectedFilename = QString(),
const QString& userSelectedPathname = QString());
};
#endif // hifi_Snapshot_h

View file

@ -1070,7 +1070,7 @@ bool EntityItem::stepKinematicMotion(float timeElapsed) {
const float MAX_TIME_ELAPSED = 1.0f; // seconds
if (timeElapsed > MAX_TIME_ELAPSED) {
qCWarning(entities) << "kinematic timestep = " << timeElapsed << " truncated to " << MAX_TIME_ELAPSED;
qCDebug(entities) << "kinematic timestep = " << timeElapsed << " truncated to " << MAX_TIME_ELAPSED;
}
timeElapsed = glm::min(timeElapsed, MAX_TIME_ELAPSED);

View file

@ -17,7 +17,6 @@ std::string GL41Backend::getBackendShaderHeader() const {
static const std::string header(
R"SHADER(#version 410 core
#define GPU_GL410
#define PRECISIONQ
#define BITFIELD int
)SHADER");
return header;

View file

@ -18,7 +18,6 @@ std::string GL45Backend::getBackendShaderHeader() const {
static const std::string header(
R"SHADER(#version 450 core
#define GPU_GL450
#define PRECISIONQ
#define BITFIELD int
)SHADER"
#ifdef GPU_SSBO_TRANSFORM_OBJECT

View file

@ -17,10 +17,9 @@ std::string GLESBackend::getBackendShaderHeader() const {
static const std::string header(
R"SHADER(#version 310 es
#extension GL_EXT_texture_buffer : enable
precision lowp float; // check precision 2
precision lowp samplerBuffer;
precision lowp sampler2DShadow;
#define PRECISIONQ highp
precision highp float;
precision highp samplerBuffer;
precision highp sampler2DShadow;
#define BITFIELD highp int
)SHADER");
return header;

View file

@ -50,47 +50,50 @@ enum Type : uint8_t {
};
// Array providing the size in bytes for a given scalar type
static const int TYPE_SIZE[NUM_TYPES] = {
4,
4,
4,
2,
2,
2,
1,
1,
4, // FLOAT
4, // INT32
4, // UINT32
2, // HALF
2, // INT16
2, // UINT16
1, // INT8
1, // UINT8
// normalized values
4,
4,
2,
2,
1,
1,
4,
4, // NINT32
4, // NUINT32
2, // NINT16
2, // NUINT16
1, // NINT8
1, // NUINT8
1, // NUINT2
1, // NINT2_10_10_10
1
1, // COMPRESSED
};
// Array answering the question Does this type is integer or not
static const bool TYPE_IS_INTEGER[NUM_TYPES] = {
false,
true,
true,
false,
true,
true,
true,
true,
false, // FLOAT
true, // INT32
true, // UINT32
false, // HALF
true, // INT16
true, // UINT16
true, // INT8
true, // UINT8
// Normalized values
false,
false,
false,
false,
false,
false,
false,
false, // NINT32
false, // NUINT32
false, // NINT16
false, // NUINT16
false, // NINT8
false, // NUINT8
false, // NUINT2
false, // NINT2_10_10_10
false,
false, // COMPRESSED
};
// Dimension of an Element
@ -367,9 +370,9 @@ public:
static const Element PART_DRAWCALL;
protected:
uint8 _semantic;
uint8 _dimension : 4;
uint8 _type : 4;
uint16 _semantic : 7;
uint16 _dimension : 4;
uint16 _type : 5;
};

View file

@ -46,13 +46,14 @@ struct GPUKTXPayload {
memcpy(data, &_samplerDesc, sizeof(Sampler::Desc));
data += sizeof(Sampler::Desc);
// We can't copy the bitset in Texture::Usage in a crossplateform manner
// So serialize it manually
*(uint32*)data = _usage._flags.to_ulong();
uint32 usageData = _usage._flags.to_ulong();
memcpy(data, &usageData, sizeof(uint32));
data += sizeof(uint32);
*(TextureUsageType*)data = _usageType;
memcpy(data, &_usageType, sizeof(TextureUsageType));
data += sizeof(TextureUsageType);
return data + PADDING;
@ -77,13 +78,15 @@ struct GPUKTXPayload {
memcpy(&_samplerDesc, data, sizeof(Sampler::Desc));
data += sizeof(Sampler::Desc);
// We can't copy the bitset in Texture::Usage in a crossplateform manner
// So unserialize it manually
_usage = Texture::Usage(*(const uint32*)data);
uint32 usageData;
memcpy(&usageData, data, sizeof(uint32));
_usage = Texture::Usage(usageData);
data += sizeof(uint32);
_usageType = *(const TextureUsageType*)data;
memcpy(&_usageType, data, sizeof(TextureUsageType));
return true;
}
@ -710,4 +713,4 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E
return false;
}
return true;
}
}

View file

@ -34,7 +34,7 @@ vec3 getLightIrradiance(Light l) { return lightIrradiance_getIrradiance(l.irradi
// Light Ambient
struct LightAmbient {
PRECISIONQ vec4 _ambient;
vec4 _ambient;
SphericalHarmonics _ambientSphere;
mat4 transform;
};

View file

@ -14,9 +14,9 @@
#define LightIrradianceConstRef LightIrradiance
struct LightIrradiance {
PRECISIONQ vec4 colorIntensity;
vec4 colorIntensity;
// falloffRadius, cutoffRadius, falloffSpot, spare
PRECISIONQ vec4 attenuation;
vec4 attenuation;
};

View file

@ -16,8 +16,8 @@
#define LightVolumeConstRef LightVolume
struct LightVolume {
PRECISIONQ vec4 positionRadius;
PRECISIONQ vec4 directionSpotCos;
vec4 positionRadius;
vec4 directionSpotCos;
};
bool lightVolume_isPoint(LightVolume lv) { return bool(lv.directionSpotCos.w < 0.f); }

View file

@ -15,10 +15,10 @@
// to what is provided by the uniform buffer, or the material key has the wrong bits
struct Material {
PRECISIONQ vec4 _emissiveOpacity;
PRECISIONQ vec4 _albedoRoughness;
PRECISIONQ vec4 _fresnelMetallic;
PRECISIONQ vec4 _scatteringSpare2Key;
vec4 _emissiveOpacity;
vec4 _albedoRoughness;
vec4 _fresnelMetallic;
vec4 _scatteringSpare2Key;
};
uniform materialBuffer {
@ -64,7 +64,4 @@ const BITFIELD OCCLUSION_MAP_BIT = 0x00004000;
const BITFIELD LIGHTMAP_MAP_BIT = 0x00008000;
const BITFIELD SCATTERING_MAP_BIT = 0x00010000;
#ifdef GL_ES
precision lowp float;
#endif
<@endif@>

View file

@ -16,15 +16,15 @@
#define SphericalHarmonicsConstRef SphericalHarmonics
struct SphericalHarmonics {
PRECISIONQ vec4 L00;
PRECISIONQ vec4 L1m1;
PRECISIONQ vec4 L10;
PRECISIONQ vec4 L11;
PRECISIONQ vec4 L2m2;
PRECISIONQ vec4 L2m1;
PRECISIONQ vec4 L20;
PRECISIONQ vec4 L21;
PRECISIONQ vec4 L22;
vec4 L00;
vec4 L1m1;
vec4 L10;
vec4 L11;
vec4 L2m2;
vec4 L2m1;
vec4 L20;
vec4 L21;
vec4 L22;
};
vec4 sphericalHarmonics_evalSphericalLight(SphericalHarmonicsConstRef sh, vec3 direction) {

View file

@ -6,8 +6,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Controllers_Logging_h
#define hifi_Controllers_Logging_h
#ifndef hifi_QML_Logging_h
#define hifi_QML_Logging_h
#include <QLoggingCategory>

View file

@ -14,10 +14,10 @@
<@func declareLightingModel()@>
struct LightingModel {
PRECISIONQ vec4 _UnlitEmissiveLightmapBackground;
PRECISIONQ vec4 _ScatteringDiffuseSpecularAlbedo;
PRECISIONQ vec4 _AmbientDirectionalPointSpot;
PRECISIONQ vec4 _ShowContourObscuranceWireframe;
vec4 _UnlitEmissiveLightmapBackground;
vec4 _ScatteringDiffuseSpecularAlbedo;
vec4 _AmbientDirectionalPointSpot;
vec4 _ShowContourObscuranceWireframe;
};
uniform lightingModelBuffer{

View file

@ -101,8 +101,6 @@ int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature
int scriptEnginePointerMetaID = qRegisterMetaType<ScriptEnginePointer>();
Q_LOGGING_CATEGORY(scriptengineScript, "hifi.scriptengine.script")
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
QString message = "";
for (int i = 0; i < context->argumentCount(); i++) {
@ -115,9 +113,9 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
if (ScriptEngine *scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
scriptEngine->print(message);
// prefix the script engine name to help disambiguate messages in the main debug log
qCDebug(scriptengineScript, "[%s] %s", qUtf8Printable(scriptEngine->getFilename()), qUtf8Printable(message));
qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(scriptEngine->getFilename()), qUtf8Printable(message));
} else {
qCDebug(scriptengineScript, "%s", qUtf8Printable(message));
qCDebug(scriptengine_script, "%s", qUtf8Printable(message));
}
return QScriptValue();

View file

@ -526,6 +526,9 @@ public:
void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
bool isUserLoaded() const { return _isUserLoaded; }
void setQuitWhenFinished(const bool quitWhenFinished) { _quitWhenFinished = quitWhenFinished; }
bool isQuitWhenFinished() const { return _quitWhenFinished; }
// NOTE - this is used by the TypedArray implementation. we need to review this for thread safety
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
@ -768,6 +771,8 @@ protected:
std::atomic<bool> _isUserLoaded { false };
bool _isReloading { false };
std::atomic<bool> _quitWhenFinished;
ArrayBufferClass* _arrayBufferClass;
AssetScriptingInterface* _assetScriptingInterface;

View file

@ -13,3 +13,4 @@
Q_LOGGING_CATEGORY(scriptengine, "hifi.scriptengine")
Q_LOGGING_CATEGORY(scriptengine_module, "hifi.scriptengine.module")
Q_LOGGING_CATEGORY(scriptengine_script, "hifi.scriptengine.script")

View file

@ -16,6 +16,7 @@
Q_DECLARE_LOGGING_CATEGORY(scriptengine)
Q_DECLARE_LOGGING_CATEGORY(scriptengine_module)
Q_DECLARE_LOGGING_CATEGORY(scriptengine_script)
#endif // hifi_ScriptEngineLogging_h

View file

@ -347,7 +347,8 @@ void ScriptEngines::saveScripts() {
{
QReadLocker lock(&_scriptEnginesHashLock);
for (auto it = _scriptEnginesHash.begin(); it != _scriptEnginesHash.end(); ++it) {
if (it.value() && it.value()->isUserLoaded()) {
// Save user-loaded scripts, only if they are set to quit when finished
if (it.value() && it.value()->isUserLoaded() && !it.value()->isQuitWhenFinished()) {
auto normalizedUrl = normalizeScriptURL(it.key());
list.append(normalizedUrl.toString());
}
@ -456,7 +457,7 @@ void ScriptEngines::reloadAllScripts() {
}
ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor,
bool activateMainWindow, bool reload) {
bool activateMainWindow, bool reload, bool quitWhenFinished) {
if (thread() != QThread::currentThread()) {
ScriptEnginePointer result { nullptr };
BLOCKING_INVOKE_METHOD(this, "loadScript", Q_RETURN_ARG(ScriptEnginePointer, result),
@ -488,6 +489,7 @@ ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool i
scriptEngine = ScriptEnginePointer(new ScriptEngine(_context, NO_SCRIPT, "about:" + scriptFilename.fileName()));
addScriptEngine(scriptEngine);
scriptEngine->setUserLoaded(isUserLoaded);
scriptEngine->setQuitWhenFinished(quitWhenFinished);
if (scriptFilename.isEmpty() || !scriptUrl.isValid()) {
launchScriptEngine(scriptEngine);
@ -496,6 +498,11 @@ ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool i
connect(scriptEngine.data(), &ScriptEngine::scriptLoaded, this, &ScriptEngines::onScriptEngineLoaded);
connect(scriptEngine.data(), &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError);
// Shutdown Interface when script finishes, if requested
if (quitWhenFinished) {
connect(scriptEngine.data(), &ScriptEngine::finished, this, &ScriptEngines::quitWhenFinished);
}
// get the script engine object to load the script at the designated script URL
scriptEngine->loadURL(scriptUrl, reload);
}
@ -536,6 +543,10 @@ void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) {
emit scriptCountChanged();
}
void ScriptEngines::quitWhenFinished() {
qApp->quit();
}
int ScriptEngines::runScriptInitializers(ScriptEnginePointer scriptEngine) {
int ii=0;
for (auto initializer : _scriptInitializers) {

View file

@ -88,10 +88,11 @@ public:
* @param {boolean} [loadScriptFromEditor=false]
* @param {boolean} [activateMainWindow=false]
* @param {boolean} [reload=false]
* @param {boolean} [quitWhenFinished=false]
* @returns {boolean}
*/
Q_INVOKABLE ScriptEnginePointer loadScript(const QUrl& scriptFilename = QString(),
bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false);
bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false, bool quitWhenFinished = false);
/**jsdoc
* @function ScriptDiscoveryService.stopScript
@ -266,6 +267,7 @@ protected:
ScriptEnginePointer reloadScript(const QString& scriptName, bool isUserLoaded = true) { return loadScript(scriptName, isUserLoaded, false, false, true); }
void removeScriptEngine(ScriptEnginePointer);
void onScriptEngineLoaded(const QString& scriptFilename);
void quitWhenFinished();
void onScriptEngineError(const QString& scriptFilename);
void launchScriptEngine(ScriptEnginePointer);

View file

@ -106,10 +106,11 @@ public slots:
void mute(const QUuid& nodeID);
/**jsdoc
* Get the user name and machine fingerprint associated with the given UUID. This will only do anything if you're an admin
* of the domain you're in.
* @function Users.getUsernameFromID
* @param {Uuid} nodeID The node or session ID of the user whose username you want.
* Request the user name and machine fingerprint associated with the given UUID. The user name will be returned in a
* {@link Users.usernameFromIDReply|usernameFromIDReply} signal. This will only do anything if you're an admin of the domain
* you're in.
* @function Users.requestUsernameFromID
* @param {Uuid} nodeID The node or session ID of the user whose user name you want.
*/
void requestUsernameFromID(const QUuid& nodeID);
@ -170,7 +171,8 @@ signals:
void enteredIgnoreRadius();
/**jsdoc
* Notifies scripts of the user name and machine fingerprint associated with a UUID.
* Triggered in response to a {@link Users.requestUsernameFromID|requestUsernameFromID} call. Provides the user name and
* machine fingerprint associated with a UUID.
* Username and machineFingerprint will be their default constructor output if the requesting user isn't an admin.
* @function Users.usernameFromIDReply
* @param {Uuid} nodeID

View file

@ -564,6 +564,9 @@ Script.include("/~/system/libraries/Xform.js");
}
} else if (this.distanceRotating) {
this.distanceRotate(otherFarGrabModule);
} else if (this.highlightedEntity) {
Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity);
this.highlightedEntity = null;
}
}
return this.exitIfDisabled(controllerData);

View file

@ -56,6 +56,12 @@
return canGrabEntity;
};
this.clearAll = function() {
this.highlightedEntities.forEach(function(entity) {
dispatcherUtils.unhighlightTargetEntity(entity);
});
};
this.hasHyperLink = function(props) {
return (props.href !== "" && props.href !== undefined);
};
@ -89,7 +95,9 @@
}
if (this.isGrabable(controllerData, props) || this.hasHyperLink(props)) {
dispatcherUtils.highlightTargetEntity(props.id);
newHighlightedEntities.push(props.id);
if (newHighlightedEntities.indexOf(props.id) < 0) {
newHighlightedEntities.push(props.id);
}
}
}
@ -119,7 +127,6 @@
if (channel === 'Hifi-unhighlight-entity') {
try {
data = JSON.parse(message);
var hand = data.hand;
if (hand === dispatcherUtils.LEFT_HAND) {
leftHighlightNearbyEntities.removeEntityFromHighlightList(data.entityID);
@ -129,6 +136,9 @@
} catch (e) {
print("Failed to parse message");
}
} else if (channel === 'Hifi-unhighlight-all') {
leftHighlightNearbyEntities.clearAll();
rightHighlightNearbyEntities.clearAll();
}
}
};
@ -143,6 +153,7 @@
dispatcherUtils.disableDispatcherModule("RightHighlightNearbyEntities");
}
Messages.subscribe('Hifi-unhighlight-entity');
Messages.subscribe('Hifi-unhighlight-all');
Messages.messageReceived.connect(handleMessage);
Script.scriptEnding.connect(cleanup);
}());

View file

@ -78,6 +78,7 @@ Script.include("/~/system/libraries/utils.js");
if (controllerData.triggerValues[this.hand] < TRIGGER_ON_VALUE) {
this.triggerClicked = false;
}
Messages.sendLocalMessage('Hifi-unhighlight-all', '');
return makeRunningValues(true, [], []);
}
this.triggerClicked = false;

View file

@ -12,14 +12,16 @@
/* jslint bitwise: true */
/* global Script, print, Entities, Picks, HMD */
/* global Script, print, Entities, Picks, HMD, Controller, MyAvatar, isInEditMode*/
(function() {
Script.include("/~/system/libraries/utils.js");
var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js");
function MouseHighlightEntities() {
this.highlightedEntity = null;
this.grabbedEntity = null;
this.parameters = dispatcherUtils.makeDispatcherModuleParameters(
5,
@ -27,13 +29,18 @@
[],
100);
this.setGrabbedEntity = function(entity) {
this.grabbedEntity = entity;
this.highlightedEntity = null;
};
this.isReady = function(controllerData) {
if (HMD.active) {
if (this.highlightedEntity) {
dispatcherUtils.unhighlightTargetEntity(this.highlightedEntity);
this.highlightedEntity = null;
}
} else {
} else if (!this.grabbedEntity && !isInEditMode()) {
var pickResult = controllerData.mouseRayPick;
if (pickResult.type === Picks.INTERSECTED_ENTITY) {
var targetEntityID = pickResult.objectID;
@ -56,6 +63,9 @@
this.highlightedEntity = targetEntityID;
}
}
} else if (this.highlightedEntity) {
dispatcherUtils.unhighlightTargetEntity(this.highlightedEntity);
this.highlightedEntity = null;
}
}
@ -70,8 +80,29 @@
var mouseHighlightEntities = new MouseHighlightEntities();
dispatcherUtils.enableDispatcherModule("MouseHighlightEntities", mouseHighlightEntities);
var handleMessage = function(channel, message, sender) {
var data;
if (sender === MyAvatar.sessionUUID) {
if (channel === 'Hifi-Object-Manipulation') {
try {
data = JSON.parse(message);
if (data.action === 'grab') {
var grabbedEntity = data.grabbedEntity;
mouseHighlightEntities.setGrabbedEntity(grabbedEntity);
} else if (data.action === 'release') {
mouseHighlightEntities.setGrabbedEntity(null);
}
} catch (e) {
print("Warning: mouseHighlightEntities -- error parsing Hifi-Object-Manipulation: " + message);
}
}
}
};
function cleanup() {
dispatcherUtils.disableDispatcherModule("MouseHighlightEntities");
}
Messages.subscribe('Hifi-Object-Manipulation');
Messages.messageReceived.connect(handleMessage);
Script.scriptEnding.connect(cleanup);
})();

View file

@ -409,7 +409,7 @@ Grabber.prototype.pressEvent = function(event) {
var args = "mouse";
Entities.callEntityMethod(this.entityID, "startDistanceGrab", args);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
Messages.sendLocalMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'grab',
grabbedEntity: this.entityID
}));
@ -450,7 +450,7 @@ Grabber.prototype.releaseEvent = function(event) {
var args = "mouse";
Entities.callEntityMethod(this.entityID, "releaseGrab", args);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
Messages.sendLocalMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'release',
grabbedEntity: this.entityID,
joint: "mouse"

View file

@ -1165,17 +1165,11 @@ function setupModelMenus() {
});
modelMenuAddedDelete = true;
}
Menu.addMenuItem({
menuName: "Edit",
menuItemName: "Entity List...",
afterItem: "Entities",
grouping: "Advanced"
});
Menu.addMenuItem({
menuName: "Edit",
menuItemName: "Parent Entity to Last",
afterItem: "Entity List...",
afterItem: "Entities",
grouping: "Advanced"
});
@ -1297,7 +1291,6 @@ function cleanupModelMenus() {
Menu.removeMenuItem("Edit", "Parent Entity to Last");
Menu.removeMenuItem("Edit", "Unparent Entity");
Menu.removeMenuItem("Edit", "Entity List...");
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
@ -1659,8 +1652,6 @@ function handeMenuEvent(menuItem) {
Window.promptTextChanged.connect(onPromptTextChanged);
Window.promptAsync("URL of SVO to import", "");
}
} else if (menuItem === "Entity List...") {
entityListTool.toggleVisible();
} else if (menuItem === "Select All Entities In Box") {
selectAllEtitiesInCurrentSelectionBox(false);
} else if (menuItem === "Select All Entities Touching Box") {

View file

@ -789,10 +789,14 @@ var selectionDisplay = null; // for gridTool.js to ignore
var savedDisablePreviewOptionLocked = false;
var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview");;
function maybeEnableHMDPreview() {
setTabletVisibleInSecondaryCamera(true);
DesktopPreviewProvider.setPreviewDisabledReason("USER");
Menu.setIsOptionChecked("Disable Preview", savedDisablePreviewOption);
savedDisablePreviewOptionLocked = false;
// Set a small timeout to prevent sensitive data from being shown during
// UI fade
Script.setTimeout(function () {
setTabletVisibleInSecondaryCamera(true);
DesktopPreviewProvider.setPreviewDisabledReason("USER");
Menu.setIsOptionChecked("Disable Preview", savedDisablePreviewOption);
savedDisablePreviewOptionLocked = false;
}, 150);
}
// Function Name: fromQml()

View file

@ -9,6 +9,8 @@
//
#include "Downloader.h"
#include <QtWidgets/QMessageBox>
Downloader::Downloader(QUrl imageUrl, QObject *parent) : QObject(parent) {
connect(
&_networkAccessManager, SIGNAL (finished(QNetworkReply*)),
@ -20,6 +22,12 @@ Downloader::Downloader(QUrl imageUrl, QObject *parent) : QObject(parent) {
}
void Downloader::fileDownloaded(QNetworkReply* reply) {
QNetworkReply::NetworkError error = reply->error();
if (error != QNetworkReply::NetworkError::NoError) {
QMessageBox::information(0, "Test Aborted", "Failed to download image: " + reply->errorString());
return;
}
_downloadedData = reply->readAll();
//emit a signal

View file

@ -27,7 +27,7 @@ Test::Test() {
mismatchWindow.setModal(true);
}
bool Test::createTestResultsFolderPath(QString directory) {
bool Test::createTestResultsFolderPath(const QString& directory) {
QDateTime now = QDateTime::currentDateTime();
testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT);
QDir testResultsFolder(testResultsFolderPath);
@ -72,7 +72,7 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
QImage expectedImage(expectedImagesFullFilenames[i]);
if (resultImage.width() != expectedImage.width() || resultImage.height() != expectedImage.height()) {
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Images are not the same size");
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Images are not the same size");
exit(-1);
}
@ -80,7 +80,7 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
try {
similarityIndex = imageComparer.compareImages(resultImage, expectedImage);
} catch (...) {
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Image not in expected format");
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Image not in expected format");
exit(-1);
}
@ -125,22 +125,22 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
return success;
}
void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
void Test::appendTestResultsToFile(const QString& testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
if (!QDir().exists(testResultsFolderPath)) {
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + testResultsFolderPath + " not found");
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + testResultsFolderPath + " not found");
exit(-1);
}
QString failureFolderPath { testResultsFolderPath + "/" + "Failure_" + QString::number(index) };
if (!QDir().mkdir(failureFolderPath)) {
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create folder " + failureFolderPath);
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create folder " + failureFolderPath);
exit(-1);
}
++index;
QFile descriptionFile(failureFolderPath + "/" + TEST_RESULTS_FILENAME);
if (!descriptionFile.open(QIODevice::ReadWrite)) {
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + TEST_RESULTS_FILENAME);
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + TEST_RESULTS_FILENAME);
exit(-1);
}
@ -160,24 +160,30 @@ 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: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile);
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "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: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile);
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile);
exit(-1);
}
comparisonImage.save(failureFolderPath + "/" + "Difference Image.jpg");
}
void Test::startTestsEvaluation() {
// Get list of JPEG images in folder, sorted by name
pathToTestResultsDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
if (pathToTestResultsDirectory == "") {
void Test::startTestsEvaluation(const QString& testFolder) {
QString pathToTestResultsDirectory;
if (testFolder.isNull()) {
// Get list of JPEG images in folder, sorted by name
pathToTestResultsDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
} else {
pathToTestResultsDirectory = testFolder;
}
if (pathToTestResultsDirectory == QString()) {
return;
}
@ -221,8 +227,8 @@ void Test::startTestsEvaluation() {
QString expectedImageFilenameTail = currentFilename.left(currentFilename.length() - 4).right(NUM_DIGITS);
QString expectedImageStoredFilename = EXPECTED_IMAGE_PREFIX + expectedImageFilenameTail + ".png";
QString imageURLString("https://github.com/" + githubUser + "/hifi_tests/blob/" + gitHubBranch + "/" +
expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename + "?raw=true");
QString imageURLString("https://raw.githubusercontent.com/" + githubUser + "/hifi_tests/" + gitHubBranch + "/" +
expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename);
expectedImagesURLs << imageURLString;
@ -237,19 +243,21 @@ void Test::startTestsEvaluation() {
autoTester->downloadImages(expectedImagesURLs, pathToTestResultsDirectory, expectedImagesFilenames);
}
void Test::finishTestsEvaluation(bool interactiveMode, QProgressBar* progressBar) {
bool success = compareImageLists(interactiveMode, progressBar);
void Test::finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar) {
bool success = compareImageLists((!isRunningFromCommandline && 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");
if (!isRunningFromCommandline) {
if (success) {
QMessageBox::information(0, "Success", "All images are as expected");
} else {
QMessageBox::information(0, "Failure", "One or more images are not as expected");
}
}
zipAndDeleteTestResultsFolder();
}
bool Test::isAValidDirectory(QString pathname) {
bool Test::isAValidDirectory(const QString& pathname) {
// Only process directories
QDir dir(pathname);
if (!dir.exists()) {
@ -264,7 +272,7 @@ bool Test::isAValidDirectory(QString pathname) {
return true;
}
QString Test::extractPathFromTestsDown(QString fullPath) {
QString Test::extractPathFromTestsDown(const QString& fullPath) {
// `fullPath` includes the full path to the test. We need the portion below (and including) `tests`
QStringList pathParts = fullPath.split('/');
int i{ 0 };
@ -273,7 +281,7 @@ QString Test::extractPathFromTestsDown(QString fullPath) {
}
if (i == pathParts.length()) {
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Bad testPathname");
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Bad testPathname");
exit(-1);
}
@ -342,14 +350,14 @@ void Test::createAllRecursiveScripts() {
}
}
messageBox.information(0, "Success", "Scripts have been created");
QMessageBox::information(0, "Success", "Scripts have been created");
}
void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode) {
void Test::createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode) {
const QString recursiveTestsFilename("testRecursive.js");
QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename);
if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) {
messageBox.critical(0,
QMessageBox::critical(0,
"Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
"Failed to create \"" + recursiveTestsFilename + "\" in directory \"" + topLevelDirectory + "\""
);
@ -358,12 +366,15 @@ void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode
}
QTextStream textStream(&allTestsFilename);
textStream << "// This is an automatically generated file, created by auto-tester" << endl << endl;
const QString DATE_TIME_FORMAT("MMM d yyyy, h:mm");
textStream << "// This is an automatically generated file, created by auto-tester on " << QDateTime::currentDateTime().toString(DATE_TIME_FORMAT) << endl << endl;
textStream << "var autoTester = Script.require(\"https://github.com/" + githubUser + "/hifi_tests/blob/"
+ gitHubBranch + "/tests/utils/autoTester.js?raw=true\");" << endl;
+ gitHubBranch + "/tests/utils/autoTester.js?raw=true\");" << endl << endl;
textStream << "autoTester.enableRecursive();" << endl << endl;
textStream << "autoTester.enableRecursive();" << endl;
textStream << "autoTester.enableAuto();" << endl << endl;
QVector<QString> testPathnames;
@ -398,7 +409,7 @@ void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode
}
if (interactiveMode && testPathnames.length() <= 0) {
messageBox.information(0, "Failure", "No \"" + TEST_FILENAME + "\" files found");
QMessageBox::information(0, "Failure", "No \"" + TEST_FILENAME + "\" files found");
allTestsFilename.close();
return;
}
@ -409,7 +420,7 @@ void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode
allTestsFilename.close();
if (interactiveMode) {
messageBox.information(0, "Success", "Script has been created");
QMessageBox::information(0, "Success", "Script has been created");
}
}
@ -434,7 +445,7 @@ void Test::createTest() {
QString fullCurrentFilename = imageSourceDirectory + "/" + currentFilename;
if (isInSnapshotFilenameFormat("jpg", currentFilename)) {
if (i >= maxImages) {
messageBox.critical(0, "Error", "More than " + QString::number(maxImages) + " images not supported");
QMessageBox::critical(0, "Error", "More than " + QString::number(maxImages) + " images not supported");
exit(-1);
}
QString newFilename = "ExpectedImage_" + QString::number(i - 1).rightJustified(5, '0') + ".png";
@ -443,14 +454,14 @@ void Test::createTest() {
try {
copyJPGtoPNG(fullCurrentFilename, fullNewFileName);
} catch (...) {
messageBox.critical(0, "Error", "Could not delete existing file: " + currentFilename + "\nTest creation aborted");
QMessageBox::critical(0, "Error", "Could not delete existing file: " + currentFilename + "\nTest creation aborted");
exit(-1);
}
++i;
}
}
messageBox.information(0, "Success", "Test images have been created");
QMessageBox::information(0, "Success", "Test images have been created");
}
ExtractedText Test::getTestScriptLines(QString testFileName) {
@ -459,7 +470,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
QFile inputFile(testFileName);
inputFile.open(QIODevice::ReadOnly);
if (!inputFile.isOpen()) {
messageBox.critical(0,
QMessageBox::critical(0,
"Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
"Failed to open \"" + testFileName
);
@ -498,6 +509,12 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
const QString regexAssertGPU(ws + functionAssertGPU + ws + "\\(" + ws + quotedString + ".*");
const QRegularExpression lineAssertGPU = QRegularExpression(regexAssertGPU);
// Assert the correct amount of memory
const QString functionAssertPhysicalMemoryGB(ws + "autoTester" + ws + "\\." + ws + "assertPhysicalMemoryGB");
const QString regexAssertPhysicalMemoryGB(ws + functionAssertPhysicalMemoryGB + ws + "\\(" + ws + quotedString + ".*");
const QRegularExpression lineAssertPhysicalMemoryGB = QRegularExpression(regexAssertPhysicalMemoryGB);
// Each step is either of the following forms:
// autoTester.addStepSnapshot("Take snapshot"...
// autoTester.addStep("Clean up after test"...
@ -514,18 +531,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
if (lineContainingTitle.match(line).hasMatch()) {
QStringList tokens = line.split('"');
relevantTextFromTest.title = tokens[1];
} else if (lineAssertPlatform.match(line).hasMatch()) {
QStringList platforms = line.split('"');
relevantTextFromTest.platform = platforms[1];
} else if (lineAssertDisplay.match(line).hasMatch()) {
QStringList displays = line.split('"');
relevantTextFromTest.display = displays[1];
} else if (lineAssertCPU.match(line).hasMatch()) {
QStringList cpus = line.split('"');
relevantTextFromTest.cpu = cpus[1];
} else if (lineAssertGPU.match(line).hasMatch()) {
QStringList gpus = line.split('"');
relevantTextFromTest.gpu = gpus[1];
} else if (lineStepSnapshot.match(line).hasMatch()) {
QStringList tokens = line.split('"');
QString nameOfStep = tokens[1];
@ -534,6 +540,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
step->text = nameOfStep;
step->takeSnapshot = true;
relevantTextFromTest.stepList.emplace_back(step);
} else if (lineStep.match(line).hasMatch()) {
QStringList tokens = line.split('"');
QString nameOfStep = tokens[1];
@ -593,15 +600,15 @@ void Test::createAllMDFiles() {
}
}
messageBox.information(0, "Success", "MD files have been created");
QMessageBox::information(0, "Success", "MD files have been created");
}
void Test::createMDFile(QString testDirectory) {
void Test::createMDFile(const QString& testDirectory) {
// Verify folder contains test.js file
QString testFileName(testDirectory + "/" + TEST_FILENAME);
QFileInfo testFileInfo(testFileName);
if (!testFileInfo.exists()) {
messageBox.critical(0, "Error", "Could not find file: " + TEST_FILENAME);
QMessageBox::critical(0, "Error", "Could not find file: " + TEST_FILENAME);
return;
}
@ -610,7 +617,7 @@ void Test::createMDFile(QString testDirectory) {
QString mdFilename(testDirectory + "/" + "test.md");
QFile mdFile(mdFilename);
if (!mdFile.open(QIODevice::WriteOnly)) {
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
exit(-1);
}
@ -628,64 +635,24 @@ void Test::createMDFile(QString testDirectory) {
stream << "## Preconditions" << "\n";
stream << "- In an empty region of a domain with editing rights." << "\n\n";
// Platform
QStringList platforms = testScriptLines.platform.split(" ");;
stream << "## Platforms\n";
stream << "Run the test on each of the following platforms\n";
for (int i = 0; i < platforms.size(); ++i) {
// Note that the platforms parameter may include extra spaces, these appear as empty strings in the list
if (platforms[i] != QString()) {
stream << " - " << platforms[i] << "\n";
}
}
// Display
QStringList displays = testScriptLines.display.split(" ");
stream << "## Displays\n";
stream << "Run the test on each of the following displays\n";
for (int i = 0; i < displays.size(); ++i) {
// Note that the displays parameter may include extra spaces, these appear as empty strings in the list
if (displays[i] != QString()) {
stream << " - " << displays[i] << "\n";
}
}
// CPU
QStringList cpus = testScriptLines.cpu.split(" ");
stream << "## Processors\n";
stream << "Run the test on each of the following processors\n";
for (int i = 0; i < cpus.size(); ++i) {
// Note that the cpus parameter may include extra spaces, these appear as empty strings in the list
if (cpus[i] != QString()) {
stream << " - " << cpus[i] << "\n";
}
}
// GPU
QStringList gpus = testScriptLines.gpu.split(" ");
stream << "## Graphics Cards\n";
stream << "Run the test on graphics cards from each of the following vendors\n";
for (int i = 0; i < gpus.size(); ++i) {
// Note that the gpus parameter may include extra spaces, these appear as empty strings in the list
if (gpus[i] != QString()) {
stream << " - " << gpus[i] << "\n";
}
}
stream << "## Steps\n";
stream << "Press space bar to advance step by step\n\n";
// Note that snapshots of step n are taken in step n+1
// (this implies that if the LAST step requests a snapshot then this will not work - caveat emptor)
int snapShotIndex { 0 };
for (size_t i = 0; i < testScriptLines.stepList.size(); ++i) {
stream << "### Step " << QString::number(i + 1) << "\n";
stream << "- " << testScriptLines.stepList[i]->text << "\n";
if (testScriptLines.stepList[i]->takeSnapshot) {
if ((i + 1 < testScriptLines.stepList.size()) && testScriptLines.stepList[i + 1]->takeSnapshot) {
stream << "- ![](./ExpectedImage_" << QString::number(snapShotIndex).rightJustified(5, '0') << ".png)\n";
++snapShotIndex;
}
}
mdFile.close();
QMessageBox::information(0, "Success", "Test MD file " + mdFilename + " has been created");
}
void Test::createTestsOutline() {
@ -698,7 +665,7 @@ void Test::createTestsOutline() {
QString mdFilename(testsRootDirectory + "/" + testsOutlineFilename);
QFile mdFile(mdFilename);
if (!mdFile.open(QIODevice::WriteOnly)) {
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
exit(-1);
}
@ -756,10 +723,10 @@ void Test::createTestsOutline() {
mdFile.close();
messageBox.information(0, "Success", "Test outline file " + testsOutlineFilename + " has been created");
QMessageBox::information(0, "Success", "Test outline file " + testsOutlineFilename + " has been created");
}
void Test::copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFullFilename) {
void Test::copyJPGtoPNG(const QString& sourceJPGFullFilename, const QString& destinationPNGFullFilename) {
QFile::remove(destinationPNGFullFilename);
QImageReader reader;
@ -772,7 +739,7 @@ void Test::copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFul
writer.write(image);
}
QStringList Test::createListOfAll_imagesInDirectory(QString imageFormat, QString pathToImageDirectory) {
QStringList Test::createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory) {
imageDirectory = QDir(pathToImageDirectory);
QStringList nameFilters;
nameFilters << "*." + imageFormat;
@ -785,7 +752,7 @@ QStringList Test::createListOfAll_imagesInDirectory(QString imageFormat, QString
// 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) {
bool Test::isInSnapshotFilenameFormat(const QString& imageFormat, const QString& filename) {
QStringList filenameParts = filename.split(".");
bool filnameHasNoPeriods = (filenameParts.size() == 2);
@ -802,7 +769,7 @@ bool Test::isInSnapshotFilenameFormat(QString imageFormat, QString filename) {
// 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 Test::getExpectedImageDestinationDirectory(const QString& filename) {
QString filenameWithoutExtension = filename.split(".")[0];
QStringList filenameParts = filenameWithoutExtension.split("_");
@ -819,7 +786,7 @@ QString Test::getExpectedImageDestinationDirectory(QString filename) {
// 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 Test::getExpectedImagePartialSourceDirectory(const QString& filename) {
QString filenameWithoutExtension = filename.split(".")[0];
QStringList filenameParts = filenameWithoutExtension.split("_");
@ -831,7 +798,7 @@ QString Test::getExpectedImagePartialSourceDirectory(QString filename) {
}
if (i < 0) {
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Bad filename");
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Bad filename");
exit(-1);
}

View file

@ -30,10 +30,6 @@ using StepList = std::vector<Step*>;
class ExtractedText {
public:
QString title;
QString platform;
QString display;
QString cpu;
QString gpu;
StepList stepList;
};
@ -41,61 +37,58 @@ class Test {
public:
Test();
void startTestsEvaluation();
void finishTestsEvaluation(bool interactiveMode, QProgressBar* progressBar);
void startTestsEvaluation(const QString& testFolder = QString());
void finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar);
void createRecursiveScript();
void createAllRecursiveScripts();
void createRecursiveScript(QString topLevelDirectory, bool interactiveMode);
void createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode);
void createTest();
void createMDFile();
void createAllMDFiles();
void createMDFile(QString topLevelDirectory);
void createMDFile(const QString& topLevelDirectory);
void createTestsOutline();
bool compareImageLists(bool isInteractiveMode, QProgressBar* progressBar);
QStringList createListOfAll_imagesInDirectory(QString imageFormat, QString pathToImageDirectory);
QStringList createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory);
bool isInSnapshotFilenameFormat(QString imageFormat, QString filename);
bool isInSnapshotFilenameFormat(const QString& imageFormat, const QString& filename);
void importTest(QTextStream& textStream, const QString& testPathname);
void appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage);
void appendTestResultsToFile(const QString& testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage);
bool createTestResultsFolderPath(QString directory);
bool createTestResultsFolderPath(const QString& directory);
void zipAndDeleteTestResultsFolder();
bool isAValidDirectory(QString pathname);
QString extractPathFromTestsDown(QString fullPath);
QString getExpectedImageDestinationDirectory(QString filename);
QString getExpectedImagePartialSourceDirectory(QString filename);
bool isAValidDirectory(const QString& pathname);
QString extractPathFromTestsDown(const QString& fullPath);
QString getExpectedImageDestinationDirectory(const QString& filename);
QString getExpectedImagePartialSourceDirectory(const QString& filename);
void copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFullFilename);
void copyJPGtoPNG(const QString& sourceJPGFullFilename, const QString& destinationPNGFullFilename);
private:
const QString TEST_FILENAME { "test.js" };
const QString TEST_RESULTS_FOLDER { "TestResults" };
const QString TEST_RESULTS_FILENAME { "TestResults.txt" };
QMessageBox messageBox;
QDir imageDirectory;
MismatchWindow mismatchWindow;
ImageComparer imageComparer;
QString testResultsFolderPath { "" };
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;

View file

@ -13,10 +13,23 @@
AutoTester* autoTester;
int main(int argc, char *argv[]) {
// Only parameter is "--testFolder"
QString testFolder;
if (argc == 3) {
if (QString(argv[1]) == "--testFolder") {
testFolder = QString(argv[2]);
}
}
QApplication application(argc, argv);
autoTester = new AutoTester();
autoTester->show();
if (!testFolder.isNull()) {
autoTester->runFromCommandLine(testFolder);
} else {
autoTester->show();
}
return application.exec();
}

View file

@ -15,9 +15,17 @@ AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
ui.checkBoxInteractiveMode->setChecked(true);
ui.progressBar->setVisible(false);
test = new Test();
signalMapper = new QSignalMapper();
connect(ui.actionClose, &QAction::triggered, this, &AutoTester::on_closeButton_clicked);
connect(ui.actionAbout, &QAction::triggered, this, &AutoTester::about);
test = new Test();
}
void AutoTester::runFromCommandLine(const QString& testFolder) {
isRunningFromCommandline = true;
test->startTestsEvaluation(testFolder);
}
void AutoTester::on_evaluateTestsButton_clicked() {
@ -90,13 +98,21 @@ void AutoTester::saveImage(int index) {
image = image.convertToFormat(QImage::Format_ARGB32);
QString fullPathname = _directoryName + "/" + _filenames[index];
image.save(fullPathname, 0, 100);
if (!image.save(fullPathname, 0, 100)) {
QMessageBox::information(0, "Test Aborted", "Failed to save image: " + _filenames[index]);
ui.progressBar->setVisible(false);
return;
}
++_numberOfImagesDownloaded;
if (_numberOfImagesDownloaded == _numberOfImagesToDownload) {
test->finishTestsEvaluation(ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
test->finishTestsEvaluation(isRunningFromCommandline, ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
} else {
ui.progressBar->setValue(_numberOfImagesDownloaded);
}
}
void AutoTester::about() {
QMessageBox::information(0, "About", QString("Built ") + __DATE__ + " : " + __TIME__);
}

View file

@ -22,6 +22,9 @@ class AutoTester : public QMainWindow {
public:
AutoTester(QWidget *parent = Q_NULLPTR);
void runFromCommandLine(const QString& testFolder);
void downloadImage(const QUrl& url);
void downloadImages(const QStringList& URLs, const QString& directoryName, const QStringList& filenames);
@ -37,6 +40,8 @@ private slots:
void saveImage(int index);
void about();
private:
Ui::AutoTesterClass ui;
Test* test;
@ -50,9 +55,11 @@ private:
// Used to enable passing a parameter to slots
QSignalMapper* signalMapper;
int _numberOfImagesToDownload;
int _numberOfImagesDownloaded;
int _index;
int _numberOfImagesToDownload { 0 };
int _numberOfImagesDownloaded { 0 };
int _index { 0 };
bool isRunningFromCommandline { false };
};
#endif // hifi_AutoTester_h

View file

@ -95,7 +95,7 @@
<number>24</number>
</property>
</widget>
<widget class="QPushButton" name="createRecursiveScriptsRecursivelyButton">
<widget class="QPushButton" name="createAllRecursiveScriptsButton">
<property name="geometry">
<rect>
<x>360</x>
@ -157,6 +157,20 @@
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionClose"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
@ -167,6 +181,16 @@
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionClose">
<property name="text">
<string>Close</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>About</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>