Skip to content

Commit

Permalink
Close GH-2795: Rewrite cucumber test caching (and support logic). Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMarex authored and Dane Springmeyer committed Sep 19, 2016
1 parent 8522cdd commit 7d124ce
Show file tree
Hide file tree
Showing 60 changed files with 863 additions and 1,136 deletions.
10 changes: 4 additions & 6 deletions cucumber.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
module.exports = {
default: '--require features --tags ~@stress --tags ~@todo',
verify: '--require features --tags ~@todo --tags ~@bug --tags ~@stress -f progress',
jenkins: '--require features --tags ~@todo --tags ~@bug --tags ~@stress --tags ~@options -f progress',
bugs: '--require features --tags @bug',
todo: '--require features --tags @todo',
all: '--require features'
default: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions',
verify: '--strict --tags ~@stress --tags ~@todo -f progress --require features/support --require features/step_definitions',
todo: '--strict --tags @todo --require features/support --require features/step_definitions',
all: '--strict --require features/support --require features/step_definitions'
}


Expand Down
9 changes: 4 additions & 5 deletions features/car/traffic_speeds.feature
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Feature: Traffic - speeds
| fb | primary |
Given the profile "testbot"
Given the extract extra arguments "--generate-edge-lookup"
Given the contract extra arguments "--segment-speed-file speeds.csv"
Given the contract extra arguments "--segment-speed-file {speeds_file}"
Given the speed file
"""
1,2,0
Expand Down Expand Up @@ -69,7 +69,7 @@ Feature: Traffic - speeds
| fb | primary |
Given the profile "testbot"
Given the extract extra arguments "--generate-edge-lookup"
Given the contract extra arguments "--segment-speed-file speeds.csv"
Given the contract extra arguments "--segment-speed-file {speeds_file}"
Given the speed file
"""
1,2,0
Expand Down Expand Up @@ -112,7 +112,6 @@ Feature: Traffic - speeds
| fb | primary |
Given the profile "testbot"
Given the extract extra arguments "--generate-edge-lookup"
Given the contract extra arguments "--segment-speed-file speeds.csv"
Given the speed file
"""
1,2,-10
Expand All @@ -123,6 +122,6 @@ Feature: Traffic - speeds
4,1,-5
"""
And the data has been extracted
When I run "osrm-contract --segment-speed-file speeds.csv {extracted_base}.osrm"
When I try to run "osrm-contract --segment-speed-file {speeds_file} {processed_file}"
And stderr should contain "malformed"
And it should exit with code not 0
And it should exit with an error
4 changes: 2 additions & 2 deletions features/car/traffic_turn_penalties.feature
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Feature: Traffic - turn penalties
8,11,12,23
1,4,5,-0.2
"""
And the contract extra arguments "--turn-penalty-file penalties.csv"
And the contract extra arguments "--turn-penalty-file {penalties_file}"
When I route I should get
| from | to | route | speed | time |
| a | h | ad,dhk,dhk | 63 km/h | 11.5s +-1 |
Expand All @@ -81,7 +81,7 @@ Feature: Traffic - turn penalties
# double left - hdc penalty ever so slightly higher than imn; forces all the way around

Scenario: Too-negative penalty clamps, but does not fail
Given the contract extra arguments "--turn-penalty-file penalties.csv"
Given the contract extra arguments "--turn-penalty-file {penalties_file}"
And the profile "testbot"
And the turn penalty file
"""
Expand Down
4 changes: 2 additions & 2 deletions features/guidance/anticipate-lanes.feature
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ Feature: Turn Lane Guidance
| waypoints | route | turns | lanes |
| a,e | main,main,main,main | depart,use lane straight,continue right,arrive | ,left:false straight:false straight:false straight:false straight:true straight:true right:false,straight:false straight:false right:false right:true right:true, |

@anticipate @todo @bug @2661
@anticipate @todo @2661
Scenario: Anticipate with lanes in roundabout: roundabouts as the unit of anticipation
Given the node map
| | | e | | |
Expand Down Expand Up @@ -667,7 +667,7 @@ Feature: Turn Lane Guidance
| a,f | abc,bdeh,feg,feg | depart,turn right,turn right,arrive | ,none:false none:false right:false right:true,left:false none:false none:false right:true, |

