From f1fda1f0fa80d8a8b52c529e2beb3c0874eed1d8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 21 Feb 2010 22:56:22 -0800 Subject: [PATCH] Pulled out action running from VM into its own Actions::Runner class --- lib/vagrant/actions/runner.rb | 73 ++++++++++++++ lib/vagrant/vm.rb | 51 +--------- test/vagrant/actions/runner_test.rb | 146 ++++++++++++++++++++++++++++ test/vagrant/vm_test.rb | 136 -------------------------- 4 files changed, 220 insertions(+), 186 deletions(-) create mode 100644 lib/vagrant/actions/runner.rb create mode 100644 test/vagrant/actions/runner_test.rb diff --git a/lib/vagrant/actions/runner.rb b/lib/vagrant/actions/runner.rb new file mode 100644 index 000000000..6d5fd0426 --- /dev/null +++ b/lib/vagrant/actions/runner.rb @@ -0,0 +1,73 @@ +module Vagrant + module Actions + # Base class for any class which will act as a runner + # for actions. A runner is simply a class which will execute + # actions. + class Runner + class << self + # Executes a specific action. + def execute!(action_klass, *args) + runner = new + runner.add_action(action_klass, *args) + runner.execute! + end + end + + # Returns an array of all the actions in queue. Because this + # will persist accross calls (calling {#actions} twice will yield + # exactly the same object), to clear or modify it, use the ruby + # array methods which act on `self`, such as `Array#clear`. + # + # @return [Array] + def actions + @actions ||= [] + end + + # Add an action to the list of queued actions to execute. This method + # appends the given action class to the end of the queue. + def add_action(action_klass, *args) + actions << action_klass.new(self, *args) + end + + # Execute the actions in queue. + def execute!(single_action=nil, *args) + if single_action + actions.clear + add_action(single_action, *args) + end + + # Call the prepare method on each once its + # initialized, then call the execute! method + return_value = nil + [:prepare, :execute!].each do |method| + actions.each do |action| + return_value = action.send(method) + end + end + return_value + end + + # Invokes an "around callback" which invokes before_name and + # after_name for the given callback name, yielding a block between + # callback invokations. + def invoke_around_callback(name, *args) + invoke_callback("before_#{name}".to_sym, *args) + yield + invoke_callback("after_#{name}".to_sym, *args) + end + + # Invokes a single callback. This method will go through each action + # and call the method given in the parameter `name` if the action + # responds to it. + def invoke_callback(name, *args) + # Attempt to call the method for the callback on each of the + # actions + results = [] + actions.each do |action| + results << action.send(name, *args) if action.respond_to?(name) + end + results + end + end + end +end \ No newline at end of file diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 85520106e..6c1fa359f 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -1,19 +1,11 @@ module Vagrant - class VM + class VM < Actions::Runner include Vagrant::Util attr_accessor :vm - attr_reader :actions attr_accessor :from class << self - # Executes a specific action - def execute!(action_klass, *args) - vm = new - vm.add_action(action_klass, *args) - vm.execute! - end - # Finds a virtual machine by a given UUID and either returns # a Vagrant::VM object or returns nil. def find(uuid) @@ -25,47 +17,6 @@ module Vagrant def initialize(vm=nil) @vm = vm - @actions = [] - end - - def add_action(action_klass, *args) - @actions << action_klass.new(self, *args) - end - - def execute!(single_action=nil, *args) - if single_action - @actions.clear - add_action(single_action, *args) - end - - # Call the prepare method on each once its - # initialized, then call the execute! method - return_value = nil - [:prepare, :execute!].each do |method| - @actions.each do |action| - return_value = action.send(method) - end - end - return_value - end - - # Invokes an "around callback" which invokes before_name and - # after_name for the given callback name, yielding a block between - # callback invokations. - def invoke_around_callback(name, *args) - invoke_callback("before_#{name}".to_sym, *args) - yield - invoke_callback("after_#{name}".to_sym, *args) - end - - def invoke_callback(name, *args) - # Attempt to call the method for the callback on each of the - # actions - results = [] - @actions.each do |action| - results << action.send(name, *args) if action.respond_to?(name) - end - results end def destroy diff --git a/test/vagrant/actions/runner_test.rb b/test/vagrant/actions/runner_test.rb new file mode 100644 index 000000000..64d736461 --- /dev/null +++ b/test/vagrant/actions/runner_test.rb @@ -0,0 +1,146 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') + +class ActionRunnerTest < Test::Unit::TestCase + context "callbacks" do + setup do + @runner = Vagrant::Actions::Runner.new + end + + context "around callbacks" do + should "invoke before/after_name for around callbacks" do + block_obj = mock("block_obj") + around_seq = sequence("around_seq") + @runner.expects(:invoke_callback).with(:before_foo).once.in_sequence(around_seq) + block_obj.expects(:foo).once.in_sequence(around_seq) + @runner.expects(:invoke_callback).with(:after_foo).once.in_sequence(around_seq) + + @runner.invoke_around_callback(:foo) do + block_obj.foo + end + end + + should "forward arguments to invoke_callback" do + @runner.expects(:invoke_callback).with(:before_foo, "foo").once + @runner.expects(:invoke_callback).with(:after_foo, "foo").once + @runner.invoke_around_callback(:foo, "foo") do; end + end + end + + should "not invoke callback on actions which don't respond to it" do + action = mock("action") + action.stubs(:respond_to?).with(:foo).returns(false) + action.expects(:foo).never + + assert_nothing_raised do + @runner.actions << action + @runner.invoke_callback(:foo) + end + end + + should "invoke callback on actions which do respond to the method" do + action = mock("action") + action.expects(:foo).once + + @runner.actions << action + @runner.invoke_callback(:foo) + end + + should "collect all the results and return them as an array" do + result = [] + 3.times do |i| + action = mock("action#{i}") + action.expects(:foo).returns("foo#{i}").once + + @runner.actions << action + result << "foo#{i}" + end + + assert_equal result, @runner.invoke_callback(:foo) + end + end + + context "actions" do + setup do + @runner = Vagrant::Actions::Runner.new + end + + should "setup actions to be an array" do + assert_nil @runner.instance_variable_get(:@actions) + actions = @runner.actions + assert actions.is_a?(Array) + assert actions.equal?(@runner.actions) + end + + should "be empty initially" do + assert @runner.actions.empty? + end + + should "initialize the action when added" do + action_klass = mock("action_class") + action_inst = mock("action_inst") + action_klass.expects(:new).once.returns(action_inst) + @runner.add_action(action_klass) + assert_equal 1, @runner.actions.length + end + + should "initialize the action with given arguments when added" do + action_klass = mock("action_class") + action_klass.expects(:new).with(@runner, "foo", "bar").once + @runner.add_action(action_klass, "foo", "bar") + end + + should "clear the actions and run a single action if given to execute!" do + action = mock("action") + run_action = mock("action_run") + run_class = mock("run_class") + run_class.expects(:new).once.returns(run_action) + @runner.actions << action + + [:prepare, :execute!].each do |method| + action.expects(method).never + run_action.expects(method).once + end + + @runner.execute!(run_class) + end + + should "run #prepare on all actions, then #execute!" do + action_seq = sequence("action_seq") + actions = [] + 5.times do |i| + action = mock("action#{i}") + + @runner.actions << action + actions << action + end + + [:prepare, :execute!].each do |method| + actions.each do |action| + action.expects(method).once.in_sequence(action_seq) + end + end + + @runner.execute! + end + + should "run actions on class method execute!" do + vm = mock("vm") + execute_seq = sequence("execute_seq") + Vagrant::VM.expects(:new).returns(vm).in_sequence(execute_seq) + vm.expects(:add_action).with("foo").in_sequence(execute_seq) + vm.expects(:execute!).once.in_sequence(execute_seq) + + Vagrant::VM.execute!("foo") + end + + should "forward arguments to add_action on class method execute!" do + vm = mock("vm") + execute_seq = sequence("execute_seq") + Vagrant::VM.expects(:new).returns(vm).in_sequence(execute_seq) + vm.expects(:add_action).with("foo", "bar", "baz").in_sequence(execute_seq) + vm.expects(:execute!).once.in_sequence(execute_seq) + + Vagrant::VM.execute!("foo", "bar", "baz") + end + end +end diff --git a/test/vagrant/vm_test.rb b/test/vagrant/vm_test.rb index 63b6ec07a..25e250daa 100644 --- a/test/vagrant/vm_test.rb +++ b/test/vagrant/vm_test.rb @@ -11,142 +11,6 @@ class VMTest < Test::Unit::TestCase Net::SSH.stubs(:start) end - context "callbacks" do - setup do - @vm = Vagrant::VM.new(@mock_vm) - end - - context "around callbacks" do - should "invoke before/after_name for around callbacks" do - block_obj = mock("block_obj") - around_seq = sequence("around_seq") - @vm.expects(:invoke_callback).with(:before_foo).once.in_sequence(around_seq) - block_obj.expects(:foo).once.in_sequence(around_seq) - @vm.expects(:invoke_callback).with(:after_foo).once.in_sequence(around_seq) - - @vm.invoke_around_callback(:foo) do - block_obj.foo - end - end - - should "forward arguments to invoke_callback" do - @vm.expects(:invoke_callback).with(:before_foo, "foo").once - @vm.expects(:invoke_callback).with(:after_foo, "foo").once - @vm.invoke_around_callback(:foo, "foo") do; end - end - end - - should "not invoke callback on actions which don't respond to it" do - action = mock("action") - action.stubs(:respond_to?).with(:foo).returns(false) - action.expects(:foo).never - - assert_nothing_raised do - @vm.actions << action - @vm.invoke_callback(:foo) - end - end - - should "invoke callback on actions which do respond to the method" do - action = mock("action") - action.expects(:foo).once - - @vm.actions << action - @vm.invoke_callback(:foo) - end - - should "collect all the results and return them as an array" do - result = [] - 3.times do |i| - action = mock("action#{i}") - action.expects(:foo).returns("foo#{i}").once - - @vm.actions << action - result << "foo#{i}" - end - - assert_equal result, @vm.invoke_callback(:foo) - end - end - - context "actions" do - setup do - @vm = Vagrant::VM.new(@mock_vm) - end - - should "be empty initially" do - assert @vm.actions.empty? - end - - should "initialize the action when added" do - action_klass = mock("action_class") - action_inst = mock("action_inst") - action_klass.expects(:new).once.returns(action_inst) - @vm.add_action(action_klass) - assert_equal 1, @vm.actions.length - end - - should "initialize the action with given arguments when added" do - action_klass = mock("action_class") - action_klass.expects(:new).with(@vm, "foo", "bar").once - @vm.add_action(action_klass, "foo", "bar") - end - - should "clear the actions and run a single action if given to execute!" do - action = mock("action") - run_action = mock("action_run") - run_class = mock("run_class") - run_class.expects(:new).once.returns(run_action) - @vm.actions << action - - [:prepare, :execute!].each do |method| - action.expects(method).never - run_action.expects(method).once - end - - @vm.execute!(run_class) - end - - should "run #prepare on all actions, then #execute!" do - action_seq = sequence("action_seq") - actions = [] - 5.times do |i| - action = mock("action#{i}") - - @vm.actions << action - actions << action - end - - [:prepare, :execute!].each do |method| - actions.each do |action| - action.expects(method).once.in_sequence(action_seq) - end - end - - @vm.execute! - end - - should "run actions on class method execute!" do - vm = mock("vm") - execute_seq = sequence("execute_seq") - Vagrant::VM.expects(:new).returns(vm).in_sequence(execute_seq) - vm.expects(:add_action).with("foo").in_sequence(execute_seq) - vm.expects(:execute!).once.in_sequence(execute_seq) - - Vagrant::VM.execute!("foo") - end - - should "forward arguments to add_action on class method execute!" do - vm = mock("vm") - execute_seq = sequence("execute_seq") - Vagrant::VM.expects(:new).returns(vm).in_sequence(execute_seq) - vm.expects(:add_action).with("foo", "bar", "baz").in_sequence(execute_seq) - vm.expects(:execute!).once.in_sequence(execute_seq) - - Vagrant::VM.execute!("foo", "bar", "baz") - end - end - context "finding a VM" do should "return nil if the VM is not found" do VirtualBox::VM.expects(:find).returns(nil)