Skip to content

PlantUML Rendering Improvements - #499

Merged
alexanderlinne merged 4 commits into
mainfrom
fix-slice-package-aliases
Sep 25, 2026
Merged

alexanderlinne merged 4 commits into
mainfrom
fix-slice-package-aliases

Conversation

@alexanderlinne

Copy link
Copy Markdown
Collaborator

The slice exporter wrote every package by its last segment only. PlantUML identifies a package by that bare name, so two packages called Domain under Orders and Billing were drawn as one, and a package arrow (-> Domain) ended in whichever of them PlantUML registered first. This PR first emits the slice packages as a single tree and then identifies every package by its full path. Two test-only commits pin the output before each production change, so every change to the generated diagrams shows up as a snapshot diff. See the commit messages for details.

  • Pin PlantUML file builder output with snapshots (d9f30c4), test only. Replaces the expected diagrams assembled from string literals with Verify snapshots, and pins how hand-built slices render today: shared parent packages, a package-only slice with a colour, flat slices next to nested ones, C4 boundaries, and a nested slice with a hyperlink. Three of the pinned outputs are rejected by PlantUML:
    • NestedSliceWithHyperlinkTest is fixed by the next commit.
    • BuildUmlByDependenciesWithObjectsWithNoDependenciesTest and SpecialCharactersInComponentNamesTest declare a class and then draw component arrows ([a] --|> [b]). This already happens on main, whose tests asserted the same strings. This PR doesn't address it.
  • Emit slice packages as a single tree (4f575b3), production change. Every shared parent package is now opened once instead of once per slice, and contents are indented by nesting level. The hyperlink now goes in front of the colour instead of after the closing braces. Of the 14 snapshots whose source changed, 13 render exactly as before. The exception is the nested slice with a hyperlink, which PlantUML rejected until now:
1-nested-slice-hyperlink
  • Pin output for same-named packages under different parents (cf80f5b), test only. Adds a SameNamedSubnamespaces fixture in which Orders and Billing both contain Domain.Model and Web depends on Billing.Domain, and pins the broken output in plain and C4 style. The "Before" column below is what this commit pins: both Model components end up in one Domain, Orders is left as an empty package, and the extension arrow runs between the two Models inside the merged package.
  • Identify slice packages by their full path (48e2421), production change. Packages keep their own segment as label but are declared with their full path as alias (package "Domain" as App.Billing.Domain), C4 boundary IDs use the same path, and package arrows point at it. Both Domain packages now stay apart:
2-same-named-packages 3-same-named-packages-c4 4-same-named-packages-builder

The C4-style builder snapshot (C4StyleSameNamedBoundariesUnderDifferentParentsTest) is fixed in the same way.

One side effect is visible: once its alias and label differ, PlantUML shows an empty package's alias above its label. Empty packages therefore get a blank placeholder label inside them (label " " as A.Y.__empty__), so they are drawn as a regular package box instead of a flat tab. C4 boundaries don't show their ID and need no placeholder.

5-empty-package

This affects the empty packages in the focus-on diagrams. Graphviz now routes the arrow into Slice1 around the outside of the diagram; the diagram still has the same packages, components and dependencies:

6-focus-on-empty-package

Apart from these, the existing diagrams render exactly as before.

How the images were made

Each .verified.txt snapshot was rendered at the commit before and after the change, with PlantUML 1.2026.8 and Graphviz. The remote !include of C4-PlantUML was replaced by PlantUML's bundled copy (!include <C4/C4_Container>). "Renders exactly as before" means the SVGs are identical once the attributes that only encode the diagram source (embedded source, source line numbers, qualified names and element IDs) are removed.

The dependency and special-character tests assembled their expected
diagrams from string literals joined with Environment.NewLine, which
hides the shape of the output. Record them as snapshots instead.

