quarantine 2.1.2 → 2.1.3

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: 5b572688cff1561139754374abf14bfc0fbbce203dd7ab8d0db5916b2c5b67ba
4
- data.tar.gz: 8dfc948827ad091c3424dd425ba157294340fff95ec603e76f1ffcc5059541bd
3
+ metadata.gz: 6ab8e4aa2ba3c2ab497cb3adfb08b6c14459eab525f26a00f4bc1fb9b27e1084
4
+ data.tar.gz: ffafe0bdf3508389ae614ab5b8946c4cb9b5303f7697ca4b378b6284f335375f
5
5
  SHA512:
6
- metadata.gz: 879da05b1135ab244f4246e8003418a0e5b50f68ac7ca408b7ead93ebc44b5b54676d84be4b6793166c84ddf4816f92c09664ad6d78cc02755c7bf4c0de181b9
7
- data.tar.gz: d7d9cc51559136896dde5ad0ebde281542cbe2ed4b0a42a7dfa508a264d070bab67d1ea9dfe1c213e50fa98308e85430791d69e57b301c1d1d11ce3883bcfeab
6
+ metadata.gz: c4d12718e41b13558f30add75b2a5d5e25f5d29d4e19b55704e7500c1980f7192ed1a8fa3e4fc83e45329486c94bae42bd56f99a718144f42c6884878b926faf
7
+ data.tar.gz: 283aa10d1dafe98d3e5ed08f1add97facdcfbe6f4a1761759e42677b7dd26fafbc323d958ab81965076a1d19e951e18c67e35e01679ad43e44d622876649641f
data/lib/quarantine.rb CHANGED
@@ -66,7 +66,7 @@ class Quarantine
66
66
 
67
67
  # Scans the test_statuses from the database and store their IDs in quarantined_ids
68
68
  sig { void }
69
- def fetch_test_statuses
69
+ def on_start
70
70
  begin
71
71
  test_statuses = database.fetch_items(@options[:test_statuses_table_name])
72
72
  rescue Quarantine::DatabaseError => e
@@ -103,24 +103,40 @@ class Quarantine
103
103
  end
104
104
 
105
105
  sig { void }
106
- def upload_tests
107
- return if @tests.empty? || @tests.values.count { |test| test.status == :quarantined } >= @options[:failsafe_limit]
106
+ def on_complete
107
+ quarantined_tests = @tests.values.select { |test| test.status == :quarantined }.sort_by(&:id)
108
108
 
109
- begin
110
- timestamp = Time.now.to_i / 1000 # Truncated millisecond from timestamp for reasons specific to Flexport
111
- database.write_items(
112
- @options[:test_statuses_table_name],
113
- @tests.values.map { |item| item.to_hash.merge('updated_at' => timestamp) }
114
- )
115
- rescue Quarantine::DatabaseError => e
116
- @database_failures << "#{e.cause&.class}: #{e.cause&.message}"
109
+ if !@options[:record_tests]
110
+ log('Recording tests disabled; skipping')
111
+ elsif @tests.empty?
112
+ log('No tests found; skipping recording')
113
+ elsif quarantined_tests.count { |test| old_tests[test.id]&.status != :quarantined } >= @options[:failsafe_limit]
114
+ log('Number of quarantined tests above failsafe limit; skipping recording')
115
+ else
116
+ begin
117
+ timestamp = Time.now.to_i / 1000 # Truncated millisecond from timestamp for reasons specific to Flexport
118
+ database.write_items(
119
+ @options[:test_statuses_table_name],
120
+ @tests.values.map { |item| item.to_hash.merge('updated_at' => timestamp) }
121
+ )
122
+ rescue Quarantine::DatabaseError => e
123
+ @database_failures << "#{e.cause&.class}: #{e.cause&.message}"
124
+ end
117
125
  end
