An implementation of failpoints for Golang.

Overview

failpoint

LICENSE Language Go Report Card Build Status Coverage Status Mentioned in Awesome Go

An implementation of failpoints for Golang. Fail points are used to add code points where errors may be injected in a user controlled fashion. Fail point is a code snippet that is only executed when the corresponding failpoint is active.

Quick Start

  1. Build failpoint-ctl from source

    git clone https://github.com/pingcap/failpoint.git
    cd failpoint
    make
    ls bin/failpoint-ctl
  2. Inject failpoints to your program, eg:

    package main
    
    import "github.com/pingcap/failpoint"
    
    func main() {
        failpoint.Inject("testPanic", func() {
            panic("failpoint triggerd")
        })
    }
  3. Transfrom your code with failpoint-ctl enable

  4. Build with go build

  5. Enable failpoints with GO_FAILPOINTS environment variable

    GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
  6. If you use go run to run the test, don't forget to add the generated binding__failpoint_binding__.go in your command, like:

    GO_FAILPOINTS="main/testPanic=return(true)" go run your-program.go binding__failpoint_binding__.go

Design principles

  • Define failpoint in valid Golang code, not comments or anything else

  • Failpoint does not have any extra cost

    • Will not take effect on regular logic
    • Will not cause regular code performance regression
    • Failpoint code will not appear in the final binary
  • Failpoint routine is writable/readable and should be checked by a compiler

  • Generated code by failpoint definition is easy to read

  • Keep the line numbers same with the injecting codes(easier to debug)

  • Support parallel tests with context.Context

Key concepts

  • Failpoint

    Faillpoint is a code snippet that is only executed when the corresponding failpoint is active. The closure will never be executed if failpoint.Disable("failpoint-name-for-demo") is executed.

    var outerVar = "declare in outer scope"
    failpoint.Inject("failpoint-name-for-demo", func(val failpoint.Value) {
        fmt.Println("unit-test", val, outerVar)
    })
  • Marker functions

    • It is just an empty function

      • To hint the rewriter to rewrite with an equality statement
      • To receive some parameters as the rewrite rule
      • It will be inline in the compiling time and emit nothing to binary (zero cost)
      • The variables in external scope can be accessed in closure by capturing, and the converted code is still legal because all the captured-variables location in outer scope of IF statement.
    • It is easy to write/read

    • Introduce a compiler check for failpoints which cannot compile in the regular mode if failpoint code is invalid

  • Marker funtion list

    • func Inject(fpname string, fpblock func(val Value)) {}
    • func InjectContext(fpname string, ctx context.Context, fpblock func(val Value)) {}
    • func Break(label ...string) {}
    • func Goto(label string) {}
    • func Continue(label ...string) {}
    • func Fallthrough() {}
    • func Return(results ...interface{}) {}
    • func Label(label string) {}
  • Supported failpoint environment variable

    failpoint can be enabled by export environment variables with the following patten, which is quite similar to freebsd failpoint SYSCTL VARIABLES

    [<percent>%][<count>*]<type>[(args...)][-><more terms>]

    The argument specifies which action to take; it can be one of:

    • off: Take no action (does not trigger failpoint code)
    • return: Trigger failpoint with specified argument
    • sleep: Sleep the specified number of milliseconds
    • panic: Panic
    • break: Execute gdb and break into debugger
    • print: Print failpoint path for inject variable
    • pause: Pause will pause until the failpoint is disabled

How to inject a failpoint to your program

  • You can call failpoint.Inject to inject a failpoint to the call site, where failpoint-name is used to trigger the failpoint and failpoint-closure will be expanded as the body of the IF statement.

    failpoint.Inject("failpoint-name", func(val failpoint.Value) {
        failpoint.Return("unit-test", val)
    })

    The converted code looks like:

    if val, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
        return "unit-test", val
    }
  • failpoint.Value is the value that passes by failpoint.Enable("failpoint-name", "return(5)") which can be ignored.

    failpoint.Inject("failpoint-name", func(_ failpoint.Value) {
        fmt.Println("unit-test")
    })

    OR

    failpoint.Inject("failpoint-name", func() {
        fmt.Println("unit-test")
    })

    And the converted code looks like:

    if _, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
        fmt.Println("unit-test")
    }
  • Also, the failpoint closure can be a function which takes context.Context. You can do some customized things with context.Context like controlling whether a failpoint is active in parallel tests or other cases. For example,

    failpoint.InjectContext(ctx, "failpoint-name", func(val failpoint.Value) {
        fmt.Println("unit-test", val)
    })

    The converted code looks like:

    if val, _err_ := failpoint.EvalContext(ctx, _curpkg_("failpoint-name")); _err_ == nil {
        fmt.Println("unit-test", val)
    }
  • You can ignore context.Context, and this will generate the same code as above non-context version. For example,

    failpoint.InjectContext(nil, "failpoint-name", func(val failpoint.Value) {
        fmt.Println("unit-test", val)
    })

    Becomes

    if val, _err_ := failpoint.EvalContext(nil, _curpkg_("failpoint-name")); _err_ == nil {
        fmt.Println("unit-test", val)
    }
  • You can control a failpoint by failpoint.WithHook

    func (s *dmlSuite) TestCRUDParallel() {
        sctx := failpoint.WithHook(context.Backgroud(), func(ctx context.Context, fpname string) bool {
            return ctx.Value(fpname) != nil // Determine by ctx key
        })
        insertFailpoints = map[string]struct{} {
            "insert-record-fp": {},
            "insert-index-fp": {},
            "on-duplicate-fp": {},
        }
        ictx := failpoint.WithHook(context.Backgroud(), func(ctx context.Context, fpname string) bool {
            _, found := insertFailpoints[fpname] // Only enables some failpoints.
            return found
        })
        deleteFailpoints = map[string]struct{} {
            "tikv-is-busy-fp": {},
            "fetch-tso-timeout": {},
        }
        dctx := failpoint.WithHook(context.Backgroud(), func(ctx context.Context, fpname string) bool {
            _, found := deleteFailpoints[fpname] // Only disables failpoints. 
            return !found
        })
        // other DML parallel test cases.
        s.RunParallel(buildSelectTests(sctx))
        s.RunParallel(buildInsertTests(ictx))
        s.RunParallel(buildDeleteTests(dctx))
    }
  • If you use a failpoint in the loop context, maybe you will use other marker functions.

    failpoint.Label("outer")
    for i := 0; i < 100; i++ {
        inner:
            for j := 0; j < 1000; j++ {
                switch rand.Intn(j) + i {
                case j / 5:
                    failpoint.Break()
                case j / 7:
                    failpoint.Continue("outer")
                case j / 9:
                    failpoint.Fallthrough()
                case j / 10:
                    failpoint.Goto("outer")
                default:
                    failpoint.Inject("failpoint-name", func(val failpoint.Value) {
                        fmt.Println("unit-test", val.(int))
                        if val == j/11 {
                            failpoint.Break("inner")
                        } else {
                            failpoint.Goto("outer")
                        }
                    })
            }
        }
    }

    The above code block will generate the following code:

    outer:
        for i := 0; i < 100; i++ {
        inner:
            for j := 0; j < 1000; j++ {
                switch rand.Intn(j) + i {
                case j / 5:
                    break
                case j / 7:
                    continue outer
                case j / 9:
                    fallthrough
                case j / 10:
                    goto outer
                default:
                    if val, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
                        fmt.Println("unit-test", val.(int))
                        if val == j/11 {
                            break inner
                        } else {
                            goto outer
                        }
                    }
                }
            }
        }
  • You may doubt why we do not use label, break, continue, and fallthrough directly instead of using failpoint marker functions.

    • Any unused symbol like an ident or a label is not permitted in Golang. It will be invalid if some label is only used in the failpoint closure. For example,

      label1: // compiler error: unused label1
          failpoint.Inject("failpoint-name", func(val failpoint.Value) {
              if val.(int) == 1000 {
                  goto label1 // illegal to use goto here
              }
              fmt.Println("unit-test", val)
          })
    • break and continue can only be used in the loop context, which is not legal in the Golang code if we use them in closure directly.

Some complicated failpoints demo

  • Inject a failpoint to the IF INITIAL statement or CONDITIONAL expression

    if a, b := func() {
        failpoint.Inject("failpoint-name", func(val failpoint.Value) {
            fmt.Println("unit-test", val)
        })
    }, func() int { return rand.Intn(200) }(); b > func() int {
        failpoint.Inject("failpoint-name", func(val failpoint.Value) int {
            return val.(int)
        })
        return rand.Intn(3000)
    }() && b < func() int {
        failpoint.Inject("failpoint-name-2", func(val failpoint.Value) {
            return rand.Intn(val.(int))
        })
        return rand.Intn(6000)
    }() {
        a()
        failpoint.Inject("failpoint-name-3", func(val failpoint.Value) {
            fmt.Println("unit-test", val)
        })
    }

    The above code block will generate something like this:

    if a, b := func() {
        if val, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
            fmt.Println("unit-test", val)
        }
    }, func() int { return rand.Intn(200) }(); b > func() int {
        if val, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
            return val.(int)
        }
        return rand.Intn(3000)
    }() && b < func() int {
        if val, ok := failpoint.Eval(_curpkg_("failpoint-name-2")); ok {
            return rand.Intn(val.(int))
        }
        return rand.Intn(6000)
    }() {
        a()
        if val, ok := failpoint.Eval(_curpkg_("failpoint-name-3")); ok {
            fmt.Println("unit-test", val)
        }
    }
  • Inject a failpoint to the SELECT statement to make it block one CASE if the failpoint is active

    func (s *StoreService) ExecuteStoreTask() {
        select {
        case <-func() chan *StoreTask {
            failpoint.Inject("priority-fp", func(_ failpoint.Value) {
                return make(chan *StoreTask)
            })
            return s.priorityHighCh
        }():
            fmt.Println("execute high priority task")
    
        case <- s.priorityNormalCh:
            fmt.Println("execute normal priority task")
    
        case <- s.priorityLowCh:
            fmt.Println("execute normal low task")
        }
    }

    The above code block will generate something like this:

    func (s *StoreService) ExecuteStoreTask() {
        select {
        case <-func() chan *StoreTask {
            if _, ok := failpoint.Eval(_curpkg_("priority-fp")); ok {
                return make(chan *StoreTask)
            })
            return s.priorityHighCh
        }():
            fmt.Println("execute high priority task")
    
        case <- s.priorityNormalCh:
            fmt.Println("execute normal priority task")
    
        case <- s.priorityLowCh:
            fmt.Println("execute normal low task")
        }
    }
  • Inject a failpoint to dynamically extend SWITCH CASE arms

    switch opType := operator.Type(); {
    case opType == "balance-leader":
        fmt.Println("create balance leader steps")
    
    case opType == "balance-region":
        fmt.Println("create balance region steps")
    
    case opType == "scatter-region":
        fmt.Println("create scatter region steps")
    
    case func() bool {
        failpoint.Inject("dynamic-op-type", func(val failpoint.Value) bool {
            return strings.Contains(val.(string), opType)
        })
        return false
    }():
        fmt.Println("do something")
    
    default:
        panic("unsupported operator type")
    }

    The above code block will generate something like this:

    switch opType := operator.Type(); {
    case opType == "balance-leader":
        fmt.Println("create balance leader steps")
    
    case opType == "balance-region":
        fmt.Println("create balance region steps")
    
    case opType == "scatter-region":
        fmt.Println("create scatter region steps")
    
    case func() bool {
        if val, ok := failpoint.Eval(_curpkg_("dynamic-op-type")); ok {
            return strings.Contains(val.(string), opType)
        }
        return false
    }():
        fmt.Println("do something")
    
    default:
        panic("unsupported operator type")
    }
  • More complicated failpoints

    • There are more complicated failpoint sites that can be injected to
      • for the loop INITIAL statement, CONDITIONAL expression and POST statement
      • for the RANGE statement
      • SWITCH INITIAL statement
    • Anywhere you can call a function

Failpoint name best practice

As you see above, _curpkg_ will automatically wrap the original failpoint name in failpoint.Eval call. You can think of _curpkg_ as a macro that automatically prepends the current package path to the failpoint name. For example,

package ddl // which parent package is `github.com/pingcap/tidb`

func demo() {
	// _curpkg_("the-original-failpoint-name") will be expanded as `github.com/pingcap/tidb/ddl/the-original-failpoint-name`
	if val, ok := failpoint.Eval(_curpkg_("the-original-failpoint-name")); ok {...}
}

