Numerous changes to update testing gems.

This work updates a number of Gems to the latest versions (rspec,
rspec-puppet), and updates and tweaks a bunch of tests to work
with the updated gems.
This commit is contained in:
Ashley Penney 2014-03-05 15:43:58 -05:00
parent 326a8fd801
commit dbb29980a1
21 changed files with 543 additions and 151 deletions

24
Gemfile Normal file
View file

@ -0,0 +1,24 @@
source ENV['GEM_SOURCE'] || 'https://rubygems.org'
group :development, :test do
gem 'rake', :require => false
gem 'rspec-puppet', :require => false
gem 'puppetlabs_spec_helper', :require => false
gem 'rspec-system', :require => false
gem 'rspec-system-puppet', :require => false
gem 'rspec-system-serverspec', :require => false
gem 'serverspec', :require => false
gem 'puppet-lint', :require => false
gem 'pry', :require => false
gem 'simplecov', :require => false
gem 'beaker', :require => false
gem 'beaker-rspec', :require => false
end
if puppetversion = ENV['PUPPET_GEM_VERSION']
gem 'puppet', puppetversion, :require => false
else
gem 'puppet', :require => false
end
# vim:ft=ruby

View file

@ -0,0 +1,31 @@
require 'spec_helper'
require 'puppet_spec/compiler'
describe "anchorrefresh" do
include PuppetSpec::Compiler
let :transaction do
apply_compiled_manifest(<<-ANCHORCLASS)
class anchored {
anchor { 'anchored::begin': }
~> anchor { 'anchored::end': }
}
class anchorrefresh {
notify { 'first': }
~> class { 'anchored': }
~> anchor { 'final': }
}
include anchorrefresh
ANCHORCLASS
end
it 'propagates events through the anchored class' do
require 'pry'
binding.pry
resource = transaction.resource_status('Anchor[final]')
expect(resource.restarted).to eq(true)
end
end

View file

@ -2,41 +2,65 @@
require 'spec_helper' require 'spec_helper'
require 'rspec-puppet' require 'rspec-puppet'
require 'puppet_spec/compiler'
describe 'ensure_packages' do describe 'ensure_packages' do
let(:scope) { PuppetlabsSpec::PuppetInternals.scope } include PuppetSpec::Compiler
describe 'argument handling' do before :each do
it 'fails with no arguments' do Puppet::Parser::Functions.autoloader.loadall
should run.with_params().and_raise_error(Puppet::ParseError) Puppet::Parser::Functions.function(:ensure_packages)
end Puppet::Parser::Functions.function(:ensure_resource)
it 'requires an array' do Puppet::Parser::Functions.function(:defined_with_params)
lambda { scope.function_ensure_packages([['foo']]) }.should_not raise_error Puppet::Parser::Functions.function(:create_resources)
end end
it 'fails when given a string' do
should run.with_params('foo').and_raise_error(Puppet::ParseError) let :node do Puppet::Node.new('localhost') end
let :compiler do Puppet::Parser::Compiler.new(node) end
let :scope do
if Puppet.version.to_f >= 3.0
Puppet::Parser::Scope.new(compiler)
else
newscope = Puppet::Parser::Scope.new
newscope.compiler = compiler
newscope.source = Puppet::Resource::Type.new(:node, :localhost)
newscope
end end
end end
context 'given a catalog containing Package[puppet]{ensure => absent}' do describe 'argument handling' do
let :pre_condition do it 'fails with no arguments' do
'package { puppet: ensure => absent }' expect {
scope.function_ensure_packages([])
}.to raise_error(Puppet::ParseError, /0 for 1/)
end
it 'accepts an array of values' do
scope.function_ensure_packages([['foo']])
end
end
context 'given a catalog with puppet package => absent' do
let :catalog do
compile_to_catalog(<<-EOS
ensure_packages(['facter'])
package { puppet: ensure => absent }
EOS
)
end end
# NOTE: should run.with_params has the side effect of making the compiler
# available to the test harness.
it 'has no effect on Package[puppet]' do it 'has no effect on Package[puppet]' do
should run.with_params(['puppet']) expect(catalog.resource(:package, 'puppet')['ensure']).to eq('absent')
rsrc = compiler.catalog.resource('Package[puppet]')
rsrc.to_hash.should == {:ensure => "absent"}
end end
end end
context 'given a clean catalog' do context 'given a clean catalog' do
let :catalog do
compile_to_catalog('ensure_packages(["facter"])')
end
it 'declares package resources with ensure => present' do it 'declares package resources with ensure => present' do
should run.with_params(['facter']) expect(catalog.resource(:package, 'facter')['ensure']).to eq('present')
rsrc = compiler.catalog.resource('Package[facter]')
rsrc.to_hash.should == {:name => "facter", :ensure => "present"}
end end
end end
end end

