yardstick 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +10 -0
- data/LICENSE +20 -0
- data/README.markdown +117 -0
- data/Rakefile +24 -0
- data/VERSION +1 -0
- data/bin/yardstick +7 -0
- data/deps.rip +1 -0
- data/lib/yardstick/autoload.rb +16 -0
- data/lib/yardstick/cli.rb +76 -0
- data/lib/yardstick/core_ext/object.rb +13 -0
- data/lib/yardstick/measurable.rb +78 -0
- data/lib/yardstick/measurement.rb +217 -0
- data/lib/yardstick/measurement_set.rb +130 -0
- data/lib/yardstick/method.rb +111 -0
- data/lib/yardstick/ordered_set.rb +115 -0
- data/lib/yardstick/processor.rb +65 -0
- data/lib/yardstick/rake/measurement.rb +101 -0
- data/lib/yardstick/rake/verify.rb +179 -0
- data/lib/yardstick/rule.rb +61 -0
- data/lib/yardstick/rule_set.rb +18 -0
- data/lib/yardstick/yard_ext.rb +20 -0
- data/lib/yardstick.rb +52 -0
- data/spec/public/yardstick/cli_spec.rb +108 -0
- data/spec/public/yardstick/measurement_set_spec.rb +265 -0
- data/spec/public/yardstick/measurement_spec.rb +256 -0
- data/spec/public/yardstick/method_spec.rb +356 -0
- data/spec/public/yardstick/rake/measurement_spec.rb +173 -0
- data/spec/public/yardstick/rake/verify_spec.rb +229 -0
- data/spec/public/yardstick_spec.rb +70 -0
- data/spec/rcov.opts +6 -0
- data/spec/semipublic/yardstick/rule_spec.rb +28 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +45 -0
- data/tasks/ci.rake +1 -0
- data/tasks/heckle.rake +52 -0
- data/tasks/metrics.rake +5 -0
- data/tasks/rdoc.rake +15 -0
- data/tasks/spec.rake +22 -0
- data/tasks/yardstick.rake +9 -0
- data/yardstick.gemspec +90 -0
- metadata +113 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'rational'
|
2
|
+
|
3
|
+
module Yardstick
|
4
|
+
class MeasurementSet < OrderedSet
|
5
|
+
|
6
|
+
# The total number of measurements
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# measurements.total # => 570
|
10
|
+
#
|
11
|
+
# @return [Integer]
|
12
|
+
# total measurements
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
def total
|
16
|
+
length
|
17
|
+
end
|
18
|
+
|
19
|
+
# The number of successful measurements
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# measurements.successful # => 561
|
23
|
+
#
|
24
|
+
# @return [Integer]
|
25
|
+
# successful measurements
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
def successful
|
29
|
+
select { |measurement| measurement.ok? }.length
|
30
|
+
end
|
31
|
+
|
32
|
+
# The number of failed measurements
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# measurements.failed # => 9
|
36
|
+
#
|
37
|
+
# @return [Integer]
|
38
|
+
# failed measurements
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def failed
|
42
|
+
total - successful
|
43
|
+
end
|
44
|
+
|
45
|
+
# The percentage of successful measurements
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# coverage = measurements.coverage # => Rational(561, 570)
|
49
|
+
# '%.1f%%' % (coverage * 100) # => "98.4%"
|
50
|
+
#
|
51
|
+
# @return [Integer, Rational]
|
52
|
+
# the coverage percentage
|
53
|
+
#
|
54
|
+
# @api public
|
55
|
+
def coverage
|
56
|
+
empty? ? 0 : Rational(successful, total)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Warn the unsuccessful measurements and a summary
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# measurements.puts # (outputs measurements results and summary)
|
63
|
+
#
|
64
|
+
# @param [#puts] io
|
65
|
+
# optional object to puts the summary
|
66
|
+
#
|
67
|
+
# @return [undefined]
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
def puts(io = $stdout)
|
71
|
+
each { |measurement| measurement.puts(io) }
|
72
|
+
puts_summary(io)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# Warn the summary of all measurements
|
78
|
+
#
|
79
|
+
# @param [#puts] io
|
80
|
+
# object to puts the summary
|
81
|
+
#
|
82
|
+
# @return [undefined]
|
83
|
+
#
|
84
|
+
# @api private
|
85
|
+
def puts_summary(io)
|
86
|
+
io.puts("\n#{[ coverage_text, successful_text, failed_text, total_text ].join(' ')}")
|
87
|
+
end
|
88
|
+
|
89
|
+
# The text for the coverage percentage to include in the summary
|
90
|
+
#
|
91
|
+
# @return [String]
|
92
|
+
# the coverage text
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
def coverage_text
|
96
|
+
'Coverage: %.1f%%' % (coverage * 100)
|
97
|
+
end
|
98
|
+
|
99
|
+
# The text for the successful measurements to include in the summary
|
100
|
+
#
|
101
|
+
# @return [String]
|
102
|
+
# the successful text
|
103
|
+
#
|
104
|
+
# @api private
|
105
|
+
def successful_text
|
106
|
+
'Success: %d' % successful
|
107
|
+
end
|
108
|
+
|
109
|
+
# The text for the failed measurements to include in the summary
|
110
|
+
#
|
111
|
+
# @return [String]
|
112
|
+
# the failed text
|
113
|
+
#
|
114
|
+
# @api private
|
115
|
+
def failed_text
|
116
|
+
'Failed: %d' % failed
|
117
|
+
end
|
118
|
+
|
119
|
+
# The text for the total measurements to include in the summary
|
120
|
+
#
|
121
|
+
# @return [String]
|
122
|
+
# the total text
|
123
|
+
#
|
124
|
+
# @api private
|
125
|
+
def total_text
|
126
|
+
'Total: %d' % total
|
127
|
+
end
|
128
|
+
|
129
|
+
end # class MeasurementSet
|
130
|
+
end # module Yardstick
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Yardstick
|
2
|
+
module Method
|
3
|
+
include Measurable
|
4
|
+
|
5
|
+
rule 'The method summary should be specified' do
|
6
|
+
skip if has_tag?('see')
|
7
|
+
summary_text != ''
|
8
|
+
end
|
9
|
+
|
10
|
+
rule 'The method summary should be less than 80 characters in length' do
|
11
|
+
summary_text.split(//).length <= 80
|
12
|
+
end
|
13
|
+
|
14
|
+
rule 'The method summary should not end in a period' do
|
15
|
+
summary_text[-1, 1] != '.'
|
16
|
+
end
|
17
|
+
|
18
|
+
rule 'The method summary should be a single line' do
|
19
|
+
!summary_text.include?("\n")
|
20
|
+
end
|
21
|
+
|
22
|
+
rule 'The public/semipublic method should have an example specified' do
|
23
|
+
skip if api?(%w[ private ]) || tag_types('return') == %w[ undefined ]
|
24
|
+
has_tag?('example')
|
25
|
+
end
|
26
|
+
|
27
|
+
rule 'The @api tag should be specified' do
|
28
|
+
has_tag?('api')
|
29
|
+
end
|
30
|
+
|
31
|
+
rule 'The @api tag must be either public, semipublic or private' do
|
32
|
+
%w[ public semipublic private ].include?(tag_text('api'))
|
33
|
+
end
|
34
|
+
|
35
|
+
rule 'A method with protected visibility must have an @api tag of semipublic or private' do
|
36
|
+
skip unless visibility == :protected
|
37
|
+
api?(%w[ semipublic private ])
|
38
|
+
end
|
39
|
+
|
40
|
+
rule 'A method with private visibility must have an @api tag of private' do
|
41
|
+
skip unless visibility == :private
|
42
|
+
api?(%w[ private ])
|
43
|
+
end
|
44
|
+
|
45
|
+
rule 'The @return tag should be specified' do
|
46
|
+
has_tag?('return')
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# The raw text for the summary
|
52
|
+
#
|
53
|
+
# @return [String]
|
54
|
+
# the summary text
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
def summary_text
|
58
|
+
split(/\r?\n\r?\n/).first || ''
|
59
|
+
end
|
60
|
+
|
61
|
+
# The text for a specified tag
|
62
|
+
#
|
63
|
+
# @param [String] tag_name
|
64
|
+
# the name of the tag
|
65
|
+
#
|
66
|
+
# @return [String, nil]
|
67
|
+
# the tag text if the tag exists
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
def tag_text(tag_name)
|
71
|
+
tag(tag_name).text if has_tag?(tag_name)
|
72
|
+
end
|
73
|
+
|
74
|
+
# The types for a specified tag
|
75
|
+
#
|
76
|
+
# @param [String] tag_name
|
77
|
+
# the name of the tag
|
78
|
+
#
|
79
|
+
# @return [Array<String>, nil]
|
80
|
+
# a collection of tag types if the tag exists
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
def tag_types(tag_name)
|
84
|
+
tag(tag_name).types if has_tag?(tag_name)
|
85
|
+
end
|
86
|
+
|
87
|
+
# The method visibility: public, protected or private
|
88
|
+
#
|
89
|
+
# @return [Symbol]
|
90
|
+
# the visibility of the method
|
91
|
+
#
|
92
|
+
# @api private
|
93
|
+
def visibility
|
94
|
+
object.visibility
|
95
|
+
end
|
96
|
+
|
97
|
+
# Check if the method API type matches
|
98
|
+
#
|
99
|
+
# @param [Array<String>] types
|
100
|
+
# a collection of API types
|
101
|
+
#
|
102
|
+
# @return [Boolean]
|
103
|
+
# true if the API type matches
|
104
|
+
#
|
105
|
+
# @api private
|
106
|
+
def api?(types)
|
107
|
+
types.include?(tag_text('api'))
|
108
|
+
end
|
109
|
+
|
110
|
+
end # module Method
|
111
|
+
end # module Yardstick
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Yardstick
|
2
|
+
class OrderedSet
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
# Returns the OrderedSet instance
|
6
|
+
#
|
7
|
+
# @param [Array] entries
|
8
|
+
# optional entries
|
9
|
+
#
|
10
|
+
# @return [OrderedSet]
|
11
|
+
# the ordered set instance
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
def initialize(entries = nil)
|
15
|
+
@entries = []
|
16
|
+
@index = {}
|
17
|
+
merge(entries) if entries
|
18
|
+
end
|
19
|
+
|
20
|
+
# Append to the OrderedSet
|
21
|
+
#
|
22
|
+
# @param [Object] entry
|
23
|
+
# the object to append
|
24
|
+
#
|
25
|
+
# @return [OrderedSet]
|
26
|
+
# returns self
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
def <<(entry)
|
30
|
+
unless include?(entry)
|
31
|
+
@index[entry] = @entries.length
|
32
|
+
@entries << entry
|
33
|
+
end
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Merge in another OrderedSet
|
38
|
+
#
|
39
|
+
# @param [#each] other
|
40
|
+
# the other ordered set
|
41
|
+
#
|
42
|
+
# @return [OrderedSet]
|
43
|
+
# returns self
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
def merge(other)
|
47
|
+
other.each { |entry| self << entry }
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
# Iterate over each entry
|
52
|
+
#
|
53
|
+
# @yield [entry]
|
54
|
+
# yield to the entry
|
55
|
+
#
|
56
|
+
# @yieldparam [Object] entry
|
57
|
+
# an entry in the ordered set
|
58
|
+
#
|
59
|
+
# @return [OrderedSet]
|
60
|
+
# returns self
|
61
|
+
#
|
62
|
+
# @api private
|
63
|
+
def each(&block)
|
64
|
+
@entries.each(&block)
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Check if there are any entries
|
69
|
+
#
|
70
|
+
# @return [Boolean]
|
71
|
+
# true if there are no entries, false if there are
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
def empty?
|
75
|
+
@entries.empty?
|
76
|
+
end
|
77
|
+
|
78
|
+
# The number of entries
|
79
|
+
#
|
80
|
+
# @return [Integer]
|
81
|
+
# number of entries
|
82
|
+
#
|
83
|
+
# @api private
|
84
|
+
def length
|
85
|
+
@entries.length
|
86
|
+
end
|
87
|
+
|
88
|
+
# Check if the entry exists in the set
|
89
|
+
#
|
90
|
+
# @param [Object] entry
|
91
|
+
# the entry to test for
|
92
|
+
#
|
93
|
+
# @return [Boolean]
|
94
|
+
# true if the entry exists in the set, false if not
|
95
|
+
#
|
96
|
+
# @api private
|
97
|
+
def include?(entry)
|
98
|
+
@index.key?(entry)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Return the index for the entry in the set
|
102
|
+
#
|
103
|
+
# @param [Object] entry
|
104
|
+
# the entry to check the set for
|
105
|
+
#
|
106
|
+
# @return [Integer, nil]
|
107
|
+
# the index for the entry, or nil if it does not exist
|
108
|
+
#
|
109
|
+
# @api private
|
110
|
+
def index(entry)
|
111
|
+
@index[entry]
|
112
|
+
end
|
113
|
+
|
114
|
+
end # class OrderedSet
|
115
|
+
end # module Yardstick
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Yardstick
|
2
|
+
class Processor
|
3
|
+
|
4
|
+
# Measure files provided
|
5
|
+
#
|
6
|
+
# @param [Array<#to_s>, #to_s] path
|
7
|
+
# the files to measure
|
8
|
+
#
|
9
|
+
# @return [MeasurementSet]
|
10
|
+
# a collection of measurements
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def self.process_path(path)
|
14
|
+
YARD.parse(Array(path).map { |file| file.to_s })
|
15
|
+
measurements
|
16
|
+
end
|
17
|
+
|
18
|
+
# Measure string provided
|
19
|
+
#
|
20
|
+
# @param [#to_str] string
|
21
|
+
# the string to measure
|
22
|
+
#
|
23
|
+
# @return [MeasurementSet]
|
24
|
+
# a collection of measurements
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
def self.process_string(string)
|
28
|
+
YARD.parse_string(string.to_str)
|
29
|
+
measurements
|
30
|
+
end
|
31
|
+
|
32
|
+
# Measure method objects in YARD registry
|
33
|
+
#
|
34
|
+
# @return [MeasurementSet]
|
35
|
+
# a collection of measurements
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
def self.measurements
|
39
|
+
measurements = MeasurementSet.new
|
40
|
+
method_objects.each do |method_object|
|
41
|
+
measurements.merge(method_object.docstring.measure)
|
42
|
+
end
|
43
|
+
measurements
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return method objects in YARD registry
|
47
|
+
#
|
48
|
+
# @return [Array<YARD::CodeObjects::MethodObject>]
|
49
|
+
# a collection of method objects
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
def self.method_objects
|
53
|
+
YARD::Registry.all(:method).sort_by do |method_object|
|
54
|
+
[ method_object.file, method_object.line ]
|
55
|
+
end
|
56
|
+
ensure
|
57
|
+
YARD::Registry.clear
|
58
|
+
end
|
59
|
+
|
60
|
+
class << self
|
61
|
+
private :measurements, :method_objects
|
62
|
+
end
|
63
|
+
|
64
|
+
end # class Processor
|
65
|
+
end # module Yardstick
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
module Yardstick
|
5
|
+
module Rake
|
6
|
+
class Measurement < ::Rake::TaskLib
|
7
|
+
|
8
|
+
# List of paths to measure
|
9
|
+
#
|
10
|
+
# @param [Array<#to_s>, #to_s] path
|
11
|
+
# optional list of paths to measure
|
12
|
+
#
|
13
|
+
# @return [undefined]
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
attr_writer :path
|
17
|
+
|
18
|
+
# The path to the file where the measurements will be written
|
19
|
+
#
|
20
|
+
# @param [String, Pathname] output
|
21
|
+
# optional output path for measurements
|
22
|
+
#
|
23
|
+
# @return [undefined]
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def output=(output)
|
27
|
+
@output = Pathname(output)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Initializes a Measurement task
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# task = Yardstick::Rake::Measurement
|
34
|
+
#
|
35
|
+
# @param [Symbol] name
|
36
|
+
# optional task name
|
37
|
+
#
|
38
|
+
# @yield [task]
|
39
|
+
# yield to self
|
40
|
+
#
|
41
|
+
# @yieldparam [Yardstick::Rake::Measurement] task
|
42
|
+
# the measurement task
|
43
|
+
#
|
44
|
+
# @return [Yardstick::Rake::Measurement]
|
45
|
+
# the measurement task
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def initialize(name = :yardstick_measure)
|
49
|
+
@name = name
|
50
|
+
@path = 'lib/**/*.rb'
|
51
|
+
|
52
|
+
self.output = 'measurements/report.txt'
|
53
|
+
|
54
|
+
yield self if block_given?
|
55
|
+
|
56
|
+
define
|
57
|
+
end
|
58
|
+
|
59
|
+
# Measure the documentation
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# task.yardstick_measure # (output measurement report)
|
63
|
+
#
|
64
|
+
# @return [undefined]
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def yardstick_measure
|
68
|
+
write_report { |io| Yardstick.measure(@path).puts(io) }
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# Define the task
|
74
|
+
#
|
75
|
+
# @return [undefined]
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
def define
|
79
|
+
desc "Measure docs in #{@path} with yardstick"
|
80
|
+
task(@name) { yardstick_measure }
|
81
|
+
end
|
82
|
+
|
83
|
+
# Open up a report for writing
|
84
|
+
#
|
85
|
+
# @yield [io]
|
86
|
+
# yield to an object that responds to #puts
|
87
|
+
#
|
88
|
+
# @yieldparam [#puts] io
|
89
|
+
# the object that responds to #puts
|
90
|
+
#
|
91
|
+
# @return [undefined]
|
92
|
+
#
|
93
|
+
# @api private
|
94
|
+
def write_report(&block)
|
95
|
+
@output.dirname.mkpath
|
96
|
+
@output.open('w', &block)
|
97
|
+
end
|
98
|
+
|
99
|
+
end # class Measurement
|
100
|
+
end # module Rake
|
101
|
+
end # module Yardstick
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
module Yardstick
|
5
|
+
module Rake
|
6
|
+
class Verify < ::Rake::TaskLib
|
7
|
+
|
8
|
+
# Set the threshold
|
9
|
+
#
|
10
|
+
# @param [Number] threshold
|
11
|
+
# the threshold to set
|
12
|
+
#
|
13
|
+
# @return [undefined]
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
attr_writer :threshold
|
17
|
+
|
18
|
+
# Specify if the threshold should match the coverage
|
19
|
+
#
|
20
|
+
# @param [Boolean] require_exact_threshold
|
21
|
+
# true if the threshold should match the coverage, false if not
|
22
|
+
#
|
23
|
+
# @return [undefined]
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
attr_writer :require_exact_threshold
|
27
|
+
|
28
|
+
# List of paths to measure
|
29
|
+
#
|
30
|
+
# @param [Array<#to_s>, #to_s] path
|
31
|
+
# optional list of paths to measure
|
32
|
+
#
|
33
|
+
# @return [undefined]
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
attr_writer :path
|
37
|
+
|
38
|
+
# Specify if the coverage summary should be displayed
|
39
|
+
#
|
40
|
+
# @param [Boolean] verbose
|
41
|
+
# true if the coverage summary should be displayed, false if not
|
42
|
+
#
|
43
|
+
# @return [undefined]
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
attr_writer :verbose
|
47
|
+
|
48
|
+
# Initialize a Verify task
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# task = Yardstick::Rake::Verify.new do |task|
|
52
|
+
# task.threshold = 100
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# @param [Symbol] name
|
56
|
+
# optional task name
|
57
|
+
#
|
58
|
+
# @yield [task]
|
59
|
+
# yield to self
|
60
|
+
#
|
61
|
+
# @yieldparam [Yardstick::Rake::Verify] task
|
62
|
+
# the verification task
|
63
|
+
#
|
64
|
+
# @return [Yardstick::Rake::Verify] task
|
65
|
+
# the verification task instance
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def initialize(name = :verify_measurements)
|
69
|
+
@name = name
|
70
|
+
@require_exact_threshold = true
|
71
|
+
@path = 'lib/**/*.rb'
|
72
|
+
@verbose = true
|
73
|
+
|
74
|
+
yield self
|
75
|
+
|
76
|
+
assert_threshold
|
77
|
+
define
|
78
|
+
end
|
79
|
+
|
80
|
+
# Verify the YARD coverage measurements
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# task.verify_measurements # output coverage and threshold
|
84
|
+
#
|
85
|
+
# @return [undefined]
|
86
|
+
#
|
87
|
+
# @raise [RuntimeError]
|
88
|
+
# raised if threshold is not met
|
89
|
+
# @raise [RuntimeError]
|
90
|
+
# raised if threshold is not equal to the coverage
|
91
|
+
#
|
92
|
+
# @api public
|
93
|
+
def verify_measurements
|
94
|
+
puts "Coverage: #{total_coverage}% (threshold: #{@threshold}%)" if verbose
|
95
|
+
assert_meets_threshold
|
96
|
+
assert_matches_threshold
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# Define the task
|
102
|
+
#
|
103
|
+
# @return [undefined]
|
104
|
+
#
|
105
|
+
# @api private
|
106
|
+
def define
|
107
|
+
desc "Verify that yardstick coverage is at least #{@threshold}%"
|
108
|
+
task(@name) { verify_measurements }
|
109
|
+
end
|
110
|
+
|
111
|
+
# The total YARD coverage
|
112
|
+
#
|
113
|
+
# @return [Float]
|
114
|
+
# the total coverage
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
def total_coverage
|
118
|
+
measurements = Yardstick.measure(@path)
|
119
|
+
round_percentage(measurements.coverage * 100)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Round percentage to 1/10th of a percent
|
123
|
+
#
|
124
|
+
# @param [Float] percentage
|
125
|
+
# the percentage to round
|
126
|
+
#
|
127
|
+
# @return [Float]
|
128
|
+
# the rounded percentage
|
129
|
+
#
|
130
|
+
# @api private
|
131
|
+
def round_percentage(percentage)
|
132
|
+
(percentage * 10).ceil / 10.0
|
133
|
+
end
|
134
|
+
|
135
|
+
# Raise an exception if threshold is not set
|
136
|
+
#
|
137
|
+
# @return [undefined]
|
138
|
+
#
|
139
|
+
# @raise [RuntimeError]
|
140
|
+
# raised if threshold is not set
|
141
|
+
#
|
142
|
+
# @api private
|
143
|
+
def assert_threshold
|
144
|
+
if @threshold.nil?
|
145
|
+
raise 'threshold must be set'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Raise an exception if the threshold is not met
|
150
|
+
#
|
151
|
+
# @return [undefined]
|
152
|
+
#
|
153
|
+
# @raise [RuntimeError]
|
154
|
+
# raised if threshold is not met
|
155
|
+
#
|
156
|
+
# @api private
|
157
|
+
def assert_meets_threshold
|
158
|
+
if total_coverage < @threshold
|
159
|
+
raise "Coverage must be at least #{@threshold}% but was #{total_coverage}%"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Raise an exception if the threshold is not equal to the coverage
|
164
|
+
#
|
165
|
+
# @return [undefined]
|
166
|
+
#
|
167
|
+
# @raise [RuntimeError]
|
168
|
+
# raised if threshold is not equal to the coverage
|
169
|
+
#
|
170
|
+
# @api private
|
171
|
+
def assert_matches_threshold
|
172
|
+
if @require_exact_threshold && total_coverage > @threshold
|
173
|
+
raise "Coverage has increased above the threshold of #{@threshold}% to #{total_coverage}%. You should update your threshold value."
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
end # class Verify
|
178
|
+
end # module Rake
|
179
|
+
end # module Yardstick
|