simplecov 0.4.2 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. data/.gitignore +4 -1
  2. data/.rvmrc +1 -1
  3. data/.travis.yml +9 -0
  4. data/CHANGELOG.md +64 -0
  5. data/Gemfile +7 -0
  6. data/README.rdoc +83 -66
  7. data/Rakefile +3 -7
  8. data/cucumber.yml +13 -0
  9. data/features/config_adapters.feature +44 -0
  10. data/features/config_autoload.feature +46 -0
  11. data/features/config_command_name.feature +33 -0
  12. data/features/config_coverage_dir.feature +20 -0
  13. data/features/config_deactivate_merging.feature +42 -0
  14. data/features/config_merge_timeout.feature +38 -0
  15. data/features/config_project_name.feature +27 -0
  16. data/features/config_styles.feature +93 -0
  17. data/features/cucumber_basic.feature +29 -0
  18. data/features/merging_test_unit_and_rspec.feature +44 -0
  19. data/features/rspec_basic.feature +31 -0
  20. data/features/rspec_groups_and_filters_basic.feature +29 -0
  21. data/features/rspec_groups_and_filters_complex.feature +35 -0
  22. data/features/rspec_without_simplecov.feature +20 -0
  23. data/features/step_definitions/html_steps.rb +42 -0
  24. data/features/step_definitions/simplecov_steps.rb +61 -0
  25. data/features/step_definitions/transformers.rb +13 -0
  26. data/features/step_definitions/web_steps.rb +64 -0
  27. data/features/support/env.rb +26 -0
  28. data/features/test_unit_basic.feature +34 -0
  29. data/features/test_unit_groups_and_filters_basic.feature +29 -0
  30. data/features/test_unit_groups_and_filters_complex.feature +35 -0
  31. data/features/test_unit_without_simplecov.feature +20 -0
  32. data/lib/simplecov.rb +15 -30
  33. data/lib/simplecov/adapters.rb +3 -26
  34. data/lib/simplecov/command_guesser.rb +2 -2
  35. data/lib/simplecov/configuration.rb +21 -21
  36. data/lib/simplecov/defaults.rb +48 -0
  37. data/lib/simplecov/file_list.rb +44 -0
  38. data/lib/simplecov/filter.rb +5 -5
  39. data/lib/simplecov/formatter.rb +1 -1
  40. data/lib/simplecov/formatter/simple_formatter.rb +1 -1
  41. data/lib/simplecov/jruby_float_fix.rb +1 -1
  42. data/lib/simplecov/merge_helpers.rb +4 -4
  43. data/lib/simplecov/result.rb +33 -30
  44. data/lib/simplecov/result_merger.rb +30 -13
  45. data/lib/simplecov/source_file.rb +78 -21
  46. data/lib/simplecov/version.rb +1 -1
  47. data/simplecov.gemspec +25 -22
  48. data/test/faked_project/Gemfile +6 -0
  49. data/test/faked_project/Rakefile +8 -0
  50. data/test/faked_project/cucumber.yml +13 -0
  51. data/test/faked_project/features/step_definitions/my_steps.rb +23 -0
  52. data/test/faked_project/features/support/env.rb +12 -0
  53. data/test/faked_project/features/test_stuff.feature +6 -0
  54. data/test/faked_project/lib/faked_project.rb +11 -0
  55. data/test/faked_project/lib/faked_project/framework_specific.rb +18 -0
  56. data/test/faked_project/lib/faked_project/meta_magic.rb +24 -0
  57. data/test/faked_project/lib/faked_project/some_class.rb +29 -0
  58. data/test/faked_project/spec/faked_spec.rb +11 -0
  59. data/test/faked_project/spec/meta_magic_spec.rb +10 -0
  60. data/test/faked_project/spec/some_class_spec.rb +10 -0
  61. data/test/faked_project/spec/spec_helper.rb +15 -0
  62. data/test/faked_project/test/faked_test.rb +11 -0
  63. data/test/faked_project/test/meta_magic_test.rb +13 -0
  64. data/test/faked_project/test/some_class_test.rb +15 -0
  65. data/test/faked_project/test/test_helper.rb +16 -0
  66. data/test/fixtures/app/controllers/sample_controller.rb +2 -2
  67. data/test/fixtures/app/models/user.rb +2 -2
  68. data/test/fixtures/frameworks/rspec_bad.rb +1 -1
  69. data/test/fixtures/frameworks/rspec_good.rb +1 -1
  70. data/test/fixtures/frameworks/testunit_bad.rb +1 -1
  71. data/test/fixtures/frameworks/testunit_good.rb +1 -1
  72. data/test/fixtures/resultset1.rb +1 -1
  73. data/test/fixtures/resultset2.rb +1 -1
  74. data/test/fixtures/sample.rb +8 -2
  75. data/test/helper.rb +17 -4
  76. data/test/shoulda_macros.rb +2 -2
  77. data/test/test_1_8_fallbacks.rb +3 -3
  78. data/test/test_command_guesser.rb +3 -3
  79. data/test/test_file_list.rb +24 -0
  80. data/test/test_filters.rb +18 -13
  81. data/test/test_merge_helpers.rb +23 -23
  82. data/test/test_result.rb +40 -31
  83. data/test/test_return_codes.rb +5 -5
  84. data/test/test_source_file.rb +39 -15
  85. data/test/test_source_file_line.rb +22 -22
  86. metadata +191 -53
  87. data/.document +0 -5
@@ -4,4 +4,4 @@ module SimpleCov
4
4
  end
5
5
  end
6
6
 
7
- require 'simplecov/formatter/simple_formatter'
7
+ require 'simplecov/formatter/simple_formatter'
@@ -16,4 +16,4 @@ class SimpleCov::Formatter::SimpleFormatter
16
16
  end
17
17
  output
18
18
  end
19
- end
19
+ end
@@ -11,4 +11,4 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby' and RUBY_VERSION == '1.9.2'
11
11
  end
12
12
  end
13
13
  end
14
- end
14
+ end
@@ -2,11 +2,11 @@ module SimpleCov::ArrayMergeHelper
2
2
  # Merges an array of coverage results with self
3
3
  def merge_resultset(array)
4
4
  new_array = []
5
-
5
+
6
6
  self.each_with_index do |element, i|
7
7
  new_array[i] = element
8
8
  end
9
-
9
+
10
10
  array.each_with_index do |element, i|
11
11
  if element.nil? and new_array[i].nil?
12
12
  new_array[i] = nil
@@ -27,7 +27,7 @@ module SimpleCov::HashMergeHelper
27
27
  (self.keys + hash.keys).each do |filename|
28
28
  new_resultset[filename] = []
29
29
  end
30
-
30
+
31
31
  new_resultset.each do |filename, data|
32
32
  new_resultset[filename] = (self[filename] || []).merge_resultset(hash[filename] || [])
33
33
  end
@@ -36,4 +36,4 @@ module SimpleCov::HashMergeHelper
36
36
  end
37
37
 
38
38
  Array.send :include, SimpleCov::ArrayMergeHelper
39
- Hash.send :include, SimpleCov::HashMergeHelper
39
+ Hash.send :include, SimpleCov::HashMergeHelper
@@ -1,5 +1,4 @@
1
1
  require 'digest/sha1'
2
- require 'yaml'
3
2
 
4
3
  module SimpleCov
5
4
  #
@@ -21,28 +20,42 @@ module SimpleCov
21
20
  # coverage data)
22
21
  def initialize(original_result)
23
22
  @original_result = original_result.freeze
24
- @files = original_result.map {|filename, coverage|
23
+ @files = SimpleCov::FileList.new(original_result.map do |filename, coverage|
25
24
  SimpleCov::SourceFile.new(filename, coverage) if File.file?(filename)
26
- }.compact.sort_by(&:filename)
25
+ end.compact.sort_by(&:filename))
27
26
  filter!
28
27
  end
29
-
28
+
30
29
  # Returns all filenames for source files contained in this result
31
30
  def filenames
32
31
  files.map(&:filename)
33
32
  end
34
-
33
+
35
34
  # Returns a Hash of groups for this result. Define groups using SimpleCov.add_group 'Models', 'app/models'
36
35
  def groups
