rspec_log_formatter 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/lib/rspec_log_formatter/analysis/analyzer.rb +43 -8
- data/lib/rspec_log_formatter/analysis/pretty_printer.rb +9 -2
- data/lib/rspec_log_formatter/analyzer_formatter.rb +20 -0
- data/lib/rspec_log_formatter/formatter.rb +15 -18
- data/lib/rspec_log_formatter/version.rb +1 -1
- data/spec/lib/rspec_log_analyzer/analysis/analyzer_spec.rb +14 -0
- data/spec/lib/rspec_log_analyzer/analysis/pretty_printer_spec.rb +36 -1
- data/spec/lib/rspec_log_analyzer/analyzer_formatter_spec.rb +32 -0
- data/spec/lib/rspec_log_analyzer/formatter_spec.rb +49 -9
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDA1NzQxMjZmZjU0ZWU3ZjkzZTRmMzIzNTYzM2Q1NTRiZTJiYTVkOA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NjhiY2JiNzgyMGEyZGFlOTIyZjk4NTZjZmU5NzFkODA3NjBlNGIwOQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NDE3M2RlZGExODYyNjc2ZGZmOGE4YjgxYzMxZDYzYTRlZWQwMGNjOGNiZGY5
|
10
|
+
MTlmOGIzYjE5ZDUyMWU0MjFjMjBhNjlkZjc0M2IzNzk3MWQ1MWVlYWQ0NTM0
|
11
|
+
ZmI3YzRlNjlhODcwZmZjYTQ2NGQxODU5ZTZhOWY2YWNiNjIwYjU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YTEwMTFlYzFjMTllODQ0NmQxZDU4Mjc1MDI4NzBhOTc2NGU4OWM1NDBhZmNk
|
14
|
+
YTNiYmY0MmYzZjdlMjMyYjc5YTgxNGE1YTk0YWZjOThiOTBmZDJlNmRkYjk1
|
15
|
+
ZTQ0NDEwNjNiZmU2OGRlZjY4MDNhMGYxMjAxNzU2ZmVhZjliNTY=
|
@@ -6,14 +6,7 @@ module RspecLogFormatter
|
|
6
6
|
def analyze(filepath, options = {})
|
7
7
|
window = options[:last_builds]
|
8
8
|
|
9
|
-
build_numbers =
|
10
|
-
results = File.open(filepath).each_line.map do |line|
|
11
|
-
result = Result.new(*CSV.parse_line(line, col_sep: "\t").first(7))
|
12
|
-
build_numbers << result.build_number
|
13
|
-
result
|
14
|
-
end
|
15
|
-
build_numbers.uniq!.sort!
|
16
|
-
|
9
|
+
build_numbers, results = result_numbers(filepath, options = {})
|
17
10
|
|
18
11
|
scores = []
|
19
12
|
results.group_by(&:description).each do |description, results|
|
@@ -33,6 +26,48 @@ module RspecLogFormatter
|
|
33
26
|
|
34
27
|
scores.sort.map(&:as_hash)
|
35
28
|
end
|
29
|
+
|
30
|
+
def truncate(filepath, opts = {})
|
31
|
+
builds = opts.fetch(:keep_builds)
|
32
|
+
build_numbers, results = result_numbers(filepath, options = {})
|
33
|
+
sio = StringIO.new
|
34
|
+
|
35
|
+
File.open(filepath, 'r').each_line do |line|
|
36
|
+
result = parse_line(line)
|
37
|
+
next unless build_numbers.last(builds).include? result.build_number
|
38
|
+
sio.puts line
|
39
|
+
end
|
40
|
+
|
41
|
+
sio.rewind
|
42
|
+
File.open(filepath, 'w') do |f|
|
43
|
+
f.write sio.read
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def parse_line(line)
|
50
|
+
Result.new(*CSV.parse_line(line, col_sep: "\t").first(7))
|
51
|
+
end
|
52
|
+
|
53
|
+
def each_result(filepath, &block)
|
54
|
+
File.open(filepath, 'r').each_line do |line|
|
55
|
+
result = parse_line(line)
|
56
|
+
block.call(result)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def result_numbers(filepath, options = {})
|
61
|
+
build_numbers = []
|
62
|
+
results = []
|
63
|
+
each_result(filepath) do |result|
|
64
|
+
build_numbers << result.build_number
|
65
|
+
results << result
|
66
|
+
end
|
67
|
+
[build_numbers.uniq.sort, results]
|
68
|
+
end
|
69
|
+
|
36
70
|
end
|
71
|
+
|
37
72
|
end
|
38
73
|
end
|
@@ -6,8 +6,15 @@ module RspecLogFormatter
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def to_s
|
9
|
-
results = @results.
|
10
|
-
|
9
|
+
results = @results.reject do |result|
|
10
|
+
result[:percent] == 0.0
|
11
|
+
end.first(10)
|
12
|
+
|
13
|
+
header = if results.empty?
|
14
|
+
"None of the specs were flaky"
|
15
|
+
else
|
16
|
+
"Top #{results.size} flakiest examples\n"
|
17
|
+
end
|
11
18
|
header + results.each_with_index.map do |result, i|
|
12
19
|
title = " #{i+1}) #{result[:description]} -- #{result[:percent]}%"
|
13
20
|
failure_messages = result[:failure_messages].map { |fm| " * #{fm}" }.join("\n")
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "csv"
|
2
|
+
require "rspec/core/formatters/base_formatter"
|
3
|
+
|
4
|
+
module RspecLogFormatter
|
5
|
+
class AnalyzerFormatter < RSpec::Core::Formatters::BaseFormatter
|
6
|
+
FILENAME = "rspec.history"
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def dump_summary(_,_,_,_)
|
14
|
+
output.puts RspecLogFormatter::Analysis::PrettyPrinter.new(
|
15
|
+
RspecLogFormatter::Analysis::Analyzer.new.analyze(FILENAME)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -5,23 +5,17 @@ module RspecLogFormatter
|
|
5
5
|
class Formatter < RSpec::Core::Formatters::BaseFormatter
|
6
6
|
FILENAME = "rspec.history"
|
7
7
|
|
8
|
-
class
|
9
|
-
def
|
10
|
-
|
8
|
+
class Maker
|
9
|
+
def new(_output)
|
10
|
+
RspecLogFormatter::Formatter.new(clock, opts)
|
11
11
|
end
|
12
|
-
def clock
|
13
|
-
@clock ||= Time
|
14
|
-
@clock
|
15
|
-
end
|
16
|
-
end
|
17
|
-
CONFIG = Config.new
|
18
|
-
|
19
|
-
def initialize(*args)
|
20
|
-
super
|
21
12
|
end
|
13
|
+
Factory = Maker.new
|
22
14
|
|
23
|
-
def
|
24
|
-
|
15
|
+
def initialize(clock=nil, opts={})
|
16
|
+
@clock = clock || Time
|
17
|
+
@build_number = opts[:build_number] || ENV["BUILD_NUMBER"]
|
18
|
+
@keep_builds = opts[:keep_builds]
|
25
19
|
end
|
26
20
|
|
27
21
|
def example_started(example)
|
@@ -36,11 +30,14 @@ module RspecLogFormatter
|
|
36
30
|
record("failed", example, clock.now, clock.now - @clock_start, example.exception)
|
37
31
|
end
|
38
32
|
|
33
|
+
def dump_summary(_,_,_,_)
|
34
|
+
return unless @keep_builds
|
35
|
+
RspecLogFormatter::Analysis::Analyzer.new.truncate(FILENAME, keep_builds: @keep_builds)
|
36
|
+
end
|
37
|
+
|
39
38
|
private
|
40
39
|
|
41
|
-
|
42
|
-
CONFIG.clock
|
43
|
-
end
|
40
|
+
attr_reader :clock
|
44
41
|
|
45
42
|
def record(outcome, example, time, duration, exception=nil)
|
46
43
|
if exception
|
@@ -53,7 +50,7 @@ module RspecLogFormatter
|
|
53
50
|
end
|
54
51
|
|
55
52
|
example_data = [
|
56
|
-
|
53
|
+
@build_number,
|
57
54
|
time,
|
58
55
|
outcome,
|
59
56
|
example.full_description.to_s.gsub(/\r|\n|\t/, " "),
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require 'tempfile'
|
2
3
|
|
3
4
|
describe RspecLogFormatter::Analysis::Analyzer do
|
4
5
|
it "sorts the parsed results by failure percentage" do
|
@@ -24,6 +25,19 @@ describe RspecLogFormatter::Analysis::Analyzer do
|
|
24
25
|
}
|
25
26
|
end
|
26
27
|
|
28
|
+
|
29
|
+
it "can truncate the log file" do
|
30
|
+
filepath = File.expand_path("../../../../fixtures/test_was_flaky_then_fixed.history", __FILE__)
|
31
|
+
temp = Tempfile.new('fixture')
|
32
|
+
FileUtils.copy(filepath, temp.path)
|
33
|
+
described_class.new.truncate(temp.path, keep_builds: 3)
|
34
|
+
File.open(temp.path, 'r').read.should == <<HEREDOC
|
35
|
+
5 2014-01-21 16:08:25 -0800 passed desc ./spec/m1k1_spec.rb
|
36
|
+
6 2014-01-21 16:08:25 -0800 passed desc ./spec/m1k1_spec.rb
|
37
|
+
7 2014-01-21 16:08:25 -0800 passed desc ./spec/m1k1_spec.rb
|
38
|
+
HEREDOC
|
39
|
+
end
|
40
|
+
|
27
41
|
it "can analyze only a window of builds" do
|
28
42
|
filepath = File.expand_path("../../../../fixtures/test_was_flaky_then_fixed.history", __FILE__)
|
29
43
|
subject.analyze(filepath, last_builds: 7).first.should == {
|
@@ -24,7 +24,7 @@ Top 2 flakiest examples
|
|
24
24
|
|
25
25
|
context "when there are more than 10 results" do
|
26
26
|
it "only prints the top 10" do
|
27
|
-
results = (1..
|
27
|
+
results = (1..11).map do |i|
|
28
28
|
{description: "hi#{i}", percent: 1, failure_messages: ["bye#{i}"]}
|
29
29
|
end
|
30
30
|
|
@@ -53,4 +53,39 @@ Top 10 flakiest examples
|
|
53
53
|
TEXT
|
54
54
|
end
|
55
55
|
end
|
56
|
+
|
57
|
+
it "only prints flaky specs" do
|
58
|
+
results = (1..7).map do |i|
|
59
|
+
{description: "hi#{i}", percent: 1, failure_messages: ["bye#{i}"]}
|
60
|
+
end + (8..10).map do |i|
|
61
|
+
{description: "hi#{i}", percent: 0.0, failure_messages: ["bye#{i}"]}
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
described_class.new(results).to_s.should == <<-TEXT.strip
|
66
|
+
Top 7 flakiest examples
|
67
|
+
1) hi1 -- 1%
|
68
|
+
* bye1
|
69
|
+
2) hi2 -- 1%
|
70
|
+
* bye2
|
71
|
+
3) hi3 -- 1%
|
72
|
+
* bye3
|
73
|
+
4) hi4 -- 1%
|
74
|
+
* bye4
|
75
|
+
5) hi5 -- 1%
|
76
|
+
* bye5
|
77
|
+
6) hi6 -- 1%
|
78
|
+
* bye6
|
79
|
+
7) hi7 -- 1%
|
80
|
+
* bye7
|
81
|
+
TEXT
|
82
|
+
end
|
83
|
+
|
84
|
+
it "only prints flaky specs" do
|
85
|
+
results = (1..11).map do |i|
|
86
|
+
{description: "hi#{i}", percent: 0, failure_messages: ["bye#{i}"]}
|
87
|
+
end
|
88
|
+
|
89
|
+
described_class.new(results).to_s.should == "None of the specs were flaky"
|
90
|
+
end
|
56
91
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RspecLogFormatter::Formatter do
|
4
|
+
|
5
|
+
|
6
|
+
it "works" do
|
7
|
+
filepath = File.expand_path("../../../fixtures/varying_flakiness.history", __FILE__)
|
8
|
+
FileUtils.cp(filepath, 'rspec.history')
|
9
|
+
out = StringIO.new
|
10
|
+
formatter = RspecLogFormatter::AnalyzerFormatter.new(out)
|
11
|
+
formatter.dump_summary(1,2,3,4)
|
12
|
+
out.rewind
|
13
|
+
out.read.should == <<HEREDOC
|
14
|
+
Top 3 flakiest examples
|
15
|
+
1) desc3 -- 75.0%
|
16
|
+
* ec10
|
17
|
+
msg10
|
18
|
+
* ec10
|
19
|
+
msg10
|
20
|
+
* ec10
|
21
|
+
msg10
|
22
|
+
2) desc2 -- 66.66666666666667%
|
23
|
+
* ec10
|
24
|
+
msg10
|
25
|
+
* ec10
|
26
|
+
msg10
|
27
|
+
3) desc1 -- 50.0%
|
28
|
+
* ec10
|
29
|
+
msg10
|
30
|
+
HEREDOC
|
31
|
+
end
|
32
|
+
end
|
@@ -10,25 +10,65 @@ describe RspecLogFormatter::Formatter do
|
|
10
10
|
}.merge(opts))
|
11
11
|
end
|
12
12
|
|
13
|
+
def formatter_for_build(build)
|
14
|
+
RspecLogFormatter::Formatter.new(double(now: Time.at(0)), keep_builds: 2, build_number: build)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "can truncate the log file" do
|
18
|
+
the_example = make_example
|
19
|
+
formatter = formatter_for_build(1)
|
20
|
+
formatter.example_started(the_example)
|
21
|
+
formatter.example_passed(the_example)
|
22
|
+
formatter.dump_summary(1,2,3,4)
|
23
|
+
|
24
|
+
formatter = formatter_for_build(2)
|
25
|
+
formatter.example_started(the_example)
|
26
|
+
formatter.example_passed(the_example)
|
27
|
+
formatter.dump_summary(1,2,3,4)
|
28
|
+
|
29
|
+
File.open(RspecLogFormatter::Formatter::FILENAME, 'r').read.should == <<HEREDOC
|
30
|
+
1 1969-12-31 16:00:00 -0800 passed description_1 path_1 0.0
|
31
|
+
2 1969-12-31 16:00:00 -0800 passed description_1 path_1 0.0
|
32
|
+
HEREDOC
|
33
|
+
|
34
|
+
formatter = formatter_for_build(3)
|
35
|
+
formatter.example_started(the_example)
|
36
|
+
formatter.example_passed(the_example)
|
37
|
+
formatter.dump_summary(1,2,3,4)
|
38
|
+
|
39
|
+
formatter = formatter_for_build(4)
|
40
|
+
formatter.example_started(the_example)
|
41
|
+
formatter.example_passed(the_example)
|
42
|
+
formatter.dump_summary(1,2,3,4)
|
43
|
+
|
44
|
+
File.open(RspecLogFormatter::Formatter::FILENAME, 'r').read.should == <<HEREDOC
|
45
|
+
3 1969-12-31 16:00:00 -0800 passed description_1 path_1 0.0
|
46
|
+
4 1969-12-31 16:00:00 -0800 passed description_1 path_1 0.0
|
47
|
+
HEREDOC
|
48
|
+
end
|
49
|
+
|
50
|
+
class FakeClock
|
51
|
+
def initialize(now)
|
52
|
+
self.now = now
|
53
|
+
end
|
54
|
+
attr_accessor :now
|
55
|
+
end
|
56
|
+
|
13
57
|
it "works" do
|
14
58
|
failed_example = make_example(exception: Exception.new("Error"))
|
15
59
|
passed_example = make_example(exception: nil)
|
16
60
|
time = Time.parse("2014-02-06 16:01:10")
|
17
|
-
clock =
|
18
|
-
RspecLogFormatter::Formatter
|
19
|
-
|
20
|
-
formatter = RspecLogFormatter::Formatter.new(StringIO.new)
|
61
|
+
clock = FakeClock.new(time)
|
62
|
+
formatter = RspecLogFormatter::Formatter.new(clock)
|
21
63
|
formatter.example_started(failed_example)
|
22
|
-
|
23
|
-
RspecLogFormatter::Formatter::CONFIG.clock = double(now: time + 5)
|
64
|
+
clock.now = time + 5
|
24
65
|
formatter.example_failed(failed_example)
|
25
66
|
|
26
67
|
formatter.example_started(passed_example)
|
27
|
-
|
28
|
-
RspecLogFormatter::Formatter::CONFIG.clock = double(now: time + 8)
|
68
|
+
clock.now = time + 8
|
29
69
|
formatter.example_passed(passed_example)
|
30
70
|
formatter.dump_summary(1,2,3,4)
|
31
|
-
File.open('rspec.history').readlines.should == [
|
71
|
+
File.open('rspec.history'). readlines.should == [
|
32
72
|
"\t2014-02-06 16:01:15 -0800\tfailed\tdescription_1\tpath_1\tError\tException\t5.0\n",
|
33
73
|
"\t2014-02-06 16:01:18 -0800\tpassed\tdescription_2\tpath_2\t\t\t3.0\n",
|
34
74
|
]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec_log_formatter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Serguei Filimonov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -85,6 +85,7 @@ files:
|
|
85
85
|
- lib/rspec_log_formatter/analysis/pretty_printer.rb
|
86
86
|
- lib/rspec_log_formatter/analysis/result.rb
|
87
87
|
- lib/rspec_log_formatter/analysis/score.rb
|
88
|
+
- lib/rspec_log_formatter/analyzer_formatter.rb
|
88
89
|
- lib/rspec_log_formatter/formatter.rb
|
89
90
|
- lib/rspec_log_formatter/version.rb
|
90
91
|
- rspec_log_formatter.gemspec
|
@@ -93,6 +94,7 @@ files:
|
|
93
94
|
- spec/fixtures/varying_flakiness.history
|
94
95
|
- spec/lib/rspec_log_analyzer/analysis/analyzer_spec.rb
|
95
96
|
- spec/lib/rspec_log_analyzer/analysis/pretty_printer_spec.rb
|
97
|
+
- spec/lib/rspec_log_analyzer/analyzer_formatter_spec.rb
|
96
98
|
- spec/lib/rspec_log_analyzer/formatter_spec.rb
|
97
99
|
- spec/spec_helper.rb
|
98
100
|
homepage: ''
|
@@ -125,5 +127,6 @@ test_files:
|
|
125
127
|
- spec/fixtures/varying_flakiness.history
|
126
128
|
- spec/lib/rspec_log_analyzer/analysis/analyzer_spec.rb
|
127
129
|
- spec/lib/rspec_log_analyzer/analysis/pretty_printer_spec.rb
|
130
|
+
- spec/lib/rspec_log_analyzer/analyzer_formatter_spec.rb
|
128
131
|
- spec/lib/rspec_log_analyzer/formatter_spec.rb
|
129
132
|
- spec/spec_helper.rb
|