Separate Vagrant::Action into Runner and Registry
This commit is contained in:
parent
5bfcbcba66
commit
daf711fd80
|
@ -10,134 +10,10 @@ require 'vagrant/action/general'
|
||||||
require 'vagrant/action/vm'
|
require 'vagrant/action/vm'
|
||||||
|
|
||||||
module Vagrant
|
module Vagrant
|
||||||
# Manages action running and registration. Every Vagrant environment
|
|
||||||
# has an instance of {Action} to allow for running in the context of
|
|
||||||
# the environment, which is accessible at {Environment#actions}. Actions
|
|
||||||
# are the foundation of most functionality in Vagrant, and are implemented
|
|
||||||
# architecturally as "middleware."
|
|
||||||
#
|
|
||||||
# # Registering an Action
|
|
||||||
#
|
|
||||||
# The main benefits of registering an action is the ability to retrieve and
|
|
||||||
# modify that registered action, as well as easily run the action. An example
|
|
||||||
# of registering an action is shown below, with a simple middleware which just
|
|
||||||
# outputs to `STDOUT`:
|
|
||||||
#
|
|
||||||
# class StdoutMiddleware
|
|
||||||
# def initialize(app, env)
|
|
||||||
# @app = app
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# def call(env)
|
|
||||||
# puts "HI!"
|
|
||||||
# @app.call(env)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Vagrant::Action.register(:stdout, StdoutMiddleware)
|
|
||||||
#
|
|
||||||
# Then to run a registered action, assuming `env` is a loaded {Environment}:
|
|
||||||
#
|
|
||||||
# env.actions.run(:stdout)
|
|
||||||
#
|
|
||||||
# Or to retrieve the action class for any reason:
|
|
||||||
#
|
|
||||||
# Vagrant::Action[:stdout]
|
|
||||||
#
|
|
||||||
# # Running an Action
|
|
||||||
#
|
|
||||||
# There are various built-in registered actions such as `start`, `stop`, `up`,
|
|
||||||
# etc. Actions are built to be run in the context of an environment, so use
|
|
||||||
# {Environment#actions} to run all actions. Then simply call {#run}:
|
|
||||||
#
|
|
||||||
# env.actions.run(:name)
|
|
||||||
#
|
|
||||||
# Where `:name` is the name of the registered action.
|
|
||||||
#
|
|
||||||
class Action
|
class Action
|
||||||
autoload :Environment, 'vagrant/action/environment'
|
autoload :Environment, 'vagrant/action/environment'
|
||||||
autoload :MultiStep, 'vagrant/action/multistep'
|
autoload :Registry, 'vagrant/action/registry'
|
||||||
autoload :Step, 'vagrant/action/step'
|
autoload :Runner, 'vagrant/action/runner'
|
||||||
autoload :Warden, 'vagrant/action/warden'
|
autoload :Warden, 'vagrant/action/warden'
|
||||||
|
|
||||||
include Util
|
|
||||||
@@reported_interrupt = false
|
|
||||||
|
|
||||||
class << self
|
|
||||||
# Returns the list of registered actions.
|
|
||||||
#
|
|
||||||
# @return [Array]
|
|
||||||
def actions
|
|
||||||
@actions ||= {}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Registers an action and associates it with a symbol. This
|
|
||||||
# symbol can then be referenced in other action builds and
|
|
||||||
# callbacks can be registered on that symbol.
|
|
||||||
#
|
|
||||||
# @param [Symbol] key
|
|
||||||
def register(key, callable)
|
|
||||||
actions[key.to_sym] = callable
|
|
||||||
end
|
|
||||||
|
|
||||||
# Retrieves a registered action by key.
|
|
||||||
#
|
|
||||||
# @param [Symbol] key
|
|
||||||
def [](key)
|
|
||||||
actions[key.to_sym]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# The environment to run the actions in.
|
|
||||||
attr_reader :env
|
|
||||||
|
|
||||||
# Initializes the action with the given environment which the actions
|
|
||||||
# will be run in.
|
|
||||||
#
|
|
||||||
# @param [Environment] env
|
|
||||||
def initialize(env)
|
|
||||||
@env = env
|
|
||||||
@logger = Log4r::Logger.new("vagrant::action")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Runs the given callable object in the context of the environment.
|
|
||||||
# If a symbol is given as the `callable` parameter, then it is looked
|
|
||||||
# up in the registered actions list which are registered with {register}.
|
|
||||||
#
|
|
||||||
# Any options given are injected into the environment hash.
|
|
||||||
#
|
|
||||||
# @param [Object] callable An object which responds to `call`.
|
|
||||||
def run(callable_id, options=nil)
|
|
||||||
callable = callable_id
|
|
||||||
callable = Builder.new.use(callable_id) if callable_id.kind_of?(Class)
|
|
||||||
callable = self.class.actions[callable_id] if callable_id.kind_of?(Symbol)
|
|
||||||
raise ArgumentError, "Argument to run must be a callable object or registered action." if !callable || !callable.respond_to?(:call)
|
|
||||||
|
|
||||||
action_environment = Action::Environment.new(env)
|
|
||||||
action_environment.merge!(options || {})
|
|
||||||
|
|
||||||
# Run the before action run callback, if we're not doing that already
|
|
||||||
run(:before_action_run, action_environment) if callable_id != :before_action_run
|
|
||||||
|
|
||||||
# Run the action chain in a busy block, marking the environment as
|
|
||||||
# interrupted if a SIGINT occurs, and exiting cleanly once the
|
|
||||||
# chain has been run.
|
|
||||||
int_callback = lambda do
|
|
||||||
if action_environment.interrupted?
|
|
||||||
env.ui.error I18n.t("vagrant.actions.runner.exit_immediately")
|
|
||||||
abort
|
|
||||||
end
|
|
||||||
|
|
||||||
env.ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup") if !@@reported_interrupt
|
|
||||||
action_environment.interrupt!
|
|
||||||
@@reported_interrupt = true
|
|
||||||
end
|
|
||||||
|
|
||||||
# We place a process lock around every action that is called
|
|
||||||
@logger.info("Running action: #{callable_id}")
|
|
||||||
env.lock do
|
|
||||||
Busy.busy(int_callback) { callable.call(action_environment) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,7 +36,7 @@ module Vagrant
|
||||||
|
|
||||||
# Use the class if it matches the given URI or if this
|
# Use the class if it matches the given URI or if this
|
||||||
# is the last class...
|
# is the last class...
|
||||||
if classes.length == (i + 1) || klass.match?(@env["box"].uri)
|
if classes.length == (i + 1) || klass.match?(@env["box_url"])
|
||||||
@env.ui.info I18n.t("vagrant.actions.box.download.with", :class => klass.to_s)
|
@env.ui.info I18n.t("vagrant.actions.box.download.with", :class => klass.to_s)
|
||||||
@downloader = klass.new(@env)
|
@downloader = klass.new(@env)
|
||||||
break
|
break
|
||||||
|
@ -47,7 +47,7 @@ module Vagrant
|
||||||
# just in case for now.
|
# just in case for now.
|
||||||
raise Errors::BoxDownloadUnknownType if !@downloader
|
raise Errors::BoxDownloadUnknownType if !@downloader
|
||||||
|
|
||||||
@downloader.prepare(@env["box"].uri)
|
@downloader.prepare(@env["box_url"])
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ module Vagrant
|
||||||
end
|
end
|
||||||
|
|
||||||
def download_to(f)
|
def download_to(f)
|
||||||
@downloader.download!(@env["box"].uri, f)
|
@downloader.download!(@env["box_url"], f)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ module Vagrant
|
||||||
# all the necessary Vagrant libraries are loaded. Hopefully
|
# all the necessary Vagrant libraries are loaded. Hopefully
|
||||||
# in the future this will no longer be necessary with autoloading.
|
# in the future this will no longer be necessary with autoloading.
|
||||||
def self.builtin!
|
def self.builtin!
|
||||||
|
return
|
||||||
# provision - Provisions a running VM
|
# provision - Provisions a running VM
|
||||||
register(:provision, Builder.new do
|
register(:provision, Builder.new do
|
||||||
use VM::CheckAccessible
|
use VM::CheckAccessible
|
||||||
|
@ -109,7 +110,7 @@ module Vagrant
|
||||||
# Other callbacks. There will be more of these in the future. For
|
# Other callbacks. There will be more of these in the future. For
|
||||||
# now, these are limited to what are needed internally.
|
# now, these are limited to what are needed internally.
|
||||||
register(:before_action_run, Builder.new do
|
register(:before_action_run, Builder.new do
|
||||||
use General::Validate
|
# use General::Validate
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,32 +4,11 @@ module Vagrant
|
||||||
# to the `call` method of each action. This environment contains
|
# to the `call` method of each action. This environment contains
|
||||||
# some helper methods for accessing the environment as well
|
# some helper methods for accessing the environment as well
|
||||||
# as being a hash, to store any additional options.
|
# as being a hash, to store any additional options.
|
||||||
class Environment < Util::HashWithIndifferentAccess
|
class Environment < Hash
|
||||||
# The {Vagrant::Environment} object represented by this
|
def initialize
|
||||||
# action environment.
|
|
||||||
attr_reader :env
|
|
||||||
|
|
||||||
def initialize(env)
|
|
||||||
super() do |h,k|
|
|
||||||
# By default, try to find the key as a method on the
|
|
||||||
# environment. Gross eval use here.
|
|
||||||
begin
|
|
||||||
value = eval("h.env.#{k}")
|
|
||||||
h[k] = value
|
|
||||||
rescue Exception
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@env = env
|
|
||||||
@interrupted = false
|
@interrupted = false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a UI object from the environment
|
|
||||||
def ui
|
|
||||||
env.ui
|
|
||||||
end
|
|
||||||
|
|
||||||
# Marks an environment as interrupted (by an outside signal or
|
# Marks an environment as interrupted (by an outside signal or
|
||||||
# anything). This will trigger any middleware sequences using this
|
# anything). This will trigger any middleware sequences using this
|
||||||
# environment to halt. This is automatically set by {Action} when
|
# environment to halt. This is automatically set by {Action} when
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
module Vagrant
|
||||||
|
class Action
|
||||||
|
# This is the action registry, which stores action steps indexed
|
||||||
|
# by a unique name. These registry names can be used to call actions
|
||||||
|
# via the `Runner` class.
|
||||||
|
class Registry
|
||||||
|
def initialize
|
||||||
|
@actions = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Register a callable by key.
|
||||||
|
#
|
||||||
|
# The callable should be given in a block which will be lazily evaluated
|
||||||
|
# when the action is needed.
|
||||||
|
#
|
||||||
|
# If an action by the given name already exists then it will be
|
||||||
|
# overwritten.
|
||||||
|
def register(key, &block)
|
||||||
|
@actions[key] = block
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get an action by the given key.
|
||||||
|
#
|
||||||
|
# This will evaluate the block given to `register` and return the resulting
|
||||||
|
# action stack.
|
||||||
|
def get(key)
|
||||||
|
return nil if !@actions.has_key?(key)
|
||||||
|
@actions[key].call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,49 @@
|
||||||
|
require 'log4r'
|
||||||
|
|
||||||
|
require 'vagrant/util/busy'
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
# * env.ui
|
||||||
|
# * env.lock
|
||||||
|
|
||||||
|
module Vagrant
|
||||||
|
class Action
|
||||||
|
class Runner
|
||||||
|
@@reported_interrupt = false
|
||||||
|
|
||||||
|
def initialize(registry)
|
||||||
|
@registry = registry
|
||||||
|
@logger = Log4r::Logger.new("vagrant::action::runner")
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(callable_id, options=nil)
|
||||||
|
callable = callable_id
|
||||||
|
callable = Builder.new.use(callable_id) if callable_id.kind_of?(Class)
|
||||||
|
callable = @registry.get(callable_id) if callable_id.kind_of?(Symbol)
|
||||||
|
raise ArgumentError, "Argument to run must be a callable object or registered action." if !callable || !callable.respond_to?(:call)
|
||||||
|
|
||||||
|
# Create the initial environment with the options given
|
||||||
|
environment = Environment.new
|
||||||
|
environment.merge!(options || {})
|
||||||
|
|
||||||
|
# Run the action chain in a busy block, marking the environment as
|
||||||
|
# interrupted if a SIGINT occurs, and exiting cleanly once the
|
||||||
|
# chain has been run.
|
||||||
|
int_callback = lambda do
|
||||||
|
if environment.interrupted?
|
||||||
|
env.ui.error I18n.t("vagrant.actions.runner.exit_immediately")
|
||||||
|
abort
|
||||||
|
end
|
||||||
|
|
||||||
|
env.ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup") if !@@reported_interrupt
|
||||||
|
environment.interrupt!
|
||||||
|
@@reported_interrupt = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# We place a process lock around every action that is called
|
||||||
|
@logger.info("Running action: #{callable_id}")
|
||||||
|
Util::Busy.busy(int_callback) { callable.call(environment) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -37,14 +37,6 @@ module Vagrant
|
||||||
directory.join(env.config.vm.box_ovf)
|
directory.join(env.config.vm.box_ovf)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Begins the process of adding a box to the vagrant installation. This
|
|
||||||
# method requires that `name` and `uri` be set. The logic of this method
|
|
||||||
# is kicked out to the `box_add` registered middleware.
|
|
||||||
def add
|
|
||||||
raise Errors::BoxAlreadyExists, :name => name if File.directory?(directory)
|
|
||||||
env.actions.run(:box_add, { "box" => self, "validate" => false })
|
|
||||||
end
|
|
||||||
|
|
||||||
# Begins the process of destroying this box. This cannot be undone!
|
# Begins the process of destroying this box. This cannot be undone!
|
||||||
def destroy
|
def destroy
|
||||||
env.actions.run(:box_remove, { "box" => self, "validate" => false })
|
env.actions.run(:box_remove, { "box" => self, "validate" => false })
|
||||||
|
|
|
@ -12,9 +12,10 @@ module Vagrant
|
||||||
attr_reader :directory
|
attr_reader :directory
|
||||||
|
|
||||||
# Initializes the class to search for boxes in the given directory.
|
# Initializes the class to search for boxes in the given directory.
|
||||||
def initialize(directory)
|
def initialize(directory, action_runner)
|
||||||
@directory = directory
|
@directory = directory
|
||||||
@boxes = []
|
@boxes = []
|
||||||
|
@action_runner = action_runner
|
||||||
|
|
||||||
reload!
|
reload!
|
||||||
end
|
end
|
||||||
|
@ -29,6 +30,12 @@ module Vagrant
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Adds a box to this collection with the given name and located
|
||||||
|
# at the given URL.
|
||||||
|
def add(name, url)
|
||||||
|
@action_runner.run(:box_add, :box_name => name, :box_url => url)
|
||||||
|
end
|
||||||
|
|
||||||
# Loads the list of all boxes from the source. This modifies the
|
# Loads the list of all boxes from the source. This modifies the
|
||||||
# current array.
|
# current array.
|
||||||
def reload!
|
def reload!
|
||||||
|
|
|
@ -5,7 +5,7 @@ module Vagrant
|
||||||
|
|
||||||
desc "add NAME URI", "Add a box to the system"
|
desc "add NAME URI", "Add a box to the system"
|
||||||
def add(name, uri)
|
def add(name, uri)
|
||||||
Box.add(env, name, uri)
|
env.boxes.add(name, uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "remove NAME", "Remove a box from the system"
|
desc "remove NAME", "Remove a box from the system"
|
||||||
|
|
|
@ -146,7 +146,7 @@ module Vagrant
|
||||||
#
|
#
|
||||||
# @return [BoxCollection]
|
# @return [BoxCollection]
|
||||||
def boxes
|
def boxes
|
||||||
@_boxes ||= BoxCollection.new(boxes_path)
|
@_boxes ||= BoxCollection.new(boxes_path, action_runner)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the VMs associated with this environment.
|
# Returns the VMs associated with this environment.
|
||||||
|
@ -205,12 +205,18 @@ module Vagrant
|
||||||
@host ||= Hosts::Base.load(self, config.vagrant.host)
|
@host ||= Hosts::Base.load(self, config.vagrant.host)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the {Action} class for this environment which allows actions
|
# Action runner for executing actions in the context of this environment.
|
||||||
# to be executed (middleware chains) in the context of this environment.
|
|
||||||
#
|
#
|
||||||
# @return [Action]
|
# @return [Action::Runner]
|
||||||
def actions
|
def action_runner
|
||||||
@actions ||= Action.new(self)
|
@action_runner ||= Action::Runner.new(action_registry)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Action registry for registering new actions with this environment.
|
||||||
|
#
|
||||||
|
# @return [Action::Registry]
|
||||||
|
def action_registry
|
||||||
|
@action_registry ||= Action::Registry.new
|
||||||
end
|
end
|
||||||
|
|
||||||
# Loads on initial access and reads data from the global data store.
|
# Loads on initial access and reads data from the global data store.
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
require File.expand_path("../../../base", __FILE__)
|
||||||
|
|
||||||
|
describe Vagrant::Action::Environment do
|
||||||
|
let(:instance) { described_class.new }
|
||||||
|
|
||||||
|
it "should be a hash" do
|
||||||
|
instance.should be_empty
|
||||||
|
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
|
|
@ -0,0 +1,26 @@
|
||||||
|
require File.expand_path("../../../base", __FILE__)
|
||||||
|
|
||||||
|
describe Vagrant::Action::Registry do
|
||||||
|
let(:instance) { described_class.new }
|
||||||
|
|
||||||
|
it "should return nil for nonexistent actions" do
|
||||||
|
instance.get("foo").should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should register an action without calling the block yet" do
|
||||||
|
expect do
|
||||||
|
instance.register("foo") do
|
||||||
|
raise Exception, "BOOM!"
|
||||||
|
end
|
||||||
|
end.to_not raise_error
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should call and return the result of a block when asking for the action" do
|
||||||
|
object = Object.new
|
||||||
|
instance.register("foo") do
|
||||||
|
object
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.get("foo").should eql(object)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,43 @@
|
||||||
|
require File.expand_path("../../../base", __FILE__)
|
||||||
|
|
||||||
|
describe Vagrant::Action::Runner do
|
||||||
|
let(:registry) do
|
||||||
|
d = double("registry")
|
||||||
|
d.stub(:get)
|
||||||
|
d
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:instance) { described_class.new(registry) }
|
||||||
|
|
||||||
|
it "should raise an error if an invalid callable is given" do
|
||||||
|
expect { instance.run(7) }.to raise_error(ArgumentError, /must be a callable/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be able to use a Proc as a callable" do
|
||||||
|
callable = Proc.new { raise Exception, "BOOM" }
|
||||||
|
expect { instance.run(callable) }.to raise_error(Exception, "BOOM")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be able to use a Class as a callable" do
|
||||||
|
callable = Class.new do
|
||||||
|
def initialize(app, env)
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
raise Exception, "BOOM"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect { instance.run(callable) }.to raise_error(Exception, "BOOM")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should pass options into hash given to callable" do
|
||||||
|
result = nil
|
||||||
|
callable = lambda do |env|
|
||||||
|
result = env["data"]
|
||||||
|
end
|
||||||
|
|
||||||
|
instance.run(callable, "data" => "foo")
|
||||||
|
result.should == "foo"
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,7 +4,8 @@ describe Vagrant::BoxCollection do
|
||||||
include_context "unit"
|
include_context "unit"
|
||||||
|
|
||||||
let(:environment) { isolated_environment }
|
let(:environment) { isolated_environment }
|
||||||
let(:instance) { described_class.new(environment.boxes_dir) }
|
let(:action_runner) { double("action runner") }
|
||||||
|
let(:instance) { described_class.new(environment.boxes_dir, action_runner) }
|
||||||
|
|
||||||
it "should list all available boxes" do
|
it "should list all available boxes" do
|
||||||
# No boxes yet.
|
# No boxes yet.
|
||||||
|
@ -29,4 +30,18 @@ describe Vagrant::BoxCollection do
|
||||||
result.name.should == "foo"
|
result.name.should == "foo"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should add the box" do
|
||||||
|
name = "foo"
|
||||||
|
url = "bar"
|
||||||
|
|
||||||
|
# Test the invocation of the action runner with the proper name
|
||||||
|
# and parameters. We leave the testing of the actual stack to
|
||||||
|
# acceptance tests, and individual pieces to unit tests of each
|
||||||
|
# step.
|
||||||
|
options = { :box_name => name, :box_url => url }
|
||||||
|
action_runner.should_receive(:run).with(:box_add, options)
|
||||||
|
|
||||||
|
instance.add(name, url)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class ActionEnvironmentTest < Test::Unit::TestCase
|
|
||||||
setup do
|
|
||||||
@klass = Vagrant::Action::Environment
|
|
||||||
@instance = @klass.new(vagrant_env)
|
|
||||||
end
|
|
||||||
|
|
||||||
should "be a hash with indifferent access" do
|
|
||||||
assert @instance.is_a?(Vagrant::Util::HashWithIndifferentAccess)
|
|
||||||
end
|
|
||||||
|
|
||||||
should "default values to those on the env" do
|
|
||||||
@instance.env.stubs(:key).returns("value")
|
|
||||||
assert_equal "value", @instance["key"]
|
|
||||||
end
|
|
||||||
|
|
||||||
should "setup the UI" do
|
|
||||||
assert_equal @instance.env.ui, @instance.ui
|
|
||||||
end
|
|
||||||
|
|
||||||
should "report interrupted if interrupt error" do
|
|
||||||
assert !@instance.interrupted?
|
|
||||||
@instance.interrupt!
|
|
||||||
assert @instance.interrupted?
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in New Issue