-- Abnahmetest fuer lmcp Phase A (nur beobachten, nie ablehnen). -- -- Vom Vertrag geschrieben, NICHT von der Implementierung: der Autor dieses -- Tests hat den zu pruefenden Code nicht gesehen. Genau daran sind die -- letzten vier Runden gescheitert - der Implementierer hat seine eigenen -- Hausaufgaben korrigiert, und der Test prueft dann verlaesslich das, was -- der Code ohnehin tut. -- -- VERTRAG -- Eine einzelne, in sich geschlossene Lua-5.4-Datei stellt eine Tabelle M -- bereit mit: -- M.report(version, peer, ua) -- Produktionseinstieg -- M.sink(version, peer, ua) -- ueberschreibbar, wird beim ERSTEN -- -- Auftreten eines Tripels gerufen -- * jedes verschiedene Tripel (version, peer, ua) genau EINMAL -- * version nil oder "" -> nichts -- * der Entprellungszustand ist ein privates Upvalue: KEINE globale -- Variable, KEIN Parameter, den der Aufrufer mitgeben muss -- * gedeckelt bei 50 verschiedenen Tripeln; danach nichts mehr -- * M.report lehnt NIE etwas ab und liefert immer nil -- -- Aufruf: lua5.4 phase_a_acceptance.lua local pfad = arg and arg[1] if not pfad then io.stderr:write("usage: lua5.4 phase_a_acceptance.lua \n") os.exit(2) end local vorher_global = {} for k in pairs(_G) do vorher_global[k] = true end local lade = assert(loadfile(pfad)) local M = lade() if type(M) ~= "table" then io.stderr:write("FAIL: die Datei liefert keine Tabelle zurueck\n") os.exit(1) end local fehler = 0 local function pruefe(name, bedingung, zusatz) if bedingung then print((" [ok ] %s"):format(name)) else fehler = fehler + 1 print((" [FAIL ] %s%s"):format(name, zusatz and (" — " .. zusatz) or "")) end end -- Faenger einhaengen local gesehen = {} M.sink = function(v, p, u) gesehen[#gesehen + 1] = { v = v, p = p, u = u } end local function zuruecksetzen() gesehen = {} end pruefe("M.report existiert und ist aufrufbar", type(M.report) == "function") if type(M.report) ~= "function" then os.exit(1) end -- 1. nil und Leerstring melden nichts zuruecksetzen() M.report(nil, "peerA", "uaA") M.report("", "peerA", "uaA") pruefe("nil und \"\" melden nichts", #gesehen == 0, ("es kamen %d Meldungen"):format(#gesehen)) -- 2. erstes Tripel meldet genau einmal zuruecksetzen() M.report("2025-06-18", "peerA", "uaA") pruefe("erstes Tripel meldet einmal", #gesehen == 1) -- 3. DER FALL, DER DREI RUNDEN LANG DURCHRUTSCHTE: -- Entprellung am ECHTEN Einstieg, ohne dass der Aufrufer Zustand mitgibt. zuruecksetzen() for _ = 1, 5 do M.report("2025-06-18", "peerA", "uaA") end pruefe("fuenf gleiche Tripel -> keine weitere Meldung", #gesehen == 0, ("es kamen %d Meldungen; Zustand ueberlebt den Aufruf nicht") :format(#gesehen)) -- 4. gleiche Fassung, andere Gegenstelle -> meldet wieder zuruecksetzen() M.report("2025-06-18", "peerB", "uaA") pruefe("gleiche Fassung, andere Gegenstelle -> Meldung", #gesehen == 1, "Entprellung nur auf die Fassung wuerde andere Klienten maskieren") -- 5. gleiche Fassung und Gegenstelle, anderer User-Agent -> meldet wieder zuruecksetzen() M.report("2025-06-18", "peerB", "uaZ") pruefe("anderer User-Agent -> Meldung", #gesehen == 1) -- 6. liefert immer nil, lehnt nie ab local r1 = M.report("2026-07-28", "peerC", "uaC") local r2 = M.report("2026-07-28", "peerC", "uaC") pruefe("M.report liefert nil (lehnt nie ab)", r1 == nil and r2 == nil) -- 7. Deckel bei 50 zuruecksetzen() for i = 1, 80 do M.report("v" .. i, "peerD", "uaD") end pruefe("Deckel greift bei 50", #gesehen <= 50, ("es kamen %d Meldungen"):format(#gesehen)) pruefe("Deckel wirft nicht zu frueh", #gesehen >= 40, ("nur %d Meldungen vor dem Deckel"):format(#gesehen)) -- 8. kein globaler Zustand local neue = {} for k in pairs(_G) do if not vorher_global[k] then neue[#neue + 1] = k end end pruefe("keine neuen globalen Variablen", #neue == 0, "hinzugekommen: " .. table.concat(neue, ", ")) print((" %d Pruefungen fehlgeschlagen"):format(fehler)) os.exit(fehler == 0 and 0 or 1)