Ports the write path of io/specctra/SesWriter.java to emit a valid session from a parsed DsnBoard plus a RoutingResult. Each _write_* function mirrors a write* method upstream: - session scope with base_design, placement (resolution + components echoed from the DSN), an empty was_is, and routes - routes carries resolution, a reduced parser scope, library_out with the via padstacks, and network_out - network_out emits (net (wire (path layer width x1 y1 ...)) (via padstack x y)) for each routed net; integer coordinates via round- half-up to match Java Math.round write_ses(board) with no result produces a valid no-op session, proving the DSN-in / SES-out round trip. Only routes > network_out is required by SesReader and kicad-cli; the rest is echoed for a well-formed file. 18 tests: indent/quoting rules, no-op session structure, placement and library_out echo, routed wire/via serialization, integer rounding, and round-trips re-parsed through the project's own DSN tokenizer/sexp (64 tests total across the suite, all green; ruff clean).
64 lines
1.7 KiB
Python
64 lines
1.7 KiB
Python
"""Tests for the SES indent writer and identifier quoting."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from freeroute.ses.indent import Identifier, IndentWriter
|
|
|
|
|
|
def test_nested_scopes_indent_by_two_spaces():
|
|
out = IndentWriter()
|
|
out.start_scope(new_line=False)
|
|
out.write("session x")
|
|
out.start_scope()
|
|
out.write("routes")
|
|
out.end_scope()
|
|
out.end_scope()
|
|
assert out.getvalue() == "(session x\n (routes\n )\n)"
|
|
|
|
|
|
def test_new_line_uses_current_indent():
|
|
out = IndentWriter()
|
|
out.start_scope(new_line=False)
|
|
out.write("a")
|
|
out.new_line()
|
|
out.write("(b)")
|
|
out.end_scope()
|
|
assert out.getvalue() == "(a\n (b)\n)"
|
|
|
|
|
|
def test_plain_name_not_quoted():
|
|
ident = Identifier()
|
|
assert ident.format("Top") == "Top"
|
|
assert ident.format("U1") == "U1"
|
|
assert ident.format("F.Cu") == "F.Cu" # '.' is not reserved
|
|
|
|
|
|
def test_reserved_chars_force_quotes():
|
|
ident = Identifier()
|
|
assert ident.format("NET_A") == '"NET_A"' # underscore reserved
|
|
assert ident.format("a-b") == '"a-b"' # hyphen reserved
|
|
assert ident.format("with space") == '"with space"'
|
|
assert ident.format("Via[0-1]_600:300_um") == '"Via[0-1]_600:300_um"'
|
|
|
|
|
|
def test_leading_digit_forces_quotes():
|
|
ident = Identifier()
|
|
assert ident.format("2layer") == '"2layer"'
|
|
assert ident.format("-3x") == '"-3x"'
|
|
|
|
|
|
def test_non_ascii_forces_quotes():
|
|
ident = Identifier()
|
|
assert ident.format("Паяльная") == '"Паяльная"'
|
|
|
|
|
|
def test_surrounding_quotes_stripped_before_reprocessing():
|
|
ident = Identifier()
|
|
# already-quoted plain name comes back unquoted
|
|
assert ident.format('"Top"') == "Top"
|
|
|
|
|
|
def test_embedded_quote_removed():
|
|
ident = Identifier()
|
|
assert ident.format('a"b') == "ab"
|