diff options
| author | Luke Kanies <luke@madstop.com> | 2008-03-31 23:56:09 -0500 |
|---|---|---|
| committer | Luke Kanies <luke@madstop.com> | 2008-03-31 23:56:09 -0500 |
| commit | 88dc49cb7b0efe757c92ce28c807b91335acb07a (patch) | |
| tree | 13fe4561f1f524f97a8bb2c1ff84c1ef981d0241 /spec | |
| parent | 4165edaeb71ee2883b1bb85ff39a52d5628b259f (diff) | |
| parent | a8592f1009040ebf30a98268610915cc33bb3f63 (diff) | |
Merge branch 'master' into master_no_global_resources
Conflicts:
lib/puppet/node/catalog.rb
lib/puppet/type/pfile.rb
lib/puppet/type/pfilebucket.rb
lib/puppet/util/filetype.rb
spec/unit/node/catalog.rb
spec/unit/other/transbucket.rb
spec/unit/ral/provider/mount/parsed.rb
spec/unit/ral/types/file.rb
spec/unit/ral/types/interface.rb
spec/unit/ral/types/mount.rb
spec/unit/ral/types/package.rb
spec/unit/ral/types/schedule.rb
spec/unit/ral/types/service.rb
test/language/compile.rb
test/language/lexer.rb
test/language/snippets.rb
test/lib/puppettest.rb
test/ral/types/basic.rb
test/ral/types/cron.rb
test/ral/types/exec.rb
test/ral/types/file.rb
test/ral/types/file/target.rb
test/ral/types/filebucket.rb
test/ral/types/fileignoresource.rb
test/ral/types/filesources.rb
test/ral/types/group.rb
test/ral/types/host.rb
test/ral/types/parameter.rb
test/ral/types/sshkey.rb
test/ral/types/tidy.rb
test/ral/types/user.rb
test/ral/types/yumrepo.rb
Diffstat (limited to 'spec')
154 files changed, 5262 insertions, 9440 deletions
diff --git a/spec/Rakefile b/spec/Rakefile index 8b45eff89..e2996f64f 100644 --- a/spec/Rakefile +++ b/spec/Rakefile @@ -7,10 +7,12 @@ puppetlibdir = File.join(basedir, "../lib") puppettestlibdir = File.join(basedir, "../test/lib") speclibdir = File.join(basedir, "lib") -desc "Run all spec unit tests" -Spec::Rake::SpecTask.new('unit') do |t| - t.spec_files = FileList['unit/**/*.rb', 'integration/**/*.rb'] - t.libs = [puppetlibdir, puppettestlibdir, speclibdir] +libs = [puppetlibdir, puppettestlibdir, speclibdir] +desc "Run all specs" +Spec::Rake::SpecTask.new('all') do |t| + t.spec_files = FileList['integration/**/*.rb', 'unit/**/*.rb'] + t.libs = libs + t.spec_opts = ['--options', 'spec.opts'] end -task :default => [:unit] +task :default => [:all] diff --git a/spec/bin/spec b/spec/bin/spec deleted file mode 100755 index aaf320f34..000000000 --- a/spec/bin/spec +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env ruby -$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib")) -require 'spec' -::Spec::Runner::CommandLine.run(ARGV, STDERR, STDOUT, true, true) diff --git a/spec/integration/file_serving/configuration.rb b/spec/integration/file_serving/configuration.rb index b6505f096..6975594a8 100755 --- a/spec/integration/file_serving/configuration.rb +++ b/spec/integration/file_serving/configuration.rb @@ -12,8 +12,6 @@ describe Puppet::FileServing::Configuration, " when finding files with Puppet::F # Just in case it already exists. Puppet::FileServing::Configuration.clear_cache - @config = Puppet::FileServing::Configuration.create - @mount = Puppet::FileServing::Mount.new("mymount") FileTest.stubs(:exists?).with("/my/path").returns(true) FileTest.stubs(:readable?).with("/my/path").returns(true) @@ -25,6 +23,8 @@ describe Puppet::FileServing::Configuration, " when finding files with Puppet::F @parser.stubs(:parse).returns("mymount" => @mount) @parser.stubs(:changed?).returns(true) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) + + @config = Puppet::FileServing::Configuration.create end it "should return nil if the file does not exist" do diff --git a/spec/integration/node.rb b/spec/integration/node.rb index e4a311998..631d4403e 100755 --- a/spec/integration/node.rb +++ b/spec/integration/node.rb @@ -10,7 +10,9 @@ require 'puppet/node' describe Puppet::Node, " when using the memory terminus" do before do @name = "me" - Puppet::Node.terminus_class = :memory + @old_terminus = Puppet::Node.indirection.terminus_class + @terminus = Puppet::Node.indirection.terminus(:memory) + Puppet::Node.indirection.stubs(:terminus).returns @terminus @node = Puppet::Node.new(@name) end @@ -39,8 +41,4 @@ describe Puppet::Node, " when using the memory terminus" do it "should fail when asked to destroy a node that does not exist" do proc { Puppet::Node.destroy(@node) }.should raise_error(ArgumentError) end - - after do - Puppet.settings.clear - end end diff --git a/spec/lib/autotest/discover.rb b/spec/lib/autotest/discover.rb deleted file mode 100644 index 0ac563724..000000000 --- a/spec/lib/autotest/discover.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'autotest' - -Autotest.add_discovery do - "rspec" -end - -Autotest.add_discovery do - "puppet" -end diff --git a/spec/lib/autotest/puppet_rspec.rb b/spec/lib/autotest/puppet_rspec.rb deleted file mode 100644 index 8536f3912..000000000 --- a/spec/lib/autotest/puppet_rspec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'autotest' -require 'autotest/rspec' - -class Autotest::PuppetRspec < Autotest::Rspec - def initialize # :nodoc: - super - @test_mappings = { - # the libraries under lib/puppet - %r%^lib/puppet/(.*)\.rb$% => proc { |filename, m| - files_matching %r!spec/(unit|integration)/#{m[1]}.rb! - }, - - # the actual spec files themselves - %r%^spec/(unit|integration)/.*\.rb$% => proc { |filename, _| - filename - }, - - # force a complete re-run for all of these: - - # main puppet lib - %r!^lib/puppet\.rb$! => proc { |filename, _| - files_matching %r!spec/(unit|integration)/.*\.rb! - }, - - # the spec_helper - %r!^spec/spec_helper\.rb$! => proc { |filename, _| - files_matching %r!spec/(unit|integration)/.*\.rb! - }, - - # the puppet test libraries - %r!^test/lib/puppettest/.*! => proc { |filename, _| - files_matching %r!spec/(unit|integration)/.*\.rb! - }, - - # the puppet spec libraries - %r!^spec/lib/spec.*! => proc { |filename, _| - files_matching %r!spec/(unit|integration)/.*\.rb! - }, - - # the monkey patches for rspec - %r!^spec/lib/monkey_patches/.*! => proc { |filename, _| - files_matching %r!spec/(unit|integration)/.*\.rb! - }, - } - end -end diff --git a/spec/lib/autotest/rspec.rb b/spec/lib/autotest/rspec.rb deleted file mode 100644 index d4b77ea6b..000000000 --- a/spec/lib/autotest/rspec.rb +++ /dev/null @@ -1,95 +0,0 @@ -require 'autotest' - -class RspecCommandError < StandardError; end - -class Autotest::Rspec < Autotest - - def initialize(kernel=Kernel, separator=File::SEPARATOR, alt_separator=File::ALT_SEPARATOR) # :nodoc: - super() - @kernel, @separator, @alt_separator = kernel, separator, alt_separator - @spec_command = spec_command - - # watch out: Ruby bug (1.8.6): - # %r(/) != /\// - # since Ruby compares the REGEXP source, not the resulting pattern - @test_mappings = { - %r%^spec/.*\.rb$% => kernel.proc { |filename, _| - filename - }, - %r%^lib/(.*)\.rb$% => kernel.proc { |_, m| - ["spec/#{m[1]}_spec.rb"] - }, - %r%^spec/(spec_helper|shared/.*)\.rb$% => kernel.proc { - files_matching %r%^spec/.*_spec\.rb$% - } - } - end - - def tests_for_file(filename) - super.select { |f| @files.has_key? f } - end - - alias :specs_for_file :tests_for_file - - def failed_results(results) - results.scan(/^\d+\)\n(?:\e\[\d*m)?(?:.*?Error in )?'([^\n]*)'(?: FAILED)?(?:\e\[\d*m)?\n(.*?)\n\n/m) - end - - def handle_results(results) - @files_to_test = consolidate_failures failed_results(results) - unless @files_to_test.empty? then - hook :red - else - hook :green - end unless $TESTING - @tainted = true unless @files_to_test.empty? - end - - def consolidate_failures(failed) - filters = Hash.new { |h,k| h[k] = [] } - failed.each do |spec, failed_trace| - @files.keys.select{|f| f =~ /spec\//}.each do |f| - if failed_trace =~ Regexp.new(f) - filters[f] << spec - break - end - end - end - return filters - end - - def make_test_cmd(files_to_test) - return "#{ruby} -S #{@spec_command} #{add_options_if_present} #{files_to_test.keys.flatten.join(' ')}" - end - - def add_options_if_present - File.exist?("spec/spec.opts") ? "-O spec/spec.opts " : "" - end - - # Finds the proper spec command to use. Precendence - # is set in the lazily-evaluated method spec_commands. Alias + Override - # that in ~/.autotest to provide a different spec command - # then the default paths provided. - def spec_command - spec_commands.each do |command| - if File.exists?(command) - return @alt_separator ? (command.gsub @separator, @alt_separator) : command - end - end - - raise RspecCommandError, "No spec command could be found!" - end - - # Autotest will look for spec commands in the following - # locations, in this order: - # - # * bin/spec - # * default spec bin/loader installed in Rubygems - def spec_commands - [ - File.join('bin', 'spec'), - File.join(Config::CONFIG['bindir'], 'spec') - ] - end - -end diff --git a/spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb b/spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb deleted file mode 100644 index bfa2a0c3c..000000000 --- a/spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb +++ /dev/null @@ -1,31 +0,0 @@ -dir = File.expand_path(File.dirname(__FILE__)) -[ "#{dir}/../../lib", "#{dir}/../../../lib", "#{dir}/../../../test/lib"].each do |dir| - fulldir = File.expand_path(dir) - $LOAD_PATH.unshift(fulldir) unless $LOAD_PATH.include?(fulldir) -end - -require 'spec' -require 'puppettest' -require 'puppettest/runnable_test' - -module Spec - module Runner - class BehaviourRunner - def run_behaviours - @behaviours.each do |behaviour| - # LAK:NOTE: this 'runnable' test is Puppet-specific. - next unless behaviour.runnable? - behaviour.run(@options.reporter, @options.dry_run, @options.reverse, @options.timeout) - end - end - end - end -end - -module Spec - module DSL - class EvalModule < Module - include PuppetTest::RunnableTest - end - end -end diff --git a/spec/lib/spec.rb b/spec/lib/spec.rb deleted file mode 100644 index 48c12595c..000000000 --- a/spec/lib/spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'spec/extensions' -require 'spec/version' -require 'spec/matchers' -require 'spec/expectations' -require 'spec/translator' -require 'spec/dsl' -require 'spec/runner' - -class Object - def metaclass - class << self; self; end - end -end diff --git a/spec/lib/spec/dsl.rb b/spec/lib/spec/dsl.rb deleted file mode 100644 index f960eb907..000000000 --- a/spec/lib/spec/dsl.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'spec/dsl/description' -require 'spec/dsl/errors' -require 'spec/dsl/configuration' -require 'spec/dsl/behaviour_callbacks' -require 'spec/dsl/behaviour' -require 'spec/dsl/behaviour_eval' -require 'spec/dsl/composite_proc_builder' -require 'spec/dsl/example' -require 'spec/dsl/example_matcher' -require 'spec/dsl/example_should_raise_handler' -require 'spec/dsl/behaviour_factory' diff --git a/spec/lib/spec/dsl/behaviour.rb b/spec/lib/spec/dsl/behaviour.rb deleted file mode 100644 index cc71ccffe..000000000 --- a/spec/lib/spec/dsl/behaviour.rb +++ /dev/null @@ -1,221 +0,0 @@ -require(File.expand_path(File.dirname(__FILE__) + '../../../../../test/lib/puppettest/runnable_test.rb')) - -module Spec - module DSL - class Behaviour - extend BehaviourCallbacks - - class << self - def add_shared_behaviour(behaviour) - return if behaviour.equal?(found_behaviour = find_shared_behaviour(behaviour.description)) - return if found_behaviour and File.expand_path(behaviour.description[:spec_path]) == File.expand_path(found_behaviour.description[:spec_path]) - raise ArgumentError.new("Shared Behaviour '#{behaviour.description}' already exists") if found_behaviour - shared_behaviours << behaviour - end - - def find_shared_behaviour(behaviour_description) - shared_behaviours.find { |b| b.description == behaviour_description } - end - - def shared_behaviours - # TODO - this needs to be global, or at least accessible from - # from subclasses of Behaviour in a centralized place. I'm not loving - # this as a solution, but it works for now. - $shared_behaviours ||= [] - end - end - - def initialize(*args, &behaviour_block) - init_description(*args) - init_eval_module - before_eval - eval_behaviour(&behaviour_block) - end - - private - - def init_description(*args) - unless self.class == Behaviour - args << {} unless Hash === args.last - args.last[:behaviour_class] = self.class - end - @description = Description.new(*args) - end - - def init_eval_module - @eval_module = EvalModule.new - @eval_module.extend BehaviourEval::ModuleMethods - @eval_module.include BehaviourEval::InstanceMethods - @eval_module.include described_type if described_type.class == Module - @eval_module.behaviour = self - @eval_module.description = @description - end - - def eval_behaviour(&behaviour_block) - @eval_module.class_eval(&behaviour_block) - end - - protected - - def before_eval - end - - public - - def run(reporter, dry_run=false, reverse=false, timeout=nil) - raise "shared behaviours should never run" if shared? - # TODO - change add_behaviour to add_description ?????? - reporter.add_behaviour(@description) - prepare_execution_context_class - before_all_errors = run_before_all(reporter, dry_run) - - exs = reverse ? examples.reverse : examples - example_execution_context = nil - - if before_all_errors.empty? - exs.each do |example| - example_execution_context = execution_context(example) - example_execution_context.copy_instance_variables_from(@before_and_after_all_context_instance) unless before_all_proc(behaviour_type).nil? - - befores = before_each_proc(behaviour_type) {|e| raise e} - afters = after_each_proc(behaviour_type) - example.run(reporter, befores, afters, dry_run, example_execution_context, timeout) - end - end - - @before_and_after_all_context_instance.copy_instance_variables_from(example_execution_context) unless after_all_proc(behaviour_type).nil? - run_after_all(reporter, dry_run) - end - - def number_of_examples - examples.length - end - - def matches?(specified_examples) - matcher ||= ExampleMatcher.new(description) - - examples.each do |example| - return true if example.matches?(matcher, specified_examples) - end - return false - end - - def shared? - @description[:shared] - end - - def retain_examples_matching!(specified_examples) - return if specified_examples.index(description) - matcher = ExampleMatcher.new(description) - examples.reject! do |example| - !example.matches?(matcher, specified_examples) - end - end - - def methods - my_methods = super - my_methods |= @eval_module.methods - my_methods - end - - # Includes modules in the Behaviour (the <tt>describe</tt> block). - def include(*args) - @eval_module.include(*args) - end - - def behaviour_type #:nodoc: - @description[:behaviour_type] - end - - # Sets the #number on each Example and returns the next number - def set_sequence_numbers(number, reverse) #:nodoc: - exs = reverse ? examples.reverse : examples - exs.each do |example| - example.number = number - number += 1 - end - number - end - - protected - - # Messages that this class does not understand - # are passed directly to the @eval_module. - def method_missing(sym, *args, &block) - @eval_module.send(sym, *args, &block) - end - - def prepare_execution_context_class - plugin_mock_framework - weave_in_included_modules - define_predicate_matchers #this is in behaviour_eval - execution_context_class - end - - def weave_in_included_modules - mods = [@eval_module] - mods << included_modules.dup - mods << Spec::Runner.configuration.modules_for(behaviour_type) - execution_context_class.class_eval do - # WARNING - the following can be executed in the context of any - # class, and should never pass more than one module to include - # even though we redefine include in this class. This is NOT - # tested anywhere, hence this comment. - mods.flatten.each {|mod| include mod} - end - end - - def execution_context(example) - execution_context_class.new(example) - end - - def run_before_all(reporter, dry_run) - errors = [] - unless dry_run - begin - @before_and_after_all_context_instance = execution_context(nil) - @before_and_after_all_context_instance.instance_eval(&before_all_proc(behaviour_type)) - rescue Exception => e - errors << e - location = "before(:all)" - # The easiest is to report this as an example failure. We don't have an Example - # at this point, so we'll just create a placeholder. - reporter.example_finished(Example.new(location), e, location) if reporter - end - end - errors - end - - def run_after_all(reporter, dry_run) - unless dry_run - begin - @before_and_after_all_context_instance ||= execution_context(nil) - @before_and_after_all_context_instance.instance_eval(&after_all_proc(behaviour_type)) - rescue Exception => e - location = "after(:all)" - reporter.example_finished(Example.new(location), e, location) if reporter - end - end - end - - def plugin_mock_framework - case mock_framework = Spec::Runner.configuration.mock_framework - when Module - include mock_framework - else - require Spec::Runner.configuration.mock_framework - include Spec::Plugins::MockFramework - end - end - - def description - @description.to_s - end - - def described_type - @description.described_type - end - - end - end -end diff --git a/spec/lib/spec/dsl/behaviour_callbacks.rb b/spec/lib/spec/dsl/behaviour_callbacks.rb deleted file mode 100644 index 8b69ad9e5..000000000 --- a/spec/lib/spec/dsl/behaviour_callbacks.rb +++ /dev/null @@ -1,82 +0,0 @@ -module Spec - module DSL - # See http://rspec.rubyforge.org/documentation/before_and_after.html - module BehaviourCallbacks - def prepend_before(*args, &block) - scope, options = scope_and_options(*args) - add(scope, options, :before, :unshift, &block) - end - def append_before(*args, &block) - scope, options = scope_and_options(*args) - add(scope, options, :before, :<<, &block) - end - alias_method :before, :append_before - - def prepend_after(*args, &block) - scope, options = scope_and_options(*args) - add(scope, options, :after, :unshift, &block) - end - alias_method :after, :prepend_after - def append_after(*args, &block) - scope, options = scope_and_options(*args) - add(scope, options, :after, :<<, &block) - end - - def scope_and_options(*args) - args, options = args_and_options(*args) - scope = (args[0] || :each), options - end - - def add(scope, options, where, how, &block) - scope ||= :each - options ||= {} - behaviour_type = options[:behaviour_type] - case scope - when :each; self.__send__("#{where}_each_parts", behaviour_type).__send__(how, block) - when :all; self.__send__("#{where}_all_parts", behaviour_type).__send__(how, block) - end - end - - def remove_after(scope, &block) - after_each_parts.delete(block) - end - - # Deprecated. Use before(:each) - def setup(&block) - before(:each, &block) - end - - # Deprecated. Use after(:each) - def teardown(&block) - after(:each, &block) - end - - def before_all_parts(behaviour_type=nil) # :nodoc: - @before_all_parts ||= {} - @before_all_parts[behaviour_type] ||= [] - end - - def after_all_parts(behaviour_type=nil) # :nodoc: - @after_all_parts ||= {} - @after_all_parts[behaviour_type] ||= [] - end - - def before_each_parts(behaviour_type=nil) # :nodoc: - @before_each_parts ||= {} - @before_each_parts[behaviour_type] ||= [] - end - - def after_each_parts(behaviour_type=nil) # :nodoc: - @after_each_parts ||= {} - @after_each_parts[behaviour_type] ||= [] - end - - def clear_before_and_after! # :nodoc: - @before_all_parts = nil - @after_all_parts = nil - @before_each_parts = nil - @after_each_parts = nil - end - end - end -end diff --git a/spec/lib/spec/dsl/behaviour_eval.rb b/spec/lib/spec/dsl/behaviour_eval.rb deleted file mode 100644 index 9f7b8281e..000000000 --- a/spec/lib/spec/dsl/behaviour_eval.rb +++ /dev/null @@ -1,231 +0,0 @@ -module Spec - module DSL - module BehaviourEval - module ModuleMethods - include BehaviourCallbacks - - attr_writer :behaviour - attr_accessor :description - - # RSpec runs every example in a new instance of Object, mixing in - # the behaviour necessary to run examples. Because this behaviour gets - # mixed in, it can get mixed in to an instance of any class at all. - # - # This is something that you would hardly ever use, but there is one - # common use case for it - inheriting from Test::Unit::TestCase. RSpec's - # Rails plugin uses this feature to provide access to all of the features - # that are available for Test::Unit within RSpec examples. - def inherit(klass) - raise ArgumentError.new("Shared behaviours cannot inherit from classes") if @behaviour.shared? - @behaviour_superclass = klass - derive_execution_context_class_from_behaviour_superclass - end - - # You can pass this one or many modules. Each module will subsequently - # be included in the each object in which an example is run. Use this - # to provide global helper methods to your examples. - # - # == Example - # - # module HelperMethods - # def helper_method - # ... - # end - # end - # - # describe Thing do - # include HelperMethods - # it "should do stuff" do - # helper_method - # end - # end - def include(*mods) - mods.each do |mod| - included_modules << mod - mod.send :included, self - end - end - - # Use this to pull in examples from shared behaviours. - # See Spec::Runner for information about shared behaviours. - def it_should_behave_like(behaviour_description) - behaviour = @behaviour.class.find_shared_behaviour(behaviour_description) - if behaviour.nil? - raise RuntimeError.new("Shared Behaviour '#{behaviour_description}' can not be found") - end - behaviour.copy_to(self) - end - - def copy_to(eval_module) # :nodoc: - examples.each { |e| eval_module.examples << e; } - before_each_parts.each { |p| eval_module.before_each_parts << p } - after_each_parts.each { |p| eval_module.after_each_parts << p } - before_all_parts.each { |p| eval_module.before_all_parts << p } - after_all_parts.each { |p| eval_module.after_all_parts << p } - included_modules.each { |m| eval_module.included_modules << m } - eval_module.included_modules << self - end - - # :call-seq: - # predicate_matchers[matcher_name] = method_on_object - # predicate_matchers[matcher_name] = [method1_on_object, method2_on_object] - # - # Dynamically generates a custom matcher that will match - # a predicate on your class. RSpec provides a couple of these - # out of the box: - # - # exist (or state expectations) - # File.should exist("path/to/file") - # - # an_instance_of (for mock argument constraints) - # mock.should_receive(:message).with(an_instance_of(String)) - # - # == Examples - # - # class Fish - # def can_swim? - # true - # end - # end - # - # describe Fish do - # predicate_matchers[:swim] = :can_swim? - # it "should swim" do - # Fish.new.should swim - # end - # end - def predicate_matchers - @predicate_matchers ||= {:exist => :exist?, :an_instance_of => :is_a?} - end - - def define_predicate_matchers(hash=nil) # :nodoc: - if hash.nil? - define_predicate_matchers(predicate_matchers) - define_predicate_matchers(Spec::Runner.configuration.predicate_matchers) - else - hash.each_pair do |matcher_method, method_on_object| - define_method matcher_method do |*args| - eval("be_#{method_on_object.to_s.gsub('?','')}(*args)") - end - end - end - end - - # Creates an instance of Spec::DSL::Example and adds - # it to a collection of examples of the current behaviour. - def it(description=:__generate_description, opts={}, &block) - examples << Example.new(description, opts, &block) - end - - # Alias for it. - def specify(description=:__generate_description, opts={}, &block) - it(description, opts, &block) - end - - def methods # :nodoc: - my_methods = super - my_methods |= behaviour_superclass.methods - my_methods - end - - protected - - def method_missing(method_name, *args) - if behaviour_superclass.respond_to?(method_name) - return execution_context_class.send(method_name, *args) - end - super - end - - def before_each_proc(behaviour_type, &error_handler) - parts = [] - parts.push(*Behaviour.before_each_parts(nil)) - parts.push(*Behaviour.before_each_parts(behaviour_type)) unless behaviour_type.nil? - parts.push(*before_each_parts(nil)) - parts.push(*before_each_parts(behaviour_type)) unless behaviour_type.nil? - CompositeProcBuilder.new(parts).proc(&error_handler) - end - - def before_all_proc(behaviour_type, &error_handler) - parts = [] - parts.push(*Behaviour.before_all_parts(nil)) - parts.push(*Behaviour.before_all_parts(behaviour_type)) unless behaviour_type.nil? - parts.push(*before_all_parts(nil)) - parts.push(*before_all_parts(behaviour_type)) unless behaviour_type.nil? - CompositeProcBuilder.new(parts).proc(&error_handler) - end - - def after_all_proc(behaviour_type) - parts = [] - parts.push(*after_all_parts(behaviour_type)) unless behaviour_type.nil? - parts.push(*after_all_parts(nil)) - parts.push(*Behaviour.after_all_parts(behaviour_type)) unless behaviour_type.nil? - parts.push(*Behaviour.after_all_parts(nil)) - CompositeProcBuilder.new(parts).proc - end - - def after_each_proc(behaviour_type) - parts = [] - parts.push(*after_each_parts(behaviour_type)) unless behaviour_type.nil? - parts.push(*after_each_parts(nil)) - parts.push(*Behaviour.after_each_parts(behaviour_type)) unless behaviour_type.nil? - parts.push(*Behaviour.after_each_parts(nil)) - CompositeProcBuilder.new(parts).proc - end - - private - - def execution_context_class - @execution_context_class ||= derive_execution_context_class_from_behaviour_superclass - end - - def derive_execution_context_class_from_behaviour_superclass - @execution_context_class = Class.new(behaviour_superclass) - behaviour_superclass.spec_inherited(self) if behaviour_superclass.respond_to?(:spec_inherited) - @execution_context_class - end - - def behaviour_superclass - @behaviour_superclass ||= Object - end - - protected - def included_modules - @included_modules ||= [::Spec::Matchers] - end - - def examples - @examples ||= [] - end - end - - module InstanceMethods - def initialize(*args, &block) #:nodoc: - # TODO - inheriting from TestUnit::TestCase fails without this - # - let's figure out why and move this somewhere else - end - - def violated(message="") - raise Spec::Expectations::ExpectationNotMetError.new(message) - end - - def inspect - "[RSpec example]" - end - - def pending(message) - if block_given? - begin - yield - rescue Exception => e - raise Spec::DSL::ExamplePendingError.new(message) - end - raise Spec::DSL::PendingFixedError.new("Expected pending '#{message}' to fail. No Error was raised.") - else - raise Spec::DSL::ExamplePendingError.new(message) - end - end - end - end - end -end diff --git a/spec/lib/spec/dsl/behaviour_factory.rb b/spec/lib/spec/dsl/behaviour_factory.rb deleted file mode 100755 index 44b60c641..000000000 --- a/spec/lib/spec/dsl/behaviour_factory.rb +++ /dev/null @@ -1,42 +0,0 @@ -module Spec - module DSL - class BehaviourFactory - - class << self - - BEHAVIOUR_CLASSES = {:default => Spec::DSL::Behaviour} - - # Registers a behaviour class +klass+ with the symbol - # +behaviour_type+. For example: - # - # Spec::DSL::BehaviourFactory.add_behaviour_class(:farm, Spec::Farm::DSL::FarmBehaviour) - # - # This will cause Kernel#describe from a file living in - # <tt>spec/farm</tt> to create behaviour instances of type - # Spec::Farm::DSL::FarmBehaviour. - def add_behaviour_class(behaviour_type, klass) - BEHAVIOUR_CLASSES[behaviour_type] = klass - end - - def remove_behaviour_class(behaviour_type) - BEHAVIOUR_CLASSES.delete(behaviour_type) - end - - def create(*args, &block) - opts = Hash === args.last ? args.last : {} - if opts[:shared] - behaviour_type = :default - elsif opts[:behaviour_type] - behaviour_type = opts[:behaviour_type] - elsif opts[:spec_path] =~ /spec(\\|\/)(#{BEHAVIOUR_CLASSES.keys.join('|')})/ - behaviour_type = $2.to_sym - else - behaviour_type = :default - end - return BEHAVIOUR_CLASSES[behaviour_type].new(*args, &block) - end - - end - end - end -end diff --git a/spec/lib/spec/dsl/composite_proc_builder.rb b/spec/lib/spec/dsl/composite_proc_builder.rb deleted file mode 100644 index 373f44953..000000000 --- a/spec/lib/spec/dsl/composite_proc_builder.rb +++ /dev/null @@ -1,33 +0,0 @@ -module Spec - module DSL - class CompositeProcBuilder < Array - def initialize(callbacks=[]) - push(*callbacks) - end - - def proc(&error_handler) - parts = self - errors = [] - Proc.new do - result = parts.collect do |part| - begin - if part.is_a?(UnboundMethod) - part.bind(self).call - else - instance_eval(&part) - end - rescue Exception => e - if error_handler - error_handler.call(e) - else - errors << e - end - end - end - raise errors.first unless errors.empty? - result - end - end - end - end -end diff --git a/spec/lib/spec/dsl/configuration.rb b/spec/lib/spec/dsl/configuration.rb deleted file mode 100755 index 709574ded..000000000 --- a/spec/lib/spec/dsl/configuration.rb +++ /dev/null @@ -1,135 +0,0 @@ -module Spec - module DSL - class Configuration - - # Chooses what mock framework to use. Example: - # - # Spec::Runner.configure do |config| - # config.mock_with :rspec, :mocha, :flexmock, or :rr - # end - # - # To use any other mock framework, you'll have to provide - # your own adapter. This is simply a module that responds to - # setup_mocks_for_rspec, verify_mocks_for_rspec and teardown_mocks_for_rspec. - # These are your hooks into the lifecycle of a given example. RSpec will - # call setup_mocks_for_rspec before running anything else in each Example. - # After executing the #after methods, RSpec will then call verify_mocks_for_rspec - # and teardown_mocks_for_rspec (this is guaranteed to run even if there are - # failures in verify_mocks_for_rspec). - # - # Once you've defined this module, you can pass that to mock_with: - # - # Spec::Runner.configure do |config| - # config.mock_with MyMockFrameworkAdapter - # end - # - def mock_with(mock_framework) - @mock_framework = case mock_framework - when Symbol - mock_framework_path(mock_framework.to_s) - else - mock_framework - end - end - - def mock_framework # :nodoc: - @mock_framework ||= mock_framework_path("rspec") - end - - # Declares modules to be included in all behaviours (<tt>describe</tt> blocks). - # - # config.include(My::Bottle, My::Cup) - # - # If you want to restrict the inclusion to a subset of all the behaviours then - # specify this in a Hash as the last argument: - # - # config.include(My::Pony, My::Horse, :behaviour_type => :farm) - # - # Only behaviours that have that type will get the modules included: - # - # describe "Downtown", :behaviour_type => :city do - # # Will *not* get My::Pony and My::Horse included - # end - # - # describe "Old Mac Donald", :behaviour_type => :farm do - # # *Will* get My::Pony and My::Horse included - # end - # - def include(*args) - args << {} unless Hash === args.last - modules, options = args_and_options(*args) - required_behaviour_type = options[:behaviour_type] - required_behaviour_type = required_behaviour_type.to_sym unless required_behaviour_type.nil? - @modules ||= {} - @modules[required_behaviour_type] ||= [] - @modules[required_behaviour_type] += modules - end - - def modules_for(required_behaviour_type) #:nodoc: - @modules ||= {} - modules = @modules[nil] || [] # general ones - modules << @modules[required_behaviour_type.to_sym] unless required_behaviour_type.nil? - modules.uniq.compact - end - - # This is just for cleanup in RSpec's own examples - def exclude(*modules) #:nodoc: - @modules.each do |behaviour_type, mods| - modules.each{|m| mods.delete(m)} - end - end - - # Defines global predicate matchers. Example: - # - # config.predicate_matchers[:swim] = :can_swim? - # - # This makes it possible to say: - # - # person.should swim # passes if person.should_swim? returns true - # - def predicate_matchers - @predicate_matchers ||= {} - end - - # Prepends a global <tt>before</tt> block to all behaviours. - # See #append_before for filtering semantics. - def prepend_before(*args, &proc) - Behaviour.prepend_before(*args, &proc) - end - # Appends a global <tt>before</tt> block to all behaviours. - # - # If you want to restrict the block to a subset of all the behaviours then - # specify this in a Hash as the last argument: - # - # config.prepend_before(:all, :behaviour_type => :farm) - # - # or - # - # config.prepend_before(:behaviour_type => :farm) - # - def append_before(*args, &proc) - Behaviour.append_before(*args, &proc) - end - alias_method :before, :append_before - - # Prepends a global <tt>after</tt> block to all behaviours. - # See #append_before for filtering semantics. - def prepend_after(*args, &proc) - Behaviour.prepend_after(*args, &proc) - end - alias_method :after, :prepend_after - # Appends a global <tt>after</tt> block to all behaviours. - # See #append_before for filtering semantics. - def append_after(*args, &proc) - Behaviour.append_after(*args, &proc) - end - - private - - def mock_framework_path(framework_name) - File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "plugins", "mock_frameworks", framework_name)) - end - - end - end -end diff --git a/spec/lib/spec/dsl/description.rb b/spec/lib/spec/dsl/description.rb deleted file mode 100755 index fe8c9b0c9..000000000 --- a/spec/lib/spec/dsl/description.rb +++ /dev/null @@ -1,76 +0,0 @@ -module Spec - module DSL - class Description - module ClassMethods - def generate_description(*args) - description = args.shift.to_s - unless args.empty? - suffix = args.shift.to_s - description << " " unless suffix =~ /^\s|\.|#/ - description << suffix - end - description - end - end - extend ClassMethods - - attr_reader :description, :described_type - - def initialize(*args) - args, @options = args_and_options(*args) - init_behaviour_type(@options) - init_spec_path(@options) - init_described_type(args) - init_description(*args) - end - - def [](key) - @options[key] - end - - def []=(key, value) - @options[key] = value - end - - def to_s; @description; end - - def ==(value) - case value - when Description - @description == value.description - else - @description == value - end - end - - private - def init_behaviour_type(options) - # NOTE - BE CAREFUL IF CHANGING THIS NEXT LINE: - # this line is as it is to satisfy JRuby - the original version - # read, simply: "if options[:behaviour_class]", which passed against ruby, but failed against jruby - if options[:behaviour_class] && options[:behaviour_class].ancestors.include?(Behaviour) - options[:behaviour_type] = parse_behaviour_type(@options[:behaviour_class]) - end - end - - def init_spec_path(options) - if options.has_key?(:spec_path) - options[:spec_path] = File.expand_path(@options[:spec_path]) - end - end - - def init_description(*args) - @description = self.class.generate_description(*args) - end - - def init_described_type(args) - @described_type = args.first unless args.first.is_a?(String) - end - - def parse_behaviour_type(behaviour_class) - behaviour_class.to_s.split("::").reverse[0].gsub!('Behaviour', '').downcase.to_sym - end - - end - end -end diff --git a/spec/lib/spec/dsl/errors.rb b/spec/lib/spec/dsl/errors.rb deleted file mode 100644 index ba7046a89..000000000 --- a/spec/lib/spec/dsl/errors.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Spec - module DSL - class ExamplePendingError < StandardError - end - - class PendingFixedError < StandardError - end - end -end diff --git a/spec/lib/spec/dsl/example.rb b/spec/lib/spec/dsl/example.rb deleted file mode 100644 index d04073f7e..000000000 --- a/spec/lib/spec/dsl/example.rb +++ /dev/null @@ -1,135 +0,0 @@ -require 'timeout' - -module Spec - module DSL - class Example - # The global sequence number of this example - attr_accessor :number - - def initialize(description, options={}, &example_block) - @from = caller(0)[3] - @options = options - @example_block = example_block - @description = description - @description_generated_proc = lambda { |desc| @generated_description = desc } - end - - def run(reporter, before_each_block, after_each_block, dry_run, execution_context, timeout=nil) - @dry_run = dry_run - reporter.example_started(self) - return reporter.example_finished(self) if dry_run - - errors = [] - location = nil - Timeout.timeout(timeout) do - before_each_ok = before_example(execution_context, errors, &before_each_block) - example_ok = run_example(execution_context, errors) if before_each_ok - after_each_ok = after_example(execution_context, errors, &after_each_block) - location = failure_location(before_each_ok, example_ok, after_each_ok) - end - - ExampleShouldRaiseHandler.new(@from, @options).handle(errors) - reporter.example_finished(self, errors.first, location, @example_block.nil?) if reporter - end - - def matches?(matcher, specified_examples) - matcher.example_desc = description - matcher.matches?(specified_examples) - end - - def description - @description == :__generate_description ? generated_description : @description - end - - def to_s - description - end - - private - - def generated_description - return @generated_description if @generated_description - if @dry_run - "NO NAME (Because of --dry-run)" - else - if @failed - "NO NAME (Because of Error raised in matcher)" - else - "NO NAME (Because there were no expectations)" - end - end - end - - def before_example(execution_context, errors, &behaviour_before_block) - setup_mocks(execution_context) - Spec::Matchers.description_generated(@description_generated_proc) - - builder = CompositeProcBuilder.new - before_proc = builder.proc(&append_errors(errors)) - execution_context.instance_eval(&before_proc) - - execution_context.instance_eval(&behaviour_before_block) if behaviour_before_block - return errors.empty? - rescue Exception => e - @failed = true - errors << e - return false - end - - def run_example(execution_context, errors) - begin - execution_context.instance_eval(&@example_block) if @example_block - return true - rescue Exception => e - @failed = true - errors << e - return false - end - end - - def after_example(execution_context, errors, &behaviour_after_each) - execution_context.instance_eval(&behaviour_after_each) if behaviour_after_each - - begin - verify_mocks(execution_context) - ensure - teardown_mocks(execution_context) - end - - Spec::Matchers.unregister_description_generated(@description_generated_proc) - - builder = CompositeProcBuilder.new - after_proc = builder.proc(&append_errors(errors)) - execution_context.instance_eval(&after_proc) - - return errors.empty? - rescue Exception => e - @failed = true - errors << e - return false - end - - def setup_mocks(execution_context) - execution_context.setup_mocks_for_rspec if execution_context.respond_to?(:setup_mocks_for_rspec) - end - - def verify_mocks(execution_context) - execution_context.verify_mocks_for_rspec if execution_context.respond_to?(:verify_mocks_for_rspec) - end - - def teardown_mocks(execution_context) - execution_context.teardown_mocks_for_rspec if execution_context.respond_to?(:teardown_mocks_for_rspec) - end - - def append_errors(errors) - proc {|error| errors << error} - end - - def failure_location(before_each_ok, example_ok, after_each_ok) - return 'before(:each)' unless before_each_ok - return description unless example_ok - return 'after(:each)' unless after_each_ok - end - end - end -end diff --git a/spec/lib/spec/dsl/example_matcher.rb b/spec/lib/spec/dsl/example_matcher.rb deleted file mode 100755 index 18cc47409..000000000 --- a/spec/lib/spec/dsl/example_matcher.rb +++ /dev/null @@ -1,40 +0,0 @@ -module Spec - module DSL - class ExampleMatcher - - attr_writer :example_desc - def initialize(behaviour_desc, example_desc=nil) - @behaviour_desc = behaviour_desc - @example_desc = example_desc - end - - def matches?(specified_examples) - specified_examples.each do |specified_example| - return true if matches_literal_example?(specified_example) || matches_example_not_considering_modules?(specified_example) - end - false - end - - private - def matches_literal_example?(specified_example) - specified_example =~ /(^#{context_regexp} #{example_regexp}$|^#{context_regexp}$|^#{example_regexp}$)/ - end - - def matches_example_not_considering_modules?(specified_example) - specified_example =~ /(^#{context_regexp_not_considering_modules} #{example_regexp}$|^#{context_regexp_not_considering_modules}$|^#{example_regexp}$)/ - end - - def context_regexp - Regexp.escape(@behaviour_desc) - end - - def context_regexp_not_considering_modules - Regexp.escape(@behaviour_desc.split('::').last) - end - - def example_regexp - Regexp.escape(@example_desc) - end - end - end -end diff --git a/spec/lib/spec/dsl/example_should_raise_handler.rb b/spec/lib/spec/dsl/example_should_raise_handler.rb deleted file mode 100644 index 942327317..000000000 --- a/spec/lib/spec/dsl/example_should_raise_handler.rb +++ /dev/null @@ -1,74 +0,0 @@ -module Spec - module DSL - class ExampleShouldRaiseHandler - def initialize(file_and_line_number, opts) - @file_and_line_number = file_and_line_number - @options = opts - @expected_error_class = determine_error_class(opts) - @expected_error_message = determine_error_message(opts) - end - - def determine_error_class(opts) - if candidate = opts[:should_raise] - if candidate.is_a?(Class) - return candidate - elsif candidate.is_a?(Array) - return candidate[0] - else - return Exception - end - end - end - - def determine_error_message(opts) - if candidate = opts[:should_raise] - if candidate.is_a?(Array) - return candidate[1] - end - end - return nil - end - - def build_message(exception=nil) - if @expected_error_message.nil? - message = "example block expected #{@expected_error_class.to_s}" - else - message = "example block expected #{@expected_error_class.new(@expected_error_message.to_s).inspect}" - end - message << " but raised #{exception.inspect}" if exception - message << " but nothing was raised" unless exception - message << "\n" - message << @file_and_line_number - end - - def error_matches?(error) - return false unless error.kind_of?(@expected_error_class) - unless @expected_error_message.nil? - if @expected_error_message.is_a?(Regexp) - return false unless error.message =~ @expected_error_message - else - return false unless error.message == @expected_error_message - end - end - return true - end - - def handle(errors) - if @expected_error_class - if errors.empty? - errors << Spec::Expectations::ExpectationNotMetError.new(build_message) - else - error_to_remove = errors.detect do |error| - error_matches?(error) - end - if error_to_remove.nil? - errors.insert(0,Spec::Expectations::ExpectationNotMetError.new(build_message(errors[0]))) - else - errors.delete(error_to_remove) - end - end - end - end - end - end -end diff --git a/spec/lib/spec/expectations.rb b/spec/lib/spec/expectations.rb deleted file mode 100644 index 65ea47425..000000000 --- a/spec/lib/spec/expectations.rb +++ /dev/null @@ -1,56 +0,0 @@ -require 'spec/matchers' -require 'spec/expectations/errors' -require 'spec/expectations/extensions' -require 'spec/expectations/handler' - -module Spec - - # Spec::Expectations lets you set expectations on your objects. - # - # result.should == 37 - # team.should have(11).players_on_the_field - # - # == How Expectations work. - # - # Spec::Expectations adds two methods to Object: - # - # should(matcher=nil) - # should_not(matcher=nil) - # - # Both methods take an optional Expression Matcher (See Spec::Matchers). - # - # When +should+ receives an Expression Matcher, it calls <tt>matches?(self)</tt>. If - # it returns +true+, the spec passes and execution continues. If it returns - # +false+, then the spec fails with the message returned by <tt>matcher.failure_message</tt>. - # - # Similarly, when +should_not+ receives a matcher, it calls <tt>matches?(self)</tt>. If - # it returns +false+, the spec passes and execution continues. If it returns - # +true+, then the spec fails with the message returned by <tt>matcher.negative_failure_message</tt>. - # - # RSpec ships with a standard set of useful matchers, and writing your own - # matchers is quite simple. See Spec::Matchers for details. - module Expectations - class << self - attr_accessor :differ - - # raises a Spec::Expectations::ExpectationNotMetError with message - # - # When a differ has been assigned and fail_with is passed - # <code>expected</code> and <code>target</code>, passes them - # to the differ to append a diff message to the failure message. - def fail_with(message, expected=nil, target=nil) # :nodoc: - if Array === message && message.length == 3 - message, expected, target = message[0], message[1], message[2] - end - unless (differ.nil? || expected.nil? || target.nil?) - if expected.is_a?(String) - message << "\nDiff:" << self.differ.diff_as_string(target.to_s, expected) - elsif !target.is_a?(Proc) - message << "\nDiff:" << self.differ.diff_as_object(target, expected) - end - end - Kernel::raise(Spec::Expectations::ExpectationNotMetError.new(message)) - end - end - end -end diff --git a/spec/lib/spec/expectations/differs/default.rb b/spec/lib/spec/expectations/differs/default.rb deleted file mode 100644 index 87e59b3a6..000000000 --- a/spec/lib/spec/expectations/differs/default.rb +++ /dev/null @@ -1,61 +0,0 @@ -begin - require 'rubygems' - require 'diff/lcs' #necessary due to loading bug on some machines - not sure why - DaC - require 'diff/lcs/hunk' -rescue LoadError ; raise "You must gem install diff-lcs to use diffing" ; end - -require 'pp' - -module Spec - module Expectations - module Differs - - # TODO add some rdoc - class Default - def initialize(format=:unified,context_lines=nil,colour=nil) - - context_lines ||= 3 - colour ||= false - - @format,@context_lines,@colour = format,context_lines,colour - end - - # This is snagged from diff/lcs/ldiff.rb (which is a commandline tool) - def diff_as_string(data_old, data_new) - data_old = data_old.split(/\n/).map! { |e| e.chomp } - data_new = data_new.split(/\n/).map! { |e| e.chomp } - output = "" - diffs = Diff::LCS.diff(data_old, data_new) - return output if diffs.empty? - oldhunk = hunk = nil - file_length_difference = 0 - diffs.each do |piece| - begin - hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @context_lines, - file_length_difference) - file_length_difference = hunk.file_length_difference - next unless oldhunk - # Hunks may overlap, which is why we need to be careful when our - # diff includes lines of context. Otherwise, we might print - # redundant lines. - if (@context_lines > 0) and hunk.overlaps?(oldhunk) - hunk.unshift(oldhunk) - else - output << oldhunk.diff(@format) - end - ensure - oldhunk = hunk - output << "\n" - end - end - #Handle the last remaining hunk - output << oldhunk.diff(@format) << "\n" - end - - def diff_as_object(target,expected) - diff_as_string(PP.pp(target,""), PP.pp(expected,"")) - end - end - end - end -end diff --git a/spec/lib/spec/expectations/errors.rb b/spec/lib/spec/expectations/errors.rb deleted file mode 100644 index 03e81a064..000000000 --- a/spec/lib/spec/expectations/errors.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Spec - module Expectations - class ExpectationNotMetError < StandardError - end - end -end diff --git a/spec/lib/spec/expectations/extensions.rb b/spec/lib/spec/expectations/extensions.rb deleted file mode 100644 index 60c9b9e7d..000000000 --- a/spec/lib/spec/expectations/extensions.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'spec/expectations/extensions/object' -require 'spec/expectations/extensions/string_and_symbol' diff --git a/spec/lib/spec/expectations/extensions/object.rb b/spec/lib/spec/expectations/extensions/object.rb deleted file mode 100644 index f59af722e..000000000 --- a/spec/lib/spec/expectations/extensions/object.rb +++ /dev/null @@ -1,66 +0,0 @@ -module Spec - module Expectations - # rspec adds #should and #should_not to every Object (and, - # implicitly, every Class). - module ObjectExpectations - - # :call-seq: - # should(matcher) - # should == expected - # should === expected - # should =~ expected - # - # receiver.should(matcher) - # => Passes if matcher.matches?(receiver) - # - # receiver.should == expected #any value - # => Passes if (receiver == expected) - # - # receiver.should === expected #any value - # => Passes if (receiver === expected) - # - # receiver.should =~ regexp - # => Passes if (receiver =~ regexp) - # - # See Spec::Matchers for more information about matchers - # - # == Warning - # - # NOTE that this does NOT support receiver.should != expected. - # Instead, use receiver.should_not == expected - def should(matcher=nil, &block) - return ExpectationMatcherHandler.handle_matcher(self, matcher, &block) if matcher - Spec::Matchers::PositiveOperatorMatcher.new(self) - end - - # :call-seq: - # should_not(matcher) - # should_not == expected - # should_not === expected - # should_not =~ expected - # - # receiver.should_not(matcher) - # => Passes unless matcher.matches?(receiver) - # - # receiver.should_not == expected - # => Passes unless (receiver == expected) - # - # receiver.should_not === expected - # => Passes unless (receiver === expected) - # - # receiver.should_not =~ regexp - # => Passes unless (receiver =~ regexp) - # - # See Spec::Matchers for more information about matchers - def should_not(matcher=nil, &block) - return NegativeExpectationMatcherHandler.handle_matcher(self, matcher, &block) if matcher - Spec::Matchers::NegativeOperatorMatcher.new(self) - end - - end - end -end - -class Object - include Spec::Expectations::ObjectExpectations -end diff --git a/spec/lib/spec/expectations/extensions/string_and_symbol.rb b/spec/lib/spec/expectations/extensions/string_and_symbol.rb deleted file mode 100644 index 29cfbddfa..000000000 --- a/spec/lib/spec/expectations/extensions/string_and_symbol.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Spec - module Expectations - module StringHelpers - def starts_with?(prefix) - to_s[0..(prefix.to_s.length - 1)] == prefix.to_s - end - end - end -end - -class String - include Spec::Expectations::StringHelpers -end - -class Symbol - include Spec::Expectations::StringHelpers -end diff --git a/spec/lib/spec/expectations/handler.rb b/spec/lib/spec/expectations/handler.rb deleted file mode 100644 index 4caa321e4..000000000 --- a/spec/lib/spec/expectations/handler.rb +++ /dev/null @@ -1,43 +0,0 @@ -module Spec - module Expectations - - module MatcherHandlerHelper - def describe(matcher) - matcher.respond_to?(:description) ? matcher.description : "[#{matcher.class.name} does not provide a description]" - end - end - - class ExpectationMatcherHandler - class << self - include MatcherHandlerHelper - def handle_matcher(actual, matcher, &block) - match = matcher.matches?(actual, &block) - ::Spec::Matchers.generated_description = "should #{describe(matcher)}" - Spec::Expectations.fail_with(matcher.failure_message) unless match - end - end - end - - class NegativeExpectationMatcherHandler - class << self - include MatcherHandlerHelper - def handle_matcher(actual, matcher, &block) - unless matcher.respond_to?(:negative_failure_message) - Spec::Expectations.fail_with( -<<-EOF -Matcher does not support should_not. -See Spec::Matchers for more information -about matchers. -EOF -) - end - match = matcher.matches?(actual, &block) - ::Spec::Matchers.generated_description = "should not #{describe(matcher)}" - Spec::Expectations.fail_with(matcher.negative_failure_message) if match - end - end - end - - end -end - diff --git a/spec/lib/spec/extensions.rb b/spec/lib/spec/extensions.rb deleted file mode 100755 index 824f03bfb..000000000 --- a/spec/lib/spec/extensions.rb +++ /dev/null @@ -1 +0,0 @@ -require 'spec/extensions/object' diff --git a/spec/lib/spec/extensions/object.rb b/spec/lib/spec/extensions/object.rb deleted file mode 100755 index 6218aa770..000000000 --- a/spec/lib/spec/extensions/object.rb +++ /dev/null @@ -1,6 +0,0 @@ -class Object - def args_and_options(*args) - options = Hash === args.last ? args.pop : {} - return args, options - end -end diff --git a/spec/lib/spec/matchers.rb b/spec/lib/spec/matchers.rb deleted file mode 100644 index fd208d628..000000000 --- a/spec/lib/spec/matchers.rb +++ /dev/null @@ -1,166 +0,0 @@ -require 'spec/matchers/be' -require 'spec/matchers/be_close' -require 'spec/matchers/change' -require 'spec/matchers/eql' -require 'spec/matchers/equal' -require 'spec/matchers/has' -require 'spec/matchers/have' -require 'spec/matchers/include' -require 'spec/matchers/match' -require 'spec/matchers/raise_error' -require 'spec/matchers/respond_to' -require 'spec/matchers/satisfy' -require 'spec/matchers/throw_symbol' -require 'spec/matchers/operator_matcher' - -module Spec - - # RSpec ships with a number of useful Expression Matchers. An Expression Matcher - # is any object that responds to the following methods: - # - # matches?(actual) - # failure_message - # negative_failure_message #optional - # description #optional - # - # See Spec::Expectations to learn how to use these as Expectation Matchers. - # See Spec::Mocks to learn how to use them as Mock Argument Constraints. - # - # == Predicates - # - # In addition to those Expression Matchers that are defined explicitly, RSpec will - # create custom Matchers on the fly for any arbitrary predicate, giving your specs - # a much more natural language feel. - # - # A Ruby predicate is a method that ends with a "?" and returns true or false. - # Common examples are +empty?+, +nil?+, and +instance_of?+. - # - # All you need to do is write +should be_+ followed by the predicate without - # the question mark, and RSpec will figure it out from there. For example: - # - # [].should be_empty => [].empty? #passes - # [].should_not be_empty => [].empty? #fails - # - # In addtion to prefixing the predicate matchers with "be_", you can also use "be_a_" - # and "be_an_", making your specs read much more naturally: - # - # "a string".should be_an_instance_of(String) =>"a string".instance_of?(String) #passes - # - # 3.should be_a_kind_of(Fixnum) => 3.kind_of?(Numeric) #passes - # 3.should be_a_kind_of(Numeric) => 3.kind_of?(Numeric) #passes - # 3.should be_an_instance_of(Fixnum) => 3.instance_of?(Fixnum) #passes - # 3.should_not be_instance_of(Numeric) => 3.instance_of?(Numeric) #fails - # - # RSpec will also create custom matchers for predicates like +has_key?+. To - # use this feature, just state that the object should have_key(:key) and RSpec will - # call has_key?(:key) on the target. For example: - # - # {:a => "A"}.should have_key(:a) => {:a => "A"}.has_key?(:a) #passes - # {:a => "A"}.should have_key(:b) => {:a => "A"}.has_key?(:b) #fails - # - # You can use this feature to invoke any predicate that begins with "has_", whether it is - # part of the Ruby libraries (like +Hash#has_key?+) or a method you wrote on your own class. - # - # == Custom Expectation Matchers - # - # When you find that none of the stock Expectation Matchers provide a natural - # feeling expectation, you can very easily write your own. - # - # For example, imagine that you are writing a game in which players can - # be in various zones on a virtual board. To specify that bob should - # be in zone 4, you could say: - # - # bob.current_zone.should eql(Zone.new("4")) - # - # But you might find it more expressive to say: - # - # bob.should be_in_zone("4") - # - # and/or - # - # bob.should_not be_in_zone("3") - # - # To do this, you would need to write a class like this: - # - # class BeInZone - # def initialize(expected) - # @expected = expected - # end - # def matches?(target) - # @target = target - # @target.current_zone.eql?(Zone.new(@expected)) - # end - # def failure_message - # "expected #{@target.inspect} to be in Zone #{@expected}" - # end - # def negative_failure_message - # "expected #{@target.inspect} not to be in Zone #{@expected}" - # end - # end - # - # ... and a method like this: - # - # def be_in_zone(expected) - # BeInZone.new(expected) - # end - # - # And then expose the method to your specs. This is normally done - # by including the method and the class in a module, which is then - # included in your spec: - # - # module CustomGameMatchers - # class BeInZone - # ... - # end - # - # def be_in_zone(expected) - # ... - # end - # end - # - # describe "Player behaviour" do - # include CustomGameMatchers - # ... - # end - # - # or you can include in globally in a spec_helper.rb file <tt>require</tt>d - # from your spec file(s): - # - # Spec::Runner.configure do |config| - # config.include(CustomGameMatchers) - # end - # - module Matchers - module ModuleMethods - def description_generated(callback) - description_generated_callbacks << callback - end - - def unregister_description_generated(callback) - description_generated_callbacks.delete(callback) - end - - def generated_description=(name) - description_generated_callbacks.each do |callback| - callback.call(name) - end - end - - private - def description_generated_callbacks - @description_generated_callbacks ||= [] - end - end - extend ModuleMethods - - def method_missing(sym, *args, &block) # :nodoc: - return Matchers::Be.new(sym, *args) if sym.starts_with?("be_") - return Matchers::Has.new(sym, *args) if sym.starts_with?("have_") - super - end - - class MatcherError < StandardError - end - - end -end diff --git a/spec/lib/spec/matchers/be.rb b/spec/lib/spec/matchers/be.rb deleted file mode 100644 index 0eb1629a6..000000000 --- a/spec/lib/spec/matchers/be.rb +++ /dev/null @@ -1,206 +0,0 @@ -module Spec - module Matchers - - class Be #:nodoc: - def initialize(*args) - @expected = parse_expected(args.shift) - @args = args - @comparison = "" - end - - def matches?(actual) - @actual = actual - return true if match_or_compare unless handling_predicate? - if handling_predicate? - begin - return @result = actual.__send__(predicate, *@args) - rescue => predicate_error - # This clause should be empty, but rcov will not report it as covered - # unless something (anything) is executed within the clause - rcov_error_report = "http://eigenclass.org/hiki.rb?rcov-0.8.0" - end - - # This supports should_exist > target.exists? in the old world. - # We should consider deprecating that ability as in the new world - # you can't write "should exist" unless you have your own custom matcher. - begin - return @result = actual.__send__(present_tense_predicate, *@args) - rescue - raise predicate_error - end - end - return false - end - - def failure_message - return "expected #{@comparison}#{expected}, got #{@actual.inspect}" unless handling_predicate? - return "expected #{predicate}#{args_to_s} to return true, got #{@result.inspect}" - end - - def negative_failure_message - return "expected not #{expected}, got #{@actual.inspect}" unless handling_predicate? - return "expected #{predicate}#{args_to_s} to return false, got #{@result.inspect}" - end - - def expected - return true if @expected == :true - return false if @expected == :false - return "nil" if @expected == :nil - return @expected.inspect - end - - def match_or_compare - return @actual == true if @expected == :true - return @actual == false if @expected == :false - return @actual.nil? if @expected == :nil - return @actual < @expected if @less_than - return @actual <= @expected if @less_than_or_equal - return @actual >= @expected if @greater_than_or_equal - return @actual > @expected if @greater_than - return @actual == @expected if @double_equal - return @actual === @expected if @triple_equal - return @actual.equal?(@expected) - end - - def ==(expected) - @double_equal = true - @comparison = "== " - @expected = expected - self - end - - def ===(expected) - @triple_equal = true - @comparison = "=== " - @expected = expected - self - end - - def <(expected) - @less_than = true - @comparison = "< " - @expected = expected - self - end - - def <=(expected) - @less_than_or_equal = true - @comparison = "<= " - @expected = expected - self - end - - def >=(expected) - @greater_than_or_equal = true - @comparison = ">= " - @expected = expected - self - end - - def >(expected) - @greater_than = true - @comparison = "> " - @expected = expected - self - end - - def description - "#{prefix_to_sentence}#{comparison}#{expected_to_sentence}#{args_to_sentence}" - end - - private - def parse_expected(expected) - if Symbol === expected - @handling_predicate = true - ["be_an_","be_a_","be_"].each do |@prefix| - return "#{expected.to_s.sub(@prefix,"")}".to_sym if expected.starts_with?(@prefix) - end - end - @prefix = "be " - return expected - end - - def handling_predicate? - return false if [:true, :false, :nil].include?(@expected) - return @handling_predicate - end - - def predicate - "#{@expected.to_s}?".to_sym - end - - def present_tense_predicate - "#{@expected.to_s}s?".to_sym - end - - def args_to_s - return "" if @args.empty? - inspected_args = @args.collect{|a| a.inspect} - return "(#{inspected_args.join(', ')})" - end - - def comparison - @comparison - end - - def expected_to_sentence - split_words(@expected) - end - - def prefix_to_sentence - split_words(@prefix) - end - - def split_words(sym) - sym.to_s.gsub(/_/,' ') - end - - def args_to_sentence - case @args.length - when 0 - "" - when 1 - " #{@args[0]}" - else - " #{@args[0...-1].join(', ')} and #{@args[-1]}" - end - end - - end - - # :call-seq: - # should be_true - # should be_false - # should be_nil - # should be_arbitrary_predicate(*args) - # should_not be_nil - # should_not be_arbitrary_predicate(*args) - # - # Given true, false, or nil, will pass if actual is - # true, false or nil (respectively). - # - # Predicates are any Ruby method that ends in a "?" and returns true or false. - # Given be_ followed by arbitrary_predicate (without the "?"), RSpec will match - # convert that into a query against the target object. - # - # The arbitrary_predicate feature will handle any predicate - # prefixed with "be_an_" (e.g. be_an_instance_of), "be_a_" (e.g. be_a_kind_of) - # or "be_" (e.g. be_empty), letting you choose the prefix that best suits the predicate. - # - # == Examples - # - # target.should be_true - # target.should be_false - # target.should be_nil - # target.should_not be_nil - # - # collection.should be_empty #passes if target.empty? - # "this string".should be_an_intance_of(String) - # - # target.should_not be_empty #passes unless target.empty? - # target.should_not be_old_enough(16) #passes unless target.old_enough?(16) - def be(*args) - Matchers::Be.new(*args) - end - end -end diff --git a/spec/lib/spec/matchers/be_close.rb b/spec/lib/spec/matchers/be_close.rb deleted file mode 100644 index 7763eb97e..000000000 --- a/spec/lib/spec/matchers/be_close.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Spec - module Matchers - - class BeClose #:nodoc: - def initialize(expected, delta) - @expected = expected - @delta = delta - end - - def matches?(actual) - @actual = actual - (@actual - @expected).abs < @delta - end - - def failure_message - "expected #{@expected} +/- (< #{@delta}), got #{@actual}" - end - - def description - "be close to #{@expected} (within +- #{@delta})" - end - end - - # :call-seq: - # should be_close(expected, delta) - # should_not be_close(expected, delta) - # - # Passes if actual == expected +/- delta - # - # == Example - # - # result.should be_close(3.0, 0.5) - def be_close(expected, delta) - Matchers::BeClose.new(expected, delta) - end - end -end diff --git a/spec/lib/spec/matchers/change.rb b/spec/lib/spec/matchers/change.rb deleted file mode 100644 index 41a718aca..000000000 --- a/spec/lib/spec/matchers/change.rb +++ /dev/null @@ -1,120 +0,0 @@ -module Spec - module Matchers - - #Based on patch from Wilson Bilkovich - class Change #:nodoc: - def initialize(receiver=nil, message=nil, &block) - @receiver = receiver - @message = message - @block = block - end - - def matches?(target, &block) - if block - raise MatcherError.new(<<-EOF -block passed to should or should_not change must use {} instead of do/end -EOF -) - end - @target = target - execute_change - return false if @from && (@from != @before) - return false if @to && (@to != @after) - return (@before + @amount == @after) if @amount - return @before != @after - end - - def execute_change - @before = @block.nil? ? @receiver.send(@message) : @block.call - @target.call - @after = @block.nil? ? @receiver.send(@message) : @block.call - end - - def failure_message - if @to - "#{result} should have been changed to #{@to.inspect}, but is now #{@after.inspect}" - elsif @from - "#{result} should have initially been #{@from.inspect}, but was #{@before.inspect}" - elsif @amount - "#{result} should have been changed by #{@amount.inspect}, but was changed by #{actual_delta.inspect}" - else - "#{result} should have changed, but is still #{@before.inspect}" - end - end - - def result - @message || "result" - end - - def actual_delta - @after - @before - end - - def negative_failure_message - "#{result} should not have changed, but did change from #{@before.inspect} to #{@after.inspect}" - end - - def by(amount) - @amount = amount - self - end - - def to(to) - @to = to - self - end - - def from (from) - @from = from - self - end - end - - # :call-seq: - # should change(receiver, message, &block) - # should change(receiver, message, &block).by(value) - # should change(receiver, message, &block).from(old).to(new) - # should_not change(receiver, message, &block) - # - # Allows you to specify that a Proc will cause some value to change. - # - # == Examples - # - # lambda { - # team.add_player(player) - # }.should change(roster, :count) - # - # lambda { - # team.add_player(player) - # }.should change(roster, :count).by(1) - # - # string = "string" - # lambda { - # string.reverse - # }.should change { string }.from("string").to("gnirts") - # - # lambda { - # person.happy_birthday - # }.should change(person, :birthday).from(32).to(33) - # - # lambda { - # employee.develop_great_new_social_networking_app - # }.should change(employee, :title).from("Mail Clerk").to("CEO") - # - # Evaluates +receiver.message+ or +block+ before and - # after it evaluates the c object (generated by the lambdas in the examples above). - # - # Then compares the values before and after the +receiver.message+ and - # evaluates the difference compared to the expected difference. - # - # == Warning - # +should_not+ +change+ only supports the form with no subsequent calls to - # +be+, +to+ or +from+. - # - # blocks passed to +should+ +change+ and +should_not+ +change+ - # must use the <tt>{}</tt> form (<tt>do/end</tt> is not supported) - def change(target=nil, message=nil, &block) - Matchers::Change.new(target, message, &block) - end - end -end diff --git a/spec/lib/spec/matchers/eql.rb b/spec/lib/spec/matchers/eql.rb deleted file mode 100644 index 280ca5454..000000000 --- a/spec/lib/spec/matchers/eql.rb +++ /dev/null @@ -1,43 +0,0 @@ -module Spec - module Matchers - - class Eql #:nodoc: - def initialize(expected) - @expected = expected - end - - def matches?(actual) - @actual = actual - @actual.eql?(@expected) - end - - def failure_message - return "expected #{@expected.inspect}, got #{@actual.inspect} (using .eql?)", @expected, @actual - end - - def negative_failure_message - return "expected #{@actual.inspect} not to equal #{@expected.inspect} (using .eql?)", @expected, @actual - end - - def description - "eql #{@expected.inspect}" - end - end - - # :call-seq: - # should eql(expected) - # should_not eql(expected) - # - # Passes if actual and expected are of equal value, but not necessarily the same object. - # - # See http://www.ruby-doc.org/core/classes/Object.html#M001057 for more information about equality in Ruby. - # - # == Examples - # - # 5.should eql(5) - # 5.should_not eql(3) - def eql(expected) - Matchers::Eql.new(expected) - end - end -end diff --git a/spec/lib/spec/matchers/equal.rb b/spec/lib/spec/matchers/equal.rb deleted file mode 100644 index 4bfc74951..000000000 --- a/spec/lib/spec/matchers/equal.rb +++ /dev/null @@ -1,43 +0,0 @@ -module Spec - module Matchers - - class Equal #:nodoc: - def initialize(expected) - @expected = expected - end - - def matches?(actual) - @actual = actual - @actual.equal?(@expected) - end - - def failure_message - return "expected #{@expected.inspect}, got #{@actual.inspect} (using .equal?)", @expected, @actual - end - - def negative_failure_message - return "expected #{@actual.inspect} not to equal #{@expected.inspect} (using .equal?)", @expected, @actual - end - - def description - "equal #{@expected.inspect}" - end - end - - # :call-seq: - # should equal(expected) - # should_not equal(expected) - # - # Passes if actual and expected are the same object (object identity). - # - # See http://www.ruby-doc.org/core/classes/Object.html#M001057 for more information about equality in Ruby. - # - # == Examples - # - # 5.should equal(5) #Fixnums are equal - # "5".should_not equal("5") #Strings that look the same are not the same object - def equal(expected) - Matchers::Equal.new(expected) - end - end -end diff --git a/spec/lib/spec/matchers/has.rb b/spec/lib/spec/matchers/has.rb deleted file mode 100644 index cc5a250b8..000000000 --- a/spec/lib/spec/matchers/has.rb +++ /dev/null @@ -1,44 +0,0 @@ -module Spec - module Matchers - - class Has #:nodoc: - def initialize(sym, *args) - @sym = sym - @args = args - end - - def matches?(target) - @target = target - begin - return target.send(predicate, *@args) - rescue => @error - # This clause should be empty, but rcov will not report it as covered - # unless something (anything) is executed within the clause - rcov_error_report = "http://eigenclass.org/hiki.rb?rcov-0.8.0" - end - return false - end - - def failure_message - raise @error if @error - "expected ##{predicate}(#{@args[0].inspect}) to return true, got false" - end - - def negative_failure_message - raise @error if @error - "expected ##{predicate}(#{@args[0].inspect}) to return false, got true" - end - - def description - "have key #{@args[0].inspect}" - end - - private - def predicate - "#{@sym.to_s.sub("have_","has_")}?".to_sym - end - - end - - end -end diff --git a/spec/lib/spec/matchers/have.rb b/spec/lib/spec/matchers/have.rb deleted file mode 100644 index f28b86ad3..000000000 --- a/spec/lib/spec/matchers/have.rb +++ /dev/null @@ -1,142 +0,0 @@ -module Spec - module Matchers - - class Have #:nodoc: - def initialize(expected, relativity=:exactly) - @expected = (expected == :no ? 0 : expected) - @relativity = relativity - end - - def relativities - @relativities ||= { - :exactly => "", - :at_least => "at least ", - :at_most => "at most " - } - end - - def method_missing(sym, *args, &block) - @collection_name = sym - @args = args - @block = block - self - end - - def matches?(collection_owner) - if collection_owner.respond_to?(@collection_name) - collection = collection_owner.send(@collection_name, *@args, &@block) - elsif (collection_owner.respond_to?(:length) || collection_owner.respond_to?(:size)) - collection = collection_owner - else - collection_owner.send(@collection_name, *@args, &@block) - end - @actual = collection.size if collection.respond_to?(:size) - @actual = collection.length if collection.respond_to?(:length) - raise not_a_collection if @actual.nil? - return @actual >= @expected if @relativity == :at_least - return @actual <= @expected if @relativity == :at_most - return @actual == @expected - end - - def not_a_collection - "expected #{@collection_name} to be a collection but it does not respond to #length or #size" - end - - def failure_message - "expected #{relative_expectation} #{@collection_name}, got #{@actual}" - end - - def negative_failure_message - if @relativity == :exactly - return "expected target not to have #{@expected} #{@collection_name}, got #{@actual}" - elsif @relativity == :at_most - return <<-EOF -Isn't life confusing enough? -Instead of having to figure out the meaning of this: - should_not have_at_most(#{@expected}).#{@collection_name} -We recommend that you use this instead: - should have_at_least(#{@expected + 1}).#{@collection_name} -EOF - elsif @relativity == :at_least - return <<-EOF -Isn't life confusing enough? -Instead of having to figure out the meaning of this: - should_not have_at_least(#{@expected}).#{@collection_name} -We recommend that you use this instead: - should have_at_most(#{@expected - 1}).#{@collection_name} -EOF - end - end - - def description - "have #{relative_expectation} #{@collection_name}" - end - - private - - def relative_expectation - "#{relativities[@relativity]}#{@expected}" - end - end - - # :call-seq: - # should have(number).named_collection__or__sugar - # should_not have(number).named_collection__or__sugar - # - # Passes if receiver is a collection with the submitted - # number of items OR if the receiver OWNS a collection - # with the submitted number of items. - # - # If the receiver OWNS the collection, you must use the name - # of the collection. So if a <tt>Team</tt> instance has a - # collection named <tt>#players</tt>, you must use that name - # to set the expectation. - # - # If the receiver IS the collection, you can use any name - # you like for <tt>named_collection</tt>. We'd recommend using - # either "elements", "members", or "items" as these are all - # standard ways of describing the things IN a collection. - # - # This also works for Strings, letting you set an expectation - # about its length - # - # == Examples - # - # # Passes if team.players.size == 11 - # team.should have(11).players - # - # # Passes if [1,2,3].length == 3 - # [1,2,3].should have(3).items #"items" is pure sugar - # - # # Passes if "this string".length == 11 - # "this string".should have(11).characters #"characters" is pure sugar - def have(n) - Matchers::Have.new(n) - end - alias :have_exactly :have - - # :call-seq: - # should have_at_least(number).items - # - # Exactly like have() with >=. - # - # == Warning - # - # +should_not+ +have_at_least+ is not supported - def have_at_least(n) - Matchers::Have.new(n, :at_least) - end - - # :call-seq: - # should have_at_most(number).items - # - # Exactly like have() with <=. - # - # == Warning - # - # +should_not+ +have_at_most+ is not supported - def have_at_most(n) - Matchers::Have.new(n, :at_most) - end - end -end diff --git a/spec/lib/spec/matchers/include.rb b/spec/lib/spec/matchers/include.rb deleted file mode 100644 index 5476f97d8..000000000 --- a/spec/lib/spec/matchers/include.rb +++ /dev/null @@ -1,70 +0,0 @@ -module Spec - module Matchers - - class Include #:nodoc: - - def initialize(*expecteds) - @expecteds = expecteds - end - - def matches?(actual) - @actual = actual - @expecteds.each do |expected| - return false unless actual.include?(expected) - end - true - end - - def failure_message - _message - end - - def negative_failure_message - _message("not ") - end - - def description - "include #{_pretty_print(@expecteds)}" - end - - private - def _message(maybe_not="") - "expected #{@actual.inspect} #{maybe_not}to include #{_pretty_print(@expecteds)}" - end - - def _pretty_print(array) - result = "" - array.each_with_index do |item, index| - if index < (array.length - 2) - result << "#{item.inspect}, " - elsif index < (array.length - 1) - result << "#{item.inspect} and " - else - result << "#{item.inspect}" - end - end - result - end - end - - # :call-seq: - # should include(expected) - # should_not include(expected) - # - # Passes if actual includes expected. This works for - # collections and Strings. You can also pass in multiple args - # and it will only pass if all args are found in collection. - # - # == Examples - # - # [1,2,3].should include(3) - # [1,2,3].should include(2,3) #would pass - # [1,2,3].should include(2,3,4) #would fail - # [1,2,3].should_not include(4) - # "spread".should include("read") - # "spread".should_not include("red") - def include(*expected) - Matchers::Include.new(*expected) - end - end -end diff --git a/spec/lib/spec/matchers/match.rb b/spec/lib/spec/matchers/match.rb deleted file mode 100644 index 61ab52429..000000000 --- a/spec/lib/spec/matchers/match.rb +++ /dev/null @@ -1,41 +0,0 @@ -module Spec - module Matchers - - class Match #:nodoc: - def initialize(expected) - @expected = expected - end - - def matches?(actual) - @actual = actual - return true if actual =~ @expected - return false - end - - def failure_message - return "expected #{@actual.inspect} to match #{@expected.inspect}", @expected, @actual - end - - def negative_failure_message - return "expected #{@actual.inspect} not to match #{@expected.inspect}", @expected, @actual - end - - def description - "match #{@expected.inspect}" - end - end - - # :call-seq: - # should match(regexp) - # should_not match(regexp) - # - # Given a Regexp, passes if actual =~ regexp - # - # == Examples - # - # email.should match(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i) - def match(regexp) - Matchers::Match.new(regexp) - end - end -end diff --git a/spec/lib/spec/matchers/operator_matcher.rb b/spec/lib/spec/matchers/operator_matcher.rb deleted file mode 100755 index 2d47ea85a..000000000 --- a/spec/lib/spec/matchers/operator_matcher.rb +++ /dev/null @@ -1,72 +0,0 @@ -module Spec - module Matchers - class BaseOperatorMatcher - - def initialize(target) - @target = target - end - - def ==(expected) - @expected = expected - __delegate_method_missing_to_target("==", expected) - end - - def ===(expected) - @expected = expected - __delegate_method_missing_to_target("===", expected) - end - - def =~(expected) - @expected = expected - __delegate_method_missing_to_target("=~", expected) - end - - def >(expected) - @expected = expected - __delegate_method_missing_to_target(">", expected) - end - - def >=(expected) - @expected = expected - __delegate_method_missing_to_target(">=", expected) - end - - def <(expected) - @expected = expected - __delegate_method_missing_to_target("<", expected) - end - - def <=(expected) - @expected = expected - __delegate_method_missing_to_target("<=", expected) - end - - def fail_with_message(message) - Spec::Expectations.fail_with(message, @expected, @target) - end - - end - - class PositiveOperatorMatcher < BaseOperatorMatcher #:nodoc: - - def __delegate_method_missing_to_target(operator, expected) - ::Spec::Matchers.generated_description = "should #{operator} #{expected.inspect}" - return if @target.send(operator, expected) - return fail_with_message("expected: #{expected.inspect},\n got: #{@target.inspect} (using #{operator})") if ['==','===', '=~'].include?(operator) - return fail_with_message("expected: #{operator} #{expected.inspect},\n got: #{operator.gsub(/./, ' ')} #{@target.inspect}") - end - - end - - class NegativeOperatorMatcher < BaseOperatorMatcher #:nodoc: - - def __delegate_method_missing_to_target(operator, expected) - ::Spec::Matchers.generated_description = "should not #{operator} #{expected.inspect}" - return unless @target.send(operator, expected) - return fail_with_message("expected not: #{operator} #{expected.inspect},\n got: #{operator.gsub(/./, ' ')} #{@target.inspect}") - end - - end - - end -end diff --git a/spec/lib/spec/matchers/raise_error.rb b/spec/lib/spec/matchers/raise_error.rb deleted file mode 100644 index b45dcf65c..000000000 --- a/spec/lib/spec/matchers/raise_error.rb +++ /dev/null @@ -1,105 +0,0 @@ -module Spec - module Matchers - - class RaiseError #:nodoc: - def initialize(error_or_message=Exception, message=nil) - if String === error_or_message - @expected_error = Exception - @expected_message = error_or_message - else - @expected_error = error_or_message - @expected_message = message - end - end - - def matches?(proc) - @raised_expected_error = false - @raised_other = false - begin - proc.call - rescue @expected_error => @actual_error - if @expected_message.nil? - @raised_expected_error = true - else - case @expected_message - when Regexp - if @expected_message =~ @actual_error.message - @raised_expected_error = true - else - @raised_other = true - end - else - if @expected_message == @actual_error.message - @raised_expected_error = true - else - @raised_other = true - end - end - end - rescue => @actual_error - @raised_other = true - ensure - return @raised_expected_error - end - end - - def failure_message - return "expected #{expected_error}#{actual_error}" if @raised_other || !@raised_expected_error - end - - def negative_failure_message - "expected no #{expected_error}#{actual_error}" - end - - def description - "raise #{expected_error}" - end - - private - def expected_error - case @expected_message - when nil - @expected_error - when Regexp - "#{@expected_error} with message matching #{@expected_message.inspect}" - else - "#{@expected_error} with #{@expected_message.inspect}" - end - end - - def actual_error - @actual_error.nil? ? " but nothing was raised" : ", got #{@actual_error.inspect}" - end - end - - # :call-seq: - # should raise_error() - # should raise_error(NamedError) - # should raise_error(NamedError, String) - # should raise_error(NamedError, Regexp) - # should_not raise_error() - # should_not raise_error(NamedError) - # should_not raise_error(NamedError, String) - # should_not raise_error(NamedError, Regexp) - # - # With no args, matches if any error is raised. - # With a named error, matches only if that specific error is raised. - # With a named error and messsage specified as a String, matches only if both match. - # With a named error and messsage specified as a Regexp, matches only if both match. - # - # == Examples - # - # lambda { do_something_risky }.should raise_error - # lambda { do_something_risky }.should raise_error(PoorRiskDecisionError) - # lambda { do_something_risky }.should raise_error(PoorRiskDecisionError, "that was too risky") - # lambda { do_something_risky }.should raise_error(PoorRiskDecisionError, /oo ri/) - # - # lambda { do_something_risky }.should_not raise_error - # lambda { do_something_risky }.should_not raise_error(PoorRiskDecisionError) - # lambda { do_something_risky }.should_not raise_error(PoorRiskDecisionError, "that was too risky") - # lambda { do_something_risky }.should_not raise_error(PoorRiskDecisionError, /oo ri/) - def raise_error(error=Exception, message=nil) - Matchers::RaiseError.new(error, message) - end - end -end diff --git a/spec/lib/spec/matchers/respond_to.rb b/spec/lib/spec/matchers/respond_to.rb deleted file mode 100644 index 3d23422aa..000000000 --- a/spec/lib/spec/matchers/respond_to.rb +++ /dev/null @@ -1,45 +0,0 @@ -module Spec - module Matchers - - class RespondTo #:nodoc: - def initialize(*names) - @names = names - @names_not_responded_to = [] - end - - def matches?(target) - @names.each do |name| - unless target.respond_to?(name) - @names_not_responded_to << name - end - end - return @names_not_responded_to.empty? - end - - def failure_message - "expected target to respond to #{@names_not_responded_to.collect {|name| name.inspect }.join(', ')}" - end - - def negative_failure_message - "expected target not to respond to #{@names.collect {|name| name.inspect }.join(', ')}" - end - - def description - "respond to ##{@names.to_s}" - end - end - - # :call-seq: - # should respond_to(*names) - # should_not respond_to(*names) - # - # Matches if the target object responds to all of the names - # provided. Names can be Strings or Symbols. - # - # == Examples - # - def respond_to(*names) - Matchers::RespondTo.new(*names) - end - end -end diff --git a/spec/lib/spec/matchers/satisfy.rb b/spec/lib/spec/matchers/satisfy.rb deleted file mode 100644 index 6c0ca95bc..000000000 --- a/spec/lib/spec/matchers/satisfy.rb +++ /dev/null @@ -1,47 +0,0 @@ -module Spec - module Matchers - - class Satisfy #:nodoc: - def initialize(&block) - @block = block - end - - def matches?(actual, &block) - @block = block if block - @actual = actual - @block.call(actual) - end - - def failure_message - "expected #{@actual} to satisfy block" - end - - def negative_failure_message - "expected #{@actual} not to satisfy block" - end - end - - # :call-seq: - # should satisfy {} - # should_not satisfy {} - # - # Passes if the submitted block returns true. Yields target to the - # block. - # - # Generally speaking, this should be thought of as a last resort when - # you can't find any other way to specify the behaviour you wish to - # specify. - # - # If you do find yourself in such a situation, you could always write - # a custom matcher, which would likely make your specs more expressive. - # - # == Examples - # - # 5.should satisfy { |n| - # n > 3 - # } - def satisfy(&block) - Matchers::Satisfy.new(&block) - end - end -end diff --git a/spec/lib/spec/matchers/throw_symbol.rb b/spec/lib/spec/matchers/throw_symbol.rb deleted file mode 100644 index 6d047bc39..000000000 --- a/spec/lib/spec/matchers/throw_symbol.rb +++ /dev/null @@ -1,72 +0,0 @@ -module Spec - module Matchers - - class ThrowSymbol #:nodoc: - def initialize(expected=nil) - @expected = expected - end - - def matches?(proc) - begin - proc.call - rescue NameError => e - @actual = e.name.to_sym - ensure - if @expected.nil? - return @actual.nil? ? false : true - else - return @actual == @expected - end - end - end - - def failure_message - if @actual - "expected #{expected}, got #{@actual.inspect}" - else - "expected #{expected} but nothing was thrown" - end - end - - def negative_failure_message - if @expected - "expected #{expected} not to be thrown" - else - "expected no Symbol, got :#{@actual}" - end - end - - def description - "throw #{expected}" - end - - private - - def expected - @expected.nil? ? "a Symbol" : @expected.inspect - end - - end - - # :call-seq: - # should throw_symbol() - # should throw_symbol(:sym) - # should_not throw_symbol() - # should_not throw_symbol(:sym) - # - # Given a Symbol argument, matches if a proc throws the specified Symbol. - # - # Given no argument, matches if a proc throws any Symbol. - # - # == Examples - # - # lambda { do_something_risky }.should throw_symbol - # lambda { do_something_risky }.should throw_symbol(:that_was_risky) - # - # lambda { do_something_risky }.should_not throw_symbol - # lambda { do_something_risky }.should_not throw_symbol(:that_was_risky) - def throw_symbol(sym=nil) - Matchers::ThrowSymbol.new(sym) - end - end -end diff --git a/spec/lib/spec/mocks.rb b/spec/lib/spec/mocks.rb deleted file mode 100644 index 66cbafb3c..000000000 --- a/spec/lib/spec/mocks.rb +++ /dev/null @@ -1,208 +0,0 @@ -require 'spec/mocks/methods' -require 'spec/mocks/argument_constraint_matchers' -require 'spec/mocks/spec_methods' -require 'spec/mocks/proxy' -require 'spec/mocks/mock' -require 'spec/mocks/argument_expectation' -require 'spec/mocks/message_expectation' -require 'spec/mocks/order_group' -require 'spec/mocks/errors' -require 'spec/mocks/error_generator' -require 'spec/mocks/extensions/object' -require 'spec/mocks/space' - - -module Spec - # == Mocks and Stubs - # - # RSpec will create Mock Objects and Stubs for you at runtime, or attach stub/mock behaviour - # to any of your real objects (Partial Mock/Stub). Because the underlying implementation - # for mocks and stubs is the same, you can intermingle mock and stub - # behaviour in either dynamically generated mocks or your pre-existing classes. - # There is a semantic difference in how they are created, however, - # which can help clarify the role it is playing within a given spec. - # - # == Mock Objects - # - # Mocks are objects that allow you to set and verify expectations that they will - # receive specific messages during run time. They are very useful for specifying how the subject of - # the spec interacts with its collaborators. This approach is widely known as "interaction - # testing". - # - # Mocks are also very powerful as a design tool. As you are - # driving the implementation of a given class, Mocks provide an anonymous - # collaborator that can change in behaviour as quickly as you can write an expectation in your - # spec. This flexibility allows you to design the interface of a collaborator that often - # does not yet exist. As the shape of the class being specified becomes more clear, so do the - # requirements for its collaborators - often leading to the discovery of new types that are - # needed in your system. - # - # Read Endo-Testing[http://www.mockobjects.com/files/endotesting.pdf] for a much - # more in depth description of this process. - # - # == Stubs - # - # Stubs are objects that allow you to set "stub" responses to - # messages. As Martin Fowler points out on his site, - # mocks_arent_stubs[http://www.martinfowler.com/articles/mocksArentStubs.html]. - # Paraphrasing Fowler's paraphrasing - # of Gerard Meszaros: Stubs provide canned responses to messages they might receive in a test, while - # mocks allow you to specify and, subsquently, verify that certain messages should be received during - # the execution of a test. - # - # == Partial Mocks/Stubs - # - # RSpec also supports partial mocking/stubbing, allowing you to add stub/mock behaviour - # to instances of your existing classes. This is generally - # something to be avoided, because changes to the class can have ripple effects on - # seemingly unrelated specs. When specs fail due to these ripple effects, the fact - # that some methods are being mocked can make it difficult to understand why a - # failure is occurring. - # - # That said, partials do allow you to expect and - # verify interactions with class methods such as +#find+ and +#create+ - # on Ruby on Rails model classes. - # - # == Further Reading - # - # There are many different viewpoints about the meaning of mocks and stubs. If you are interested - # in learning more, here is some recommended reading: - # - # * Mock Objects: http://www.mockobjects.com/ - # * Endo-Testing: http://www.mockobjects.com/files/endotesting.pdf - # * Mock Roles, Not Objects: http://www.mockobjects.com/files/mockrolesnotobjects.pdf - # * Test Double Patterns: http://xunitpatterns.com/Test%20Double%20Patterns.html - # * Mocks aren't stubs: http://www.martinfowler.com/articles/mocksArentStubs.html - # - # == Creating a Mock - # - # You can create a mock in any specification (or setup) using: - # - # mock(name, options={}) - # - # The optional +options+ argument is a +Hash+. Currently the only supported - # option is +:null_object+. Setting this to true instructs the mock to ignore - # any messages it hasn’t been told to expect – and quietly return itself. For example: - # - # mock("person", :null_object => true) - # - # == Creating a Stub - # - # You can create a stub in any specification (or setup) using: - # - # stub(name, stub_methods_and_values_hash) - # - # For example, if you wanted to create an object that always returns - # "More?!?!?!" to "please_sir_may_i_have_some_more" you would do this: - # - # stub("Mr Sykes", :please_sir_may_i_have_some_more => "More?!?!?!") - # - # == Creating a Partial Mock - # - # You don't really "create" a partial mock, you simply add method stubs and/or - # mock expectations to existing classes and objects: - # - # Factory.should_receive(:find).with(id).and_return(value) - # obj.stub!(:to_i).and_return(3) - # etc ... - # - # == Expecting Messages - # - # my_mock.should_receive(:sym) - # my_mock.should_not_receive(:sym) - # - # == Expecting Arguments - # - # my_mock.should_receive(:sym).with(*args) - # my_mock.should_not_receive(:sym).with(*args) - # - # == Argument Constraints using Expression Matchers - # - # Arguments that are passed to #with are compared with actual arguments received - # using == by default. In cases in which you want to specify things about the arguments - # rather than the arguments themselves, you can use any of the Expression Matchers. - # They don't all make syntactic sense (they were primarily designed for use with - # Spec::Expectations), but you are free to create your own custom Spec::Matchers. - # - # Spec::Mocks does provide one additional Matcher method named #ducktype. - # - # In addition, Spec::Mocks adds some keyword Symbols that you can use to - # specify certain kinds of arguments: - # - # my_mock.should_receive(:sym).with(no_args()) - # my_mock.should_receive(:sym).with(any_args()) - # my_mock.should_receive(:sym).with(1, an_instance_of(Numeric), "b") #2nd argument can any type of Numeric - # my_mock.should_receive(:sym).with(1, boolean(), "b") #2nd argument can true or false - # my_mock.should_receive(:sym).with(1, /abc/, "b") #2nd argument can be any String matching the submitted Regexp - # my_mock.should_receive(:sym).with(1, anything(), "b") #2nd argument can be anything at all - # my_mock.should_receive(:sym).with(1, ducktype(:abs, :div), "b") - # #2nd argument can be object that responds to #abs and #div - # - # == Receive Counts - # - # my_mock.should_receive(:sym).once - # my_mock.should_receive(:sym).twice - # my_mock.should_receive(:sym).exactly(n).times - # my_mock.should_receive(:sym).at_least(:once) - # my_mock.should_receive(:sym).at_least(:twice) - # my_mock.should_receive(:sym).at_least(n).times - # my_mock.should_receive(:sym).at_most(:once) - # my_mock.should_receive(:sym).at_most(:twice) - # my_mock.should_receive(:sym).at_most(n).times - # my_mock.should_receive(:sym).any_number_of_times - # - # == Ordering - # - # my_mock.should_receive(:sym).ordered - # my_mock.should_receive(:other_sym).ordered - # #This will fail if the messages are received out of order - # - # == Setting Reponses - # - # Whether you are setting a mock expectation or a simple stub, you can tell the - # object precisely how to respond: - # - # my_mock.should_receive(:sym).and_return(value) - # my_mock.should_receive(:sym).exactly(3).times.and_return(value1, value2, value3) - # # returns value1 the first time, value2 the second, etc - # my_mock.should_receive(:sym).and_return { ... } #returns value returned by the block - # my_mock.should_receive(:sym).and_raise(error) - # #error can be an instantiated object or a class - # #if it is a class, it must be instantiable with no args - # my_mock.should_receive(:sym).and_throw(:sym) - # my_mock.should_receive(:sym).and_yield([array,of,values,to,yield]) - # - # Any of these responses can be applied to a stub as well, but stubs do - # not support any qualifiers about the message received (i.e. you can't specify arguments - # or receive counts): - # - # my_mock.stub!(:sym).and_return(value) - # my_mock.stub!(:sym).and_return(value1, value2, value3) - # my_mock.stub!(:sym).and_raise(error) - # my_mock.stub!(:sym).and_throw(:sym) - # my_mock.stub!(:sym).and_yield([array,of,values,to,yield]) - # - # == Arbitrary Handling - # - # Once in a while you'll find that the available expectations don't solve the - # particular problem you are trying to solve. Imagine that you expect the message - # to come with an Array argument that has a specific length, but you don't care - # what is in it. You could do this: - # - # my_mock.should_receive(:sym) do |arg| - # arg.should be_an_istance_of(Array) - # arg.length.should == 7 - # end - # - # Note that this would fail if the number of arguments received was different from - # the number of block arguments (in this case 1). - # - # == Combining Expectation Details - # - # Combining the message name with specific arguments, receive counts and responses - # you can get quite a bit of detail in your expectations: - # - # my_mock.should_receive(:<<).with("illegal value").once.and_raise(ArgumentError) - module Mocks - end -end diff --git a/spec/lib/spec/mocks/argument_constraint_matchers.rb b/spec/lib/spec/mocks/argument_constraint_matchers.rb deleted file mode 100644 index 0e4777082..000000000 --- a/spec/lib/spec/mocks/argument_constraint_matchers.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Spec - module Mocks - module ArgumentConstraintMatchers - - # Shortcut for creating an instance of Spec::Mocks::DuckTypeArgConstraint - def duck_type(*args) - DuckTypeArgConstraint.new(*args) - end - - def any_args - AnyArgsConstraint.new - end - - def anything - AnyArgConstraint.new(nil) - end - - def boolean - BooleanArgConstraint.new(nil) - end - - def no_args - NoArgsConstraint.new - end - end - end -end diff --git a/spec/lib/spec/mocks/argument_expectation.rb b/spec/lib/spec/mocks/argument_expectation.rb deleted file mode 100644 index 5da069b87..000000000 --- a/spec/lib/spec/mocks/argument_expectation.rb +++ /dev/null @@ -1,183 +0,0 @@ -module Spec - module Mocks - - class MatcherConstraint - def initialize(matcher) - @matcher = matcher - end - - def matches?(value) - @matcher.matches?(value) - end - end - - class LiteralArgConstraint - def initialize(literal) - @literal_value = literal - end - - def matches?(value) - @literal_value == value - end - end - - class RegexpArgConstraint - def initialize(regexp) - @regexp = regexp - end - - def matches?(value) - return value =~ @regexp unless value.is_a?(Regexp) - value == @regexp - end - end - - class AnyArgConstraint - def initialize(ignore) - end - - def ==(other) - true - end - - # TODO - need this? - def matches?(value) - true - end - end - - class AnyArgsConstraint - def description - "any args" - end - end - - class NoArgsConstraint - def description - "no args" - end - - def ==(args) - args == [] - end - end - - class NumericArgConstraint - def initialize(ignore) - end - - def matches?(value) - value.is_a?(Numeric) - end - end - - class BooleanArgConstraint - def initialize(ignore) - end - - def ==(value) - matches?(value) - end - - def matches?(value) - return true if value.is_a?(TrueClass) - return true if value.is_a?(FalseClass) - false - end - end - - class StringArgConstraint - def initialize(ignore) - end - - def matches?(value) - value.is_a?(String) - end - end - - class DuckTypeArgConstraint - def initialize(*methods_to_respond_to) - @methods_to_respond_to = methods_to_respond_to - end - - def matches?(value) - @methods_to_respond_to.all? { |sym| value.respond_to?(sym) } - end - - def description - "duck_type" - end - end - - class ArgumentExpectation - attr_reader :args - @@constraint_classes = Hash.new { |hash, key| LiteralArgConstraint} - @@constraint_classes[:anything] = AnyArgConstraint - @@constraint_classes[:numeric] = NumericArgConstraint - @@constraint_classes[:boolean] = BooleanArgConstraint - @@constraint_classes[:string] = StringArgConstraint - - def initialize(args) - @args = args - if [:any_args] == args - @expected_params = nil - warn_deprecated(:any_args.inspect, "any_args()") - elsif args.length == 1 && args[0].is_a?(AnyArgsConstraint) then @expected_params = nil - elsif [:no_args] == args - @expected_params = [] - warn_deprecated(:no_args.inspect, "no_args()") - elsif args.length == 1 && args[0].is_a?(NoArgsConstraint) then @expected_params = [] - else @expected_params = process_arg_constraints(args) - end - end - - def process_arg_constraints(constraints) - constraints.collect do |constraint| - convert_constraint(constraint) - end - end - - def warn_deprecated(deprecated_method, instead) - STDERR.puts "The #{deprecated_method} constraint is deprecated. Use #{instead} instead." - end - - def convert_constraint(constraint) - if [:anything, :numeric, :boolean, :string].include?(constraint) - case constraint - when :anything - instead = "anything()" - when :boolean - instead = "boolean()" - when :numeric - instead = "an_instance_of(Numeric)" - when :string - instead = "an_instance_of(String)" - end - warn_deprecated(constraint.inspect, instead) - return @@constraint_classes[constraint].new(constraint) - end - return MatcherConstraint.new(constraint) if is_matcher?(constraint) - return RegexpArgConstraint.new(constraint) if constraint.is_a?(Regexp) - return LiteralArgConstraint.new(constraint) - end - - def is_matcher?(obj) - return obj.respond_to?(:matches?) && obj.respond_to?(:description) - end - - def check_args(args) - return true if @expected_params.nil? - return true if @expected_params == args - return constraints_match?(args) - end - - def constraints_match?(args) - return false if args.length != @expected_params.length - @expected_params.each_index { |i| return false unless @expected_params[i].matches?(args[i]) } - return true - end - - end - - end -end diff --git a/spec/lib/spec/mocks/error_generator.rb b/spec/lib/spec/mocks/error_generator.rb deleted file mode 100644 index 01d8f720d..000000000 --- a/spec/lib/spec/mocks/error_generator.rb +++ /dev/null @@ -1,84 +0,0 @@ -module Spec - module Mocks - class ErrorGenerator - attr_writer :opts - - def initialize(target, name) - @target = target - @name = name - end - - def opts - @opts ||= {} - end - - def raise_unexpected_message_error(sym, *args) - __raise "#{intro} received unexpected message :#{sym}#{arg_message(*args)}" - end - - def raise_unexpected_message_args_error(expectation, *args) - expected_args = format_args(*expectation.expected_args) - actual_args = args.empty? ? "(no args)" : format_args(*args) - __raise "#{intro} expected #{expectation.sym.inspect} with #{expected_args} but received it with #{actual_args}" - end - - def raise_expectation_error(sym, expected_received_count, actual_received_count, *args) - __raise "#{intro} expected :#{sym}#{arg_message(*args)} #{count_message(expected_received_count)}, but received it #{count_message(actual_received_count)}" - end - - def raise_out_of_order_error(sym) - __raise "#{intro} received :#{sym} out of order" - end - - def raise_block_failed_error(sym, detail) - __raise "#{intro} received :#{sym} but passed block failed with: #{detail}" - end - - def raise_missing_block_error(args_to_yield) - __raise "#{intro} asked to yield |#{arg_list(*args_to_yield)}| but no block was passed" - end - - def raise_wrong_arity_error(args_to_yield, arity) - __raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with arity of #{arity}" - end - - private - def intro - @name ? "Mock '#{@name}'" : @target.inspect - end - - def __raise(message) - message = opts[:message] unless opts[:message].nil? - Kernel::raise(Spec::Mocks::MockExpectationError, message) - end - - def arg_message(*args) - " with " + format_args(*args) - end - - def format_args(*args) - return "(no args)" if args.empty? || args == [:no_args] - return "(any args)" if args == [:any_args] - "(" + arg_list(*args) + ")" - end - - def arg_list(*args) - args.collect do |arg| - arg.respond_to?(:description) ? arg.description : arg.inspect - end.join(", ") - end - - def count_message(count) - return "at least #{pretty_print(count.abs)}" if count < 0 - return pretty_print(count) - end - - def pretty_print(count) - return "once" if count == 1 - return "twice" if count == 2 - return "#{count} times" - end - - end - end -end diff --git a/spec/lib/spec/mocks/errors.rb b/spec/lib/spec/mocks/errors.rb deleted file mode 100644 index 68fdfe006..000000000 --- a/spec/lib/spec/mocks/errors.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Spec - module Mocks - class MockExpectationError < StandardError - end - - class AmbiguousReturnError < StandardError - end - end -end - diff --git a/spec/lib/spec/mocks/extensions/object.rb b/spec/lib/spec/mocks/extensions/object.rb deleted file mode 100644 index 4b7531066..000000000 --- a/spec/lib/spec/mocks/extensions/object.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Object - include Spec::Mocks::Methods -end diff --git a/spec/lib/spec/mocks/message_expectation.rb b/spec/lib/spec/mocks/message_expectation.rb deleted file mode 100644 index 74ade3c58..000000000 --- a/spec/lib/spec/mocks/message_expectation.rb +++ /dev/null @@ -1,242 +0,0 @@ -module Spec - module Mocks - - class BaseExpectation - attr_reader :sym - - def initialize(error_generator, expectation_ordering, expected_from, sym, method_block, expected_received_count=1, opts={}) - @error_generator = error_generator - @error_generator.opts = opts - @expected_from = expected_from - @sym = sym - @method_block = method_block - @return_block = lambda {} - @received_count = 0 - @expected_received_count = expected_received_count - @args_expectation = ArgumentExpectation.new([AnyArgsConstraint.new]) - @consecutive = false - @exception_to_raise = nil - @symbol_to_throw = nil - @order_group = expectation_ordering - @at_least = nil - @at_most = nil - @args_to_yield = nil - end - - def expected_args - @args_expectation.args - end - - def and_return(*values, &return_block) - Kernel::raise AmbiguousReturnError unless @method_block.nil? - if values.size == 0 - value = nil - elsif values.size == 1 - value = values[0] - else - value = values - @consecutive = true - @expected_received_count = values.size if @expected_received_count != :any && - @expected_received_count < values.size - end - @return_block = block_given? ? return_block : lambda { value } - end - - # :call-seq: - # and_raise() - # and_raise(Exception) #any exception class - # and_raise(exception) #any exception object - # - # == Warning - # - # When you pass an exception class, the MessageExpectation will - # raise an instance of it, creating it with +new+. If the exception - # class initializer requires any parameters, you must pass in an - # instance and not the class. - def and_raise(exception=Exception) - @exception_to_raise = exception - end - - def and_throw(symbol) - @symbol_to_throw = symbol - end - - def and_yield(*args) - @args_to_yield = args - end - - def matches(sym, args) - @sym == sym and @args_expectation.check_args(args) - end - - def invoke(args, block) - @order_group.handle_order_constraint self - - begin - if @exception_to_raise.class == Class - @exception_instance_to_raise = @exception_to_raise.new - else - @exception_instance_to_raise = @exception_to_raise - end - Kernel::raise @exception_to_raise unless @exception_to_raise.nil? - Kernel::throw @symbol_to_throw unless @symbol_to_throw.nil? - - if !@method_block.nil? - return invoke_method_block(args) - elsif !@args_to_yield.nil? - return invoke_with_yield(block) - elsif @consecutive - return invoke_consecutive_return_block(args, block) - else - return invoke_return_block(args, block) - end - ensure - @received_count += 1 - end - end - - protected - - def invoke_method_block(args) - begin - @method_block.call(*args) - rescue => detail - @error_generator.raise_block_failed_error @sym, detail.message - end - end - - def invoke_with_yield(block) - if block.nil? - @error_generator.raise_missing_block_error @args_to_yield - end - if block.arity > -1 && @args_to_yield.length != block.arity - @error_generator.raise_wrong_arity_error @args_to_yield, block.arity - end - block.call(*@args_to_yield) - end - - def invoke_consecutive_return_block(args, block) - args << block unless block.nil? - value = @return_block.call(*args) - - index = [@received_count, value.size-1].min - value[index] - end - - def invoke_return_block(args, block) - args << block unless block.nil? - value = @return_block.call(*args) - - value - end - end - - class MessageExpectation < BaseExpectation - - def matches_name_but_not_args(sym, args) - @sym == sym and not @args_expectation.check_args(args) - end - - def verify_messages_received - return if @expected_received_count == :any - return if (@at_least) && (@received_count >= @expected_received_count) - return if (@at_most) && (@received_count <= @expected_received_count) - return if @expected_received_count == @received_count - - begin - @error_generator.raise_expectation_error(@sym, @expected_received_count, @received_count, *@args_expectation.args) - rescue => error - error.backtrace.insert(0, @expected_from) - Kernel::raise error - end - end - - def with(*args, &block) - @method_block = block if block - @args_expectation = ArgumentExpectation.new(args) - self - end - - def exactly(n) - set_expected_received_count :exactly, n - self - end - - def at_least(n) - set_expected_received_count :at_least, n - self - end - - def at_most(n) - set_expected_received_count :at_most, n - self - end - - def times(&block) - @method_block = block if block - self - end - - def any_number_of_times(&block) - @method_block = block if block - @expected_received_count = :any - self - end - - def never - @expected_received_count = 0 - self - end - - def once(&block) - @method_block = block if block - @expected_received_count = 1 - self - end - - def twice(&block) - @method_block = block if block - @expected_received_count = 2 - self - end - - def ordered(&block) - @method_block = block if block - @order_group.register(self) - @ordered = true - self - end - - def negative_expectation_for?(sym) - return false - end - - protected - def set_expected_received_count(relativity, n) - @at_least = (relativity == :at_least) - @at_most = (relativity == :at_most) - @expected_received_count = 1 if n == :once - @expected_received_count = 2 if n == :twice - @expected_received_count = n if n.kind_of? Numeric - end - - end - - class NegativeMessageExpectation < MessageExpectation - def initialize(message, expectation_ordering, expected_from, sym, method_block) - super(message, expectation_ordering, expected_from, sym, method_block, 0) - end - - def negative_expectation_for?(sym) - return @sym == sym - end - end - - class MethodStub < BaseExpectation - def initialize(message, expectation_ordering, expected_from, sym, method_block) - super(message, expectation_ordering, expected_from, sym, method_block, 0) - @expected_received_count = :any - end - end - end -end diff --git a/spec/lib/spec/mocks/methods.rb b/spec/lib/spec/mocks/methods.rb deleted file mode 100644 index 3d898cf31..000000000 --- a/spec/lib/spec/mocks/methods.rb +++ /dev/null @@ -1,39 +0,0 @@ -module Spec - module Mocks - module Methods - def should_receive(sym, opts={}, &block) - __mock_proxy.add_message_expectation(opts[:expected_from] || caller(1)[0], sym.to_sym, opts, &block) - end - - def should_not_receive(sym, &block) - __mock_proxy.add_negative_message_expectation(caller(1)[0], sym.to_sym, &block) - end - - def stub!(sym) - __mock_proxy.add_stub(caller(1)[0], sym.to_sym) - end - - def received_message?(sym, *args, &block) #:nodoc: - __mock_proxy.received_message?(sym.to_sym, *args, &block) - end - - def rspec_verify #:nodoc: - __mock_proxy.verify - end - - def rspec_reset #:nodoc: - __mock_proxy.reset - end - - private - - def __mock_proxy - if Mock === self - @mock_proxy ||= Proxy.new(self, @name, @options) - else - @mock_proxy ||= Proxy.new(self, self.class.name) - end - end - end - end -end diff --git a/spec/lib/spec/mocks/mock.rb b/spec/lib/spec/mocks/mock.rb deleted file mode 100644 index aa380e0af..000000000 --- a/spec/lib/spec/mocks/mock.rb +++ /dev/null @@ -1,29 +0,0 @@ -module Spec - module Mocks - class Mock - include Methods - - # Creates a new mock with a +name+ (that will be used in error messages only) - # == Options: - # * <tt>:null_object</tt> - if true, the mock object acts as a forgiving null object allowing any message to be sent to it. - def initialize(name, options={}) - @name = name - @options = options - end - - def method_missing(sym, *args, &block) - __mock_proxy.instance_eval {@messages_received << [sym, args, block]} - begin - return self if __mock_proxy.null_object? - super(sym, *args, &block) - rescue NoMethodError - __mock_proxy.raise_unexpected_message_error sym, *args - end - end - - def inspect - "#<#{self.class}:#{sprintf '0x%x', self.object_id} @name=#{@name.inspect}>" - end - end - end -end diff --git a/spec/lib/spec/mocks/order_group.rb b/spec/lib/spec/mocks/order_group.rb deleted file mode 100644 index 9983207eb..000000000 --- a/spec/lib/spec/mocks/order_group.rb +++ /dev/null @@ -1,29 +0,0 @@ -module Spec - module Mocks - class OrderGroup - def initialize error_generator - @error_generator = error_generator - @ordering = Array.new - end - - def register(expectation) - @ordering << expectation - end - - def ready_for?(expectation) - return @ordering.first == expectation - end - - def consume - @ordering.shift - end - - def handle_order_constraint expectation - return unless @ordering.include? expectation - return consume if ready_for?(expectation) - @error_generator.raise_out_of_order_error expectation.sym - end - - end - end -end diff --git a/spec/lib/spec/mocks/proxy.rb b/spec/lib/spec/mocks/proxy.rb deleted file mode 100644 index 6c79d1068..000000000 --- a/spec/lib/spec/mocks/proxy.rb +++ /dev/null @@ -1,167 +0,0 @@ -module Spec - module Mocks - class Proxy - DEFAULT_OPTIONS = { - :null_object => false, - } - - def initialize(target, name, options={}) - @target = target - @name = name - @error_generator = ErrorGenerator.new target, name - @expectation_ordering = OrderGroup.new @error_generator - @expectations = [] - @messages_received = [] - @stubs = [] - @proxied_methods = [] - @options = options ? DEFAULT_OPTIONS.dup.merge(options) : DEFAULT_OPTIONS - end - - def null_object? - @options[:null_object] - end - - def add_message_expectation(expected_from, sym, opts={}, &block) - __add sym, block - @expectations << MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil, 1, opts) - @expectations.last - end - - def add_negative_message_expectation(expected_from, sym, &block) - __add sym, block - @expectations << NegativeMessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil) - @expectations.last - end - - def add_stub(expected_from, sym) - __add sym, nil - @stubs.unshift MethodStub.new(@error_generator, @expectation_ordering, expected_from, sym, nil) - @stubs.first - end - - def verify #:nodoc: - begin - verify_expectations - ensure - reset - end - end - - def reset - clear_expectations - clear_stubs - reset_proxied_methods - clear_proxied_methods - end - - def received_message?(sym, *args, &block) - return true if @messages_received.find {|array| array == [sym, args, block]} - return false - end - - def has_negative_expectation?(sym) - @expectations.detect {|expectation| expectation.negative_expectation_for?(sym)} - end - - def message_received(sym, *args, &block) - if expectation = find_matching_expectation(sym, *args) - expectation.invoke(args, block) - elsif stub = find_matching_method_stub(sym) - stub.invoke([], block) - elsif expectation = find_almost_matching_expectation(sym, *args) - raise_unexpected_message_args_error(expectation, *args) unless has_negative_expectation?(sym) unless null_object? - else - @target.send :method_missing, sym, *args, &block - end - end - - def raise_unexpected_message_args_error(expectation, *args) - @error_generator.raise_unexpected_message_args_error expectation, *args - end - - def raise_unexpected_message_error(sym, *args) - @error_generator.raise_unexpected_message_error sym, *args - end - - private - - def __add(sym, block) - $rspec_mocks.add(@target) unless $rspec_mocks.nil? - define_expected_method(sym) - end - - def define_expected_method(sym) - if target_responds_to?(sym) && !@proxied_methods.include?(sym) - metaclass.__send__(:alias_method, munge(sym), sym) if metaclass.instance_methods.include?(sym.to_s) - @proxied_methods << sym - end - - metaclass_eval(<<-EOF, __FILE__, __LINE__) - def #{sym}(*args, &block) - __mock_proxy.message_received :#{sym}, *args, &block - end - EOF - end - - def target_responds_to?(sym) - return @target.send(munge(:respond_to?),sym) if @already_proxied_respond_to - return @already_proxied_respond_to = true if sym == :respond_to? - return @target.respond_to?(sym) - end - - def munge(sym) - "proxied_by_rspec__#{sym.to_s}".to_sym - end - - def clear_expectations - @expectations.clear - end - - def clear_stubs - @stubs.clear - end - - def clear_proxied_methods - @proxied_methods.clear - end - - def metaclass_eval(str, filename, lineno) - metaclass.class_eval(str, filename, lineno) - end - - def metaclass - (class << @target; self; end) - end - - def verify_expectations - @expectations.each do |expectation| - expectation.verify_messages_received - end - end - - def reset_proxied_methods - @proxied_methods.each do |sym| - if metaclass.instance_methods.include?(munge(sym).to_s) - metaclass.__send__(:alias_method, sym, munge(sym)) - metaclass.__send__(:undef_method, munge(sym)) - else - metaclass.__send__(:undef_method, sym) - end - end - end - - def find_matching_expectation(sym, *args) - @expectations.find {|expectation| expectation.matches(sym, args)} - end - - def find_almost_matching_expectation(sym, *args) - @expectations.find {|expectation| expectation.matches_name_but_not_args(sym, args)} - end - - def find_matching_method_stub(sym) - @stubs.find {|stub| stub.matches(sym, [])} - end - - end - end -end diff --git a/spec/lib/spec/mocks/space.rb b/spec/lib/spec/mocks/space.rb deleted file mode 100644 index e04bc5ccb..000000000 --- a/spec/lib/spec/mocks/space.rb +++ /dev/null @@ -1,28 +0,0 @@ -module Spec - module Mocks - class Space - def add(obj) - mocks << obj unless mocks.include?(obj) - end - - def verify_all - mocks.each do |mock| - mock.rspec_verify - end - end - - def reset_all - mocks.each do |mock| - mock.rspec_reset - end - mocks.clear - end - - private - - def mocks - @mocks ||= [] - end - end - end -end diff --git a/spec/lib/spec/mocks/spec_methods.rb b/spec/lib/spec/mocks/spec_methods.rb deleted file mode 100644 index fd67fd210..000000000 --- a/spec/lib/spec/mocks/spec_methods.rb +++ /dev/null @@ -1,30 +0,0 @@ -module Spec - module Mocks - module SpecMethods - include Spec::Mocks::ArgumentConstraintMatchers - - # Shortcut for creating an instance of Spec::Mocks::Mock. - def mock(name, options={}) - Spec::Mocks::Mock.new(name, options) - end - - # Shortcut for creating an instance of Spec::Mocks::Mock with - # predefined method stubs. - # - # == Examples - # - # stub_thing = stub("thing", :a => "A") - # stub_thing.a == "A" => true - # - # stub_person = stub("thing", :name => "Joe", :email => "joe@domain.com") - # stub_person.name => "Joe" - # stub_person.email => "joe@domain.com" - def stub(name, stubs={}) - object_stub = mock(name) - stubs.each { |key, value| object_stub.stub!(key).and_return(value) } - object_stub - end - - end - end -end diff --git a/spec/lib/spec/rake/spectask.rb b/spec/lib/spec/rake/spectask.rb deleted file mode 100644 index f8c6809a9..000000000 --- a/spec/lib/spec/rake/spectask.rb +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/env ruby - -# Define a task library for running RSpec contexts. - -require 'rake' -require 'rake/tasklib' - -module Spec - module Rake - - # A Rake task that runs a set of RSpec contexts. - # - # Example: - # - # Spec::Rake::SpecTask.new do |t| - # t.warning = true - # t.rcov = true - # end - # - # This will create a task that can be run with: - # - # rake spec - # - # If rake is invoked with a "SPEC=filename" command line option, - # then the list of spec files will be overridden to include only the - # filename specified on the command line. This provides an easy way - # to run just one spec. - # - # If rake is invoked with a "SPEC_OPTS=options" command line option, - # then the given options will override the value of the +spec_opts+ - # attribute. - # - # If rake is invoked with a "RCOV_OPTS=options" command line option, - # then the given options will override the value of the +rcov_opts+ - # attribute. - # - # Examples: - # - # rake spec # run specs normally - # rake spec SPEC=just_one_file.rb # run just one spec file. - # rake spec SPEC_OPTS="--diff" # enable diffing - # rake spec RCOV_OPTS="--aggregate myfile.txt" # see rcov --help for details - # - # Each attribute of this task may be a proc. This allows for lazy evaluation, - # which is sometimes handy if you want to defer the evaluation of an attribute value - # until the task is run (as opposed to when it is defined). - class SpecTask < ::Rake::TaskLib - class << self - def attr_accessor(*names) - super(*names) - names.each do |name| - module_eval "def #{name}() evaluate(@#{name}) end" # Allows use of procs - end - end - end - - # Name of spec task. (default is :spec) - attr_accessor :name - - # Array of directories to be added to $LOAD_PATH before running the - # specs. Defaults to ['<the absolute path to RSpec's lib directory>'] - attr_accessor :libs - - # If true, requests that the specs be run with the warning flag set. - # E.g. warning=true implies "ruby -w" used to run the specs. Defaults to false. - attr_accessor :warning - - # Glob pattern to match spec files. (default is 'spec/**/*_spec.rb') - # Setting the SPEC environment variable overrides this. - attr_accessor :pattern - - # Array of commandline options to pass to RSpec. Defaults to []. - # Setting the SPEC_OPTS environment variable overrides this. - attr_accessor :spec_opts - - # Whether or not to use RCov (default is false) - # See http://eigenclass.org/hiki.rb?rcov - attr_accessor :rcov - - # Array of commandline options to pass to RCov. Defaults to ['--exclude', 'lib\/spec,bin\/spec']. - # Ignored if rcov=false - # Setting the RCOV_OPTS environment variable overrides this. - attr_accessor :rcov_opts - - # Directory where the RCov report is written. Defaults to "coverage" - # Ignored if rcov=false - attr_accessor :rcov_dir - - # Array of commandline options to pass to ruby. Defaults to []. - attr_accessor :ruby_opts - - # Whether or not to fail Rake when an error occurs (typically when specs fail). - # Defaults to true. - attr_accessor :fail_on_error - - # A message to print to stderr when there are failures. - attr_accessor :failure_message - - # Where RSpec's output is written. Defaults to STDOUT. - # DEPRECATED. Use --format FORMAT:WHERE in spec_opts. - attr_accessor :out - - # Explicitly define the list of spec files to be included in a - # spec. +spec_files+ is expected to be an array of file names (a - # FileList is acceptable). If both +pattern+ and +spec_files+ are - # used, then the list of spec files is the union of the two. - # Setting the SPEC environment variable overrides this. - attr_accessor :spec_files - - # Defines a new task, using the name +name+. - def initialize(name=:spec) - @name = name - @libs = [File.expand_path(File.dirname(__FILE__) + '/../../../lib')] - @pattern = nil - @spec_files = nil - @spec_opts = [] - @warning = false - @ruby_opts = [] - @fail_on_error = true - @rcov = false - @rcov_opts = ['--exclude', 'lib\/spec,bin\/spec,config\/boot.rb'] - @rcov_dir = "coverage" - - yield self if block_given? - @pattern = 'spec/**/*_spec.rb' if pattern.nil? && spec_files.nil? - define - end - - def define # :nodoc: - spec_script = File.expand_path(File.dirname(__FILE__) + '/../../../bin/spec') - - lib_path = libs.join(File::PATH_SEPARATOR) - actual_name = Hash === name ? name.keys.first : name - unless ::Rake.application.last_comment - desc "Run specs" + (rcov ? " using RCov" : "") - end - task name do - RakeFileUtils.verbose(verbose) do - unless spec_file_list.empty? - # ruby [ruby_opts] -Ilib -S rcov [rcov_opts] bin/spec -- examples [spec_opts] - # or - # ruby [ruby_opts] -Ilib bin/spec examples [spec_opts] - cmd = "ruby " - - rb_opts = ruby_opts.clone - rb_opts << "-I\"#{lib_path}\"" - rb_opts << "-S rcov" if rcov - rb_opts << "-w" if warning - cmd << rb_opts.join(" ") - cmd << " " - cmd << rcov_option_list - cmd << %[ -o "#{rcov_dir}" ] if rcov - cmd << %Q|"#{spec_script}"| - cmd << " " - cmd << "-- " if rcov - cmd << spec_file_list.collect { |fn| %["#{fn}"] }.join(' ') - cmd << " " - cmd << spec_option_list - if out - cmd << " " - cmd << %Q| > "#{out}"| - STDERR.puts "The Spec::Rake::SpecTask#out attribute is DEPRECATED and will be removed in a future version. Use --format FORMAT:WHERE instead." - end - unless system(cmd) - STDERR.puts failure_message if failure_message - raise("Command #{cmd} failed") if fail_on_error - end - end - end - end - - if rcov - desc "Remove rcov products for #{actual_name}" - task paste("clobber_", actual_name) do - rm_r rcov_dir rescue nil - end - - clobber_task = paste("clobber_", actual_name) - task :clobber => [clobber_task] - - task actual_name => clobber_task - end - self - end - - def rcov_option_list # :nodoc: - return "" unless rcov - ENV['RCOV_OPTS'] || rcov_opts.join(" ") || "" - end - - def spec_option_list # :nodoc: - STDERR.puts "RSPECOPTS is DEPRECATED and will be removed in a future version. Use SPEC_OPTS instead." if ENV['RSPECOPTS'] - ENV['SPEC_OPTS'] || ENV['RSPECOPTS'] || spec_opts.join(" ") || "" - end - - def evaluate(o) # :nodoc: - case o - when Proc then o.call - else o - end - end - - def spec_file_list # :nodoc: - if ENV['SPEC'] - FileList[ ENV['SPEC'] ] - else - result = [] - result += spec_files.to_a if spec_files - result += FileList[ pattern ].to_a if pattern - FileList[result] - end - end - - end - end -end - diff --git a/spec/lib/spec/rake/verify_rcov.rb b/spec/lib/spec/rake/verify_rcov.rb deleted file mode 100644 index 9715744e9..000000000 --- a/spec/lib/spec/rake/verify_rcov.rb +++ /dev/null @@ -1,52 +0,0 @@ -module RCov - # A task that can verify that the RCov coverage doesn't - # drop below a certain threshold. It should be run after - # running Spec::Rake::SpecTask. - class VerifyTask < Rake::TaskLib - # Name of the task. Defaults to :verify_rcov - attr_accessor :name - - # Path to the index.html file generated by RCov, which - # is the file containing the total coverage. - # Defaults to 'coverage/index.html' - attr_accessor :index_html - - # Whether or not to output details. Defaults to true. - attr_accessor :verbose - - # The threshold value (in percent) for coverage. If the - # actual coverage is not equal to this value, the task will raise an - # exception. - attr_accessor :threshold - - # Require the threshold value be met exactly. This is the default. - attr_accessor :require_exact_threshold - - def initialize(name=:verify_rcov) - @name = name - @index_html = 'coverage/index.html' - @verbose = true - @require_exact_threshold = true - yield self if block_given? - raise "Threshold must be set" if @threshold.nil? - define - end - - def define - desc "Verify that rcov coverage is at least #{threshold}%" - task @name do - total_coverage = nil - - File.open(index_html).each_line do |line| - if line =~ /<tt.*>(\d+\.\d+)%<\/tt> <\/td>/ - total_coverage = eval($1) - break - end - end - puts "Coverage: #{total_coverage}% (threshold: #{threshold}%)" if verbose - raise "Coverage must be at least #{threshold}% but was #{total_coverage}%" if total_coverage < threshold - raise "Coverage has increased above the threshold of #{threshold}% to #{total_coverage}%. You should update your threshold value." if (total_coverage > threshold) and require_exact_threshold - end - end - end -end diff --git a/spec/lib/spec/runner.rb b/spec/lib/spec/runner.rb deleted file mode 100644 index 9d801adc3..000000000 --- a/spec/lib/spec/runner.rb +++ /dev/null @@ -1,165 +0,0 @@ -require 'spec/runner/formatter' -require 'spec/runner/behaviour_runner' -require 'spec/runner/options' -require 'spec/runner/option_parser' -require 'spec/runner/command_line' -require 'spec/runner/drb_command_line' -require 'spec/runner/backtrace_tweaker' -require 'spec/runner/reporter' -require 'spec/runner/extensions/object' -require 'spec/runner/extensions/kernel' -require 'spec/runner/spec_parser' - -module Spec - # == Behaviours and Examples - # - # Rather than expressing examples in classes, RSpec uses a custom domain specific language to - # describe Behaviours and Examples of those behaviours. - # - # A Behaviour is the equivalent of a fixture in xUnit-speak. It is a metaphor for the context - # in which you will run your executable example - a set of known objects in a known starting state. - # We begin be describing - # - # describe Account do - # - # before do - # @account = Account.new - # end - # - # it "should have a balance of $0" do - # @account.balance.should == Money.new(0, :dollars) - # end - # - # end - # - # We use the before block to set up the Behaviour (given), and then the #it method to - # hold the example code that expresses the event (when) and the expected outcome (then). - # - # == Helper Methods - # - # A primary goal of RSpec is to keep the examples clear. We therefore prefer - # less indirection than you might see in xUnit examples and in well factored, DRY production code. We feel - # that duplication is OK if removing it makes it harder to understand an example without - # having to look elsewhere to understand its context. - # - # That said, RSpec does support some level of encapsulating common code in helper - # methods that can exist within a context or within an included module. - # - # == Setup and Teardown - # - # You can use before and after within a Behaviour. Both methods take an optional - # scope argument so you can run the block before :each example or before :all examples - # - # describe "..." do - # before :all do - # ... - # end - # - # before :each do - # ... - # end - # - # it "should do something" do - # ... - # end - # - # it "should do something else" do - # ... - # end - # - # after :each do - # ... - # end - # - # after :all do - # ... - # end - # - # end - # - # The <tt>before :each</tt> block will run before each of the examples, once for each example. Likewise, - # the <tt>after :each</tt> block will run after each of the examples. - # - # It is also possible to specify a <tt>before :all</tt> and <tt>after :all</tt> - # block that will run only once for each behaviour, respectively before the first <code>before :each</code> - # and after the last <code>after :each</code>. The use of these is generally discouraged, because it - # introduces dependencies between the examples. Still, it might prove useful for very expensive operations - # if you know what you are doing. - # - # == Local helper methods - # - # You can include local helper methods by simply expressing them within a context: - # - # describe "..." do - # - # it "..." do - # helper_method - # end - # - # def helper_method - # ... - # end - # - # end - # - # == Included helper methods - # - # You can include helper methods in multiple contexts by expressing them within - # a module, and then including that module in your context: - # - # module AccountExampleHelperMethods - # def helper_method - # ... - # end - # end - # - # describe "A new account" do - # include AccountExampleHelperMethods - # before do - # @account = Account.new - # end - # - # it "should have a balance of $0" do - # helper_method - # @account.balance.should eql(Money.new(0, :dollars)) - # end - # end - # - # == Shared behaviour - # - # You can define a shared behaviour, that may be used on other behaviours - # - # describe "All Editions", :shared => true do - # it "all editions behaviour" ... - # end - # - # describe SmallEdition do - # it_should_behave_like "All Editions" - # - # it "should do small edition stuff" do - # ... - # end - # end - module Runner - class << self - def configuration # :nodoc: - @configuration ||= Spec::DSL::Configuration.new - end - - # Use this to configure various configurable aspects of - # RSpec: - # - # Spec::Runner.configure do |configuration| - # # Configure RSpec here - # end - # - # The yielded <tt>configuration</tt> object is a - # Spec::DSL::Configuration instance. See its RDoc - # for details about what you can do with it. - # - def configure - yield configuration if @configuration.nil? - end - end - end -end diff --git a/spec/lib/spec/runner/backtrace_tweaker.rb b/spec/lib/spec/runner/backtrace_tweaker.rb deleted file mode 100644 index aacc2c8b8..000000000 --- a/spec/lib/spec/runner/backtrace_tweaker.rb +++ /dev/null @@ -1,57 +0,0 @@ -module Spec - module Runner - class BacktraceTweaker - def clean_up_double_slashes(line) - line.gsub!('//','/') - end - end - - class NoisyBacktraceTweaker < BacktraceTweaker - def tweak_backtrace(error, spec_name) - return if error.backtrace.nil? - error.backtrace.each do |line| - clean_up_double_slashes(line) - end - end - end - - # Tweaks raised Exceptions to mask noisy (unneeded) parts of the backtrace - class QuietBacktraceTweaker < BacktraceTweaker - unless defined?(IGNORE_PATTERNS) - root_dir = File.expand_path(File.join(__FILE__, '..', '..', '..', '..')) - spec_files = Dir["#{root_dir}/lib/spec/*"].map do |path| - subpath = path[root_dir.length..-1] - /#{subpath}/ - end - IGNORE_PATTERNS = spec_files + [ - /\/lib\/ruby\//, - /bin\/spec:/, - /bin\/rcov:/, - /lib\/rspec_on_rails/, - /vendor\/rails/, - # TextMate's Ruby and RSpec plugins - /Ruby\.tmbundle\/Support\/tmruby.rb:/, - /RSpec\.tmbundle\/Support\/lib/, - /temp_textmate\./, - /mock_frameworks\/rspec/, - /spec_server/ - ] - end - - def tweak_backtrace(error, spec_name) - return if error.backtrace.nil? - error.backtrace.collect! do |line| - clean_up_double_slashes(line) - IGNORE_PATTERNS.each do |ignore| - if line =~ ignore - line = nil - break - end - end - line - end - error.backtrace.compact! - end - end - end -end diff --git a/spec/lib/spec/runner/behaviour_runner.rb b/spec/lib/spec/runner/behaviour_runner.rb deleted file mode 100644 index 1ac891f3c..000000000 --- a/spec/lib/spec/runner/behaviour_runner.rb +++ /dev/null @@ -1,123 +0,0 @@ -module Spec - module Runner - class BehaviourRunner - - def initialize(options, arg=nil) - @behaviours = [] - @options = options - end - - def add_behaviour(behaviour) - if !specified_examples.nil? && !specified_examples.empty? - behaviour.retain_examples_matching!(specified_examples) - end - @behaviours << behaviour if behaviour.number_of_examples != 0 && !behaviour.shared? - end - - # Runs all behaviours and returns the number of failures. - def run(paths, exit_when_done) - prepare!(paths) - begin - run_behaviours - rescue Interrupt - ensure - report_end - end - failure_count = report_dump - - heckle if(failure_count == 0 && !@options.heckle_runner.nil?) - - if(exit_when_done) - exit_code = (failure_count == 0) ? 0 : 1 - exit(exit_code) - end - failure_count - end - - def report_end - @options.reporter.end - end - - def report_dump - @options.reporter.dump - end - - def prepare!(paths) - unless paths.nil? # It's nil when running single specs with ruby - paths = find_paths(paths) - sorted_paths = sort_paths(paths) - load_specs(sorted_paths) # This will populate @behaviours via callbacks to add_behaviour - end - @options.reporter.start(number_of_examples) - @behaviours.reverse! if @options.reverse - set_sequence_numbers - end - - def run_behaviours - @behaviours.each do |behaviour| - behaviour.run(@options.reporter, @options.dry_run, @options.reverse, @options.timeout) - end - end - - def number_of_examples - @behaviours.inject(0) {|sum, behaviour| sum + behaviour.number_of_examples} - end - - FILE_SORTERS = { - 'mtime' => lambda {|file_a, file_b| File.mtime(file_b) <=> File.mtime(file_a)} - } - - def sorter(paths) - FILE_SORTERS[@options.loadby] - end - - def sort_paths(paths) - sorter = sorter(paths) - paths = paths.sort(&sorter) unless sorter.nil? - paths - end - - private - - # Sets the #number on each Example - def set_sequence_numbers - number = 0 - @behaviours.each do |behaviour| - number = behaviour.set_sequence_numbers(number, @options.reverse) - end - end - - def find_paths(paths) - result = [] - paths.each do |path| - if File.directory?(path) - result += Dir["#{path}/**/*.rb"] - elsif File.file?(path) - result << path - else - raise "File or directory not found: #{path}" - end - end - result - end - - def load_specs(paths) - paths.each do |path| - load path - end - end - - def specified_examples - @options.examples - end - - def heckle - heckle_runner = @options.heckle_runner - @options.heckle_runner = nil - behaviour_runner = self.class.new(@options) - behaviour_runner.instance_variable_set(:@behaviours, @behaviours) - heckle_runner.heckle_with(behaviour_runner) - end - end - end -end diff --git a/spec/lib/spec/runner/command_line.rb b/spec/lib/spec/runner/command_line.rb deleted file mode 100644 index 0d70337e1..000000000 --- a/spec/lib/spec/runner/command_line.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'spec/runner/option_parser' - -module Spec - module Runner - # Facade to run specs without having to fork a new ruby process (using `spec ...`) - class CommandLine - # Runs specs. +argv+ is the commandline args as per the spec commandline API, +err+ - # and +out+ are the streams output will be written to. +exit+ tells whether or - # not a system exit should be called after the specs are run and - # +warn_if_no_files+ tells whether or not a warning (the help message) - # should be printed to +err+ in case no files are specified. - def self.run(argv, err, out, exit=true, warn_if_no_files=true) - old_behaviour_runner = defined?($behaviour_runner) ? $behaviour_runner : nil - $behaviour_runner = OptionParser.new.create_behaviour_runner(argv, err, out, warn_if_no_files) - return if $behaviour_runner.nil? # This is the case if we use --drb - - $behaviour_runner.run(argv, exit) - $behaviour_runner = old_behaviour_runner - end - end - end -end diff --git a/spec/lib/spec/runner/drb_command_line.rb b/spec/lib/spec/runner/drb_command_line.rb deleted file mode 100644 index 7e745fb71..000000000 --- a/spec/lib/spec/runner/drb_command_line.rb +++ /dev/null @@ -1,21 +0,0 @@ -require "drb/drb" - -module Spec - module Runner - # Facade to run specs by connecting to a DRB server - class DrbCommandLine - # Runs specs on a DRB server. Note that this API is similar to that of - # CommandLine - making it possible for clients to use both interchangeably. - def self.run(argv, stderr, stdout, exit=true, warn_if_no_files=true) - begin - DRb.start_service - spec_server = DRbObject.new_with_uri("druby://localhost:8989") - spec_server.run(argv, stderr, stdout) - rescue DRb::DRbConnError - stderr.puts "No server is running" - exit 1 if exit - end - end - end - end -end diff --git a/spec/lib/spec/runner/extensions/kernel.rb b/spec/lib/spec/runner/extensions/kernel.rb deleted file mode 100644 index 75f2c335e..000000000 --- a/spec/lib/spec/runner/extensions/kernel.rb +++ /dev/null @@ -1,50 +0,0 @@ -module Kernel - # Creates and registers an instance of a Spec::DSL::Behaviour (or a subclass). - # The instantiated behaviour class depends on the directory of the file - # calling this method. For example, Spec::Rails will use different - # classes for specs living in <tt>spec/models</tt>, <tt>spec/helpers</tt>, - # <tt>spec/views</tt> and <tt>spec/controllers</tt>. - # - # It is also possible to override autodiscovery of the behaviour class - # with an options Hash as the last argument: - # - # describe "name", :behaviour_type => :something_special do ... - # - # The reason for using different behaviour classes is to have - # different matcher methods available from within the <tt>describe</tt> - # block. - # - # See Spec::DSL::BehaviourFactory#add_behaviour_class for details about - # how to register special Spec::DSL::Behaviour implementations. - # - def describe(*args, &block) - raise ArgumentError if args.empty? - args << {} unless Hash === args.last - args.last[:spec_path] = caller(0)[1] - register_behaviour(Spec::DSL::BehaviourFactory.create(*args, &block)) - end - alias :context :describe - - def respond_to(*names) - Spec::Matchers::RespondTo.new(*names) - end - -private - - def register_behaviour(behaviour) - if behaviour.shared? - Spec::DSL::Behaviour.add_shared_behaviour(behaviour) - else - behaviour_runner.add_behaviour(behaviour) - end - end - - def behaviour_runner - # TODO: Figure out a better way to get this considered "covered" and keep this statement on multiple lines - unless $behaviour_runner; \ - $behaviour_runner = ::Spec::Runner::OptionParser.new.create_behaviour_runner(ARGV.dup, STDERR, STDOUT, false); \ - at_exit { $behaviour_runner.run(nil, false) }; \ - end - $behaviour_runner - end -end diff --git a/spec/lib/spec/runner/extensions/object.rb b/spec/lib/spec/runner/extensions/object.rb deleted file mode 100644 index 49745352f..000000000 --- a/spec/lib/spec/runner/extensions/object.rb +++ /dev/null @@ -1,32 +0,0 @@ -# The following copyright applies to Object#copy_instance_variables_from, -# which we borrowed from active_support. -# -# Copyright (c) 2004 David Heinemeier Hansson -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -class Object - # From active_support - def copy_instance_variables_from(object, exclude = []) # :nodoc: - exclude += object.protected_instance_variables if object.respond_to? :protected_instance_variables - - instance_variables = object.instance_variables - exclude.map { |name| name.to_s } - instance_variables.each { |name| instance_variable_set(name, object.instance_variable_get(name)) } - end -end diff --git a/spec/lib/spec/runner/formatter.rb b/spec/lib/spec/runner/formatter.rb deleted file mode 100644 index 17512d958..000000000 --- a/spec/lib/spec/runner/formatter.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'spec/runner/formatter/base_formatter' -require 'spec/runner/formatter/base_text_formatter' -require 'spec/runner/formatter/progress_bar_formatter' -require 'spec/runner/formatter/rdoc_formatter' -require 'spec/runner/formatter/specdoc_formatter' -require 'spec/runner/formatter/html_formatter' -require 'spec/runner/formatter/failing_examples_formatter' -require 'spec/runner/formatter/failing_behaviours_formatter' -require 'spec/runner/formatter/snippet_extractor' diff --git a/spec/lib/spec/runner/formatter/base_formatter.rb b/spec/lib/spec/runner/formatter/base_formatter.rb deleted file mode 100644 index 7cc43ef0e..000000000 --- a/spec/lib/spec/runner/formatter/base_formatter.rb +++ /dev/null @@ -1,76 +0,0 @@ -module Spec - module Runner - module Formatter - # Baseclass for formatters that implements all required methods as no-ops. - class BaseFormatter - def initialize(where) - @where = where - end - - # This method is invoked before any examples are run, right after - # they have all been collected. This can be useful for special - # formatters that need to provide progress on feedback (graphical ones) - # - # This method will only be invoked once, and the next one to be invoked - # is #add_behaviour - def start(example_count) - end - - # This method is invoked at the beginning of the execution of each behaviour. - # +name+ is the name of the behaviour and +first+ is true if it is the - # first behaviour - otherwise it's false. - # - # The next method to be invoked after this is #example_failed or #example_finished - def add_behaviour(name) - end - - # This method is invoked when an +example+ starts. - def example_started(example) - end - - # This method is invoked when an +example+ passes. - def example_passed(example) - end - - # This method is invoked when an +example+ fails, i.e. an exception occurred - # inside it (such as a failed should or other exception). +counter+ is the - # sequence number of the failure (starting at 1) and +failure+ is the associated - # Failure object. - def example_failed(example, counter, failure) - end - - # This method is invoked when an example is not yet implemented (i.e. has not - # been provided a block), or when an ExamplePendingError is raised. - # +name+ is the name of the example. - # +message+ is the message from the ExamplePendingError, if it exists, or the - # default value of "Not Yet Implemented" - def example_pending(behaviour_name, example_name, message) - end - - # This method is invoked after all of the examples have executed. The next method - # to be invoked after this one is #dump_failure (once for each failed example), - def start_dump - end - - # Dumps detailed information about an example failure. - # This method is invoked for each failed example after all examples have run. +counter+ is the sequence number - # of the associated example. +failure+ is a Failure object, which contains detailed - # information about the failure. - def dump_failure(counter, failure) - end - - # This method is invoked after the dumping of examples and failures. - def dump_summary(duration, example_count, failure_count, pending_count) - end - - # This gets invoked after the summary if option is set to do so. - def dump_pending - end - - # This method is invoked at the very end. Allows the formatter to clean up, like closing open streams. - def close - end - end - end - end -end diff --git a/spec/lib/spec/runner/formatter/base_text_formatter.rb b/spec/lib/spec/runner/formatter/base_text_formatter.rb deleted file mode 100644 index c3cf01b76..000000000 --- a/spec/lib/spec/runner/formatter/base_text_formatter.rb +++ /dev/null @@ -1,130 +0,0 @@ -module Spec - module Runner - module Formatter - # Baseclass for text-based formatters. Can in fact be used for - # non-text based ones too - just ignore the +output+ constructor - # argument. - class BaseTextFormatter < BaseFormatter - attr_writer :dry_run - - # Creates a new instance that will write to +where+. If +where+ is a - # String, output will be written to the File with that name, otherwise - # +where+ is exected to be an IO (or an object that responds to #puts and #write). - def initialize(where) - super(where) - if where.is_a?(String) - @output = File.open(where, 'w') - elsif where == STDOUT - @output = Kernel - def @output.flush - STDOUT.flush - end - else - @output = where - end - @colour = false - @dry_run = false - @snippet_extractor = SnippetExtractor.new - @pending_examples = [] - end - - def example_pending(behaviour_name, example_name, message) - @pending_examples << ["#{behaviour_name} #{example_name}", message] - end - - def colour=(colour) - @colour = colour - begin ; require 'Win32/Console/ANSI' if @colour && PLATFORM =~ /win32/ ; rescue LoadError ; raise "You must gem install win32console to use colour on Windows" ; end - end - - def dump_failure(counter, failure) - @output.puts - @output.puts "#{counter.to_s})" - @output.puts colourise("#{failure.header}\n#{failure.exception.message}", failure) - @output.puts format_backtrace(failure.exception.backtrace) - @output.flush - end - - def colourise(s, failure) - if(failure.expectation_not_met?) - red(s) - elsif(failure.pending_fixed?) - blue(s) - else - magenta(s) - end - end - - def dump_summary(duration, example_count, failure_count, pending_count) - return if @dry_run - @output.puts - @output.puts "Finished in #{duration} seconds" - @output.puts - - summary = "#{example_count} example#{'s' unless example_count == 1}, #{failure_count} failure#{'s' unless failure_count == 1}" - summary << ", #{pending_count} pending" if pending_count > 0 - - if failure_count == 0 - if pending_count > 0 - @output.puts yellow(summary) - else - @output.puts green(summary) - end - else - @output.puts red(summary) - end - @output.flush - dump_pending - end - - def dump_pending - unless @pending_examples.empty? - @output.puts - @output.puts "Pending:" - @pending_examples.each do |pending_example| - @output.puts "#{pending_example[0]} (#{pending_example[1]})" - end - end - @output.flush - end - - def close - if IO === @output - @output.close - end - end - - def format_backtrace(backtrace) - return "" if backtrace.nil? - backtrace.map { |line| backtrace_line(line) }.join("\n") - end - - protected - - def backtrace_line(line) - line.sub(/\A([^:]+:\d+)$/, '\\1:') - end - - def colour(text, colour_code) - return text unless @colour && output_to_tty? - "#{colour_code}#{text}\e[0m" - end - - def output_to_tty? - begin - @output == Kernel || @output.tty? - rescue NoMethodError - false - end - end - - def green(text); colour(text, "\e[32m"); end - def red(text); colour(text, "\e[31m"); end - def magenta(text); colour(text, "\e[35m"); end - def yellow(text); colour(text, "\e[33m"); end - def blue(text); colour(text, "\e[34m"); end - - end - end - end -end diff --git a/spec/lib/spec/runner/formatter/failing_behaviours_formatter.rb b/spec/lib/spec/runner/formatter/failing_behaviours_formatter.rb deleted file mode 100644 index 2b3940fd3..000000000 --- a/spec/lib/spec/runner/formatter/failing_behaviours_formatter.rb +++ /dev/null @@ -1,29 +0,0 @@ -module Spec - module Runner - module Formatter - class FailingBehavioursFormatter < BaseTextFormatter - def add_behaviour(behaviour_name) - if behaviour_name =~ /(.*) \(druby.*\)$/ - @behaviour_name = $1 - else - @behaviour_name = behaviour_name - end - end - - def example_failed(example, counter, failure) - unless @behaviour_name.nil? - @output.puts @behaviour_name - @behaviour_name = nil - @output.flush - end - end - - def dump_failure(counter, failure) - end - - def dump_summary(duration, example_count, failure_count, pending_count) - end - end - end - end -end diff --git a/spec/lib/spec/runner/formatter/failing_examples_formatter.rb b/spec/lib/spec/runner/formatter/failing_examples_formatter.rb deleted file mode 100644 index 9728deaf0..000000000 --- a/spec/lib/spec/runner/formatter/failing_examples_formatter.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Spec - module Runner - module Formatter - class FailingExamplesFormatter < BaseTextFormatter - def add_behaviour(behaviour_name) - @behaviour_name = behaviour_name - end - - def example_failed(example, counter, failure) - @output.puts "#{@behaviour_name} #{example.description}" - @output.flush - end - - def dump_failure(counter, failure) - end - - def dump_summary(duration, example_count, failure_count, pending_count) - end - end - end - end -end diff --git a/spec/lib/spec/runner/formatter/html_formatter.rb b/spec/lib/spec/runner/formatter/html_formatter.rb deleted file mode 100644 index d9c422e55..000000000 --- a/spec/lib/spec/runner/formatter/html_formatter.rb +++ /dev/null @@ -1,323 +0,0 @@ -require 'erb' - -module Spec - module Runner - module Formatter - class HtmlFormatter < BaseTextFormatter - include ERB::Util # for the #h method - - def initialize(output) - super - @current_behaviour_number = 0 - @current_example_number = 0 - end - - # The number of the currently running behaviour - def current_behaviour_number - @current_behaviour_number - end - - # The number of the currently running example (a global counter) - def current_example_number - @current_example_number - end - - def start(example_count) - @example_count = example_count - - @output.puts html_header - @output.puts report_header - @output.flush - end - - def add_behaviour(name) - @behaviour_red = false - @behaviour_red = false - @current_behaviour_number += 1 - unless current_behaviour_number == 1 - @output.puts " </dl>" - @output.puts "</div>" - end - @output.puts "<div class=\"behaviour\">" - @output.puts " <dl>" - @output.puts " <dt id=\"behaviour_#{current_behaviour_number}\">#{h(name)}</dt>" - @output.flush - end - - def start_dump - @output.puts " </dl>" - @output.puts "</div>" - @output.flush - end - - def example_started(example) - @current_example_number = example.number - end - - def example_passed(example) - move_progress - @output.puts " <dd class=\"spec passed\"><span class=\"passed_spec_name\">#{h(example.description)}</span></dd>" - @output.flush - end - - def example_failed(example, counter, failure) - extra = extra_failure_content(failure) - failure_style = failure.pending_fixed? ? 'pending_fixed' : 'failed' - @output.puts " <script type=\"text/javascript\">makeRed('rspec-header');</script>" unless @header_red - @header_red = true - @output.puts " <script type=\"text/javascript\">makeRed('behaviour_#{current_behaviour_number}');</script>" unless @behaviour_red - @behaviour_red = true - move_progress - @output.puts " <dd class=\"spec #{failure_style}\">" - @output.puts " <span class=\"failed_spec_name\">#{h(example.description)}</span>" - @output.puts " <div class=\"failure\" id=\"failure_#{counter}\">" - @output.puts " <div class=\"message\"><pre>#{h(failure.exception.message)}</pre></div>" unless failure.exception.nil? - @output.puts " <div class=\"backtrace\"><pre>#{format_backtrace(failure.exception.backtrace)}</pre></div>" unless failure.exception.nil? - @output.puts extra unless extra == "" - @output.puts " </div>" - @output.puts " </dd>" - @output.flush - end - - def example_pending(behaviour_name, example_name, message) - @output.puts " <script type=\"text/javascript\">makeYellow('rspec-header');</script>" unless @header_red - @output.puts " <script type=\"text/javascript\">makeYellow('behaviour_#{current_behaviour_number}');</script>" unless @behaviour_red - move_progress - @output.puts " <dd class=\"spec not_implemented\"><span class=\"not_implemented_spec_name\">#{h(example_name)}</span></dd>" - @output.flush - end - - # Override this method if you wish to output extra HTML for a failed spec. For example, you - # could output links to images or other files produced during the specs. - # - def extra_failure_content(failure) - " <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(failure.exception)}</code></pre>" - end - - def move_progress - percent_done = @example_count == 0 ? 100.0 : ((current_example_number + 1).to_f / @example_count.to_f * 1000).to_i / 10.0 - @output.puts " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>" - @output.flush - end - - def dump_failure(counter, failure) - end - - def dump_summary(duration, example_count, failure_count, pending_count) - if @dry_run - totals = "This was a dry-run" - else - totals = "#{example_count} example#{'s' unless example_count == 1}, #{failure_count} failure#{'s' unless failure_count == 1}" - totals << ", #{pending_count} pending" if pending_count > 0 - end - @output.puts "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{duration} seconds</strong>\";</script>" - @output.puts "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{totals}\";</script>" - @output.puts "</div>" - @output.puts "</div>" - @output.puts "</body>" - @output.puts "</html>" - @output.flush - end - - def html_header - <<-EOF -<?xml version="1.0" encoding="iso-8859-1"?> -<!DOCTYPE html - PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <title>RSpec results</title> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> - <meta http-equiv="Expires" content="-1" /> - <meta http-equiv="Pragma" content="no-cache" /> - <style type="text/css"> - body { - margin: 0; - padding: 0; - background: #fff; - font-size: 80%; - } - </style> -</head> -<body> -EOF - end - - def report_header - <<-EOF -<div class="rspec-report"> - <script type="text/javascript"> - // <![CDATA[ -#{global_scripts} - // ]]> - </script> - <style type="text/css"> -#{global_styles} - </style> - -<div id="rspec-header"> - <h1>RSpec Results</h1> - - <div id="summary"> - <p id="totals"> </p> - <p id="duration"> </p> - </div> -</div> - -<div class="results"> -EOF - end - - def global_scripts - <<-EOF -function moveProgressBar(percentDone) { - document.getElementById("rspec-header").style.width = percentDone +"%"; -} -function makeRed(element_id) { - document.getElementById(element_id).style.background = '#C40D0D'; - document.getElementById(element_id).style.color = '#FFFFFF'; -} - -function makeYellow(element_id) { - if (element_id == "rspec-header" && document.getElementById(element_id).style.background != '#C40D0D') - { - document.getElementById(element_id).style.background = '#FAF834'; - document.getElementById(element_id).style.color = '#000000'; - } - else - { - document.getElementById(element_id).style.background = '#FAF834'; - document.getElementById(element_id).style.color = '#000000'; - } -} -EOF - end - - def global_styles - <<-EOF -#rspec-header { - background: #65C400; color: #fff; -} - -.rspec-report h1 { - margin: 0px 10px 0px 10px; - padding: 10px; - font-family: "Lucida Grande", Helvetica, sans-serif; - font-size: 1.8em; -} - -#summary { - margin: 0; padding: 5px 10px; - font-family: "Lucida Grande", Helvetica, sans-serif; - text-align: right; - position: absolute; - top: 0px; - right: 0px; -} - -#summary p { - margin: 0 0 0 2px; -} - -#summary #totals { - font-size: 1.2em; -} - -.behaviour { - margin: 0 10px 5px; - background: #fff; -} - -dl { - margin: 0; padding: 0 0 5px; - font: normal 11px "Lucida Grande", Helvetica, sans-serif; -} - -dt { - padding: 3px; - background: #65C400; - color: #fff; - font-weight: bold; -} - -dd { - margin: 5px 0 5px 5px; - padding: 3px 3px 3px 18px; -} - -dd.spec.passed { - border-left: 5px solid #65C400; - border-bottom: 1px solid #65C400; - background: #DBFFB4; color: #3D7700; -} - -dd.spec.failed { - border-left: 5px solid #C20000; - border-bottom: 1px solid #C20000; - color: #C20000; background: #FFFBD3; -} - -dd.spec.not_implemented { - border-left: 5px solid #FAF834; - border-bottom: 1px solid #FAF834; - background: #FCFB98; color: #131313; -} - -dd.spec.pending_fixed { - border-left: 5px solid #0000C2; - border-bottom: 1px solid #0000C2; - color: #0000C2; background: #D3FBFF; -} - -.backtrace { - color: #000; - font-size: 12px; -} - -a { - color: #BE5C00; -} - -/* Ruby code, style similar to vibrant ink */ -.ruby { - font-size: 12px; - font-family: monospace; - color: white; - background-color: black; - padding: 0.1em 0 0.2em 0; -} - -.ruby .keyword { color: #FF6600; } -.ruby .constant { color: #339999; } -.ruby .attribute { color: white; } -.ruby .global { color: white; } -.ruby .module { color: white; } -.ruby .class { color: white; } -.ruby .string { color: #66FF00; } -.ruby .ident { color: white; } -.ruby .method { color: #FFCC00; } -.ruby .number { color: white; } -.ruby .char { color: white; } -.ruby .comment { color: #9933CC; } -.ruby .symbol { color: white; } -.ruby .regex { color: #44B4CC; } -.ruby .punct { color: white; } -.ruby .escape { color: white; } -.ruby .interp { color: white; } -.ruby .expr { color: white; } - -.ruby .offending { background-color: gray; } -.ruby .linenum { - width: 75px; - padding: 0.1em 1em 0.2em 0; - color: #000000; - background-color: #FFFBD3; -} -EOF - end - end - end - end -end diff --git a/spec/lib/spec/runner/formatter/progress_bar_formatter.rb b/spec/lib/spec/runner/formatter/progress_bar_formatter.rb deleted file mode 100644 index 624f06e7c..000000000 --- a/spec/lib/spec/runner/formatter/progress_bar_formatter.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Spec - module Runner - module Formatter - class ProgressBarFormatter < BaseTextFormatter - def add_behaviour(name) - end - - def example_failed(example, counter, failure) - @output.print colourise('F', failure) - @output.flush - end - - def example_passed(example) - @output.print green('.') - @output.flush - end - - def example_pending(behaviour_name, example_name, message) - super - @output.print yellow('P') - @output.flush - end - - def start_dump - @output.puts - @output.flush - end - end - end - end -end diff --git a/spec/lib/spec/runner/formatter/rdoc_formatter.rb b/spec/lib/spec/runner/formatter/rdoc_formatter.rb deleted file mode 100644 index 0fd22ba6c..000000000 --- a/spec/lib/spec/runner/formatter/rdoc_formatter.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Spec - module Runner - module Formatter - class RdocFormatter < BaseTextFormatter - def add_behaviour(name) - @output.puts "# #{name}" - end - - def example_passed(example) - @output.puts "# * #{example.description}" - @output.flush - end - - def example_failed(example, counter, failure) - @output.puts "# * #{example.description} [#{counter} - FAILED]" - end - - def example_pending(behaviour_name, example_name, message) - @output.puts "# * #{behaviour_name} #{example_name} [PENDING: #{message}]" - end - end - end - end -end diff --git a/spec/lib/spec/runner/formatter/snippet_extractor.rb b/spec/lib/spec/runner/formatter/snippet_extractor.rb deleted file mode 100644 index 41119fe46..000000000 --- a/spec/lib/spec/runner/formatter/snippet_extractor.rb +++ /dev/null @@ -1,52 +0,0 @@ -module Spec - module Runner - module Formatter - # This class extracts code snippets by looking at the backtrace of the passed error - class SnippetExtractor #:nodoc: - class NullConverter; def convert(code, pre); code; end; end #:nodoc: - begin; require 'rubygems'; require 'syntax/convertors/html'; @@converter = Syntax::Convertors::HTML.for_syntax "ruby"; rescue LoadError => e; @@converter = NullConverter.new; end - - def snippet(error) - raw_code, line = snippet_for(error.backtrace[0]) - highlighted = @@converter.convert(raw_code, false) - highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter) - post_process(highlighted, line) - end - - def snippet_for(error_line) - if error_line =~ /(.*):(\d+)/ - file = $1 - line = $2.to_i - [lines_around(file, line), line] - else - ["# Couldn't get snippet for #{error_line}", 1] - end - end - - def lines_around(file, line) - if File.file?(file) - lines = File.open(file).read.split("\n") - min = [0, line-3].max - max = [line+1, lines.length-1].min - selected_lines = [] - selected_lines.join("\n") - lines[min..max].join("\n") - else - "# Couldn't get snippet for #{file}" - end - end - - def post_process(highlighted, offending_line) - new_lines = [] - highlighted.split("\n").each_with_index do |line, i| - new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}" - new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2 - new_lines << new_line - end - new_lines.join("\n") - end - - end - end - end -end diff --git a/spec/lib/spec/runner/formatter/specdoc_formatter.rb b/spec/lib/spec/runner/formatter/specdoc_formatter.rb deleted file mode 100644 index ad794b238..000000000 --- a/spec/lib/spec/runner/formatter/specdoc_formatter.rb +++ /dev/null @@ -1,29 +0,0 @@ -module Spec - module Runner - module Formatter - class SpecdocFormatter < BaseTextFormatter - def add_behaviour(name) - @output.puts - @output.puts name - @output.flush - end - - def example_failed(example, counter, failure) - @output.puts failure.expectation_not_met? ? red("- #{example.description} (FAILED - #{counter})") : magenta("- #{example.description} (ERROR - #{counter})") - @output.flush - end - - def example_passed(example) - @output.puts green("- #{example.description}") - @output.flush - end - - def example_pending(behaviour_name, example_name, message) - super - @output.puts yellow("- #{example_name} (PENDING: #{message})") - @output.flush - end - end - end - end -end diff --git a/spec/lib/spec/runner/heckle_runner.rb b/spec/lib/spec/runner/heckle_runner.rb deleted file mode 100644 index b6de4ef73..000000000 --- a/spec/lib/spec/runner/heckle_runner.rb +++ /dev/null @@ -1,72 +0,0 @@ -begin - require 'rubygems' - require 'heckle' -rescue LoadError ; raise "You must gem install heckle to use --heckle" ; end - -module Spec - module Runner - # Creates a new Heckler configured to heckle all methods in the classes - # whose name matches +filter+ - class HeckleRunner - def initialize(filter, heckle_class=Heckler) - @filter = filter - @heckle_class = heckle_class - end - - # Runs all the contexts held by +behaviour_runner+ once for each of the - # methods in the matched classes. - def heckle_with(behaviour_runner) - if @filter =~ /(.*)[#\.](.*)/ - heckle_method($1, $2) - else - heckle_class_or_module(@filter) - end - end - - def heckle_method(class_name, method_name) - verify_constant(class_name) - heckle = @heckle_class.new(class_name, method_name, behaviour_runner) - heckle.validate - end - - def heckle_class_or_module(class_or_module_name) - verify_constant(class_or_module_name) - pattern = /^#{class_or_module_name}/ - classes = [] - ObjectSpace.each_object(Class) do |klass| - classes << klass if klass.name =~ pattern - end - - classes.each do |klass| - klass.instance_methods(false).each do |method_name| - heckle = @heckle_class.new(klass.name, method_name, behaviour_runner) - heckle.validate - end - end - end - - def verify_constant(name) - begin - # This is defined in Heckle - name.to_class - rescue - raise "Heckling failed - \"#{name}\" is not a known class or module" - end - end - end - - #Supports Heckle 1.2 and prior (earlier versions used Heckle::Base) - class Heckler < (Heckle.const_defined?(:Base) ? Heckle::Base : Heckle) - def initialize(klass_name, method_name, behaviour_runner) - super(klass_name, method_name) - @behaviour_runner = behaviour_runner - end - - def tests_pass? - paths = [] # We can pass an empty array of paths - our specs are already loaded. - failure_count = @behaviour_runner.run(paths, false) - failure_count == 0 - end - end - end -end diff --git a/spec/lib/spec/runner/heckle_runner_unsupported.rb b/spec/lib/spec/runner/heckle_runner_unsupported.rb deleted file mode 100644 index 02aa37953..000000000 --- a/spec/lib/spec/runner/heckle_runner_unsupported.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Spec - module Runner - # Dummy implementation for Windows that just fails (Heckle is not supported on Windows) - class HeckleRunner - def initialize(filter) - raise "Heckle not supported on Windows" - end - end - end -end diff --git a/spec/lib/spec/runner/option_parser.rb b/spec/lib/spec/runner/option_parser.rb deleted file mode 100644 index 1facb85a8..000000000 --- a/spec/lib/spec/runner/option_parser.rb +++ /dev/null @@ -1,227 +0,0 @@ -require 'optparse' -require 'stringio' - -module Spec - module Runner - class OptionParser - BUILT_IN_FORMATTERS = { - 'specdoc' => Formatter::SpecdocFormatter, - 's' => Formatter::SpecdocFormatter, - 'html' => Formatter::HtmlFormatter, - 'h' => Formatter::HtmlFormatter, - 'rdoc' => Formatter::RdocFormatter, - 'r' => Formatter::RdocFormatter, - 'progress' => Formatter::ProgressBarFormatter, - 'p' => Formatter::ProgressBarFormatter, - 'failing_examples' => Formatter::FailingExamplesFormatter, - 'e' => Formatter::FailingExamplesFormatter, - 'failing_behaviours' => Formatter::FailingBehavioursFormatter, - 'b' => Formatter::FailingBehavioursFormatter - } - - COMMAND_LINE = { - :diff => ["-D", "--diff [FORMAT]", "Show diff of objects that are expected to be equal when they are not", - "Builtin formats: unified|u|context|c", - "You can also specify a custom differ class", - "(in which case you should also specify --require)"], - :colour => ["-c", "--colour", "--color", "Show coloured (red/green) output"], - :example => ["-e", "--example [NAME|FILE_NAME]", "Execute example(s) with matching name(s). If the argument is", - "the path to an existing file (typically generated by a previous", - "run using --format failing_examples:file.txt), then the examples", - "on each line of thatfile will be executed. If the file is empty,", - "all examples will be run (as if --example was not specified).", - " ", - "If the argument is not an existing file, then it is treated as", - "an example name directly, causing RSpec to run just the example", - "matching that name"], - :specification => ["-s", "--specification [NAME]", "DEPRECATED - use -e instead", "(This will be removed when autotest works with -e)"], - :line => ["-l", "--line LINE_NUMBER", Integer, "Execute behaviout or specification at given line.", - "(does not work for dynamically generated specs)"], - :format => ["-f", "--format FORMAT[:WHERE]", "Specifies what format to use for output. Specify WHERE to tell", - "the formatter where to write the output. All built-in formats", - "expect WHERE to be a file name, and will write to STDOUT if it's", - "not specified. The --format option may be specified several times", - "if you want several outputs", - " ", - "Builtin formats: ", - "progress|p : Text progress", - "specdoc|s : Behaviour doc as text", - "rdoc|r : Behaviour doc as RDoc", - "html|h : A nice HTML report", - "failing_examples|e : Write all failing examples - input for --example", - "failing_behaviours|b : Write all failing behaviours - input for --example", - " ", - "FORMAT can also be the name of a custom formatter class", - "(in which case you should also specify --require to load it)"], - :require => ["-r", "--require FILE", "Require FILE before running specs", - "Useful for loading custom formatters or other extensions.", - "If this option is used it must come before the others"], - :backtrace => ["-b", "--backtrace", "Output full backtrace"], - :loadby => ["-L", "--loadby STRATEGY", "Specify the strategy by which spec files should be loaded.", - "STRATEGY can currently only be 'mtime' (File modification time)", - "By default, spec files are loaded in alphabetical order if --loadby", - "is not specified."], - :reverse => ["-R", "--reverse", "Run examples in reverse order"], - :timeout => ["-t", "--timeout FLOAT", "Interrupt and fail each example that doesn't complete in the", - "specified time"], - :heckle => ["-H", "--heckle CODE", "If all examples pass, this will mutate the classes and methods", - "identified by CODE little by little and run all the examples again", - "for each mutation. The intent is that for each mutation, at least", - "one example *should* fail, and RSpec will tell you if this is not the", - "case. CODE should be either Some::Module, Some::Class or", - "Some::Fabulous#method}"], - :dry_run => ["-d", "--dry-run", "Invokes formatters without executing the examples."], - :options_file => ["-O", "--options PATH", "Read options from a file"], - :generate_options => ["-G", "--generate-options PATH", "Generate an options file for --options"], - :runner => ["-U", "--runner RUNNER", "Use a custom BehaviourRunner."], - :drb => ["-X", "--drb", "Run examples via DRb. (For example against script/spec_server)"], - :version => ["-v", "--version", "Show version"], - :help => ["-h", "--help", "You're looking at it"] - } - - def initialize - @spec_parser = SpecParser.new - @file_factory = File - end - - def create_behaviour_runner(args, err, out, warn_if_no_files) - options = parse(args, err, out, warn_if_no_files) - # Some exit points in parse (--generate-options, --drb) don't return the options, - # but hand over control. In that case we don't want to continue. - return nil unless options.is_a?(Options) - options.configure - options.behaviour_runner - end - - def parse(args, err, out, warn_if_no_files) - options_file = nil - args_copy = args.dup - options = Options.new(err, out) - - opts = ::OptionParser.new do |opts| - opts.banner = "Usage: spec (FILE|DIRECTORY|GLOB)+ [options]" - opts.separator "" - - def opts.rspec_on(name, &block) - on(*COMMAND_LINE[name], &block) - end - - opts.rspec_on(:diff) {|diff| options.parse_diff(diff)} - - opts.rspec_on(:colour) {options.colour = true} - - opts.rspec_on(:example) {|example| options.parse_example(example)} - - opts.rspec_on(:specification) {|example| options.parse_example(example)} - - opts.rspec_on(:line) {|line_number| options.line_number = line_number.to_i} - - opts.rspec_on(:format) {|format| options.parse_format(format)} - - opts.rspec_on(:require) {|req| options.parse_require(req)} - - opts.rspec_on(:backtrace) {options.backtrace_tweaker = NoisyBacktraceTweaker.new} - - opts.rspec_on(:loadby) {|loadby| options.loadby = loadby} - - opts.rspec_on(:reverse) {options.reverse = true} - - opts.rspec_on(:timeout) {|timeout| options.timeout = timeout.to_f} - - opts.rspec_on(:heckle) {|heckle| options.parse_heckle(heckle)} - - opts.rspec_on(:dry_run) {options.dry_run = true} - - opts.rspec_on(:options_file) do |options_file| - return parse_options_file(options_file, out, err, args_copy, warn_if_no_files) - end - - opts.rspec_on(:generate_options) do |options_file| - options.parse_generate_options(options_file, args_copy, out) - end - - opts.rspec_on(:runner) do |runner| - options.runner_arg = runner - end - - opts.rspec_on(:drb) do - return parse_drb(args_copy, out, err, warn_if_no_files) - end - - opts.rspec_on(:version) {parse_version(out)} - - opts.on_tail(*COMMAND_LINE[:help]) {parse_help(opts, out)} - end - opts.parse!(args) - - if args.empty? && warn_if_no_files - err.puts "No files specified." - err.puts opts - exit(6) if err == $stderr - end - - if options.line_number - set_spec_from_line_number(options, args, err) - end - - if options.formatters.empty? - options.formatters << Formatter::ProgressBarFormatter.new(out) - end - - options - end - - def parse_options_file(options_file, out_stream, error_stream, args_copy, warn_if_no_files) - # Remove the --options option and the argument before writing to file - index = args_copy.index("-O") || args_copy.index("--options") - args_copy.delete_at(index) - args_copy.delete_at(index) - - new_args = args_copy + IO.readlines(options_file).map {|l| l.chomp.split " "}.flatten - return CommandLine.run(new_args, error_stream, out_stream, true, warn_if_no_files) - end - - def parse_drb(args_copy, out_stream, error_stream, warn_if_no_files) - # Remove the --drb option - index = args_copy.index("-X") || args_copy.index("--drb") - args_copy.delete_at(index) - - return DrbCommandLine.run(args_copy, error_stream, out_stream, true, warn_if_no_files) - end - - def parse_version(out_stream) - out_stream.puts ::Spec::VERSION::DESCRIPTION - exit if out_stream == $stdout - end - - def parse_help(opts, out_stream) - out_stream.puts opts - exit if out_stream == $stdout - end - - def set_spec_from_line_number(options, args, err) - if options.examples.empty? - if args.length == 1 - if @file_factory.file?(args[0]) - source = @file_factory.open(args[0]) - example = @spec_parser.spec_name_for(source, options.line_number) - options.parse_example(example) - elsif @file_factory.directory?(args[0]) - err.puts "You must specify one file, not a directory when using the --line option" - exit(1) if err == $stderr - else - err.puts "#{args[0]} does not exist" - exit(2) if err == $stderr - end - else - err.puts "Only one file can be specified when using the --line option: #{args.inspect}" - exit(3) if err == $stderr - end - else - err.puts "You cannot use both --line and --example" - exit(4) if err == $stderr - end - end - end - end -end diff --git a/spec/lib/spec/runner/options.rb b/spec/lib/spec/runner/options.rb deleted file mode 100644 index a940133eb..000000000 --- a/spec/lib/spec/runner/options.rb +++ /dev/null @@ -1,175 +0,0 @@ -module Spec - module Runner - class Options - BUILT_IN_FORMATTERS = { - 'specdoc' => Formatter::SpecdocFormatter, - 's' => Formatter::SpecdocFormatter, - 'html' => Formatter::HtmlFormatter, - 'h' => Formatter::HtmlFormatter, - 'rdoc' => Formatter::RdocFormatter, - 'r' => Formatter::RdocFormatter, - 'progress' => Formatter::ProgressBarFormatter, - 'p' => Formatter::ProgressBarFormatter, - 'failing_examples' => Formatter::FailingExamplesFormatter, - 'e' => Formatter::FailingExamplesFormatter, - 'failing_behaviours' => Formatter::FailingBehavioursFormatter, - 'b' => Formatter::FailingBehavioursFormatter - } - - attr_accessor( - :backtrace_tweaker, - :colour, - :context_lines, - :diff_format, - :differ_class, - :dry_run, - :examples, - :failure_file, - :formatters, - :generate, - :heckle_runner, - :line_number, - :loadby, - :reporter, - :reverse, - :timeout, - :verbose, - :runner_arg, - :behaviour_runner - ) - - def initialize(err, out) - @err, @out = err, out - @backtrace_tweaker = QuietBacktraceTweaker.new - @examples = [] - @formatters = [] - @colour = false - @dry_run = false - end - - def configure - configure_formatters - create_reporter - configure_differ - create_behaviour_runner - end - - def create_behaviour_runner - return nil if @generate - @behaviour_runner = if @runner_arg - klass_name, arg = split_at_colon(@runner_arg) - runner_type = load_class(klass_name, 'behaviour runner', '--runner') - runner_type.new(self, arg) - else - BehaviourRunner.new(self) - end - end - - def configure_formatters - @formatters.each do |formatter| - formatter.colour = @colour if formatter.respond_to?(:colour=) - formatter.dry_run = @dry_run if formatter.respond_to?(:dry_run=) - end - end - - def create_reporter - @reporter = Reporter.new(@formatters, @backtrace_tweaker) - end - - def configure_differ - if @differ_class - Spec::Expectations.differ = @differ_class.new(@diff_format, @context_lines, @colour) - end - end - - def parse_diff(format) - @context_lines = 3 - case format - when :context, 'context', 'c' - @diff_format = :context - when :unified, 'unified', 'u', '', nil - @diff_format = :unified - end - - if [:context,:unified].include? @diff_format - require 'spec/expectations/differs/default' - @differ_class = Spec::Expectations::Differs::Default - else - @diff_format = :custom - @differ_class = load_class(format, 'differ', '--diff') - end - end - - def parse_example(example) - if(File.file?(example)) - @examples = File.open(example).read.split("\n") - else - @examples = [example] - end - end - - def parse_format(format_arg) - format, where = split_at_colon(format_arg) - # This funky regexp checks whether we have a FILE_NAME or not - if where.nil? - raise "When using several --format options only one of them can be without a file" if @out_used - where = @out - @out_used = true - end - - formatter_type = BUILT_IN_FORMATTERS[format] || load_class(format, 'formatter', '--format') - @formatters << formatter_type.new(where) - end - - def parse_require(req) - req.split(",").each{|file| require file} - end - - def parse_heckle(heckle) - heckle_require = [/mswin/, /java/].detect{|p| p =~ RUBY_PLATFORM} ? 'spec/runner/heckle_runner_unsupported' : 'spec/runner/heckle_runner' - require heckle_require - @heckle_runner = HeckleRunner.new(heckle) - end - - def parse_generate_options(options_file, args_copy, out_stream) - # Remove the --generate-options option and the argument before writing to file - index = args_copy.index("-G") || args_copy.index("--generate-options") - args_copy.delete_at(index) - args_copy.delete_at(index) - File.open(options_file, 'w') do |io| - io.puts args_copy.join("\n") - end - out_stream.puts "\nOptions written to #{options_file}. You can now use these options with:" - out_stream.puts "spec --options #{options_file}" - @generate = true - end - - def split_at_colon(s) - if s =~ /([a-zA-Z_]+(?:::[a-zA-Z_]+)*):?(.*)/ - arg = $2 == "" ? nil : $2 - [$1, arg] - else - raise "Couldn't parse #{s.inspect}" - end - end - - def load_class(name, kind, option) - if name =~ /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ - arg = $2 == "" ? nil : $2 - [$1, arg] - else - m = "#{name.inspect} is not a valid class name" - @err.puts m - raise m - end - begin - eval(name, binding, __FILE__, __LINE__) - rescue NameError => e - @err.puts "Couldn't find #{kind} class #{name}" - @err.puts "Make sure the --require option is specified *before* #{option}" - if $_spec_spec ; raise e ; else exit(1) ; end - end - end - end - end -end diff --git a/spec/lib/spec/runner/reporter.rb b/spec/lib/spec/runner/reporter.rb deleted file mode 100644 index b1dc2a27a..000000000 --- a/spec/lib/spec/runner/reporter.rb +++ /dev/null @@ -1,125 +0,0 @@ -module Spec - module Runner - class Reporter - - def initialize(formatters, backtrace_tweaker) - @formatters = formatters - @backtrace_tweaker = backtrace_tweaker - clear! - end - - def add_behaviour(name) - @formatters.each{|f| f.add_behaviour(name)} - @behaviour_names << name - end - - def example_started(name) - @formatters.each{|f| f.example_started(name)} - end - - def example_finished(name, error=nil, failure_location=nil, not_implemented = false) - @example_names << name - - if not_implemented - example_pending(@behaviour_names.last, name) - elsif error.nil? - example_passed(name) - elsif Spec::DSL::ExamplePendingError === error - example_pending(@behaviour_names.last, name, error.message) - else - example_failed(name, error, failure_location) - end - end - - def start(number_of_examples) - clear! - @start_time = Time.new - @formatters.each{|f| f.start(number_of_examples)} - end - - def end - @end_time = Time.new - end - - # Dumps the summary and returns the total number of failures - def dump - @formatters.each{|f| f.start_dump} - dump_failures - @formatters.each do |f| - f.dump_summary(duration, @example_names.length, @failures.length, @pending_count) - f.close - end - @failures.length - end - - private - - def clear! - @behaviour_names = [] - @failures = [] - @pending_count = 0 - @example_names = [] - @start_time = nil - @end_time = nil - end - - def dump_failures - return if @failures.empty? - @failures.inject(1) do |index, failure| - @formatters.each{|f| f.dump_failure(index, failure)} - index + 1 - end - end - - def duration - return @end_time - @start_time unless (@end_time.nil? or @start_time.nil?) - return "0.0" - end - - def example_passed(name) - @formatters.each{|f| f.example_passed(name)} - end - - def example_failed(name, error, failure_location) - @backtrace_tweaker.tweak_backtrace(error, failure_location) - example_name = "#{@behaviour_names.last} #{name}" - failure = Failure.new(example_name, error) - @failures << failure - @formatters.each{|f| f.example_failed(name, @failures.length, failure)} - end - - def example_pending(behaviour_name, example_name, message="Not Yet Implemented") - @pending_count += 1 - @formatters.each{|f| f.example_pending(behaviour_name, example_name, message)} - end - - class Failure - attr_reader :exception - - def initialize(example_name, exception) - @example_name = example_name - @exception = exception - end - - def header - if expectation_not_met? - "'#{@example_name}' FAILED" - elsif pending_fixed? - "'#{@example_name}' FIXED" - else - "#{@exception.class.name} in '#{@example_name}'" - end - end - - def pending_fixed? - @exception.is_a?(Spec::DSL::PendingFixedError) - end - - def expectation_not_met? - @exception.is_a?(Spec::Expectations::ExpectationNotMetError) - end - - end - end - end -end diff --git a/spec/lib/spec/runner/spec_parser.rb b/spec/lib/spec/runner/spec_parser.rb deleted file mode 100644 index bc9170065..000000000 --- a/spec/lib/spec/runner/spec_parser.rb +++ /dev/null @@ -1,50 +0,0 @@ -module Spec - module Runner - # Parses a spec file and finds the nearest example for a given line number. - class SpecParser - def spec_name_for(io, line_number) - source = io.read - behaviour, behaviour_line = behaviour_at_line(source, line_number) - example, example_line = example_at_line(source, line_number) - if behaviour && example && (behaviour_line < example_line) - "#{behaviour} #{example}" - elsif behaviour - behaviour - else - nil - end - end - - protected - - def behaviour_at_line(source, line_number) - find_above(source, line_number, /^\s*(context|describe)\s+(.*)\s+do/) - end - - def example_at_line(source, line_number) - find_above(source, line_number, /^\s*(specify|it)\s+(.*)\s+do/) - end - - # Returns the context/describe or specify/it name and the line number - def find_above(source, line_number, pattern) - lines_above_reversed(source, line_number).each_with_index do |line, n| - return [parse_description($2), line_number-n] if line =~ pattern - end - nil - end - - def lines_above_reversed(source, line_number) - lines = source.split("\n") - lines[0...line_number].reverse - end - - def parse_description(str) - return str[1..-2] if str =~ /^['"].*['"]$/ - if matches = /^(.*)\s*,\s*['"](.*)['"]$/.match(str) - return ::Spec::DSL::Description.generate_description(matches[1], matches[2]) - end - return str - end - end - end -end diff --git a/spec/lib/spec/test_case_adapter.rb b/spec/lib/spec/test_case_adapter.rb deleted file mode 100755 index 992e098fd..000000000 --- a/spec/lib/spec/test_case_adapter.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'spec/expectations' -require 'spec/matchers' - -module Test - module Unit - class TestCase - include Spec::Matchers - end - end -end diff --git a/spec/lib/spec/translator.rb b/spec/lib/spec/translator.rb deleted file mode 100644 index c1e07eda4..000000000 --- a/spec/lib/spec/translator.rb +++ /dev/null @@ -1,114 +0,0 @@ -require 'fileutils' - -module Spec - class Translator - def translate(from, to) - from = File.expand_path(from) - to = File.expand_path(to) - if File.directory?(from) - translate_dir(from, to) - elsif(from =~ /\.rb$/) - translate_file(from, to) - end - end - - def translate_dir(from, to) - FileUtils.mkdir_p(to) unless File.directory?(to) - Dir["#{from}/*"].each do |sub_from| - path = sub_from[from.length+1..-1] - sub_to = File.join(to, path) - translate(sub_from, sub_to) - end - end - - def translate_file(from, to) - translation = "" - File.open(from) do |io| - io.each_line do |line| - translation << translate_line(line) - end - end - File.open(to, "w") do |io| - io.write(translation) - end - end - - def translate_line(line) - # Translate deprecated mock constraints - line.gsub!(/:any_args/, 'any_args') - line.gsub!(/:anything/, 'anything') - line.gsub!(/:boolean/, 'boolean') - line.gsub!(/:no_args/, 'no_args') - line.gsub!(/:numeric/, 'an_instance_of(Numeric)') - line.gsub!(/:string/, 'an_instance_of(String)') - - return line if line =~ /(should_not|should)_receive/ - - line.gsub!(/(^\s*)context([\s*|\(]['|"|A-Z])/, '\1describe\2') - line.gsub!(/(^\s*)specify([\s*|\(]['|"|A-Z])/, '\1it\2') - line.gsub!(/(^\s*)context_setup(\s*[do|\{])/, '\1before(:all)\2') - line.gsub!(/(^\s*)context_teardown(\s*[do|\{])/, '\1after(:all)\2') - line.gsub!(/(^\s*)setup(\s*[do|\{])/, '\1before(:each)\2') - line.gsub!(/(^\s*)teardown(\s*[do|\{])/, '\1after(:each)\2') - - if line =~ /(.*\.)(should_not|should)(?:_be)(?!_)(.*)/m - pre = $1 - should = $2 - post = $3 - be_or_equal = post =~ /(<|>)/ ? "be" : "equal" - - return "#{pre}#{should} #{be_or_equal}#{post}" - end - - if line =~ /(.*\.)(should_not|should)_(?!not)\s*(.*)/m - pre = $1 - should = $2 - post = $3 - - post.gsub!(/^raise/, 'raise_error') - post.gsub!(/^throw/, 'throw_symbol') - - unless standard_matcher?(post) - post = "be_#{post}" - end - - # Add parenthesis - post.gsub!(/^(\w+)\s+([\w|\.|\,|\(.*\)|\'|\"|\:|@| ]+)(\})/, '\1(\2)\3') # inside a block - post.gsub!(/^(redirect_to)\s+(.*)/, '\1(\2)') # redirect_to, which often has http: - post.gsub!(/^(\w+)\s+([\w|\.|\,|\(.*\)|\{.*\}|\'|\"|\:|@| ]+)/, '\1(\2)') - post.gsub!(/(\s+\))/, ')') - post.gsub!(/\)\}/, ') }') - post.gsub!(/^(\w+)\s+(\/.*\/)/, '\1(\2)') #regexps - line = "#{pre}#{should} #{post}" - end - - line - end - - def standard_matcher?(matcher) - patterns = [ - /^be/, - /^be_close/, - /^eql/, - /^equal/, - /^has/, - /^have/, - /^change/, - /^include/, - /^match/, - /^raise_error/, - /^respond_to/, - /^redirect_to/, - /^satisfy/, - /^throw_symbol/, - # Extra ones that we use in spec_helper - /^pass/, - /^fail/, - /^fail_with/, - ] - matched = patterns.detect{ |p| matcher =~ p } - !matched.nil? - end - - end -end diff --git a/spec/lib/spec/version.rb b/spec/lib/spec/version.rb deleted file mode 100644 index 5b1db9b37..000000000 --- a/spec/lib/spec/version.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Spec - module VERSION - unless defined? MAJOR - MAJOR = 1 - MINOR = 0 - TINY = 8 - RELEASE_CANDIDATE = nil - - # RANDOM_TOKEN: 0.510454315029681 - REV = "$LastChangedRevision: 2338 $".match(/LastChangedRevision: (\d+)/)[1] - - STRING = [MAJOR, MINOR, TINY].join('.') - TAG = "REL_#{[MAJOR, MINOR, TINY, RELEASE_CANDIDATE].compact.join('_')}".upcase.gsub(/\.|-/, '_') - FULL_VERSION = "#{[MAJOR, MINOR, TINY, RELEASE_CANDIDATE].compact.join('.')} (r#{REV})" - - NAME = "RSpec" - URL = "http://rspec.rubyforge.org/" - - DESCRIPTION = "#{NAME}-#{FULL_VERSION} - BDD for Ruby\n#{URL}" - end - end -end - diff --git a/spec/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb b/spec/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb new file mode 100644 index 000000000..e2f3d4728 --- /dev/null +++ b/spec/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb @@ -0,0 +1,43 @@ +dir = File.expand_path(File.dirname(__FILE__)) +[ "#{dir}/../../lib", "#{dir}/../../test/lib"].each do |dir| + fulldir = File.expand_path(dir) + $LOAD_PATH.unshift(fulldir) unless $LOAD_PATH.include?(fulldir) +end + +require 'spec' +require 'puppettest' +require 'puppettest/runnable_test' + +module Spec + module Runner + class ExampleGroupRunner + def run + prepare + success = true + example_groups.each do |example_group| + next unless example_group.runnable? + success = success & example_group.run + end + return success + ensure + finish + end + end + end +end + +module Spec + module Example + class ExampleGroup + extend PuppetTest::RunnableTest + end + end +end + +module Test + module Unit + class TestCase + extend PuppetTest::RunnableTest + end + end +end diff --git a/spec/plugins/mock_frameworks/flexmock.rb b/spec/plugins/mock_frameworks/flexmock.rb deleted file mode 100644 index 6875a5222..000000000 --- a/spec/plugins/mock_frameworks/flexmock.rb +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Jim Weirich on 2007-04-10. -# Copyright (c) 2007. All rights reserved. - -require 'flexmock/rspec' - -module Spec - module Plugins - module MockFramework - include FlexMock::MockContainer - def setup_mocks_for_rspec - # No setup required - end - def verify_mocks_for_rspec - flexmock_verify - end - def teardown_mocks_for_rspec - flexmock_close - end - end - end -end diff --git a/spec/plugins/mock_frameworks/mocha.rb b/spec/plugins/mock_frameworks/mocha.rb deleted file mode 100644 index 69d11636c..000000000 --- a/spec/plugins/mock_frameworks/mocha.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'mocha/standalone' -require 'mocha/object' - -module Spec - module Plugins - module MockFramework - include Mocha::Standalone - def setup_mocks_for_rspec - mocha_setup - end - def verify_mocks_for_rspec - mocha_verify - end - def teardown_mocks_for_rspec - mocha_teardown - end - end - end -end diff --git a/spec/plugins/mock_frameworks/rr.rb b/spec/plugins/mock_frameworks/rr.rb deleted file mode 100644 index c019c18a1..000000000 --- a/spec/plugins/mock_frameworks/rr.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'rr' - -patterns = ::Spec::Runner::QuietBacktraceTweaker::IGNORE_PATTERNS -patterns.push(RR::Errors::BACKTRACE_IDENTIFIER) - -module Spec - module Plugins - module MockFramework - include RR::Extensions::InstanceMethods - def setup_mocks_for_rspec - RR::Space.instance.reset - end - def verify_mocks_for_rspec - RR::Space.instance.verify_doubles - end - def teardown_mocks_for_rspec - RR::Space.instance.reset - end - end - end -end diff --git a/spec/plugins/mock_frameworks/rspec.rb b/spec/plugins/mock_frameworks/rspec.rb deleted file mode 100644 index e606c3089..000000000 --- a/spec/plugins/mock_frameworks/rspec.rb +++ /dev/null @@ -1,18 +0,0 @@ -require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib", "spec", "mocks")) - -module Spec - module Plugins - module MockFramework - include Spec::Mocks::SpecMethods - def setup_mocks_for_rspec - $rspec_mocks ||= Spec::Mocks::Space.new - end - def verify_mocks_for_rspec - $rspec_mocks.verify_all - end - def teardown_mocks_for_rspec - $rspec_mocks.reset_all - end - end - end -end diff --git a/spec/lib/shared_behaviours/file_server_terminus.rb b/spec/shared_behaviours/file_server_terminus.rb index de08f29fc..de08f29fc 100644 --- a/spec/lib/shared_behaviours/file_server_terminus.rb +++ b/spec/shared_behaviours/file_server_terminus.rb diff --git a/spec/lib/shared_behaviours/file_serving.rb b/spec/shared_behaviours/file_serving.rb index b5ab6b0fd..b5ab6b0fd 100644 --- a/spec/lib/shared_behaviours/file_serving.rb +++ b/spec/shared_behaviours/file_serving.rb diff --git a/spec/spec.opts b/spec/spec.opts index 2cac5f260..2f9bf0d0a 100644 --- a/spec/spec.opts +++ b/spec/spec.opts @@ -1,5 +1,3 @@ --colour ---format -s --loadby -mtime
\ No newline at end of file +mtime diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bfac9095f..db14b47cb 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,22 +1,36 @@ dir = File.expand_path(File.dirname(__FILE__)) -$LOAD_PATH.unshift("#{dir}/lib") + +$LOAD_PATH.unshift("#{dir}/") $LOAD_PATH.unshift("#{dir}/../lib") $LOAD_PATH.unshift("#{dir}/../test/lib") # Add the old test dir, so that we can still find our local mocha and spec +# include any gems in vendor/gems +Dir["#{dir}/../vendor/gems/**"].each do |path| + libpath = File.join(path, "lib") + if File.directory?(libpath) + $LOAD_PATH.unshift(libpath) + else + $LOAD_PATH.unshift(path) + end +end + require 'puppettest' require 'puppettest/runnable_test' require 'mocha' require 'spec' +# load any monkey-patches +Dir["#{dir}/monkey_patches/*.rb"].map { |file| require file } + Spec::Runner.configure do |config| config.mock_with :mocha - config.prepend_before :each do - setup() if respond_to? :setup - end - config.prepend_after :each do - teardown() if respond_to? :teardown - end +# config.prepend_before :all do +# setup_mocks_for_rspec +# setup() if respond_to? :setup +# end +# +# config.prepend_after :all do +# teardown() if respond_to? :teardown +# end end - -require "#{dir}/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl" diff --git a/spec/unit/file_serving/configuration.rb b/spec/unit/file_serving/configuration.rb index df46b9b6a..eecaefe5f 100755 --- a/spec/unit/file_serving/configuration.rb +++ b/spec/unit/file_serving/configuration.rb @@ -4,17 +4,6 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/file_serving/configuration' -module FSConfigurationTesting - def setup - @path = "/path/to/configuration/file.conf" - Puppet.settings.stubs(:value).with(:fileserverconfig).returns(@path) - end - - def teardown - Puppet::FileServing::Configuration.clear_cache - end -end - describe Puppet::FileServing::Configuration do it "should make :new a private method" do proc { Puppet::FileServing::Configuration.new }.should raise_error @@ -35,193 +24,201 @@ describe Puppet::FileServing::Configuration do end end -describe Puppet::FileServing::Configuration, " when initializing" do - include FSConfigurationTesting - - it "should work without a configuration file" do - FileTest.stubs(:exists?).with(@path).returns(false) - proc { Puppet::FileServing::Configuration.create }.should_not raise_error - end - - it "should parse the configuration file if present" do - FileTest.stubs(:exists?).with(@path).returns(true) - @parser = mock 'parser' - @parser.expects(:parse).returns({}) - Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) - Puppet::FileServing::Configuration.create - end - - it "should determine the path to the configuration file from the Puppet settings" do - Puppet::FileServing::Configuration.create - end -end - -describe Puppet::FileServing::Configuration, " when parsing the configuration file" do - include FSConfigurationTesting - - before do - FileTest.stubs(:exists?).with(@path).returns(true) - @parser = mock 'parser' - Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) - end - - it "should set the mount list to the results of parsing" do - @parser.expects(:parse).returns("one" => mock("mount")) - config = Puppet::FileServing::Configuration.create - config.mounted?("one").should be_true - end - - it "should not raise exceptions" do - @parser.expects(:parse).raises(ArgumentError) - proc { Puppet::FileServing::Configuration.create }.should_not raise_error - end - - it "should replace the existing mount list with the results of reparsing" do - @parser.expects(:parse).returns("one" => mock("mount")) - config = Puppet::FileServing::Configuration.create - config.mounted?("one").should be_true - # Now parse again - @parser.expects(:parse).returns("two" => mock('other')) - config.send(:readconfig, false) - config.mounted?("one").should be_false - config.mounted?("two").should be_true - end - - it "should not replace the mount list until the file is entirely parsed successfully" do - @parser.expects(:parse).returns("one" => mock("mount")) - @parser.expects(:parse).raises(ArgumentError) - config = Puppet::FileServing::Configuration.create - # Now parse again, so the exception gets thrown - config.send(:readconfig, false) - config.mounted?("one").should be_true - end -end - -describe Puppet::FileServing::Configuration, " when finding files" do - include FSConfigurationTesting - - before do - @parser = mock 'parser' - @parser.stubs(:changed?).returns true - FileTest.stubs(:exists?).with(@path).returns(true) - Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) - - @mount1 = stub 'mount', :name => "one" - @mounts = {"one" => @mount1} - - Facter.stubs(:value).with("hostname").returns("whatever") - - @config = Puppet::FileServing::Configuration.create - end - - it "should fail if the uri does not match a leading slash followed by a valid mount name" do - @parser.expects(:parse).returns(@mounts) - proc { @config.file_path("something") }.should raise_error(ArgumentError) - end - - it "should use the first term after the first slash for the mount name" do - @parser.expects(:parse).returns(@mounts) - FileTest.stubs(:exists?).returns(true) - @mount1.expects(:file) - @config.file_path("/one") - end - - it "should use the remainder of the URI after the mount name as the file name" do - @parser.expects(:parse).returns(@mounts) - @mount1.expects(:file).with("something/else", {}) - FileTest.stubs(:exists?).returns(true) - @config.file_path("/one/something/else") - end - - it "should treat a bare name as a mount and no relative file" do - @parser.expects(:parse).returns(@mounts) - @mount1.expects(:file).with(nil, {}) - FileTest.stubs(:exists?).returns(true) - @config.file_path("/one") - end - - it "should treat a name with a trailing slash equivalently to a name with no trailing slash" do - @parser.expects(:parse).returns(@mounts) - @mount1.expects(:file).with(nil, {}) - FileTest.stubs(:exists?).returns(true) - @config.file_path("/one/") - end - - it "should return nil if the mount cannot be found" do - @parser.expects(:changed?).returns(true) - @parser.expects(:parse).returns({}) - @config.file_path("/one/something").should be_nil - end - - it "should return nil if the mount does not contain the file" do - @parser.expects(:parse).returns(@mounts) - @mount1.expects(:file).with("something/else", {}).returns(nil) - @config.file_path("/one/something/else").should be_nil - end - - it "should return the fully qualified path if the mount exists" do - @parser.expects(:parse).returns(@mounts) - @mount1.expects(:file).with("something/else", {}).returns("/full/path") - @config.file_path("/one/something/else").should == "/full/path" - end - - it "should reparse the configuration file when it has changed" do - @mount1.stubs(:file).returns("whatever") - @parser.expects(:changed?).returns(true) - @parser.expects(:parse).returns(@mounts) - FileTest.stubs(:exists?).returns(true) - @config.file_path("/one/something") - - @parser.expects(:changed?).returns(true) - @parser.expects(:parse).returns({}) - @config.file_path("/one/something").should be_nil - end -end - -describe Puppet::FileServing::Configuration, " when checking authorization" do - include FSConfigurationTesting - - before do - @parser = mock 'parser' - @parser.stubs(:changed?).returns true - FileTest.stubs(:exists?).with(@path).returns(true) - Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) - - @mount1 = stub 'mount', :name => "one" - @mounts = {"one" => @mount1} - @parser.stubs(:parse).returns(@mounts) - - Facter.stubs(:value).with("hostname").returns("whatever") - - @config = Puppet::FileServing::Configuration.create - end - - it "should return false if the mount cannot be found" do - @config.authorized?("/nope/my/file").should be_false - end - - it "should use the mount to determine authorization" do - @mount1.expects(:allowed?) - @config.authorized?("/one/my/file") - end - - it "should pass the client's name to the mount if provided" do - @mount1.expects(:allowed?).with("myhost", nil) - @config.authorized?("/one/my/file", :node => "myhost") - end +describe Puppet::FileServing::Configuration do - it "should pass the client's IP to the mount if provided" do - @mount1.expects(:allowed?).with("myhost", "myip") - @config.authorized?("/one/my/file", :node => "myhost", :ipaddress => "myip") + before :each do + @path = "/path/to/configuration/file.conf" + Puppet.settings.stubs(:value).with(:fileserverconfig).returns(@path) end - it "should return true if the mount allows the client" do - @mount1.expects(:allowed?).returns(true) - @config.authorized?("/one/my/file").should be_true + after :each do + Puppet::FileServing::Configuration.clear_cache end - it "should return false if the mount denies the client" do - @mount1.expects(:allowed?).returns(false) - @config.authorized?("/one/my/file").should be_false - end -end + describe Puppet::FileServing::Configuration, " when initializing" do + + it "should work without a configuration file" do + FileTest.stubs(:exists?).with(@path).returns(false) + proc { Puppet::FileServing::Configuration.create }.should_not raise_error + end + + it "should parse the configuration file if present" do + FileTest.stubs(:exists?).with(@path).returns(true) + @parser = mock 'parser' + @parser.expects(:parse).returns({}) + Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) + Puppet::FileServing::Configuration.create + end + + it "should determine the path to the configuration file from the Puppet settings" do + Puppet::FileServing::Configuration.create + end + end + + describe Puppet::FileServing::Configuration, " when parsing the configuration file" do + + before do + FileTest.stubs(:exists?).with(@path).returns(true) + @parser = mock 'parser' + Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) + end + + it "should set the mount list to the results of parsing" do + @parser.expects(:parse).returns("one" => mock("mount")) + config = Puppet::FileServing::Configuration.create + config.mounted?("one").should be_true + end + + it "should not raise exceptions" do + @parser.expects(:parse).raises(ArgumentError) + proc { Puppet::FileServing::Configuration.create }.should_not raise_error + end + + it "should replace the existing mount list with the results of reparsing" do + @parser.expects(:parse).returns("one" => mock("mount")) + config = Puppet::FileServing::Configuration.create + config.mounted?("one").should be_true + # Now parse again + @parser.expects(:parse).returns("two" => mock('other')) + config.send(:readconfig, false) + config.mounted?("one").should be_false + config.mounted?("two").should be_true + end + + it "should not replace the mount list until the file is entirely parsed successfully" do + @parser.expects(:parse).returns("one" => mock("mount")) + @parser.expects(:parse).raises(ArgumentError) + config = Puppet::FileServing::Configuration.create + # Now parse again, so the exception gets thrown + config.send(:readconfig, false) + config.mounted?("one").should be_true + end + end + + describe Puppet::FileServing::Configuration, " when finding files" do + + before do + @parser = mock 'parser' + @parser.stubs(:changed?).returns true + FileTest.stubs(:exists?).with(@path).returns(true) + Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) + + @mount1 = stub 'mount', :name => "one" + @mounts = {"one" => @mount1} + + Facter.stubs(:value).with("hostname").returns("whatever") + + @config = Puppet::FileServing::Configuration.create + end + + it "should fail if the uri does not match a leading slash followed by a valid mount name" do + @parser.expects(:parse).returns(@mounts) + proc { @config.file_path("something") }.should raise_error(ArgumentError) + end + + it "should use the first term after the first slash for the mount name" do + @parser.expects(:parse).returns(@mounts) + FileTest.stubs(:exists?).returns(true) + @mount1.expects(:file) + @config.file_path("/one") + end + + it "should use the remainder of the URI after the mount name as the file name" do + @parser.expects(:parse).returns(@mounts) + @mount1.expects(:file).with("something/else", {}) + FileTest.stubs(:exists?).returns(true) + @config.file_path("/one/something/else") + end + + it "should treat a bare name as a mount and no relative file" do + @parser.expects(:parse).returns(@mounts) + @mount1.expects(:file).with(nil, {}) + FileTest.stubs(:exists?).returns(true) + @config.file_path("/one") + end + + it "should treat a name with a trailing slash equivalently to a name with no trailing slash" do + @parser.expects(:parse).returns(@mounts) + @mount1.expects(:file).with(nil, {}) + FileTest.stubs(:exists?).returns(true) + @config.file_path("/one/") + end + + it "should return nil if the mount cannot be found" do + @parser.expects(:changed?).returns(true) + @parser.expects(:parse).returns({}) + @config.file_path("/one/something").should be_nil + end + + it "should return nil if the mount does not contain the file" do + @parser.expects(:parse).returns(@mounts) + @mount1.expects(:file).with("something/else", {}).returns(nil) + @config.file_path("/one/something/else").should be_nil + end + + it "should return the fully qualified path if the mount exists" do + @parser.expects(:parse).returns(@mounts) + @mount1.expects(:file).with("something/else", {}).returns("/full/path") + @config.file_path("/one/something/else").should == "/full/path" + end + + it "should reparse the configuration file when it has changed" do + @mount1.stubs(:file).returns("whatever") + @parser.expects(:changed?).returns(true) + @parser.expects(:parse).returns(@mounts) + FileTest.stubs(:exists?).returns(true) + @config.file_path("/one/something") + + @parser.expects(:changed?).returns(true) + @parser.expects(:parse).returns({}) + @config.file_path("/one/something").should be_nil + end + end + + describe Puppet::FileServing::Configuration, " when checking authorization" do + + before do + @parser = mock 'parser' + @parser.stubs(:changed?).returns true + FileTest.stubs(:exists?).with(@path).returns(true) + Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) + + @mount1 = stub 'mount', :name => "one" + @mounts = {"one" => @mount1} + @parser.stubs(:parse).returns(@mounts) + + Facter.stubs(:value).with("hostname").returns("whatever") + + @config = Puppet::FileServing::Configuration.create + end + + it "should return false if the mount cannot be found" do + @config.authorized?("/nope/my/file").should be_false + end + + it "should use the mount to determine authorization" do + @mount1.expects(:allowed?) + @config.authorized?("/one/my/file") + end + + it "should pass the client's name to the mount if provided" do + @mount1.expects(:allowed?).with("myhost", nil) + @config.authorized?("/one/my/file", :node => "myhost") + end + + it "should pass the client's IP to the mount if provided" do + @mount1.expects(:allowed?).with("myhost", "myip") + @config.authorized?("/one/my/file", :node => "myhost", :ipaddress => "myip") + end + + it "should return true if the mount allows the client" do + @mount1.expects(:allowed?).returns(true) + @config.authorized?("/one/my/file").should be_true + end + + it "should return false if the mount denies the client" do + @mount1.expects(:allowed?).returns(false) + @config.authorized?("/one/my/file").should be_false + end + end +end
\ No newline at end of file diff --git a/spec/unit/file_serving/configuration/parser.rb b/spec/unit/file_serving/configuration/parser.rb index aa296cf43..df2f629d5 100755 --- a/spec/unit/file_serving/configuration/parser.rb +++ b/spec/unit/file_serving/configuration/parser.rb @@ -10,8 +10,17 @@ describe Puppet::FileServing::Configuration::Parser do end end + module FSConfigurationParserTesting - def setup + def mock_file_content(content) + # We want an array, but we actually want our carriage returns on all of it. + lines = content.split("\n").collect { |l| l + "\n" } + @filehandle.stubs(:each).multiple_yields(*lines) + end +end + +describe Puppet::FileServing::Configuration::Parser do + before :each do @path = "/my/config.conf" FileTest.stubs(:exists?).with(@path).returns(true) FileTest.stubs(:readable?).with(@path).returns(true) @@ -20,113 +29,107 @@ module FSConfigurationParserTesting @parser = Puppet::FileServing::Configuration::Parser.new(@path) end - def mock_file_content(content) - # We want an array, but we actually want our carriage returns on all of it. - lines = content.split("\n").collect { |l| l + "\n" } - @filehandle.stubs(:each).multiple_yields(*lines) - end -end - -describe Puppet::FileServing::Configuration::Parser, " when parsing" do - include FSConfigurationParserTesting + describe Puppet::FileServing::Configuration::Parser, " when parsing" do + include FSConfigurationParserTesting - before do - @parser.stubs(:add_modules_mount) - end + before do + @parser.stubs(:add_modules_mount) + end - it "should allow comments" do - @filehandle.expects(:each).yields("# this is a comment\n") - proc { @parser.parse }.should_not raise_error - end + it "should allow comments" do + @filehandle.expects(:each).yields("# this is a comment\n") + proc { @parser.parse }.should_not raise_error + end - it "should allow blank lines" do - @filehandle.expects(:each).yields("\n") - proc { @parser.parse }.should_not raise_error - end + it "should allow blank lines" do + @filehandle.expects(:each).yields("\n") + proc { @parser.parse }.should_not raise_error + end - it "should create a new mount for each section in the configuration" do - mount1 = mock 'one' - mount2 = mock 'two' - Puppet::FileServing::Mount.expects(:new).with("one").returns(mount1) - Puppet::FileServing::Mount.expects(:new).with("two").returns(mount2) - mock_file_content "[one]\n[two]\n" - @parser.parse - end + it "should create a new mount for each section in the configuration" do + mount1 = mock 'one' + mount2 = mock 'two' + Puppet::FileServing::Mount.expects(:new).with("one").returns(mount1) + Puppet::FileServing::Mount.expects(:new).with("two").returns(mount2) + mock_file_content "[one]\n[two]\n" + @parser.parse + end - # This test is almost the exact same as the previous one. - it "should return a hash of the created mounts" do - mount1 = mock 'one' - mount2 = mock 'two' - Puppet::FileServing::Mount.expects(:new).with("one").returns(mount1) - Puppet::FileServing::Mount.expects(:new).with("two").returns(mount2) - mock_file_content "[one]\n[two]\n" + # This test is almost the exact same as the previous one. + it "should return a hash of the created mounts" do + mount1 = mock 'one' + mount2 = mock 'two' + Puppet::FileServing::Mount.expects(:new).with("one").returns(mount1) + Puppet::FileServing::Mount.expects(:new).with("two").returns(mount2) + mock_file_content "[one]\n[two]\n" - @parser.parse.should == {"one" => mount1, "two" => mount2} - end + @parser.parse.should == {"one" => mount1, "two" => mount2} + end - it "should only allow mount names that are alphanumeric plus dashes" do - mock_file_content "[a*b]\n" - proc { @parser.parse }.should raise_error(ArgumentError) - end + it "should only allow mount names that are alphanumeric plus dashes" do + mock_file_content "[a*b]\n" + proc { @parser.parse }.should raise_error(ArgumentError) + end - it "should fail if the value for path/allow/deny starts with an equals sign" do - mock_file_content "[one]\npath = /testing" - proc { @parser.parse }.should raise_error(ArgumentError) + it "should fail if the value for path/allow/deny starts with an equals sign" do + mock_file_content "[one]\npath = /testing" + proc { @parser.parse }.should raise_error(ArgumentError) + end end -end -describe Puppet::FileServing::Configuration::Parser, " when parsing mount attributes" do - include FSConfigurationParserTesting + describe Puppet::FileServing::Configuration::Parser, " when parsing mount attributes" do + include FSConfigurationParserTesting - before do - @mount = stub 'mount', :name => "one" - Puppet::FileServing::Mount.expects(:new).with("one").returns(@mount) - @parser.stubs(:add_modules_mount) - end + before do + @mount = stub 'mount', :name => "one" + Puppet::FileServing::Mount.expects(:new).with("one").returns(@mount) + @parser.stubs(:add_modules_mount) + end - it "should set the mount path to the path attribute from that section" do - mock_file_content "[one]\npath /some/path\n" + it "should set the mount path to the path attribute from that section" do + mock_file_content "[one]\npath /some/path\n" - @mount.expects(:path=).with("/some/path") - @parser.parse - end + @mount.expects(:path=).with("/some/path") + @parser.parse + end - it "should tell the mount to allow any allow values from the section" do - mock_file_content "[one]\nallow something\n" + it "should tell the mount to allow any allow values from the section" do + mock_file_content "[one]\nallow something\n" - @mount.expects(:info) - @mount.expects(:allow).with("something") - @parser.parse - end + @mount.expects(:info) + @mount.expects(:allow).with("something") + @parser.parse + end - it "should tell the mount to deny any deny values from the section" do - mock_file_content "[one]\ndeny something\n" + it "should tell the mount to deny any deny values from the section" do + mock_file_content "[one]\ndeny something\n" - @mount.expects(:info) - @mount.expects(:deny).with("something") - @parser.parse - end + @mount.expects(:info) + @mount.expects(:deny).with("something") + @parser.parse + end - it "should fail on any attributes other than path, allow, and deny" do - mock_file_content "[one]\ndo something\n" + it "should fail on any attributes other than path, allow, and deny" do + mock_file_content "[one]\ndo something\n" - proc { @parser.parse }.should raise_error(ArgumentError) + proc { @parser.parse }.should raise_error(ArgumentError) + end end -end -describe Puppet::FileServing::Configuration::Parser, " when parsing the modules mount" do - include FSConfigurationParserTesting + describe Puppet::FileServing::Configuration::Parser, " when parsing the modules mount" do + include FSConfigurationParserTesting - before do - @mount = stub 'mount', :name => "modules" - Puppet::FileServing::Mount.expects(:new).with("modules").returns(@mount) - end + before do + @mount = stub 'mount', :name => "modules" + Puppet::FileServing::Mount.expects(:new).with("modules").returns(@mount) + end - it "should warn if a path is set" do - mock_file_content "[modules]\npath /some/path\n" + it "should warn if a path is set" do + mock_file_content "[modules]\npath /some/path\n" - @modules.expects(:path=).never - Puppet.expects(:warning) - @parser.parse + @modules.expects(:path=).never + Puppet.expects(:warning) + @parser.parse + end end -end +end
\ No newline at end of file diff --git a/spec/unit/file_serving/file_base.rb b/spec/unit/file_serving/file_base.rb index 4c7724f7c..ded6ae4a8 100755 --- a/spec/unit/file_serving/file_base.rb +++ b/spec/unit/file_serving/file_base.rb @@ -4,7 +4,7 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/file_serving/file_base' -describe Puppet::FileServing::FileBase, " when initializing" do +describe Puppet::FileServing::FileBase do it "should accept a key in the form of a URI" do Puppet::FileServing::FileBase.new("puppet://host/module/dir/file").key.should == "puppet://host/module/dir/file" end @@ -13,7 +13,11 @@ describe Puppet::FileServing::FileBase, " when initializing" do Puppet::FileServing::FileBase.new("puppet://host/module/dir/file", :links => :manage).links.should == :manage end - it "should fail if :links is set to anything other than :manage or :follow" do + it "should consider :ignore links equivalent to :manage links" do + Puppet::FileServing::FileBase.new("puppet://host/module/dir/file", :links => :ignore).links.should == :manage + end + + it "should fail if :links is set to anything other than :manage, :follow, or :ignore" do proc { Puppet::FileServing::FileBase.new("puppet://host/module/dir/file", :links => :else) }.should raise_error(ArgumentError) end @@ -30,72 +34,91 @@ describe Puppet::FileServing::FileBase, " when initializing" do FileTest.stubs(:exists?).returns(true) Puppet::FileServing::FileBase.new("puppet://host/module/dir/file", :relative_path => "my/file").relative_path.should == "my/file" end -end -describe Puppet::FileServing::FileBase, " when setting the base path" do - before do - @file = Puppet::FileServing::FileBase.new("puppet://host/module/dir/file") + it "should have a means of determining if the file exists" do + Puppet::FileServing::FileBase.new("blah").should respond_to(:exist?) end - it "should require that the base path be fully qualified" do - FileTest.stubs(:exists?).returns(true) - proc { @file.path = "unqualified/file" }.should raise_error(ArgumentError) + it "should correctly indicate if the file is present" do + File.expects(:lstat).with("/my/file").returns(mock("stat")) + Puppet::FileServing::FileBase.new("blah", :path => "/my/file").exist?.should be_true end -end -describe Puppet::FileServing::FileBase, " when setting the relative path" do - it "should require that the relative path be unqualified" do - @file = Puppet::FileServing::FileBase.new("puppet://host/module/dir/file") - FileTest.stubs(:exists?).returns(true) - proc { @file.relative_path = "/qualified/file" }.should raise_error(ArgumentError) + it "should correctly indicate if the file is asbsent" do + File.expects(:lstat).with("/my/file").raises RuntimeError + Puppet::FileServing::FileBase.new("blah", :path => "/my/file").exist?.should be_false end -end -describe Puppet::FileServing::FileBase, " when determining the full file path" do - before do - @file = Puppet::FileServing::FileBase.new("mykey", :path => "/this/file") - end + describe "when setting the base path" do + before do + @file = Puppet::FileServing::FileBase.new("puppet://host/module/dir/file") + end - it "should return the path if there is no relative path" do - @file.full_path.should == "/this/file" + it "should require that the base path be fully qualified" do + FileTest.stubs(:exists?).returns(true) + proc { @file.path = "unqualified/file" }.should raise_error(ArgumentError) + end end - it "should return the path joined with the relative path if there is a relative path" do - @file.relative_path = "not/qualified" - @file.full_path.should == "/this/file/not/qualified" + describe "when setting the relative path" do + it "should require that the relative path be unqualified" do + @file = Puppet::FileServing::FileBase.new("puppet://host/module/dir/file") + FileTest.stubs(:exists?).returns(true) + proc { @file.relative_path = "/qualified/file" }.should raise_error(ArgumentError) + end end - it "should should fail if there is no path set" do - @file = Puppet::FileServing::FileBase.new("not/qualified") - proc { @file.full_path }.should raise_error(ArgumentError) - end -end + describe "when determining the full file path" do + before do + @file = Puppet::FileServing::FileBase.new("mykey", :path => "/this/file") + end -describe Puppet::FileServing::FileBase, " when stat'ing files" do - before do - @file = Puppet::FileServing::FileBase.new("mykey", :path => "/this/file") - end + it "should return the path if there is no relative path" do + @file.full_path.should == "/this/file" + end - it "should stat the file's full path" do - @file.stubs(:full_path).returns("/this/file") - File.expects(:lstat).with("/this/file").returns stub("stat", :ftype => "file") - @file.stat - end + it "should return the path if the relative_path is set to ''" do + @file.relative_path = "" + @file.full_path.should == "/this/file" + end - it "should fail if the file does not exist" do - @file.stubs(:full_path).returns("/this/file") - File.expects(:lstat).with("/this/file").raises(Errno::ENOENT) - proc { @file.stat }.should raise_error(Errno::ENOENT) - end + it "should return the path joined with the relative path if there is a relative path and it is not set to '/' or ''" do + @file.relative_path = "not/qualified" + @file.full_path.should == "/this/file/not/qualified" + end - it "should use :lstat if :links is set to :manage" do - File.expects(:lstat).with("/this/file").returns stub("stat", :ftype => "file") - @file.stat + it "should should fail if there is no path set" do + @file = Puppet::FileServing::FileBase.new("not/qualified") + proc { @file.full_path }.should raise_error(ArgumentError) + end end - it "should use :stat if :links is set to :follow" do - File.expects(:stat).with("/this/file").returns stub("stat", :ftype => "file") - @file.links = :follow - @file.stat + describe "when stat'ing files" do + before do + @file = Puppet::FileServing::FileBase.new("mykey", :path => "/this/file") + end + + it "should stat the file's full path" do + @file.stubs(:full_path).returns("/this/file") + File.expects(:lstat).with("/this/file").returns stub("stat", :ftype => "file") + @file.stat + end + + it "should fail if the file does not exist" do + @file.stubs(:full_path).returns("/this/file") + File.expects(:lstat).with("/this/file").raises(Errno::ENOENT) + proc { @file.stat }.should raise_error(Errno::ENOENT) + end + + it "should use :lstat if :links is set to :manage" do + File.expects(:lstat).with("/this/file").returns stub("stat", :ftype => "file") + @file.stat + end + + it "should use :stat if :links is set to :follow" do + File.expects(:stat).with("/this/file").returns stub("stat", :ftype => "file") + @file.links = :follow + @file.stat + end end end diff --git a/spec/unit/file_serving/metadata.rb b/spec/unit/file_serving/metadata.rb index f7ab0c8d6..9743370c1 100755 --- a/spec/unit/file_serving/metadata.rb +++ b/spec/unit/file_serving/metadata.rb @@ -26,8 +26,8 @@ describe Puppet::FileServing::Metadata, " when finding the file to use for setti @metadata.path = @full - # Use a symlink because it's easier to test -- no checksumming - @stat = stub "stat", :uid => 10, :gid => 20, :mode => 0755, :ftype => "symlink" + # Use a link because it's easier to test -- no checksumming + @stat = stub "stat", :uid => 10, :gid => 20, :mode => 0755, :ftype => "link" end it "should accept a base path path to which the file should be relative" do @@ -55,16 +55,19 @@ end describe Puppet::FileServing::Metadata, " when collecting attributes" do before do @path = "/my/file" - @stat = stub 'stat', :uid => 10, :gid => 20, :mode => 0755, :ftype => "file" + # Use a real file mode, so we can validate the masking is done. + @stat = stub 'stat', :uid => 10, :gid => 20, :mode => 33261, :ftype => "file" File.stubs(:lstat).returns(@stat) - @filehandle = mock 'filehandle' - @filehandle.expects(:each_line).yields("some content\n") - File.stubs(:open).with(@path, 'r').yields(@filehandle) @checksum = Digest::MD5.hexdigest("some content\n") @metadata = Puppet::FileServing::Metadata.new("file", :path => "/my/file") + @metadata.stubs(:md5_file).returns(@checksum) @metadata.collect_attributes end + it "should be able to produce xmlrpc-style attribute information" do + @metadata.should respond_to(:attributes_with_tabs) + end + # LAK:FIXME This should actually change at some point it "should set the owner by id" do @metadata.owner.should be_instance_of(Fixnum) @@ -83,28 +86,63 @@ describe Puppet::FileServing::Metadata, " when collecting attributes" do @metadata.group.should == 20 end - it "should set the mode to a string version of the mode in octal" do - @metadata.mode.should == "755" - end - - it "should set the mode to the file's current mode" do - @metadata.mode.should == "755" + it "should set the mode to the file's masked mode" do + @metadata.mode.should == 0755 end it "should set the checksum to the file's current checksum" do @metadata.checksum.should == "{md5}" + @checksum end - it "should default to a checksum of type MD5" do - @metadata.checksum.should == "{md5}" + @checksum + describe "when managing files" do + it "should default to a checksum of type MD5" do + @metadata.checksum.should == "{md5}" + @checksum + end + + it "should produce tab-separated mode, type, owner, group, and checksum for xmlrpc" do + @metadata.attributes_with_tabs.should == "#{0755.to_s}\tfile\t10\t20\t{md5}#{@checksum}" + end + end + + describe "when managing directories" do + before do + @stat.stubs(:ftype).returns("directory") + @time = Time.now + @metadata.expects(:ctime_file).returns(@time) + @metadata.collect_attributes + end + + it "should only use checksums of type 'ctime' for directories" do + @metadata.checksum.should == "{ctime}" + @time.to_s + end + + it "should produce tab-separated mode, type, owner, group, and checksum for xmlrpc" do + @metadata.attributes_with_tabs.should == "#{0755.to_s}\tdirectory\t10\t20\t{ctime}#{@time.to_s}" + end + end + + describe "when managing links" do + before do + @stat.stubs(:ftype).returns("link") + File.expects(:readlink).with("/my/file").returns("/path/to/link") + @metadata.collect_attributes + end + + it "should read links instead of returning their checksums" do + @metadata.destination.should == "/path/to/link" + end + + it "should produce tab-separated mode, type, owner, group, and destination for xmlrpc" do + @metadata.attributes_with_tabs.should == "#{0755.to_s}\tlink\t10\t20\t/path/to/link" + end end end -describe Puppet::FileServing::Metadata, " when pointing to a symlink" do - it "should store the destination of the symlink in :destination if links are :manage" do +describe Puppet::FileServing::Metadata, " when pointing to a link" do + it "should store the destination of the link in :destination if links are :manage" do file = Puppet::FileServing::Metadata.new("mykey", :links => :manage, :path => "/base/path/my/file") - File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "symlink", :mode => 0755) + File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755) File.expects(:readlink).with("/base/path/my/file").returns "/some/other/path" file.collect_attributes @@ -114,7 +152,7 @@ describe Puppet::FileServing::Metadata, " when pointing to a symlink" do it "should not collect the checksum" do file = Puppet::FileServing::Metadata.new("my/file", :links => :manage, :path => "/base/path/my/file") - File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "symlink", :mode => 0755) + File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755) File.expects(:readlink).with("/base/path/my/file").returns "/some/other/path" file.collect_attributes diff --git a/spec/unit/indirector/catalog/compiler.rb b/spec/unit/indirector/catalog/compiler.rb index 77638a410..a4a0acd58 100755 --- a/spec/unit/indirector/catalog/compiler.rb +++ b/spec/unit/indirector/catalog/compiler.rb @@ -171,7 +171,7 @@ describe Puppet::Node::Catalog::Compiler, " when determining a client's availabl end it "should use the client's Facts version as the available catalog version if it is the most recent" do - Puppet::Node::Facts.expects(:version).with(@name).returns(5) + Puppet::Node::Facts.stubs(:version).with(@name).returns(5) Puppet::Node.expects(:version).with(@name).returns(3) @catalog.interpreter.stubs(:catalog_version).returns(4) @@ -179,7 +179,7 @@ describe Puppet::Node::Catalog::Compiler, " when determining a client's availabl end it "should use the client's Node version as the available catalog version if it is the most recent" do - Puppet::Node::Facts.expects(:version).with(@name).returns(3) + Puppet::Node::Facts.stubs(:version).with(@name).returns(3) Puppet::Node.expects(:version).with(@name).returns(5) @catalog.interpreter.stubs(:catalog_version).returns(4) @@ -187,7 +187,7 @@ describe Puppet::Node::Catalog::Compiler, " when determining a client's availabl end it "should use the last parse date as the available catalog version if it is the most recent" do - Puppet::Node::Facts.expects(:version).with(@name).returns(3) + Puppet::Node::Facts.stubs(:version).with(@name).returns(3) Puppet::Node.expects(:version).with(@name).returns(4) @catalog.interpreter.stubs(:catalog_version).returns(5) diff --git a/spec/unit/indirector/checksum/file.rb b/spec/unit/indirector/checksum/file.rb index 82319fa40..4f8ee98b2 100755 --- a/spec/unit/indirector/checksum/file.rb +++ b/spec/unit/indirector/checksum/file.rb @@ -7,22 +7,6 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/indirector/checksum/file' -module FileChecksumTesting - def setup - Puppet.settings.stubs(:use) - @store = Puppet::Checksum::File.new - - @value = "70924d6fa4b2d745185fa4660703a5c0" - @sum = stub 'sum', :name => @value - - @dir = "/what/ever" - - Puppet.stubs(:[]).with(:bucketdir).returns(@dir) - - @path = @store.path(@value) - end -end - describe Puppet::Checksum::File do it "should be a subclass of the File terminus class" do Puppet::Checksum::File.superclass.should equal(Puppet::Indirector::File) @@ -40,103 +24,117 @@ describe Puppet::Checksum::File, " when initializing" do end end -describe Puppet::Checksum::File, " when determining file paths" do - include FileChecksumTesting - # I was previously passing the object in. - it "should use the value passed in to path() as the checksum" do - @value.expects(:name).never - @store.path(@value) - end +describe Puppet::Checksum::File do + before :each do + Puppet.settings.stubs(:use) + @store = Puppet::Checksum::File.new - it "should use the value of the :bucketdir setting as the root directory" do - @path.should =~ %r{^#{@dir}} - end + @value = "70924d6fa4b2d745185fa4660703a5c0" + @sum = stub 'sum', :name => @value - it "should choose a path 8 directories deep with each directory name being the respective character in the checksum" do - dirs = @value[0..7].split("").join(File::SEPARATOR) - @path.should be_include(dirs) - end + @dir = "/what/ever" - it "should use the full checksum as the final directory name" do - File.basename(File.dirname(@path)).should == @value - end + Puppet.stubs(:[]).with(:bucketdir).returns(@dir) - it "should use 'contents' as the actual file name" do - File.basename(@path).should == "contents" + @path = @store.path(@value) end - it "should use the bucketdir, the 8 sum character directories, the full checksum, and 'contents' as the full file name" do - @path.should == [@dir, @value[0..7].split(""), @value, "contents"].flatten.join(File::SEPARATOR) - end -end -describe Puppet::Checksum::File, " when retrieving files" do - include FileChecksumTesting + describe Puppet::Checksum::File, " when determining file paths" do - # The smallest test that will use the calculated path - it "should look for the calculated path" do - File.expects(:exist?).with(@path).returns(false) - @store.find(@value) - end + # I was previously passing the object in. + it "should use the value passed in to path() as the checksum" do + @value.expects(:name).never + @store.path(@value) + end - it "should return an instance of Puppet::Checksum created with the content if the file exists" do - content = "my content" - sum = stub 'file' - Puppet::Checksum.expects(:new).with(content).returns(sum) + it "should use the value of the :bucketdir setting as the root directory" do + @path.should =~ %r{^#{@dir}} + end - File.expects(:exist?).with(@path).returns(true) - File.expects(:read).with(@path).returns(content) + it "should choose a path 8 directories deep with each directory name being the respective character in the checksum" do + dirs = @value[0..7].split("").join(File::SEPARATOR) + @path.should be_include(dirs) + end - @store.find(@value).should equal(sum) - end + it "should use the full checksum as the final directory name" do + File.basename(File.dirname(@path)).should == @value + end - it "should return nil if no file is found" do - File.expects(:exist?).with(@path).returns(false) - @store.find(@value).should be_nil - end + it "should use 'contents' as the actual file name" do + File.basename(@path).should == "contents" + end - it "should fail intelligently if a found file cannot be read" do - File.expects(:exist?).with(@path).returns(true) - File.expects(:read).with(@path).raises(RuntimeError) - proc { @store.find(@value) }.should raise_error(Puppet::Error) + it "should use the bucketdir, the 8 sum character directories, the full checksum, and 'contents' as the full file name" do + @path.should == [@dir, @value[0..7].split(""), @value, "contents"].flatten.join(File::SEPARATOR) + end end -end -describe Puppet::Checksum::File, " when saving files" do - include FileChecksumTesting + describe Puppet::Checksum::File, " when retrieving files" do + + # The smallest test that will use the calculated path + it "should look for the calculated path" do + File.expects(:exist?).with(@path).returns(false) + @store.find(@value) + end + + it "should return an instance of Puppet::Checksum created with the content if the file exists" do + content = "my content" + sum = stub 'file' + Puppet::Checksum.expects(:new).with(content).returns(sum) + + File.expects(:exist?).with(@path).returns(true) + File.expects(:read).with(@path).returns(content) - # LAK:FIXME I don't know how to include in the spec the fact that we're - # using the superclass's save() method and thus are acquiring all of - # it's behaviours. - it "should save the content to the calculated path" do - File.stubs(:directory?).with(File.dirname(@path)).returns(true) - File.expects(:open).with(@path, "w") + @store.find(@value).should equal(sum) + end - file = stub 'file', :name => @value - @store.save(file) + it "should return nil if no file is found" do + File.expects(:exist?).with(@path).returns(false) + @store.find(@value).should be_nil + end + + it "should fail intelligently if a found file cannot be read" do + File.expects(:exist?).with(@path).returns(true) + File.expects(:read).with(@path).raises(RuntimeError) + proc { @store.find(@value) }.should raise_error(Puppet::Error) + end end - it "should make any directories necessary for storage" do - FileUtils.expects(:mkdir_p).with do |arg| - File.umask == 0007 and arg == File.dirname(@path) + describe Puppet::Checksum::File, " when saving files" do + + # LAK:FIXME I don't know how to include in the spec the fact that we're + # using the superclass's save() method and thus are acquiring all of + # it's behaviours. + it "should save the content to the calculated path" do + File.stubs(:directory?).with(File.dirname(@path)).returns(true) + File.expects(:open).with(@path, "w") + + file = stub 'file', :name => @value + @store.save(file) end - File.expects(:directory?).with(File.dirname(@path)).returns(true) - File.expects(:open).with(@path, "w") - file = stub 'file', :name => @value - @store.save(file) + it "should make any directories necessary for storage" do + FileUtils.expects(:mkdir_p).with do |arg| + File.umask == 0007 and arg == File.dirname(@path) + end + File.expects(:directory?).with(File.dirname(@path)).returns(true) + File.expects(:open).with(@path, "w") + + file = stub 'file', :name => @value + @store.save(file) + end end -end -describe Puppet::Checksum::File, " when deleting files" do - include FileChecksumTesting + describe Puppet::Checksum::File, " when deleting files" do - it "should remove the file at the calculated path" do - File.expects(:exist?).with(@path).returns(true) - File.expects(:unlink).with(@path) + it "should remove the file at the calculated path" do + File.expects(:exist?).with(@path).returns(true) + File.expects(:unlink).with(@path) - file = stub 'file', :name => @value - @store.destroy(file) + file = stub 'file', :name => @value + @store.destroy(file) + end end -end +end
\ No newline at end of file diff --git a/spec/unit/indirector/direct_file_server.rb b/spec/unit/indirector/direct_file_server.rb index 2a8ec1a49..9f3652536 100755 --- a/spec/unit/indirector/direct_file_server.rb +++ b/spec/unit/indirector/direct_file_server.rb @@ -7,8 +7,8 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/indirector/direct_file_server' -module DirectFileServerTerminusTesting - def setup +describe Puppet::Indirector::DirectFileServer do + before :each do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model @@ -24,72 +24,69 @@ module DirectFileServerTerminusTesting @uri = "file:///my/local" end -end -describe Puppet::Indirector::DirectFileServer, "when finding a single file" do - include DirectFileServerTerminusTesting + describe Puppet::Indirector::DirectFileServer, "when finding a single file" do - it "should return nil if the file does not exist" do - FileTest.expects(:exists?).with("/my/local").returns false - @server.find(@uri).should be_nil - end + it "should return nil if the file does not exist" do + FileTest.expects(:exists?).with("/my/local").returns false + @server.find(@uri).should be_nil + end - it "should return a Content instance created with the full path to the file if the file exists" do - FileTest.expects(:exists?).with("/my/local").returns true - @model.expects(:new).returns(:mycontent) - @server.find(@uri).should == :mycontent + it "should return a Content instance created with the full path to the file if the file exists" do + FileTest.expects(:exists?).with("/my/local").returns true + @model.expects(:new).returns(:mycontent) + @server.find(@uri).should == :mycontent + end end -end -describe Puppet::Indirector::DirectFileServer, "when creating the instance for a single found file" do - include DirectFileServerTerminusTesting + describe Puppet::Indirector::DirectFileServer, "when creating the instance for a single found file" do - before do - @data = mock 'content' - @data.stubs(:collect_attributes) - FileTest.expects(:exists?).with("/my/local").returns true - end + before do + @data = mock 'content' + @data.stubs(:collect_attributes) + FileTest.expects(:exists?).with("/my/local").returns true + end - it "should create the Content instance with the original key as the key" do - @model.expects(:new).with { |key, options| key == @uri }.returns(@data) - @server.find(@uri) - end + it "should create the Content instance with the original key as the key" do + @model.expects(:new).with { |key, options| key == @uri }.returns(@data) + @server.find(@uri) + end - it "should pass the full path to the instance" do - @model.expects(:new).with { |key, options| options[:path] == "/my/local" }.returns(@data) - @server.find(@uri) - end + it "should pass the full path to the instance" do + @model.expects(:new).with { |key, options| options[:path] == "/my/local" }.returns(@data) + @server.find(@uri) + end - it "should pass the :links setting on to the created Content instance if the file exists and there is a value for :links" do - @model.expects(:new).returns(@data) - @data.expects(:links=).with(:manage) - @server.find(@uri, :links => :manage) + it "should pass the :links setting on to the created Content instance if the file exists and there is a value for :links" do + @model.expects(:new).returns(@data) + @data.expects(:links=).with(:manage) + @server.find(@uri, :links => :manage) + end end -end -describe Puppet::Indirector::DirectFileServer, "when searching for multiple files" do - include DirectFileServerTerminusTesting + describe Puppet::Indirector::DirectFileServer, "when searching for multiple files" do - it "should return nil if the file does not exist" do - FileTest.expects(:exists?).with("/my/local").returns false - @server.find(@uri).should be_nil - end + it "should return nil if the file does not exist" do + FileTest.expects(:exists?).with("/my/local").returns false + @server.find(@uri).should be_nil + end - it "should pass the original key to :path2instances" do - FileTest.expects(:exists?).with("/my/local").returns true - @server.expects(:path2instances).with { |uri, path, options| uri == @uri } - @server.search(@uri) - end + it "should pass the original key to :path2instances" do + FileTest.expects(:exists?).with("/my/local").returns true + @server.expects(:path2instances).with { |uri, path, options| uri == @uri } + @server.search(@uri) + end - it "should use :path2instances from the terminus_helper to return instances if the file exists" do - FileTest.expects(:exists?).with("/my/local").returns true - @server.expects(:path2instances) - @server.search(@uri) - end + it "should use :path2instances from the terminus_helper to return instances if the file exists" do + FileTest.expects(:exists?).with("/my/local").returns true + @server.expects(:path2instances) + @server.search(@uri) + end - it "should pass any options on to :path2instances" do - FileTest.expects(:exists?).with("/my/local").returns true - @server.expects(:path2instances).with { |uri, path, options| options == {:testing => :one, :other => :two}} - @server.search(@uri, :testing => :one, :other => :two) + it "should pass any options on to :path2instances" do + FileTest.expects(:exists?).with("/my/local").returns true + @server.expects(:path2instances).with { |uri, path, options| options == {:testing => :one, :other => :two}} + @server.search(@uri, :testing => :one, :other => :two) + end end -end +end
\ No newline at end of file diff --git a/spec/unit/indirector/facts/facter.rb b/spec/unit/indirector/facts/facter.rb index 1fad4c859..0974a60ec 100755 --- a/spec/unit/indirector/facts/facter.rb +++ b/spec/unit/indirector/facts/facter.rb @@ -31,49 +31,46 @@ describe Puppet::Node::Facts::Facter do end end -module TestingCodeFacts - def setup +describe Puppet::Node::Facts::Facter do + before :each do @facter = Puppet::Node::Facts::Facter.new Facter.stubs(:to_hash).returns({}) @name = "me" @facts = @facter.find(@name) end -end -describe Puppet::Node::Facts::Facter, " when finding facts" do - include TestingCodeFacts + describe Puppet::Node::Facts::Facter, " when finding facts" do - it "should return a Facts instance" do - @facts.should be_instance_of(Puppet::Node::Facts) - end + it "should return a Facts instance" do + @facts.should be_instance_of(Puppet::Node::Facts) + end - it "should return a Facts instance with the provided key as the name" do - @facts.name.should == @name - end + it "should return a Facts instance with the provided key as the name" do + @facts.name.should == @name + end - it "should return the Facter facts as the values in the Facts instance" do - Facter.expects(:to_hash).returns("one" => "two") - facts = @facter.find(@name) - facts.values["one"].should == "two" + it "should return the Facter facts as the values in the Facts instance" do + Facter.expects(:to_hash).returns("one" => "two") + facts = @facter.find(@name) + facts.values["one"].should == "two" + end end -end -describe Puppet::Node::Facts::Facter, " when saving facts" do - include TestingCodeFacts + describe Puppet::Node::Facts::Facter, " when saving facts" do - it "should fail" do - proc { @facter.save(@facts) }.should raise_error(Puppet::DevError) + it "should fail" do + proc { @facter.save(@facts) }.should raise_error(Puppet::DevError) + end end -end -describe Puppet::Node::Facts::Facter, " when destroying facts" do - include TestingCodeFacts + describe Puppet::Node::Facts::Facter, " when destroying facts" do - it "should fail" do - proc { @facter.destroy(@facts) }.should raise_error(Puppet::DevError) + it "should fail" do + proc { @facter.destroy(@facts) }.should raise_error(Puppet::DevError) + end end -end -describe Puppet::Node::Facts::Facter, " when loading facts from the factpath" do - it "should load every fact in each factpath directory" -end + describe Puppet::Node::Facts::Facter, " when loading facts from the factpath" do + it "should load every fact in each factpath directory" + end +end
\ No newline at end of file diff --git a/spec/unit/indirector/file.rb b/spec/unit/indirector/file.rb index 216c9bfe1..37740f0d0 100755 --- a/spec/unit/indirector/file.rb +++ b/spec/unit/indirector/file.rb @@ -3,8 +3,9 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/indirector/file' -module FileTerminusTesting - def setup + +describe Puppet::Indirector::File do + before :each do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model @@ -21,140 +22,137 @@ module FileTerminusTesting @path = "/my/file" @dir = "/my" end -end + + describe Puppet::Indirector::File, " when finding files" do -describe Puppet::Indirector::File, " when finding files" do - include FileTerminusTesting + it "should provide a method to return file contents at a specified path" do + @searcher.should respond_to(:find) + end - it "should provide a method to return file contents at a specified path" do - @searcher.should respond_to(:find) - end + it "should return file contents as an instance of the model" do + content = "my content" - it "should return file contents as an instance of the model" do - content = "my content" + file = mock 'file' + @model.expects(:new).with(content).returns(file) - file = mock 'file' - @model.expects(:new).with(content).returns(file) + File.expects(:exist?).with(@path).returns(true) + File.expects(:read).with(@path).returns(content) + @searcher.find(@path) + end - File.expects(:exist?).with(@path).returns(true) - File.expects(:read).with(@path).returns(content) - @searcher.find(@path) - end + it "should create the model instance with the content as the only argument to initialization" do + content = "my content" - it "should create the model instance with the content as the only argument to initialization" do - content = "my content" + file = mock 'file' + @model.expects(:new).with(content).returns(file) - file = mock 'file' - @model.expects(:new).with(content).returns(file) + File.expects(:exist?).with(@path).returns(true) + File.expects(:read).with(@path).returns(content) + @searcher.find(@path).should equal(file) + end - File.expects(:exist?).with(@path).returns(true) - File.expects(:read).with(@path).returns(content) - @searcher.find(@path).should equal(file) - end + it "should return nil if no file is found" do + File.expects(:exist?).with(@path).returns(false) + @searcher.find(@path).should be_nil + end - it "should return nil if no file is found" do - File.expects(:exist?).with(@path).returns(false) - @searcher.find(@path).should be_nil - end + it "should fail intelligently if a found file cannot be read" do + File.expects(:exist?).with(@path).returns(true) + File.expects(:read).with(@path).raises(RuntimeError) + proc { @searcher.find(@path) }.should raise_error(Puppet::Error) + end - it "should fail intelligently if a found file cannot be read" do - File.expects(:exist?).with(@path).returns(true) - File.expects(:read).with(@path).raises(RuntimeError) - proc { @searcher.find(@path) }.should raise_error(Puppet::Error) - end + it "should use the path() method to calculate the path if it exists" do + @searcher.meta_def(:path) do |name| + name.upcase + end - it "should use the path() method to calculate the path if it exists" do - @searcher.meta_def(:path) do |name| - name.upcase + File.expects(:exist?).with(@path.upcase).returns(false) + @searcher.find(@path) end - - File.expects(:exist?).with(@path.upcase).returns(false) - @searcher.find(@path) end -end -describe Puppet::Indirector::File, " when saving files" do - include FileTerminusTesting + describe Puppet::Indirector::File, " when saving files" do - it "should provide a method to save file contents at a specified path" do - filehandle = mock 'file' - content = "my content" - File.expects(:directory?).with(@dir).returns(true) - File.expects(:open).with(@path, "w").yields(filehandle) - filehandle.expects(:print).with(content) + it "should provide a method to save file contents at a specified path" do + filehandle = mock 'file' + content = "my content" + File.expects(:directory?).with(@dir).returns(true) + File.expects(:open).with(@path, "w").yields(filehandle) + filehandle.expects(:print).with(content) - file = stub 'file', :content => content, :path => @path, :name => @path + file = stub 'file', :content => content, :path => @path, :name => @path - @searcher.save(file) - end + @searcher.save(file) + end - it "should fail intelligently if the file's parent directory does not exist" do - File.expects(:directory?).with(@dir).returns(false) + it "should fail intelligently if the file's parent directory does not exist" do + File.expects(:directory?).with(@dir).returns(false) - file = stub 'file', :path => @path, :name => @path + file = stub 'file', :path => @path, :name => @path - proc { @searcher.save(file) }.should raise_error(Puppet::Error) - end - - it "should fail intelligently if a file cannot be written" do - filehandle = mock 'file' - content = "my content" - File.expects(:directory?).with(@dir).returns(true) - File.expects(:open).with(@path, "w").yields(filehandle) - filehandle.expects(:print).with(content).raises(ArgumentError) + proc { @searcher.save(file) }.should raise_error(Puppet::Error) + end - file = stub 'file', :content => content, :path => @path, :name => @path + it "should fail intelligently if a file cannot be written" do + filehandle = mock 'file' + content = "my content" + File.expects(:directory?).with(@dir).returns(true) + File.expects(:open).with(@path, "w").yields(filehandle) + filehandle.expects(:print).with(content).raises(ArgumentError) - proc { @searcher.save(file) }.should raise_error(Puppet::Error) - end + file = stub 'file', :content => content, :path => @path, :name => @path - it "should use the path() method to calculate the path if it exists" do - @searcher.meta_def(:path) do |name| - name.upcase + proc { @searcher.save(file) }.should raise_error(Puppet::Error) end - file = stub 'file', :name => "/yay" + it "should use the path() method to calculate the path if it exists" do + @searcher.meta_def(:path) do |name| + name.upcase + end + + file = stub 'file', :name => "/yay" - File.expects(:open).with("/YAY", "w") - @searcher.save(file) + File.expects(:open).with("/YAY", "w") + @searcher.save(file) + end end -end -describe Puppet::Indirector::File, " when removing files" do - include FileTerminusTesting + describe Puppet::Indirector::File, " when removing files" do - it "should provide a method to remove files at a specified path" do - file = stub 'file', :path => @path, :name => @path - File.expects(:exist?).with(@path).returns(true) - File.expects(:unlink).with(@path) - - @searcher.destroy(file) - end + it "should provide a method to remove files at a specified path" do + file = stub 'file', :path => @path, :name => @path + File.expects(:exist?).with(@path).returns(true) + File.expects(:unlink).with(@path) - it "should throw an exception if the file is not found" do - file = stub 'file', :path => @path, :name => @path - File.expects(:exist?).with(@path).returns(false) + @searcher.destroy(file) + end - proc { @searcher.destroy(file) }.should raise_error(Puppet::Error) - end + it "should throw an exception if the file is not found" do + file = stub 'file', :path => @path, :name => @path + File.expects(:exist?).with(@path).returns(false) - it "should fail intelligently if the file cannot be removed" do - file = stub 'file', :path => @path, :name => @path - File.expects(:exist?).with(@path).returns(true) - File.expects(:unlink).with(@path).raises(ArgumentError) + proc { @searcher.destroy(file) }.should raise_error(Puppet::Error) + end - proc { @searcher.destroy(file) }.should raise_error(Puppet::Error) - end + it "should fail intelligently if the file cannot be removed" do + file = stub 'file', :path => @path, :name => @path + File.expects(:exist?).with(@path).returns(true) + File.expects(:unlink).with(@path).raises(ArgumentError) - it "should use the path() method to calculate the path if it exists" do - @searcher.meta_def(:path) do |name| - name.upcase + proc { @searcher.destroy(file) }.should raise_error(Puppet::Error) end - file = stub 'file', :name => "/yay" - File.expects(:exist?).with("/YAY").returns(true) - File.expects(:unlink).with("/YAY") + it "should use the path() method to calculate the path if it exists" do + @searcher.meta_def(:path) do |name| + name.upcase + end + + file = stub 'file', :name => "/yay" + File.expects(:exist?).with("/YAY").returns(true) + File.expects(:unlink).with("/YAY") - @searcher.destroy(file) + @searcher.destroy(file) + end end -end +end
\ No newline at end of file diff --git a/spec/unit/indirector/file_server.rb b/spec/unit/indirector/file_server.rb index fda60f1ec..974b95e0e 100755 --- a/spec/unit/indirector/file_server.rb +++ b/spec/unit/indirector/file_server.rb @@ -8,8 +8,9 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/indirector/file_server' require 'puppet/file_serving/configuration' -module FileServerTerminusTesting - def setup +describe Puppet::Indirector::FileServer do + + before :each do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model @@ -27,147 +28,141 @@ module FileServerTerminusTesting @configuration = mock 'configuration' Puppet::FileServing::Configuration.stubs(:create).returns(@configuration) end -end -describe Puppet::Indirector::FileServer, " when finding files" do - include FileServerTerminusTesting + describe Puppet::Indirector::FileServer, " when finding files" do - it "should use the path portion of the URI as the file name" do - @configuration.expects(:file_path).with("/my/local/file", :node => nil) - @file_server.find(@uri) - end + it "should use the path portion of the URI as the file name" do + @configuration.expects(:file_path).with("/my/local/file", :node => nil) + @file_server.find(@uri) + end - it "should use the FileServing configuration to convert the file name to a fully qualified path" do - @configuration.expects(:file_path).with("/my/local/file", :node => nil) - @file_server.find(@uri) - end + it "should use the FileServing configuration to convert the file name to a fully qualified path" do + @configuration.expects(:file_path).with("/my/local/file", :node => nil) + @file_server.find(@uri) + end - it "should pass the node name to the FileServing configuration if one is provided" do - @configuration.expects(:file_path).with("/my/local/file", :node => "testing") - @file_server.find(@uri, :node => "testing") - end + it "should pass the node name to the FileServing configuration if one is provided" do + @configuration.expects(:file_path).with("/my/local/file", :node => "testing") + @file_server.find(@uri, :node => "testing") + end - it "should return nil if no fully qualified path is found" do - @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns(nil) - @file_server.find(@uri).should be_nil - end + it "should return nil if no fully qualified path is found" do + @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns(nil) + @file_server.find(@uri).should be_nil + end - it "should return an instance of the model created with the full path if a file is found" do - @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/some/file") - @model.expects(:new).returns(:myinstance) - @file_server.find(@uri).should == :myinstance + it "should return an instance of the model created with the full path if a file is found" do + @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/some/file") + @model.expects(:new).returns(:myinstance) + @file_server.find(@uri).should == :myinstance + end end -end + describe Puppet::Indirector::FileServer, " when returning instances" do + before :each do + @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/some/file") + @instance = mock 'instance' + end -describe Puppet::Indirector::FileServer, " when returning instances" do - include FileServerTerminusTesting - - before do - @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/some/file") - @instance = mock 'instance' - end - - it "should create the instance with the key used to find the instance" do - @model.expects(:new).with { |key, *options| key == @uri } - @file_server.find(@uri) - end + it "should create the instance with the key used to find the instance" do + @model.expects(:new).with { |key, *options| key == @uri } + @file_server.find(@uri) + end - it "should create the instance with the path at which the instance was found" do - @model.expects(:new).with { |key, options| options[:path] == "/some/file" } - @file_server.find(@uri) - end + it "should create the instance with the path at which the instance was found" do + @model.expects(:new).with { |key, options| options[:path] == "/some/file" } + @file_server.find(@uri) + end - it "should set the provided :links setting on to the instance if one is provided" do - @model.expects(:new).returns(@instance) - @instance.expects(:links=).with(:mytest) - @file_server.find(@uri, :links => :mytest) - end + it "should set the provided :links setting on to the instance if one is provided" do + @model.expects(:new).returns(@instance) + @instance.expects(:links=).with(:mytest) + @file_server.find(@uri, :links => :mytest) + end - it "should not set a :links value if no :links parameter is provided" do - @model.expects(:new).returns(@instance) - @file_server.find(@uri) + it "should not set a :links value if no :links parameter is provided" do + @model.expects(:new).returns(@instance) + @file_server.find(@uri) + end end -end -describe Puppet::Indirector::FileServer, " when checking authorization" do - include FileServerTerminusTesting + describe Puppet::Indirector::FileServer, " when checking authorization" do - it "should have an authorization hook" do - @file_server.should respond_to(:authorized?) - end + it "should have an authorization hook" do + @file_server.should respond_to(:authorized?) + end - it "should deny the :destroy method" do - @file_server.authorized?(:destroy, "whatever").should be_false - end + it "should deny the :destroy method" do + @file_server.authorized?(:destroy, "whatever").should be_false + end - it "should deny the :save method" do - @file_server.authorized?(:save, "whatever").should be_false - end + it "should deny the :save method" do + @file_server.authorized?(:save, "whatever").should be_false + end - it "should use the file server configuration to determine authorization" do - @configuration.expects(:authorized?) - @file_server.authorized?(:find, "puppetmounts://host/my/file") - end + it "should use the file server configuration to determine authorization" do + @configuration.expects(:authorized?) + @file_server.authorized?(:find, "puppetmounts://host/my/file") + end - it "should pass the file path from the URI to the file server configuration" do - @configuration.expects(:authorized?).with { |uri, *args| uri == "/my/file" } - @file_server.authorized?(:find, "puppetmounts://host/my/file") - end + it "should pass the file path from the URI to the file server configuration" do + @configuration.expects(:authorized?).with { |uri, *args| uri == "/my/file" } + @file_server.authorized?(:find, "puppetmounts://host/my/file") + end - it "should pass the node name to the file server configuration" do - @configuration.expects(:authorized?).with { |key, options| options[:node] == "mynode" } - @file_server.authorized?(:find, "puppetmounts://host/my/file", :node => "mynode") - end + it "should pass the node name to the file server configuration" do + @configuration.expects(:authorized?).with { |key, options| options[:node] == "mynode" } + @file_server.authorized?(:find, "puppetmounts://host/my/file", :node => "mynode") + end - it "should pass the IP address to the file server configuration" do - @configuration.expects(:authorized?).with { |key, options| options[:ipaddress] == "myip" } - @file_server.authorized?(:find, "puppetmounts://host/my/file", :ipaddress => "myip") - end + it "should pass the IP address to the file server configuration" do + @configuration.expects(:authorized?).with { |key, options| options[:ipaddress] == "myip" } + @file_server.authorized?(:find, "puppetmounts://host/my/file", :ipaddress => "myip") + end - it "should return false if the file server configuration denies authorization" do - @configuration.expects(:authorized?).returns(false) - @file_server.authorized?(:find, "puppetmounts://host/my/file").should be_false - end + it "should return false if the file server configuration denies authorization" do + @configuration.expects(:authorized?).returns(false) + @file_server.authorized?(:find, "puppetmounts://host/my/file").should be_false + end - it "should return true if the file server configuration approves authorization" do - @configuration.expects(:authorized?).returns(true) - @file_server.authorized?(:find, "puppetmounts://host/my/file").should be_true + it "should return true if the file server configuration approves authorization" do + @configuration.expects(:authorized?).returns(true) + @file_server.authorized?(:find, "puppetmounts://host/my/file").should be_true + end end -end -describe Puppet::Indirector::FileServer, " when searching for files" do - include FileServerTerminusTesting + describe Puppet::Indirector::FileServer, " when searching for files" do - it "should use the path portion of the URI as the file name" do - @configuration.expects(:file_path).with("/my/local/file", :node => nil) - @file_server.search(@uri) - end + it "should use the path portion of the URI as the file name" do + @configuration.expects(:file_path).with("/my/local/file", :node => nil) + @file_server.search(@uri) + end - it "should use the FileServing configuration to convert the file name to a fully qualified path" do - @configuration.expects(:file_path).with("/my/local/file", :node => nil) - @file_server.search(@uri) - end + it "should use the FileServing configuration to convert the file name to a fully qualified path" do + @configuration.expects(:file_path).with("/my/local/file", :node => nil) + @file_server.search(@uri) + end - it "should pass the node name to the FileServing configuration if one is provided" do - @configuration.expects(:file_path).with("/my/local/file", :node => "testing") - @file_server.search(@uri, :node => "testing") - end + it "should pass the node name to the FileServing configuration if one is provided" do + @configuration.expects(:file_path).with("/my/local/file", :node => "testing") + @file_server.search(@uri, :node => "testing") + end - it "should return nil if no fully qualified path is found" do - @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns(nil) - @file_server.search(@uri).should be_nil - end + it "should return nil if no fully qualified path is found" do + @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns(nil) + @file_server.search(@uri).should be_nil + end - it "should use :path2instances from the terminus_helper to return instances if a module is found and the file exists" do - @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/my/file") - @file_server.expects(:path2instances).with(@uri, "/my/file", {}) - @file_server.search(@uri) - end + it "should use :path2instances from the terminus_helper to return instances if a module is found and the file exists" do + @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/my/file") + @file_server.expects(:path2instances).with(@uri, "/my/file", {}) + @file_server.search(@uri) + end - it "should pass any options on to :path2instances" do - @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/my/file") - @file_server.expects(:path2instances).with(@uri, "/my/file", :testing => :one, :other => :two) - @file_server.search(@uri, :testing => :one, :other => :two) + it "should pass any options on to :path2instances" do + @configuration.expects(:file_path).with("/my/local/file", :node => nil).returns("/my/file") + @file_server.expects(:path2instances).with(@uri, "/my/file", :testing => :one, :other => :two) + @file_server.search(@uri, :testing => :one, :other => :two) + end end -end +end
\ No newline at end of file diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb index 36192b8e5..fe456b61e 100755 --- a/spec/unit/indirector/indirection.rb +++ b/spec/unit/indirector/indirection.rb @@ -4,21 +4,7 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/indirector' -module IndirectionTesting - def setup - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = stub 'terminus', :has_most_recent? => false - @indirection.stubs(:terminus).returns(@terminus) - @indirection.stubs(:terminus_class).returns(:whatever) - @instance = stub 'instance', :version => nil, :version= => nil, :name => "whatever" - @name = :mything - end - - def teardown - @indirection.delete - Puppet::Indirector::Indirection.clear_cache - end -end + describe Puppet::Indirector::Indirection, " when initializing" do # (LAK) I've no idea how to test this, really. @@ -55,103 +41,183 @@ describe Puppet::Indirector::Indirection, " when initializing" do end end -describe Puppet::Indirector::Indirection, " when looking for a model instance" do - include IndirectionTesting +describe Puppet::Indirector::Indirection do + before :each do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @terminus = stub 'terminus', :has_most_recent? => false + @indirection.stubs(:terminus).returns(@terminus) + @indirection.stubs(:terminus_class).returns(:whatever) + @instance = stub 'instance', :version => nil, :version= => nil, :name => "whatever" + @name = :mything + end + + describe Puppet::Indirector::Indirection, " when looking for a model instance" do + + it "should let the appropriate terminus perform the lookup" do + @terminus.expects(:find).with(@name).returns(@instance) + @indirection.find(@name).should == @instance + end + + it "should not attempt to set a timestamp if the terminus cannot find the instance" do + @terminus.expects(:find).with(@name).returns(nil) + proc { @indirection.find(@name) }.should_not raise_error + end - it "should let the appropriate terminus perform the lookup" do - @terminus.expects(:find).with(@name).returns(@instance) - @indirection.find(@name).should == @instance + it "should pass the instance to the :post_find hook if there is one" do + class << @terminus + def post_find + end + end + @terminus.expects(:post_find).with(@instance) + @terminus.expects(:find).with(@name).returns(@instance) + @indirection.find(@name) + end end + + describe Puppet::Indirector::Indirection, " when removing a model instance" do - it "should not attempt to set a timestamp if the terminus cannot find the instance" do - @terminus.expects(:find).with(@name).returns(nil) - proc { @indirection.find(@name) }.should_not raise_error + it "should let the appropriate terminus remove the instance" do + @terminus.expects(:destroy).with(@name).returns(@instance) + @indirection.destroy(@name).should == @instance + end end - it "should pass the instance to the :post_find hook if there is one" do - class << @terminus - def post_find + describe Puppet::Indirector::Indirection, " when searching for multiple model instances" do + + it "should let the appropriate terminus find the matching instances" do + @terminus.expects(:search).with(@name).returns(@instance) + @indirection.search(@name).should == @instance + end + + it "should pass the instances to the :post_search hook if there is one" do + class << @terminus + def post_search + end end + @terminus.expects(:post_search).with(@instance) + @terminus.expects(:search).with(@name).returns(@instance) + @indirection.search(@name) end - @terminus.expects(:post_find).with(@instance) - @terminus.expects(:find).with(@name).returns(@instance) - @indirection.find(@name) end -end -describe Puppet::Indirector::Indirection, " when removing a model instance" do - include IndirectionTesting + describe Puppet::Indirector::Indirection, " when storing a model instance" do - it "should let the appropriate terminus remove the instance" do - @terminus.expects(:destroy).with(@name).returns(@instance) - @indirection.destroy(@name).should == @instance + it "should let the appropriate terminus store the instance" do + @terminus.expects(:save).with(@instance).returns(@instance) + @indirection.save(@instance).should == @instance + end end -end + + describe Puppet::Indirector::Indirection, " when handling instance versions" do -describe Puppet::Indirector::Indirection, " when searching for multiple model instances" do - include IndirectionTesting - - it "should let the appropriate terminus find the matching instances" do - @terminus.expects(:search).with(@name).returns(@instance) - @indirection.search(@name).should == @instance - end + it "should let the appropriate terminus perform the lookup" do + @terminus.expects(:version).with(@name).returns(5) + @indirection.version(@name).should == 5 + end + + it "should add versions to found instances that do not already have them" do + @terminus.expects(:find).with(@name).returns(@instance) + time = mock 'time' + time.expects(:utc).returns(:mystamp) + Time.expects(:now).returns(time) + @instance.expects(:version=).with(:mystamp) + @indirection.find(@name) + end + + it "should add versions to saved instances that do not already have them" do + time = mock 'time' + time.expects(:utc).returns(:mystamp) + Time.expects(:now).returns(time) + @instance.expects(:version=).with(:mystamp) + @terminus.stubs(:save) + @indirection.save(@instance) + end - it "should pass the instances to the :post_search hook if there is one" do - class << @terminus - def post_search + # We've already tested this, basically, but... + it "should use the current time in UTC for versions" do + @instance.expects(:version=).with do |time| + time.utc? end + @terminus.stubs(:save) + @indirection.save(@instance) end - @terminus.expects(:post_search).with(@instance) - @terminus.expects(:search).with(@name).returns(@instance) - @indirection.search(@name) end -end -describe Puppet::Indirector::Indirection, " when storing a model instance" do - include IndirectionTesting - - it "should let the appropriate terminus store the instance" do - @terminus.expects(:save).with(@instance).returns(@instance) - @indirection.save(@instance).should == @instance - end -end -describe Puppet::Indirector::Indirection, " when handling instance versions" do - include IndirectionTesting - - it "should let the appropriate terminus perform the lookup" do - @terminus.expects(:version).with(@name).returns(5) - @indirection.version(@name).should == 5 - end + describe Puppet::Indirector::Indirection, " when an authorization hook is present" do - it "should add versions to found instances that do not already have them" do - @terminus.expects(:find).with(@name).returns(@instance) - time = mock 'time' - time.expects(:utc).returns(:mystamp) - Time.expects(:now).returns(time) - @instance.expects(:version=).with(:mystamp) - @indirection.find(@name) - end + before do + # So the :respond_to? turns out right. + class << @terminus + def authorized? + end + end + end - it "should add versions to saved instances that do not already have them" do - time = mock 'time' - time.expects(:utc).returns(:mystamp) - Time.expects(:now).returns(time) - @instance.expects(:version=).with(:mystamp) - @terminus.stubs(:save) - @indirection.save(@instance) - end + it "should not check authorization if a node name is not provided" do + @terminus.expects(:authorized?).never + @terminus.stubs(:find) + @indirection.find("/my/key") + end + + it "should fail while finding instances if authorization returns false" do + @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(false) + @terminus.stubs(:find) + proc { @indirection.find("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + end + + it "should continue finding instances if authorization returns true" do + @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(true) + @terminus.stubs(:find) + @indirection.find("/my/key", :node => "mynode") + end + + it "should fail while saving instances if authorization returns false" do + @terminus.expects(:authorized?).with(:save, :myinstance, :node => "mynode").returns(false) + @terminus.stubs(:save) + proc { @indirection.save(:myinstance, :node => "mynode") }.should raise_error(ArgumentError) + end + + it "should continue saving instances if authorization returns true" do + instance = stub 'instance', :version => 1.0, :name => "eh" + @terminus.expects(:authorized?).with(:save, instance, :node => "mynode").returns(true) + @terminus.stubs(:save) + @indirection.save(instance, :node => "mynode") + end + + it "should fail while destroying instances if authorization returns false" do + @terminus.expects(:authorized?).with(:destroy, "/my/key", :node => "mynode").returns(false) + @terminus.stubs(:destroy) + proc { @indirection.destroy("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + end + + it "should continue destroying instances if authorization returns true" do + instance = stub 'instance', :version => 1.0, :name => "eh" + @terminus.expects(:authorized?).with(:destroy, instance, :node => "mynode").returns(true) + @terminus.stubs(:destroy) + @indirection.destroy(instance, :node => "mynode") + end + + it "should fail while searching for instances if authorization returns false" do + @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(false) + @terminus.stubs(:search) + proc { @indirection.search("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + end - # We've already tested this, basically, but... - it "should use the current time in UTC for versions" do - @instance.expects(:version=).with do |time| - time.utc? + it "should continue searching for instances if authorization returns true" do + @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(true) + @terminus.stubs(:search) + @indirection.search("/my/key", :node => "mynode") end - @terminus.stubs(:save) - @indirection.save(@instance) + end + + after :each do + @indirection.delete + Puppet::Indirector::Indirection.clear_cache end end + describe Puppet::Indirector::Indirection, " when managing indirection instances" do it "should allow an indirection to be retrieved by name" do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @@ -397,8 +463,8 @@ describe Puppet::Indirector::Indirection, " when deciding whether to cache" do end end -module IndirectionCacheTesting - def setup +describe Puppet::Indirector::Indirection do + before :each do Puppet.settings.stubs(:value).with("test_terminus").returns("test_terminus") @terminus_class = mock 'terminus_class' @terminus = mock 'terminus' @@ -411,175 +477,104 @@ module IndirectionCacheTesting @indirection.terminus_class = :test_terminus end - def teardown - @indirection.delete - Puppet::Indirector::Indirection.clear_cache - end -end - -describe Puppet::Indirector::Indirection, " when managing the cache terminus" do - include IndirectionCacheTesting - - it "should not create a cache terminus at initialization" do - # This is weird, because all of the code is in the setup. If we got - # new() called on the cache class, we'd get an exception here. - end - - it "should reuse the cache terminus" do - @cache_class.expects(:new).returns(@cache) - Puppet.settings.stubs(:value).with("test_cache").returns("cache_terminus") - @indirection.cache_class = :cache_terminus - @indirection.cache.should equal(@cache) - @indirection.cache.should equal(@cache) - end - - it "should remove the cache terminus when all other terminus instances are cleared" do - cache2 = mock 'cache2' - @cache_class.stubs(:new).returns(@cache, cache2) - @indirection.cache_class = :cache_terminus - @indirection.cache.should equal(@cache) - @indirection.clear_cache - @indirection.cache.should equal(cache2) - end -end - -describe Puppet::Indirector::Indirection, " when saving and using a cache" do - include IndirectionCacheTesting - - before do - @indirection.cache_class = :cache_terminus - @cache_class.expects(:new).returns(@cache) - @name = "testing" - @instance = stub 'instance', :version => 5, :name => @name - end - - it "should not update the cache or terminus if the new object is not different" do - @cache.expects(:has_most_recent?).with(@name, 5).returns(true) - @indirection.save(@instance) - end + describe Puppet::Indirector::Indirection, " when managing the cache terminus" do - it "should update the original and the cache if the cached object is different" do - @cache.expects(:has_most_recent?).with(@name, 5).returns(false) - @terminus.expects(:save).with(@instance) - @cache.expects(:save).with(@instance) - @indirection.save(@instance) - end -end + it "should not create a cache terminus at initialization" do + # This is weird, because all of the code is in the setup. If we got + # new() called on the cache class, we'd get an exception here. + end -describe Puppet::Indirector::Indirection, " when finding and using a cache" do - include IndirectionCacheTesting + it "should reuse the cache terminus" do + @cache_class.expects(:new).returns(@cache) + Puppet.settings.stubs(:value).with("test_cache").returns("cache_terminus") + @indirection.cache_class = :cache_terminus + @indirection.cache.should equal(@cache) + @indirection.cache.should equal(@cache) + end - before do - @indirection.cache_class = :cache_terminus - @cache_class.expects(:new).returns(@cache) + it "should remove the cache terminus when all other terminus instances are cleared" do + cache2 = mock 'cache2' + @cache_class.stubs(:new).returns(@cache, cache2) + @indirection.cache_class = :cache_terminus + @indirection.cache.should equal(@cache) + @indirection.clear_cache + @indirection.cache.should equal(cache2) + end end - it "should return the cached object if the cache is up to date" do - cached = mock 'cached object' + describe Puppet::Indirector::Indirection, " when saving and using a cache" do - name = "myobject" - - @terminus.expects(:version).with(name).returns(1) - @cache.expects(:has_most_recent?).with(name, 1).returns(true) + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) + @name = "testing" + @instance = stub 'instance', :version => 5, :name => @name + end - @cache.expects(:find).with(name).returns(cached) + it "should not update the cache or terminus if the new object is not different" do + @cache.expects(:has_most_recent?).with(@name, 5).returns(true) + @indirection.save(@instance) + end - @indirection.find(name).should equal(cached) + it "should update the original and the cache if the cached object is different" do + @cache.expects(:has_most_recent?).with(@name, 5).returns(false) + @terminus.expects(:save).with(@instance) + @cache.expects(:save).with(@instance) + @indirection.save(@instance) + end end + + describe Puppet::Indirector::Indirection, " when finding and using a cache" do - it "should return the original object if the cache is not up to date" do - real = stub 'real object', :version => 1 - - name = "myobject" + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) + end - @cache.stubs(:save) - @cache.expects(:has_most_recent?).with(name, 1).returns(false) - @terminus.expects(:version).with(name).returns(1) + it "should return the cached object if the cache is up to date" do + cached = mock 'cached object' - @terminus.expects(:find).with(name).returns(real) + name = "myobject" - @indirection.find(name).should equal(real) - end + @terminus.expects(:version).with(name).returns(1) + @cache.expects(:has_most_recent?).with(name, 1).returns(true) - it "should cache any newly returned objects" do - real = stub 'real object', :version => 1 + @cache.expects(:find).with(name).returns(cached) - name = "myobject" + @indirection.find(name).should equal(cached) + end - @terminus.expects(:version).with(name).returns(1) - @cache.expects(:has_most_recent?).with(name, 1).returns(false) + it "should return the original object if the cache is not up to date" do + real = stub 'real object', :version => 1 - @terminus.expects(:find).with(name).returns(real) - @cache.expects(:save).with(real) + name = "myobject" - @indirection.find(name).should equal(real) - end -end + @cache.stubs(:save) + @cache.expects(:has_most_recent?).with(name, 1).returns(false) + @terminus.expects(:version).with(name).returns(1) -describe Puppet::Indirector::Indirection, " when an authorization hook is present" do - include IndirectionTesting + @terminus.expects(:find).with(name).returns(real) - before do - # So the :respond_to? turns out right. - class << @terminus - def authorized? - end + @indirection.find(name).should equal(real) end - end - it "should not check authorization if a node name is not provided" do - @terminus.expects(:authorized?).never - @terminus.stubs(:find) - @indirection.find("/my/key") - end - - it "should fail while finding instances if authorization returns false" do - @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(false) - @terminus.stubs(:find) - proc { @indirection.find("/my/key", :node => "mynode") }.should raise_error(ArgumentError) - end - - it "should continue finding instances if authorization returns true" do - @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(true) - @terminus.stubs(:find) - @indirection.find("/my/key", :node => "mynode") - end + it "should cache any newly returned objects" do + real = stub 'real object', :version => 1 - it "should fail while saving instances if authorization returns false" do - @terminus.expects(:authorized?).with(:save, :myinstance, :node => "mynode").returns(false) - @terminus.stubs(:save) - proc { @indirection.save(:myinstance, :node => "mynode") }.should raise_error(ArgumentError) - end + name = "myobject" - it "should continue saving instances if authorization returns true" do - instance = stub 'instance', :version => 1.0, :name => "eh" - @terminus.expects(:authorized?).with(:save, instance, :node => "mynode").returns(true) - @terminus.stubs(:save) - @indirection.save(instance, :node => "mynode") - end + @terminus.expects(:version).with(name).returns(1) + @cache.expects(:has_most_recent?).with(name, 1).returns(false) - it "should fail while destroying instances if authorization returns false" do - @terminus.expects(:authorized?).with(:destroy, "/my/key", :node => "mynode").returns(false) - @terminus.stubs(:destroy) - proc { @indirection.destroy("/my/key", :node => "mynode") }.should raise_error(ArgumentError) - end + @terminus.expects(:find).with(name).returns(real) + @cache.expects(:save).with(real) - it "should continue destroying instances if authorization returns true" do - instance = stub 'instance', :version => 1.0, :name => "eh" - @terminus.expects(:authorized?).with(:destroy, instance, :node => "mynode").returns(true) - @terminus.stubs(:destroy) - @indirection.destroy(instance, :node => "mynode") - end - - it "should fail while searching for instances if authorization returns false" do - @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(false) - @terminus.stubs(:search) - proc { @indirection.search("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + @indirection.find(name).should equal(real) + end end - - it "should continue searching for instances if authorization returns true" do - @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(true) - @terminus.stubs(:search) - @indirection.search("/my/key", :node => "mynode") + + after :each do + @indirection.delete + Puppet::Indirector::Indirection.clear_cache end end diff --git a/spec/unit/indirector/module_files.rb b/spec/unit/indirector/module_files.rb index 9011530dd..0f6dbb6df 100755 --- a/spec/unit/indirector/module_files.rb +++ b/spec/unit/indirector/module_files.rb @@ -7,239 +7,237 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/indirector/module_files' -module ModuleFilesTerminusTesting - def setup - Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) - Puppet::Indirector::Terminus.stubs(:register_terminus_class) - @model = mock 'model' - @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model - Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - @module_files_class = Class.new(Puppet::Indirector::ModuleFiles) do - def self.to_s - "Testing::Mytype" - end - end +describe Puppet::Indirector::ModuleFiles do - @module_files = @module_files_class.new + before :each do + Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) + Puppet::Indirector::Terminus.stubs(:register_terminus_class) + @model = mock 'model' + @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model + Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) - @uri = "puppetmounts://host/modules/my/local/file" - @module = Puppet::Module.new("mymod", "/module/path") - end -end + @module_files_class = Class.new(Puppet::Indirector::ModuleFiles) do + def self.to_s + "Testing::Mytype" + end + end -describe Puppet::Indirector::ModuleFiles, " when finding files" do - include ModuleFilesTerminusTesting + @module_files = @module_files_class.new - it "should strip off the leading '/modules' mount name" do - Puppet::Module.expects(:find).with('my', "myenv").returns @module - @module_files.find(@uri) - end + @uri = "puppetmounts://host/modules/my/local/file" + @module = Puppet::Module.new("mymod", "/module/path") + end - it "should not strip off leading terms that start with '/modules' but are longer words" do - Puppet::Module.expects(:find).with('modulestart', "myenv").returns nil - @module_files.find("puppetmounts://host/modulestart/my/local/file") - end + describe Puppet::Indirector::ModuleFiles, " when finding files" do - it "should search for a module whose name is the first term in the remaining file path" do - Puppet::Module.expects(:find).with('my', "myenv").returns @module - @module_files.find(@uri) - end + it "should strip off the leading '/modules' mount name" do + Puppet::Module.expects(:find).with('my', "myenv").returns @module + @module_files.find(@uri) + end - it "should search for a file relative to the module's files directory" do - Puppet::Module.expects(:find).with('my', "myenv").returns @module - FileTest.expects(:exists?).with("/module/path/files/local/file") - @module_files.find(@uri) - end + it "should not strip off leading terms that start with '/modules' but are longer words" do + Puppet::Module.expects(:find).with('modulestart', "myenv").returns nil + @module_files.find("puppetmounts://host/modulestart/my/local/file") + end - it "should return nil if the module does not exist" do - Puppet::Module.expects(:find).with('my', "myenv").returns nil - @module_files.find(@uri).should be_nil - end + it "should search for a module whose name is the first term in the remaining file path" do + Puppet::Module.expects(:find).with('my', "myenv").returns @module + @module_files.find(@uri) + end - it "should return nil if the module exists but the file does not" do - Puppet::Module.expects(:find).with('my', "myenv").returns @module - FileTest.expects(:exists?).with("/module/path/files/local/file").returns(false) - @module_files.find(@uri).should be_nil - end + it "should search for a file relative to the module's files directory" do + Puppet::Module.expects(:find).with('my', "myenv").returns @module + FileTest.expects(:exists?).with("/module/path/files/local/file") + @module_files.find(@uri) + end - it "should return an instance of the model if a module is found and the file exists" do - Puppet::Module.expects(:find).with('my', "myenv").returns @module - FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true) - @model.expects(:new).returns(:myinstance) - @module_files.find(@uri).should == :myinstance - end + it "should return nil if the module does not exist" do + Puppet::Module.expects(:find).with('my', "myenv").returns nil + @module_files.find(@uri).should be_nil + end - it "should use the node's environment to look up the module if the node name is provided" do - node = stub "node", :environment => "testing" - Puppet::Node.expects(:find).with("mynode").returns(node) - Puppet::Module.expects(:find).with('my', "testing") - @module_files.find(@uri, :node => "mynode") - end + it "should return nil if the module exists but the file does not" do + Puppet::Module.expects(:find).with('my', "myenv").returns @module + FileTest.expects(:exists?).with("/module/path/files/local/file").returns(false) + @module_files.find(@uri).should be_nil + end - it "should use the default environment setting to look up the module if the node name is not provided" do - env = stub "environment", :name => "testing" - Puppet::Node::Environment.stubs(:new).returns(env) - Puppet::Module.expects(:find).with('my', "testing") - @module_files.find(@uri) - end -end + it "should return an instance of the model if a module is found and the file exists" do + Puppet::Module.expects(:find).with('my', "myenv").returns @module + FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true) + @model.expects(:new).returns(:myinstance) + @module_files.find(@uri).should == :myinstance + end -describe Puppet::Indirector::ModuleFiles, " when returning instances" do - include ModuleFilesTerminusTesting + it "should use the node's environment to look up the module if the node name is provided" do + node = stub "node", :environment => "testing" + Puppet::Node.expects(:find).with("mynode").returns(node) + Puppet::Module.expects(:find).with('my', "testing") + @module_files.find(@uri, :node => "mynode") + end - before do - Puppet::Module.expects(:find).with('my', "myenv").returns @module - FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true) - @instance = mock 'instance' + it "should use the default environment setting to look up the module if the node name is not provided" do + env = stub "environment", :name => "testing" + Puppet::Node::Environment.stubs(:new).returns(env) + Puppet::Module.expects(:find).with('my', "testing") + @module_files.find(@uri) + end end - it "should create the instance with the key used to find the instance" do - @model.expects(:new).with { |key, *options| key == @uri } - @module_files.find(@uri) - end + describe Puppet::Indirector::ModuleFiles, " when returning instances" do - it "should create the instance with the path at which the instance was found" do - @model.expects(:new).with { |key, options| options[:path] == "/module/path/files/local/file" } - @module_files.find(@uri) - end + before do + Puppet::Module.expects(:find).with('my', "myenv").returns @module + FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true) + @instance = mock 'instance' + end - it "should set the provided :links setting on to the instance if one is provided" do - @model.expects(:new).returns(@instance) - @instance.expects(:links=).with(:mytest) - @module_files.find(@uri, :links => :mytest) - end + it "should create the instance with the key used to find the instance" do + @model.expects(:new).with { |key, *options| key == @uri } + @module_files.find(@uri) + end - it "should not set a :links value if no :links parameter is provided" do - @model.expects(:new).returns(@instance) - @module_files.find(@uri) - end -end + it "should create the instance with the path at which the instance was found" do + @model.expects(:new).with { |key, options| options[:path] == "/module/path/files/local/file" } + @module_files.find(@uri) + end -describe Puppet::Indirector::ModuleFiles, " when authorizing" do - include ModuleFilesTerminusTesting + it "should set the provided :links setting on to the instance if one is provided" do + @model.expects(:new).returns(@instance) + @instance.expects(:links=).with(:mytest) + @module_files.find(@uri, :links => :mytest) + end - before do - @configuration = mock 'configuration' - Puppet::FileServing::Configuration.stubs(:create).returns(@configuration) + it "should not set a :links value if no :links parameter is provided" do + @model.expects(:new).returns(@instance) + @module_files.find(@uri) + end end - it "should have an authorization hook" do - @module_files.should respond_to(:authorized?) - end + describe Puppet::Indirector::ModuleFiles, " when authorizing" do - it "should deny the :destroy method" do - @module_files.authorized?(:destroy, "whatever").should be_false - end + before do + @configuration = mock 'configuration' + Puppet::FileServing::Configuration.stubs(:create).returns(@configuration) + end - it "should deny the :save method" do - @module_files.authorized?(:save, "whatever").should be_false - end + it "should have an authorization hook" do + @module_files.should respond_to(:authorized?) + end - it "should use the file server configuration to determine authorization" do - @configuration.expects(:authorized?) - @module_files.authorized?(:find, "puppetmounts://host/my/file") - end + it "should deny the :destroy method" do + @module_files.authorized?(:destroy, "whatever").should be_false + end - it "should use the path directly from the URI if it already includes /modules" do - @configuration.expects(:authorized?).with { |uri, *args| uri == "/modules/my/file" } - @module_files.authorized?(:find, "puppetmounts://host/modules/my/file") - end + it "should deny the :save method" do + @module_files.authorized?(:save, "whatever").should be_false + end - it "should add /modules to the file path if it's not included in the URI" do - @configuration.expects(:authorized?).with { |uri, *args| uri == "/modules/my/file" } - @module_files.authorized?(:find, "puppetmounts://host/my/file") - end + it "should use the file server configuration to determine authorization" do + @configuration.expects(:authorized?) + @module_files.authorized?(:find, "puppetmounts://host/my/file") + end - it "should pass the node name to the file server configuration" do - @configuration.expects(:authorized?).with { |key, options| options[:node] == "mynode" } - @module_files.authorized?(:find, "puppetmounts://host/my/file", :node => "mynode") - end + it "should use the path directly from the URI if it already includes /modules" do + @configuration.expects(:authorized?).with { |uri, *args| uri == "/modules/my/file" } + @module_files.authorized?(:find, "puppetmounts://host/modules/my/file") + end - it "should pass the IP address to the file server configuration" do - @configuration.expects(:authorized?).with { |key, options| options[:ipaddress] == "myip" } - @module_files.authorized?(:find, "puppetmounts://host/my/file", :ipaddress => "myip") - end + it "should add /modules to the file path if it's not included in the URI" do + @configuration.expects(:authorized?).with { |uri, *args| uri == "/modules/my/file" } + @module_files.authorized?(:find, "puppetmounts://host/my/file") + end - it "should return false if the file server configuration denies authorization" do - @configuration.expects(:authorized?).returns(false) - @module_files.authorized?(:find, "puppetmounts://host/my/file").should be_false - end + it "should pass the node name to the file server configuration" do + @configuration.expects(:authorized?).with { |key, options| options[:node] == "mynode" } + @module_files.authorized?(:find, "puppetmounts://host/my/file", :node => "mynode") + end - it "should return true if the file server configuration approves authorization" do - @configuration.expects(:authorized?).returns(true) - @module_files.authorized?(:find, "puppetmounts://host/my/file").should be_true - end -end + it "should pass the IP address to the file server configuration" do + @configuration.expects(:authorized?).with { |key, options| options[:ipaddress] == "myip" } + @module_files.authorized?(:find, "puppetmounts://host/my/file", :ipaddress => "myip") + end -describe Puppet::Indirector::ModuleFiles, " when searching for files" do - include ModuleFilesTerminusTesting + it "should return false if the file server configuration denies authorization" do + @configuration.expects(:authorized?).returns(false) + @module_files.authorized?(:find, "puppetmounts://host/my/file").should be_false + end - it "should strip off the leading '/modules' mount name" do - Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) - Puppet::Module.expects(:find).with('my', "myenv").returns @module - @module_files.search(@uri) + it "should return true if the file server configuration approves authorization" do + @configuration.expects(:authorized?).returns(true) + @module_files.authorized?(:find, "puppetmounts://host/my/file").should be_true + end end - it "should not strip off leading terms that start with '/modules' but are longer words" do - Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) - Puppet::Module.expects(:find).with('modulestart', "myenv").returns nil - @module_files.search("puppetmounts://host/modulestart/my/local/file") - end + describe Puppet::Indirector::ModuleFiles, " when searching for files" do - it "should search for a module whose name is the first term in the remaining file path" do - Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) - Puppet::Module.expects(:find).with('my', "myenv").returns @module - @module_files.search(@uri) - end + it "should strip off the leading '/modules' mount name" do + Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) + Puppet::Module.expects(:find).with('my', "myenv").returns @module + @module_files.search(@uri) + end - it "should search for a file relative to the module's files directory" do - Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) - Puppet::Module.expects(:find).with('my', "myenv").returns @module - FileTest.expects(:exists?).with("/module/path/files/local/file") - @module_files.search(@uri) - end + it "should not strip off leading terms that start with '/modules' but are longer words" do + Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) + Puppet::Module.expects(:find).with('modulestart', "myenv").returns nil + @module_files.search("puppetmounts://host/modulestart/my/local/file") + end - it "should return nil if the module does not exist" do - Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) - Puppet::Module.expects(:find).with('my', "myenv").returns nil - @module_files.search(@uri).should be_nil - end + it "should search for a module whose name is the first term in the remaining file path" do + Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) + Puppet::Module.expects(:find).with('my', "myenv").returns @module + @module_files.search(@uri) + end - it "should return nil if the module exists but the file does not" do - Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) - Puppet::Module.expects(:find).with('my', "myenv").returns @module - FileTest.expects(:exists?).with("/module/path/files/local/file").returns(false) - @module_files.search(@uri).should be_nil - end + it "should search for a file relative to the module's files directory" do + Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) + Puppet::Module.expects(:find).with('my', "myenv").returns @module + FileTest.expects(:exists?).with("/module/path/files/local/file") + @module_files.search(@uri) + end - it "should use the node's environment to look up the module if the node name is provided" do - node = stub "node", :environment => "testing" - Puppet::Node.expects(:find).with("mynode").returns(node) - Puppet::Module.expects(:find).with('my', "testing") - @module_files.search(@uri, :node => "mynode") - end + it "should return nil if the module does not exist" do + Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) + Puppet::Module.expects(:find).with('my', "myenv").returns nil + @module_files.search(@uri).should be_nil + end - it "should use the default environment setting to look up the module if the node name is not provided and the environment is not set to ''" do - env = stub 'env', :name => "testing" - Puppet::Node::Environment.stubs(:new).returns(env) - Puppet::Module.expects(:find).with('my', "testing") - @module_files.search(@uri) - end + it "should return nil if the module exists but the file does not" do + Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) + Puppet::Module.expects(:find).with('my', "myenv").returns @module + FileTest.expects(:exists?).with("/module/path/files/local/file").returns(false) + @module_files.search(@uri).should be_nil + end - it "should use :path2instances from the terminus_helper to return instances if a module is found and the file exists" do - Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) - Puppet::Module.expects(:find).with('my', "myenv").returns @module - FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true) - @module_files.expects(:path2instances).with(@uri, "/module/path/files/local/file", {}) - @module_files.search(@uri) - end + it "should use the node's environment to look up the module if the node name is provided" do + node = stub "node", :environment => "testing" + Puppet::Node.expects(:find).with("mynode").returns(node) + Puppet::Module.expects(:find).with('my', "testing") + @module_files.search(@uri, :node => "mynode") + end - it "should pass any options on to :path2instances" do - Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) - Puppet::Module.expects(:find).with('my', "myenv").returns @module - FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true) - @module_files.expects(:path2instances).with(@uri, "/module/path/files/local/file", :testing => :one, :other => :two) - @module_files.search(@uri, :testing => :one, :other => :two) + it "should use the default environment setting to look up the module if the node name is not provided and the environment is not set to ''" do + env = stub 'env', :name => "testing" + Puppet::Node::Environment.stubs(:new).returns(env) + Puppet::Module.expects(:find).with('my', "testing") + @module_files.search(@uri) + end + + it "should use :path2instances from the terminus_helper to return instances if a module is found and the file exists" do + Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) + Puppet::Module.expects(:find).with('my', "myenv").returns @module + FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true) + @module_files.expects(:path2instances).with(@uri, "/module/path/files/local/file", {}) + @module_files.search(@uri) + end + + it "should pass any options on to :path2instances" do + Puppet::Node::Environment.stubs(:new).returns(stub('env', :name => "myenv")) + Puppet::Module.expects(:find).with('my', "myenv").returns @module + FileTest.expects(:exists?).with("/module/path/files/local/file").returns(true) + @module_files.expects(:path2instances).with(@uri, "/module/path/files/local/file", :testing => :one, :other => :two) + @module_files.search(@uri, :testing => :one, :other => :two) + end end end diff --git a/spec/unit/indirector/node/exec.rb b/spec/unit/indirector/node/exec.rb index 744a32b0a..b67e0fe97 100755 --- a/spec/unit/indirector/node/exec.rb +++ b/spec/unit/indirector/node/exec.rb @@ -4,59 +4,70 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/indirector/node/exec' -describe Puppet::Node::Exec, " when constructing the command to run" do +describe Puppet::Node::Exec do before do @indirection = mock 'indirection' Puppet.settings.stubs(:value).with(:external_nodes).returns("/echo") @searcher = Puppet::Node::Exec.new end - it "should use the external_node script as the command" do - Puppet.expects(:[]).with(:external_nodes).returns("/bin/echo") - @searcher.command.should == %w{/bin/echo} + it "should use the version of the facts as its version" do + version = mock 'version' + Puppet::Node::Facts.expects(:version).with("me").returns version + @searcher.version("me").should equal(version) end - it "should throw an exception if no external node command is set" do - Puppet.expects(:[]).with(:external_nodes).returns("none") - proc { @searcher.find("foo") }.should raise_error(ArgumentError) - end -end + describe "when constructing the command to run" do + it "should use the external_node script as the command" do + Puppet.expects(:[]).with(:external_nodes).returns("/bin/echo") + @searcher.command.should == %w{/bin/echo} + end -describe Puppet::Node::Exec, " when handling the results of the command" do - before do - @indirection = mock 'indirection' - Puppet.settings.stubs(:value).with(:external_nodes).returns("/echo") - @searcher = Puppet::Node::Exec.new - @node = stub 'node', :fact_merge => nil - @name = "yay" - Puppet::Node.expects(:new).with(@name).returns(@node) - @result = {} - # Use a local variable so the reference is usable in the execute() definition. - result = @result - @searcher.meta_def(:execute) do |command| - return YAML.dump(result) + it "should throw an exception if no external node command is set" do + Puppet.expects(:[]).with(:external_nodes).returns("none") + proc { @searcher.find("foo") }.should raise_error(ArgumentError) end end - it "should translate the YAML into a Node instance" do - # Use an empty hash - @searcher.find(@name).should equal(@node) - end + describe "when handling the results of the command" do + before do + @node = stub 'node', :fact_merge => nil + @name = "yay" + Puppet::Node.expects(:new).with(@name).returns(@node) + @result = {} + # Use a local variable so the reference is usable in the execute() definition. + result = @result + @searcher.meta_def(:execute) do |command| + return YAML.dump(result) + end + end - it "should set the resulting parameters as the node parameters" do - @result[:parameters] = {"a" => "b", "c" => "d"} - @node.expects(:parameters=).with "a" => "b", "c" => "d" - @searcher.find(@name) - end + it "should translate the YAML into a Node instance" do + # Use an empty hash + @searcher.find(@name).should equal(@node) + end - it "should set the resulting classes as the node classes" do - @result[:classes] = %w{one two} - @node.expects(:classes=).with %w{one two} - @searcher.find(@name) - end + it "should set the resulting parameters as the node parameters" do + @result[:parameters] = {"a" => "b", "c" => "d"} + @node.expects(:parameters=).with "a" => "b", "c" => "d" + @searcher.find(@name) + end - it "should merge the node's facts with its parameters" do - @node.expects(:fact_merge) - @searcher.find(@name) + it "should set the resulting classes as the node classes" do + @result[:classes] = %w{one two} + @node.expects(:classes=).with %w{one two} + @searcher.find(@name) + end + + it "should merge the node's facts with its parameters" do + @node.expects(:fact_merge) + @searcher.find(@name) + end + + it "should set the node's environment if one is provided" do + @result[:environment] = "yay" + @node.expects(:environment=).with "yay" + @searcher.find(@name) + end end end diff --git a/spec/unit/indirector/node/ldap.rb b/spec/unit/indirector/node/ldap.rb index 8c6888357..34456703d 100755 --- a/spec/unit/indirector/node/ldap.rb +++ b/spec/unit/indirector/node/ldap.rb @@ -4,168 +4,202 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/indirector/node/ldap' -module LdapNodeSearching - def setup +describe Puppet::Node::Ldap do + it "should use the version of the facts as its version" do @searcher = Puppet::Node::Ldap.new - @entries = {} - entries = @entries - - @connection = mock 'connection' - @entry = mock 'entry' - @connection.stubs(:search).yields(@entry) - @searcher.stubs(:connection).returns(@connection) - @searcher.stubs(:class_attributes).returns([]) - @searcher.stubs(:parent_attribute).returns(nil) - @searcher.stubs(:search_base).returns(:yay) - @searcher.stubs(:search_filter).returns(:filter) - - @node = mock 'node' - @node.stubs(:fact_merge) - @name = "mynode" - Puppet::Node.stubs(:new).with(@name).returns(@node) + version = mock 'version' + Puppet::Node::Facts.expects(:version).with("me").returns version + @searcher.version("me").should equal(version) end -end -describe Puppet::Node::Ldap, " when searching for nodes" do - include LdapNodeSearching + describe "when searching for nodes" do + before :each do + @searcher = Puppet::Node::Ldap.new + @entries = {} + entries = @entries + + @connection = mock 'connection' + @entry = mock 'entry' + @connection.stubs(:search).yields(@entry) + @searcher.stubs(:connection).returns(@connection) + @searcher.stubs(:class_attributes).returns([]) + @searcher.stubs(:parent_attribute).returns(nil) + @searcher.stubs(:search_base).returns(:yay) + @searcher.stubs(:search_filter).returns(:filter) + + @node = mock 'node' + @node.stubs(:fact_merge) + @name = "mynode" + Puppet::Node.stubs(:new).with(@name).returns(@node) + end - it "should return nil if no results are found in ldap" do - @connection.stubs(:search) - @searcher.find("mynode").should be_nil - end + it "should return nil if no results are found in ldap" do + @connection.stubs(:search) + @searcher.find("mynode").should be_nil + end - it "should return a node object if results are found in ldap" do - @entry.stubs(:to_hash).returns({}) - @searcher.find("mynode").should equal(@node) - end + it "should return a node object if results are found in ldap" do + @entry.stubs(:to_hash).returns({}) + @searcher.find("mynode").should equal(@node) + end - it "should deduplicate class values" do - @entry.stubs(:to_hash).returns({}) - @searcher.stubs(:class_attributes).returns(%w{one two}) - @entry.stubs(:vals).with("one").returns(%w{a b}) - @entry.stubs(:vals).with("two").returns(%w{b c}) - @node.expects(:classes=).with(%w{a b c}) - @searcher.find("mynode") - end + it "should deduplicate class values" do + @entry.stubs(:to_hash).returns({}) + @searcher.stubs(:class_attributes).returns(%w{one two}) + @entry.stubs(:vals).with("one").returns(%w{a b}) + @entry.stubs(:vals).with("two").returns(%w{b c}) + @node.expects(:classes=).with(%w{a b c}) + @searcher.find("mynode") + end - it "should add any values stored in the class_attributes attributes to the node classes" do - @entry.stubs(:to_hash).returns({}) - @searcher.stubs(:class_attributes).returns(%w{one two}) - @entry.stubs(:vals).with("one").returns(%w{a b}) - @entry.stubs(:vals).with("two").returns(%w{c d}) - @node.expects(:classes=).with(%w{a b c d}) - @searcher.find("mynode") - end + it "should add any values stored in the class_attributes attributes to the node classes" do + @entry.stubs(:to_hash).returns({}) + @searcher.stubs(:class_attributes).returns(%w{one two}) + @entry.stubs(:vals).with("one").returns(%w{a b}) + @entry.stubs(:vals).with("two").returns(%w{c d}) + @node.expects(:classes=).with(%w{a b c d}) + @searcher.find("mynode") + end - it "should add all entry attributes as node parameters" do - @entry.stubs(:to_hash).returns("one" => ["two"], "three" => ["four"]) - @node.expects(:parameters=).with("one" => "two", "three" => "four") - @searcher.find("mynode") - end + it "should add all entry attributes as node parameters" do + @entry.stubs(:to_hash).returns("one" => ["two"], "three" => ["four"]) + @node.expects(:parameters=).with("one" => "two", "three" => "four") + @searcher.find("mynode") + end - it "should retain false parameter values" do - @entry.stubs(:to_hash).returns("one" => [false]) - @node.expects(:parameters=).with("one" => false) - @searcher.find("mynode") - end + it "should set the node's environment to the environment of the results" do + @entry.stubs(:to_hash).returns("environment" => ["test"]) + @node.stubs(:parameters=) + @node.expects(:environment=).with("test") + @searcher.find("mynode") + end - it "should turn single-value parameter value arrays into single non-arrays" do - @entry.stubs(:to_hash).returns("one" => ["a"]) - @node.expects(:parameters=).with("one" => "a") - @searcher.find("mynode") - end + it "should retain false parameter values" do + @entry.stubs(:to_hash).returns("one" => [false]) + @node.expects(:parameters=).with("one" => false) + @searcher.find("mynode") + end - it "should keep multi-valued parametes as arrays" do - @entry.stubs(:to_hash).returns("one" => ["a", "b"]) - @node.expects(:parameters=).with("one" => ["a", "b"]) - @searcher.find("mynode") - end -end + it "should turn single-value parameter value arrays into single non-arrays" do + @entry.stubs(:to_hash).returns("one" => ["a"]) + @node.expects(:parameters=).with("one" => "a") + @searcher.find("mynode") + end -describe Puppet::Node::Ldap, " when a parent node is specified" do - include LdapNodeSearching + it "should keep multi-valued parametes as arrays" do + @entry.stubs(:to_hash).returns("one" => ["a", "b"]) + @node.expects(:parameters=).with("one" => ["a", "b"]) + @searcher.find("mynode") + end - before do - @parent = mock 'parent' - @parent_parent = mock 'parent_parent' + describe "and a parent node is specified" do + before do + @parent = mock 'parent' + @parent_parent = mock 'parent_parent' - @searcher.meta_def(:search_filter) do |name| - return name - end - @connection.stubs(:search).with { |*args| args[2] == @name }.yields(@entry) - @connection.stubs(:search).with { |*args| args[2] == 'parent' }.yields(@parent) - @connection.stubs(:search).with { |*args| args[2] == 'parent_parent' }.yields(@parent_parent) + @searcher.meta_def(:search_filter) do |name| + return name + end + @connection.stubs(:search).with { |*args| args[2] == @name }.yields(@entry) + @connection.stubs(:search).with { |*args| args[2] == 'parent' }.yields(@parent) + @connection.stubs(:search).with { |*args| args[2] == 'parent_parent' }.yields(@parent_parent) - @searcher.stubs(:parent_attribute).returns(:parent) - end + @searcher.stubs(:parent_attribute).returns(:parent) + end - it "should fail if the parent cannot be found" do - @connection.stubs(:search).with { |*args| args[2] == 'parent' }.returns("whatever") + it "should fail if the parent cannot be found" do + @connection.stubs(:search).with { |*args| args[2] == 'parent' }.returns("whatever") - @entry.stubs(:to_hash).returns({}) - @entry.stubs(:vals).with(:parent).returns(%w{parent}) + @entry.stubs(:to_hash).returns({}) + @entry.stubs(:vals).with(:parent).returns(%w{parent}) - proc { @searcher.find("mynode") }.should raise_error(Puppet::Error) - end + proc { @searcher.find("mynode") }.should raise_error(Puppet::Error) + end - it "should add any parent classes to the node's classes" do - @entry.stubs(:to_hash).returns({}) - @entry.stubs(:vals).with(:parent).returns(%w{parent}) - @entry.stubs(:vals).with("classes").returns(%w{a b}) + it "should add any parent classes to the node's classes" do + @entry.stubs(:to_hash).returns({}) + @entry.stubs(:vals).with(:parent).returns(%w{parent}) + @entry.stubs(:vals).with("classes").returns(%w{a b}) - @parent.stubs(:to_hash).returns({}) - @parent.stubs(:vals).with("classes").returns(%w{c d}) - @parent.stubs(:vals).with(:parent).returns(nil) + @parent.stubs(:to_hash).returns({}) + @parent.stubs(:vals).with("classes").returns(%w{c d}) + @parent.stubs(:vals).with(:parent).returns(nil) - @searcher.stubs(:class_attributes).returns(%w{classes}) - @node.expects(:classes=).with(%w{a b c d}) - @searcher.find("mynode") - end + @searcher.stubs(:class_attributes).returns(%w{classes}) + @node.expects(:classes=).with(%w{a b c d}) + @searcher.find("mynode") + end - it "should add any parent parameters to the node's parameters" do - @entry.stubs(:to_hash).returns("one" => "two") - @entry.stubs(:vals).with(:parent).returns(%w{parent}) + it "should add any parent parameters to the node's parameters" do + @entry.stubs(:to_hash).returns("one" => "two") + @entry.stubs(:vals).with(:parent).returns(%w{parent}) - @parent.stubs(:to_hash).returns("three" => "four") - @parent.stubs(:vals).with(:parent).returns(nil) + @parent.stubs(:to_hash).returns("three" => "four") + @parent.stubs(:vals).with(:parent).returns(nil) - @node.expects(:parameters=).with("one" => "two", "three" => "four") - @searcher.find("mynode") - end + @node.expects(:parameters=).with("one" => "two", "three" => "four") + @searcher.find("mynode") + end - it "should prefer node parameters over parent parameters" do - @entry.stubs(:to_hash).returns("one" => "two") - @entry.stubs(:vals).with(:parent).returns(%w{parent}) + it "should prefer node parameters over parent parameters" do + @entry.stubs(:to_hash).returns("one" => "two") + @entry.stubs(:vals).with(:parent).returns(%w{parent}) - @parent.stubs(:to_hash).returns("one" => "three") - @parent.stubs(:vals).with(:parent).returns(nil) + @parent.stubs(:to_hash).returns("one" => "three") + @parent.stubs(:vals).with(:parent).returns(nil) - @node.expects(:parameters=).with("one" => "two") - @searcher.find("mynode") - end + @node.expects(:parameters=).with("one" => "two") + @searcher.find("mynode") + end - it "should recursively look up parent information" do - @entry.stubs(:to_hash).returns("one" => "two") - @entry.stubs(:vals).with(:parent).returns(%w{parent}) + it "should use the parent's environment if the node has none" do + @entry.stubs(:to_hash).returns({}) + @entry.stubs(:vals).with(:parent).returns(%w{parent}) - @parent.stubs(:to_hash).returns("three" => "four") - @parent.stubs(:vals).with(:parent).returns(['parent_parent']) + @parent.stubs(:to_hash).returns("environment" => ["parent"]) + @parent.stubs(:vals).with(:parent).returns(nil) - @parent_parent.stubs(:to_hash).returns("five" => "six") - @parent_parent.stubs(:vals).with(:parent).returns(nil) - @parent_parent.stubs(:vals).with(:parent).returns(nil) + @node.stubs(:parameters=) + @node.expects(:environment=).with("parent") + @searcher.find("mynode") + end - @node.expects(:parameters=).with("one" => "two", "three" => "four", "five" => "six") - @searcher.find("mynode") - end + it "should prefer the node's environment to the parent's" do + @entry.stubs(:to_hash).returns("environment" => %w{child}) + @entry.stubs(:vals).with(:parent).returns(%w{parent}) + + @parent.stubs(:to_hash).returns("environment" => ["parent"]) + @parent.stubs(:vals).with(:parent).returns(nil) + + @node.stubs(:parameters=) + @node.expects(:environment=).with("child") + @searcher.find("mynode") + end + + it "should recursively look up parent information" do + @entry.stubs(:to_hash).returns("one" => "two") + @entry.stubs(:vals).with(:parent).returns(%w{parent}) - it "should not allow loops in parent declarations" do - @entry.stubs(:to_hash).returns("one" => "two") - @entry.stubs(:vals).with(:parent).returns(%w{parent}) + @parent.stubs(:to_hash).returns("three" => "four") + @parent.stubs(:vals).with(:parent).returns(['parent_parent']) - @parent.stubs(:to_hash).returns("three" => "four") - @parent.stubs(:vals).with(:parent).returns([@name]) - proc { @searcher.find("mynode") }.should raise_error(ArgumentError) + @parent_parent.stubs(:to_hash).returns("five" => "six") + @parent_parent.stubs(:vals).with(:parent).returns(nil) + @parent_parent.stubs(:vals).with(:parent).returns(nil) + + @node.expects(:parameters=).with("one" => "two", "three" => "four", "five" => "six") + @searcher.find("mynode") + end + + it "should not allow loops in parent declarations" do + @entry.stubs(:to_hash).returns("one" => "two") + @entry.stubs(:vals).with(:parent).returns(%w{parent}) + + @parent.stubs(:to_hash).returns("three" => "four") + @parent.stubs(:vals).with(:parent).returns([@name]) + proc { @searcher.find("mynode") }.should raise_error(ArgumentError) + end + end end end diff --git a/spec/unit/indirector/node/plain.rb b/spec/unit/indirector/node/plain.rb index 105d0ed63..943af52b4 100755 --- a/spec/unit/indirector/node/plain.rb +++ b/spec/unit/indirector/node/plain.rb @@ -15,4 +15,10 @@ describe Puppet::Node::Plain do node.expects(:fact_merge) @searcher.find("mynode") end + + it "should use the version of the facts as its version" do + version = mock 'version' + Puppet::Node::Facts.expects(:version).with("me").returns version + @searcher.version("me").should equal(version) + end end diff --git a/spec/unit/indirector/rest.rb b/spec/unit/indirector/rest.rb index cf29577b9..3c4716dc8 100755 --- a/spec/unit/indirector/rest.rb +++ b/spec/unit/indirector/rest.rb @@ -13,7 +13,7 @@ describe Puppet::Indirector::REST do @rest_class = Class.new(Puppet::Indirector::REST) do def self.to_s - "Testing" + "This::Is::A::Test::Class" end end diff --git a/spec/unit/indirector/terminus.rb b/spec/unit/indirector/terminus.rb index 99193dbc3..86813e4e5 100755 --- a/spec/unit/indirector/terminus.rb +++ b/spec/unit/indirector/terminus.rb @@ -5,8 +5,8 @@ require 'puppet/defaults' require 'puppet/indirector' require 'puppet/indirector/file' -module TerminusInstanceTesting - def setup +describe Puppet::Indirector::Terminus do + before :each do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @indirection = stub 'indirection', :name => :my_stuff, :register_terminus_type => nil Puppet::Indirector::Indirection.stubs(:instance).with(:my_stuff).returns(@indirection) @@ -22,51 +22,143 @@ module TerminusInstanceTesting end @terminus = @terminus_class.new end -end -describe Puppet::Indirector::Terminus do - include TerminusInstanceTesting + describe Puppet::Indirector::Terminus do - it "should provide a method for setting terminus class documentation" do - @terminus_class.should respond_to(:desc) - end + it "should provide a method for setting terminus class documentation" do + @terminus_class.should respond_to(:desc) + end - it "should support a class-level name attribute" do - @terminus_class.should respond_to(:name) - end + it "should support a class-level name attribute" do + @terminus_class.should respond_to(:name) + end - it "should support a class-level indirection attribute" do - @terminus_class.should respond_to(:indirection) - end + it "should support a class-level indirection attribute" do + @terminus_class.should respond_to(:indirection) + end - it "should support a class-level terminus-type attribute" do - @terminus_class.should respond_to(:terminus_type) - end + it "should support a class-level terminus-type attribute" do + @terminus_class.should respond_to(:terminus_type) + end - it "should support a class-level model attribute" do - @terminus_class.should respond_to(:model) + it "should support a class-level model attribute" do + @terminus_class.should respond_to(:model) + end + + it "should accept indirection instances as its indirection" do + indirection = stub 'indirection', :is_a? => true, :register_terminus_type => nil + proc { @terminus_class.indirection = indirection }.should_not raise_error + @terminus_class.indirection.should equal(indirection) + end + + it "should look up indirection instances when only a name has been provided" do + indirection = mock 'indirection' + Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(indirection) + @terminus_class.indirection = :myind + @terminus_class.indirection.should equal(indirection) + end + + it "should fail when provided a name that does not resolve to an indirection" do + Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(nil) + proc { @terminus_class.indirection = :myind }.should raise_error(ArgumentError) + + # It shouldn't overwrite our existing one (or, more normally, it shouldn't set + # anything). + @terminus_class.indirection.should equal(@indirection) + end end - it "should accept indirection instances as its indirection" do - indirection = stub 'indirection', :is_a? => true, :register_terminus_type => nil - proc { @terminus_class.indirection = indirection }.should_not raise_error - @terminus_class.indirection.should equal(indirection) + describe Puppet::Indirector::Terminus, " when creating terminus classes" do + it "should associate the subclass with an indirection based on the subclass constant" do + @terminus.indirection.should equal(@indirection) + end + + it "should set the subclass's type to the abstract terminus name" do + @terminus.terminus_type.should == :abstract + end + + it "should set the subclass's name to the indirection name" do + @terminus.name.should == :term_type + end + + it "should set the subclass's model to the indirection model" do + @indirection.expects(:model).returns :yay + @terminus.model.should == :yay + end end - it "should look up indirection instances when only a name has been provided" do - indirection = mock 'indirection' - Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(indirection) - @terminus_class.indirection = :myind - @terminus_class.indirection.should equal(indirection) + describe Puppet::Indirector::Terminus, " when a terminus instance" do + + it "should return the class's name as its name" do + @terminus.name.should == :term_type + end + + it "should return the class's indirection as its indirection" do + @terminus.indirection.should equal(@indirection) + end + + it "should set the instances's type to the abstract terminus type's name" do + @terminus.terminus_type.should == :abstract + end + + it "should set the instances's model to the indirection's model" do + @indirection.expects(:model).returns :yay + @terminus.model.should == :yay + end end - it "should fail when provided a name that does not resolve to an indirection" do - Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(nil) - proc { @terminus_class.indirection = :myind }.should raise_error(ArgumentError) + describe Puppet::Indirector::Terminus, " when managing indirected instances" do + + it "should support comparing an instance's version with the terminus's version using just the instance's key" do + @terminus.should respond_to(:has_most_recent?) + end + + it "should fail if the :version method has not been overridden and no :find method is available" do + proc { @terminus.version('yay') }.should raise_error(Puppet::DevError) + end + + it "should use a found instance's version by default" do + name = 'instance' + instance = stub name, :version => 2 + @terminus.expects(:find).with(name).returns(instance) + @terminus.version(name).should == 2 + end - # It shouldn't overwrite our existing one (or, more normally, it shouldn't set - # anything). - @terminus_class.indirection.should equal(@indirection) + it "should return nil as the version if no instance can be found" do + name = 'instance' + @terminus.expects(:find).with(name).returns(nil) + @terminus.version(name).should be_nil + end + + it "should consider an instance fresh if its version is more recent than the version provided" do + name = "yay" + @terminus.expects(:version).with(name).returns(5) + @terminus.has_most_recent?(name, 4).should be_true + end + + it "should consider an instance fresh if its version is equal to the version provided" do + name = "yay" + @terminus.expects(:version).with(name).returns(5) + @terminus.has_most_recent?(name, 5).should be_true + end + + it "should consider an instance not fresh if the provided version is more recent than its version" do + name = "yay" + @terminus.expects(:version).with(name).returns(4) + @terminus.has_most_recent?(name, 5).should be_false + end + + # Times annoyingly can't be compared directly to numbers, and our + # default version is 0. + it "should convert versions to floats when checking for freshness" do + existing = mock 'existing version' + new = mock 'new version' + existing.expects(:to_f).returns(1.0) + new.expects(:to_f).returns(1.0) + name = "yay" + @terminus.expects(:version).with(name).returns(existing) + @terminus.has_most_recent?(name, new) + end end end @@ -205,98 +297,3 @@ describe Puppet::Indirector::Terminus, " when creating terminus class types" do end end -describe Puppet::Indirector::Terminus, " when creating terminus classes" do - include TerminusInstanceTesting - it "should associate the subclass with an indirection based on the subclass constant" do - @terminus.indirection.should equal(@indirection) - end - - it "should set the subclass's type to the abstract terminus name" do - @terminus.terminus_type.should == :abstract - end - - it "should set the subclass's name to the indirection name" do - @terminus.name.should == :term_type - end - - it "should set the subclass's model to the indirection model" do - @indirection.expects(:model).returns :yay - @terminus.model.should == :yay - end -end - -describe Puppet::Indirector::Terminus, " when a terminus instance" do - include TerminusInstanceTesting - - it "should return the class's name as its name" do - @terminus.name.should == :term_type - end - - it "should return the class's indirection as its indirection" do - @terminus.indirection.should equal(@indirection) - end - - it "should set the instances's type to the abstract terminus type's name" do - @terminus.terminus_type.should == :abstract - end - - it "should set the instances's model to the indirection's model" do - @indirection.expects(:model).returns :yay - @terminus.model.should == :yay - end -end - -describe Puppet::Indirector::Terminus, " when managing indirected instances" do - include TerminusInstanceTesting - - it "should support comparing an instance's version with the terminus's version using just the instance's key" do - @terminus.should respond_to(:has_most_recent?) - end - - it "should fail if the :version method has not been overridden and no :find method is available" do - proc { @terminus.version('yay') }.should raise_error(Puppet::DevError) - end - - it "should use a found instance's version by default" do - name = 'instance' - instance = stub name, :version => 2 - @terminus.expects(:find).with(name).returns(instance) - @terminus.version(name).should == 2 - end - - it "should return nil as the version if no instance can be found" do - name = 'instance' - @terminus.expects(:find).with(name).returns(nil) - @terminus.version(name).should be_nil - end - - it "should consider an instance fresh if its version is more recent than the version provided" do - name = "yay" - @terminus.expects(:version).with(name).returns(5) - @terminus.has_most_recent?(name, 4).should be_true - end - - it "should consider an instance fresh if its version is equal to the version provided" do - name = "yay" - @terminus.expects(:version).with(name).returns(5) - @terminus.has_most_recent?(name, 5).should be_true - end - - it "should consider an instance not fresh if the provided version is more recent than its version" do - name = "yay" - @terminus.expects(:version).with(name).returns(4) - @terminus.has_most_recent?(name, 5).should be_false - end - - # Times annoyingly can't be compared directly to numbers, and our - # default version is 0. - it "should convert versions to floats when checking for freshness" do - existing = mock 'existing version' - new = mock 'new version' - existing.expects(:to_f).returns(1.0) - new.expects(:to_f).returns(1.0) - name = "yay" - @terminus.expects(:version).with(name).returns(existing) - @terminus.has_most_recent?(name, new) - end -end diff --git a/spec/unit/indirector/yaml.rb b/spec/unit/indirector/yaml.rb index f217a31b5..339529ab0 100755 --- a/spec/unit/indirector/yaml.rb +++ b/spec/unit/indirector/yaml.rb @@ -4,8 +4,8 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/indirector/yaml' -module YamlTesting - def setup +describe Puppet::Indirector::Yaml, " when choosing file location" do + before :each do @indirection = stub 'indirection', :name => :my_yaml, :register_terminus_type => nil Puppet::Indirector::Indirection.stubs(:instance).with(:my_yaml).returns(@indirection) @store_class = Class.new(Puppet::Indirector::Yaml) do @@ -20,85 +20,91 @@ module YamlTesting @subject.name = :me @dir = "/what/ever" - Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).with(:yamldir).returns(@dir) end -end -describe Puppet::Indirector::Yaml, " when choosing file location" do - include YamlTesting + it "should use the mtime of the written file as the version" do + stat = mock 'stat' + FileTest.stubs(:exist?).returns true + File.expects(:stat).returns stat + time = Time.now + stat.expects(:mtime).returns time - it "should store all files in a single file root set in the Puppet defaults" do - @store.send(:path, :me).should =~ %r{^#{@dir}} + @store.version(:me).should equal(time) end - it "should use the terminus name for choosing the subdirectory" do - @store.send(:path, :me).should =~ %r{^#{@dir}/my_yaml} - end + describe Puppet::Indirector::Yaml, " when choosing file location" do - it "should use the object's name to determine the file name" do - @store.send(:path, :me).should =~ %r{me.yaml$} - end -end + it "should store all files in a single file root set in the Puppet defaults" do + @store.send(:path, :me).should =~ %r{^#{@dir}} + end -describe Puppet::Indirector::Yaml, " when storing objects as YAML" do - include YamlTesting + it "should use the terminus name for choosing the subdirectory" do + @store.send(:path, :me).should =~ %r{^#{@dir}/my_yaml} + end - it "should only store objects that respond to :name" do - proc { @store.save(Object.new) }.should raise_error(ArgumentError) + it "should use the object's name to determine the file name" do + @store.send(:path, :me).should =~ %r{me.yaml$} + end end - it "should convert Ruby objects to YAML and write them to disk" do - yaml = @subject.to_yaml - file = mock 'file' - path = @store.send(:path, @subject.name) - FileTest.expects(:exist?).with(File.dirname(path)).returns(true) - File.expects(:open).with(path, "w", 0660).yields(file) - file.expects(:print).with(yaml) + describe Puppet::Indirector::Yaml, " when storing objects as YAML" do - @store.save(@subject) - end + it "should only store objects that respond to :name" do + proc { @store.save(Object.new) }.should raise_error(ArgumentError) + end - it "should create the indirection subdirectory if it does not exist" do - yaml = @subject.to_yaml - file = mock 'file' - path = @store.send(:path, @subject.name) - dir = File.dirname(path) - FileTest.expects(:exist?).with(dir).returns(false) - Dir.expects(:mkdir).with(dir) - File.expects(:open).with(path, "w", 0660).yields(file) - file.expects(:print).with(yaml) - - @store.save(@subject) - end -end + it "should convert Ruby objects to YAML and write them to disk" do + yaml = @subject.to_yaml + file = mock 'file' + path = @store.send(:path, @subject.name) + FileTest.expects(:exist?).with(File.dirname(path)).returns(true) + File.expects(:open).with(path, "w", 0660).yields(file) + file.expects(:print).with(yaml) -describe Puppet::Indirector::Yaml, " when retrieving YAML" do - include YamlTesting + @store.save(@subject) + end - it "should require the name of the object to retrieve" do - proc { @store.find(nil) }.should raise_error(ArgumentError) + it "should create the indirection subdirectory if it does not exist" do + yaml = @subject.to_yaml + file = mock 'file' + path = @store.send(:path, @subject.name) + dir = File.dirname(path) + FileTest.expects(:exist?).with(dir).returns(false) + Dir.expects(:mkdir).with(dir) + File.expects(:open).with(path, "w", 0660).yields(file) + file.expects(:print).with(yaml) + + @store.save(@subject) + end end - it "should read YAML in from disk and convert it to Ruby objects" do - path = @store.send(:path, @subject.name) + describe Puppet::Indirector::Yaml, " when retrieving YAML" do + + it "should require the name of the object to retrieve" do + proc { @store.find(nil) }.should raise_error(ArgumentError) + end - yaml = @subject.to_yaml - FileTest.expects(:exist?).with(path).returns(true) - File.expects(:read).with(path).returns(yaml) + it "should read YAML in from disk and convert it to Ruby objects" do + path = @store.send(:path, @subject.name) - @store.find(@subject.name).instance_variable_get("@name").should == :me - end + yaml = @subject.to_yaml + FileTest.expects(:exist?).with(path).returns(true) + File.expects(:read).with(path).returns(yaml) - it "should fail coherently when the stored YAML is invalid" do - path = @store.send(:path, @subject.name) + @store.find(@subject.name).instance_variable_get("@name").should == :me + end - # Something that will fail in yaml - yaml = "--- !ruby/object:Hash" + it "should fail coherently when the stored YAML is invalid" do + path = @store.send(:path, @subject.name) - FileTest.expects(:exist?).with(path).returns(true) - File.expects(:read).with(path).returns(yaml) + # Something that will fail in yaml + yaml = "--- !ruby/object:Hash" - proc { @store.find(@subject.name) }.should raise_error(Puppet::Error) + FileTest.expects(:exist?).with(path).returns(true) + File.expects(:read).with(path).returns(yaml) + + proc { @store.find(@subject.name) }.should raise_error(Puppet::Error) + end end end diff --git a/spec/unit/network/client.rb b/spec/unit/network/client.rb new file mode 100644 index 000000000..bc41efb4f --- /dev/null +++ b/spec/unit/network/client.rb @@ -0,0 +1,43 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-3-24. +# Copyright (c) 2008. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/client' + +describe Puppet::Network::Client do + before do + Puppet::Network::HttpPool.stubs(:cert_setup) + end + describe "when keep-alive is enabled" do + before do + Puppet::Network::HttpPool.stubs(:keep_alive?).returns true + end + it "should start the http client up on creation" do + http = mock 'http' + http.stub_everything + http.expects(:start) + Net::HTTP.stubs(:new).returns http + + # Pick a random subclass... + Puppet::Network::Client.master.new :Server => Puppet[:server] + end + end + + describe "when keep-alive is disabled" do + before do + Puppet::Network::HttpPool.stubs(:keep_alive?).returns false + end + it "should not start the http client up on creation" do + http = mock 'http' + http.stub_everything + http.expects(:start).never + Net::HTTP.stubs(:new).returns http + + # Pick a random subclass... + Puppet::Network::Client.master.new :Server => Puppet[:server] + end + end +end diff --git a/spec/unit/network/http_pool.rb b/spec/unit/network/http_pool.rb index 49da7d8f3..1fbc17471 100755 --- a/spec/unit/network/http_pool.rb +++ b/spec/unit/network/http_pool.rb @@ -9,6 +9,13 @@ require 'puppet/network/http_pool' describe Puppet::Network::HttpPool, " when adding certificate information to http instances" do before do @http = mock 'http' + [:cert_store=, :verify_mode=, :ca_file=, :cert=, :key=].each { |m| @http.stubs(m) } + @store = stub 'store' + [:add_file,:purpose=].each { |m| @store.stubs(m) } + end + + it "should have keep-alive disabled" do + Puppet::Network::HttpPool::HTTP_KEEP_ALIVE.should be_false end it "should do nothing if no certificate is available" do @@ -19,26 +26,19 @@ describe Puppet::Network::HttpPool, " when adding certificate information to htt it "should add a certificate store" do Puppet::Network::HttpPool.stubs(:read_cert).returns(true) - store = stub "store" - OpenSSL::X509::Store.expects(:new).returns(store) - store.stubs(:add_file) - store.stubs(:purpose=) - [:verify_mode=, :ca_file=, :cert=, :key=].each { |method| @http.stubs(method) } - @http.expects(:cert_store=).with(store) + Puppet::Network::HttpPool.stubs(:key).returns(:mykey) + OpenSSL::X509::Store.expects(:new).returns(@store) + @http.expects(:cert_store=).with(@store) Puppet::Network::HttpPool.cert_setup(@http) end it "should add the local CA cert to the certificate store" do Puppet::Network::HttpPool.stubs(:read_cert).returns(true) - store = stub "store" - OpenSSL::X509::Store.expects(:new).returns(store) - store.stubs(:purpose=) - @http.stubs(:cert_store=) + OpenSSL::X509::Store.expects(:new).returns(@store) Puppet.settings.stubs(:value).with(:localcacert).returns("/some/file") Puppet.settings.stubs(:value).with(:localcacert).returns("/some/file") - store.expects(:add_file).with("/some/file") - [:store=, :verify_mode=, :ca_file=, :cert=, :key=].each { |method| @http.stubs(method) } + @store.expects(:add_file).with("/some/file") Puppet::Network::HttpPool.stubs(:key).returns(:whatever) @@ -47,12 +47,10 @@ describe Puppet::Network::HttpPool, " when adding certificate information to htt it "should set the purpose of the cert store to OpenSSL::X509::PURPOSE_SSL_CLIENT" do Puppet::Network::HttpPool.stubs(:read_cert).returns(true) - store = stub "store" - OpenSSL::X509::Store.expects(:new).returns(store) - store.stubs(:add_file) - [:cert_store=, :verify_mode=, :ca_file=, :cert=, :key=].each { |method| @http.stubs(method) } + Puppet::Network::HttpPool.stubs(:key).returns(:mykey) + OpenSSL::X509::Store.expects(:new).returns(@store) - store.expects(:purpose=).with(OpenSSL::X509::PURPOSE_SSL_CLIENT) + @store.expects(:purpose=).with(OpenSSL::X509::PURPOSE_SSL_CLIENT) Puppet::Network::HttpPool.cert_setup(@http) end @@ -60,7 +58,8 @@ describe Puppet::Network::HttpPool, " when adding certificate information to htt it "should add the client certificate" do Puppet::Network::HttpPool.stubs(:read_cert).returns(true) Puppet::Network::HttpPool.stubs(:cert).returns(:mycert) - [:cert_store=, :verify_mode=, :ca_file=, :key=].each { |method| @http.stubs(method) } + Puppet::Network::HttpPool.stubs(:key).returns(:mykey) + OpenSSL::X509::Store.expects(:new).returns(@store) @http.expects(:cert=).with(:mycert) @@ -70,7 +69,7 @@ describe Puppet::Network::HttpPool, " when adding certificate information to htt it "should add the client key" do Puppet::Network::HttpPool.stubs(:read_cert).returns(true) Puppet::Network::HttpPool.stubs(:key).returns(:mykey) - [:cert_store=, :verify_mode=, :cert=, :ca_file=].each { |method| @http.stubs(method) } + OpenSSL::X509::Store.expects(:new).returns(@store) @http.expects(:key=).with(:mykey) @@ -79,7 +78,8 @@ describe Puppet::Network::HttpPool, " when adding certificate information to htt it "should set the verify mode to OpenSSL::SSL::VERIFY_PEER" do Puppet::Network::HttpPool.stubs(:read_cert).returns(true) - [:key=, :cert=, :cert_store=, :ca_file=].each { |method| @http.stubs(method) } + Puppet::Network::HttpPool.stubs(:key).returns(:mykey) + OpenSSL::X509::Store.expects(:new).returns(@store) @http.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) @@ -89,12 +89,7 @@ describe Puppet::Network::HttpPool, " when adding certificate information to htt it "should set the ca file" do Puppet::Network::HttpPool.stubs(:read_cert).returns(true) Puppet.settings.stubs(:value).with(:localcacert).returns("/some/file") - [:key=, :cert=, :cert_store=, :verify_mode=].each { |method| @http.stubs(method) } - - store = stub "store" - OpenSSL::X509::Store.expects(:new).returns(store) - store.stubs(:purpose=) - store.stubs(:add_file) + OpenSSL::X509::Store.expects(:new).returns(@store) @http.expects(:ca_file=).with("/some/file") @@ -111,117 +106,135 @@ describe Puppet::Network::HttpPool, " when adding certificate information to htt after do Puppet::Network::HttpPool.clear_http_instances end -end -describe Puppet::Network::HttpPool, " when managing http instances" do - def stub_settings(settings) - settings.each do |param, value| - Puppet.settings.stubs(:value).with(param).returns(value) + describe "when managing http instances" do + def stub_settings(settings) + settings.each do |param, value| + Puppet.settings.stubs(:value).with(param).returns(value) + end end - end - - before do - # All of hte cert stuff is tested elsewhere - Puppet::Network::HttpPool.stubs(:cert_setup) - end - it "should return an http instance created with the passed host and port" do - http = stub 'http', :use_ssl= => nil, :read_timeout= => nil, :open_timeout= => nil, :enable_post_connection_check= => nil, :started? => false - Net::HTTP.expects(:new).with("me", 54321, nil, nil).returns(http) - Puppet::Network::HttpPool.http_instance("me", 54321).should equal(http) - end - - it "should enable ssl on the http instance" do - Puppet::Network::HttpPool.http_instance("me", 54321).instance_variable_get("@use_ssl").should be_true - end - - it "should set the read timeout" do - Puppet::Network::HttpPool.http_instance("me", 54321).read_timeout.should == 120 - end + before do + # All of hte cert stuff is tested elsewhere + Puppet::Network::HttpPool.stubs(:cert_setup) + end - it "should set the open timeout" do - Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 - end + it "should return an http instance created with the passed host and port" do + http = stub 'http', :use_ssl= => nil, :read_timeout= => nil, :open_timeout= => nil, :enable_post_connection_check= => nil, :started? => false + Net::HTTP.expects(:new).with("me", 54321, nil, nil).returns(http) + Puppet::Network::HttpPool.http_instance("me", 54321).should equal(http) + end - it "should default to http_enable_post_connection_check being enabled" do - Puppet.settings[:http_enable_post_connection_check].should be_true - end + it "should enable ssl on the http instance" do + Puppet::Network::HttpPool.http_instance("me", 54321).instance_variable_get("@use_ssl").should be_true + end - # JJM: I'm not sure if this is correct, as this really follows the - # configuration option. - it "should set enable_post_connection_check true " do - Puppet::Network::HttpPool.http_instance("me", 54321).instance_variable_get("@enable_post_connection_check").should be_true - end + it "should set the read timeout" do + Puppet::Network::HttpPool.http_instance("me", 54321).read_timeout.should == 120 + end - it "should create the http instance with the proxy host and port set if the http_proxy is not set to 'none'" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true - Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 - end + it "should set the open timeout" do + Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 + end - it "should cache http instances" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true - old = Puppet::Network::HttpPool.http_instance("me", 54321) - Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) - end + it "should default to http_enable_post_connection_check being enabled" do + Puppet.settings[:http_enable_post_connection_check].should be_true + end - it "should have a mechanism for getting a new http instance instead of the cached instance" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true - old = Puppet::Network::HttpPool.http_instance("me", 54321) - Puppet::Network::HttpPool.http_instance("me", 54321, true).should_not equal(old) - end + # JJM: I'm not sure if this is correct, as this really follows the + # configuration option. + it "should set enable_post_connection_check true " do + Puppet::Network::HttpPool.http_instance("me", 54321).instance_variable_get("@enable_post_connection_check").should be_true + end - it "should close existing, open connections when requesting a new connection" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true - old = Puppet::Network::HttpPool.http_instance("me", 54321) - old.expects(:started?).returns(true) - old.expects(:finish) - Puppet::Network::HttpPool.http_instance("me", 54321, true) - end + it "should create the http instance with the proxy host and port set if the http_proxy is not set to 'none'" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true + Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 + end - it "should have a mechanism for clearing the http cache" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true - old = Puppet::Network::HttpPool.http_instance("me", 54321) - Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) - old = Puppet::Network::HttpPool.http_instance("me", 54321) - Puppet::Network::HttpPool.clear_http_instances - Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) - end + describe "when http keep-alive is enabled" do + before do + Puppet::Network::HttpPool.stubs(:keep_alive?).returns true + end + + it "should cache http instances" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) + end + + it "should have a mechanism for getting a new http instance instead of the cached instance" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.http_instance("me", 54321, true).should_not equal(old) + end + + it "should close existing, open connections when requesting a new connection" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true + old = Puppet::Network::HttpPool.http_instance("me", 54321) + old.expects(:started?).returns(true) + old.expects(:finish) + Puppet::Network::HttpPool.http_instance("me", 54321, true) + end + + it "should have a mechanism for clearing the http cache" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.clear_http_instances + Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) + end + + it "should close open http connections when clearing the cache" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true + one = Puppet::Network::HttpPool.http_instance("me", 54321) + one.expects(:started?).returns(true) + one.expects(:finish).returns(true) + Puppet::Network::HttpPool.clear_http_instances + end + + it "should not close unopened http connections when clearing the cache" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true + one = Puppet::Network::HttpPool.http_instance("me", 54321) + one.expects(:started?).returns(false) + one.expects(:finish).never + Puppet::Network::HttpPool.clear_http_instances + end + end - it "should close open http connections when clearing the cache" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true - one = Puppet::Network::HttpPool.http_instance("me", 54321) - one.expects(:started?).returns(true) - one.expects(:finish).returns(true) - Puppet::Network::HttpPool.clear_http_instances - end + describe "when http keep-alive is disabled" do + before do + Puppet::Network::HttpPool.stubs(:keep_alive?).returns false + end - it "should not close unopened http connections when clearing the cache" do - stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true - one = Puppet::Network::HttpPool.http_instance("me", 54321) - one.expects(:started?).returns(false) - one.expects(:finish).never - Puppet::Network::HttpPool.clear_http_instances - end + it "should not cache http instances" do + stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :http_enable_post_connection_check => true + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) + end + end - # We mostly have to do this for testing, since in real life people - # won't change certs within a single process. - it "should remove its loaded certificate when clearing the cache" do - Puppet::Network::HttpPool.instance_variable_set("@cert", :yay) - Puppet::Network::HttpPool.clear_http_instances - # Can't use the accessor, because it will read the cert in - Puppet::Network::HttpPool.instance_variable_get("@cert").should be_nil - end + # We mostly have to do this for testing, since in real life people + # won't change certs within a single process. + it "should remove its loaded certificate when clearing the cache" do + Puppet::Network::HttpPool.instance_variable_set("@cert", :yay) + Puppet::Network::HttpPool.clear_http_instances + # Can't use the accessor, because it will read the cert in + Puppet::Network::HttpPool.instance_variable_get("@cert").should be_nil + end - # We mostly have to do this for testing, since in real life people - # won't change certs within a single process. - it "should remove its loaded key when clearing the cache" do - Puppet::Network::HttpPool.instance_variable_set("@key", :yay) - Puppet::Network::HttpPool.clear_http_instances - # Can't use the accessor, because it will read the cert in - Puppet::Network::HttpPool.instance_variable_get("@key").should be_nil - end + # We mostly have to do this for testing, since in real life people + # won't change certs within a single process. + it "should remove its loaded key when clearing the cache" do + Puppet::Network::HttpPool.instance_variable_set("@key", :yay) + Puppet::Network::HttpPool.clear_http_instances + # Can't use the accessor, because it will read the cert in + Puppet::Network::HttpPool.instance_variable_get("@key").should be_nil + end - after do - Puppet::Network::HttpPool.clear_http_instances + after do + Puppet::Network::HttpPool.clear_http_instances + end end end diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb index 3e29807ad..846b5471d 100644 --- a/spec/unit/network/server.rb +++ b/spec/unit/network/server.rb @@ -212,7 +212,7 @@ describe Puppet::Network::Server, "when listening is on" do @server.listen end - it "should indicate that listening is turned off" do + it "should indicate that it is listening" do @server.should be_listening end @@ -235,14 +235,14 @@ describe Puppet::Network::Server, "when listening is being turned on" do @mock_http_server.stubs(:listen) end - it "should fetch an instance of an HTTP server when listening is turned on" do + it "should fetch an instance of an HTTP server" do mock_http_server_class = mock('http server class') mock_http_server_class.expects(:new).returns(@mock_http_server) @server.expects(:http_server_class).returns(mock_http_server_class) @server.listen end - it "should cause the HTTP server to listen when listening is turned on" do + it "should cause the HTTP server to listen" do @mock_http_server.expects(:listen) @server.expects(:http_server).returns(@mock_http_server) @server.listen @@ -261,7 +261,7 @@ describe Puppet::Network::Server, "when listening is being turned off" do @server.listen end - it "should cause the HTTP server to stop listening when listening is turned off" do + it "should cause the HTTP server to stop listening" do @mock_http_server.expects(:unlisten) @server.unlisten end diff --git a/spec/unit/node.rb b/spec/unit/node.rb index 0ce702936..bb99378d9 100755 --- a/spec/unit/node.rb +++ b/spec/unit/node.rb @@ -113,6 +113,22 @@ describe Puppet::Node, " when merging facts" do @node.merge "two" => "three" @node.parameters["two"].should == "three" end + + it "should add the environment to the list of parameters" do + Puppet.settings.stubs(:value).with(:environments).returns("one,two") + Puppet.settings.stubs(:value).with(:environment).returns("one") + @node = Puppet::Node.new("testnode", :environment => "one") + @node.merge "two" => "three" + @node.parameters["environment"].should == "one" + end + + it "should not set the environment if it is already set in the parameters" do + Puppet.settings.stubs(:value).with(:environments).returns("one,two") + Puppet.settings.stubs(:value).with(:environment).returns("one") + @node = Puppet::Node.new("testnode", :environment => "one") + @node.merge "environment" => "two" + @node.parameters["environment"].should == "two" + end end describe Puppet::Node, " when indirecting" do @@ -127,6 +143,10 @@ describe Puppet::Node, " when indirecting" do Puppet::Node.indirection.terminus_class.should == :plain end + it "should not have a cache class defined" do + Puppet::Node.indirection.cache_class.should be_nil + end + after do Puppet::Indirector::Indirection.clear_cache end diff --git a/spec/unit/node/catalog.rb b/spec/unit/node/catalog.rb index 4f9613d00..434242103 100755 --- a/spec/unit/node/catalog.rb +++ b/spec/unit/node/catalog.rb @@ -57,11 +57,11 @@ describe Puppet::Node::Catalog, " when extracting transobjects" do def mkscope @parser = Puppet::Parser::Parser.new :Code => "" @node = Puppet::Node.new("mynode") - @compile = Puppet::Parser::Compile.new(@node, @parser) + @compiler = Puppet::Parser::Compiler.new(@node, @parser) # XXX This is ridiculous. - @compile.send(:evaluate_main) - @scope = @compile.topscope + @compiler.send(:evaluate_main) + @scope = @compiler.topscope end def mkresource(type, name) @@ -75,7 +75,7 @@ describe Puppet::Node::Catalog, " when extracting transobjects" do @source = mock 'source' main = mkresource("class", :main) - config.add_vertex!(main) + config.add_vertex(main) bucket = mock 'bucket' bucket.expects(:classes=).with(config.classes) @@ -95,7 +95,7 @@ describe Puppet::Node::Catalog, " when extracting transobjects" do defined = mkresource("class", :main) builtin = mkresource("file", "/yay") - config.add_edge!(defined, builtin) + config.add_edge(defined, builtin) bucket = [] bucket.expects(:classes=).with(config.classes) @@ -121,21 +121,21 @@ describe Puppet::Node::Catalog, " when extracting transobjects" do top.expects(:to_trans).returns(topbucket) topres = mkresource "file", "/top" topres.expects(:to_trans).returns(:topres) - config.add_edge! top, topres + config.add_edge top, topres middle = mkresource "class", "middle" middle.expects(:to_trans).returns([]) - config.add_edge! top, middle + config.add_edge top, middle midres = mkresource "file", "/mid" midres.expects(:to_trans).returns(:midres) - config.add_edge! middle, midres + config.add_edge middle, midres bottom = mkresource "class", "bottom" bottom.expects(:to_trans).returns([]) - config.add_edge! middle, bottom + config.add_edge middle, bottom botres = mkresource "file", "/bot" botres.expects(:to_trans).returns(:botres) - config.add_edge! bottom, botres + config.add_edge bottom, botres toparray = config.extract_to_transportable @@ -196,13 +196,13 @@ describe Puppet::Node::Catalog, " when converting to a transobject catalog" do @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] - @original.add_edge!(@top, @topobject) - @original.add_edge!(@top, @virtual) - @original.add_edge!(@virtual, @virtualobject) - @original.add_edge!(@top, @middle) - @original.add_edge!(@middle, @middleobject) - @original.add_edge!(@middle, @bottom) - @original.add_edge!(@bottom, @bottomobject) + @original.add_edge(@top, @topobject) + @original.add_edge(@top, @virtual) + @original.add_edge(@virtual, @virtualobject) + @original.add_edge(@top, @middle) + @original.add_edge(@middle, @middleobject) + @original.add_edge(@middle, @bottom) + @original.add_edge(@bottom, @bottomobject) @catalog = @original.to_transportable end @@ -261,11 +261,11 @@ describe Puppet::Node::Catalog, " when converting to a RAL catalog" do @original.add_resource(*@resources) - @original.add_edge!(@top, @topobject) - @original.add_edge!(@top, @middle) - @original.add_edge!(@middle, @middleobject) - @original.add_edge!(@middle, @bottom) - @original.add_edge!(@bottom, @bottomobject) + @original.add_edge(@top, @topobject) + @original.add_edge(@top, @middle) + @original.add_edge(@middle, @middleobject) + @original.add_edge(@middle, @bottom) + @original.add_edge(@bottom, @bottomobject) @catalog = @original.to_ral end @@ -300,10 +300,12 @@ describe Puppet::Node::Catalog, " when converting to a RAL catalog" do config.add_resource(changer) config.add_resource(@top) - config.add_edge!(@top, changer) + config.add_edge(@top, changer) resource = stub 'resource', :name => "changer2", :title => "changer2", :ref => "Test[changer2]", :catalog= => nil, :remove => nil + #changer is going to get duplicated as part of a fix for aliases 1094 + changer.expects(:dup).returns(changer) changer.expects(:to_type).returns(resource) newconfig = nil @@ -363,6 +365,12 @@ describe Puppet::Node::Catalog, " when functioning as a resource container" do it "should not allow two resources with the same resource reference" do @catalog.add_resource(@one) + + # These are used to build the failure + @dupe.stubs(:file) + @dupe.stubs(:line) + @one.stubs(:file) + @one.stubs(:line) proc { @catalog.add_resource(@dupe) }.should raise_error(ArgumentError) end @@ -469,9 +477,15 @@ describe Puppet::Node::Catalog, " when functioning as a resource container" do proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError) end - it "should fail to add an alias if the aliased name already exists as an alias" do - @catalog.alias(@one, "yayness") - proc { @catalog.alias @two, "yayness" }.should raise_error(ArgumentError) + it "should not fail when a resource has duplicate aliases created" do + @catalog.add_resource @one + proc { @catalog.alias @one, "one" }.should_not raise_error + end + + it "should be able to look resources up by their aliases" do + @catalog.add_resource @one + @catalog.alias @one, "two" + @catalog.resource(:me, "two").should equal(@one) end it "should remove resource aliases when the target resource is removed" do @@ -482,15 +496,32 @@ describe Puppet::Node::Catalog, " when functioning as a resource container" do @catalog.resource("me", "other").should be_nil end - it "should return aliased resources when asked for the resource by the alias" do - @catalog.add_resource @one - @catalog.alias(@one, "other") - @catalog.resource("Me[other]").should equal(@one) + it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do + resource = Puppet::Type.type(:file).create :path => "/something", :title => "other", :content => "blah" + resource.expects(:isomorphic?).returns(true) + @catalog.add_resource(resource) + @catalog.resource(:file, "other").should equal(resource) + @catalog.resource(:file, "/something").ref.should == resource.ref + end + + it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do + resource = Puppet::Type.type(:file).create :path => "/something", :title => "other", :content => "blah" + resource.expects(:isomorphic?).returns(false) + @catalog.add_resource(resource) + @catalog.resource(:file, resource.title).should equal(resource) + # We can't use .should here, because the resources respond to that method. + if @catalog.resource(:file, resource.name) + raise "Aliased non-isomorphic resource" + end + end + + after do + Puppet::Type.allclear end end -module ApplyingCatalogs - def setup +describe Puppet::Node::Catalog do + before :each do @catalog = Puppet::Node::Catalog.new("host") @catalog.retrieval_duration = Time.now @@ -500,130 +531,124 @@ module ApplyingCatalogs @transaction.stubs(:cleanup) @transaction.stubs(:addtimes) end -end -describe Puppet::Node::Catalog, " when applying" do - include ApplyingCatalogs + describe Puppet::Node::Catalog, " when applying" do - it "should create and evaluate a transaction" do - @transaction.expects(:evaluate) - @catalog.apply - end + it "should create and evaluate a transaction" do + @transaction.expects(:evaluate) + @catalog.apply + end - it "should provide the catalog time to the transaction" do - @transaction.expects(:addtimes).with do |arg| - arg[:config_retrieval].should be_instance_of(Time) - true + it "should provide the catalog time to the transaction" do + @transaction.expects(:addtimes).with do |arg| + arg[:config_retrieval].should be_instance_of(Time) + true + end + @catalog.apply end - @catalog.apply - end - it "should clean up the transaction" do - @transaction.expects :cleanup - @catalog.apply - end + it "should clean up the transaction" do + @transaction.expects :cleanup + @catalog.apply + end - it "should return the transaction" do - @catalog.apply.should equal(@transaction) - end + it "should return the transaction" do + @catalog.apply.should equal(@transaction) + end - it "should yield the transaction if a block is provided" do - @catalog.apply do |trans| - trans.should equal(@transaction) + it "should yield the transaction if a block is provided" do + @catalog.apply do |trans| + trans.should equal(@transaction) + end end - end - it "should default to not being a host catalog" do - @catalog.host_config.should be_nil - end + it "should default to not being a host catalog" do + @catalog.host_config.should be_nil + end - it "should pass supplied tags on to the transaction" do - @transaction.expects(:tags=).with(%w{one two}) - @catalog.apply(:tags => %w{one two}) - end + it "should pass supplied tags on to the transaction" do + @transaction.expects(:tags=).with(%w{one two}) + @catalog.apply(:tags => %w{one two}) + end - it "should set ignoreschedules on the transaction if specified in apply()" do - @transaction.expects(:ignoreschedules=).with(true) - @catalog.apply(:ignoreschedules => true) + it "should set ignoreschedules on the transaction if specified in apply()" do + @transaction.expects(:ignoreschedules=).with(true) + @catalog.apply(:ignoreschedules => true) + end end -end -describe Puppet::Node::Catalog, " when applying host catalogs" do - include ApplyingCatalogs + describe Puppet::Node::Catalog, " when applying host catalogs" do - # super() doesn't work in the setup method for some reason - before do - @catalog.host_config = true - end + # super() doesn't work in the setup method for some reason + before do + @catalog.host_config = true + end - it "should send a report if reporting is enabled" do - Puppet[:report] = true - @transaction.expects :send_report - @transaction.stubs :any_failed? => false - @catalog.apply - end + it "should send a report if reporting is enabled" do + Puppet[:report] = true + @transaction.expects :send_report + @transaction.stubs :any_failed? => false + @catalog.apply + end - it "should send a report if report summaries are enabled" do - Puppet[:summarize] = true - @transaction.expects :send_report - @transaction.stubs :any_failed? => false - @catalog.apply - end + it "should send a report if report summaries are enabled" do + Puppet[:summarize] = true + @transaction.expects :send_report + @transaction.stubs :any_failed? => false + @catalog.apply + end - it "should initialize the state database before applying a catalog" do - Puppet::Util::Storage.expects(:load) + it "should initialize the state database before applying a catalog" do + Puppet::Util::Storage.expects(:load) - # Short-circuit the apply, so we know we're loading before the transaction - Puppet::Transaction.expects(:new).raises ArgumentError - proc { @catalog.apply }.should raise_error(ArgumentError) - end + # Short-circuit the apply, so we know we're loading before the transaction + Puppet::Transaction.expects(:new).raises ArgumentError + proc { @catalog.apply }.should raise_error(ArgumentError) + end - it "should sync the state database after applying" do - Puppet::Util::Storage.expects(:store) - @transaction.stubs :any_failed? => false - @catalog.apply - end + it "should sync the state database after applying" do + Puppet::Util::Storage.expects(:store) + @transaction.stubs :any_failed? => false + @catalog.apply + end - after { Puppet.settings.clear } -end + after { Puppet.settings.clear } + end -describe Puppet::Node::Catalog, " when applying non-host catalogs" do - include ApplyingCatalogs + describe Puppet::Node::Catalog, " when applying non-host catalogs" do - before do - @catalog.host_config = false - end + before do + @catalog.host_config = false + end - it "should never send reports" do - Puppet[:report] = true - Puppet[:summarize] = true - @transaction.expects(:send_report).never - @catalog.apply - end + it "should never send reports" do + Puppet[:report] = true + Puppet[:summarize] = true + @transaction.expects(:send_report).never + @catalog.apply + end - it "should never modify the state database" do - Puppet::Util::Storage.expects(:load).never - Puppet::Util::Storage.expects(:store).never - @catalog.apply - end + it "should never modify the state database" do + Puppet::Util::Storage.expects(:load).never + Puppet::Util::Storage.expects(:store).never + @catalog.apply + end - after { Puppet.settings.clear } + after { Puppet.settings.clear } + end end describe Puppet::Node::Catalog, " when creating a relationship graph" do before do + Puppet::Type.type(:component) @catalog = Puppet::Node::Catalog.new("host") @compone = Puppet::Type::Component.create :name => "one" @comptwo = Puppet::Type::Component.create :name => "two", :require => ["class", "one"] @file = Puppet::Type.type(:file) @one = @file.create :path => "/one" @two = @file.create :path => "/two" - @sub = @file.create :path => "/two/three" - - @sub.stubs(:autorequire).returns([Puppet::Relationship.new(@two, @sub)]) - - @catalog.add_edge! @compone, @one - @catalog.add_edge! @comptwo, @two + @catalog.add_edge @compone, @one + @catalog.add_edge @comptwo, @two @three = @file.create :path => "/three" @four = @file.create :path => "/four", :require => ["file", "/three"] @@ -796,7 +821,7 @@ end describe Puppet::Node::Catalog, " when converting to yaml" do before do @catalog = Puppet::Node::Catalog.new("me") - @catalog.add_edge!("one", "two") + @catalog.add_edge("one", "two") end it "should be able to be dumped to yaml" do @@ -807,7 +832,7 @@ end describe Puppet::Node::Catalog, " when converting from yaml" do before do @catalog = Puppet::Node::Catalog.new("me") - @catalog.add_edge!("one", "two") + @catalog.add_edge("one", "two") text = YAML.dump(@catalog) @newcatalog = YAML.load(text) diff --git a/spec/unit/other/pgraph.rb b/spec/unit/other/pgraph.rb index 252a807ec..10ab934a6 100755 --- a/spec/unit/other/pgraph.rb +++ b/spec/unit/other/pgraph.rb @@ -5,6 +5,7 @@ require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/pgraph' require 'puppet/util/graph' class Container @@ -35,8 +36,8 @@ describe Puppet::PGraph do end it "should correctly clear vertices and edges when asked" do - @graph.add_edge!("a", "b") - @graph.add_vertex! "c" + @graph.add_edge("a", "b") + @graph.add_vertex "c" @graph.clear @graph.vertices.should be_empty @graph.edges.should be_empty @@ -52,7 +53,7 @@ describe Puppet::PGraph, " when matching edges" do @edges = {} @edges["a/b"] = Puppet::Relationship.new("a", "b", {:event => :yay, :callback => :refresh}) @edges["a/c"] = Puppet::Relationship.new("a", "c", {:event => :yay, :callback => :refresh}) - @graph.add_edge!(@edges["a/b"]) + @graph.add_edge(@edges["a/b"]) end it "should match edges whose source matches the source of the event" do @@ -64,7 +65,7 @@ describe Puppet::PGraph, " when matching edges" do end it "should match multiple edges" do - @graph.add_edge!(@edges["a/c"]) + @graph.add_edge(@edges["a/c"]) edges = @graph.matching_edges([@event]) edges.should be_include(@edges["a/b"]) edges.should be_include(@edges["a/c"]) @@ -75,9 +76,9 @@ describe Puppet::PGraph, " when determining dependencies" do before do @graph = Puppet::PGraph.new - @graph.add_edge!("a", "b") - @graph.add_edge!("a", "c") - @graph.add_edge!("b", "d") + @graph.add_edge("a", "b") + @graph.add_edge("a", "c") + @graph.add_edge("b", "d") end it "should find all dependents when they are on multiple levels" do @@ -118,19 +119,19 @@ describe Puppet::PGraph, " when splicing the relationship graph" do # We have to add the container to the main graph, else it won't # be spliced in the dependency graph. - @contgraph.add_vertex!(@empty) + @contgraph.add_vertex(@empty) end def dependency_graph @depgraph = Puppet::PGraph.new @contgraph.vertices.each do |v| - @depgraph.add_vertex!(v) + @depgraph.add_vertex(v) end # We have to specify a relationship to our empty container, else it # never makes it into the dep graph in the first place. {@one => @two, "f" => "c", "h" => @middle, "c" => @empty}.each do |source, target| - @depgraph.add_edge!(source, target, :callback => :refresh) + @depgraph.add_edge(source, target, :callback => :refresh) end end @@ -176,13 +177,13 @@ describe Puppet::PGraph, " when splicing the relationship graph" do end it "should not add labels to edges that have none" do - @depgraph.add_edge!(@two, @three) + @depgraph.add_edge(@two, @three) splice @depgraph.edge_label("c", "i").should == {} end it "should copy labels over edges that have none" do - @depgraph.add_edge!("c", @three, {:callback => :refresh}) + @depgraph.add_edge("c", @three, {:callback => :refresh}) splice # And make sure the label got copied. @depgraph.edge_label("c", "i").should == {:callback => :refresh} @@ -190,18 +191,18 @@ describe Puppet::PGraph, " when splicing the relationship graph" do it "should not replace a label with a nil label" do # Lastly, add some new label-less edges and make sure the label stays. - @depgraph.add_edge!(@middle, @three) - @depgraph.add_edge!("c", @three, {:callback => :refresh}) + @depgraph.add_edge(@middle, @three) + @depgraph.add_edge("c", @three, {:callback => :refresh}) splice @depgraph.edge_label("c", "i").should == {:callback => :refresh} end it "should copy labels to all created edges" do - @depgraph.add_edge!(@middle, @three) - @depgraph.add_edge!("c", @three, {:callback => :refresh}) + @depgraph.add_edge(@middle, @three) + @depgraph.add_edge("c", @three, {:callback => :refresh}) splice @three.each do |child| - edge = @depgraph.edge_class.new("c", child) + edge = Puppet::Relationship.new("c", child) @depgraph.should be_edge(edge.source, edge.target) @depgraph.edge_label(edge.source, edge.target).should == {:callback => :refresh} end diff --git a/spec/unit/other/transaction.rb b/spec/unit/other/transaction.rb index d88f03005..0db470d26 100755 --- a/spec/unit/other/transaction.rb +++ b/spec/unit/other/transaction.rb @@ -2,6 +2,8 @@ require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/transaction' + describe Puppet::Transaction, " when determining tags" do before do @config = Puppet::Node::Catalog.new @@ -23,4 +25,9 @@ describe Puppet::Transaction, " when determining tags" do @transaction.tags = %w{one two} @transaction.tags.should == %w{one two} end + + it "should always convert assigned tags to an array" do + @transaction.tags = "one::two" + @transaction.tags.should == %w{one::two} + end end diff --git a/spec/unit/other/transbucket.rb b/spec/unit/other/transbucket.rb index 89f48ad41..4494f2abb 100755 --- a/spec/unit/other/transbucket.rb +++ b/spec/unit/other/transbucket.rb @@ -87,35 +87,49 @@ describe Puppet::TransBucket, " when generating a catalog" do @top.push(@topobj) @top.push(@middle) - @config = @top.to_catalog - @users = %w{top middle bottom} @fakes = %w{Fake[bottom] Fake[middle] Fake[top]} end + after do + Puppet::Type.allclear + end + it "should convert all transportable objects to RAL resources" do + @catalog = @top.to_catalog @users.each do |name| - @config.vertices.find { |r| r.class.name == :user and r.title == name }.should be_instance_of(Puppet::Type.type(:user)) + @catalog.vertices.find { |r| r.class.name == :user and r.title == name }.should be_instance_of(Puppet::Type.type(:user)) end end + it "should fail if any transportable resources fail to convert to RAL resources" do + @bottomobj.expects(:to_type).raises ArgumentError + lambda { @bottom.to_catalog }.should raise_error(ArgumentError) + end + it "should convert all transportable buckets to RAL components" do + @catalog = @top.to_catalog @fakes.each do |name| - @config.vertices.find { |r| r.class.name == :component and r.title == name }.should be_instance_of(Puppet::Type.type(:component)) + @catalog.vertices.find { |r| r.class.name == :component and r.title == name }.should be_instance_of(Puppet::Type.type(:component)) end end it "should add all resources to the graph's resource table" do - @config.resource("fake[top]").should equal(@top) + @catalog = @top.to_catalog + @catalog.resource("fake[top]").should equal(@top) end it "should finalize all resources" do - @config.vertices.each do |vertex| vertex.should be_finalized end + @catalog = @top.to_catalog + @catalog.vertices.each do |vertex| vertex.should be_finalized end end it "should only call to_type on each resource once" do - @topobj.expects(:to_type) - @bottomobj.expects(:to_type) + # We just raise exceptions here because we're not interested in + # what happens with the result, only that the method only + # gets called once. + resource = @topobj.to_type + @topobj.expects(:to_type).once.returns resource @top.to_catalog end diff --git a/spec/unit/parser/ast/definition.rb b/spec/unit/parser/ast/definition.rb index a27fb4721..a58e4d00e 100755 --- a/spec/unit/parser/ast/definition.rb +++ b/spec/unit/parser/ast/definition.rb @@ -12,8 +12,8 @@ describe Puppet::Parser::AST::Definition, "when evaluating" do @source = @parser.newclass "" @definition = @parser.newdefine "mydefine" @node = Puppet::Node.new("yaynode") - @compile = Puppet::Parser::Compile.new(@node, @parser) - @scope = @compile.topscope + @compiler = Puppet::Parser::Compiler.new(@node, @parser) + @scope = @compiler.topscope @resource = Puppet::Parser::Resource.new(:type => "mydefine", :title => "myresource", :scope => @scope, :source => @source) end @@ -21,12 +21,12 @@ describe Puppet::Parser::AST::Definition, "when evaluating" do it "should create a new scope" do scope = nil code = mock 'code' - code.expects(:safeevaluate).with do |options| - options[:scope].object_id.should_not == @scope.object_id + code.expects(:safeevaluate).with do |scope| + scope.object_id.should_not == @scope.object_id true end @definition.stubs(:code).returns(code) - @definition.evaluate(:scope => @scope, :resource => @resource) + @definition.evaluate_code(@resource) end # it "should copy its namespace to the scope" @@ -44,4 +44,152 @@ describe Puppet::Parser::AST::Definition, "when evaluating" do # it "should not copy the resource's title as the name if 'name' is one of the resource parameters" # # it "should evaluate the associated code with the new scope" + + def old_test_initialize + parser = mkparser + + # Create a new definition + klass = parser.newdefine "yayness", + :arguments => [["owner", stringobj("nobody")], %w{mode}], + :code => AST::ASTArray.new( + :children => [resourcedef("file", "/tmp/$name", + "owner" => varref("owner"), "mode" => varref("mode"))] + ) + + # Test validattr? a couple different ways + [:owner, "owner", :schedule, "schedule"].each do |var| + assert(klass.validattr?(var), "%s was not considered valid" % var.inspect) + end + + [:random, "random"].each do |var| + assert(! klass.validattr?(var), "%s was considered valid" % var.inspect) + end + + end + + def oldtest_evaluate + parser = mkparser + config = mkcompiler + config.send(:evaluate_main) + scope = config.topscope + klass = parser.newdefine "yayness", + :arguments => [["owner", stringobj("nobody")], %w{mode}], + :code => AST::ASTArray.new( + :children => [resourcedef("file", "/tmp/$name", + "owner" => varref("owner"), "mode" => varref("mode"))] + ) + + resource = Puppet::Parser::Resource.new( + :title => "first", + :type => "yayness", + :exported => false, + :virtual => false, + :scope => scope, + :source => scope.source + ) + resource.send(:set_parameter, "name", "first") + resource.send(:set_parameter, "mode", "755") + + resource.stubs(:title) + assert_nothing_raised do + klass.evaluate_code(resource) + end + + firstobj = config.findresource("File[/tmp/first]") + assert(firstobj, "Did not create /tmp/first obj") + + assert_equal("File", firstobj.type) + assert_equal("/tmp/first", firstobj.title) + assert_equal("nobody", firstobj[:owner]) + assert_equal("755", firstobj[:mode]) + + # Make sure we can't evaluate it with the same args + assert_raise(Puppet::ParseError) do + klass.evaluate_code(resource) + end + + # Now create another with different args + resource2 = Puppet::Parser::Resource.new( + :title => "second", + :type => "yayness", + :exported => false, + :virtual => false, + :scope => scope, + :source => scope.source + ) + resource2.send(:set_parameter, "name", "second") + resource2.send(:set_parameter, "mode", "755") + resource2.send(:set_parameter, "owner", "daemon") + + assert_nothing_raised do + klass.evaluate_code(resource2) + end + + secondobj = config.findresource("File[/tmp/second]") + assert(secondobj, "Did not create /tmp/second obj") + + assert_equal("File", secondobj.type) + assert_equal("/tmp/second", secondobj.title) + assert_equal("daemon", secondobj[:owner]) + assert_equal("755", secondobj[:mode]) + end + + # #539 - definitions should support both names and titles + def oldtest_names_and_titles + parser = mkparser + scope = mkscope :parser => parser + + [ + {:name => "one", :title => "two"}, + {:title => "mytitle"} + ].each_with_index do |hash, i| + # Create a definition that uses both name and title. Put this + # inside the loop so the subscope expectations work. + klass = parser.newdefine "yayness%s" % i + + resource = Puppet::Parser::Resource.new( + :title => hash[:title], + :type => "yayness%s" % i, + :exported => false, + :virtual => false, + :scope => scope, + :source => scope.source + ) + + subscope = klass.subscope(scope, resource) + + klass.expects(:subscope).returns(subscope) + + if hash[:name] + resource.stubs(:to_hash).returns({:name => hash[:name]}) + end + + assert_nothing_raised("Could not evaluate definition with %s" % hash.inspect) do + klass.evaluate_code(resource) + end + + name = hash[:name] || hash[:title] + title = hash[:title] + + assert_equal(name, subscope.lookupvar("name"), + "Name did not get set correctly") + assert_equal(title, subscope.lookupvar("title"), + "title did not get set correctly") + + [:name, :title].each do |param| + val = resource.send(param) + assert(subscope.tags.include?(val), + "Scope was not tagged with %s '%s'" % [param, val]) + end + end + end + + # Testing the root cause of #615. We should be using the fqname for the type, instead + # of just the short name. + def oldtest_fully_qualified_types + parser = mkparser + klass = parser.newclass("one::two") + + assert_equal("one::two", klass.classname, "Class did not get fully qualified class name") + end end diff --git a/spec/unit/parser/ast/hostclass.rb b/spec/unit/parser/ast/hostclass.rb new file mode 100755 index 000000000..0abc174d9 --- /dev/null +++ b/spec/unit/parser/ast/hostclass.rb @@ -0,0 +1,135 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::HostClass do + before :each do + @node = Puppet::Node.new "testnode" + @parser = Puppet::Parser::Parser.new :environment => "development" + @scope_resource = stub 'scope_resource', :builtin? => true + @compiler = Puppet::Parser::Compiler.new(@node, @parser) + + @scope = @compiler.topscope + end + + describe Puppet::Parser::AST::HostClass, "when evaluating" do + + before do + @top = @parser.newclass "top" + @middle = @parser.newclass "middle", :parent => "top" + end + + it "should create a resource that references itself" do + @top.evaluate(@scope) + + @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) + end + + it "should evaluate the parent class if one exists" do + @middle.evaluate(@scope) + + @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) + end + + it "should fail to evaluate if a parent class is defined but cannot be found" do + othertop = @parser.newclass "something", :parent => "yay" + lambda { othertop.evaluate(@scope) }.should raise_error(Puppet::ParseError) + end + + it "should not create a new resource if one already exists" do + @compiler.catalog.expects(:resource).with(:class, "top").returns("something") + @compiler.catalog.expects(:add_resource).never + @top.evaluate(@scope) + end + + it "should return the existing resource when not creating a new one" do + @compiler.catalog.expects(:resource).with(:class, "top").returns("something") + @compiler.catalog.expects(:add_resource).never + @top.evaluate(@scope).should == "something" + end + + it "should not create a new parent resource if one already exists and it has a parent class" do + @top.evaluate(@scope) + + top_resource = @compiler.catalog.resource(:class, "top") + + @middle.evaluate(@scope) + + @compiler.catalog.resource(:class, "top").should equal(top_resource) + end + + # #795 - tag before evaluation. + it "should tag the catalog with the resource tags when it is evaluated" do + @middle.evaluate(@scope) + + @compiler.catalog.should be_tagged("middle") + end + + it "should tag the catalog with the parent class tags when it is evaluated" do + @middle.evaluate(@scope) + + @compiler.catalog.should be_tagged("top") + end + end + + describe Puppet::Parser::AST::HostClass, "when evaluating code" do + + before do + @top_resource = stub "top_resource" + @top = @parser.newclass "top", :code => @top_resource + + @middle_resource = stub "middle_resource" + @middle = @parser.newclass "top::middle", :parent => "top", :code => @middle_resource + end + + it "should set its namespace to its fully qualified name" do + @middle.namespace.should == "top::middle" + end + + it "should evaluate the code referred to by the class" do + @top_resource.expects(:safeevaluate) + + resource = @top.evaluate(@scope) + + @top.evaluate_code(resource) + end + + it "should evaluate the parent class's code if it has a parent" do + @top_resource.expects(:safeevaluate) + @middle_resource.expects(:safeevaluate) + + resource = @middle.evaluate(@scope) + + @middle.evaluate_code(resource) + end + + it "should not evaluate the parent class's code if the parent has already been evaluated" do + @top_resource.stubs(:safeevaluate) + resource = @top.evaluate(@scope) + @top.evaluate_code(resource) + + @top_resource.expects(:safeevaluate).never + @middle_resource.stubs(:safeevaluate) + resource = @middle.evaluate(@scope) + @middle.evaluate_code(resource) + end + + it "should use the parent class's scope as its parent scope" do + @top_resource.stubs(:safeevaluate) + @middle_resource.stubs(:safeevaluate) + resource = @middle.evaluate(@scope) + @middle.evaluate_code(resource) + + @compiler.class_scope(@middle).parent.should equal(@compiler.class_scope(@top)) + end + + it "should add the parent class's namespace to its namespace search path" do + @top_resource.stubs(:safeevaluate) + @middle_resource.stubs(:safeevaluate) + resource = @middle.evaluate(@scope) + @middle.evaluate_code(resource) + + @compiler.class_scope(@middle).namespaces.should be_include(@top.namespace) + end + end +end diff --git a/spec/unit/parser/ast/node.rb b/spec/unit/parser/ast/node.rb new file mode 100755 index 000000000..757934415 --- /dev/null +++ b/spec/unit/parser/ast/node.rb @@ -0,0 +1,125 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Node do + before :each do + @node = Puppet::Node.new "testnode" + @parser = Puppet::Parser::Parser.new :environment => "development" + @scope_resource = stub 'scope_resource', :builtin? => true + @compiler = Puppet::Parser::Compiler.new(@node, @parser) + + @scope = @compiler.topscope + end + + describe Puppet::Parser::AST::Node, "when evaluating" do + + before do + @top = @parser.newnode("top").shift + @middle = @parser.newnode("middle", :parent => "top").shift + end + + it "should create a resource that references itself" do + @top.evaluate(@scope) + + @compiler.catalog.resource(:node, "top").should be_an_instance_of(Puppet::Parser::Resource) + end + + it "should evaluate the parent class if one exists" do + @middle.evaluate(@scope) + + @compiler.catalog.resource(:node, "top").should be_an_instance_of(Puppet::Parser::Resource) + end + + it "should fail to evaluate if a parent class is defined but cannot be found" do + othertop = @parser.newnode("something", :parent => "yay").shift + lambda { othertop.evaluate(@scope) }.should raise_error(Puppet::ParseError) + end + + it "should not create a new resource if one already exists" do + @compiler.catalog.expects(:resource).with(:node, "top").returns("something") + @compiler.catalog.expects(:add_resource).never + @top.evaluate(@scope) + end + + it "should not create a new parent resource if one already exists and it has a parent class" do + @top.evaluate(@scope) + + top_resource = @compiler.catalog.resource(:node, "top") + + @middle.evaluate(@scope) + + @compiler.catalog.resource(:node, "top").should equal(top_resource) + end + + # #795 - tag before evaluation. + it "should tag the catalog with the resource tags when it is evaluated" do + @middle.evaluate(@scope) + + @compiler.catalog.should be_tagged("middle") + end + + it "should tag the catalog with the parent class tags when it is evaluated" do + @middle.evaluate(@scope) + + @compiler.catalog.should be_tagged("top") + end + end + + describe Puppet::Parser::AST::Node, "when evaluating code" do + + before do + @top_resource = stub "top_resource" + @top = @parser.newnode("top", :code => @top_resource).shift + + @middle_resource = stub "middle_resource" + @middle = @parser.newnode("middle", :parent => "top", :code => @middle_resource).shift + end + + it "should evaluate the code referred to by the class" do + @top_resource.expects(:safeevaluate) + + resource = @top.evaluate(@scope) + + @top.evaluate_code(resource) + end + + it "should evaluate the parent class's code if it has a parent" do + @top_resource.expects(:safeevaluate) + @middle_resource.expects(:safeevaluate) + + resource = @middle.evaluate(@scope) + + @middle.evaluate_code(resource) + end + + it "should not evaluate the parent class's code if the parent has already been evaluated" do + @top_resource.stubs(:safeevaluate) + resource = @top.evaluate(@scope) + @top.evaluate_code(resource) + + @top_resource.expects(:safeevaluate).never + @middle_resource.stubs(:safeevaluate) + resource = @middle.evaluate(@scope) + @middle.evaluate_code(resource) + end + + it "should use the parent class's scope as its parent scope" do + @top_resource.stubs(:safeevaluate) + @middle_resource.stubs(:safeevaluate) + resource = @middle.evaluate(@scope) + @middle.evaluate_code(resource) + + @compiler.class_scope(@middle).parent.should equal(@compiler.class_scope(@top)) + end + + it "should add the parent class's namespace to its namespace search path" do + @top_resource.stubs(:safeevaluate) + @middle_resource.stubs(:safeevaluate) + resource = @middle.evaluate(@scope) + @middle.evaluate_code(resource) + + @compiler.class_scope(@middle).namespaces.should be_include(@top.namespace) + end + end +end
\ No newline at end of file diff --git a/spec/unit/parser/collector.rb b/spec/unit/parser/collector.rb index 9b5eab1f4..e1ceb23ed 100755 --- a/spec/unit/parser/collector.rb +++ b/spec/unit/parser/collector.rb @@ -79,8 +79,8 @@ describe Puppet::Parser::Collector, "when collecting specific virtual resources" @collector.resources = ["File[virtual1]"] one = mock 'one' one.stubs(:virtual=) - @compile.expects(:delete_collection).with(@collector) - @scope.expects(:compile).returns(@compile) + @compiler.expects(:delete_collection).with(@collector) + @scope.expects(:compiler).returns(@compiler) @scope.stubs(:findresource).with("File[virtual1]").returns(one) @collector.evaluate end @@ -89,7 +89,7 @@ describe Puppet::Parser::Collector, "when collecting specific virtual resources" @collector.resources = ["File[virtual1]"] one = mock 'one' one.stubs(:virtual=) - @compile.expects(:delete_collection).never + @compiler.expects(:delete_collection).never @scope.stubs(:findresource).with("File[virtual1]").returns(nil) @collector.evaluate end @@ -98,8 +98,8 @@ end describe Puppet::Parser::Collector, "when collecting virtual resources" do before do @scope = mock 'scope' - @compile = mock 'compile' - @scope.stubs(:compile).returns(@compile) + @compiler = mock 'compile' + @scope.stubs(:compiler).returns(@compiler) @resource_type = "Mytype" @vquery = proc { |res| true } @@ -113,7 +113,7 @@ describe Puppet::Parser::Collector, "when collecting virtual resources" do one.stubs(:virtual=) two.stubs(:virtual=) - @compile.expects(:resources).returns([one, two]) + @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one, two] end @@ -123,7 +123,7 @@ describe Puppet::Parser::Collector, "when collecting virtual resources" do one.expects(:virtual=).with(false) - @compile.expects(:resources).returns([one]) + @compiler.expects(:resources).returns([one]) @collector.evaluate end @@ -135,7 +135,7 @@ describe Puppet::Parser::Collector, "when collecting virtual resources" do one.stubs(:virtual=) two.stubs(:virtual=) - @compile.expects(:resources).returns([one, two]) + @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one, two] end @@ -147,7 +147,7 @@ describe Puppet::Parser::Collector, "when collecting virtual resources" do one.expects(:virtual=).with(false) two.expects(:virtual=).with(false) - @compile.expects(:resources).returns([one, two]) + @compiler.expects(:resources).returns([one, two]) @collector = Puppet::Parser::Collector.new(@scope, @resource_type, nil, nil, :virtual) @@ -161,7 +161,7 @@ describe Puppet::Parser::Collector, "when collecting virtual resources" do one.expects(:virtual=).with(false) two.expects(:virtual=).never - @compile.expects(:resources).returns([one, two]) + @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one] end @@ -173,7 +173,7 @@ describe Puppet::Parser::Collector, "when collecting virtual resources" do one.expects(:virtual=).never two.expects(:virtual=).never - @compile.expects(:resources).returns([one, two]) + @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should be_false end @@ -187,7 +187,7 @@ describe Puppet::Parser::Collector, "when collecting virtual resources" do one.expects(:virtual=).with(false) two.expects(:virtual=).never - @compile.expects(:resources).returns([one, two]) + @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one] end @@ -198,8 +198,8 @@ describe Puppet::Parser::Collector, "when collecting exported resources" do before do @scope = stub 'scope', :host => "myhost", :debug => nil - @compile = mock 'compile' - @scope.stubs(:compile).returns(@compile) + @compiler = mock 'compile' + @scope.stubs(:compiler).returns(@compiler) @resource_type = "Mytype" @equery = "test = true" @vquery = proc { |r| true } @@ -218,7 +218,7 @@ describe Puppet::Parser::Collector, "when collecting exported resources" do end it "should use initialize the Rails support if ActiveRecord is not connected" do - @compile.stubs(:resources).returns([]) + @compiler.stubs(:resources).returns([]) ActiveRecord::Base.expects(:connected?).returns(false) Puppet::Rails.expects(:init) Puppet::Rails::Host.stubs(:find_by_name).returns(nil) @@ -238,7 +238,7 @@ describe Puppet::Parser::Collector, "when collecting exported resources" do two.stubs(:exported=) two.stubs(:virtual=) - @compile.expects(:resources).returns([one, two]) + @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one, two] end @@ -251,7 +251,7 @@ describe Puppet::Parser::Collector, "when collecting exported resources" do one.stubs(:exported=) one.expects(:virtual=).with(false) - @compile.expects(:resources).returns([one]) + @compiler.expects(:resources).returns([one]) @collector.evaluate.should == [one] end @@ -268,10 +268,10 @@ describe Puppet::Parser::Collector, "when collecting exported resources" do resource.stubs(:exported=) resource.stubs(:virtual=) - @compile.stubs(:resources).returns([]) + @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(nil) - @compile.stubs(:store_resource) + @compiler.stubs(:add_resource) @collector.evaluate.should == [resource] end @@ -288,10 +288,10 @@ describe Puppet::Parser::Collector, "when collecting exported resources" do resource.stubs(:exported=) resource.stubs(:virtual=) - @compile.stubs(:resources).returns([]) + @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(nil) - @compile.expects(:store_resource).with(@scope, resource) + @compiler.expects(:add_resource).with(@scope, resource) @collector.evaluate.should == [resource] end @@ -309,10 +309,10 @@ describe Puppet::Parser::Collector, "when collecting exported resources" do resource.expects(:exported=).with(false) resource.stubs(:virtual=) - @compile.stubs(:resources).returns([]) + @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(nil) - @compile.stubs(:store_resource) + @compiler.stubs(:add_resource) @collector.evaluate end @@ -328,10 +328,10 @@ describe Puppet::Parser::Collector, "when collecting exported resources" do resource = mock 'resource' - @compile.stubs(:resources).returns([]) + @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(inmemory) - @compile.stubs(:store_resource) + @compiler.stubs(:add_resource) proc { @collector.evaluate }.should raise_error(Puppet::ParseError) end @@ -347,10 +347,10 @@ describe Puppet::Parser::Collector, "when collecting exported resources" do resource = mock 'resource' - @compile.stubs(:resources).returns([]) + @compiler.stubs(:resources).returns([]) @scope.stubs(:findresource).returns(inmemory) - @compile.stubs(:store_resource) + @compiler.stubs(:add_resource) proc { @collector.evaluate }.should_not raise_error(Puppet::ParseError) end @@ -361,14 +361,14 @@ describe Puppet::Parser::Collector, "when building its ActiveRecord query for co before do @scope = stub 'scope', :host => "myhost", :debug => nil - @compile = mock 'compile' - @scope.stubs(:compile).returns(@compile) + @compiler = mock 'compile' + @scope.stubs(:compiler).returns(@compiler) @resource_type = "Mytype" @equery = nil @vquery = proc { |r| true } @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, :exported) - @compile.stubs(:resources).returns([]) + @compiler.stubs(:resources).returns([]) ActiveRecord::Base.stubs(:connected?).returns(false) diff --git a/spec/unit/parser/compile.rb b/spec/unit/parser/compile.rb deleted file mode 100755 index 092bece0c..000000000 --- a/spec/unit/parser/compile.rb +++ /dev/null @@ -1,281 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Parser::Compile, " when compiling" do - before do - @node = stub 'node', :name => 'mynode' - @parser = stub 'parser', :version => "1.0" - @compile = Puppet::Parser::Compile.new(@node, @parser) - end - - def compile_methods - [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, - :finish, :store, :extract] - end - - # Stub all of the main compile methods except the ones we're specifically interested in. - def compile_stub(*except) - (compile_methods - except).each { |m| @compile.stubs(m) } - end - - it "should set node parameters as variables in the top scope" do - params = {"a" => "b", "c" => "d"} - @node.stubs(:parameters).returns(params) - compile_stub(:set_node_parameters) - @compile.compile - @compile.topscope.lookupvar("a").should == "b" - @compile.topscope.lookupvar("c").should == "d" - end - - it "should evaluate any existing classes named in the node" do - classes = %w{one two three four} - main = stub 'main' - one = stub 'one', :classname => "one" - three = stub 'three', :classname => "three" - @node.stubs(:name).returns("whatever") - @node.stubs(:classes).returns(classes) - - @compile.expects(:evaluate_classes).with(classes, @compile.topscope) - @compile.send :evaluate_node_classes - end - - it "should enable ast_nodes if the parser has any nodes" do - @parser.expects(:nodes).returns(:one => :yay) - @compile.ast_nodes?.should be_true - end - - it "should disable ast_nodes if the parser has no nodes" do - @parser.expects(:nodes).returns({}) - @compile.ast_nodes?.should be_false - end -end - -describe Puppet::Parser::Compile, " when evaluating classes" do - before do - @node = stub 'node', :name => 'mynode' - @parser = stub 'parser', :version => "1.0" - @scope = stub 'scope', :source => mock("source") - @compile = Puppet::Parser::Compile.new(@node, @parser) - end - - it "should fail if there's no source listed for the scope" do - scope = stub 'scope', :source => nil - proc { @compile.evaluate_classes(%w{one two}, scope) }.should raise_error(Puppet::DevError) - end - - it "should tag the catalog with the name of each not-found class" do - @compile.catalog.expects(:tag).with("notfound") - @scope.expects(:findclass).with("notfound").returns(nil) - @compile.evaluate_classes(%w{notfound}, @scope) - end -end - -describe Puppet::Parser::Compile, " when evaluating collections" do - before do - @node = stub 'node', :name => 'mynode' - @parser = stub 'parser', :version => "1.0" - @scope = stub 'scope', :source => mock("source") - @compile = Puppet::Parser::Compile.new(@node, @parser) - end - - it "should evaluate each collection" do - 2.times { |i| - coll = mock 'coll%s' % i - @compile.add_collection(coll) - - # This is the hard part -- we have to emulate the fact that - # collections delete themselves if they are done evaluating. - coll.expects(:evaluate).with do - @compile.delete_collection(coll) - end - } - - @compile.class.publicize_methods(:evaluate_collections) { @compile.evaluate_collections } - end -end - - -describe Puppet::Parser::Compile, " when evaluating found classes" do - before do - @node = stub 'node', :name => 'mynode' - @parser = stub 'parser', :version => "1.0" - @scope = stub 'scope', :source => mock("source") - @compile = Puppet::Parser::Compile.new(@node, @parser) - - @class = stub 'class', :classname => "my::class" - @scope.stubs(:findclass).with("myclass").returns(@class) - - @resource = stub 'resource', :ref => 'Class[myclass]' - end - - it "should create a resource for each found class" do - @compile.catalog.stubs(:tag) - - @compile.stubs :store_resource - - Puppet::Parser::Resource.expects(:new).with(:scope => @scope, :source => @scope.source, :title => "my::class", :type => "class").returns(@resource) - @compile.evaluate_classes(%w{myclass}, @scope) - end - - it "should store each created resource in the compile" do - @compile.catalog.stubs(:tag) - - @compile.expects(:store_resource).with(@scope, @resource) - - Puppet::Parser::Resource.stubs(:new).returns(@resource) - @compile.evaluate_classes(%w{myclass}, @scope) - end - - it "should tag the catalog with the fully-qualified name of each found class" do - @compile.catalog.expects(:tag).with("my::class") - - @compile.stubs(:store_resource) - - Puppet::Parser::Resource.stubs(:new).returns(@resource) - @compile.evaluate_classes(%w{myclass}, @scope) - end - - it "should not evaluate the resources created for found classes unless asked" do - @compile.catalog.stubs(:tag) - - @compile.stubs(:store_resource) - @resource.expects(:evaluate).never - - Puppet::Parser::Resource.stubs(:new).returns(@resource) - @compile.evaluate_classes(%w{myclass}, @scope) - end - - it "should immediately evaluate the resources created for found classes when asked" do - @compile.catalog.stubs(:tag) - - @compile.stubs(:store_resource) - @resource.expects(:evaluate) - - Puppet::Parser::Resource.stubs(:new).returns(@resource) - @compile.evaluate_classes(%w{myclass}, @scope, false) - end - - it "should skip classes that have already been evaluated" do - @compile.catalog.stubs(:tag) - - @compile.expects(:class_scope).with(@class).returns("something") - - @compile.expects(:store_resource).never - - @resource.expects(:evaluate).never - - Puppet::Parser::Resource.expects(:new).never - @compile.evaluate_classes(%w{myclass}, @scope, false) - end - - it "should return the list of found classes" do - @compile.catalog.stubs(:tag) - - @compile.stubs(:store_resource) - @scope.stubs(:findclass).with("notfound").returns(nil) - - Puppet::Parser::Resource.stubs(:new).returns(@resource) - @compile.evaluate_classes(%w{myclass notfound}, @scope).should == %w{myclass} - end -end - -describe Puppet::Parser::Compile, " when evaluating AST nodes with no AST nodes present" do - before do - @node = stub 'node', :name => "foo" - @parser = stub 'parser', :version => "1.0", :nodes => {} - @compile = Puppet::Parser::Compile.new(@node, @parser) - end - - it "should do nothing" do - @compile.expects(:ast_nodes?).returns(false) - @compile.parser.expects(:nodes).never - Puppet::Parser::Resource.expects(:new).never - - @compile.send(:evaluate_ast_node) - end -end - -describe Puppet::Parser::Compile, " when evaluating AST nodes with AST nodes present" do - before do - @node = stub 'node', :name => "foo" - @parser = stub 'parser', :version => "1.0", :nodes => {} - @compile = Puppet::Parser::Compile.new(@node, @parser) - - @nodes = mock 'node_hash' - @compile.stubs(:ast_nodes?).returns(true) - @compile.parser.stubs(:nodes).returns(@nodes) - - # Set some names for our test - @node.stubs(:names).returns(%w{a b c}) - @nodes.stubs(:[]).with("a").returns(nil) - @nodes.stubs(:[]).with("b").returns(nil) - @nodes.stubs(:[]).with("c").returns(nil) - - # It should check this last, of course. - @nodes.stubs(:[]).with("default").returns(nil) - end - - it "should fail if the named node cannot be found" do - proc { @compile.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError) - end - - it "should create a resource for the first node class matching the node name" do - node_class = stub 'node', :classname => "c" - @nodes.stubs(:[]).with("c").returns(node_class) - - node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil - Puppet::Parser::Resource.expects(:new).with { |args| args[:title] == "c" and args[:type] == "node" }.returns(node_resource) - - @compile.send(:evaluate_ast_node) - end - - it "should match the default node if no matching node can be found" do - node_class = stub 'node', :classname => "default" - @nodes.stubs(:[]).with("default").returns(node_class) - - node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil - Puppet::Parser::Resource.expects(:new).with { |args| args[:title] == "default" and args[:type] == "node" }.returns(node_resource) - - @compile.send(:evaluate_ast_node) - end - - it "should tag the catalog with the found node name" do - node_class = stub 'node', :classname => "c" - @nodes.stubs(:[]).with("c").returns(node_class) - - node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil - Puppet::Parser::Resource.stubs(:new).returns(node_resource) - - @compile.catalog.expects(:tag).with("c") - @compile.send(:evaluate_ast_node) - end - - it "should evaluate the node resource immediately rather than using lazy evaluation" do - node_class = stub 'node', :classname => "c" - @nodes.stubs(:[]).with("c").returns(node_class) - - node_resource = stub 'node resource', :ref => "Node[c]" - Puppet::Parser::Resource.stubs(:new).returns(node_resource) - - node_resource.expects(:evaluate) - - @compile.send(:evaluate_ast_node) - end - - it "should set the node's scope as the top scope" do - node_class = stub 'node', :classname => "c" - @nodes.stubs(:[]).with("c").returns(node_class) - - node_resource = stub 'node resource', :ref => "Node[c]" - Puppet::Parser::Resource.stubs(:new).returns(node_resource) - - # The #evaluate method normally does this. - @compile.class_set(node_class.classname, :my_node_scope) - node_resource.stubs(:evaluate) - - @compile.send(:evaluate_ast_node) - - @compile.topscope.should == :my_node_scope - end -end diff --git a/spec/unit/parser/compiler.rb b/spec/unit/parser/compiler.rb new file mode 100755 index 000000000..ab430da62 --- /dev/null +++ b/spec/unit/parser/compiler.rb @@ -0,0 +1,532 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Parser::Compiler do + before :each do + @node = Puppet::Node.new "testnode" + @parser = Puppet::Parser::Parser.new :environment => "development" + + @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' + @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") + @compiler = Puppet::Parser::Compiler.new(@node, @parser) + end + + describe Puppet::Parser::Compiler do + + it "should be able to store references to class scopes" do + lambda { @compiler.class_set "myname", "myscope" }.should_not raise_error + end + + it "should be able to retrieve class scopes by name" do + @compiler.class_set "myname", "myscope" + @compiler.class_scope("myname").should == "myscope" + end + + it "should be able to retrieve class scopes by object" do + klass = mock 'ast_class' + klass.expects(:classname).returns("myname") + @compiler.class_set "myname", "myscope" + @compiler.class_scope(klass).should == "myscope" + end + + it "should be able to return a class list containing all set classes" do + @compiler.class_set "", "empty" + @compiler.class_set "one", "yep" + @compiler.class_set "two", "nope" + + @compiler.classlist.sort.should == %w{one two}.sort + end + end + + describe Puppet::Parser::Compiler, " when initializing" do + + it "should set its node attribute" do + @compiler.node.should equal(@node) + end + + it "should set its parser attribute" do + @compiler.parser.should equal(@parser) + end + + it "should detect when ast nodes are absent" do + @compiler.ast_nodes?.should be_false + end + + it "should detect when ast nodes are present" do + @parser.nodes["testing"] = "yay" + @compiler.ast_nodes?.should be_true + end + end + + describe Puppet::Parser::Compiler, "when managing scopes" do + + it "should create a top scope" do + @compiler.topscope.should be_instance_of(Puppet::Parser::Scope) + end + + it "should be able to create new scopes" do + @compiler.newscope(@compiler.topscope).should be_instance_of(Puppet::Parser::Scope) + end + + it "should correctly set the level of newly created scopes" do + @compiler.newscope(@compiler.topscope, :level => 5).level.should == 5 + end + + it "should set the parent scope of the new scope to be the passed-in parent" do + scope = mock 'scope' + newscope = @compiler.newscope(scope) + + @compiler.parent(newscope).should equal(scope) + end + end + + describe Puppet::Parser::Compiler, " when compiling" do + + def compile_methods + [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, + :finish, :store, :extract] + end + + # Stub all of the main compile methods except the ones we're specifically interested in. + def compile_stub(*except) + (compile_methods - except).each { |m| @compiler.stubs(m) } + end + + it "should set node parameters as variables in the top scope" do + params = {"a" => "b", "c" => "d"} + @node.stubs(:parameters).returns(params) + compile_stub(:set_node_parameters) + @compiler.compile + @compiler.topscope.lookupvar("a").should == "b" + @compiler.topscope.lookupvar("c").should == "d" + end + + it "should evaluate any existing classes named in the node" do + classes = %w{one two three four} + main = stub 'main' + one = stub 'one', :classname => "one" + three = stub 'three', :classname => "three" + @node.stubs(:name).returns("whatever") + @node.stubs(:classes).returns(classes) + + @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) + @compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes } + end + + it "should enable ast_nodes if the parser has any nodes" do + @parser.expects(:nodes).returns(:one => :yay) + @compiler.ast_nodes?.should be_true + end + + it "should disable ast_nodes if the parser has no nodes" do + @parser.expects(:nodes).returns({}) + @compiler.ast_nodes?.should be_false + end + + it "should evaluate the main class if it exists" do + compile_stub(:evaluate_main) + main_class = mock 'main_class' + main_class.expects(:evaluate_code).with { |r| r.is_a?(Puppet::Parser::Resource) } + @compiler.topscope.expects(:source=).with(main_class) + @parser.stubs(:findclass).with("", "").returns(main_class) + + @compiler.compile + end + + it "should evaluate any node classes" do + @node.stubs(:classes).returns(%w{one two three four}) + @compiler.expects(:evaluate_classes).with(%w{one two three four}, @compiler.topscope) + @compiler.send(:evaluate_node_classes) + end + + it "should evaluate all added collections" do + colls = [] + # And when the collections fail to evaluate. + colls << mock("coll1-false") + colls << mock("coll2-false") + colls.each { |c| c.expects(:evaluate).returns(false) } + + @compiler.add_collection(colls[0]) + @compiler.add_collection(colls[1]) + + compile_stub(:evaluate_generators) + @compiler.compile + end + + it "should ignore builtin resources" do + resource = stub 'builtin', :ref => "File[testing]", :builtin? => true + + @compiler.add_resource(@scope, resource) + resource.expects(:evaluate).never + + @compiler.compile + end + + it "should evaluate unevaluated resources" do + resource = stub 'notevaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => false, :virtual? => false + @compiler.add_resource(@scope, resource) + + # We have to now mark the resource as evaluated + resource.expects(:evaluate).with { |*whatever| resource.stubs(:evaluated?).returns true } + + @compiler.compile + end + + it "should not evaluate already-evaluated resources" do + resource = stub 'already_evaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => true, :virtual? => false + @compiler.add_resource(@scope, resource) + resource.expects(:evaluate).never + + @compiler.compile + end + + it "should evaluate unevaluated resources created by evaluating other resources" do + resource = stub 'notevaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => false, :virtual? => false + @compiler.add_resource(@scope, resource) + + resource2 = stub 'created', :ref => "File[other]", :builtin? => false, :evaluated? => false, :virtual? => false + + # We have to now mark the resource as evaluated + resource.expects(:evaluate).with { |*whatever| resource.stubs(:evaluated?).returns(true); @compiler.add_resource(@scope, resource2) } + resource2.expects(:evaluate).with { |*whatever| resource2.stubs(:evaluated?).returns(true) } + + + @compiler.compile + end + + it "should call finish() on all resources" do + # Add a resource that does respond to :finish + resource = Puppet::Parser::Resource.new :scope => @scope, :type => "file", :title => "finish" + resource.expects(:finish) + + @compiler.add_resource(@scope, resource) + + # And one that does not + dnf = stub "dnf", :ref => "File[dnf]" + + @compiler.add_resource(@scope, dnf) + + @compiler.send(:finish) + end + + it "should add resources that do not conflict with existing resources" do + resource = stub "noconflict", :ref => "File[yay]" + @compiler.add_resource(@scope, resource) + + @compiler.catalog.should be_vertex(resource) + end + + it "should fail to add resources that conflict with existing resources" do + type = stub 'faketype', :isomorphic? => true, :name => "mytype" + Puppet::Type.stubs(:type).with("mytype").returns(type) + + resource1 = stub "iso1conflict", :ref => "Mytype[yay]", :type => "mytype", :file => "eh", :line => 0 + resource2 = stub "iso2conflict", :ref => "Mytype[yay]", :type => "mytype", :file => "eh", :line => 0 + + @compiler.add_resource(@scope, resource1) + lambda { @compiler.add_resource(@scope, resource2) }.should raise_error(ArgumentError) + end + + it "should have a method for looking up resources" do + resource = stub 'resource', :ref => "Yay[foo]" + @compiler.add_resource(@scope, resource) + @compiler.findresource("Yay[foo]").should equal(resource) + end + + it "should be able to look resources up by type and title" do + resource = stub 'resource', :ref => "Yay[foo]" + @compiler.add_resource(@scope, resource) + @compiler.findresource("Yay", "foo").should equal(resource) + end + + it "should not evaluate virtual defined resources" do + resource = stub 'notevaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => false, :virtual? => true + @compiler.add_resource(@scope, resource) + + resource.expects(:evaluate).never + + @compiler.compile + end + end + + describe Puppet::Parser::Compiler, " when evaluating collections" do + + it "should evaluate each collection" do + 2.times { |i| + coll = mock 'coll%s' % i + @compiler.add_collection(coll) + + # This is the hard part -- we have to emulate the fact that + # collections delete themselves if they are done evaluating. + coll.expects(:evaluate).with do + @compiler.delete_collection(coll) + end + } + + @compiler.class.publicize_methods(:evaluate_collections) { @compiler.evaluate_collections } + end + + it "should not fail when there are unevaluated resource collections that do not refer to specific resources" do + coll = stub 'coll', :evaluate => false + coll.expects(:resources).returns(nil) + + @compiler.add_collection(coll) + + lambda { @compiler.compile }.should_not raise_error + end + + it "should fail when there are unevaluated resource collections that refer to a specific resource" do + coll = stub 'coll', :evaluate => false + coll.expects(:resources).returns(:something) + + @compiler.add_collection(coll) + + lambda { @compiler.compile }.should raise_error(Puppet::ParseError) + end + + it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do + coll = stub 'coll', :evaluate => false + coll.expects(:resources).returns([:one, :two]) + + @compiler.add_collection(coll) + + lambda { @compiler.compile }.should raise_error(Puppet::ParseError) + end + end + + describe Puppet::Parser::Compiler, "when told to evaluate missing classes" do + + it "should fail if there's no source listed for the scope" do + scope = stub 'scope', :source => nil + proc { @compiler.evaluate_classes(%w{one two}, scope) }.should raise_error(Puppet::DevError) + end + + it "should tag the catalog with the name of each not-found class" do + @compiler.catalog.expects(:tag).with("notfound") + @scope.expects(:findclass).with("notfound").returns(nil) + @compiler.evaluate_classes(%w{notfound}, @scope) + end + end + + describe Puppet::Parser::Compiler, " when evaluating found classes" do + + before do + @class = stub 'class', :classname => "my::class" + @scope.stubs(:findclass).with("myclass").returns(@class) + + @resource = stub 'resource', :ref => "Class[myclass]" + end + + it "should evaluate each class" do + @compiler.catalog.stubs(:tag) + + @class.expects(:evaluate).with(@scope) + + @compiler.evaluate_classes(%w{myclass}, @scope) + end + + it "should not evaluate the resources created for found classes unless asked" do + @compiler.catalog.stubs(:tag) + + @resource.expects(:evaluate).never + + @class.expects(:evaluate).returns(@resource) + + @compiler.evaluate_classes(%w{myclass}, @scope) + end + + it "should immediately evaluate the resources created for found classes when asked" do + @compiler.catalog.stubs(:tag) + + @resource.expects(:evaluate) + @class.expects(:evaluate).returns(@resource) + + @compiler.evaluate_classes(%w{myclass}, @scope, false) + end + + it "should skip classes that have already been evaluated" do + @compiler.catalog.stubs(:tag) + + @compiler.expects(:class_scope).with(@class).returns("something") + + @compiler.expects(:add_resource).never + + @resource.expects(:evaluate).never + + Puppet::Parser::Resource.expects(:new).never + @compiler.evaluate_classes(%w{myclass}, @scope, false) + end + + it "should return the list of found classes" do + @compiler.catalog.stubs(:tag) + + @compiler.stubs(:add_resource) + @scope.stubs(:findclass).with("notfound").returns(nil) + + Puppet::Parser::Resource.stubs(:new).returns(@resource) + @class.stubs :evaluate + @compiler.evaluate_classes(%w{myclass notfound}, @scope).should == %w{myclass} + end + end + + describe Puppet::Parser::Compiler, " when evaluating AST nodes with no AST nodes present" do + + it "should do nothing" do + @compiler.expects(:ast_nodes?).returns(false) + @compiler.parser.expects(:nodes).never + Puppet::Parser::Resource.expects(:new).never + + @compiler.send(:evaluate_ast_node) + end + end + + describe Puppet::Parser::Compiler, " when evaluating AST nodes with AST nodes present" do + + before do + @nodes = mock 'node_hash' + @compiler.stubs(:ast_nodes?).returns(true) + @compiler.parser.stubs(:nodes).returns(@nodes) + + # Set some names for our test + @node.stubs(:names).returns(%w{a b c}) + @nodes.stubs(:[]).with("a").returns(nil) + @nodes.stubs(:[]).with("b").returns(nil) + @nodes.stubs(:[]).with("c").returns(nil) + + # It should check this last, of course. + @nodes.stubs(:[]).with("default").returns(nil) + end + + it "should fail if the named node cannot be found" do + proc { @compiler.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError) + end + + it "should evaluate the first node class matching the node name" do + node_class = stub 'node', :classname => "c", :evaluate_code => nil + @nodes.stubs(:[]).with("c").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil + node_class.expects(:evaluate).returns(node_resource) + + @compiler.compile + end + + it "should match the default node if no matching node can be found" do + node_class = stub 'node', :classname => "default", :evaluate_code => nil + @nodes.stubs(:[]).with("default").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil + node_class.expects(:evaluate).returns(node_resource) + + @compiler.compile + end + + it "should evaluate the node resource immediately rather than using lazy evaluation" do + node_class = stub 'node', :classname => "c" + @nodes.stubs(:[]).with("c").returns(node_class) + + node_resource = stub 'node resource', :ref => "Node[c]" + node_class.expects(:evaluate).returns(node_resource) + + node_resource.expects(:evaluate) + + @compiler.send(:evaluate_ast_node) + end + + it "should set the node's scope as the top scope" do + node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil + node_class = stub 'node', :classname => "c", :evaluate => node_resource + + @nodes.stubs(:[]).with("c").returns(node_class) + + # The #evaluate method normally does this. + scope = stub 'scope', :source => "mysource" + @compiler.class_set(node_class.classname, scope) + node_resource.stubs(:evaluate) + + @compiler.compile + + @compiler.topscope.should equal(scope) + end + end + + describe Puppet::Parser::Compiler, "when storing compiled resources" do + + it "should store the resources" do + Puppet.features.expects(:rails?).returns(true) + Puppet::Rails.expects(:connect) + + @compiler.catalog.expects(:vertices).returns(:resources) + + @compiler.expects(:store_to_active_record).with(@node, :resources) + @compiler.send(:store) + end + + it "should store to active_record" do + @node.expects(:name).returns("myname") + Puppet::Rails::Host.stubs(:transaction).yields + Puppet::Rails::Host.expects(:store).with(@node, :resources) + @compiler.send(:store_to_active_record, @node, :resources) + end + end + + describe Puppet::Parser::Compiler, "when managing resource overrides" do + + before do + @override = stub 'override', :ref => "My[ref]" + @resource = stub 'resource', :ref => "My[ref]", :builtin? => true + end + + it "should be able to store overrides" do + lambda { @compiler.add_override(@override) }.should_not raise_error + end + + it "should apply overrides to the appropriate resources" do + @compiler.add_resource(@scope, @resource) + @resource.expects(:merge).with(@override) + + @compiler.add_override(@override) + + @compiler.compile + end + + it "should accept overrides before the related resource has been created" do + @resource.expects(:merge).with(@override) + + # First store the override + @compiler.add_override(@override) + + # Then the resource + @compiler.add_resource(@scope, @resource) + + # And compile, so they get resolved + @compiler.compile + end + + it "should fail if the compile is finished and resource overrides have not been applied" do + @compiler.add_override(@override) + + lambda { @compiler.compile }.should raise_error(Puppet::ParseError) + end + end + + # #620 - Nodes and classes should conflict, else classes don't get evaluated + describe Puppet::Parser::Compiler, "when evaluating nodes and classes with the same name (#620)" do + + before do + @node = stub :nodescope? => true + @class = stub :nodescope? => false + end + + it "should fail if a node already exists with the same name as the class being evaluated" do + @compiler.class_set("one", @node) + lambda { @compiler.class_set("one", @class) }.should raise_error(Puppet::ParseError) + end + + it "should fail if a class already exists with the same name as the node being evaluated" do + @compiler.class_set("one", @class) + lambda { @compiler.class_set("one", @node) }.should raise_error(Puppet::ParseError) + end + end +end diff --git a/spec/unit/parser/interpreter.rb b/spec/unit/parser/interpreter.rb index ed30ced93..f2526c73d 100755 --- a/spec/unit/parser/interpreter.rb +++ b/spec/unit/parser/interpreter.rb @@ -2,147 +2,158 @@ require File.dirname(__FILE__) + '/../../spec_helper' -describe Puppet::Parser::Interpreter, " when creating parser instances" do +describe Puppet::Parser::Interpreter do before do @interp = Puppet::Parser::Interpreter.new - @parser = mock('parser') - end - - it "should create a parser with code if there is code defined in the :code setting" do - Puppet.settings.stubs(:value).with(:code, :myenv).returns("mycode") - @parser.expects(:string=).with("mycode") - @parser.expects(:parse) - Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).returns(@parser) - @interp.send(:create_parser, :myenv).object_id.should equal(@parser.object_id) - end - - it "should create a parser with the main manifest when the code setting is an empty string" do - Puppet.settings.stubs(:value).with(:code, :myenv).returns("") - Puppet.settings.stubs(:value).with(:manifest, :myenv).returns("/my/file") - @parser.expects(:parse) - @parser.expects(:file=).with("/my/file") - Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).returns(@parser) - @interp.send(:create_parser, :myenv).should equal(@parser) - end - - it "should return nothing when new parsers fail" do - Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).raises(ArgumentError) - proc { @interp.send(:create_parser, :myenv) }.should raise_error(Puppet::Error) - end - - it "should create parsers with environment-appropriate manifests" do - # Set our per-environment values. We can't just stub :value, because - # it's called by too much of the rest of the code. - text = "[env1]\nmanifest = /t/env1.pp\n[env2]\nmanifest = /t/env2.pp" - file = mock 'file' - file.stubs(:changed?).returns(true) - file.stubs(:file).returns("/whatever") - Puppet.settings.stubs(:read_file).with(file).returns(text) - Puppet.settings.parse(file) - - parser1 = mock 'parser1' - Puppet::Parser::Parser.expects(:new).with(:environment => :env1).returns(parser1) - parser1.expects(:file=).with("/t/env1.pp") - parser1.expects(:parse) - @interp.send(:create_parser, :env1) - - parser2 = mock 'parser2' - Puppet::Parser::Parser.expects(:new).with(:environment => :env2).returns(parser2) - parser2.expects(:file=).with("/t/env2.pp") - parser2.expects(:parse) - @interp.send(:create_parser, :env2) - end -end - -describe Puppet::Parser::Interpreter, " when managing parser instances" do - before do - @interp = Puppet::Parser::Interpreter.new - @parser = mock('parser') - end - - it "should use the same parser when the parser does not need reparsing" do - @interp.expects(:create_parser).with(:myenv).returns(@parser) - @interp.send(:parser, :myenv).should equal(@parser) - - @parser.expects(:reparse?).returns(false) - @interp.send(:parser, :myenv).should equal(@parser) - end - - it "should create a new parser when reparse is true" do - oldparser = mock('oldparser') - newparser = mock('newparser') - oldparser.expects(:reparse?).returns(true) - oldparser.expects(:clear) - - @interp.expects(:create_parser).with(:myenv).returns(oldparser) - @interp.send(:parser, :myenv).should equal(oldparser) - @interp.expects(:create_parser).with(:myenv).returns(newparser) - @interp.send(:parser, :myenv).should equal(newparser) - end - - it "should fail intelligently if a parser cannot be created and one does not already exist" do - @interp.expects(:create_parser).with(:myenv).raises(ArgumentError) - proc { @interp.send(:parser, :myenv) }.should raise_error(ArgumentError) - end - - it "should keep the old parser if a new parser cannot be created" do - # Get the first parser in the hash. - @interp.expects(:create_parser).with(:myenv).returns(@parser) - @interp.send(:parser, :myenv).should equal(@parser) - - # Have it indicate something has changed - @parser.expects(:reparse?).returns(true) - - # But fail to create a new parser - @interp.expects(:create_parser).with(:myenv).raises(ArgumentError) - - # And make sure we still get the old valid parser - @interp.send(:parser, :myenv).should equal(@parser) - end - - it "should use different parsers for different environments" do - # get one for the first env - @interp.expects(:create_parser).with(:first_env).returns(@parser) - @interp.send(:parser, :first_env).should equal(@parser) - - other_parser = mock('otherparser') - @interp.expects(:create_parser).with(:second_env).returns(other_parser) - @interp.send(:parser, :second_env).should equal(other_parser) - end -end - -describe Puppet::Parser::Interpreter, " when compiling catalog" do - before do - @interp = Puppet::Parser::Interpreter.new - @node = stub 'node', :environment => :myenv - @compile = mock 'compile' @parser = mock 'parser' end - it "should create a compile with the node and parser" do - @compile.expects(:compile).returns(:config) - @interp.expects(:parser).with(:myenv).returns(@parser) - Puppet::Parser::Compile.expects(:new).with(@node, @parser).returns(@compile) - @interp.compile(@node) - end - - it "should fail intelligently when no parser can be found" do - @interp.expects(:parser).with(:myenv).returns(nil) - proc { @interp.compile(@node) }.should raise_error(Puppet::ParseError) - end -end - -describe Puppet::Parser::Interpreter, " when returning catalog version" do - before do - @interp = Puppet::Parser::Interpreter.new - end - - it "should ask the appropriate parser for the catalog version" do - node = mock 'node' - node.expects(:environment).returns(:myenv) - parser = mock 'parser' - parser.expects(:version).returns(:myvers) - @interp.expects(:parser).with(:myenv).returns(parser) - @interp.configuration_version(node).should equal(:myvers) + describe "when creating parser instances" do + it "should create a parser with code if there is code defined in the :code setting" do + Puppet.settings.stubs(:value).with(:code, :myenv).returns("mycode") + @parser.expects(:string=).with("mycode") + @parser.expects(:parse) + Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).returns(@parser) + @interp.send(:create_parser, :myenv).object_id.should equal(@parser.object_id) + end + + it "should create a parser with the main manifest when the code setting is an empty string" do + Puppet.settings.stubs(:value).with(:code, :myenv).returns("") + Puppet.settings.stubs(:value).with(:manifest, :myenv).returns("/my/file") + @parser.expects(:parse) + @parser.expects(:file=).with("/my/file") + Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).returns(@parser) + @interp.send(:create_parser, :myenv).should equal(@parser) + end + + it "should return nothing when new parsers fail" do + Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).raises(ArgumentError) + proc { @interp.send(:create_parser, :myenv) }.should raise_error(Puppet::Error) + end + + it "should create parsers with environment-appropriate manifests" do + # Set our per-environment values. We can't just stub :value, because + # it's called by too much of the rest of the code. + text = "[env1]\nmanifest = /t/env1.pp\n[env2]\nmanifest = /t/env2.pp" + file = mock 'file' + file.stubs(:changed?).returns(true) + file.stubs(:file).returns("/whatever") + Puppet.settings.stubs(:read_file).with(file).returns(text) + Puppet.settings.parse(file) + + parser1 = mock 'parser1' + Puppet::Parser::Parser.expects(:new).with(:environment => :env1).returns(parser1) + parser1.expects(:file=).with("/t/env1.pp") + parser1.expects(:parse) + @interp.send(:create_parser, :env1) + + parser2 = mock 'parser2' + Puppet::Parser::Parser.expects(:new).with(:environment => :env2).returns(parser2) + parser2.expects(:file=).with("/t/env2.pp") + parser2.expects(:parse) + @interp.send(:create_parser, :env2) + end + end + + describe "when managing parser instances" do + it "should use the same parser when the parser does not need reparsing" do + @interp.expects(:create_parser).with(:myenv).returns(@parser) + @interp.send(:parser, :myenv).should equal(@parser) + + @parser.expects(:reparse?).returns(false) + @interp.send(:parser, :myenv).should equal(@parser) + end + + it "should fail intelligently if a parser cannot be created and one does not already exist" do + @interp.expects(:create_parser).with(:myenv).raises(ArgumentError) + proc { @interp.send(:parser, :myenv) }.should raise_error(ArgumentError) + end + + it "should use different parsers for different environments" do + # get one for the first env + @interp.expects(:create_parser).with(:first_env).returns(@parser) + @interp.send(:parser, :first_env).should equal(@parser) + + other_parser = mock('otherparser') + @interp.expects(:create_parser).with(:second_env).returns(other_parser) + @interp.send(:parser, :second_env).should equal(other_parser) + end + + describe "when files need reparsing" do + it "should create a new parser" do + oldparser = mock('oldparser') + newparser = mock('newparser') + oldparser.expects(:reparse?).returns(true) + oldparser.expects(:clear) + + @interp.expects(:create_parser).with(:myenv).returns(oldparser) + @interp.send(:parser, :myenv).should equal(oldparser) + @interp.expects(:create_parser).with(:myenv).returns(newparser) + @interp.send(:parser, :myenv).should equal(newparser) + end + + it "should keep the old parser if a new parser cannot be created" do + # Get the first parser in the hash. + @interp.expects(:create_parser).with(:myenv).returns(@parser) + @interp.send(:parser, :myenv).should equal(@parser) + + # Have it indicate something has changed + @parser.expects(:reparse?).returns(true) + + # But fail to create a new parser + @interp.expects(:create_parser).with(:myenv).raises(ArgumentError) + + # And make sure we still get the old valid parser + @interp.send(:parser, :myenv).should equal(@parser) + end + + it "should log syntax errors when using the old parser" do + # Get the first parser in the hash. + @interp.stubs(:create_parser).with(:myenv).returns(@parser) + @interp.send(:parser, :myenv) + + # Have it indicate something has changed + @parser.stubs(:reparse?).returns(true) + + # But fail to create a new parser + @interp.stubs(:create_parser).with(:myenv).raises(ArgumentError) + + Puppet.expects(:err) + + # And make sure we still get the old valid parser + @interp.send(:parser, :myenv) + end + end + end + + describe "when compiling a catalog" do + before do + @node = stub 'node', :environment => :myenv + @compiler = mock 'compile' + end + + it "should create a compile with the node and parser" do + @compiler.expects(:compile).returns(:config) + @interp.expects(:parser).with(:myenv).returns(@parser) + Puppet::Parser::Compiler.expects(:new).with(@node, @parser).returns(@compiler) + @interp.compile(@node) + end + + it "should fail intelligently when no parser can be found" do + @node.stubs(:name).returns("whatever") + @interp.expects(:parser).with(:myenv).returns(nil) + proc { @interp.compile(@node) }.should raise_error(Puppet::ParseError) + end + end + + describe "when returning catalog version" do + it "should ask the appropriate parser for the catalog version" do + node = mock 'node' + node.expects(:environment).returns(:myenv) + parser = mock 'parser' + parser.expects(:version).returns(:myvers) + @interp.expects(:parser).with(:myenv).returns(parser) + @interp.configuration_version(node).should equal(:myvers) + end end end diff --git a/spec/unit/parser/lexer.rb b/spec/unit/parser/lexer.rb new file mode 100755 index 000000000..9972f7268 --- /dev/null +++ b/spec/unit/parser/lexer.rb @@ -0,0 +1,465 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/parser/lexer' + +describe Puppet::Parser::Lexer::Token do + before do + @token = Puppet::Parser::Lexer::Token.new(%r{something}, :NAME) + end + + [:regex, :name, :string, :skip, :incr_line, :skip_text].each do |param| + it "should have a #{param.to_s} reader" do + @token.should be_respond_to(param) + end + + it "should have a #{param.to_s} writer" do + @token.should be_respond_to(param.to_s + "=") + end + end +end + +describe Puppet::Parser::Lexer::Token, "when initializing" do + it "should create a regex if the first argument is a string" do + Puppet::Parser::Lexer::Token.new("something", :NAME).regex.should == %r{something} + end + + it "should set the string if the first argument is one" do + Puppet::Parser::Lexer::Token.new("something", :NAME).string.should == "something" + end + + it "should set the regex if the first argument is one" do + Puppet::Parser::Lexer::Token.new(%r{something}, :NAME).regex.should == %r{something} + end +end + +describe Puppet::Parser::Lexer::TokenList do + before do + @list = Puppet::Parser::Lexer::TokenList.new + end + + it "should have a method for retrieving tokens by the name" do + token = @list.add_token :name, "whatever" + @list[:name].should equal(token) + end + + it "should have a method for retrieving string tokens by the string" do + token = @list.add_token :name, "whatever" + @list.lookup("whatever").should equal(token) + end + + it "should add tokens to the list when directed" do + token = @list.add_token :name, "whatever" + @list[:name].should equal(token) + end + + it "should have a method for adding multiple tokens at once" do + @list.add_tokens "whatever" => :name, "foo" => :bar + @list[:name].should_not be_nil + @list[:bar].should_not be_nil + end + + it "should fail to add tokens sharing a name with an existing token" do + @list.add_token :name, "whatever" + lambda { @list.add_token :name, "whatever" }.should raise_error(ArgumentError) + end + + it "should set provided options on tokens being added" do + token = @list.add_token :name, "whatever", :skip_text => true + token.skip_text.should == true + end + + it "should define any provided blocks as a :convert method" do + token = @list.add_token(:name, "whatever") do "foo" end + token.convert.should == "foo" + end + + it "should store all string tokens in the :string_tokens list" do + one = @list.add_token(:name, "1") + @list.string_tokens.should be_include(one) + end + + it "should store all regex tokens in the :regex_tokens list" do + one = @list.add_token(:name, %r{one}) + @list.regex_tokens.should be_include(one) + end + + it "should not store string tokens in the :regex_tokens list" do + one = @list.add_token(:name, "1") + @list.regex_tokens.should_not be_include(one) + end + + it "should not store regex tokens in the :string_tokens list" do + one = @list.add_token(:name, %r{one}) + @list.string_tokens.should_not be_include(one) + end + + it "should sort the string tokens inversely by length when asked" do + one = @list.add_token(:name, "1") + two = @list.add_token(:other, "12") + @list.sort_tokens + @list.string_tokens.should == [two, one] + end +end + +describe Puppet::Parser::Lexer::TOKENS do + before do + @lexer = Puppet::Parser::Lexer.new() + end + + { + :LBRACK => '[', + :RBRACK => ']', + :LBRACE => '{', + :RBRACE => '}', + :LPAREN => '(', + :RPAREN => ')', + :EQUALS => '=', + :ISEQUAL => '==', + :GREATEREQUAL => '>=', + :GREATERTHAN => '>', + :LESSTHAN => '<', + :LESSEQUAL => '<=', + :NOTEQUAL => '!=', + :NOT => '!', + :COMMA => ',', + :DOT => '.', + :COLON => ':', + :AT => '@', + :LLCOLLECT => '<<|', + :RRCOLLECT => '|>>', + :LCOLLECT => '<|', + :RCOLLECT => '|>', + :SEMIC => ';', + :QMARK => '?', + :BACKSLASH => '\\', + :FARROW => '=>', + :PARROW => '+>' + }.each do |name, string| + it "should have a token named #{name.to_s}" do + Puppet::Parser::Lexer::TOKENS[name].should_not be_nil + end + + it "should match '#{string}' for the token #{name.to_s}" do + Puppet::Parser::Lexer::TOKENS[name].string.should == string + end + end + + { + "case" => :CASE, + "class" => :CLASS, + "default" => :DEFAULT, + "define" => :DEFINE, + "import" => :IMPORT, + "if" => :IF, + "elsif" => :ELSIF, + "else" => :ELSE, + "inherits" => :INHERITS, + "node" => :NODE, + "and" => :AND, + "or" => :OR, + "undef" => :UNDEF, + "false" => :FALSE, + "true" => :TRUE + }.each do |string, name| + it "should have a keyword named #{name.to_s}" do + Puppet::Parser::Lexer::KEYWORDS[name].should_not be_nil + end + + it "should have the keyword for #{name.to_s} set to #{string}" do + Puppet::Parser::Lexer::KEYWORDS[name].string.should == string + end + end + + # These tokens' strings don't matter, just that the tokens exist. + [:DQTEXT, :SQTEXT, :BOOLEAN, :NAME, :NUMBER, :COMMENT, :RETURN, :SQUOTE, :DQUOTE, :VARIABLE].each do |name| + it "should have a token named #{name.to_s}" do + Puppet::Parser::Lexer::TOKENS[name].should_not be_nil + end + end +end + +describe Puppet::Parser::Lexer::TOKENS[:NAME] do + before { @token = Puppet::Parser::Lexer::TOKENS[:NAME] } + + it "should match against lower-case alpha-numeric terms" do + @token.regex.should =~ "one-two" + end + + it "should return itself and the value if the matched term is not a keyword" do + Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(nil) + @token.convert(stub("lexer"), "myval").should == [Puppet::Parser::Lexer::TOKENS[:NAME], "myval"] + end + + it "should return the keyword token and the value if the matched term is a keyword" do + keyword = stub 'keyword', :name => :testing + Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) + @token.convert(stub("lexer"), "myval").should == [keyword, "myval"] + end + + it "should return the BOOLEAN token and 'true' if the matched term is the string 'true'" do + keyword = stub 'keyword', :name => :TRUE + Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) + @token.convert(stub('lexer'), "true").should == [Puppet::Parser::Lexer::TOKENS[:BOOLEAN], true] + end + + it "should return the BOOLEAN token and 'false' if the matched term is the string 'false'" do + keyword = stub 'keyword', :name => :FALSE + Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) + @token.convert(stub('lexer'), "false").should == [Puppet::Parser::Lexer::TOKENS[:BOOLEAN], false] + end +end + +describe Puppet::Parser::Lexer::TOKENS[:NUMBER] do + before { @token = Puppet::Parser::Lexer::TOKENS[:NUMBER] } + + it "should match against numeric terms" do + @token.regex.should =~ "2982383139" + end + + it "should return the NAME token and the value" do + @token.convert(stub("lexer"), "myval").should == [Puppet::Parser::Lexer::TOKENS[:NAME], "myval"] + end +end + +describe Puppet::Parser::Lexer::TOKENS[:COMMENT] do + before { @token = Puppet::Parser::Lexer::TOKENS[:COMMENT] } + + it "should match against lines starting with '#'" do + @token.regex.should =~ "# this is a comment" + end + + it "should be marked to get skipped" do + @token.skip?.should be_true + end +end + +describe Puppet::Parser::Lexer::TOKENS[:RETURN] do + before { @token = Puppet::Parser::Lexer::TOKENS[:RETURN] } + + it "should match against carriage returns" do + @token.regex.should =~ "\n" + end + + it "should be marked to initiate text skipping" do + @token.skip_text.should be_true + end + + it "should be marked to increment the line" do + @token.incr_line.should be_true + end +end + +describe Puppet::Parser::Lexer::TOKENS[:SQUOTE] do + before { @token = Puppet::Parser::Lexer::TOKENS[:SQUOTE] } + + it "should match against single quotes" do + @token.regex.should =~ "'" + end + + it "should slurp the rest of the quoted string" do + lexer = stub("lexer") + lexer.expects(:slurpstring).with("myval").returns("otherval") + @token.convert(lexer, "myval") + end + + it "should return the SQTEXT token with the slurped string" do + lexer = stub("lexer") + lexer.stubs(:slurpstring).with("myval").returns("otherval") + @token.convert(lexer, "myval").should == [Puppet::Parser::Lexer::TOKENS[:SQTEXT], "otherval"] + end +end + +describe Puppet::Parser::Lexer::TOKENS[:DQUOTE] do + before { @token = Puppet::Parser::Lexer::TOKENS[:DQUOTE] } + + it "should match against single quotes" do + @token.regex.should =~ '"' + end + + it "should slurp the rest of the quoted string" do + lexer = stub("lexer") + lexer.expects(:slurpstring).with("myval").returns("otherval") + @token.convert(lexer, "myval") + end + + it "should return the DQTEXT token with the slurped string" do + lexer = stub("lexer") + lexer.stubs(:slurpstring).with("myval").returns("otherval") + @token.convert(lexer, "myval").should == [Puppet::Parser::Lexer::TOKENS[:DQTEXT], "otherval"] + end +end + +describe Puppet::Parser::Lexer::TOKENS[:VARIABLE] do + before { @token = Puppet::Parser::Lexer::TOKENS[:VARIABLE] } + + it "should match against alpha words prefixed with '$'" do + @token.regex.should =~ '$this_var' + end + + it "should return the VARIABLE token and the variable name stripped of the '$'" do + @token.convert(stub("lexer"), "$myval").should == [Puppet::Parser::Lexer::TOKENS[:VARIABLE], "myval"] + end +end + +# FIXME: We need to rewrite all of these tests, but I just don't want to take the time right now. +describe "Puppet::Parser::Lexer in the old tests" do + before { @lexer = Puppet::Parser::Lexer.new } + + it "should do simple lexing" do + strings = { +%q{\\} => [[:BACKSLASH,"\\"],[false,false]], +%q{simplest scanner test} => [[:NAME,"simplest"],[:NAME,"scanner"],[:NAME,"test"],[false,false]], +%q{returned scanner test +} => [[:NAME,"returned"],[:NAME,"scanner"],[:NAME,"test"],[false,false]] + } + strings.each { |str,ary| + @lexer.string = str + @lexer.fullscan().should == ary + } + end + + it "should correctly lex quoted strings" do + strings = { +%q{a simple "scanner" test +} => [[:NAME,"a"],[:NAME,"simple"],[:DQTEXT,"scanner"],[:NAME,"test"],[false,false]], +%q{a simple 'single quote scanner' test +} => [[:NAME,"a"],[:NAME,"simple"],[:SQTEXT,"single quote scanner"],[:NAME,"test"],[false,false]], +%q{a harder 'a $b \c"' +} => [[:NAME,"a"],[:NAME,"harder"],[:SQTEXT,'a $b \c"'],[false,false]], +%q{a harder "scanner test" +} => [[:NAME,"a"],[:NAME,"harder"],[:DQTEXT,"scanner test"],[false,false]], +%q{a hardest "scanner \"test\"" +} => [[:NAME,"a"],[:NAME,"hardest"],[:DQTEXT,'scanner "test"'],[false,false]], +%q{a hardestest "scanner \"test\" +" +} => [[:NAME,"a"],[:NAME,"hardestest"],[:DQTEXT,'scanner "test" +'],[false,false]], +%q{function("call")} => [[:NAME,"function"],[:LPAREN,"("],[:DQTEXT,'call'],[:RPAREN,")"],[false,false]] +} + strings.each { |str,array| + @lexer.string = str + @lexer.fullscan().should == array + } + end + + it "should fail usefully" do + strings = %w{ + ^ + } + strings.each { |str| + @lexer.string = str + lambda { @lexer.fullscan() }.should raise_error(RuntimeError) + } + end + + it "should fail if the string is not set" do + lambda { @lexer.fullscan() }.should raise_error(Puppet::LexError) + end + + it "should correctly identify keywords" do + @lexer.string = "case" + @lexer.fullscan.should == [[:CASE, "case"], [false, false]] + end + + it "should correctly match strings" do + names = %w{this is a bunch of names} + types = %w{Many Different Words A Word} + words = %w{differently Cased words A a} + + names.each { |t| + @lexer.string = t + @lexer.fullscan.should == [[:NAME,t],[false,false]] + } + types.each { |t| + @lexer.string = t + @lexer.fullscan.should == [[:CLASSREF,t],[false,false]] + } + end + + it "should correctly parse empty strings" do + bit = '$var = ""' + + @lexer.string = bit + + lambda { @lexer.fullscan }.should_not raise_error + end + + it "should correctly parse virtual resources" do + string = "@type {" + + @lexer.string = string + + @lexer.fullscan.should == [[:AT, "@"], [:NAME, "type"], [:LBRACE, "{"], [false,false]] + end + + it "should correctly deal with namespaces" do + @lexer.string = %{class myclass} + + @lexer.fullscan + + @lexer.namespace.should == "myclass" + + @lexer.namepop + + @lexer.namespace.should == "" + + @lexer.string = "class base { class sub { class more" + + @lexer.fullscan + + @lexer.namespace.should == "base::sub::more" + + @lexer.namepop + + @lexer.namespace.should == "base::sub" + end + + it "should correctly handle fully qualified names" do + @lexer.string = "class base { class sub::more {" + + @lexer.fullscan + + @lexer.namespace.should == "base::sub::more" + + @lexer.namepop + + @lexer.namespace.should == "base" + end + + it "should correctly lex variables" do + ["$variable", "$::variable", "$qualified::variable", "$further::qualified::variable"].each do |string| + @lexer.string = string + + @lexer.scan do |t, s| + t.should == :VARIABLE + string.sub(/^\$/, '').should == s + break + end + end + end + + # #774 + it "should correctly parse the CLASSREF token" do + string = ["Foo", "::Foo","Foo::Bar","::Foo::Bar"] + + string.each do |foo| + @lexer.string = foo + @lexer.fullscan[0].should == [:CLASSREF, foo] + end + end +end + +require 'puppettest/support/utils' +describe "Puppet::Parser::Lexer in the old tests when lexing example files" do + extend PuppetTest + extend PuppetTest::Support::Utils + textfiles() do |file| + it "should correctly lex #{file}" do + lexer = Puppet::Parser::Lexer.new() + lexer.file = file + lambda { lexer.fullscan() }.should_not raise_error + end + end +end diff --git a/spec/unit/parser/resource.rb b/spec/unit/parser/resource.rb index 3d048f7e6..776e9c742 100755 --- a/spec/unit/parser/resource.rb +++ b/spec/unit/parser/resource.rb @@ -4,86 +4,316 @@ require File.dirname(__FILE__) + '/../../spec_helper' # LAK: FIXME This is just new tests for resources; I have # not moved all tests over yet. -describe Puppet::Parser::Resource, " when evaluating" do - before do - @type = Puppet::Parser::Resource +describe Puppet::Parser::Resource do + before do @parser = Puppet::Parser::Parser.new :Code => "" @source = @parser.newclass "" - @definition = @parser.newdefine "mydefine" - @class = @parser.newclass "myclass" - @nodedef = @parser.newnode("mynode")[0] @node = Puppet::Node.new("yaynode") - @compile = Puppet::Parser::Compile.new(@node, @parser) - @scope = @compile.topscope + @compiler = Puppet::Parser::Compiler.new(@node, @parser) + @scope = @compiler.topscope end - it "should evaluate the associated AST definition" do - res = @type.new(:type => "mydefine", :title => "whatever", :scope => @scope, :source => @source) - @definition.expects(:evaluate).with(:scope => @scope, :resource => res) + def mkresource(args = {}) + args[:source] ||= "source" + args[:scope] ||= stub('scope', :source => mock('source')) + + {:type => "resource", :title => "testing", :source => "source", :scope => "scope"}.each do |param, value| + args[param] ||= value + end + + params = args[:params] || {:one => "yay", :three => "rah"} + if args[:params] == :none + args.delete(:params) + else + args[:params] = paramify(args[:source], params) + end - res.evaluate + Puppet::Parser::Resource.new(args) end - it "should evaluate the associated AST class" do - res = @type.new(:type => "class", :title => "myclass", :scope => @scope, :source => @source) - @class.expects(:evaluate).with(:scope => @scope, :resource => res) - res.evaluate + def param(name, value, source) + Puppet::Parser::Resource::Param.new(:name => name, :value => value, :source => source) end - it "should evaluate the associated AST node" do - res = @type.new(:type => "node", :title => "mynode", :scope => @scope, :source => @source) - @nodedef.expects(:evaluate).with(:scope => @scope, :resource => res) - res.evaluate + def paramify(source, hash) + hash.collect do |name, value| + Puppet::Parser::Resource::Param.new( + :name => name, :value => value, :source => source + ) + end end -end -describe Puppet::Parser::Resource, " when finishing" do - before do - @parser = Puppet::Parser::Parser.new :Code => "" - @source = @parser.newclass "" - @definition = @parser.newdefine "mydefine" - @class = @parser.newclass "myclass" - @nodedef = @parser.newnode("mynode")[0] - @node = Puppet::Node.new("yaynode") - @compile = Puppet::Parser::Compile.new(@node, @parser) - @scope = @compile.topscope + it "should be isomorphic if it is builtin and models an isomorphic type" do + Puppet::Type.type(:file).expects(:isomorphic?).returns(true) + @resource = Puppet::Parser::Resource.new(:type => "file", :title => "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true + end + + it "should not be isomorphic if it is builtin and models a non-isomorphic type" do + Puppet::Type.type(:file).expects(:isomorphic?).returns(false) + @resource = Puppet::Parser::Resource.new(:type => "file", :title => "whatever", :scope => @scope, :source => @source).isomorphic?.should be_false + end - @resource = Puppet::Parser::Resource.new(:type => "mydefine", :title => "whatever", :scope => @scope, :source => @source) + it "should be isomorphic if it is not builtin" do + @parser.newdefine "whatever" + @resource = Puppet::Parser::Resource.new(:type => "whatever", :title => "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true end - it "should copy metaparams from its scope" do - @scope.setvar("noop", "true") + it "should have a array-indexing method for retrieving parameter values" do + @resource = mkresource + @resource[:one].should == "yay" + end + + describe "when initializing" do + before do + @arguments = {:type => "resource", :title => "testing", :scope => stub('scope', :source => mock('source'))} + end - @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } + [:type, :title, :scope].each do |name| + it "should fail unless #{name.to_s} is specified" do + try = @arguments.dup + try.delete(name) + lambda { Puppet::Parser::Resource.new(try) }.should raise_error(ArgumentError) + end + end - @resource["noop"].should == "true" + it "should set the reference correctly" do + res = Puppet::Parser::Resource.new(@arguments) + res.ref.should == "Resource[testing]" + end end - it "should not copy metaparams that it already has" do - @resource.class.publicize_methods(:set_parameter) { @resource.set_parameter("noop", "false") } - @scope.setvar("noop", "true") + describe "when evaluating" do + before do + @type = Puppet::Parser::Resource + + @definition = @parser.newdefine "mydefine" + @class = @parser.newclass "myclass" + @nodedef = @parser.newnode("mynode")[0] + end + + it "should evaluate the associated AST definition" do + res = @type.new(:type => "mydefine", :title => "whatever", :scope => @scope, :source => @source) + @definition.expects(:evaluate_code).with(res) + + res.evaluate + end + + it "should evaluate the associated AST class" do + res = @type.new(:type => "class", :title => "myclass", :scope => @scope, :source => @source) + @class.expects(:evaluate_code).with(res) + res.evaluate + end + + it "should evaluate the associated AST node" do + res = @type.new(:type => "node", :title => "mynode", :scope => @scope, :source => @source) + @nodedef.expects(:evaluate_code).with(res) + res.evaluate + end + end + + describe "when finishing" do + before do + @class = @parser.newclass "myclass" + @nodedef = @parser.newnode("mynode")[0] + + @resource = Puppet::Parser::Resource.new(:type => "file", :title => "whatever", :scope => @scope, :source => @source) + end + + it "should do nothing if it has already been finished" do + @resource.finish + @resource.expects(:add_metaparams).never + @resource.finish + end + + it "should add all defaults available from the scope" do + @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => param(:owner, "default", @resource.source)) + @resource.finish + + @resource[:owner].should == "default" + end + + it "should not replace existing parameters with defaults" do + @resource.set_parameter :owner, "oldvalue" + @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => :replaced) + @resource.finish + + @resource[:owner].should == "oldvalue" + end + + it "should add a copy of each default, rather than the actual default parameter instance" do + newparam = param(:owner, "default", @resource.source) + other = newparam.dup + other.value = "other" + newparam.expects(:dup).returns(other) + @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => newparam) + @resource.finish + + @resource[:owner].should == "other" + end + + it "should copy metaparams from its scope" do + @scope.setvar("noop", "true") + + @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } - @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } + @resource["noop"].should == "true" + end - @resource["noop"].should == "false" + it "should not copy metaparams that it already has" do + @resource.set_parameter("noop", "false") + @scope.setvar("noop", "true") + + @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } + + @resource["noop"].should == "false" + end + + it "should stack relationship metaparams from its container if it already has them" do + @resource.set_parameter("require", "resource") + @scope.setvar("require", "container") + + @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } + + @resource["require"].sort.should == %w{container resource} + end + + it "should flatten the array resulting from stacking relationship metaparams" do + @resource.set_parameter("require", ["resource1", "resource2"]) + @scope.setvar("require", %w{container1 container2}) + + @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } + + @resource["require"].sort.should == %w{container1 container2 resource1 resource2} + end + + it "should add any tags from the scope resource" do + scope_resource = stub 'scope_resource', :tags => %w{one two} + @scope.stubs(:resource).returns(scope_resource) + + @resource.class.publicize_methods(:add_scope_tags) { @resource.add_scope_tags } + + @resource.tags.should be_include("one") + @resource.tags.should be_include("two") + end end - it "should stack relationship metaparams from its container if it already has them" do - @resource.class.publicize_methods(:set_parameter) { @resource.set_parameter("require", "resource") } - @scope.setvar("require", "container") + describe "when being tagged" do + before do + @scope_resource = stub 'scope_resource', :tags => %w{srone srtwo} + @scope = stub 'scope', :resource => @scope_resource + @resource = Puppet::Parser::Resource.new(:type => "file", :title => "yay", :scope => @scope, :source => mock('source')) + end + + it "should get tagged with the resource type" do + @resource.tags.should be_include("file") + end + + it "should get tagged with the title" do + @resource.tags.should be_include("yay") + end + + it "should get tagged with each name in the title if the title is a qualified class name" do + resource = Puppet::Parser::Resource.new(:type => "file", :title => "one::two", :scope => @scope, :source => mock('source')) + resource.tags.should be_include("one") + resource.tags.should be_include("two") + end + + it "should get tagged with each name in the type if the type is a qualified class name" do + resource = Puppet::Parser::Resource.new(:type => "one::two", :title => "whatever", :scope => @scope, :source => mock('source')) + resource.tags.should be_include("one") + resource.tags.should be_include("two") + end + + it "should not get tagged with non-alphanumeric titles" do + resource = Puppet::Parser::Resource.new(:type => "file", :title => "this is a test", :scope => @scope, :source => mock('source')) + resource.tags.should_not be_include("this is a test") + end - @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } + it "should fail on tags containing '*' characters" do + lambda { @resource.tag("bad*tag") }.should raise_error(Puppet::ParseError) + end - @resource["require"].sort.should == %w{container resource} + it "should fail on tags starting with '-' characters" do + lambda { @resource.tag("-badtag") }.should raise_error(Puppet::ParseError) + end + + it "should fail on tags containing ' ' characters" do + lambda { @resource.tag("bad tag") }.should raise_error(Puppet::ParseError) + end + + it "should allow alpha tags" do + lambda { @resource.tag("good_tag") }.should_not raise_error(Puppet::ParseError) + end end - it "should flatten the array resulting from stacking relationship metaparams" do - @resource.class.publicize_methods(:set_parameter) { @resource.set_parameter("require", ["resource1", "resource2"]) } - @scope.setvar("require", %w{container1 container2}) + describe "when merging overrides" do + before do + @source = "source1" + @resource = mkresource :source => @source + @override = mkresource :source => @source + end + + it "should fail when the override was not created by a parent class" do + @override.source = "source2" + @override.source.expects(:child_of?).with("source1").returns(false) + lambda { @resource.merge(@override) }.should raise_error(Puppet::ParseError) + end + + it "should succeed when the override was created in the current scope" do + @resource.source = "source3" + @override.source = @resource.source + @override.source.expects(:child_of?).with("source3").never + params = {:a => :b, :c => :d} + @override.expects(:params).returns(params) + @resource.expects(:override_parameter).with(:b) + @resource.expects(:override_parameter).with(:d) + @resource.merge(@override) + end + + it "should succeed when a parent class created the override" do + @resource.source = "source3" + @override.source = "source4" + @override.source.expects(:child_of?).with("source3").returns(true) + params = {:a => :b, :c => :d} + @override.expects(:params).returns(params) + @resource.expects(:override_parameter).with(:b) + @resource.expects(:override_parameter).with(:d) + @resource.merge(@override) + end + + it "should add new parameters when the parameter is not set" do + @source.stubs(:child_of?).returns true + @override.set_parameter(:testing, "value") + @resource.merge(@override) + + @resource[:testing].should == "value" + end + + it "should replace existing parameter values" do + @source.stubs(:child_of?).returns true + @resource.set_parameter(:testing, "old") + @override.set_parameter(:testing, "value") + + @resource.merge(@override) + + @resource[:testing].should == "value" + end + + it "should add values to the parameter when the override was created with the '+>' syntax" do + @source.stubs(:child_of?).returns true + param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "testing", :source => @resource.source) + param.add = true + + @override.set_parameter(param) + + @resource.set_parameter(:testing, "other") + + @resource.merge(@override) + + @resource[:testing].should == %w{other testing} + end - @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } - @resource["require"].sort.should == %w{container1 container2 resource1 resource2} end end diff --git a/spec/unit/parser/resource/reference.rb b/spec/unit/parser/resource/reference.rb index e7385f796..147f772d1 100755 --- a/spec/unit/parser/resource/reference.rb +++ b/spec/unit/parser/resource/reference.rb @@ -52,23 +52,23 @@ describe Puppet::Parser::Resource::Reference, " when modeling defined types" do @nodedef = @parser.newnode("mynode")[0] @node = Puppet::Node.new("yaynode") - @compile = Puppet::Parser::Compile.new(@node, @parser) + @compiler = Puppet::Parser::Compiler.new(@node, @parser) end it "should be able to find defined types" do - ref = @type.new(:type => "mydefine", :title => "/tmp/yay", :scope => @compile.topscope) + ref = @type.new(:type => "mydefine", :title => "/tmp/yay", :scope => @compiler.topscope) ref.builtin?.should be_false ref.definedtype.should equal(@definition) end it "should be able to find classes" do - ref = @type.new(:type => "class", :title => "myclass", :scope => @compile.topscope) + ref = @type.new(:type => "class", :title => "myclass", :scope => @compiler.topscope) ref.builtin?.should be_false ref.definedtype.should equal(@class) end it "should be able to find nodes" do - ref = @type.new(:type => "node", :title => "mynode", :scope => @compile.topscope) + ref = @type.new(:type => "node", :title => "mynode", :scope => @compiler.topscope) ref.builtin?.should be_false ref.definedtype.object_id.should == @nodedef.object_id end diff --git a/spec/unit/ral/provider/interface/redhat.rb b/spec/unit/ral/provider/interface/redhat.rb index 0b3b75ffc..9bf1b9722 100755 --- a/spec/unit/ral/provider/interface/redhat.rb +++ b/spec/unit/ral/provider/interface/redhat.rb @@ -5,9 +5,6 @@ require File.dirname(__FILE__) + '/../../../../spec_helper' -require 'puppet/provider/interface/redhat' - - provider_class = Puppet::Type.type(:interface).provider(:redhat) describe provider_class do diff --git a/spec/unit/ral/provider/mount.rb b/spec/unit/ral/provider/mount.rb index 65aaf7053..0b90d53c9 100755 --- a/spec/unit/ral/provider/mount.rb +++ b/spec/unit/ral/provider/mount.rb @@ -4,8 +4,8 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/provider/mount' -module MountModuleTesting - def setup +describe Puppet::Provider::Mount do + before :each do @mounter = Object.new @mounter.extend(Puppet::Provider::Mount) @@ -16,119 +16,115 @@ module MountModuleTesting @mounter.stubs(:resource).returns(@resource) end -end -describe Puppet::Provider::Mount, " when mounting" do - include MountModuleTesting + describe Puppet::Provider::Mount, " when mounting" do - it "should use the 'mountcmd' method to mount" do - @mounter.stubs(:options).returns(nil) - @mounter.expects(:mountcmd) + it "should use the 'mountcmd' method to mount" do + @mounter.stubs(:options).returns(nil) + @mounter.expects(:mountcmd) - @mounter.mount - end + @mounter.mount + end - it "should flush before mounting if a flush method exists" do - @mounter.meta_def(:flush) { } - @mounter.expects(:flush) - @mounter.stubs(:mountcmd) - @mounter.stubs(:options).returns(nil) + it "should flush before mounting if a flush method exists" do + @mounter.meta_def(:flush) { } + @mounter.expects(:flush) + @mounter.stubs(:mountcmd) + @mounter.stubs(:options).returns(nil) - @mounter.mount - end + @mounter.mount + end - it "should add the options following '-o' if they exist and are not set to :absent" do - @mounter.stubs(:options).returns("ro") - @mounter.expects(:mountcmd).with { |*ary| ary[0] == "-o" and ary[1] == "ro" } + it "should add the options following '-o' if they exist and are not set to :absent" do + @mounter.stubs(:options).returns("ro") + @mounter.expects(:mountcmd).with { |*ary| ary[0] == "-o" and ary[1] == "ro" } - @mounter.mount - end + @mounter.mount + end - it "should specify the filesystem name to the mount command" do - @mounter.stubs(:options).returns(nil) - @mounter.expects(:mountcmd).with { |*ary| ary[-1] == @name } + it "should specify the filesystem name to the mount command" do + @mounter.stubs(:options).returns(nil) + @mounter.expects(:mountcmd).with { |*ary| ary[-1] == @name } - @mounter.mount + @mounter.mount + end end -end -describe Puppet::Provider::Mount, " when remounting" do - include MountModuleTesting - - it "should use '-o remount' if the resource specifies it supports remounting" do - @mounter.stubs(:info) - @resource.stubs(:[]).with(:remounts).returns(:true) - @mounter.expects(:mountcmd).with("-o", "remount", @name) - @mounter.remount + describe Puppet::Provider::Mount, " when remounting" do + + it "should use '-o remount' if the resource specifies it supports remounting" do + @mounter.stubs(:info) + @resource.stubs(:[]).with(:remounts).returns(:true) + @mounter.expects(:mountcmd).with("-o", "remount", @name) + @mounter.remount + end + + it "should unmount and mount if the resource does not specify it supports remounting" do + @mounter.stubs(:info) + @resource.stubs(:[]).with(:remounts).returns(false) + @mounter.expects(:unmount) + @mounter.expects(:mount) + @mounter.remount + end + + it "should log that it is remounting" do + @resource.stubs(:[]).with(:remounts).returns(:true) + @mounter.stubs(:mountcmd) + @mounter.expects(:info).with("Remounting") + @mounter.remount + end end - it "should unmount and mount if the resource does not specify it supports remounting" do - @mounter.stubs(:info) - @resource.stubs(:[]).with(:remounts).returns(false) - @mounter.expects(:unmount) - @mounter.expects(:mount) - @mounter.remount - end + describe Puppet::Provider::Mount, " when unmounting" do - it "should log that it is remounting" do - @resource.stubs(:[]).with(:remounts).returns(:true) - @mounter.stubs(:mountcmd) - @mounter.expects(:info).with("Remounting") - @mounter.remount + it "should call the :umount command with the resource name" do + @mounter.expects(:umount).with(@name) + @mounter.unmount + end end -end -describe Puppet::Provider::Mount, " when unmounting" do - include MountModuleTesting + describe Puppet::Provider::Mount, " when determining if it is mounted" do - it "should call the :umount command with the resource name" do - @mounter.expects(:umount).with(@name) - @mounter.unmount - end -end - -describe Puppet::Provider::Mount, " when determining if it is mounted" do - include MountModuleTesting + it "should parse the results of running the mount command with no arguments" do + Facter.stubs(:value).returns("whatever") + @mounter.expects(:mountcmd).returns("") - it "should parse the results of running the mount command with no arguments" do - Facter.stubs(:value).returns("whatever") - @mounter.expects(:mountcmd).returns("") - - @mounter.mounted? - end + @mounter.mounted? + end - it "should match ' on /private/var/automount<name>' if the operating system is Darwin" do - Facter.stubs(:value).with("operatingsystem").returns("Darwin") - @mounter.expects(:mountcmd).returns("/dev/whatever on /private/var/automount/\ndevfs on /dev") + it "should match ' on /private/var/automount<name>' if the operating system is Darwin" do + Facter.stubs(:value).with("operatingsystem").returns("Darwin") + @mounter.expects(:mountcmd).returns("/dev/whatever on /private/var/automount/\ndevfs on /dev") - @mounter.should be_mounted - end + @mounter.should be_mounted + end - it "should match ' on <name>' if the operating system is Darwin" do - Facter.stubs(:value).with("operatingsystem").returns("Darwin") - @mounter.expects(:mountcmd).returns("/dev/disk03 on / (local, journaled)\ndevfs on /dev") + it "should match ' on <name>' if the operating system is Darwin" do + Facter.stubs(:value).with("operatingsystem").returns("Darwin") + @mounter.expects(:mountcmd).returns("/dev/disk03 on / (local, journaled)\ndevfs on /dev") - @mounter.should be_mounted - end + @mounter.should be_mounted + end - it "should match '^<name> on' if the operating system is Solaris" do - Facter.stubs(:value).with("operatingsystem").returns("Solaris") - @mounter.expects(:mountcmd).returns("/ on /dev/dsk/whatever\n/var on /dev/dsk/other") + it "should match '^<name> on' if the operating system is Solaris" do + Facter.stubs(:value).with("operatingsystem").returns("Solaris") + @mounter.expects(:mountcmd).returns("/ on /dev/dsk/whatever\n/var on /dev/dsk/other") - @mounter.should be_mounted - end + @mounter.should be_mounted + end - it "should match ' on <name>' if the operating system is not Darwin or Solaris" do - Facter.stubs(:value).with("operatingsystem").returns("Debian") - @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on / and stuff\n/dev/other/disk on /var and stuff") + it "should match ' on <name>' if the operating system is not Darwin or Solaris" do + Facter.stubs(:value).with("operatingsystem").returns("Debian") + @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on / and stuff\n/dev/other/disk on /var and stuff") - @mounter.should be_mounted - end + @mounter.should be_mounted + end - it "should not be considered mounted if it did not match the mount output" do - Facter.stubs(:value).with("operatingsystem").returns("Debian") - @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on /something/else and stuff\n/dev/other/disk on /var and stuff") + it "should not be considered mounted if it did not match the mount output" do + Facter.stubs(:value).with("operatingsystem").returns("Debian") + @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on /something/else and stuff\n/dev/other/disk on /var and stuff") - @mounter.should_not be_mounted + @mounter.should_not be_mounted + end end end diff --git a/spec/unit/ral/provider/mount/parsed.rb b/spec/unit/ral/provider/mount/parsed.rb index 2fd15a543..b7f08a464 100755 --- a/spec/unit/ral/provider/mount/parsed.rb +++ b/spec/unit/ral/provider/mount/parsed.rb @@ -12,11 +12,6 @@ module ParsedMountTesting include PuppetTest::Support::Utils include PuppetTest::FileParsing - def setup - @mount_class = Puppet.type(:mount) - @provider_class = @mount_class.provider(:parsed) - end - def fake_fstab os = Facter['operatingsystem'] if os == "Solaris" @@ -79,100 +74,108 @@ end provider_class = Puppet::Type.type(:mount).provider(:parsed) describe provider_class do - include ParsedMountTesting + before :each do + @mount_class = Puppet.type(:mount) + @provider_class = @mount_class.provider(:parsed) + end - it "should be able to parse all of the example mount tabs" do - tab = fake_fstab - @provider = @provider_class - # LAK:FIXME Again, a relatively bad test, but I don't know how to rspec-ify this. - # I suppose this is more of an integration test? I dunno. - fakedataparse(tab) do - # Now just make we've got some mounts we know will be there - hashes = @provider_class.target_records(tab).find_all { |i| i.is_a? Hash } - (hashes.length > 0).should be_true - root = hashes.find { |i| i[:name] == "/" } + describe provider_class do + include ParsedMountTesting - proc { @provider_class.to_file(hashes) }.should_not raise_error - end - end + it "should be able to parse all of the example mount tabs" do + tab = fake_fstab + @provider = @provider_class + + # LAK:FIXME Again, a relatively bad test, but I don't know how to rspec-ify this. + # I suppose this is more of an integration test? I dunno. + fakedataparse(tab) do + # Now just make we've got some mounts we know will be there + hashes = @provider_class.target_records(tab).find_all { |i| i.is_a? Hash } + (hashes.length > 0).should be_true + root = hashes.find { |i| i[:name] == "/" } - # LAK:FIXME I can't mock Facter because this test happens at parse-time. - it "should default to /etc/vfstab on Solaris and /etc/fstab everywhere else" do - should = case Facter.value(:operatingsystem) - when "Solaris": "/etc/vfstab" - else - "/etc/fstab" + proc { @provider_class.to_file(hashes) }.should_not raise_error end - Puppet::Type.type(:mount).provider(:parsed).default_target.should == should + end + + # LAK:FIXME I can't mock Facter because this test happens at parse-time. + it "should default to /etc/vfstab on Solaris and /etc/fstab everywhere else" do + should = case Facter.value(:operatingsystem) + when "Solaris": "/etc/vfstab" + else + "/etc/fstab" + end + Puppet::Type.type(:mount).provider(:parsed).default_target.should == should + end end -end -describe provider_class, " when mounting an absent filesystem" do - include ParsedMountTesting + describe provider_class, " when mounting an absent filesystem" do + include ParsedMountTesting - # #730 - Make sure 'flush' is called when a mount is moving from absent to mounted - it "should flush the fstab to disk" do - mount = mkmount + # #730 - Make sure 'flush' is called when a mount is moving from absent to mounted + it "should flush the fstab to disk" do + mount = mkmount - # Mark the mount as absent - mount.property_hash[:ensure] = :absent + # Mark the mount as absent + mount.property_hash[:ensure] = :absent - mount.stubs(:mountcmd) # just so we don't actually try to mount anything + mount.stubs(:mountcmd) # just so we don't actually try to mount anything - mount.expects(:flush) - mount.mount + mount.expects(:flush) + mount.mount + end end -end -describe provider_class, " when modifying the filesystem tab" do - include ParsedMountTesting - before do - @mount = mkmount - @target = @provider_class.default_target + describe provider_class, " when modifying the filesystem tab" do + include ParsedMountTesting + before do + @mount = mkmount + @target = @provider_class.default_target - # Never write to disk, only to RAM. - @provider_class.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) - end + # Never write to disk, only to RAM. + @provider_class.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) + end - it "should write the mount to disk when :flush is called" do - @mount.flush + it "should write the mount to disk when :flush is called" do + @mount.flush - text = @provider_class.target_object(@provider_class.default_target).read - text.should == @mount.class.to_line(@mount.property_hash) + "\n" + text = @provider_class.target_object(@provider_class.default_target).read + text.should == @mount.class.to_line(@mount.property_hash) + "\n" + end end -end -describe provider_class, " when parsing information about the root filesystem" do - confine "Mount type not tested on Darwin" => Facter["operatingsystem"].value != "Darwin" - include ParsedMountTesting + describe provider_class, " when parsing information about the root filesystem" do + confine "Mount type not tested on Darwin" => Facter["operatingsystem"].value != "Darwin" + include ParsedMountTesting - before do - @mount = @mount_class.create :name => "/" - @provider = @mount.provider - end + before do + @mount = @mount_class.create :name => "/" + @provider = @mount.provider + end - it "should have a filesystem tab" do - FileTest.should be_exist(@provider_class.default_target) - end + it "should have a filesystem tab" do + FileTest.should be_exist(@provider_class.default_target) + end - it "should find the root filesystem" do - @provider_class.prefetch("/" => @mount) - @mount.provider.property_hash[:ensure].should == :present - end + it "should find the root filesystem" do + @provider_class.prefetch("/" => @mount) + @mount.provider.property_hash[:ensure].should == :present + end - it "should determine that the root fs is mounted" do - @provider_class.prefetch("/" => @mount) - @mount.provider.should be_mounted + it "should determine that the root fs is mounted" do + @provider_class.prefetch("/" => @mount) + @mount.provider.should be_mounted + end end -end -describe provider_class, " when mounting and unmounting" do - include ParsedMountTesting + describe provider_class, " when mounting and unmounting" do + include ParsedMountTesting - it "should call the 'mount' command to mount the filesystem" + it "should call the 'mount' command to mount the filesystem" - it "should call the 'unmount' command to unmount the filesystem" + it "should call the 'unmount' command to unmount the filesystem" - it "should specify the filesystem when remounting a filesystem" + it "should specify the filesystem when remounting a filesystem" + end end diff --git a/spec/unit/ral/type.rb b/spec/unit/ral/type.rb index 25f8cbaf1..5980167d6 100755 --- a/spec/unit/ral/type.rb +++ b/spec/unit/ral/type.rb @@ -11,8 +11,8 @@ describe Puppet::Type, " when in a configuration" do @catalog.add_resource @container @catalog.add_resource @one @catalog.add_resource @two - @catalog.add_edge! @container, @one - @catalog.add_edge! @container, @two + @catalog.add_edge @container, @one + @catalog.add_edge @container, @two end it "should have no parent if there is no in edge" do diff --git a/spec/unit/ral/types/exec.rb b/spec/unit/ral/type/exec.rb index 260804227..260804227 100755 --- a/spec/unit/ral/types/exec.rb +++ b/spec/unit/ral/type/exec.rb diff --git a/spec/unit/ral/type/file.rb b/spec/unit/ral/type/file.rb new file mode 100755 index 000000000..83546bef0 --- /dev/null +++ b/spec/unit/ral/type/file.rb @@ -0,0 +1,96 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Type.type(:file) do + before do + @path = Tempfile.new("puppetspec") + @path.close!() + @path = @path.path + @file = Puppet::Type::File.create(:name => @path) + end + + describe "when used with content and replace=>false" do + before do + @file[:content] = "foo" + @file[:replace] = false + end + + it "should be insync if the file exists and the content is different" do + File.open(@path, "w") do |f| f.puts "bar" end + @file.property(:content).insync?("bar").should be_true + end + + it "should be insync if the file exists and the content is right" do + File.open(@path, "w") do |f| f.puts "foo" end + @file.property(:content).insync?("foo").should be_true + end + + it "should not be insync if the file does not exist" do + @file.property(:content).insync?(:nil).should be_false + end + end + + describe "when retrieving remote files" do + before do + @filesource = Puppet::Type::File::FileSource.new + @filesource.server = mock 'fileserver' + + @file.stubs(:uri2obj).returns(@filesource) + + @file[:source] = "puppet:///test" + end + + it "should fail without writing if it cannot retrieve remote contents" do + # create the file, because we only get the problem when it starts + # out absent. + File.open(@file[:path], "w") { |f| f.puts "a" } + @file.expects(:write).never + + @filesource.server.stubs(:describe).returns("493\tfile\t100\t0\t{md5}3f5fef3bddbc4398c46a7bd7ba7b3af7") + @filesource.server.stubs(:retrieve).raises(RuntimeError) + @file.property(:source).retrieve + lambda { @file.property(:source).sync }.should raise_error(Puppet::Error) + end + end + + describe "when managing links" do + require 'puppettest/support/assertions' + include PuppetTest + require 'tempfile' + + before do + @basedir = tempfile + Dir.mkdir(@basedir) + @file = File.join(@basedir, "file") + @link = File.join(@basedir, "link") + + File.open(@file, "w", 0644) { |f| f.puts "yayness"; f.flush } + File.symlink(@file, @link) + + @resource = Puppet.type(:file).create( + :path => @link, + :mode => "755" + ) + @catalog = Puppet::Node::Catalog.new + @catalog.add_resource @resource + end + + after do + remove_tmp_files + end + + it "should default to managing the link" do + @catalog.apply + # I convert them to strings so they display correctly if there's an error. + ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0644 + end + + it "should be able to follow links" do + @resource[:links] = :follow + @catalog.apply + + ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0755 + end + end +end diff --git a/spec/unit/ral/types/interface.rb b/spec/unit/ral/type/interface.rb index e51465a0c..e51465a0c 100755 --- a/spec/unit/ral/types/interface.rb +++ b/spec/unit/ral/type/interface.rb diff --git a/spec/unit/ral/type/mount.rb b/spec/unit/ral/type/mount.rb new file mode 100755 index 000000000..b1a01749d --- /dev/null +++ b/spec/unit/ral/type/mount.rb @@ -0,0 +1,184 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/type/mount' + +describe Puppet::Type::Mount do + it "should have a :refreshable feature that requires the :remount method" do + Puppet::Type::Mount.provider_feature(:refreshable).methods.should == [:remount] + end + + it "should have no default value for :ensure" do + mount = Puppet::Type::Mount.create(:name => "yay") + mount.should(:ensure).should be_nil + end +end + +describe Puppet::Type::Mount, "when validating attributes" do + [:name, :remounts].each do |param| + it "should have a #{param} parameter" do + Puppet::Type::Mount.attrtype(param).should == :param + end + end + + [:ensure, :device, :blockdevice, :fstype, :options, :pass, :dump, :atboot, :target].each do |param| + it "should have a #{param} property" do + Puppet::Type::Mount.attrtype(param).should == :property + end + end +end + +describe Puppet::Type::Mount::Ensure, "when validating values" do + before do + @provider = stub 'provider', :class => Puppet::Type::Mount.defaultprovider, :clear => nil + Puppet::Type::Mount.defaultprovider.expects(:new).returns(@provider) + end + + it "should support :present as a value to :ensure" do + Puppet::Type::Mount.create(:name => "yay", :ensure => :present) + end + + it "should alias :unmounted to :present as a value to :ensure" do + mount = Puppet::Type::Mount.create(:name => "yay", :ensure => :unmounted) + mount.should(:ensure).should == :present + end + + it "should support :absent as a value to :ensure" do + Puppet::Type::Mount.create(:name => "yay", :ensure => :absent) + end + + it "should support :mounted as a value to :ensure" do + Puppet::Type::Mount.create(:name => "yay", :ensure => :mounted) + end +end + +describe Puppet::Type::Mount::Ensure do + before :each do + @provider = stub 'provider', :class => Puppet::Type::Mount.defaultprovider, :clear => nil, :satisfies? => true, :name => :mock + Puppet::Type::Mount.defaultprovider.stubs(:new).returns(@provider) + @mount = Puppet::Type::Mount.create(:name => "yay", :check => :ensure) + + @ensure = @mount.property(:ensure) + end + + def mount_stub(params) + Puppet::Type::Mount.validproperties.each do |prop| + unless params[prop] + params[prop] = :absent + @mount[prop] = :absent + end + end + + params.each do |param, value| + @provider.stubs(param).returns(value) + end + end + + describe Puppet::Type::Mount::Ensure, "when retrieving its current state" do + + it "should return the provider's value if it is :absent" do + @provider.expects(:ensure).returns(:absent) + @ensure.retrieve.should == :absent + end + + it "should return :mounted if the provider indicates it is mounted and the value is not :absent" do + @provider.expects(:ensure).returns(:present) + @provider.expects(:mounted?).returns(true) + @ensure.retrieve.should == :mounted + end + + it "should return :present if the provider indicates it is not mounted and the value is not :absent" do + @provider.expects(:ensure).returns(:present) + @provider.expects(:mounted?).returns(false) + @ensure.retrieve.should == :present + end + end + + describe Puppet::Type::Mount::Ensure, "when changing the host" do + + it "should destroy itself if it should be absent" do + @provider.stubs(:mounted?).returns(false) + @provider.expects(:destroy) + @ensure.should = :absent + @ensure.sync + end + + it "should unmount itself before destroying if it is mounted and should be absent" do + @provider.expects(:mounted?).returns(true) + @provider.expects(:unmount) + @provider.expects(:destroy) + @ensure.should = :absent + @ensure.sync + end + + it "should create itself if it is absent and should be present" do + @provider.stubs(:mounted?).returns(false) + @provider.expects(:create) + @ensure.should = :present + @ensure.sync + end + + it "should unmount itself if it is mounted and should be present" do + @provider.stubs(:mounted?).returns(true) + + # The interface here is just too much work to test right now. + @ensure.stubs(:syncothers) + @provider.expects(:unmount) + @ensure.should = :present + @ensure.sync + end + + it "should create and mount itself if it does not exist and should be mounted" do + @provider.stubs(:ensure).returns(:absent) + @provider.stubs(:mounted?).returns(false) + @provider.expects(:create) + @ensure.stubs(:syncothers) + @provider.expects(:mount) + @ensure.should = :mounted + @ensure.sync + end + + it "should mount itself if it is present and should be mounted" do + @provider.stubs(:ensure).returns(:present) + @provider.stubs(:mounted?).returns(false) + @ensure.stubs(:syncothers) + @provider.expects(:mount) + @ensure.should = :mounted + @ensure.sync + end + + it "should create but not mount itself if it is absent and mounted and should be mounted" do + @provider.stubs(:ensure).returns(:absent) + @provider.stubs(:mounted?).returns(true) + @ensure.stubs(:syncothers) + @provider.expects(:create) + @ensure.should = :mounted + @ensure.sync + end + end + + describe Puppet::Type::Mount, "when responding to events" do + + it "should remount if it is currently mounted" do + @provider.expects(:mounted?).returns(true) + @provider.expects(:remount) + + @mount.refresh + end + + it "should not remount if it is not currently mounted" do + @provider.expects(:mounted?).returns(false) + @provider.expects(:remount).never + + @mount.refresh + end + + it "should not remount swap filesystems" do + @mount[:fstype] = "swap" + @provider.expects(:remount).never + + @mount.refresh + end + end +end diff --git a/spec/unit/ral/types/nagios.rb b/spec/unit/ral/type/nagios.rb index 8aca7d401..8aca7d401 100755 --- a/spec/unit/ral/types/nagios.rb +++ b/spec/unit/ral/type/nagios.rb diff --git a/spec/unit/ral/type/noop_metaparam.rb b/spec/unit/ral/type/noop_metaparam.rb new file mode 100755 index 000000000..0cbed3714 --- /dev/null +++ b/spec/unit/ral/type/noop_metaparam.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/metatype/metaparams' + +describe Puppet::Type.type(:file).attrclass(:noop) do + before do + @file = Puppet::Type.newfile :path => "/what/ever" + end + + after { Puppet::Type::File.clear } + + it "should accept true as a value" do + lambda { @file[:noop] = true }.should_not raise_error + end + + it "should accept false as a value" do + lambda { @file[:noop] = false }.should_not raise_error + end + + describe "when set on a resource" do + it "should default to the :noop setting" do + Puppet.settings.expects(:value).with(:noop).returns "myval" + @file.noop.should == "myval" + end + + it "should prefer true values from the attribute" do + @file[:noop] = true + @file.noop.should be_true + end + + it "should prefer false values from the attribute" do + @file[:noop] = false + @file.noop.should be_false + end + end +end diff --git a/spec/unit/ral/types/package.rb b/spec/unit/ral/type/package.rb index e9e3b9e4e..8cc11cc2c 100755 --- a/spec/unit/ral/types/package.rb +++ b/spec/unit/ral/type/package.rb @@ -92,13 +92,19 @@ describe Puppet::Type::Package, "when validating attribute values" do proc { Puppet::Type::Package.create(:name => "yay", :ensure => "1.0") }.should raise_error(Puppet::Error) end - it "should only accept files and URLs as values to :source" do - proc { Puppet::Type::Package.create(:name => "yay", :source => "stuff") }.should raise_error(Puppet::Error) + it "should accept any string as an argument to :source" do + proc { Puppet::Type::Package.create(:name => "yay", :source => "stuff") }.should_not raise_error(Puppet::Error) end end module PackageEvaluationTesting - def setup + def setprops(properties) + @provider.stubs(:properties).returns(properties) + end +end + +describe Puppet::Type::Package do + before :each do @provider = stub 'provider', :class => Puppet::Type::Package.defaultprovider, :clear => nil, :satisfies? => true, :name => :mock Puppet::Type::Package.defaultprovider.stubs(:new).returns(@provider) @package = Puppet::Type::Package.create(:name => "yay") @@ -107,129 +113,125 @@ module PackageEvaluationTesting @catalog.add_resource(@package) end - def setprops(properties) - @provider.stubs(:properties).returns(properties) - end -end - -describe Puppet::Type::Package, "when it should be purged" do - include PackageEvaluationTesting + describe Puppet::Type::Package, "when it should be purged" do + include PackageEvaluationTesting - before { @package[:ensure] = :purged } + before { @package[:ensure] = :purged } - it "should do nothing if it is :purged" do - @provider.expects(:properties).returns(:ensure => :purged) - @catalog.apply - end - - [:absent, :installed, :present, :latest].each do |state| - it "should purge if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:purge) + it "should do nothing if it is :purged" do + @provider.expects(:properties).returns(:ensure => :purged) @catalog.apply end + + [:absent, :installed, :present, :latest].each do |state| + it "should purge if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:purge) + @catalog.apply + end + end end -end -describe Puppet::Type::Package, "when it should be absent" do - include PackageEvaluationTesting + describe Puppet::Type::Package, "when it should be absent" do + include PackageEvaluationTesting - before { @package[:ensure] = :absent } + before { @package[:ensure] = :absent } - [:purged, :absent].each do |state| - it "should do nothing if it is #{state.to_s}" do - @provider.expects(:properties).returns(:ensure => state) - @catalog.apply + [:purged, :absent].each do |state| + it "should do nothing if it is #{state.to_s}" do + @provider.expects(:properties).returns(:ensure => state) + @catalog.apply + end end - end - [:installed, :present, :latest].each do |state| - it "should uninstall if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:uninstall) - @catalog.apply + [:installed, :present, :latest].each do |state| + it "should uninstall if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:uninstall) + @catalog.apply + end end end -end -describe Puppet::Type::Package, "when it should be present" do - include PackageEvaluationTesting + describe Puppet::Type::Package, "when it should be present" do + include PackageEvaluationTesting - before { @package[:ensure] = :present } + before { @package[:ensure] = :present } - [:present, :latest, "1.0"].each do |state| - it "should do nothing if it is #{state.to_s}" do - @provider.expects(:properties).returns(:ensure => state) - @catalog.apply + [:present, :latest, "1.0"].each do |state| + it "should do nothing if it is #{state.to_s}" do + @provider.expects(:properties).returns(:ensure => state) + @catalog.apply + end end - end - [:purged, :absent].each do |state| - it "should install if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:install) - @catalog.apply + [:purged, :absent].each do |state| + it "should install if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:install) + @catalog.apply + end end end -end -describe Puppet::Type::Package, "when it should be latest" do - include PackageEvaluationTesting + describe Puppet::Type::Package, "when it should be latest" do + include PackageEvaluationTesting - before { @package[:ensure] = :latest } + before { @package[:ensure] = :latest } - [:purged, :absent].each do |state| - it "should upgrade if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) + [:purged, :absent].each do |state| + it "should upgrade if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:update) + @catalog.apply + end + end + + it "should upgrade if the current version is not equal to the latest version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.stubs(:latest).returns("2.0") @provider.expects(:update) @catalog.apply end - end - it "should upgrade if the current version is not equal to the latest version" do - @provider.stubs(:properties).returns(:ensure => "1.0") - @provider.stubs(:latest).returns("2.0") - @provider.expects(:update) - @catalog.apply - end + it "should do nothing if it is equal to the latest version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.stubs(:latest).returns("1.0") + @provider.expects(:update).never + @catalog.apply + end - it "should do nothing if it is equal to the latest version" do - @provider.stubs(:properties).returns(:ensure => "1.0") - @provider.stubs(:latest).returns("1.0") - @provider.expects(:update).never - @catalog.apply + it "should do nothing if the provider returns :present as the latest version" do + @provider.stubs(:properties).returns(:ensure => :present) + @provider.stubs(:latest).returns("1.0") + @provider.expects(:update).never + @catalog.apply + end end - it "should do nothing if the provider returns :present as the latest version" do - @provider.stubs(:properties).returns(:ensure => :present) - @provider.stubs(:latest).returns("1.0") - @provider.expects(:update).never - @catalog.apply - end -end + describe Puppet::Type::Package, "when it should be a specific version" do + include PackageEvaluationTesting -describe Puppet::Type::Package, "when it should be a specific version" do - include PackageEvaluationTesting + before { @package[:ensure] = "1.0" } - before { @package[:ensure] = "1.0" } + [:purged, :absent].each do |state| + it "should install if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:install) + @catalog.apply + end + end - [:purged, :absent].each do |state| - it "should install if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:install) + it "should do nothing if the current version is equal to the desired version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.expects(:install).never @catalog.apply end - end - it "should do nothing if the current version is equal to the desired version" do - @provider.stubs(:properties).returns(:ensure => "1.0") - @provider.expects(:install).never - @catalog.apply - end - - it "should install if the current version is not equal to the specified version" do - @provider.stubs(:properties).returns(:ensure => "2.0") - @provider.expects(:install) - @catalog.apply + it "should install if the current version is not equal to the specified version" do + @provider.stubs(:properties).returns(:ensure => "2.0") + @provider.expects(:install) + @catalog.apply + end end end diff --git a/spec/unit/ral/type/schedule.rb b/spec/unit/ral/type/schedule.rb new file mode 100755 index 000000000..e53138c5e --- /dev/null +++ b/spec/unit/ral/type/schedule.rb @@ -0,0 +1,335 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/type/schedule' + +module ScheduleTesting + + def format(time) + time.strftime("%H:%M:%S") + end + + def diff(unit, incr, method, count) + diff = Time.now.to_i.send(method, incr * count) + Time.at(diff) + end + + def month(method, count) + diff(:hour, 3600 * 24 * 30, method, count) + end + + def week(method, count) + diff(:hour, 3600 * 24 * 7, method, count) + end + + def day(method, count) + diff(:hour, 3600 * 24, method, count) + end + + def hour(method, count) + diff(:hour, 3600, method, count) + end + + def min(method, count) + diff(:min, 60, method, count) + end + + def sec(method, count) + diff(:sec, 1, method, count) + end + +end + +describe Puppet::Type::Schedule do + before :each do + Puppet.settings.stubs(:value).with(:ignoreschedules).returns(false) + + @schedule = Puppet::Type::Schedule.create(:name => "testing") + end + + describe Puppet::Type::Schedule do + include ScheduleTesting + + it "should default to :distance for period-matching" do + @schedule[:periodmatch].should == :distance + end + + it "should default to a :repeat of 1" do + @schedule[:repeat].should == 1 + end + + it "should never match when the period is :never" do + @schedule[:period] = :never + @schedule.match?.should be_false + end + end + + describe Puppet::Type::Schedule, "when producing default schedules" do + include ScheduleTesting + + %w{hourly daily weekly monthly never}.each do |period| + period = period.to_sym + it "should produce a #{period} schedule with the period set appropriately" do + schedules = Puppet::Type::Schedule.mkdefaultschedules + schedules.find { |s| s[:name] == period.to_s and s[:period] == period }.should be_instance_of(Puppet::Type::Schedule) + end + end + + it "should produce a schedule named puppet with a period of hourly and a repeat of 2" do + schedules = Puppet::Type::Schedule.mkdefaultschedules + schedules.find { |s| + s[:name] == "puppet" and s[:period] == :hourly and s[:repeat] == 2 + }.should be_instance_of(Puppet::Type::Schedule) + end + end + + describe Puppet::Type::Schedule, "when matching ranges" do + include ScheduleTesting + + it "should match when the start time is before the current time and the end time is after the current time" do + @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now + 10)] + @schedule.match?.should be_true + end + + it "should not match when the start time is after the current time" do + @schedule[:range] = "%s - %s" % [format(Time.now + 5), format(Time.now + 10)] + @schedule.match?.should be_false + end + + it "should not match when the end time is previous to the current time" do + @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now - 5)] + @schedule.match?.should be_false + end + end + + describe Puppet::Type::Schedule, "when matching hourly by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :hourly + @schedule[:periodmatch] = :distance + end + + it "should match an hour ago" do + @schedule.match?(hour("-", 1)).should be_true + end + + it "should not match now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match 59 minutes ago" do + @schedule.match?(min("-", 59)).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching daily by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :daily + @schedule[:periodmatch] = :distance + end + + it "should match when the previous time was one day ago" do + @schedule.match?(day("-", 1)).should be_true + end + + it "should not match when the previous time is now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match when the previous time was 23 hours ago" do + @schedule.match?(hour("-", 23)).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching weekly by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :weekly + @schedule[:periodmatch] = :distance + end + + it "should match seven days ago" do + @schedule.match?(day("-", 7)).should be_true + end + + it "should not match now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match six days ago" do + @schedule.match?(day("-", 6)).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching monthly by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :monthly + @schedule[:periodmatch] = :distance + end + + it "should match 32 days ago" do + @schedule.match?(day("-", 32)).should be_true + end + + it "should not match now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match 27 days ago" do + @schedule.match?(day("-", 27)).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching hourly by number" do + include ScheduleTesting + + before do + @schedule[:period] = :hourly + @schedule[:periodmatch] = :number + end + + it "should match if the times are one minute apart and the current minute is 0" do + current = Time.now + + # Subtract an hour, reset the minute to zero, then add 59 minutes, so we're the previous hour plus 59 minutes. + previous = (current - 3600 - (current.min * 60) + (59 * 60)) + + # Now set the "current" time to the zero minute of the current hour. + now = (current - (current.min * 60)) + Time.stubs(:now).returns(now) + @schedule.match?(previous).should be_true + end + + it "should not match if the times are 58 minutes apart and the current minute is 59" do + current = Time.now + + # reset the minute to zero + previous = current - (current.min * 60) + + # Now set the "current" time to the 59th minute of the current hour. + now = (current - (current.min * 60) + (59 * 60)) + Time.stubs(:now).returns(now) + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching daily by number" do + include ScheduleTesting + + before do + @schedule[:period] = :daily + @schedule[:periodmatch] = :number + end + + it "should match if the times are one minute apart and the current minute and hour are 0" do + zero = Time.now + + # Reset the current time to X:00:00 + current = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec + + # Now set the previous time to one minute before that + previous = current - 60 + + Time.stubs(:now).returns(current) + @schedule.match?(previous).should be_true + end + + it "should not match if the times are 23 hours and 58 minutes apart and the current hour is 23 and the current minute is 59" do + zero = Time.now + + # Reset the previous time to 00:00:00 + previous = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec + + # Set the current time to 23:59 + now = previous + (23 * 3600) + (59 * 60) + + Time.stubs(:now).returns(now) + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching weekly by number" do + include ScheduleTesting + + before do + @schedule[:period] = :weekly + @schedule[:periodmatch] = :number + end + + it "should match if the previous time is prior to the most recent Sunday" do + now = Time.now + + # Subtract the number days we've progressed into the week, plus one because we're zero-indexed. + previous = now - (3600 * 24 * (now.wday + 1)) + + @schedule.match?(previous).should be_true + end + + it "should not match if the previous time is after the most recent Saturday" do + now = Time.now + + # Subtract the number days we've progressed into the week + previous = now - (3600 * 24 * now.wday) + + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching monthly by number" do + include ScheduleTesting + + before do + @schedule[:period] = :monthly + @schedule[:periodmatch] = :number + end + + it "should match when the previous time is prior to the first day of this month" do + now = Time.now + + # Subtract the number days we've progressed into the month + previous = now - (3600 * 24 * now.day) + + @schedule.match?(previous).should be_true + end + + it "should not match when the previous time is after the last day of last month" do + now = Time.now + + # Subtract the number days we've progressed into the month, minus one + previous = now - (3600 * 24 * (now.day - 1)) + + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching with a repeat greater than one" do + include ScheduleTesting + + before do + @schedule[:period] = :daily + @schedule[:repeat] = 2 + end + + it "should fail if the periodmatch is 'number'" do + @schedule[:periodmatch] = :number + proc { @schedule[:repeat] = 2 }.should raise_error(Puppet::Error) + end + + it "should match if the previous run was further away than the distance divided by the repeat" do + previous = Time.now - (3600 * 13) + @schedule.match?(previous).should be_true + end + + it "should not match if the previous run was closer than the distance divided by the repeat" do + previous = Time.now - (3600 * 11) + @schedule.match?(previous).should be_false + end + end +end diff --git a/spec/unit/ral/types/service.rb b/spec/unit/ral/type/service.rb index 3944f146c..17cb2105d 100755 --- a/spec/unit/ral/types/service.rb +++ b/spec/unit/ral/type/service.rb @@ -15,7 +15,7 @@ describe Puppet::Type::Service do end describe Puppet::Type::Service, "when validating attributes" do - [:name, :binary, :hasstatus, :path, :pattern, :start, :restart, :stop, :status, :hasrestart].each do |param| + [:name, :binary, :hasstatus, :path, :pattern, :start, :restart, :stop, :status, :hasrestart, :control].each do |param| it "should have a #{param} parameter" do Puppet::Type::Service.attrtype(param).should == :param end @@ -30,7 +30,7 @@ end describe Puppet::Type::Service, "when validating attribute values" do before do - @provider = stub 'provider', :class => Puppet::Type::Service.defaultprovider, :clear => nil + @provider = stub 'provider', :class => Puppet::Type::Service.defaultprovider, :clear => nil, :controllable? => false Puppet::Type::Service.defaultprovider.stubs(:new).returns(@provider) end @@ -130,15 +130,22 @@ describe Puppet::Type::Service, "when setting default attribute values" do svc[:path].should == ["testing"] end - it "should default to the binary for the pattern if one is provided" do + it "should default 'pattern' to the binary if one is provided" do svc = Puppet::Type::Service.create(:name => "other", :binary => "/some/binary") svc[:pattern].should == "/some/binary" end - it "should default to the name for the pattern if no pattern is provided" do + it "should default 'pattern' to the name if no pattern is provided" do svc = Puppet::Type::Service.create(:name => "other") svc[:pattern].should == "other" end + + it "should default 'control' to the upcased service name with periods replaced by underscores if the provider supports the 'controllable' feature" do + provider = stub 'provider', :controllable? => true, :class => Puppet::Type::Service.defaultprovider, :clear => nil + Puppet::Type::Service.defaultprovider.stubs(:new).returns(provider) + svc = Puppet::Type::Service.create(:name => "nfs.client") + svc[:control].should == "NFS_CLIENT_START" + end end describe Puppet::Type::Service, "when retrieving the host's current state" do diff --git a/spec/unit/ral/types/user.rb b/spec/unit/ral/type/user.rb index 4e43a8ceb..4e43a8ceb 100755 --- a/spec/unit/ral/types/user.rb +++ b/spec/unit/ral/type/user.rb diff --git a/spec/unit/ral/types/file.rb b/spec/unit/ral/types/file.rb deleted file mode 100755 index 5ade79012..000000000 --- a/spec/unit/ral/types/file.rb +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/type/pfile' - -describe Puppet::Type::File, " when used with replace=>false and content" do - before do - @path = Tempfile.new("puppetspec") - @path.close!() - @path = @path.path - @file = Puppet::Type::File.create( { :name => @path, :content => "foo", :replace => :false } ) - end - - it "should be insync if the file exists and the content is different" do - File.open(@path, "w") do |f| f.puts "bar" end - @file.property(:content).insync?("bar").should be_true - end - - it "should be insync if the file exists and the content is right" do - File.open(@path, "w") do |f| f.puts "foo" end - @file.property(:content).insync?("foo").should be_true - end - - it "should not be insync if the file doesnot exist" do - @file.property(:content).insync?(:nil).should be_false - end -end diff --git a/spec/unit/ral/types/mount.rb b/spec/unit/ral/types/mount.rb deleted file mode 100755 index 5965908cb..000000000 --- a/spec/unit/ral/types/mount.rb +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/type/mount' - -describe Puppet::Type::Mount do - it "should have a :refreshable feature that requires the :remount method" do - Puppet::Type::Mount.provider_feature(:refreshable).methods.should == [:remount] - end - - it "should have no default value for :ensure" do - mount = Puppet::Type::Mount.create(:name => "yay") - mount.should(:ensure).should be_nil - end -end - -describe Puppet::Type::Mount, "when validating attributes" do - [:name, :remounts].each do |param| - it "should have a #{param} parameter" do - Puppet::Type::Mount.attrtype(param).should == :param - end - end - - [:ensure, :device, :blockdevice, :fstype, :options, :pass, :dump, :atboot, :target].each do |param| - it "should have a #{param} property" do - Puppet::Type::Mount.attrtype(param).should == :property - end - end -end - -describe Puppet::Type::Mount::Ensure, "when validating values" do - before do - @provider = stub 'provider', :class => Puppet::Type::Mount.defaultprovider, :clear => nil - Puppet::Type::Mount.defaultprovider.expects(:new).returns(@provider) - end - - it "should support :present as a value to :ensure" do - Puppet::Type::Mount.create(:name => "yay", :ensure => :present) - end - - it "should alias :unmounted to :present as a value to :ensure" do - mount = Puppet::Type::Mount.create(:name => "yay", :ensure => :unmounted) - mount.should(:ensure).should == :present - end - - it "should support :absent as a value to :ensure" do - Puppet::Type::Mount.create(:name => "yay", :ensure => :absent) - end - - it "should support :mounted as a value to :ensure" do - Puppet::Type::Mount.create(:name => "yay", :ensure => :mounted) - end -end - -module MountEvaluationTesting - def setup - @provider = stub 'provider', :class => Puppet::Type::Mount.defaultprovider, :clear => nil, :satisfies? => true, :name => :mock - Puppet::Type::Mount.defaultprovider.stubs(:new).returns(@provider) - @mount = Puppet::Type::Mount.create(:name => "yay", :check => :ensure) - - @ensure = @mount.property(:ensure) - end - - def mount_stub(params) - Puppet::Type::Mount.validproperties.each do |prop| - unless params[prop] - params[prop] = :absent - @mount[prop] = :absent - end - end - - params.each do |param, value| - @provider.stubs(param).returns(value) - end - end -end - -describe Puppet::Type::Mount::Ensure, "when retrieving its current state" do - include MountEvaluationTesting - - it "should return the provider's value if it is :absent" do - @provider.expects(:ensure).returns(:absent) - @ensure.retrieve.should == :absent - end - - it "should return :mounted if the provider indicates it is mounted and the value is not :absent" do - @provider.expects(:ensure).returns(:present) - @provider.expects(:mounted?).returns(true) - @ensure.retrieve.should == :mounted - end - - it "should return :present if the provider indicates it is not mounted and the value is not :absent" do - @provider.expects(:ensure).returns(:present) - @provider.expects(:mounted?).returns(false) - @ensure.retrieve.should == :present - end -end - -describe Puppet::Type::Mount::Ensure, "when changing the host" do - include MountEvaluationTesting - - it "should destroy itself if it should be absent" do - @provider.stubs(:mounted?).returns(false) - @provider.expects(:destroy) - @ensure.should = :absent - @ensure.sync - end - - it "should unmount itself before destroying if it is mounted and should be absent" do - @provider.expects(:mounted?).returns(true) - @provider.expects(:unmount) - @provider.expects(:destroy) - @ensure.should = :absent - @ensure.sync - end - - it "should create itself if it is absent and should be present" do - @provider.stubs(:mounted?).returns(false) - @provider.expects(:create) - @ensure.should = :present - @ensure.sync - end - - it "should unmount itself if it is mounted and should be present" do - @provider.stubs(:mounted?).returns(true) - - # The interface here is just too much work to test right now. - @ensure.stubs(:syncothers) - @provider.expects(:unmount) - @ensure.should = :present - @ensure.sync - end - - it "should create and mount itself if it does not exist and should be mounted" do - @provider.stubs(:ensure).returns(:absent) - @provider.stubs(:mounted?).returns(false) - @provider.expects(:create) - @ensure.stubs(:syncothers) - @provider.expects(:mount) - @ensure.should = :mounted - @ensure.sync - end - - it "should mount itself if it is present and should be mounted" do - @provider.stubs(:ensure).returns(:present) - @provider.stubs(:mounted?).returns(false) - @ensure.stubs(:syncothers) - @provider.expects(:mount) - @ensure.should = :mounted - @ensure.sync - end - - it "should create but not mount itself if it is absent and mounted and should be mounted" do - @provider.stubs(:ensure).returns(:absent) - @provider.stubs(:mounted?).returns(true) - @ensure.stubs(:syncothers) - @provider.expects(:create) - @ensure.should = :mounted - @ensure.sync - end -end - -describe Puppet::Type::Mount, "when responding to events" do - include MountEvaluationTesting - - it "should remount if it is currently mounted" do - @provider.expects(:mounted?).returns(true) - @provider.expects(:remount) - - @mount.refresh - end - - it "should not remount if it is not currently mounted" do - @provider.expects(:mounted?).returns(false) - @provider.expects(:remount).never - - @mount.refresh - end - - it "should not remount swap filesystems" do - @mount[:fstype] = "swap" - @provider.expects(:remount).never - - @mount.refresh - end -end diff --git a/spec/unit/ral/types/schedule.rb b/spec/unit/ral/types/schedule.rb deleted file mode 100755 index 856a73186..000000000 --- a/spec/unit/ral/types/schedule.rb +++ /dev/null @@ -1,370 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/type/schedule' - -module ScheduleTesting - def setup - Puppet.settings.stubs(:value).with(:ignoreschedules).returns(false) - - @schedule = Puppet::Type::Schedule.create(:name => "testing") - end - - def format(time) - time.strftime("%H:%M:%S") - end - - def diff(unit, incr, method, count) - diff = Time.now.to_i.send(method, incr * count) - Time.at(diff) - end - - def month(method, count) - diff(:hour, 3600 * 24 * 30, method, count) - end - - def week(method, count) - diff(:hour, 3600 * 24 * 7, method, count) - end - - def day(method, count) - diff(:hour, 3600 * 24, method, count) - end - - def hour(method, count) - diff(:hour, 3600, method, count) - end - - def min(method, count) - diff(:min, 60, method, count) - end - - def sec(method, count) - diff(:sec, 1, method, count) - end -end - -describe Puppet::Type::Schedule do - include ScheduleTesting - - it "should default to :distance for period-matching" do - @schedule[:periodmatch].should == :distance - end - - it "should default to a :repeat of 1" do - @schedule[:repeat].should == 1 - end - - it "should never match when the period is :never" do - @schedule[:period] = :never - @schedule.match?.should be_false - end -end - -describe Puppet::Type::Schedule, "when producing default schedules" do - include ScheduleTesting - - %w{hourly daily weekly monthly never}.each do |period| - period = period.to_sym - it "should produce a #{period} schedule with the period set appropriately" do - schedules = Puppet::Type::Schedule.mkdefaultschedules - schedules.find { |s| s[:name] == period.to_s and s[:period] == period }.should be_instance_of(Puppet::Type::Schedule) - end - end - - it "should produce a schedule named puppet with a period of hourly and a repeat of 2" do - schedules = Puppet::Type::Schedule.mkdefaultschedules - schedules.find { |s| - s[:name] == "puppet" and s[:period] == :hourly and s[:repeat] == 2 - }.should be_instance_of(Puppet::Type::Schedule) - end -end - -describe Puppet::Type::Schedule, "when matching ranges" do - include ScheduleTesting - - it "should match when the start time is before the current time and the end time is after the current time" do - @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now + 10)] - @schedule.match?.should be_true - end - - it "should not match when the start time is after the current time" do - @schedule[:range] = "%s - %s" % [format(Time.now + 5), format(Time.now + 10)] - @schedule.match?.should be_false - end - - it "should not match when the end time is previous to the current time" do - @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now - 5)] - @schedule.match?.should be_false - end -end - -describe Puppet::Type::Schedule, "when matching hourly by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :hourly - @schedule[:periodmatch] = :distance - end - - it "should match an hour ago" do - @schedule.match?(hour("-", 1)).should be_true - end - - it "should not match now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match 59 minutes ago" do - @schedule.match?(min("-", 59)).should be_false - end -end - -describe Puppet::Type::Schedule, "when matching daily by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :daily - @schedule[:periodmatch] = :distance - end - - it "should match when the previous time was one day ago" do - @schedule.match?(day("-", 1)).should be_true - end - - it "should not match when the previous time is now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match when the previous time was 23 hours ago" do - @schedule.match?(hour("-", 23)).should be_false - end -end - -describe Puppet::Type::Schedule, "when matching weekly by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :weekly - @schedule[:periodmatch] = :distance - end - - it "should match seven days ago" do - @schedule.match?(day("-", 7)).should be_true - end - - it "should not match now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match six days ago" do - @schedule.match?(day("-", 6)).should be_false - end -end - -describe Puppet::Type::Schedule, "when matching monthly by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :monthly - @schedule[:periodmatch] = :distance - end - - it "should match 32 days ago" do - @schedule.match?(day("-", 32)).should be_true - end - - it "should not match now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match 27 days ago" do - @schedule.match?(day("-", 27)).should be_false - end -end - -describe Puppet::Type::Schedule, "when matching hourly by number" do - include ScheduleTesting - - before do - @schedule[:period] = :hourly - @schedule[:periodmatch] = :number - end - - it "should match if the times are one minute apart and the current minute is 0" do - current = Time.now - - # Subtract an hour, reset the minute to zero, then add 59 minutes, so we're the previous hour plus 59 minutes. - previous = (current - 3600 - (current.min * 60) + (59 * 60)) - - # Now set the "current" time to the zero minute of the current hour. - now = (current - (current.min * 60)) - Time.stubs(:now).returns(now) - @schedule.match?(previous).should be_true - end - - it "should not match if the times are 58 minutes apart and the current minute is 59" do - current = Time.now - - # reset the minute to zero - previous = current - (current.min * 60) - - # Now set the "current" time to the 59th minute of the current hour. - now = (current - (current.min * 60) + (59 * 60)) - Time.stubs(:now).returns(now) - @schedule.match?(previous).should be_false - end -end - -describe Puppet::Type::Schedule, "when matching daily by number" do - include ScheduleTesting - - before do - @schedule[:period] = :daily - @schedule[:periodmatch] = :number - end - - it "should match if the times are one minute apart and the current minute and hour are 0" do - zero = Time.now - - # Reset the current time to X:00:00 - current = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec - - # Now set the previous time to one minute before that - previous = current - 60 - - Time.stubs(:now).returns(current) - @schedule.match?(previous).should be_true - end - - it "should not match if the times are 23 hours and 58 minutes apart and the current hour is 23 and the current minute is 59" do - zero = Time.now - - # Reset the previous time to 00:00:00 - previous = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec - - # Set the current time to 23:59 - now = previous + (23 * 3600) + (59 * 60) - - Time.stubs(:now).returns(now) - @schedule.match?(previous).should be_false - end -end - -describe Puppet::Type::Schedule, "when matching weekly by number" do - include ScheduleTesting - - before do - @schedule[:period] = :weekly - @schedule[:periodmatch] = :number - end - - it "should match if the previous time is prior to the most recent Sunday" do - now = Time.now - - # Subtract the number days we've progressed into the week, plus one because we're zero-indexed. - previous = now - (3600 * 24 * (now.wday + 1)) - - @schedule.match?(previous).should be_true - end - - it "should not match if the previous time is after the most recent Saturday" do - now = Time.now - - # Subtract the number days we've progressed into the week - previous = now - (3600 * 24 * now.wday) - - @schedule.match?(previous).should be_false - end -end - -describe Puppet::Type::Schedule, "when matching monthly by number" do - include ScheduleTesting - - before do - @schedule[:period] = :monthly - @schedule[:periodmatch] = :number - end - - it "should match when the previous time is prior to the first day of this month" do - now = Time.now - - # Subtract the number days we've progressed into the month - previous = now - (3600 * 24 * now.day) - - @schedule.match?(previous).should be_true - end - - it "should not match when the previous time is after the last day of last month" do - now = Time.now - - # Subtract the number days we've progressed into the month, minus one - previous = now - (3600 * 24 * (now.day - 1)) - - @schedule.match?(previous).should be_false - end -end - -describe Puppet::Type::Schedule, "when matching with a repeat greater than one" do - include ScheduleTesting - - before do - @schedule[:period] = :daily - @schedule[:repeat] = 2 - end - - it "should fail if the periodmatch is 'number'" do - @schedule[:periodmatch] = :number - proc { @schedule[:repeat] = 2 }.should raise_error(Puppet::Error) - end - - it "should match if the previous run was further away than the distance divided by the repeat" do - previous = Time.now - (3600 * 13) - @schedule.match?(previous).should be_true - end - - it "should not match if the previous run was closer than the distance divided by the repeat" do - previous = Time.now - (3600 * 11) - @schedule.match?(previous).should be_false - end -end - -module OldTesting - def mksched - @stype.create(:name => "testsched") - end - - def test_period_with_repeat - previous = @now - - s = mksched - s[:period] = :hourly - - assert_nothing_raised("Was not able to set periodmatch") { - s[:periodmatch] = :number - } - assert_raise(Puppet::Error) { - s[:repeat] = 2 - } - assert_nothing_raised("Was not able to reset periodmatch") { - s[:periodmatch] = :distance - } - - assert(! s.match?(min("-", 40)), "matched minus 40 minutes") - - assert_nothing_raised("Was not able to set period") { - s[:repeat] = 2 - } - - assert(! s.match?(min("-", 20)), "matched minus 20 minutes with half-hourly") - assert(s.match?(min("-", 40)), "Did not match minus 40 with half-hourly") - - assert_nothing_raised("Was not able to set period") { - s[:repeat] = 3 - } - - assert(! s.match?(min("-", 15)), "matched minus 15 minutes with half-hourly") - assert(s.match?(min("-", 25)), "Did not match minus 25 with half-hourly") - end -end diff --git a/spec/unit/resource_reference.rb b/spec/unit/resource_reference.rb index e629d0b82..ee71a5077 100755 --- a/spec/unit/resource_reference.rb +++ b/spec/unit/resource_reference.rb @@ -40,6 +40,12 @@ describe Puppet::ResourceReference do ref.type.should == "Foo::Bar" ref.title.should == "yay" end + + it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains nested square brackets" do + ref = Puppet::ResourceReference.new(nil, "foo::bar[baz[yay]]") + ref.type.should == "Foo::Bar" + ref.title.should =="baz[yay]" + end end describe Puppet::ResourceReference, "when resolving resources with a catalog" do diff --git a/spec/unit/simple_graph.rb b/spec/unit/simple_graph.rb index 061a07458..e1e42e40f 100755 --- a/spec/unit/simple_graph.rb +++ b/spec/unit/simple_graph.rb @@ -9,8 +9,8 @@ require 'puppet/simple_graph' describe Puppet::SimpleGraph do it "should return the number of its vertices as its length" do @graph = Puppet::SimpleGraph.new - @graph.add_vertex!("one") - @graph.add_vertex!("two") + @graph.add_vertex("one") + @graph.add_vertex("two") @graph.size.should == 2 end @@ -20,13 +20,13 @@ describe Puppet::SimpleGraph do it "should provide a method for reversing the graph" do @graph = Puppet::SimpleGraph.new - @graph.add_edge!(:one, :two) + @graph.add_edge(:one, :two) @graph.reversal.edge?(:two, :one).should be_true end it "should be able to produce a dot graph" do @graph = Puppet::SimpleGraph.new - @graph.add_edge!(:one, :two) + @graph.add_edge(:one, :two) proc { @graph.to_dot_graph }.should_not raise_error end @@ -38,17 +38,17 @@ describe Puppet::SimpleGraph, " when managing vertices" do end it "should provide a method to add a vertex" do - @graph.add_vertex!(:test) + @graph.add_vertex(:test) @graph.vertex?(:test).should be_true end it "should ignore already-present vertices when asked to add a vertex" do - @graph.add_vertex!(:test) - proc { @graph.add_vertex!(:test) }.should_not raise_error + @graph.add_vertex(:test) + proc { @graph.add_vertex(:test) }.should_not raise_error end it "should return true when asked if a vertex is present" do - @graph.add_vertex!(:test) + @graph.add_vertex(:test) @graph.vertex?(:test).should be_true end @@ -57,15 +57,15 @@ describe Puppet::SimpleGraph, " when managing vertices" do end it "should return all set vertices when asked" do - @graph.add_vertex!(:one) - @graph.add_vertex!(:two) + @graph.add_vertex(:one) + @graph.add_vertex(:two) @graph.vertices.length.should == 2 @graph.vertices.should include(:one) @graph.vertices.should include(:two) end it "should remove a given vertex when asked" do - @graph.add_vertex!(:one) + @graph.add_vertex(:one) @graph.remove_vertex!(:one) @graph.vertex?(:one).should be_false end @@ -86,49 +86,49 @@ describe Puppet::SimpleGraph, " when managing edges" do it "should provide a method to add an edge as an instance of the edge class" do edge = Puppet::Relationship.new(:one, :two) - @graph.add_edge!(edge) + @graph.add_edge(edge) @graph.edge?(:one, :two).should be_true end it "should provide a method to add an edge by specifying the two vertices" do - @graph.add_edge!(:one, :two) + @graph.add_edge(:one, :two) @graph.edge?(:one, :two).should be_true end it "should provide a method to add an edge by specifying the two vertices and a label" do - @graph.add_edge!(:one, :two, :stuff => :awesome) + @graph.add_edge(:one, :two, :stuff => :awesome) @graph.edge?(:one, :two).should be_true end it "should provide a method for retrieving an edge label" do edge = Puppet::Relationship.new(:one, :two, :stuff => :awesome) - @graph.add_edge!(edge) + @graph.add_edge(edge) @graph.edge_label(:one, :two).should == {:stuff => :awesome} end it "should provide a method for retrieving an edge" do edge = Puppet::Relationship.new(:one, :two) - @graph.add_edge!(edge) + @graph.add_edge(edge) @graph.edge(:one, :two).should equal(edge) end it "should add the edge source as a vertex if it is not already" do edge = Puppet::Relationship.new(:one, :two) - @graph.add_edge!(edge) + @graph.add_edge(edge) @graph.vertex?(:one).should be_true end it "should add the edge target as a vertex if it is not already" do edge = Puppet::Relationship.new(:one, :two) - @graph.add_edge!(edge) + @graph.add_edge(edge) @graph.vertex?(:two).should be_true end it "should return all edges as edge instances when asked" do one = Puppet::Relationship.new(:one, :two) two = Puppet::Relationship.new(:two, :three) - @graph.add_edge!(one) - @graph.add_edge!(two) + @graph.add_edge(one) + @graph.add_edge(two) edges = @graph.edges edges.length.should == 2 edges.should include(one) @@ -137,7 +137,7 @@ describe Puppet::SimpleGraph, " when managing edges" do it "should remove an edge when asked" do edge = Puppet::Relationship.new(:one, :two) - @graph.add_edge!(edge) + @graph.add_edge(edge) @graph.remove_edge!(edge) @graph.edge?(edge.source, edge.target).should be_false end @@ -145,8 +145,8 @@ describe Puppet::SimpleGraph, " when managing edges" do it "should remove all related edges when a vertex is removed" do one = Puppet::Relationship.new(:one, :two) two = Puppet::Relationship.new(:two, :three) - @graph.add_edge!(one) - @graph.add_edge!(two) + @graph.add_edge(one) + @graph.add_edge(two) @graph.remove_vertex!(:two) @graph.edge?(:one, :two).should be_false @graph.edge?(:two, :three).should be_false @@ -160,9 +160,9 @@ describe Puppet::SimpleGraph, " when finding adjacent vertices" do @one_two = Puppet::Relationship.new(:one, :two) @two_three = Puppet::Relationship.new(:two, :three) @one_three = Puppet::Relationship.new(:one, :three) - @graph.add_edge!(@one_two) - @graph.add_edge!(@one_three) - @graph.add_edge!(@two_three) + @graph.add_edge(@one_two) + @graph.add_edge(@one_three) + @graph.add_edge(@two_three) end it "should return adjacent vertices" do @@ -193,8 +193,8 @@ describe Puppet::SimpleGraph, " when clearing" do @graph = Puppet::SimpleGraph.new one = Puppet::Relationship.new(:one, :two) two = Puppet::Relationship.new(:two, :three) - @graph.add_edge!(one) - @graph.add_edge!(two) + @graph.add_edge(one) + @graph.add_edge(two) @graph.clear end @@ -214,18 +214,18 @@ describe Puppet::SimpleGraph, " when reversing graphs" do end it "should provide a method for reversing the graph" do - @graph.add_edge!(:one, :two) + @graph.add_edge(:one, :two) @graph.reversal.edge?(:two, :one).should be_true end it "should add all vertices to the reversed graph" do - @graph.add_edge!(:one, :two) + @graph.add_edge(:one, :two) @graph.vertex?(:one).should be_true @graph.vertex?(:two).should be_true end it "should retain labels on edges" do - @graph.add_edge!(:one, :two, :stuff => :awesome) + @graph.add_edge(:one, :two, :stuff => :awesome) edge = @graph.reversal.edge(:two, :one) edge.label.should == {:stuff => :awesome} end @@ -238,7 +238,7 @@ describe Puppet::SimpleGraph, " when sorting the graph" do def add_edges(hash) hash.each do |a,b| - @graph.add_edge!(a, b) + @graph.add_edge(a, b) end end @@ -266,4 +266,15 @@ describe Puppet::SimpleGraph, " when sorting the graph" do add_edges :a => :b, :b => :e, :c => :a, :d => :c proc { @graph.topsort }.should_not raise_error end + + # Our graph's add_edge method is smart enough not to add + # duplicate edges, so we use the objects, which it doesn't + # check. + it "should be able to sort graphs with duplicate edges" do + one = Puppet::Relationship.new(:a, :b) + @graph.add_edge(one) + two = Puppet::Relationship.new(:a, :b) + @graph.add_edge(two) + proc { @graph.topsort }.should_not raise_error + end end diff --git a/spec/unit/util/checksums.rb b/spec/unit/util/checksums.rb new file mode 100755 index 000000000..0e0d06c0d --- /dev/null +++ b/spec/unit/util/checksums.rb @@ -0,0 +1,99 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-9-22. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/checksums' + +describe Puppet::Util::Checksums do + before do + @summer = Object.new + @summer.extend(Puppet::Util::Checksums) + end + + content_sums = [:md5, :md5lite, :sha1, :sha1lite] + file_only = [:ctime, :mtime] + + content_sums.each do |sumtype| + it "should be able to calculate %s sums from strings" % sumtype do + @summer.should be_respond_to(sumtype) + end + end + + [content_sums, file_only].flatten.each do |sumtype| + it "should be able to calculate %s sums from files" % sumtype do + @summer.should be_respond_to(sumtype.to_s + "_file") + end + end + + {:md5 => Digest::MD5, :sha1 => Digest::SHA1}.each do |sum, klass| + describe("when using %s" % sum) do + it "should use #{klass} to calculate string checksums" do + klass.expects(:hexdigest).with("mycontent").returns "whatever" + @summer.send(sum, "mycontent").should == "whatever" + end + + it "should use incremental #{klass} sums to calculate file checksums" do + digest = mock 'digest' + klass.expects(:new).returns digest + + file = "/path/to/my/file" + + fh = mock 'filehandle' + fh.expects(:read).with(512).times(3).returns("firstline").then.returns("secondline").then.returns(nil) + #fh.expects(:read).with(512).returns("secondline") + #fh.expects(:read).with(512).returns(nil) + + File.expects(:open).with(file, "r").yields(fh) + + digest.expects(:<<).with "firstline" + digest.expects(:<<).with "secondline" + digest.expects(:hexdigest).returns :mydigest + + @summer.send(sum.to_s + "_file", file).should == :mydigest + end + end + end + + {:md5lite => Digest::MD5, :sha1lite => Digest::SHA1}.each do |sum, klass| + describe("when using %s" % sum) do + it "should use #{klass} to calculate string checksums from the first 512 characters of the string" do + content = "this is a test" * 100 + klass.expects(:hexdigest).with(content[0..511]).returns "whatever" + @summer.send(sum, content).should == "whatever" + end + + it "should use #{klass} to calculate a sum from the first 512 characters in the file" do + digest = mock 'digest' + klass.expects(:new).returns digest + + file = "/path/to/my/file" + + fh = mock 'filehandle' + fh.expects(:read).with(512).returns('my content') + + File.expects(:open).with(file, "r").yields(fh) + + digest.expects(:<<).with "my content" + digest.expects(:hexdigest).returns :mydigest + + @summer.send(sum.to_s + "_file", file).should == :mydigest + end + end + end + + [:ctime, :mtime].each do |sum| + describe("when using %s" % sum) do + it "should use the '#{sum}' on the file to determine the ctime" do + file = "/my/file" + stat = mock 'stat', sum => "mysum" + + File.expects(:stat).with(file).returns(stat) + + @summer.send(sum.to_s + "_file", file).should == "mysum" + end + end + end +end diff --git a/spec/unit/util/constant_inflector.rb b/spec/unit/util/constant_inflector.rb new file mode 100755 index 000000000..5112e730f --- /dev/null +++ b/spec/unit/util/constant_inflector.rb @@ -0,0 +1,70 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-02-12. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/constant_inflector' + +describe Puppet::Util::ConstantInflector, "when converting file names to constants" do + before do + @inflector = Object.new + @inflector.extend(Puppet::Util::ConstantInflector) + end + + it "should capitalize terms" do + @inflector.file2constant("file").should == "File" + end + + it "should switch all '/' characters to double colons" do + @inflector.file2constant("file/other").should == "File::Other" + end + + it "should remove underscores and capitalize the proceeding letter" do + @inflector.file2constant("file_other").should == "FileOther" + end + + it "should correctly replace as many underscores as exist in the file name" do + @inflector.file2constant("two_under_scores/with_some_more_underscores").should == "TwoUnderScores::WithSomeMoreUnderscores" + end + + it "should collapse multiple underscores" do + @inflector.file2constant("many___scores").should == "ManyScores" + end + + it "should correctly handle file names deeper than two directories" do + @inflector.file2constant("one_two/three_four/five_six").should == "OneTwo::ThreeFour::FiveSix" + end +end + +describe Puppet::Util::ConstantInflector, "when converting constnats to file names" do + before do + @inflector = Object.new + @inflector.extend(Puppet::Util::ConstantInflector) + end + + it "should convert them to a string if necessary" do + @inflector.constant2file(Puppet::Util::ConstantInflector).should be_instance_of(String) + end + + it "should accept string inputs" do + @inflector.constant2file("Puppet::Util::ConstantInflector").should be_instance_of(String) + end + + it "should downcase all terms" do + @inflector.constant2file("Puppet").should == "puppet" + end + + it "should convert '::' to '/'" do + @inflector.constant2file("Puppet::Util::Constant").should == "puppet/util/constant" + end + + it "should convert mid-word capitalization to an underscore" do + @inflector.constant2file("OneTwo::ThreeFour").should == "one_two/three_four" + end + + it "should correctly handle constants with more than two parts" do + @inflector.constant2file("OneTwoThree::FourFiveSixSeven").should == "one_two_three/four_five_six_seven" + end +end diff --git a/spec/unit/util/settings.rb b/spec/unit/util/settings.rb index 540743d7e..9f7018697 100755 --- a/spec/unit/util/settings.rb +++ b/spec/unit/util/settings.rb @@ -284,11 +284,6 @@ describe Puppet::Util::Settings, " when parsing its configuration" do lambda { @settings.parse(file) }.should_not raise_error end - it "should support an old parse method when per-executable configuration files still exist" do - # I'm not going to bother testing this method. - @settings.should respond_to(:old_parse) - end - it "should convert booleans in the configuration file into Ruby booleans" do text = "[main] one = true @@ -481,7 +476,7 @@ describe Puppet::Util::Settings, " when being used to manage the host machine" d def stub_transaction @bucket = mock 'bucket' - @config = mock 'config' + @config = mock 'config', :clear => nil @trans = mock 'transaction' @settings.expects(:to_transportable).with(:whatever).returns(@bucket) @@ -597,6 +592,72 @@ describe Puppet::Util::Settings, " when being used to manage the host machine" d file.should be_nil end + it "should not try to manage files in memory" do + main = Puppet::Type.type(:file).create(:path => "/maindir") + + trans = @settings.to_transportable + + lambda { trans.to_catalog }.should_not raise_error + end + + it "should do nothing if a catalog cannot be created" do + bucket = mock 'bucket' + catalog = mock 'catalog' + + @settings.expects(:to_transportable).returns bucket + bucket.expects(:to_catalog).raises RuntimeError + catalog.expects(:apply).never + + @settings.use(:mysection) + end + + it "should clear the catalog after applying" do + bucket = mock 'bucket' + catalog = mock 'catalog' + + @settings.expects(:to_transportable).returns bucket + bucket.expects(:to_catalog).returns catalog + catalog.stubs(:host_config=) + catalog.stubs(:apply) + catalog.expects(:clear) + + @settings.use(:mysection) + end + + it "should clear the catalog even if there is an exception during applying" do + bucket = mock 'bucket' + catalog = mock 'catalog' + + @settings.expects(:to_transportable).returns bucket + bucket.expects(:to_catalog).returns catalog + catalog.stubs(:host_config=) + catalog.expects(:apply).raises(ArgumentError) + catalog.expects(:clear) + + # We don't care about the raised exception, we just care that + # we clear the catalog even with the exception + lambda { @settings.use(:mysection) }.should raise_error + end + + it "should do nothing if all specified sections have already been used" do + bucket = mock 'bucket' + catalog = mock 'catalog' + + @settings.expects(:to_transportable).once.returns(bucket) + bucket.expects(:to_catalog).returns catalog + catalog.stub_everything + + @settings.use(:whatever) + + @settings.use(:whatever) + end + + it "should ignore file settings whose values are not strings" do + @settings[:maindir] = false + + lambda { trans = @settings.to_transportable }.should_not raise_error + end + it "should be able to turn the current configuration into a parseable manifest" it "should convert octal numbers correctly when producing a manifest" @@ -635,4 +696,6 @@ describe Puppet::Util::Settings, " when being used to manage the host machine" d proc { @settings.use(:whatever) }.should raise_error(RuntimeError) end + + after { Puppet::Type.allclear } end diff --git a/spec/unit/util/tagging.rb b/spec/unit/util/tagging.rb new file mode 100755 index 000000000..d61ee8ccb --- /dev/null +++ b/spec/unit/util/tagging.rb @@ -0,0 +1,92 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-01-19. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/tagging' + +describe Puppet::Util::Tagging, "when adding tags" do + before do + @tagger = Object.new + @tagger.extend(Puppet::Util::Tagging) + end + + it "should have a method for adding tags" do + @tagger.should be_respond_to(:tag) + end + + it "should have a method for returning all tags" do + @tagger.should be_respond_to(:tags) + end + + it "should add tags to the returned tag list" do + @tagger.tag("one") + @tagger.tags.should be_include("one") + end + + it "should not add duplicate tags to the returned tag list" do + @tagger.tag("one") + @tagger.tag("one") + @tagger.tags.should == ["one"] + end + + it "should return a duplicate of the tag list, rather than the original" do + @tagger.tag("one") + tags = @tagger.tags + tags << "two" + @tagger.tags.should_not be_include("two") + end + + it "should add all provided tags to the tag list" do + @tagger.tag("one", "two") + @tagger.tags.should be_include("one") + @tagger.tags.should be_include("two") + end + + it "should fail on tags containing '*' characters" do + lambda { @tagger.tag("bad*tag") }.should raise_error(Puppet::ParseError) + end + + it "should fail on tags starting with '-' characters" do + lambda { @tagger.tag("-badtag") }.should raise_error(Puppet::ParseError) + end + + it "should fail on tags containing ' ' characters" do + lambda { @tagger.tag("bad tag") }.should raise_error(Puppet::ParseError) + end + + it "should allow alpha tags" do + lambda { @tagger.tag("good_tag") }.should_not raise_error(Puppet::ParseError) + end + + it "should allow tags containing '.' characters" do + lambda { @tagger.tag("good.tag") }.should_not raise_error(Puppet::ParseError) + end + + it "should provide a method for testing tag validity" do + @tagger.metaclass.publicize_methods(:valid_tag?) { @tagger.should be_respond_to(:valid_tag?) } + end + + it "should add qualified classes as tags" do + @tagger.tag("one::two") + @tagger.tags.should be_include("one::two") + end + + it "should add each part of qualified classes as tags" do + @tagger.tag("one::two::three") + @tagger.tags.should be_include("one") + @tagger.tags.should be_include("two") + @tagger.tags.should be_include("three") + end + + it "should indicate when the object is tagged with a provided tag" do + @tagger.tag("one") + @tagger.should be_tagged("one") + end + + it "should indicate when the object is not tagged with a provided tag" do + @tagger.should_not be_tagged("one") + end +end |
