Adds the cucumber test framework referenced in issues #26, #95, #114

This commit is contained in:
Emil Tin
2012-02-14 17:21:07 +01:00
committed by DennisOSRM
parent 84b35f47a1
commit eeea5b0e81
28 changed files with 1818 additions and 1 deletions
+41
View File
@@ -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
+189
View File
@@ -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
+38
View File
@@ -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
+17
View File
@@ -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
+71
View File
@@ -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
+66
View File
@@ -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
+14
View File
@@ -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
+68
View File
@@ -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