mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-11 22:13:00 +02:00
Merge branch '20116_nitpick_v1.2' of https://github.com/NissimHadar/hifi into 20116_nitpick_v1.2
This commit is contained in:
commit
936119b246
35 changed files with 322 additions and 186 deletions
|
@ -337,6 +337,13 @@ void AudioMixerClientData::removeAgentAvatarAudioStream() {
|
|||
|
||||
if (it != _audioStreams.end()) {
|
||||
_audioStreams.erase(it);
|
||||
|
||||
// Clear mixing structures so that they get recreated with up to date
|
||||
// data if the stream comes back
|
||||
setHasReceivedFirstMix(false);
|
||||
_streams.skipped.clear();
|
||||
_streams.inactive.clear();
|
||||
_streams.active.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ Item {
|
|||
width: root.pane.width
|
||||
property bool failAfterSignUp: false
|
||||
|
||||
onWidthChanged: d.resize();
|
||||
|
||||
function login() {
|
||||
flavorText.visible = false
|
||||
mainTextContainer.visible = false
|
||||
|
@ -127,7 +129,7 @@ Item {
|
|||
Column {
|
||||
id: form
|
||||
width: parent.width
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
onHeightChanged: d.resize();
|
||||
|
||||
anchors {
|
||||
top: mainTextContainer.bottom
|
||||
|
|
|
@ -44,14 +44,14 @@ Rectangle {
|
|||
|
||||
|
||||
onPasswordChanged: {
|
||||
var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard");
|
||||
var use3DKeyboard = (typeof KeyboardScriptingInterface === "undefined") ? false : KeyboardScriptingInterface.use3DKeyboard;
|
||||
if (use3DKeyboard) {
|
||||
KeyboardScriptingInterface.password = password;
|
||||
}
|
||||
}
|
||||
|
||||
onRaisedChanged: {
|
||||
var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard");
|
||||
var use3DKeyboard = (typeof KeyboardScriptingInterface === "undefined") ? false : KeyboardScriptingInterface.use3DKeyboard;
|
||||
if (!use3DKeyboard) {
|
||||
keyboardBase.height = raised ? raisedHeight : 0;
|
||||
keyboardBase.visible = raised;
|
||||
|
|
|
@ -61,7 +61,7 @@ Item {
|
|||
RalewaySemiBold {
|
||||
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.right: parent.right
|
||||
Layout.alignment: Qt.AlignRight
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ Item {
|
|||
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
|
||||
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.right: parent.right
|
||||
Layout.alignment: Qt.AlignRight
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
}
|
||||
|
|
|
@ -2572,6 +2572,8 @@ void Application::cleanupBeforeQuit() {
|
|||
QString webengineRemoteDebugging = QProcessEnvironment::systemEnvironment().value("QTWEBENGINE_REMOTE_DEBUGGING", "false");
|
||||
qCDebug(interfaceapp) << "QTWEBENGINE_REMOTE_DEBUGGING =" << webengineRemoteDebugging;
|
||||
|
||||
DependencyManager::prepareToExit();
|
||||
|
||||
if (tracing::enabled()) {
|
||||
auto tracer = DependencyManager::get<tracing::Tracer>();
|
||||
tracer->stopTracing();
|
||||
|
@ -2684,6 +2686,7 @@ void Application::cleanupBeforeQuit() {
|
|||
|
||||
// destroy Audio so it and its threads have a chance to go down safely
|
||||
// this must happen after QML, as there are unexplained audio crashes originating in qtwebengine
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "stop");
|
||||
DependencyManager::destroy<AudioClient>();
|
||||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
DependencyManager::destroy<AudioScriptingInterface>();
|
||||
|
@ -2775,7 +2778,7 @@ Application::~Application() {
|
|||
// quit the thread used by the closure event sender
|
||||
closeEventSender->thread()->quit();
|
||||
|
||||
// Can't log to file passed this point, FileLogger about to be deleted
|
||||
// Can't log to file past this point, FileLogger about to be deleted
|
||||
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
||||
|
||||
_renderEventHandler->deleteLater();
|
||||
|
@ -6304,6 +6307,11 @@ void Application::update(float deltaTime) {
|
|||
PerformanceTimer perfTimer("enqueueFrame");
|
||||
getMain3DScene()->enqueueFrame();
|
||||
}
|
||||
|
||||
// If the display plugin is inactive then the frames won't be processed so process them here.
|
||||
if (!getActiveDisplayPlugin()->isActive()) {
|
||||
getMain3DScene()->processTransactionQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateRenderArgs(float deltaTime) {
|
||||
|
|
|
@ -247,25 +247,35 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() {
|
|||
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
|
||||
QScriptEngine scriptEngine;
|
||||
QVariantList wearableEntities;
|
||||
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
|
||||
auto avatarEntities = myAvatar->getAvatarEntityData();
|
||||
for (auto entityID : avatarEntities.keys()) {
|
||||
auto entity = entityTree->findEntityByID(entityID);
|
||||
if (!entity || !isWearableEntity(entity)) {
|
||||
continue;
|
||||
|
||||
if (entityTree) {
|
||||
QScriptEngine scriptEngine;
|
||||
auto avatarEntities = myAvatar->getAvatarEntityData();
|
||||
for (auto entityID : avatarEntities.keys()) {
|
||||
auto entity = entityTree->findEntityByID(entityID);
|
||||
if (!entity || !isWearableEntity(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QVariantMap avatarEntityData;
|
||||
|
||||
EncodeBitstreamParams params;
|
||||
auto desiredProperties = entity->getEntityProperties(params);
|
||||
desiredProperties += PROP_LOCAL_POSITION;
|
||||
desiredProperties += PROP_LOCAL_ROTATION;
|
||||
desiredProperties -= PROP_JOINT_ROTATIONS_SET;
|
||||
desiredProperties -= PROP_JOINT_ROTATIONS;
|
||||
desiredProperties -= PROP_JOINT_TRANSLATIONS_SET;
|
||||
desiredProperties -= PROP_JOINT_TRANSLATIONS;
|
||||
|
||||
EntityItemProperties entityProperties = entity->getProperties(desiredProperties);
|
||||
QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(&scriptEngine, entityProperties);
|
||||
avatarEntityData["properties"] = scriptProperties.toVariant();
|
||||
wearableEntities.append(QVariant(avatarEntityData));
|
||||
}
|
||||
QVariantMap avatarEntityData;
|
||||
EncodeBitstreamParams params;
|
||||
auto desiredProperties = entity->getEntityProperties(params);
|
||||
desiredProperties += PROP_LOCAL_POSITION;
|
||||
desiredProperties += PROP_LOCAL_ROTATION;
|
||||
EntityItemProperties entityProperties = entity->getProperties(desiredProperties);
|
||||
QScriptValue scriptProperties = EntityItemPropertiesToScriptValue(&scriptEngine, entityProperties);
|
||||
avatarEntityData["properties"] = scriptProperties.toVariant();
|
||||
wearableEntities.append(QVariant(avatarEntityData));
|
||||
}
|
||||
bookmark.insert(ENTRY_AVATAR_ENTITIES, wearableEntities);
|
||||
return bookmark;
|
||||
|
|
|
@ -364,8 +364,6 @@ Menu::Menu() {
|
|||
qApp->setHmdTabletBecomesToolbarSetting(action->isChecked());
|
||||
});
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::Use3DKeyboard, 0, true);
|
||||
|
||||
// Developer > Render >>>
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
|
||||
|
|
|
@ -213,7 +213,6 @@ namespace MenuOption {
|
|||
const QString TurnWithHead = "Turn using Head";
|
||||
const QString UseAudioForMouth = "Use Audio for Mouth";
|
||||
const QString UseCamera = "Use Camera";
|
||||
const QString Use3DKeyboard = "Use 3D Keyboard";
|
||||
const QString VelocityFilter = "Velocity Filter";
|
||||
const QString VisibleToEveryone = "Everyone";
|
||||
const QString VisibleToFriends = "Friends";
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <QStandardPaths>
|
||||
|
||||
static const QString AVATAR_HEAD_AND_BODY_STRING = "Avatar Body with Head";
|
||||
static const QString AVATAR_ATTACHEMENT_STRING = "Avatar Attachment";
|
||||
static const QString ENTITY_MODEL_STRING = "Entity Model";
|
||||
|
||||
ModelSelector::ModelSelector() {
|
||||
|
|
|
@ -32,3 +32,7 @@ void KeyboardScriptingInterface::setPassword(bool password) {
|
|||
void KeyboardScriptingInterface::loadKeyboardFile(const QString& keyboardFile) {
|
||||
DependencyManager::get<Keyboard>()->loadKeyboardFile(keyboardFile);
|
||||
}
|
||||
|
||||
bool KeyboardScriptingInterface::getUse3DKeyboard() {
|
||||
return DependencyManager::get<Keyboard>()->getUse3DKeyboard();
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ class KeyboardScriptingInterface : public QObject, public Dependency {
|
|||
Q_OBJECT
|
||||
Q_PROPERTY(bool raised READ isRaised WRITE setRaised)
|
||||
Q_PROPERTY(bool password READ isPassword WRITE setPassword)
|
||||
Q_PROPERTY(bool use3DKeyboard READ getUse3DKeyboard);
|
||||
|
||||
public:
|
||||
Q_INVOKABLE void loadKeyboardFile(const QString& string);
|
||||
|
@ -39,5 +40,7 @@ private:
|
|||
|
||||
bool isPassword();
|
||||
void setPassword(bool password);
|
||||
|
||||
bool getUse3DKeyboard();
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -241,6 +241,17 @@ void Keyboard::registerKeyboardHighlighting() {
|
|||
selection->enableListToScene(KEY_PRESSED_HIGHLIGHT);
|
||||
}
|
||||
|
||||
bool Keyboard::getUse3DKeyboard() const {
|
||||
return _use3DKeyboardLock.resultWithReadLock<bool>([&] {
|
||||
return _use3DKeyboard.get();
|
||||
});
|
||||
}
|
||||
|
||||
void Keyboard::setUse3DKeyboard(bool use) {
|
||||
_use3DKeyboardLock.withWriteLock([&] {
|
||||
_use3DKeyboard.set(use);
|
||||
});
|
||||
}
|
||||
|
||||
void Keyboard::createKeyboard() {
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <Sound.h>
|
||||
#include <AudioInjector.h>
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
#include <SettingHandle.h>
|
||||
|
||||
#include "ui/overlays/Overlay.h"
|
||||
|
||||
|
@ -97,6 +98,9 @@ public:
|
|||
bool isPassword() const;
|
||||
void setPassword(bool password);
|
||||
|
||||
bool getUse3DKeyboard() const;
|
||||
void setUse3DKeyboard(bool use);
|
||||
|
||||
void loadKeyboardFile(const QString& keyboardFile);
|
||||
QVector<OverlayID> getKeysID();
|
||||
|
||||
|
@ -143,6 +147,9 @@ private:
|
|||
SharedSoundPointer _keySound { nullptr };
|
||||
std::shared_ptr<QTimer> _layerSwitchTimer { std::make_shared<QTimer>() };
|
||||
|
||||
mutable ReadWriteLockable _use3DKeyboardLock;
|
||||
Setting::Handle<bool> _use3DKeyboard { "use3DKeyboard", true };
|
||||
|
||||
QString _typedCharacters;
|
||||
TextDisplay _textDisplay;
|
||||
Anchor _anchor;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "Snapshot.h"
|
||||
#include "SnapshotAnimated.h"
|
||||
#include "UserActivityLogger.h"
|
||||
#include "ui/Keyboard.h"
|
||||
|
||||
void setupPreferences() {
|
||||
auto preferences = DependencyManager::get<Preferences>();
|
||||
|
@ -119,6 +120,12 @@ void setupPreferences() {
|
|||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use reticle cursor instead of arrow", getter, setter));
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []()->bool { return DependencyManager::get<Keyboard>()->getUse3DKeyboard(); };
|
||||
auto setter = [](bool value) { DependencyManager::get<Keyboard>()->setUse3DKeyboard(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use Virtual Keyboard", getter, setter));
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getMiniTabletEnabled(); };
|
||||
auto setter = [](bool value) { qApp->setMiniTabletEnabled(value); };
|
||||
|
|
|
@ -302,7 +302,6 @@ void AudioClient::customDeleter() {
|
|||
#if defined(Q_OS_ANDROID)
|
||||
_shouldRestartInputSetup = false;
|
||||
#endif
|
||||
stop();
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,9 @@ public:
|
|||
#endif
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
static void prepareToExit() { manager()._exiting = true; }
|
||||
|
||||
private:
|
||||
static DependencyManager& manager();
|
||||
|
||||
|
@ -84,6 +87,8 @@ private:
|
|||
|
||||
QHash<size_t, QSharedPointer<Dependency>> _instanceHash;
|
||||
QHash<size_t, size_t> _inheritanceHash;
|
||||
|
||||
bool _exiting { false };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
@ -95,9 +100,17 @@ QSharedPointer<T> DependencyManager::get() {
|
|||
instance = qSharedPointerCast<T>(manager().safeGet(hashCode));
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
// debug builds...
|
||||
if (instance.isNull()) {
|
||||
qWarning() << "DependencyManager::get(): No instance available for" << typeid(T).name();
|
||||
}
|
||||
#else
|
||||
// for non-debug builds, don't print "No instance available" during shutdown, because
|
||||
// the act of printing this often causes crashes (because the LogHandler has-been/is-being
|
||||
// deleted).
|
||||
if (!manager()._exiting && instance.isNull()) {
|
||||
qWarning() << "DependencyManager::get(): No instance available for" << typeid(T).name();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,8 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
this.reticleMinY = MARGIN;
|
||||
this.reticleMaxY = 0;
|
||||
this.lastUnexpectedChildrenCheckTime = 0;
|
||||
this.endedGrab = 0;
|
||||
this.MIN_HAPTIC_PULSE_INTERVAL = 500; // ms
|
||||
|
||||
var FAR_GRAB_JOINTS = [65527, 65528]; // FARGRAB_LEFTHAND_INDEX, FARGRAB_RIGHTHAND_INDEX
|
||||
|
||||
|
@ -144,7 +146,12 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
// compute the mass for the purpose of energy and how quickly to move object
|
||||
this.mass = this.getMass(grabbedProperties.dimensions, grabbedProperties.density);
|
||||
|
||||
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
|
||||
// Debounce haptic pules. Can occur as near grab controller module vacillates between being ready or not due to
|
||||
// changing positions and floating point rounding.
|
||||
if (Date.now() - this.endedGrab > this.MIN_HAPTIC_PULSE_INTERVAL) {
|
||||
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
|
||||
}
|
||||
|
||||
unhighlightTargetEntity(this.targetEntityID);
|
||||
var message = {
|
||||
hand: this.hand,
|
||||
|
@ -256,7 +263,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
};
|
||||
|
||||
this.endFarParentGrab = function (controllerData) {
|
||||
this.hapticTargetID = null;
|
||||
this.endedGrab = Date.now();
|
||||
// var endProps = controllerData.nearbyEntityPropertiesByID[this.targetEntityID];
|
||||
var endProps = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES);
|
||||
if (this.thisFarGrabJointIsParent(endProps)) {
|
||||
|
@ -410,11 +417,6 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
if (targetEntity) {
|
||||
var gtProps = Entities.getEntityProperties(targetEntity, DISPATCHER_PROPERTIES);
|
||||
if (entityIsGrabbable(gtProps)) {
|
||||
// give haptic feedback
|
||||
if (gtProps.id !== this.hapticTargetID) {
|
||||
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
|
||||
this.hapticTargetID = gtProps.id;
|
||||
}
|
||||
// if we've attempted to grab a child, roll up to the root of the tree
|
||||
var groupRootProps = findGroupParent(controllerData, gtProps);
|
||||
if (entityIsGrabbable(groupRootProps)) {
|
||||
|
|
|
@ -24,7 +24,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
this.hand = hand;
|
||||
this.targetEntityID = null;
|
||||
this.actionID = null; // action this script created...
|
||||
this.hapticTargetID = null;
|
||||
|
||||
this.parameters = makeDispatcherModuleParameters(
|
||||
500,
|
||||
|
@ -164,10 +163,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
break;
|
||||
}
|
||||
if (entityIsGrabbable(props) || entityIsCloneable(props)) {
|
||||
if (props.id !== this.hapticTargetID) {
|
||||
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
|
||||
this.hapticTargetID = props.id;
|
||||
}
|
||||
if (!entityIsCloneable(props)) {
|
||||
// if we've attempted to grab a non-cloneable child, roll up to the root of the tree
|
||||
var groupRootProps = findGroupParent(controllerData, props);
|
||||
|
@ -199,7 +194,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
return makeRunningValues(true, [this.targetEntityID], []);
|
||||
}
|
||||
} else {
|
||||
this.hapticTargetID = null;
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
};
|
||||
|
@ -209,7 +203,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE &&
|
||||
controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) {
|
||||
this.endNearGrabAction();
|
||||
this.hapticTargetID = null;
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS,
|
||||
findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH,
|
||||
HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME,
|
||||
TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid,
|
||||
TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid, NEAR_GRAB_DISTANCE,
|
||||
distanceBetweenEntityLocalPositionAndBoundingBox, getGrabbableData, getGrabPointSphereOffset, DISPATCHER_PROPERTIES
|
||||
*/
|
||||
|
||||
|
@ -24,15 +24,6 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
// XXX this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true;
|
||||
// XXX this.kinematicGrab = (grabbableData.kinematic !== undefined) ? grabbableData.kinematic : NEAR_GRABBING_KINEMATIC;
|
||||
|
||||
// this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp:378
|
||||
var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral
|
||||
|
||||
function getGrabOffset(handController) {
|
||||
var offset = getGrabPointSphereOffset(handController, true);
|
||||
offset.y = -offset.y;
|
||||
return Vec3.multiply(MyAvatar.sensorToWorldScale, offset);
|
||||
}
|
||||
|
||||
function NearParentingGrabEntity(hand) {
|
||||
this.hand = hand;
|
||||
this.targetEntityID = null;
|
||||
|
@ -40,7 +31,6 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
this.previousParentID = {};
|
||||
this.previousParentJointIndex = {};
|
||||
this.previouslyUnhooked = {};
|
||||
this.hapticTargetID = null;
|
||||
this.lastUnequipCheckTime = 0;
|
||||
this.autoUnequipCounter = 0;
|
||||
this.lastUnexpectedChildrenCheckTime = 0;
|
||||
|
@ -138,7 +128,6 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
};
|
||||
|
||||
this.endNearParentingGrabEntity = function (controllerData) {
|
||||
this.hapticTargetID = null;
|
||||
var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID];
|
||||
if (this.thisHandIsParent(props) && !this.robbed) {
|
||||
Entities.editEntity(this.targetEntityID, {
|
||||
|
@ -169,8 +158,10 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
this.lastUnequipCheckTime = now;
|
||||
if (props.parentID === MyAvatar.SELF_ID) {
|
||||
var tearAwayDistance = TEAR_AWAY_DISTANCE * MyAvatar.sensorToWorldScale;
|
||||
var controllerIndex = (this.hand === LEFT_HAND ? Controller.Standard.LeftHand : Controller.Standard.RightHand);
|
||||
var controllerGrabOffset = getGrabOffset(controllerIndex);
|
||||
var controllerIndex =
|
||||
this.hand === LEFT_HAND ? Controller.Standard.LeftHand : Controller.Standard.RightHand;
|
||||
var controllerGrabOffset = getGrabPointSphereOffset(controllerIndex, true);
|
||||
controllerGrabOffset = Vec3.multiply(-MyAvatar.sensorToWorldScale, controllerGrabOffset);
|
||||
var distance = distanceBetweenEntityLocalPositionAndBoundingBox(props, controllerGrabOffset);
|
||||
if (distance > tearAwayDistance) {
|
||||
this.autoUnequipCounter++;
|
||||
|
@ -233,21 +224,18 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
// nearbyEntityProperties is already sorted by length from controller
|
||||
var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand];
|
||||
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
|
||||
var nearGrabDistance = NEAR_GRAB_DISTANCE * sensorScaleFactor;
|
||||
var nearGrabRadius = NEAR_GRAB_RADIUS * sensorScaleFactor;
|
||||
for (var i = 0; i < nearbyEntityProperties.length; i++) {
|
||||
var props = nearbyEntityProperties[i];
|
||||
var handPosition = controllerData.controllerLocations[this.hand].position;
|
||||
var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props);
|
||||
var distance = Vec3.distance(handPosition, props.position);
|
||||
if ((dist > TEAR_AWAY_DISTANCE) ||
|
||||
(distance > NEAR_GRAB_RADIUS * sensorScaleFactor)) {
|
||||
var grabPosition = controllerData.controllerLocations[this.hand].position; // Is offset from hand position.
|
||||
var dist = distanceBetweenPointAndEntityBoundingBox(grabPosition, props);
|
||||
var distance = Vec3.distance(grabPosition, props.position);
|
||||
if ((dist > nearGrabDistance) ||
|
||||
(distance > nearGrabRadius)) { // Only smallish entities can be near grabbed.
|
||||
continue;
|
||||
}
|
||||
if (entityIsGrabbable(props) || entityIsCloneable(props)) {
|
||||
// give haptic feedback
|
||||
if (props.id !== this.hapticTargetID) {
|
||||
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
|
||||
this.hapticTargetID = props.id;
|
||||
}
|
||||
if (!entityIsCloneable(props)) {
|
||||
// if we've attempted to grab a non-cloneable child, roll up to the root of the tree
|
||||
var groupRootProps = findGroupParent(controllerData, props);
|
||||
|
@ -284,7 +272,6 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
return makeRunningValues(true, [this.targetEntityID], []);
|
||||
}
|
||||
} else {
|
||||
this.hapticTargetID = null;
|
||||
this.robbed = false;
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
@ -303,7 +290,6 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
// entity was deleted
|
||||
this.grabbing = false;
|
||||
this.targetEntityID = null;
|
||||
this.hapticTargetID = null;
|
||||
this.robbed = false;
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
|
|
@ -916,7 +916,7 @@ div.refresh input[type="button"] {
|
|||
}
|
||||
.draggable-number div {
|
||||
height: 28px;
|
||||
width: 92px;
|
||||
width: 124px;
|
||||
}
|
||||
.draggable-number.text {
|
||||
display: inline-block;
|
||||
|
@ -929,6 +929,7 @@ div.refresh input[type="button"] {
|
|||
height: 28px;
|
||||
width: 100%;
|
||||
line-height: 2;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.draggable-number.text:hover {
|
||||
cursor: ew-resize;
|
||||
|
@ -944,12 +945,12 @@ div.refresh input[type="button"] {
|
|||
cursor: default;
|
||||
}
|
||||
.draggable-number.left-arrow {
|
||||
top: -5px;
|
||||
top: 3px;
|
||||
left: 0px;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.draggable-number.right-arrow {
|
||||
top: -5px;
|
||||
top: 3px;
|
||||
right: 0px;
|
||||
}
|
||||
.draggable-number input[type=number] {
|
||||
|
@ -971,14 +972,14 @@ div.refresh input[type="button"] {
|
|||
left: 12px;
|
||||
}
|
||||
.draggable-number.fstuple + .draggable-number.fstuple {
|
||||
padding-left: 28px;
|
||||
margin-left: 28px;
|
||||
}
|
||||
.draggable-number.fstuple input {
|
||||
right: -10px;
|
||||
}
|
||||
.draggable-number.fstuple .sublabel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
top: 6px;
|
||||
left: -16px;
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
|
|
|
@ -309,6 +309,9 @@ For usage and examples: colpick.com/plugin
|
|||
},
|
||||
// Fix the values if the user enters a negative or high value
|
||||
fixHSB = function (hsb) {
|
||||
hsb.h = isNaN(hsb.h) ? 0 : hsb.h;
|
||||
hsb.s = isNaN(hsb.s) ? 0 : hsb.s;
|
||||
hsb.b = isNaN(hsb.b) ? 0 : hsb.b;
|
||||
return {
|
||||
h: Math.min(360, Math.max(0, hsb.h)),
|
||||
s: Math.min(100, Math.max(0, hsb.s)),
|
||||
|
@ -316,6 +319,9 @@ For usage and examples: colpick.com/plugin
|
|||
};
|
||||
},
|
||||
fixRGB = function (rgb) {
|
||||
rgb.r = isNaN(rgb.r) ? 0 : rgb.r;
|
||||
rgb.g = isNaN(rgb.g) ? 0 : rgb.g;
|
||||
rgb.b = isNaN(rgb.b) ? 0 : rgb.b;
|
||||
return {
|
||||
r: Math.min(255, Math.max(0, rgb.r)),
|
||||
g: Math.min(255, Math.max(0, rgb.g)),
|
||||
|
|
|
@ -23,6 +23,20 @@ function DraggableNumber(min, max, step, decimals, dragStart, dragEnd) {
|
|||
}
|
||||
|
||||
DraggableNumber.prototype = {
|
||||
showInput: function() {
|
||||
this.elText.style.visibility = "hidden";
|
||||
this.elLeftArrow.style.visibility = "hidden";
|
||||
this.elRightArrow.style.visibility = "hidden";
|
||||
this.elInput.style.opacity = 1;
|
||||
},
|
||||
|
||||
hideInput: function() {
|
||||
this.elText.style.visibility = "visible";
|
||||
this.elLeftArrow.style.visibility = "visible";
|
||||
this.elRightArrow.style.visibility = "visible";
|
||||
this.elInput.style.opacity = 0;
|
||||
},
|
||||
|
||||
mouseDown: function(event) {
|
||||
if (event.target === this.elText) {
|
||||
this.initialMouseEvent = event;
|
||||
|
@ -36,8 +50,8 @@ DraggableNumber.prototype = {
|
|||
if (event.target === this.elText && this.initialMouseEvent) {
|
||||
let dx = event.clientX - this.initialMouseEvent.clientX;
|
||||
if (Math.abs(dx) <= DELTA_X_FOCUS_THRESHOLD) {
|
||||
this.elInput.style.visibility = "visible";
|
||||
this.elText.style.visibility = "hidden";
|
||||
this.showInput();
|
||||
this.elInput.focus();
|
||||
}
|
||||
this.initialMouseEvent = null;
|
||||
}
|
||||
|
@ -125,9 +139,8 @@ DraggableNumber.prototype = {
|
|||
this.setValue(this.elInput.value);
|
||||
},
|
||||
|
||||
inputBlur: function() {
|
||||
this.elInput.style.visibility = "hidden";
|
||||
this.elText.style.visibility = "visible";
|
||||
inputBlur: function(ev) {
|
||||
this.hideInput();
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
|
@ -171,13 +184,14 @@ DraggableNumber.prototype = {
|
|||
if (this.step !== undefined) {
|
||||
this.elInput.setAttribute("step", this.step);
|
||||
}
|
||||
this.elInput.style.visibility = "hidden";
|
||||
this.elInput.style.opacity = 0;
|
||||
this.elInput.addEventListener("change", this.onInputChange);
|
||||
this.elInput.addEventListener("blur", this.onInputBlur);
|
||||
this.elInput.addEventListener("focus", this.showInput.bind(this));
|
||||
|
||||
this.elText.appendChild(this.elLeftArrow);
|
||||
this.elText.appendChild(this.elInput);
|
||||
this.elText.appendChild(this.elRightArrow);
|
||||
this.elDiv.appendChild(this.elLeftArrow);
|
||||
this.elDiv.appendChild(this.elInput);
|
||||
this.elDiv.appendChild(this.elRightArrow);
|
||||
this.elDiv.appendChild(this.elText);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -197,7 +197,7 @@ const GROUPS = [
|
|||
multiplier: DEGREES_TO_RADIANS,
|
||||
decimals: 2,
|
||||
unit: "deg",
|
||||
propertyID: "keyLight.direction.x",
|
||||
propertyID: "keyLight.direction.y",
|
||||
showPropertyRule: { "keyLightMode": "enabled" },
|
||||
},
|
||||
{
|
||||
|
@ -206,7 +206,7 @@ const GROUPS = [
|
|||
multiplier: DEGREES_TO_RADIANS,
|
||||
decimals: 2,
|
||||
unit: "deg",
|
||||
propertyID: "keyLight.direction.y",
|
||||
propertyID: "keyLight.direction.x",
|
||||
showPropertyRule: { "keyLightMode": "enabled" },
|
||||
},
|
||||
{
|
||||
|
@ -2149,7 +2149,7 @@ function createTupleNumberInput(property, subLabel) {
|
|||
propertyData.decimals, dragStartFunction, dragEndFunction);
|
||||
elDraggableNumber.elInput.setAttribute("id", elementID);
|
||||
elDraggableNumber.elDiv.className += " fstuple";
|
||||
elDraggableNumber.elText.insertBefore(elLabel, elDraggableNumber.elLeftArrow);
|
||||
elDraggableNumber.elDiv.insertBefore(elLabel, elDraggableNumber.elLeftArrow);
|
||||
|
||||
return elDraggableNumber;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
TEAR_AWAY_DISTANCE:true,
|
||||
TEAR_AWAY_COUNT:true,
|
||||
TEAR_AWAY_CHECK_TIME:true,
|
||||
NEAR_GRAB_DISTANCE: true,
|
||||
distanceBetweenPointAndEntityBoundingBox:true,
|
||||
entityIsEquipped:true,
|
||||
entityIsFarGrabbedByOther:true,
|
||||
|
@ -99,6 +100,10 @@ NEAR_GRAB_RADIUS = 1.0;
|
|||
TEAR_AWAY_DISTANCE = 0.15; // ungrab an entity if its bounding-box moves this far from the hand
|
||||
TEAR_AWAY_COUNT = 2; // multiply by TEAR_AWAY_CHECK_TIME to know how long the item must be away
|
||||
TEAR_AWAY_CHECK_TIME = 0.15; // seconds, duration between checks
|
||||
|
||||
NEAR_GRAB_DISTANCE = 0.14; // Grab an entity if its bounding box is within this distance.
|
||||
// Smaller than TEAR_AWAY_DISTANCE for hysteresis.
|
||||
|
||||
DISPATCHER_HOVERING_LIST = "dispactherHoveringList";
|
||||
DISPATCHER_HOVERING_STYLE = {
|
||||
isOutlineSmooth: true,
|
||||
|
|
Binary file not shown.
|
@ -50,10 +50,12 @@ if (WIN32)
|
|||
)
|
||||
|
||||
# add a custom command to copy the empty Apps/Data High Fidelity folder (i.e. - a valid folder with no entities)
|
||||
# this also copied to the containing folder, to facilitate running from Visual Studio
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/AppDataHighFidelity" "$<TARGET_FILE_DIR:${TARGET_NAME}>/AppDataHighFidelity"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/AppDataHighFidelity" "AppDataHighFidelity"
|
||||
)
|
||||
|
||||
# add a custom command to copy the SSL DLLs
|
||||
|
@ -62,5 +64,12 @@ if (WIN32)
|
|||
POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "$ENV{VCPKG_ROOT}/installed/x64-windows/bin" "$<TARGET_FILE_DIR:${TARGET_NAME}>"
|
||||
)
|
||||
elseif (APPLE)
|
||||
# add a custom command to copy the empty Apps/Data High Fidelity folder (i.e. - a valid folder with no entities)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/AppDataHighFidelity" "$<TARGET_FILE_DIR:${TARGET_NAME}>/AppDataHighFidelity"
|
||||
)
|
||||
endif ()
|
||||
|
||||
endif ()
|
|
@ -1,6 +1,6 @@
|
|||
# nitpick
|
||||
|
||||
Nitpick is a stand alone application that provides a mechanism for regression testing. The general idea is simple:
|
||||
Nitpick is a stand alone application that provides a mechanism for regression testing. The general idea is simple:
|
||||
* Each test folder has a script that produces a set of snapshots.
|
||||
* The snapshots are compared to a 'canonical' set of images that have been produced beforehand.
|
||||
* The result, if any test failed, is a zipped folder describing the failure.
|
||||
|
@ -22,9 +22,9 @@ Nitpick is built as part of the High Fidelity build.
|
|||
1. Select all, right-click and select 7-Zip->Add to archive...
|
||||
1. Set Archive format to 7z
|
||||
1. Check "Create SFX archive
|
||||
1. Enter installer name (i.e. `nitpick-installer-v1.0.exe`)
|
||||
1. Enter installer name (i.e. `nitpick-installer-v1.1.exe`)
|
||||
1. Click "OK"
|
||||
1. Copy created installer to https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-v1.0.exe: aws s3 cp nitpick-installer-v1.0.exe s3://hifi-qa/nitpick/Mac/nitpick-installer-v1.0.exe
|
||||
1. Copy created installer to https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-v1.1.exe: aws s3 cp nitpick-installer-v1.1.exe s3://hifi-qa/nitpick/Mac/nitpick-installer-v1.1.exe
|
||||
#### Mac
|
||||
These steps assume the hifi repository has been cloned to `~/hifi`.
|
||||
1. (first time) Install brew
|
||||
|
@ -37,12 +37,12 @@ These steps assume the hifi repository has been cloned to `~/hifi`.
|
|||
1. Change the loader instruction to find the dynamic library locally
|
||||
In a terminal: `install_name_tool -change ~/hifi/build/ext/Xcode/quazip/project/lib/libquazip5.1.dylib libquazip5.1.dylib nitpick`
|
||||
1. Delete any existing disk images. In a terminal: `rm *.dmg`
|
||||
1. Create installer (note final period).In a terminal: `create-dmg --volname nitpick-installer-v1.0 nitpick-installer-v1.0.dmg .`
|
||||
1. Create installer (note final period).In a terminal: `create-dmg --volname nitpick-installer-v1.1 nitpick-installer-v1.1.dmg .`
|
||||
Make sure to wait for completion.
|
||||
1. Copy created installer to AWS: `~/Library/Python/3.7/bin/aws s3 cp nitpick-installer-v1.0.dmg s3://hifi-qa/nitpick/Mac/nitpick-installer-v1.0.dmg`
|
||||
1. Copy created installer to AWS: `~/Library/Python/3.7/bin/aws s3 cp nitpick-installer-v1.1.dmg s3://hifi-qa/nitpick/Mac/nitpick-installer-v1.1.dmg`
|
||||
### Installation
|
||||
#### Windows
|
||||
1. (First time) download and install vc_redist.x64.exe (available at https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-v1.0.exe)
|
||||
1. (First time) download and install vc_redist.x64.exe (available at https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-v1.1.exe)
|
||||
1. (First time) download and install Python 3 from https://hifi-qa.s3.amazonaws.com/nitpick/Windows/python-3.7.0-amd64.exe (also located at https://www.python.org/downloads/)
|
||||
1. After installation - create an environment variable called PYTHON_PATH and set it to the folder containing the Python executable.
|
||||
1. (First time) download and install AWS CLI from https://hifi-qa.s3.amazonaws.com/nitpick/Windows/AWSCLI64PY3.msi (also available at https://aws.amazon.com/cli/
|
||||
|
@ -52,7 +52,7 @@ These steps assume the hifi repository has been cloned to `~/hifi`.
|
|||
1. Leave region name and ouput format as default [None]
|
||||
1. Install the latest release of Boto3 via pip: `pip install boto3`
|
||||
|
||||
1. Download the installer by browsing to [here](<https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-v1.0.exe>)
|
||||
1. Download the installer by browsing to [here](<https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-v1.1.exe>)
|
||||
1. Double click on the installer and install to a convenient location
|
||||

|
||||
|
||||
|
@ -76,14 +76,14 @@ In a terminal: `python3 get-pip.py --user`
|
|||
1. Enter the secret key
|
||||
1. Leave region name and ouput format as default [None]
|
||||
1. Install the latest release of Boto3 via pip: pip3 install boto3
|
||||
1. Download the installer by browsing to [here](<https://hifi-qa.s3.amazonaws.com/nitpick/Mac/nitpick-installer-v1.0.dmg>).
|
||||
1. Download the installer by browsing to [here](<https://hifi-qa.s3.amazonaws.com/nitpick/Mac/nitpick-installer-v1.1.dmg>).
|
||||
1. Double-click on the downloaded image to mount it
|
||||
1. Create a folder for the nitpick files (e.g. ~/nitpick)
|
||||
If this folder exists then delete all it's contents.
|
||||
1. Copy the downloaded files to the folder
|
||||
In a terminal:
|
||||
`cd ~/nitpick`
|
||||
`cp -r /Volumes/nitpick-installer-v1.0/* .`
|
||||
`cp -r /Volumes/nitpick-installer-v1.1/* .`
|
||||
|
||||
1. __To run nitpick, cd to the folder that you copied to and run `./nitpick`__
|
||||
# Usage
|
||||
|
|
|
@ -22,12 +22,12 @@ AWSInterface::AWSInterface(QObject* parent) : QObject(parent) {
|
|||
}
|
||||
|
||||
void AWSInterface::createWebPageFromResults(const QString& testResults,
|
||||
const QString& snapshotDirectory,
|
||||
const QString& workingDirectory,
|
||||
QCheckBox* updateAWSCheckBox,
|
||||
QLineEdit* urlLineEdit) {
|
||||
_testResults = testResults;
|
||||
_snapshotDirectory = snapshotDirectory;
|
||||
|
||||
_workingDirectory = workingDirectory;
|
||||
|
||||
_urlLineEdit = urlLineEdit;
|
||||
_urlLineEdit->setEnabled(false);
|
||||
|
||||
|
@ -36,6 +36,9 @@ void AWSInterface::createWebPageFromResults(const QString& testResults,
|
|||
|
||||
if (updateAWSCheckBox->isChecked()) {
|
||||
updateAWS();
|
||||
QMessageBox::information(0, "Success", "HTML file has been created and copied to AWS");
|
||||
} else {
|
||||
QMessageBox::information(0, "Success", "HTML file has been created");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,14 +46,14 @@ void AWSInterface::extractTestFailuresFromZippedFolder() {
|
|||
// For a test results zip file called `D:/tt/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ].zip`
|
||||
// the folder will be called `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]`
|
||||
// and, this folder will be in the working directory
|
||||
QStringList parts =_testResults.split('/');
|
||||
QString zipFolderName = _snapshotDirectory + "/" + parts[parts.length() - 1].split('.')[0];
|
||||
QStringList parts = _testResults.split('/');
|
||||
QString zipFolderName = _workingDirectory + "/" + parts[parts.length() - 1].split('.')[0];
|
||||
if (QDir(zipFolderName).exists()) {
|
||||
QDir dir = zipFolderName;
|
||||
dir.removeRecursively();
|
||||
}
|
||||
|
||||
JlCompress::extractDir(_testResults, _snapshotDirectory);
|
||||
JlCompress::extractDir(_testResults, _workingDirectory);
|
||||
}
|
||||
|
||||
void AWSInterface::createHTMLFile() {
|
||||
|
@ -60,7 +63,7 @@ void AWSInterface::createHTMLFile() {
|
|||
QString filename = pathComponents[pathComponents.length() - 1];
|
||||
_resultsFolder = filename.left(filename.length() - 4);
|
||||
|
||||
QString resultsPath = _snapshotDirectory + "/" + _resultsFolder + "/";
|
||||
QString resultsPath = _workingDirectory + "/" + _resultsFolder + "/";
|
||||
QDir().mkdir(resultsPath);
|
||||
_htmlFilename = resultsPath + HTML_FILENAME;
|
||||
|
||||
|
@ -116,7 +119,7 @@ void AWSInterface::writeTitle(QTextStream& stream) {
|
|||
QString date_buildorPR_hostName = tokens[tokens.length() - 1].split("--")[1].split(".")[0];
|
||||
|
||||
QString buildorPR = date_buildorPR_hostName.split('(')[1].split(')')[0];
|
||||
QString hostName = date_buildorPR_hostName.split('[')[1].split(']')[0];
|
||||
QString hostName = date_buildorPR_hostName.split('[')[1].split(']')[0];
|
||||
|
||||
QStringList dateList = date_buildorPR_hostName.split('(')[0].split('_')[0].split('-');
|
||||
QString year = dateList[0];
|
||||
|
@ -156,7 +159,7 @@ void AWSInterface::writeTable(QTextStream& stream) {
|
|||
// Note that failures are processed first, then successes
|
||||
QStringList originalNamesFailures;
|
||||
QStringList originalNamesSuccesses;
|
||||
QDirIterator it1(_snapshotDirectory.toStdString().c_str());
|
||||
QDirIterator it1(_workingDirectory);
|
||||
while (it1.hasNext()) {
|
||||
QString nextDirectory = it1.next();
|
||||
|
||||
|
@ -189,11 +192,11 @@ void AWSInterface::writeTable(QTextStream& stream) {
|
|||
for (int i = 0; i < originalNamesSuccesses.length(); ++i) {
|
||||
newNamesSuccesses.append(originalNamesSuccesses[i].split("--tests.")[1]);
|
||||
}
|
||||
|
||||
_htmlFailuresFolder = _snapshotDirectory + "/" + _resultsFolder + "/" + FAILURES_FOLDER;
|
||||
|
||||
_htmlFailuresFolder = _workingDirectory + "/" + _resultsFolder + "/" + FAILURES_FOLDER;
|
||||
QDir().mkdir(_htmlFailuresFolder);
|
||||
|
||||
_htmlSuccessesFolder = _snapshotDirectory + "/" + _resultsFolder + "/" + SUCCESSES_FOLDER;
|
||||
_htmlSuccessesFolder = _workingDirectory + "/" + _resultsFolder + "/" + SUCCESSES_FOLDER;
|
||||
QDir().mkdir(_htmlSuccessesFolder);
|
||||
|
||||
for (int i = 0; i < newNamesFailures.length(); ++i) {
|
||||
|
@ -204,7 +207,11 @@ void AWSInterface::writeTable(QTextStream& stream) {
|
|||
QDir().rename(originalNamesSuccesses[i], _htmlSuccessesFolder + "/" + newNamesSuccesses[i]);
|
||||
}
|
||||
|
||||
QDirIterator it2((_htmlFailuresFolder).toStdString().c_str());
|
||||
// Mac does not read folders in lexicographic order, so this step is divided into 2
|
||||
// Each test consists of the test name and its index.
|
||||
QDirIterator it2(_htmlFailuresFolder);
|
||||
QStringList folderNames;
|
||||
|
||||
while (it2.hasNext()) {
|
||||
QString nextDirectory = it2.next();
|
||||
|
||||
|
@ -214,10 +221,17 @@ void AWSInterface::writeTable(QTextStream& stream) {
|
|||
}
|
||||
|
||||
QStringList pathComponents = nextDirectory.split('/');
|
||||
QString filename = pathComponents[pathComponents.length() - 1];
|
||||
int splitIndex = filename.lastIndexOf(".");
|
||||
QString testName = filename.left(splitIndex).replace(".", " / ");
|
||||
QString testNumber = filename.right(filename.length() - (splitIndex + 1));
|
||||
QString folderName = pathComponents[pathComponents.length() - 1];
|
||||
|
||||
folderNames << folderName;
|
||||
}
|
||||
|
||||
folderNames.sort();
|
||||
for (const auto& folderName : folderNames) {
|
||||
int splitIndex = folderName.lastIndexOf(".");
|
||||
QString testName = folderName.left(splitIndex).replace('.', " / ");
|
||||
|
||||
int testNumber = folderName.right(folderName.length() - (splitIndex + 1)).toInt();
|
||||
|
||||
// The failures are ordered lexicographically, so we know that we can rely on the testName changing to create a new table
|
||||
if (testName != previousTestName) {
|
||||
|
@ -232,14 +246,14 @@ void AWSInterface::writeTable(QTextStream& stream) {
|
|||
openTable(stream);
|
||||
}
|
||||
|
||||
createEntry(testNumber.toInt(), filename, stream, true);
|
||||
createEntry(testNumber, folderName, stream, true);
|
||||
}
|
||||
|
||||
closeTable(stream);
|
||||
stream << "\t" << "\t" << "<font color=\"blue\">\n";
|
||||
stream << "\t" << "\t" << "<h1>The following tests passed:</h1>";
|
||||
|
||||
QDirIterator it3((_htmlSuccessesFolder).toStdString().c_str());
|
||||
QDirIterator it3(_htmlSuccessesFolder);
|
||||
while (it3.hasNext()) {
|
||||
QString nextDirectory = it3.next();
|
||||
|
||||
|
@ -290,7 +304,7 @@ void AWSInterface::closeTable(QTextStream& stream) {
|
|||
void AWSInterface::createEntry(int index, const QString& testResult, QTextStream& stream, const bool isFailure) {
|
||||
stream << "\t\t\t<tr>\n";
|
||||
stream << "\t\t\t\t<td><h1>" << QString::number(index) << "</h1></td>\n";
|
||||
|
||||
|
||||
// For a test named `D:/t/fgadhcUDHSFaidsfh3478JJJFSDFIUSOEIrf/Failure_1--tests.engine.interaction.pick.collision.many.00000`
|
||||
// we need `Failure_1--tests.engine.interaction.pick.collision.many.00000`
|
||||
QStringList resultNameComponents = testResult.split('/');
|
||||
|
@ -302,11 +316,11 @@ void AWSInterface::createEntry(int index, const QString& testResult, QTextStream
|
|||
folder = FAILURES_FOLDER;
|
||||
differenceFileFound = QFile::exists(_htmlFailuresFolder + "/" + resultName + "/Difference Image.png");
|
||||
} else {
|
||||
folder = SUCCESSES_FOLDER;
|
||||
folder = SUCCESSES_FOLDER;
|
||||
differenceFileFound = QFile::exists(_htmlSuccessesFolder + "/" + resultName + "/Difference Image.png");
|
||||
}
|
||||
|
||||
|
||||
|
||||
stream << "\t\t\t\t<td><img src=\"./" << folder << "/" << resultName << "/Actual Image.png\" width = \"576\" height = \"324\" ></td>\n";
|
||||
stream << "\t\t\t\t<td><img src=\"./" << folder << "/" << resultName << "/Expected Image.png\" width = \"576\" height = \"324\" ></td>\n";
|
||||
|
||||
|
@ -320,7 +334,7 @@ void AWSInterface::createEntry(int index, const QString& testResult, QTextStream
|
|||
}
|
||||
|
||||
void AWSInterface::updateAWS() {
|
||||
QString filename = _snapshotDirectory + "/updateAWS.py";
|
||||
QString filename = _workingDirectory + "/updateAWS.py";
|
||||
if (QFile::exists(filename)) {
|
||||
QFile::remove(filename);
|
||||
}
|
||||
|
@ -337,7 +351,7 @@ void AWSInterface::updateAWS() {
|
|||
stream << "import boto3\n";
|
||||
stream << "s3 = boto3.resource('s3')\n\n";
|
||||
|
||||
QDirIterator it1(_htmlFailuresFolder.toStdString().c_str());
|
||||
QDirIterator it1(_htmlFailuresFolder);
|
||||
while (it1.hasNext()) {
|
||||
QString nextDirectory = it1.next();
|
||||
|
||||
|
@ -345,26 +359,26 @@ void AWSInterface::updateAWS() {
|
|||
if (nextDirectory.right(1) == ".") {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// nextDirectory looks like `D:/t/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]/failures/engine.render.effect.bloom.00000`
|
||||
// We need to concatenate the last 3 components, to get `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]/failures/engine.render.effect.bloom.00000`
|
||||
QStringList parts = nextDirectory.split('/');
|
||||
QString filename = parts[parts.length() - 3] + "/" + parts[parts.length() - 2] + "/" + parts[parts.length() - 1];
|
||||
|
||||
stream << "data = open('" << _snapshotDirectory << "/" << filename << "/"
|
||||
stream << "data = open('" << _workingDirectory << "/" << filename << "/"
|
||||
<< "Actual Image.png"
|
||||
<< "', 'rb')\n";
|
||||
|
||||
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Actual Image.png" << "', Body=data)\n\n";
|
||||
|
||||
stream << "data = open('" << _snapshotDirectory << "/" << filename << "/"
|
||||
stream << "data = open('" << _workingDirectory << "/" << filename << "/"
|
||||
<< "Expected Image.png"
|
||||
<< "', 'rb')\n";
|
||||
|
||||
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Expected Image.png" << "', Body=data)\n\n";
|
||||
|
||||
if (QFile::exists(_htmlFailuresFolder + "/" + parts[parts.length() - 1] + "/Difference Image.png")) {
|
||||
stream << "data = open('" << _snapshotDirectory << "/" << filename << "/"
|
||||
stream << "data = open('" << _workingDirectory << "/" << filename << "/"
|
||||
<< "Difference Image.png"
|
||||
<< "', 'rb')\n";
|
||||
|
||||
|
@ -372,7 +386,7 @@ void AWSInterface::updateAWS() {
|
|||
}
|
||||
}
|
||||
|
||||
QDirIterator it2(_htmlSuccessesFolder.toStdString().c_str());
|
||||
QDirIterator it2(_htmlSuccessesFolder);
|
||||
while (it2.hasNext()) {
|
||||
QString nextDirectory = it2.next();
|
||||
|
||||
|
@ -386,20 +400,20 @@ void AWSInterface::updateAWS() {
|
|||
QStringList parts = nextDirectory.split('/');
|
||||
QString filename = parts[parts.length() - 3] + "/" + parts[parts.length() - 2] + "/" + parts[parts.length() - 1];
|
||||
|
||||
stream << "data = open('" << _snapshotDirectory << "/" << filename << "/"
|
||||
stream << "data = open('" << _workingDirectory << "/" << filename << "/"
|
||||
<< "Actual Image.png"
|
||||
<< "', 'rb')\n";
|
||||
|
||||
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Actual Image.png" << "', Body=data)\n\n";
|
||||
|
||||
stream << "data = open('" << _snapshotDirectory << "/" << filename << "/"
|
||||
stream << "data = open('" << _workingDirectory << "/" << filename << "/"
|
||||
<< "Expected Image.png"
|
||||
<< "', 'rb')\n";
|
||||
|
||||
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Expected Image.png" << "', Body=data)\n\n";
|
||||
|
||||
if (QFile::exists(_htmlSuccessesFolder + "/" + parts[parts.length() - 1] + "/Difference Image.png")) {
|
||||
stream << "data = open('" << _snapshotDirectory << "/" << filename << "/"
|
||||
stream << "data = open('" << _workingDirectory << "/" << filename << "/"
|
||||
<< "Difference Image.png"
|
||||
<< "', 'rb')\n";
|
||||
|
||||
|
@ -407,7 +421,7 @@ void AWSInterface::updateAWS() {
|
|||
}
|
||||
}
|
||||
|
||||
stream << "data = open('" << _snapshotDirectory << "/" << _resultsFolder << "/" << HTML_FILENAME << "', 'rb')\n";
|
||||
stream << "data = open('" << _workingDirectory << "/" << _resultsFolder << "/" << HTML_FILENAME << "', 'rb')\n";
|
||||
stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << _resultsFolder << "/"
|
||||
<< HTML_FILENAME << "', Body=data, ContentType='text/html')\n";
|
||||
|
||||
|
@ -426,10 +440,10 @@ void AWSInterface::updateAWS() {
|
|||
[=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); });
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QStringList parameters = QStringList() << filename ;
|
||||
QStringList parameters = QStringList() << filename;
|
||||
process->start(_pythonCommand, parameters);
|
||||
#elif defined Q_OS_MAC
|
||||
QStringList parameters = QStringList() << "-c" << _pythonCommand + " " + filename;
|
||||
QStringList parameters = QStringList() << "-c" << _pythonCommand + " " + filename;
|
||||
process->start("sh", parameters);
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ public:
|
|||
explicit AWSInterface(QObject* parent = 0);
|
||||
|
||||
void createWebPageFromResults(const QString& testResults,
|
||||
const QString& snapshotDirectory,
|
||||
const QString& workingDirectory,
|
||||
QCheckBox* updateAWSCheckBox,
|
||||
QLineEdit* urlLineEdit);
|
||||
|
||||
|
@ -49,7 +49,7 @@ public:
|
|||
|
||||
private:
|
||||
QString _testResults;
|
||||
QString _snapshotDirectory;
|
||||
QString _workingDirectory;
|
||||
QString _resultsFolder;
|
||||
QString _htmlFailuresFolder;
|
||||
QString _htmlSuccessesFolder;
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
Downloader::Downloader(QUrl fileURL, QObject *parent) : QObject(parent) {
|
||||
_networkAccessManager.get(QNetworkRequest(fileURL));
|
||||
|
||||
connect(
|
||||
&_networkAccessManager, SIGNAL (finished(QNetworkReply*)),
|
||||
this, SLOT (fileDownloaded(QNetworkReply*))
|
||||
);
|
||||
|
||||
_networkAccessManager.get(QNetworkRequest(fileURL));
|
||||
}
|
||||
|
||||
void Downloader::fileDownloaded(QNetworkReply* reply) {
|
||||
|
|
|
@ -105,7 +105,7 @@ int Test::compareImageLists() {
|
|||
++numberOfFailures;
|
||||
|
||||
if (!isInteractiveMode) {
|
||||
appendTestResultsToFile(_testResultsFolderPath, testResult, _mismatchWindow.getComparisonImage(), true);
|
||||
appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), true);
|
||||
} else {
|
||||
_mismatchWindow.exec();
|
||||
|
||||
|
@ -113,7 +113,7 @@ int Test::compareImageLists() {
|
|||
case USER_RESPONSE_PASS:
|
||||
break;
|
||||
case USE_RESPONSE_FAIL:
|
||||
appendTestResultsToFile(_testResultsFolderPath, testResult, _mismatchWindow.getComparisonImage(), true);
|
||||
appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), true);
|
||||
break;
|
||||
case USER_RESPONSE_ABORT:
|
||||
keepOn = false;
|
||||
|
@ -124,7 +124,7 @@ int Test::compareImageLists() {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
appendTestResultsToFile(_testResultsFolderPath, testResult, _mismatchWindow.getComparisonImage(), false);
|
||||
appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), false);
|
||||
}
|
||||
|
||||
_progressBar->setValue(i);
|
||||
|
@ -134,12 +134,31 @@ int Test::compareImageLists() {
|
|||
return numberOfFailures;
|
||||
}
|
||||
|
||||
void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestResult testResult, QPixmap comparisonImage, bool hasFailed) {
|
||||
int Test::checkTextResults() {
|
||||
// Create lists of failed and passed tests
|
||||
QStringList nameFilterFailed;
|
||||
nameFilterFailed << "*.failed.txt";
|
||||
QStringList testsFailed = QDir(_snapshotDirectory).entryList(nameFilterFailed, QDir::Files, QDir::Name);
|
||||
|
||||
QStringList nameFilterPassed;
|
||||
nameFilterPassed << "*.passed.txt";
|
||||
QStringList testsPassed = QDir(_snapshotDirectory).entryList(nameFilterPassed, QDir::Files, QDir::Name);
|
||||
|
||||
// Add results to Test Results folder
|
||||
foreach(QString currentFilename, testsFailed) {
|
||||
}
|
||||
|
||||
return testsFailed.length();
|
||||
}
|
||||
|
||||
void Test::appendTestResultsToFile(TestResult testResult, QPixmap comparisonImage, bool hasFailed) {
|
||||
// Critical error if Test Results folder does not exist
|
||||
if (!QDir().exists(_testResultsFolderPath)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + _testResultsFolderPath + " not found");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// There are separate subfolders for failures and passes
|
||||
QString resultFolderPath;
|
||||
if (hasFailed) {
|
||||
resultFolderPath = _testResultsFolderPath + "/Failure_" + QString::number(_failureIndex) + "--" +
|
||||
|
@ -195,6 +214,22 @@ void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestRe
|
|||
comparisonImage.save(resultFolderPath + "/" + "Difference Image.png");
|
||||
}
|
||||
|
||||
void::Test::appendTestResultsToFile(QString testResultFilename, bool hasFailed) {
|
||||
QString resultFolderPath;
|
||||
if (hasFailed) {
|
||||
resultFolderPath = _testResultsFolderPath + "/Failure_";
|
||||
++_failureIndex;
|
||||
} else {
|
||||
resultFolderPath = _testResultsFolderPath + "/Success_";
|
||||
++_successIndex;
|
||||
}
|
||||
|
||||
if (!QFile::copy(testResultFilename, resultFolderPath)) {
|
||||
//// QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void Test::startTestsEvaluation(const bool isRunningFromCommandLine,
|
||||
const bool isRunningInAutomaticTestRun,
|
||||
const QString& snapshotDirectory,
|
||||
|
@ -270,9 +305,14 @@ void Test::startTestsEvaluation(const bool isRunningFromCommandLine,
|
|||
|
||||
nitpick->downloadFiles(expectedImagesURLs, _snapshotDirectory, _expectedImagesFilenames, (void *)this);
|
||||
}
|
||||
|
||||
void Test::finishTestsEvaluation() {
|
||||
// First - compare the pairs of images
|
||||
int numberOfFailures = compareImageLists();
|
||||
|
||||
|
||||
// Next - check text results
|
||||
numberOfFailures += checkTextResults();
|
||||
|
||||
if (!_isRunningFromCommandLine && !_isRunningInAutomaticTestRun) {
|
||||
if (numberOfFailures == 0) {
|
||||
QMessageBox::information(0, "Success", "All images are as expected");
|
||||
|
@ -542,7 +582,7 @@ void Test::createAllMDFiles() {
|
|||
createMDFile(_testsRootDirectory);
|
||||
}
|
||||
|
||||
QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(_testsRootDirectory, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
@ -620,7 +660,7 @@ void Test::createTestAutoScript() {
|
|||
}
|
||||
|
||||
if (createTestAutoScript(_testDirectory)) {
|
||||
QMessageBox::information(0, "Success", "'nitpick.js` script has been created");
|
||||
QMessageBox::information(0, "Success", "'testAuto.js` script has been created");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -636,7 +676,7 @@ void Test::createAllTestAutoScripts() {
|
|||
createTestAutoScript(_testsRootDirectory);
|
||||
}
|
||||
|
||||
QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(_testsRootDirectory, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
@ -653,7 +693,7 @@ void Test::createAllTestAutoScripts() {
|
|||
}
|
||||
}
|
||||
|
||||
QMessageBox::information(0, "Success", "'nitpick.js' scripts have been created");
|
||||
QMessageBox::information(0, "Success", "All 'testAuto.js' scripts have been created");
|
||||
}
|
||||
|
||||
bool Test::createTestAutoScript(const QString& directory) {
|
||||
|
@ -677,7 +717,7 @@ bool Test::createTestAutoScript(const QString& directory) {
|
|||
|
||||
stream << "if (typeof PATH_TO_THE_REPO_PATH_UTILS_FILE === 'undefined') PATH_TO_THE_REPO_PATH_UTILS_FILE = 'https://raw.githubusercontent.com/highfidelity/hifi_tests/master/tests/utils/branchUtils.js';\n";
|
||||
stream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);\n";
|
||||
stream << "var nitpick = createAutoTester(Script.resolvePath('.'));\n\n";
|
||||
stream << "var nitpick = createNitpick(Script.resolvePath('.'));\n\n";
|
||||
stream << "nitpick.enableAuto();\n\n";
|
||||
stream << "Script.include('./test.js?raw=true');\n";
|
||||
|
||||
|
@ -704,7 +744,7 @@ void Test::createAllRecursiveScripts() {
|
|||
|
||||
createRecursiveScript(_testsRootDirectory, false);
|
||||
|
||||
QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(_testsRootDirectory, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
@ -716,7 +756,7 @@ void Test::createAllRecursiveScripts() {
|
|||
|
||||
// Only process directories that have sub-directories
|
||||
bool hasNoSubDirectories{ true };
|
||||
QDirIterator it2(directory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it2(directory, QDirIterator::Subdirectories);
|
||||
while (it2.hasNext()) {
|
||||
QString directory2 = it2.next();
|
||||
|
||||
|
@ -737,20 +777,21 @@ void Test::createAllRecursiveScripts() {
|
|||
}
|
||||
|
||||
void Test::createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode) {
|
||||
const QString recursiveTestsFilename("testRecursive.js");
|
||||
QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename);
|
||||
if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
const QString recursiveTestsScriptName("testRecursive.js");
|
||||
const QString recursiveTestsFilename(topLevelDirectory + "/" + recursiveTestsScriptName);
|
||||
QFile recursiveTestsFile(recursiveTestsFilename);
|
||||
if (!recursiveTestsFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
||||
"Failed to create \"" + recursiveTestsFilename + "\" in directory \"" + topLevelDirectory + "\"");
|
||||
"Failed to create \"" + recursiveTestsScriptName + "\" in directory \"" + topLevelDirectory + "\"");
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QTextStream textStream(&allTestsFilename);
|
||||
QTextStream textStream(&recursiveTestsFile);
|
||||
|
||||
textStream << "// This is an automatically generated file, created by auto-tester" << endl;
|
||||
textStream << "// This is an automatically generated file, created by nitpick" << endl;
|
||||
|
||||
// Include 'autoTest.js'
|
||||
// Include 'nitpick.js'
|
||||
QString branch = nitpick->getSelectedBranch();
|
||||
QString user = nitpick->getSelectedUser();
|
||||
|
||||
|
@ -758,7 +799,7 @@ void Test::createRecursiveScript(const QString& topLevelDirectory, bool interact
|
|||
"/tests/utils/branchUtils.js\";"
|
||||
<< endl;
|
||||
textStream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);" << endl;
|
||||
textStream << "var nitpick = createAutoTester(Script.resolvePath(\".\"));" << endl << endl;
|
||||
textStream << "var nitpick = createNitpick(Script.resolvePath(\".\"));" << endl << endl;
|
||||
|
||||
textStream << "var testsRootPath = nitpick.getTestsRootPath();" << endl << endl;
|
||||
|
||||
|
@ -787,7 +828,7 @@ void Test::createRecursiveScript(const QString& topLevelDirectory, bool interact
|
|||
testFound = true;
|
||||
}
|
||||
|
||||
QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(topLevelDirectory, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
@ -809,7 +850,15 @@ void Test::createRecursiveScript(const QString& topLevelDirectory, bool interact
|
|||
|
||||
if (interactiveMode && !testFound) {
|
||||
QMessageBox::information(0, "Failure", "No \"" + TEST_FILENAME + "\" files found");
|
||||
allTestsFilename.close();
|
||||
recursiveTestsFile.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// If 'directories' is empty, this means that this recursive script has no tests to call, so it is redundant
|
||||
// The script will be closed and deleted
|
||||
if (directories.length() == 0) {
|
||||
recursiveTestsFile.close();
|
||||
QFile::remove(recursiveTestsFilename);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -821,7 +870,7 @@ void Test::createRecursiveScript(const QString& topLevelDirectory, bool interact
|
|||
textStream << endl;
|
||||
textStream << "nitpick.runRecursive();" << endl;
|
||||
|
||||
allTestsFilename.close();
|
||||
recursiveTestsFile.close();
|
||||
}
|
||||
|
||||
void Test::createTestsOutline() {
|
||||
|
@ -858,7 +907,7 @@ void Test::createTestsOutline() {
|
|||
int rootDepth { _testDirectory.count('/') };
|
||||
|
||||
// Each test is shown as the folder name linking to the matching GitHub URL, and the path to the associated test.md file
|
||||
QDirIterator it(_testDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
|
||||
QDirIterator it(_testDirectory, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString directory = it.next();
|
||||
|
||||
|
@ -1052,11 +1101,11 @@ void Test::createWebPage(QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit) {
|
|||
return;
|
||||
}
|
||||
|
||||
QString snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select a folder to store temporary files in",
|
||||
QString workingDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select a folder to store temporary files in",
|
||||
nullptr, QFileDialog::ShowDirsOnly);
|
||||
if (snapshotDirectory.isNull()) {
|
||||
if (workingDirectory.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_awsInterface.createWebPageFromResults(testResults, snapshotDirectory, updateAWSCheckBox, urlLineEdit);
|
||||
_awsInterface.createWebPageFromResults(testResults, workingDirectory, updateAWSCheckBox, urlLineEdit);
|
||||
}
|
|
@ -77,6 +77,7 @@ public:
|
|||
void createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode);
|
||||
|
||||
int compareImageLists();
|
||||
int checkTextResults();
|
||||
|
||||
QStringList createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory);
|
||||
|
||||
|
@ -84,7 +85,8 @@ public:
|
|||
|
||||
void includeTest(QTextStream& textStream, const QString& testPathname);
|
||||
|
||||
void appendTestResultsToFile(const QString& testResultsFolderPath, TestResult testResult, QPixmap comparisonImage, bool hasFailed);
|
||||
void appendTestResultsToFile(TestResult testResult, QPixmap comparisonImage, bool hasFailed);
|
||||
void appendTestResultsToFile(QString testResultFilename, bool hasFailed);
|
||||
|
||||
bool createTestResultsFolderPath(const QString& directory);
|
||||
QString zipAndDeleteTestResultsFolder();
|
||||
|
|
|
@ -275,7 +275,7 @@ void TestRailInterface::processDirectoryPython(const QString& directory,
|
|||
const QString& userGitHub,
|
||||
const QString& branchGitHub) {
|
||||
// Loop over all entries in directory
|
||||
QDirIterator it(directory.toStdString().c_str());
|
||||
QDirIterator it(directory);
|
||||
while (it.hasNext()) {
|
||||
QString nextDirectory = it.next();
|
||||
|
||||
|
@ -855,7 +855,7 @@ QDomElement TestRailInterface::processDirectoryXML(const QString& directory,
|
|||
QDomElement result = element;
|
||||
|
||||
// Loop over all entries in directory
|
||||
QDirIterator it(directory.toStdString().c_str());
|
||||
QDirIterator it(directory);
|
||||
while (it.hasNext()) {
|
||||
QString nextDirectory = it.next();
|
||||
|
||||
|
|
|
@ -332,23 +332,23 @@ void TestRunner::verifyInstallationSucceeded() {
|
|||
}
|
||||
|
||||
void TestRunner::saveExistingHighFidelityAppDataFolder() {
|
||||
#ifdef Q_OS_WIN
|
||||
QString dataDirectory{ "NOT FOUND" };
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
dataDirectory = qgetenv("USERPROFILE") + "\\AppData\\Roaming";
|
||||
|
||||
#elif defined Q_OS_MAC
|
||||
dataDirectory = QDir::homePath() + "/Library/Application Support";
|
||||
#endif
|
||||
if (_runLatest->isChecked()) {
|
||||
_appDataFolder = dataDirectory + "\\High Fidelity";
|
||||
_appDataFolder = dataDirectory + "/High Fidelity";
|
||||
} else {
|
||||
// We are running a PR build
|
||||
_appDataFolder = dataDirectory + "\\High Fidelity - " + getPRNumberFromURL(_url->text());
|
||||
_appDataFolder = dataDirectory + "/High Fidelity - " + getPRNumberFromURL(_url->text());
|
||||
}
|
||||
|
||||
_savedAppDataFolder = dataDirectory + "/" + UNIQUE_FOLDER_NAME;
|
||||
if (_savedAppDataFolder.exists()) {
|
||||
if (QDir(_savedAppDataFolder).exists()) {
|
||||
_savedAppDataFolder.removeRecursively();
|
||||
}
|
||||
|
||||
if (_appDataFolder.exists()) {
|
||||
// The original folder is saved in a unique name
|
||||
_appDataFolder.rename(_appDataFolder.path(), _savedAppDataFolder.path());
|
||||
|
@ -356,9 +356,6 @@ void TestRunner::saveExistingHighFidelityAppDataFolder() {
|
|||
|
||||
// Copy an "empty" AppData folder (i.e. no entities)
|
||||
copyFolder(QDir::currentPath() + "/AppDataHighFidelity", _appDataFolder.path());
|
||||
#elif defined Q_OS_MAC
|
||||
// TODO: find Mac equivalent of AppData
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestRunner::createSnapshotFolder() {
|
||||
|
@ -369,7 +366,7 @@ void TestRunner::createSnapshotFolder() {
|
|||
// Note that we cannot use just a `png` filter, as the filenames include periods
|
||||
// Also, delete any `jpg` and `txt` files
|
||||
// The idea is to leave only previous zipped result folders
|
||||
QDirIterator it(_snapshotFolder.toStdString().c_str());
|
||||
QDirIterator it(_snapshotFolder);
|
||||
while (it.hasNext()) {
|
||||
QString filename = it.next();
|
||||
if (filename.right(4) == ".png" || filename.right(4) == ".jpg" || filename.right(4) == ".txt") {
|
||||
|
@ -469,12 +466,7 @@ void TestRunner::runInterfaceWithTestScript() {
|
|||
// Move to an empty area
|
||||
url = "file:///~serverless/tutorial.json";
|
||||
} else {
|
||||
#ifdef Q_OS_WIN
|
||||
url = "hifi://localhost";
|
||||
#elif defined Q_OS_MAC
|
||||
// TODO: Find out Mac equivalent of AppData, then this won't be needed
|
||||
url = "hifi://localhost/9999,9999,9999";
|
||||
#endif
|
||||
}
|
||||
|
||||
QString testScript =
|
||||
|
@ -535,8 +527,6 @@ void TestRunner::runInterfaceWithTestScript() {
|
|||
}
|
||||
|
||||
void TestRunner::interfaceExecutionComplete() {
|
||||
killProcesses();
|
||||
|
||||
QFileInfo testCompleted(QDir::toNativeSeparators(_snapshotFolder) +"/tests_completed.txt");
|
||||
if (!testCompleted.exists()) {
|
||||
QMessageBox::critical(0, "Tests not completed", "Interface seems to have crashed before completion of the test scripts\nExisting images will be evaluated");
|
||||
|
@ -544,6 +534,8 @@ void TestRunner::interfaceExecutionComplete() {
|
|||
|
||||
evaluateResults();
|
||||
|
||||
killProcesses();
|
||||
|
||||
// The High Fidelity AppData folder will be restored after evaluation has completed
|
||||
}
|
||||
|
||||
|
@ -589,15 +581,11 @@ void TestRunner::addBuildNumberToResults(QString zippedFolderName) {
|
|||
}
|
||||
|
||||
void TestRunner::restoreHighFidelityAppDataFolder() {
|
||||
#ifdef Q_OS_WIN
|
||||
_appDataFolder.removeRecursively();
|
||||
|
||||
if (_savedAppDataFolder != QDir()) {
|
||||
_appDataFolder.rename(_savedAppDataFolder.path(), _appDataFolder.path());
|
||||
}
|
||||
#elif defined Q_OS_MAC
|
||||
// TODO: find Mac equivalent of AppData
|
||||
#endif
|
||||
}
|
||||
|
||||
// Copies a folder recursively
|
||||
|
|
|
@ -36,7 +36,7 @@ Nitpick::Nitpick(QWidget* parent) : QMainWindow(parent) {
|
|||
_ui.statusLabel->setText("");
|
||||
_ui.plainTextEdit->setReadOnly(true);
|
||||
|
||||
setWindowTitle("Nitpick - v1.0");
|
||||
setWindowTitle("Nitpick - v1.2");
|
||||
|
||||
// Coming soon to a nitpick near you...
|
||||
//// _helpWindow.textBrowser->setText()
|
||||
|
|
Loading…
Reference in a new issue