@anticipate
Scenario: Tripple Right keeping Left
Scenario: Triple Right keeping Left
Given the node map
| a | | | | b | | i |
| | | | | | | |
Expand Down
2 changes: 1 addition & 1 deletion features/guidance/turn-lanes.feature
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ Feature: Turn Lane Guidance
| a,d | hwy,hwy | depart,arrive | , |
| a,e | hwy,ramp,ramp | depart,off ramp slight right,arrive | ,straight:false straight:false straight;slight right:true slight right:true, |

@bug @todo
@todo
Scenario: Turning Off Ramp
Given the node map
| | a | |
Expand Down
31 changes: 31 additions & 0 deletions features/lib/hash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use strict';

const fs = require('fs');
const crypto = require('crypto');
const d3 = require('d3-queue');

module.exports = {
hashOfFiles: (paths, cb) => {
let queue = d3.queue();
for (let i = 0; i < paths.length; ++i) {
queue.defer(fs.readFile, paths[i]);
}
queue.awaitAll((err, results) => {
if (err) return cb(err);
let checksum = crypto.createHash('md5');
for (let i = 0; i < results.length; ++i) {
checksum.update(results[i]);
}
cb(null, checksum.digest('hex'));
});
},

hashOfFile: (path, cb) => {
fs.readFile(path, (err, result) => {
if (err) return cb(err);
let checksum = crypto.createHash('md5');
checksum.update(result);
cb(null, checksum.digest('hex'));
});
}
};
8 changes: 2 additions & 6 deletions features/support/build_osm.js → features/lib/osm.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
'use strict';

var builder = require('xmlbuilder');

var ensureDecimal = (i) => {
if (parseInt(i) === i) return i.toFixed(1);
else return i;
};
const builder = require('xmlbuilder');
const ensureDecimal = require('./utils').ensureDecimal;

