canon 0.2.11 → 0.2.12
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/.rubocop_todo.yml +12 -22
- data/Rakefile +5 -2
- data/lib/canon/cache.rb +3 -1
- data/lib/canon/cli.rb +0 -3
- data/lib/canon/commands/diff_command.rb +0 -6
- data/lib/canon/commands/format_command.rb +0 -4
- data/lib/canon/commands.rb +9 -0
- data/lib/canon/comparison/child_realignment.rb +0 -2
- data/lib/canon/comparison/compare_profile.rb +30 -36
- data/lib/canon/comparison/comparison_result.rb +0 -2
- data/lib/canon/comparison/diff_node_builder.rb +353 -0
- data/lib/canon/comparison/dimensions/dimension.rb +51 -0
- data/lib/canon/comparison/dimensions/dimension_set.rb +49 -0
- data/lib/canon/comparison/dimensions/registry.rb +101 -60
- data/lib/canon/comparison/dimensions.rb +15 -46
- data/lib/canon/comparison/html_comparator.rb +18 -141
- data/lib/canon/comparison/html_compare_profile.rb +15 -18
- data/lib/canon/comparison/json_comparator.rb +4 -165
- data/lib/canon/comparison/json_parser.rb +0 -2
- data/lib/canon/comparison/markup_comparator.rb +14 -210
- data/lib/canon/comparison/match_options/base_resolver.rb +18 -29
- data/lib/canon/comparison/match_options/json_resolver.rb +4 -28
- data/lib/canon/comparison/match_options/xml_resolver.rb +4 -45
- data/lib/canon/comparison/match_options/yaml_resolver.rb +4 -30
- data/lib/canon/comparison/match_options.rb +13 -88
- data/lib/canon/comparison/pipeline.rb +269 -0
- data/lib/canon/comparison/profile_definition.rb +0 -2
- data/lib/canon/comparison/ruby_object_comparator.rb +1 -1
- data/lib/canon/comparison/strategies/match_strategy_factory.rb +9 -58
- data/lib/canon/comparison/strategies/semantic_tree_match_strategy.rb +4 -11
- data/lib/canon/comparison/strategies.rb +16 -0
- data/lib/canon/comparison/xml_comparator/attribute_comparator.rb +0 -3
- data/lib/canon/comparison/xml_comparator/attribute_filter.rb +0 -3
- data/lib/canon/comparison/xml_comparator/child_comparison.rb +0 -6
- data/lib/canon/comparison/xml_comparator/namespace_comparator.rb +1 -6
- data/lib/canon/comparison/xml_comparator/node_parser.rb +0 -4
- data/lib/canon/comparison/xml_comparator.rb +4 -492
- data/lib/canon/comparison/xml_comparator_helpers.rb +21 -0
- data/lib/canon/comparison/xml_node_comparison.rb +4 -119
- data/lib/canon/comparison/yaml_comparator.rb +0 -3
- data/lib/canon/comparison.rb +143 -266
- data/lib/canon/config/config_dsl.rb +159 -0
- data/lib/canon/config/env_provider.rb +0 -3
- data/lib/canon/config/env_schema.rb +48 -58
- data/lib/canon/config/profile_loader.rb +0 -1
- data/lib/canon/config.rb +116 -468
- data/lib/canon/diff/diff_block_builder.rb +0 -2
- data/lib/canon/diff/diff_classifier.rb +0 -5
- data/lib/canon/diff/diff_context.rb +0 -2
- data/lib/canon/diff/diff_context_builder.rb +0 -2
- data/lib/canon/diff/diff_line_builder.rb +0 -3
- data/lib/canon/diff/diff_node_enricher.rb +0 -4
- data/lib/canon/diff/diff_node_mapper.rb +0 -4
- data/lib/canon/diff/diff_report_builder.rb +0 -4
- data/lib/canon/diff/formatting_detector.rb +0 -1
- data/lib/canon/diff/node_serializer.rb +0 -7
- data/lib/canon/diff.rb +39 -0
- data/lib/canon/diff_formatter/by_line/base_formatter.rb +4 -17
- data/lib/canon/diff_formatter/by_line/html_formatter.rb +7 -19
- data/lib/canon/diff_formatter/by_line/json_formatter.rb +0 -3
- data/lib/canon/diff_formatter/by_line/simple_formatter.rb +0 -3
- data/lib/canon/diff_formatter/by_line/xml_formatter.rb +7 -26
- data/lib/canon/diff_formatter/by_line/yaml_formatter.rb +0 -3
- data/lib/canon/diff_formatter/by_object/base_formatter.rb +8 -15
- data/lib/canon/diff_formatter/by_object/json_formatter.rb +0 -2
- data/lib/canon/diff_formatter/by_object/xml_formatter.rb +0 -2
- data/lib/canon/diff_formatter/by_object/yaml_formatter.rb +0 -2
- data/lib/canon/diff_formatter/debug_output.rb +0 -2
- data/lib/canon/diff_formatter/diff_detail_formatter/dimension_formatter.rb +24 -58
- data/lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb +0 -2
- data/lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb +1 -2
- data/lib/canon/diff_formatter/diff_detail_formatter/text_utils.rb +1 -7
- data/lib/canon/diff_formatter/diff_detail_formatter.rb +0 -7
- data/lib/canon/diff_formatter/diff_detail_formatter_helpers.rb +23 -0
- data/lib/canon/diff_formatter.rb +11 -9
- data/lib/canon/formatters/html4_formatter.rb +0 -2
- data/lib/canon/formatters/html5_formatter.rb +0 -2
- data/lib/canon/formatters/html_formatter.rb +0 -3
- data/lib/canon/formatters/json_formatter.rb +0 -1
- data/lib/canon/formatters/xml_formatter.rb +0 -4
- data/lib/canon/formatters/yaml_formatter.rb +0 -1
- data/lib/canon/formatters.rb +16 -0
- data/lib/canon/html/data_model.rb +0 -10
- data/lib/canon/html.rb +4 -3
- data/lib/canon/options/cli_generator.rb +0 -2
- data/lib/canon/options/registry.rb +0 -2
- data/lib/canon/options.rb +9 -0
- data/lib/canon/pretty_printer/html.rb +0 -1
- data/lib/canon/pretty_printer/xml_normalized.rb +0 -2
- data/lib/canon/pretty_printer.rb +12 -0
- data/lib/canon/tree_diff/adapters/html_adapter.rb +1 -1
- data/lib/canon/tree_diff/adapters.rb +14 -0
- data/lib/canon/tree_diff/core/attribute_comparator.rb +0 -6
- data/lib/canon/tree_diff/core/node_signature.rb +1 -1
- data/lib/canon/tree_diff/core/tree_node.rb +12 -5
- data/lib/canon/tree_diff/core.rb +17 -0
- data/lib/canon/tree_diff/matchers/hash_matcher.rb +0 -7
- data/lib/canon/tree_diff/matchers/similarity_matcher.rb +1 -5
- data/lib/canon/tree_diff/matchers/structural_propagator.rb +1 -5
- data/lib/canon/tree_diff/matchers.rb +15 -0
- data/lib/canon/tree_diff/operation_converter.rb +0 -8
- data/lib/canon/tree_diff/operation_converter_helpers/metadata_enricher.rb +2 -12
- data/lib/canon/tree_diff/operation_converter_helpers/post_processor.rb +13 -7
- data/lib/canon/tree_diff/operation_converter_helpers/reason_builder.rb +2 -2
- data/lib/canon/tree_diff/operation_converter_helpers/update_change_handler.rb +4 -6
- data/lib/canon/tree_diff/operation_converter_helpers.rb +18 -0
- data/lib/canon/tree_diff/operations/operation_detector.rb +2 -5
- data/lib/canon/tree_diff/operations.rb +13 -0
- data/lib/canon/tree_diff.rb +26 -27
- data/lib/canon/validators/base_validator.rb +0 -2
- data/lib/canon/validators/html_validator.rb +0 -1
- data/lib/canon/validators/json_validator.rb +0 -1
- data/lib/canon/validators/xml_validator.rb +0 -1
- data/lib/canon/validators/yaml_validator.rb +0 -1
- data/lib/canon/validators.rb +12 -0
- data/lib/canon/version.rb +1 -1
- data/lib/canon/xml/c14n.rb +0 -4
- data/lib/canon/xml/data_model.rb +0 -10
- data/lib/canon/xml/line_range_mapper.rb +0 -2
- data/lib/canon/xml/nodes/attribute_node.rb +0 -2
- data/lib/canon/xml/nodes/comment_node.rb +0 -2
- data/lib/canon/xml/nodes/element_node.rb +0 -2
- data/lib/canon/xml/nodes/namespace_node.rb +0 -2
- data/lib/canon/xml/nodes/processing_instruction_node.rb +0 -2
- data/lib/canon/xml/nodes/root_node.rb +0 -2
- data/lib/canon/xml/nodes/text_node.rb +0 -2
- data/lib/canon/xml/nodes.rb +19 -0
- data/lib/canon/xml/processor.rb +0 -5
- data/lib/canon/xml/sax_builder.rb +0 -7
- data/lib/canon/xml.rb +33 -0
- data/lib/canon/xml_backend.rb +50 -14
- data/lib/canon/xml_parsing.rb +4 -2
- data/lib/canon.rb +25 -15
- data/lib/tasks/performance.rake +0 -58
- data/lib/tasks/performance_comparator.rb +132 -65
- data/lib/tasks/performance_helpers.rb +4 -249
- data/lib/tasks/performance_report.rb +309 -0
- metadata +24 -11
- data/lib/canon/comparison/dimensions/attribute_order_dimension.rb +0 -64
- data/lib/canon/comparison/dimensions/attribute_presence_dimension.rb +0 -64
- data/lib/canon/comparison/dimensions/attribute_values_dimension.rb +0 -167
- data/lib/canon/comparison/dimensions/base_dimension.rb +0 -107
- data/lib/canon/comparison/dimensions/comments_dimension.rb +0 -117
- data/lib/canon/comparison/dimensions/element_position_dimension.rb +0 -86
- data/lib/canon/comparison/dimensions/structural_whitespace_dimension.rb +0 -115
- data/lib/canon/comparison/dimensions/text_content_dimension.rb +0 -102
- data/lib/canon/comparison/xml_comparator/diff_node_builder.rb +0 -300
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "benchmark/ips"
|
|
4
|
+
require "json"
|
|
5
|
+
require "securerandom"
|
|
6
|
+
require "stringio"
|
|
7
|
+
require "time"
|
|
8
|
+
require "yaml"
|
|
9
|
+
|
|
10
|
+
lib_path = File.expand_path(File.join(__dir__, "..", "..", "lib"))
|
|
11
|
+
$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
|
|
12
|
+
|
|
13
|
+
require "canon"
|
|
14
|
+
|
|
15
|
+
begin
|
|
16
|
+
require "canon/xml/sax_builder"
|
|
17
|
+
SAX_AVAILABLE = true
|
|
18
|
+
rescue LoadError
|
|
19
|
+
SAX_AVAILABLE = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Standalone benchmark runner that emits JSON results on stdout.
|
|
23
|
+
#
|
|
24
|
+
# Used by performance_comparator.rb to run benchmarks in a clean Ruby
|
|
25
|
+
# process per branch — sharing no state with the comparator itself.
|
|
26
|
+
# This isolates each branch's Canon implementation from the other.
|
|
27
|
+
module Performance
|
|
28
|
+
REPORT_ID = SecureRandom.hex(4).freeze
|
|
29
|
+
|
|
30
|
+
DEFAULT_RUN_TIME = Integer(ENV.fetch("CANON_PERF_RUN_TIME", "5"))
|
|
31
|
+
DEFAULT_WARMUP = Integer(ENV.fetch("CANON_PERF_WARMUP", "2"))
|
|
32
|
+
DEFAULT_ITEMS = Integer(ENV.fetch("CANON_PERF_ITEMS", "50"))
|
|
33
|
+
|
|
34
|
+
BENCHMARKS = {
|
|
35
|
+
xml_parse_dom_simple: { category: "xml_parsing", name: "DOM (simple)" },
|
|
36
|
+
xml_parse_sax_simple: { category: "xml_parsing", name: "SAX (simple)" },
|
|
37
|
+
xml_parse_dom_large: { category: "xml_parsing", name: "DOM (large)" },
|
|
38
|
+
xml_parse_sax_large: { category: "xml_parsing", name: "SAX (large)" },
|
|
39
|
+
html_parse_simple: { category: "html_parsing", name: "Simple HTML" },
|
|
40
|
+
html_parse_complex: { category: "html_parsing", name: "Complex HTML" },
|
|
41
|
+
xml_compare_identical: { category: "xml_comparison", name: "Identical XML" },
|
|
42
|
+
xml_compare_similar: { category: "xml_comparison", name: "Similar XML" },
|
|
43
|
+
xml_compare_different: { category: "xml_comparison", name: "Different XML" },
|
|
44
|
+
html_compare_identical: { category: "html_comparison", name: "Identical HTML" },
|
|
45
|
+
html_compare_similar: { category: "html_comparison", name: "Similar HTML" },
|
|
46
|
+
html_compare_different: { category: "html_comparison", name: "Different HTML" },
|
|
47
|
+
xml_c14n_format: { category: "formatting", name: "XML C14N" },
|
|
48
|
+
json_format: { category: "formatting", name: "JSON" },
|
|
49
|
+
yaml_format: { category: "formatting", name: "YAML" },
|
|
50
|
+
}.freeze
|
|
51
|
+
|
|
52
|
+
module DataGenerator
|
|
53
|
+
DEFAULT_ITEMS = Performance::DEFAULT_ITEMS
|
|
54
|
+
|
|
55
|
+
class << self
|
|
56
|
+
def generate_xml(items: DEFAULT_ITEMS, depth: 1, with_namespaces: false,
|
|
57
|
+
with_attributes: true)
|
|
58
|
+
ns = with_namespaces ? 'xmlns:ns="http://example.org"' : ""
|
|
59
|
+
prefix = with_namespaces ? "ns:" : ""
|
|
60
|
+
|
|
61
|
+
build_xml_element(items, depth, prefix, with_attributes, ns)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def build_xml_element(items, depth, prefix, with_attrs, ns_decl)
|
|
65
|
+
attrs = with_attrs ? " id=\"#{rand(1000)}\" status=\"active\"" : ""
|
|
66
|
+
ns_attr = ns_decl.empty? ? "" : " #{ns_decl}"
|
|
67
|
+
|
|
68
|
+
if depth <= 1
|
|
69
|
+
children = Array.new(items) do |i|
|
|
70
|
+
inner_attrs = with_attrs ? " index=\"#{i}\"" : ""
|
|
71
|
+
"<#{prefix}item#{inner_attrs}>Item #{i} content with some text</#{prefix}item>"
|
|
72
|
+
end.join
|
|
73
|
+
"<#{prefix}root#{ns_attr}#{attrs}>#{children}</#{prefix}root>"
|
|
74
|
+
else
|
|
75
|
+
child = build_xml_element(items / 2, depth - 1, prefix, with_attrs,
|
|
76
|
+
"")
|
|
77
|
+
"<#{prefix}root#{ns_attr}#{attrs}>#{child}</#{prefix}root>"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def generate_html(items: DEFAULT_ITEMS, with_scripts: false,
|
|
82
|
+
with_tables: false)
|
|
83
|
+
scripts = if with_scripts
|
|
84
|
+
<<~HTML
|
|
85
|
+
<script type="text/javascript">
|
|
86
|
+
function test() { return true; }
|
|
87
|
+
</script>
|
|
88
|
+
<style>
|
|
89
|
+
body { margin: 0; }
|
|
90
|
+
</style>
|
|
91
|
+
HTML
|
|
92
|
+
else
|
|
93
|
+
""
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
tables = if with_tables
|
|
97
|
+
rows = Array.new(items) do |i|
|
|
98
|
+
"<tr><td>Cell #{i}A</td><td>Cell #{i}B</td></tr>"
|
|
99
|
+
end.join
|
|
100
|
+
"<table>#{rows}</table>"
|
|
101
|
+
else
|
|
102
|
+
""
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
list_items = Array.new(items) do |i|
|
|
106
|
+
"<li class=\"item-#{i}\">List item #{i} with bold text</li>"
|
|
107
|
+
end.join("\n ")
|
|
108
|
+
|
|
109
|
+
nav_count = [(items / 5), 2].max
|
|
110
|
+
nav_items = items.times.first(nav_count).map do |i|
|
|
111
|
+
"<li class=\"nav-#{i}\">Nav #{i}</li>"
|
|
112
|
+
end.join("\n ")
|
|
113
|
+
|
|
114
|
+
<<~HTML
|
|
115
|
+
<!DOCTYPE html>
|
|
116
|
+
<html lang="en">
|
|
117
|
+
<head>
|
|
118
|
+
<meta charset="UTF-8">
|
|
119
|
+
<title>Benchmark Document</title>
|
|
120
|
+
#{scripts}
|
|
121
|
+
</head>
|
|
122
|
+
<body>
|
|
123
|
+
<header>
|
|
124
|
+
<h1>Benchmark Test Document</h1>
|
|
125
|
+
<nav>
|
|
126
|
+
<ul>
|
|
127
|
+
#{nav_items}
|
|
128
|
+
</ul>
|
|
129
|
+
</nav>
|
|
130
|
+
</header>
|
|
131
|
+
<main>
|
|
132
|
+
<section id="content">
|
|
133
|
+
<p>This is a paragraph with emphasized and strong text.</p>
|
|
134
|
+
<ul>
|
|
135
|
+
#{list_items}
|
|
136
|
+
</ul>
|
|
137
|
+
#{tables}
|
|
138
|
+
</section>
|
|
139
|
+
</main>
|
|
140
|
+
<footer>
|
|
141
|
+
<p>Copyright 2024</p>
|
|
142
|
+
</footer>
|
|
143
|
+
</body>
|
|
144
|
+
</html>
|
|
145
|
+
HTML
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def generate_json(items: DEFAULT_ITEMS)
|
|
149
|
+
data = {
|
|
150
|
+
metadata: { version: "1.0", generated: Time.now.iso8601 },
|
|
151
|
+
items: Array.new(items) do |i|
|
|
152
|
+
{
|
|
153
|
+
id: i,
|
|
154
|
+
name: "Item #{i}",
|
|
155
|
+
value: rand * 1000,
|
|
156
|
+
tags: ["tag1", "tag2", "tag#{i % 10}"],
|
|
157
|
+
nested: {
|
|
158
|
+
level1: { level2: { data: "deeply nested value #{i}" } },
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
end,
|
|
162
|
+
}
|
|
163
|
+
JSON.generate(data)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def generate_yaml(items: DEFAULT_ITEMS)
|
|
167
|
+
JSON.parse(generate_json(items: items)).to_yaml
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def generate_large_xml(items: 500)
|
|
171
|
+
generate_xml(items: items, depth: 3, with_namespaces: true,
|
|
172
|
+
with_attributes: true)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
class Report
|
|
178
|
+
def initialize(run_time: DEFAULT_RUN_TIME, warmup: DEFAULT_WARMUP,
|
|
179
|
+
items: DEFAULT_ITEMS)
|
|
180
|
+
@run_time = run_time
|
|
181
|
+
@warmup = warmup
|
|
182
|
+
@items = items
|
|
183
|
+
@results = {}
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def run_all
|
|
187
|
+
BENCHMARKS.each do |method, meta|
|
|
188
|
+
next if method.to_s.start_with?("xml_parse_sax") && !SAX_AVAILABLE
|
|
189
|
+
|
|
190
|
+
label = "#{meta[:category]}: #{meta[:name]}"
|
|
191
|
+
@results[label] = run_benchmark(method)
|
|
192
|
+
end
|
|
193
|
+
@results
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def as_json
|
|
197
|
+
{
|
|
198
|
+
report_id: REPORT_ID,
|
|
199
|
+
ruby_version: RUBY_VERSION,
|
|
200
|
+
platform: RUBY_PLATFORM,
|
|
201
|
+
run_time: @run_time,
|
|
202
|
+
warmup: @warmup,
|
|
203
|
+
items: @items,
|
|
204
|
+
sax_available: SAX_AVAILABLE,
|
|
205
|
+
benchmarks: @results,
|
|
206
|
+
}
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private
|
|
210
|
+
|
|
211
|
+
def run_benchmark(method)
|
|
212
|
+
original_stdout = $stdout
|
|
213
|
+
$stdout = StringIO.new
|
|
214
|
+
begin
|
|
215
|
+
job = Benchmark::IPS::Job.new
|
|
216
|
+
job.config(time: @run_time, warmup: @warmup)
|
|
217
|
+
job.report("test") { dispatch(method) }
|
|
218
|
+
job.run
|
|
219
|
+
|
|
220
|
+
entry = job.full_report.entries.first
|
|
221
|
+
ensure
|
|
222
|
+
$stdout = original_stdout
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
samples = entry.stats.samples
|
|
226
|
+
return { lower: 0, upper: 0 } if samples.empty?
|
|
227
|
+
|
|
228
|
+
mean = samples.sum.to_f / samples.size
|
|
229
|
+
variance = samples.sum { |x| (x - mean)**2 } / (samples.size - 1)
|
|
230
|
+
std_dev = Math.sqrt(variance)
|
|
231
|
+
error_margin = std_dev / mean
|
|
232
|
+
error_pct = error_margin.round(4)
|
|
233
|
+
|
|
234
|
+
# Clamp lower bound to positive — for very fast operations the
|
|
235
|
+
# error margin can exceed the mean, producing nonsensical
|
|
236
|
+
# negative IPS values that break the regression comparison.
|
|
237
|
+
lower = (mean * (1 - error_pct)).round(4)
|
|
238
|
+
lower = (mean * 0.5).round(4) if lower <= 0
|
|
239
|
+
upper = (mean * (1 + error_pct)).round(4)
|
|
240
|
+
|
|
241
|
+
{ lower: lower, upper: upper }
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def dispatch(method)
|
|
245
|
+
case method
|
|
246
|
+
when :xml_parse_dom_simple
|
|
247
|
+
Canon::Xml::DataModel.from_xml(DataGenerator.generate_xml(items: @items))
|
|
248
|
+
when :xml_parse_sax_simple
|
|
249
|
+
Canon::Xml::SaxBuilder.parse(DataGenerator.generate_xml(items: @items))
|
|
250
|
+
when :xml_parse_dom_large
|
|
251
|
+
Canon::Xml::DataModel.from_xml(DataGenerator.generate_large_xml(items: @items * 5))
|
|
252
|
+
when :xml_parse_sax_large
|
|
253
|
+
Canon::Xml::SaxBuilder.parse(DataGenerator.generate_large_xml(items: @items * 5))
|
|
254
|
+
when :html_parse_simple
|
|
255
|
+
Canon.parse_html(DataGenerator.generate_html(items: @items))
|
|
256
|
+
when :html_parse_complex
|
|
257
|
+
Canon.parse_html(DataGenerator.generate_html(items: @items,
|
|
258
|
+
with_scripts: true,
|
|
259
|
+
with_tables: true))
|
|
260
|
+
when :xml_compare_identical
|
|
261
|
+
xml = DataGenerator.generate_xml(items: @items)
|
|
262
|
+
Canon::Comparison.equivalent?(xml, xml, format: :xml)
|
|
263
|
+
when :xml_compare_similar
|
|
264
|
+
Canon::Comparison.equivalent?(
|
|
265
|
+
DataGenerator.generate_xml(items: @items),
|
|
266
|
+
DataGenerator.generate_xml(items: @items),
|
|
267
|
+
format: :xml,
|
|
268
|
+
)
|
|
269
|
+
when :xml_compare_different
|
|
270
|
+
Canon::Comparison.equivalent?(
|
|
271
|
+
DataGenerator.generate_xml(items: @items),
|
|
272
|
+
DataGenerator.generate_xml(items: @items, with_namespaces: true),
|
|
273
|
+
format: :xml,
|
|
274
|
+
)
|
|
275
|
+
when :html_compare_identical
|
|
276
|
+
html = DataGenerator.generate_html(items: @items)
|
|
277
|
+
Canon::Comparison.equivalent?(html, html, format: :html)
|
|
278
|
+
when :html_compare_similar
|
|
279
|
+
Canon::Comparison.equivalent?(
|
|
280
|
+
DataGenerator.generate_html(items: @items),
|
|
281
|
+
DataGenerator.generate_html(items: @items),
|
|
282
|
+
format: :html,
|
|
283
|
+
)
|
|
284
|
+
when :html_compare_different
|
|
285
|
+
Canon::Comparison.equivalent?(
|
|
286
|
+
DataGenerator.generate_html(items: @items),
|
|
287
|
+
DataGenerator.generate_html(items: @items, with_tables: true),
|
|
288
|
+
format: :html,
|
|
289
|
+
)
|
|
290
|
+
when :xml_c14n_format
|
|
291
|
+
Canon.format_xml(DataGenerator.generate_xml(items: @items,
|
|
292
|
+
with_namespaces: true))
|
|
293
|
+
when :json_format
|
|
294
|
+
Canon.format_json(JSON.parse(DataGenerator.generate_json(items: @items)))
|
|
295
|
+
when :yaml_format
|
|
296
|
+
Canon.format_yaml(YAML.safe_load(DataGenerator.generate_yaml(items: @items),
|
|
297
|
+
permitted_classes: [Time]))
|
|
298
|
+
else
|
|
299
|
+
raise "Unknown benchmark: #{method}"
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
if $PROGRAM_NAME == __FILE__
|
|
306
|
+
report = Performance::Report.new
|
|
307
|
+
report.run_all
|
|
308
|
+
puts JSON.generate(report.as_json)
|
|
309
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: canon
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.12
|
|
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-
|
|
11
|
+
date: 2026-06-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: diff-lcs
|
|
@@ -219,6 +219,7 @@ files:
|
|
|
219
219
|
- lib/canon/cache.rb
|
|
220
220
|
- lib/canon/cli.rb
|
|
221
221
|
- lib/canon/color_detector.rb
|
|
222
|
+
- lib/canon/commands.rb
|
|
222
223
|
- lib/canon/commands/diff_command.rb
|
|
223
224
|
- lib/canon/commands/format_command.rb
|
|
224
225
|
- lib/canon/comparison.rb
|
|
@@ -226,16 +227,11 @@ files:
|
|
|
226
227
|
- lib/canon/comparison/child_realignment.rb
|
|
227
228
|
- lib/canon/comparison/compare_profile.rb
|
|
228
229
|
- lib/canon/comparison/comparison_result.rb
|
|
230
|
+
- lib/canon/comparison/diff_node_builder.rb
|
|
229
231
|
- lib/canon/comparison/dimensions.rb
|
|
230
|
-
- lib/canon/comparison/dimensions/
|
|
231
|
-
- lib/canon/comparison/dimensions/
|
|
232
|
-
- lib/canon/comparison/dimensions/attribute_values_dimension.rb
|
|
233
|
-
- lib/canon/comparison/dimensions/base_dimension.rb
|
|
234
|
-
- lib/canon/comparison/dimensions/comments_dimension.rb
|
|
235
|
-
- lib/canon/comparison/dimensions/element_position_dimension.rb
|
|
232
|
+
- lib/canon/comparison/dimensions/dimension.rb
|
|
233
|
+
- lib/canon/comparison/dimensions/dimension_set.rb
|
|
236
234
|
- lib/canon/comparison/dimensions/registry.rb
|
|
237
|
-
- lib/canon/comparison/dimensions/structural_whitespace_dimension.rb
|
|
238
|
-
- lib/canon/comparison/dimensions/text_content_dimension.rb
|
|
239
235
|
- lib/canon/comparison/format_detector.rb
|
|
240
236
|
- lib/canon/comparison/html_comparator.rb
|
|
241
237
|
- lib/canon/comparison/html_compare_profile.rb
|
|
@@ -249,8 +245,10 @@ files:
|
|
|
249
245
|
- lib/canon/comparison/match_options/xml_resolver.rb
|
|
250
246
|
- lib/canon/comparison/match_options/yaml_resolver.rb
|
|
251
247
|
- lib/canon/comparison/node_inspector.rb
|
|
248
|
+
- lib/canon/comparison/pipeline.rb
|
|
252
249
|
- lib/canon/comparison/profile_definition.rb
|
|
253
250
|
- lib/canon/comparison/ruby_object_comparator.rb
|
|
251
|
+
- lib/canon/comparison/strategies.rb
|
|
254
252
|
- lib/canon/comparison/strategies/base_match_strategy.rb
|
|
255
253
|
- lib/canon/comparison/strategies/match_strategy_factory.rb
|
|
256
254
|
- lib/canon/comparison/strategies/semantic_tree_match_strategy.rb
|
|
@@ -259,14 +257,15 @@ files:
|
|
|
259
257
|
- lib/canon/comparison/xml_comparator/attribute_comparator.rb
|
|
260
258
|
- lib/canon/comparison/xml_comparator/attribute_filter.rb
|
|
261
259
|
- lib/canon/comparison/xml_comparator/child_comparison.rb
|
|
262
|
-
- lib/canon/comparison/xml_comparator/diff_node_builder.rb
|
|
263
260
|
- lib/canon/comparison/xml_comparator/namespace_comparator.rb
|
|
264
261
|
- lib/canon/comparison/xml_comparator/node_parser.rb
|
|
265
262
|
- lib/canon/comparison/xml_comparator/node_type_comparator.rb
|
|
263
|
+
- lib/canon/comparison/xml_comparator_helpers.rb
|
|
266
264
|
- lib/canon/comparison/xml_node_comparison.rb
|
|
267
265
|
- lib/canon/comparison/xml_parser.rb
|
|
268
266
|
- lib/canon/comparison/yaml_comparator.rb
|
|
269
267
|
- lib/canon/config.rb
|
|
268
|
+
- lib/canon/config/config_dsl.rb
|
|
270
269
|
- lib/canon/config/env_provider.rb
|
|
271
270
|
- lib/canon/config/env_schema.rb
|
|
272
271
|
- lib/canon/config/override_resolver.rb
|
|
@@ -275,6 +274,7 @@ files:
|
|
|
275
274
|
- lib/canon/config/profiles/metanorma_debug.yml
|
|
276
275
|
- lib/canon/config/type_converter.rb
|
|
277
276
|
- lib/canon/data_model.rb
|
|
277
|
+
- lib/canon/diff.rb
|
|
278
278
|
- lib/canon/diff/diff_block.rb
|
|
279
279
|
- lib/canon/diff/diff_block_builder.rb
|
|
280
280
|
- lib/canon/diff/diff_char_range.rb
|
|
@@ -315,10 +315,12 @@ files:
|
|
|
315
315
|
- lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb
|
|
316
316
|
- lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb
|
|
317
317
|
- lib/canon/diff_formatter/diff_detail_formatter/text_utils.rb
|
|
318
|
+
- lib/canon/diff_formatter/diff_detail_formatter_helpers.rb
|
|
318
319
|
- lib/canon/diff_formatter/legend.rb
|
|
319
320
|
- lib/canon/diff_formatter/pretty_diff_formatter.rb
|
|
320
321
|
- lib/canon/diff_formatter/theme.rb
|
|
321
322
|
- lib/canon/errors.rb
|
|
323
|
+
- lib/canon/formatters.rb
|
|
322
324
|
- lib/canon/formatters/html4_formatter.rb
|
|
323
325
|
- lib/canon/formatters/html5_formatter.rb
|
|
324
326
|
- lib/canon/formatters/html_formatter.rb
|
|
@@ -328,8 +330,10 @@ files:
|
|
|
328
330
|
- lib/canon/formatters/yaml_formatter.rb
|
|
329
331
|
- lib/canon/html.rb
|
|
330
332
|
- lib/canon/html/data_model.rb
|
|
333
|
+
- lib/canon/options.rb
|
|
331
334
|
- lib/canon/options/cli_generator.rb
|
|
332
335
|
- lib/canon/options/registry.rb
|
|
336
|
+
- lib/canon/pretty_printer.rb
|
|
333
337
|
- lib/canon/pretty_printer/html.rb
|
|
334
338
|
- lib/canon/pretty_printer/html_void_elements.rb
|
|
335
339
|
- lib/canon/pretty_printer/json.rb
|
|
@@ -337,34 +341,41 @@ files:
|
|
|
337
341
|
- lib/canon/pretty_printer/xml_normalized.rb
|
|
338
342
|
- lib/canon/rspec_matchers.rb
|
|
339
343
|
- lib/canon/tree_diff.rb
|
|
344
|
+
- lib/canon/tree_diff/adapters.rb
|
|
340
345
|
- lib/canon/tree_diff/adapters/html_adapter.rb
|
|
341
346
|
- lib/canon/tree_diff/adapters/json_adapter.rb
|
|
342
347
|
- lib/canon/tree_diff/adapters/xml_adapter.rb
|
|
343
348
|
- lib/canon/tree_diff/adapters/yaml_adapter.rb
|
|
349
|
+
- lib/canon/tree_diff/core.rb
|
|
344
350
|
- lib/canon/tree_diff/core/attribute_comparator.rb
|
|
345
351
|
- lib/canon/tree_diff/core/matching.rb
|
|
346
352
|
- lib/canon/tree_diff/core/node_signature.rb
|
|
347
353
|
- lib/canon/tree_diff/core/node_weight.rb
|
|
348
354
|
- lib/canon/tree_diff/core/tree_node.rb
|
|
349
355
|
- lib/canon/tree_diff/core/xml_entity_decoder.rb
|
|
356
|
+
- lib/canon/tree_diff/matchers.rb
|
|
350
357
|
- lib/canon/tree_diff/matchers/hash_matcher.rb
|
|
351
358
|
- lib/canon/tree_diff/matchers/similarity_matcher.rb
|
|
352
359
|
- lib/canon/tree_diff/matchers/structural_propagator.rb
|
|
353
360
|
- lib/canon/tree_diff/matchers/universal_matcher.rb
|
|
354
361
|
- lib/canon/tree_diff/operation_converter.rb
|
|
362
|
+
- lib/canon/tree_diff/operation_converter_helpers.rb
|
|
355
363
|
- lib/canon/tree_diff/operation_converter_helpers/metadata_enricher.rb
|
|
356
364
|
- lib/canon/tree_diff/operation_converter_helpers/post_processor.rb
|
|
357
365
|
- lib/canon/tree_diff/operation_converter_helpers/reason_builder.rb
|
|
358
366
|
- lib/canon/tree_diff/operation_converter_helpers/update_change_handler.rb
|
|
367
|
+
- lib/canon/tree_diff/operations.rb
|
|
359
368
|
- lib/canon/tree_diff/operations/operation.rb
|
|
360
369
|
- lib/canon/tree_diff/operations/operation_detector.rb
|
|
361
370
|
- lib/canon/tree_diff/tree_diff_integrator.rb
|
|
371
|
+
- lib/canon/validators.rb
|
|
362
372
|
- lib/canon/validators/base_validator.rb
|
|
363
373
|
- lib/canon/validators/html_validator.rb
|
|
364
374
|
- lib/canon/validators/json_validator.rb
|
|
365
375
|
- lib/canon/validators/xml_validator.rb
|
|
366
376
|
- lib/canon/validators/yaml_validator.rb
|
|
367
377
|
- lib/canon/version.rb
|
|
378
|
+
- lib/canon/xml.rb
|
|
368
379
|
- lib/canon/xml/attribute_handler.rb
|
|
369
380
|
- lib/canon/xml/c14n.rb
|
|
370
381
|
- lib/canon/xml/character_encoder.rb
|
|
@@ -374,6 +385,7 @@ files:
|
|
|
374
385
|
- lib/canon/xml/namespace_handler.rb
|
|
375
386
|
- lib/canon/xml/namespace_helper.rb
|
|
376
387
|
- lib/canon/xml/node.rb
|
|
388
|
+
- lib/canon/xml/nodes.rb
|
|
377
389
|
- lib/canon/xml/nodes/attribute_node.rb
|
|
378
390
|
- lib/canon/xml/nodes/comment_node.rb
|
|
379
391
|
- lib/canon/xml/nodes/element_node.rb
|
|
@@ -392,6 +404,7 @@ files:
|
|
|
392
404
|
- lib/tasks/performance.rake
|
|
393
405
|
- lib/tasks/performance_comparator.rb
|
|
394
406
|
- lib/tasks/performance_helpers.rb
|
|
407
|
+
- lib/tasks/performance_report.rb
|
|
395
408
|
- lib/xml-c14n.rb
|
|
396
409
|
- sig/xml/c14n.rbs
|
|
397
410
|
homepage: https://github.com/lutaml/canon
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "base_dimension"
|
|
4
|
-
|
|
5
|
-
module Canon
|
|
6
|
-
module Comparison
|
|
7
|
-
module Dimensions
|
|
8
|
-
# Attribute order dimension
|
|
9
|
-
#
|
|
10
|
-
# Handles comparison of attribute ordering.
|
|
11
|
-
# Supports :strict and :ignore behaviors.
|
|
12
|
-
#
|
|
13
|
-
# Behaviors:
|
|
14
|
-
# - :strict - Attributes must appear in the same order
|
|
15
|
-
# - :ignore - Attribute order doesn't matter
|
|
16
|
-
class AttributeOrderDimension < BaseDimension
|
|
17
|
-
# Extract attribute order from a node
|
|
18
|
-
#
|
|
19
|
-
# @param node [Moxml::Node, Nokogiri::XML::Node] Node to extract from
|
|
20
|
-
# @return [Array<Symbol>] Array of attribute names in order
|
|
21
|
-
def extract_data(node)
|
|
22
|
-
return [] unless node
|
|
23
|
-
|
|
24
|
-
if Canon::XmlBackend.nokogiri?
|
|
25
|
-
extract_from_nokogiri(node)
|
|
26
|
-
else
|
|
27
|
-
extract_from_moxml(node)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Strict attribute order comparison
|
|
32
|
-
#
|
|
33
|
-
# @param order1 [Array<Symbol>] First attribute order
|
|
34
|
-
# @param order2 [Array<Symbol>] Second attribute order
|
|
35
|
-
# @return [Boolean] true if attribute order is exactly the same
|
|
36
|
-
def compare_strict(order1, order2) # rubocop:disable Naming/PredicateMethod
|
|
37
|
-
order1 == order2
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
# Extract attribute order from Moxml node
|
|
43
|
-
#
|
|
44
|
-
# @param node [Moxml::Node] Moxml node
|
|
45
|
-
# @return [Array<Symbol>] Array of attribute names in order
|
|
46
|
-
def extract_from_moxml(node)
|
|
47
|
-
return [] unless node.node_type == :element
|
|
48
|
-
|
|
49
|
-
node.attributes.map { |attr| attr.name.to_sym }
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Extract attribute order from Nokogiri node
|
|
53
|
-
#
|
|
54
|
-
# @param node [Nokogiri::XML::Node] Nokogiri node
|
|
55
|
-
# @return [Array<Symbol>] Array of attribute names in order
|
|
56
|
-
def extract_from_nokogiri(node)
|
|
57
|
-
return [] unless node.node_type == Nokogiri::XML::Node::ELEMENT_NODE
|
|
58
|
-
|
|
59
|
-
node.attribute_nodes.map { |attr| attr.name.to_sym }
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "base_dimension"
|
|
4
|
-
|
|
5
|
-
module Canon
|
|
6
|
-
module Comparison
|
|
7
|
-
module Dimensions
|
|
8
|
-
# Attribute presence dimension
|
|
9
|
-
#
|
|
10
|
-
# Handles comparison of attribute presence (which attributes exist).
|
|
11
|
-
# Supports :strict and :ignore behaviors.
|
|
12
|
-
#
|
|
13
|
-
# Behaviors:
|
|
14
|
-
# - :strict - Attribute names must match exactly
|
|
15
|
-
# - :ignore - Skip attribute presence comparison
|
|
16
|
-
class AttributePresenceDimension < BaseDimension
|
|
17
|
-
# Extract attribute names from a node
|
|
18
|
-
#
|
|
19
|
-
# @param node [Moxml::Node, Nokogiri::XML::Node] Node to extract from
|
|
20
|
-
# @return [Array<Symbol>] Array of attribute names
|
|
21
|
-
def extract_data(node)
|
|
22
|
-
return [] unless node
|
|
23
|
-
|
|
24
|
-
if Canon::XmlBackend.nokogiri?
|
|
25
|
-
extract_from_nokogiri(node)
|
|
26
|
-
else
|
|
27
|
-
extract_from_moxml(node)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Strict attribute presence comparison
|
|
32
|
-
#
|
|
33
|
-
# @param names1 [Array<Symbol>] First attribute names
|
|
34
|
-
# @param names2 [Array<Symbol>] Second attribute names
|
|
35
|
-
# @return [Boolean] true if attribute names are exactly equal
|
|
36
|
-
def compare_strict(names1, names2) # rubocop:disable Naming/PredicateMethod
|
|
37
|
-
names1.sort == names2.sort
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
# Extract attribute names from Moxml node
|
|
43
|
-
#
|
|
44
|
-
# @param node [Moxml::Node] Moxml node
|
|
45
|
-
# @return [Array<Symbol>] Array of attribute names
|
|
46
|
-
def extract_from_moxml(node)
|
|
47
|
-
return [] unless node.node_type == :element
|
|
48
|
-
|
|
49
|
-
node.attributes.map { |attr| attr.name.to_sym }
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Extract attribute names from Nokogiri node
|
|
53
|
-
#
|
|
54
|
-
# @param node [Nokogiri::XML::Node] Nokogiri node
|
|
55
|
-
# @return [Array<Symbol>] Array of attribute names
|
|
56
|
-
def extract_from_nokogiri(node)
|
|
57
|
-
return [] unless node.node_type == Nokogiri::XML::Node::ELEMENT_NODE
|
|
58
|
-
|
|
59
|
-
node.attribute_nodes.map { |attr| attr.name.to_sym }
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|