You do not need to care about _curpkg_ in your application. It is automatically generated after running failpoint-ctl enable and is deleted with failpoint-ctl disable.

Because all failpoints in a package share the same namespace, we need to be careful to avoid name conflict. There are some recommended naming rules to improve this situation.

  • Keep name unique in current subpackage

  • Use a self-explanatory name for the failpoint

    You can enable failpoints by environment variables

    GO_FAILPOINTS="github.com/pingcap/tidb/ddl/renameTableErr=return(100);github.com/pingcap/tidb/planner/core/illegalPushDown=return(true);github.com/pingcap/pd/server/schedulers/balanceLeaderFailed=return(true)"

Implementation details

  1. Define a group of marker functions
  2. Parse imports and prune a source file which does not import a failpoint
  3. Traverse AST to find marker function calls
  4. Marker function calls will be rewritten with an IF statement, which calls failpoint.Eval to determine whether a failpoint is active and executes failpoint code if the failpoint is enabled

rewrite-demo

Acknowledgments

  • Thanks gofail to provide initial implementation.
Issues
  •  go test failed

    go test failed

    Bug Report

    go get github.com/pingcap/failpoint/ cd failpoint go test .

    /V/d/g/s/g/p/failpoint (master|✔) [2] $ go test .
    failed to parse "invalid" past "invalid"
    failed to enable "failpoint-name=invalid" (failpoint: could not parse terms)
    
    ----------------------------------------------------------------------
    FAIL: http_test.go:47: httpSuite.TestServeHTTP
    
    http_test.go:124:
        c.Assert(err, IsNil)
    ... value *url.Error = &url.Error{Op:"Get", URL:"http://127.0.0.1:23389/failpoint-env1", Err:(*net.OpError)(0xc000172050)} ("Get http://127.0.0.1:23389/failpoint-env1: dial tcp 127.0.0.1:23389: connect: connection refused")
    
    failed to parse "invalid" past "invalid"
    failed to enable "runtime-test-2=invalid" (failpoint: could not parse terms)
    
    ----------------------------------------------------------------------
    FAIL: runtime_test.go:21: runtimeSuite.TestRuntime
    
    runtime_test.go:133:
        c.Assert(ok, IsTrue)
    ... obtained bool = false
    
    OOPS: 1 passed, 2 FAILED
    --- FAIL: TestFailpoint (1.00s)
    failed to parse "invalid" past "invalid"
    failed to enable "failpoint-name=invalid" (failpoint: could not parse terms)
    
    ----------------------------------------------------------------------
    FAIL: http_test.go:47: httpSuite.TestServeHTTP
    
    http_test.go:124:
        c.Assert(err, IsNil)
    ... value *url.Error = &url.Error{Op:"Get", URL:"http://127.0.0.1:23389/failpoint-env1", Err:(*net.OpError)(0xc000172d70)} ("Get http://127.0.0.1:23389/failpoint-env1: dial tcp 127.0.0.1:23389: connect: connection refused")
    
    failed to parse "invalid" past "invalid"
    failed to enable "runtime-test-2=invalid" (failpoint: could not parse terms)
    
    ----------------------------------------------------------------------
    FAIL: runtime_test.go:21: runtimeSuite.TestRuntime
    
    runtime_test.go:133:
        c.Assert(ok, IsTrue)
    ... obtained bool = false
    
    OOPS: 1 passed, 2 FAILED
    --- FAIL: TestHttp (1.00s)
    failed to parse "invalid" past "invalid"
    failed to enable "failpoint-name=invalid" (failpoint: could not parse terms)
    
    ----------------------------------------------------------------------
    FAIL: http_test.go:47: httpSuite.TestServeHTTP
    
    http_test.go:124:
        c.Assert(err, IsNil)
    ... value *url.Error = &url.Error{Op:"Get", URL:"http://127.0.0.1:23389/failpoint-env1", Err:(*net.OpError)(0xc000146500)} ("Get http://127.0.0.1:23389/failpoint-env1: dial tcp 127.0.0.1:23389: connect: connection refused")
    
    failed to parse "invalid" past "invalid"
    failed to enable "runtime-test-2=invalid" (failpoint: could not parse terms)
    
    ----------------------------------------------------------------------
    FAIL: runtime_test.go:21: runtimeSuite.TestRuntime
    
    runtime_test.go:133:
        c.Assert(ok, IsTrue)
    ... obtained bool = false
    
    OOPS: 1 passed, 2 FAILED
    --- FAIL: TestNewRestorer (1.01s)
    FAIL
    FAIL	github.com/pingcap/failpoint	3.030s
    /V/d/g/s/g/p/failpoint (master|✔) [1] $
    
    bug 
    opened by nkbai 8
  • code: improve rewriter test coverage

    code: improve rewriter test coverage

    What problem does this PR solve?

    Improve test coverage.

    What is changed and how it works?

    Improve test coverage.

    Check List

    Tests

    • Unit test

    Side effects

    N/A

    Related changes

    N/A

    S:LGT1 
    opened by Deardrops 8
  • rewriter,restorer: add the DO NOT EDIT comment to the generated Go code

    rewriter,restorer: add the DO NOT EDIT comment to the generated Go code

    What problem does this PR solve?

    Fix #37.

    What is changed and how it works?

    This is a resubmission of #39 which puts the comment at the end of the file.

    Check List

    Tests

    • Unit test

    Side effects

    Related changes

    S:PTAL 
    opened by kennytm 5
  • code: auto extend failpoint name with package name

    code: auto extend failpoint name with package name

    Signed-off-by: Lonng [email protected]

    What problem does this PR solve?

    Will auto extend failpoint name with package name. For example,

    pacakge ddl // which contains in project github.com/pingcap/tidb
    
    func createTable() {
    	failpoint.Inject("createTableErr"), func() {...})
    }
    

    The name will auto extend to github.com/pingcap/tidb/ddl/createTableErr

    What is changed and how it works?

    Rewrite some rules

    Check List

    Tests

    • Unit test

    Side effects

    N/A

    Related changes

    N/A

    feature S:LGT1 
    opened by lonng 5
  • Upgrade go-diff for support Golang 1.16

    Upgrade go-diff for support Golang 1.16

    What problem does this PR solve?

    After upgrade golang to 1.16 at TiDB make test will got error messages:

    ../../../../pkg/mod/github.com/pingcap/[email protected]/code/restorer.go:24:2: missing go.sum entry for module providing package github.com/sergi/go-diff/diffmatchpatch (imported by github.com/pingcap/failpoint/code); to add:
    	go get github.com/pingcap/failpoint/[email protected]
    

    What is changed and how it works?

    Upgrade go-diff to v1.1.0 will solve this problem.

    Check List

    Tests

    • No Code

    Side effects

    • Not sure yet, but tests are passed

    Related changes

    • Need to upgrade go.mod at TiDB and other use failpoint projects
    opened by blacktear23 4
  • Export Failpoint/Failpoints as public types

    Export Failpoint/Failpoints as public types

    What problem does this PR solve?

    The failpoint package is not flexible enough in use cases like

    • Using in the unit tests
    • Building a mock library
    • As a core of other fail point implementations
    • Making a service fail fast
    • Chaos Engineering

    What is changed and how it works?

    • Make Failpointa public type
    • Make Failpoints a public type
    • Return an error when calling Eval
    • Remove the failpath field from term struct.

    A proposal is available in Chinese: https://docs.google.com/document/d/1OUheSlxWkQZqIb8mLZYKE8pxlSc1YElXFutjnPQe5UE/edit?usp=sharing

    Check List

    Tests

    • Unit test
    • Manual test (add detailed scripts or steps below)

    Side effects

    • Breaking backward compatibility

    The signature of Eval is changed.

    opened by shafreeck 4
  • rewriter,restorer: add the DO NOT EDIT comment to the generated Go code

    rewriter,restorer: add the DO NOT EDIT comment to the generated Go code

    What problem does this PR solve?

    Fix #37.

    What is changed and how it works?

    Add the // Code generated by failpoint DO NOT EDIT. at the beginning of the failpoint-enabled and binding source code. (Not touching the stash files for now.)

    Check List

    Tests

    • Unit test

    Side effects

    Related changes

    S:LGT1 
    opened by kennytm 4
  • failpoint can not use a constant-string as marker function name

    failpoint can not use a constant-string as marker function name

    Bug Report

    Please answer these questions before submitting your issue. Thanks!

    1. What did you do? If possible, provide a recipe for reproducing the error. failpoint fail to rewrite the following circumstance:
    package main
    
    import (
    
        "github.com/pingcap/failpoint"
    )
    const (
       Marker = "failpoint-name"
    )
    func F1(name string) string {
    
        failpoint.Inject(Marker, nil)
    
        return "hello " + name
    }
    
    
    
    1. What did you expect to see? This file can be correctly rewritten by failpoint-ctl tool

    2. What did you see instead? rewrite failed.

    /bin/sh: Rewrite: command not found
    make: *** [failpoint-enable] Error 127
    
    
    1. Versions of the failpoint

      • failpoint-ctl version (run failpoint-ctl -V):

    ReleaseVersion bf45ab2 BuildTS 2019-05-15 09:48:44 GitHash bf45ab20bfc48d624b61944a4820768c16d7db99 GitBranch master GoVersion go version go1.12 darwin/amd64

    bug 
    opened by spongedu 4
  • README: refine quick start guide

    README: refine quick start guide

    What problem does this PR solve?

    Refine Quick Start Guide section of README.md according to the discussion in https://github.com/pingcap/failpoint/issues/33

    What is changed and how it works?

    Check List

    The README.md Tests

    • No code

    Side effects

    None

    Related changes

    • Need to update the documentation
    opened by spongedu 4
  • *: add comment for exported elements

    *: add comment for exported elements

    Signed-off-by: Lonng [email protected]

    What problem does this PR solve?

    Add comment for exported elements to make linter happy.

    What is changed and how it works?

    enhancement S:LGT1 
    opened by lonng 4
  • *: refactor interface to make lint happy

    *: refactor interface to make lint happy

    Signed-off-by: Lonng [email protected]

    What problem does this PR solve?

    Refactor Eval/EvalContext/InjectContext to make code more idiom.

    What is changed and how it works?

    Check List

    Tests

    • Unit test
    • Integration test

    Side effects

    N/A

    Related changes

    N/A

    opened by lonng 4
  • Register an action func to be called when a failpoint is evaluated

    Register an action func to be called when a failpoint is evaluated

    Feature Request

    Is your feature request related to a problem? Please describe: When writing a unit test, I want to know if a failpoint is evaluated and the evaluated value.

    Describe the feature you'd like: When enabling a failpoint, pass in a function callback func(val interface{}). Afterwards, whenever the failpoint is evaluated, failpoint is responsible to call callback.

    Describe alternatives you've considered: Allow user to register a function to replace term. Every time the failpoint is evaluated, it calls the function to get the value.

    Teachability, Documentation, Adoption, Optimization:

    opened by disksing 0
  • Why the type of failpoint.Value can't be string

    Why the type of failpoint.Value can't be string

    the definition in the source code about Value's type in "github.com/pingcap/failpoint/failpoint.go"

    	// Value represents value that retrieved from failpoint terms.
    	// It can be used as following types:
    	// 1. val.(int)      // GO_FAILPOINTS="failpoint-name=return(1)"
    	// 2. val.(string)   // GO_FAILPOINTS="failpoint-name=return('1')"
    	// 3. val.(bool)     // GO_FAILPOINTS="failpoint-name=return(true)"
    	Value interface{}
    

    but in my code

    failpoint.Inject("demoPanic", func(val failpoint.Value) {
    		fmt.Println(val)
    		fmt.Println(val.(string))
    }
    

    and start my progrom with cmd: GO_FAILPOINTS="mypackagename/demoPanic=return('100')" ./beego-backend An exception was thrown [panic.go:522] Handler crashed with error interface conversion: failpoint.Value is bool, not string ···

    question 
    opened by SmartMalphite 2
  • Create Release Binary amd and Darwin

    Create Release Binary amd and Darwin

    Feature Request

    Is your feature request related to a problem? Please describe:

    Yes

    Describe the feature you'd like:

    Release tags and binary for failpoint.

    Describe alternatives you've considered:

    Create git tags and release binary for both linux and mac builds? Its easier for download the builds directly from github and include in CI/CD pipelines as apposed to clone the entire repo and make builds.

    Teachability, Documentation, Adoption, Optimization:

    Users wanting to include failopint in their build pipelines , could directly curl/wget the tar files from github and use failpoint-ctl enable in their repository. Its much simpler that way , instead of cloning the entire repo and making builds manually.

    opened by rahul8590 1
  • failpoint not activated

    failpoint not activated

    I write this in foo.go:

    func pingCapFail() (string, failpoint.Value) {
    	failpoint.Inject("failpoint-name", func(val failpoint.Value) {
    		failpoint.Return("unit-test", val)
    	})
    	return "success", nil
    }
    

    and i run failpoint-ctl enable , code becomes this;

    func pingCapFail() (string, failpoint.Value) {
    	if val, ok := failpoint.Eval(_curpkg_("failpoint-name")); ok {
    		return "unit-test", val
    	}
    	return "success", nil
    }
    

    then in foo_test.go ,i write my test code like this:

    func Test_pingCapFail(t *testing.T) {
    	failpoint.Enable("failpoint-name", "return(5)")
    	got, got1 := pingCapFail()
    	if got != "unit-test" {
    		t.Errorf("pingCapFail() got = %v, want %v", got, "unit-test")
    	}
    	if !reflect.DeepEqual(got1, 5) {
    		t.Errorf("pingCapFail() got1 = %v, want %v", got1, 5)
    	}
    }
    

    and it fails, and obviously the failpoint is not activated, can someone tell me why?

    question 
    opened by yechenyuyu 1
  • failpoint doesn't work if the marker function is defined in the main package

    failpoint doesn't work if the marker function is defined in the main package

    Bug Report

    Please answer these questions before submitting your issue. Thanks!

    1. What did you do? If possible, provide a recipe for reproducing the error.

    For example, we have a file named main.go:

    package main
    
    import (
    
        "github.com/pingcap/failpoint"
    )
    
    func F1(name string) string {
    
        failpoint.Inject("failpoint-name", nil)
    
        return "hello " + name
    }
    
    func main() {
        failpoint.Enable("failpoint-name", "panic")
        F1("ttt")
    }
    

    After transfrom the code with failpoint-ctl enable, the generated binding__failpoint_binding__.go looks like this:

    package main
    
    import "reflect"
    
    type __failpointBindingType struct {pkgpath string}
    var __failpointBindingCache = &__failpointBindingType{}
    
    func init() {
        __failpointBindingCache.pkgpath = reflect.TypeOf(__failpointBindingType{}).PkgPath()
    }
    func _curpkg_(name string) string {
        return  __failpointBindingCache.pkgpath + "/" + name
    }
    
    

    You may notice that binding__failpoint_binding__.go is also in package main, that means binding__failpoint_binding__.go could not be found by main.go.

    Run go run main.go then, you will find the following error:

     ./main.go:8:17: undefined: _curpkg_
    
    1. What did you expect to see?

    program panics due to failpoint

    1. What did you see instead?
     ./main.go:8:17: undefined: _curpkg_
    
    1. Versions of the failpoint

    $ ./bin/failpoint-ctl -V ReleaseVersion bf45ab2 BuildTS 2019-05-15 09:48:44 GitHash bf45ab20bfc48d624b61944a4820768c16d7db99 GitBranch master GoVersion go version go1.12 darwin/amd64

    bug 
    opened by spongedu 7
  • Is there any demo or userguide for failpoint?

    Is there any demo or userguide for failpoint?

    I cannot have any idea that how to use failpoint. I tried write demo like this:

     var outerVar = "declare in outer scope"
    
    err := failpoint.Enable("failpoint-name", "return")
    if err != nil {
    	fmt.Println(err)
    	return
    }
    
    failpoint.Inject("failpoint-name", func(val failpoint.Value) {
    	fmt.Println("unit-test", val, outerVar)
    })
    

    There isn't have any output. How could I use it in the right way?

    question 
    opened by sh1yu 7
Owner
PingCAP
The team behind TiDB TiKV, an open source MySQL compatible NewSQL HTAP database
PingCAP
A Go implementation of Servirtium, a library that helps test interactions with APIs.

Servirtium is a server that serves as a man-in-the-middle: it processes incoming requests, forwards them to a destination API and writes the response into a Markdown file with a special format that is common across all of the implementations of the library.

Servirtium 6 Jun 16, 2022
An implementation of the MarsRover coding challenge

MarsRover An implementation of the MarsRover coding challenge Mars Rovers A squad of robotic rovers are to be landed by NASA on a plateau on Mars. Thi

Emre 2 Jun 7, 2022
Fortio load testing library, command line tool, advanced echo server and web UI in go (golang). Allows to specify a set query-per-second load and record latency histograms and other useful stats.

Fortio Fortio (Φορτίο) started as, and is, Istio's load testing tool and now graduated to be its own project. Fortio is also used by, among others, Me

Fortio (Φορτίο) 2.6k Aug 9, 2022
Golang HTTP client testing framework

flute Golang HTTP client testing framework Presentation https://speakerdeck.com/szksh/flute-golang-http-client-testing-framework Overview flute is the

Shunsuke Suzuki 17 Apr 19, 2022
Extremely flexible golang deep comparison, extends the go testing package and tests HTTP APIs

go-testdeep Extremely flexible golang deep comparison, extends the go testing package. Latest news Synopsis Description Installation Functions Availab

Maxime Soulé 312 Aug 4, 2022
Cucumber for golang

Godog The API is likely to change a few times before we reach 1.0.0 Please read the full README, you may find it very useful. And do not forget to pee

Cucumber 1.7k Aug 10, 2022
Testing API Handler written in Golang.

Gofight API Handler Testing for Golang Web framework. Support Framework Http Handler Golang package http provides HTTP client and server implementatio

Bo-Yi Wu 423 Jul 27, 2022
Sql mock driver for golang to test database interactions

Sql driver mock for Golang sqlmock is a mock library implementing sql/driver. Which has one and only purpose - to simulate any sql driver behavior in

DATA-DOG 4.6k Aug 6, 2022
Immutable transaction isolated sql driver for golang

Single transaction based sql.Driver for GO Package txdb is a single transaction based database sql driver. When the connection is opened, it starts a

DATA-DOG 457 Aug 4, 2022
HTTP mock for Golang: record and replay HTTP/HTTPS interactions for offline testing

govcr A Word Of Warning I'm in the process of partly rewriting govcr to offer better support for cassette mutations. This is necessary because when I

Seb C 107 Aug 12, 2022
HTTP mocking for Golang

httpmock Easy mocking of http responses from external resources. Install Currently supports Go 1.7 - 1.15. v1 branch has to be used instead of master.

Jared Morse 1.4k Aug 12, 2022
A test-friendly replacement for golang's time package

timex timex is a test-friendly replacement for the time package. Usage Just replace your time.Now() by a timex.Now() call, etc. Mocking Use timex.Over

Cabify 63 May 3, 2022
mockery - A mock code autogenerator for Golang

mockery - A mock code autogenerator for Golang

Vektra 3.7k Aug 6, 2022
Cucumber for golang

Godog The API is likely to change a few times before we reach 1.0.0 Please read the full README, you may find it very useful. And do not forget to pee

Cucumber 1.7k Aug 8, 2022
Go (Golang) Fake Data Generator for Struct

Docs faker Struct Data Fake Generator Faker will generate you a fake data based on your Struct. Index Support Getting Started Example Limitation Contr

Iman Tumorang 2k Aug 7, 2022
Check your internet speed right from your terminal. Built on GOlang using chromedp

adhocore/fast A GO lang command line tool to check internet speed right from the terminal. Uses fast.com through headless chrome. Prerequistie Chrome

Jitendra Adhikari 63 Jul 10, 2022
A bytecode-based virtual machine to implement scripting/filtering support in your golang project.

eval-filter Implementation Scripting Facilities Types Built-In Functions Conditionals Loops Functions Case/Switch Use Cases Security Denial of service

Steve Kemp 84 Jul 23, 2022
Lightweight HTTP mocking in Go (aka golang)

httpmock This library builds on Go's built-in httptest library, adding a more mockable interface that can be used easily with other mocking tools like

null 78 Aug 11, 2022
WebDriverAgent ( iOS ) Client Library in Golang

appium/WebDriverAgent Client Library in Golang

雷系泡泡 243 Aug 6, 2022