Files
autocad-mcp-pro/tests/test_styles.py
T

644 lines
27 KiB
Python

"""Dimension, text and multileader styles on both engines.
Headless: real ezdxf tables, and the *rendered* dimension block as the witness
that a current style reaches the next dimension (the header alone was never
the thing that could lie — see tests/test_dimension_header_vars.py). Live:
a fake ActiveX surface that records the call shapes the COM block relies on
and models the one AutoCAD rule it depends on — making a style current
restores that style's saved DIM* settings.
"""
from __future__ import annotations
import types
import pytest
from backends.base import UnsupportedCapabilityError
from backends.capability import is_capability_default
from backends.ezdxf_backend import _current_dimstyle_name, _current_textstyle_name
from engineering.standards.dimstyles import PRESET_VARIABLES, resolve_dimstyle
from engineering.standards.mleaderstyles import resolve_mleaderstyle
pytestmark = pytest.mark.asyncio
def _rendered(backend, handle):
"""(text, text height, arrow xscales) out of the dimension's geometry block."""
dim = backend._doc.entitydb.get(handle)
block = backend._doc.blocks.get(dim.dxf.geometry)
text = height = None
arrows = []
for entity in block:
if entity.dxftype() == "MTEXT" and text is None:
text, height = entity.text, round(float(entity.dxf.char_height), 4)
elif entity.dxftype() == "TEXT" and text is None:
text, height = entity.dxf.text, round(float(entity.dxf.height), 4)
elif entity.dxftype() == "INSERT":
arrows.append(round(float(entity.dxf.get("xscale", 1.0)), 4))
if text is None:
raise AssertionError("the dimension block carries no text")
return text, height, sorted(set(arrows))
# ── headless engine ─────────────────────────────────────────────────────────
async def test_dimstyle_list_reports_standard_current_with_the_seventeen_values(backend):
rows = await backend.dimstyle_list()
assert [r["name"] for r in rows] == ["Standard"]
assert rows[0]["current"] is True and rows[0]["values_available"] is True
assert tuple(rows[0]["values"]) == PRESET_VARIABLES
assert rows[0]["values"]["DIMTXT"] == 2.5
assert rows[0]["values"]["DIMDSEP"] == ".", "drawing_new's ISO point"
assert rows[0]["values"]["DIMTXSTY"] == "Standard", "unstored code reports the schema default"
async def test_dimstyle_create_iso25_writes_every_value_and_creates_isocp(backend):
result = await backend.dimstyle_create("ISO-25", resolve_dimstyle("iso-25", None))
assert result["ok"] is True and result["name"] == "ISO-25"
assert result["textstyle_created"] is True and result["current"] is False
assert result["values"]["DIMDSEP"] == "," and result["values"]["DIMTXSTY"] == "ISOCP"
assert result["written"] == sorted(PRESET_VARIABLES)
style = backend._doc.dimstyles.get("ISO-25")
assert style.dxf.dimtxt == 2.5 and style.dxf.dimdsep == 44 and style.dxf.dimtad == 1
assert style.dxf.dimtxsty == "ISOCP" and style.dxf.dimlwd == -2 and style.dxf.dimblk == ""
assert backend._doc.styles.get("ISOCP").dxf.font == "isocp.shx"
# ezdxf.new() writes $DIMSTYLE "ISO-25" with no such entry; creating the
# entry must not make it current by accident.
assert backend._doc.header["$DIMSTYLE"] == "Standard"
assert _current_dimstyle_name(backend._doc) == "Standard"
async def test_set_current_syncs_the_header_and_the_next_dimension_renders_with_it(backend):
await backend.dimstyle_create("ISO-25", resolve_dimstyle("iso-25", None), set_current=True)
header = backend._doc.header
assert header["$DIMSTYLE"] == "ISO-25"
assert header["$DIMDSEP"] == 44 and header["$DIMTXT"] == 2.5 and header["$DIMTXSTY"] == "ISOCP"
dim = await backend.dimension_linear(0, 0, 33.333, 0, 16, -30)
assert backend._doc.entitydb.get(dim.handle).dxf.dimstyle == "ISO-25"
assert _rendered(backend, dim.handle) == ("33,33", 2.5, [2.5])
await backend.dimstyle_create("ANSI", resolve_dimstyle("ansi", None), set_current=True)
dim2 = await backend.dimension_linear(0, 0, 33.333, 0, 16, -60)
assert backend._doc.entitydb.get(dim2.handle).dxf.dimstyle == "ANSI"
assert _rendered(backend, dim2.handle) == ("33.33", 3.0, [3.0])
for creator, args in (
(backend.dimension_aligned, (0, 0, 10, 10, 5, 15)),
(backend.dimension_radius, (50, 50, 60, 50)),
(backend.dimension_diameter, (70, 0, 90, 0)),
(backend.dimension_angular, (0, 0, 10, 0, 0, 10, 5, 5)),
):
created = await creator(*args)
assert backend._doc.entitydb.get(created.handle).dxf.dimstyle == "ANSI", creator.__name__
async def test_dimstyle_modify_reports_only_what_moved_and_the_dimensions_using_it(backend):
await backend.dimstyle_create("ISO-25", resolve_dimstyle("iso-25", None), set_current=True)
dim = await backend.dimension_linear(0, 0, 10, 0, 5, -10)
result = await backend.dimstyle_modify("iso-25", {"DIMTXT": 2.5, "DIMDEC": 3, "dimrnd": 0})
assert result["name"] == "ISO-25"
assert result["changed"] == {"DIMDEC": [2, 3]}
assert result["dimensions_using_style"] == [dim.handle]
assert result["rerender_required"] is True
assert backend._doc.header["$DIMDEC"] == 3, "the current style's header twin follows"
assert not backend._doc.dimstyles.get("ISO-25").dxf.hasattr("dimrnd"), (
"DIMRND 0 is 'no rounding' and must be discarded, not stored (xround(x, 0.0) rounds)"
)
again = await backend.dimstyle_modify("ISO-25", {"DIMDEC": 3})
assert again["changed"] == {} and again["rerender_required"] is False
on_standard = await backend.dimstyle_modify("Standard", {"DIMDEC": 4})
assert on_standard["dimensions_using_style"] == [] and on_standard["rerender_required"] is False
assert backend._doc.header["$DIMDEC"] == 3, "a non-current style does not touch the header"
async def test_refusals_happen_before_anything_is_written(backend):
await backend.dimstyle_create("ISO-25", resolve_dimstyle("iso-25", None))
with pytest.raises(ValueError, match="already exists"):
await backend.dimstyle_create("iso-25", resolve_dimstyle("iso-25", None))
with pytest.raises(ValueError, match="DIMTXSTY"):
await backend.dimstyle_create("X", resolve_dimstyle("iso-25", {"DIMTXSTY": "NOPE"}))
assert not backend._doc.dimstyles.has_entry("X")
with pytest.raises(ValueError, match="DIMFOO"):
await backend.dimstyle_create("Y", {"DIMFOO": 1})
assert not backend._doc.dimstyles.has_entry("Y")
with pytest.raises(ValueError, match="DIMDEC"):
await backend.dimstyle_modify("ISO-25", {"DIMDEC": 9})
with pytest.raises(ValueError, match="does not exist"):
await backend.dimstyle_modify("NOPE", {"DIMDEC": 1})
with pytest.raises(ValueError, match="empty"):
await backend.dimstyle_modify("ISO-25", {})
with pytest.raises(ValueError, match="does not exist"):
await backend.dimstyle_set_current("NOPE")
assert backend._doc.dimstyles.get("ISO-25").dxf.dimdec == 2
async def test_builtin_arrowheads_render_save_and_report_one_spelling(backend, tmp_path):
"""`validate_overrides` hands the canonical `_OBLIQUE`; stored verbatim ezdxf
raises at render and at `doc.write`. The witness is the rendered INSERT and
a save/reopen that reports the same name before and after."""
values = resolve_dimstyle(
"iso-25", {"DIMBLK": "oblique", "DIMSAH": 1, "DIMBLK1": "_DOT", "DIMBLK2": "."}
)
result = await backend.dimstyle_create("ISO-25", values, set_current=True)
assert result["values"]["DIMBLK"] == "_OBLIQUE"
style = backend._doc.dimstyles.get("ISO-25")
assert style.dxf.dimblk == "OBLIQUE" and style.dxf.dimblk1 == "DOT" and style.dxf.dimblk2 == ""
dim = await backend.dimension_linear(0, 0, 20, 0, 10, -10)
block = backend._doc.blocks.get(backend._doc.entitydb.get(dim.handle).dxf.geometry)
assert {e.dxf.name for e in block if e.dxftype() == "INSERT"} == {"_DOT", "_CLOSEDFILLED"}, (
"DIMSAH on: the two separate arrowheads win over DIMBLK"
)
again = await backend.dimstyle_modify("ISO-25", {"DIMBLK": "OBLIQUE", "DIMBLK1": "_dot"})
assert again["changed"] == {}, "re-setting a value to itself is not a change (spec §8.3)"
path = str(tmp_path / "arrows.dxf")
await backend.drawing_save_as(path)
await backend.drawing_open(path)
rows = {r["name"]: r for r in await backend.dimstyle_list()}
assert rows["ISO-25"]["values"]["DIMBLK"] == "_OBLIQUE", "reloaded, the same spelling"
reopened = await backend.dimstyle_modify("ISO-25", {"DIMBLK": "oblique"})
assert reopened["changed"] == {}
async def test_user_arrowhead_block_must_exist_before_anything_is_written(backend):
with pytest.raises(ValueError, match="DIMBLK: block 'MYARROW' does not exist"):
await backend.dimstyle_create("X", resolve_dimstyle("iso-25", {"DIMBLK": "MYARROW"}))
assert not backend._doc.dimstyles.has_entry("X")
await backend.dimstyle_create("ISO-25", resolve_dimstyle("iso-25", None))
with pytest.raises(ValueError, match="DIMBLK1: block 'MYARROW' does not exist"):
await backend.dimstyle_modify("ISO-25", {"DIMSAH": 1, "DIMBLK1": "MYARROW"})
assert not backend._doc.dimstyles.get("ISO-25").dxf.hasattr("dimsah"), "nothing written"
backend._doc.blocks.new("MYARROW").add_line((0, 0), (1, 1))
result = await backend.dimstyle_modify("ISO-25", {"DIMBLK": "myarrow"})
assert result["changed"] == {"DIMBLK": ["", "MYARROW"]}, "the block table's own spelling"
assert backend._doc.dimstyles.get("ISO-25").dxf.dimblk == "MYARROW"
async def test_dimstyle_set_current_reports_previous_and_survives_save_reopen(backend, tmp_path):
await backend.dimstyle_create("ISO-25", resolve_dimstyle("iso-25", None))
result = await backend.dimstyle_set_current("iso-25")
assert result == {"ok": True, "current": "ISO-25", "previous": "Standard", "changed": True}
same = await backend.dimstyle_set_current("ISO-25")
assert same["changed"] is False and same["previous"] == "ISO-25"
path = str(tmp_path / "styles.dxf")
await backend.drawing_save_as(path)
await backend.drawing_open(path)
rows = await backend.dimstyle_list()
assert [r["name"] for r in rows if r["current"]] == ["ISO-25"]
assert rows[0]["name"] == "ISO-25" and rows[0]["values"]["DIMDSEP"] == ","
async def test_textstyle_create_list_set_current_and_new_text_uses_it(backend):
result = await backend.textstyle_create("ISOCP", "isocp", set_current=True)
assert result == {
"ok": True,
"name": "ISOCP",
"font": "isocp.shx",
"font_resolved": True,
"current": True,
}
rows = await backend.textstyle_list()
assert [r["name"] for r in rows] == ["ISOCP", "Standard"]
assert rows[0] == {
"name": "ISOCP",
"font": "isocp.shx",
"height": 0.0,
"width_factor": 1.0,
"oblique_deg": 0.0,
"current": True,
}
assert backend._doc.header["$TEXTSTYLE"] == "ISOCP"
assert _current_textstyle_name(backend._doc) == "ISOCP"
text = await backend.entity_create_text("note", 0, 0)
mtext = await backend.entity_create_mtext("note", 0, 10)
assert backend._doc.entitydb.get(text.handle).dxf.style == "ISOCP"
assert backend._doc.entitydb.get(mtext.handle).dxf.style == "ISOCP"
back = await backend.textstyle_set_current("standard")
assert back == {"ok": True, "current": "Standard", "previous": "ISOCP", "changed": True}
assert (
backend._doc.entitydb.get((await backend.entity_create_text("x", 0, 0)).handle).dxf.style
== "Standard"
)
async def test_textstyle_unknown_font_is_written_but_reported_unresolved(backend):
result = await backend.textstyle_create("ODD", "acadmcp-missing-font.ttf", 3.5, 0.8, 15)
assert result["font_resolved"] is False and result["current"] is False
style = backend._doc.styles.get("ODD")
assert style.dxf.font == "acadmcp-missing-font.ttf"
assert style.dxf.height == 3.5 and style.dxf.width == 0.8 and style.dxf.oblique == 15.0
async def test_textstyle_refusals_write_nothing(backend):
await backend.textstyle_create("ISOCP", "isocp.shx")
with pytest.raises(ValueError, match="already exists"):
await backend.textstyle_create("isocp", "isocp.shx")
with pytest.raises(ValueError, match="width_factor"):
await backend.textstyle_create("Z", "arial.ttf", width_factor=0)
with pytest.raises(ValueError, match="oblique_deg"):
await backend.textstyle_create("Z", "arial.ttf", oblique_deg=90)
with pytest.raises(ValueError, match="height"):
await backend.textstyle_create("Z", "arial.ttf", height=-1)
with pytest.raises(ValueError, match="font"):
await backend.textstyle_create("Z", "")
assert not backend._doc.styles.has_entry("Z")
with pytest.raises(ValueError, match="does not exist"):
await backend.textstyle_set_current("NOPE")
async def test_mleaderstyle_create_iso_and_list(backend):
result = await backend.mleaderstyle_create("ISO", resolve_mleaderstyle("iso", None))
assert result["ok"] is True and result["textstyle_created"] is True
assert result["values"] == {
"arrow_size": 2.5,
"landing_gap": 1.0,
"text_style": "ISOCP",
"text_height": 2.5,
}
style = backend._doc.mleader_styles.get("ISO")
assert style.dxf.arrow_head_size == 2.5 and style.dxf.landing_gap_size == 1.0
assert backend._doc.entitydb[style.dxf.text_style_handle].dxf.name == "ISOCP"
rows = await backend.mleaderstyle_list()
assert rows == [
{
"name": "ISO",
"arrow_size": 2.5,
"landing_gap": 1.0,
"text_style": "ISOCP",
"text_height": 2.5,
"values_available": True,
},
{
"name": "Standard",
"arrow_size": 4.0,
"landing_gap": 2.0,
"text_style": "Standard",
"text_height": 4.0,
"values_available": True,
},
]
async def test_mleaderstyle_refusals_write_nothing(backend):
await backend.mleaderstyle_create("ISO", resolve_mleaderstyle("iso", None))
with pytest.raises(ValueError, match="already exists"):
await backend.mleaderstyle_create("iso", resolve_mleaderstyle("iso", None))
with pytest.raises(ValueError, match="text_style"):
await backend.mleaderstyle_create("X", resolve_mleaderstyle("iso", {"text_style": "NOPE"}))
with pytest.raises(ValueError, match="missing"):
await backend.mleaderstyle_create("X", {"arrow_size": 2.5})
assert not backend._doc.mleader_styles.has_entry("X")
async def test_both_capability_maps_declare_mleaderstyle(backend):
from backends.com_backend import ComBackend
assert backend.capabilities().to_dict()["features"]["mleaderstyle"]["supported"] is True
com = ComBackend().capabilities().to_dict()["features"]["mleaderstyle"]
assert com["supported"] is False and "leader_create_mleader" in com["reason"]
# ── live engine, against a fake ActiveX surface ──────────────────────────────
class _FakeStyle:
def __init__(self, name, calls):
self.Name = name
self._calls = calls
self.fontFile = "txt.shx"
self.Width = 1.0
self.ObliqueAngle = 0.0
self.Height = 0.0
self.saved: dict | None = None # the DIM* values this style holds, once saved
def CopyFrom(self, source):
# AcadDimStyle.CopyFrom(document) saves the document's current
# dimension settings into this style.
self._calls.append(("CopyFrom", self.Name, source))
self.saved = dict(source.variables)
class _FakeCollection:
def __init__(self, kind, calls, *names):
self._kind = kind
self._calls = calls
self.entries = [_FakeStyle(name, calls) for name in names]
@property
def Count(self):
return len(self.entries)
def Item(self, index):
return self.entries[index]
def Add(self, name):
self._calls.append((f"{self._kind}.Add", name))
style = _FakeStyle(name, self._calls)
self.entries.append(style)
return style
class _FakeBlock:
def __init__(self, *entities):
self.entities = list(entities)
@property
def Count(self):
return len(self.entities)
def Item(self, index):
return self.entities[index]
class _FakeLayouts:
def __init__(self, *blocks):
self.layouts = [types.SimpleNamespace(Block=block) for block in blocks]
@property
def Count(self):
return len(self.layouts)
def Item(self, index):
return self.layouts[index]
class _FakeDictionary:
def __init__(self, *names):
self.objects = [types.SimpleNamespace(mleader_name=name) for name in names]
@property
def Count(self):
return len(self.objects)
def Item(self, index):
return self.objects[index]
def GetName(self, obj):
return obj.mleader_name
class _FakeDictionaries:
def __init__(self, entries):
self.entries = entries
def Item(self, name):
if name not in self.entries:
raise RuntimeError(f"no dictionary {name}")
return self.entries[name]
CURRENT_DIMVARS = {
"DIMTXT": 2.5,
"DIMASZ": 2.5,
"DIMEXE": 1.25,
"DIMEXO": 0.625,
"DIMGAP": 0.625,
"DIMTAD": 1,
"DIMTIH": 0,
"DIMTOH": 0,
"DIMDEC": 2,
"DIMDSEP": ".",
"DIMLUNIT": 2,
"DIMZIN": 8,
"DIMBLK": "",
"DIMTXSTY": "Standard",
"DIMLWD": -2,
"DIMLWE": -2,
"DIMSCALE": 1.0,
}
class _FakeDocument:
def __init__(self):
self.calls: list[tuple] = []
self.DimStyles = _FakeCollection("DimStyles", self.calls, "Standard")
self.TextStyles = _FakeCollection("TextStyles", self.calls, "Standard")
self.variables = dict(CURRENT_DIMVARS)
self._active_dim = self.DimStyles.entries[0]
self._active_dim.saved = dict(CURRENT_DIMVARS)
self._active_text = self.TextStyles.entries[0]
dim = types.SimpleNamespace(
ObjectName="AcDbRotatedDimension", StyleName="ISO-25", Handle="2A"
)
line = types.SimpleNamespace(ObjectName="AcDbLine", Handle="2B")
sheet_dim = types.SimpleNamespace(
ObjectName="AcDbAlignedDimension", StyleName="Standard", Handle="2C"
)
self.Layouts = _FakeLayouts(_FakeBlock(dim, line), _FakeBlock(sheet_dim))
self.Dictionaries = _FakeDictionaries(
{"ACAD_MLEADERSTYLE": _FakeDictionary("Standard", "Annotative")}
)
def GetVariable(self, name):
return self.variables[name]
def SetVariable(self, name, value):
self.calls.append(("SetVariable", name, value))
self.variables[name] = value
@property
def ActiveDimStyle(self):
return self._active_dim
@ActiveDimStyle.setter
def ActiveDimStyle(self, style):
# Making a style current restores its saved settings as the current
# DIM* variables and discards unsaved overrides (AutoCAD's -DIMSTYLE
# Restore rule; scripts/smoke_settings_com.py confirms it live). A
# style never saved (fresh DimStyles.Add) keeps the current variables.
self.calls.append(("ActiveDimStyle", style.Name))
self._active_dim = style
if style.saved is not None:
self.variables.update(style.saved)
@property
def ActiveTextStyle(self):
return self._active_text
@ActiveTextStyle.setter
def ActiveTextStyle(self, style):
self.calls.append(("ActiveTextStyle", style.Name))
self._active_text = style
@pytest.fixture
def com_backend(monkeypatch):
pytest.importorskip("win32com.client", reason="pywin32 not installed")
from backends import com_backend as module
document = _FakeDocument()
app = types.SimpleNamespace(
Preferences=types.SimpleNamespace(Files=types.SimpleNamespace(SupportPath=""))
)
monkeypatch.setattr(module, "_acad_doc", lambda: document)
monkeypatch.setattr(module, "_acad_app", lambda: app)
monkeypatch.setattr(module, "_regen", lambda: None)
backend = module.ComBackend()
async def _run_inline(func, *args, **kwargs):
return func(*args, **kwargs)
monkeypatch.setattr(backend, "_run", _run_inline)
return backend, document
async def test_com_create_sets_variables_then_copies_from_the_document(com_backend):
backend, document = com_backend
result = await backend.dimstyle_create("ISO-25", resolve_dimstyle("iso-25", None))
assert result["ok"] is True and result["current"] is False
assert result["textstyle_created"] is True
names = [call[0] for call in document.calls]
assert names[:2] == ["TextStyles.Add", "DimStyles.Add"], (
"the text style exists before DIMTXSTY names it"
)
assert document.calls[2] == ("ActiveDimStyle", "ISO-25")
sets = [call for call in document.calls if call[0] == "SetVariable"]
assert len(sets) == 17
assert ("SetVariable", "DIMDSEP", ",") in sets, "DIMDSEP is a string on ActiveX"
assert ("SetVariable", "DIMTXSTY", "ISOCP") in sets
assert ("SetVariable", "DIMTXT", 2.5) in sets and ("SetVariable", "DIMTAD", 1) in sets
copy_index = names.index("CopyFrom")
assert copy_index > names.index("SetVariable")
assert document.calls[copy_index] == ("CopyFrom", "ISO-25", document)
assert document.calls[-1] == ("ActiveDimStyle", "Standard"), "restored: set_current was False"
assert result["values"]["DIMDSEP"] == "," and result["values"]["DIMTXSTY"] == "ISOCP"
isocp = document.TextStyles.entries[-1]
assert isocp.Name == "ISOCP" and isocp.fontFile == "isocp.shx"
async def test_com_create_set_current_keeps_the_new_style_active(com_backend):
backend, document = com_backend
result = await backend.dimstyle_create("ANSI", resolve_dimstyle("ansi", None), set_current=True)
assert result["current"] is True
activations = [c for c in document.calls if c[0] == "ActiveDimStyle"]
assert activations == [("ActiveDimStyle", "ANSI")]
assert document.ActiveDimStyle.Name == "ANSI"
async def test_com_create_refuses_before_touching_activex(com_backend):
backend, document = com_backend
with pytest.raises(ValueError, match="already exists"):
await backend.dimstyle_create("standard", resolve_dimstyle("iso-25", None))
with pytest.raises(ValueError, match="DIMTXSTY"):
await backend.dimstyle_create("X", resolve_dimstyle("iso-25", {"DIMTXSTY": "NOPE"}))
with pytest.raises(ValueError, match="DIMDEC"):
await backend.dimstyle_create("X", {"DIMDEC": 9})
assert document.calls == []
async def test_com_list_reports_values_for_the_current_style_only(com_backend):
backend, document = com_backend
await backend.dimstyle_create("ISO-25", resolve_dimstyle("iso-25", None))
rows = await backend.dimstyle_list()
assert [(r["name"], r["current"], r["values_available"]) for r in rows] == [
("ISO-25", False, False),
("Standard", True, True),
]
assert rows[0]["values"] is None
assert rows[1]["values"]["DIMDSEP"] == "." and tuple(rows[1]["values"]) == PRESET_VARIABLES
async def test_com_modify_switches_saves_and_switches_back(com_backend):
backend, document = com_backend
await backend.dimstyle_create("ISO-25", resolve_dimstyle("iso-25", None))
document.calls.clear()
result = await backend.dimstyle_modify("iso-25", {"DIMDEC": 3, "DIMTXT": 2.5})
assert result["name"] == "ISO-25"
assert result["changed"] == {"DIMDEC": [2, 3]}
assert result["dimensions_using_style"] == ["2A"], "model and paper space, dimensions only"
assert result["rerender_required"] is False, "AutoCAD re-renders on regen"
assert document.calls == [
("ActiveDimStyle", "ISO-25"),
("SetVariable", "DIMDEC", 3),
("CopyFrom", "ISO-25", document),
("ActiveDimStyle", "Standard"),
]
document.calls.clear()
again = await backend.dimstyle_modify("ISO-25", {"DIMDEC": 3})
assert again["changed"] == {}
assert [c[0] for c in document.calls] == ["ActiveDimStyle", "ActiveDimStyle"], (
"no CopyFrom when nothing moved"
)
with pytest.raises(ValueError, match="does not exist"):
await backend.dimstyle_modify("NOPE", {"DIMDEC": 1})
async def test_com_set_current(com_backend):
backend, document = com_backend
await backend.dimstyle_create("ISO-25", resolve_dimstyle("iso-25", None))
result = await backend.dimstyle_set_current("ISO-25")
assert result == {"ok": True, "current": "ISO-25", "previous": "Standard", "changed": True}
assert document.ActiveDimStyle.Name == "ISO-25"
with pytest.raises(ValueError, match="does not exist"):
await backend.dimstyle_set_current("NOPE")
async def test_com_textstyle_create_sets_font_width_oblique_height(com_backend):
backend, document = com_backend
result = await backend.textstyle_create("ISOCP", "isocp", 0.0, 1.0, 0.0, set_current=True)
assert result == {
"ok": True,
"name": "ISOCP",
"font": "isocp.shx",
"font_resolved": True,
"current": True,
}
style = document.TextStyles.entries[-1]
assert (style.fontFile, style.Width, style.ObliqueAngle, style.Height) == (
"isocp.shx",
1.0,
0.0,
0.0,
)
assert document.ActiveTextStyle.Name == "ISOCP"
odd = await backend.textstyle_create("ODD", "acadmcp-missing-font.ttf", 3.5, 0.8, 15.0)
assert odd["font_resolved"] is False
assert document.TextStyles.entries[-1].ObliqueAngle == pytest.approx(0.2617993878), "radians"
rows = await backend.textstyle_list()
assert [(r["name"], r["current"]) for r in rows] == [
("ISOCP", True),
("ODD", False),
("Standard", False),
]
assert rows[1]["oblique_deg"] == pytest.approx(15.0)
with pytest.raises(ValueError, match="already exists"):
await backend.textstyle_create("isocp", "isocp.shx")
back = await backend.textstyle_set_current("standard")
assert back == {"ok": True, "current": "Standard", "previous": "ISOCP", "changed": True}
async def test_com_mleaderstyle_list_reads_the_dictionary_names_only(com_backend):
backend, document = com_backend
rows = await backend.mleaderstyle_list()
assert rows == [
{
"name": "Annotative",
"arrow_size": None,
"landing_gap": None,
"text_style": None,
"text_height": None,
"values_available": False,
},
{
"name": "Standard",
"arrow_size": None,
"landing_gap": None,
"text_style": None,
"text_height": None,
"values_available": False,
},
]
async def test_com_mleaderstyle_create_refuses_with_the_capability_key(com_backend):
from backends.com_backend import ComBackend
backend, document = com_backend
assert is_capability_default(ComBackend, "mleaderstyle_create")
with pytest.raises(UnsupportedCapabilityError) as excinfo:
await backend.mleaderstyle_create("ISO", resolve_mleaderstyle("iso", None))
assert excinfo.value.capability == "mleaderstyle"
assert "leader_create_mleader" in str(excinfo.value)
assert document.calls == []