svg_conform 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/rake.yml +15 -0
- data/.github/workflows/release.yml +23 -0
- data/.github/workflows/svgcheck-compatibility.yml +135 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/.rubocop_todo.yml +183 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Gemfile +14 -0
- data/README.adoc +1384 -0
- data/Rakefile +15 -0
- data/config/profiles/base.yml +34 -0
- data/config/profiles/lucid_fix.yml +37 -0
- data/config/profiles/metanorma.yml +212 -0
- data/config/profiles/no_external_css.yml +20 -0
- data/config/profiles/svg_1_2_rfc.yml +284 -0
- data/config/profiles/svg_1_2_rfc_with_rdf.yml +145 -0
- data/config/svgcheck_mapping.yml +180 -0
- data/docs/profiles.adoc +547 -0
- data/docs/rdf_metadata_support.adoc +212 -0
- data/docs/remediation.adoc +732 -0
- data/docs/requirements.adoc +709 -0
- data/examples/demo.rb +116 -0
- data/examples/requirements_demo.rb +240 -0
- data/exe/svg_conform +7 -0
- data/lib/svg_conform/batch_report.rb +70 -0
- data/lib/svg_conform/cli.rb +107 -0
- data/lib/svg_conform/commands/check.rb +390 -0
- data/lib/svg_conform/commands/profiles.rb +118 -0
- data/lib/svg_conform/commands/svgcheck.rb +90 -0
- data/lib/svg_conform/commands/svgcheck_compare.rb +92 -0
- data/lib/svg_conform/commands/svgcheck_compatibility.rb +43 -0
- data/lib/svg_conform/commands/svgcheck_generate.rb +262 -0
- data/lib/svg_conform/compatibility/analysis_context.rb +68 -0
- data/lib/svg_conform/compatibility/comparison_result.rb +147 -0
- data/lib/svg_conform/compatibility/compatibility_analyzer.rb +85 -0
- data/lib/svg_conform/compatibility/file_processor.rb +109 -0
- data/lib/svg_conform/compatibility/pattern_discovery.rb +319 -0
- data/lib/svg_conform/compatibility/report_formatter.rb +359 -0
- data/lib/svg_conform/compatibility/svg_analysis_engine.rb +316 -0
- data/lib/svg_conform/compatibility/validity_analysis.rb +252 -0
- data/lib/svg_conform/compatibility/xml_analysis_engine.rb +198 -0
- data/lib/svg_conform/compatibility_analyzer.rb +285 -0
- data/lib/svg_conform/conformance_report.rb +267 -0
- data/lib/svg_conform/constants.rb +199 -0
- data/lib/svg_conform/css_color.rb +262 -0
- data/lib/svg_conform/document.rb +203 -0
- data/lib/svg_conform/external_checkers/svgcheck/compatibility_engine.rb +166 -0
- data/lib/svg_conform/external_checkers/svgcheck/output_generator.rb +101 -0
- data/lib/svg_conform/external_checkers/svgcheck/parser.rb +200 -0
- data/lib/svg_conform/external_checkers/svgcheck/report_comparator.rb +175 -0
- data/lib/svg_conform/external_checkers/svgcheck/report_generator.rb +82 -0
- data/lib/svg_conform/external_checkers/svgcheck/validation_pipeline.rb +249 -0
- data/lib/svg_conform/external_checkers/svgcheck.rb +56 -0
- data/lib/svg_conform/external_checkers.rb +34 -0
- data/lib/svg_conform/fixer.rb +56 -0
- data/lib/svg_conform/profile.rb +164 -0
- data/lib/svg_conform/profiles.rb +60 -0
- data/lib/svg_conform/remediation_engine.rb +92 -0
- data/lib/svg_conform/remediation_result.rb +36 -0
- data/lib/svg_conform/remediation_runner.rb +225 -0
- data/lib/svg_conform/remediations/base_remediation.rb +165 -0
- data/lib/svg_conform/remediations/color_remediation.rb +226 -0
- data/lib/svg_conform/remediations/font_embedding_remediation.rb +145 -0
- data/lib/svg_conform/remediations/font_remediation.rb +122 -0
- data/lib/svg_conform/remediations/image_embedding_remediation.rb +154 -0
- data/lib/svg_conform/remediations/invalid_id_references_remediation.rb +129 -0
- data/lib/svg_conform/remediations/namespace_attribute_remediation.rb +244 -0
- data/lib/svg_conform/remediations/namespace_remediation.rb +151 -0
- data/lib/svg_conform/remediations/no_external_css_remediation.rb +192 -0
- data/lib/svg_conform/remediations/style_promotion_remediation.rb +93 -0
- data/lib/svg_conform/remediations/viewbox_remediation.rb +127 -0
- data/lib/svg_conform/remediations.rb +40 -0
- data/lib/svg_conform/report_comparator.rb +772 -0
- data/lib/svg_conform/requirements/allowed_elements_requirement.rb +367 -0
- data/lib/svg_conform/requirements/base_requirement.rb +98 -0
- data/lib/svg_conform/requirements/color_restrictions_requirement.rb +126 -0
- data/lib/svg_conform/requirements/element_requirement_config.rb +75 -0
- data/lib/svg_conform/requirements/font_family_requirement.rb +133 -0
- data/lib/svg_conform/requirements/forbidden_content_requirement.rb +60 -0
- data/lib/svg_conform/requirements/id_reference_requirement.rb +133 -0
- data/lib/svg_conform/requirements/invalid_id_references_requirement.rb +132 -0
- data/lib/svg_conform/requirements/link_validation_requirement.rb +55 -0
- data/lib/svg_conform/requirements/namespace_attributes_requirement.rb +211 -0
- data/lib/svg_conform/requirements/namespace_requirement.rb +294 -0
- data/lib/svg_conform/requirements/no_external_css_requirement.rb +132 -0
- data/lib/svg_conform/requirements/no_external_fonts_requirement.rb +121 -0
- data/lib/svg_conform/requirements/no_external_images_requirement.rb +91 -0
- data/lib/svg_conform/requirements/style_promotion_requirement.rb +72 -0
- data/lib/svg_conform/requirements/style_requirement.rb +226 -0
- data/lib/svg_conform/requirements/viewbox_required_requirement.rb +96 -0
- data/lib/svg_conform/requirements.rb +49 -0
- data/lib/svg_conform/semantic_comparator.rb +829 -0
- data/lib/svg_conform/validation_context.rb +408 -0
- data/lib/svg_conform/validation_result.rb +146 -0
- data/lib/svg_conform/validator.rb +91 -0
- data/lib/svg_conform/version.rb +5 -0
- data/lib/svg_conform.rb +68 -0
- data/lib/tasks/fixtures.rake +321 -0
- data/lib/tasks/svgcheck.rake +111 -0
- data/reference-docs/SVG-1.2-RFC.rnc.txt +1676 -0
- data/reference-docs/Scalable Vector Graphics (SVG) 1.1 (Second Edition).html +40764 -0
- data/reference-docs/Scalable Vector Graphics (SVG) Tiny 1.2 Specification.html +44591 -0
- data/reference-docs/rfc7996.txt +2971 -0
- data/sig/svg_conform.rbs +4 -0
- data/spec/fixtures/allowed_elements/inputs/basic_violations.svg +21 -0
- data/spec/fixtures/allowed_elements/repair/basic_violations.svg +18 -0
- data/spec/fixtures/color_restrictions/inputs/basic_violations.svg +23 -0
- data/spec/fixtures/color_restrictions/repair/basic_violations.svg +23 -0
- data/spec/fixtures/comprehensive/inputs/multiple_violations.svg +21 -0
- data/spec/fixtures/comprehensive/repair/multiple_violations.svg +16 -0
- data/spec/fixtures/font_family/inputs/basic_violations.svg +17 -0
- data/spec/fixtures/font_family/repair/basic_violations.svg +17 -0
- data/spec/fixtures/forbidden_content/inputs/basic_violations.svg +27 -0
- data/spec/fixtures/forbidden_content/repair/basic_violations.svg +19 -0
- data/spec/fixtures/id_reference/inputs/basic_violations.svg +17 -0
- data/spec/fixtures/id_reference/repair/basic_violations.svg +15 -0
- data/spec/fixtures/link_validation/inputs/basic_violations.svg +20 -0
- data/spec/fixtures/link_validation/repair/basic_violations.svg +16 -0
- data/spec/fixtures/lucid/inputs/simple.svg +67 -0
- data/spec/fixtures/lucid/repair/simple.svg +63 -0
- data/spec/fixtures/namespace/inputs/basic_violations.svg +20 -0
- data/spec/fixtures/namespace/repair/basic_violations.svg +17 -0
- data/spec/fixtures/namespace_attributes/inputs/basic_violations.svg +16 -0
- data/spec/fixtures/namespace_attributes/repair/basic_violations.svg +15 -0
- data/spec/fixtures/no_external_css/inputs/basic_violations.svg +20 -0
- data/spec/fixtures/no_external_css/repair/basic_violations.svg +18 -0
- data/spec/fixtures/style/inputs/basic_violations.svg +22 -0
- data/spec/fixtures/style/repair/basic_violations.svg +22 -0
- data/spec/fixtures/style_promotion/inputs/basic_test.svg +15 -0
- data/spec/fixtures/style_promotion/repair/basic_test.svg +15 -0
- data/spec/fixtures/svg_1_2_rfc/inputs/allowed_elements_violations.svg +18 -0
- data/spec/fixtures/svg_1_2_rfc/inputs/color_restrictions_violations.svg +23 -0
- data/spec/fixtures/svgcheck/check/DrawBerry-sample-2.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/DrawBerry-sample-2.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/DrawBerry-sample-2.svg.out +23 -0
- data/spec/fixtures/svgcheck/check/IETF-test.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/IETF-test.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/IETF-test.svg.out +20 -0
- data/spec/fixtures/svgcheck/check/circle.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/circle.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/circle.svg.out +2 -0
- data/spec/fixtures/svgcheck/check/colors.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/colors.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/colors.svg.out +13 -0
- data/spec/fixtures/svgcheck/check/dia-sample-svg.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/dia-sample-svg.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/dia-sample-svg.svg.out +76 -0
- data/spec/fixtures/svgcheck/check/example-dot.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/example-dot.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/example-dot.svg.out +11 -0
- data/spec/fixtures/svgcheck/check/full-tiny.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/full-tiny.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/full-tiny.svg.out +5835 -0
- data/spec/fixtures/svgcheck/check/good.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/good.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/good.svg.out +1 -0
- data/spec/fixtures/svgcheck/check/httpbis-proxy20-fig6.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/httpbis-proxy20-fig6.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/httpbis-proxy20-fig6.svg.out +5 -0
- data/spec/fixtures/svgcheck/check/malformed.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/malformed.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/malformed.svg.out +8 -0
- data/spec/fixtures/svgcheck/check/rfc-svg.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/rfc-svg.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/rfc-svg.svg.out +1 -0
- data/spec/fixtures/svgcheck/check/rfc.xml.code +1 -0
- data/spec/fixtures/svgcheck/check/rfc.xml.err +0 -0
- data/spec/fixtures/svgcheck/check/rfc.xml.out +2 -0
- data/spec/fixtures/svgcheck/check/rgb.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/rgb.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/rgb.svg.out +9 -0
- data/spec/fixtures/svgcheck/check/svg-wordle.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/svg-wordle.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/svg-wordle.svg.out +508 -0
- data/spec/fixtures/svgcheck/check/threshold.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/threshold.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/threshold.svg.out +20 -0
- data/spec/fixtures/svgcheck/check/utf8.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/utf8.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/utf8.svg.out +162 -0
- data/spec/fixtures/svgcheck/check/viewBox-both.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/viewBox-both.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/viewBox-both.svg.out +4 -0
- data/spec/fixtures/svgcheck/check/viewBox-height.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/viewBox-height.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/viewBox-height.svg.out +3 -0
- data/spec/fixtures/svgcheck/check/viewBox-none.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/viewBox-none.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/viewBox-none.svg.out +3 -0
- data/spec/fixtures/svgcheck/check/viewBox-width.svg.code +1 -0
- data/spec/fixtures/svgcheck/check/viewBox-width.svg.err +0 -0
- data/spec/fixtures/svgcheck/check/viewBox-width.svg.out +3 -0
- data/spec/fixtures/svgcheck/inputs/DrawBerry-sample-2.svg +28 -0
- data/spec/fixtures/svgcheck/inputs/IETF-test.svg +28 -0
- data/spec/fixtures/svgcheck/inputs/circle.svg +3 -0
- data/spec/fixtures/svgcheck/inputs/colors.svg +18 -0
- data/spec/fixtures/svgcheck/inputs/dia-sample-svg.svg +47 -0
- data/spec/fixtures/svgcheck/inputs/example-dot.svg +75 -0
- data/spec/fixtures/svgcheck/inputs/full-tiny.svg +16194 -0
- data/spec/fixtures/svgcheck/inputs/good.svg +19 -0
- data/spec/fixtures/svgcheck/inputs/httpbis-proxy20-fig6.svg +2 -0
- data/spec/fixtures/svgcheck/inputs/malformed.svg +11 -0
- data/spec/fixtures/svgcheck/inputs/rfc-svg.svg +1028 -0
- data/spec/fixtures/svgcheck/inputs/rfc.xml +37 -0
- data/spec/fixtures/svgcheck/inputs/rgb.svg +9 -0
- data/spec/fixtures/svgcheck/inputs/svg-wordle.svg +330 -0
- data/spec/fixtures/svgcheck/inputs/threshold.svg +26 -0
- data/spec/fixtures/svgcheck/inputs/utf8.svg +448 -0
- data/spec/fixtures/svgcheck/inputs/viewBox-both.svg +3 -0
- data/spec/fixtures/svgcheck/inputs/viewBox-height.svg +3 -0
- data/spec/fixtures/svgcheck/inputs/viewBox-none.svg +3 -0
- data/spec/fixtures/svgcheck/inputs/viewBox-width.svg +3 -0
- data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.file +0 -0
- data/spec/fixtures/svgcheck/repair/DrawBerry-sample-2.svg.out +23 -0
- data/spec/fixtures/svgcheck/repair/IETF-test.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/IETF-test.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/IETF-test.svg.file +29 -0
- data/spec/fixtures/svgcheck/repair/IETF-test.svg.out +20 -0
- data/spec/fixtures/svgcheck/repair/circle.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/circle.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/circle.svg.file +4 -0
- data/spec/fixtures/svgcheck/repair/circle.svg.out +2 -0
- data/spec/fixtures/svgcheck/repair/colors.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/colors.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/colors.svg.file +19 -0
- data/spec/fixtures/svgcheck/repair/colors.svg.out +13 -0
- data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.file +47 -0
- data/spec/fixtures/svgcheck/repair/dia-sample-svg.svg.out +76 -0
- data/spec/fixtures/svgcheck/repair/example-dot.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/example-dot.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/example-dot.svg.file +69 -0
- data/spec/fixtures/svgcheck/repair/example-dot.svg.out +11 -0
- data/spec/fixtures/svgcheck/repair/good.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/good.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/good.svg.file +0 -0
- data/spec/fixtures/svgcheck/repair/good.svg.out +1 -0
- data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.file +73 -0
- data/spec/fixtures/svgcheck/repair/httpbis-proxy20-fig6.svg.out +5 -0
- data/spec/fixtures/svgcheck/repair/malformed.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/malformed.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/malformed.svg.file +9 -0
- data/spec/fixtures/svgcheck/repair/malformed.svg.out +8 -0
- data/spec/fixtures/svgcheck/repair/rfc-svg.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/rfc-svg.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/rfc-svg.svg.file +0 -0
- data/spec/fixtures/svgcheck/repair/rfc-svg.svg.out +1 -0
- data/spec/fixtures/svgcheck/repair/rfc.xml.code +1 -0
- data/spec/fixtures/svgcheck/repair/rfc.xml.err +0 -0
- data/spec/fixtures/svgcheck/repair/rfc.xml.file +37 -0
- data/spec/fixtures/svgcheck/repair/rfc.xml.out +2 -0
- data/spec/fixtures/svgcheck/repair/rgb.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/rgb.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/rgb.svg.file +10 -0
- data/spec/fixtures/svgcheck/repair/rgb.svg.out +9 -0
- data/spec/fixtures/svgcheck/repair/svg-wordle.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/svg-wordle.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/svg-wordle.svg.file +112 -0
- data/spec/fixtures/svgcheck/repair/svg-wordle.svg.out +508 -0
- data/spec/fixtures/svgcheck/repair/threshold.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/threshold.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/threshold.svg.file +27 -0
- data/spec/fixtures/svgcheck/repair/threshold.svg.out +20 -0
- data/spec/fixtures/svgcheck/repair/utf8.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/utf8.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/utf8.svg.file +381 -0
- data/spec/fixtures/svgcheck/repair/utf8.svg.out +162 -0
- data/spec/fixtures/svgcheck/repair/viewBox-both.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/viewBox-both.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/viewBox-both.svg.file +4 -0
- data/spec/fixtures/svgcheck/repair/viewBox-both.svg.out +4 -0
- data/spec/fixtures/svgcheck/repair/viewBox-height.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/viewBox-height.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/viewBox-height.svg.file +4 -0
- data/spec/fixtures/svgcheck/repair/viewBox-height.svg.out +3 -0
- data/spec/fixtures/svgcheck/repair/viewBox-none.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/viewBox-none.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/viewBox-none.svg.file +4 -0
- data/spec/fixtures/svgcheck/repair/viewBox-none.svg.out +3 -0
- data/spec/fixtures/svgcheck/repair/viewBox-width.svg.code +1 -0
- data/spec/fixtures/svgcheck/repair/viewBox-width.svg.err +0 -0
- data/spec/fixtures/svgcheck/repair/viewBox-width.svg.file +4 -0
- data/spec/fixtures/svgcheck/repair/viewBox-width.svg.out +3 -0
- data/spec/fixtures/viewbox_required/inputs/missing_viewbox.svg +10 -0
- data/spec/fixtures/viewbox_required/repair/missing_viewbox.svg +10 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/svg_conform/batch_report_spec.rb +99 -0
- data/spec/svg_conform/commands/check_command_spec.rb +90 -0
- data/spec/svg_conform/commands/profiles_command_spec.rb +20 -0
- data/spec/svg_conform/commands/svgcheck_compare_command_spec.rb +13 -0
- data/spec/svg_conform/commands/svgcheck_compatibility_command_spec.rb +13 -0
- data/spec/svg_conform/commands/svgcheck_generate_command_spec.rb +14 -0
- data/spec/svg_conform/profiles/base_profile_spec.rb +42 -0
- data/spec/svg_conform/profiles/lucid_fix_profile_spec.rb +46 -0
- data/spec/svg_conform/profiles/lucid_profile_spec.rb +84 -0
- data/spec/svg_conform/profiles/metanorma_profile_spec.rb +62 -0
- data/spec/svg_conform/profiles/no_external_css_profile_spec.rb +66 -0
- data/spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb +200 -0
- data/spec/svg_conform/profiles/svg_1_2_rfc_with_rdf_profile_spec.rb +81 -0
- data/spec/svg_conform/remediations/color_remediation_spec.rb +95 -0
- data/spec/svg_conform/remediations/font_embedding_remediation_spec.rb +20 -0
- data/spec/svg_conform/remediations/font_remediation_spec.rb +95 -0
- data/spec/svg_conform/remediations/image_embedding_remediation_spec.rb +20 -0
- data/spec/svg_conform/remediations/invalid_id_references_remediation_spec.rb +97 -0
- data/spec/svg_conform/remediations/namespace_attribute_remediation_spec.rb +97 -0
- data/spec/svg_conform/remediations/namespace_remediation_spec.rb +95 -0
- data/spec/svg_conform/remediations/no_external_css_remediation_spec.rb +97 -0
- data/spec/svg_conform/remediations/style_promotion_remediation_spec.rb +97 -0
- data/spec/svg_conform/remediations/viewbox_remediation_spec.rb +95 -0
- data/spec/svg_conform/requirements/allowed_elements_requirement_spec.rb +118 -0
- data/spec/svg_conform/requirements/color_restrictions_requirement_spec.rb +168 -0
- data/spec/svg_conform/requirements/font_family_requirement_spec.rb +188 -0
- data/spec/svg_conform/requirements/forbidden_content_requirement_spec.rb +195 -0
- data/spec/svg_conform/requirements/id_reference_requirement_spec.rb +78 -0
- data/spec/svg_conform/requirements/invalid_id_references_requirement_spec.rb +78 -0
- data/spec/svg_conform/requirements/link_validation_requirement_spec.rb +78 -0
- data/spec/svg_conform/requirements/namespace_attributes_requirement_spec.rb +86 -0
- data/spec/svg_conform/requirements/namespace_requirement_spec.rb +184 -0
- data/spec/svg_conform/requirements/no_external_css_requirement_spec.rb +78 -0
- data/spec/svg_conform/requirements/no_external_fonts_requirement_spec.rb +20 -0
- data/spec/svg_conform/requirements/no_external_images_requirement_spec.rb +20 -0
- data/spec/svg_conform/requirements/style_promotion_requirement_spec.rb +78 -0
- data/spec/svg_conform/requirements/style_requirement_spec.rb +76 -0
- data/spec/svg_conform/requirements/viewbox_required_requirement_spec.rb +165 -0
- data/spec/svg_conform_spec.rb +32 -0
- data/spec/svgcheck_compatibility_spec.rb +355 -0
- data/svg_conform.gemspec +35 -0
- metadata +436 -0
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "table_tennis"
|
|
4
|
+
require "paint"
|
|
5
|
+
|
|
6
|
+
module SvgConform
|
|
7
|
+
# Compares SvgConform validation reports with svgcheck reports
|
|
8
|
+
# and displays the differences in a formatted table
|
|
9
|
+
class ReportComparator
|
|
10
|
+
def initialize(profile: :svg_1_2_rfc,
|
|
11
|
+
reports_dir: "spec/fixtures/svgcheck_reports")
|
|
12
|
+
@profile = profile
|
|
13
|
+
@reports_dir = reports_dir
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Compare two reports and display the results in a table
|
|
17
|
+
def compare_reports(svg_conform_report, svgcheck_report, filename)
|
|
18
|
+
puts "\n#{Paint['=' * 80, :cyan]}"
|
|
19
|
+
puts Paint["📊 REPORT COMPARISON: #{filename}", :blue, :bold]
|
|
20
|
+
puts Paint["=" * 80, :cyan]
|
|
21
|
+
|
|
22
|
+
# Summary table
|
|
23
|
+
display_summary_table(svg_conform_report, svgcheck_report)
|
|
24
|
+
|
|
25
|
+
# Error mapping table
|
|
26
|
+
display_error_mapping_table(svg_conform_report, svgcheck_report)
|
|
27
|
+
|
|
28
|
+
# Generate comparison result for requirement mapping
|
|
29
|
+
result = generate_comparison_result(svg_conform_report, svgcheck_report,
|
|
30
|
+
filename)
|
|
31
|
+
|
|
32
|
+
# Display requirement-based mapping
|
|
33
|
+
display_requirement_mapping(result)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Compare a single file and return structured results
|
|
37
|
+
def compare_single_file(filename)
|
|
38
|
+
validator = SvgConform::Validator.new
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
# Extract just the basename if a full path was provided
|
|
42
|
+
basename = File.basename(filename)
|
|
43
|
+
|
|
44
|
+
# Load svgcheck report
|
|
45
|
+
svgcheck_report_path = File.join(@reports_dir,
|
|
46
|
+
"#{basename}.svgcheck.yaml")
|
|
47
|
+
return nil unless File.exist?(svgcheck_report_path)
|
|
48
|
+
|
|
49
|
+
# Use the new external checker parser
|
|
50
|
+
parser = SvgConform::ExternalCheckers::Svgcheck::Parser.new
|
|
51
|
+
error_content = File.read(svgcheck_report_path)
|
|
52
|
+
svgcheck_report = parser.parse(error_content, nil, filename: basename)
|
|
53
|
+
|
|
54
|
+
# Generate SvgConform report - use the original filename for the SVG file path
|
|
55
|
+
svg_file_path = filename.start_with?("svgcheck/") ? filename : "svgcheck/svgcheck/Tests/#{basename}"
|
|
56
|
+
return nil unless File.exist?(svg_file_path)
|
|
57
|
+
|
|
58
|
+
svg_content = File.read(svg_file_path)
|
|
59
|
+
validation_result = validator.validate(svg_content, profile: @profile)
|
|
60
|
+
svg_conform_report = SvgConform::ConformanceReport.from_svg_conform_result(basename, validation_result,
|
|
61
|
+
profile: @profile, use_svgcheck_mapping: true)
|
|
62
|
+
|
|
63
|
+
# Generate structured comparison result
|
|
64
|
+
generate_comparison_result(svg_conform_report, svgcheck_report,
|
|
65
|
+
basename)
|
|
66
|
+
rescue StandardError => e
|
|
67
|
+
puts "❌ Error processing #{filename}: #{e.message}"
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Display single file comparison result using the table formatting
|
|
73
|
+
def display_single_file_result(result)
|
|
74
|
+
return unless result
|
|
75
|
+
|
|
76
|
+
filename = result[:filename]
|
|
77
|
+
|
|
78
|
+
puts "\n#{Paint['=' * 80, :cyan]}"
|
|
79
|
+
puts Paint["📊 REPORT COMPARISON: #{filename}", :blue, :bold]
|
|
80
|
+
puts Paint["=" * 80, :cyan]
|
|
81
|
+
|
|
82
|
+
# Summary table
|
|
83
|
+
headers = ["Metric", "SvgConform", "svgcheck", "Match?"]
|
|
84
|
+
rows = [
|
|
85
|
+
["Valid", result[:svg_conform_valid], result[:svgcheck_valid],
|
|
86
|
+
result[:valid_match] ? "✓" : "✗"],
|
|
87
|
+
["Total Errors", result[:svg_conform_errors], result[:svgcheck_errors],
|
|
88
|
+
result[:errors_match] ? "✓" : "✗"],
|
|
89
|
+
["Error Types", result[:svg_conform_error_types], result[:svgcheck_error_types],
|
|
90
|
+
result[:error_types_match] ? "✓" : "✗"],
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
puts "\nSUMMARY:"
|
|
94
|
+
render_gh_style_table(headers, rows)
|
|
95
|
+
|
|
96
|
+
# Semantic error mapping table
|
|
97
|
+
if result[:semantic_mapping]&.any?
|
|
98
|
+
headers = ["Error Type", "SvgConform Count", "svgcheck Count", "Status"]
|
|
99
|
+
rows = []
|
|
100
|
+
|
|
101
|
+
result[:semantic_mapping].each do |error_type, counts|
|
|
102
|
+
svg_count = counts[:svg_conform]
|
|
103
|
+
svgcheck_count = counts[:svgcheck]
|
|
104
|
+
status = if svg_count == svgcheck_count && svg_count.positive?
|
|
105
|
+
"✓ MATCH"
|
|
106
|
+
elsif svg_count == svgcheck_count && svg_count.zero?
|
|
107
|
+
"✓ BOTH NONE"
|
|
108
|
+
elsif svg_count > svgcheck_count
|
|
109
|
+
"⚠ MISMATCH"
|
|
110
|
+
elsif svg_count < svgcheck_count
|
|
111
|
+
"⚠ MISMATCH"
|
|
112
|
+
else
|
|
113
|
+
"? UNKNOWN"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
rows << [error_type, svg_count, svgcheck_count, status]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Determine overall coverage status
|
|
120
|
+
coverage_status = determine_coverage_status(result[:semantic_mapping])
|
|
121
|
+
|
|
122
|
+
puts "\nSEMANTIC ERROR MAPPING: #{coverage_status}"
|
|
123
|
+
render_gh_style_table(headers, rows)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Display requirement-based mapping
|
|
127
|
+
display_requirement_mapping(result)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Compare all test files and generate a comprehensive report
|
|
131
|
+
def compare_all_test_files
|
|
132
|
+
puts "\n#{Paint['=' * 100, :cyan]}"
|
|
133
|
+
puts Paint["🔍 COMPREHENSIVE SVGCHECK COMPATIBILITY ANALYSIS", :magenta,
|
|
134
|
+
:bold]
|
|
135
|
+
puts Paint["=" * 100, :cyan]
|
|
136
|
+
|
|
137
|
+
test_files = Dir.glob(File.join(@reports_dir,
|
|
138
|
+
"*.svgcheck.yaml")).map do |report_file|
|
|
139
|
+
File.basename(report_file, ".svgcheck.yaml")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
results = []
|
|
143
|
+
validator = SvgConform::Validator.new
|
|
144
|
+
|
|
145
|
+
test_files.each do |filename|
|
|
146
|
+
next if filename == "rfc.xml" # Skip non-SVG files
|
|
147
|
+
|
|
148
|
+
begin
|
|
149
|
+
# Load svgcheck report
|
|
150
|
+
svgcheck_report_path = File.join(@reports_dir,
|
|
151
|
+
"#{filename}.svgcheck.yaml")
|
|
152
|
+
# Use the new external checker parser
|
|
153
|
+
parser = SvgConform::ExternalCheckers::Svgcheck::Parser.new
|
|
154
|
+
error_content = File.read(svgcheck_report_path)
|
|
155
|
+
svgcheck_report = parser.parse(error_content, nil, filename: filename)
|
|
156
|
+
|
|
157
|
+
# Generate SvgConform report
|
|
158
|
+
svg_file_path = "svgcheck/svgcheck/Tests/#{filename}"
|
|
159
|
+
if File.exist?(svg_file_path)
|
|
160
|
+
svg_content = File.read(svg_file_path)
|
|
161
|
+
validation_result = validator.validate(svg_content,
|
|
162
|
+
profile: @profile)
|
|
163
|
+
svg_conform_report = SvgConform::ConformanceReport.from_svg_conform_result(filename, validation_result,
|
|
164
|
+
profile: @profile, use_svgcheck_mapping: true)
|
|
165
|
+
|
|
166
|
+
# Compare reports
|
|
167
|
+
compare_reports(svg_conform_report, svgcheck_report, filename)
|
|
168
|
+
|
|
169
|
+
# Track results
|
|
170
|
+
results << {
|
|
171
|
+
filename: filename,
|
|
172
|
+
svg_conform_errors: svg_conform_report.errors.total_count,
|
|
173
|
+
svgcheck_errors: svgcheck_report.errors.total_count,
|
|
174
|
+
match: svg_conform_report.errors.total_count == svgcheck_report.errors.total_count,
|
|
175
|
+
}
|
|
176
|
+
else
|
|
177
|
+
puts "\n⚠ SVG file not found: #{svg_file_path}"
|
|
178
|
+
end
|
|
179
|
+
rescue StandardError => e
|
|
180
|
+
puts "\n❌ Error processing #{filename}: #{e.message}"
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Overall summary
|
|
185
|
+
display_overall_summary(results)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
private
|
|
189
|
+
|
|
190
|
+
def generate_comparison_result(svg_conform_report, svgcheck_report,
|
|
191
|
+
filename)
|
|
192
|
+
# Group errors by semantic meaning for comparison
|
|
193
|
+
svg_conform_errors = group_errors_by_semantic_meaning(svg_conform_report.errors.issues)
|
|
194
|
+
svgcheck_errors = group_errors_by_semantic_meaning(svgcheck_report.errors.issues)
|
|
195
|
+
|
|
196
|
+
# Create semantic mapping
|
|
197
|
+
all_error_types = (svg_conform_errors.keys + svgcheck_errors.keys).uniq.sort
|
|
198
|
+
semantic_mapping = {}
|
|
199
|
+
|
|
200
|
+
all_error_types.each do |error_type|
|
|
201
|
+
svg_count = svg_conform_errors[error_type] || 0
|
|
202
|
+
svgcheck_count = svgcheck_errors[error_type] || 0
|
|
203
|
+
semantic_mapping[error_type] = {
|
|
204
|
+
svg_conform: svg_count,
|
|
205
|
+
svgcheck: svgcheck_count,
|
|
206
|
+
}
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Find message-level differences
|
|
210
|
+
svg_conform_messages = group_errors_by_message(svg_conform_report.errors.issues)
|
|
211
|
+
svgcheck_messages = group_errors_by_message(svgcheck_report.errors.issues)
|
|
212
|
+
|
|
213
|
+
extra_errors = []
|
|
214
|
+
missing_errors = []
|
|
215
|
+
|
|
216
|
+
svg_conform_messages.each do |message, count|
|
|
217
|
+
svgcheck_count = svgcheck_messages[message] || 0
|
|
218
|
+
extra_errors << message if count > svgcheck_count
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
svgcheck_messages.each do |message, count|
|
|
222
|
+
svg_conform_count = svg_conform_messages[message] || 0
|
|
223
|
+
missing_errors << message if count > svg_conform_count
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
{
|
|
227
|
+
filename: filename,
|
|
228
|
+
svg_conform_valid: svg_conform_report.valid,
|
|
229
|
+
svgcheck_valid: svgcheck_report.valid,
|
|
230
|
+
valid_match: svg_conform_report.valid == svgcheck_report.valid,
|
|
231
|
+
svg_conform_errors: svg_conform_report.errors.total_count,
|
|
232
|
+
svgcheck_errors: svgcheck_report.errors.total_count,
|
|
233
|
+
errors_match: svg_conform_report.errors.total_count == svgcheck_report.errors.total_count,
|
|
234
|
+
svg_conform_error_types: svg_conform_report.errors.by_requirement.keys.length,
|
|
235
|
+
svgcheck_error_types: svgcheck_report.errors.by_requirement.keys.length,
|
|
236
|
+
error_types_match: svg_conform_report.errors.by_requirement.keys.length == svgcheck_report.errors.by_requirement.keys.length,
|
|
237
|
+
semantic_mapping: semantic_mapping,
|
|
238
|
+
extra_errors: extra_errors,
|
|
239
|
+
missing_errors: missing_errors,
|
|
240
|
+
}
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def display_summary_table(svg_conform_report, svgcheck_report)
|
|
244
|
+
headers = ["Metric", "SvgConform", "svgcheck", "Match?"]
|
|
245
|
+
rows = [
|
|
246
|
+
["Valid", svg_conform_report.valid, svgcheck_report.valid,
|
|
247
|
+
match_status(svg_conform_report.valid, svgcheck_report.valid)],
|
|
248
|
+
["Total Errors", svg_conform_report.errors.total_count, svgcheck_report.errors.total_count,
|
|
249
|
+
match_status(svg_conform_report.errors.total_count, svgcheck_report.errors.total_count)],
|
|
250
|
+
["Error Types", svg_conform_report.errors.by_requirement.keys.length,
|
|
251
|
+
svgcheck_report.errors.by_requirement.keys.length, match_status(svg_conform_report.errors.by_requirement.keys.length, svgcheck_report.errors.by_requirement.keys.length)],
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
puts "\nSUMMARY:"
|
|
255
|
+
render_gh_style_table(headers, rows)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def display_error_mapping_table(svg_conform_report, svgcheck_report)
|
|
259
|
+
# Group errors by semantic meaning for comparison
|
|
260
|
+
svg_conform_errors = group_errors_by_semantic_meaning(svg_conform_report.errors.issues)
|
|
261
|
+
svgcheck_errors = group_errors_by_semantic_meaning(svgcheck_report.errors.issues)
|
|
262
|
+
|
|
263
|
+
# Create mapping table
|
|
264
|
+
headers = ["Error Type", "SvgConform Count", "svgcheck Count", "Status"]
|
|
265
|
+
rows = []
|
|
266
|
+
|
|
267
|
+
# All unique error types
|
|
268
|
+
all_error_types = (svg_conform_errors.keys + svgcheck_errors.keys).uniq.sort
|
|
269
|
+
|
|
270
|
+
all_error_types.each do |error_type|
|
|
271
|
+
svg_count = svg_conform_errors[error_type] || 0
|
|
272
|
+
svgcheck_count = svgcheck_errors[error_type] || 0
|
|
273
|
+
status = if svg_count == svgcheck_count && svg_count.positive?
|
|
274
|
+
"✓ MATCH"
|
|
275
|
+
elsif svg_count == svgcheck_count && svg_count.zero?
|
|
276
|
+
"✓ BOTH NONE"
|
|
277
|
+
elsif svg_count > svgcheck_count
|
|
278
|
+
"⚠ EXTRA"
|
|
279
|
+
elsif svg_count < svgcheck_count
|
|
280
|
+
"⚠ MISSING"
|
|
281
|
+
else
|
|
282
|
+
"? UNKNOWN"
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
rows << [error_type, svg_count, svgcheck_count, status]
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
puts "\nSEMANTIC ERROR MAPPING:"
|
|
289
|
+
render_gh_style_table(headers, rows)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def group_errors_by_message(errors)
|
|
293
|
+
errors.group_by(&:message).transform_values(&:length)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Group errors by their semantic meaning rather than exact message text
|
|
297
|
+
def group_errors_by_semantic_meaning(errors)
|
|
298
|
+
errors.group_by do |error|
|
|
299
|
+
extract_semantic_meaning(error)
|
|
300
|
+
end.transform_values(&:length)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Extract semantic meaning from an error for comparison
|
|
304
|
+
# Uses the configuration-driven approach from SvgcheckCompatibilityEngine
|
|
305
|
+
def extract_semantic_meaning(error)
|
|
306
|
+
# Use the compatibility engine for consistent semantic extraction
|
|
307
|
+
engine = SvgConform::ExternalCheckers::Svgcheck::CompatibilityEngine.new
|
|
308
|
+
engine.extract_semantic_meaning(error)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Helper methods to extract semantic components from error messages
|
|
312
|
+
def extract_attribute_from_message(message)
|
|
313
|
+
# Extract attribute name from various message formats
|
|
314
|
+
case message
|
|
315
|
+
when /attribute ['"]([^'"]+)['"]/
|
|
316
|
+
::Regexp.last_match(1)
|
|
317
|
+
when /in attribute ['"]([^'"]+)['"]/
|
|
318
|
+
::Regexp.last_match(1)
|
|
319
|
+
when /property ['"]([^'"]+)['"]/
|
|
320
|
+
::Regexp.last_match(1)
|
|
321
|
+
else
|
|
322
|
+
"unknown"
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def extract_value_from_message(message)
|
|
327
|
+
# Extract value from various message formats
|
|
328
|
+
if message =~ /value ['"]([^'"]+)['"]/
|
|
329
|
+
::Regexp.last_match(1)
|
|
330
|
+
elsif message =~ /Color ['"]([^'"]+)['"]/
|
|
331
|
+
::Regexp.last_match(1)
|
|
332
|
+
else
|
|
333
|
+
"unknown"
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def extract_element_from_message(message)
|
|
338
|
+
# Extract element name from various message formats
|
|
339
|
+
if message =~ /element ['"]([^'"]+)['"]/
|
|
340
|
+
::Regexp.last_match(1)
|
|
341
|
+
elsif message =~ /Element ['"]([^'"]+)['"]/
|
|
342
|
+
::Regexp.last_match(1)
|
|
343
|
+
else
|
|
344
|
+
"unknown"
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def extract_element_from_context(message)
|
|
349
|
+
# Extract element name from context like "not allowed on element 'g'"
|
|
350
|
+
if message =~ /on element ['"]([^'"]+)['"]/
|
|
351
|
+
::Regexp.last_match(1)
|
|
352
|
+
elsif message =~ /element ['"]([^'"]+)['"]/
|
|
353
|
+
::Regexp.last_match(1)
|
|
354
|
+
else
|
|
355
|
+
"unknown"
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def extract_style_property_from_message(message)
|
|
360
|
+
# Extract style property name from style promotion messages
|
|
361
|
+
if message =~ /Style property ['"]([^'"]+)['"]/
|
|
362
|
+
::Regexp.last_match(1)
|
|
363
|
+
else
|
|
364
|
+
"unknown"
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def match_status(value1, value2)
|
|
369
|
+
value1 == value2 ? "✓" : "✗"
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Render table using table_tennis with proper formatting and color scale
|
|
373
|
+
def render_gh_style_table(headers, rows)
|
|
374
|
+
# Convert to hash format that TableTennis expects
|
|
375
|
+
table_data = []
|
|
376
|
+
|
|
377
|
+
rows.each do |row|
|
|
378
|
+
row_hash = {}
|
|
379
|
+
headers.each_with_index do |header, index|
|
|
380
|
+
row_hash[header.to_sym] = row[index]
|
|
381
|
+
end
|
|
382
|
+
table_data << row_hash
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Determine color scale options based on table content
|
|
386
|
+
color_scale_options = determine_color_scale_options(headers, rows)
|
|
387
|
+
|
|
388
|
+
# Create TableTennis table with options
|
|
389
|
+
options = {
|
|
390
|
+
color: true,
|
|
391
|
+
separators: true,
|
|
392
|
+
**color_scale_options,
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
puts TableTennis.new(table_data, options)
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Determine appropriate color scale options for TableTennis
|
|
399
|
+
def determine_color_scale_options(headers, _rows)
|
|
400
|
+
# Analyze the table content to determine the best color scale
|
|
401
|
+
if headers.include?("Match?") || headers.include?("Status")
|
|
402
|
+
# For status tables, use mark to highlight matches/mismatches
|
|
403
|
+
{
|
|
404
|
+
mark: lambda { |row|
|
|
405
|
+
status_col = headers.index("Match?") || headers.index("Status")
|
|
406
|
+
return false unless status_col
|
|
407
|
+
|
|
408
|
+
status_value = row[status_col].to_s
|
|
409
|
+
status_value.include?("✓") || status_value.include?("MATCH")
|
|
410
|
+
},
|
|
411
|
+
zebra: true,
|
|
412
|
+
}
|
|
413
|
+
elsif headers.any? { |h| h.include?("Count") || h.include?("Errors") }
|
|
414
|
+
# For numeric tables, use color scales on count columns
|
|
415
|
+
count_columns = headers.each_with_index.select do |h, _i|
|
|
416
|
+
h.include?("Count") || h.include?("Errors")
|
|
417
|
+
end.map(&:last)
|
|
418
|
+
if count_columns.any?
|
|
419
|
+
# Use the first count column for color scaling
|
|
420
|
+
column_name = headers[count_columns.first].downcase.gsub(/\s+/,
|
|
421
|
+
"_").to_sym
|
|
422
|
+
{
|
|
423
|
+
color_scales: column_name,
|
|
424
|
+
zebra: true,
|
|
425
|
+
}
|
|
426
|
+
else
|
|
427
|
+
{ zebra: true }
|
|
428
|
+
end
|
|
429
|
+
else
|
|
430
|
+
# Default options
|
|
431
|
+
{ zebra: true }
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
# Apply color scale to individual cells
|
|
436
|
+
def apply_color_scale(cell, col_index, _row_index, color_scale)
|
|
437
|
+
cell_str = cell.to_s
|
|
438
|
+
|
|
439
|
+
case color_scale[:type]
|
|
440
|
+
when :status
|
|
441
|
+
apply_status_color_scale(cell_str)
|
|
442
|
+
when :numeric
|
|
443
|
+
apply_numeric_color_scale(cell_str, col_index)
|
|
444
|
+
else
|
|
445
|
+
apply_default_color_scale(cell_str)
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Apply status-based color scale
|
|
450
|
+
def apply_status_color_scale(cell_str)
|
|
451
|
+
if cell_str.include?("✓") || cell_str.include?("MATCH")
|
|
452
|
+
Paint[cell_str, :green]
|
|
453
|
+
elsif cell_str.include?("✗") || cell_str.include?("MISMATCH")
|
|
454
|
+
Paint[cell_str, :red]
|
|
455
|
+
elsif cell_str.include?("⚠") || cell_str.include?("EXTRA") || cell_str.include?("MISSING")
|
|
456
|
+
Paint[cell_str, :yellow]
|
|
457
|
+
elsif cell_str.include?("BOTH NONE")
|
|
458
|
+
Paint[cell_str, :cyan]
|
|
459
|
+
else
|
|
460
|
+
cell_str
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# Apply numeric-based color scale
|
|
465
|
+
def apply_numeric_color_scale(cell_str, _col_index)
|
|
466
|
+
# Only apply to numeric columns
|
|
467
|
+
return cell_str unless cell_str.match?(/^\d+$/)
|
|
468
|
+
|
|
469
|
+
value = cell_str.to_i
|
|
470
|
+
|
|
471
|
+
if value.zero?
|
|
472
|
+
Paint[cell_str, :cyan]
|
|
473
|
+
elsif value <= 5
|
|
474
|
+
Paint[cell_str, :green]
|
|
475
|
+
elsif value <= 20
|
|
476
|
+
Paint[cell_str, :yellow]
|
|
477
|
+
else
|
|
478
|
+
Paint[cell_str, :red]
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
# Apply default color scale
|
|
483
|
+
def apply_default_color_scale(cell_str)
|
|
484
|
+
# Basic highlighting for common patterns
|
|
485
|
+
if cell_str.include?("true") || cell_str.include?("false")
|
|
486
|
+
if cell_str.include?("true")
|
|
487
|
+
Paint[cell_str,
|
|
488
|
+
:green]
|
|
489
|
+
else
|
|
490
|
+
Paint[cell_str, :red]
|
|
491
|
+
end
|
|
492
|
+
elsif cell_str.match?(/^\d+%$/) # Percentage
|
|
493
|
+
percentage = cell_str.to_f
|
|
494
|
+
if percentage >= 90
|
|
495
|
+
Paint[cell_str, :green]
|
|
496
|
+
elsif percentage >= 70
|
|
497
|
+
Paint[cell_str, :yellow]
|
|
498
|
+
else
|
|
499
|
+
Paint[cell_str, :red]
|
|
500
|
+
end
|
|
501
|
+
else
|
|
502
|
+
cell_str
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# Display requirement-based error mapping with colors
|
|
507
|
+
def display_requirement_mapping(result)
|
|
508
|
+
# Get the original reports to access detailed error information
|
|
509
|
+
filename = result[:filename]
|
|
510
|
+
|
|
511
|
+
begin
|
|
512
|
+
# Load reports again to get detailed error information
|
|
513
|
+
svgcheck_report_path = File.join(@reports_dir,
|
|
514
|
+
"#{filename}.svgcheck.yaml")
|
|
515
|
+
# Use the new external checker parser
|
|
516
|
+
parser = SvgConform::ExternalCheckers::Svgcheck::Parser.new
|
|
517
|
+
error_content = File.read(svgcheck_report_path)
|
|
518
|
+
svgcheck_report = parser.parse(error_content, nil, filename: filename)
|
|
519
|
+
|
|
520
|
+
# Generate SvgConform report
|
|
521
|
+
validator = SvgConform::Validator.new
|
|
522
|
+
svg_file_path = "svgcheck/svgcheck/Tests/#{filename}"
|
|
523
|
+
svg_content = File.read(svg_file_path)
|
|
524
|
+
validation_result = validator.validate(svg_content, profile: @profile)
|
|
525
|
+
svg_conform_report = SvgConform::ConformanceReport.from_svg_conform_result(filename, validation_result,
|
|
526
|
+
profile: @profile, use_svgcheck_mapping: true)
|
|
527
|
+
|
|
528
|
+
# Group errors by requirement
|
|
529
|
+
svg_conform_by_requirement = group_errors_by_requirement(svg_conform_report.errors.issues)
|
|
530
|
+
svgcheck_by_requirement = group_errors_by_requirement(svgcheck_report.errors.issues)
|
|
531
|
+
|
|
532
|
+
# Identify unmapped svgcheck errors
|
|
533
|
+
unmapped_svgcheck_errors = identify_unmapped_svgcheck_errors(
|
|
534
|
+
svgcheck_report, svg_conform_report
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
# Get all requirements that have errors in either system
|
|
538
|
+
all_requirements = (svg_conform_by_requirement.keys + svgcheck_by_requirement.keys).uniq.sort
|
|
539
|
+
|
|
540
|
+
if all_requirements.any?
|
|
541
|
+
puts "\n#{Paint['🔍 REQUIREMENT MAPPING:', :blue, :bold]}"
|
|
542
|
+
|
|
543
|
+
all_requirements.each do |requirement|
|
|
544
|
+
svg_errors = svg_conform_by_requirement[requirement] || []
|
|
545
|
+
svgcheck_errors = svgcheck_by_requirement[requirement] || []
|
|
546
|
+
|
|
547
|
+
# Display requirement header
|
|
548
|
+
requirement_name = format_requirement_name(requirement)
|
|
549
|
+
puts "\n #{Paint["🔍 #{requirement_name}:", :cyan, :bold]}"
|
|
550
|
+
|
|
551
|
+
# Display SvgConform errors
|
|
552
|
+
if svg_errors.any?
|
|
553
|
+
svg_errors.each do |error|
|
|
554
|
+
puts " #{Paint['•', :blue]} #{Paint[error.message, :blue]}"
|
|
555
|
+
end
|
|
556
|
+
else
|
|
557
|
+
puts " #{Paint['• (no errors)', :gray]}"
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# Display mapped svgcheck errors
|
|
561
|
+
if svgcheck_errors.any?
|
|
562
|
+
svgcheck_errors.each do |error|
|
|
563
|
+
puts " #{Paint['↳ [svgcheck]',
|
|
564
|
+
:magenta]} #{Paint[error.message, :magenta]}"
|
|
565
|
+
end
|
|
566
|
+
else
|
|
567
|
+
puts " #{Paint['↳ [svgcheck] (no errors)', :gray]}"
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
else
|
|
571
|
+
puts "\n#{Paint['🎉 ✓ NO ERRORS IN EITHER SYSTEM! 🎉', :green, :bold]}"
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# Display unmapped svgcheck errors in RED
|
|
575
|
+
if unmapped_svgcheck_errors.any?
|
|
576
|
+
puts "\n#{Paint['🚨 UNMAPPED SVGCHECK ERRORS:', :red, :bold]}"
|
|
577
|
+
puts Paint["These svgcheck errors do not correspond to any SvgConform requirement:",
|
|
578
|
+
:red]
|
|
579
|
+
unmapped_svgcheck_errors.each do |error|
|
|
580
|
+
puts " #{Paint['⚠️ [svgcheck]', :red,
|
|
581
|
+
:bold]} #{Paint[error.message, :red]}"
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
rescue StandardError
|
|
585
|
+
# Fallback to the old display if we can't load detailed reports
|
|
586
|
+
if result[:extra_errors]&.any?
|
|
587
|
+
puts "\n#{Paint['⚠️ EXTRA ERRORS IN SVGCONFORM:', :yellow, :bold]}"
|
|
588
|
+
result[:extra_errors].each do |error|
|
|
589
|
+
puts " • #{error}"
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
if result[:missing_errors]&.any?
|
|
594
|
+
puts "\n#{Paint['❌ MISSING ERRORS IN SVGCONFORM:', :red, :bold]}"
|
|
595
|
+
result[:missing_errors].each do |error|
|
|
596
|
+
puts " • #{error}"
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
return unless result[:extra_errors]&.empty? && result[:missing_errors]&.empty?
|
|
601
|
+
|
|
602
|
+
puts "\n#{Paint['🎉 ✓ ERROR MESSAGES MATCH PERFECTLY! 🎉', :green,
|
|
603
|
+
:bold]}"
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
# Group errors by requirement ID, using compatibility engine for svgcheck errors
|
|
608
|
+
def group_errors_by_requirement(errors)
|
|
609
|
+
# Check if these are svgcheck errors that need remapping
|
|
610
|
+
engine = SvgConform::ExternalCheckers::Svgcheck::CompatibilityEngine.new
|
|
611
|
+
|
|
612
|
+
grouped = {}
|
|
613
|
+
errors.each do |error|
|
|
614
|
+
# For svgcheck errors, try to remap using the compatibility engine
|
|
615
|
+
requirement_id = if error.respond_to?(:requirement_id)
|
|
616
|
+
# Try to remap using the compatibility engine for all svgcheck errors
|
|
617
|
+
mapped_req = engine.map_requirement_for_svgcheck(error)
|
|
618
|
+
mapped_req || error.requirement_id
|
|
619
|
+
else
|
|
620
|
+
error.requirement_id
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
grouped[requirement_id] ||= []
|
|
624
|
+
grouped[requirement_id] << error
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
grouped
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
# Identify svgcheck errors that don't map to any SvgConform requirement
|
|
631
|
+
def identify_unmapped_svgcheck_errors(svgcheck_report, _svg_conform_report)
|
|
632
|
+
unmapped_errors = []
|
|
633
|
+
|
|
634
|
+
# Find svgcheck errors that are marked as "unmapped" by the parser
|
|
635
|
+
svgcheck_report.errors.issues.each do |svgcheck_error|
|
|
636
|
+
# Check if this error was categorized as "unmapped" by the parser
|
|
637
|
+
unmapped_errors << svgcheck_error if svgcheck_error.requirement_id == "unmapped"
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
unmapped_errors
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
# Format requirement name for display
|
|
644
|
+
def format_requirement_name(requirement_id)
|
|
645
|
+
case requirement_id
|
|
646
|
+
when "color_restrictions"
|
|
647
|
+
"color_restrictions requirement"
|
|
648
|
+
when "viewbox_required"
|
|
649
|
+
"viewbox_required requirement"
|
|
650
|
+
when "viewbox"
|
|
651
|
+
"viewbox requirement"
|
|
652
|
+
when "namespace"
|
|
653
|
+
"namespace requirement"
|
|
654
|
+
when "namespace_validation"
|
|
655
|
+
"namespace_validation requirement"
|
|
656
|
+
when "allowed_elements"
|
|
657
|
+
"allowed_elements requirement"
|
|
658
|
+
when "font_family"
|
|
659
|
+
"font_family requirement"
|
|
660
|
+
when "no_external_css"
|
|
661
|
+
"no_external_css requirement"
|
|
662
|
+
when "namespace_attributes"
|
|
663
|
+
"namespace_attributes requirement"
|
|
664
|
+
when "style_promotion"
|
|
665
|
+
"style_promotion requirement"
|
|
666
|
+
when "invalid_id_references"
|
|
667
|
+
"invalid_id_references requirement"
|
|
668
|
+
when "forbidden_content"
|
|
669
|
+
"forbidden_content requirement"
|
|
670
|
+
when "property_value"
|
|
671
|
+
"property_value requirement"
|
|
672
|
+
when "link_validation"
|
|
673
|
+
"link_validation requirement"
|
|
674
|
+
when "id_reference"
|
|
675
|
+
"id_reference requirement"
|
|
676
|
+
when "style_syntax"
|
|
677
|
+
"style_syntax requirement"
|
|
678
|
+
when "style_validation"
|
|
679
|
+
"style_validation requirement"
|
|
680
|
+
when "datatype_validation"
|
|
681
|
+
"datatype_validation requirement"
|
|
682
|
+
when "required_attribute"
|
|
683
|
+
"required_attribute requirement"
|
|
684
|
+
when "unmapped"
|
|
685
|
+
"🚨 UNMAPPED SVGCHECK ERROR"
|
|
686
|
+
else
|
|
687
|
+
"🚨 UNKNOWN REQUIREMENT: #{requirement_id}"
|
|
688
|
+
end
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
# Determine coverage status based on semantic mapping
|
|
692
|
+
def determine_coverage_status(semantic_mapping)
|
|
693
|
+
return "🤷 NO DATA" unless semantic_mapping&.any?
|
|
694
|
+
|
|
695
|
+
svg_conform_errors = semantic_mapping.values.sum do |counts|
|
|
696
|
+
counts[:svg_conform]
|
|
697
|
+
end
|
|
698
|
+
svgcheck_errors = semantic_mapping.values.sum do |counts|
|
|
699
|
+
counts[:svgcheck]
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
# Check if SvgConform covers all svgcheck errors
|
|
703
|
+
all_svgcheck_covered = semantic_mapping.all? do |_error_type, counts|
|
|
704
|
+
svg_count = counts[:svg_conform]
|
|
705
|
+
svgcheck_count = counts[:svgcheck]
|
|
706
|
+
|
|
707
|
+
# SvgConform covers this error type if it has at least as many errors as svgcheck
|
|
708
|
+
svg_count >= svgcheck_count
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
# Check if it's a perfect match (100% both ways)
|
|
712
|
+
perfect_match = semantic_mapping.all? do |_error_type, counts|
|
|
713
|
+
counts[:svg_conform] == counts[:svgcheck]
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
if perfect_match && svg_conform_errors.positive? && svgcheck_errors.positive?
|
|
717
|
+
"🎯 FULL MATCH"
|
|
718
|
+
elsif all_svgcheck_covered && svgcheck_errors.positive?
|
|
719
|
+
"✅ COVERED"
|
|
720
|
+
else
|
|
721
|
+
"⚠️ MISMATCH"
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
def display_overall_summary(results)
|
|
726
|
+
puts "\n#{Paint['=' * 100, :cyan]}"
|
|
727
|
+
puts Paint["📈 OVERALL COMPATIBILITY SUMMARY", :yellow, :bold]
|
|
728
|
+
puts Paint["=" * 100, :cyan]
|
|
729
|
+
|
|
730
|
+
headers = ["File", "SvgConform Errors", "svgcheck Errors", "Match?"]
|
|
731
|
+
rows = []
|
|
732
|
+
|
|
733
|
+
results.each do |result|
|
|
734
|
+
status = result[:match] ? "✓" : "✗"
|
|
735
|
+
rows << [
|
|
736
|
+
result[:filename],
|
|
737
|
+
result[:svg_conform_errors],
|
|
738
|
+
result[:svgcheck_errors],
|
|
739
|
+
status,
|
|
740
|
+
]
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
# Add totals
|
|
744
|
+
total_files = results.length
|
|
745
|
+
matching_files = results.count { |r| r[:match] }
|
|
746
|
+
match_percentage = total_files.positive? ? (matching_files.to_f / total_files * 100).round(1) : 0
|
|
747
|
+
|
|
748
|
+
rows << ["---", "---", "---", "---"]
|
|
749
|
+
rows << ["TOTAL", "#{total_files} files", "#{matching_files} matches",
|
|
750
|
+
"#{match_percentage}%"]
|
|
751
|
+
|
|
752
|
+
render_gh_style_table(headers, rows)
|
|
753
|
+
|
|
754
|
+
puts "\n#{Paint['📊 COMPATIBILITY METRICS:', :blue, :bold]}"
|
|
755
|
+
puts " 🗂️ Total files processed: #{Paint[total_files.to_s, :cyan,
|
|
756
|
+
:bold]}"
|
|
757
|
+
puts " ✅ Files with matching error counts: #{Paint[matching_files.to_s,
|
|
758
|
+
:green, :bold]}"
|
|
759
|
+
|
|
760
|
+
# Color-code the percentage based on value
|
|
761
|
+
percentage_color = if match_percentage >= 90
|
|
762
|
+
:green
|
|
763
|
+
elsif match_percentage >= 70
|
|
764
|
+
:yellow
|
|
765
|
+
else
|
|
766
|
+
:red
|
|
767
|
+
end
|
|
768
|
+
puts " 📈 Compatibility percentage: #{Paint["#{match_percentage}%",
|
|
769
|
+
percentage_color, :bold]}"
|
|
770
|
+
end
|
|
771
|
+
end
|
|
772
|
+
end
|