Closed rnelson0 closed 7 years ago
This seems to be related to, if not a duplicate of #316 . There seems to be a fundamental issue in how ruby code coverage is setup up by rspec-puppet, but I haven't had any luck in uncovering what's happening, when I looked the last time :-(
Unfortunately, this is not something that can be easily worked around. SimpleCov uses the builtin Coverage library in Ruby which is reset every time the file is loaded. Because of the way that this fact is written (if statement around multiple calls to Facter.add so that only one resolution is ever loaded), it has to be reloaded in order to change the value after the hostname value changes.
For example, if this fact were rewritten like
Facter.add(:puppet_role) do
confine :hostname do |value|
value =~ /^[a-z]+[0-9]+$/
end
setcode { Facter.value(:hostname)[/^([a-z]+)[0-9]$/, 1] }
end
Facter.add(:puppet_role) do
confine :hostname do |value|
value =~ /^[a-z]+$/
end
setcode { Facter.value(:hostname) }
end
Facter.add(:puppet_role) do
setcode { 'default' }
end
Then the spec can be rewritten to not require the use of Facter.clear
, which will prevent the facts from being reloaded from disk and you'll get a complete coverage report.
@rodjek I finally got back to this and am still seeing issues. Here's the new fact file and tests:
#dist/profile/lib/facter/roles.rb
# ([a-z]+)[0-9]+, i.e. www01 or logger22 have a puppet_role of www or logger
Facter.add(:puppet_role) do
confine :hostname do |value|
value =~ /^[a-z]+[0-9]+$/
end
setcode { Facter.value(:hostname)[/^([a-z]+)[0-9]+$/, 1] }
end
# ([a-z]+), i.e. www or logger have a puppet_role of www or logger
Facter.add(:puppet_role) do
confine :hostname do |value|
value =~ /^[a-z]+$/
end
setcode { Facter.value(:hostname) }
end
# Set to 'default' if no patterns match
Facter.add(:puppet_role) do
setcode { 'default' }
end
# spec/unit/facter/puppet_role_spec.rb
require 'spec_helper'
require './dist/profile/lib/facter/roles.rb'
describe 'puppet_role', :type => :fact do
context "test72" do
before do
Facter.fact(:hostname).stubs(:value).returns('test72')
end
it "should return test" do
expect(Facter.fact(:puppet_role).value).to eq('test')
end
end
context "role" do
before do
Facter.fact(:hostname).stubs(:value).returns('role')
end
it "should return role" do
expect(Facter.fact(:puppet_role).value).to eq('role')
end
end
context "default" do
before do
Facter.fact(:hostname).stubs(:value).returns('99luftballoons')
end
it "should return default" do
expect(Facter.fact(:puppet_role).value).to eq('default')
end
end
end
Here's the output:
puppet_role
test72
should return test
role
should return role (FAILED - 1)
default
should return default (FAILED - 2)
Failures:
1) puppet_role role should return role
Failure/Error: expect(Facter.fact(:puppet_role).value).to eq('role')
expected: "role"
got: "test"
(compared using ==)
# ./spec/unit/facter/puppet_role_spec.rb:22:in `block (3 levels) in <top (required)>'
2) puppet_role default should return default
Failure/Error: expect(Facter.fact(:puppet_role).value).to eq('default')
expected: "default"
got: "test"
(compared using ==)
# ./spec/unit/facter/puppet_role_spec.rb:32:in `block (3 levels) in <top (required)>'
Finished in 0.01986 seconds (files took 1.08 seconds to load)
3 examples, 2 failures
Failed examples:
rspec ./spec/unit/facter/puppet_role_spec.rb:21 # puppet_role role should return role
rspec ./spec/unit/facter/puppet_role_spec.rb:31 # puppet_role default should return default
Coverage report generated for RSpec to /home/rnelson0/puppet/controlrepo/coverage. 10 / 10 LOC (100.0%) covered.
COVERAGE: 100.00% -- 10/10 lines in 1 files
The coverage is up, but removing the clear appears to not reset the fact value between runs. If I add Facter.clear
back in, then I get nil
for all the values. Not sure what is going on here or how to fix it.
@rnelson0 Sorry, I missed a bit. You'll need to call Facter.flush
after stubbing the hostname fact. This will flush the cached value but not reload any ruby files.
asmodean :0: tmp/rnelson0 » cat test.rb
require 'facter'
require 'mocha'
require 'simplecov'
require 'simplecov-console'
SimpleCov.start do
add_filter '/spec'
add_filter '/vendor'
formatter SimpleCov::Formatter::Console
end
# Configure rspec to use mocha to match setup done by puppetlabs_spec_helper
RSpec.configure do |c|
c.mock_with :mocha
end
require './lib/facter/puppet_role.rb'
describe 'puppet_role' do
let(:hostname_fact) { self.class.description }
before(:each) do
Facter.fact(:hostname).stubs(:value).returns(hostname_fact)
Facter.flush
end
subject { Facter.fact(:puppet_role).value }
context 'test72' do
it { is_expected.to eq('test') }
end
context 'role' do
it { is_expected.to eq('role') }
end
context '99luftballoons' do
it { is_expected.to eq('default') }
end
end
asmodean :0: tmp/rnelson0 » bundle exec rspec test.rb --format documentation
puppet_role
test72
should eq "test"
role
should eq "role"
99luftballoons
should eq "default"
Finished in 0.00365 seconds (files took 0.14032 seconds to load)
3 examples, 0 failures
COVERAGE: 100.00% -- 10/10 lines in 1 files
@rodjek Thanks so much, I finally found time to get back to this and implement in production. Awesomesauce!
Woot, glad to hear it worked!
The coverage report generated for the test of a custom fact with multiple
setcode
points is dependent on the order of the tests; only the last test seems to count as covered. Given this custom fact:There are three
setcode
points that need tested. Running rspec without any tests for this fact results in:With this spec code:
This is the result:
By re-ordering the tests, I will receive different
missing
values, where all but the last test count asmissing
:Here is my Rakefile, spec_helper.rb, and bundle gem versions: