192 lines
9.0 KiB
JavaScript
192 lines
9.0 KiB
JavaScript
const util = require('util');
|
|
const d3 = require('d3-queue');
|
|
const classes = require('../support/data_classes');
|
|
|
|
module.exports = function () {
|
|
this.Then(/^routability should be$/, (table, callback) => {
|
|
this.buildWaysFromTable(table, () => {
|
|
var directions = ['forw','backw','bothw'],
|
|
testedHeaders = ['forw','backw','bothw','forw_rate','backw_rate','bothw_rate'],
|
|
headers = new Set(Object.keys(table.hashes()[0]));
|
|
|
|
if (!testedHeaders.some(k => !!headers.has(k))) {
|
|
throw new Error('*** routability table must contain either "forw", "backw", "bothw", "forw_rate" or "backw_mode" column');
|
|
}
|
|
|
|
this.reprocessAndLoadData((e) => {
|
|
if (e) return callback(e);
|
|
var testRow = (row, i, cb) => {
|
|
var outputRow = Object.assign({}, row);
|
|
// clear the fields that are tested for in the copied response object
|
|
for (var field in outputRow) {
|
|
if (testedHeaders.indexOf(field) != -1)
|
|
outputRow[field] = '';
|
|
}
|
|
|
|
testRoutabilityRow(i, (err, result) => {
|
|
if (err) return cb(err);
|
|
directions.filter(d => headers.has(d + '_rate')).forEach((direction) => {
|
|
var rate = direction + '_rate';
|
|
var want = row[rate];
|
|
|
|
switch (true) {
|
|
case '' === want:
|
|
outputRow[rate] = result[direction].status ?
|
|
result[direction].status.toString() : '';
|
|
break;
|
|
case /^\d+(\.\d+){0,1}$/.test(want):
|
|
if (result[direction].rate !== undefined && !isNaN(result[direction].rate)) {
|
|
outputRow[rate] = result[direction].rate.toString();
|
|
} else {
|
|
outputRow[rate] = '';
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error(util.format('*** Unknown expectation format: %s for header %s', want, rate));
|
|
}
|
|
});
|
|
|
|
directions.filter(d => headers.has(d)).forEach((direction) => {
|
|
var usingShortcut = false,
|
|
want = row[direction];
|
|
// shortcuts are when a test has mapped a value like `foot` to
|
|
// a value like `5 km/h`, to represent the speed that one
|
|
// can travel by foot. we check for these and use the mapped to
|
|
// value for later comparison.
|
|
if (this.shortcutsHash[row[direction]]) {
|
|
want = this.shortcutsHash[row[direction]];
|
|
usingShortcut = row[direction];
|
|
}
|
|
|
|
// TODO split out accessible/not accessible value from forw/backw headers
|
|
// rename forw/backw to forw/backw_speed
|
|
switch (true) {
|
|
case '' === want:
|
|
outputRow[direction] = result[direction].status ?
|
|
result[direction].mode : '';
|
|
break;
|
|
case 'x' === want:
|
|
outputRow[direction] = result[direction].status ?
|
|
'x' : '';
|
|
break;
|
|
case /^[\d.]+ s/.test(want):
|
|
// the result here can come back as a non-number value like
|
|
// `diff`, but we only want to apply the unit when it comes
|
|
// back as a number, for tableDiff's literal comparison
|
|
if (result[direction].time) {
|
|
outputRow[direction] = !isNaN(result[direction].time) ?
|
|
result[direction].time.toString()+' s' :
|
|
result[direction].time.toString() || '';
|
|
} else {
|
|
outputRow[direction] = '';
|
|
}
|
|
break;
|
|
case /^\d+ km\/h/.test(want):
|
|
if (result[direction].speed) {
|
|
outputRow[direction] = !isNaN(result[direction].speed) ?
|
|
result[direction].speed.toString()+' km/h' :
|
|
result[direction].speed.toString() || '';
|
|
} else {
|
|
outputRow[direction] = '';
|
|
}
|
|
break;
|
|
default:
|
|
outputRow[direction] = result[direction].mode || '';
|
|
}
|
|
|
|
if (this.FuzzyMatch.match(outputRow[direction], want)) {
|
|
outputRow[direction] = usingShortcut ? usingShortcut : row[direction];
|
|
}
|
|
});
|
|
|
|
cb(null, outputRow);
|
|
});
|
|
};
|
|
this.processRowsAndDiff(table, testRow, callback);
|
|
});
|
|
});
|
|
});
|
|
|
|
// makes simple a-b request using the given cucumber test routability conditions
|
|
// result is an object containing the calculated values for 'rate', 'status',
|
|
// 'time', 'distance', 'speed' and 'mode', for forwards and backwards routing, as well as
|
|
// a bothw object that diffs forwards/backwards
|
|
var testRoutabilityRow = (i, cb) => {
|
|
var result = {};
|
|
|
|
var testDirection = (dir, callback) => {
|
|
const coordA = this.offsetOriginBy(1+this.WAY_SPACING*i, 0);
|
|
const coordB = this.offsetOriginBy(3+this.WAY_SPACING*i, 0);
|
|
|
|
var a = new classes.Location(coordA[0], coordA[1]),
|
|
b = new classes.Location(coordB[0], coordB[1]),
|
|
r = {};
|
|
|
|
r.which = dir;
|
|
|
|
this.requestRoute((dir === 'forw' ? [a, b] : [b, a]), [], [], this.queryParams, (err, res, body) => {
|
|
if (err) return callback(err);
|
|
|
|
r.query = this.query;
|
|
r.json = JSON.parse(body);
|
|
r.code = r.json.code;
|
|
r.status = res.statusCode === 200 ? 'x' : null;
|
|
if (r.status) {
|
|
r.route = this.wayList(r.json.routes[0]);
|
|
r.summary = r.json.routes[0].legs.map(l => l.summary).join(',');
|
|
|
|
if (r.route.split(',')[0] === util.format('w%d', i)) {
|
|
r.time = r.json.routes[0].duration;
|
|
r.distance = r.json.routes[0].distance;
|
|
r.rate = Math.round(r.distance / r.json.routes[0].weight * 10) / 10.;
|
|
r.speed = r.time > 0 ? parseInt(3.6 * r.distance / r.time) : null;
|
|
|
|
// use the mode of the first step of the route
|
|
// for routability table test, we can assume the mode is the same throughout the route,
|
|
// since the route is just a single way
|
|
if( r.json.routes[0].legs[0] && r.json.routes[0].legs[0].steps[0] ) {
|
|
r.mode = r.json.routes[0].legs[0].steps[0].mode;
|
|
}
|
|
} else {
|
|
r.status = null;
|
|
}
|
|
}
|
|
|
|
callback(null, r);
|
|
});
|
|
};
|
|
|
|
d3.queue(1)
|
|
.defer(testDirection, 'forw')
|
|
.defer(testDirection, 'backw')
|
|
.awaitAll((err, res) => {
|
|
if (err) return cb(err);
|
|
// check if forw and backw returned the same values
|
|
res.forEach((dirRes) => {
|
|
var which = dirRes.which;
|
|
delete dirRes.which;
|
|
result[which] = dirRes;
|
|
});
|
|
|
|
result.bothw = {};
|
|
|
|
var sq = d3.queue();
|
|
|
|
var parseRes = (key, scb) => {
|
|
if (result.forw[key] === result.backw[key]) {
|
|
result.bothw[key] = result.forw[key];
|
|
} else {
|
|
result.bothw[key] = 'diff';
|
|
}
|
|
scb();
|
|
};
|
|
|
|
['rate', 'status', 'time', 'distance', 'speed' ,'mode'].forEach((key) => {
|
|
sq.defer(parseRes, key);
|
|
});
|
|
|
|
sq.awaitAll((err) => { cb(err, result); });
|
|
});
|
|
};
|
|
};
|