$LOAD_PATH.unshift File.expand_path('..', File.dirname(__FILE__)) require 'roby/test/common' require 'roby/planning' require 'flexmock' require 'roby/test/tasks/simple_task' class TC_Planner < Test::Unit::TestCase include Roby::Planning include Roby::Test def test_id_validation assert_equal(15, Planner.validate_method_id("15")) assert_equal('foo', Planner.validate_method_id(:foo)) assert_equal('foo', Planner.validate_method_id('foo')) end def test_method_definition base_model, base_1, base_15, base_foobar, base_barfoo, recursive = nil model = Class.new(Planner) do base_model = method(:base) base_1 = method(:base) { NullTask.new } base_15 = method(:base, :id => "15") { NullTask.new } base_foobar = method(:base, :id => :foobar) { NullTask.new } base_barfoo = method(:base, :id => 'barfoo') { NullTask.new } recursive = method(:recursive, :recursive => true) { NullTask.new } end assert_equal(17, model.next_id) assert(model.respond_to?(:base_methods)) assert(model.respond_to?(:each_base_method), model.methods.find_all { |name| name =~ /base/ }.inspect) assert_equal({ 1 => base_1, 15 => base_15, "foobar" => base_foobar, "barfoo" => base_barfoo }.to_set, model.enum_for(:each_base_method).to_set) assert(model.respond_to?(:base_model)) assert(model.find_methods(:base)) assert_equal(4, model.find_methods(:base).size) assert_equal(1, model.find_methods(:base, :id => 1).size) assert_equal(1, model.find_methods(:base, :id => 15).size) # Check handling of the string -> integer convertion assert_equal(1, model.find_methods(:base, :id => 'foobar').size) # Check handling of the symbol -> string convertion assert_equal(1, model.find_methods(:base, :id => :barfoo).size) assert_equal(nil, model.find_methods('recursive', :recursive => false)) assert_equal([recursive], model.find_methods('recursive', :recursive => true)) planner = model.new(plan) assert(planner.respond_to?(:base)) assert(planner.base.null?) assert(planner.respond_to?(:recursive)) assert_raises(Planning::NotFound) { planner.recursive(:recursive => false) } end def test_reuse task_model = Class.new(Task) derived_model = Class.new(task_model) planner_model = Class.new(Planner) do method(:reusable, :returns => task_model) method(:not_reusable, :returns => task_model, :reuse => false) end assert_raise(ArgumentError) { planner_model.method(:not_reusable, :reuse => true) } assert_nothing_raised { planner_model.method(:not_reusable, :reuse => false) } assert_nothing_raised { planner_model.method(:reusable, :reuse => true) } planner_model.class_eval do method(:reusable, :id => 'base') { task_model.new } method(:reusable, :id => 'derived', :returns => derived_model) { derived_model.new } method(:not_reusable) { task_model.new } # This one should build two tasks method(:check_not_reusable, :id => 1) do [reusable(:id => 'base'), not_reusable] end # This one should build two tasks method(:check_not_reusable, :id => 2) do [not_reusable, not_reusable] end # This one should build one task method(:check_reusable, :id => 1) do [not_reusable, reusable(:id => 'base')] end # This one should build only one task method(:check_reusable, :id => 2) do [reusable(:id => 'base'), reusable(:id => 'base')] end # This one whouls build two tasks method(:check_reusable, :id => 3) do [reusable(:id => 'base'), reusable(:id => 'derived')] end # This one whouls build one task method(:check_reusable, :id => 4) do [reusable(:id => 'derived'), reusable(:id => 'base')] end end assert_result_plan_size(1, planner_model, :check_reusable, :id => 1) assert_result_plan_size(1, planner_model, :check_reusable, :id => 2) assert_result_plan_size(2, planner_model, :check_reusable, :id => 3) assert_result_plan_size(1, planner_model, :check_reusable, :id => 4) assert_result_plan_size(2, planner_model, :check_not_reusable, :id => 1) assert_result_plan_size(2, planner_model, :check_not_reusable, :id => 2) end def test_empty_method_set task_model = Class.new(Roby::Task) model = Class.new(Roby::Planning::Planner) do method(:empty_set, :returns => task_model) end planner = model.new(plan) assert_raises(NotFound) { planner.empty_set } plan.insert(task = task_model.new) found_task = nil assert_nothing_raised { found_task = planner.empty_set } assert_equal(found_task, task) assert_raises(NotFound) { planner.empty_set :reuse => false } end def assert_result_plan_size(size, planner_model, method, options) planner = planner_model.new(plan) result = planner.send(method, options) result.each do |task| planner.plan.insert(task) end assert_equal(size, planner.plan.size, planner.plan.known_tasks.to_a.inspect) new_plan end def test_recursive task_model = Class.new(Roby::Task) do argument :id end model = Class.new(Planner) do method(:not_recursive) { root } method(:recursive, :recursive => true) do if @rec_already_called task_model.new(:id => 'recursive') else @rec_already_called = true root end end method(:root, :recursive => true) do if @root_already_called task_model.new(:id => 'root') else @root_already_called = true [recursive, not_recursive] end end end planner = model.new(plan) assert(planner.has_method?(:recursive)) assert(planner.respond_to?(:recursive)) recursive = planner.class.find_methods(:recursive) assert_equal(1, recursive.size) assert(recursive.first.recursive?) # Calls: # not_recursive # - root # - recursive # - not_recursive <= FAILS HERE assert_raises(NotFound) { model.new(new_plan).not_recursive } # Calls: # recursive # - root # - recursive => Task(id: recursive) # - not_recursive # - root => Task(id: root) planner = model.new(new_plan) assert_nothing_raised { planner.recursive } assert_equal(2, plan.size, plan.known_tasks) assert_equal(1, plan.find_tasks.which_fullfills(task_model, :id => 'recursive').to_a.size) assert_equal(1, plan.find_tasks.which_fullfills(task_model, :id => 'root').to_a.size) end def test_method_model # Some task models tm_a = Class.new(Roby::Task) tm_a_a = Class.new(tm_a) tm_b = Class.new(Roby::Task) foo_klass = Class.new # The planning model model = Class.new(Planner) # Fails because foo_klass is not a task assert_raises(ArgumentError) { model.method(:root, :returns => foo_klass) } # Check the definition of instance methods on Planner instances model.method(:root, :returns => tm_a) assert_equal( model.root_model, model.method_model(:root) ) assert_equal(tm_a, model.method_model(:root).returns) # Fails because we can't override a :returns option assert_raises(ArgumentError) { model.method(:root, :returns => tm_b) } # Does not fail since tm_a is the curren :returns task model assert_nothing_raised { model.method(:root, :returns => tm_a) } # Check that :returns is properly validated on methods model.method(:root, :id => 1) {} assert_raises(ArgumentError) { model.method(:root, :returns => tm_b) {} } assert_nothing_raised { model.method(:root, :returns => tm_a) {} } assert_nothing_raised { model.method(:root, :returns => tm_a_a) {} } # Cannot redefine the model since there are methods assert_raises(ArgumentError) { model.method(:root, :returns => tm_a) } # Check that we can't override an already-defined method assert_raises(ArgumentError) { model.method(:root, :id => 1) {} } end def test_model_of tm1 = Class.new(Roby::Task) tm2 = Class.new(tm1) tm3 = Class.new(tm2) base = Class.new(Planner) do method(:root, :returns => tm1) method(:root, :id => 'nil') { } method(:root, :id => 'tm2', :returns => tm2) { } end derived = Class.new(base) do method(:root, :id => 'derived', :returns => tm2) { } end assert_equal(tm1, base.model_of(:root).returns) assert_equal(tm1, base.model_of(:root, :id => 'nil').returns) assert_equal(tm2, base.model_of(:root, :id => 'tm2').returns) assert_equal(tm1, derived.model_of(:root).returns) assert_equal(tm1, derived.model_of(:root, :id => 'nil').returns) assert_equal(tm2, derived.model_of(:root, :id => 'tm2').returns) assert_equal(tm2, derived.model_of(:root, :id => 'derived').returns) end def test_returns_validation task_model = Class.new(Roby::Task) task_tag = TaskModelTag.new planner_model = Class.new(Planning::Planner) assert_nothing_raised { planner_model.method(:returns_task, :returns => task_model) } assert_nothing_raised { planner_model.method(:returns_tag, :returns => task_tag) } end def test_returns_inheritance # Some task models tm_a = Class.new(Roby::Task) tm_a_a = Class.new(tm_a) tm_b = Class.new(Roby::Task) foo_klass = Class.new # The planning models base = Class.new(Planner) base.method(:root, :returns => tm_a) derived = Class.new(base) # Check that we can override the model on derived assert_raises(ArgumentError) { derived.method(:root, :returns => tm_b) } assert_nothing_raised { derived.method(:root, :returns => tm_a_a) } assert_equal(base.root_model.returns, tm_a) assert_equal(derived.root_model.returns, tm_a_a) end def test_method_inheritance # Define a few task models tm_a = Class.new(Roby::Task) tm_b = Class.new(Roby::Task) tm_a_a = Class.new(tm_a) tm_a_a_a = Class.new(tm_a_a) tm_b_a = Class.new(tm_a) base = Class.new(Planner) do method(:root, :returns => tm_a) method(:root, :id => 1, :returns => tm_a_a) {} end base_root = base.enum_for(:each_root_method).to_a d1 = Class.new(base) # There are methods defined on :root, cannot override the :returns option assert_raises(ArgumentError) { d1.method(:root, :returns => tm_a_a) } assert_raises(ArgumentError) { d1.method(:root, :returns => tm_b) } assert_raises(ArgumentError) { d1.method(:root, :returns => tm_b) {} } d1_root = [] # Define a few methods and check :returns is validated properly assert_nothing_raised { d1_root << d1.method(:root, :returns => tm_a) {} } assert_nothing_raised { d1_root << d1.method(:root, :returns => tm_a_a) {} } assert_nothing_raised { d1_root << d1.method(:root, :returns => tm_b_a) {} } d2_root = d1_root.dup assert_nothing_raised { d1_root << d1.method(:root, :id => 1, :returns => tm_a_a) {} } d2 = Class.new(d1) assert_nothing_raised { d2_root << d2.method(:root, :id => 1, :returns => tm_a_a_a) {} } # Check that methods are defined at the proper level in the class hierarchy assert_equal(base_root.to_set, base.enum_for(:each_root_method).to_set) assert_equal(d1_root.to_set, d1.enum_for(:each_root_method).map { |_, x| x }.to_set) assert_equal(d2_root.to_set, d2.enum_for(:each_root_method).map { |_, x| x }.to_set) end def test_library a = Planning::Library.new do method(:root, :id => 'a') { } end b = Planning::Library.new do include a method(:root, :id => 'b') { } end planner = Class.new(Planner) do include b end assert( planner.find_methods(:root) ) assert_equal(['a', 'b'], planner.find_methods(:root).map { |m| m.id } ) c = Module.new do planning_library using a end planner = Class.new(Planner) { include c } assert_equal(['a'], planner.find_methods(:root).map { |m| m.id } ) d = Module.new do include b end assert_nothing_raised { d.method(:root, :id => 'c') { } } e = Module.new do planning_library(:id => "e") method(:test) { Roby::Test::SimpleTask.new } end planner = Class.new(Planner) do using e end.new(plan) assert_nothing_raised { planner.test(:id => 'e') } end def test_return_type task_model = Class.new(Task) do argument :arg end planner = Class.new(Planner) do method(:test, :returns => task_model, :reuse => false) method(:test, :id => "good") { task_model.new(:arg => 42, :unmatched => 21) } method(:test, :id => "bad_argument") { task_model.new(:arg => 21) } method(:test, :id => "bad_model") { NullTask.new(:arg => 42) } method(:test, :id => "array") { [task_model.new] } method(:not_a_task) { nil } end.new(plan) assert_nothing_raised { planner.test(:id => "good", :arg => 42, :unmatched => 10) } assert_raises(Planning::NotFound) { planner.test(:id => "bad_argument", :arg => 42) } assert_raises(Planning::NotFound) { planner.test(:id => "bad_model", :arg => 42) } assert_raises(Planning::NotFound) { planner.test(:id => "array", :arg => 42) } assert_raises(Planning::NotFound) { planner.not_a_task } end def test_planning_methods_names model = Class.new(Planner) do def not_a_planning_method end method(:test) { } method(:localization) { } method(:model_only) end assert_equal(['test', 'localization', 'model_only'].to_set, model.planning_methods_names.to_set) end def test_method_filter base = Class.new(Planner) do method(:test, :id => 1) { arguments[:mock].m(1) } method(:test, :id => 2) { arguments[:mock].m(2) } end assert_raises(ArgumentError) { Class.new(base).filter(:test) { || true } } assert_raises(ArgumentError) { Class.new(base).filter(:test) { |a| true } } assert_raises(ArgumentError) { Class.new(base).filter(:test) { |a, b, c| true } } planner, filter_block = nil, lambda { |a, b| true } assert_nothing_raised do planner = Class.new(base) do filter(:test, &filter_block) end end assert(planner.respond_to?(:each_test_filter)) assert_equal([filter_block], planner.enum_for(:each_test_filter).to_a) assert_equal(2, planner.find_methods('test', :index => 10).size) planner = Class.new(base) do filter(:test) { false } end assert(!planner.find_methods('test', :index => 10)) (1..2).each do |i| FlexMock.use do |mock| planner = Class.new(base) do filter(:test) do |opt, m| mock.filtered(m.id) m.id == i end end.new(plan) mock.should_receive(:m).with(i).once.returns(NullTask.new) mock.should_receive(:filtered).with(2).once mock.should_receive(:filtered).with(1).once planner.test(:mock => mock) end end planner = Class.new(base) do filter(:test) { false } end.new(plan) assert_raises(Planning::NotFound) { planner.test } end end