View file

@ -1,40 +1,61 @@
#! /usr/bin/env ruby -S rspec
require 'spec_helper' require 'spec_helper'
require 'rspec-puppet' require 'rspec-puppet'
require 'puppet_spec/compiler'
describe 'ensure_resource' do describe 'ensure_resource' do
describe 'when a type or title is not specified' do include PuppetSpec::Compiler
it do
should run.with_params().and_raise_error(ArgumentError) before :all do
should run.with_params(['type']).and_raise_error(ArgumentError) Puppet::Parser::Functions.autoloader.loadall
end Puppet::Parser::Functions.function(:ensure_packages)
end end
let :node do Puppet::Node.new('localhost') end
let :compiler do Puppet::Parser::Compiler.new(node) end
let :scope do Puppet::Parser::Scope.new(compiler) end
describe 'when a type or title is not specified' do
it { expect { scope.function_ensure_resource([]) }.to raise_error }
it { expect { scope.function_ensure_resource(['type']) }.to raise_error }
end
describe 'when compared against a resource with no attributes' do describe 'when compared against a resource with no attributes' do
let :pre_condition do let :catalog do
'user { "dan": }' compile_to_catalog(<<-EOS
user { "dan": }
ensure_resource('user', 'dan', {})
EOS
)
end end
it do
should run.with_params('user', 'dan', {}) it 'should contain the the ensured resources' do
compiler.catalog.resource('User[dan]').to_s.should == 'User[dan]' expect(catalog.resource(:user, 'dan').to_s).to eq('User[dan]')
end end
end end
describe 'when compared against a resource with attributes' do describe 'works when compared against a resource with non-conflicting attributes' do
let :pre_condition do [
'user { "dan": ensure => present, shell => "/bin/csh", managehome => false}' "ensure_resource('User', 'dan', {})",
end "ensure_resource('User', 'dan', '')",
it do "ensure_resource('User', 'dan', {'ensure' => 'present'})",
# these first three should not fail "ensure_resource('User', 'dan', {'ensure' => 'present', 'managehome' => false})"
should run.with_params('User', 'dan', {}) ].each do |ensure_resource|
should run.with_params('User', 'dan', '') pp = <<-EOS
should run.with_params('User', 'dan', {'ensure' => 'present'}) user { "dan": ensure => present, shell => "/bin/csh", managehome => false}
should run.with_params('User', 'dan', #{ensure_resource}
{'ensure' => 'present', 'managehome' => false} EOS
)
# test that this fails it { expect { compile_to_catalog(pp) }.to_not raise_error }
should run.with_params('User', 'dan',
{'ensure' => 'absent', 'managehome' => false}
).and_raise_error(Puppet::Error)
end end
end end
describe 'fails when compared against a resource with conflicting attributes' do
pp = <<-EOS
user { "dan": ensure => present, shell => "/bin/csh", managehome => false}
ensure_resource('User', 'dan', {'ensure' => 'absent', 'managehome' => false})
EOS
it { expect { compile_to_catalog(pp) }.to raise_error }
end
end end

View file

@ -0,0 +1,46 @@
module PuppetSpec::Compiler
def compile_to_catalog(string, node = Puppet::Node.new('foonode'))
Puppet[:code] = string
Puppet::Parser::Compiler.compile(node)
end
def compile_to_ral(manifest)
catalog = compile_to_catalog(manifest)
ral = catalog.to_ral
ral.finalize
ral
end
def compile_to_relationship_graph(manifest, prioritizer = Puppet::Graph::SequentialPrioritizer.new)
ral = compile_to_ral(manifest)
graph = Puppet::Graph::RelationshipGraph.new(prioritizer)
graph.populate_from(ral)
graph
end
if Puppet.version.to_f >= 3.3
def apply_compiled_manifest(manifest, prioritizer = Puppet::Graph::SequentialPrioritizer.new)
transaction = Puppet::Transaction.new(compile_to_ral(manifest),
Puppet::Transaction::Report.new("apply"),
prioritizer)
transaction.evaluate
transaction.report.finalize_report
transaction
end
else
def apply_compiled_manifest(manifest)
transaction = Puppet::Transaction.new(compile_to_ral(manifest), Puppet::Transaction::Report.new("apply"))
transaction.evaluate
transaction.report.finalize_report
transaction
end
end
def order_resources_traversed_in(relationships)
order_seen = []
relationships.traverse { |resource| order_seen << resource.ref }
order_seen
end
end

View file

@ -0,0 +1,29 @@
# This just makes some nice things available at global scope, and for setup of
# tests to use a real fake database, rather than a fake stubs-that-don't-work
# version of the same. Fun times.
def sqlite?
if $sqlite.nil?
begin
require 'sqlite3'
$sqlite = true
rescue LoadError
$sqlite = false
end
end
$sqlite
end
def can_use_scratch_database?
sqlite? and Puppet.features.rails?
end
# This is expected to be called in your `before :each` block, and will get you
# ready to roll with a serious database and all. Cleanup is handled
# automatically for you. Nothing to do there.
def setup_scratch_database
Puppet[:dbadapter] = 'sqlite3'
Puppet[:dblocation] = ':memory:'
Puppet[:railslog] = PuppetSpec::Files.tmpfile('storeconfigs.log')
Puppet::Rails.init
end

60
spec/lib/puppet_spec/files.rb Executable file
View file

@ -0,0 +1,60 @@
require 'fileutils'
require 'tempfile'
require 'tmpdir'
require 'pathname'
# A support module for testing files.
module PuppetSpec::Files
def self.cleanup
$global_tempfiles ||= []
while path = $global_tempfiles.pop do
begin
Dir.unstub(:entries)
FileUtils.rm_rf path, :secure => true
rescue Errno::ENOENT
# nothing to do
end
end
end
def make_absolute(path) PuppetSpec::Files.make_absolute(path) end
def self.make_absolute(path)
path = File.expand_path(path)
path[0] = 'c' if Puppet.features.microsoft_windows?
path
end
def tmpfile(name, dir = nil) PuppetSpec::Files.tmpfile(name, dir) end
def self.tmpfile(name, dir = nil)
# Generate a temporary file, just for the name...
source = dir ? Tempfile.new(name, dir) : Tempfile.new(name)
path = source.path
source.close!
record_tmp(File.expand_path(path))
path
end
def file_containing(name, contents) PuppetSpec::Files.file_containing(name, contents) end
def self.file_containing(name, contents)
file = tmpfile(name)
File.open(file, 'wb') { |f| f.write(contents) }
file
end
def tmpdir(name) PuppetSpec::Files.tmpdir(name) end
def self.tmpdir(name)
dir = Dir.mktmpdir(name)
record_tmp(dir)
dir
end
def self.record_tmp(tmp)
# ...record it for cleanup,
$global_tempfiles ||= []
$global_tempfiles << tmp
end
end

View file

@ -0,0 +1,28 @@
module PuppetSpec::Fixtures
def fixtures(*rest)
File.join(PuppetSpec::FIXTURE_DIR, *rest)
end
def my_fixture_dir
callers = caller
while line = callers.shift do
next unless found = line.match(%r{/spec/(.*)_spec\.rb:})
return fixtures(found[1])
end
fail "sorry, I couldn't work out your path from the caller stack!"
end
def my_fixture(name)
file = File.join(my_fixture_dir, name)
unless File.readable? file then
fail Puppet::DevError, "fixture '#{name}' for #{my_fixture_dir} is not readable"
end
return file
end
def my_fixtures(glob = '*', flags = 0)
files = Dir.glob(File.join(my_fixture_dir, glob), flags)
unless files.length > 0 then
fail Puppet::DevError, "fixture '#{glob}' for #{my_fixture_dir} had no files!"
end
block_given? and files.each do |file| yield file end
files
end
end

View file

@ -0,0 +1,120 @@
require 'stringio'
########################################################################
# Backward compatibility for Jenkins outdated environment.
module RSpec
module Matchers
module BlockAliases
alias_method :to, :should unless method_defined? :to
alias_method :to_not, :should_not unless method_defined? :to_not
alias_method :not_to, :should_not unless method_defined? :not_to
end
end
end
########################################################################
# Custom matchers...
RSpec::Matchers.define :have_matching_element do |expected|
match do |actual|
actual.any? { |item| item =~ expected }
end
end
RSpec::Matchers.define :exit_with do |expected|
actual = nil
match do |block|
begin
block.call
rescue SystemExit => e
actual = e.status
end
actual and actual == expected
end
failure_message_for_should do |block|
"expected exit with code #{expected} but " +
(actual.nil? ? " exit was not called" : "we exited with #{actual} instead")
end
failure_message_for_should_not do |block|
"expected that exit would not be called with #{expected}"
end
description do
"expect exit with #{expected}"
end
end
class HavePrintedMatcher
attr_accessor :expected, :actual
def initialize(expected)
case expected
when String, Regexp
@expected = expected
else
@expected = expected.to_s
end
end
def matches?(block)
begin
$stderr = $stdout = StringIO.new
$stdout.set_encoding('UTF-8') if $stdout.respond_to?(:set_encoding)
block.call
$stdout.rewind
@actual = $stdout.read
ensure
$stdout = STDOUT
$stderr = STDERR
end
if @actual then
case @expected
when String
@actual.include? @expected
when Regexp
@expected.match @actual
end
else
false
end
end
def failure_message_for_should
if @actual.nil? then
"expected #{@expected.inspect}, but nothing was printed"
else
"expected #{@expected.inspect} to be printed; got:\n#{@actual}"
end
end
def failure_message_for_should_not
"expected #{@expected.inspect} to not be printed; got:\n#{@actual}"
end
def description
"expect #{@expected.inspect} to be printed"
end
end
def have_printed(what)
HavePrintedMatcher.new(what)
end
RSpec::Matchers.define :equal_attributes_of do |expected|
match do |actual|
actual.instance_variables.all? do |attr|
actual.instance_variable_get(attr) == expected.instance_variable_get(attr)
end
end
end
RSpec::Matchers.define :be_one_of do |*expected|
match do |actual|
expected.include? actual
end
failure_message_for_should do |actual|
"expected #{actual.inspect} to be one of #{expected.map(&:inspect).join(' or ')}"
end
end

View file

@ -0,0 +1,26 @@
module PuppetSpec::Modules
class << self
def create(name, dir, options = {})
module_dir = File.join(dir, name)
FileUtils.mkdir_p(module_dir)
environment = options[:environment]
if metadata = options[:metadata]
metadata[:source] ||= 'github'
metadata[:author] ||= 'puppetlabs'
metadata[:version] ||= '9.9.9'
metadata[:license] ||= 'to kill'
metadata[:dependencies] ||= []
metadata[:name] = "#{metadata[:author]}/#{name}"
File.open(File.join(module_dir, 'metadata.json'), 'w') do |f|
f.write(metadata.to_pson)
end
end
Puppet::Module.new(name, module_dir, environment)
end
end
end

View file

@ -0,0 +1,16 @@
module PuppetSpec::Pops
extend RSpec::Matchers::DSL
# Checks if an Acceptor has a specific issue in its list of diagnostics
matcher :have_issue do |expected|
match do |actual|
actual.diagnostics.index { |i| i.issue == expected } != nil
end
failure_message_for_should do |actual|
"expected Acceptor[#{actual.diagnostics.collect { |i| i.issue.issue_code }.join(',')}] to contain issue #{expected.issue_code}"
end
failure_message_for_should_not do |actual|
"expected Acceptor[#{actual.diagnostics.collect { |i| i.issue.issue_code }.join(',')}] to not contain issue #{expected.issue_code}"
end
end
end

View file

@ -0,0 +1,14 @@
module PuppetSpec::Scope
# Initialize a new scope suitable for testing.
#
def create_test_scope_for_node(node_name)
node = Puppet::Node.new(node_name)
compiler = Puppet::Parser::Compiler.new(node)
scope = Puppet::Parser::Scope.new(compiler)
scope.source = Puppet::Resource::Type.new(:node, node_name)
scope.parent = compiler.topscope
scope
end
end

View file

@ -0,0 +1,15 @@
module PuppetSpec::Settings
# It would probably be preferable to refactor defaults.rb such that the real definitions of
# these settings were available as a variable, which was then accessible for use during tests.
# However, I'm not doing that yet because I don't want to introduce any additional moving parts
# to this already very large changeset.
# Would be nice to clean this up later. --cprice 2012-03-20
TEST_APP_DEFAULT_DEFINITIONS = {
:name => { :default => "test", :desc => "name" },
:logdir => { :type => :directory, :default => "test", :desc => "logdir" },
:confdir => { :type => :directory, :default => "test", :desc => "confdir" },
:vardir => { :type => :directory, :default => "test", :desc => "vardir" },
:rundir => { :type => :directory, :default => "test", :desc => "rundir" },
}
end

View file

@ -0,0 +1,9 @@
# Support code for running stuff with warnings disabled.
module Kernel
def with_verbose_disabled
verbose, $VERBOSE = $VERBOSE, nil
result = yield
$VERBOSE = verbose
return result
end
end

View file

@ -1,21 +1,31 @@
dir = File.expand_path(File.dirname(__FILE__)) dir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift File.join(dir, 'lib') $LOAD_PATH.unshift File.join(dir, 'lib')
# Don't want puppet getting the command line arguments for rake or autotest # So everyone else doesn't have to include this base constant.
ARGV.clear module PuppetSpec
FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR)
end
require 'puppet' require 'puppet'
require 'facter' require 'rspec-puppet'
require 'mocha' require 'simplecov'
gem 'rspec', '>=2.0.0'
require 'rspec/expectations'
require 'puppetlabs_spec_helper/module_spec_helper' require 'puppetlabs_spec_helper/module_spec_helper'
require 'puppet_spec/verbose'
require 'puppet_spec/files'
require 'puppet_spec/settings'
require 'puppet_spec/fixtures'
require 'puppet_spec/matchers'
require 'puppet_spec/database'
require 'monkey_patches/alias_should_to_must'
require 'mocha/setup'
SimpleCov.start do
add_filter "/spec/"
end
RSpec.configure do |config| RSpec.configure do |config|
# FIXME REVISIT - We may want to delegate to Facter like we do in
# Puppet::PuppetSpecInitializer.initialize_via_testhelper(config) because
# this behavior is a duplication of the spec_helper in Facter.
config.before :each do config.before :each do
# Ensure that we don't accidentally cache facts and environment between # Ensure that we don't accidentally cache facts and environment between
# test cases. This requires each example group to explicitly load the # test cases. This requires each example group to explicitly load the

View file

@ -25,7 +25,12 @@ describe Puppet::Parser::Functions.function(:merge) do
describe 'when calling merge on the scope instance' do describe 'when calling merge on the scope instance' do
it 'should require all parameters are hashes' do it 'should require all parameters are hashes' do
expect { new_hash = scope.function_merge([{}, '2'])}.should raise_error(Puppet::ParseError, /unexpected argument type String/) expect { new_hash = scope.function_merge([{}, '2'])}.to raise_error(Puppet::ParseError, /unexpected argument type String/)
expect { new_hash = scope.function_merge([{}, 2])}.to raise_error(Puppet::ParseError, /unexpected argument type Fixnum/)
end
it 'should accept empty strings as puppet undef' do
expect { new_hash = scope.function_merge([{}, ''])}.to raise_error
end end
it 'should be able to merge two hashes' do it 'should be able to merge two hashes' do

View file

@ -35,7 +35,7 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do
end end
valid_paths.each do |path| valid_paths.each do |path|
it "validate_absolute_path(#{path.inspect}) should not fail" do it "validate_absolute_path(#{path.inspect}) should not fail" do
expect { subject.call [path] }.not_to raise_error Puppet::ParseError expect { subject.call [path] }.not_to raise_error
end end
end end
end end
@ -43,7 +43,7 @@ describe Puppet::Parser::Functions.function(:validate_absolute_path) do
context "Puppet without mocking" do context "Puppet without mocking" do
valid_paths.each do |path| valid_paths.each do |path|
it "validate_absolute_path(#{path.inspect}) should not fail" do it "validate_absolute_path(#{path.inspect}) should not fail" do
expect { subject.call [path] }.not_to raise_error Puppet::ParseError expect { subject.call [path] }.not_to raise_error
end end
end end
end end

View file

@ -1,4 +1,4 @@
require 'puppet' require 'spec_helper'
require 'tempfile' require 'tempfile'
provider_class = Puppet::Type.type(:file_line).provider(:ruby) provider_class = Puppet::Type.type(:file_line).provider(:ruby)
describe provider_class do describe provider_class do

View file

@ -1,6 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require 'puppet' require 'spec_helper'
anchor = Puppet::Type.type(:anchor).new(:name => "ntp::begin") anchor = Puppet::Type.type(:anchor).new(:name => "ntp::begin")

View file

@ -1,4 +1,4 @@
require 'puppet' require 'spec_helper'
require 'tempfile' require 'tempfile'
describe Puppet::Type.type(:file_line) do describe Puppet::Type.type(:file_line) do
let :file_line do let :file_line do

View file

@ -1,86 +0,0 @@
ENV['FOG_MOCK'] ||= 'true'
ENV['AUTOTEST'] = 'true'
ENV['WATCHR'] = '1'
system 'clear'
def growl(message)
growlnotify = `which growlnotify`.chomp
title = "Watchr Test Results"
image = case message
when /(\d+)\s+?(failure|error)/i
($1.to_i == 0) ? "~/.watchr_images/passed.png" : "~/.watchr_images/failed.png"
else
'~/.watchr_images/unknown.png'
end
options = "-w -n Watchr --image '#{File.expand_path(image)}' -m '#{message}' '#{title}'"
system %(#{growlnotify} #{options} &)
end
def run(cmd)
puts(cmd)
`#{cmd}`
end
def run_spec_test(file)
if File.exist? file
result = run "rspec --format p --color #{file}"
growl result.split("\n").last
puts result
else
puts "FIXME: No test #{file} [#{Time.now}]"
end
end
def filter_rspec(data)
data.split("\n").find_all do |l|
l =~ /^(\d+)\s+exampl\w+.*?(\d+).*?failur\w+.*?(\d+).*?pending/
end.join("\n")
end
def run_all_tests
system('clear')
files = Dir.glob("spec/**/*_spec.rb").join(" ")
result = run "rspec #{files}"
growl_results = filter_rspec result
growl growl_results
puts result
puts "GROWL: #{growl_results}"
end
# Ctrl-\
Signal.trap 'QUIT' do
puts " --- Running all tests ---\n\n"
run_all_tests
end
@interrupted = false
# Ctrl-C
Signal.trap 'INT' do
if @interrupted then
@wants_to_quit = true
abort("\n")
else
puts "Interrupt a second time to quit"
@interrupted = true
Kernel.sleep 1.5
# raise Interrupt, nil # let the run loop catch it
run_suite
end
end
def file2spec(file)
result = file.sub('lib/puppet/', 'spec/unit/puppet/').gsub(/\.rb$/, '_spec.rb')
result = file.sub('lib/facter/', 'spec/unit/facter/').gsub(/\.rb$/, '_spec.rb')
end
watch( 'spec/.*_spec\.rb' ) do |md|
#run_spec_test(md[0])
run_all_tests
end
watch( 'lib/.*\.rb' ) do |md|
# run_spec_test(file2spec(md[0]))
run_all_tests
end