ruby_ci 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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