canon 0.1.8 → 0.1.9

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.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +112 -25
  3. data/docs/Gemfile +1 -0
  4. data/docs/_config.yml +90 -1
  5. data/docs/advanced/diff-classification.adoc +82 -2
  6. data/docs/features/match-options/index.adoc +239 -1
  7. data/lib/canon/comparison/format_detector.rb +2 -1
  8. data/lib/canon/comparison/html_comparator.rb +19 -8
  9. data/lib/canon/comparison/html_compare_profile.rb +8 -2
  10. data/lib/canon/comparison/match_options/base_resolver.rb +7 -0
  11. data/lib/canon/comparison/whitespace_sensitivity.rb +208 -0
  12. data/lib/canon/comparison/xml_comparator/child_comparison.rb +15 -7
  13. data/lib/canon/comparison/xml_comparator/node_parser.rb +10 -5
  14. data/lib/canon/comparison/xml_comparator/node_type_comparator.rb +14 -7
  15. data/lib/canon/comparison/xml_comparator.rb +48 -23
  16. data/lib/canon/comparison/xml_node_comparison.rb +25 -3
  17. data/lib/canon/diff/diff_classifier.rb +101 -2
  18. data/lib/canon/diff/formatting_detector.rb +1 -1
  19. data/lib/canon/rspec_matchers.rb +37 -8
  20. data/lib/canon/version.rb +1 -1
  21. data/lib/canon/xml/data_model.rb +24 -13
  22. metadata +3 -78
  23. data/docs/plans/2025-01-17-html-parser-selection-fix.adoc +0 -250
  24. data/false_positive_analysis.txt +0 -0
  25. data/file1.html +0 -1
  26. data/file2.html +0 -1
  27. data/old-docs/ADVANCED_TOPICS.adoc +0 -20
  28. data/old-docs/BASIC_USAGE.adoc +0 -16
  29. data/old-docs/CHARACTER_VISUALIZATION.adoc +0 -567
  30. data/old-docs/CLI.adoc +0 -497
  31. data/old-docs/CUSTOMIZING_BEHAVIOR.adoc +0 -19
  32. data/old-docs/DIFF_ARCHITECTURE.adoc +0 -435
  33. data/old-docs/DIFF_FORMATTING.adoc +0 -540
  34. data/old-docs/DIFF_PARAMETERS.adoc +0 -261
  35. data/old-docs/DOM_DIFF.adoc +0 -1017
  36. data/old-docs/ENV_CONFIG.adoc +0 -876
  37. data/old-docs/FORMATS.adoc +0 -867
  38. data/old-docs/INPUT_VALIDATION.adoc +0 -477
  39. data/old-docs/MATCHER_BEHAVIOR.adoc +0 -90
  40. data/old-docs/MATCH_ARCHITECTURE.adoc +0 -463
  41. data/old-docs/MATCH_OPTIONS.adoc +0 -912
  42. data/old-docs/MODES.adoc +0 -432
  43. data/old-docs/NORMATIVE_INFORMATIVE_DIFFS.adoc +0 -219
  44. data/old-docs/OPTIONS.adoc +0 -1387
  45. data/old-docs/PREPROCESSING.adoc +0 -491
  46. data/old-docs/README.old.adoc +0 -2831
  47. data/old-docs/RSPEC.adoc +0 -814
  48. data/old-docs/RUBY_API.adoc +0 -485
  49. data/old-docs/SEMANTIC_DIFF_REPORT.adoc +0 -646
  50. data/old-docs/SEMANTIC_TREE_DIFF.adoc +0 -765
  51. data/old-docs/STRING_COMPARE.adoc +0 -345
  52. data/old-docs/TMP.adoc +0 -3384
  53. data/old-docs/TREE_DIFF.adoc +0 -1080
  54. data/old-docs/UNDERSTANDING_CANON.adoc +0 -17
  55. data/old-docs/VERBOSE.adoc +0 -482
  56. data/old-docs/VISUALIZATION_MAP.adoc +0 -625
  57. data/old-docs/WHITESPACE_TREATMENT.adoc +0 -1155
  58. data/scripts/analyze_current_state.rb +0 -85
  59. data/scripts/analyze_false_positives.rb +0 -114
  60. data/scripts/analyze_remaining_failures.rb +0 -105
  61. data/scripts/compare_current_failures.rb +0 -95
  62. data/scripts/compare_dom_tree_diff.rb +0 -158
  63. data/scripts/compare_failures.rb +0 -151
  64. data/scripts/debug_attribute_extraction.rb +0 -66
  65. data/scripts/debug_blocks_839.rb +0 -115
  66. data/scripts/debug_meta_matching.rb +0 -52
  67. data/scripts/debug_p_matching.rb +0 -192
  68. data/scripts/debug_signature_matching.rb +0 -118
  69. data/scripts/debug_sourcecode_124.rb +0 -32
  70. data/scripts/debug_whitespace_sensitive.rb +0 -192
  71. data/scripts/extract_false_positives.rb +0 -138
  72. data/scripts/find_actual_false_positives.rb +0 -125
  73. data/scripts/investigate_all_false_positives.rb +0 -161
  74. data/scripts/investigate_batch1.rb +0 -127
  75. data/scripts/investigate_classification.rb +0 -150
  76. data/scripts/investigate_classification_detailed.rb +0 -190
  77. data/scripts/investigate_common_failures.rb +0 -342
  78. data/scripts/investigate_false_negative.rb +0 -80
  79. data/scripts/investigate_false_positive.rb +0 -83
  80. data/scripts/investigate_false_positives.rb +0 -227
  81. data/scripts/investigate_false_positives_batch.rb +0 -163
  82. data/scripts/investigate_mixed_content.rb +0 -125
  83. data/scripts/investigate_remaining_16.rb +0 -214
  84. data/scripts/run_single_test.rb +0 -29
  85. data/scripts/test_all_false_positives.rb +0 -95
  86. data/scripts/test_attribute_details.rb +0 -61
  87. data/scripts/test_both_algorithms.rb +0 -49
  88. data/scripts/test_both_simple.rb +0 -49
  89. data/scripts/test_enhanced_semantic_output.rb +0 -125
  90. data/scripts/test_readme_examples.rb +0 -131
  91. data/scripts/test_semantic_tree_diff.rb +0 -99
  92. data/scripts/test_semantic_ux_improvements.rb +0 -135
  93. data/scripts/test_single_false_positive.rb +0 -119
  94. data/scripts/test_size_limits.rb +0 -99
  95. data/test_html_1.html +0 -21
  96. data/test_html_2.html +0 -21
  97. data/test_nokogiri.rb +0 -33
  98. data/test_normalize.rb +0 -45
@@ -1,342 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Investigate the 43 common failures to determine if they represent Canon classification bugs
5
- # Both DOM and semantic algorithms agree these tests fail, but they might BOTH be wrong
6
-
7
- require "json"
8
- require "fileutils"
9
-
10
- # Sample tests from the 43 common failures
11
- SAMPLE_TESTS = [
12
- { file: "blocks_notes_spec.rb", line: 494 },
13
- { file: "blocks_provisions_spec.rb", line: 4 },
14
- { file: "cleanup_spec.rb", line: 180 },
15
- { file: "figures_spec.rb", line: 5 },
16
- { file: "tables_spec.rb", line: 4 },
17
- # Add more samples for thorough investigation
18
- { file: "blocks_notes_spec.rb", line: 12 },
19
- { file: "blocks_notes_spec.rb", line: 15 },
20
- { file: "blocks_notes_spec.rb", line: 18 },
21
- { file: "blocks_notes_spec.rb", line: 21 },
22
- { file: "cleanup_spec.rb", line: 126 },
23
- ].freeze
24
-
25
- ISODOC_PATH = "/Users/mulgogi/src/mn/isodoc"
26
- OUTPUT_DIR = "/tmp/common_failure_investigation"
27
- FileUtils.mkdir_p(OUTPUT_DIR)
28
-
29
- class CommonFailureInvestigator
30
- def initialize
31
- @findings = []
32
- @bugs_found = []
33
- end
34
-
35
- def investigate_all
36
- puts "=" * 80
37
- puts "INVESTIGATING 43 COMMON FAILURES"
38
- puts "Hypothesis: Both algorithms might incorrectly classify differences"
39
- puts "=" * 80
40
- puts
41
-
42
- SAMPLE_TESTS.each_with_index do |test, idx|
43
- puts "\n#{'-' * 80}"
44
- puts "Test #{idx + 1}/#{SAMPLE_TESTS.size}: #{test[:file]}:#{test[:line]}"
45
- puts "-" * 80
46
-
47
- investigate_test(test)
48
- end
49
-
50
- generate_report
51
- end
52
-
53
- private
54
-
55
- def investigate_test(test)
56
- spec_file = "spec/isodoc/#{test[:file]}"
57
- line = test[:line]
58
-
59
- # Run with DOM algorithm first
60
- puts "\n1. Running with DOM algorithm (verbose)..."
61
- dom_result = run_test_verbose(spec_file, line, "dom")
62
-
63
- # Run with Semantic algorithm
64
- puts "\n2. Running with Semantic algorithm (verbose)..."
65
- semantic_result = run_test_verbose(spec_file, line, "semantic")
66
-
67
- # Analyze both results
68
- finding = analyze_results(test, dom_result, semantic_result)
69
- @findings << finding
70
-
71
- if finding[:bug_suspected]
72
- @bugs_found << finding
73
- puts "\n⚠️ POTENTIAL BUG FOUND!"
74
- puts " #{finding[:bug_description]}"
75
- else
76
- puts "\n✓ Classification appears correct"
77
- end
78
- end
79
-
80
- def run_test_verbose(spec_file, line, algorithm)
81
- output_file = "#{OUTPUT_DIR}/#{algorithm}_#{spec_file.gsub('/',
82
- '_')}_#{line}.txt"
83
-
84
- cmd = <<~CMD
85
- cd #{ISODOC_PATH} && \
86
- CANON_ALGORITHM=#{algorithm} \
87
- CANON_VERBOSE=true \
88
- bundle exec rspec #{spec_file}:#{line} 2>&1 | tee #{output_file}
89
- CMD
90
-
91
- system(cmd)
92
-
93
- parse_test_output(output_file)
94
- end
95
-
96
- def parse_test_output(file)
97
- return nil unless File.exist?(file)
98
-
99
- content = File.read(file)
100
-
101
- {
102
- passed: content.include?("0 failures"),
103
- differences: extract_differences(content),
104
- match_options: extract_match_options(content),
105
- dimensions: extract_dimensions(content),
106
- raw_output: content,
107
- }
108
- end
109
-
110
- def extract_differences(content)
111
- diffs = []
112
- current_diff = nil
113
-
114
- content.each_line do |line|
115
- if line =~ /DIFFERENCE #(\d+):/
116
- current_diff = { number: $1.to_i, lines: [line] }
117
- diffs << current_diff
118
- elsif current_diff && line =~ /^\s*[│┌└├]/
119
- current_diff[:lines] << line
120
- elsif current_diff && line.strip.empty?
121
- current_diff = nil
122
- elsif current_diff
123
- current_diff[:lines] << line
124
- end
125
- end
126
-
127
- diffs
128
- end
129
-
130
- def extract_match_options(content)
131
- options = {}
132
-
133
- if content =~ /Match Options:\s*\{([^}]+)\}/
134
- options_str = $1
135
- options_str.scan(/(\w+):\s*:?(\w+)/) do |key, value|
136
- options[key.to_sym] = value.to_sym
137
- end
138
- end
139
-
140
- options
141
- end
142
-
143
- def extract_dimensions(content)
144
- dimensions = []
145
-
146
- content.scan(/Dimension:\s*(\w+)/) do |match|
147
- dimensions << match[0]
148
- end
149
-
150
- dimensions.uniq
151
- end
152
-
153
- def analyze_results(test, dom_result, semantic_result)
154
- finding = {
155
- test: test,
156
- bug_suspected: false,
157
- bug_description: nil,
158
- dom_analysis: analyze_single_result(dom_result),
159
- semantic_analysis: analyze_single_result(semantic_result),
160
- }
161
-
162
- # Check for classification bugs
163
- bugs = check_for_bugs(dom_result, semantic_result)
164
- if bugs.any?
165
- finding[:bug_suspected] = true
166
- finding[:bug_description] = bugs.join("; ")
167
- end
168
-
169
- finding
170
- end
171
-
172
- def analyze_single_result(result)
173
- return nil unless result
174
-
175
- {
176
- passed: result[:passed],
177
- match_options: result[:match_options],
178
- dimensions: result[:dimensions],
179
- diff_count: result[:differences].size,
180
- }
181
- end
182
-
183
- def check_for_bugs(dom_result, semantic_result)
184
- bugs = []
185
-
186
- return bugs unless dom_result && semantic_result
187
-
188
- # Check DOM result for bugs
189
- bugs.concat(check_result_for_bugs(dom_result, "DOM"))
190
-
191
- # Check Semantic result for bugs
192
- bugs.concat(check_result_for_bugs(semantic_result, "Semantic"))
193
-
194
- bugs
195
- end
196
-
197
- def check_result_for_bugs(result, algorithm)
198
- bugs = []
199
- return bugs if result[:passed]
200
-
201
- options = result[:match_options]
202
- dimensions = result[:dimensions]
203
-
204
- # Bug 1: attribute_order: ignore but order diffs are NORMATIVE
205
- if options[:attribute_order] == :ignore && dimensions.include?("attribute_order")
206
- bugs << "#{algorithm}: attribute_order:ignore but order diffs reported as NORMATIVE"
207
- end
208
-
209
- # Bug 2: text_content normalization issues
210
- if options[:text_content] == :normalize && dimensions.include?("text_content")
211
- # Check if the diff is about normalized-equivalent text
212
- result[:differences].each do |diff|
213
- diff_text = diff[:lines].join
214
- if diff_text.include?("whitespace") || diff_text.include?("spacing")
215
- bugs << "#{algorithm}: text_content:normalize but whitespace diffs reported"
216
- end
217
- end
218
- end
219
-
220
- # Bug 3: comments: ignore but comments cause failure
221
- if options[:comments] == :ignore && dimensions.include?("comment")
222
- bugs << "#{algorithm}: comments:ignore but comment diffs reported as NORMATIVE"
223
- end
224
-
225
- # Bug 4: whitespace_only: ignore but whitespace-only changes fail
226
- if options[:whitespace_only] == :ignore
227
- result[:differences].each do |diff|
228
- diff_text = diff[:lines].join
229
- if diff_text.match?(/only.*whitespace/i)
230
- bugs << "#{algorithm}: whitespace_only:ignore but whitespace-only diffs reported"
231
- end
232
- end
233
- end
234
-
235
- bugs
236
- end
237
-
238
- def generate_report
239
- report_file = "#{OUTPUT_DIR}/CLASSIFICATION_INVESTIGATION_REPORT.md"
240
-
241
- File.open(report_file, "w") do |f|
242
- f.puts "# Classification Investigation Report"
243
- f.puts
244
- f.puts "Investigation of the 43 common failures where both DOM and semantic algorithms agree."
245
- f.puts
246
- f.puts "**Date:** #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
247
- f.puts
248
- f.puts "## Executive Summary"
249
- f.puts
250
- f.puts "- Tests investigated: #{@findings.size}"
251
- f.puts "- Potential bugs found: #{@bugs_found.size}"
252
- f.puts
253
-
254
- if @bugs_found.any?
255
- f.puts "## ⚠️ Bugs Found"
256
- f.puts
257
- @bugs_found.each_with_index do |bug, idx|
258
- f.puts "### Bug #{idx + 1}: #{bug[:test][:file]}:#{bug[:test][:line]}"
259
- f.puts
260
- f.puts "**Description:** #{bug[:bug_description]}"
261
- f.puts
262
- f.puts "**DOM Analysis:**"
263
- f.puts "```"
264
- f.puts bug[:dom_analysis].inspect
265
- f.puts "```"
266
- f.puts
267
- f.puts "**Semantic Analysis:**"
268
- f.puts "```"
269
- f.puts bug[:semantic_analysis].inspect
270
- f.puts "```"
271
- f.puts
272
- end
273
- else
274
- f.puts "## ✅ No Classification Bugs Found"
275
- f.puts
276
- f.puts "All investigated failures appear to be legitimate test failures,"
277
- f.puts "not bugs in Canon's classification logic."
278
- f.puts
279
- end
280
-
281
- f.puts "## Detailed Findings"
282
- f.puts
283
- @findings.each_with_index do |finding, idx|
284
- f.puts "### Test #{idx + 1}: #{finding[:test][:file]}:#{finding[:test][:line]}"
285
- f.puts
286
- f.puts "**Bug Suspected:** #{finding[:bug_suspected] ? 'YES ⚠️' : 'No'}"
287
- f.puts
288
- if finding[:bug_description]
289
- f.puts "**Issue:** #{finding[:bug_description]}"
290
- f.puts
291
- end
292
- f.puts "**DOM:**"
293
- f.puts "- Match Options: #{finding[:dom_analysis][:match_options]}"
294
- f.puts "- Dimensions: #{finding[:dom_analysis][:dimensions].join(', ')}"
295
- f.puts "- Diff Count: #{finding[:dom_analysis][:diff_count]}"
296
- f.puts
297
- f.puts "**Semantic:**"
298
- f.puts "- Match Options: #{finding[:semantic_analysis][:match_options]}"
299
- f.puts "- Dimensions: #{finding[:semantic_analysis][:dimensions].join(', ')}"
300
- f.puts "- Diff Count: #{finding[:semantic_analysis][:diff_count]}"
301
- f.puts
302
- f.puts "---"
303
- f.puts
304
- end
305
-
306
- f.puts "## Next Steps"
307
- f.puts
308
- if @bugs_found.any?
309
- f.puts "1. Review the bugs found above"
310
- f.puts "2. Fix classification logic in:"
311
- f.puts " - `lib/canon/diff/diff_classifier.rb`"
312
- f.puts " - `lib/canon/tree_diff/operation_converter.rb`"
313
- f.puts "3. Re-run tests to verify fixes"
314
- else
315
- f.puts "The 43 common failures appear to be legitimate test failures,"
316
- f.puts "not Canon classification bugs. These tests fail correctly in both algorithms."
317
- end
318
- end
319
-
320
- puts "\n\n#{'=' * 80}"
321
- puts "INVESTIGATION COMPLETE"
322
- puts "=" * 80
323
- puts
324
- puts "Report saved to: #{report_file}"
325
- puts
326
- puts "Summary:"
327
- puts " Tests investigated: #{@findings.size}"
328
- puts " Bugs found: #{@bugs_found.size}"
329
- puts
330
-
331
- if @bugs_found.any?
332
- puts "⚠️ Classification bugs detected! See report for details."
333
- else
334
- puts "✓ No classification bugs found. Failures are legitimate."
335
- end
336
- puts
337
- end
338
- end
339
-
340
- # Run investigation
341
- investigator = CommonFailureInvestigator.new
342
- investigator.investigate_all
@@ -1,80 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "canon"
6
- require "nokogiri"
7
-
8
- # Test one of the false negative cases
9
- # These are passing semantic but failing DOM
10
-
11
- # Test case: Check if the space insertion is causing problems
12
- def test_space_insertion_edge_cases
13
- puts "=" * 80
14
- puts "Testing Space Insertion Edge Cases"
15
- puts "=" * 80
16
-
17
- # Case 1: Elements without any child elements should not get spaces
18
- xml1 = "<root><text>Hello World</text></root>"
19
- xml2 = "<root><text>Hello World</text></root>"
20
-
21
- puts "\nCase 1: Simple text (no child elements)"
22
- puts " XML1: #{xml1}"
23
- puts " XML2: #{xml2}"
24
-
25
- doc1 = Nokogiri::XML(xml1)
26
- doc2 = Nokogiri::XML(xml2)
27
-
28
- adapter = Canon::TreeDiff::Adapters::XMLAdapter.new
29
- tree1 = adapter.to_tree(doc1)
30
- tree2 = adapter.to_tree(doc2)
31
-
32
- text_node1 = tree1.children.first
33
- text_node2 = tree2.children.first
34
-
35
- puts " Tree1 text value: #{text_node1.value.inspect}"
36
- puts " Tree2 text value: #{text_node2.value.inspect}"
37
- puts " Should be different: #{text_node1.value != text_node2.value}"
38
-
39
- # Case 2: Mixed content WITH br
40
- xml3 = "<root><text>A<br/>B</text></root>"
41
- xml4 = "<root><text>A<br/>C</text></root>"
42
-
43
- puts "\nCase 2: Mixed content with <br/>"
44
- puts " XML3: #{xml3}"
45
- puts " XML4: #{xml4}"
46
-
47
- doc3 = Nokogiri::XML(xml3)
48
- doc4 = Nokogiri::XML(xml4)
49
-
50
- tree3 = adapter.to_tree(doc3)
51
- tree4 = adapter.to_tree(doc4)
52
-
53
- text_node3 = tree3.children.first
54
- text_node4 = tree4.children.first
55
-
56
- puts " Tree3 text value: #{text_node3.value.inspect}"
57
- puts " Tree4 text value: #{text_node4.value.inspect}"
58
- puts " Should be different: #{text_node3.value != text_node4.value}"
59
-
60
- # Case 3: Text nodes that are just whitespace between elements
61
- xml5 = "<root><a>X</a> <b>Y</b></root>"
62
- xml6 = "<root><a>X</a><b>Y</b></root>"
63
-
64
- puts "\nCase 3: Whitespace between elements"
65
- puts " XML5: #{xml5}"
66
- puts " XML6: #{xml6}"
67
-
68
- doc5 = Nokogiri::XML(xml5)
69
- doc6 = Nokogiri::XML(xml6)
70
-
71
- tree5 = adapter.to_tree(doc5)
72
- tree6 = adapter.to_tree(doc6)
73
-
74
- puts " Tree5 root value: #{tree5.value.inspect}"
75
- puts " Tree6 root value: #{tree6.value.inspect}"
76
- puts " Tree5 root has #{tree5.children.size} children"
77
- puts " Tree6 root has #{tree6.children.size} children"
78
- end
79
-
80
- test_space_insertion_edge_cases
@@ -1,83 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Script to investigate false positives in semantic tree algorithm
5
- # Usage: ruby scripts/investigate_false_positive.rb
6
-
7
- require "bundler/setup"
8
- require "canon"
9
-
10
- # Simple test case demonstrating whitespace in sourcecode
11
- expected = <<~XML
12
- <div>
13
- <pre class="sourcecode">
14
- Hey
15
- Que?
16
- </pre>
17
- </div>
18
- XML
19
-
20
- actual = <<~XML
21
- <div>
22
- <pre class="sourcecode">Hey
23
- Que?</pre>
24
- </div>
25
- XML
26
-
27
- puts "=" * 80
28
- puts "Testing Whitespace Handling in <pre> Elements"
29
- puts "=" * 80
30
-
31
- # Test with DOM diff algorithm
32
- puts "\n1. DOM DIFF ALGORITHM:"
33
- puts "-" * 80
34
- result_dom = Canon::Comparison.equivalent?(expected, actual,
35
- format: :html,
36
- diff_algorithm: :dom,
37
- verbose: true)
38
-
39
- dom_match = result_dom.is_a?(Canon::Comparison::ComparisonResult) ? result_dom.equivalent? : result_dom
40
- puts "Match: #{dom_match}"
41
- if result_dom.is_a?(Canon::Comparison::ComparisonResult)
42
- puts "Normative diffs: #{result_dom.normative_differences.count}"
43
- puts "Total diffs: #{result_dom.differences.count}"
44
- end
45
-
46
- # Test with Semantic Tree diff algorithm
47
- puts "\n2. SEMANTIC TREE ALGORITHM:"
48
- puts "-" * 80
49
- result_semantic = Canon::Comparison.equivalent?(expected, actual,
50
- format: :html,
51
- diff_algorithm: :semantic,
52
- verbose: true)
53
-
54
- semantic_match = result_semantic.is_a?(Canon::Comparison::ComparisonResult) ? result_semantic.equivalent? : result_semantic
55
- puts "Match: #{semantic_match}"
56
- if result_semantic.is_a?(Canon::Comparison::ComparisonResult)
57
- puts "Normative diffs: #{result_semantic.normative_differences.count}"
58
- puts "Total diffs: #{result_semantic.differences.count}"
59
- end
60
-
61
- puts "\n#{'=' * 80}"
62
- puts "ANALYSIS:"
63
- puts "=" * 80
64
-
65
- if dom_match && !semantic_match
66
- puts "❌ FALSE POSITIVE: Semantic tree incorrectly reports difference"
67
-
68
- if result_semantic.is_a?(Canon::Comparison::ComparisonResult)
69
- puts "\nDifferences found by semantic tree:"
70
- result_semantic.differences.each_with_index do |diff, i|
71
- puts "\n Diff #{i + 1}:"
72
- puts " Type: #{diff.class}"
73
- puts " Normative: #{diff.normative?}" if diff.respond_to?(:normative?)
74
- puts " Details: #{diff.inspect}"
75
- end
76
- end
77
- elsif !dom_match && semantic_match
78
- puts "❌ FALSE NEGATIVE: Semantic tree misses real difference"
79
- elsif dom_match && semantic_match
80
- puts "✅ BOTH AGREE: No difference (correct)"
81
- else
82
- puts "✅ BOTH AGREE: Difference exists (correct)"
83
- end