rspec-tracer 0.9.3 → 1.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +27 -2
- data/lib/rspec_tracer/cache.rb +75 -40
- data/lib/rspec_tracer/configuration.rb +16 -9
- data/lib/rspec_tracer/coverage_merger.rb +41 -0
- data/lib/rspec_tracer/coverage_reporter.rb +31 -28
- data/lib/rspec_tracer/coverage_writer.rb +58 -0
- data/lib/rspec_tracer/html_reporter/reporter.rb +7 -6
- data/lib/rspec_tracer/remote_cache/validator.rb +1 -1
- data/lib/rspec_tracer/report_generator.rb +158 -0
- data/lib/rspec_tracer/report_merger.rb +81 -0
- data/lib/rspec_tracer/report_writer.rb +141 -0
- data/lib/rspec_tracer/reporter.rb +4 -158
- data/lib/rspec_tracer/rspec_runner.rb +2 -4
- data/lib/rspec_tracer/runner.rb +2 -112
- data/lib/rspec_tracer/version.rb +1 -1
- data/lib/rspec_tracer.rb +197 -27
- metadata +21 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9aaeb8a00f4cebc311367e6a61d59526014849df9f7fe890455d64cef16d435f
|
4
|
+
data.tar.gz: b803d5104a0a35f783bdd15b7a43252a1cfddcdd56d9f0a26c5a07bb8c36ef5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56acd78d6d4bf7b6270e8517a82d0bb1237dd78518a22d1a84a96de29e00c59379092cd725cc276d3f59ca2916306835c38426917747dcaf278e45d611c99389
|
7
|
+
data.tar.gz: a4e026b2c6fdaad653bd48063c9d0b771e7e7bc8876f653cc2e32279930d6a043b546ab1c7a713e12aac934f1f069497decdb5856246d4ee8033e905577be1db
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## [1.0.0] - 2021-10-21
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
- [JRuby](https://github.com/jruby/jruby) support
|
6
|
+
- [Parallel Tests](https://github.com/grosser/parallel_tests) support
|
7
|
+
|
8
|
+
### Breaking Changes
|
9
|
+
|
10
|
+
The first run on this version will not use any cache on the CI because the number
|
11
|
+
of files changed from eight to eleven, so there will be no appropriate cache to use.
|
12
|
+
|
13
|
+
## [0.9.3] - 2021-10-03
|
14
|
+
|
15
|
+
Generate reports ignoring duplicate examples (#42)
|
16
|
+
|
1
17
|
## [0.9.2] - 2021-09-30
|
2
18
|
|
3
19
|
### Fixed
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|

|
2
2
|
|
3
|
+
[](https://discord.gg/H2G9yWeuRZ)
|
3
4
|
[](https://codeclimate.com/github/avmnu-sng/rspec-tracer/maintainability)
|
4
5
|
[](https://codeclimate.com/github/avmnu-sng/rspec-tracer/test_coverage)
|
5
6
|
[](https://badge.fury.io/rb/rspec-tracer)
|
@@ -34,6 +35,8 @@ installed.
|
|
34
35
|
|
35
36
|
* [Demo](#demo)
|
36
37
|
* [Getting Started](#getting-started)
|
38
|
+
* [Working with JRuby](#working-with-jruby)
|
39
|
+
* [Working with Parallel Tests](#working-with-parallel-tests)
|
37
40
|
* [Configuring CI Caching](#configuring-ci-caching)
|
38
41
|
* [Advanced Configuration](#advanced-configuration)
|
39
42
|
* [Filters](#filters)
|
@@ -135,6 +138,28 @@ any of the application code.**
|
|
135
138
|
3. After running your tests, open `rspec_tracer_report/index.html` in the browser
|
136
139
|
of your choice.
|
137
140
|
|
141
|
+
### Working with JRuby
|
142
|
+
|
143
|
+
It is recommend to use **JRuby 9.2.10.0+**. Also, configure it with **`JRUBY_OPTS="--debug -X+O"`**
|
144
|
+
or have the `.jrubyrc` file:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
debug.fullTrace=true
|
148
|
+
objectspace.enabled=true
|
149
|
+
```
|
150
|
+
|
151
|
+
### Working with Parallel Tests
|
152
|
+
|
153
|
+
The Rspec tracer, by default, supports working with [parallel_tests](https://github.com/grosser/parallel_tests/)
|
154
|
+
gem. It maintains a lock file `/tmp/parallel_tests.lock` to identify the last
|
155
|
+
running process. Usually, you are not required to do anything special unless you
|
156
|
+
interrupt the execution in between and the process did not complete correctly.
|
157
|
+
In such a case, you must delete the lock file before the next run.
|
158
|
+
|
159
|
+
```sh
|
160
|
+
rm -f /tmp/parallel_tests.lock && bundle exec parallel_rspec
|
161
|
+
```
|
162
|
+
|
138
163
|
## Configuring CI Caching
|
139
164
|
|
140
165
|
To enable RSpec Tracer to share cache between different builds on CI, update the
|
@@ -228,11 +253,11 @@ variables:
|
|
228
253
|
```
|
229
254
|
- **`RSPEC_TRACER_COVERAGE_DIR`** to update the default coverage directory (`rspec_tracer_coverage`).
|
230
255
|
```sh
|
231
|
-
export
|
256
|
+
export RSPEC_TRACER_COVERAGE_DIR=/tmp/rspec_tracer_coverage
|
232
257
|
```
|
233
258
|
- **`RSPEC_TRACER_REPORT_DIR`** to update the default html reports directory (`rspec_tracer_report`).
|
234
259
|
```sh
|
235
|
-
export
|
260
|
+
export RSPEC_TRACER_REPORT_DIR=/tmp/rspec_tracer_report
|
236
261
|
```
|
237
262
|
|
238
263
|
These settings are available through environment variables because the rake tasks
|
data/lib/rspec_tracer/cache.rb
CHANGED
@@ -2,16 +2,15 @@
|
|
2
2
|
|
3
3
|
module RSpecTracer
|
4
4
|
class Cache
|
5
|
-
attr_reader :all_examples, :
|
6
|
-
:
|
5
|
+
attr_reader :all_examples, :duplicate_examples, :interrupted_examples,
|
6
|
+
:flaky_examples, :failed_examples, :pending_examples, :skipped_examples,
|
7
|
+
:all_files, :dependency, :examples_coverage, :run_id
|
7
8
|
|
8
9
|
def initialize
|
9
|
-
@run_id = last_run_id
|
10
|
-
@cache_dir = File.join(RSpecTracer.cache_path, @run_id) if @run_id
|
11
|
-
|
12
10
|
@cached = false
|
13
11
|
|
14
12
|
@all_examples = {}
|
13
|
+
@duplicate_examples = {}
|
15
14
|
@interrupted_examples = Set.new
|
16
15
|
@flaky_examples = Set.new
|
17
16
|
@failed_examples = Set.new
|
@@ -21,52 +20,67 @@ module RSpecTracer
|
|
21
20
|
end
|
22
21
|
|
23
22
|
def load_cache_for_run
|
24
|
-
return if @
|
23
|
+
return if @cached
|
24
|
+
|
25
|
+
cache_path = RSpecTracer.cache_path
|
26
|
+
cache_path = File.dirname(cache_path) if RSpecTracer.parallel_tests?
|
27
|
+
run_id = last_run_id(cache_path)
|
28
|
+
|
29
|
+
return if run_id.nil?
|
25
30
|
|
26
31
|
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
32
|
+
cache_dir = File.join(cache_path, run_id)
|
27
33
|
|
28
|
-
load_all_examples_cache
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
+
load_all_examples_cache(cache_dir)
|
35
|
+
load_duplicate_examples_cache(cache_dir)
|
36
|
+
load_interrupted_examples_cache(cache_dir)
|
37
|
+
load_flaky_examples_cache(cache_dir)
|
38
|
+
load_failed_examples_cache(cache_dir)
|
39
|
+
load_pending_examples_cache(cache_dir)
|
40
|
+
load_all_files_cache(cache_dir)
|
41
|
+
load_dependency_cache(cache_dir)
|
34
42
|
|
35
43
|
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
36
44
|
|
37
45
|
@cached = true
|
38
46
|
|
39
|
-
|
47
|
+
elapsed = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
40
48
|
|
41
|
-
puts "RSpec tracer loaded cache from #{
|
49
|
+
puts "RSpec tracer loaded cache from #{cache_dir} (took #{elapsed})"
|
42
50
|
end
|
43
51
|
|
44
52
|
def cached_examples_coverage
|
45
53
|
return @examples_coverage if defined?(@examples_coverage)
|
46
|
-
|
54
|
+
|
55
|
+
cache_path = RSpecTracer.cache_path
|
56
|
+
cache_path = File.dirname(cache_path) if RSpecTracer.parallel_tests?
|
57
|
+
run_id = last_run_id(cache_path)
|
58
|
+
|
59
|
+
return @examples_coverage = {} if run_id.nil?
|
47
60
|
|
48
61
|
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
49
|
-
|
62
|
+
cache_dir = File.join(cache_path, run_id)
|
63
|
+
coverage = load_examples_coverage_cache(cache_dir)
|
50
64
|
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
51
|
-
|
65
|
+
elapsed = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
52
66
|
|
53
|
-
puts "RSpec tracer loaded cached examples coverage (took #{
|
67
|
+
puts "RSpec tracer loaded cached examples coverage (took #{elapsed})" if RSpecTracer.verbose?
|
54
68
|
|
55
69
|
coverage
|
56
70
|
end
|
57
71
|
|
58
72
|
private
|
59
73
|
|
60
|
-
def last_run_id
|
61
|
-
file_name = File.join(
|
74
|
+
def last_run_id(cache_dir)
|
75
|
+
file_name = File.join(cache_dir, 'last_run.json')
|
62
76
|
|
63
77
|
return unless File.file?(file_name)
|
64
78
|
|
65
79
|
JSON.parse(File.read(file_name))['run_id']
|
66
80
|
end
|
67
81
|
|
68
|
-
def load_all_examples_cache
|
69
|
-
file_name = File.join(
|
82
|
+
def load_all_examples_cache(cache_dir, discard_run_reason: true)
|
83
|
+
file_name = File.join(cache_dir, 'all_examples.json')
|
70
84
|
|
71
85
|
return unless File.file?(file_name)
|
72
86
|
|
@@ -75,42 +89,63 @@ module RSpecTracer
|
|
75
89
|
end
|
76
90
|
|
77
91
|
@all_examples.each_value do |example|
|
78
|
-
if example.key?(:execution_result)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
92
|
+
example[:execution_result].transform_keys!(&:to_sym) if example.key?(:execution_result)
|
93
|
+
example[:run_reason] = nil if discard_run_reason
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def load_duplicate_examples_cache(cache_dir)
|
98
|
+
file_name = File.join(cache_dir, 'duplicate_examples.json')
|
99
|
+
|
100
|
+
return unless File.file?(file_name)
|
83
101
|
|
84
|
-
|
102
|
+
@duplicate_examples = JSON.parse(File.read(file_name)).transform_values do |examples|
|
103
|
+
examples.map { |example| example.transform_keys(&:to_sym) }
|
85
104
|
end
|
86
105
|
end
|
87
106
|
|
88
|
-
def
|
89
|
-
file_name = File.join(
|
107
|
+
def load_interrupted_examples_cache(cache_dir)
|
108
|
+
file_name = File.join(cache_dir, 'interrupted_examples.json')
|
109
|
+
|
110
|
+
return unless File.file?(file_name)
|
111
|
+
|
112
|
+
@interrupted_examples = JSON.parse(File.read(file_name)).to_set
|
113
|
+
end
|
114
|
+
|
115
|
+
def load_flaky_examples_cache(cache_dir)
|
116
|
+
file_name = File.join(cache_dir, 'flaky_examples.json')
|
90
117
|
|
91
118
|
return unless File.file?(file_name)
|
92
119
|
|
93
120
|
@flaky_examples = JSON.parse(File.read(file_name)).to_set
|
94
121
|
end
|
95
122
|
|
96
|
-
def load_failed_examples_cache
|
97
|
-
file_name = File.join(
|
123
|
+
def load_failed_examples_cache(cache_dir)
|
124
|
+
file_name = File.join(cache_dir, 'failed_examples.json')
|
98
125
|
|
99
126
|
return unless File.file?(file_name)
|
100
127
|
|
101
128
|
@failed_examples = JSON.parse(File.read(file_name)).to_set
|
102
129
|
end
|
103
130
|
|
104
|
-
def load_pending_examples_cache
|
105
|
-
file_name = File.join(
|
131
|
+
def load_pending_examples_cache(cache_dir)
|
132
|
+
file_name = File.join(cache_dir, 'pending_examples.json')
|
106
133
|
|
107
134
|
return unless File.file?(file_name)
|
108
135
|
|
109
136
|
@pending_examples = JSON.parse(File.read(file_name)).to_set
|
110
137
|
end
|
111
138
|
|
112
|
-
def
|
113
|
-
file_name = File.join(
|
139
|
+
def load_skipped_examples_cache(cache_dir)
|
140
|
+
file_name = File.join(cache_dir, 'skipped_examples.json')
|
141
|
+
|
142
|
+
return unless File.file?(file_name)
|
143
|
+
|
144
|
+
@skipped_examples = JSON.parse(File.read(file_name)).to_set
|
145
|
+
end
|
146
|
+
|
147
|
+
def load_all_files_cache(cache_dir)
|
148
|
+
file_name = File.join(cache_dir, 'all_files.json')
|
114
149
|
|
115
150
|
return unless File.file?(file_name)
|
116
151
|
|
@@ -119,16 +154,16 @@ module RSpecTracer
|
|
119
154
|
end
|
120
155
|
end
|
121
156
|
|
122
|
-
def load_dependency_cache
|
123
|
-
file_name = File.join(
|
157
|
+
def load_dependency_cache(cache_dir)
|
158
|
+
file_name = File.join(cache_dir, 'dependency.json')
|
124
159
|
|
125
160
|
return unless File.file?(file_name)
|
126
161
|
|
127
162
|
@dependency = JSON.parse(File.read(file_name)).transform_values(&:to_set)
|
128
163
|
end
|
129
164
|
|
130
|
-
def load_examples_coverage_cache
|
131
|
-
file_name = File.join(
|
165
|
+
def load_examples_coverage_cache(cache_dir)
|
166
|
+
file_name = File.join(cache_dir, 'examples_coverage.json')
|
132
167
|
|
133
168
|
return unless File.file?(file_name)
|
134
169
|
|
@@ -31,7 +31,8 @@ module RSpecTracer
|
|
31
31
|
def cache_path
|
32
32
|
@cache_path ||= begin
|
33
33
|
cache_path = File.expand_path(cache_dir, root)
|
34
|
-
cache_path = File.join(cache_path, ENV['TEST_SUITE_ID'].to_s)
|
34
|
+
cache_path = File.join(cache_path, ENV['TEST_SUITE_ID'].to_s) if ENV['TEST_SUITE_ID']
|
35
|
+
cache_path = File.join(cache_path, parallel_tests_id) if RSpecTracer.parallel_tests?
|
35
36
|
|
36
37
|
FileUtils.mkdir_p(cache_path)
|
37
38
|
|
@@ -46,7 +47,8 @@ module RSpecTracer
|
|
46
47
|
def report_path
|
47
48
|
@report_path ||= begin
|
48
49
|
report_path = File.expand_path(report_dir, root)
|
49
|
-
report_path = File.join(report_path, ENV['TEST_SUITE_ID'].to_s)
|
50
|
+
report_path = File.join(report_path, ENV['TEST_SUITE_ID'].to_s) if ENV['TEST_SUITE_ID']
|
51
|
+
report_path = File.join(report_path, parallel_tests_id) if RSpecTracer.parallel_tests?
|
50
52
|
|
51
53
|
FileUtils.mkdir_p(report_path)
|
52
54
|
|
@@ -61,7 +63,8 @@ module RSpecTracer
|
|
61
63
|
def coverage_path
|
62
64
|
@coverage_path ||= begin
|
63
65
|
coverage_path = File.expand_path(coverage_dir, root)
|
64
|
-
coverage_path = File.join(coverage_path, ENV['TEST_SUITE_ID'].to_s)
|
66
|
+
coverage_path = File.join(coverage_path, ENV['TEST_SUITE_ID'].to_s) if ENV['TEST_SUITE_ID']
|
67
|
+
coverage_path = File.join(coverage_path, parallel_tests_id) if RSpecTracer.parallel_tests?
|
65
68
|
|
66
69
|
FileUtils.mkdir_p(coverage_path)
|
67
70
|
|
@@ -93,6 +96,10 @@ module RSpecTracer
|
|
93
96
|
@coverage_filters ||= []
|
94
97
|
end
|
95
98
|
|
99
|
+
def parallel_tests_lock_file
|
100
|
+
'/tmp/parallel_tests.lock'
|
101
|
+
end
|
102
|
+
|
96
103
|
def verbose?
|
97
104
|
@verbose ||= (ENV.fetch('RSPEC_TRACER_VERBOSE', 'false') == 'true')
|
98
105
|
end
|
@@ -103,12 +110,12 @@ module RSpecTracer
|
|
103
110
|
|
104
111
|
private
|
105
112
|
|
106
|
-
def
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
113
|
+
def parallel_tests_id
|
114
|
+
if ParallelTests.first_process?
|
115
|
+
'parallel_tests_1'
|
116
|
+
else
|
117
|
+
"parallel_tests_#{ENV['TEST_ENV_NUMBER']}"
|
118
|
+
end
|
112
119
|
end
|
113
120
|
|
114
121
|
def at_exit(&block)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
class CoverageMerger
|
5
|
+
attr_reader :coverage
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@coverage = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def merge(reports_dir)
|
12
|
+
return if RSpecTracer.simplecov?
|
13
|
+
|
14
|
+
reports_dir.each do |report_dir|
|
15
|
+
next unless File.directory?(report_dir)
|
16
|
+
|
17
|
+
cache_coverage = JSON.parse(File.read("#{report_dir}/coverage.json"))['RSpecTracer']['coverage']
|
18
|
+
|
19
|
+
cache_coverage.each_pair do |file_name, line_coverage|
|
20
|
+
unless @coverage.key?(file_name)
|
21
|
+
@coverage[file_name] = line_coverage
|
22
|
+
|
23
|
+
next
|
24
|
+
end
|
25
|
+
|
26
|
+
merge_line_coverage(file_name, line_coverage)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def merge_line_coverage(file_name, line_coverage)
|
34
|
+
line_coverage.each_with_index do |strength, line_number|
|
35
|
+
next unless strength && @coverage[file_name][line_number]
|
36
|
+
|
37
|
+
@coverage[file_name][line_number] += strength
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -88,8 +88,6 @@ module RSpecTracer
|
|
88
88
|
all_files.each do |file_path|
|
89
89
|
@coverage[file_path] ||= line_stub(file_path).freeze
|
90
90
|
end
|
91
|
-
|
92
|
-
generate_final_coverage_stat
|
93
91
|
end
|
94
92
|
|
95
93
|
private
|
@@ -131,31 +129,6 @@ module RSpecTracer
|
|
131
129
|
all_files.sort
|
132
130
|
end
|
133
131
|
|
134
|
-
def generate_final_coverage_stat
|
135
|
-
total_loc = 0
|
136
|
-
covered_loc = 0
|
137
|
-
|
138
|
-
@coverage.each_pair do |_file_path, line_coverage|
|
139
|
-
line_coverage.each do |strength|
|
140
|
-
next if strength.nil?
|
141
|
-
|
142
|
-
total_loc += 1
|
143
|
-
covered_loc += 1 if strength.positive?
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
@coverage_stat = {
|
148
|
-
total_lines: total_loc,
|
149
|
-
covered_lines: covered_loc,
|
150
|
-
missed_lines: total_loc - covered_loc,
|
151
|
-
covered_percent: 0.0
|
152
|
-
}
|
153
|
-
|
154
|
-
return if total_loc.zero?
|
155
|
-
|
156
|
-
@coverage_stat[:covered_percent] = (100.0 * covered_loc / total_loc).round(2)
|
157
|
-
end
|
158
|
-
|
159
132
|
def peek_coverage
|
160
133
|
data = ::Coverage.peek_result.select do |file_path, _|
|
161
134
|
file_path.start_with?(RSpecTracer.root)
|
@@ -167,8 +140,17 @@ module RSpecTracer
|
|
167
140
|
end
|
168
141
|
|
169
142
|
def line_stub(file_path)
|
143
|
+
case RUBY_ENGINE
|
144
|
+
when 'ruby'
|
145
|
+
ruby_line_stub(file_path)
|
146
|
+
when 'jruby'
|
147
|
+
jruby_line_stub(file_path)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def ruby_line_stub(file_path)
|
170
152
|
lines = File.foreach(file_path).map { nil }
|
171
|
-
iseqs = [RubyVM::InstructionSequence.compile_file(file_path)]
|
153
|
+
iseqs = [::RubyVM::InstructionSequence.compile_file(file_path)]
|
172
154
|
|
173
155
|
until iseqs.empty?
|
174
156
|
iseq = iseqs.pop
|
@@ -179,5 +161,26 @@ module RSpecTracer
|
|
179
161
|
|
180
162
|
lines
|
181
163
|
end
|
164
|
+
|
165
|
+
def jruby_line_stub(file_path)
|
166
|
+
lines = File.foreach(file_path).map { nil }
|
167
|
+
root_node = ::JRuby.parse(File.read(file_path))
|
168
|
+
|
169
|
+
visitor = org.jruby.ast.visitor.NodeVisitor.impl do |_name, node|
|
170
|
+
if node.newline?
|
171
|
+
if node.respond_to?(:position)
|
172
|
+
lines[node.position.line] = 0
|
173
|
+
else
|
174
|
+
lines[node.line] = 0
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
node.child_nodes.each { |child| child&.accept(visitor) }
|
179
|
+
end
|
180
|
+
|
181
|
+
root_node.accept(visitor)
|
182
|
+
|
183
|
+
lines
|
184
|
+
end
|
182
185
|
end
|
183
186
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
class CoverageWriter
|
5
|
+
def initialize(file_name, reporter)
|
6
|
+
@file_name = file_name
|
7
|
+
@reporter = reporter
|
8
|
+
end
|
9
|
+
|
10
|
+
def write_report
|
11
|
+
report = {
|
12
|
+
RSpecTracer: {
|
13
|
+
coverage: @reporter.coverage,
|
14
|
+
timestamp: Time.now.utc.to_i
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
File.write(@file_name, JSON.pretty_generate(report))
|
19
|
+
end
|
20
|
+
|
21
|
+
def print_stats(elapsed_time)
|
22
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
23
|
+
|
24
|
+
total, covered, percent = coverage_stats
|
25
|
+
|
26
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
27
|
+
elapsed = RSpecTracer::TimeFormatter.format_time((ending - starting) + elapsed_time)
|
28
|
+
|
29
|
+
puts <<-STATS.strip.gsub(/\s+/, ' ')
|
30
|
+
Coverage report generated for RSpecTracer to #{@file_name}.
|
31
|
+
#{covered} / #{total} LOC (#{percent}%) covered (took #{elapsed})
|
32
|
+
STATS
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def coverage_stats
|
38
|
+
total_loc = 0
|
39
|
+
covered_loc = 0
|
40
|
+
covered_percent = 0.0
|
41
|
+
|
42
|
+
@reporter.coverage.each_pair do |_file_path, line_coverage|
|
43
|
+
line_coverage.each do |strength|
|
44
|
+
next if strength.nil?
|
45
|
+
|
46
|
+
total_loc += 1
|
47
|
+
covered_loc += 1 if strength.positive?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
return [total_loc, covered_loc, covered_percent] if total_loc.zero?
|
52
|
+
|
53
|
+
covered_percent = (100.0 * covered_loc / total_loc).round(2)
|
54
|
+
|
55
|
+
[total_loc, covered_loc, covered_percent]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -8,8 +8,9 @@ module RSpecTracer
|
|
8
8
|
class Reporter
|
9
9
|
attr_reader :last_run, :examples, :flaky_examples, :examples_dependency, :files_dependency
|
10
10
|
|
11
|
-
def initialize
|
12
|
-
@
|
11
|
+
def initialize(report_dir, reporter)
|
12
|
+
@report_dir = report_dir
|
13
|
+
@reporter = reporter
|
13
14
|
end
|
14
15
|
|
15
16
|
def generate_report
|
@@ -17,16 +18,16 @@ module RSpecTracer
|
|
17
18
|
|
18
19
|
prepare
|
19
20
|
|
20
|
-
file_name = File.join(
|
21
|
+
file_name = File.join(@report_dir, 'index.html')
|
21
22
|
|
22
23
|
File.open(file_name, 'wb') do |file|
|
23
24
|
file.puts(template('layout').result(binding))
|
24
25
|
end
|
25
26
|
|
26
27
|
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
27
|
-
|
28
|
+
elapsed = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
28
29
|
|
29
|
-
puts "RSpecTracer generated HTML report to #{file_name} (took #{
|
30
|
+
puts "RSpecTracer generated HTML report to #{file_name} (took #{elapsed})"
|
30
31
|
end
|
31
32
|
|
32
33
|
private
|
@@ -153,7 +154,7 @@ module RSpecTracer
|
|
153
154
|
|
154
155
|
def asset_output_path
|
155
156
|
@asset_output_path ||= begin
|
156
|
-
asset_output_path = File.join(
|
157
|
+
asset_output_path = File.join(@report_dir, 'assets', RSpecTracer::VERSION)
|
157
158
|
|
158
159
|
FileUtils.mkdir_p(asset_output_path)
|
159
160
|
|