class DB {
constructor () {
Expand Down
169 changes: 169 additions & 0 deletions features/lib/osrm_loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
'use strict';

const fs = require('fs');
const util = require('util');
const Timeout = require('node-timeout');
const tryConnect = require('../lib/try_connect');
const errorReason = require('./utils').errorReason;

class OSRMBaseLoader{
constructor (scope) {
this.scope = scope;
this.child = null;
}

launch (callback) {
var limit = Timeout(this.scope.TIMEOUT, { err: new Error('*** Launching osrm-routed timed out.') });

var runLaunch = (cb) => {
this.osrmUp(() => { this.waitForConnection(cb); });
};

runLaunch(limit((e) => { if (e) callback(e); else callback(); }));
}

shutdown (callback) {
if (!this.osrmIsRunning()) return callback();

var limit = Timeout(this.scope.TIMEOUT, { err: new Error('*** Shutting down osrm-routed timed out.')});

this.osrmDown(limit(callback));
}

osrmIsRunning () {
return this.child && !this.child.killed;
}

osrmDown (callback) {
if (this.osrmIsRunning()) {
this.child.on('exit', (code, signal) => {callback();});
this.child.kill();
} else callback();
}

waitForConnection (callback) {
var retryCount = 0;
let retry = (err) => {
if (err) {
if (retryCount < 10) {
retryCount++;
setTimeout(() => { tryConnect(this.scope.OSRM_PORT, retry); }, 10);
} else {
callback(new Error("Could not connect to osrm-routed after ten retries."));
}
}
else
{
callback();
}
};

tryConnect(this.scope.OSRM_PORT, retry);
}
};

class OSRMDirectLoader extends OSRMBaseLoader {
constructor (scope) {
super(scope);
}

load (inputFile, callback) {
this.inputFile = inputFile;
this.shutdown(() => {
this.launch(callback);
});
}

osrmUp (callback) {
if (this.osrmIsRunning()) return callback(new Error("osrm-routed already running!"));

this.child = this.scope.runBin('osrm-routed', util.format("%s -p %d", this.inputFile, this.scope.OSRM_PORT), this.scope.environment, (err) => {
if (err) {
throw new Error(util.format('osrm-routed %s: %s', errorReason(err), err.cmd));
}
});
callback();
}
};

class OSRMDatastoreLoader extends OSRMBaseLoader {
constructor (scope) {
super(scope);
}

load (inputFile, callback) {
this.inputFile = inputFile;

this.loadData((err) => {
if (err) return callback(err);
if (!this.osrmIsRunning()) this.launch(callback);
else {
this.scope.setupOutputLog(this.child, fs.createWriteStream(this.scope.scenarioLogFile, {'flags': 'a'}));
callback();
}
});
}

loadData (callback) {
this.scope.runBin('osrm-datastore', this.inputFile, this.scope.environment, (err) => {
if (err) return callback(new Error('*** osrm-datastore exited with ' + err.code + ': ' + err));
callback();
});
}

osrmUp (callback) {
if (this.osrmIsRunning()) return callback();

this.child = this.scope.runBin('osrm-routed', util.format('--shared-memory=1 -p %d', this.scope.OSRM_PORT), this.scope.environment, (err) => {
if (err) {
throw new Error(util.format('osrm-routed %s: %s', errorReason(err), err.cmd));
}
});

// we call the callback here, becuase we don't want to wait for the child process to finish
callback();
}
};

class OSRMLoader {
constructor (scope) {
this.scope = scope;
this.sharedLoader = new OSRMDatastoreLoader(this.scope);
this.directLoader = new OSRMDirectLoader(this.scope);
this.method = scope.DEFAULT_LOAD_METHOD;
}

load (inputFile, callback) {
if (this.method === 'datastore') {
this.directLoader.shutdown((err) => {
if (err) return callback(err);
this.loader = this.sharedLoader;
this.sharedLoader.load(inputFile, callback);
});
} else if (this.method === 'directly') {
this.sharedLoader.shutdown((err) => {
if (err) return callback(err);
this.loader = this.directLoader;
this.directLoader.load(inputFile, callback);
});
} else {
callback(new Error('*** Unknown load method ' + method));
}
}

setLoadMethod (method) {
this.method = method;
}

shutdown (callback) {
if (!this.loader) return callback();

this.loader.shutdown(callback);
}

up () {
return this.loader ? this.loader.osrmIsRunning() : false;
}
};

module.exports = OSRMLoader;
54 changes: 54 additions & 0 deletions features/lib/table_diff.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict';

var util = require('util');
var path = require('path');
var fs = require('fs');
var chalk = require('chalk');

var unescapeStr = (str) => str.replace(/\\\|/g, '\|').replace(/\\\\/g, '\\');

module.exports = function (expected, actual) {
let headers = expected.raw()[0];
let expected_keys = expected.hashes();
let diff = [];
let hasErrors = false;

var good = 0, bad = 0;

expected_keys.forEach((row, i) => {
var rowError = false;

for (var j in row) {
if (unescapeStr(row[j]) != actual[i][j]) {
rowError = true;
hasErrors = true;
break;
}
}

if (rowError) {
bad++;
diff.push(Object.assign({}, row, {c_status: 'undefined'}));
diff.push(Object.assign({}, actual[i], {c_status: 'comment'}));
} else {
good++;
diff.push(row);
}
});

if (!hasErrors) return null;

var s = ['Tables were not identical:'];
s.push(headers.map(key => ' ' + key).join(' | '));
diff.forEach((row) => {
var rowString = '| ';
headers.forEach((header) => {
if (!row.c_status) rowString += chalk.green(' ' + row[header] + ' | ');
else if (row.c_status === 'undefined') rowString += chalk.yellow('(-) ' + row[header] + ' | ');
else rowString += chalk.red('(+) ' + row[header] + ' | ');
});
s.push(rowString);
});

return s.join('\n') + '\nTODO this is a temp workaround waiting for https://github.com/cucumber/cucumber-js/issues/534';
};
13 changes: 13 additions & 0 deletions features/lib/try_connect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict';

const net = require('net');
const Timeout = require('node-timeout');

module.exports = function tryConnect(port, callback) {
net.connect({ port: port, host: '127.0.0.1' })
.on('connect', () => { callback(); })
.on('error', () => {
callback(new Error('Could not connect.'));
});
}

17 changes: 17 additions & 0 deletions features/lib/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

const util = require('util');

module.exports = {

ensureDecimal: (i) => {
if (parseInt(i) === i) return i.toFixed(1);
else return i;
},

errorReason: (err) => {
return err.signal ?
util.format('killed by signal %s', err.signal) :
util.format('exited with code %d', err.code);
}
};
Loading

0 comments on commit 7d124ce

Please sign in to comment.