Also pin how hand-built slices render today: slices sharing parent
packages, a package-only slice with a colour, flat slices next to
nested ones, C4-style boundaries and a nested slice with a hyperlink.
These cover more than the fixture assembly does (several root packages,
a component next to a deeper package), and show every shared package
being re-opened once per slice and the hyperlink of a nested slice
landing after its closing braces.

Signed-off-by: Alexander Linne <alexander.linne@tngtech.com>
PlantUmlSlice rendered every nested slice on its own, together with the
full chain of packages around it, so a diagram re-opened each shared
parent package once per slice. PlantUML merges re-opened packages, so
the drawing was fine, but the source read nothing like a hand-written
diagram.

PlantUmlDiagram now collects the nested slices into a PlantUmlSliceTree
and renders each root package once, where its first slice used to be.
Slices without a namespace keep their position. A package-only slice
(as used by focus-on) colours its package node instead of re-opening
it. The rendered diagrams are unchanged: the old and new snapshots
produce the same clusters, entities, links and fills.

While moving the component line, put the hyperlink in front of the
colour. PlantUML rejects "[X] #color [[link]]" as a syntax error, and
for nested slices the link used to be appended after the closing braces.

Indent the contents of every package and boundary by two spaces per
level, as a hand-written diagram would.

Signed-off-by: Alexander Linne <alexander.linne@tngtech.com>
…ents

PlantUML identifies a package by its bare name, not by where it is
nested. The slice exporter writes every package by its last segment and
points package-level arrows at that segment as well, so two packages
called "Domain" under "Orders" and "Billing" are drawn as one: both
"Model" components end up in the same "Domain", and the other parent is
left as an empty package. An arrow into "Billing.Domain" is written as
"-> Domain" and ends in whichever "Domain" PlantUML saw first.

Add a SameNamedSubnamespaces fixture in which Orders and Billing both
contain Domain.Model and Web depends on Billing.Domain, and pin what the
exporter currently produces for it, in plain and C4 style. Pin the
builder output for the same shape, the package arrows of every
dependency type and an empty package, which the fix has to keep
readable.

Signed-off-by: Alexander Linne <alexander.linne@tngtech.com>
PlantUML identifies a package by its bare name, so the exporter merged
every two packages with the same last segment into one, and a package
arrow ("-> Domain") ended in whichever of them PlantUML registered
first.

Label every package with its own segment but identify it by its full
path ('package "Domain" as App.Billing.Domain', and the same for C4
boundary IDs), and point package arrows at that path. It is the slice
description the dependency already holds, so no lookup is needed. Both
have to change together: with aliased packages, an arrow to a bare name
no longer matches anything and PlantUML draws a free-floating entity
for it.

An empty package is drawn by PlantUML as a single node that shows its
alias above its label once the two differ. Give it a blank placeholder
label, so it stays a regular package that shows only its label. That
changes the look of the empty packages in the focus-on diagrams from a
flat tab to a package box. C4 boundaries do not show their ID and need
no placeholder.

Apart from that, the existing diagrams render the same clusters,
entities, links and fills as before; only the new SameNamedSubnamespaces
diagrams change, and now keep both Domain packages apart.

Signed-off-by: Alexander Linne <alexander.linne@tngtech.com>
@alexanderlinne alexanderlinne changed the title Fix slice package aliases PlantUML Rendering Improvements Sep 25, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.93103% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.33%. Comparing base (61dfcf2) to head (48e2421).

Files with missing lines Patch % Lines
...rchUnitNET/Domain/PlantUml/Export/PlantUmlSlice.cs 91.42% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #499      +/-   ##
==========================================
+ Coverage   86.31%   86.33%   +0.02%     
==========================================
  Files         259      260       +1     
  Lines       12450    12496      +46     
  Branches     1210     1216       +6     
==========================================
+ Hits        10746    10789      +43     
- Misses       1370     1372       +2     
- Partials      334      335       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@alexanderlinne
alexanderlinne merged commit dcf76a3 into main Sep 25, 2026
10 checks passed
@alexanderlinne
alexanderlinne deleted the fix-slice-package-aliases branch September 25, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants