Qualitätssicherung mit Monotone VCS: mtn testresult
Es gibt verschiedene Möglichkeiten eine Qualitätssicherung mit dem Versionsverwaltungssystem monotone zu realisieren. Diese bauen auf der Beschränkung des Versionsgraphen auf, der im Produktivsystem verwendet wird. Das geht auf zwei Wegen:
Durch Beschränkung auf vertrauenswürdige Zweigzertifikate fordert man, dass festgelegte Gutachter eine bestimmte Revision akzeptieren.
Durch Beschränkung auf vertrauenswürdige
testresult
Zertifikate fordert man, dass die Endpunkte von Update-Operationen ein Zertifikat besitzen, das besagt, dass die fragliche Revision einen bestimmten Test bestanden hat.
Natürlich kann man die Verfahren mischen und kaskadieren, je nach dem bevorzugten Qualitätssicherungsprozess.
In diesem Artikel beschäftige ich mich mit der Anwendung von
Test-Zertifikaten.
Die Beschränkung auf zusätzliche Zweigzertifikate beschrieb ich bereits
in einem anderen Artikel.
Der Workflow ist fast der gleiche, mit der Ausnahme, dass statt mtn approve
hier mtn testresult
sowohl für pass als auch fail verwendet wird.
Generell funktioniert die Qualitätssicherung so, dass eine Instanz, die unabhängig vom Entwicklungsprozess ist, für bestimmte Revisionen Tests durchführt und die Ergebnisse dieser Tests mit zusätzlichen Zertifikaten dokumentiert. Bei der Aktualisierung der Produktivumgebung oder für die Freigabe eines Entwicklungsstandes werden dann nur Revisionen, die Zertifikate für bestandene Tests enthalten, berücksichtigt.
Die Tests sind frei wählbar und vom Projekt abhängig. Prinzipiell ist es möglich, die Testzertifikate auch ohne die Tests zu vergeben, aber das widerspräche dem Sinn der Qualitätssicherung.
Wichtig ist, dass man für die Tests eine Extra-Instanz und ein separates Zertifikat verwendet. Mit dem Testzertifikat werden keine Commits signiert sondern nur Testergebnisse.
Wie funktioniert es?
Laut Dokumentation von Monotone genügt es, im Verzeichnis _MTN eine Datei namens wanted-testresults abzulegen, die die Schlüssel der Gutachter enthält. Dabei handelt es sich nicht um die Schlüsselnamen sondern Hashes, die man zum Beispiel mit folgendem Befehl erhält:
$ mtn ls keys testkey \
| perl -n -e 's/^([0-9a-z]+)\s+testkey$/$1/ && print && exit' \
>> _MTN/wanted-testresults
Leider funktioniert die Default-Implementation des Hooks
accept_testresult_change (old_results, new_results)
zumindest in
Version 1.0 von Monotone nicht wie gewünscht und in der
Dokumentation beschrieben.
Der folgende Workaround in der Datei _MTN/monotonerc hilft:
function HexDumpString(str,spacer)
return (string.gsub(str,"(.)",
function (c)
return string.format("%02x%s",string.byte(c), spacer or "")
end)
)
end
function accept_testresult_change(old_results, new_results)
-- Hex encode each of the key hashes to match those in 'wanted-testresults'
local old_results_hex = {}
for k, v in pairs(old_results) do
local hexdump = HexDumpString(k)
old_results_hex[HexDumpString(k)] = v
end
local new_results_hex = {}
for k, v in pairs(new_results) do
local hexdump = HexDumpString(k)
new_results_hex[HexDumpString(k)] = v
end
local reqfile = io.open("_MTN/wanted-testresults", "r")
if (reqfile == nil) then return true end
local line = reqfile:read()
local required = {}
while (line ~= nil)
do
required[line] = true
line = reqfile:read()
end
io.close(reqfile)
for test, res in pairs(required)
do
if old_results_hex[test] == true and new_results_hex[test] ~= true
then
return false
end
end
return true
end
Mit dieser Definition des Hooks und den Keyhashes der Gutachterschlüssel
in _MTN/wanted-testresults aktualisiert der Befehl mtn update
genau dann nicht, wenn bei der alten Revision ein Schlüssel mit einem
positiven Testergebnis war und bei der neuen Revision der gleiche
Schlüssel kein positives Testergebnis aufweist, so wie es in der
Dokumentation beschrieben ist.
Bevor ich es vergesse; die Testresult-Zertifikate fügt man mit mtn
testresult
an eine Revision an:
mtn -k testkey testresult 99b505974f54aa4d0344b597362791590fd85028 pass
mtn -k testkey testresult a4bd7c92fca04dd92e10175fa6bd300ebe0531e4 fail
Das folgende Beispiel, bei dem der oben beschriebene Hook in der Datei monotonerc im aktuellen Verzeichnis liegt, soll die Verwendung verdeutlichen:
$ mtn --keydir keys genkey testkey1
$ mtn --keydir keys genkey testkey2
$ mtn --db test.mtn db init
$ mtn --db test.mtn --keydir keys --branch testbranch setup testbranch1
$ cd testbranch1
$ echo abc > testfile
$ mtn add testfile
mtn: füge 'testfile' dem Arbeitsbereich-Manifest hinzu
$ mtn commit -m Initial -k testkey1
mtn: beginne Einpflegen auf Zweig 'testbranch'
mtn: Revision 99b505974f54aa4d0344b597362791590fd85028 eingepflegt
$ mtn -k testkey2 testresult 99b505974f54aa4d0344b597362791590fd85028 pass
$ cd ..
$ mtn --db test.mtn --keydir keys --branch testbranch co testbranch2
$ cd testbranch1
$ echo def >> testfile
$ mtn commit -m Second -k testkey1
mtn: beginne Einpflegen auf Zweig 'testbranch'
mtn: Revision a4bd7c92fca04dd92e10175fa6bd300ebe0531e4 eingepflegt
$ mtn -k testkey2 testresult a4bd7c92fca04dd92e10175fa6bd300ebe0531e4 fail
$ cd ../testbranch2
$ mtn ls keys
| perl -n -e s/^([0-9a-z]+)\s+testkey2$/$1/ && print && exit
> _MTN/wanted-testresults
$ cp ../monotonerc _MTN
$ mtn update
mtn: aktualisiere vorwärts auf dem Zweig 'testbranch'
mtn: bereits aktualisiert auf 99b505974f54aa4d0344b597362791590fd85028
$ mtn status
----------------------------------------------------------------------
Revision: 446e6d19dea54502e23dbc27465492fe94e40f5b
Elternrev.: 99b505974f54aa4d0344b597362791590fd85028
Autor: ???
Datum: 04.01.2013 21:17:48
Zweig: testbranch
*** DIESE REVISION WIRD EINEN NEUEN KOPF ERZEUGEN ***
Veränderungen ggü. Elternrev.
99b505974f54aa4d0344b597362791590fd85028
keine Änderungen
Das bedeutet, Monotone erkennt, dass die Revision im
Arbeitsverzeichnis nicht die aktuelle ist, aktualisiert aber nicht auf
die neueste Version, da diese im Testzertifikat 0
(fail) stehen hat,
während die alte dort 1
(pass) stehen hatte. Das erkennt man auch
gut bei der Auflistung der Zertifikate:
$ mtn ls certs 99b505974f54aa4d0344b597362791590fd85028
--------------------------------------------------------------------------------
Schlüssel : testkey1 (8b50e0b2d1...)
Signatur : in Ordnung
Name : author
Wert : testkey1
--------------------------------------------------------------------------------
Schlüssel : testkey1 (8b50e0b2d1...)
Signatur : in Ordnung
Name : branch
Wert : testbranch
--------------------------------------------------------------------------------
Schlüssel : testkey1 (8b50e0b2d1...)
Signatur : in Ordnung
Name : changelog
Wert : Initial
--------------------------------------------------------------------------------
Schlüssel : testkey1 (8b50e0b2d1...)
Signatur : in Ordnung
Name : date
Wert : 2013-01-04T19:43:43
--------------------------------------------------------------------------------
Schlüssel : testkey2 (75325ca467...)
Signatur : in Ordnung
Name : testresult
Wert : 1
$ mtn ls certs a4bd7c92fca04dd92e10175fa6bd300ebe0531e4
--------------------------------------------------------------------------------
Schlüssel : testkey1 (8b50e0b2d1...)
Signatur : in Ordnung
Name : author
Wert : testkey1
--------------------------------------------------------------------------------
Schlüssel : testkey1 (8b50e0b2d1...)
Signatur : in Ordnung
Name : branch
Wert : testbranch
--------------------------------------------------------------------------------
Schlüssel : testkey1 (8b50e0b2d1...)
Signatur : in Ordnung
Name : changelog
Wert : Second
--------------------------------------------------------------------------------
Schlüssel : testkey1 (8b50e0b2d1...)
Signatur : in Ordnung
Name : date
Wert : 2013-01-04T19:43:44
--------------------------------------------------------------------------------
Schlüssel : testkey2 (75325ca467...)
Signatur : in Ordnung
Name : testresult
Wert : 0
Posted 2013-08-30