126
+
127
+ log(<<~MESSAGE)
128
+ \n[quarantine] Quarantined tests:
129
+ #{quarantined_tests.map { |test| "#{test.id} #{test.full_description}" }.join("\n ")}
130
+
131
+ [quarantine] Database errors:
132
+ #{@database_failures.join("\n ")}
133
+ MESSAGE
118
134
  end
119
135
 
120
136
  # Param: RSpec::Core::Example
121
137
  # Add the example to the internal tests list
122
138
  sig { params(example: T.untyped, status: Symbol, passed: T::Boolean).void }
123
- def record_test(example, status, passed:)
139
+ def on_test(example, status, passed:)
124
140
  extra_attributes = @options[:extra_attributes] ? @options[:extra_attributes].call(example) : {}
125
141
 
126
142
  new_consecutive_passes = passed ? (@old_tests[example.id]&.consecutive_passes || 0) + 1 : 0
@@ -145,15 +161,8 @@ class Quarantine
145
161
  @old_tests[example.id]&.status == :quarantined
146
162
  end
147
163
 
148
- sig { returns(String) }
149
- def summary
150
- quarantined_tests = @tests.values.select { |test| test.status == :quarantined }.sort_by(&:id)
151
- <<~MESSAGE
152
- \n[quarantine] Quarantined tests:
153
- #{quarantined_tests.map { |test| "#{test.id} #{test.full_description}" }.join("\n ")}
154
-
155
- [quarantine] Database errors:
156
- #{@database_failures.join("\n ")}
157
- MESSAGE
164
+ sig { params(message: String).void }
165
+ def log(message)
166
+ @options[:log].call(message) if @options[:logging]
158
167
  end
159
168
  end
@@ -21,7 +21,7 @@ class Quarantine
21
21
  sig { override.params(table_name: String).returns(T::Enumerable[Item]) }
22
22
  def fetch_items(table_name)
23
23
  parse_rows(spreadsheet.worksheet_by_title(table_name))
24
- rescue GoogleDrive::Error
24
+ rescue GoogleDrive::Error, Google::Apis::Error
25
25
  raise Quarantine::DatabaseError
26
26
  end
27
27
 
@@ -56,7 +56,7 @@ class Quarantine
56
56
  # Insert any items whose IDs weren't found in existing rows at the end
57
57
  worksheet.insert_rows(parsed_rows.count + 2, new_rows)
58
58
  worksheet.save
59
- rescue GoogleDrive::Error
59
+ rescue GoogleDrive::Error, Google::Apis::Error
60
60
  raise Quarantine::DatabaseError
61
61
  end
62
62
 
@@ -4,16 +4,12 @@ class Quarantine
4
4
  module RSpecAdapter
5
5
  extend T::Sig
6
6
 
7
- # Purpose: create an instance of Quarantine which contains information
8
- # about the test suite (ie. quarantined tests) and binds RSpec configurations
9
- # and hooks onto the global RSpec class
10
7
  sig { void }
11
8
  def self.bind
12
- bind_rspec_configurations
13
- bind_fetch_test_statuses
14
- bind_record_tests
15
- bind_upload_tests
16
- bind_logger
9
+ register_rspec_configurations
10
+ bind_on_start
11
+ bind_on_test
12
+ bind_on_complete
17
13
  end
18
14
 
19
15
  sig { returns(Quarantine) }
@@ -25,12 +21,15 @@ class Quarantine
25
21
  extra_attributes: RSpec.configuration.quarantine_extra_attributes,
26
22
  failsafe_limit: RSpec.configuration.quarantine_failsafe_limit,
27
23
  release_at_consecutive_passes: RSpec.configuration.quarantine_release_at_consecutive_passes,
24
+ logging: RSpec.configuration.quarantine_logging,
25
+ log: method(:log),
26
+ record_tests: RSpec.configuration.quarantine_record_tests
28
27
  )
29
28
  end
30
29
 
31
30
  # Purpose: binds rspec configuration variables
32
31
  sig { void }
33
- def self.bind_rspec_configurations
32
+ def self.register_rspec_configurations
34
33
  ::RSpec.configure do |config|
35
34
  config.add_setting(:quarantine_database, default: { type: :dynamodb, region: 'us-west-1' })
36
35
  config.add_setting(:quarantine_test_statuses, { default: 'test_statuses' })
@@ -45,17 +44,17 @@ class Quarantine
45
44
 
46
45
  # Purpose: binds quarantine to fetch the test_statuses from dynamodb in the before suite
47
46
  sig { void }
48
- def self.bind_fetch_test_statuses
47
+ def self.bind_on_start
49
48
  ::RSpec.configure do |config|
50
49
  config.before(:suite) do
51
- Quarantine::RSpecAdapter.quarantine.fetch_test_statuses
50
+ Quarantine::RSpecAdapter.quarantine.on_start
52
51
  end
53
52
  end
54
53
  end
55
54
 
56
55
  # Purpose: binds quarantine to record test statuses
57
56
  sig { void }
58
- def self.bind_record_tests
57
+ def self.bind_on_test
59
58
  ::RSpec.configure do |config|
60
59
  config.after(:each) do |example|
61
60
  metadata = example.metadata
@@ -68,42 +67,35 @@ class Quarantine
68
67
  # will record the failed test if it's final retry from the rspec-retry gem
69
68
  if RSpec.configuration.skip_quarantined_tests && quarantined
70
69
  example.clear_exception!
71
- Quarantine::RSpecAdapter.quarantine.record_test(example, :quarantined, passed: false)
70
+ Quarantine::RSpecAdapter.quarantine.on_test(example, :quarantined, passed: false)
72
71
  else
73
- Quarantine::RSpecAdapter.quarantine.record_test(example, :failing, passed: false)
72
+ Quarantine::RSpecAdapter.quarantine.on_test(example, :failing, passed: false)
74
73
  end
75
74
  end
76
75
  elsif metadata[:retry_attempts] > 0
77
76
  # will record the flaky test if it failed the first run but passed a subsequent run
78
- Quarantine::RSpecAdapter.quarantine.record_test(example, :quarantined, passed: false)
77
+ Quarantine::RSpecAdapter.quarantine.on_test(example, :quarantined, passed: false)
79
78
  elsif quarantined
80
- Quarantine::RSpecAdapter.quarantine.record_test(example, :quarantined, passed: true)
79
+ Quarantine::RSpecAdapter.quarantine.on_test(example, :quarantined, passed: true)
81
80
  else
82
- Quarantine::RSpecAdapter.quarantine.record_test(example, :passing, passed: true)
81
+ Quarantine::RSpecAdapter.quarantine.on_test(example, :passing, passed: true)
83
82
  end
84
83
  end
85
84
  end
86
85
  end
87
86
 
88
87
  sig { void }
89
- def self.bind_upload_tests
88
+ def self.bind_on_complete
90
89
  ::RSpec.configure do |config|
91
90
  config.after(:suite) do
92
- Quarantine::RSpecAdapter.quarantine.upload_tests if RSpec.configuration.quarantine_record_tests
91
+ Quarantine::RSpecAdapter.quarantine.on_complete
93
92
  end
94
93
  end
95
94
  end
96
95
 
97
- # Purpose: binds quarantine logger to output test to RSpec formatter messages
98
- sig { void }
99
- def self.bind_logger
100
- ::RSpec.configure do |config|
101
- config.after(:suite) do
102
- if RSpec.configuration.quarantine_logging
103
- RSpec.configuration.reporter.message(Quarantine::RSpecAdapter.quarantine.summary)
104
- end
105
- end
106
- end
96
+ sig { params(message: String).void }
97
+ def self.log(message)
98
+ RSpec.configuration.reporter.message(message)
107
99
  end
108
100
  end
109
101
  end
@@ -1,3 +1,3 @@
1
1
  class Quarantine
2
- VERSION = '2.1.2'.freeze
2
+ VERSION = '2.1.3'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quarantine
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 2.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Flexport Engineering, Eric Zhu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-14 00:00:00.000000000 Z
11
+ date: 2021-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec