harbour-fernschreiber/qml/js/debug.js
John Gibbon 214e6d335b Implement suggested changes
- (rebase to current master)
 - put compareAndRepeat in debug.js
 - create a new DebugPage for tests instead of AboutPage; only visible in debug mode
 - finalize PR state (remove functionsOLD left for comparisons)
2020-12-04 21:23:02 +01:00

130 lines
5.3 KiB
JavaScript

/*
Copyright (C) 2020 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
Fernschreiber is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Fernschreiber is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
.pragma library
.import WerkWolf.Fernschreiber 1.0 as Fernschreiber
var enabled = Fernschreiber.DebugLog.enabled;
var log = enabled ? console.log : function(){};
var assert = enabled ? console.assert : function(){};
var time = enabled ? console.time : function(){};
var timeEnd = enabled ? console.timeEnd : function(){};
var trace = enabled ? console.trace : function(){};
var count = enabled ? console.count : function(){};
var profile = enabled ? console.profile : function(){};
var profileEnd = enabled ? console.profileEnd : function(){};
var exception = enabled ? console.exception : function(){};
var warn = enabled ? console.warn: function(){};
// Debug.error is always enabled.
var error = console.error;
Fernschreiber.DebugLog.enabledChanged.connect(function() {
enabled = Fernschreiber.DebugLog.enabled;
if(enabled) {
log = console.log;
assert = console.assert;
time = console.time;
timeEnd = console.timeEnd;
trace = console.trace;
count = console.count;
profile = console.profile;
profileEnd = console.profileEnd;
exception = console.exception;
} else {
log = assert = time = timeEnd = trace = count = profile = profileEnd = exception = warn = function(){};
}
});
/**
* @function compareAndRepeat
* This function compares results of two functions for multiple sets of arguments and then repeats those calls to compare them with profiling.
* Testing showed that results are slightly skewed in favor of the secondaryProfileFunction, which (together with external factors)
* is almost compensated by running the calls often.
*
* @param {string} title - used for Debug output
* @param {function} profileFunction - function to compare
* @param {function} [secondaryProfileFunction] - secondary function to compare. Use falsy value to skip comparison.
* @param {Array.<Array.<*>>} [functionArgumentsArray = []] - argument sets to run both functions with
* @param {number} [testCount = 10000] - times to run each function in addition to result comparison
* @param {Object} [testContext = null] - If the functions use `this` or otherwise depend on a Closure/Execution context, it can be set here. If they, for example, use properties of a QML `Item`, you can use that `Item` as `testContext`.
*
* @example
* // to compare the results of Debug.log("first", "second", "third")
* // and Debug.log("some value")
* // with the same calls of console.log and run 100 times each:
* Debug.compareAndRepeat("Debuglog",
* Debug.log, console.log,
* [["first", "second", "third"], ["some value"]],
* 100
* );
*
*/
function compareAndRepeat(title, profileFunction, secondaryProfileFunction, functionArgumentsArray, testCount, testContext) {
if(!enabled) {
log("Debugging disabled, compareAndRepeat ran uselessly:", title);
return;
}
var numberOfTests = testCount || 10000,
context = testContext || null,
args = functionArgumentsArray || [],
argumentIterator = args.length || 1,
results,
resultsComparison,
functionIterator = numberOfTests;
if(secondaryProfileFunction) {
while(argumentIterator--) {
results = JSON.stringify(profileFunction.apply(context, args[argumentIterator] || null));
resultsComparison = JSON.stringify(secondaryProfileFunction.apply(context, args[argumentIterator] || null));
if(resultsComparison !== results) {
warn("\x1b[1m✘ Different return values!\x1b[0m", title)
warn("profileFunction result:", results);
warn("secondaryProfileFunction result:", resultsComparison);
return;
}
}
log("\x1b[1m✔ Comparison of", title, "return values successful!\x1b[0m")
}
log("Running", title, "with", args.length, "different argument sets", numberOfTests, "times each")
if(secondaryProfileFunction) {
// "secondaryProfileFunction"
functionIterator = numberOfTests;
while(functionIterator--) {
argumentIterator = args.length || 1;
while(argumentIterator--) {
secondaryProfileFunction.apply(context, args[argumentIterator] || null);
}
}
}
// "profileFunction"
functionIterator = numberOfTests;
while(functionIterator--) {
argumentIterator = args.length || 1;
while(argumentIterator--) {
profileFunction.apply(context, args[argumentIterator] || null);
}
}
log("Ran", title, args.length * numberOfTests, "times", secondaryProfileFunction ? "and compared results successfully" : "");
}