ruby_ci 0.1.5 → 0.2.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/lib/minitest/reporters/rubyci_reporter.rb +95 -0
- data/lib/minitest/rubyci_plugin.rb +9 -0
- data/lib/ruby_ci/extract_definitions.rb +44 -0
- data/lib/ruby_ci/rspec_formatter.rb +57 -0
- data/lib/ruby_ci/runner_prepend.rb +80 -0
- data/lib/ruby_ci/version.rb +1 -1
- data/lib/ruby_ci.rb +18 -9
- data/ruby_ci.gemspec +1 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d34d2bb75becc90c41cf2366ef0c7d6bb6ccbcc5b33e6c6e460d7a3a5fd6976f
|
4
|
+
data.tar.gz: c0846f4d4ed597b738d09687d8a14697e1dff4403fbbf852ab483f70df172b5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee9dcf45a74418a994a369c2de76b213c0181462a7ad14ef88782cd0e9cab2f18fb4eb011920a26650f0ac68f6bad32203828a8a617d3675297e66a6656f84ef
|
7
|
+
data.tar.gz: f4135d4de92276f7572ef08810fb0873375a28354fd4f7841305ebd6930d26a1d8f9aceb9d25b00dc5a74c28b90365c0b4a3d0554a5b35a2759423e76ab9be8f
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Minitest
|
2
|
+
module Reporters
|
3
|
+
class RubyciReporter
|
4
|
+
attr_accessor :tests, :test_results, :ids
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@tests = {}
|
8
|
+
@test_results = {}
|
9
|
+
@ids = {}
|
10
|
+
|
11
|
+
RubyCI.minitest_ws.on(:enq_request) do
|
12
|
+
tests
|
13
|
+
end
|
14
|
+
|
15
|
+
RubyCI.minitest_ws.on(:deq) do |api_tests|
|
16
|
+
test_results
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def prerecord(klass, name)
|
21
|
+
description = test_description(name)
|
22
|
+
path = test_path(klass.name)
|
23
|
+
|
24
|
+
test_results[path] ||= { run_time: 0.0, file_status: 'pending', test_count: 0, test_counters: { failed: 0, passed: 0, pending: 0 }, '1' => { description: klass.name } }
|
25
|
+
test_results[path][:test_count] += 1
|
26
|
+
|
27
|
+
id = (test_results[path]['1'].keys.size + 1).to_s
|
28
|
+
ids[description] = id
|
29
|
+
|
30
|
+
test_results[path]['1'][id] ||= { status: 'pending', description: description }
|
31
|
+
test_results[path]['1'][id][:start] = Minitest.clock_time
|
32
|
+
|
33
|
+
tests[path] ||= { run_time: 0.0, file_status: 'pending', test_count: 0, test_counters: { failed: 0, passed: 0, pending: 0 }, '1' => {} }
|
34
|
+
tests[path][:test_count] += 1
|
35
|
+
tests[path]['1'][id] ||= { status: 'pending' }
|
36
|
+
end
|
37
|
+
|
38
|
+
def record(result)
|
39
|
+
description = test_description(result.name)
|
40
|
+
id = ids[description]
|
41
|
+
path = test_path(result.klass)
|
42
|
+
|
43
|
+
test_results[path]['1'][id][:end] = Minitest.clock_time
|
44
|
+
test_results[path]['1'][id][:run_time] = test_results[path]['1'][id][:end] - test_results[path]['1'][id][:start]
|
45
|
+
test_results[path]['1'][id][:status] = result_status(result).to_s
|
46
|
+
test_results[path][:test_counters][result_status(result)] += 1
|
47
|
+
test_results[path][:run_time] += test_results[path]['1'][id][:run_time]
|
48
|
+
end
|
49
|
+
|
50
|
+
def report
|
51
|
+
test_results.each do |path, file_results|
|
52
|
+
file_status = 'pending'
|
53
|
+
file_results['1'].each do |id, test_result|
|
54
|
+
next if id == :description
|
55
|
+
if (test_result[:status] == 'passed') && (file_status != 'failed')
|
56
|
+
file_status = 'passed'
|
57
|
+
elsif file_status == 'failed'
|
58
|
+
file_status = 'failed'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
test_results[path][:file_status] = file_status
|
62
|
+
end
|
63
|
+
|
64
|
+
RubyCI.minitest_await
|
65
|
+
end
|
66
|
+
|
67
|
+
def method_missing(method, *args)
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def test_description(name)
|
74
|
+
test_name = name.split('test_').last
|
75
|
+
test_name = test_name[2..-1] if test_name.starts_with?(': ')
|
76
|
+
|
77
|
+
return test_name.strip
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_path(klass)
|
81
|
+
return "./#{Object.const_source_location(klass)[0].gsub(Regexp.new("^#{::Rails.root}/"), '')}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def result_status(result)
|
85
|
+
if result.passed?
|
86
|
+
:passed
|
87
|
+
elsif result.skipped?
|
88
|
+
:skipped
|
89
|
+
else
|
90
|
+
:failed
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyCI
|
4
|
+
class ExtractDescriptions
|
5
|
+
def call(example_group, count: false)
|
6
|
+
data = {}
|
7
|
+
|
8
|
+
data[scoped_id(example_group)] = {
|
9
|
+
description: description(example_group),
|
10
|
+
line_number: line_number(example_group),
|
11
|
+
}
|
12
|
+
|
13
|
+
if count
|
14
|
+
data[:test_count] ||= 0
|
15
|
+
data[:test_count] += RSpec.world.example_count([example_group])
|
16
|
+
end
|
17
|
+
|
18
|
+
example_group.examples.each do |ex|
|
19
|
+
data[scoped_id(example_group)][scoped_id(ex)] = {
|
20
|
+
line_number: line_number(ex),
|
21
|
+
description: description(ex),
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
example_group.children.each do |child|
|
26
|
+
data[scoped_id(example_group)].merge! call(child)
|
27
|
+
end
|
28
|
+
|
29
|
+
data
|
30
|
+
end
|
31
|
+
|
32
|
+
def scoped_id(example_group)
|
33
|
+
example_group.metadata[:scoped_id].split(":").last
|
34
|
+
end
|
35
|
+
|
36
|
+
def line_number(example_group)
|
37
|
+
example_group.metadata[:line_number]
|
38
|
+
end
|
39
|
+
|
40
|
+
def description(example_group)
|
41
|
+
example_group.metadata[:description]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyCI
|
4
|
+
class RspecFormatter
|
5
|
+
attr_reader :current_test_key
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@output = {}
|
9
|
+
@is_failed = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def passed?
|
13
|
+
!@is_failed
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_test_key=(value)
|
17
|
+
@current_test_key = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def example_finished(notification)
|
21
|
+
example = notification.example
|
22
|
+
metadata = example.metadata
|
23
|
+
|
24
|
+
*example_group_ids, example_id = metadata[:scoped_id].split(":")
|
25
|
+
|
26
|
+
file_output = @output[current_test_key] ||= {}
|
27
|
+
|
28
|
+
example_group = example_group_ids.reduce(file_output) do |output, scope_id|
|
29
|
+
output[scope_id] ||= {}
|
30
|
+
output[scope_id]
|
31
|
+
end
|
32
|
+
|
33
|
+
example_group[example_id] = {
|
34
|
+
run_time: example.execution_result.run_time,
|
35
|
+
status: example.execution_result.status
|
36
|
+
}
|
37
|
+
|
38
|
+
if example.execution_result.status == :failed
|
39
|
+
@is_failed = true
|
40
|
+
example_group[example_id][:fully_formatted] =
|
41
|
+
notification.fully_formatted(0, ::RSpec::Core::Formatters::ConsoleCodes)
|
42
|
+
elsif metadata[:retry_attempts] && metadata[:retry_attempts] > 0
|
43
|
+
example_group[example_id][:retry_attempts] = metadata[:retry_attempts]
|
44
|
+
example_group[example_id][:fully_formatted] =
|
45
|
+
example.set_exception metadata[:retry_exceptions].first.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
example_group[example_id]
|
49
|
+
end
|
50
|
+
|
51
|
+
def dump_and_reset
|
52
|
+
output = @output
|
53
|
+
@output = {}
|
54
|
+
output
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "rspec_formatter"
|
3
|
+
require_relative "extract_definitions"
|
4
|
+
|
5
|
+
module RubyCI
|
6
|
+
module RunnerPrepend
|
7
|
+
def run_specs(example_groups)
|
8
|
+
examples_count = @world.example_count(example_groups)
|
9
|
+
|
10
|
+
example_groups = example_groups.reduce({}) do |acc, ex_group|
|
11
|
+
if acc[ex_group.file_path]
|
12
|
+
acc[ex_group.file_path] << ex_group
|
13
|
+
else
|
14
|
+
acc[ex_group.file_path] = [ex_group]
|
15
|
+
end
|
16
|
+
acc
|
17
|
+
end
|
18
|
+
|
19
|
+
RubyCI.configure { |c| c.run_key = "rspec" }
|
20
|
+
|
21
|
+
RubyCI.rspec_ws.on(:enq_request) do
|
22
|
+
example_groups.reduce({}) do |example_group_descriptions, (file, example_groups)|
|
23
|
+
example_groups.each do |example_group|
|
24
|
+
data = RubyCI::ExtractDescriptions.new.call(example_group, count: true)
|
25
|
+
|
26
|
+
next if data[:test_count] == 0
|
27
|
+
|
28
|
+
if example_group_descriptions[file]
|
29
|
+
example_group_descriptions[file].merge!(data) do |k, v1, v2|
|
30
|
+
v1 + v2
|
31
|
+
end
|
32
|
+
else
|
33
|
+
example_group_descriptions[file] = data
|
34
|
+
end
|
35
|
+
end
|
36
|
+
example_group_descriptions
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
examples_passed = @configuration.reporter.report(examples_count) do |reporter|
|
41
|
+
@configuration.with_suite_hooks do
|
42
|
+
if examples_count == 0 && @configuration.fail_if_no_examples
|
43
|
+
return @configuration.failure_exit_code
|
44
|
+
end
|
45
|
+
|
46
|
+
formatter = RubyCI::RspecFormatter.new
|
47
|
+
|
48
|
+
reporter.register_listener(formatter, :example_finished)
|
49
|
+
|
50
|
+
RubyCI.rspec_ws.on(:deq) do |tests|
|
51
|
+
tests.each do |test|
|
52
|
+
file, scoped_id = test.split(":", 2)
|
53
|
+
Thread.current[:rubyci_scoped_ids] = scoped_id
|
54
|
+
example_groups[file].each do |file_group|
|
55
|
+
formatter.current_test_key = test
|
56
|
+
|
57
|
+
file_group.run(reporter)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
formatter.dump_and_reset
|
62
|
+
end
|
63
|
+
|
64
|
+
RubyCI.rspec_await
|
65
|
+
|
66
|
+
formatter.passed?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
exit_code(examples_passed)
|
71
|
+
end
|
72
|
+
|
73
|
+
def exit_code(examples_passed=false)
|
74
|
+
return @configuration.error_exit_code || @configuration.failure_exit_code if @world.non_example_failure
|
75
|
+
return @configuration.failure_exit_code unless examples_passed
|
76
|
+
|
77
|
+
0
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/ruby_ci/version.rb
CHANGED
data/lib/ruby_ci.rb
CHANGED
@@ -20,12 +20,20 @@ module RubyCI
|
|
20
20
|
yield(configuration)
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
@
|
23
|
+
def rspec_ws
|
24
|
+
@rspec_ws ||= WebSocket.new('rspec')
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
|
27
|
+
def minitest_ws
|
28
|
+
@minitest_ws ||= WebSocket.new('minitest')
|
29
|
+
end
|
30
|
+
|
31
|
+
def rspec_await
|
32
|
+
rspec_ws.await
|
33
|
+
end
|
34
|
+
|
35
|
+
def minitest_await
|
36
|
+
minitest_ws.await
|
29
37
|
end
|
30
38
|
|
31
39
|
def debug(msg)
|
@@ -35,13 +43,14 @@ module RubyCI
|
|
35
43
|
|
36
44
|
class WebSocket
|
37
45
|
attr_reader :node_index
|
38
|
-
attr_accessor :connection, :task
|
46
|
+
attr_accessor :connection, :task, :run_key
|
39
47
|
|
40
48
|
SUPPORTED_EVENTS = %i[enq_request deq].freeze
|
41
49
|
|
42
|
-
def initialize
|
50
|
+
def initialize(run_key)
|
43
51
|
@on = {}
|
44
52
|
@ref = 0
|
53
|
+
@run_key = run_key
|
45
54
|
end
|
46
55
|
|
47
56
|
def on(event, &block)
|
@@ -148,13 +157,13 @@ module RubyCI
|
|
148
157
|
end
|
149
158
|
|
150
159
|
def topic
|
151
|
-
"test_orchestrator:#{
|
160
|
+
"test_orchestrator:#{run_key}-#{RubyCI.configuration.build_id}"
|
152
161
|
end
|
153
162
|
|
154
163
|
def endpoint
|
155
164
|
params = URI.encode_www_form({
|
156
165
|
build_id: RubyCI.configuration.build_id,
|
157
|
-
run_key:
|
166
|
+
run_key: run_key,
|
158
167
|
secret_key: RubyCI.configuration.secret_key,
|
159
168
|
branch: RubyCI.configuration.branch,
|
160
169
|
commit: RubyCI.configuration.commit,
|
@@ -164,7 +173,7 @@ module RubyCI
|
|
164
173
|
|
165
174
|
url = "wss://#{RubyCI.configuration.api_url}/test_orchestrators/socket/websocket?#{params}"
|
166
175
|
|
167
|
-
Async::HTTP::Endpoint.parse(url)
|
176
|
+
Async::HTTP::Endpoint.parse(url, alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
|
168
177
|
end
|
169
178
|
end
|
170
179
|
end
|
data/ruby_ci.gemspec
CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
29
29
|
|
30
|
+
spec.add_dependency "console", "~> 1.15.0"
|
30
31
|
spec.add_dependency "async-websocket", '<= 0.20.0'
|
31
32
|
spec.add_development_dependency "pry"
|
32
33
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_ci
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ale ∴
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: console
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.15.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.15.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: async-websocket
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -57,9 +71,14 @@ files:
|
|
57
71
|
- Rakefile
|
58
72
|
- bin/console
|
59
73
|
- bin/setup
|
74
|
+
- lib/minitest/reporters/rubyci_reporter.rb
|
75
|
+
- lib/minitest/rubyci_plugin.rb
|
60
76
|
- lib/ruby_ci.rb
|
61
77
|
- lib/ruby_ci/configuration.rb
|
62
78
|
- lib/ruby_ci/exceptions.rb
|
79
|
+
- lib/ruby_ci/extract_definitions.rb
|
80
|
+
- lib/ruby_ci/rspec_formatter.rb
|
81
|
+
- lib/ruby_ci/runner_prepend.rb
|
63
82
|
- lib/ruby_ci/version.rb
|
64
83
|
- ruby_ci.gemspec
|
65
84
|
homepage: https://github.com/RubyCI/ruby_ci_gem
|