diff --git a/.gitignore b/.gitignore index 15a823372..64b88a1e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,11 @@ +# Test folder # +################### +test/speedprofile.ini +test/data/*.osrm +test/data/*.osrm.* +test/data/denmark.osm.pbf + + # Compiled source # ################### *.com @@ -6,6 +14,9 @@ *.exe *.o *.so +osrm-extract +osrm-prepare +osrm-routed # Packages # ############ @@ -39,10 +50,6 @@ SconsBuilder* .scon* .build -# sandbox directory # -####################### -sandbox - # Eclipse related files # ######################### .setting* diff --git a/Rakefile b/Rakefile index e151feec3..dd2cf1cf4 100644 --- a/Rakefile +++ b/Rakefile @@ -1,67 +1,53 @@ -testdata_folder = "testdata" #where to locate test data -sandbox = "sandbox" #where to locate builds, server configs and test data -area_name = "amager" #name of OSM data file +#$:.unshift(File.dirname(__FILE__) + '/../../lib') +require 'cucumber/rake/task' -desc "Rebuild, reprocess OSM data and run server" -task :default => [:build, :process, :run] +Cucumber::Rake::Task.new do |t| + t.cucumber_opts = %w{--format pretty} +end + +osm_data_country = 'denmark' +osm_data_area_name = 'kbh' +osm_data_area_bbox = 'top=55.6972 left=12.5222 right=12.624 bottom=55.6376' + + +desc "Rebuild and run tests" +task :default => [:build, :cucumber] desc "Build using Scons" task :build do system "scons" end +namespace :data do + desc "Download OSM data extract for Denmark, and crop test areas" + task :download do + raise "Error while downloading data." unless system "curl http://download.geofabrik.de/osm/europe/#{osm_data_country}.osm.pbf -o test/data/#{osm_data_country}.osm.pbf" + raise "Error while cropping data." unless system "osmosis --read-pbf file=test/data/#{osm_data_country}.osm.pbf --bounding-box #{osm_data_area_bbox} --write-pbf file=test/data/#{osm_data_area_name}.osm.pbf omitmetadata=true" + end + + desc "Reprocess OSM data" + task :process do + Dir.chdir "test" do #we must be in the test_folder folder to use the speedprofile.ini in that folder + raise "Error while extracting data." unless system "./osrm-extract data/#{osm_data_area_name}.osm.pbf" + raise "Error while preparing data." unless system "./osrm-prepare data/#{osm_data_area_name}.osrm #{osm_data_area_name}.osrm.restrictions" + end + end -file "#{sandbox}/#{area_name}.osm.pbf" => "#{testdata_folder}/#{area_name}.osm.pbf" do |t| - raise unless system "cp #{t.prerequisites.join} #{t.name}" + desc "Delete preprocessing files" + task :clean do + File.delete *Dir.glob('test/data/*.osrm') + File.delete *Dir.glob('test/data/*.osrm.*') + end end -desc "Process OSM test data" -task :process => ["#{sandbox}/#{area_name}.osm.pbf", :setup] do - prev = Dir.pwd - cd sandbox #we must be in the sandbox folder to use the speedprofile.ini in that folder - raise "Error while extracting data." unless system "./osrm-extract #{area_name}.osm.pbf" - raise "Error while preparing data." unless system "./osrm-prepare #{area_name}.osrm #{area_name}.osrm.restrictions" - cd prev -end - -desc "Download fresh OSM for the test data" -task :download => :setup do - start = Time.now - country = 'denmark' - bbox = 'top=55.6655 left=12.5589 bottom=55.6462 right=12.5963' - area = area_name - - raise "Error while downloading data." unless system "curl http://download.geofabrik.de/osm/europe/#{country}.osm.pbf -o #{sandbox}/#{country}.osm.pbf" - raise "Error while cropping data." unless system "osmosis --read-pbf file=#{sandbox}/#{country}.osm.pbf --bounding-box #{bbox} --write-pbf file=#{sandbox}/#{area}.osm.pbf omitmetadata=true" -end - -desc "Setup server files" -task :setup => ["#{sandbox}/speedprofile.ini", "#{sandbox}/extractor.ini", "#{sandbox}/server.ini"] - -file "#{sandbox}/speedprofile.ini" => "speedprofile.ini" do |t| - system "cp #{t.prerequisites.join} #{t.name}" -end - -file "#{sandbox}/extractor.ini" => "extractor.ini" do |t| - system "cp #{t.prerequisites.join} #{t.name}" -end - -file "#{sandbox}/server.ini" => "server.ini" do |t| - #first time the file is copied, we adjusts server settings to point to data files in our sandbox folder - text = File.read(t.prerequisites.join) - text.gsub!('/opt/osm/germany', "#{Dir.pwd}/sandbox/#{area_name}") - file = File.new( t.name, "w+") - file.puts text - file.close -end - -desc "Run the OSRM server" -task :run => :setup do - cd sandbox - system "osrm-routed" +desc "Launch the routing server" +task :run do + Dir.chdir "test" do + system "./osrm-routed" + end end desc "Run all test" task :test do puts "Test would go here..." -end \ No newline at end of file +end diff --git a/SConstruct b/SConstruct index 9df1512f8..3054bb7ba 100644 --- a/SConstruct +++ b/SConstruct @@ -216,8 +216,8 @@ env.Protobuf('DataStructures/pbf-proto/fileformat.proto') env.Protobuf('DataStructures/pbf-proto/osmformat.proto') #env.Append(LINKFLAGS = ['-lboost_system']) -env.Program(target = 'sandbox/osrm-extract', source = ["extractor.cpp", 'DataStructures/pbf-proto/fileformat.pb.cc', 'DataStructures/pbf-proto/osmformat.pb.cc']) -env.Program(target = 'sandbox/osrm-prepare', source = ["createHierarchy.cpp", 'Contractor/EdgeBasedGraphFactory.cpp']) -env.Program(target = 'sandbox/osrm-routed', source = ["routed.cpp", 'Descriptors/DescriptionFactory.cpp']) +env.Program(target = 'test/osrm-extract', source = ["extractor.cpp", 'DataStructures/pbf-proto/fileformat.pb.cc', 'DataStructures/pbf-proto/osmformat.pb.cc']) +env.Program(target = 'test/osrm-prepare', source = ["createHierarchy.cpp", 'Contractor/EdgeBasedGraphFactory.cpp']) +env.Program(target = 'test/osrm-routed', source = ["routed.cpp", 'Descriptors/DescriptionFactory.cpp']) env = conf.Finish() diff --git a/contractor.ini b/contractor.ini deleted file mode 100644 index d82afbbff..000000000 --- a/contractor.ini +++ /dev/null @@ -1,2 +0,0 @@ -Threads = 4 -SRTM = /opt/storage/srtm/Eurasia \ No newline at end of file diff --git a/extractor.ini b/extractor.ini deleted file mode 100644 index 15f135ff8..000000000 --- a/extractor.ini +++ /dev/null @@ -1 +0,0 @@ -Memory = 2 diff --git a/features/0_processing.feature b/features/0_processing.feature new file mode 100644 index 000000000..ef2789162 --- /dev/null +++ b/features/0_processing.feature @@ -0,0 +1,25 @@ +@process +Feature: Preprocessing OpenStreetMap data + In order to enable efficient routing + As the OSRM server + I want to be able to preprocess OpenStreetMap data + + Scenario: Processing OpenStreetMap data using bicycle profile + Given I am in the test folder + And the data file "data/kbh.osm.pbf" is present + And the "bicycle" speedprofile is used + + When I run the extractor with "./osrm-extract data/kbh.osm.pbf" + Then the response should include "extracting data from input file data/kbh.osm.pbf" + And the response should include 'Using profile "bicycle"' + And the response should include "[extractor] finished" + And I should see the file "data/kbh.osrm" + And I should see the file "data/kbh.osrm.names" + And I should see the file "data/kbh.osrm.restrictions" + + When I run the preprocessor with "./osrm-prepare data/kbh.osrm data/kbh.osrm.restrictions" + Then the response should include "finished preprocessing" + And I should see the file "data/kbh.osrm.hsgr" + And I should see the file "data/kbh.osrm.nodes" + And I should see the file "data/kbh.osrm.ramIndex" + And I should see the file "data/kbh.osrm.fileIndex" diff --git a/features/1_launch.feature b/features/1_launch.feature new file mode 100644 index 000000000..9e29bab38 --- /dev/null +++ b/features/1_launch.feature @@ -0,0 +1,13 @@ +@launch +Feature: Launching OSRM server + In order to handle routing request + As a user + I want to launch the OSRM server + +Scenario: Launching the OSRM server + Given I am in the test folder + And the preprocessed files for "data/kbh" are present and up to date + When I start the server with "./osrm-routed" + Then a process called "osrm-routed" should be running + And I should see "running and waiting for requests" on the terminal + diff --git a/features/routing.feature b/features/routing.feature new file mode 100644 index 000000000..c9fdc1857 --- /dev/null +++ b/features/routing.feature @@ -0,0 +1,57 @@ +@routing +Feature: Routing from A to B + In order to make it easier to navigate + As someone using the public road network + I want to be able to use OSRM to compute routes based on OpenStreetMap data + + Scenario: Phantom shortcut + When I request a route from 55.662740149207,12.576105114488& to 55.665753800212,12.575547215013 + Then I should get a route + And the distance should be close to 450m + + Scenario: Start and stop markers should snap to closest streets + When I request a route from 55.6634,12.5724 to 55.6649,12.5742 + Then I should get a route + And the route should stay on "Islands Brygge" + And the distance should be close to 200m + + Scenario: Crossing round-about at Amalienborg + When I request a route from 55.683797649183,12.593940686704 to 55.6842149924,12.592476200581 + Then I should get a route + And the distance should be close to 150m + + @bicycle + Scenario: Handle cycleway=opposite_lane + When I request a route from 55.689236488,12.55317804955 to 55.688510764046,12.552909828648 + Then I should get a route + And the route should follow "Kapelvej" + And there should not be any turns + And the distance should be close to 80m + + @bicycle + Scenario: Handle oneway:bicycle + When I request a route from 55.673168935147,12.563557740441 to 55.67380116846,12.563107129324& + Then I should get a route + And the route should follow "Banegårdspladsen" + And there should not be any turns + And the distance should be close to 70m + + Scenario: Requesting invalid routes + When I request a route from 0,0 to 0,0 + Then I should not get a route + And no error should be reported in terminal + + Scenario: Dont flicker + When I request a route from 55.658833555366,12.592788454378 to 55.663871808364,12.583497282355 + Then I should get a route + And the route should follow "Amagerfælledvej, Njalsgade, Artillerivej" + And no error should be reported in terminal + When I request a route from 55.658821450674,12.592466589296 to 55.663871808364,12.583497282355 + Then I should get a route + And the route should follow "Kaj Munks Vej, Tom Kristensens Vej, Ørestads Boulevard, Njalsgade, Artillerivej" + And no error should be reported in terminal + When I request a route from 55.658857764739,12.592058893525 to 55.663871808364,12.583497282355 + Then I should get a route + And the route should follow "Kaj Munks Vej, Tom Kristensens Vej, Ørestads Boulevard, Njalsgade, Artillerivej" + And no error should be reported in terminal + \ No newline at end of file diff --git a/features/step_definitions/launch.rb b/features/step_definitions/launch.rb new file mode 100644 index 000000000..d79c24ca9 --- /dev/null +++ b/features/step_definitions/launch.rb @@ -0,0 +1,78 @@ +require 'pathname' +require 'json' +require 'open4' +require "net/http" +require "uri" + +$stdout.sync = true +$server_pipe = nil +$server_running = false + +def read_terminal + return $server_pipe.read_nonblock 10000 rescue nil +end + +def launch cmd + $server_pipe = IO.popen(cmd) + sleep 2 # so the daemon has a chance to boot + + at_exit do + Process.kill("KILL", $server_pipe.pid) # clean up the daemon when the tests finish + end +end + +Given /^I am in the test folder$/ do + @root = Pathname.new(File.dirname(__FILE__)).parent.parent.expand_path + @test_folder = "#{@root}/test" + Dir.chdir @test_folder +end + +Given /^the server is configured for bike routing$/ do + pending # express the regexp above with the code you wish you had +end + +Given /^the "([^"]*)" speedprofile is used$/ do |profile| + FileUtils.cp "speedprofiles/#{profile}.ini", "speedprofile.ini" +end + +Then /^the response should include "([^"]*)"$/ do |string| + @response.include?(string).should_not == nil +end + +Then /^the response should include '([^']*)'$/ do |string| + @response.include?(string).should_not == nil +end + +Given /^the server is running$/ do + unless $server_running + step 'a process called "osrm-routed" should be running' + @server_running = true + end +end + +When /^I start the server with "([^']*)"$/ do |cmd| + launch cmd +end + +When /^I stop the server$/ do + Process.kill("KILL", $server_pipe.pid) + $server_pipe = nil +end + +Then /^a process called "([^']*)" should be running$/ do |daemon| + `ps -eo command | grep #{@test_folder}/#{daemon}`.size.should > 0 +end + +Then /^a process called "([^']*)" should not be running$/ do |daemon| + puts `ps -eo command | grep #{@test_folder}/#{daemon}$` + `ps -eo command | grep #{@test_folder}/#{daemon}$`.size.should == 0 +end + +Then /^I should see "([^']*)" on the terminal$/ do |string| + out = read_terminal + out.should =~ /#{string}/ +end + +Then /^no error should be reported in terminal$/ do + read_terminal.should_not =~ /error/ +end diff --git a/features/step_definitions/preprocessing.rb b/features/step_definitions/preprocessing.rb new file mode 100644 index 000000000..0d92db87b --- /dev/null +++ b/features/step_definitions/preprocessing.rb @@ -0,0 +1,28 @@ +Given /^the data file "([^"]*)" is present$/ do |file| + File.exists?(file).should == true +end + +When /^I run the extractor with "([^"]*)"$/ do |cmd| + @response = `#{cmd}` + #Dir.chdir @test_folder do + # @response = IO.popen([cmd, :err=>[:child, :out]]) { |ls_io| ls_result_with_error = ls_io.read } + #end +end + +When /^I run the preprocessor with "([^"]*)"$/ do |cmd| + @response = `#{cmd}` +end + +Given /^the preprocessed files for "([^"]*)" are present and up to date$/ do |area| + File.exists?("#{area}.osrm").should == true + File.exists?("#{area}.osrm.names").should == true + File.exists?("#{area}.osrm.restrictions").should == true + File.exists?("#{area}.osrm.hsgr").should == true + File.exists?("#{area}.osrm.nodes").should == true + File.exists?("#{area}.osrm.ramIndex").should == true + File.exists?("#{area}.osrm.fileIndex").should == true +end + +Then /^I should see the file "([^"]*)"$/ do |file| + File.exists?(file).should == true +end diff --git a/features/step_definitions/routing.rb b/features/step_definitions/routing.rb new file mode 100644 index 000000000..16e832a20 --- /dev/null +++ b/features/step_definitions/routing.rb @@ -0,0 +1,107 @@ + +def request_route a,b + uri = URI.parse "http://localhost:5000/viaroute&start=#{a}&dest=#{b}&output=json&geomformat=cmp" + Net::HTTP.get_response uri +rescue Errno::ECONNREFUSED => e + raise "NOTE: The OSRM server is not running. Start it manully, or include tests that start it." +end + +When /^I request a route from (.+) to (.+)$/ do |a,b| + @response = request_route a,b +end + +When /^I request a route from "([^"]*)" to "([^"]*)"$/ do |a,b| + pending + #store hash og adress => coordinate points in a file under version control + #if the adress is not found, then use nominatim to get one and store it in the file + + #TODO convert a/b from adress to coordinate + #@response = request_route a,b +end + +Then /^I should get a response/ do + @response.code.should == "200" + @response.body.should_not == nil + @response.body.should_not == '' +end + +Then /^response should be valid JSON$/ do + @json = JSON.parse @response.body +end + +Then /^response should be well-formed$/ do + @json['version'].class.should == Float + @json['status'].class.should == Fixnum + @json['status_message'].class.should == String + @json['route_summary'].class.should == Hash + @json['route_geometry'].class.should == String + @json['route_instructions'].class.should == Array + @json['via_points'].class.should == Array + @json['transactionId'].class.should == String +end + +Then /^a route should be found$/ do + @json['status'].should == 0 + @json['status_message'].should == "Found route between points" +end + +Then /^no route should be found$/ do + @json['status'].should == 207 + @json['status_message'].should == "Cannot find route between points" +end + +Then /^I should get a valid response$/ do + step "I should get a response" + step "response should be valid JSON" + step "response should be well-formed" + step "no error should be reported in terminal" +end + +Then /^I should get a route$/ do + step "I should get a valid response" + step "a route should be found" +end + +Then /^I should not get a route$/ do + step "I should get a valid response" + step "no route should be found" +end + +Then /^starting point should be "([^']*)"$/ do |name| + @json['route_summary']['start_point'].should == name +end + +Then /^end point should be "([^']*)"$/ do |name| + @json['route_summary']['end_point'].should == name +end + +Then /^distance should be between (\d+) and (\d+)$/ do |min,max| + @json['route_summary']['total_distance'].to_i.should >= min.to_i + @json['route_summary']['total_distance'].to_i.should <= max.to_i +end + +Then /^the distance should be close to (\d+)m$/ do |d| + @json['route_summary']['total_distance'].to_i.should >= d.to_i*0.95 + @json['route_summary']['total_distance'].to_i.should <= d.to_i/0.95 +end + +Then /^number of instructions should be (\d+)$/ do |n| + @json['route_instructions'].size.should == n +end + +Then /^there should not be any turns$/ do + (@json['route_instructions'].size-1).should == 0 +end + +Then /^the route should follow "([^"]*)"$/ do |route| + route = route.split(',').map{|w| w.strip}.reject(&:empty?).join(', ') + @json['route_instructions'].map { |r| r[1].strip }.reject(&:empty?).join(', ').should == route +end + +Then /^the route should stay on "([^"]*)"$/ do |route| + step 'starting point should be "Islands Brygge"' + step 'end point should be "Islands Brygge"' + step 'the route should follow "Islands Brygge"' + step 'there should not be any turns' +end + diff --git a/server.ini b/server.ini index fbf0108d2..2a6d1dac5 100644 --- a/server.ini +++ b/server.ini @@ -2,8 +2,8 @@ Threads = 8 IP = 0.0.0.0 Port = 5000 -hsgrData=/opt/osm/germany.osrm.hsgr -nodesData=/opt/osm/germany.osrm.nodes -ramIndex=/opt/osm/germany.osrm.ramIndex -fileIndex=/opt/osm/germany.osrm.fileIndex -namesData=/opt/osm/germany.osrm.names +hsgrData=data/kbh.osrm.hsgr +nodesData=data/kbh.osrm.nodes +ramIndex=data/kbh.osrm.ramIndex +fileIndex=data/kbh.osrm.fileIndex +namesData=data/kbh.osrm.names diff --git a/speedprofile.ini b/speedprofile.ini deleted file mode 100644 index 2a504a0cd..000000000 --- a/speedprofile.ini +++ /dev/null @@ -1,48 +0,0 @@ -[car] - motorway = 110 - motorway_link = 90 - trunk = 90 - trunk_link = 70 - primary = 70 - primary_link = 60 - secondary = 60 - secondary_link = 50 - tertiary = 55 - unclassified = 25 - residential = 40 - living_street = 10 - service = 30 - ferry = 5 - pier = 5 - obeyBollards = yes - obeyOneways = yes - useRestrictions = yes - accessTag = motorcar - excludeFromGrid = ferry - defaultSpeed = 50 - trafficLightPenalty = 15 -[bike] - trunk = 16 - trunk_link = 16 - primary = 16 - primary_link = 16 - secondary = 16 - secondary_link = 16 - tertiary = 16 - unclassified = 16 - residential = 16 - living_street = 16 - service = 16 - track = 16 - cycleway = 16 - path = 16 - ferry = 5 - pier = 5 - obeyOneways = yes - useRestrictions = no - accessTag = bicycle - excludeFromGrid = ferry - defaultSpeed = 5 - trafficLightPenalty = 15 - obeyBollards = no - \ No newline at end of file diff --git a/test/contractor.ini b/test/contractor.ini new file mode 100644 index 000000000..fa39ddf73 --- /dev/null +++ b/test/contractor.ini @@ -0,0 +1,2 @@ +Threads = 4 +SRTM = data/srtm/Eurasia \ No newline at end of file diff --git a/test/data/kbh.osm.pbf b/test/data/kbh.osm.pbf new file mode 100644 index 000000000..e00a5c3e2 Binary files /dev/null and b/test/data/kbh.osm.pbf differ diff --git a/test/speedprofiles/bicycle.ini b/test/speedprofiles/bicycle.ini new file mode 100644 index 000000000..bd570c051 --- /dev/null +++ b/test/speedprofiles/bicycle.ini @@ -0,0 +1,23 @@ +[bicycle] + cycleway = 20 + trunk = 20 + trunk_link = 20 + primary = 20 + primary_link = 20 + secondary = 20 + secondary_link = 20 + tertiary = 20 + unclassified = 18 + residential = 18 + living_street = 18 + service = 15 + track = 15 + path = 15 + ferry = 5 + pier = 5 + steps = 3 + obeyOneways = yes + useRestrictions = no + accessTag = bicycle + excludeFromGrid = ferry + defaultSpeed = 15 diff --git a/testdata/amager.osm.pbf b/testdata/amager.osm.pbf deleted file mode 100644 index bfcb5b654..000000000 Binary files a/testdata/amager.osm.pbf and /dev/null differ