judges 0.56.0 ā 0.57.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/bin/judges +2 -0
- data/features/update.feature +41 -0
- data/judges.gemspec +1 -1
- data/lib/judges/commands/update.rb +20 -8
- data/lib/judges/statistics.rb +83 -0
- data/lib/judges.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25236f211b356d3dfe62fd6ba9eb7eb70a04e2de64d2581d682db989967cefa9
|
|
4
|
+
data.tar.gz: c804cf366c81db1834496d71852422dbb245edfc76f0a24511621a4aa7979ada
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c33f8ae0312222d0ae2ec848d031648fc5f04cb2a64d49e63a26176157a2d9e82bb2fa1f8e010aa0fe8a482fe16690145bdca92f367f05afa851687a18f09af
|
|
7
|
+
data.tar.gz: 6bfafca7127eca496160d7b57325e92815ced1872da8f929380aa0fee185d5f3931cdf5eb753d39a3c68c9c19800a2cfa100b99da60288d9dbae33ccb9680309
|
data/bin/judges
CHANGED
|
@@ -135,6 +135,8 @@ class JudgesGLI extend GLI::App
|
|
|
135
135
|
c.flag([:summary], must_match: %w[off add append], default_value: 'off')
|
|
136
136
|
c.desc 'Use default logging facility'
|
|
137
137
|
c.switch([:log], default_value: true)
|
|
138
|
+
c.desc 'Show execution statistics for each judge'
|
|
139
|
+
c.switch([:statistics], default_value: false)
|
|
138
140
|
c.desc 'Expect at least one judge to be used (fail if none are used)'
|
|
139
141
|
c.switch([:'expect-judges'], default_value: true)
|
|
140
142
|
run_it(c, 'update')
|
data/features/update.feature
CHANGED
|
@@ -214,3 +214,44 @@ Feature: Update
|
|
|
214
214
|
Then Stdout contains "Running delayed"
|
|
215
215
|
Then Stdout contains "3 judge(s) processed"
|
|
216
216
|
And Exit code is zero
|
|
217
|
+
|
|
218
|
+
Scenario: Show statistics for judge execution
|
|
219
|
+
Given I make a temp directory
|
|
220
|
+
Then I have a "alpha/alpha.rb" file with content:
|
|
221
|
+
"""
|
|
222
|
+
$fb.insert.name = 'alpha'
|
|
223
|
+
"""
|
|
224
|
+
Then I have a "beta/beta.rb" file with content:
|
|
225
|
+
"""
|
|
226
|
+
$fb.insert.name = 'beta'
|
|
227
|
+
"""
|
|
228
|
+
Then I run bin/judges with "update --statistics --quiet --max-cycles 1 . stats.fb"
|
|
229
|
+
Then Stdout contains "Judge execution summary:"
|
|
230
|
+
Then Stdout contains "Judge"
|
|
231
|
+
Then Stdout contains "Seconds"
|
|
232
|
+
Then Stdout contains "Changes"
|
|
233
|
+
Then Stdout contains "Cycles"
|
|
234
|
+
Then Stdout contains "Result"
|
|
235
|
+
Then Stdout contains "alpha"
|
|
236
|
+
Then Stdout contains "beta"
|
|
237
|
+
Then Stdout contains "OK"
|
|
238
|
+
And Exit code is zero
|
|
239
|
+
|
|
240
|
+
Scenario: Summarize results in statistics
|
|
241
|
+
Given I make a temp directory
|
|
242
|
+
Then I have a "alpha/alpha.rb" file with content:
|
|
243
|
+
"""
|
|
244
|
+
$fb.insert.name = 'alpha'
|
|
245
|
+
sleep 1.91
|
|
246
|
+
"""
|
|
247
|
+
Then I have a "beta/beta.rb" file with content:
|
|
248
|
+
"""
|
|
249
|
+
$fb.insert.name = 'beta'
|
|
250
|
+
"""
|
|
251
|
+
Then I run bin/judges with "update --statistics --quiet --max-cycles 2 --lifetime 4 --timeout 3 . stats.fb"
|
|
252
|
+
Then Stdout contains "Judge execution summary:"
|
|
253
|
+
Then Stdout contains "alpha"
|
|
254
|
+
Then Stdout contains "beta"
|
|
255
|
+
Then Stdout contains "1xOK"
|
|
256
|
+
Then Stdout contains "1xSKIPPED (timeout)"
|
|
257
|
+
And Exit code is zero
|
data/judges.gemspec
CHANGED
|
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
|
9
9
|
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
|
|
10
10
|
s.required_ruby_version = '>=3.2'
|
|
11
11
|
s.name = 'judges'
|
|
12
|
-
s.version = '0.
|
|
12
|
+
s.version = '0.57.0'
|
|
13
13
|
s.license = 'MIT'
|
|
14
14
|
s.summary = 'Command-Line Tool for a Factbase'
|
|
15
15
|
s.description =
|
|
@@ -16,6 +16,7 @@ require_relative '../../judges'
|
|
|
16
16
|
require_relative '../../judges/impex'
|
|
17
17
|
require_relative '../../judges/judges'
|
|
18
18
|
require_relative '../../judges/options'
|
|
19
|
+
require_relative '../../judges/statistics'
|
|
19
20
|
require_relative '../../judges/to_rel'
|
|
20
21
|
|
|
21
22
|
# The +update+ command.
|
|
@@ -88,6 +89,7 @@ class Judges::Update
|
|
|
88
89
|
c = 0
|
|
89
90
|
churn = Factbase::Churn.new
|
|
90
91
|
errors = []
|
|
92
|
+
statistics = opts['statistics'] ? Judges::Statistics.new : nil
|
|
91
93
|
sum = fb.query('(eq what "judges-summary")').each.to_a
|
|
92
94
|
if sum.empty?
|
|
93
95
|
@loog.info('Summary fact not found') unless opts['summary'] == 'off'
|
|
@@ -108,7 +110,7 @@ class Judges::Update
|
|
|
108
110
|
end
|
|
109
111
|
@loog.info("\nStarting cycle ##{c}#{" (out of #{opts['max-cycles']})" if opts['max-cycles']}...")
|
|
110
112
|
end
|
|
111
|
-
delta = cycle(opts, judges, fb, options, errors)
|
|
113
|
+
delta = cycle(opts, judges, fb, options, errors, statistics)
|
|
112
114
|
churn += delta
|
|
113
115
|
impex.export(fb)
|
|
114
116
|
if delta.zero?
|
|
@@ -127,6 +129,7 @@ class Judges::Update
|
|
|
127
129
|
end
|
|
128
130
|
throw :"š Update completed in #{c} cycle(s), did #{churn}"
|
|
129
131
|
end
|
|
132
|
+
statistics&.report(@loog)
|
|
130
133
|
return unless %w[add append].include?(opts['summary'])
|
|
131
134
|
summarize(fb, churn, errors, c)
|
|
132
135
|
impex.export(fb)
|
|
@@ -172,9 +175,10 @@ class Judges::Update
|
|
|
172
175
|
# @param [Factbase] fb The factbase
|
|
173
176
|
# @param [Judges::Options] options The options
|
|
174
177
|
# @param [Array<String>] errors List of errors
|
|
178
|
+
# @param [Judges::Statistics] statistics Statistics tracking object (optional)
|
|
175
179
|
# @return [Factbase::Churn] How many modifications have been made
|
|
176
|
-
def cycle(opts, judges, fb, options, errors)
|
|
177
|
-
|
|
180
|
+
def cycle(opts, judges, fb, options, errors, statistics = nil)
|
|
181
|
+
delta = Factbase::Churn.new
|
|
178
182
|
global = {}
|
|
179
183
|
used = 0
|
|
180
184
|
elapsed(@loog, level: Logger::INFO) do
|
|
@@ -182,27 +186,35 @@ class Judges::Update
|
|
|
182
186
|
judges.each_with_index do |judge, i|
|
|
183
187
|
if opts['fail-fast'] && !errors.empty?
|
|
184
188
|
@loog.info("Not running #{judge.name.inspect} due to #{errors.count} errors above, in --fail-fast mode")
|
|
189
|
+
statistics&.record(judge.name, 0, 'SKIPPED (fail-fast)') if include?(opts, judge.name)
|
|
185
190
|
next
|
|
186
191
|
end
|
|
187
192
|
if opts['lifetime'] && opts['timeout']
|
|
188
193
|
remained = @start + opts['lifetime'] - Time.now
|
|
189
194
|
if remained < opts['timeout'].to_f / 16
|
|
190
195
|
@loog.info("Not running #{judge.name.inspect}, not enough time left (just #{remained.seconds})")
|
|
196
|
+
statistics&.record(judge.name, 0, 'SKIPPED (timeout)') if include?(opts, judge.name)
|
|
191
197
|
next
|
|
192
198
|
end
|
|
193
199
|
end
|
|
194
200
|
next unless include?(opts, judge.name)
|
|
195
201
|
@loog.info("\nš Running #{judge.name} (##{i}) at #{judge.dir.to_rel} (#{@start.ago} already)...")
|
|
196
202
|
used += 1
|
|
203
|
+
start_time = Time.now
|
|
204
|
+
result = 'OK'
|
|
205
|
+
impact = nil
|
|
197
206
|
elapsed(@loog, level: Logger::INFO) do
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
throw :"š The '#{judge.name}' judge made zero changes to #{fb.size} facts" if
|
|
201
|
-
throw :"š The '#{judge.name}' judge #{
|
|
207
|
+
impact = one_judge(opts, fb, judge, global, options, errors)
|
|
208
|
+
delta += impact
|
|
209
|
+
throw :"š The '#{judge.name}' judge made zero changes to #{fb.size} facts" if impact.zero?
|
|
210
|
+
throw :"š The '#{judge.name}' judge #{impact} out of #{fb.size} facts"
|
|
202
211
|
end
|
|
203
212
|
rescue StandardError, SyntaxError => e
|
|
204
213
|
@loog.warn(Backtrace.new(e))
|
|
205
214
|
errors << e.message
|
|
215
|
+
result = 'ERROR'
|
|
216
|
+
ensure
|
|
217
|
+
statistics&.record(judge.name, Time.now - start_time, result, impact) if start_time
|
|
206
218
|
end
|
|
207
219
|
throw :"š #{done} judge(s) processed" if errors.empty?
|
|
208
220
|
throw :"ā #{done} judge(s) processed with #{errors.size} errors"
|
|
@@ -215,7 +227,7 @@ class Judges::Update
|
|
|
215
227
|
raise "Failed to update correctly (#{errors.size} errors)" unless opts['quiet']
|
|
216
228
|
@loog.info('Not failing because of the --quiet flag provided')
|
|
217
229
|
end
|
|
218
|
-
|
|
230
|
+
delta
|
|
219
231
|
end
|
|
220
232
|
|
|
221
233
|
# Run a single judge.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
require_relative '../judges'
|
|
7
|
+
|
|
8
|
+
# Statistics collector for judge executions.
|
|
9
|
+
#
|
|
10
|
+
# This class collects and aggregates statistics about judge executions
|
|
11
|
+
# across multiple cycles, providing insights into performance and results.
|
|
12
|
+
#
|
|
13
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
14
|
+
# Copyright:: Copyright (c) 2024-2025 Yegor Bugayenko
|
|
15
|
+
# License:: MIT
|
|
16
|
+
class Judges::Statistics
|
|
17
|
+
# Initialize empty statistics.
|
|
18
|
+
def initialize
|
|
19
|
+
@data = {}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Check if statistics are empty.
|
|
23
|
+
# @return [Boolean] True if no statistics have been collected
|
|
24
|
+
def empty?
|
|
25
|
+
@data.empty?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Record statistics for a judge execution.
|
|
29
|
+
# @param [String] name The judge name
|
|
30
|
+
# @param [Float] time The execution time for this run
|
|
31
|
+
# @param [String] result The result for this run
|
|
32
|
+
# @param [Churn] churn The churn for this run (can be nil)
|
|
33
|
+
def record(name, time, result, churn = nil)
|
|
34
|
+
unless @data[name]
|
|
35
|
+
@data[name] = {
|
|
36
|
+
total_time: 0.0,
|
|
37
|
+
cycles: 0,
|
|
38
|
+
results: [],
|
|
39
|
+
total_churn: nil
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
stats = @data[name]
|
|
43
|
+
stats[:total_time] += time
|
|
44
|
+
stats[:cycles] += 1
|
|
45
|
+
stats[:results] << result
|
|
46
|
+
return unless churn
|
|
47
|
+
if stats[:total_churn]
|
|
48
|
+
stats[:total_churn] += churn
|
|
49
|
+
else
|
|
50
|
+
stats[:total_churn] = churn
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Generate a formatted statistics report.
|
|
55
|
+
# @param [Loog] loog Logging facility for output
|
|
56
|
+
def report(loog)
|
|
57
|
+
return if empty?
|
|
58
|
+
fmt = "%-30s\t%9s\t%7s\t%15s\t%-15s"
|
|
59
|
+
loog.info(
|
|
60
|
+
[
|
|
61
|
+
'Judge execution summary:',
|
|
62
|
+
format(fmt, 'Judge', 'Seconds', 'Cycles', 'Changes', 'Results'),
|
|
63
|
+
format(fmt, '---', '---', '---', '---', '---'),
|
|
64
|
+
@data.sort_by { |_, stats| stats[:total_time] }.reverse.map do |name, stats|
|
|
65
|
+
format(fmt, name, format('%.3f', stats[:total_time]), stats[:cycles],
|
|
66
|
+
stats[:total_churn] ? stats[:total_churn].to_s : 'N/A', summarize(stats[:results]))
|
|
67
|
+
end.join("\n ")
|
|
68
|
+
].join("\n ")
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# Summarize results across multiple cycles into a compact string.
|
|
75
|
+
# @param [Array<String>] results Array of result strings from different cycles
|
|
76
|
+
# @return [String] Compact summary of results
|
|
77
|
+
def summarize(results)
|
|
78
|
+
return 'N/A' if results.empty?
|
|
79
|
+
counts = results.each_with_object(Hash.new(0)) { |result, hash| hash[result] += 1 }
|
|
80
|
+
return results.first if counts.size == 1
|
|
81
|
+
counts.sort_by { |_, count| -count }.map { |result, count| "#{count}x#{result}" }.join(', ')
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/judges.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: judges
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.57.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yegor Bugayenko
|
|
@@ -298,6 +298,7 @@ files:
|
|
|
298
298
|
- lib/judges/judge.rb
|
|
299
299
|
- lib/judges/judges.rb
|
|
300
300
|
- lib/judges/options.rb
|
|
301
|
+
- lib/judges/statistics.rb
|
|
301
302
|
- lib/judges/to_rel.rb
|
|
302
303
|
- package-lock.json
|
|
303
304
|
- package.json
|