37
36
  @groups ||= SimpleCov.grouped(files)
38
37
  end
39
-
38
+
40
39
  # The overall percentual coverage for this result
41
40
  def covered_percent
42
41
  # Make sure that weird rounding error from #15, #23 and #24 does not occur again!
43
42
  total_lines.zero? ? 0 : 100.0 * covered_lines / total_lines
44
43
  end
45
-
44
+
45
+ # The multiple of coverage for this result
46
+ def covered_strength
47
+ return @covered_strength if @convered_strength
48
+ m = 0
49
+ @files.each do |file|
50
+ original_result[file.filename].each do |line_result|
51
+ if line_result
52
+ m += line_result
53
+ end
54
+ end
55
+ end
56
+ @covered_strength = m.to_f / total_lines
57
+ end
58
+
46
59
  # Returns the count of lines that are covered
47
60
  def covered_lines
48
61
  return @covered_lines if @covered_lines
@@ -54,7 +67,7 @@ module SimpleCov
54
67
  end
55
68
  @covered_lines
56
69
  end
57
-
70
+
58
71
  # Returns the count of missed lines
59
72
  def missed_lines
60
73
  return @missed_lines if @missed_lines
@@ -66,54 +79,44 @@ module SimpleCov
66
79
  end
67
80
  @missed_lines
68
81
  end
69
-
82
+
70
83
  # Total count of relevant lines (covered + missed)
71
84
  def total_lines
72
85
  @total_lines ||= (covered_lines + missed_lines)
73
86
  end
74
-
87
+
75
88
  # Applies the configured SimpleCov.formatter on this result
76
89
  def format!
77
90
  SimpleCov.formatter.new.format(self)
78
91
  end
79
-
92
+
80
93
  # Defines when this result has been created. Defaults to Time.now
81
94
  def created_at
82
95
  @created_at ||= Time.now
83
96
  end
84
-
97
+
85
98
  # The command name that launched this result.
86
- # Retrieved from SimpleCov.command_name
99
+ # Delegated to SimpleCov.command_name if not set manually
87
100
  def command_name
88
101
  @command_name ||= SimpleCov.command_name
89
102
  end
90
-
103
+
91
104
  # Returns a hash representation of this Result that can be used for marshalling it into YAML
92
105
  def to_hash
93
- {command_name => {:original_result => original_result.reject {|filename, result| !filenames.include?(filename) }, :created_at => created_at}}
106
+ {command_name => {"coverage" => original_result.reject {|filename, result| !filenames.include?(filename) }, "timestamp" => created_at.to_i}}
94
107
  end
95
-
96
- # Returns a yaml dump of this result, which then can be reloaded using SimpleCov::Result.from_yaml
97
- def to_yaml
98
- to_hash.to_yaml
99
- end
100
-
108
+
101
109
  # Loads a SimpleCov::Result#to_hash dump
102
110
  def self.from_hash(hash)
103
111
  command_name, data = hash.first
104
- result = SimpleCov::Result.new(data[:original_result])
112
+ result = SimpleCov::Result.new(data["coverage"])
105
113
  result.command_name = command_name
106
- result.created_at = data[:created_at]
114
+ result.created_at = Time.at(data["timestamp"])
107
115
  result
108
116
  end
109
-
110
- # Loads a SimpleCov::Result#to_yaml dump
111
- def self.from_yaml(yaml)
112
- from_hash(YAML.load(yaml))
113
- end
114
-
117
+
115
118
  private
116
-
119
+
117
120
  # Applies all configured SimpleCov filters on this result's source files
118
121
  def filter!
119
122
  @files = SimpleCov.filtered(files)
@@ -1,4 +1,5 @@
1
- require 'yaml'
1
+ require 'multi_json'
2
+
2
3
  #
3
4
  # Singleton that is responsible for caching, loading and merging
4
5
  # SimpleCov::Results into a single result for coverage analysis based
@@ -8,22 +9,34 @@ module SimpleCov::ResultMerger
8
9
  class << self
9
10
  # The path to the resultset.yml cache file
10
11
  def resultset_path
