
Fehlerinjektions-Bibliothek für Go, die zur Laufzeit steuerbare Failpoints hinzufügt, um über Umgebungsvariablen Panics, Sleeps, Returns und bedingte Fehlerpfade auszulösen.
Eine Implementierung von Failpoints für Golang. Failpoints werden verwendet, um Codepunkte einzufügen, an denen Fehler in einer benutzergesteuerten Weise injiziert werden können. Ein Failpoint ist ein Codeausschnitt, der nur ausgeführt wird, wenn der entsprechende Failpoint aktiv ist.
failpoint-ctl)Baue failpoint-ctl aus dem Quellcode
git clone https://github.com/pingcap/failpoint.git
cd failpoint
make
ls bin/failpoint-ctl
Injiziere Failpoints in dein Programm, z. B.:
package main
import "github.com/pingcap/failpoint"
func main() {
failpoint.Inject("testPanic", func() {
panic("failpoint triggerd")
})
}
Transformiere deinen Code mit failpoint-ctl enable
Baue mit go build
Aktiviere Failpoints mit der Umgebungsvariable GO_FAILPOINTS
GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
Hinweis: GO_FAILPOINTS funktioniert nicht mit Markern vom Typ InjectCall.
Wenn du go run zum Ausführen des Tests verwendest, vergiss nicht, die generierte Datei binding__failpoint_binding__.go in deinen Befehl aufzunehmen, z. B.:
GO_FAILPOINTS="main/testPanic=return(true)" go run your-program.go binding__failpoint_binding__.go
failpoint-toolexec)Baue failpoint-toolexec aus dem Quellcode
git clone https://github.com/pingcap/failpoint.git
cd failpoint
make
ls bin/failpoint-toolexec
Injiziere Failpoints in dein Programm, z. B.:
package main
import "github.com/pingcap/failpoint"
func main() {
failpoint.Inject("testPanic", func() {
panic("failpoint triggerd")
})
}
Verwende einen separaten Build-Cache, um eine Vermischung mit Caches ohne failpoint-toolexec zu vermeiden, und baue:
GOCACHE=/tmp/failpoint-cache go build -toolexec path/to/failpoint-toolexec
Aktiviere Failpoints mit der Umgebungsvariable GO_FAILPOINTS
GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
Du kannst auch go run oder go test verwenden, z. B.:
GOCACHE=/tmp/failpoint-cache GO_FAILPOINTS="main/testPanic=return(true)" go run -toolexec path/to/failpoint-toolexec your-program.go
Definiere Failpoints als gültigen Golang-Code, nicht als Kommentare oder etwas anderes
Failpoints verursachen keine zusätzlichen Kosten
Failpoint-Routinen sind schreib-/lesbar und sollten von einem Compiler geprüft werden
Der durch die Failpoint-Definition erzeugte Code ist leicht lesbar
Die Zeilennummern bleiben identisch mit den injizierten Codes (einfacheres Debugging)
Unterstützung paralleler Tests mit context.Context
Failpoint
Ein Failpoint ist ein Codeausschnitt, der nur ausgeführt wird, wenn der entsprechende Failpoint aktiv ist.
Die Closure wird niemals ausgeführt, wenn failpoint.Disable("failpoint-name-for-demo") ausgeführt wird.
var outerVar = "declare in outer scope"
failpoint.Inject("failpoint-name-for-demo", func(val failpoint.Value) {
fmt.Println("unit-test", val, outerVar)
})
Markerfunktionen
Sie sind nur leere Funktionen
Sie sind leicht zu schreiben/lesen
Sie führen eine Compilerprüfung für Failpoints ein, die im regulären Modus nicht kompilieren können, wenn der Failpoint-Code ungültig ist
Liste der Markerfunktionen
func Inject(fpname string, fpblock func(val Value)) {}func InjectContext(fpname string, ctx context.Context, fpblock func(val Value)) {}func InjectCall(fpname string, args ...any) {}func Break(label ...string) {}func Goto(label string) {}func Continue(label ...string) {}func Fallthrough() {}func Return(results ...interface{}) {}func Label(label string) {}Unterstützte Failpoint-Umgebungsvariablen
Failpoints können durch Exportieren von Umgebungsvariablen mit dem folgenden Muster aktiviert werden, das den FreeBSD-Failpoint-SYSCTL-VARIABLEN recht ähnlich ist.
Hinweis: InjectCall kann nicht über Umgebungsvariablen aktiviert werden.
[<percent>%][<count>*]<type>[(args...)][-><more terms>]
Das Argument gibt an, welche Aktion ausgeführt werden soll; es kann eines der folgenden sein:
Du kannst failpoint.Inject aufrufen, um einen Failpoint an der Aufrufstelle zu injizieren, wobei failpoint-name
zum Auslösen des Failpoints verwendet wird und failpoint-closure als Rumpf der IF-Anweisung expandiert wird.
failpoint.Inject("failpoint-name", func(val failpoint.Value) {
failpoint.Return("unit-test", val)
})
Der konvertierte Code sieht wie folgt aus:
if val, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
return "unit-test", val
}
failpoint.Value ist der Wert, der durch failpoint.Enable("failpoint-name", "return(5)") übergeben wird,
und kann ignoriert werden.
failpoint.Inject("failpoint-name", func(_ failpoint.Value) {
fmt.Println("unit-test")
})
ODER
failpoint.Inject("failpoint-name", func() {
fmt.Println("unit-test")
})
Und der konvertierte Code sieht wie folgt aus:
if _, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
fmt.Println("unit-test")
}
Außerdem kann die Failpoint-Closure eine Funktion sein, die context.Context entgegennimmt. Du kannst
mit context.Context eigene Dinge tun, z. B. steuern, ob ein Failpoint in
parallelen Tests oder anderen Fällen aktiv ist. Zum Beispiel: