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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +112 -25
- data/docs/Gemfile +1 -0
- data/docs/_config.yml +90 -1
- data/docs/advanced/diff-classification.adoc +82 -2
- data/docs/features/match-options/index.adoc +239 -1
- data/lib/canon/comparison/format_detector.rb +2 -1
- data/lib/canon/comparison/html_comparator.rb +19 -8
- data/lib/canon/comparison/html_compare_profile.rb +8 -2
- data/lib/canon/comparison/match_options/base_resolver.rb +7 -0
- data/lib/canon/comparison/whitespace_sensitivity.rb +208 -0
- data/lib/canon/comparison/xml_comparator/child_comparison.rb +15 -7
- data/lib/canon/comparison/xml_comparator/node_parser.rb +10 -5
- data/lib/canon/comparison/xml_comparator/node_type_comparator.rb +14 -7
- data/lib/canon/comparison/xml_comparator.rb +48 -23
- data/lib/canon/comparison/xml_node_comparison.rb +25 -3
- data/lib/canon/diff/diff_classifier.rb +101 -2
- data/lib/canon/diff/formatting_detector.rb +1 -1
- data/lib/canon/rspec_matchers.rb +37 -8
- data/lib/canon/version.rb +1 -1
- data/lib/canon/xml/data_model.rb +24 -13
- metadata +3 -78
- data/docs/plans/2025-01-17-html-parser-selection-fix.adoc +0 -250
- data/false_positive_analysis.txt +0 -0
- data/file1.html +0 -1
- data/file2.html +0 -1
- data/old-docs/ADVANCED_TOPICS.adoc +0 -20
- data/old-docs/BASIC_USAGE.adoc +0 -16
- data/old-docs/CHARACTER_VISUALIZATION.adoc +0 -567
- data/old-docs/CLI.adoc +0 -497
- data/old-docs/CUSTOMIZING_BEHAVIOR.adoc +0 -19
- data/old-docs/DIFF_ARCHITECTURE.adoc +0 -435
- data/old-docs/DIFF_FORMATTING.adoc +0 -540
- data/old-docs/DIFF_PARAMETERS.adoc +0 -261
- data/old-docs/DOM_DIFF.adoc +0 -1017
- data/old-docs/ENV_CONFIG.adoc +0 -876
- data/old-docs/FORMATS.adoc +0 -867
- data/old-docs/INPUT_VALIDATION.adoc +0 -477
- data/old-docs/MATCHER_BEHAVIOR.adoc +0 -90
- data/old-docs/MATCH_ARCHITECTURE.adoc +0 -463
- data/old-docs/MATCH_OPTIONS.adoc +0 -912
- data/old-docs/MODES.adoc +0 -432
- data/old-docs/NORMATIVE_INFORMATIVE_DIFFS.adoc +0 -219
- data/old-docs/OPTIONS.adoc +0 -1387
- data/old-docs/PREPROCESSING.adoc +0 -491
- data/old-docs/README.old.adoc +0 -2831
- data/old-docs/RSPEC.adoc +0 -814
- data/old-docs/RUBY_API.adoc +0 -485
- data/old-docs/SEMANTIC_DIFF_REPORT.adoc +0 -646
- data/old-docs/SEMANTIC_TREE_DIFF.adoc +0 -765
- data/old-docs/STRING_COMPARE.adoc +0 -345
- data/old-docs/TMP.adoc +0 -3384
- data/old-docs/TREE_DIFF.adoc +0 -1080
- data/old-docs/UNDERSTANDING_CANON.adoc +0 -17
- data/old-docs/VERBOSE.adoc +0 -482
- data/old-docs/VISUALIZATION_MAP.adoc +0 -625
- data/old-docs/WHITESPACE_TREATMENT.adoc +0 -1155
- data/scripts/analyze_current_state.rb +0 -85
- data/scripts/analyze_false_positives.rb +0 -114
- data/scripts/analyze_remaining_failures.rb +0 -105
- data/scripts/compare_current_failures.rb +0 -95
- data/scripts/compare_dom_tree_diff.rb +0 -158
- data/scripts/compare_failures.rb +0 -151
- data/scripts/debug_attribute_extraction.rb +0 -66
- data/scripts/debug_blocks_839.rb +0 -115
- data/scripts/debug_meta_matching.rb +0 -52
- data/scripts/debug_p_matching.rb +0 -192
- data/scripts/debug_signature_matching.rb +0 -118
- data/scripts/debug_sourcecode_124.rb +0 -32
- data/scripts/debug_whitespace_sensitive.rb +0 -192
- data/scripts/extract_false_positives.rb +0 -138
- data/scripts/find_actual_false_positives.rb +0 -125
- data/scripts/investigate_all_false_positives.rb +0 -161
- data/scripts/investigate_batch1.rb +0 -127
- data/scripts/investigate_classification.rb +0 -150
- data/scripts/investigate_classification_detailed.rb +0 -190
- data/scripts/investigate_common_failures.rb +0 -342
- data/scripts/investigate_false_negative.rb +0 -80
- data/scripts/investigate_false_positive.rb +0 -83
- data/scripts/investigate_false_positives.rb +0 -227
- data/scripts/investigate_false_positives_batch.rb +0 -163
- data/scripts/investigate_mixed_content.rb +0 -125
- data/scripts/investigate_remaining_16.rb +0 -214
- data/scripts/run_single_test.rb +0 -29
- data/scripts/test_all_false_positives.rb +0 -95
- data/scripts/test_attribute_details.rb +0 -61
- data/scripts/test_both_algorithms.rb +0 -49
- data/scripts/test_both_simple.rb +0 -49
- data/scripts/test_enhanced_semantic_output.rb +0 -125
- data/scripts/test_readme_examples.rb +0 -131
- data/scripts/test_semantic_tree_diff.rb +0 -99
- data/scripts/test_semantic_ux_improvements.rb +0 -135
- data/scripts/test_single_false_positive.rb +0 -119
- data/scripts/test_size_limits.rb +0 -99
- data/test_html_1.html +0 -21
- data/test_html_2.html +0 -21
- data/test_nokogiri.rb +0 -33
- 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
|