From 0be6e41ca5ae1c16aab308931616f1ec84ea3eba Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Fri, 28 Aug 2026 08:13:29 +0200 Subject: Der Korpus sagt, woran die Reste im Ganzen liegen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Change-Id: I4e10b041b78d95772da1c3ff18ab92b0b45dacaa --- .../mulk/aendggner/bericht/KorpusberichtTest.java | 134 +++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/test/java/eu/mulk/aendggner/bericht/KorpusberichtTest.java (limited to 'src/test/java') 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 +// 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. + * + *

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"); + } +} -- cgit v1.2.1