mirror of
https://github.com/gundb/panic-server.git
synced 2026-04-15 03:00:16 -04:00
Full configuration, automatic browser tab manipulation, statistics, progress events, completion event, de-duplication of acknowledgments, accurate recursive test runners, prep for selenium instantiation, included polyfills and extensions.
New barrage of options now accepted (providing defaults in absence), such as a progress callback, a done callback and timeout configuration, a data generation function, etc... Browser constructor meant to open new tabs, run the test (using the given options), and close the tab when finished. There is no loss of options by doing this (although it has had a notable effect on performance when the tab is not in focus). Each progress event and the done event recieve the options object with statistics embeded in the "stats" property. It has details like the average latency, the total elapsed time, the acknowledged packets, the fastest/slowest times, etc... Acknowledgments can fire more than once, and for now it dedupes those and only fires once. In the future I may change this to include every acknowledgment. For data! The recursive test runner wasn't running the correct number of times (due to confusion and off-by-1 errors)... that has been fixed. The test file checks to see if there were options globally declared. If so, run the test using those. In the future we may use selenium to boot up the browsers, and we'll need some way to kick off the test and provide options. That was a nice compromise. I've included some useful polyfills (like interfacing with console.log so it can be passed naked to other functions, Object.keys polyfill for counting confirmed objects) and some extensions (like valMapDone). Next steps include adding the gun instance to the options object, statistics for errors, node compatibility and the such.
This commit is contained in:
11
browser.js
11
browser.js
@@ -8,7 +8,16 @@ var Browser;
|
||||
return new Browser(opt);
|
||||
}
|
||||
var browser = this;
|
||||
browser.opt = patch(opt);
|
||||
browser.opt = opt;
|
||||
opt.done.cb = (function () {
|
||||
var cb = opt.done.cb;
|
||||
return function (opt) {
|
||||
if (cb) {
|
||||
cb.apply(this, arguments);
|
||||
}
|
||||
browser.close();
|
||||
};
|
||||
}());
|
||||
browser.window = window.open('./', browser.opt.id);
|
||||
browser.window.addEventListener('load', function () {
|
||||
browser.window.test(browser.opt);
|
||||
|
||||
42
done.js
Normal file
42
done.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/*globals Gun, console, stats */
|
||||
|
||||
/*
|
||||
check it against opt.id
|
||||
make sure everything is fabulous
|
||||
|
||||
begin with a timeout + interval,
|
||||
restart on each acknowledgment
|
||||
*/
|
||||
function finisher() {
|
||||
'use strict';
|
||||
|
||||
return (function () {
|
||||
var timeout;
|
||||
|
||||
function done(db, opt) {
|
||||
var saved = 0;
|
||||
opt.requests.end = Gun.time.is();
|
||||
|
||||
function scan(obj) {
|
||||
if (obj.id === opt.id) {
|
||||
saved += 1;
|
||||
}
|
||||
}
|
||||
|
||||
db.path(opt.path).each(scan, function () {
|
||||
stats(opt, saved);
|
||||
opt.done.cb(opt);
|
||||
});
|
||||
}
|
||||
|
||||
return function (db, opt) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(function () {
|
||||
done(db, opt);
|
||||
}, opt.interval + opt.done.timeout);
|
||||
};
|
||||
|
||||
|
||||
|
||||
}());
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
<title>Battle Test</title>
|
||||
<link rel="stylesheet" href="index.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -65,6 +66,10 @@
|
||||
Any help on building this out would be appreciated!
|
||||
</p>
|
||||
<script src="gun.js"></script>
|
||||
<script src="polyfill.js"></script>
|
||||
<script src="statistics.js"></script>
|
||||
<script src="done.js"></script>
|
||||
<script src="node_modules/mavis/index.js"></script>
|
||||
<script src="patch.js"></script>
|
||||
<script src="index.js"></script>
|
||||
<script src="browser.js"></script>
|
||||
|
||||
54
index.js
54
index.js
@@ -1,35 +1,57 @@
|
||||
/*globals Gun, console, patch */
|
||||
var test, gun;
|
||||
/*globals Gun, console, patch, finisher, stats */
|
||||
var test;
|
||||
|
||||
test = function (opt) {
|
||||
'use strict';
|
||||
|
||||
opt = patch(opt);
|
||||
var i, count, db;
|
||||
|
||||
var gun, resetDoneTimer, db, confirmed = {};
|
||||
resetDoneTimer = finisher();
|
||||
gun = new Gun(opt.peers);
|
||||
db = gun.get(opt.key).set();
|
||||
count = 0;
|
||||
|
||||
function ack(num) {
|
||||
return function (err, ok) {
|
||||
count += 1;
|
||||
console.log('ACK:', err, ok, 'on', num);
|
||||
if (count === opt.amount) {
|
||||
console.log('0% loss');
|
||||
if (confirmed[num]) {
|
||||
return;
|
||||
}
|
||||
opt.requests[num - 1].end = Gun.time.is();
|
||||
confirmed[num] = true;
|
||||
opt.progress(opt, num);
|
||||
resetDoneTimer(db, stats(opt));
|
||||
};
|
||||
}
|
||||
function run(num) {
|
||||
db.set(opt.data, ack(num));
|
||||
|
||||
if (num === opt.amount) {
|
||||
return db;
|
||||
function run(num) {
|
||||
var cb, packet = opt.packet();
|
||||
cb = ack(num);
|
||||
opt.requests[num - 1] = {
|
||||
start: packet.time
|
||||
};
|
||||
|
||||
db.path(opt.path).set(packet, cb);
|
||||
|
||||
if (num === opt.packets) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
run(num + 1);
|
||||
}, opt.interval > 16 ? opt.interval : Infinity);
|
||||
return db;
|
||||
}
|
||||
}, opt.interval);
|
||||
|
||||
return run(0);
|
||||
return opt;
|
||||
}
|
||||
resetDoneTimer(db, opt);
|
||||
|
||||
return run(1);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
if (window.options) {
|
||||
test(window.options);
|
||||
}
|
||||
}
|
||||
|
||||
116
patch.js
116
patch.js
@@ -1,33 +1,109 @@
|
||||
/*globals Gun*/
|
||||
/*globals Gun, type, console*/
|
||||
/*
|
||||
Expect an object like this:
|
||||
Return an object like this:
|
||||
|
||||
|
||||
var thing = {
|
||||
return {
|
||||
expect: Object,
|
||||
done: Function,
|
||||
progress: Function,
|
||||
interval: Number,
|
||||
peers: Array,
|
||||
amount: Number,
|
||||
packets: Number,
|
||||
id: String,
|
||||
key: String,
|
||||
path: String,
|
||||
data: *
|
||||
packet: Function
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
function patch(opt) {
|
||||
var patch;
|
||||
(function () {
|
||||
'use strict';
|
||||
var url = location.protocol + '//';
|
||||
url += location.host;
|
||||
url += '/gun';
|
||||
var random = Gun.text.random;
|
||||
|
||||
opt = opt || {};
|
||||
opt.key = opt.key || 'panic/test/';
|
||||
opt.interval = opt.interval || 0;
|
||||
opt.peers = opt.peers || [url];
|
||||
opt.amount = opt.amount || 300;
|
||||
opt.id = opt.id || Gun.text.random();
|
||||
opt.path = opt.path || Gun.text.random();
|
||||
opt.data = opt.data || Gun.text.random();
|
||||
return opt;
|
||||
}
|
||||
function is(data, expected) {
|
||||
var string, error, actual = type(data);
|
||||
if (actual !== expected) {
|
||||
string = JSON.stringify(data);
|
||||
error = 'Expected ' + expected + ', was ' + actual + ': ' + string;
|
||||
throw new TypeError(error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function or(obj, prop, val) {
|
||||
return (obj[prop] = obj[prop] || val);
|
||||
}
|
||||
|
||||
function done(opt) {
|
||||
or(opt, 'done', {});
|
||||
or(opt.done, 'cb', function () {
|
||||
console.log('No finishing callback');
|
||||
});
|
||||
or(opt.done, 'timeout', 2000);
|
||||
is(opt.done, 'object');
|
||||
is(opt.done.timeout, 'number');
|
||||
is(opt.done.cb, 'function');
|
||||
}
|
||||
|
||||
function packet(opt) {
|
||||
|
||||
// wrap the packet function
|
||||
// set time and id for
|
||||
// every returned object
|
||||
opt.packet = (function () {
|
||||
var makePacket = opt.packet || function () {
|
||||
return random();
|
||||
};
|
||||
|
||||
return function () {
|
||||
return {
|
||||
data: makePacket(),
|
||||
time: Gun.time.is(),
|
||||
id: opt.id
|
||||
};
|
||||
};
|
||||
}());
|
||||
|
||||
is(opt.packet(), 'object');
|
||||
}
|
||||
|
||||
|
||||
patch = function (opt) {
|
||||
var url = location.protocol + '//';
|
||||
url += location.host;
|
||||
url += '/gun';
|
||||
|
||||
opt = opt || {};
|
||||
opt.interval = opt.interval || 0;
|
||||
opt.peers = opt.peers || [url];
|
||||
opt.packets = opt.packets || 50;
|
||||
opt.id = opt.id || random(10);
|
||||
opt.key = opt.key || random(15);
|
||||
opt.path = opt.path || random(5);
|
||||
opt.requests = [];
|
||||
opt.requests.start = Gun.time.is();
|
||||
or(opt, 'progress', function (opt, num) {
|
||||
console.log('Saved', num);
|
||||
});
|
||||
done(opt);
|
||||
packet(opt);
|
||||
|
||||
|
||||
is(opt.key, 'string');
|
||||
is(opt.interval, 'number');
|
||||
is(opt.peers, 'array');
|
||||
is(opt.packets, 'number');
|
||||
is(opt.id, 'string');
|
||||
is(opt.path, 'string');
|
||||
is(opt.progress, 'function');
|
||||
|
||||
if (opt.packets < 0) {
|
||||
throw new RangeError("Negative packets? Are you crazy?");
|
||||
}
|
||||
|
||||
return opt;
|
||||
};
|
||||
|
||||
}());
|
||||
|
||||
61
polyfill.js
Normal file
61
polyfill.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/*globals Gun, console*/
|
||||
|
||||
Object.keys = Object.keys || function (obj) {
|
||||
'use strict';
|
||||
var key, keys = [];
|
||||
for (key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
Object.values = Object.values || function (obj) {
|
||||
'use strict';
|
||||
return Object.keys(obj).map(function (key) {
|
||||
return obj[key];
|
||||
});
|
||||
};
|
||||
var setImmediate = setImmediate || function (cb) {
|
||||
'use strict';
|
||||
setTimeout(cb, Infinity);
|
||||
};
|
||||
console.log = (function () {
|
||||
'use strict';
|
||||
var peersError, log = console.log;
|
||||
peersError = 'Warning! You have no peers to connect to!';
|
||||
return function (msg) {
|
||||
if (msg === peersError) {
|
||||
return;
|
||||
}
|
||||
log.apply(console, arguments);
|
||||
};
|
||||
}());
|
||||
|
||||
Gun.chain.each = function (cb, end) {
|
||||
'use strict';
|
||||
var n = function () {},
|
||||
count = 0,
|
||||
props = [],
|
||||
gun = this;
|
||||
cb = cb || n;
|
||||
end = end || n;
|
||||
|
||||
gun.val(function (list) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
Gun.is.node(list, function (n, prop) {
|
||||
count += 1;
|
||||
props.push(prop);
|
||||
});
|
||||
props.forEach(function (prop) {
|
||||
gun.path(prop).val(function (val, key) {
|
||||
count -= 1;
|
||||
cb.apply(this, arguments);
|
||||
if (!count) {
|
||||
end.apply(this, args);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return gun;
|
||||
};
|
||||
37
statistics.js
Normal file
37
statistics.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/*globals Gun*/
|
||||
function stats(opt, saved) {
|
||||
'use strict';
|
||||
var average, slowest, fastest, elapsed, acks = [];
|
||||
fastest = null;
|
||||
slowest = null;
|
||||
average = 0;
|
||||
acks = opt.requests.filter(function (request) {
|
||||
return Boolean(request.end);
|
||||
});
|
||||
acks.forEach(function (ack) {
|
||||
var time = ack.end - ack.start;
|
||||
if (time > slowest) {
|
||||
slowest = time;
|
||||
}
|
||||
if (time < fastest || !fastest) {
|
||||
fastest = time;
|
||||
}
|
||||
average += (ack.end - ack.start);
|
||||
});
|
||||
average = average / acks.length;
|
||||
elapsed = (opt.requests.end - opt.requests.start) - opt.done.timeout - opt.interval;
|
||||
opt.stats = {
|
||||
'average latency': average,
|
||||
requested: opt.packets,
|
||||
acknowledged: acks.length,
|
||||
'slowest response': slowest,
|
||||
'fastest response': fastest,
|
||||
'total elapsed': elapsed || Gun.time.is() - opt.requests.start
|
||||
};
|
||||
|
||||
if (saved !== undefined) {
|
||||
opt.stats.confirmed = saved;
|
||||
}
|
||||
|
||||
return opt;
|
||||
}
|
||||
Reference in New Issue
Block a user