@@ -0,0 +1,41 @@
|
||||
def speedprofile
|
||||
@speedprofile ||= reset_speedprofile
|
||||
end
|
||||
|
||||
def reset_speedprofile
|
||||
@speedprofile = {}
|
||||
read_speedprofile DEFAULT_SPEEDPROFILE
|
||||
end
|
||||
|
||||
def read_speedprofile profile
|
||||
@speedprofile = {}
|
||||
@speedprofile_str = nil
|
||||
s = File.read "speedprofiles/#{profile}.ini"
|
||||
s.scan /(.*)=(.*)/ do |option|
|
||||
@speedprofile[option[0].strip] = option[1].strip
|
||||
end
|
||||
end
|
||||
|
||||
def speedprofile_str
|
||||
@speedprofile_str ||= "[Scenario: #{@scenario_title}]\n" + @speedprofile.map { |k,v| " #{k} = #{v}" }.join("\n")
|
||||
end
|
||||
|
||||
def write_speedprofile
|
||||
File.open( 'speedprofile.ini', 'w') {|f| f.write( speedprofile_str ) }
|
||||
end
|
||||
|
||||
def write_server_ini
|
||||
s=<<-EOF
|
||||
Threads = 1
|
||||
IP = 0.0.0.0
|
||||
Port = 5000
|
||||
|
||||
hsgrData=#{@osm_file}.osrm.hsgr
|
||||
nodesData=#{@osm_file}.osrm.nodes
|
||||
ramIndex=#{@osm_file}.osrm.ramIndex
|
||||
fileIndex=#{@osm_file}.osrm.fileIndex
|
||||
namesData=#{@osm_file}.osrm.names
|
||||
EOF
|
||||
File.open( 'server.ini', 'w') {|f| f.write( s ) }
|
||||
end
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
require 'OSM/objects' #osmlib gem
|
||||
require 'OSM/Database'
|
||||
require 'builder'
|
||||
|
||||
OSM_USER = 'osrm'
|
||||
OSM_GENERATOR = 'osrm-test'
|
||||
OSM_UID = 1
|
||||
TEST_FOLDER = 'test'
|
||||
DATA_FOLDER = 'cache'
|
||||
PREPROCESS_LOG_FILE = 'preprocessing.log'
|
||||
LOG_FILE = 'fail.log'
|
||||
OSM_TIMESTAMP = '2000-00-00T00:00:00Z'
|
||||
DEFAULT_SPEEDPROFILE = 'bicycle'
|
||||
WAY_SPACING = 10
|
||||
|
||||
ORIGIN = [1,1]
|
||||
NODE_SPACING = 100 #meters
|
||||
ZOOM = 0.001*(NODE_SPACING.to_f/111.0)
|
||||
|
||||
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
|
||||
#this is relatated to the fact that a oneway deadend doesn't make a lot of sense
|
||||
|
||||
#if we stack ways on different x coordinates, outability 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]
|
||||
node1.uid = OSM_UID
|
||||
node2.uid = OSM_UID
|
||||
node3.uid = OSM_UID
|
||||
node4.uid = OSM_UID
|
||||
node1 << { :name => "a#{ri}" }
|
||||
node2 << { :name => "b#{ri}" }
|
||||
node3 << { :name => "c#{ri}" }
|
||||
node4 << { :name => "d#{ri}" }
|
||||
|
||||
osm_db << node1
|
||||
osm_db << node2
|
||||
osm_db << node3
|
||||
osm_db << node4
|
||||
|
||||
#...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
|
||||
tags = row.dup
|
||||
tags.delete 'forw'
|
||||
tags.delete 'backw'
|
||||
tags['name'] = "abcd#{ri}"
|
||||
tags.reject! { |k,v| v=='' }
|
||||
way << tags
|
||||
osm_db << way
|
||||
end
|
||||
end
|
||||
|
||||
def find_node_by_name s
|
||||
name_node_hash[s.to_s]
|
||||
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_speedprofile
|
||||
reset_osm
|
||||
@fingerprint = nil
|
||||
end
|
||||
|
||||
def make_osm_id
|
||||
@osm_id = @osm_id+1
|
||||
end
|
||||
|
||||
def reset_osm
|
||||
osm_db.clear
|
||||
name_node_hash.clear
|
||||
name_way_hash.clear
|
||||
@osm_str = nil
|
||||
@osm_hash = nil
|
||||
|
||||
##ID -1 causes trouble, so add a few nodes to avoid it
|
||||
#node = OSM::Node.new nil, OSM_USER, OSM_TIMESTAMP, 0,0
|
||||
#node = OSM::Node.new nil, OSM_USER, OSM_TIMESTAMP, 0,0
|
||||
@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 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 write_osm
|
||||
#write .oms file if needed
|
||||
Dir.mkdir DATA_FOLDER unless File.exist? DATA_FOLDER
|
||||
@osm_file = "#{DATA_FOLDER}/#{fingerprint}"
|
||||
unless File.exist?("#{@osm_file}.osm")
|
||||
File.open( "#{@osm_file}.osm", 'w') {|f| f.write(osm_str) }
|
||||
end
|
||||
end
|
||||
|
||||
def convert_osm_to_pbf
|
||||
unless File.exist?("#{@osm_file}.osm.pbf")
|
||||
log_preprocess_info
|
||||
log "== Converting #{@osm_file}.osm to protobuffer format...", :preprocess
|
||||
#redirect stdout and stderr to a log file avoid output in the cucumber console
|
||||
unless system "osmosis --read-xml #{@osm_file}.osm --write-pbf #{@osm_file}.osm.pbf omitmetadata=true 1>>#{PREPROCESS_LOG_FILE} 2>>#{PREPROCESS_LOG_FILE}"
|
||||
raise "Failed to convert to proto buffer format. Please see #{hash}.log for more info."
|
||||
end
|
||||
log '', :preprocess
|
||||
end
|
||||
end
|
||||
|
||||
def extracted?
|
||||
File.exist?("#{@osm_file}.osrm") &&
|
||||
File.exist?("#{@osm_file}.osrm.names") &&
|
||||
File.exist?("#{@osm_file}.osrm.restrictions")
|
||||
end
|
||||
|
||||
def prepared?
|
||||
base = "#{DATA_FOLDER}/#{fingerprint}"
|
||||
File.exist?("#{base}.osrm.hsgr")
|
||||
end
|
||||
|
||||
def reprocess
|
||||
Dir.chdir TEST_FOLDER do
|
||||
write_speedprofile
|
||||
write_osm
|
||||
convert_osm_to_pbf
|
||||
unless extracted?
|
||||
log_preprocess_info
|
||||
log "== Extracting #{@osm_file}.osm...", :preprocess
|
||||
unless system "../osrm-extract #{@osm_file}.osm.pbf 1>>#{PREPROCESS_LOG_FILE} 2>>#{PREPROCESS_LOG_FILE}"
|
||||
log "*** Exited with code #{$?.exitstatus}.", :preprocess
|
||||
raise "*** osrm-extract exited with code #{$?.exitstatus}. The file preprocess.log might contain more info."
|
||||
end
|
||||
log '', :preprocess
|
||||
end
|
||||
unless prepared?
|
||||
log_preprocess_info
|
||||
log "== Preparing #{@osm_file}.osm...", :preprocess
|
||||
unless system "../osrm-prepare #{@osm_file}.osrm #{@osm_file}.restrictions 1>>#{PREPROCESS_LOG_FILE} 2>>#{PREPROCESS_LOG_FILE}"
|
||||
log "*** Exited with code #{$?.exitstatus}.", :preprocess
|
||||
raise "*** osrm-prepare exited with code #{$?.exitstatus}. The file preprocess.log might contain more info."
|
||||
end
|
||||
log '', :preprocess
|
||||
end
|
||||
log_preprocess_done
|
||||
write_server_ini
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
require 'digest/sha1'
|
||||
|
||||
def hash_of_file path
|
||||
hash = Digest::SHA1.new
|
||||
open(path,'r') do |io|
|
||||
while !io.eof
|
||||
buf = io.readpartial 1024
|
||||
hash.update buf
|
||||
end
|
||||
end
|
||||
return hash.hexdigest
|
||||
end
|
||||
|
||||
def speedprofile_hash
|
||||
@speedprofile_hash ||= Digest::SHA1.hexdigest speedprofile_str
|
||||
end
|
||||
|
||||
def osm_hash
|
||||
@osm_hash ||= Digest::SHA1.hexdigest osm_str
|
||||
end
|
||||
|
||||
def osm_hash
|
||||
@osm_hash ||= Digest::SHA1.hexdigest osm_str
|
||||
end
|
||||
|
||||
def bin_extract_hash
|
||||
@bin_hash ||= hash_of_file '../osrm-extract'
|
||||
end
|
||||
|
||||
def bin_prepare_hash
|
||||
@bin_hash ||= hash_of_file '../osrm-prepare'
|
||||
end
|
||||
|
||||
#combine state of data, speedprofile and binaries into a hash that identifies the exact test scenario
|
||||
def fingerprint
|
||||
@fingerprint ||= Digest::SHA1.hexdigest "#{bin_extract_hash}-#{bin_prepare_hash}-#{speedprofile_hash}-#{osm_hash}"
|
||||
end
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Before do |scenario|
|
||||
@scenario_title = scenario.title
|
||||
@scenario_time = Time.now.strftime("%Y-%m-%dT%H:%m:%SZ")
|
||||
reset_data
|
||||
@has_logged_preprocess_info = false
|
||||
@has_logged_scenario_info = false
|
||||
end
|
||||
|
||||
Around('@routing') do |scenario, block|
|
||||
Timeout.timeout(10) do
|
||||
block.call
|
||||
end
|
||||
end
|
||||
|
||||
After do
|
||||
osrm_kill
|
||||
end
|
||||
@@ -0,0 +1,71 @@
|
||||
require 'socket'
|
||||
require 'sys/proctable'
|
||||
|
||||
class OSRMLauncher
|
||||
def initialize &block
|
||||
Dir.chdir TEST_FOLDER do
|
||||
osrm_up
|
||||
yield
|
||||
osrm_down
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def each_process name, &block
|
||||
Sys::ProcTable.ps do |process|
|
||||
if process.comm.strip == name.strip
|
||||
yield process.pid.to_i, process.state.strip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def osrm_up?
|
||||
find_pid('osrm-routed') != nil
|
||||
end
|
||||
|
||||
def find_pid name
|
||||
each_process(name) { |pid,state| return pid.to_i }
|
||||
return nil
|
||||
end
|
||||
|
||||
def osrm_up
|
||||
return if osrm_up?
|
||||
pipe = IO.popen('../osrm-routed 1>>osrm-routed.log 2>>osrm-routed.log')
|
||||
timeout = 5
|
||||
(timeout*10).times do
|
||||
begin
|
||||
socket = TCPSocket.new('localhost', 5000)
|
||||
socket.puts 'ping'
|
||||
rescue Errno::ECONNREFUSED
|
||||
sleep 0.1
|
||||
end
|
||||
end
|
||||
sleep 0.1
|
||||
end
|
||||
|
||||
def osrm_down
|
||||
each_process('osrm-routed') { |pid,state| Process.kill 'TERM', pid }
|
||||
each_process('osrm-prepare') { |pid,state| Process.kill 'TERM', pid }
|
||||
each_process('osrm-extract') { |pid,state| Process.kill 'TERM', pid }
|
||||
wait_for_shutdown 'osrm-routed'
|
||||
wait_for_shutdown 'osrm-prepare'
|
||||
wait_for_shutdown 'osrm-extract'
|
||||
end
|
||||
|
||||
def osrm_kill
|
||||
each_process('osrm-routed') { |pid,state| Process.kill 'KILL', pid }
|
||||
each_process('osrm-prepare') { |pid,state| Process.kill 'KILL', pid }
|
||||
each_process('osrm-extract') { |pid,state| Process.kill 'KILL', pid }
|
||||
wait_for_shutdown 'osrm-routed'
|
||||
wait_for_shutdown 'osrm-prepare'
|
||||
wait_for_shutdown 'osrm-extract'
|
||||
end
|
||||
|
||||
def wait_for_shutdown name
|
||||
timeout = 10
|
||||
(timeout*10).times do
|
||||
return if find_pid(name) == nil
|
||||
sleep 0.1
|
||||
end
|
||||
raise "*** Could not terminate #{name}."
|
||||
end
|
||||
@@ -0,0 +1,66 @@
|
||||
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
|
||||
log '```xml' #so output can be posted directly to github comment fields
|
||||
log osm_str.strip
|
||||
log '```'
|
||||
log
|
||||
log speedprofile_str
|
||||
log
|
||||
@has_logged_scenario_info = true
|
||||
end
|
||||
|
||||
def log_fail expected,actual,failed
|
||||
log_scenario_fail_info
|
||||
log "== "
|
||||
log "Expected: #{expected}"
|
||||
log "Got: #{actual}"
|
||||
log
|
||||
failed.each do |fail|
|
||||
log "Attempt: #{fail[:attempt]}"
|
||||
log "Query: #{fail[:query]}"
|
||||
log "Response: #{fail[:response].body}"
|
||||
log
|
||||
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 "== Speed profile:", :preprocess
|
||||
log speedprofile_str.strip, :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
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#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
|
||||
@@ -0,0 +1,68 @@
|
||||
require 'net/http'
|
||||
|
||||
def request_route a,b
|
||||
@query = "http://localhost:5000/viaroute&start=#{a}&dest=#{b}&output=json&geomformat=cmp"
|
||||
#log @query
|
||||
uri = URI.parse @query
|
||||
Net::HTTP.get_response uri
|
||||
rescue Errno::ECONNREFUSED => e
|
||||
raise "*** osrm-routed is not running."
|
||||
rescue Timeout::Error
|
||||
raise "*** osrm-routed did not respond."
|
||||
end
|
||||
|
||||
def parse_response response
|
||||
if response.code == "200" && response.body.empty? == false
|
||||
json = JSON.parse response.body
|
||||
if json['status'] == 0
|
||||
route = way_list json['route_instructions']
|
||||
if route.empty?
|
||||
"Empty route: #{json['route_instructions']}"
|
||||
else
|
||||
"Route: #{route}"
|
||||
end
|
||||
elsif json['status'] == 207
|
||||
"No route"
|
||||
else
|
||||
"Status: #{json['status']}"
|
||||
end
|
||||
else
|
||||
"HTTP: #{response.code}"
|
||||
end
|
||||
end
|
||||
|
||||
def got_route? response
|
||||
if response.code == "200" && !response.body.empty?
|
||||
json = JSON.parse response.body
|
||||
if json['status'] == 0
|
||||
return way_list( json['route_instructions']).empty? == false
|
||||
end
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def route_status response
|
||||
if response.code == "200" && !response.body.empty?
|
||||
json = JSON.parse response.body
|
||||
if json['status'] == 0
|
||||
if way_list( json['route_instructions']).empty?
|
||||
return 'Empty route'
|
||||
else
|
||||
return 'x'
|
||||
end
|
||||
elsif json['status'] == 207
|
||||
''
|
||||
else
|
||||
"Status #{json['status']}"
|
||||
end
|
||||
else
|
||||
"HTTP #{response.code}"
|
||||
end
|
||||
end
|
||||
|
||||
def way_list instructions
|
||||
instructions.
|
||||
#reject { |i| i[2]<=1 }. #FIXME temporary hack to ignore instructions with length==0
|
||||
map { |r| r[1] }.
|
||||
reject(&:empty?).join(',')
|
||||
end
|
||||
Reference in New Issue
Block a user