xmi 0.6.1 → 0.6.2

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.
@@ -9,7 +9,7 @@ module Xmi
9
9
  # interface_realization — but signals a different deployment role
10
10
  # to consumers.
11
11
  #
12
- # Phase A of TODO.next/01: subclass is a type tag on
12
+ # Phase A: subclass is a type tag on
13
13
  # PackagedElement. The union-bag attribute set is inherited
14
14
  # unchanged so existing consumers keep working.
15
15
  class Component < PackagedElement
@@ -5,7 +5,7 @@ module Xmi
5
5
  # UML `<packagedElement xmi:type="uml:Dependency">`. A relationship
6
6
  # between a client and supplier packageable element.
7
7
  #
8
- # Phase A of TODO.next/01: subclass is a type tag on
8
+ # Phase A: subclass is a type tag on
9
9
  # PackagedElement. The union-bag attribute set is inherited
10
10
  # unchanged so existing consumers keep working. Phase B
11
11
  # (narrowing attrs to the subclass) is future work.
@@ -6,7 +6,7 @@ module Xmi
6
6
  # associates a stereotype with a metaclass to make the stereotype
7
7
  # applicable to instances of that metaclass (UML 2.5 §22.3).
8
8
  #
9
- # Phase A of TODO.next/01: subclass is a type tag on
9
+ # Phase A: subclass is a type tag on
10
10
  # PackagedElement. The union-bag attribute set is inherited
11
11
  # unchanged so existing consumers keep working. Phase B
12
12
  # (narrowing attrs to the subclass) is future work.
@@ -7,7 +7,7 @@ module Xmi
7
7
  attribute :body_element, :string
8
8
  attribute :body_attribute, :string
9
9
  attribute :annotated_attribute, :string
10
- attribute :annotated_element, AnnotatedElement
10
+ attribute :annotated_element, AnnotatedElement, collection: true
11
11
 
12
12
  xml do
13
13
  root "ownedComment"
@@ -15,7 +15,8 @@ module Xmi
15
15
  map_attribute "body", to: :body_attribute
16
16
  map_attribute "annotatedElement", to: :annotated_attribute
17
17
 
18
- map_element "annotatedElement", to: :annotated_element
18
+ map_element "annotatedElement", to: :annotated_element,
19
+ value_map: VALUE_MAP
19
20
  map_element "body", to: :body_element
20
21
  end
21
22
  end
@@ -12,7 +12,7 @@ module Xmi
12
12
  attribute :bounds, Bounds, collection: true
13
13
  attribute :source, :string
14
14
  attribute :target, :string
15
- attribute :waypoint, Waypoint
15
+ attribute :waypoint, Waypoint, collection: true
16
16
 
17
17
  xml do
18
18
  root "ownedElement"
@@ -23,7 +23,7 @@ module Xmi
23
23
  map_element "bounds", to: :bounds, value_map: VALUE_MAP
24
24
  map_element "source", to: :source
25
25
  map_element "target", to: :target
26
- map_element "waypoint", to: :waypoint
26
+ map_element "waypoint", to: :waypoint, value_map: VALUE_MAP
27
27
  end
28
28
  end
29
29
  end
@@ -7,17 +7,23 @@ module Xmi
7
7
  # packaged_element recursion on PackagedElement itself, and by
8
8
  # UmlModel.packaged_element.
9
9
  #
10
- # Phase A of TODO.next/01: subclasses are type tags on
11
- # PackagedElement (no attrs narrowed). Phase B will narrow each
12
- # subclass's attrs to its UML-2.5-conformant subset.
13
- #
14
- # Fallback contract: unknown or missing `xmi:type` resolves to
10
+ # Fallback contract: an unknown `xmi:type` resolves to
15
11
  # `Xmi::Uml::PackagedElement` (the union-bag base) via the
16
- # class_map's default_proc. This avoids the lutaml-model
12
+ # class_map's Hash default value. This avoids the lutaml-model
17
13
  # `Object.const_get(nil)` TypeError when a Sparx XMI emits a
18
14
  # type we haven't modelled yet, at the cost of treating the
19
- # element as generic. polymorphic_robustness_spec locks in the
20
- # graceful-fallback behavior.
15
+ # element as generic. polymorphic_map_contract_spec and
16
+ # polymorphic_robustness_spec lock in the graceful-fallback
17
+ # behavior.
18
+ #
19
+ # Two paths land at the base:
20
+ # 1. Missing discriminator — lutaml-model's
21
+ # `polymorphic_map_defined?` (lib/lutaml/model/attribute.rb)
22
+ # short-circuits and returns the declared attribute type
23
+ # (`PackagedElement`). The class_map default is not consulted.
24
+ # 2. Unknown discriminator — `polymorphic_map_defined?` is true,
25
+ # the class_map lookup hits a missing key, and the Hash default
26
+ # value supplies the fallback class name.
21
27
  PACKAGED_ELEMENT_POLYMORPHIC_MAP = {
22
28
  attribute: "xmi:type",
23
29
  class_map: Hash.new("Xmi::Uml::PackagedElement").merge!(
@@ -37,7 +43,7 @@ module Xmi
37
43
  "uml:Stereotype" => "Xmi::Uml::Stereotype",
38
44
  "uml:Usage" => "Xmi::Uml::Usage",
39
45
  "uml:Component" => "Xmi::Uml::Component",
40
- ),
46
+ ).freeze,
41
47
  }.freeze
42
48
 
43
49
  class PackagedElement < Base
@@ -6,7 +6,7 @@ module Xmi
6
6
  # classifier that communicates the sending of a message instance
7
7
  # (UML 2.5 §17.4).
8
8
  #
9
- # Phase A of TODO.next/01: subclass is a type tag on
9
+ # Phase A: subclass is a type tag on
10
10
  # PackagedElement. The union-bag attribute set is inherited
11
11
  # unchanged so existing consumers keep working. Phase B
12
12
  # (narrowing attrs to the subclass) is future work.
@@ -6,7 +6,7 @@ module Xmi
6
6
  # is a kind of class defined to extend the UML metamodel
7
7
  # (UML 2.5 §22.3).
8
8
  #
9
- # Phase A of TODO.next/01: subclass is a type tag on
9
+ # Phase A: subclass is a type tag on
10
10
  # PackagedElement. The union-bag attribute set is inherited
11
11
  # unchanged so existing consumers keep working. Phase B
12
12
  # (narrowing attrs to the subclass) is future work.
@@ -5,7 +5,7 @@ module Xmi
5
5
  # UML `<packagedElement xmi:type="uml:Class">`. Classifier with
6
6
  # attributes, operations, and structural relationships.
7
7
  #
8
- # Phase A of TODO.next/01: subclass is a type tag on
8
+ # Phase A: subclass is a type tag on
9
9
  # PackagedElement. The union-bag attribute set is inherited
10
10
  # unchanged so existing consumers keep working. Phase B
11
11
  # (narrowing attrs to the subclass) is future work.
data/lib/xmi/uml/usage.rb CHANGED
@@ -6,7 +6,7 @@ module Xmi
6
6
  # subtype indicating that a client requires a supplier for its
7
7
  # proper functioning (UML 2.5 §19.3).
8
8
  #
9
- # Phase A of TODO.next/01: subclass is a type tag on
9
+ # Phase A: subclass is a type tag on
10
10
  # PackagedElement. The union-bag attribute set is inherited
11
11
  # unchanged so existing consumers keep working. Phase B
12
12
  # (narrowing attrs to the subclass) is future work.
@@ -32,10 +32,16 @@ module Xmi
32
32
  # The literal class names are string form because lutaml-model's
33
33
  # resolve_polymorphic_class calls Object.const_get on the value.
34
34
  #
35
- # Fallback contract: unknown or missing `xmi:type` resolves to
35
+ # Fallback contract: an unknown `xmi:type` resolves to
36
36
  # `Xmi::Uml::ValueSpecification` (the abstract base) via the
37
- # class_map's default_proc. polymorphic_robustness_spec locks in
38
- # the graceful-fallback behavior.
37
+ # class_map's Hash default value. polymorphic_map_contract_spec
38
+ # and polymorphic_robustness_spec lock in the graceful-fallback
39
+ # behavior.
40
+ #
41
+ # Two paths land at the base (see PACKAGED_ELEMENT_POLYMORPHIC_MAP
42
+ # for the full rationale): missing discriminator short-circuits via
43
+ # lutaml-model's `polymorphic_map_defined?`; unknown discriminator
44
+ # hits the Hash default value.
39
45
  VALUE_SPECIFICATION_POLYMORPHIC_MAP = {
40
46
  attribute: "xmi:type",
41
47
  class_map: Hash.new("Xmi::Uml::ValueSpecification").merge!(
@@ -45,7 +51,7 @@ module Xmi
45
51
  "uml:LiteralBoolean" => "Xmi::Uml::LiteralBoolean",
46
52
  "uml:LiteralUnlimitedNatural" => "Xmi::Uml::LiteralUnlimitedNatural",
47
53
  "uml:LiteralNull" => "Xmi::Uml::LiteralNull",
48
- ),
54
+ ).freeze,
49
55
  }.freeze
50
56
  end
51
57
  end
data/lib/xmi/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Xmi
4
- VERSION = "0.6.1"
4
+ VERSION = "0.6.2"
5
5
  end
data/lib/xmi.rb CHANGED
@@ -71,6 +71,7 @@ module Xmi
71
71
  autoload :CustomProfile, "xmi/custom_profile"
72
72
  autoload :Root, "xmi/root"
73
73
  autoload :Sparx, "xmi/sparx"
74
+ autoload :Performance, "xmi/performance"
74
75
 
75
76
  # Unified parsing API
76
77
  autoload :Parsing, "xmi/parsing"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xmi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-09 00:00:00.000000000 Z
11
+ date: 2026-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -61,10 +61,7 @@ files:
61
61
  - bin/setup
62
62
  - docs/migration.md
63
63
  - docs/versioning.md
64
- - lib/tasks/benchmark_runner.rb
65
64
  - lib/tasks/performance.rake
66
- - lib/tasks/performance_comparator.rb
67
- - lib/tasks/performance_helpers.rb
68
65
  - lib/xmi.rb
69
66
  - lib/xmi/add.rb
70
67
  - lib/xmi/custom_profile.rb
@@ -97,6 +94,10 @@ files:
97
94
  - lib/xmi/namespace_registry.rb
98
95
  - lib/xmi/parser_pipeline.rb
99
96
  - lib/xmi/parsing.rb
97
+ - lib/xmi/performance.rb
98
+ - lib/xmi/performance/comparator.rb
99
+ - lib/xmi/performance/helpers.rb
100
+ - lib/xmi/performance/runner.rb
100
101
  - lib/xmi/replace.rb
101
102
  - lib/xmi/root.rb
102
103
  - lib/xmi/sparx.rb
