judges 0.60.3 → 0.60.4

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.
@@ -3,16 +3,16 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require 'typhoeus'
7
- require 'iri'
8
6
  require 'baza-rb'
9
7
  require 'elapsed'
8
+ require 'iri'
9
+ require 'typhoeus'
10
10
  require_relative '../../judges'
11
11
  require_relative '../../judges/impex'
12
12
 
13
13
  # The +pull+ command.
14
14
  #
15
- # This class is instantiated by the +bin/judge+ command line interface. You
15
+ # This class is instantiated by the +bin/judges+ command line interface. You
16
16
  # are not supposed to instantiate it yourself.
17
17
  #
18
18
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -30,7 +30,7 @@ class Judges::Pull
30
30
  # @param [Array] args List of command line arguments
31
31
  # @raise [RuntimeError] If not exactly two arguments provided
32
32
  def run(opts, args)
33
- raise 'Exactly two arguments required' unless args.size == 2
33
+ raise(ArgumentError, 'Exactly two arguments required') unless args.size == 2
34
34
  fb = Factbase.new
35
35
  baza = BazaRb.new(
36
36
  opts['host'], opts['port'].to_i, opts['token'],
@@ -47,16 +47,16 @@ class Judges::Pull
47
47
  jid = baza.recent(name)
48
48
  unless baza.exit_code(jid).zero?
49
49
  @loog.warn("STDOUT of the job ##{jid} (from the server):\n#{baza.stdout(jid)}")
50
- raise "The job ##{jid} ('#{name}') is broken, maybe you should expire it"
50
+ raise(StandardError, "The job ##{jid} ('#{name}') is broken, maybe you should expire it")
51
51
  end
52
52
  fb.import(baza.pull(wait(name, baza, jid, opts['wait'])))
53
53
  Judges::Impex.new(@loog, args[1]).export(fb)
54
54
  ensure
55
55
  baza.unlock(name, opts['owner'])
56
56
  end
57
- throw :"👍 Pulled #{fb.size} facts by name '#{name}'"
57
+ throw(:"👍 Pulled #{fb.size} facts by name '#{name}'")
58
58
  else
59
- throw :"⚠️ Nothing to pull - name '#{name}' not found on server"
59
+ throw(:"⚠️ Nothing to pull - name '#{name}' not found on server")
60
60
  end
61
61
  end
62
62
  end
@@ -64,14 +64,17 @@ class Judges::Pull
64
64
  private
65
65
 
66
66
  def wait(name, baza, id, limit)
67
- raise 'Waiting time is nil' if limit.nil?
67
+ raise(StandardError, 'Waiting time is nil') if limit.nil?
68
68
  start = Time.now
69
69
  loop do
70
70
  break if baza.finished?(id)
71
- sleep 1
72
- raise "Time is over, the job ##{id} ('#{name}') is still not completed" if Time.now - start > limit
73
- lapsed = Time.now - start
74
- @loog.debug("Still waiting for the job ##{id} ('#{name}') to finish... (#{format('%.2f', lapsed)}s already)")
71
+ sleep(1)
72
+ if Time.now - start > limit
73
+ raise(StandardError, "Time is over, the job ##{id} ('#{name}') is still not completed")
74
+ end
75
+ @loog.debug(
76
+ "Still waiting for the job ##{id} ('#{name}') to finish... (#{format('%.2f', Time.now - start)}s already)"
77
+ )
75
78
  end
76
79
  id
77
80
  end
@@ -3,16 +3,16 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require 'typhoeus'
7
- require 'iri'
8
6
  require 'baza-rb'
9
7
  require 'elapsed'
8
+ require 'iri'
9
+ require 'typhoeus'
10
10
  require_relative '../../judges'
11
11
  require_relative '../../judges/impex'
12
12
 
13
13
  # The +push+ command.
14
14
  #
15
- # This class is instantiated by the +bin/judge+ command line interface. You
15
+ # This class is instantiated by the +bin/judges+ command line interface. You
16
16
  # are not supposed to instantiate it yourself.
17
17
  #
18
18
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -30,7 +30,7 @@ class Judges::Push
30
30
  # @param [Array] args List of command line arguments
31
31
  # @raise [RuntimeError] If not exactly two arguments provided
32
32
  def run(opts, args)
33
- raise 'Exactly two arguments required: <name> and <path>' unless args.size == 2
33
+ raise(ArgumentError, 'Exactly two arguments required: <name> and <path>') unless args.size == 2
34
34
  name = args[0]
35
35
  fb = Judges::Impex.new(@loog, args[1]).import
36
36
  baza = BazaRb.new(
@@ -45,7 +45,7 @@ class Judges::Push
45
45
  baza.lock(name, opts['owner'])
46
46
  begin
47
47
  baza.push(name, fb.export, opts['meta'] || [])
48
- throw :"👍 Pushed #{fb.size} facts to baza"
48
+ throw(:"👍 Pushed #{fb.size} facts to baza")
49
49
  ensure
50
50
  baza.unlock(name, opts['owner'])
51
51
  end
@@ -2,22 +2,23 @@
2
2
 
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
- require 'nokogiri'
6
- require 'factbase'
5
+
7
6
  require 'backtrace'
8
- require 'factbase/to_xml'
9
7
  require 'elapsed'
10
8
  require 'ellipsized'
9
+ require 'factbase'
10
+ require 'factbase/to_xml'
11
+ require 'nokogiri'
11
12
  require 'timeout'
12
13
  require_relative '../../judges'
13
- require_relative '../../judges/to_rel'
14
+ require_relative '../../judges/categories'
14
15
  require_relative '../../judges/judges'
15
16
  require_relative '../../judges/options'
16
- require_relative '../../judges/categories'
17
+ require_relative '../../judges/to_rel'
17
18
 
18
19
  # The +test+ command.
19
20
  #
20
- # This class is instantiated by the +bin/judge+ command line interface. You
21
+ # This class is instantiated by the +bin/judges+ command line interface. You
21
22
  # are not supposed to instantiate it yourself.
22
23
  #
23
24
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -34,8 +35,9 @@ class Judges::Test
34
35
  # @param [Hash] opts Command line options (start with '--')
35
36
  # @param [Array] args List of command line arguments
36
37
  # @raise [RuntimeError] If not exactly one argument provided
38
+ # rubocop:disable Metrics/MethodLength
37
39
  def run(opts, args)
38
- raise 'Exactly one argument required' unless args.size == 1
40
+ raise(ArgumentError, 'Exactly one argument required') unless args.size == 1
39
41
  dir = args[0]
40
42
  @loog.info("Testing judges in #{dir.to_rel}...")
41
43
  errors = []
@@ -51,87 +53,112 @@ class Judges::Test
51
53
  @loog.info("👉 Testing #{judge.script} (##{i}) in #{judge.dir.to_rel}...")
52
54
  buf = Loog::Buffer.new
53
55
  judge = judge.with_loog(buf)
54
- judge.tests.each do |f|
55
- tname = File.basename(f).gsub(/\.yml$/, '')
56
- visible << " #{judge.name}/#{tname}"
57
- next unless include?(opts, judge.name, tname)
58
- yaml = YAML.load_file(f, permitted_classes: [Time])
59
- if yaml['skip']
60
- buf.info("Skipped #{f.to_rel}")
61
- next
62
- end
63
- unless Judges::Categories.new(opts['enable'], opts['disable']).ok?(yaml['category'])
64
- buf.info("Skipped #{f.to_rel} because of its category")
65
- next
66
- end
67
- buf.info("🛠️ Testing #{f.to_rel}:")
68
- start = Time.now
69
- badge = "#{judge.name}/#{tname}"
70
- begin
71
- fb = Factbase.new
72
- prepare(fb, yaml)
73
- yaml['before']&.each do |n|
74
- j = judges.get(n).with_loog(buf)
75
- buf.info("Running #{j.script} judge as a pre-condition...")
76
- test_one(fb, opts, j, n, yaml, assert: false)
77
- end
78
- test_one(fb, opts, judge, tname, yaml)
79
- yaml['after']&.each do |rb|
80
- buf.info("Running #{rb} assertion script...")
81
- $fb = fb
82
- $loog = buf
83
- if yaml['timeout']
84
- Timeout.timeout(yaml['timeout']) do
85
- load(File.join(judge.dir, rb), true)
86
- end
87
- else
88
- load(File.join(judge.dir, rb), true)
89
- end
90
- end
91
- tests += 1
92
- rescue StandardError => e
93
- @loog.info(buf.to_s)
94
- @loog.warn(Backtrace.new(e))
95
- errors << badge
96
- end
97
- times[badge] = Time.now - start
98
- end
56
+ tests += run_judge_tests(judge, buf, opts, judges, visible, times, errors)
99
57
  tested += 1
100
58
  end
101
- unless times.empty?
102
- fmt = "%-60s\t%9s\t%-9s"
103
- @loog.info(
104
- [
105
- 'Test summary:',
106
- format(fmt, 'Script', 'Seconds', 'Result'),
107
- format(fmt, '---', '---', '---'),
108
- times.sort_by { |_, v| v }.reverse.map do |script, sec|
109
- format(fmt, script.ellipsized(50), format('%.3f', sec), errors.include?(script) ? 'ERROR' : 'OK')
110
- end.join("\n ")
111
- ].join("\n ")
112
- )
113
- end
114
- throw :'👍 No judges tested' if tested.zero?
115
- throw :"👍 All #{tested} judge(s) but no tests passed" if tests.zero?
116
- throw :"👍 All #{tested} judge(s) and #{tests} tests passed" if errors.empty?
117
- throw :"❌ #{tested} judge(s) tested, #{errors.size} of them failed"
59
+ print_test_summary(times, errors, tested, tests)
118
60
  end
119
61
  unless errors.empty?
120
- raise "#{errors.size} tests failed" unless opts['quiet']
121
- @loog.debug('Not failing the build with tests failures, due to the --quiet option')
62
+ raise(StandardError, "#{errors.size} tests failed") unless opts['quiet']
63
+ @loog.debug('Not failing the build with test failures, due to the --quiet option')
122
64
  end
123
65
  return unless tested.zero? || tests.zero?
124
66
  if opts['judge'].nil?
125
- raise 'There are seems to be no judges' unless opts['quiet']
67
+ raise(StandardError, 'There seem to be no judges') unless opts['quiet']
126
68
  @loog.debug('Not failing the build with no judges tested, due to the --quiet option')
127
69
  else
128
- raise 'There are seems to be no judges' if visible.empty?
70
+ raise(StandardError, 'There seem to be no judges') if visible.empty?
129
71
  @loog.info("The following judges are available to use with the --judge option:\n #{visible.join("\n ")}")
130
72
  end
131
73
  end
74
+ # rubocop:enable Metrics/MethodLength
132
75
 
133
76
  private
134
77
 
78
+ def run_judge_tests(judge, buf, opts, judges, visible, times, errors)
79
+ count = 0
80
+ judge.tests.each do |f|
81
+ tname = File.basename(f).gsub(/\.yml$/, '')
82
+ visible << " #{judge.name}/#{tname}"
83
+ next unless include?(opts, judge.name, tname)
84
+ yaml = YAML.load_file(f, permitted_classes: [Time])
85
+ next if skip_test?(buf, f, yaml, opts)
86
+ buf.info("🛠️ Testing #{f.to_rel}:")
87
+ badge = "#{judge.name}/#{tname}"
88
+ start = Time.now
89
+ begin
90
+ count += run_single_test(judge, buf, opts, judges, yaml, badge)
91
+ rescue StandardError => e
92
+ @loog.info(buf.to_s)
93
+ @loog.warn(Backtrace.new(e))
94
+ errors << badge
95
+ end
96
+ times[badge] = Time.now - start
97
+ end
98
+ count
99
+ end
100
+
101
+ def skip_test?(buf, path, yaml, opts)
102
+ if yaml['skip']
103
+ buf.info("Skipped #{path.to_rel}")
104
+ return true
105
+ end
106
+ unless Judges::Categories.new(opts['enable'], opts['disable']).ok?(yaml['category'])
107
+ buf.info("Skipped #{path.to_rel} because of its category")
108
+ return true
109
+ end
110
+ false
111
+ end
112
+
113
+ def run_single_test(judge, buf, opts, judges, yaml, badge)
114
+ fb = Factbase.new
115
+ prepare(fb, yaml)
116
+ yaml['before']&.each do |n|
117
+ j = judges.get(n).with_loog(buf)
118
+ buf.info("Running #{j.script} judge as a pre-condition...")
119
+ test_one(fb, opts, j, n, yaml, assert: false)
120
+ end
121
+ tname = badge.split('/').last
122
+ test_one(fb, opts, judge, tname, yaml)
123
+ run_after_assertions(judge, buf, fb, yaml)
124
+ 1
125
+ end
126
+
127
+ def run_after_assertions(judge, buf, fb, yaml)
128
+ yaml['after']&.each do |rb|
129
+ buf.info("Running #{rb} assertion script...")
130
+ $fb = fb
131
+ $loog = buf
132
+ if yaml['timeout']
133
+ Timeout.timeout(yaml['timeout']) do
134
+ load(File.join(judge.dir, rb), true)
135
+ end
136
+ else
137
+ load(File.join(judge.dir, rb), true)
138
+ end
139
+ end
140
+ end
141
+
142
+ def print_test_summary(times, errors, tested, tests)
143
+ unless times.empty?
144
+ fmt = "%-60s\t%9s\t%-9s"
145
+ @loog.info(
146
+ [
147
+ 'Test summary:',
148
+ format(fmt, 'Script', 'Seconds', 'Result'),
149
+ format(fmt, '---', '---', '---'),
150
+ times.sort_by { |_, v| v }.reverse.map do |script, sec|
151
+ format(fmt, script.ellipsized(50), format('%.3f', sec), errors.include?(script) ? 'ERROR' : 'OK')
152
+ end.join("\n ")
153
+ ].join("\n ")
154
+ )
155
+ end
156
+ throw(:'👍 No judges tested') if tested.zero?
157
+ throw(:"👍 All #{tested} judge(s) but no tests passed") if tests.zero?
158
+ throw(:"👍 All #{tested} judge(s) and #{tests} tests passed") if errors.empty?
159
+ throw(:"❌ #{tested} judge(s) tested, #{errors.size} of them failed")
160
+ end
161
+
135
162
  def include?(opts, name, tname = nil)
136
163
  judges = opts['judge'] || []
137
164
  return true if judges.empty?
@@ -148,14 +175,14 @@ class Judges::Test
148
175
  i.each do |k, vv|
149
176
  if vv.is_a?(Array)
150
177
  vv.each do |v|
151
- f.send(:"#{k}=", v)
178
+ f.public_send(:"#{k}=", v)
152
179
  end
153
180
  else
154
181
  if k == '_id'
155
182
  vv = id
156
183
  id += 1
157
184
  end
158
- f.send(:"#{k}=", vv)
185
+ f.public_send(:"#{k}=", vv)
159
186
  end
160
187
  end
161
188
  end
@@ -170,6 +197,7 @@ class Judges::Test
170
197
  # @param [Hash] yaml The YAML to be tested
171
198
  # @param [Boolean] assert Should we assert (TRUE) or simply skip (FALSE)?
172
199
  # @return [nil] Always NIL
200
+ # rubocop:disable Metrics/MethodLength
173
201
  def test_one(fb, opts, judge, tname, yaml, assert: true)
174
202
  options = Judges::Options.new(opts['option']) + Judges::Options.new(yaml['options'])
175
203
  runs = opts['runs'] || yaml['runs'] || 1
@@ -177,10 +205,10 @@ class Judges::Test
177
205
  (1..runs).each do |r|
178
206
  fbx = fb
179
207
  if opts['log']
180
- require 'factbase/logged'
208
+ require('factbase/logged')
181
209
  fbx = Factbase::Logged.new(fb, @loog)
182
210
  end
183
- expected_failure = yaml['expected_failure']
211
+ failure = yaml['expected_failure']
184
212
  begin
185
213
  if timeout
186
214
  Timeout.timeout(timeout) do
@@ -189,28 +217,32 @@ class Judges::Test
189
217
  else
190
218
  judge.run(fbx, {}, {}, options)
191
219
  end
192
- raise 'Exception expected but not raised' if expected_failure
220
+ raise(StandardError, 'Exception expected but not raised') if failure
193
221
  rescue Timeout::Error => e
194
- raise "Test timed out after #{timeout} seconds"
222
+ raise(StandardError, "Test timed out after #{timeout} seconds")
195
223
  # rubocop:disable Lint/RescueException
196
224
  rescue Exception => e
197
225
  # rubocop:enable Lint/RescueException
198
- raise e unless expected_failure
199
- if expected_failure.is_a?(Array) && expected_failure.none? { |s| e.message.include?(s) }
200
- raise "Exception #{e.class} raised with #{e.message.inspect}, but this is not what was expected"
226
+ raise(e) unless failure
227
+ if failure.is_a?(Array) && failure.none? { |s| e.message.include?(s) }
228
+ raise(
229
+ StandardError,
230
+ "Exception #{e.class} raised with #{e.message.inspect}, but this is not what was expected"
231
+ )
201
232
  end
202
233
  end
203
234
  next unless assert
204
235
  assert(judge, tname, fb, yaml) if r == runs || opts['assert_once'].is_a?(FalseClass)
205
236
  end
206
237
  end
238
+ # rubocop:enable Metrics/MethodLength
207
239
 
208
240
  def assert(judge, tname, fb, yaml)
209
241
  xpaths = yaml['expected']
210
242
  return if xpaths.nil?
211
243
  xml = Nokogiri::XML.parse(Factbase::ToXML.new(fb).xml)
212
244
  xpaths.each do |xp|
213
- raise "#{judge.name}/#{tname} doesn't match '#{xp}':\n#{xml}" if xml.xpath(xp).empty?
245
+ raise(StandardError, "#{judge.name}/#{tname} doesn't match '#{xp}':\n#{xml}") if xml.xpath(xp).empty?
214
246
  end
215
247
  end
216
248
  end
@@ -3,14 +3,14 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2024-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require 'time'
7
6
  require 'elapsed'
7
+ require 'time'
8
8
  require_relative '../../judges'
9
9
  require_relative '../../judges/impex'
10
10
 
11
11
  # The +trim+ command.
12
12
  #
13
- # This class is instantiated by the +bin/judge+ command line interface. You
13
+ # This class is instantiated by the +bin/judges+ command line interface. You
14
14
  # are not supposed to instantiate it yourself.
15
15
  #
16
16
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -28,14 +28,14 @@ class Judges::Trim
28
28
  # @param [Array] args List of command line arguments
29
29
  # @raise [RuntimeError] If not exactly one argument provided
30
30
  def run(opts, args)
31
- raise 'Exactly one argument required' unless args.size == 1
31
+ raise(ArgumentError, 'Exactly one argument required') unless args.size == 1
32
32
  impex = Judges::Impex.new(@loog, args[0])
33
33
  fb = impex.import
34
34
  elapsed(@loog, level: Logger::INFO) do
35
35
  deleted = fb.query(opts['query']).delete!
36
- throw :'⚠️ No facts deleted' if deleted.zero?
36
+ throw(:'⚠️ No facts deleted') if deleted.zero?
37
37
  impex.export(fb)
38
- throw :"👍 #{deleted} fact(s) deleted"
38
+ throw(:"👍 #{deleted} fact(s) deleted")
39
39
  end
40
40
  end
41
41
  end