github.com/pingcap/failpoint@v0.0.0-20240412033321-fd0796e60f86/failpoints_test.go (about)

     1  // Copyright 2021 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package failpoint_test
    16  
    17  import (
    18  	"io/ioutil"
    19  	"os"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/pingcap/errors"
    24  	"github.com/pingcap/failpoint"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  func TestFailpoints(t *testing.T) {
    29  	var fps failpoint.Failpoints
    30  
    31  	err := fps.Enable("failpoints-test-1", "return(1)")
    32  	require.NoError(t, err)
    33  	val, err := fps.Eval("failpoints-test-1")
    34  	require.NoError(t, err)
    35  	require.Equal(t, 1, val.(int))
    36  
    37  	err = fps.Enable("failpoints-test-2", "invalid")
    38  	require.EqualError(t, err, `error on failpoints-test-2: failpoint: failed to parse "invalid" past "invalid"`)
    39  
    40  	val, err = fps.Eval("failpoints-test-2")
    41  	require.Error(t, err)
    42  	require.Nil(t, val)
    43  
    44  	err = fps.Disable("failpoints-test-1")
    45  	require.NoError(t, err)
    46  
    47  	val, err = fps.Eval("failpoints-test-1")
    48  	require.Error(t, err)
    49  	require.Nil(t, val)
    50  
    51  	err = fps.Disable("failpoints-test-1")
    52  	require.NoError(t, err)
    53  
    54  	err = fps.Enable("failpoints-test-1", "return(1)")
    55  	require.NoError(t, err)
    56  
    57  	status, err := fps.Status("failpoints-test-1")
    58  	require.NoError(t, err)
    59  	require.Equal(t, "return(1)", status)
    60  
    61  	err = fps.Enable("failpoints-test-3", "return(2)")
    62  	require.NoError(t, err)
    63  
    64  	ch := make(chan struct{})
    65  	go func() {
    66  		time.Sleep(time.Second)
    67  		err := fps.Disable("gofail/testPause")
    68  		require.NoError(t, err)
    69  		close(ch)
    70  	}()
    71  	err = fps.Enable("gofail/testPause", "pause")
    72  	require.NoError(t, err)
    73  	start := time.Now()
    74  	v, err := fps.Eval("gofail/testPause")
    75  	require.NoError(t, err)
    76  	require.Nil(t, v)
    77  
    78  	require.True(t, time.Since(start) > 100*time.Millisecond)
    79  	<-ch
    80  
    81  	err = fps.Enable("failpoints-test-4", "50.0%return(5)")
    82  	require.NoError(t, err)
    83  	var succ int
    84  	for i := 0; i < 1000; i++ {
    85  		val, err = fps.Eval("failpoints-test-4")
    86  		if err == nil && val != nil {
    87  			succ++
    88  			require.Equal(t, 5, val.(int))
    89  		}
    90  	}
    91  
    92  	if succ < 450 || succ > 550 {
    93  		require.FailNow(t, "prop failure: %v", succ)
    94  	}
    95  
    96  	err = fps.Enable("failpoints-test-5", "50*return(5)")
    97  	require.NoError(t, err)
    98  	for i := 0; i < 50; i++ {
    99  		val, err = fps.Eval("failpoints-test-5")
   100  		require.NoError(t, err)
   101  		require.Equal(t, 5, val.(int))
   102  	}
   103  	val, err = fps.Eval("failpoints-test-5")
   104  	require.Equal(t, failpoint.ErrNotAllowed, errors.Cause(err))
   105  	require.Nil(t, val)
   106  
   107  	points := map[string]struct{}{}
   108  	for _, fp := range fps.List() {
   109  		points[fp] = struct{}{}
   110  	}
   111  	require.Contains(t, points, "failpoints-test-1")
   112  	require.Contains(t, points, "failpoints-test-2")
   113  	require.Contains(t, points, "failpoints-test-3")
   114  	require.Contains(t, points, "failpoints-test-4")
   115  	require.Contains(t, points, "failpoints-test-5")
   116  
   117  	err = fps.Enable("failpoints-test-6", "50*return(5)->1*return(true)->1*return(false)->10*return(20)")
   118  	require.NoError(t, err)
   119  	// 50*return(5)
   120  	for i := 0; i < 50; i++ {
   121  		val, err = fps.Eval("failpoints-test-6")
   122  		require.NoError(t, err)
   123  		require.Equal(t, 5, val.(int))
   124  	}
   125  	// 1*return(true)
   126  	val, err = fps.Eval("failpoints-test-6")
   127  	require.NoError(t, err)
   128  	require.True(t, val.(bool))
   129  	// 1*return(false)
   130  	val, err = fps.Eval("failpoints-test-6")
   131  	require.NoError(t, err)
   132  	require.False(t, val.(bool))
   133  	// 10*return(20)
   134  	for i := 0; i < 10; i++ {
   135  		val, err = fps.Eval("failpoints-test-6")
   136  		require.NoError(t, err)
   137  		require.Equal(t, 20, val.(int))
   138  	}
   139  	val, err = fps.Eval("failpoints-test-6")
   140  	require.Equal(t, failpoint.ErrNotAllowed, errors.Cause(err))
   141  	require.Nil(t, val)
   142  
   143  	val, err = fps.Eval("failpoints-test-7")
   144  	require.Equal(t, failpoint.ErrNotExist, errors.Cause(err))
   145  	require.Nil(t, val)
   146  
   147  	val, err = failpoint.Eval("failpoint-env1")
   148  	require.NoError(t, err)
   149  	require.Equal(t, 10, val.(int))
   150  	val, err = failpoint.Eval("failpoint-env2")
   151  	require.NoError(t, err)
   152  	require.True(t, val.(bool))
   153  
   154  	// Tests for sleep
   155  	ch = make(chan struct{})
   156  	go func() {
   157  		defer close(ch)
   158  		time.Sleep(time.Second)
   159  		err := failpoint.Disable("gofail/test-sleep")
   160  		require.NoError(t, err)
   161  	}()
   162  	err = failpoint.Enable("gofail/test-sleep", "sleep(100)")
   163  	require.NoError(t, err)
   164  	start = time.Now()
   165  	v, err = failpoint.Eval("gofail/test-sleep")
   166  	require.NoError(t, err)
   167  	require.Nil(t, v)
   168  	require.GreaterOrEqual(t, time.Since(start).Milliseconds(), int64(90))
   169  	<-ch
   170  
   171  	// Tests for sleep duration
   172  	ch = make(chan struct{})
   173  	go func() {
   174  		defer close(ch)
   175  		time.Sleep(time.Second)
   176  		err := failpoint.Disable("gofail/test-sleep2")
   177  		require.NoError(t, err)
   178  	}()
   179  	err = failpoint.Enable("gofail/test-sleep2", `sleep("100ms")`)
   180  	require.NoError(t, err)
   181  	start = time.Now()
   182  	v, err = failpoint.Eval("gofail/test-sleep2")
   183  	require.NoError(t, err)
   184  	require.Nil(t, v)
   185  	require.GreaterOrEqual(t, time.Since(start).Milliseconds(), int64(90))
   186  	<-ch
   187  
   188  	// Tests for print
   189  	oldStdio := os.Stdout
   190  	r, w, err := os.Pipe()
   191  	require.NoError(t, err)
   192  	os.Stdout = w
   193  	err = fps.Enable("test-print", `print("hello world")`)
   194  	require.NoError(t, err)
   195  	val, err = fps.Eval("test-print")
   196  	require.NoError(t, err)
   197  	require.Nil(t, val)
   198  	outC := make(chan string)
   199  	// copy the output in a separate goroutine so printing can't block indefinitely
   200  	go func() {
   201  		defer close(outC)
   202  		s, err := ioutil.ReadAll(r)
   203  		require.NoError(t, err)
   204  		outC <- string(s)
   205  	}()
   206  	require.NoError(t, w.Close())
   207  	os.Stdout = oldStdio
   208  	out := <-outC
   209  	require.Equal(t, "failpoint print: hello world\n", out)
   210  
   211  	// Tests for panic
   212  	require.PanicsWithValue(t, "failpoint panic: {}", testPanic)
   213  
   214  	err = fps.Enable("failpoints-test-7", `return`)
   215  	require.NoError(t, err)
   216  	val, err = fps.Eval("failpoints-test-7")
   217  	require.NoError(t, err)
   218  	require.Equal(t, struct{}{}, val)
   219  
   220  	err = fps.Enable("failpoints-test-8", `return()`)
   221  	require.NoError(t, err)
   222  	val, err = fps.Eval("failpoints-test-8")
   223  	require.NoError(t, err)
   224  	require.Equal(t, struct{}{}, val)
   225  }
   226  
   227  func testPanic() {
   228  	_ = failpoint.Enable("test-panic", `panic`)
   229  	_, _ = failpoint.Eval("test-panic")
   230  }