@@ -1,274 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "benchmark/ips"
4
-
5
- # Ensure lib/ is on the load path regardless of tmp location
6
- lib_path = File.expand_path(File.join(__dir__, "..", "..", "lib"))
7
- $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
8
-
9
- require "xmi"
10
-
11
- # Pretty terminal formatting for benchmark output
12
- module Term
13
- CLEAR = "\e[0m"
14
- BOLD = "\e[1m"
15
- DIM = "\e[2m"
16
- RED = "\e[31m"
17
- GREEN = "\e[32m"
18
- YELLOW = "\e[33m"
19
- CYAN = "\e[36m"
20
- MAGENTA = "\e[35m"
21
-
22
- HL = "─"
23
- VL = "│"
24
- TL = "┌"
25
- TR = "┐"
26
- BL = "└"
27
- BR = "┘"
28
-
29
- def self.header(title, color: CYAN)
30
- width = 78
31
- line = HL * width
32
- puts
33
- puts "#{color}#{TL}#{line}#{TR}#{CLEAR}"
34
- puts "#{color}#{VL}#{CLEAR} #{BOLD}#{color}#{title}#{CLEAR}#{' ' * (width - title.length - 4)}#{color}#{VL}#{CLEAR}"
35
- puts "#{color}#{BL}#{line}#{BR}#{CLEAR}"
36
- end
37
-
38
- def self.sep(char: HL, width: 78)
39
- puts "#{DIM}#{char * width}#{CLEAR}"
40
- end
41
-
42
- def self.env_info(ruby_version, platform)
43
- puts
44
- puts " #{DIM}Environment:#{CLEAR}"
45
- puts " #{VL} Ruby #{ruby_version} on #{platform}#{' ' * (60 - ruby_version.length - platform.length)}#{VL}"
46
- puts " #{DIM}#{BL}#{HL * 76}#{BR}#{CLEAR}"
47
- puts
48
- end
49
-
50
- def self.category(title, icon:, description:, failure_means:,
51
- compare_against: nil)
52
- puts
53
- puts "#{CYAN}#{VL}#{CLEAR} #{BOLD}#{MAGENTA}#{icon} #{title}#{CLEAR}"
54
- puts
55
- puts " #{DIM}#{description}#{CLEAR}"
56
- puts
57
-
58
- if compare_against
59
- puts " #{CYAN}Comparing against:#{CLEAR} #{compare_against}"
60
- puts
61
- end
62
-
63
- puts " #{YELLOW}⚠️ Failure means:#{CLEAR} #{failure_means}"
64
- puts
65
- sep(width: 76)
66
- puts
67
- end
68
- end
69
-
70
- class BenchmarkRunner
71
- REPO_ROOT = File.expand_path(File.join(__dir__, "..", ".."))
72
-
73
- # Benchmark configuration
74
- DEFAULT_RUN_TIME = 5
75
- DEFAULT_WARMUP = 2
76
-
77
- # Category definitions with descriptions
78
- CATEGORIES = {
79
- xmi_parsing: {
80
- name: "XMI Parsing",
81
- icon: "📄",
82
- description: "XMI parsing performance tests. Measures how quickly we can convert XMI files into Ruby objects.",
83
- failure_means: "Slow XMI parsing impacts all downstream operations. A regression here means users will experience delays when processing XMI documents.",
84
- compare_against: "Previous branch (main).",
85
- },
86
- }.freeze
87
-
88
- # Test definitions
89
- BENCHMARKS = {
90
- xmi_parsing: [
91
- { name: "XMI 2.4.2 (small)", method: :xmi_parse_242_small,
92
- desc: "XMI 2.4.2 ~100KB file" },
93
- { name: "XMI 2.4.2 (medium)", method: :xmi_parse_242_medium,
94
- desc: "XMI 2.4.2 ~500KB file with extensions" },
95
- { name: "XMI 2.4.2 (large)", method: :xmi_parse_242_large,
96
- desc: "XMI 2.4.2 ~3.5MB file" },
97
- { name: "XMI 2.5.1", method: :xmi_parse_251,
98
- desc: "XMI 2.5.1 ~100KB file" },
99
- ],
100
- }.freeze
101
-
102
- # Test data - fixture paths
103
- FIXTURES = {
104
- xmi_parse_242_small: "spec/fixtures/xmi-v2-4-2-default.xmi",
105
- xmi_parse_242_medium: "spec/fixtures/xmi-v2-4-2-default-with-citygml.xmi",
106
- xmi_parse_242_large: "spec/fixtures/full-242.xmi",
107
- xmi_parse_251: "spec/fixtures/ea-xmi-2.5.1.xmi",
108
- }.freeze
109
-
110
- def initialize(run_time: nil, warmup: nil, benchmark: nil)
111
- @run_time = run_time || DEFAULT_RUN_TIME
112
- @warmup = warmup || DEFAULT_WARMUP
113
- @benchmark = benchmark
114
- @results = {}
115
- @env_shown = false
116
- @all_results = []
117
- end
118
-
119
- def run_benchmarks
120
- Term.header("XMI Performance Benchmarks", color: Term::CYAN)
121
-
122
- unless @env_shown
123
- Term.env_info(RUBY_VERSION, RUBY_PLATFORM)
124
- @env_shown = true
125
- end
126
-
127
- BENCHMARKS.each do |category, tests|
128
- run_category(category, tests)
129
- end
130
-
131
- print_summary
132
-
133
- @results
134
- end
135
-
136
- private
137
-
138
- def run_category(category, tests)
139
- config = CATEGORIES[category]
140
- Term.category(
141
- config[:name],
142
- icon: config[:icon],
143
- description: config[:description],
144
- failure_means: config[:failure_means],
145
- compare_against: config[:compare_against],
146
- )
147
-
148
- category_results = []
149
-
150
- tests.each do |test|
151
- # Redirect stdout during benchmark
152
- original_stdout = $stdout
153
- $stdout = StringIO.new
154
-
155
- result = run_single_test(test[:method])
156
- (result[:lower] + result[:upper]) / 2.0
157
- category_results << { name: test[:name], result: result }
158
-
159
- # Restore stdout
160
- $stdout = original_stdout
161
- end
162
-
163
- # Print results
164
- puts " #{'Benchmark'.ljust(40)} #{'IPS'.rjust(12)} #{'Deviation'.rjust(12)}"
165
- puts " #{Term::DIM}#{Term::HL * 66}#{Term::CLEAR}"
166
-
167
- category_results.each do |r|
168
- ips = (r[:result][:lower] + r[:result][:upper]) / 2.0
169
- deviation = calculate_deviation(r[:result])
170
- label = "#{config[:name]}: #{r[:name]}"
171
- @all_results << { label: label, ips: ips }
172
- @results[label] = r[:result]
173
-
174
- puts " #{r[:name].ljust(40)} #{format('%.2f',
175
- ips).rjust(12)} #{format('%.1f%%',
176
- deviation).rjust(12)}"
177
- end
178
-
179
- puts
180
- end
181
-
182
- def run_single_test(method)
183
- fixture_path = FIXTURES[method]
184
- raise "Unknown fixture: #{method}" unless fixture_path
185
-
186
- # Try to resolve fixture path relative to REPO_ROOT
187
- full_path = File.join(REPO_ROOT, fixture_path)
188
- unless File.exist?(full_path)
189
- # Fallback: try current directory
190
- full_path = fixture_path
191
- end
192
-
193
- xml_content = File.read(full_path)
194
-
195
- case method
196
- when :xmi_parse_242_small, :xmi_parse_242_medium, :xmi_parse_242_large, :xmi_parse_251
197
- measure_time { Xmi::Sparx::Root.parse_xml(xml_content) }
198
- else
199
- raise "Unknown benchmark: #{method}"
200
- end
201
- end
202
-
203
- def measure(&)
204
- job = Benchmark::IPS::Job.new
205
- job.config(time: @run_time, warmup: @warmup)
206
- job.report("test", &)
207
- job.run
208
-
209
- entry = job.full_report.entries.first
210
- samples = entry.stats.samples
211
-
212
- return { lower: 0, upper: 0 } if samples.empty?
213
-
214
- mean = samples.sum.to_f / samples.size
215
- variance = samples.sum { |x| (x - mean)**2 } / (samples.size - 1)
216
- std_dev = Math.sqrt(variance)
217
- error_margin = std_dev / mean
218
- error_pct = error_margin.round(4)
219
-
220
- { lower: mean.round(4) * (1 - error_pct),
221
- upper: mean.round(4) * (1 + error_pct) }
222
- end
223
-
224
- def measure_time
225
- times = []
226
- iterations = 5
227
-
228
- iterations.times do
229
- start_t = Process.clock_gettime(Process::CLOCK_MONOTONIC)
230
- yield
231
- finish_t = Process.clock_gettime(Process::CLOCK_MONOTONIC)
232
- times << (finish_t - start_t)
233
- end
234
-
235
- mean = times.sum / times.size
236
- variance = times.sum { |t| (t - mean)**2 } / (times.size - 1)
237
- std_dev = Math.sqrt(variance)
238
-
239
- # Use conservative estimates for time-based measurement
240
- lower_time = [mean - std_dev, mean * 0.5].max
241
- lower_ips = (1.0 / (lower_time * 1.5)).round(4)
242
- upper_ips = (1.0 / mean).round(4)
243
-
244
- # For fast operations, estimate more conservatively
245
- if mean < 0.001
246
- upper_ips = (1.0 / mean).round(4)
247
- lower_ips = (upper_ips * 0.8).round(4)
248
- end
249
-
250
- { lower: lower_ips, upper: upper_ips }
251
- end
252
-
253
- def calculate_deviation(metrics)
254
- return 0 if metrics[:upper].zero?
255
-
256
- ((metrics[:upper] - metrics[:lower]) / metrics[:upper] * 100).round(1)
257
- end
258
-
259
- def print_summary
260
- puts
261
- Term.sep(width: 78)
262
- puts
263
- puts " #{Term::BOLD}#{Term::MAGENTA}SUMMARY#{Term::CLEAR}"
264
- puts
265
-
266
- @all_results.each do |r|
267
- puts " #{r[:label].ljust(60)} #{format('%.2f', r[:ips]).rjust(10)} IPS"
268
- end
269
-
270
- puts
271
- puts " #{Term::DIM}#{@all_results.length} benchmarks completed#{Term::CLEAR}"
272
- puts
273
- end
274
- end
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "performance_helpers"
4
-
5
- class PerformanceComparator
6
- REPO_ROOT = File.expand_path(File.join(__dir__, "..", ".."))
7
- DEFAULT_RUN_TIME = 10
8
- DEFAULT_THRESHOLD = 0.10 # 10% (more lenient for complex operations)
9
- DEFAULT_BASE = "main"
10
- TMP_PERF_DIR = File.join(REPO_ROOT, "tmp", "performance")
11
- BENCH_SCRIPT = File.join(TMP_PERF_DIR, "benchmark_runner.rb")
12
-
13
- def run
14
- setup_environment
15
- run_benchmarks_comparison
16
- ensure
17
- cleanup
18
- end
19
-
20
- private
21
-
22
- def setup_environment
23
- Dir.chdir(REPO_ROOT)
24
- FileUtils.mkdir_p(TMP_PERF_DIR)
25
- FileUtils.cp(File.join(REPO_ROOT, "lib", "tasks", "benchmark_runner.rb"),
26
- BENCH_SCRIPT)
27
-
28
- PerformanceHelpers.load_into_namespace(PerformanceHelpers::Current,
29
- BENCH_SCRIPT)
30
- PerformanceHelpers.clone_base_repo(DEFAULT_BASE, TMP_PERF_DIR, BENCH_SCRIPT)
31
- end
32
-
33
- def run_benchmarks_comparison
34
- all_current = {}
35
- all_base = {}
36
-
37
- puts PerformanceHelpers::Term.header("Performance Comparison", color: PerformanceHelpers::CYAN)
38
- puts
39
- puts " #{PerformanceHelpers::DIM}Comparing#{PerformanceHelpers::CLEAR}:"
40
- puts " #{PerformanceHelpers::CYAN} Current#{PerformanceHelpers::CLEAR}: #{PerformanceHelpers.current_branch}"
41
- puts " #{PerformanceHelpers::CYAN} Base#{PerformanceHelpers::CLEAR}: #{DEFAULT_BASE}"
42
- puts " #{PerformanceHelpers::CYAN} Threshold#{PerformanceHelpers::CLEAR}: #{(DEFAULT_THRESHOLD * 100).round(0)}% regression allowed"
43
- puts
44
-
45
- # Run all benchmarks
46
- base_runner = PerformanceHelpers::Base::BenchmarkRunner.new(
47
- run_time: DEFAULT_RUN_TIME,
48
- )
49
- current_runner = PerformanceHelpers::Current::BenchmarkRunner.new(
50
- run_time: DEFAULT_RUN_TIME,
51
- )
52
-
53
- PerformanceHelpers.run_benchmarks(
54
- base_runner,
55
- current_runner,
56
- DEFAULT_THRESHOLD,
57
- all_base,
58
- all_current,
59
- )
60
-
61
- summary = PerformanceHelpers.summary_report(
62
- all_current,
63
- all_base,
64
- DEFAULT_BASE,
65
- DEFAULT_RUN_TIME,
66
- DEFAULT_THRESHOLD,
67
- )
68
-
69
- handle_results(summary)
70
- end
71
-
72
- def handle_results(summary)
73
- puts
74
- if summary[:regressions].any?
75
- puts " #{PerformanceHelpers::RED}#{PerformanceHelpers::BOLD}❌ PERFORMANCE REGRESSIONS DETECTED#{PerformanceHelpers::CLEAR}"
76
- puts " #{PerformanceHelpers::RED}#{summary[:regressions].length} benchmark(s) regressed beyond threshold#{PerformanceHelpers::CLEAR}"
77
- puts
78
- exit(1)
79
- else
80
- puts " #{PerformanceHelpers::GREEN}#{PerformanceHelpers::BOLD}✅ ALL BENCHMARKS PASSED#{PerformanceHelpers::CLEAR}"
81
- puts
82
- end
83
- end
84
-
85
- def cleanup
86
- FileUtils.rm_rf(TMP_PERF_DIR)
87
- end
88
- end