aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/java/eu/mulk/aendggner/EndToEndTest.java30
-rw-r--r--src/test/java/eu/mulk/aendggner/aenderung/parse/LesereihenfolgeTest.java114
-rw-r--r--src/test/resources/sampledata/Landesrecht-Beispiele.adoc29
3 files changed, 139 insertions, 34 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);
+ }
+}
diff --git a/src/test/resources/sampledata/Landesrecht-Beispiele.adoc b/src/test/resources/sampledata/Landesrecht-Beispiele.adoc
index fef01d9..ea218a0 100644
--- a/src/test/resources/sampledata/Landesrecht-Beispiele.adoc
+++ b/src/test/resources/sampledata/Landesrecht-Beispiele.adoc
@@ -26,11 +26,12 @@ Stammfassung kommt je Land aus dem Portal des Landes oder als handgepflegter Kla
|Berlin
|`Berlin/GVBl-2026-17_ASOG-LAF-AendG.pdf` — GVBl. für Berlin Nr. 17 vom 11.06.2026, S. 234
(Gesetz zur Änderung des ASOG und des LAF-Errichtungsgesetzes vom 03.06.2026)
-|*Zweispaltiges Layout* (Herausgabe über Wolters Kluwer). Der Spaltenspike ist erledigt: Die
-Reihenfolge des Inhaltsstroms *ist* bereits die Lesereihenfolge, eine koordinatenbasierte
-Spaltenerkennung ist nicht nötig. Verbleibende Layout-Eigenheit sind ganzseitenbreite Rahmen
-(Titelblock, laufender Seitenkopf), die an falscher Stelle im Strom stehen — der Seitenkopf wird
-herausgeschnitten, der Titelblock landet weiterhin mitten im Text. Tiefe Verschachtelung
+|*Zweispaltiges Layout* (Herausgabe über Wolters Kluwer). Innerhalb einer Spalte ist die
+Reihenfolge des Inhaltsstroms bereits die Lesereihenfolge; die ganzseitenbreiten Rahmen dagegen
+stehen an falscher Stelle im Strom — der Titelblock wird *zuletzt* gezeichnet, obgleich er über
+beiden Spalten steht, und landete damit mitten im Fließtext. Seit Welle 21 folgt die Reihenfolge
+dem Satzbild (Rinne zwischen den Spalten, breite Zeilen als Bandgrenzen), und der Titelblock steht,
+wo er hingehört. Tiefe Verschachtelung
a)/aa)/bb)/cc); Artikel 1 ändert eine *Anlage*, deren Einheiten als *Überschriften* („Nummer 6")
statt als Aufzählungsmarken gesetzt sind. Artikel 2 (LAF-Errichtungsgesetz) ist ein voller
Akzeptanzfall; das Portal setzt die amtlichen *Satznummern als angeklebte Ziffern* („(2) 1Gegen …").
@@ -152,16 +153,17 @@ vollständig belegt.
* *Berlin* ist umgesetzt (`EndToEndTest.asogLafAendGBerlin`). Artikel 2
(LAF-Errichtungsgesetz) ist ein *voller* Akzeptanzfall: beide Befehle angewandt, alle fünf
Normen gleich der amtlichen Nachfassung vom 12. Juni 2026. Artikel 1 (ASOG) wird vollständig
- erkannt und zu fünf Sechsteln angewandt; 168 der 171 Normen gleichen danach der Nachfassung.
+ erkannt und zu fünf Sechsteln angewandt; 169 der 171 Normen gleichen danach der Nachfassung.
Die Nummern der Anlage sind seit dieser Welle *eigene Normen* („Anlage Nummer 23“), wie das
Portal sie führt — damit trägt die vorhandene Absatz-Maschinerie ihre Befehle. Beide
Stammfassungen sind über die Browsersteuerung beschafft.
+
--
-Die drei Abweichungen sind benannt und liegen nicht in der Anwendung: § 67 (das Portal setzt
-amtliche Satznummern, das Gesetzblatt nicht), Anlage Nummer 23 (der eine Rest, siehe unten) und
-Anlage Nummer 31 (der ganzseitenbreite Titelblock steht im Inhaltsstrom mitten im Zitat — die
-offene Layout-Frage). Der eine liegengebliebene Befehl benennt eine Grenze des Klartextformats:
+Die zwei Abweichungen sind benannt und liegen nicht in der Anwendung: § 67 (das Portal setzt
+amtliche Satznummern, das Gesetzblatt nicht) und Anlage Nummer 23 (der eine Rest, siehe unten).
+Die Anlage Nummer 31 war die dritte, bis die Lesereihenfolge dem Satzbild folgte: Der
+ganzseitenbreite Titelblock stand im Inhaltsstrom mitten in ihrem Zitat. Der eine liegengebliebene
+Befehl benennt eine Grenze des Klartextformats:
Ein Zwischentitel ohne Absatzbezeichnung („Aus dem Bereich Verkehr:“) wird dem vorangehenden
Absatz zugeschlagen, weshalb dessen Text nicht auf den Punkt endet, den der Befehl meint.
--
@@ -253,9 +255,6 @@ Klartext-Zweig des Loaders normalisiert zur Sicherheit mit.
dagegen als Aufzählungsmarken im Wortlaut der Anlage; dort bleibt es beim Markensuchen, und
ein Befehl auf „die Überschrift der Nummer 1 der Anlage 8“ (GEG, Artikel 1 Nr. 43 b) aa))
findet seine Stelle nicht. Ob die Anlagen des Bundes ebenso zu zerlegen sind, ist offen.
-* *Berlin, ganzseitenbreite Rahmen*: Titelblock und Impressum stehen im Inhaltsstrom an anderer
- Stelle als im Satzbild (der Titelblock mitten im Fließtext der ersten Seite). Das zu beheben
- verlangt eine geometrische Lesereihenfolge (XY-Cut), nicht bloß eine Spaltenerkennung.
* *Rheinland-Pfalz*: Die Verkündungsplattform `verkuendung.rlp.de` verkündet erst seit dem
01.07.2026 elektronisch; ältere GVBl-Hefte liegen nicht als freie Direkt-PDF vor.
* Bremen, Hamburg, Mecklenburg-Vorpommern, Saarland, Brandenburg, Sachsen-Anhalt: noch nicht
@@ -266,10 +265,6 @@ Klartext-Zweig des Loaders normalisiert zur Sicherheit mit.
Kein Land scheitert noch an reiner Extraktionsschwäche, und jedes Land mit einem Änderungs-
dokument trägt nunmehr einen Belegfall bis zur Anwendung. Nach absteigendem Nutzen:
-. *Berlin, ganzseitenbreite Rahmen* (XY-Cut) — die letzte offene Layout-Frage, siehe „Noch
- offen“. Sie ist seit dieser Welle *belegt*: Die Anlage Nummer 31 des ASOG weicht allein
- deshalb von der Nachfassung ab, weil der Titelblock des Änderungsgesetzes im Inhaltsstrom
- mitten in ihrem Zitat steht.
. *Weitere Länder erfassen* — Rheinland-Pfalz (Verkündungsplattform erst ab 01.07.2026), Bremen,
Hamburg, Mecklenburg-Vorpommern, Saarland, Brandenburg, Sachsen-Anhalt.