Class: ChefSpec::Coverage

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/chefspec/coverage.rb,
lib/chefspec/coverage/filters.rb

Defined Under Namespace

Classes: BerkshelfFilter, BlockFilter, Filter, RegexpFilter, ResourceWrapper, StringFilter

Constant Summary collapse

EXIT_FAILURE =
1
EXIT_SUCCESS =
0

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCoverage

Create a new coverage object singleton.


28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/chefspec/coverage.rb', line 28

def initialize
  @collection = {}
  @filters    = {}
  @outputs    = []
  add_output do |report|
    begin
      erb = Erubis::Eruby.new(File.read(@template))
      puts erb.evaluate(report)
    rescue NameError => e
      raise Error::ErbTemplateParseError.new(original_error: e.message)
    end
  end
  @template = ChefSpec.root.join('templates', 'coverage', 'human.erb')
end

Instance Attribute Details

#filtersObject (readonly)

Returns the value of attribute filters


23
24
25
# File 'lib/chefspec/coverage.rb', line 23

def filters
  @filters
end

Class Method Details

.method_added(name) ⇒ Object


9
10
11
12
13
14
15
16
17
18
# File 'lib/chefspec/coverage.rb', line 9

def method_added(name)
  # Only delegate public methods
  if method_defined?(name)
    instance_eval <<-EOH, __FILE__, __LINE__ + 1
      def #{name}(*args, &block)
        instance.public_send(:#{name}, *args, &block)
      end
    EOH
  end
end

Instance Method Details

#add(resource) ⇒ Object

Add a resource to the resource collection. Only new resources are added and only resources that match the given filter are covered (which is * by default).

Parameters:


119
120
121
122
123
# File 'lib/chefspec/coverage.rb', line 119

def add(resource)
  if !exists?(resource) && !filtered?(resource)
    @collection[resource.to_s] = ResourceWrapper.new(resource)
  end
end

#add_filter(filter = nil, &block) ⇒ true

Add a filter to the coverage analysis.

Parameters:

  • filter (Filter, String, Regexp) (defaults to: nil)

    the filter to add

  • block (Proc)

    the block to use as a filter

Returns:

  • (true)

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/chefspec/coverage.rb', line 62

def add_filter(filter = nil, &block)
  id = "#{filter.inspect}/#{block.inspect}".hash

  @filters[id] = if filter.kind_of?(Filter)
                   filter
                 elsif filter.kind_of?(String)
                   StringFilter.new(filter)
                 elsif filter.kind_of?(Regexp)
                   RegexpFilter.new(filter)
                 elsif block
                   BlockFilter.new(block)
                 else
                   raise ArgumentError, 'Please specify either a string, ' \
                     'filter, or block to filter source files with!'
                 end

  true
end

#add_output(&block) ⇒ true

Add an output to send the coverage results to.

Parameters:

  • block (Proc)

    the block to use as the output

Returns:

  • (true)

88
89
90
# File 'lib/chefspec/coverage.rb', line 88

def add_output(&block)
  @outputs << block
end

#cover!(resource) ⇒ Object

Called when a resource is matched to indicate it has been tested.

Parameters:


130
131
132
133
134
# File 'lib/chefspec/coverage.rb', line 130

def cover!(resource)
  if wrapper = find(resource)
    wrapper.touch!
  end
end

#filtered?(resource) ⇒ Boolean

Called to check if a resource belongs to a cookbook from the specified directories.

Parameters:

Returns:

  • (Boolean)

142
143
144
# File 'lib/chefspec/coverage.rb', line 142

def filtered?(resource)
  filters.any? { |_, filter| filter.matches?(resource) }
end

#report!Object

Generate a coverage report. This report *must* be generated at_exit or else the entire resource collection may not be complete!

Examples:

Generating a report


ChefSpec::Coverage.report!

154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/chefspec/coverage.rb', line 154

def report!
  # Borrowed from simplecov#41
  #
  # If an exception is thrown that isn't a "SystemExit", we need to capture
  # that error and re-raise.
  if $!
    exit_status = $!.is_a?(SystemExit) ? $!.status : EXIT_FAILURE
  else
    exit_status = EXIT_SUCCESS
  end

  report = {}.tap do |h|
    h[:total]     = @collection.size
    h[:touched]   = @collection.count { |_, resource| resource.touched? }
    h[:coverage]  = ((h[:touched]/h[:total].to_f)*100).round(2)
  end

  report[:untouched_resources] = @collection.collect do |_, resource|
    resource unless resource.touched?
  end.compact
  report[:all_resources] = @collection.values

  @outputs.each do |block|
    self.instance_exec(report, &block)
  end

  # Ensure we exit correctly (#351)
  Kernel.exit(exit_status) if exit_status && exit_status > 0
end

#set_template(file = 'human.erb') ⇒ true

Change the template for reporting of converage analysis.

Parameters:

  • path (string)

    The template file to use for the output of the report

Returns:

  • (true)

Raises:


100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/chefspec/coverage.rb', line 100

def set_template(file = 'human.erb')
  [
    ChefSpec.root.join('templates', 'coverage', file),
    File.expand_path(file, Dir.pwd)
  ].each do |temp|
    if File.exist?(temp)
      @template = temp
      return
    end
  end
  raise Error::TemplateNotFound.new(path: file)
end

#start!(&block) ⇒ Object

Start the coverage reporting analysis. This method also adds the the at_exit handler for printing the coverage report.


47
48
49
50
# File 'lib/chefspec/coverage.rb', line 47

def start!(&block)
  instance_eval(&block) if block
  at_exit { ChefSpec::Coverage.report! }
end