Unit tests for Vagrant::Action::Builder

This commit is contained in:
Mitchell Hashimoto 2011-12-22 20:17:45 -08:00
parent 9f498ccb06
commit 3cd262ed75
7 changed files with 182 additions and 251 deletions

View File

@ -23,14 +23,6 @@ module Vagrant
instance_eval(&block) if block_given?
end
# Returns the current stack of middlewares. You probably won't
# need to use this directly, and it's recommended that you don't.
#
# @return [Array]
def stack
@stack ||= []
end
# Returns a mergeable version of the builder. If `use` is called with
# the return value of this method, then the stack will merge, instead
# of being treated as a separate single middleware.
@ -75,9 +67,9 @@ module Vagrant
insert(index + 1, middleware, *args, &block)
end
# Swaps out the given middlware object or index with the new
# Replaces the given middlware object or index with the new
# middleware.
def swap(index, middleware, *args, &block)
def replace(index, middleware, *args, &block)
if index.is_a?(Integer)
delete(index)
insert(index, middleware, *args, &block)
@ -93,6 +85,13 @@ module Vagrant
stack.delete_at(index)
end
# Runs the builder stack with the given environment.
def call(env)
to_app(env).call(env)
end
protected
# Returns the numeric index for the given middleware object.
#
# @param [Object] object The item to find the index for
@ -105,6 +104,14 @@ module Vagrant
nil
end
# Returns the current stack of middlewares. You probably won't
# need to use this directly, and it's recommended that you don't.
#
# @return [Array]
def stack
@stack ||= []
end
# Converts the builder stack to a runnable action sequence.
#
# @param [Vagrant::Action::Environment] env The action environment
@ -114,11 +121,6 @@ module Vagrant
# and predictable behavior upon exceptions.
Warden.new(stack.dup, env)
end
# Runs the builder stack with the given environment.
def call(env)
to_app(env).call(env)
end
end
end
end

View File

@ -7,25 +7,6 @@ module Vagrant
# some helper methods for accessing the environment as well
# as being a hash, to store any additional options.
class Environment < Util::HashWithIndifferentAccess
def initialize
@interrupted = false
end
# Marks an environment as interrupted (by an outside signal or
# anything). This will trigger any middleware sequences using this
# environment to halt. This is automatically set by {Action} when
# a SIGINT is captured.
def interrupt!
@interrupted = true
end
# Returns a boolean denoting if environment has been interrupted
# with a SIGINT.
#
# @return [Bool]
def interrupted?
!!@interrupted
end
end
end
end

View File

@ -34,13 +34,13 @@ module Vagrant
# chain has been run.
ui = environment[:ui] if environment.has_key?(:ui)
int_callback = lambda do
if environment.interrupted?
if environment[:interrupted]
ui.error I18n.t("vagrant.actions.runner.exit_immediately") if ui
abort
end
ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup") if ui && !@@reported_interrupt
environment.interrupt!
environment[:interrupted] = true
@@reported_interrupt = true
end

View File

@ -27,11 +27,11 @@ module Vagrant
begin
# Call the next middleware in the sequence, appending to the stack
# of "recoverable" middlewares in case something goes wrong!
raise Errors::VagrantInterrupt if env.interrupted?
raise Errors::VagrantInterrupt if env[:interrupted]
action = @actions.shift
@logger.info("Calling action: #{action}")
@stack.unshift(action).first.call(env)
raise Errors::VagrantInterrupt if env.interrupted?
raise Errors::VagrantInterrupt if env[:interrupted]
rescue SystemExit
# This means that an "exit" or "abort" was called. In these cases,
# we just exit immediately.

View File

@ -0,0 +1,161 @@
require File.expand_path("../../../base", __FILE__)
describe Vagrant::Action::Builder do
let(:data) { { :data => [] } }
let(:instance) { described_class.new }
# This returns a proc that can be used with the builder
# that simply appends data to an array in the env.
def appender_proc(data)
Proc.new { |env| env[:data] << data }
end
context "basic `use`" do
it "should add items to the stack and make them callable" do
data = {}
proc = Proc.new { |env| env[:data] = true }
instance.use proc
instance.call(data)
data[:data].should == true
end
it "should be able to add multiple items" do
data = {}
proc1 = Proc.new { |env| env[:one] = true }
proc2 = Proc.new { |env| env[:two] = true }
instance.use proc1
instance.use proc2
instance.call(data)
data[:one].should == true
data[:two].should == true
end
it "should be able to add another builder" do
data = {}
proc1 = Proc.new { |env| env[:one] = true }
# Build the first builder
one = described_class.new
one.use proc1
# Add it to this builder
two = described_class.new
two.use one
# Call the 2nd and verify results
two.call(data)
data[:one].should == true
end
it "should be able to set additional variables if using another builder" do
data = { }
proc1 = Proc.new { |env| env[:data] += 1 }
# Build the first builder
one = described_class.new
one.use proc1
# Add it to this builder
two = described_class.new
two.use one, :data => 10
# Call the 2nd and verify results
two.call(data)
data[:data].should == 11
end
end
context "inserting" do
it "can insert at an index" do
instance.use appender_proc(1)
instance.insert(0, appender_proc(2))
instance.call(data)
data[:data].should == [2, 1]
end
it "can insert next to a previous object" do
proc2 = appender_proc(2)
instance.use appender_proc(1)
instance.use proc2
instance.insert(proc2, appender_proc(3))
instance.call(data)
data[:data].should == [1, 3, 2]
end
it "can insert before" do
instance.use appender_proc(1)
instance.insert_before 0, appender_proc(2)
instance.call(data)
data[:data].should == [2, 1]
end
it "can insert after" do
instance.use appender_proc(1)
instance.use appender_proc(3)
instance.insert_after 0, appender_proc(2)
instance.call(data)
data[:data].should == [1, 2, 3]
end
it "raises an exception if an invalid object given" do
expect { instance.insert_after "object", appender_proc(1) }.
to raise_error(RuntimeError)
end
end
context "replace" do
it "can replace an object" do
proc1 = appender_proc(1)
proc2 = appender_proc(2)
instance.use proc1
instance.replace proc1, proc2
instance.call(data)
data[:data].should == [2]
end
it "can replace by index" do
proc1 = appender_proc(1)
proc2 = appender_proc(2)
instance.use proc1
instance.replace 0, proc2
instance.call(data)
data[:data].should == [2]
end
end
context "deleting" do
it "can delete by object" do
proc1 = appender_proc(1)
instance.use proc1
instance.use appender_proc(2)
instance.delete proc1
instance.call(data)
data[:data].should == [2]
end
it "can delete by index" do
proc1 = appender_proc(1)
instance.use proc1
instance.use appender_proc(2)
instance.delete 0
instance.call(data)
data[:data].should == [2]
end
end
end

View File

@ -13,10 +13,4 @@ describe Vagrant::Action::Environment do
instance["foo"] = "bar"
instance[:foo].should == "bar"
end
it "should keep track of interrupted state" do
instance.should_not be_interrupted
instance.interrupt!
instance.should be_interrupted
end
end

View File

@ -1,207 +0,0 @@
require "test_helper"
require "logger"
class ActionBuilderTest < Test::Unit::TestCase
setup do
@klass = Vagrant::Action::Builder
end
context "initializing" do
should "setup empty middleware stack" do
builder = @klass.new
assert builder.stack.empty?
end
should "take block to setup stack" do
builder = @klass.new do
use Hash
use lambda { |i| i }
end
assert !builder.stack.empty?
assert_equal 2, builder.stack.length
end
end
context "with an instance" do
setup do
@instance = @klass.new
end
context "adding to the stack" do
should "return self" do
assert @instance.equal?(@instance.use(1))
end
should "add to the end" do
@instance.use 1
@instance.use 2
assert_equal [2, [], nil], @instance.stack.last
end
should "merge in other builder's stack" do
other = @klass.new do
use 2
use 3
end
@instance.use 1
@instance.use other
assert_equal 3, @instance.stack.length
end
should "prepend a set environment task for merging middlewares if given" do
other = @klass.new do
use 1
end
@instance.use 0
@instance.use(other, :foo => :bar)
assert_equal 3, @instance.stack.length
assert_equal Vagrant::Action::Env::Set, @instance.stack[1].first
end
end
context "flatten" do
should "return the flattened format of the builder" do
env = Vagrant::Action::Environment.new(nil)
env["logger"] = Logger.new(nil)
env.expects(:foo).once
func = lambda { |x| x.foo }
@instance.use func
proc = @instance.flatten
assert proc.respond_to?(:call)
proc.call(env)
end
end
context "inserting" do
setup do
@instance.use "1"
@instance.use "2"
end
should "insert at the proper numeric index" do
@instance.insert(1, "3")
assert_equal "3", @instance.stack[1].first
end
should "insert next to the proper object if given" do
@instance.insert("2", "3")
assert_equal "3", @instance.stack[1].first
end
should "be able to call insert_before as well" do
@instance.insert_before("1", "0")
assert_equal "0", @instance.stack.first.first
end
should "be able to insert_after" do
@instance.insert_after("1", "0")
assert_equal "0", @instance.stack[1].first
end
should "be able to insert_after using numeric index" do
@instance.insert_after(1, "0")
assert_equal "0", @instance.stack[2].first
end
should "raise an exception if invalid index" do
assert_raises(RuntimeError) {
@instance.insert_after("15", "0")
}
end
end
context "swapping" do
setup do
@instance.use "1"
@instance.use "2"
end
should "be able to swap using the object" do
@instance.swap "1", "3"
assert_equal "3", @instance.stack.first.first
end
should "be able to swap using the index" do
@instance.swap 0, "3"
assert_equal "3", @instance.stack.first.first
end
end
context "deleting" do
setup do
@instance.use "1"
end
should "delete the proper object" do
@instance.delete("1")
assert @instance.stack.empty?
end
should "delete by index if given" do
@instance.delete(0)
assert @instance.stack.empty?
end
end
context "getting an index of an object" do
should "return the proper index if it exists" do
@instance.use 1
@instance.use 2
@instance.use 3
assert_equal 1, @instance.index(2)
end
end
context "converting to an app" do
should "make non-classes lambdas" do
env = Vagrant::Action::Environment.new(nil)
env["logger"] = Logger.new(nil)
env.expects(:foo).once
func = lambda { |x| x.foo }
@instance.use func
@instance.to_app(env).call(env)
end
should "raise exception if given invalid middleware" do
@instance.use 7
assert_raises(RuntimeError) {
@instance.to_app(nil)
}
end
end
context "calling" do
def mock_middleware
middleware = Class.new do
def initialize(app, env)
@app = app
end
def call(env)
@app.call(env)
end
end
end
should "convert to an app then call with the env" do
mw = mock_middleware
mw.any_instance.expects(:call).with() do |env|
assert env.has_key?(:key)
true
end
env = Vagrant::Action::Environment.new(nil)
env["logger"] = Logger.new(nil)
env[:key] = :value
@instance.use(mw)
@instance.call(env)
end
end
end
end