fritzkit 0.0.0-stage → 0.1.1
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.
- package/.claude-plugin/marketplace.json +12 -0
- package/.claude-plugin/plugin.json +13 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +429 -2
- package/dist/cli/commands/doctor.d.ts +4 -0
- package/dist/cli/commands/doctor.js +7 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/export.d.ts +17 -0
- package/dist/cli/commands/export.js +81 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/kicad.d.ts +10 -0
- package/dist/cli/commands/kicad.js +22 -0
- package/dist/cli/commands/kicad.js.map +1 -0
- package/dist/cli/commands/parts.d.ts +22 -0
- package/dist/cli/commands/parts.js +46 -0
- package/dist/cli/commands/parts.js.map +1 -0
- package/dist/cli/commands/prune.d.ts +14 -0
- package/dist/cli/commands/prune.js +67 -0
- package/dist/cli/commands/prune.js.map +1 -0
- package/dist/cli/commands/render.d.ts +24 -0
- package/dist/cli/commands/render.js +62 -0
- package/dist/cli/commands/render.js.map +1 -0
- package/dist/cli/commands/schema.d.ts +10 -0
- package/dist/cli/commands/schema.js +35 -0
- package/dist/cli/commands/schema.js.map +1 -0
- package/dist/cli/commands/ui.d.ts +14 -0
- package/dist/cli/commands/ui.js +67 -0
- package/dist/cli/commands/ui.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +24 -0
- package/dist/cli/commands/validate.js +91 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/context.d.ts +81 -0
- package/dist/cli/context.js +139 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +8 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/program.d.ts +21 -0
- package/dist/cli/program.js +158 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/raster.d.ts +4 -0
- package/dist/cli/raster.js +9 -0
- package/dist/cli/raster.js.map +1 -0
- package/dist/core/compile/breadboard-util.d.ts +12 -0
- package/dist/core/compile/breadboard-util.js +107 -0
- package/dist/core/compile/breadboard-util.js.map +1 -0
- package/dist/core/compile/breadboard.d.ts +60 -0
- package/dist/core/compile/breadboard.js +513 -0
- package/dist/core/compile/breadboard.js.map +1 -0
- package/dist/core/compile/fzp.d.ts +12 -0
- package/dist/core/compile/fzp.js +183 -0
- package/dist/core/compile/fzp.js.map +1 -0
- package/dist/core/compile/icon.d.ts +17 -0
- package/dist/core/compile/icon.js +27 -0
- package/dist/core/compile/icon.js.map +1 -0
- package/dist/core/compile/index.d.ts +4 -0
- package/dist/core/compile/index.js +62 -0
- package/dist/core/compile/index.js.map +1 -0
- package/dist/core/compile/pcb.d.ts +39 -0
- package/dist/core/compile/pcb.js +164 -0
- package/dist/core/compile/pcb.js.map +1 -0
- package/dist/core/compile/schematic.d.ts +69 -0
- package/dist/core/compile/schematic.js +240 -0
- package/dist/core/compile/schematic.js.map +1 -0
- package/dist/core/compile/style.d.ts +52 -0
- package/dist/core/compile/style.js +72 -0
- package/dist/core/compile/style.js.map +1 -0
- package/dist/core/compile/types.d.ts +50 -0
- package/dist/core/compile/types.js +15 -0
- package/dist/core/compile/types.js.map +1 -0
- package/dist/core/export/bin.d.ts +61 -0
- package/dist/core/export/bin.js +221 -0
- package/dist/core/export/bin.js.map +1 -0
- package/dist/core/export/fzpz.d.ts +37 -0
- package/dist/core/export/fzpz.js +121 -0
- package/dist/core/export/fzpz.js.map +1 -0
- package/dist/core/export/index.d.ts +5 -0
- package/dist/core/export/index.js +6 -0
- package/dist/core/export/index.js.map +1 -0
- package/dist/core/export/install.d.ts +77 -0
- package/dist/core/export/install.js +183 -0
- package/dist/core/export/install.js.map +1 -0
- package/dist/core/export/prune.d.ts +72 -0
- package/dist/core/export/prune.js +165 -0
- package/dist/core/export/prune.js.map +1 -0
- package/dist/core/fritzing.d.ts +22 -0
- package/dist/core/fritzing.js +77 -0
- package/dist/core/fritzing.js.map +1 -0
- package/dist/core/identity.d.ts +29 -0
- package/dist/core/identity.js +70 -0
- package/dist/core/identity.js.map +1 -0
- package/dist/core/image/bbox.d.ts +27 -0
- package/dist/core/image/bbox.js +24 -0
- package/dist/core/image/bbox.js.map +1 -0
- package/dist/core/image/index.d.ts +8 -0
- package/dist/core/image/index.js +9 -0
- package/dist/core/image/index.js.map +1 -0
- package/dist/core/image/key.d.ts +72 -0
- package/dist/core/image/key.js +317 -0
- package/dist/core/image/key.js.map +1 -0
- package/dist/core/image/prepare.d.ts +31 -0
- package/dist/core/image/prepare.js +98 -0
- package/dist/core/image/prepare.js.map +1 -0
- package/dist/core/image/remover.d.ts +17 -0
- package/dist/core/image/remover.js +51 -0
- package/dist/core/image/remover.js.map +1 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.js +17 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/kicad/footprint.d.ts +104 -0
- package/dist/core/kicad/footprint.js +317 -0
- package/dist/core/kicad/footprint.js.map +1 -0
- package/dist/core/kicad/index.d.ts +25 -0
- package/dist/core/kicad/index.js +197 -0
- package/dist/core/kicad/index.js.map +1 -0
- package/dist/core/kicad/sexpr.d.ts +35 -0
- package/dist/core/kicad/sexpr.js +163 -0
- package/dist/core/kicad/sexpr.js.map +1 -0
- package/dist/core/resolve.d.ts +113 -0
- package/dist/core/resolve.js +301 -0
- package/dist/core/resolve.js.map +1 -0
- package/dist/core/schema/part.d.ts +995 -0
- package/dist/core/schema/part.js +278 -0
- package/dist/core/schema/part.js.map +1 -0
- package/dist/core/schema/part.schema.json +1122 -0
- package/dist/core/svg.d.ts +20 -0
- package/dist/core/svg.js +56 -0
- package/dist/core/svg.js.map +1 -0
- package/dist/core/units.d.ts +28 -0
- package/dist/core/units.js +42 -0
- package/dist/core/units.js.map +1 -0
- package/dist/core/validate/checker.d.ts +46 -0
- package/dist/core/validate/checker.js +142 -0
- package/dist/core/validate/checker.js.map +1 -0
- package/dist/core/validate/doctor.d.ts +25 -0
- package/dist/core/validate/doctor.js +156 -0
- package/dist/core/validate/doctor.js.map +1 -0
- package/dist/core/validate/explain.d.ts +10 -0
- package/dist/core/validate/explain.js +135 -0
- package/dist/core/validate/explain.js.map +1 -0
- package/dist/core/validate/index.d.ts +21 -0
- package/dist/core/validate/index.js +96 -0
- package/dist/core/validate/index.js.map +1 -0
- package/dist/core/validate/structural.d.ts +74 -0
- package/dist/core/validate/structural.js +664 -0
- package/dist/core/validate/structural.js.map +1 -0
- package/dist/core/workspace.d.ts +69 -0
- package/dist/core/workspace.js +213 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/mcp/jsonpatch.d.ts +30 -0
- package/dist/mcp/jsonpatch.js +145 -0
- package/dist/mcp/jsonpatch.js.map +1 -0
- package/dist/mcp/main.d.ts +2 -0
- package/dist/mcp/main.js +5 -0
- package/dist/mcp/main.js.map +1 -0
- package/dist/mcp/server.d.ts +28 -0
- package/dist/mcp/server.js +310 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/server/api.d.ts +72 -0
- package/dist/server/api.js +507 -0
- package/dist/server/api.js.map +1 -0
- package/dist/server/events.d.ts +28 -0
- package/dist/server/events.js +134 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/http.d.ts +36 -0
- package/dist/server/http.js +127 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/index.d.ts +65 -0
- package/dist/server/index.js +193 -0
- package/dist/server/index.js.map +1 -0
- package/dist/ui/assets/index-DLU8opyI.js +63 -0
- package/dist/ui/assets/index-GhUA-T3b.css +1 -0
- package/dist/ui/index.html +14 -0
- package/package.json +89 -4
- package/skills/fritzkit/SKILL.md +690 -0
- package/skills/fritzkit/reference/examples.md +275 -0
- package/skills/fritzkit/reference/part-json.md +333 -0
- package/skills/fritzkit/scripts/measure.py +197 -0
- package/tools/fzp-checker/.python-version +1 -0
- package/tools/fzp-checker/README.md +45 -0
- package/tools/fzp-checker/check_fzp.py +48 -0
- package/tools/fzp-checker/pyproject.toml +26 -0
- package/tools/fzp-checker/uv.lock +266 -0
- package/tools/fzp-checker/vendor/LICENSE.txt +8 -0
- package/tools/fzp-checker/vendor/UPSTREAM.md +25 -0
- package/tools/fzp-checker/vendor/fzp_checker.py +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/__init__.py +26 -0
- package/tools/fzp-checker/vendor/scripts/checks/check_missing_leg_ids.py +44 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkascii.py +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkcase.py +156 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkcopies.py +104 -0
- package/tools/fzp-checker/vendor/scripts/checks/connector_numbering.py +79 -0
- package/tools/fzp-checker/vendor/scripts/checks/connectors_misnumbered.py +228 -0
- package/tools/fzp-checker/vendor/scripts/checks/explain_errors.md +95 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_checker_runner.py +717 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_checkers.py +1069 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_svg_checkers.py +62 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_utils.py +222 -0
- package/tools/fzp-checker/vendor/scripts/checks/inspector_fc51.png +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/invisibleconnectors.py +118 -0
- package/tools/fzp-checker/vendor/scripts/checks/nolayeridcheck.txt +758 -0
- package/tools/fzp-checker/vendor/scripts/checks/requirements.txt +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/svg_checkers.py +953 -0
- package/tools/fzp-checker/vendor/scripts/checks/svg_utils.py +96 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_checkers.py +839 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_id.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_node_members.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_present_valid.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_missing_attributes.fzp.test +16 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_present_valid.fzp.test +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_invalid.fzp.test +70 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_valid.fzp.test +70 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_refs_valid.fzp.test +53 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_mixed.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_silkscreen_nested.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_style_attr.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_white_stroke.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_wrong_color.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_inherited.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_legacy.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_nested_copper.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_none.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_standard.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_uppercase.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/css_connector.fzp.test +100 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/duplicate_ids.fzp.test +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_missing.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_present_valid.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/font_size.fzp.test +46 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_invalid_format.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_missing.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_present_valid.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_absent.fzp.test +15 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_present.fzp.test +15 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/hybrid_connectors.fzp.test +232 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/invalid_xml.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_match.fzp.test +29 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_mismatch.fzp.test +30 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_invalid.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_valid.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/matrix_transform.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_connector_refs.fzp.test +63 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_leg_ids.fzp.test +34 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_missing.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_non_alnum.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_absent.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_too_short.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_invalid.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_namedview.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_valid.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/pcb_only.fzp.test +48 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_name.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_value.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_present.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_present.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/stroke_test.fzp.test +60 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/template_svgs.fzp.test +35 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_ids.fzp.test +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_duplicates.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_unique.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/valid_xml.fzp.test +249 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_invalid_format.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_present_valid.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_present.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/connector_refs_valid_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/duplicate_ids_breadboard.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_absent_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_present_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/hybrid_connectors_breadboard.svg +102 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_invalid.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_valid.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/matrix_transform.svg +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_connector_refs_breadboard.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_leg_ids_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_match_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_mismatch_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/unique_ids_breadboard.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/hybrid_connectors_icon.svg +78 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_invalid.svg +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_namedview.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_valid.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/plain_pcb.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/connector_refs_valid_pcb.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_mixed.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_silkscreen_nested.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_style_attr.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_white_stroke.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_wrong_color.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_inherited.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_legacy.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_nested_copper.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_none.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_standard.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_uppercase.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/css_connector.svg +25 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/font_size.svg +54 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/hybrid_connectors_pcb.svg +35 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/missing_connector_refs_pcb.svg +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/pcb-arduino-shield.svg +25 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/stroke_test.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_match_pcb.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_mismatch_pcb.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/connector_refs_valid_schematic.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/hybrid_connectors_schematic.svg +59 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/missing_connector_refs_schematic.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_match_schematic.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_mismatch_schematic.svg +6 -0
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from io import StringIO
|
|
5
|
+
from .fzp_checker_runner import FZPCheckerRunner, AVAILABLE_CHECKERS, SVG_AVAILABLE_CHECKERS
|
|
6
|
+
import tempfile
|
|
7
|
+
import shutil
|
|
8
|
+
from lxml import etree
|
|
9
|
+
from .svg_checkers import SVGIdsChecker
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestCheckers(unittest.TestCase):
|
|
13
|
+
def setUp(self):
|
|
14
|
+
self.test_data_dir = 'test_data/core'
|
|
15
|
+
self.verbose = True
|
|
16
|
+
|
|
17
|
+
def test_valid_xml(self):
|
|
18
|
+
fzp_file = os.path.join(self.test_data_dir, 'valid_xml.fzp.test')
|
|
19
|
+
checker_runner = FZPCheckerRunner(fzp_file)
|
|
20
|
+
|
|
21
|
+
captured_output = StringIO()
|
|
22
|
+
sys.stdout = captured_output
|
|
23
|
+
checker_runner.check([], [])
|
|
24
|
+
sys.stdout = sys.__stdout__
|
|
25
|
+
|
|
26
|
+
# 4 missing SVG file errors (test data doesn't include the referenced SVGs)
|
|
27
|
+
self.assertEqual(checker_runner.total_errors, 4)
|
|
28
|
+
self.assertNotIn('Invalid XML', captured_output.getvalue())
|
|
29
|
+
|
|
30
|
+
def test_invalid_xml(self):
|
|
31
|
+
fzp_file = os.path.join(self.test_data_dir, 'invalid_xml.fzp.test')
|
|
32
|
+
checker_runner = FZPCheckerRunner(fzp_file)
|
|
33
|
+
|
|
34
|
+
checker_runner.check([], [])
|
|
35
|
+
|
|
36
|
+
self.assertEqual(checker_runner.total_errors, 1)
|
|
37
|
+
# Check that the error is recorded in the errors list
|
|
38
|
+
self.assertTrue(len(checker_runner.all_issues) > 0)
|
|
39
|
+
error_messages = [issue.message for issue in checker_runner.all_issues]
|
|
40
|
+
self.assertTrue(any('Invalid XML' in msg for msg in error_messages))
|
|
41
|
+
|
|
42
|
+
def run_checker(self, fzp_filename, fzp_checkers, svg_checkers, expected_errors, expected_message, expected_warnings=None):
|
|
43
|
+
fzp_file = os.path.join(self.test_data_dir, fzp_filename)
|
|
44
|
+
checker_runner = FZPCheckerRunner(fzp_file)
|
|
45
|
+
|
|
46
|
+
# Run specific FZP and SVG checkers for this test case
|
|
47
|
+
checker_runner.check(fzp_checkers, svg_checkers)
|
|
48
|
+
|
|
49
|
+
self.assertEqual(expected_errors, checker_runner.total_errors)
|
|
50
|
+
if expected_warnings is not None:
|
|
51
|
+
self.assertEqual(expected_warnings, checker_runner.total_warnings)
|
|
52
|
+
|
|
53
|
+
def test_pcb_only_part(self):
|
|
54
|
+
self.run_checker('pcb_only.fzp.test',
|
|
55
|
+
['missing_tags','connector_terminal','connector_svg_ref'],
|
|
56
|
+
['font_size','viewbox','ids'], 0, None)
|
|
57
|
+
|
|
58
|
+
def test_hybrid_connectors_part(self):
|
|
59
|
+
self.run_checker('hybrid_connectors.fzp.test',
|
|
60
|
+
['missing_tags','connector_terminal','connector_svg_ref'],
|
|
61
|
+
['font_size','viewbox','ids'], 0, None)
|
|
62
|
+
# The errors are now 0 after the invisibility check has been removed.
|
|
63
|
+
|
|
64
|
+
def test_css_connector_part(self):
|
|
65
|
+
self.run_checker('css_connector.fzp.test',
|
|
66
|
+
['connector_terminal','connector_svg_ref'],
|
|
67
|
+
[], 0, None)
|
|
68
|
+
# The errors are now 0 after the invisibility check has been removed.
|
|
69
|
+
|
|
70
|
+
def test_font_size(self):
|
|
71
|
+
self.run_checker('font_size.fzp.test',
|
|
72
|
+
[], ['font_size'], 4, None)
|
|
73
|
+
# Expected errors:
|
|
74
|
+
# No font size found for element [
|
|
75
|
+
# Test No Font Size 1
|
|
76
|
+
# ]
|
|
77
|
+
# No font size found for element [ Test No Font Size 2 ]
|
|
78
|
+
# Invalid font size 5px unit in element: [
|
|
79
|
+
# Test px unit
|
|
80
|
+
# ]
|
|
81
|
+
# Invalid font size 2mm unit in element: [
|
|
82
|
+
# Test mm unit
|
|
83
|
+
# ]
|
|
84
|
+
|
|
85
|
+
def test_font_size_fix(self):
|
|
86
|
+
"""Test that font-size unit suffixes are automatically removed"""
|
|
87
|
+
test_svg = 'test_data/svg/core/pcb/font_size.svg'
|
|
88
|
+
|
|
89
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
90
|
+
temp_svg = os.path.join(temp_dir, 'temp_font_size.svg')
|
|
91
|
+
shutil.copy(test_svg, temp_svg)
|
|
92
|
+
|
|
93
|
+
# Parse and check - should find unit errors
|
|
94
|
+
svg_doc = etree.parse(temp_svg)
|
|
95
|
+
from .svg_checkers import SVGFontSizeChecker
|
|
96
|
+
checker = SVGFontSizeChecker(svg_doc, ['silkscreen'])
|
|
97
|
+
errors, warnings = checker.check()
|
|
98
|
+
self.assertGreater(errors, 0, "Should initially have font-size errors")
|
|
99
|
+
|
|
100
|
+
# Apply fix
|
|
101
|
+
fix_results = checker.fix(temp_svg)
|
|
102
|
+
self.assertGreater(len(fix_results), 0, "Should have applied fixes")
|
|
103
|
+
|
|
104
|
+
# Verify the file no longer has unit suffixes
|
|
105
|
+
with open(temp_svg, 'r') as f:
|
|
106
|
+
content = f.read()
|
|
107
|
+
self.assertNotIn('font-size="5px"', content)
|
|
108
|
+
self.assertNotIn('font-size="2mm"', content)
|
|
109
|
+
self.assertIn('font-size="5"', content)
|
|
110
|
+
self.assertIn('font-size="2"', content)
|
|
111
|
+
|
|
112
|
+
# Re-check: only the 2 "missing font-size" errors should remain
|
|
113
|
+
svg_doc2 = etree.parse(temp_svg)
|
|
114
|
+
checker2 = SVGFontSizeChecker(svg_doc2, ['silkscreen'])
|
|
115
|
+
errors2, _ = checker2.check()
|
|
116
|
+
self.assertEqual(errors2, 2, "After fix, only missing font-size errors should remain")
|
|
117
|
+
|
|
118
|
+
# def test_missing_tags(self):
|
|
119
|
+
# self.run_checker('missing_tags.fzp.test', ['missing_tags'], [], 1, 'Missing required tag')
|
|
120
|
+
#
|
|
121
|
+
# def test_invalid_terminal(self):
|
|
122
|
+
# self.run_checker('invalid_terminal.fzp.test', ['connector_terminal'], [], 1, 'references missing terminal')
|
|
123
|
+
#
|
|
124
|
+
# def test_invisible_connector(self):
|
|
125
|
+
# self.run_checker('invisible_connector.fzp.test', ['connector_svg_ref'], [], 1, 'Invisible connector')
|
|
126
|
+
|
|
127
|
+
def test_stroke_checker(self):
|
|
128
|
+
self.run_checker('stroke_test.fzp.test',
|
|
129
|
+
['pcb_connector_stroke'], [], 2, None)
|
|
130
|
+
|
|
131
|
+
def test_missing_connector_refs(self):
|
|
132
|
+
self.run_checker('missing_connector_refs.fzp.test',
|
|
133
|
+
['missing_connector_refs'],
|
|
134
|
+
[], 4, None) # 4 errors: 2 copper0, 1 breadboard, 1 schematic
|
|
135
|
+
|
|
136
|
+
def test_connector_refs_valid(self):
|
|
137
|
+
self.run_checker('connector_refs_valid.fzp.test',
|
|
138
|
+
['missing_connector_refs'],
|
|
139
|
+
[], 0, None) # No errors when all references are present
|
|
140
|
+
|
|
141
|
+
def test_missing_leg_ids(self):
|
|
142
|
+
self.run_checker('missing_leg_ids.fzp.test',
|
|
143
|
+
['missing_leg_ids'],
|
|
144
|
+
[], 2, None) # 2 errors for missing leg refs
|
|
145
|
+
|
|
146
|
+
def test_fritzing_version_present_valid(self):
|
|
147
|
+
self.run_checker('fritzing_version_present_valid.fzp.test',
|
|
148
|
+
['fritzing_version'],
|
|
149
|
+
[], 0, None, 0)
|
|
150
|
+
|
|
151
|
+
def test_fritzing_version_missing(self):
|
|
152
|
+
self.run_checker('fritzing_version_missing.fzp.test',
|
|
153
|
+
['fritzing_version'],
|
|
154
|
+
[], 1, None, 0)
|
|
155
|
+
|
|
156
|
+
def test_fritzing_version_missing(self):
|
|
157
|
+
self.run_checker('fritzing_version_invalid_format.fzp.test',
|
|
158
|
+
['fritzing_version'],
|
|
159
|
+
[], 1, None, 0)
|
|
160
|
+
|
|
161
|
+
def _make_fzp_tree(self, xml_string):
|
|
162
|
+
"""Helper: parse XML string into an ElementTree (as FZPChecker expects)"""
|
|
163
|
+
root = etree.fromstring(xml_string)
|
|
164
|
+
return etree.ElementTree(root)
|
|
165
|
+
|
|
166
|
+
def test_fritzing_version_range_current(self):
|
|
167
|
+
"""Test that version >= 1.0.4 passes without issues"""
|
|
168
|
+
from .fzp_checkers import FZPFritzingVersionRangeChecker
|
|
169
|
+
fzp_doc = self._make_fzp_tree('<module fritzingVersion="1.0.4" moduleId="test"/>')
|
|
170
|
+
checker = FZPFritzingVersionRangeChecker(fzp_doc)
|
|
171
|
+
errors, warnings = checker.check()
|
|
172
|
+
self.assertEqual(errors, 0)
|
|
173
|
+
self.assertEqual(warnings, 0)
|
|
174
|
+
|
|
175
|
+
def test_fritzing_version_range_outdated_conventions(self):
|
|
176
|
+
"""Test that version < 1.0.4 but >= 0.9.4 warns about outdated conventions"""
|
|
177
|
+
from .fzp_checkers import FZPFritzingVersionRangeChecker
|
|
178
|
+
fzp_doc = self._make_fzp_tree('<module fritzingVersion="1.0.3" moduleId="test"/>')
|
|
179
|
+
checker = FZPFritzingVersionRangeChecker(fzp_doc)
|
|
180
|
+
errors, warnings = checker.check()
|
|
181
|
+
self.assertEqual(errors, 0)
|
|
182
|
+
self.assertEqual(warnings, 1)
|
|
183
|
+
self.assertIn("outdated conventions", checker.issues[0].message)
|
|
184
|
+
|
|
185
|
+
def test_fritzing_version_range_very_old(self):
|
|
186
|
+
"""Test that version < 0.8 produces error + ten years old warning"""
|
|
187
|
+
from .fzp_checkers import FZPFritzingVersionRangeChecker
|
|
188
|
+
fzp_doc = self._make_fzp_tree('<module fritzingVersion="0.5.2b.02.18.4756" moduleId="test"/>')
|
|
189
|
+
checker = FZPFritzingVersionRangeChecker(fzp_doc)
|
|
190
|
+
errors, warnings = checker.check()
|
|
191
|
+
self.assertEqual(errors, 1)
|
|
192
|
+
self.assertEqual(warnings, 1)
|
|
193
|
+
error_msgs = [i.message for i in checker.issues if i.severity == 'error']
|
|
194
|
+
warning_msgs = [i.message for i in checker.issues if i.severity == 'warning']
|
|
195
|
+
self.assertIn("below 0.8", error_msgs[0])
|
|
196
|
+
self.assertIn("ten years old", warning_msgs[0])
|
|
197
|
+
|
|
198
|
+
def test_fritzing_version_range_at_threshold(self):
|
|
199
|
+
"""Test that version exactly 0.9.4 gets the outdated conventions warning"""
|
|
200
|
+
from .fzp_checkers import FZPFritzingVersionRangeChecker
|
|
201
|
+
fzp_doc = self._make_fzp_tree('<module fritzingVersion="0.9.4" moduleId="test"/>')
|
|
202
|
+
checker = FZPFritzingVersionRangeChecker(fzp_doc)
|
|
203
|
+
errors, warnings = checker.check()
|
|
204
|
+
self.assertEqual(errors, 0)
|
|
205
|
+
self.assertEqual(warnings, 1)
|
|
206
|
+
self.assertIn("outdated conventions", checker.issues[0].message)
|
|
207
|
+
|
|
208
|
+
def test_module_id_present(self):
|
|
209
|
+
self.run_checker('module_id_present.fzp.test',
|
|
210
|
+
['module_id'],
|
|
211
|
+
[], 0, None, 0)
|
|
212
|
+
|
|
213
|
+
def test_module_id_missing(self):
|
|
214
|
+
self.run_checker('module_id_missing.fzp.test',
|
|
215
|
+
['module_id'],
|
|
216
|
+
[], 1, None, 0)
|
|
217
|
+
|
|
218
|
+
def test_module_id_unsafe_filename_chars(self):
|
|
219
|
+
self.run_checker('module_id_special_chars_present.fzp.test',
|
|
220
|
+
['module_id_special_chars'],
|
|
221
|
+
[], 1, None, 0) # 1 error for '*' (unsafe for filenames)
|
|
222
|
+
|
|
223
|
+
def test_module_id_clean(self):
|
|
224
|
+
self.run_checker('module_id_special_chars_absent.fzp.test',
|
|
225
|
+
['module_id_special_chars'],
|
|
226
|
+
[], 0, None, 0)
|
|
227
|
+
|
|
228
|
+
def test_module_id_non_alnum_warns(self):
|
|
229
|
+
self.run_checker('module_id_non_alnum.fzp.test',
|
|
230
|
+
['module_id_special_chars'],
|
|
231
|
+
[], 0, None, 1) # 1 warning for space
|
|
232
|
+
|
|
233
|
+
def test_module_id_too_short(self):
|
|
234
|
+
self.run_checker('module_id_too_short.fzp.test',
|
|
235
|
+
['module_id_special_chars'],
|
|
236
|
+
[], 1, None, 0) # 1 error for too short (<8 chars)
|
|
237
|
+
|
|
238
|
+
def test_module_id_special_chars_fix(self):
|
|
239
|
+
"""Test that the fixer replaces unsafe and non-alnum chars with underscores."""
|
|
240
|
+
import tempfile
|
|
241
|
+
import shutil
|
|
242
|
+
src = os.path.join(self.test_data_dir, 'module_id_special_chars_present.fzp.test')
|
|
243
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.fzp', delete=False) as tmp:
|
|
244
|
+
tmp_path = tmp.name
|
|
245
|
+
shutil.copy2(src, tmp_path)
|
|
246
|
+
try:
|
|
247
|
+
runner = FZPCheckerRunner(tmp_path)
|
|
248
|
+
runner.check(['module_id_special_chars'], [], fix=True)
|
|
249
|
+
with open(tmp_path, 'r', encoding='UTF-8') as f:
|
|
250
|
+
content = f.read()
|
|
251
|
+
self.assertIn('moduleId="SparkFun-DigitalIC-74_08-SE"', content)
|
|
252
|
+
finally:
|
|
253
|
+
os.unlink(tmp_path)
|
|
254
|
+
|
|
255
|
+
def test_version_present_valid(self):
|
|
256
|
+
self.run_checker('version_present_valid.fzp.test',
|
|
257
|
+
['version'],
|
|
258
|
+
[], 0, None, 0)
|
|
259
|
+
|
|
260
|
+
def test_version_missing(self):
|
|
261
|
+
self.run_checker('version_missing.fzp.test',
|
|
262
|
+
['version'],
|
|
263
|
+
[], 0, None, 1)
|
|
264
|
+
|
|
265
|
+
def test_version_invalid_format(self):
|
|
266
|
+
self.run_checker('version_invalid_format.fzp.test',
|
|
267
|
+
['version'],
|
|
268
|
+
[], 0, None, 1)
|
|
269
|
+
|
|
270
|
+
def test_title_present(self):
|
|
271
|
+
self.run_checker('title_present.fzp.test',
|
|
272
|
+
['title'],
|
|
273
|
+
[], 0, None, 0)
|
|
274
|
+
|
|
275
|
+
def test_title_missing(self):
|
|
276
|
+
self.run_checker('title_missing.fzp.test',
|
|
277
|
+
['title'],
|
|
278
|
+
[], 1, None, 0)
|
|
279
|
+
|
|
280
|
+
def test_description_present(self):
|
|
281
|
+
self.run_checker('description_present.fzp.test',
|
|
282
|
+
['description'],
|
|
283
|
+
[], 0, None, 0)
|
|
284
|
+
|
|
285
|
+
def test_description_missing(self):
|
|
286
|
+
self.run_checker('description_missing.fzp.test',
|
|
287
|
+
['description'],
|
|
288
|
+
[], 0, None, 1)
|
|
289
|
+
|
|
290
|
+
def test_author_present(self):
|
|
291
|
+
self.run_checker('author_present.fzp.test',
|
|
292
|
+
['author'],
|
|
293
|
+
[], 0, None, 0)
|
|
294
|
+
|
|
295
|
+
def test_author_missing(self):
|
|
296
|
+
self.run_checker('author_missing.fzp.test',
|
|
297
|
+
['author'],
|
|
298
|
+
[], 0, None, 1)
|
|
299
|
+
|
|
300
|
+
def test_required_tags_present(self):
|
|
301
|
+
self.run_checker('required_tags_present.fzp.test',
|
|
302
|
+
['required_tags'],
|
|
303
|
+
[], 4, None, 0)
|
|
304
|
+
|
|
305
|
+
def test_required_tags_missing(self):
|
|
306
|
+
self.run_checker('required_tags_missing.fzp.test',
|
|
307
|
+
['required_tags'],
|
|
308
|
+
[], 6, None, 0)
|
|
309
|
+
|
|
310
|
+
def test_family_property_present_and_valid(self):
|
|
311
|
+
self.run_checker('family_property_present_valid.fzp.test',
|
|
312
|
+
['family_property'],
|
|
313
|
+
[], 0, None, 0)
|
|
314
|
+
|
|
315
|
+
def test_family_property_missing(self):
|
|
316
|
+
self.run_checker('family_property_missing.fzp.test',
|
|
317
|
+
['family_property'],
|
|
318
|
+
[], 1, None, 0)
|
|
319
|
+
|
|
320
|
+
def test_unique_property_names_unique(self):
|
|
321
|
+
self.run_checker('unique_property_names_unique.fzp.test',
|
|
322
|
+
['unique_property_names'],
|
|
323
|
+
[], 0, None, 0)
|
|
324
|
+
|
|
325
|
+
def test_unique_property_names_duplicates(self):
|
|
326
|
+
self.run_checker('unique_property_names_duplicates.fzp.test',
|
|
327
|
+
['unique_property_names'],
|
|
328
|
+
[], 1, None, 0)
|
|
329
|
+
|
|
330
|
+
def test_property_fields_present(self):
|
|
331
|
+
self.run_checker('property_fields_present.fzp.test',
|
|
332
|
+
['property_fields'],
|
|
333
|
+
[], 0, None, 0)
|
|
334
|
+
|
|
335
|
+
def test_property_fields_missing_name(self):
|
|
336
|
+
self.run_checker('property_fields_missing_name.fzp.test',
|
|
337
|
+
['property_fields'],
|
|
338
|
+
[], 1, None, 0)
|
|
339
|
+
|
|
340
|
+
def test_property_fields_missing_value(self):
|
|
341
|
+
self.run_checker('property_fields_missing_value.fzp.test',
|
|
342
|
+
['property_fields'],
|
|
343
|
+
[], 1, None, 0)
|
|
344
|
+
|
|
345
|
+
def test_views_present(self):
|
|
346
|
+
self.run_checker('views_present.fzp.test',
|
|
347
|
+
['views'],
|
|
348
|
+
[], 3, None, 0)
|
|
349
|
+
|
|
350
|
+
def test_views_missing(self):
|
|
351
|
+
self.run_checker('views_missing.fzp.test',
|
|
352
|
+
['views'],
|
|
353
|
+
[], 1, None, 0)
|
|
354
|
+
|
|
355
|
+
def test_buses_present_valid(self):
|
|
356
|
+
self.run_checker('buses_present_valid.fzp.test',
|
|
357
|
+
['buses'],
|
|
358
|
+
[], 0, None, 0)
|
|
359
|
+
|
|
360
|
+
def test_buses_missing_id(self):
|
|
361
|
+
self.run_checker('buses_missing_id.fzp.test',
|
|
362
|
+
['buses'],
|
|
363
|
+
[], 1, None, 0)
|
|
364
|
+
|
|
365
|
+
def test_buses_missing_node_members(self):
|
|
366
|
+
self.run_checker('buses_missing_node_members.fzp.test',
|
|
367
|
+
['buses'],
|
|
368
|
+
[], 1, None, 0)
|
|
369
|
+
|
|
370
|
+
def test_connector_layers_present_valid(self):
|
|
371
|
+
self.run_checker('connector_layers_present_valid.fzp.test',
|
|
372
|
+
['connector_layers'],
|
|
373
|
+
[], 0, None, 0)
|
|
374
|
+
|
|
375
|
+
def test_connector_layers_missing_attributes(self):
|
|
376
|
+
self.run_checker('connector_layers_missing_attributes.fzp.test',
|
|
377
|
+
['connector_layers'],
|
|
378
|
+
[], 4, None, 0)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def test_layer_ids_match(self):
|
|
382
|
+
self.run_checker('layer_ids_match.fzp.test',
|
|
383
|
+
['layer_ids'],
|
|
384
|
+
[], 0, None)
|
|
385
|
+
|
|
386
|
+
def test_layer_ids_mismatch(self):
|
|
387
|
+
self.run_checker('layer_ids_mismatch.fzp.test',
|
|
388
|
+
['layer_ids'],
|
|
389
|
+
[], 3, None)
|
|
390
|
+
|
|
391
|
+
def test_matrix_transform(self):
|
|
392
|
+
self.run_checker('matrix_transform.fzp.test',
|
|
393
|
+
[],
|
|
394
|
+
['matrix'],
|
|
395
|
+
5,
|
|
396
|
+
None)
|
|
397
|
+
|
|
398
|
+
def test_layer_nesting_valid(self):
|
|
399
|
+
self.run_checker('layer_nesting_valid.fzp.test',
|
|
400
|
+
[],
|
|
401
|
+
['layer_nesting'],
|
|
402
|
+
0,
|
|
403
|
+
None)
|
|
404
|
+
|
|
405
|
+
def test_layer_nesting_invalid(self):
|
|
406
|
+
self.run_checker('layer_nesting_invalid.fzp.test',
|
|
407
|
+
[],
|
|
408
|
+
['layer_nesting'],
|
|
409
|
+
2,
|
|
410
|
+
None)
|
|
411
|
+
|
|
412
|
+
def test_gorn_present(self):
|
|
413
|
+
"""Test that gorn attributes are detected in SVG files"""
|
|
414
|
+
self.run_checker('gorn_present.fzp.test',
|
|
415
|
+
[],
|
|
416
|
+
['svg-gorn'],
|
|
417
|
+
3, # Should find 3 gorn attributes
|
|
418
|
+
None)
|
|
419
|
+
|
|
420
|
+
def test_gorn_absent(self):
|
|
421
|
+
"""Test that files without gorn attributes pass the check"""
|
|
422
|
+
self.run_checker('gorn_absent.fzp.test',
|
|
423
|
+
[],
|
|
424
|
+
['svg-gorn'],
|
|
425
|
+
0, # Should find no gorn attributes
|
|
426
|
+
None)
|
|
427
|
+
|
|
428
|
+
def test_gorn_fix(self):
|
|
429
|
+
"""Test that gorn attributes can be automatically removed"""
|
|
430
|
+
import tempfile
|
|
431
|
+
import shutil
|
|
432
|
+
|
|
433
|
+
# Create temporary copies of test files
|
|
434
|
+
test_fzp = os.path.join(self.test_data_dir, 'gorn_present.fzp.test')
|
|
435
|
+
test_svg = 'test_data/svg/core/breadboard/gorn_present_breadboard.svg'
|
|
436
|
+
|
|
437
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
438
|
+
# Copy test files to temp directory
|
|
439
|
+
temp_fzp = os.path.join(temp_dir, 'temp_gorn.fzp')
|
|
440
|
+
temp_svg = os.path.join(temp_dir, 'temp_gorn.svg')
|
|
441
|
+
|
|
442
|
+
shutil.copy(test_fzp, temp_fzp)
|
|
443
|
+
shutil.copy(test_svg, temp_svg)
|
|
444
|
+
|
|
445
|
+
# Update FZP to reference temp SVG
|
|
446
|
+
with open(temp_fzp, 'r') as f:
|
|
447
|
+
content = f.read()
|
|
448
|
+
content = content.replace('gorn_present_breadboard.svg', 'temp_gorn.svg')
|
|
449
|
+
with open(temp_fzp, 'w') as f:
|
|
450
|
+
f.write(content)
|
|
451
|
+
|
|
452
|
+
# Test initial state - should have gorn attributes
|
|
453
|
+
checker_runner = FZPCheckerRunner(temp_fzp)
|
|
454
|
+
checker_runner.check([], ['svg-gorn'], fix=False)
|
|
455
|
+
self.assertEqual(checker_runner.total_errors, 3, "Should initially have 3 gorn errors")
|
|
456
|
+
|
|
457
|
+
# Apply fix
|
|
458
|
+
checker_runner = FZPCheckerRunner(temp_fzp)
|
|
459
|
+
checker_runner.check([], ['svg-gorn'], fix=True)
|
|
460
|
+
|
|
461
|
+
# Check that fix was successful
|
|
462
|
+
checker_runner = FZPCheckerRunner(temp_fzp)
|
|
463
|
+
checker_runner.check([], ['svg-gorn'], fix=False)
|
|
464
|
+
self.assertEqual(checker_runner.total_errors, 0, "Should have no gorn errors after fix")
|
|
465
|
+
|
|
466
|
+
def test_unique_ids_valid(self):
|
|
467
|
+
"""Test that files with unique IDs pass the check"""
|
|
468
|
+
self.run_checker('unique_ids.fzp.test',
|
|
469
|
+
[],
|
|
470
|
+
['ids'],
|
|
471
|
+
0, # Should find no duplicate IDs
|
|
472
|
+
None)
|
|
473
|
+
|
|
474
|
+
def test_duplicate_ids_invalid(self):
|
|
475
|
+
"""Test that duplicate IDs are detected"""
|
|
476
|
+
self.run_checker('duplicate_ids.fzp.test',
|
|
477
|
+
[],
|
|
478
|
+
['ids'],
|
|
479
|
+
5, # Should find 5 duplicate ID errors (5 elements with id="label")
|
|
480
|
+
None)
|
|
481
|
+
|
|
482
|
+
def test_duplicate_ids_fix(self):
|
|
483
|
+
|
|
484
|
+
# Use the test SVG file directly
|
|
485
|
+
test_svg = 'test_data/svg/core/breadboard/duplicate_ids_breadboard.svg'
|
|
486
|
+
|
|
487
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
488
|
+
# Copy SVG to temp directory
|
|
489
|
+
temp_svg = os.path.join(temp_dir, 'temp_duplicate_ids.svg')
|
|
490
|
+
shutil.copy(test_svg, temp_svg)
|
|
491
|
+
|
|
492
|
+
# Parse the SVG
|
|
493
|
+
svg_doc = etree.parse(temp_svg)
|
|
494
|
+
|
|
495
|
+
# Create the checker directly
|
|
496
|
+
checker = SVGIdsChecker(svg_doc, [])
|
|
497
|
+
|
|
498
|
+
# Test initial state - should have duplicate ID errors
|
|
499
|
+
initial_errors, initial_warnings = checker.check()
|
|
500
|
+
self.assertGreater(initial_errors, 0, "Should initially have duplicate ID errors")
|
|
501
|
+
|
|
502
|
+
# Debug: Print the SVG content before fix to understand what we're working with
|
|
503
|
+
with open(temp_svg, 'r') as f:
|
|
504
|
+
before_content = f.read()
|
|
505
|
+
print(f"DEBUG: SVG content before fix:")
|
|
506
|
+
print(before_content)
|
|
507
|
+
|
|
508
|
+
# Apply fix directly
|
|
509
|
+
fix_results = checker.fix(temp_svg)
|
|
510
|
+
|
|
511
|
+
# Debug output
|
|
512
|
+
print(f"DEBUG: fix_results={fix_results}")
|
|
513
|
+
print(f"DEBUG: fixes_count={checker.get_fixes_count()}")
|
|
514
|
+
for fix in checker.fixes:
|
|
515
|
+
print(f"DEBUG: Fix: {fix.message}")
|
|
516
|
+
|
|
517
|
+
self.assertIsInstance(fix_results, list, "Fix method should return list of FixResult objects")
|
|
518
|
+
self.assertGreater(len(fix_results), 0, "Should have applied some fixes - there are consecutive duplicate label IDs to fix")
|
|
519
|
+
self.assertEqual(len(fix_results), checker.get_fixes_count(), "Fix results count should match fixes count")
|
|
520
|
+
|
|
521
|
+
def test_date_format_valid_iso(self):
|
|
522
|
+
"""Test that valid ISO dates pass without changes"""
|
|
523
|
+
fzp_xml = """<?xml version="1.0"?>
|
|
524
|
+
<module fritzingVersion="1.0.0" moduleId="test">
|
|
525
|
+
<version>1.0</version>
|
|
526
|
+
<date>2024-06-13</date>
|
|
527
|
+
</module>"""
|
|
528
|
+
|
|
529
|
+
from .fzp_checkers import FZPDateFormatChecker
|
|
530
|
+
|
|
531
|
+
fzp_doc = etree.fromstring(fzp_xml)
|
|
532
|
+
checker = FZPDateFormatChecker(fzp_doc)
|
|
533
|
+
errors, warnings = checker.check()
|
|
534
|
+
|
|
535
|
+
self.assertEqual(errors, 0, "Valid ISO date should not produce errors")
|
|
536
|
+
self.assertEqual(warnings, 0, "Valid ISO date should not produce warnings")
|
|
537
|
+
self.assertEqual(len(checker.fixes), 0, "Valid ISO date should not need fixes")
|
|
538
|
+
|
|
539
|
+
# Date should remain unchanged
|
|
540
|
+
date_element = fzp_doc.find('.//date')
|
|
541
|
+
self.assertEqual(date_element.text, "2024-06-13")
|
|
542
|
+
|
|
543
|
+
def test_date_format_common_format_check(self):
|
|
544
|
+
"""Test that common date format 'Thu Jun 13 2024' is detected by check method"""
|
|
545
|
+
fzp_xml = """<?xml version="1.0"?>
|
|
546
|
+
<module fritzingVersion="1.0.0" moduleId="test">
|
|
547
|
+
<version>1.0</version>
|
|
548
|
+
<date>Thu Jun 13 2024</date>
|
|
549
|
+
</module>"""
|
|
550
|
+
|
|
551
|
+
from .fzp_checkers import FZPDateFormatChecker
|
|
552
|
+
|
|
553
|
+
fzp_doc = etree.fromstring(fzp_xml)
|
|
554
|
+
checker = FZPDateFormatChecker(fzp_doc)
|
|
555
|
+
errors, warnings = checker.check()
|
|
556
|
+
|
|
557
|
+
self.assertEqual(errors, 0, "Fixable date should not produce errors")
|
|
558
|
+
self.assertEqual(warnings, 1, "Fixable date should produce one warning")
|
|
559
|
+
self.assertEqual(len(checker.fixes), 0, "Check method should not apply fixes")
|
|
560
|
+
|
|
561
|
+
# Date should remain unchanged after check
|
|
562
|
+
date_element = fzp_doc.find('.//date')
|
|
563
|
+
self.assertEqual(date_element.text, "Thu Jun 13 2024", "Date should be unchanged by check method")
|
|
564
|
+
|
|
565
|
+
# Check warning message
|
|
566
|
+
self.assertIn("2024-06-13", checker.issues[0].message)
|
|
567
|
+
|
|
568
|
+
def test_date_format_fix_method(self):
|
|
569
|
+
"""Test that fix method properly converts date format"""
|
|
570
|
+
import tempfile
|
|
571
|
+
fzp_content = """<?xml version="1.0"?>
|
|
572
|
+
<module fritzingVersion="1.0.0" moduleId="test">
|
|
573
|
+
<version>1.0</version>
|
|
574
|
+
<date>Thu Jun 13 2024</date>
|
|
575
|
+
</module>"""
|
|
576
|
+
|
|
577
|
+
from .fzp_checkers import FZPDateFormatChecker
|
|
578
|
+
|
|
579
|
+
# Create temporary file
|
|
580
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.fzp', delete=False) as temp_file:
|
|
581
|
+
temp_file.write(fzp_content)
|
|
582
|
+
temp_filename = temp_file.name
|
|
583
|
+
|
|
584
|
+
try:
|
|
585
|
+
# Parse and check
|
|
586
|
+
fzp_doc = etree.fromstring(fzp_content)
|
|
587
|
+
checker = FZPDateFormatChecker(fzp_doc)
|
|
588
|
+
|
|
589
|
+
# Apply fixes
|
|
590
|
+
fixes = checker.fix(temp_filename)
|
|
591
|
+
|
|
592
|
+
# Read the modified file
|
|
593
|
+
with open(temp_filename, 'r') as f:
|
|
594
|
+
modified_content = f.read()
|
|
595
|
+
|
|
596
|
+
# Check that the date was converted
|
|
597
|
+
self.assertIn("2024-06-13", modified_content)
|
|
598
|
+
self.assertNotIn("Thu Jun 13 2024", modified_content)
|
|
599
|
+
|
|
600
|
+
# Check fix results
|
|
601
|
+
self.assertEqual(len(fixes), 1, "Should have applied one fix")
|
|
602
|
+
self.assertIn("Thu Jun 13 2024", fixes[0].message)
|
|
603
|
+
self.assertIn("2024-06-13", fixes[0].message)
|
|
604
|
+
|
|
605
|
+
finally:
|
|
606
|
+
# Clean up
|
|
607
|
+
os.unlink(temp_filename)
|
|
608
|
+
|
|
609
|
+
def test_date_format_japanese_format(self):
|
|
610
|
+
"""Test Japanese date format conversion"""
|
|
611
|
+
fzp_xml = """<?xml version="1.0"?>
|
|
612
|
+
<module fritzingVersion="1.0.0" moduleId="test">
|
|
613
|
+
<version>1.0</version>
|
|
614
|
+
<date>木 3 31 2016</date>
|
|
615
|
+
</module>"""
|
|
616
|
+
|
|
617
|
+
from .fzp_checkers import FZPDateFormatChecker
|
|
618
|
+
|
|
619
|
+
fzp_doc = etree.fromstring(fzp_xml)
|
|
620
|
+
checker = FZPDateFormatChecker(fzp_doc)
|
|
621
|
+
errors, warnings = checker.check()
|
|
622
|
+
|
|
623
|
+
self.assertEqual(errors, 0, "Japanese date should be fixable")
|
|
624
|
+
self.assertEqual(warnings, 1, "Should produce one warning for fixable date")
|
|
625
|
+
self.assertEqual(len(checker.fixes), 0, "Check method should not apply fixes")
|
|
626
|
+
|
|
627
|
+
# Date should remain unchanged after check
|
|
628
|
+
date_element = fzp_doc.find('.//date')
|
|
629
|
+
self.assertEqual(date_element.text, "木 3 31 2016", "Date should remain unchanged by check method")
|
|
630
|
+
|
|
631
|
+
def test_date_format_ambiguous_dates(self):
|
|
632
|
+
"""Test ambiguous date formats (DD/MM vs MM/DD)"""
|
|
633
|
+
test_cases = [
|
|
634
|
+
("25/06/2024", "2024-06-25"), # Clearly DD/MM (25 > 12)
|
|
635
|
+
("06/25/2024", "2024-06-25"), # Clearly MM/DD (25 > 12)
|
|
636
|
+
("05/06/2024", "2024-06-05"), # Ambiguous, defaults to DD/MM
|
|
637
|
+
]
|
|
638
|
+
|
|
639
|
+
from .fzp_checkers import FZPDateFormatChecker
|
|
640
|
+
|
|
641
|
+
for input_date, expected_output in test_cases:
|
|
642
|
+
with self.subTest(input_date=input_date):
|
|
643
|
+
fzp_xml = f"""<?xml version="1.0"?>
|
|
644
|
+
<module fritzingVersion="1.0.0" moduleId="test">
|
|
645
|
+
<version>1.0</version>
|
|
646
|
+
<date>{input_date}</date>
|
|
647
|
+
</module>"""
|
|
648
|
+
|
|
649
|
+
fzp_doc = etree.fromstring(fzp_xml)
|
|
650
|
+
checker = FZPDateFormatChecker(fzp_doc)
|
|
651
|
+
errors, warnings = checker.check()
|
|
652
|
+
|
|
653
|
+
self.assertEqual(errors, 0, f"Date {input_date} should be fixable")
|
|
654
|
+
self.assertEqual(warnings, 1, f"Date {input_date} should produce one warning")
|
|
655
|
+
|
|
656
|
+
# Date should remain unchanged after check
|
|
657
|
+
date_element = fzp_doc.find('.//date')
|
|
658
|
+
self.assertEqual(date_element.text, input_date,
|
|
659
|
+
f"Date {input_date} should remain unchanged by check method")
|
|
660
|
+
|
|
661
|
+
def test_date_format_invalid_dates(self):
|
|
662
|
+
"""Test invalid date formats"""
|
|
663
|
+
invalid_dates = [
|
|
664
|
+
"Not a date",
|
|
665
|
+
"32/13/2024",
|
|
666
|
+
"June 32 2024",
|
|
667
|
+
"2024-13-01",
|
|
668
|
+
"",
|
|
669
|
+
]
|
|
670
|
+
|
|
671
|
+
from .fzp_checkers import FZPDateFormatChecker
|
|
672
|
+
|
|
673
|
+
for invalid_date in invalid_dates:
|
|
674
|
+
with self.subTest(invalid_date=invalid_date):
|
|
675
|
+
fzp_xml = f"""<?xml version="1.0"?>
|
|
676
|
+
<module fritzingVersion="1.0.0" moduleId="test">
|
|
677
|
+
<version>1.0</version>
|
|
678
|
+
<date>{invalid_date}</date>
|
|
679
|
+
</module>"""
|
|
680
|
+
|
|
681
|
+
fzp_doc = etree.fromstring(fzp_xml)
|
|
682
|
+
checker = FZPDateFormatChecker(fzp_doc)
|
|
683
|
+
errors, warnings = checker.check()
|
|
684
|
+
|
|
685
|
+
# Should produce either an error or warning
|
|
686
|
+
total_issues = errors + warnings
|
|
687
|
+
self.assertGreater(total_issues, 0, f"Invalid date '{invalid_date}' should produce issues")
|
|
688
|
+
|
|
689
|
+
def test_date_format_empty_date(self):
|
|
690
|
+
"""Test empty date element"""
|
|
691
|
+
fzp_xml = """<?xml version="1.0"?>
|
|
692
|
+
<module fritzingVersion="1.0.0" moduleId="test">
|
|
693
|
+
<version>1.0</version>
|
|
694
|
+
<date></date>
|
|
695
|
+
</module>"""
|
|
696
|
+
|
|
697
|
+
from .fzp_checkers import FZPDateFormatChecker
|
|
698
|
+
|
|
699
|
+
fzp_doc = etree.fromstring(fzp_xml)
|
|
700
|
+
checker = FZPDateFormatChecker(fzp_doc)
|
|
701
|
+
errors, warnings = checker.check()
|
|
702
|
+
|
|
703
|
+
self.assertEqual(warnings, 1, "Empty date should produce one warning")
|
|
704
|
+
self.assertIn("empty", checker.issues[0].message.lower())
|
|
705
|
+
|
|
706
|
+
def test_date_format_checker_name_and_description(self):
|
|
707
|
+
"""Test checker metadata"""
|
|
708
|
+
from .fzp_checkers import FZPDateFormatChecker
|
|
709
|
+
|
|
710
|
+
self.assertEqual(FZPDateFormatChecker.get_name(), "date_format")
|
|
711
|
+
self.assertIn("date format", FZPDateFormatChecker.get_description().lower())
|
|
712
|
+
self.assertIn("ISO format", FZPDateFormatChecker.get_description())
|
|
713
|
+
|
|
714
|
+
def test_connector_numbering_valid(self):
|
|
715
|
+
self.run_checker('connector_numbering_valid.fzp.test',
|
|
716
|
+
['connector_numbering'],
|
|
717
|
+
[],
|
|
718
|
+
0,
|
|
719
|
+
None)
|
|
720
|
+
|
|
721
|
+
def test_connector_numbering_invalid(self):
|
|
722
|
+
self.run_checker('connector_numbering_invalid.fzp.test',
|
|
723
|
+
['connector_numbering'],
|
|
724
|
+
[],
|
|
725
|
+
0,
|
|
726
|
+
None,
|
|
727
|
+
3) # 3 warnings for mismatched connector numbering
|
|
728
|
+
|
|
729
|
+
def test_copper_color_valid_standard(self):
|
|
730
|
+
self.run_checker('copper_color_valid_standard.fzp.test',
|
|
731
|
+
[],
|
|
732
|
+
['copper_layer_content'],
|
|
733
|
+
0,
|
|
734
|
+
None)
|
|
735
|
+
|
|
736
|
+
def test_copper_color_valid_uppercase(self):
|
|
737
|
+
self.run_checker('copper_color_valid_uppercase.fzp.test',
|
|
738
|
+
[],
|
|
739
|
+
['copper_layer_content'],
|
|
740
|
+
0,
|
|
741
|
+
None)
|
|
742
|
+
|
|
743
|
+
def test_copper_color_valid_legacy(self):
|
|
744
|
+
self.run_checker('copper_color_valid_legacy.fzp.test',
|
|
745
|
+
[],
|
|
746
|
+
['copper_layer_content'],
|
|
747
|
+
0,
|
|
748
|
+
None)
|
|
749
|
+
|
|
750
|
+
def test_copper_color_valid_inherited(self):
|
|
751
|
+
self.run_checker('copper_color_valid_inherited.fzp.test',
|
|
752
|
+
[],
|
|
753
|
+
['copper_layer_content'],
|
|
754
|
+
0,
|
|
755
|
+
None)
|
|
756
|
+
|
|
757
|
+
def test_copper_color_valid_none(self):
|
|
758
|
+
self.run_checker('copper_color_valid_none.fzp.test',
|
|
759
|
+
[],
|
|
760
|
+
['copper_layer_content'],
|
|
761
|
+
0,
|
|
762
|
+
None)
|
|
763
|
+
|
|
764
|
+
def test_copper_color_valid_nested_copper(self):
|
|
765
|
+
self.run_checker('copper_color_valid_nested_copper.fzp.test',
|
|
766
|
+
[],
|
|
767
|
+
['copper_layer_content'],
|
|
768
|
+
0,
|
|
769
|
+
None)
|
|
770
|
+
|
|
771
|
+
def test_copper_color_invalid_silkscreen_nested(self):
|
|
772
|
+
self.run_checker('copper_color_invalid_silkscreen_nested.fzp.test',
|
|
773
|
+
[],
|
|
774
|
+
['copper_layer_content'],
|
|
775
|
+
2,
|
|
776
|
+
None)
|
|
777
|
+
|
|
778
|
+
def test_copper_color_invalid_wrong_color(self):
|
|
779
|
+
self.run_checker('copper_color_invalid_wrong_color.fzp.test',
|
|
780
|
+
[],
|
|
781
|
+
['copper_layer_content'],
|
|
782
|
+
1,
|
|
783
|
+
None)
|
|
784
|
+
|
|
785
|
+
def test_copper_color_invalid_white_stroke(self):
|
|
786
|
+
self.run_checker('copper_color_invalid_white_stroke.fzp.test',
|
|
787
|
+
[],
|
|
788
|
+
['copper_layer_content'],
|
|
789
|
+
1,
|
|
790
|
+
None)
|
|
791
|
+
|
|
792
|
+
def test_copper_color_invalid_mixed(self):
|
|
793
|
+
self.run_checker('copper_color_invalid_mixed.fzp.test',
|
|
794
|
+
[],
|
|
795
|
+
['copper_layer_content'],
|
|
796
|
+
2,
|
|
797
|
+
None)
|
|
798
|
+
|
|
799
|
+
def test_copper_color_invalid_style_attr(self):
|
|
800
|
+
self.run_checker('copper_color_invalid_style_attr.fzp.test',
|
|
801
|
+
[],
|
|
802
|
+
['copper_layer_content'],
|
|
803
|
+
1,
|
|
804
|
+
None)
|
|
805
|
+
|
|
806
|
+
def test_no_layer_valid(self):
|
|
807
|
+
"""Test that SVG with elements inside proper layer group passes."""
|
|
808
|
+
self.run_checker('no_layer_valid.fzp.test',
|
|
809
|
+
[],
|
|
810
|
+
['no_layer'],
|
|
811
|
+
0,
|
|
812
|
+
None)
|
|
813
|
+
|
|
814
|
+
def test_no_layer_with_namedview(self):
|
|
815
|
+
"""Test that sodipodi:namedview elements are correctly skipped"""
|
|
816
|
+
self.run_checker('no_layer_namedview.fzp.test',
|
|
817
|
+
[],
|
|
818
|
+
['no_layer'],
|
|
819
|
+
0,
|
|
820
|
+
None)
|
|
821
|
+
|
|
822
|
+
def test_no_layer_invalid(self):
|
|
823
|
+
"""Test that SVG with elements outside any layer group is detected"""
|
|
824
|
+
self.run_checker('no_layer_invalid.fzp.test',
|
|
825
|
+
[],
|
|
826
|
+
['no_layer'],
|
|
827
|
+
1,
|
|
828
|
+
None)
|
|
829
|
+
|
|
830
|
+
def test_template_svgs_not_reported_missing(self):
|
|
831
|
+
"""Test that template SVGs (generic_ic_*, dip_*, etc.) are not reported as missing"""
|
|
832
|
+
self.run_checker('template_svgs.fzp.test',
|
|
833
|
+
[],
|
|
834
|
+
[],
|
|
835
|
+
0,
|
|
836
|
+
None)
|
|
837
|
+
|
|
838
|
+
if __name__ == '__main__':
|
|
839
|
+
unittest.main()
|