Permalink
Browse files

(#10372) Add initial rspec test coverage.

- Added test scaffolding
- Puppet::Provider::F5 coverage
- Puppet::Type::* coverage
- In cases where the types have no validation I've setup the shared example
  to return pending so that these can be added later.
- Added rake tasks for running tests and coverage reports
  • Loading branch information...
1 parent a07e3d5 commit 817783a6c61a62956bbd4e6f835a16c60e7805ce @kbarber kbarber committed Oct 31, 2011
View
@@ -280,3 +280,14 @@ F5_virtualserver does not atomically change rules (F5 API limitation), so to reo
'vlans' => ['default'] },
wildmask => '255.255.255.255',
}
+
+## Development
+
+The following section applies to developers of this module only.
+
+### Testing
+
+You will need to install the 'f5-icontrol' gem for most of the tests to work.
+This file is available in the 'files' section of this module.
+
+ gem install --no-ri files/f5-icontrol-10.2.0.2.gem
View
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rspec/core/rake_task'
+
+task :default do
+ sh %{rake -T}
+end
+
+# Aliases for spec. The (s) versions are used by rvm specs/tests.
+task :test => [:spec]
+task :tests => [:spec]
+task :specs => [:spec]
+
+desc 'Run all RSpec tests'
+RSpec::Core::RakeTask.new(:spec) do |t|
+ t.rspec_opts = ['--color']
+end
+
+desc 'Generate code coverage'
+RSpec::Core::RakeTask.new(:coverage) do |t|
+ t.rcov = true
+ t.rcov_opts = ['--exclude', 'spec']
+end
@@ -0,0 +1,8 @@
+require 'rspec'
+
+class Object
+ # This is necessary because the RAL has a 'should'
+ # method.
+ alias :must :should
+ alias :must_not :should_not
+end
@@ -0,0 +1,11 @@
+# Some monkey-patching to allow us to test private methods.
+class Class
+ def publicize_methods(*methods)
+ saved_private_instance_methods = methods.empty? ? self.private_instance_methods : methods
+
+ self.class_eval { public(*saved_private_instance_methods) }
+ yield
+ self.class_eval { private(*saved_private_instance_methods) }
+ end
+end
+
View
@@ -0,0 +1,53 @@
+require 'fileutils'
+require 'tempfile'
+
+# A support module for testing files.
+module PuppetSpec::Files
+ # This code exists only to support tests that run as root, pretty much.
+ # Once they have finally been eliminated this can all go... --daniel 2011-04-08
+ if Puppet.features.posix? then
+ def self.in_tmp(path)
+ path =~ /^\/tmp/ or path =~ /^\/var\/folders/
+ end
+ elsif Puppet.features.microsoft_windows?
+ def self.in_tmp(path)
+ tempdir = File.expand_path(File.join(Dir::LOCAL_APPDATA, "Temp"))
+ path =~ /^#{tempdir}/
+ end
+ else
+ fail "Help! Can't find in_tmp for this platform"
+ end
+
+ def self.cleanup
+ $global_tempfiles ||= []
+ while path = $global_tempfiles.pop do
+ fail "Not deleting tmpfile #{path} outside regular tmpdir" unless in_tmp(path)
+
+ begin
+ FileUtils.rm_r path, :secure => true
+ rescue Errno::ENOENT
+ # nothing to do
+ end
+ end
+ end
+
+ def tmpfile(name)
+ # Generate a temporary file, just for the name...
+ source = Tempfile.new(name)
+ path = source.path
+ source.close!
+
+ # ...record it for cleanup,
+ $global_tempfiles ||= []
+ $global_tempfiles << File.expand_path(path)
+
+ # ...and bam.
+ path
+ end
+
+ def tmpdir(name)
+ path = tmpfile(name)
+ FileUtils.mkdir_p(path)
+ path
+ end
+end
@@ -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
@@ -0,0 +1,87 @@
+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
+
+
+RSpec::Matchers.define :have_printed do |expected|
+ match do |block|
+ $stderr = $stdout = StringIO.new
+
+ begin
+ block.call
+ ensure
+ $stdout.rewind
+ @actual = $stdout.read
+
+ $stdout = STDOUT
+ $stderr = STDERR
+ end
+
+ if @actual then
+ case expected
+ when String
+ @actual.include? expected
+ when Regexp
+ expected.match @actual
+ else
+ raise ArgumentError, "No idea how to match a #{@actual.class.name}"
+ end
+ end
+ end
+
+ failure_message_for_should do |actual|
+ if actual.nil? then
+ "expected #{expected.inspect}, but nothing was printed"
+ else
+ "expected #{expected.inspect} to be printed; got:\n#{actual}"
+ end
+ end
+
+ description do
+ "expect #{expected.inspect} to be printed"
+ end
+
+ diffable
+end
@@ -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
@@ -0,0 +1,82 @@
+dir = File.expand_path(File.dirname(__FILE__))
+$LOAD_PATH.unshift File.join(dir, 'lib')
+
+# Don't want puppet getting the command line arguments for rake or autotest
+ARGV.clear
+
+require 'puppet'
+require 'mocha'
+gem 'rspec', '>=2.0.0'
+require 'rspec/expectations'
+
+# So everyone else doesn't have to include this base constant.
+module PuppetSpec
+ FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR)
+end
+
+require 'pathname'
+require 'tmpdir'
+
+require 'puppet_spec/verbose'
+require 'puppet_spec/files'
+require 'puppet_spec/fixtures'
+require 'puppet_spec/matchers'
+require 'monkey_patches/alias_should_to_must'
+require 'monkey_patches/publicize_methods'
+
+Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour|
+ require behaviour.relative_path_from(Pathname.new(dir))
+end
+
+RSpec.configure do |config|
+ include PuppetSpec::Fixtures
+
+ config.mock_with :mocha
+
+ config.before :each do
+ GC.disable
+
+ # these globals are set by Application
+ $puppet_application_mode = nil
+ $puppet_application_name = nil
+
+ # REVISIT: I think this conceals other bad tests, but I don't have time to
+ # fully diagnose those right now. When you read this, please come tell me
+ # I suck for letting this float. --daniel 2011-04-21
+ Signal.stubs(:trap)
+
+ # Set the confdir and vardir to gibberish so that tests
+ # have to be correctly mocked.
+ Puppet[:confdir] = "/dev/null"
+ Puppet[:vardir] = "/dev/null"
+
+ # Avoid opening ports to the outside world
+ Puppet.settings[:bindaddress] = "127.0.0.1"
+
+ @logs = []
+ # This tests allows the spec_helper to be >2.6.7 and >2.7.1 compatible
+ # as the Puppet::Test::LogCollector facility wasn't available until 2.7.x
+ if Puppet.const_defined?("Test") and Puppet::Test.const_defined?("LogCollector")
+ Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs))
+ else
+ Puppet::Util::Log.newdestination(@logs)
+ end
+
+ @log_level = Puppet::Util::Log.level
+ end
+
+ config.after :each do
+ Puppet.settings.clear
+ Puppet::Node::Environment.clear
+ Puppet::Util::Storage.clear
+ Puppet::Util::ExecutionStub.reset
+
+ PuppetSpec::Files.cleanup
+
+ @logs.clear
+ Puppet::Util::Log.close_all
+ Puppet::Util::Log.level = @log_level
+
+ GC.enable
+ end
+end
@@ -0,0 +1,61 @@
+shared_examples "a puppet type" do |parameter_tests,res_type_name|
+ res_type = Puppet::Type.type(res_type_name)
+
+ let(:provider) {
+ prov = stub 'provider'
+ prov.stubs(:name).returns(res_type_name)
+ prov
+ }
+ let(:type) {
+ val = res_type
+ val.stubs(:defaultprovider).returns provider
+ val
+ }
+ let(:resource) {
+ type.new({:name => 'test'})
+ }
+
+ parameter_tests.each do |param, tests|
+ describe "parameter #{param}" do
+ it "should exist" do
+ expect { resource[param] }.to_not raise_error
+ end
+
+ if tests.has_key?(:default) then
+ it "should have a default of #{tests[:default]}" do
+ resource[param].should == tests[:default]
+ end
+ else
+ pending("should have a default")
+ end
+
+ if tests[:valid] then
+ tests[:valid].each do |test_value|
+ it "should allow a valid value, for example: #{test_value}" do
+ expect { resource[param] = test_value }.to_not raise_error(Puppet::Error)
+ resource[param].should == test_value
+ end
+ end
+ else
+ pending("should accept valid values")
+ end
+
+ if tests[:invalid] then
+ tests[:invalid].each do |test_value|
+ it "should throw an error for an invalid value, for example: #{test_value.inspect}" do
+ expect { resource[param] = test_value }.to raise_error(Puppet::Error, /^Parameter #{param.to_s} failed:/)
+ end
+ end
+ else
+ pending("should throw an error for invalid values")
+ end
+
+ if prop = res_type.propertybyname(param) then
+ it "should have docs" do
+ prop.doc.should_not == nil
+ prop.doc.should_not == ""
+ end
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 817783a

Please sign in to comment.