diff --git a/gpii/node_modules/eventLog/README.md b/gpii/node_modules/eventLog/README.md index df88fd68c..13ba35e67 100644 --- a/gpii/node_modules/eventLog/README.md +++ b/gpii/node_modules/eventLog/README.md @@ -37,7 +37,21 @@ All logged events contain the following data: "installID": "hK/Dvo1GG8", // Unique identifier of the installation. "sequence": 60689584, // Incrementing number, unique per installation. "timestamp": "2018-10-31T06:45:21.971-07:00", // When the event occurred - "version": "0.4.0" // The version of this module (or the windows metrics, if higher). + "version": "0.4.0", // The version of this module (or the windows metrics, if higher). + + // The following are optional, depending on the current state: + "sessionID": "4acr8sks-4069", // unique session identifier + "subSessionID": "4acr8sks-4069-5", // unique sub-session identifier + "logon": "on", // "in" or "out" if currently logging in/out - "in-after"/"out-after" 10 seconds after log in/out. + // for any other time, this field does not exist. + "gpiiKey": "d90eda4e-d1cd-415b-9605-dd9ce8be6359", // gpiiKey currently in use, + // from the UI: + "app": "active", // The active window belongs to Morphic. + "focus": "openUSB", // The button that currently has focus (combine with "app" to determine if it has system focus) + "hover": "launch-documorph", // The button that the mouse is currently hovering over. + "widget-hover": "openUSB", // The QSS pop-up that the mouse is over. + "field-hover": "ejectUsbButton", // The field within the pop-up that the mouse is over. + "field-focus": "regular-contrast", // The field within the pop-up that has key focus. } ``` @@ -66,11 +80,7 @@ When a user session starts. ```json5 { "module": "lifecycle", - "event": "SessionStart", - "data": { - "gpiiKey": "c7584703-a4bd-48d0-ac96-bc3e2028b1a7", - "session": "ea4d17ff-909" - } + "event": "SessionStart" } ``` @@ -79,8 +89,7 @@ When a user session starts. "module": "lifecycle", "event": "SessionStop", "data": { - "gpiiKey": "c7584703-a4bd-48d0-ac96-bc3e2028b1a7", - "session": "ea4d17ff-909" + "duration": 481 // number of seconds the session lasted. } } ``` diff --git a/gpii/node_modules/eventLog/package.json b/gpii/node_modules/eventLog/package.json index 421f79714..b8c2270ce 100644 --- a/gpii/node_modules/eventLog/package.json +++ b/gpii/node_modules/eventLog/package.json @@ -1,7 +1,7 @@ { "name": "eventLog", "description": "Event logging.", - "version": "0.4.0", + "version": "0.5.0", "author": "GPII", "bugs": "http://issues.gpii.net/browse/GPII", "homepage": "http://gpii.net/", diff --git a/gpii/node_modules/eventLog/src/eventLog.js b/gpii/node_modules/eventLog/src/eventLog.js index 96de8dd52..3acb2439f 100644 --- a/gpii/node_modules/eventLog/src/eventLog.js +++ b/gpii/node_modules/eventLog/src/eventLog.js @@ -22,7 +22,6 @@ var fluid = require("infusion"); var fs = require("fs"), - path = require("path"), moment = require("moment"), net = require("net"); @@ -39,9 +38,6 @@ fluid.defaults("gpii.eventLog", { }, settingsDir: { type: "gpii.settingsDir" - }, - metrics: { - type: "gpii.metrics" } }, invokers: { @@ -51,23 +47,41 @@ fluid.defaults("gpii.eventLog", { }, logError: "gpii.eventLog.logError", getGpiiSettingsDir: "{settingsDir}.getGpiiSettingsDir", - getLogFile: "gpii.eventLog.getLogFile", - getVersion: "gpii.eventLog.getVersion" + getVersion: "gpii.eventLog.getVersion", + setState: { + funcName: "gpii.eventLog.setState", + args: ["{eventLog}", "{arguments}.0", "{arguments}.1"] // name, value + } }, members: { - // The installation ID - installationID: "@expand:{that}.installID.getInstallID()", - version: "@expand:{that}.getVersion()", sequence: 0, // Buffered log lines while there's not log server connection logBuffer: [], + logLevel: fluid.logLevel.INFO, // Maximum number of lines to buffer. - maxBufferlength: 0xfff + maxBufferlength: 0xfff, + // A TCP socket to send the log entries to (filebeat service). + logServer: { + host: null, + port: null + }, + // A file to write the log entries to. + logPath: null, + // Data to include in every log entry. + eventData: { + // The installation ID + installID: "@expand:{that}.installID.getInstallID()", + version: "@expand:{that}.getVersion()", + sessionID: undefined, + gpiiKey: undefined + }, + // The start times for events which require durations to be recorded. + eventTimes: {} }, listeners: { "onCreate.logFile": { - func: "gpii.eventLog.getLogFile", - args: ["{that}"] + func: "gpii.eventLog.initLogDestination", + args: ["{that}", "{that}.options.logDestination"] }, "onCreate.log": { func: "gpii.eventLog.logStartStop", @@ -78,10 +92,22 @@ fluid.defaults("gpii.eventLog", { args: ["{that}", "stop"] } }, - - logDestination: "tcp://127.0.0.1:51481" + // File path, or tcp://host:port + logDestination: null }); +/** + * A log event + * @typedef {Object} LogEvent + * @property {String} module The area of GPII the event is from. + * @property {String} event The name of the event. + * @property {String} data [optional] Extra information about the event. + * @property {Object} level The severity of the event (from fluid.logLevelsSpec, default: fluid.INFO). + * @property {String} version The eventLog module version. (automatically added) + * @property {String} timestamp Current time of the event. (automatically added) + * @property {String} sequence Ordered unique identifier to the event. (automatically added) + */ + /** * Returns the actual date and time, as a string, in ISO 8601 format with the localtime + offset from UTC. * eg: '2018-07-13T13:03:03.863+01:00' @@ -125,7 +151,7 @@ gpii.eventLog.logStartStop = function (that, state) { * @param {String} event - Name of the event. * @param {Any} [data] - [optional] Event specific data. * @param {Object} level -[optional] Level of the log, see fluid.logLevelsSpec [FATAL,FAIL,WARN,IMPORTANT,INFO,TRACE]. - * @return {Object} The log object. + * @return {LogEvent} The log object. */ gpii.eventLog.createLogObject = function (moduleName, event, data, level) { var eventObject = { @@ -184,8 +210,11 @@ gpii.eventLog.logError = function (that, moduleName, errType, err, level) { if (err instanceof Error) { // Error doesn't serialise data.error = {}; - fluid.each(Object.getOwnPropertyNames(err), function (a) { - data.error[a] = err[a]; + fluid.each(Object.getOwnPropertyNames(err), function (source) { + // Ensure the first character of the field is lowercase - "Message" vs "message" was causing a duplicate + // field during the analysis. + var dest = source.charAt(0).toLowerCase() + source.slice(1); + data.error[dest] = err[source]; }); } else if (fluid.isPlainObject(err, true)) { data.error = Object.assign({}, err); @@ -228,43 +257,25 @@ gpii.eventLog.gotError = function (err, errType) { fluid.onUncaughtException.addListener(gpii.eventLog.gotError, "gpii-eventLog"); /** - * Gets the path of the new log file for this instance of gpii. - * It can use one of the following environment variables to override the configured: - * GPII_EVENT_LOG: The path to the log file (a file, or tcp://:). - * GPII_EVENT_LOG_DIRECTORY: A directory where per-instance logs are kept + * Parses the log path (which can be overridden by the GPII_EVENT_LOG environment variable), and sets either the + * logServer or logPath member. * * @param {Component} that - The gpii.eventLog instance. - * @return {String} The path to the new log file. + * @param {String} logPath - The path to the log file (a file, or a tcp socket in the format of `tcp://:`). */ -gpii.eventLog.getLogFile = function (that) { - var logPath; - if (!process.env.GPII_EVENT_LOG_DIRECTORY) { - logPath = process.env.GPII_EVENT_LOG || that.logFilePath || that.options.logDestination; - } +gpii.eventLog.initLogDestination = function (that, logPath) { + logPath = process.env.GPII_EVENT_LOG || logPath; - if (logPath) { - if (logPath.startsWith("tcp:")) { - var m = /tcp:\/*([^:]+):([0-9]+)/.exec(logPath); - if (m) { - that.logHost = m[1]; - that.logPort = m[2]; - that.logFilePath = null; - } - } else { - that.logHost = null; - that.logFilePath = logPath; - } + var match = logPath && /tcp:\/*([^:]+):([0-9]+)/.exec(logPath); + if (match) { + that.logServer.host = match[1]; + that.logServer.port = match[2]; } else { - that.logHost = null; - var startupTime = Date.now(); - var gpiiSettingsDir = process.env.GPII_EVENT_LOG_DIRECTORY || that.getGpiiSettingsDir(); - that.logFilePath = path.join(gpiiSettingsDir, "gpii-" + gpii.journal.formatTimestamp(startupTime) + ".log"); + that.logPath = logPath; } - var dest = that.logHost ? ("tcp://" + that.logHost + ":" + that.logPort) : that.logFilePath; + var dest = that.logServer.host ? ("tcp://" + that.logServer.host + ":" + that.logServer.port) : that.logPath; fluid.log(fluid.logLevel.IMPORTANT, "Writing event log to " + dest); - - return that.logFilePath; }; /** @@ -272,29 +283,60 @@ gpii.eventLog.getLogFile = function (that) { * * @param {Component} that - The gpii.eventLog instance. * @param {Object} level - Level of the log, see fluid.logLevelsSpec [FATAL,FAIL,WARN,IMPORTANT,INFO,TRACE]. - * @param {Object} event - The object. This will be modified to what has been sent to the log, adding the installID and + * @param {LogEvent} event - The object. This will be modified to what has been sent to the log, adding the installID and * timestamp fields. */ gpii.eventLog.writeLog = function (that, level, event) { - var intLevel = gpii.eventLog.checkLevel(level); - event.level = intLevel.value; + var eventLevel = gpii.eventLog.checkLevel(level); + event.level = eventLevel.value; - // Log to console before the installation ID and timestamp are added (no one wants to see it). - fluid.log(fluid.logLevel.TRACE, event); + gpii.eventLog.recordDuration(that, event); - event.installID = that.installationID; + Object.assign(event, that.eventData); event.timestamp = gpii.eventLog.getTimestamp(); - event.version = that.version; event.sequence = that.sequence++; - var logLine = JSON.stringify(event) + "\n"; + if (eventLevel.priority <= that.logLevel.priority) { + var logLine = JSON.stringify(event) + "\n"; + + if (that.logServer.host && that.logServer.port) { + gpii.eventLog.writeLogTcp(that, logLine); + } - if (that.logHost && that.logPort) { - gpii.eventLog.writeLogTcp(that, logLine); + if (that.logPath) { + fs.appendFileSync(that.logPath, logLine); + } } +}; - if (that.logFilePath) { - fs.appendFileSync(that.logFilePath, logLine); +/** + * Handles the timing of a pair of duration events. + * + * Some events can be paired into having a duration. For example, tooltip-shown and tooltip-hidden. The timestamp for + * the start event is recorded, so when the end event is seen the duration is calculated and added to the event data. + * + * @param {Component} that The gpii.eventLog instance. + * @param {LogEvent} event The event. + */ +gpii.eventLog.recordDuration = function (that, event) { + var endEvent = that.options.durationEvents[event.event]; + if (endEvent) { + // This is the start of a pair of duration events. + if (!that.eventTimes[endEvent]) { + that.eventTimes[endEvent] = []; + } + that.eventTimes[endEvent].push(process.hrtime()); + } else { + var startTimes = that.eventTimes[event.event]; + var startTime = startTimes && startTimes.pop(); + if (startTime) { + // This is the ending of a pair of duration events. + if (!event.data) { + event.data = {}; + } + var memberName = event.data.hasOwnProperty("duration") ? "duration_auto" : "duration"; + event.data[memberName] = process.hrtime(startTime)[0]; + } } }; @@ -334,7 +376,7 @@ gpii.eventLog.connectLog = function (that) { // Connect to the server. that.logSocket = new net.Socket(); - that.logSocket.connect(that.logPort, that.logHost, function () { + that.logSocket.connect(that.logServer.port, that.logServer.host, function () { gpii.eventLog.connectLog.lastError = null; fluid.log("Connected to log server"); // Send the initial data. @@ -368,12 +410,29 @@ gpii.eventLog.connectLog = function (that) { * Sets INFO as default loglevel * * @param {Object} level - Level to check, can be a string that represents the value or a property of fluid.logLevel. - * @return {Integer} A valid fluid.logLevel, with INFO as default. + * @return {Object} A valid fluid.logLevel, with INFO as default. */ gpii.eventLog.checkLevel = function (level) { + var togo; if (typeof level === "string" && level in fluid.logLevelsSpec) { - return fluid.logLevel[level]; + togo = fluid.logLevel[level]; + } else { + togo = fluid.isLogLevel(level) && level; + } + return togo || fluid.logLevel.INFO; +}; + +/** + * Specifies a field which gets logged with every subsequent event. + * + * @param {Component} that The gpii.eventLog instance. + * @param {String} name The name of the field. + * @param {Object} value The field value. undefined or null will remove the field. + */ +gpii.eventLog.setState = function (that, name, value) { + if (value === undefined || value === null) { + delete that.eventData[name]; } else { - return fluid.isLogLevel(level) ? level : fluid.logLevel.INFO; + that.eventData[name] = value; } }; diff --git a/gpii/node_modules/eventLog/src/metrics.js b/gpii/node_modules/eventLog/src/metrics.js index ce401a875..cde0238ec 100644 --- a/gpii/node_modules/eventLog/src/metrics.js +++ b/gpii/node_modules/eventLog/src/metrics.js @@ -37,29 +37,60 @@ fluid.defaults("gpii.metrics", { gradeNames: "gpii.windowsMetrics" } } - }, - metrics: { - checks: { - standaloneMetrics: { - contextValue: "{gpii.contexts.standalone-metrics}" - } - }, - defaultGradeNames: "gpii.metrics.lifecycle" } }, invokers: { logMetric: { func: "{eventLog}.logEvent", args: ["metrics", "{arguments}.0", "{arguments}.1"] // event, data + }, + startSubSession: { + funcName: "gpii.metrics.startSubSession", + args: ["{that}", "{eventLog}"] + }, + stopSubSession: { + funcName: "gpii.metrics.stopSubSession", + args: ["{that}", "{eventLog}"] } }, members: { - sessionSolutions: {} + sessionSolutions: {}, + // Incrementing sub-session id. + subSessionIncrementer: 0 }, events: { "onStartMetrics": null, - "onStopMetrics": null - } + "onStopMetrics": null, + // The user has become active (called on the first input after onInactive) + "onActive": null, // args: duration-inactive + // The user has become inactive (no input was received after {that}.config.input.inactiveTime) + "onInactive": null // args: {sleep:true} + }, + listeners: { + "onStartMetrics.log": { + funcName: "fluid.log", + args: "Metrics started" + }, + "onActive": [{ + func: "{that}.logMetric", + args: ["inactive-stop"] + }, { + func: "{that}.startSubSession" + }], + "onInactive": [{ + func: "{that}.logMetric", + args: ["inactive-begin", "{arguments}.0"] + }, { + func: "{that}.stopSubSession" + }] + }, + durationEvents: { + "start": "stop", + "inactive-begin": "inactive-stop", + "SessionStart": "SessionStop", + "subsession-begin": "subsession-end" + }, + siteConfig: {} }); // Mixin grade for gpii.metrics to log lifecycle manager things @@ -67,7 +98,13 @@ fluid.defaults("gpii.metrics.lifecycle", { modelListeners: { "{lifecycleManager}.model.logonChange": { funcName: "gpii.metrics.logonStateChanged", - args: ["{that}", "{lifecycleManager}", "{change}.oldValue", "{change}.value"] + args: ["{that}", "{eventLog}", "{lifecycleManager}", "{change}.oldValue", "{change}.value"] + } + }, + invokers: { + sessionStopped: { + funcName: "gpii.metrics.sessionStopped", + args: [ "{that}", "{eventLog}"] } }, listeners: { @@ -78,36 +115,17 @@ fluid.defaults("gpii.metrics.lifecycle", { }, "{lifecycleManager}.events.onSessionStart": [ { - namespace: "eventLog", - func: "{eventLog}.logEvent", - args: [ - "lifecycle", - "SessionStart", - { - // onSessionStart fired with [gradeName, gpiiKey] - gpiiKey: "{arguments}.1", - session: "@expand:{lifecycleManager}.getSession({arguments}.1)" - } - ] + namespace: "metrics.session", + funcName: "gpii.metrics.sessionStarted", + args: ["{that}", "{eventLog}", "{arguments}.1"] }, { + func: "{that}.events.onStartMetrics" }], "{lifecycleManager}.events.onSessionStop": [{ - namespace: "eventLog", - func: "{eventLog}.logEvent", - args: [ - "lifecycle", - "SessionStop", - { - // onSessionStop fired with [{lifecycleManager}, {session}] - gpiiKey: "{arguments}.1.model.gpiiKey", - session: "{arguments}.1.id" - } - ] - }, { namespace: "metrics.session", - funcName: "gpii.metrics.sessionStopped", - args: ["{that}", "{arguments}.1.id"] + func: "{that}.sessionStopped", + args: ["{that}", "{eventLog}", "{arguments}.1.id"] }, { "func": "{that}.events.onStopMetrics", "priority": "before:eventLog" @@ -115,11 +133,21 @@ fluid.defaults("gpii.metrics.lifecycle", { "{lifecycleManager}.events.onSessionSnapshotUpdate": { namespace: "metrics", funcName: "gpii.metrics.snapshotUpdate", - args: ["{that}", "{arguments}.1.id", "{arguments}.2"] + args: ["{that}", "{arguments}.2"] + }, + "onDestroy.session": { + func: "{that}.sessionStopped" } } }); +fluid.defaults("gpii.metrics.standalone", { + listeners: { + "onCreate": "{that}.events.onStartMetrics.fire", + "onDestroy": "{that}.events.onStopMetrics.fire" + } +}); + /** * Attached as the lifecycleManager.onCreate event. * @@ -147,22 +175,20 @@ gpii.metrics.trackPrefsSetChange = function (that, lifecycleManager) { * Log the solutions as they're applied. * * @param {Component} that - The gpii.metrics instance. - * @param {String} sessionID - Session ID. * @param {Object} originalSettings - The original settings (only interested in the keys). */ -gpii.metrics.snapshotUpdate = function (that, sessionID, originalSettings) { +gpii.metrics.snapshotUpdate = function (that, originalSettings) { var ids = fluid.keys(originalSettings); - var solutionIDs = that.sessionSolutions[sessionID]; - if (!solutionIDs) { - solutionIDs = that.sessionSolutions[sessionID] = {}; + if (!that.sessionSolutions) { + that.sessionSolutions = {}; } // Log the solution IDs that haven't been logged. fluid.each(ids, function (id) { - if (!solutionIDs[id]) { + if (!that.sessionSolutions[id]) { that.logMetric("solution-applied", { solutionID: id }); - solutionIDs[id] = true; + that.sessionSolutions[id] = true; } }); }; @@ -194,19 +220,37 @@ gpii.metrics.preferenceChanged = function (that, current, previous) { fluid.each(changedPreferences, function (value, name) { that.logMetric("preference", { name: name, - value: value + newValue: value.toString() }); }); } }; /** - * Removes the logged solution IDs for the session. + * Updates the logged gpiiKey + * @param {Component} that - The gpii.metrics instance. + * @param {Component} eventLog - The gpii.eventLog instance. + * @param {String} gpiiKey - The gpiiKey used for this session. + */ +gpii.metrics.sessionStarted = function (that, eventLog) { + eventLog.eventData.sessionID = fluid.allocateGuid(); + eventLog.logEvent("lifecycle", "SessionStart"); + that.startSubSession(); +}; + +/** + * Removes the logged solution IDs for the session, and the current gpii key. * @param {Component} that - The gpii.metrics instance. + * @param {Component} eventLog - The gpii.eventLog instance. * @param {String} sessionID - Session ID. */ -gpii.metrics.sessionStopped = function (that, sessionID) { - delete that.sessionSolutions[sessionID]; +gpii.metrics.sessionStopped = function (that, eventLog) { + if (eventLog.eventData.sessionID) { + eventLog.logEvent("lifecycle", "SessionStop"); + delete eventLog.eventData.sessionID; + that.sessionSolutions = {}; + that.startSubSession(); + } }; /** @@ -214,11 +258,40 @@ gpii.metrics.sessionStopped = function (that, sessionID) { * When the login state changes from "login" to "logout", log the solutions that did not get applied for that session. * * @param {Component} that - The gpii.metrics instance. + * @param {Component} eventLog - The eventLog instance. * @param {Component} lifecycleManager - The lifecycleManager instance. * @param {Object} oldValue - The old value. * @param {Object} newValue - The new value. */ -gpii.metrics.logonStateChanged = function (that, lifecycleManager, oldValue, newValue) { +gpii.metrics.logonStateChanged = function (that, eventLog, lifecycleManager, oldValue, newValue) { + if (newValue.type === "login") { + if (newValue.inProgress) { + // Start marking all events with the gpiiKey. + if (newValue.gpiiKey === "noUser") { + delete eventLog.eventData.gpiiKey; + } else { + eventLog.eventData.gpiiKey = newValue.gpiiKey; + } + // So metrics knows the next set of events is due to someone keying in + eventLog.eventData.logon = "in"; + } else { + // Login is complete, so all further events are unrelated to key-in. There's a 15 second grace period for + // events that may occur shortly after, however. + eventLog.eventData.logon = "in-after"; + setTimeout(function () {delete eventLog.eventData.logon;}, 15000); + } + } else if (newValue.type === "logout" && !newValue.inProgress) { + // Stop marking events with the gpiiKey when they've completely logged out + delete eventLog.eventData.gpiiKey; + + if (newValue.inProgress) { + eventLog.eventData.logon = "out"; + } else { + eventLog.eventData.logon = "out-after"; + } + setTimeout(function () {delete eventLog.eventData.logon;}, 15000); + } + if (oldValue && oldValue.type === "login" && (newValue.type === "logout" || !newValue.inProgress)) { var session = lifecycleManager.getSession(oldValue.gpiiKey); // The reason for this null checker is that the lifecyclaManager's "session" may be null after @@ -237,3 +310,17 @@ gpii.metrics.logonStateChanged = function (that, lifecycleManager, oldValue, new } } }; + +gpii.metrics.startSubSession = function (that, eventLog) { + eventLog.eventData.subSessionID = eventLog.eventData.sessionID + "-" + that.subSessionIncrementer++; + that.logMetric("subsession-begin", { subSessionID: eventLog.eventData.subSessionID}); +}; + +gpii.metrics.stopSubSession = function (that, eventLog) { + if (eventLog.eventData.subSessionID) { + that.logMetric("subsession-end", { + subSessionID: eventLog.eventData.subSessionID + }); + } + delete eventLog.eventData.subSessionID; +}; diff --git a/gpii/node_modules/eventLog/test/EventLogTests.js b/gpii/node_modules/eventLog/test/EventLogTests.js index 89c9d06d8..b3bb1038a 100644 --- a/gpii/node_modules/eventLog/test/EventLogTests.js +++ b/gpii/node_modules/eventLog/test/EventLogTests.js @@ -60,15 +60,26 @@ gpii.tests.eventLog.getMachineID = function () { */ gpii.tests.eventLog.createEventLogComponent = function (logFile) { var eventLog = gpii.eventLog({ - gradeNames: "gpii.lifecycleManager", + gradeNames: ["gpii.lifecycleManager", "gpii.metrics"], distributeOptions: { - record: "gpii.installID.test", - target: "{that installID}.options.gradeNames" + installID: { + record: "gpii.installID.test", + target: "{that installID}.options.gradeNames" + } + }, + durationEvents: { + "test-start1": "test-end1", + "test-start2": "test-end2", + "test-start3": "test-end3", + "test-start4": "test-end4" }, members: { - installationID: gpii.tests.eventLog.installID, - logFilePath: logFile - } + eventData: { + installID: gpii.tests.eventLog.installID, + sessionID: gpii.tests.eventLog.sessionID + } + }, + logDestination: logFile }); teardowns.push(eventLog.destroy); return eventLog; @@ -77,6 +88,7 @@ gpii.tests.eventLog.createEventLogComponent = function (logFile) { gpii.tests.eventLog.installID = "installation-id"; gpii.tests.eventLog.timestamp = "2000-00-00T00:00:00.000Z"; gpii.tests.eventLog.version = require("../package.json").version; +gpii.tests.eventLog.sessionID = "session-id"; gpii.tests.eventLog.testDefs = fluid.freezeRecursive(fluid.transform([ { @@ -176,6 +188,14 @@ gpii.tests.eventLog.testDefs = fluid.freezeRecursive(fluid.transform([ "data": {"data0": "value0"} } }, + { + // test log level (dropped) + testData: { + func: "log", + args: ["{that}", "module14b", "event14b", {"data0": "value0"}, fluid.logLevel.TRACE] + }, + expected: null + }, { // logError testData: { @@ -190,12 +210,306 @@ gpii.tests.eventLog.testDefs = fluid.freezeRecursive(fluid.transform([ "error": {"error15": "value15", "stack": ""} } } + }, + // Duration events + { + // start + testData: { + func: "log", + args: ["{that}", "module", "test-start1", {"value": "yes"}] + }, + expected: { + "module": "module", + "event": "test-start1", + "level": "INFO", + "data": { + "value": "yes" + } + } + }, + { + // end with data + testData: { + func: "log", + args: ["{that}", "module", "test-end1", {"value": "yes"}] + }, + expected: { + "module": "module", + "event": "test-end1", + "level": "INFO", + "data": { + "value": "yes", + "duration": 2 + } + } + }, + { + // start with no data + testData: { + func: "log", + args: ["{that}", "module", "test-start1" ] + }, + expected: { + "module": "module", + "event": "test-start1", + "level": "INFO" + } + }, + { + // end with no data + testData: { + func: "log", + args: ["{that}", "module", "test-end1" ] + }, + expected: { + "module": "module", + "event": "test-end1", + "level": "INFO", + "data": { + "duration": 2 + } + } + }, + // multiple pairs stacked + { + // start 1 + testData: { + func: "log", + args: ["{that}", "module", "test-start1" ] + }, + expected: { + "module": "module", + "event": "test-start1", + "level": "INFO" + } + }, + { + // start 2 + testData: { + func: "log", + args: ["{that}", "module", "test-start2" ] + }, + expected: { + "module": "module", + "event": "test-start2", + "level": "INFO" + } + }, + { + // end 2 + testData: { + func: "log", + args: ["{that}", "module", "test-end2" ] + }, + expected: { + "module": "module", + "event": "test-end2", + "level": "INFO", + "data": { + "duration": 2 + } + } + }, + { + // end 1 + testData: { + func: "log", + args: ["{that}", "module", "test-end1" ] + }, + expected: { + "module": "module", + "event": "test-end1", + "level": "INFO", + "data": { + "duration": 2 + } + } + }, + // multiple pairs intersected + { + // start 1 + testData: { + func: "log", + args: ["{that}", "module", "test-start1" ] + }, + expected: { + "module": "module", + "event": "test-start1", + "level": "INFO" + } + }, + { + // start 2 + testData: { + func: "log", + args: ["{that}", "module", "test-start2" ] + }, + expected: { + "module": "module", + "event": "test-start2", + "level": "INFO" + } + }, + { + // end 1 + testData: { + func: "log", + args: ["{that}", "module", "test-end1" ] + }, + expected: { + "module": "module", + "event": "test-end1", + "level": "INFO", + "data": { + "duration": 2 + } + } + }, + { + // end 2 + testData: { + func: "log", + args: ["{that}", "module", "test-end2" ] + }, + expected: { + "module": "module", + "event": "test-end2", + "level": "INFO", + "data": { + "duration": 2 + } + } + }, + // same pairs stacked + { + // start 1 + testData: { + func: "log", + args: ["{that}", "module", "test-start1" ] + }, + expected: { + "module": "module", + "event": "test-start1", + "level": "INFO" + } + }, + { + // start 1 (again) + testData: { + func: "log", + args: ["{that}", "module", "test-start1" ] + }, + expected: { + "module": "module", + "event": "test-start1", + "level": "INFO" + } + }, + { + // end 1 + testData: { + func: "log", + args: ["{that}", "module", "test-end1" ] + }, + expected: { + "module": "module", + "event": "test-end1", + "level": "INFO", + "data": { + "duration": 2 + } + } + }, + { + // end 1 (again) + testData: { + func: "log", + args: ["{that}", "module", "test-end1" ] + }, + expected: { + "module": "module", + "event": "test-end1", + "level": "INFO", + "data": { + "duration": 2 + } + } + }, + // End without start + { + // end 3 (no start) + testData: { + func: "log", + args: ["{that}", "module", "test-end3" ] + }, + expected: { + "module": "module", + "event": "test-end3", + "level": "INFO" + } + }, + { + // start 3 (prove the test previous is correct) + testData: { + func: "log", + args: ["{that}", "module", "test-start3" ] + }, + expected: { + "module": "module", + "event": "test-start3", + "level": "INFO" + } + }, + { + // end 3 + testData: { + func: "log", + args: ["{that}", "module", "test-end3" ] + }, + expected: { + "module": "module", + "event": "test-end3", + "level": "INFO", + "data": { + "duration": 2 + } + } + }, + // Already has duration data + { + // start 4 + testData: { + func: "log", + args: ["{that}", "module", "test-start3" ] + }, + expected: { + "module": "module", + "event": "test-start3", + "level": "INFO" + } + }, + { + // end 3 + testData: { + func: "log", + args: ["{that}", "module", "test-end3", {"duration": "hello"} ] + }, + expected: { + "module": "module", + "event": "test-end3", + "level": "INFO", + "data": { + "duration": "hello", + "duration_auto": 2 + } + } } ], function (item) { fluid.each(fluid.makeArray(item.expected), function (expected) { expected.timestamp = gpii.tests.eventLog.timestamp; expected.installID = gpii.tests.eventLog.installID; expected.version = gpii.tests.eventLog.version; + expected.sessionID = gpii.tests.eventLog.sessionID; }); return item; })); @@ -316,6 +630,7 @@ jqUnit.asyncTest("Uncaught exception test", function () { "installID": gpii.tests.eventLog.installID, "timestamp": gpii.tests.eventLog.timestamp, "version": gpii.tests.eventLog.version, + "sessionID": gpii.tests.eventLog.sessionID, "level": "FAIL", "module": "GPII", "event": "Error.Exception", @@ -367,6 +682,7 @@ jqUnit.asyncTest("fluid.fail test", function () { "installID": gpii.tests.eventLog.installID, "timestamp": gpii.tests.eventLog.timestamp, "version": gpii.tests.eventLog.version, + "sessionID": gpii.tests.eventLog.sessionID, "level": "FAIL", "module": "GPII", "event": "Error.Fail", @@ -395,6 +711,13 @@ jqUnit.asyncTest("eventLog tests #1", function () { var that = gpii.tests.eventLog.createEventLogComponent(logFile); + // mock hrtime so it return something expected + var hrtime = process.hrtime; + process.hrtime = function (arg) { + return arg ? [2, 0] : [3, 0]; + }; + + // Log the test data for (var n = 0; n < tests.length; n++) { var test = tests[n]; @@ -410,6 +733,8 @@ jqUnit.asyncTest("eventLog tests #1", function () { expected.push.apply(expected, fluid.makeArray(test.expected)); } + process.hrtime = hrtime; + gpii.tests.eventLog.checkLogFile(logFile, expected) .then(jqUnit.start); }); diff --git a/gpii/node_modules/flowManager/src/FlowManager.js b/gpii/node_modules/flowManager/src/FlowManager.js index ea576829e..3edd75775 100644 --- a/gpii/node_modules/flowManager/src/FlowManager.js +++ b/gpii/node_modules/flowManager/src/FlowManager.js @@ -177,7 +177,10 @@ fluid.defaults("gpii.flowManager.local", { } }, eventLog: { - type: "gpii.eventLog" + type: "gpii.eventLog", + options: { + gradeNames: ["gpii.metrics", "gpii.metrics.lifecycle"] + } }, userListeners: { type: "gpii.userListeners" diff --git a/testData/deviceReporter/installedSolutions.json b/testData/deviceReporter/installedSolutions.json index 61e6d7f1f..ff3f394df 100644 --- a/testData/deviceReporter/installedSolutions.json +++ b/testData/deviceReporter/installedSolutions.json @@ -40,6 +40,10 @@ "id": "com.microsoft.windows.cursors" }, + { + "id": "com.microsoft.windows.colorFilters" + }, + { "id": "com.microsoft.windows.filterKeys" }, diff --git a/testData/preferences/brenda.json5 b/testData/preferences/brenda.json5 new file mode 100644 index 000000000..3f31f290d --- /dev/null +++ b/testData/preferences/brenda.json5 @@ -0,0 +1,23 @@ +// # Brenda.json5 +// +// This preference sets changes the desktop wallpaper with another default Windows 10 one. +// +// ## Testing +// +// Windows desktop background should be replaced by with other one. +// +{ + "flat": { + "name": "Brenda", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.desktopBackground": { + "Image": "C:\\Windows\\Web\\Wallpaper\\Theme1\\img2.jpg" + } + } + } + } + } +} diff --git a/testData/preferences/bryan.json5 b/testData/preferences/bryan.json5 new file mode 100644 index 000000000..f206d410f --- /dev/null +++ b/testData/preferences/bryan.json5 @@ -0,0 +1,25 @@ +// # Bryan.json5 +// +// This preference sets changes the desktop wallpaper with another default Windows 10 one, +// at sets the Scaling to "Fill". +// +// ## Testing +// +// Windows desktop background should be replaced by with other one, and scaling set to "Fit". +// +{ + "flat": { + "name": "Bryan", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.desktopBackground": { + "Scaling": "Fill", + "Image": "C:\\Windows\\Web\\Wallpaper\\Theme1\\img3.jpg" + } + } + } + } + } +} diff --git a/testData/preferences/caroline.json5 b/testData/preferences/caroline.json5 new file mode 100644 index 000000000..2b4a5895f --- /dev/null +++ b/testData/preferences/caroline.json5 @@ -0,0 +1,22 @@ +//# Caroline +// +// This preference set sets the Windows color filters for a user with deuteranopia. +// +//## Testing +// +// Color should cahnge when the user is logged in, green color should be weaker than before. +{ + "flat": { + "name": "Caroline", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.colorFilters": { + "FilterType": 3 + } + } + } + } + } +} diff --git a/testData/preferences/daniel-raw.json5 b/testData/preferences/daniel-raw.json5 new file mode 100644 index 000000000..95e0c4eea --- /dev/null +++ b/testData/preferences/daniel-raw.json5 @@ -0,0 +1,30 @@ +// # Daniel-raw.json5 +// +// This preference sets the desktop background to a solid color. This is the same +// preference set as Daniels one, but without using the convenience transforms. +// +// ## Testing +// +// Windows desktop background should be replace by a green image. +// +{ + "flat": { + "name": "Daniel-raw", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.desktopBackgroundColor": { + "ImageConfig": { + "path": "pvParam", + "value": "" + }, + "SolidColorConfig": { + "value": { "r": 67, "g": 187, "b": 19 } + } + } + } + } + } + } +} diff --git a/testData/preferences/daniel.json5 b/testData/preferences/daniel.json5 new file mode 100644 index 000000000..2b47eb224 --- /dev/null +++ b/testData/preferences/daniel.json5 @@ -0,0 +1,23 @@ +// # Daniel.json5 +// +// This preference sets the desktop background to a solid color. +// +// ## Testing +// +// Windows desktop background should be replace by a green image. +// +{ + "flat": { + "name": "Daniel", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.desktopBackgroundColor": { + "SolidColor": { "r": 67, "g": 187, "b": 19 } + } + } + } + } + } +} diff --git a/testData/solutions/win32.json5 b/testData/solutions/win32.json5 index 0cfba1702..7c4e30a0d 100644 --- a/testData/solutions/win32.json5 +++ b/testData/solutions/win32.json5 @@ -4821,7 +4821,8 @@ "path": "Control Panel\\Desktop", "dataTypes": { "TileWallpaper": "REG_SZ", - "WallpaperStyle": "REG_SZ" + "WallpaperStyle": "REG_SZ", + "Scaling": "REG_SZ" } }, "supportedSettings": { @@ -4905,15 +4906,15 @@ "type": "fluid.transforms.valueMapper", "defaultInputPath": "http://registry\\.gpii\\.net/applications/com\\.microsoft\\.windows\\.desktopBackground.Scaling", "match": { - "Fill": "6", - "Fit": "10", + "Fill": "10", + "Fit": "6", "Stretch": "2", "Tile": "0", "Center": "0", "Span": "22" }, "noMatch": { - "outputValue": "6" + "outputValue": "10" } } } @@ -4924,8 +4925,8 @@ "type": "fluid.transforms.valueMapper", "defaultInputPath": "WallpaperStyle", "match": { - "6": "Fill", - "10": "Fit", + "10": "Fill", + "6": "Fit", "2": "Stretch", "0": { "outputValue": { @@ -5047,6 +5048,9 @@ } } }, + "capabilities": [ + "http://registry\\.gpii\\.net/applications/com\\.microsoft\\.windows\\.desktopBackgroundColor.SolidColor" + ], "capabilitiesTransformations": { "ImageConfig": { "transform": { @@ -5061,9 +5065,6 @@ } } } - }, - "inverseCapabilitiesTransformations": { - "http://registry\\.gpii\\.net/applications/com\\.microsoft\\.windows\\.desktopBackgroundColor.Image": "ImageConfig.value" } }, "configureSolidColor": { @@ -5119,6 +5120,14 @@ } } }, + "configure": [ + "settings.configureImage", + "settings.configureSolidColor" + ], + "restore": [ + "settings.configureImage", + "settings.configureSolidColor" + ], "isInstalled": [ { "type": "gpii.deviceReporter.alwaysInstalled" @@ -6278,6 +6287,203 @@ } ] }, + "com.microsoft.windows.colorFilters": { + "name": "Windows Built-in Screen Magnifier", + "contexts": { + "OS": [ + { + "id": "win32", + "version": ">=5.0" + } + ] + }, + "capabilities": [ + ], + "settingsHandlers": { + "configure": { + "type": "gpii.windows.systemSettingsHandler", + "liveness": "live", + "options": { + "CheckResult": true + }, + "supportedSettings": { + "SystemSettings_Accessibility_ColorFiltering_IsEnabled": { + "schema": { + "title": "Enable color filtering", + "description": "Enable or disables Windows color filters.", + "type": "boolean", + "default": false + } + }, + "SystemSettings_Accessibility_ColorFiltering_FilterType": { + "schema": { + "title": "Color filter type", + "description": "Selects the Windows color filter to be used.", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "enumLabels": [ + "Grayscale", + "Inverted", + "Grayscale inverted", + "Red-green (green weak, deuteranopia)", + "Red-green (red weak, protanopia)", + "Blue-yellow (tritanopia)" + ], + "default": 1 + } + }, + "SystemSettings_Accessibility_ColorFiltering_IsShortcutKeyEnabled": { + "schema": { + "title": "Shortcut key for color filters", + "description": "Allow the shurtcut key (Windows + Ctrl + C) to toggle color filter on or off.", + "type": "boolean", + "default": false + } + } + }, + "capabilitiesTransformations": { + "SystemSettings_Accessibility_ColorFiltering_IsEnabled": { + "transform": { + "type": "fluid.transforms.valueMapper", + "defaultInputPath": "http://registry\\.gpii\\.net/applications/com\\.microsoft\\.windows\\.colorFilters.FilterType", + "defaultOutputPath": "value", + "match": [ + { + "inputValue": 0, + "outputValue": false + } + ], + "noMatch": { + "outputValue": true + } + } + }, + "SystemSettings_Accessibility_ColorFiltering_FilterType": { + "transform": { + "type": "fluid.transforms.valueMapper", + "defaultInputPath": "http://registry\\.gpii\\.net/applications/com\\.microsoft\\.windows\\.colorFilters.FilterType", + "defaultOutputPath": "value", + "match": [ + { + "inputValue": 1, + "outputValue": 0 + }, + { + "intputValue": 2, + "outputValue": 1 + }, + { + "inputValue": 3, + "outputValue": 2 + }, + { + "inputValue": 4, + "outputValue": 3 + }, + { + "inputValue": 5, + "outputValue": 4 + }, + { + "inputValue": 6, + "outputValue": 5 + } + ], + "noMatch": { + "outputValue": 0 + } + } + }, + "SystemSettings_Accessibility_ColorFiltering_IsShortcutKeyEnabled": { + "transform": { + "type": "fluid.transforms.value", + "inputPath": "http://registry\\.gpii\\.net/applications/com\\.microsoft\\.windows\\.colorFilters.ShortcutKeyEnable", + "outputPath": "value" + } + } + }, + "inverseCapabilitiesTransformations": { + "http://registry\\.gpii\\.net/applications/com\\.microsoft\\.windows\\.colorFilters.Enable": "SystemSettings_Accessibility_ColorFiltering_IsEnabled.value", + "http://registry\\.gpii\\.net/applications/com\\.microsoft\\.windows\\.colorFilters.FilterType": { + "transform": { + "type": "fluid.transforms.valueMapper", + "defaultInputPath": "SystemSettings_Accessibility_ColorFiltering_IsEnabled.value", + "defaultOutputPath": "value", + "match": [ + { + "inputValue": 0, + "outputValue": 0 + }, + { + "inputValue": 1, + "outputValue": { + "transform": { + "type": "fluid.transforms.valueMapper", + "defaultInputPath": "SystemSettings_Accessibility_ColorFiltering_FilterType.value", + "defaultOutputPath": "value", + "match": [ + { + "inputValue": 0, + "outputValue": 1 + }, + { + "intputValue": 1, + "outputValue": 2 + }, + { + "inputValue": 2, + "outputValue": 3 + }, + { + "inputValue": 3, + "outputValue": 4 + }, + { + "inputValue": 4, + "outputValue": 5 + }, + { + "inputValue": 5, + "outputValue": 6 + } + ], + "noMatch": { + "outputValue": 0 + } + } + } + } + ], + "noMatch": { + "outputValue": 0 + } + } + }, + "http://registry\\.gpii\\.net/applications/com\\.microsoft\\.windows\\.colorFilters.ShortcutKeyEnable": "SystemSettings_Accessibility_ColorFiltering_IsShortcutKeyEnabled.value" + } + } + }, + "update": [ + "settings.configure" + ], + "configure": [ + "settings.configure" + ], + "restore": [ + "settings.configure" + ], + "isInstalled": [ + { + "type": "gpii.deviceReporter.alwaysInstalled" + } + ] + }, "com.microsoft.windows.filterKeys": { "name": "Windows FilterKeys", "contexts": { diff --git a/tests/data/preferences/os_win_2.json5 b/tests/data/preferences/os_win_solidColor.json5 similarity index 100% rename from tests/data/preferences/os_win_2.json5 rename to tests/data/preferences/os_win_solidColor.json5 diff --git a/tests/data/preferences/os_win_solidColor_tf.json5 b/tests/data/preferences/os_win_solidColor_tf.json5 new file mode 100644 index 000000000..d9d572c71 --- /dev/null +++ b/tests/data/preferences/os_win_solidColor_tf.json5 @@ -0,0 +1,14 @@ +{ + "flat": { + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.desktopBackgroundColor": { + "SolidColor": { "r": 67, "g": 187, "b": 19 } + } + } + } + } + } +} diff --git a/tests/data/preferences/os_win_wallpaper_fill.json5 b/tests/data/preferences/os_win_wallpaper_fill.json5 new file mode 100644 index 000000000..c1017bf77 --- /dev/null +++ b/tests/data/preferences/os_win_wallpaper_fill.json5 @@ -0,0 +1,15 @@ +{ + "flat": { + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.desktopBackground": { + "Scaling": "Fill", + "Image": "C:\\Windows\\Web\\Wallpaper\\Theme1\\img3.jpg" + } + } + } + } + } +} diff --git a/tests/data/preferences/os_win_wallpaper_fit.json5 b/tests/data/preferences/os_win_wallpaper_fit.json5 new file mode 100644 index 000000000..0fe76f136 --- /dev/null +++ b/tests/data/preferences/os_win_wallpaper_fit.json5 @@ -0,0 +1,15 @@ +{ + "flat": { + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.desktopBackground": { + "Scaling": "Fit", + "Image": "C:\\Windows\\Web\\Wallpaper\\Theme1\\img2.jpg" + } + } + } + } + } +} diff --git a/tests/data/preferences/os_win_wallpaper_tile.json5 b/tests/data/preferences/os_win_wallpaper_tile.json5 new file mode 100644 index 000000000..38cf5cfad --- /dev/null +++ b/tests/data/preferences/os_win_wallpaper_tile.json5 @@ -0,0 +1,15 @@ +{ + "flat": { + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.desktopBackground": { + "Scaling": "Tile", + "Image": "C:\\Windows\\Web\\Wallpaper\\Theme1\\img4.jpg" + } + } + } + } + } +} diff --git a/tests/platform/windows/windows-builtIn-testSpec.js b/tests/platform/windows/windows-builtIn-testSpec.js index b4813b62a..726dfa430 100644 --- a/tests/platform/windows/windows-builtIn-testSpec.js +++ b/tests/platform/windows/windows-builtIn-testSpec.js @@ -836,8 +836,8 @@ gpii.tests.windows.builtIn = [ }, gradeNames: "gpii.test.integration.actionHandlersAware.windows" }, { - name: "Testing os_win_2 using default matchmaker", - gpiiKey: "os_win_2", + name: "Testing os_win_solidColor using default matchmaker", + gpiiKey: "os_win_solidColor", initialState: {}, settingsHandlers: { "gpii.windows.spiSettingsHandler": { @@ -875,7 +875,178 @@ gpii.tests.windows.builtIn = [ }] } } - }, { + }, + { + name: "Testing os_win_solidColor_tf using default matchmaker", + gpiiKey: "os_win_solidColor_tf", + initialState: {}, + settingsHandlers: { + "gpii.windows.spiSettingsHandler": { + "com.microsoft.windows.desktopBackgroundColor": [{ + "settings": { + "ImageConfig": { + "path": "pvParam", + "value": "" + } + }, + "options": { + "getAction": "SPI_GETDESKWALLPAPER", + "setAction": "SPI_SETDESKWALLPAPER", + "uiParam": 260, + "pvParam": { + "type": "array", + "valueType": "TCHAR", + "length": 260 + } + } + }] + }, + "gpii.windows.nativeSettingsHandler": { + "com.microsoft.windows.desktopBackgroundColor": [{ + "settings": { + "SolidColorConfig": { + "value": { + "r": 67, "g": 187, "b": 19 + } + } + }, + "options": { + "functionName": "SolidColor" + } + }] + } + } + }, + { + name: "Testing os_win_wallpaper_fill using default matchmaker", + gpiiKey: "os_win_wallpaper_fill", + initialState: {}, + settingsHandlers: { + "gpii.windows.registrySettingsHandler": { + "com.microsoft.windows.desktopBackground": [{ + "settings": { + "TileWallpaper": "0", + "WallpaperStyle": "10" + }, + "options": { + "hKey": "HKEY_CURRENT_USER", + "path": "Control Panel\\Desktop", + "dataTypes": { + "TileWallpaper": "REG_SZ", + "WallpaperStyle": "REG_SZ" + } + } + }] + }, + "gpii.windows.spiSettingsHandler": { + "com.microsoft.windows.desktopBackground": [{ + "settings": { + "ImageConfig": { + "path": "pvParam", + "value": "C:\\Windows\\Web\\Wallpaper\\Theme1\\img3.jpg" + } + }, + "options": { + "getAction": "SPI_GETDESKWALLPAPER", + "setAction": "SPI_SETDESKWALLPAPER", + "uiParam": 260, + "pvParam": { + "type": "array", + "valueType": "TCHAR", + "length": 260 + } + } + }] + } + } + }, + { + name: "Testing os_win_wallpaper_fit using default matchmaker", + gpiiKey: "os_win_wallpaper_fit", + initialState: {}, + settingsHandlers: { + "gpii.windows.registrySettingsHandler": { + "com.microsoft.windows.desktopBackground": [{ + "settings": { + "TileWallpaper": "0", + "WallpaperStyle": "6" + }, + "options": { + "hKey": "HKEY_CURRENT_USER", + "path": "Control Panel\\Desktop", + "dataTypes": { + "TileWallpaper": "REG_SZ", + "WallpaperStyle": "REG_SZ" + } + } + }] + }, + "gpii.windows.spiSettingsHandler": { + "com.microsoft.windows.desktopBackground": [{ + "settings": { + "ImageConfig": { + "path": "pvParam", + "value": "C:\\Windows\\Web\\Wallpaper\\Theme1\\img2.jpg" + } + }, + "options": { + "getAction": "SPI_GETDESKWALLPAPER", + "setAction": "SPI_SETDESKWALLPAPER", + "uiParam": 260, + "pvParam": { + "type": "array", + "valueType": "TCHAR", + "length": 260 + } + } + }] + } + } + }, + { + name: "Testing os_win_wallpaper_tile using default matchmaker", + gpiiKey: "os_win_wallpaper_tile", + initialState: {}, + settingsHandlers: { + "gpii.windows.registrySettingsHandler": { + "com.microsoft.windows.desktopBackground": [{ + "settings": { + "TileWallpaper": "1", + "WallpaperStyle": "0" + }, + "options": { + "hKey": "HKEY_CURRENT_USER", + "path": "Control Panel\\Desktop", + "dataTypes": { + "TileWallpaper": "REG_SZ", + "WallpaperStyle": "REG_SZ" + } + } + }] + }, + "gpii.windows.spiSettingsHandler": { + "com.microsoft.windows.desktopBackground": [{ + "settings": { + "ImageConfig": { + "path": "pvParam", + "value": "C:\\Windows\\Web\\Wallpaper\\Theme1\\img4.jpg" + } + }, + "options": { + "getAction": "SPI_GETDESKWALLPAPER", + "setAction": "SPI_SETDESKWALLPAPER", + "uiParam": 260, + "pvParam": { + "type": "array", + "valueType": "TCHAR", + "length": 260 + } + } + }] + } + } + }, + { name: "Testing os_common using default matchmaker", gpiiKey: "os_common", initialState: {