Pulled out action running from VM into its own Actions::Runner class

This commit is contained in:
Mitchell Hashimoto 2010-02-21 22:56:22 -08:00
parent cbcb877624
commit f1fda1f0fa
4 changed files with 220 additions and 186 deletions

View File

@ -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

View File

@ -1,19 +1,11 @@
module Vagrant module Vagrant
class VM class VM < Actions::Runner
include Vagrant::Util include Vagrant::Util
attr_accessor :vm attr_accessor :vm
attr_reader :actions
attr_accessor :from attr_accessor :from
class << self 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 # Finds a virtual machine by a given UUID and either returns
# a Vagrant::VM object or returns nil. # a Vagrant::VM object or returns nil.
def find(uuid) def find(uuid)
@ -25,47 +17,6 @@ module Vagrant
def initialize(vm=nil) def initialize(vm=nil)
@vm = vm @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 end
def destroy def destroy

View File

@ -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

View File

@ -11,142 +11,6 @@ class VMTest < Test::Unit::TestCase
Net::SSH.stubs(:start) Net::SSH.stubs(:start)
end 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 context "finding a VM" do
should "return nil if the VM is not found" do should "return nil if the VM is not found" do
VirtualBox::VM.expects(:find).returns(nil) VirtualBox::VM.expects(:find).returns(nil)