From b230351a07cbb45ae7119bf1895a9321ca2340dd Mon Sep 17 00:00:00 2001 From: 1fanwang <1fannnw@gmail.com> Date: Sun, 20 Sep 2026 15:13:42 -0700 Subject: [PATCH] terms: skip injected code for off actions Return ErrNotAllowed for off so generated Eval guards skip the injection. Fixes https://github.com/pingcap/failpoint/issues/86 Signed-off-by: 1fanwang <1fannnw@gmail.com> --- README.md | 2 +- failpoints_test.go | 32 ++++++++++++++++++++++++++++++++ terms.go | 2 +- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 95046e4..1986351 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ An implementation of [failpoints][failpoint] for Golang. Fail points are used to The argument specifies which action to take; it can be one of: - - off: Take no action (does not trigger failpoint code) + - off: Take no action (does not trigger failpoint code; `Eval` returns `ErrNotAllowed`) - return: Trigger failpoint with specified argument - sleep: Sleep the specified number of milliseconds - panic: Panic diff --git a/failpoints_test.go b/failpoints_test.go index 000fef9..3a25985 100644 --- a/failpoints_test.go +++ b/failpoints_test.go @@ -229,6 +229,38 @@ func testPanic() { _, _ = failpoint.Eval("test-panic") } +func TestOff(t *testing.T) { + tests := []struct { + terms string + want []failpoint.Value + }{ + {"off", []failpoint.Value{nil, nil}}, + {"off->return(2)", []failpoint.Value{nil, nil}}, + {"1*return(1)->off", []failpoint.Value{1, nil, nil}}, + {"1*off->return(2)", []failpoint.Value{nil, 2, 2}}, + {"0*off->return(2)", []failpoint.Value{2, 2}}, + {"return", []failpoint.Value{struct{}{}, struct{}{}}}, + } + for _, tt := range tests { + t.Run(tt.terms, func(t *testing.T) { + var fps failpoint.Failpoints + require.NoError(t, fps.Enable("test-off", tt.terms)) + for _, want := range tt.want { + value, err := fps.Eval("test-off") + if want == nil { + require.ErrorIs(t, err, failpoint.ErrNotAllowed) + } else { + require.NoError(t, err) + } + require.Equal(t, want, value) + } + status, err := fps.Status("test-off") + require.NoError(t, err) + require.Equal(t, tt.terms, status) + }) + } +} + func TestCall(t *testing.T) { var capturedArg int require.NoError(t, failpoint.EnableCall("test", func(a int) { diff --git a/terms.go b/terms.go index 2595f90..b7e6c8f 100644 --- a/terms.go +++ b/terms.go @@ -310,7 +310,7 @@ var actMap = map[string]actFunc{ func (t *term) do() (interface{}, error) { return t.act(t) } -func actOff(t *term) (interface{}, error) { return nil, nil } +func actOff(t *term) (interface{}, error) { return nil, ErrNotAllowed } func actReturn(t *term) (interface{}, error) { return t.val, nil }