diff options
Diffstat (limited to 'src/test/java/eu')
| -rw-r--r-- | src/test/java/eu/mulk/aendggner/EndToEndTest.java | 30 | ||||
| -rw-r--r-- | src/test/java/eu/mulk/aendggner/aenderung/parse/LesereihenfolgeTest.java | 114 |
2 files changed, 127 insertions, 17 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java index fe98b84..d9f36d7 100644 --- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java +++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java @@ -1034,7 +1034,7 @@ class EndToEndTest { .containsExactly(null, "1", "2", "3", "4", "4a", "5", "6", "7", "8", "9", "10"); // Norm für Norm gegen die amtliche Nachfassung vom 12. Juni 2026. Es bleiben genau zwei - // benannte Abweichungen — beide nicht in der Anwendung begründet: + // benannte Abweichungen — keine davon in der Anwendung begründet: var sollAsog = SAMPLEDATA.resolve("Berlin/ASOG-Bln-neu.txt"); assumeTrue(Files.exists(sollAsog), "ASOG-Nachfassung fehlt"); var soll = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(sollAsog); @@ -1049,27 +1049,23 @@ class EndToEndTest { abweichend.add(normSoll.enbez()); } } - // Drei benannte Abweichungen, keine davon in der Anwendung begründet: + // Zwei benannte Abweichungen, keine davon in der Anwendung begründet: // // § 67 — Das Portal setzt in der neuen Fassung amtliche Satznummern („(2) ¹Gegen einen …“); // das Gesetzblatt, aus dem der Wortlaut stammt, setzt keine. // Anlage Nummer 23 — der oben benannte Rest: dort steht der Punkt, wo die Nachfassung ein // Semikolon führt. - // Anlage Nummer 31 — das Zitat läuft im Gesetzblatt über einen Seitenwechsel, und der - // ganzseitenbreite Titelblock des Änderungsgesetzes steht im Inhaltsstrom mitten darin - // („… zur Sicherung des Be-“ / Titelblock / „triebs von Unterkünften …“). Das ist die - // bekannte Berliner Layout-Frage, die erst eine geometrische Lesereihenfolge löst; sie - // betrifft die Extraktion, nicht die Anwendung. - assertThat(abweichend).containsExactly("§ 67", "Anlage Nummer 23", "Anlage Nummer 31"); - assertThat( - anwendung - .neu() - .norm("Anlage Nummer 31") - .orElseThrow() - .gesamtText() - .replaceAll("\\s+", " ")) - .as("der Titelblock steht im Zitat — Beleg der offenen Layout-Frage") - .contains("Gesetz zur Änderung des Allgemeinen Sicherheits- und Ordnungsgesetzes"); + assertThat(abweichend).containsExactly("§ 67", "Anlage Nummer 23"); + // Die Nummer 31 war die dritte: Ihr Zitat läuft über einen Seitenwechsel, und der + // ganzseitenbreite Titelblock des Änderungsgesetzes steht im Inhaltsstrom mitten darin + // („… zur Sicherung des Be-“ / Titelblock / „triebs von Unterkünften …“). Seit die + // Lesereihenfolge dem Satzbild folgt, steht der Titel, wo er hingehört, und das Zitat liest + // sich durch. + var nummer31 = + anwendung.neu().norm("Anlage Nummer 31").orElseThrow().gesamtText().replaceAll("\\s+", " "); + assertThat(nummer31) + .doesNotContain("Gesetz zur Änderung des Allgemeinen Sicherheits- und Ordnungsgesetzes") + .contains("die Ordnungsaufgaben zur Sicherung des Betriebs von Unterkünften"); assertThat(anwendung.neu().norm("§ 67").orElseThrow().gesamtText()) .contains("Gegen einen straßenverkehrsrechtlichen Verwaltungsakt"); } diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/LesereihenfolgeTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/LesereihenfolgeTest.java new file mode 100644 index 0000000..f213d07 --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/LesereihenfolgeTest.java @@ -0,0 +1,114 @@ +// SPDX-FileCopyrightText: 2020 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// SPDX-License-Identifier: AGPL-3.0-or-later +package eu.mulk.aendggner.aenderung.parse; + +import static org.assertj.core.api.Assertions.assertThat; + +import eu.mulk.aendggner.aenderung.parse.FontgroessenFilter.Zeile; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** Die Lesereihenfolge am nachgestellten Satzbild eines Gesetzblatts. */ +class LesereihenfolgeTest { + + /** Der Satzspiegel des Berliner Blattes: zwei Spalten von 45 bis 289 und von 332 bis 547. */ + private static Zeile links(int seite, float y, String text) { + return new Zeile(seite, y, 45f, 289f, text); + } + + private static Zeile rechts(int seite, float y, String text) { + return new Zeile(seite, y, 332f, 547f, text); + } + + private static Zeile breit(int seite, float y, String text) { + return new Zeile(seite, y, 127f, 465f, text); + } + + private static List<String> texte(List<Zeile> zeilen) { + var namen = new ArrayList<String>(); + for (var zeile : zeilen) { + namen.add(zeile.text()); + } + return namen; + } + + /** + * Der Fall, der den Anstoß gab: Das Blatt zeichnet den ganzseitenbreiten Titelblock zuletzt, + * obgleich er über beiden Spalten steht. Er gehört vor sie — und die Spalten bleiben ganz. + */ + @Test + void derZuletztGezeichneteTitelblockGehoertNachOben() { + var seite = new ArrayList<Zeile>(); + seite.add(breit(1, 51, "Kolumnentitel")); + for (int i = 0; i < 5; i++) { + seite.add(links(1, 345 + 10 * i, "links " + i)); + } + for (int i = 0; i < 5; i++) { + seite.add(rechts(1, 345 + 10 * i, "rechts " + i)); + } + seite.add(breit(1, 280, "Gesetz zur Änderung")); + seite.add(breit(1, 293, "Vom 3. Juni 2026")); + + assertThat(texte(Lesereihenfolge.ordne(seite))) + .containsExactly( + "Kolumnentitel", + "Gesetz zur Änderung", + "Vom 3. Juni 2026", + "links 0", + "links 1", + "links 2", + "links 3", + "links 4", + "rechts 0", + "rechts 1", + "rechts 2", + "rechts 3", + "rechts 4"); + } + + /** + * Die Falle des einfachen XY-Schnitts: Läge zwischen den Zeilen 4 und 5 ein weites Band, so + * zerfiele die Seite erst in oben und unten — gelesen würde links oben, rechts oben, links unten, + * rechts unten. Die Spalte hat Vorrang vor dem Band. + */ + @Test + void eineLueckeAufGleicherHoeheZerreisstDieSpaltenNicht() { + var seite = new ArrayList<Zeile>(); + seite.add(breit(1, 51, "Kolumnentitel")); + float[] hoehen = {100, 110, 120, 130, 300, 310, 320, 330}; + for (float y : hoehen) { + seite.add(links(1, y, "links " + (int) y)); + } + for (float y : hoehen) { + seite.add(rechts(1, y, "rechts " + (int) y)); + } + + var geordnet = texte(Lesereihenfolge.ordne(seite)); + assertThat(geordnet).startsWith("Kolumnentitel", "links 100"); + assertThat(geordnet.subList(1, 9)).allMatch(t -> t.startsWith("links")); + assertThat(geordnet.subList(9, 17)).allMatch(t -> t.startsWith("rechts")); + } + + /** Einspaltiger Satz hat keine Rinne; da bleibt es beim Inhaltsstrom. */ + @Test + void einspaltigerSatzBleibtUnangetastet() { + var seite = new ArrayList<Zeile>(); + for (int i = 0; i < 10; i++) { + seite.add(breit(1, 100 + 10 * i, "zeile " + i)); + } + assertThat(Lesereihenfolge.ordne(seite)).isEqualTo(seite); + } + + /** Fehlt einer Zeile die Geometrie, so ist die ganze Seite nicht zu vermessen. */ + @Test + void ohneGeometrieBleibtDieSeiteWieSieIst() { + var seite = new ArrayList<Zeile>(); + seite.add(new Zeile(0, Float.NaN, Float.NaN, Float.NaN, "unbekannt")); + for (int i = 0; i < 5; i++) { + seite.add(links(0, 100 + 10 * i, "links " + i)); + seite.add(rechts(0, 100 + 10 * i, "rechts " + i)); + } + assertThat(Lesereihenfolge.ordne(seite)).isEqualTo(seite); + } +} |
