Rewrite cucumber test suite in JS

This commit is contained in:
Lauren Budorick
2016-03-04 12:11:05 -08:00
parent ea027a7cc1
commit c8bb50497b
70 changed files with 2889 additions and 2368 deletions
+160
View File
@@ -0,0 +1,160 @@
'use strict';
var builder = require('xmlbuilder');
class DB {
constructor () {
this.nodes = new Array();
this.ways = new Array();
this.relations = new Array();
}
addNode (node) {
this.nodes.push(node);
}
addWay (way) {
this.ways.push(way);
}
addRelation (relation) {
this.relations.push(relation);
}
clear () {
this.nodes = [];
this.ways = [];
this.relations = [];
}
toXML (callback) {
var xml = builder.create('osm', {'encoding':'UTF-8'});
xml.att('generator', 'osrm-test')
.att('version', '0.6');
this.nodes.forEach((n) => {
var node = xml.ele('node', {
id: n.id,
version: 1,
uid: n.OSM_UID,
user: n.OSM_USER,
timestamp: n.OSM_TIMESTAMP,
lon: n.lon,
lat: n.lat
});
for (var k in n.tags) {
node.ele('tag')
.att('k', k)
.att('v', n.tags[k]);
}
});
this.ways.forEach((w) => {
var way = xml.ele('way', {
id: w.id,
version: 1,
uid: w.OSM_UID,
user: w.OSM_USER,
timestamp: w.OSM_TIMESTAMP
});
w.nodes.forEach((k) => {
way.ele('nd')
.att('ref', k.id);
});
for (var k in w.tags) {
way.ele('tag')
.att('k', k)
.att('v', w.tags[k]);
}
});
this.relations.forEach((r) => {
var relation = xml.ele('relation', {
id: r.id,
user: r.OSM_USER,
timestamp: r.OSM_TIMESTAMP,
uid: r.OSM_UID
});
r.members.forEach((m) => {
relation.ele('member', {
type: m.type,
ref: m.id,
role: m.role
});
});
for (var k in r.tags) {
relation.ele('tag')
.att('k', k)
.att('v', r.tags[k]);
}
});
callback(xml.end({ pretty: true, indent: ' ' }));
}
}
class Node {
constructor (id, OSM_USER, OSM_TIMESTAMP, OSM_UID, lon, lat, tags) {
this.id = id;
this.OSM_USER = OSM_USER;
this.OSM_TIMESTAMP = OSM_TIMESTAMP;
this.OSM_UID = OSM_UID;
this.lon = lon;
this.lat = lat;
this.tags = tags;
}
addTag (k, v) {
this.tags[k] = v;
}
}
class Way {
constructor (id, OSM_USER, OSM_TIMESTAMP, OSM_UID) {
this.id = id;
this.OSM_USER = OSM_USER;
this.OSM_TIMESTAMP = OSM_TIMESTAMP;
this.OSM_UID = OSM_UID;
this.tags = {};
this.nodes = [];
}
addNode (node) {
this.nodes.push(node);
}
setTags (tags) {
this.tags = tags;
}
}
class Relation {
constructor (id, OSM_USER, OSM_TIMESTAMP, OSM_UID) {
this.id = id;
this.OSM_USER = OSM_USER;
this.OSM_TIMESTAMP = OSM_TIMESTAMP;
this.OSM_UID = OSM_UID;
this.members = [];
this.tags = {};
}
addMember (memberType, id, role) {
this.members.push({type: memberType, id: id, role: role});
}
addTag (k, v) {
this.tags[k] = v;
}
}
module.exports = {
DB: DB,
Node: Node,
Way: Way,
Relation: Relation
};
+115
View File
@@ -0,0 +1,115 @@
var fs = require('fs');
var path = require('path');
var util = require('util');
var d3 = require('d3-queue');
var OSM = require('./build_osm');
var classes = require('./data_classes');
module.exports = function () {
this.initializeOptions = (callback) => {
this.profile = this.profile || this.DEFAULT_SPEEDPROFILE;
this.OSMDB = this.OSMDB || new OSM.DB();
this.nameNodeHash = this.nameNodeHash || {};
this.locationHash = this.locationHash || {};
this.nameWayHash = this.nameWayHash || {};
this.osmData = new classes.osmData(this);
this.STRESS_TIMEOUT = 300;
this.OSRMLoader = this._OSRMLoader();
this.PREPROCESS_LOG_FILE = path.resolve(this.TEST_FOLDER, 'preprocessing.log');
this.LOG_FILE = path.resolve(this.TEST_FOLDER, 'fail.log');
this.HOST = 'http://127.0.0.1:' + this.OSRM_PORT;
this.DESTINATION_REACHED = 15; // OSRM instruction code
this.shortcutsHash = this.shortcutsHash || {};
var hashLuaLib = (cb) => {
fs.readdir(path.normalize(this.PROFILES_PATH + '/lib/'), (err, files) => {
if (err) cb(err);
var luaFiles = files.filter(f => !!f.match(/\.lua$/)).map(f => path.normalize(this.PROFILES_PATH + '/lib/' + f));
this.hashOfFiles(luaFiles, hash => {
this.luaLibHash = hash;
cb();
});
});
};
var hashProfile = (cb) => {
this.hashProfile((hash) => {
this.profileHash = hash;
cb();
});
};
var hashExtract = (cb) => {
this.hashOfFiles(util.format('%s/osrm-extract%s', this.BIN_PATH, this.EXE), (hash) => {
this.binExtractHash = hash;
cb();
});
};
var hashContract = (cb) => {
this.hashOfFiles(util.format('%s/osrm-contract%s', this.BIN_PATH, this.EXE), (hash) => {
this.binContractHash = hash;
this.fingerprintContract = this.hashString(this.binContractHash);
cb();
});
};
var hashRouted = (cb) => {
this.hashOfFiles(util.format('%s/osrm-routed%s', this.BIN_PATH, this.EXE), (hash) => {
this.binRoutedHash = hash;
this.fingerprintRoute = this.hashString(this.binRoutedHash);
cb();
});
};
d3.queue()
.defer(hashLuaLib)
.defer(hashProfile)
.defer(hashExtract)
.defer(hashContract)
.defer(hashRouted)
.awaitAll(() => {
this.fingerprintExtract = this.hashString([this.profileHash, this.luaLibHash, this.binExtractHash].join('-'));
this.AfterConfiguration(() => {
callback();
});
});
};
this.setProfileBasedHashes = () => {
this.fingerprintExtract = this.hashString([this.profileHash, this.luaLibHash, this.binExtractHash].join('-'));
this.fingerprintContract = this.hashString(this.binContractHash);
};
this.setProfile = (profile, cb) => {
var lastProfile = this.profile;
if (profile !== lastProfile) {
this.profile = profile;
this.hashProfile((hash) => {
this.profileHash = hash;
this.setProfileBasedHashes();
cb();
});
} else cb();
};
this.setExtractArgs = (args) => {
this.extractArgs = args;
};
this.setContractArgs = (args) => {
this.contractArgs = args;
};
};
-20
View File
@@ -1,20 +0,0 @@
def profile
@profile ||= reset_profile
end
def reset_profile
@profile = nil
set_profile DEFAULT_SPEEDPROFILE
end
def set_profile profile
@profile = profile
end
def set_extract_args args
@extract_args = args
end
def set_contract_args args
@contract_args = args
end
+339
View File
@@ -0,0 +1,339 @@
var fs = require('fs');
var path = require('path');
var util = require('util');
var exec = require('child_process').exec;
var d3 = require('d3-queue');
var OSM = require('./build_osm');
var classes = require('./data_classes');
module.exports = function () {
this.setGridSize = (meters) => {
// the constant is calculated (with BigDecimal as: 1.0/(DEG_TO_RAD*EARTH_RADIUS_IN_METERS
// see ApproximateDistance() in ExtractorStructs.h
// it's only accurate when measuring along the equator, or going exactly north-south
this.zoom = parseFloat(meters) * 0.8990679362704610899694577444566908445396483347536032203503E-5;
};
this.setOrigin = (origin) => {
this.origin = origin;
};
this.buildWaysFromTable = (table, callback) => {
// add one unconnected way for each row
var buildRow = (row, ri, cb) => {
// comments ported directly from ruby suite:
// NOTE: currently osrm crashes when processing an isolated oneway with just 2 nodes, so we use 4 edges
// this is related to the fact that a oneway dead-end street doesn't make a lot of sense
// if we stack ways on different x coordinates, routability tests get messed up, because osrm might pick a neighboring way if the one test can't be used.
// instead we place all lines as a string on the same y coordinate. this prevents using neighboring ways.
// add some nodes
var makeFakeNode = (namePrefix, offset) => {
return new OSM.Node(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP,
this.OSM_UID, this.origin[0]+(offset + this.WAY_SPACING * ri) * this.zoom,
this.origin[1], {name: util.format('%s%d', namePrefix, ri)});
};
var nodes = ['a','b','c','d','e'].map((l, i) => makeFakeNode(l, i));
nodes.forEach(node => { this.OSMDB.addNode(node); });
// ...with a way between them
var way = new OSM.Way(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID);
nodes.forEach(node => { way.addNode(node); });
// remove tags that describe expected test result, reject empty tags
var tags = {};
for (var rkey in row) {
if (!rkey.match(/^forw\b/) &&
!rkey.match(/^backw\b/) &&
!rkey.match(/^bothw\b/) &&
row[rkey].length)
tags[rkey] = row[rkey];
}
var wayTags = { highway: 'primary' },
nodeTags = {};
for (var key in tags) {
var nodeMatch = key.match(/node\/(.*)/);
if (nodeMatch) {
if (tags[key] === '(nil)') {
delete nodeTags[key];
} else {
nodeTags[nodeMatch[1]] = tags[key];
}
} else {
if (tags[key] === '(nil)') {
delete wayTags[key];
} else {
wayTags[key] = tags[key];
}
}
}
wayTags.name = util.format('w%d', ri);
way.setTags(wayTags);
this.OSMDB.addWay(way);
for (var k in nodeTags) {
nodes[2].addTag(k, nodeTags[k]);
}
cb();
};
var q = d3.queue();
table.hashes().forEach((row, ri) => {
q.defer(buildRow, row, ri);
});
q.awaitAll(callback);
};
var ensureDecimal = (i) => {
if (parseInt(i) === i) return i.toFixed(1);
else return i;
};
this.tableCoordToLonLat = (ci, ri) => {
return [this.origin[0] + ci * this.zoom, this.origin[1] - ri * this.zoom].map(ensureDecimal);
};
this.addOSMNode = (name, lon, lat, id) => {
id = id || this.makeOSMId();
var node = new OSM.Node(id, this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID, lon, lat, {name: name});
this.OSMDB.addNode(node);
this.nameNodeHash[name] = node;
};
this.addLocation = (name, lon, lat) => {
this.locationHash[name] = new classes.Location(lon, lat);
};
this.findNodeByName = (s) => {
if (s.length !== 1) throw new Error(util.format('*** invalid node name "%s", must be single characters', s));
if (!s.match(/[a-z0-9]/)) throw new Error(util.format('*** invalid node name "%s", must be alphanumeric', s));
var fromNode;
if (s.match(/[a-z]/)) {
fromNode = this.nameNodeHash[s.toString()];
} else {
fromNode = this.locationHash[s.toString()];
}
return fromNode;
};
this.findWayByName = (s) => {
return this.nameWayHash[s.toString()] || this.nameWayHash[s.toString().split('').reverse().join('')];
};
this.resetData = () => {
this.resetOSM();
};
this.makeOSMId = () => {
this.osmID = this.osmID + 1;
return this.osmID;
};
this.resetOSM = () => {
this.OSMDB.clear();
this.osmData.reset();
this.nameNodeHash = {};
this.locationHash = {};
this.nameWayHash = {};
this.osmID = 0;
};
this.writeOSM = (callback) => {
fs.exists(this.DATA_FOLDER, (exists) => {
var mkDirFn = exists ? (cb) => { cb(); } : fs.mkdir.bind(fs.mkdir, this.DATA_FOLDER);
mkDirFn((err) => {
if (err) return callback(err);
var osmPath = path.resolve(this.DATA_FOLDER, util.format('%s.osm', this.osmData.osmFile));
fs.exists(osmPath, (exists) => {
if (!exists) fs.writeFile(osmPath, this.osmData.str, callback);
else callback();
});
});
});
};
this.isExtracted = (callback) => {
fs.exists(util.format('%s.osrm', this.osmData.extractedFile), (core) => {
if (!core) return callback(false);
fs.exists(util.format('%s.osrm.names', this.osmData.extractedFile), (names) => {
if (!names) return callback(false);
fs.exists(util.format('%s.osrm.restrictions', this.osmData.extractedFile), (restrictions) => {
return callback(restrictions);
});
});
});
};
this.isContracted = (callback) => {
fs.exists(util.format('%s.osrm.hsgr', this.osmData.contractedFile), callback);
};
this.writeTimestamp = (callback) => {
fs.writeFile(util.format('%s.osrm.timestamp', this.osmData.contractedFile), this.OSM_TIMESTAMP, callback);
};
this.writeInputData = (callback) => {
this.writeOSM((err) => {
if (err) return callback(err);
this.writeTimestamp(callback);
});
};
this.extractData = (callback) => {
this.logPreprocessInfo();
this.log(util.format('== Extracting %s.osm...', this.osmData.osmFile), 'preprocess');
var cmd = util.format('%s%s/osrm-extract %s.osm %s --profile %s/%s.lua >>%s 2>&1',
this.LOAD_LIBRARIES, this.BIN_PATH, this.osmData.osmFile, this.extractArgs || '', this.PROFILES_PATH, this.profile, this.PREPROCESS_LOG_FILE);
this.log(cmd);
process.chdir(this.TEST_FOLDER);
exec(cmd, (err) => {
if (err) {
this.log(util.format('*** Exited with code %d', err.code), 'preprocess');
return callback(this.ExtractError(err.code, util.format('osrm-extract exited with code %d', err.code)));
}
var q = d3.queue();
var rename = (file, cb) => {
this.log(util.format('Renaming %s.%s to %s.%s', this.osmData.osmFile, file, this.osmData.extractedFile, file), 'preprocess');
fs.rename([this.osmData.osmFile, file].join('.'), [this.osmData.extractedFile, file].join('.'), (err) => {
if (err) return cb(this.FileError(null, 'failed to rename data file after extracting'));
cb();
});
};
var renameIfExists = (file, cb) => {
fs.stat([this.osmData.osmFile, file].join('.'), (doesNotExistErr, exists) => {
if (exists) rename(file, cb);
else cb();
});
};
['osrm','osrm.names','osrm.restrictions','osrm.ebg','osrm.enw','osrm.edges','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex'].forEach(file => {
q.defer(rename, file);
});
['osrm.edge_segment_lookup','osrm.edge_penalties'].forEach(file => {
q.defer(renameIfExists, file);
});
q.awaitAll((err) => {
this.log('Finished extracting ' + this.osmData.extractedFile, 'preprocess');
process.chdir('../');
callback(err);
});
});
};
this.contractData = (callback) => {
this.logPreprocessInfo();
this.log(util.format('== Contracting %s.osm...', this.osmData.extractedFile), 'preprocess');
var cmd = util.format('%s%s/osrm-contract %s %s.osrm >>%s 2>&1',
this.LOAD_LIBRARIES, this.BIN_PATH, this.contractArgs || '', this.osmData.extractedFile, this.PREPROCESS_LOG_FILE);
this.log(cmd);
process.chdir(this.TEST_FOLDER);
exec(cmd, (err) => {
if (err) {
this.log(util.format('*** Exited with code %d', err.code), 'preprocess');
return callback(this.ContractError(err.code, util.format('osrm-contract exited with code %d', err.code)));
}
var rename = (file, cb) => {
this.log(util.format('Renaming %s.%s to %s.%s', this.osmData.extractedFile, file, this.osmData.contractedFile, file), 'preprocess');
fs.rename([this.osmData.extractedFile, file].join('.'), [this.osmData.contractedFile, file].join('.'), (err) => {
if (err) return cb(this.FileError(null, 'failed to rename data file after contracting.'));
cb();
});
};
var copy = (file, cb) => {
this.log(util.format('Copying %s.%s to %s.%s', this.osmData.extractedFile, file, this.osmData.contractedFile, file), 'preprocess');
fs.createReadStream([this.osmData.extractedFile, file].join('.'))
.pipe(fs.createWriteStream([this.osmData.contractedFile, file].join('.'))
.on('finish', cb)
)
.on('error', () => {
return cb(this.FileError(null, 'failed to copy data after contracting.'));
});
};
var q = d3.queue();
['osrm.hsgr','osrm.fileIndex','osrm.geometry','osrm.nodes','osrm.ramIndex','osrm.core','osrm.edges'].forEach((file) => {
q.defer(rename, file);
});
['osrm.names','osrm.restrictions','osrm'].forEach((file) => {
q.defer(copy, file);
});
q.awaitAll((err) => {
this.log('Finished contracting ' + this.osmData.contractedFile, 'preprocess');
process.chdir('../');
callback(err);
});
});
};
var noop = (cb) => cb();
this.reprocess = (callback) => {
this.writeAndExtract((e) => {
if (e) return callback(e);
this.isContracted((isContracted) => {
var contractFn = isContracted ? noop : this.contractData;
if (isContracted) this.log('Already contracted ' + this.osmData.contractedFile, 'preprocess');
contractFn((e) => {
if (e) return callback(e);
this.logPreprocessDone();
callback();
});
});
});
};
this.writeAndExtract = (callback) => {
this.osmData.populate(() => {
this.writeInputData((e) => {
if (e) return callback(e);
this.isExtracted((isExtracted) => {
var extractFn = isExtracted ? noop : this.extractData;
if (isExtracted) this.log('Already extracted ' + this.osmData.extractedFile, 'preprocess');
extractFn((e) => {
callback(e);
});
});
});
});
};
this.reprocessAndLoadData = (callback) => {
this.reprocess(() => {
this.OSRMLoader.load(util.format('%s.osrm', this.osmData.contractedFile), callback);
});
};
this.processRowsAndDiff = (table, fn, callback) => {
var q = d3.queue(1);
table.hashes().forEach((row, i) => q.defer(fn, row, i));
q.awaitAll((err, actual) => {
if (err) return callback(err);
this.diffTables(table, actual, {}, callback);
});
};
};
-321
View File
@@ -1,321 +0,0 @@
require 'OSM/objects' #osmlib gem
require 'OSM/Database'
require 'builder'
require 'fileutils'
class Location
attr_accessor :lon,:lat
def initialize lon,lat
@lat = lat
@lon = lon
end
end
def set_input_format format
raise '*** Input format must be eiter "osm" or "pbf"' unless ['pbf','osm'].include? format.to_s
@input_format = format.to_s
end
def input_format
@input_format || DEFAULT_INPUT_FORMAT
end
def sanitized_scenario_title
@sanitized_scenario_title ||= @scenario_title.to_s.gsub /[^0-9A-Za-z.\-]/, '_'
end
def set_grid_size meters
#the constant is calculated (with BigDecimal as: 1.0/(DEG_TO_RAD*EARTH_RADIUS_IN_METERS
#see ApproximateDistance() in ExtractorStructs.h
#it's only accurate when measuring along the equator, or going exactly north-south
@zoom = meters.to_f*0.8990679362704610899694577444566908445396483347536032203503E-5
end
def set_origin origin
@origin = origin
end
def build_ways_from_table table
#add one unconnected way for each row
table.hashes.each_with_index do |row,ri|
#NOTE:
#currently osrm crashes when processing an isolated oneway with just 2 nodes, so we use 4 edges
#this is relatated to the fact that a oneway dead-end street doesn't make a lot of sense
#if we stack ways on different x coordinates, routability tests get messed up, because osrm might pick a neighboring way if the one test can't be used.
#instead we place all lines as a string on the same y coordinate. this prevents using neightboring ways.
#a few nodes...
node1 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, @origin[0]+(0+WAY_SPACING*ri)*@zoom, @origin[1]
node2 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, @origin[0]+(1+WAY_SPACING*ri)*@zoom, @origin[1]
node3 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, @origin[0]+(2+WAY_SPACING*ri)*@zoom, @origin[1]
node4 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, @origin[0]+(3+WAY_SPACING*ri)*@zoom, @origin[1]
node5 = OSM::Node.new make_osm_id, OSM_USER, OSM_TIMESTAMP, @origin[0]+(4+WAY_SPACING*ri)*@zoom, @origin[1]
node1.uid = OSM_UID
node2.uid = OSM_UID
node3.uid = OSM_UID
node4.uid = OSM_UID
node5.uid = OSM_UID
node1 << { :name => "a#{ri}" }
node2 << { :name => "b#{ri}" }
node3 << { :name => "c#{ri}" }
node4 << { :name => "d#{ri}" }
node5 << { :name => "e#{ri}" }
osm_db << node1
osm_db << node2
osm_db << node3
osm_db << node4
osm_db << node5
#...with a way between them
way = OSM::Way.new make_osm_id, OSM_USER, OSM_TIMESTAMP
way.uid = OSM_UID
way << node1
way << node2
way << node3
way << node4
way << node5
tags = row.dup
# remove tags that describe expected test result
tags.reject! do |k,v|
k =~ /^forw\b/ ||
k =~ /^backw\b/ ||
k =~ /^bothw\b/
end
##remove empty tags
tags.reject! { |k,v| v=='' }
# sort tag keys in the form of 'node/....'
way_tags = { 'highway' => 'primary' }
node_tags = {}
tags.each_pair do |k,v|
if k =~ /node\/(.*)/
if v=='(nil)'
node_tags.delete k
else
node_tags[$1] = v
end
else
if v=='(nil)'
way_tags.delete k
else
way_tags[k] = v
end
end
end
way_tags['name'] = "w#{ri}"
way << way_tags
node3 << node_tags
osm_db << way
end
end
def table_coord_to_lonlat ci,ri
[@origin[0]+ci*@zoom, @origin[1]-ri*@zoom]
end
def add_osm_node name,lon,lat,id
id = make_osm_id if id == nil
node = OSM::Node.new id, OSM_USER, OSM_TIMESTAMP, lon, lat
node << { :name => name }
node.uid = OSM_UID
osm_db << node
name_node_hash[name] = node
end
def add_location name,lon,lat
location_hash[name] = Location.new(lon,lat)
end
def find_node_by_name s
raise "***invalid node name '#{s}', must be single characters" unless s.size == 1
raise "*** invalid node name '#{s}', must be alphanumeric" unless s.match /[a-z0-9]/
if s.match /[a-z]/
from_node = name_node_hash[ s.to_s ]
else
from_node = location_hash[ s.to_s ]
end
end
def find_way_by_name s
name_way_hash[s.to_s] || name_way_hash[s.to_s.reverse]
end
def reset_data
Dir.chdir TEST_FOLDER do
#clear_log
#clear_data_files
end
reset_profile
reset_osm
@fingerprint_osm = nil
@fingerprint_extract = nil
@fingerprint_prepare = nil
@fingerprint_route = nil
end
def make_osm_id
@osm_id = @osm_id+1
end
def reset_osm
osm_db.clear
name_node_hash.clear
location_hash.clear
name_way_hash.clear
@osm_str = nil
@osm_hash = nil
@osm_id = 0
end
def clear_data_files
File.delete *Dir.glob("#{DATA_FOLDER}/test.*")
end
def clear_log
File.delete *Dir.glob("*.log")
end
def osm_db
@osm_db ||= OSM::Database.new
end
def name_node_hash
@name_node_hash ||= {}
end
def location_hash
@location_hash ||= {}
end
def name_way_hash
@name_way_hash ||= {}
end
def osm_str
return @osm_str if @osm_str
@osm_str = ''
doc = Builder::XmlMarkup.new :indent => 2, :target => @osm_str
doc.instruct!
osm_db.to_xml doc, OSM_GENERATOR
@osm_str
end
def osm_file
@osm_file ||= "#{DATA_FOLDER}/#{fingerprint_osm}"
end
def extracted_file
@extracted_file ||= "#{osm_file}_#{fingerprint_extract}"
end
def contracted_file
@contracted_file ||= "#{osm_file}_#{fingerprint_extract}_#{fingerprint_prepare}"
end
def write_osm
Dir.mkdir DATA_FOLDER unless File.exist? DATA_FOLDER
unless File.exist?("#{osm_file}.osm")
File.open( "#{osm_file}.osm", 'w') {|f| f.write(osm_str) }
end
end
def extracted?
Dir.chdir TEST_FOLDER do
File.exist?("#{extracted_file}.osrm") &&
File.exist?("#{extracted_file}.osrm.names") &&
File.exist?("#{extracted_file}.osrm.restrictions")
end
end
def contracted?
Dir.chdir TEST_FOLDER do
File.exist?("#{contracted_file}.osrm.hsgr")
end
end
def write_timestamp
File.open( "#{contracted_file}.osrm.timestamp", 'w') {|f| f.write(OSM_TIMESTAMP) }
end
def write_input_data
Dir.chdir TEST_FOLDER do
write_osm
write_timestamp
end
end
def extract_data
Dir.chdir TEST_FOLDER do
log_preprocess_info
log "== Extracting #{osm_file}.osm...", :preprocess
log "#{LOAD_LIBRARIES}#{BIN_PATH}/osrm-extract #{osm_file}.osm #{@extract_args} --profile #{PROFILES_PATH}/#{@profile}.lua >>#{PREPROCESS_LOG_FILE} 2>&1"
unless system "#{LOAD_LIBRARIES}#{BIN_PATH}/osrm-extract #{osm_file}.osm #{@extract_args} --profile #{PROFILES_PATH}/#{@profile}.lua >>#{PREPROCESS_LOG_FILE} 2>&1"
log "*** Exited with code #{$?.exitstatus}.", :preprocess
raise ExtractError.new $?.exitstatus, "osrm-extract exited with code #{$?.exitstatus}."
end
begin
["osrm","osrm.names","osrm.restrictions","osrm.ebg","osrm.enw","osrm.edges","osrm.fileIndex","osrm.geometry","osrm.nodes","osrm.ramIndex"].each do |file|
log "Renaming #{osm_file}.#{file} to #{extracted_file}.#{file}", :preprocess
File.rename "#{osm_file}.#{file}", "#{extracted_file}.#{file}"
end
rescue Exception => e
raise FileError.new nil, "failed to rename data file after extracting."
end
begin
["osrm.edge_segment_lookup","osrm.edge_penalties"].each do |file|
if File.exists?("#{osm_file}.#{file}")
log "Renaming #{osm_file}.#{file} to #{extracted_file}.#{file}", :preprocess
File.rename "#{osm_file}.#{file}", "#{extracted_file}.#{file}"
end
end
rescue Exception => e
raise FileError.new nil, "failed to rename data file after extracting."
end
end
end
def prepare_data
Dir.chdir TEST_FOLDER do
log_preprocess_info
log "== Preparing #{extracted_file}.osm...", :preprocess
log "#{LOAD_LIBRARIES}#{BIN_PATH}/osrm-contract #{@contract_args} #{extracted_file}.osrm >>#{PREPROCESS_LOG_FILE} 2>&1"
unless system "#{LOAD_LIBRARIES}#{BIN_PATH}/osrm-contract #{@contract_args} #{extracted_file}.osrm >>#{PREPROCESS_LOG_FILE} 2>&1"
log "*** Exited with code #{$?.exitstatus}.", :preprocess
raise PrepareError.new $?.exitstatus, "osrm-contract exited with code #{$?.exitstatus}."
end
begin
["osrm.hsgr","osrm.fileIndex","osrm.geometry","osrm.nodes","osrm.ramIndex","osrm.core","osrm.edges"].each do |file|
log "Renaming #{extracted_file}.#{file} to #{contracted_file}.#{file}", :preprocess
File.rename "#{extracted_file}.#{file}", "#{contracted_file}.#{file}"
end
rescue Exception => e
raise FileError.new nil, "failed to rename data file after preparing."
end
begin
["osrm.names","osrm.restrictions","osrm"].each do |file|
log "Copying #{extracted_file}.#{file} to #{contracted_file}.#{file}", :preprocess
FileUtils.cp "#{extracted_file}.#{file}", "#{contracted_file}.#{file}"
end
rescue Exception => e
raise FileError.new nil, "failed to copy data file after preparing."
end
log '', :preprocess
end
end
def reprocess
write_input_data
extract_data unless extracted?
prepare_data unless contracted?
log_preprocess_done
end
+85
View File
@@ -0,0 +1,85 @@
'use strict';
var util = require('util');
var path = require('path');
module.exports = {
Location: class {
constructor (lon, lat) {
this.lon = lon;
this.lat = lat;
}
},
osmData: class {
constructor (scope) {
this.scope = scope;
this.str = null;
this.hash = null;
this.fingerprintOSM = null;
this.osmFile = null;
this.extractedFile = null;
this.contractedFile = null;
}
populate (callback) {
this.scope.OSMDB.toXML((str) => {
this.str = str;
this.hash = this.scope.hashString(str);
this.fingerprintOSM = this.scope.hashString(this.hash);
this.osmFile = path.resolve(this.scope.DATA_FOLDER, this.fingerprintOSM);
this.extractedFile = path.resolve([this.osmFile, this.scope.fingerprintExtract].join('_'));
this.contractedFile = path.resolve([this.osmFile, this.scope.fingerprintExtract, this.scope.fingerprintContract].join('_'));
callback();
});
}
reset () {
this.str = null;
this.hash = null;
this.fingerprintOSM = null;
this.osmFile = null;
this.extractedFile = null;
this.contractedFile = null;
}
},
FuzzyMatch: class {
match (got, want) {
var matchPercent = want.match(/(.*)\s+~(.+)%$/),
matchAbs = want.match(/(.*)\s+\+\-(.+)$/),
matchRe = want.match(/^\/(.*)\/$/);
if (got === want) {
return true;
} else if (matchPercent) { // percentage range: 100 ~ 5%
var target = parseFloat(matchPercent[1]),
percentage = parseFloat(matchPercent[2]);
if (target === 0) {
return true;
} else {
var ratio = Math.abs(1 - parseFloat(got) / target);
return 100 * ratio < percentage;
}
} else if (matchAbs) { // absolute range: 100 +-5
var margin = parseFloat(matchAbs[2]),
fromR = parseFloat(matchAbs[1]) - margin,
toR = parseFloat(matchAbs[1]) + margin;
return parseFloat(got) >= fromR && parseFloat(got) <= toR;
} else if (matchRe) { // regex: /a,b,.*/
return got.match(matchRe[1]);
} else {
return false;
}
}
matchLocation (got, want) {
return this.match(got[0], util.format('%d ~0.0025%', want.lat)) &&
this.match(got[1], util.format('%d ~0.0025%', want.lon));
}
}
};
+125
View File
@@ -0,0 +1,125 @@
var path = require('path');
var util = require('util');
var fs = require('fs');
var exec = require('child_process').exec;
var d3 = require('d3-queue');
module.exports = function () {
this.initializeEnv = (callback) => {
this.DEFAULT_PORT = 5000;
this.DEFAULT_TIMEOUT = 2000;
this.setDefaultTimeout(this.DEFAULT_TIMEOUT);
this.ROOT_FOLDER = process.cwd();
this.OSM_USER = 'osrm';
this.OSM_GENERATOR = 'osrm-test';
this.OSM_UID = 1;
this.TEST_FOLDER = path.resolve(this.ROOT_FOLDER, 'test');
this.DATA_FOLDER = path.resolve(this.TEST_FOLDER, 'cache');
this.OSM_TIMESTAMP = '2000-01-01T00:00:00Z';
this.DEFAULT_SPEEDPROFILE = 'bicycle';
this.WAY_SPACING = 100;
this.DEFAULT_GRID_SIZE = 100; // meters
this.PROFILES_PATH = path.resolve(this.ROOT_FOLDER, 'profiles');
this.FIXTURES_PATH = path.resolve(this.ROOT_FOLDER, 'unit_tests/fixtures');
this.BIN_PATH = path.resolve(this.ROOT_FOLDER, 'build');
this.DEFAULT_INPUT_FORMAT = 'osm';
this.DEFAULT_ORIGIN = [1,1];
this.LAUNCH_TIMEOUT = 1000;
this.SHUTDOWN_TIMEOUT = 10000;
this.DEFAULT_LOAD_METHOD = 'datastore';
this.OSRM_ROUTED_LOG_FILE = path.resolve(this.TEST_FOLDER, 'osrm-routed.log');
this.ERROR_LOG_FILE = path.resolve(this.TEST_FOLDER, 'error.log');
// OS X shim to ensure shared libraries from custom locations can be loaded
// This is needed in OS X >= 10.11 because DYLD_LIBRARY_PATH is blocked
// https://forums.developer.apple.com/thread/9233
this.LOAD_LIBRARIES = process.env.OSRM_SHARED_LIBRARY_PATH ? util.format('DYLD_LIBRARY_PATH=%s ', process.env.OSRM_SHARED_LIBRARY_PATH) : '';
// TODO make sure this works on win
if (process.platform.match(/indows.*/)) {
this.TERMSIGNAL = 9;
this.EXE = '.exe';
this.QQ = '"';
} else {
this.TERMSIGNAL = 'SIGTERM';
this.EXE = '';
this.QQ = '';
}
// eslint-disable-next-line no-console
console.info(util.format('Node Version', process.version));
if (parseInt(process.version.match(/v(\d)/)[1]) < 4) throw new Error('*** PLease upgrade to Node 4.+ to run OSRM cucumber tests');
if (process.env.OSRM_PORT) {
this.OSRM_PORT = parseInt(process.env.OSRM_PORT);
// eslint-disable-next-line no-console
console.info(util.format('Port set to %d', this.OSRM_PORT));
} else {
this.OSRM_PORT = this.DEFAULT_PORT;
// eslint-disable-next-line no-console
console.info(util.format('Using default port %d', this.OSRM_PORT));
}
if (process.env.OSRM_TIMEOUT) {
this.OSRM_TIMEOUT = parseInt(process.env.OSRM_TIMEOUT);
// eslint-disable-next-line no-console
console.info(util.format('Timeout set to %d', this.OSRM_TIMEOUT));
} else {
this.OSRM_TIMEOUT = this.DEFAULT_TIMEOUT;
// eslint-disable-next-line no-console
console.info(util.format('Using default timeout %d', this.OSRM_TIMEOUT));
}
fs.exists(this.TEST_FOLDER, (exists) => {
if (!exists) throw new Error(util.format('*** Test folder %s doesn\'t exist.', this.TEST_FOLDER));
callback();
});
};
this.verifyOSRMIsNotRunning = () => {
if (this.OSRMLoader.up()) {
throw new Error('*** osrm-routed is already running.');
}
};
this.verifyExistenceOfBinaries = (callback) => {
var verify = (bin, cb) => {
var binPath = path.resolve(util.format('%s/%s%s', this.BIN_PATH, bin, this.EXE));
fs.exists(binPath, (exists) => {
if (!exists) throw new Error(util.format('%s is missing. Build failed?', binPath));
var helpPath = util.format('%s%s --help > /dev/null 2>&1', this.LOAD_LIBRARIES, binPath);
exec(helpPath, (err) => {
if (err) {
this.log(util.format('*** Exited with code %d', err.code), 'preprocess');
throw new Error(util.format('*** %s exited with code %d', helpPath, err.code));
}
cb();
});
});
};
var q = d3.queue();
['osrm-extract', 'osrm-contract', 'osrm-routed'].forEach(bin => { q.defer(verify, bin); });
q.awaitAll(() => {
callback();
});
};
this.AfterConfiguration = (callback) => {
this.clearLogFiles(() => {
this.verifyOSRMIsNotRunning();
this.verifyExistenceOfBinaries(() => {
callback();
});
});
};
process.on('exit', () => {
if (this.OSRMLoader.loader) this.OSRMLoader.shutdown(() => {});
});
process.on('SIGINT', () => {
process.exit(2);
// TODO need to handle for windows??
});
};
-111
View File
@@ -1,111 +0,0 @@
require 'rspec/expectations'
DEFAULT_PORT = 5000
DEFAULT_TIMEOUT = 2
ROOT_FOLDER = Dir.pwd
OSM_USER = 'osrm'
OSM_GENERATOR = 'osrm-test'
OSM_UID = 1
TEST_FOLDER = File.join ROOT_FOLDER, 'test'
DATA_FOLDER = 'cache'
OSM_TIMESTAMP = '2000-01-01T00:00:00Z'
DEFAULT_SPEEDPROFILE = 'bicycle'
WAY_SPACING = 100
DEFAULT_GRID_SIZE = 100 #meters
PROFILES_PATH = File.join ROOT_FOLDER, 'profiles'
FIXTURES_PATH = File.join ROOT_FOLDER, 'unit_tests/fixtures'
BIN_PATH = File.join ROOT_FOLDER, 'build'
DEFAULT_INPUT_FORMAT = 'osm'
DEFAULT_ORIGIN = [1,1]
LAUNCH_TIMEOUT = 1
SHUTDOWN_TIMEOUT = 10
DEFAULT_LOAD_METHOD = 'datastore'
OSRM_ROUTED_LOG_FILE = 'osrm-routed.log'
# OS X shim to ensure shared libraries from custom locations can be loaded
# This is needed in OS X >= 10.11 because DYLD_LIBRARY_PATH is blocked
# https://forums.developer.apple.com/thread/9233
if ENV['OSRM_SHARED_LIBRARY_PATH']
LOAD_LIBRARIES="DYLD_LIBRARY_PATH=#{ENV['OSRM_SHARED_LIBRARY_PATH']} "
else
LOAD_LIBRARIES=""
end
if ENV['OS']=~/Windows.*/ then
TERMSIGNAL=9
else
TERMSIGNAL='TERM'
end
def log_time_and_run cmd
log_time cmd
`#{cmd}`
end
def log_time cmd
puts "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S:%L')}] #{cmd}"
end
puts "Ruby version #{RUBY_VERSION}"
unless RUBY_VERSION.to_f >= 1.9
raise "*** Please upgrade to Ruby 1.9.x to run the OSRM cucumber tests"
end
if ENV["OSRM_PORT"]
OSRM_PORT = ENV["OSRM_PORT"].to_i
puts "Port set to #{OSRM_PORT}"
else
OSRM_PORT = DEFAULT_PORT
puts "Using default port #{OSRM_PORT}"
end
if ENV["OSRM_TIMEOUT"]
OSRM_TIMEOUT = ENV["OSRM_TIMEOUT"].to_i
puts "Timeout set to #{OSRM_TIMEOUT}"
else
OSRM_TIMEOUT = DEFAULT_TIMEOUT
puts "Using default timeout #{OSRM_TIMEOUT}"
end
unless File.exists? TEST_FOLDER
raise "*** Test folder #{TEST_FOLDER} doesn't exist."
end
def verify_osrm_is_not_running
if OSRMLoader::OSRMBaseLoader.new.osrm_up?
raise "*** osrm-routed is already running."
end
end
def verify_existance_of_binaries
["osrm-extract", "osrm-contract", "osrm-routed"].each do |bin|
unless File.exists? "#{BIN_PATH}/#{bin}#{EXE}"
raise "*** #{BIN_PATH}/#{bin}#{EXE} is missing. Build failed?"
end
unless system "#{LOAD_LIBRARIES}#{BIN_PATH}/#{bin}#{EXE} --help > /dev/null 2>&1"
log "*** Exited with code #{$?.exitstatus}.", :preprocess
raise "*** #{LOAD_LIBRARIES}#{BIN_PATH}/#{bin}#{EXE} --help exited with code #{$?.exitstatus}."
end
end
end
if ENV['OS']=~/Windows.*/ then
EXE='.exe'
QQ='"'
else
EXE=''
QQ=''
end
AfterConfiguration do |config|
clear_log_files
verify_osrm_is_not_running
verify_existance_of_binaries
end
at_exit do
OSRMLoader::OSRMBaseLoader.new.shutdown
end
+130
View File
@@ -0,0 +1,130 @@
'use strict';
var util = require('util');
var fs = require('fs');
var OSRMError = class extends Error {
constructor (process, code, msg, log, lines) {
super(msg);
this.process = process;
this.code = code;
this.msg = msg;
this.lines = lines;
this.log = log;
}
extract (callback) {
this.logTail(this.log, this.lines, callback);
}
toString (callback) {
this.extract((tail) => {
callback(util.format('*** %s\nLast %s from %s:\n%s\n', this.msg, this.lines, this.log, tail));
});
}
logTail (path, n, callback) {
var expanded = path.resolve(this.TEST_FOLDER, path);
fs.exists(expanded, (exists) => {
if (exists) {
fs.readFile(expanded, (err, data) => {
var lines = data.trim().split('\n');
callback(lines
.slice(lines.length - n)
.map(line => util.format(' %s', line))
.join('\n'));
});
} else {
callback(util.format('File %s does not exist!', expanded));
}
});
}
};
var unescapeStr = (str) => str.replace(/\\\|/g, '\|').replace(/\\\\/g, '\\');
module.exports = {
OSRMError: OSRMError,
FileError: class extends OSRMError {
constructor (logFile, code, msg) {
super ('fileutil', code, msg, logFile, 5);
}
},
LaunchError: class extends OSRMError {
constructor (logFile, launchProcess, code, msg) {
super (launchProcess, code, msg, logFile, 5);
}
},
ExtractError: class extends OSRMError {
constructor (logFile, code, msg) {
super('osrm-extract', code, msg, logFile, 3);
}
},
ContractError: class extends OSRMError {
constructor (logFile, code, msg) {
super('osrm-contract', code, msg, logFile, 3);
}
},
RoutedError: class extends OSRMError {
constructor (logFile, msg) {
super('osrm-routed', null, msg, logFile, 3);
}
},
TableDiffError: class extends Error {
constructor (expected, actual) {
super();
this.headers = expected.raw()[0];
this.expected = expected.hashes();
this.actual = actual;
this.diff = [];
this.hasErrors = false;
var good = 0, bad = 0;
this.expected.forEach((row, i) => {
var rowError = false;
for (var j in row) {
if (unescapeStr(row[j]) != actual[i][j]) {
rowError = true;
this.hasErrors = true;
break;
}
}
if (rowError) {
bad++;
this.diff.push(Object.assign({}, row, {status: 'undefined'}));
this.diff.push(Object.assign({}, actual[i], {status: 'comment'}));
} else {
good++;
this.diff.push(row);
}
});
}
get string () {
if (!this.hasErrors) return null;
var s = ['Tables were not identical:'];
s.push(this.headers.map(key => ' ' + key).join(' | '));
this.diff.forEach((row) => {
var rowString = '| ';
this.headers.forEach((header) => {
if (!row.status) rowString += ' ' + row[header] + ' | ';
else if (row.status === 'undefined') rowString += '(-) ' + row[header] + ' | ';
else rowString += '(+) ' + 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';
}
}
};
+15
View File
@@ -0,0 +1,15 @@
var exceptions = require('./exception_classes');
module.exports = function () {
this.OSRMError = exceptions.OSRMError,
this.FileError = (code, msg) => new (exceptions.FileError.bind(exceptions.FileError, this.PREPROCESS_LOG_FILE))(code, msg);
this.LaunchError = (code, launchProcess, msg) => new (exceptions.LaunchError.bind(exceptions.LaunchError, this.ERROR_LOG_FILE))(code, launchProcess, msg);
this.ExtractError = (code, msg) => new (exceptions.ExtractError.bind(exceptions.ExtractError, this.PREPROCESS_LOG_FILE))(code, msg);
this.ContractError = (code, msg) => new (exceptions.ContractError.bind(exceptions.ContractError, this.PREPROCESS_LOG_FILE))(code, msg);
this.RoutedError = (msg) => new (exceptions.RoutedError.bind(exceptions.RoutedError, this.OSRM_ROUTED_LOG_FILE))(msg);
};
-56
View File
@@ -1,56 +0,0 @@
class OSRMError < StandardError
attr_accessor :msg, :code, :process
def initialize process, code, msg, log, lines
@process = process
@code = code
@msg = msg
@lines = lines
@log = log
@extract = log_tail @log, @lines
end
def to_s
"*** #{@msg}\nLast #{@lines} lines from #{@log}:\n#{@extract}\n"
end
private
def log_tail path, n
Dir.chdir TEST_FOLDER do
expanded = File.expand_path path
if File.exists? expanded
File.open(expanded) do |f|
return f.tail(n).map { |line| " #{line}" }.join "\n"
end
else
return "File '#{expanded} does not exist!"
end
end
end
end
class FileError < OSRMError
def initialize code, msg
super 'fileutil', code, msg, PREPROCESS_LOG_FILE, 5
end
end
class ExtractError < OSRMError
def initialize code, msg
super 'osrm-extract', code, msg, PREPROCESS_LOG_FILE, 3
end
end
class PrepareError < OSRMError
def initialize code, msg
super 'osrm-contract', code, msg, PREPROCESS_LOG_FILE, 3
end
end
class RoutedError < OSRMError
def initialize msg
super 'osrm-routed', nil, msg, OSRM_ROUTED_LOG_FILE, 3
end
end
-34
View File
@@ -1,34 +0,0 @@
class File
# read last n lines of a file (trailing newlines are ignored)
def tail(n)
return [] if size==0
buffer = 1024
str = nil
if size>buffer
chunks = []
lines = 0
idx = size
begin
idx -= buffer # rewind
if idx<0
buffer += idx # adjust last read to avoid negative index
idx = 0
end
seek(idx)
chunk = read(buffer)
chunk.gsub!(/\n+\Z/,"") if chunks.empty? # strip newlines from end of file (first chunk)
lines += chunk.count("\n") # update total lines found
chunks.unshift chunk # prepend
end while lines<(n) && idx>0 # stop when enough lines found or no more to read
str = chunks.join('')
else
str = read(buffer)
end
# return last n lines of str
lines = str.split("\n")
lines.size>=n ? lines[-n,n] : lines
end
end
+5
View File
@@ -0,0 +1,5 @@
var classes = require('./data_classes');
module.exports = function() {
this.FuzzyMatch = new classes.FuzzyMatch();
};
-32
View File
@@ -1,32 +0,0 @@
class FuzzyMatch
def self.match got, want
if got == want
return true
elsif want.match /(.*)\s+~(.+)%$/ #percentage range: 100 ~5%
target = $1.to_f
percentage = $2.to_f
if target==0
return true
else
ratio = (1-(got.to_f / target)).abs;
return 100*ratio < percentage;
end
elsif want.match /(.*)\s+\+\-(.+)$/ #absolute range: 100 +-5
margin = $2.to_f
from = $1.to_f-margin
to = $1.to_f+margin
return got.to_f >= from && got.to_f <= to
elsif want =~ /^\/(.*)\/$/ #regex: /a,b,.*/
return got =~ /#{$1}/
else
return false
end
end
def self.match_location got, want
match( got[0], "#{want.lat} ~0.0025%" ) &&
match( got[1], "#{want.lon} ~0.0025%" )
end
end
+37
View File
@@ -0,0 +1,37 @@
var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var d3 = require('d3-queue');
module.exports = function () {
this.hashOfFiles = (paths, cb) => {
paths = Array.isArray(paths) ? paths : [paths];
var shasum = crypto.createHash('sha1');
var q = d3.queue(1);
var addFile = (path, cb) => {
fs.readFile(path, (err, data) => {
shasum.update(data);
cb(err);
});
};
paths.forEach(path => { q.defer(addFile, path); });
q.awaitAll(err => {
if (err) throw new Error('*** Error reading files:', err);
cb(shasum.digest('hex'));
});
};
this.hashProfile = (cb) => {
this.hashOfFiles(path.resolve(this.PROFILES_PATH, this.profile + '.lua'), cb);
};
this.hashString = (str) => {
return crypto.createHash('sha1').update(str).digest('hex');
};
return this;
};
-63
View File
@@ -1,63 +0,0 @@
require 'digest/sha1'
bin_extract_hash = nil
profile_hashes = nil
def hash_of_files paths
paths = [paths] unless paths.is_a? Array
hash = Digest::SHA1.new
for path in paths do
open(path,'rb') do |io|
while !io.eof
buf = io.readpartial 1024
hash.update buf
end
end
end
return hash.hexdigest
end
def profile_hash
profile_hashes ||= {}
profile_hashes[@profile] ||= hash_of_files "#{PROFILES_PATH}/#{@profile}.lua"
end
def osm_hash
@osm_hash ||= Digest::SHA1.hexdigest osm_str
end
def lua_lib_hash
@lua_lib_hash ||= hash_of_files Dir.glob("../profiles/lib/*.lua")
end
def bin_extract_hash
@bin_extract_hash ||= hash_of_files "#{BIN_PATH}/osrm-extract#{EXE}"
@bin_extract_hash
end
def bin_prepare_hash
@bin_prepare_hash ||= hash_of_files "#{BIN_PATH}/osrm-contract#{EXE}"
end
def bin_routed_hash
@bin_routed_hash ||= hash_of_files "#{BIN_PATH}/osrm-routed#{EXE}"
end
# combine state of data, profile and binaries into a hashes that identifies
# the exact test situation at different stages, so we can later skip steps when possible.
def fingerprint_osm
@fingerprint_osm ||= Digest::SHA1.hexdigest "#{osm_hash}"
end
def fingerprint_extract
@fingerprint_extract ||= Digest::SHA1.hexdigest "#{profile_hash}-#{lua_lib_hash}-#{bin_extract_hash}"
end
def fingerprint_prepare
@fingerprint_prepare ||= Digest::SHA1.hexdigest "#{bin_prepare_hash}"
end
def fingerprint_route
@fingerprint_route ||= Digest::SHA1.hexdigest "#{bin_routed_hash}"
end
+37
View File
@@ -0,0 +1,37 @@
var util = require('util');
module.exports = function () {
this.BeforeFeatures((features, callback) => {
this.pid = null;
this.initializeEnv(() => {
this.initializeOptions(callback);
});
});
this.Before((scenario, callback) => {
this.scenarioTitle = scenario.getName();
this.loadMethod = this.DEFAULT_LOAD_METHOD;
this.queryParams = [];
var d = new Date();
this.scenarioTime = util.format('%d-%d-%dT%s:%s:%sZ', d.getFullYear(), d.getMonth()+1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds());
this.resetData();
this.hasLoggedPreprocessInfo = false;
this.hasLoggedScenarioInfo = false;
this.setGridSize(this.DEFAULT_GRID_SIZE);
this.setOrigin(this.DEFAULT_ORIGIN);
callback();
});
this.After((scenario, callback) => {
this.setExtractArgs('');
this.setContractArgs('');
if (this.loadMethod === 'directly' && !!this.OSRMLoader.loader) this.OSRMLoader.shutdown(callback);
else callback();
});
this.Around('@stress', (scenario, callback) => {
// TODO implement stress timeout? Around support is being dropped in cucumber-js anyway
callback();
});
};
-35
View File
@@ -1,35 +0,0 @@
STRESS_TIMEOUT = 300
Before do |scenario|
# fetch scenario and feature name, so we can use it in log files if needed
case scenario
when Cucumber::RunningTestCase::Scenario
@feature_name = scenario.feature.name
@scenario_title = scenario.name
when Cucumber::RunningTestCase::ExampleRow
@feature_name = scenario.scenario_outline.feature.name
@scenario_title = scenario.scenario_outline.name
end
@load_method = DEFAULT_LOAD_METHOD
@query_params = []
@scenario_time = Time.now.strftime("%Y-%m-%dT%H:%m:%SZ")
reset_data
@has_logged_preprocess_info = false
@has_logged_scenario_info = false
set_grid_size DEFAULT_GRID_SIZE
set_origin DEFAULT_ORIGIN
end
Around('@stress') do |scenario, block|
Timeout.timeout(STRESS_TIMEOUT) do
block.call
end
end
After do
end
+47
View File
@@ -0,0 +1,47 @@
var Timeout = require('node-timeout');
var request = require('request');
module.exports = function () {
// Converts an array [["param","val1"], ["param","val2"]] into param=val1&param=val2
this.paramsToString = (params) => {
var kvPairs = params.map((kv) => kv[0].toString() + '=' + kv[1].toString());
var url = kvPairs.length ? kvPairs.join('&') : '';
return url.trim();
};
this.sendRequest = (baseUri, parameters, callback) => {
var limit = Timeout(this.OSRM_TIMEOUT, { err: { statusCode: 408 } });
var runRequest = (cb) => {
var params = this.paramsToString(parameters);
this.query = baseUri + (params.length ? '?' + params : '');
var options = this.httpMethod === 'POST' ? {
method: 'POST',
body: params,
url: baseUri
} : this.query;
request(options, (err, res, body) => {
if (err && err.code === 'ECONNREFUSED') {
throw new Error('*** osrm-routed is not running.');
} else if (err && err.statusCode === 408) {
throw new Error();
}
return cb(err, res, body);
});
};
runRequest(limit((err, res, body) => {
if (err) {
if (err.statusCode === 408)
return callback(this.RoutedError('*** osrm-routed did not respond'));
else if (err.code === 'ECONNREFUSED')
return callback(this.RoutedError('*** osrm-routed is not running'));
}
return callback(err, res, body);
}));
};
};
-33
View File
@@ -1,33 +0,0 @@
require 'net/http'
# Converts an array [["param","val1"], ["param","val2"]] into param=val1&param=val2
def params_to_string params
kv_pairs = params.map { |kv| kv[0].to_s + "=" + kv[1].to_s }
url = kv_pairs.size > 0 ? kv_pairs.join("&") : ""
return url
end
def send_request base_uri, parameters
Timeout.timeout(OSRM_TIMEOUT) do
uri_string = base_uri
params = params_to_string(parameters)
if not params.eql? ""
uri_string = uri_string + "?" + params
end
uri = URI.parse(uri_string)
@query = uri.to_s
if @http_method.eql? "POST"
Net::HTTP.start(uri.hostname, uri.port) do |http|
req = Net::HTTP::Post.new(uri.path)
req.body = params_to_string parameters
response = http.request(req)
end
else
response = Net::HTTP.get_response uri
end
end
rescue Errno::ECONNREFUSED => e
raise "*** osrm-routed is not running."
rescue Timeout::Error
raise "*** osrm-routed did not respond."
end
+5
View File
@@ -0,0 +1,5 @@
var launchClasses = require('./launch_classes');
module.exports = function () {
this._OSRMLoader = () => new (launchClasses._OSRMLoader.bind(launchClasses._OSRMLoader, this))();
};
-137
View File
@@ -1,137 +0,0 @@
require 'socket'
require 'open3'
require 'json'
# Only one isntance of osrm-routed is ever launched, to avoid collisions.
# The default is to keep osrm-routed running and load data with datastore.
# however, osrm-routed it shut down and relaunched for each scenario thats
# loads data directly.
class OSRMLoader
class OSRMBaseLoader
@@pid = nil
def launch
Timeout.timeout(LAUNCH_TIMEOUT) do
osrm_up
wait_for_connection
end
rescue Timeout::Error
raise RoutedError.new "Launching osrm-routed timed out."
end
def shutdown
Timeout.timeout(SHUTDOWN_TIMEOUT) do
osrm_down
end
rescue Timeout::Error
kill
raise RoutedError.new "Shutting down osrm-routed timed out."
end
def osrm_up?
if @@pid
begin
if Process.waitpid(@@pid, Process::WNOHANG) then
false
else
true
end
rescue Errno::ESRCH, Errno::ECHILD
false
end
end
end
def osrm_down
if @@pid
Process.kill TERMSIGNAL, @@pid
wait_for_shutdown
@@pid = nil
end
end
def kill
if @@pid
Process.kill 'KILL', @@pid
end
end
def wait_for_connection
while true
begin
socket = TCPSocket.new('127.0.0.1', OSRM_PORT)
return
rescue Errno::ECONNREFUSED
sleep 0.1
end
end
end
def wait_for_shutdown
while osrm_up?
sleep 0.01
end
end
end
# looading data directly when lauching osrm-routed:
# under this scheme, osmr-routed is launched and shutdown for each scenario,
# and osrm-datastore is not used
class OSRMDirectLoader < OSRMBaseLoader
def load world, input_file, &block
@world = world
@input_file = input_file
Dir.chdir TEST_FOLDER do
shutdown
launch
yield
shutdown
end
end
def osrm_up
return if @@pid
@@pid = Process.spawn("#{LOAD_LIBRARIES}#{BIN_PATH}/osrm-routed #{@input_file} --port #{OSRM_PORT}",:out=>OSRM_ROUTED_LOG_FILE, :err=>OSRM_ROUTED_LOG_FILE)
Process.detach(@@pid) # avoid zombie processes
end
end
# looading data with osrm-datastore:
# under this scheme, osmr-routed is launched once and kept running for all scenarios,
# and osrm-datastore is used to load data for each scenario
class OSRMDatastoreLoader < OSRMBaseLoader
def load world, input_file, &block
@world = world
@input_file = input_file
Dir.chdir TEST_FOLDER do
load_data
launch unless @@pid
yield
end
end
def load_data
run_bin "osrm-datastore", @input_file
end
def osrm_up
return if osrm_up?
@@pid = Process.spawn("#{LOAD_LIBRARIES}#{BIN_PATH}/osrm-routed --shared-memory=1 --port #{OSRM_PORT}",:out=>OSRM_ROUTED_LOG_FILE, :err=>OSRM_ROUTED_LOG_FILE)
Process.detach(@@pid) # avoid zombie processes
end
end
def self.load world, input_file, &block
method = world.instance_variable_get "@load_method"
if method == 'datastore'
OSRMDatastoreLoader.new.load world, input_file, &block
elsif method == 'directly'
OSRMDirectLoader.new.load world, input_file, &block
else
raise "*** Unknown load method '#{method}'"
end
end
end
+163
View File
@@ -0,0 +1,163 @@
'use strict';
var fs = require('fs');
var net = require('net');
var spawn = require('child_process').spawn;
var util = require('util');
var Timeout = require('node-timeout');
var OSRMBaseLoader = class {
constructor (scope) {
this.scope = scope;
}
launch (callback) {
var limit = Timeout(this.scope.LAUNCH_TIMEOUT, { err: this.scope.RoutedError('Launching osrm-routed timed out.') });
var runLaunch = (cb) => {
this.osrmUp(() => {
this.waitForConnection(cb);
});
};
runLaunch(limit((e) => { if (e) callback(e); callback(); }));
}
shutdown (callback) {
var limit = Timeout(this.scope.SHUTDOWN_TIMEOUT, { err: this.scope.RoutedError('Shutting down osrm-routed timed out.')});
var runShutdown = (cb) => {
this.osrmDown(cb);
};
runShutdown(limit((e) => { if (e) callback(e); callback(); }));
}
osrmIsRunning () {
return !!this.scope.pid && this.child && !this.child.killed;
}
osrmDown (callback) {
if (this.scope.pid) {
process.kill(this.scope.pid, this.scope.TERMSIGNAL);
this.waitForShutdown(callback);
this.scope.pid = null;
} else callback(true);
}
waitForConnection (callback) {
net.connect({
port: this.scope.OSRM_PORT,
host: '127.0.0.1'
})
.on('connect', () => {
callback();
})
.on('error', (e) => {
setTimeout(() => {
callback(e);
}, 100);
});
}
waitForShutdown (callback) {
var check = () => {
if (!this.osrmIsRunning()) callback();
};
setTimeout(check, 100);
}
};
var OSRMDirectLoader = class extends OSRMBaseLoader {
constructor (scope) {
super(scope);
}
load (inputFile, callback) {
this.inputFile = inputFile;
this.shutdown(() => {
this.launch(callback);
});
}
osrmUp (callback) {
if (this.scope.pid) return callback();
var writeToLog = (data) => {
fs.appendFile(this.scope.OSRM_ROUTED_LOG_FILE, data, (err) => { if (err) throw err; });
};
var child = spawn(util.format('%s%s/osrm-routed', this.scope.LOAD_LIBRARIES, this.scope.BIN_PATH), [this.inputFile, util.format('-p%d', this.scope.OSRM_PORT)], {detached: true});
this.scope.pid = child.pid;
child.stdout.on('data', writeToLog);
child.stderr.on('data', writeToLog);
callback();
}
};
var OSRMDatastoreLoader = class extends OSRMBaseLoader {
constructor (scope) {
super(scope);
}
load (inputFile, callback) {
this.inputFile = inputFile;
this.loadData((err) => {
if (err) return callback(err);
if (!this.scope.pid) return this.launch(callback);
else callback();
});
}
loadData (callback) {
this.scope.runBin('osrm-datastore', this.inputFile, (err) => {
if (err) return callback(new this.LaunchError(this.exitCode, 'datastore', err));
callback();
});
}
osrmUp (callback) {
if (this.scope.pid) return callback();
var writeToLog = (data) => {
fs.appendFile(this.scope.OSRM_ROUTED_LOG_FILE, data, (err) => { if (err) throw err; });
};
var child = spawn(util.format('%s%s/osrm-routed', this.scope.LOAD_LIBRARIES, this.scope.BIN_PATH), ['--shared-memory=1', util.format('-p%d', this.scope.OSRM_PORT)], {detached: true});
this.child = child;
this.scope.pid = child.pid;
child.stdout.on('data', writeToLog);
child.stderr.on('data', writeToLog);
callback();
}
};
module.exports = {
_OSRMLoader: class {
constructor (scope) {
this.scope = scope;
this.loader = null;
}
load (inputFile, callback) {
var method = this.scope.loadMethod;
if (method === 'datastore') {
this.loader = new OSRMDatastoreLoader(this.scope);
this.loader.load(inputFile, callback);
} else if (method === 'directly') {
this.loader = new OSRMDirectLoader(this.scope);
this.loader.load(inputFile, callback);
} else {
throw new Error('*** Unknown load method ' + method);
}
}
shutdown (callback) {
this.loader.shutdown(callback);
}
up () {
return this.loader ? this.loader.osrmIsRunning() : false;
}
}
};
+90
View File
@@ -0,0 +1,90 @@
var fs = require('fs');
module.exports = function () {
this.clearLogFiles = (callback) => {
// emptying existing files, rather than deleting and writing new ones makes it
// easier to use tail -f from the command line
fs.writeFile(this.OSRM_ROUTED_LOG_FILE, '', err => {
if (err) throw err;
fs.writeFile(this.PREPROCESS_LOG_FILE, '', err => {
if (err) throw err;
fs.writeFile(this.LOG_FILE, '', err => {
if (err) throw err;
callback();
});
});
});
};
var log = this.log = (s, type) => {
s = s || '';
type = type || null;
var file = type === 'preprocess' ? this.PREPROCESS_LOG_FILE : this.LOG_FILE;
fs.appendFile(file, s + '\n', err => {
if (err) throw err;
});
};
this.logScenarioFailInfo = () => {
if (this.hasLoggedScenarioInfo) return;
log('=========================================');
log('Failed scenario: ' + this.scenarioTitle);
log('Time: ' + this.scenarioTime);
log('Fingerprint osm stage: ' + this.osmData.fingerprintOSM);
log('Fingerprint extract stage: ' + this.fingerprintExtract);
log('Fingerprint contract stage: ' + this.fingerprintContract);
log('Fingerprint route stage: ' + this.fingerprintRoute);
log('Profile: ' + this.profile);
log();
log('```xml'); // so output can be posted directly to github comment fields
log(this.osmData.str.trim());
log('```');
log();
log();
this.hasLoggedScenarioInfo = true;
};
this.logFail = (expected, got, attempts) => {
this.logScenarioFailInfo();
log('== ');
log('Expected: ' + JSON.stringify(expected));
log('Got: ' + JSON.stringify(got));
log();
['route','forw','backw'].forEach((direction) => {
if (attempts[direction]) {
log('Direction: ' + direction);
log('Query: ' + attempts[direction].query);
log('Response: ' + attempts[direction].response.body);
log();
}
});
};
this.logPreprocessInfo = () => {
if (this.hasLoggedPreprocessInfo) return;
log('=========================================', 'preprocess');
log('Preprocessing data for scenario: ' + this.scenarioTitle, 'preprocess');
log('Time: ' + this.scenarioTime, 'preprocess');
log('', 'preprocess');
log('== OSM data:', 'preprocess');
log('```xml', 'preprocess'); // so output can be posted directly to github comment fields
log(this.osmData.str, 'preprocess');
log('```', 'preprocess');
log('', 'preprocess');
log('== Profile:', 'preprocess');
log(this.profile, 'preprocess');
log('', 'preprocess');
this.hasLoggedPreprocessInfo = true;
};
this.logPreprocess = (str) => {
this.logPreprocessInfo();
log(str, 'preprocess');
};
this.logPreprocessDone = () => {
log('Done with preprocessing at ' + new Date(), 'preprocess');
};
};
-88
View File
@@ -1,88 +0,0 @@
# logging
PREPROCESS_LOG_FILE = 'preprocessing.log'
LOG_FILE = 'fail.log'
def clear_log_files
Dir.chdir TEST_FOLDER do
# emptying existing files, rather than deleting and writing new ones makes it
# easier to use tail -f from the command line
`echo '' > #{OSRM_ROUTED_LOG_FILE}`
`echo '' > #{PREPROCESS_LOG_FILE}`
`echo '' > #{LOG_FILE}`
end
end
def log s='', type=nil
if type == :preprocess
file = PREPROCESS_LOG_FILE
else
file = LOG_FILE
end
File.open(file, 'a') {|f| f.write("#{s}\n") }
end
def log_scenario_fail_info
return if @has_logged_scenario_info
log "========================================="
log "Failed scenario: #{@scenario_title}"
log "Time: #{@scenario_time}"
log "Fingerprint osm stage: #{@fingerprint_osm}"
log "Fingerprint extract stage: #{@fingerprint_extract}"
log "Fingerprint prepare stage: #{@fingerprint_prepare}"
log "Fingerprint route stage: #{@fingerprint_route}"
log "Profile: #{@profile}"
log
log '```xml' #so output can be posted directly to github comment fields
log osm_str.strip
log '```'
log
log
@has_logged_scenario_info = true
end
def log_fail expected,got,attempts
return
log_scenario_fail_info
log "== "
log "Expected: #{expected}"
log "Got: #{got}"
log
['route','forw','backw'].each do |direction|
if attempts[direction]
attempts[direction]
log "Direction: #{direction}"
log "Query: #{attempts[direction][:query]}"
log "Response: #{attempts[direction][:response].body}"
log
end
end
end
def log_preprocess_info
return if @has_logged_preprocess_info
log "=========================================", :preprocess
log "Preprocessing data for scenario: #{@scenario_title}", :preprocess
log "Time: #{@scenario_time}", :preprocess
log '', :preprocess
log "== OSM data:", :preprocess
log '```xml', :preprocess #so output can be posted directly to github comment fields
log osm_str, :preprocess
log '```', :preprocess
log '', :preprocess
log "== Profile:", :preprocess
log @profile, :preprocess
log '', :preprocess
@has_logged_preprocess_info = true
end
def log_preprocess str
log_preprocess_info
log str, :preprocess
end
def log_preprocess_done
end
-25
View File
@@ -1,25 +0,0 @@
require 'OSM/StreamParser'
locations = nil
class OSMTestParserCallbacks < OSM::Callbacks
locations = nil
def self.locations
if locations
locations
else
#parse the test file, so we can later reference nodes and ways by name in tests
locations = {}
file = 'test/data/test.osm'
callbacks = OSMTestParserCallbacks.new
parser = OSM::StreamParser.new(:filename => file, :callbacks => callbacks)
parser.parse
puts locations
end
end
def node(node)
locations[node.name] = [node.lat,node.lon]
end
end
-14
View File
@@ -1,14 +0,0 @@
#monkey-patch osmlib to fix a bug
module OSM
class Way
def to_xml(xml)
xml.way(attributes) do
nodes.each do |node|
xml.nd(:ref => node)
end
tags.to_xml(xml)
end
end
end
end
+160
View File
@@ -0,0 +1,160 @@
var Timeout = require('node-timeout');
var request = require('request');
module.exports = function () {
this.requestPath = (service, params, callback) => {
var uri = [this.HOST, service].join('/');
return this.sendRequest(uri, params, callback);
};
this.requestUrl = (path, callback) => {
var uri = this.query = [this.HOST, path].join('/'),
limit = Timeout(this.OSRM_TIMEOUT, { err: { statusCode: 408 } });
function runRequest (cb) {
request(uri, cb);
}
runRequest(limit((err, res, body) => {
if (err) {
if (err.statusCode === 408) return callback(this.RoutedError('*** osrm-routed did not respond'));
else if (err.code === 'ECONNREFUSED')
return callback(this.RoutedError('*** osrm-routed is not running'));
} else
return callback(err, res, body);
}));
};
// Overwrites the default values in defaults
// e.g. [[a, 1], [b, 2]], [[a, 5], [d, 10]] => [[a, 5], [b, 2], [d, 10]]
this.overwriteParams = (defaults, other) => {
var merged = {};
var overwrite = (o) => {
merged[o[0]] = o[1];
};
defaults.forEach(overwrite);
other.forEach(overwrite);
return Object.keys(merged).map((key) => [key, merged[key]]);
};
var encodeWaypoints = (waypoints) => {
return waypoints.map(w => ['loc', [w.lat, w.lon].join(',')]);
};
this.requestRoute = (waypoints, bearings, userParams, callback) => {
if (bearings.length && bearings.length !== waypoints.length) throw new Error('*** number of bearings does not equal the number of waypoints');
var defaults = [['output','json'], ['instructions','true'], ['alt',false]],
params = this.overwriteParams(defaults, userParams),
encodedWaypoints = encodeWaypoints(waypoints);
if (bearings.length) {
var encodedBearings = bearings.map(b => ['b', b.toString()]);
params = Array.prototype.concat.apply(params, encodedWaypoints.map((o, i) => [o, encodedBearings[i]]));
} else {
params = params.concat(encodedWaypoints);
}
return this.requestPath('viaroute', params, callback);
};
this.requestNearest = (node, userParams, callback) => {
var defaults = [['output', 'json']],
params = this.overwriteParams(defaults, userParams);
params.push(['loc', [node.lat, node.lon].join(',')]);
return this.requestPath('nearest', params, callback);
};
this.requestTable = (waypoints, userParams, callback) => {
var defaults = [['output', 'json']],
params = this.overwriteParams(defaults, userParams);
params = params.concat(waypoints.map(w => [w.type, [w.coord.lat, w.coord.lon].join(',')]));
return this.requestPath('table', params, callback);
};
this.requestTrip = (waypoints, userParams, callback) => {
var defaults = [['output', 'json']],
params = this.overwriteParams(defaults, userParams);
params = params.concat(encodeWaypoints(waypoints));
return this.requestPath('trip', params, callback);
};
this.requestMatching = (waypoints, timestamps, userParams, callback) => {
var defaults = [['output', 'json']],
params = this.overwriteParams(defaults, userParams);
var encodedWaypoints = encodeWaypoints(waypoints);
if (timestamps.length) {
var encodedTimestamps = timestamps.map(t => ['t', t.toString()]);
params = Array.prototype.concat.apply(params, encodedWaypoints.map((o, i) => [o, encodedTimestamps[i]]));
} else {
params = params.concat(encodedWaypoints);
}
return this.requestPath('match', params, callback);
};
this.extractInstructionList = (instructions, index, postfix) => {
postfix = postfix || null;
if (instructions) {
return instructions.filter(r => r[0].toString() !== this.DESTINATION_REACHED.toString())
.map(r => r[index])
.map(r => (isNaN(parseInt(r)) && (!r || r == '')) ? '""' : '' + r + (postfix || ''))
.join(',');
}
};
this.wayList = (instructions) => {
return this.extractInstructionList(instructions, 1);
};
this.compassList = (instructions) => {
return this.extractInstructionList(instructions, 6);
};
this.bearingList = (instructions) => {
return this.extractInstructionList(instructions, 7);
};
this.turnList = (instructions) => {
var types = {
'0': 'none',
'1': 'straight',
'2': 'slight_right',
'3': 'right',
'4': 'sharp_right',
'5': 'u_turn',
'6': 'sharp_left',
'7': 'left',
'8': 'slight_left',
'9': 'via',
'10': 'head',
'11': 'enter_roundabout',
'12': 'leave_roundabout',
'13': 'stay_roundabout',
'14': 'start_end_of_street',
'15': 'destination',
'16': 'name_changes',
'17': 'enter_contraflow',
'18': 'leave_contraflow'
};
// replace instructions codes with strings, e.g. '11-3' gets converted to 'enter_roundabout-3'
return instructions ? instructions.map(r => r[0].toString().replace(/^(\d*)/, (match, num) => types[num])).join(',') : instructions;
};
this.modeList = (instructions) => {
return this.extractInstructionList(instructions, 8);
};
this.timeList = (instructions) => {
return this.extractInstructionList(instructions, 4, 's');
};
this.distanceList = (instructions) => {
return this.extractInstructionList(instructions, 2, 'm');
};
};
-182
View File
@@ -1,182 +0,0 @@
require 'net/http'
HOST = "http://127.0.0.1:#{OSRM_PORT}"
DESTINATION_REACHED = 15 #OSRM instruction code
def request_path service, params
uri = "#{HOST}/" + service
response = send_request uri, params
return response
end
def request_url path
uri = URI.parse"#{HOST}/#{path}"
@query = uri.to_s
Timeout.timeout(OSRM_TIMEOUT) do
Net::HTTP.get_response uri
end
rescue Errno::ECONNREFUSED => e
raise "*** osrm-routed is not running."
rescue Timeout::Error
raise "*** osrm-routed did not respond."
end
# Overwriters the default values in defaults.
# e.g. [[a, 1], [b, 2]], [[a, 5], [d, 10]] => [[a, 5], [b, 2], [d, 10]]
def overwrite_params defaults, other
merged = []
defaults.each do |k,v|
idx = other.index { |p| p[0] == k }
if idx == nil then
merged << [k, v]
else
merged << [k, other[idx][1]]
end
end
other.each do |k,v|
if merged.index { |pair| pair[0] == k} == nil then
merged << [k, v]
end
end
return merged
end
def request_route waypoints, bearings, user_params
raise "*** number of bearings does not equal the number of waypoints" unless bearings.size == 0 || bearings.size == waypoints.size
defaults = [['output','json'], ['instructions',true], ['alt',false]]
params = overwrite_params defaults, user_params
encoded_waypoint = waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] }
if bearings.size > 0
encoded_bearings = bearings.map { |b| ["b", b.to_s]}
parasm = params.concat encoded_waypoint.zip(encoded_bearings).flatten! 1
else
params = params.concat encoded_waypoint
end
return request_path "viaroute", params
end
def request_nearest node, user_params
defaults = [['output', 'json']]
params = overwrite_params defaults, user_params
params << ["loc", "#{node.lat},#{node.lon}"]
return request_path "nearest", params
end
def request_table waypoints, user_params
defaults = [['output', 'json']]
params = overwrite_params defaults, user_params
params = params.concat waypoints.map{ |w| [w[:type],"#{w[:coord].lat},#{w[:coord].lon}"] }
return request_path "table", params
end
def request_trip waypoints, user_params
defaults = [['output', 'json']]
params = overwrite_params defaults, user_params
params = params.concat waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] }
return request_path "trip", params
end
def request_matching waypoints, timestamps, user_params
defaults = [['output', 'json']]
params = overwrite_params defaults, user_params
encoded_waypoint = waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] }
if timestamps.size > 0
encoded_timestamps = timestamps.map { |t| ["t", t.to_s]}
parasm = params.concat encoded_waypoint.zip(encoded_timestamps).flatten! 1
else
params = params.concat encoded_waypoint
end
return request_path "match", params
end
def got_route? response
if response.code == "200" && !response.body.empty?
json = JSON.parse response.body
if json['status'] == 200
return way_list( json['route_instructions']).empty? == false
end
end
return false
end
def route_status response
if response.code == "200" && !response.body.empty?
json = JSON.parse response.body
return json['status']
else
"HTTP #{response.code}"
end
end
def extract_instruction_list instructions, index, postfix=nil
if instructions
instructions.reject { |r| r[0].to_s=="#{DESTINATION_REACHED}" }.
map { |r| r[index] }.
map { |r| (r=="" || r==nil) ? '""' : "#{r}#{postfix}" }.
join(',')
end
end
def way_list instructions
extract_instruction_list instructions, 1
end
def compass_list instructions
extract_instruction_list instructions, 6
end
def bearing_list instructions
extract_instruction_list instructions, 7
end
def turn_list instructions
if instructions
types = {
0 => :none,
1 => :straight,
2 => :slight_right,
3 => :right,
4 => :sharp_right,
5 => :u_turn,
6 => :sharp_left,
7 => :left,
8 => :slight_left,
9 => :via,
10 => :head,
11 => :enter_roundabout,
12 => :leave_roundabout,
13 => :stay_roundabout,
14 => :start_end_of_street,
15 => :destination,
16 => :name_changes,
17 => :enter_contraflow,
18 => :leave_contraflow
}
# replace instructions codes with strings
# "11-3" (enter roundabout and leave a 3rd exit) gets converted to "enter_roundabout-3"
instructions.map do |r|
r[0].to_s.gsub(/^\d*/) do |match|
types[match.to_i].to_s
end
end.join(',')
end
end
def mode_list instructions
extract_instruction_list instructions, 8
end
def time_list instructions
extract_instruction_list instructions, 4, "s"
end
def distance_list instructions
extract_instruction_list instructions, 2, "m"
end
+40
View File
@@ -0,0 +1,40 @@
var fs = require('fs');
var util = require('util');
var exec = require('child_process').exec;
module.exports = function () {
this.runBin = (bin, options, callback) => {
var opts = options.slice();
if (opts.match('{osm_base}')) {
if (!this.osmData.osmFile) throw new Error('*** {osm_base} is missing');
opts = opts.replace('{osm_base}', this.osmData.osmFile);
}
if (opts.match('{extracted_base}')) {
if (!this.osmData.extractedFile) throw new Error('*** {extracted_base} is missing');
opts = opts.replace('{extracted_base}', this.osmData.extractedFile);
}
if (opts.match('{contracted_base}')) {
if (!this.osmData.contractedFile) throw new Error('*** {contracted_base} is missing');
opts = opts.replace('{contracted_base}', this.osmData.contractedFile);
}
if (opts.match('{profile}')) {
opts = opts.replace('{profile}', [this.PROFILES_PATH, this.profile + '.lua'].join('/'));
}
var cmd = util.format('%s%s%s/%s%s%s %s 2>%s', this.QQ, this.LOAD_LIBRARIES, this.BIN_PATH, bin, this.EXE, this.QQ, opts, this.ERROR_LOG_FILE);
process.chdir(this.TEST_FOLDER);
exec(cmd, (err, stdout, stderr) => {
this.stdout = stdout.toString();
fs.readFile(this.ERROR_LOG_FILE, (e, data) => {
this.stderr = data ? data.toString() : '';
this.exitCode = err && err.code || 0;
process.chdir('../');
callback(err, stdout, stderr);
});
});
};
};
-28
View File
@@ -1,28 +0,0 @@
def run_bin bin, options
Dir.chdir TEST_FOLDER do
opt = options.dup
if opt.include? '{osm_base}'
raise "*** {osm_base} is missing" unless osm_file
opt.gsub! "{osm_base}", "#{osm_file}"
end
if opt.include? '{extracted_base}'
raise "*** {extracted_base} is missing" unless extracted_file
opt.gsub! "{extracted_base}", "#{extracted_file}"
end
if opt.include? '{contracted_base}'
raise "*** {contracted_base} is missing" unless contracted_file
opt.gsub! "{contracted_base}", "#{contracted_file}"
end
if opt.include? '{profile}'
opt.gsub! "{profile}", "#{PROFILES_PATH}/#{@profile}.lua"
end
cmd = "#{QQ}#{LOAD_LIBRARIES}#{BIN_PATH}/#{bin}#{EXE}#{QQ} #{opt} 2>error.log"
@stdout = `#{cmd}`
@stderr = File.read 'error.log'
@exit_code = $?.exitstatus
end
end
+203
View File
@@ -0,0 +1,203 @@
var util = require('util');
var assert = require('assert');
module.exports = function () {
this.ShouldGetAResponse = () => {
assert.equal(this.response.statusCode, 200);
assert.ok(this.response.body);
assert.ok(this.response.body.length);
};
this.ShouldBeValidJSON = (callback) => {
try {
this.json = JSON.parse(this.response.body);
callback();
} catch (e) {
callback(e);
}
};
this.ShouldBeWellFormed = () => {
assert.equal(typeof this.json.status, 'number');
};
this.WhenIRouteIShouldGet = (table, callback) => {
this.reprocessAndLoadData(() => {
var headers = new Set(table.raw()[0]);
var requestRow = (row, ri, cb) => {
var got,
json;
var afterRequest = (err, res, body) => {
if (err) return cb(err);
if (body && body.length) {
var instructions, bearings, compasses, turns, modes, times, distances;
json = JSON.parse(body);
var hasRoute = json.status === 200;
if (hasRoute) {
instructions = this.wayList(json.route_instructions);
bearings = this.bearingList(json.route_instructions);
compasses = this.compassList(json.route_instructions);
turns = this.turnList(json.route_instructions);
modes = this.modeList(json.route_instructions);
times = this.timeList(json.route_instructions);
distances = this.distanceList(json.route_instructions);
}
if (headers.has('status')) {
got.status = json.status.toString();
}
if (headers.has('message')) {
got.message = json.status_message;
}
if (headers.has('#')) {
// comment column
got['#'] = row['#'];
}
if (headers.has('start')) {
got.start = instructions ? json.route_summary.start_point : null;
}
if (headers.has('end')) {
got.end = instructions ? json.route_summary.end_point : null;
}
if (headers.has('geometry')) {
got.geometry = json.route_geometry;
}
if (headers.has('route')) {
got.route = (instructions || '').trim();
if (headers.has('alternative')) {
got.alternative = json.found_alternative ?
this.wayList(json.alternative_instructions[0]) : '';
}
var distance = hasRoute && json.route_summary.total_distance,
time = hasRoute && json.route_summary.total_time;
if (headers.has('distance')) {
if (row.distance.length) {
if (!row.distance.match(/\d+m/))
throw new Error('*** Distance must be specified in meters. (ex: 250m)');
got.distance = instructions ? util.format('%dm', distance) : '';
} else {
got.distance = '';
}
}
if (headers.has('time')) {
if (!row.time.match(/\d+s/))
throw new Error('*** Time must be specied in seconds. (ex: 60s)');
got.time = instructions ? util.format('%ds', time) : '';
}
if (headers.has('speed')) {
if (row.speed !== '' && instructions) {
if (!row.speed.match(/\d+ km\/h/))
throw new Error('*** Speed must be specied in km/h. (ex: 50 km/h)');
var speed = time > 0 ? Math.round(3.6*distance/time) : null;
got.speed = util.format('%d km/h', speed);
} else {
got.speed = '';
}
}
var putValue = (key, value) => {
if (headers.has(key)) got[key] = instructions ? value : '';
};
putValue('bearing', bearings);
putValue('compass', compasses);
putValue('turns', turns);
putValue('modes', modes);
putValue('times', times);
putValue('distances', distances);
}
var ok = true;
for (var key in row) {
if (this.FuzzyMatch.match(got[key], row[key])) {
got[key] = row[key];
} else {
ok = false;
}
}
if (!ok) {
this.logFail(row, got, { route: { query: this.query, response: res }});
}
cb(null, got);
} else {
cb(new Error('request failed to return valid body'));
}
};
if (headers.has('request')) {
got = { request: row.request };
this.requestUrl(row.request, afterRequest);
} else {
var defaultParams = this.queryParams;
var userParams = [];
got = {};
for (var k in row) {
var match = k.match(/param:(.*)/);
if (match) {
if (row[k] === '(nil)') {
userParams.push([match[1], null]);
} else if (row[k]) {
userParams.push([match[1], row[k]]);
}
got[k] = row[k];
}
}
var params = this.overwriteParams(defaultParams, userParams),
waypoints = [],
bearings = [];
if (row.bearings) {
got.bearings = row.bearings;
bearings = row.bearings.split(' ').filter(b => !!b);
}
if (row.from && row.to) {
var fromNode = this.findNodeByName(row.from);
if (!fromNode) throw new Error(util.format('*** unknown from-node "%s"'), row.from);
waypoints.push(fromNode);
var toNode = this.findNodeByName(row.to);
if (!toNode) throw new Error(util.format('*** unknown to-node "%s"'), row.to);
waypoints.push(toNode);
got.from = row.from;
got.to = row.to;
this.requestRoute(waypoints, bearings, params, afterRequest);
} else if (row.waypoints) {
row.waypoints.split(',').forEach((n) => {
var node = this.findNodeByName(n.trim());
if (!node) throw new Error('*** unknown waypoint node "%s"', n.trim());
waypoints.push(node);
});
got.waypoints = row.waypoints;
this.requestRoute(waypoints, bearings, params, afterRequest);
} else {
throw new Error('*** no waypoints');
}
}
};
this.processRowsAndDiff(table, requestRow, callback);
});
};
};
-3
View File
@@ -1,3 +0,0 @@
def shortcuts_hash
@shortcuts_hash ||= {}
end
+11
View File
@@ -0,0 +1,11 @@
var DifferentError = require('./exception_classes').TableDiffError;
module.exports = function () {
this.diffTables = (expected, actual, options, callback) => {
// this is a temp workaround while waiting for https://github.com/cucumber/cucumber-js/issues/534
var error = new DifferentError(expected, actual);
return callback(error.string);
};
};