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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08df68ec0f6719aa07d3505b7471a7e95caea8d5be3e120b08e11f53a148aee6'
4
- data.tar.gz: 0217ed880ef0117d6bce4af23d77482f29113d4847c2101cce7144a7da458bbd
3
+ metadata.gz: d34d2bb75becc90c41cf2366ef0c7d6bb6ccbcc5b33e6c6e460d7a3a5fd6976f
4
+ data.tar.gz: c0846f4d4ed597b738d09687d8a14697e1dff4403fbbf852ab483f70df172b5a
5
5
  SHA512:
6
- metadata.gz: cb15a2b5a52cb7f961d4602aac9eb4283301fec44badf1d218bc0b5c180dbb1f8644e1e585cf016fd0173defbebe8cdf829818ec21e0a8040e24f63eb6d78069
7
- data.tar.gz: f28fd122467ac0803965cc2eec1e8227ffb41175d9366f9f596c61f247918843257277dfe351af06c32b91bead9d4ae32114680ffa2e00cbec663c846d2c7fdd
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,9 @@
1
+ require_relative "reporters/rubyci_reporter"
2
+
3
+ module Minitest
4
+ def self.plugin_rubyci_init(options)
5
+ if ENV['RUBY_CI_SECRET_KEY'].present?
6
+ Minitest.reporter << Minitest::Reporters::RubyciReporter.new
7
+ end
8
+ end
9
+ 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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyCI
4
- VERSION = "0.1.5"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/ruby_ci.rb CHANGED
@@ -20,12 +20,20 @@ module RubyCI
20
20
  yield(configuration)
21
21
  end
22
22
 
23
- def ws
24
- @ws ||= WebSocket.new
23
+ def rspec_ws
24
+ @rspec_ws ||= WebSocket.new('rspec')
25
25
  end
26
26
 
27
- def await
28
- ws.await
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:#{RubyCI.configuration.run_key}-#{RubyCI.configuration.build_id}"
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: RubyCI.configuration.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.1.5
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: 2023-10-19 00:00:00.000000000 Z
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