diff --git a/lib/roby/plan.rb b/lib/roby/plan.rb index 406deeb5a..7c656d2f9 100644 --- a/lib/roby/plan.rb +++ b/lib/roby/plan.rb @@ -1317,8 +1317,28 @@ def remote_tasks end end - # Ensures that the given tasks will end up being processed without - # forcefully stopping anything + # Return all mission/permanent tasks that current depend on the given task + def useful_tasks_using(tasks) + all_tasks = compute_useful_tasks( + Array(tasks), graphs: default_useful_task_graphs.map(&:reverse) + ).to_set + all_tasks.compare_by_identity + + result = [] + @task_index.mission_tasks.dup.each do |t| + result << t if all_tasks.include?(t) + end + @task_index.permanent_tasks.dup.each do |t| + result << t if all_tasks.include?(t) + end + result + end + + # Unmark mission/permanent tasks that depend on the tasks given as argument + # + # By doing so, it makes the tasks eligible for garbage collection. This + # is mostly used to shut down tasks from a specific task within their + # dependency graph. def make_useless(tasks) all_tasks = compute_useful_tasks( Array(tasks), graphs: default_useful_task_graphs.map(&:reverse) diff --git a/test/test_plan.rb b/test/test_plan.rb index 4d0645840..fc44b2f0a 100644 --- a/test/test_plan.rb +++ b/test/test_plan.rb @@ -1169,6 +1169,49 @@ def action; end end end + describe "#useful_tasks_using" do + it "returns the given task if it is a mission" do + plan.add_mission_task(task = Tasks::Simple.new) + assert_equal Set[task], plan.useful_tasks_using(task) + end + + it "returns the given task if it is permanent" do + plan.add_permanent_task(task = Tasks::Simple.new) + assert_equal Set[task], plan.useful_tasks_using(task) + end + + it "returns parents of the given task that are missions" do + plan.add_mission_task(a = Tasks::Simple.new) + a.depends_on(b = Tasks::Simple.new) + plan.add_mission_task(c = Tasks::Simple.new) + b.depends_on(leaf = Tasks::Simple.new) + c.depends_on(leaf = Tasks::Simple.new) + assert_equal Set[b, c], plan.useful_tasks_using(leaf) + end + + it "returns parents of the given task that are permanent" do + plan.add_permanent_task(a = Tasks::Simple.new) + a.depends_on(b = Tasks::Simple.new) + plan.add_permanent_task(c = Tasks::Simple.new) + b.depends_on(leaf = Tasks::Simple.new) + c.depends_on(leaf = Tasks::Simple.new) + assert_equal Set[b, c], plan.useful_tasks_using(leaf) + plan.add_permanent_task(a = Tasks::Simple.new) + a.depends_on(b = Tasks::Simple.new) + plan.add_permanent_task(c = Tasks::Simple.new) + b.depends_on(leaf = Tasks::Simple.new) + c.depends_on(leaf = Tasks::Simple.new) + assert_equal Set[b, c], plan.useful_tasks_using(leaf) + end + + it "goes across useful graphs" do + plan.add_permanent_task(a = Tasks::Simple.new) + a.planned_by(b = Tasks::Simple.new) + b.depends_on(leaf = Tasks::Simple.new) + assert_equal Set[a], plan.useful_tasks_using(leaf) + end + end + describe "#make_useless" do it "marks a mission task as non-mission" do plan.add_mission_task(task = Roby::Task.new)