11
- File.join(SimpleCov.coverage_path, 'resultset.yml')
12
+ File.join(SimpleCov.coverage_path, '.resultset.json')
12
13
  end
13
-
14
+
14
15
  # Loads the cached resultset from YAML and returns it as a Hash
15
16
  def resultset
16
- return {} unless File.exist?(resultset_path)
17
- YAML.load(File.read(resultset_path)) || {}
17
+ if stored_data
18
+ MultiJson.decode(stored_data)
19
+ else
20
+ {}
21
+ end
22
+ end
23
+
24
+ # Returns the contents of the resultset cache as a string or if the file is missing or empty nil
25
+ def stored_data
26
+ if File.exist?(resultset_path) and stored_data = File.read(resultset_path) and stored_data.length >= 2
27
+ stored_data
28
+ else
29
+ nil
30
+ end
18
31
  end
19
-
32
+
20
33
  # Gets the resultset hash and re-creates all included instances
21
34
  # of SimpleCov::Result from that.
22
35
  # All results that are above the SimpleCov.merge_timeout will be
23
36
  # dropped. Returns an array of SimpleCov::Result items.
24
37
  def results
25
38
  results = []
26
- resultset.each do |command_name, data|
39
+ resultset.each do |command_name, data|
27
40
  result = SimpleCov::Result.from_hash(command_name => data)
28
41
  # Only add result if the timeout is above the configured threshold
29
42
  if (Time.now - result.created_at) < SimpleCov.merge_timeout
@@ -32,10 +45,10 @@ module SimpleCov::ResultMerger
32
45
  end
33
46
  results
34
47
  end
35
-
48
+
36
49
  #
37
50
  # Gets all SimpleCov::Results from cache, merges them and produces a new
38
- # SimpleCov::Result with merged coverage data and the command_name
51
+ # SimpleCov::Result with merged coverage data and the command_name
39
52
  # for the result consisting of a join on all source result's names
40
53
  #
41
54
  def merged_result
@@ -45,19 +58,23 @@ module SimpleCov::ResultMerger
45
58
  end
46
59
  result = SimpleCov::Result.new(merged)
47
60
  # Specify the command name
48
- result.command_name = results.map(&:command_name).join(", ")
61
+ result.command_name = results.map(&:command_name).sort.join(", ")
49
62
  result
50
63
  end
51
-
64
+
52
65
  # Saves the given SimpleCov::Result in the resultset cache
53
66
  def store_result(result)
54
67
  new_set = resultset
55
68
  command_name, data = result.to_hash.first
56
69
  new_set[command_name] = data
57
70
  File.open(resultset_path, "w+") do |f|
58
- f.puts new_set.to_yaml
71
+ if defined? ::JSON
72
+ f.puts JSON.pretty_generate(new_set)
73
+ else
74
+ f.puts MultiJson.encode(new_set)
75
+ end
59
76
  end
60
77
  true
61
78
  end
62
79
  end
63
- end
80
+ end
@@ -7,7 +7,7 @@ module SimpleCov
7
7
  # Representation of a single line in a source file including
8
8
  # this specific line's source code, line_number and code coverage,
9
9
  # with the coverage being either nil (coverage not applicable, e.g. comment
10
- # line), 0 (line not covered) or >1 (the amount of times the line was
10
+ # line), 0 (line not covered) or >1 (the amount of times the line was
11
11
  # executed)
12
12
  class Line
13
13
  # The source code for this line. Aliased as :source
@@ -16,34 +16,41 @@ module SimpleCov
16
16
  attr_reader :line_number
17
17
  # The coverage data for this line: either nil (never), 0 (missed) or >=1 (times covered)
18
18
  attr_reader :coverage
19
+ # Whether this line was skipped
20
+ attr_reader :skipped
21
+
19
22
  # Lets grab some fancy aliases, shall we?
20
23
  alias_method :source, :src
21
24
  alias_method :line, :line_number
22
25
  alias_method :number, :line_number
23
-
24
- def initialize(src, line_number, coverage)
26
+
27
+ def initialize(src, line_number, coverage, skipped = false)
25
28
  raise ArgumentError, "Only String accepted for source" unless src.kind_of?(String)
