aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-28 08:13:29 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-28 08:13:29 +0200
commit0be6e41ca5ae1c16aab308931616f1ec84ea3eba (patch)
treed2d5342f687747b36c0ad00798df34c62ea73a95 /src/test
parent3203725daa2c8db9ce25ff4541dd4ea438ac5cd7 (diff)
Der Korpus sagt, woran die Reste im Ganzen liegen
Der Befund über das Erzeugnis ruhte auf handverlesenen Fällen. Rund drei Dutzend Akzeptanzprüfungen sagen, dass diese Hefte aufgehen; sie sagen nicht, woran die Reste im Ganzen liegen — ob an der Befehlssprache eines Landes, an der Aufbereitung des Druckwerks oder am Alter der eingesetzten Stammfassung. Die Auszählung der Gründe beantwortet das für ein Heft; über einen Korpus geführt beantwortet sie es für das Erzeugnis. Der Schalter --korpus arbeitet eine Auftragsliste ab und gibt je Auftrag eine Zeile Kennzahlen aus: erschlossene, angewandte, liegengebliebene und am Stichtag zurückgestellte Befehle, sodann die gleichen, geprüften, fehlenden, überzähligen und abweichenden Normen des Abgleichs, zuletzt die Gründe nach Häufigkeit. Die Liste nennt dieselben Angaben, die auch die Befehlszeile entgegennimmt — ein Auftrag ist nichts anderes als ein Lauf. Mit --synopsen bleiben die Einzelsynopsen samt einer Übersichtsseite stehen; ohne sie wäre der Bericht eine Zahlenreihe, aus der niemand den Grund eines Restes erführe. Eingecheckt ist der Bericht die Grundlinie. --grundlinie hält den Lauf dagegen und rügt allein, was fällt: weniger erschlossene oder angewandte Befehle, weniger gleiche Normen, mehr liegengebliebene, fehlende, überzählige oder abweichende. Ein Fortschritt ist kein Fehler, sondern der Zweck der Arbeit; er verlangt allein, die Grundlinie fortzuschreiben. Der Rückschritt endet mit 3 wie ein nicht aufgehender Abgleich. Ein gescheiterter Auftrag hält den Lauf nicht an, sondern trägt seinen Fehlertext in seiner Zeile — der Grundsatz der Nichtverwerfung gilt auch hier. Der mitgelieferte Korpus umfasst einunddreißig Aufträge und gibt 1413 Befehle her, von denen 1287 angewandt werden; 632 von 641 verglichenen Normen gehen auf. Er reproduziert die Zahlen der bestehenden Akzeptanzfälle (UWG 19/0, GEG 119/119, BayJG 154/154, Berlin 171/171, WDR 101, Bremen 41/37, Hamburg 21/17). Drei Aufträge tragen mit Absicht hohe Reste: Bei den beiden Beschlussempfehlungen und bei Rheinland-Pfalz schreibt die Vorlage eine ältere Fassung fort als die eingesetzte Stammfassung. Sie sind gleichwohl Wächter — steigen ihre Reste, so hat sich etwas verschlechtert. Nebenbei: Die Auszählung der Gründe stand doppelt (Befehlszeile und Synopse) und liegt nun in Pipeline.zaehleGruende; das Ergebnis der Pipeline führt sie mit. Geprüft: mvnw verify (443 Testfälle) und reuse lint (214/214) gehen durch; der Korpuslauf ist Teil der Prüfung. Die Rügeregel ist am ganzen Korpus erprobt: Eine um eins verschärfte Grundlinie führt zur Rüge „UWG-BGBl: angewandte Befehle 19 statt 20“ und zum Rückgabewert 3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Change-Id: I4e10b041b78d95772da1c3ff18ab92b0b45dacaa
Diffstat (limited to 'src/test')
-rw-r--r--src/test/java/eu/mulk/aendggner/bericht/KorpusberichtTest.java134
-rw-r--r--src/test/resources/sampledata/korpus-grundlinie.tsv44
-rw-r--r--src/test/resources/sampledata/korpus.tsv46
3 files changed, 224 insertions, 0 deletions
diff --git a/src/test/java/eu/mulk/aendggner/bericht/KorpusberichtTest.java b/src/test/java/eu/mulk/aendggner/bericht/KorpusberichtTest.java
new file mode 100644
index 0000000..e709337
--- /dev/null
+++ b/src/test/java/eu/mulk/aendggner/bericht/KorpusberichtTest.java
@@ -0,0 +1,134 @@
+// SPDX-FileCopyrightText: 2026 Matthias Andreas Benkard <code@mail.matthias.benkard.de>
+// SPDX-License-Identifier: AGPL-3.0-or-later
+package eu.mulk.aendggner.bericht;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Der Massenlauf über den Beispielkorpus, gehalten gegen die eingecheckte Grundlinie.
+ *
+ * <p>Die Einzelprüfungen sagen, dass ein bestimmtes Heft aufgeht. Diese sagt, dass keines der
+ * einunddreißig Hefte hinter das zurückfällt, was es einmal hergegeben hat — und zwar in einer
+ * Zahl, die sich fortschreiben lässt, statt in Dutzenden verstreuter Behauptungen.
+ */
+class KorpusberichtTest {
+
+ private static final Path SAMPLEDATA = Path.of("src/test/resources/sampledata");
+ private static final Path LISTE = SAMPLEDATA.resolve("korpus.tsv");
+ private static final Path GRUNDLINIE = SAMPLEDATA.resolve("korpus-grundlinie.tsv");
+
+ @Test
+ void derKorpusFaelltNichtHinterDieGrundlinieZurueck() throws Exception {
+ assumeTrue(Files.exists(LISTE) && Files.exists(GRUNDLINIE), "Korpusliste fehlt");
+ var auftraege = Korpusbericht.liesListe(LISTE);
+ assumeTrue(
+ auftraege.stream().allMatch(a -> Files.exists(SAMPLEDATA.resolve(a.stamm()))),
+ "Beispieldaten des Korpus fehlen");
+
+ var zeilen = Korpusbericht.fuehreAus(auftraege, SAMPLEDATA.toAbsolutePath(), null);
+
+ // Ein gescheiterter Auftrag hält den Lauf nicht an — unbemerkt bleiben darf er aber nicht;
+ // sonst meldete der Bericht lauter Nullen und niemand sähe den Grund.
+ assertThat(zeilen).hasSameSizeAs(auftraege);
+ assertThat(zeilen).allSatisfy(z -> assertThat(z.fehler()).isNull());
+
+ var ruegen =
+ Korpusbericht.gegenGrundlinie(zeilen, Files.readString(GRUNDLINIE, StandardCharsets.UTF_8));
+
+ assertThat(ruegen)
+ .as(
+ "Rückschritte gegenüber der Grundlinie; steht dagegen ein Fortschritt darin, so ist"
+ + " korpus-grundlinie.tsv fortzuschreiben:\n%s\n\nLauf:\n%s",
+ String.join("\n", ruegen), Korpusbericht.alsTsv(zeilen))
+ .isEmpty();
+ }
+
+ @Test
+ void dieListeWirdVollstaendigGelesen() throws Exception {
+ assumeTrue(Files.exists(LISTE), "Korpusliste fehlt");
+ var auftraege = Korpusbericht.liesListe(LISTE);
+
+ assertThat(auftraege).isNotEmpty();
+ // Kommentar- und Leerzeilen sind übergangen, „-“ heißt „nicht angegeben“.
+ assertThat(auftraege).noneMatch(a -> a.bezeichnung().startsWith("#"));
+ assertThat(auftraege).allSatisfy(a -> assertThat(a.hefte()).isNotEmpty());
+ var mitArtikel =
+ auftraege.stream().filter(a -> "GEG-BGBl-zeitrichtig".equals(a.bezeichnung())).findFirst();
+ assertThat(mitArtikel).isPresent();
+ assertThat(mitArtikel.orElseThrow().artikel()).isEqualTo("1");
+ assertThat(mitArtikel.orElseThrow().nachfassung()).isNull();
+ var mehrereHefte =
+ auftraege.stream()
+ .filter(a -> "BayJG-mit-Aenderungsantrag".equals(a.bezeichnung()))
+ .findFirst()
+ .orElseThrow();
+ assertThat(mehrereHefte.hefte()).hasSize(2);
+ var mitStichtag =
+ auftraege.stream()
+ .filter(a -> "IfSG-Stichtag".equals(a.bezeichnung()))
+ .findFirst()
+ .orElseThrow();
+ assertThat(mitStichtag.stichtag()).isNotNull();
+ }
+
+ @Test
+ void nurDerRueckschrittWirdGeruegt() {
+ var grundlinie =
+ """
+ # Bezeichnung\tBefehle\tangewandt\tmanuell\tzurückgestellt\tgleich\tgeprüft\tfehlend\tüberzählig\tabweichend\tGründe
+ Fall\t10\t8\t2\t0\t20\t22\t1\t0\t1\t-
+ """;
+
+ var besser =
+ List.of(new Korpusbericht.Zeile("Fall", 10, 9, 1, 0, 21, 22, 0, 0, 1, Map.of(), null));
+ assertThat(Korpusbericht.gegenGrundlinie(besser, grundlinie)).isEmpty();
+
+ var schlechter =
+ List.of(new Korpusbericht.Zeile("Fall", 10, 7, 3, 0, 19, 22, 1, 0, 2, Map.of(), null));
+ assertThat(Korpusbericht.gegenGrundlinie(schlechter, grundlinie))
+ .anySatisfy(r -> assertThat(r).contains("angewandte Befehle 7 statt 8"))
+ .anySatisfy(r -> assertThat(r).contains("liegengebliebene Befehle 3 statt 2"))
+ .anySatisfy(r -> assertThat(r).contains("gleiche Normen 19 statt 20"))
+ .anySatisfy(r -> assertThat(r).contains("abweichende Normen 2 statt 1"));
+ }
+
+ @Test
+ void einGescheiterterAuftragWirdGeruegtStattUebergangen() {
+ var grundlinie =
+ """
+ # Bezeichnung\tBefehle\tangewandt\tmanuell\tzurückgestellt\tgleich\tgeprüft\tfehlend\tüberzählig\tabweichend\tGründe
+ Fall\t10\t8\t2\t0\t-\t-\t-\t-\t-\t-
+ """;
+
+ var gescheitert =
+ List.of(
+ new Korpusbericht.Zeile(
+ "Fall", 0, 0, 0, 0, null, null, null, null, null, Map.of(), "IOException: weg"));
+ assertThat(Korpusbericht.gegenGrundlinie(gescheitert, grundlinie))
+ .singleElement()
+ .asString()
+ .contains("gescheitert");
+ }
+
+ @Test
+ void einVerschwundenerAuftragFaelltAuf() {
+ var grundlinie =
+ """
+ # Bezeichnung\tBefehle\tangewandt\tmanuell\tzurückgestellt\tgleich\tgeprüft\tfehlend\tüberzählig\tabweichend\tGründe
+ Fall\t10\t8\t2\t0\t-\t-\t-\t-\t-\t-
+ """;
+
+ assertThat(Korpusbericht.gegenGrundlinie(List.of(), grundlinie))
+ .singleElement()
+ .asString()
+ .contains("ist im Lauf aber nicht vorgekommen");
+ }
+}
diff --git a/src/test/resources/sampledata/korpus-grundlinie.tsv b/src/test/resources/sampledata/korpus-grundlinie.tsv
new file mode 100644
index 0000000..60da4f6
--- /dev/null
+++ b/src/test/resources/sampledata/korpus-grundlinie.tsv
@@ -0,0 +1,44 @@
+# Grundlinie des Korpuslaufs (§ 6d des Handbuchs), Stand 28. August 2026.
+#
+# Jede Zeile hält fest, was ein Auftrag der Liste „korpus.tsv“ zu diesem Zeitpunkt hergegeben hat.
+# Der Lauf wird dagegen gehalten (KorpusberichtTest, „--grundlinie“): Gerügt wird allein, was
+# FÄLLT — weniger erschlossene oder angewandte Befehle, weniger gleiche Normen, mehr
+# liegengebliebene, fehlende, überzählige oder abweichende. Ein Fortschritt ist kein Fehler,
+# sondern der Zweck der Arbeit; er verlangt allein, diese Datei fortzuschreiben.
+#
+# Die hohen Reste bei GEG-Beschlussempfehlung, IfSG-Beschlussempfehlung und RP-UKAPOGS beruhen
+# darauf, dass die Vorlage eine ältere Fassung fortschreibt als die eingesetzte Stammfassung
+# („Zieltext nicht vorhanden“); sie sind kein Mangel des Erzeugnisses, wohl aber ein Wächter:
+# Steigen sie, hat sich etwas verschlechtert.
+# Bezeichnung Befehle angewandt manuell zurückgestellt gleich geprüft fehlend überzählig abweichend Gründe
+UWG-BGBl 19 19 0 0 - - - - - -
+UWG-RegE 20 20 0 0 - - - - - -
+AGG-RegE 24 23 1 0 - - - - - Stelle nicht auffindbar:1
+ProdHaftG-RegE 1 1 0 0 - - - - - -
+GEG-BGBl-zeitrichtig 119 119 0 0 - - - - - -
+IfSG-BGBl-zeitrichtig 75 75 0 0 - - - - - -
+BayJG-GVBl 154 154 0 0 - - - - - -
+SN-SaechsBeamtVG 5 5 0 0 - - - - - -
+NI-NEFG 7 7 0 0 - - - - - -
+SH-Gemeindeordnung 1 1 0 0 167 167 0 0 0 -
+SH-Kreisordnung 1 1 0 0 86 86 0 0 0 -
+NRW-WDR-Gesetz 101 101 0 0 - - - - - -
+NRW-LMG 18 18 0 0 - - - - - -
+NRW-TMZ-Gesetz 4 4 0 0 - - - - - -
+NRW-17-RAEStV-AusfG 1 1 0 0 - - - - - -
+BE-ASOG 6 6 0 0 171 171 0 0 0 -
+BE-LAF-ErrG 2 2 0 0 5 5 0 0 0 -
+BW-KomWO 37 31 6 0 91 93 0 0 2 Befehl nicht erkannt:6
+HE-StVRZustV 21 21 0 0 52 52 0 0 0 -
+TH-KiGaFinVO 10 10 0 0 - - - - - -
+BB-FraktG 4 4 0 0 24 24 0 0 0 -
+HH-GutachterausschussVO 21 17 4 0 10 14 0 0 4 Befehl nicht erkannt:2; Stelle nicht auffindbar:1; Zieltext nicht vorhanden:1
+HB-BerufsfachschulVO 41 37 4 0 26 29 1 0 2 Befehl nicht erkannt:3; Fundstelle mehrdeutig:1
+MV-APOAmtsTA 24 20 4 0 - - - - - Befehl nicht erkannt:4
+RP-UKAPOGS 23 9 14 0 - - - - - Stelle nicht auffindbar:2; Zieltext nicht vorhanden:10; Bestand widerspricht dem Befehl:2
+GEG-GModG-RegE 97 87 10 0 - - - - - Befehl nicht erkannt:6; Stelle nicht auffindbar:2; Zieltext nicht vorhanden:2
+GEG-Beschlussempfehlung 119 70 49 0 - - - - - Befehl nicht erkannt:1; Stelle nicht auffindbar:3; Zieltext nicht vorhanden:40; Fundstelle mehrdeutig:1; Bestand widerspricht dem Befehl:4
+IfSG-Beschlussempfehlung 75 51 24 0 - - - - - Befehl nicht erkannt:1; Stelle nicht auffindbar:1; Zieltext nicht vorhanden:21; Bestand widerspricht dem Befehl:1
+BayJG-Landtagsentwurf 154 154 0 0 - - - - - -
+BayJG-mit-Aenderungsantrag 154 154 0 0 - - - - - -
+IfSG-Stichtag 75 65 0 10 - - - - - -
diff --git a/src/test/resources/sampledata/korpus.tsv b/src/test/resources/sampledata/korpus.tsv
new file mode 100644
index 0000000..2832539
--- /dev/null
+++ b/src/test/resources/sampledata/korpus.tsv
@@ -0,0 +1,46 @@
+# Auftragsliste des Korpuslaufs (§ 6d des Handbuchs).
+#
+# Spalten, durch Tabulator getrennt:
+# Bezeichnung | Stammfassung | Hefte (durch Komma) | Artikel | Nachfassung | Stichtag
+# „-“ heißt: nicht angegeben. Die Pfade beziehen sich auf das Verzeichnis dieser Datei.
+#
+# Aufgenommen ist, was die Akzeptanzprüfungen des EndToEndTest fahren. Deren Zahlen müssen hier
+# unverändert herauskommen; weichen sie ab, liegt der Fehler an der Liste und nicht am Erzeugnis.
+#
+# Bezeichnung Stamm Hefte Artikel Nachfassung Stichtag
+UWG-BGBl UWG/BJNR141400004.xml UWG/bgbl126s0043_regelungstext.pdf - - -
+UWG-RegE UWG/BJNR141400004.xml UWG/BT-Drs-21-1855_Regierungsentwurf.pdf - - -
+AGG-RegE AGG/BJNR189710006.xml AGG/BT-Drs-21-6178_Regierungsentwurf.pdf - - -
+ProdHaftG-RegE ProdHaftG/BJNR021980989.xml ProdHaftG/RegE_Produkthaftungsrecht_2025-12-17.pdf - - -
+GEG-BGBl-zeitrichtig GEG/BJNR172810020-2023.xml GEG/bgbl123s0280_regelungstext.pdf 1 - -
+IfSG-BGBl-zeitrichtig IfSG/BJNR104510000-2020.xml IfSG/bgbl120s2397_78991.pdf - - -
+BayJG-GVBl BayJG/BayJG-alt.txt BayJG/gvbl-2026-06.pdf - - -
+SN-SaechsBeamtVG Sachsen/SaechsBeamtVG-alt.txt Sachsen/SaechsGVBl-2026-S134_AendG-SaechsBeamtVG_revosax.pdf - - -
+NI-NEFG Niedersachsen/NEFG-alt.txt Niedersachsen/Nds-GVBl-2026-10_ELER-Foerdergesetz-AendG.pdf - - -
+SH-Gemeindeordnung SchleswigHolstein/GO-SH-alt.txt SchleswigHolstein/GVOBl-2026-27_Kommunalrecht-AendG.pdf 1 SchleswigHolstein/GO-SH-neu.txt -
+SH-Kreisordnung SchleswigHolstein/KrO-SH-alt.txt SchleswigHolstein/GVOBl-2026-27_Kommunalrecht-AendG.pdf 2 SchleswigHolstein/KrO-SH-neu.txt -
+NRW-WDR-Gesetz NRW/WDR-Gesetz-alt.txt NRW/GV-NRW-2026-S202_22-Rundfunkaenderungsgesetz.pdf - - -
+NRW-LMG NRW/LMG-NRW-alt.txt NRW/GV-NRW-2026-S202_22-Rundfunkaenderungsgesetz.pdf - - -
+NRW-TMZ-Gesetz NRW/TMZ-Gesetz-alt.txt NRW/GV-NRW-2026-S202_22-Rundfunkaenderungsgesetz.pdf - - -
+NRW-17-RAEStV-AusfG NRW/17-RAEStV-AusfG-alt.txt NRW/GV-NRW-2026-S202_22-Rundfunkaenderungsgesetz.pdf - - -
+BE-ASOG Berlin/ASOG-Bln-alt.txt Berlin/GVBl-2026-17_ASOG-LAF-AendG.pdf 1 Berlin/ASOG-Bln-neu.txt -
+BE-LAF-ErrG Berlin/LAF-ErrG-alt.txt Berlin/GVBl-2026-17_ASOG-LAF-AendG.pdf 2 Berlin/LAF-ErrG-neu.txt -
+BW-KomWO BadenWuerttemberg/KomWO-BW-alt.txt BadenWuerttemberg/GBl-2026-26_KommunalwahlO-AendVO.pdf - BadenWuerttemberg/KomWO-BW-neu.txt -
+HE-StVRZustV Hessen/StVRZustV-alt.txt Hessen/GVBl-2026-05_11-AendVO-verkehrsrechtl-Zustaendigkeiten.pdf - Hessen/StVRZustV-neu.txt -
+TH-KiGaFinVO Thueringen/ThuerKigaFinVO-alt.txt Thueringen/GVBl-TH-2026-02_KiGaFinanzVO-AendVO-ua.pdf - - -
+BB-FraktG Brandenburg/FraktG-alt.txt Brandenburg/GVBl-I-2026-12_FraktG-AendG.pdf - Brandenburg/FraktG-neu.txt -
+HH-GutachterausschussVO Hamburg/GAusschV-HH-alt.txt Hamburg/HmbGVBl-2026-17_GutachterausschussVO-AendVO.pdf - Hamburg/GAusschV-HH-neu.txt -
+HB-BerufsfachschulVO Bremen/AssBerFSchulV-BR-alt.txt Bremen/BremGBl-2026-87_BerufsfachschulVO-AendVO.pdf - Bremen/AssBerFSchulV-BR-neu.txt -
+MV-APOAmtsTA MecklenburgVorpommern/APOAmtsTA-MV-alt.txt MecklenburgVorpommern/GVOBl-MV-2026-21_Besoldungsanpassung-ua.pdf 44 - -
+# Rheinland-Pfalz führt keine Vorfassungen; angewandt wird deshalb auf die Nachfassung, und
+# aussagekräftig ist allein die Zahl der erschlossenen Befehle (23). Die liegengebliebenen sind
+# die Folge des falschen Standes, nicht eines Mangels — siehe § 13 Absatz 4 des Handbuchs.
+RP-UKAPOGS RheinlandPfalz/UKAPOGS-E3-neu.txt RheinlandPfalz/GVBl-2026-21_UKAPOGS-E3-AendVO.pdf - - -
+# Entwürfe und Zusammenstellungen: hier gibt es keine Nachfassung, wohl aber die Frage, wie viel
+# eine Vorlage hergibt.
+GEG-GModG-RegE GEG/BJNR172810020.xml GEG/Regierungsentwurf_GModG_2026-05-13.pdf - - -
+GEG-Beschlussempfehlung GEG/BJNR172810020.xml GEG/BT-Drs-20-7619_Beschlussempfehlung.pdf - - -
+IfSG-Beschlussempfehlung IfSG/BJNR104510000.xml IfSG/1924334.pdf - - -
+BayJG-Landtagsentwurf BayJG/BayJG-alt.txt BayJG/Ltg-Drs-19-9707_Gesetzentwurf.pdf - - -
+BayJG-mit-Aenderungsantrag BayJG/BayJG-alt.txt BayJG/Ltg-Drs-19-9707_Gesetzentwurf.pdf,BayJG/Ltg-Drs-19-10365_Aenderungsantrag-Gruene.pdf - - -
+IfSG-Stichtag IfSG/BJNR104510000-2020.xml IfSG/bgbl120s2397_78991.pdf - - 2020-11-19