
Libreria di fault-injection per Go che aggiunge failpoint controllabili a runtime per innescare panic, sleep, return e percorsi di errore condizionali tramite variabili d'ambiente.
Un'implementazione dei failpoint per Golang. I failpoint vengono usati per aggiungere punti nel codice in cui gli errori possono essere iniettati in modo controllato dall'utente. Un failpoint è uno snippet di codice che viene eseguito solo quando il corrispondente failpoint è attivo.
failpoint-ctl)Compila failpoint-ctl dal sorgente
git clone https://github.com/pingcap/failpoint.git
cd failpoint
make
ls bin/failpoint-ctl
Inietta i failpoint nel tuo programma, ad esempio:
package main
import "github.com/pingcap/failpoint"
func main() {
failpoint.Inject("testPanic", func() {
panic("failpoint triggerd")
})
}
Trasforma il tuo codice con failpoint-ctl enable
Compila con go build
Abilita i failpoint con la variabile d'ambiente GO_FAILPOINTS
GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
Nota: GO_FAILPOINTS non funziona con il tipo di marcatore InjectCall.
Se usi go run per eseguire il test, non dimenticare di aggiungere il file generato binding__failpoint_binding__.go al tuo comando, ad esempio:
GO_FAILPOINTS="main/testPanic=return(true)" go run your-program.go binding__failpoint_binding__.go
failpoint-toolexec)Compila failpoint-toolexec dal sorgente
git clone https://github.com/pingcap/failpoint.git
cd failpoint
make
ls bin/failpoint-toolexec
Inietta i failpoint nel tuo programma, ad esempio:
package main
import "github.com/pingcap/failpoint"
func main() {
failpoint.Inject("testPanic", func() {
panic("failpoint triggerd")
})
}
Usa una cache di build separata per evitare di mescolare le cache senza failpoint-toolexec, e compila
GOCACHE=/tmp/failpoint-cache go build -toolexec path/to/failpoint-toolexec
Abilita i failpoint con la variabile d'ambiente GO_FAILPOINTS
GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
Puoi anche usare go run o go test, ad esempio:
GOCACHE=/tmp/failpoint-cache GO_FAILPOINTS="main/testPanic=return(true)" go run -toolexec path/to/failpoint-toolexec your-program.go
Definisci il failpoint in codice Golang valido, non in commenti o altro
Un failpoint non comporta alcun costo aggiuntivo
La routine del failpoint è scrivibile/leggibile e deve essere controllata da un compilatore
Il codice generato dalla definizione del failpoint è facile da leggere
Mantieni gli stessi numeri di riga dei codici di iniezione (più facile da debuggare)
Supporto per test paralleli con context.Context
Failpoint
Un failpoint è uno snippet di codice che viene eseguito solo quando il corrispondente failpoint è attivo.
La closure non verrà mai eseguita se viene eseguito failpoint.Disable("failpoint-name-for-demo").
var outerVar = "declare in outer scope"
failpoint.Inject("failpoint-name-for-demo", func(val failpoint.Value) {
fmt.Println("unit-test", val, outerVar)
})
Funzioni marcatore
Sono solo funzioni vuote
Sono facili da scrivere/leggere
Introducono un controllo del compilatore per i failpoint che non possono compilare in modalità normale se il codice del failpoint non è valido
Elenco delle funzioni marcatore
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) {}Variabili d'ambiente supportate per i failpoint
Un failpoint può essere abilitato esportando variabili d'ambiente con il seguente pattern, molto simile a freebsd failpoint SYSCTL VARIABLES
Nota: InjectCall non può essere abilitato tramite variabili d'ambiente.
[<percent>%][<count>*]<type>[(args...)][-><more terms>]
L'argomento specifica quale azione intraprendere; può essere uno dei seguenti:
Puoi chiamare failpoint.Inject per iniettare un failpoint nel punto di chiamata, dove failpoint-name viene usato per attivare il failpoint e failpoint-closure verrà espansa come corpo dell'istruzione IF.
failpoint.Inject("failpoint-name", func(val failpoint.Value) {
failpoint.Return("unit-test", val)
})
Il codice convertito è simile a:
if val, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
return "unit-test", val
}
failpoint.Value è il valore passato da failpoint.Enable("failpoint-name", "return(5)") che può essere ignorato.
failpoint.Inject("failpoint-name", func(_ failpoint.Value) {
fmt.Println("unit-test")
})
OPPURE
failpoint.Inject("failpoint-name", func() {
fmt.Println("unit-test")
})
E il codice convertito è simile a:
if _, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
fmt.Println("unit-test")
}
Inoltre, la closure del failpoint può essere una funzione che accetta context.Context. Puoi fare cose personalizzate con context.Context, come controllare se un failpoint è attivo nei test paralleli o in altri casi. Per esempio,
failpoint.InjectContext(ctx, "failpoint-name", func(val failpoint.Value) {
fmt.Println("unit-test", val)
})