26
29
  raise ArgumentError, "Only Fixnum accepted for line_number" unless line_number.kind_of?(Fixnum)
27
30
  raise ArgumentError, "Only Fixnum and nil accepted for coverage" unless coverage.kind_of?(Fixnum) or coverage.nil?
28
- @src, @line_number, @coverage = src, line_number, coverage
31
+ @src, @line_number, @coverage, @skipped = src, line_number, coverage, skipped
29
32
  end
30
-
33
+
31
34
  # Returns true if this is a line that should have been covered, but was not
32
35
  def missed?
33
- not never? and coverage == 0
36
+ not never? and not skipped? and coverage == 0
34
37
  end
35
-
38
+
36
39
  # Returns true if this is a line that has been covered
37
40
  def covered?
38
- not never? and coverage > 0
41
+ not never? and not skipped? and coverage > 0
39
42
  end
40
-
43
+
41
44
  # Returns true if this line is not relevant for coverage
42
45
  def never?
43
- coverage.nil?
46
+ not skipped? and coverage.nil?
47
+ end
48
+
49
+ def skipped?
50
+ @skipped
44
51
  end
45
52
  end
46
-
53
+
47
54
  # The full path to this source file (e.g. /User/colszowka/projects/simplecov/lib/simplecov/source_file.rb)
48
55
  attr_reader :filename
49
56
  # The array of coverage data received from the Coverage.result
@@ -51,50 +58,100 @@ module SimpleCov
51
58
  # The source code for this file. Aliased as :source
52
59
  attr_reader :src
53
60
  alias_method :source, :src
54
-
61
+
55
62
  def initialize(filename, coverage)
56
63
  @filename, @coverage, @src = filename, coverage, File.readlines(filename)
64
+ @skipped_line_numbers = process_skipped_lines
57
65
  end
58
-
66
+
59
67
  # Returns all source lines for this file as instances of SimpleCov::SourceFile::Line,
60
68
  # and thus including coverage data. Aliased as :source_lines
61
69
  def lines
62
70
  return @lines unless @lines.nil?
71
+
72
+ # Warning to identify condition from Issue #56
73
+ if coverage.size > src.size
74
+ $stderr.puts "Warning: coverage data provided by Coverage [#{coverage.size}] exceeds number of lines in #{filename} [#{src.size}]"
75
+ end
76
+
63
77
  # Initialize lines
64
78
  @lines = []
65
- coverage.each_with_index do |coverage, i|
66
- @lines << SimpleCov::SourceFile::Line.new(src[i], i+1, coverage)
79
+ src.each_with_index do |src, i|
80
+ @lines << SimpleCov::SourceFile::Line.new(src, i+1, coverage[i], @skipped_line_numbers.include?(i + 1))
67
81
  end
68
82
  @lines
69
83
  end
70
84
  alias_method :source_lines, :lines
71
-
85
+
72
86
  # Access SimpleCov::SourceFile::Line source lines by line number
73
87
  def line(number)
74
88
  lines[number-1]
75
89
  end
76
-
90
+
77
91
  # The coverage for this file in percent. 0 if the file has no relevant lines
78
92
  def covered_percent
79
93
  return 100.0 if lines.length == 0 or lines.length == never_lines.count
80
- (covered_lines.count) * 100 / (lines.count-never_lines.count).to_f
94
+ (covered_lines.count) * 100 / (lines.count - never_lines.count - skipped_lines.count).to_f
81
95
  end
82
-
96
+
97
+ #
98
+ def covered_strength
99
+ return 0 if lines.length == 0 or lines.length == never_lines.count
100
+ lines_strength = 0
101
+ lines.each do |c|
102
+ lines_strength += c.coverage if c.coverage
103
+ end
104
+ effective_lines_count = (lines.count - never_lines.count - skipped_lines.count).to_f
105
+ strength = lines_strength / effective_lines_count
106
+ round_float(strength, 1)
107
+ end
108
+
83
109
  # Returns all covered lines as SimpleCov::SourceFile::Line
84
110
  def covered_lines
85
111
  @covered_lines ||= lines.select {|c| c.covered? }
86
112
  end
87
-
113
+
88
114
  # Returns all lines that should have been, but were not covered
89
115
  # as instances of SimpleCov::SourceFile::Line
90
116
  def missed_lines
91
117
  @missed_lines ||= lines.select {|c| c.missed? }
92
118
  end
93
-
119
+
94
120
  # Returns all lines that are not relevant for coverage as
95
121
  # SimpleCov::SourceFile::Line instances
96
122
  def never_lines
97
123
  @never_lines ||= lines.select {|c| c.never? }
98
124
  end
125
+
126
+ # Returns all lines that were skipped as SimpleCov::SourceFile::Line instances
127
+ def skipped_lines
128
+ @skipped_lines ||= lines.select {|c| c.skipped? }
129
+ end
130
+
131
+ # Returns the number of relevant lines (covered + missed)
132
+ def lines_of_code
133
+ covered_lines.count + missed_lines.count
134
+ end
135
+
136
+ def process_skipped_lines
137
+ skipped_line_numbers = []
138
+ skipping = false
139
+ @src.each_with_index do |line, i|
140
+ if line =~ /^\s*#\:nocov\:/
141
+ skipping = !skipping
142
+ else
143
+ skipped_line_numbers << i + 1 if skipping
144
+ end
145
+ end
146
+ skipped_line_numbers
147
+ end
148
+
149
+ private
150
+
151
+ # ruby 1.9 could use Float#round(places) instead
152
+ def round_float(float, places)
153
+ factor = (10 * places).to_f
154
+ (float * factor).round / factor
155
+ end
99
156
  end
100
157
  end
@@ -1,3 +1,3 @@
1
1
  module SimpleCov
2
- VERSION = "0.4.2"
2
+ VERSION = "0.5.2"
3
3
  end
data/simplecov.gemspec CHANGED
@@ -1,25 +1,28 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "simplecov/version"
1
+ # encoding: utf-8
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'simplecov/version'
4
4
 
5
- Gem::Specification.new do |s|
6
- s.name = "simplecov"
7
- s.version = SimpleCov::VERSION
8
- s.platform = Gem::Platform::RUBY
9
- s.authors = ["Christoph Olszowka"]
10
- s.email = ["christoph at olszowka de"]
11
- s.homepage = "http://github.com/colszowka/simplecov"
12
- s.summary = %Q{Code coverage for Ruby 1.9 with a powerful configuration library and automatic merging of coverage across test suites}
13
- s.description = %Q{Code coverage for Ruby 1.9 with a powerful configuration library and automatic merging of coverage across test suites}
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'simplecov'
7
+ gem.version = SimpleCov::VERSION
8
+ gem.platform = Gem::Platform::RUBY
9
+ gem.authors = ["Christoph Olszowka"]
10
+ gem.email = ['christoph at olszowka de']
11
+ gem.homepage = 'http://github.com/colszowka/simplecov'
12
+ gem.description = %Q{Code coverage for Ruby 1.9 with a powerful configuration library and automatic merging of coverage across test suites}
13
+ gem.summary = gem.description
14
14
 
15
- s.rubyforge_project = "simplecov"
16
-
17
- s.add_dependency 'simplecov-html', "~> 0.4.4"
18
- s.add_development_dependency "shoulda", "2.10.3"
19
- s.add_development_dependency "rspec", "~> 2.0.0"
15
+ gem.add_dependency 'multi_json', '~> 1.0.3'
16
+ gem.add_dependency 'simplecov-html', '~> 0.5.0'
17
+ gem.add_development_dependency 'aruba', '~> 0.4'
18
+ gem.add_development_dependency 'capybara', '~> 1.0'
19
+ gem.add_development_dependency 'cucumber', '~> 1.0'
20
+ gem.add_development_dependency 'rake', '~> 0.8'
21
+ gem.add_development_dependency 'rspec', '~> 2.6'
22
+ gem.add_development_dependency 'shoulda', '~> 2.10'
20
23
 
21
- s.files = `git ls-files`.split("\n")
22
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
- s.require_paths = ["lib"]
25
- end
24
+ gem.files = `git ls-files`.split("\n")
25
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ gem.require_paths = ['lib']
28
+ end