github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/iavl/tree_fuzz_test.go (about) 1 // nolint:errcheck 2 package iavl 3 4 import ( 5 "fmt" 6 "math/rand" 7 "testing" 8 9 "github.com/stretchr/testify/require" 10 11 cmn "github.com/fibonacci-chain/fbc/libs/iavl/common" 12 ) 13 14 // This file implement fuzz testing by generating programs and then running 15 // them. If an error occurs, the program that had the error is printed. 16 17 // A program is a list of instructions. 18 type program struct { 19 instructions []instruction 20 } 21 22 func (p *program) Execute(tree *MutableTree) (err error) { 23 var errLine int 24 25 defer func() { 26 if r := recover(); r != nil { 27 var str string 28 29 for i, instr := range p.instructions { 30 prefix := " " 31 if i == errLine { 32 prefix = ">> " 33 } 34 str += prefix + instr.String() + "\n" 35 } 36 err = fmt.Errorf("program panicked with: %s\n%s", r, str) 37 } 38 }() 39 40 for i, instr := range p.instructions { 41 errLine = i 42 instr.Execute(tree) 43 } 44 return 45 } 46 47 func (p *program) addInstruction(i instruction) { 48 p.instructions = append(p.instructions, i) 49 } 50 51 func (p *program) size() int { 52 return len(p.instructions) 53 } 54 55 type instruction struct { 56 op string 57 k, v []byte 58 version int64 59 } 60 61 func (i instruction) Execute(tree *MutableTree) { 62 switch i.op { 63 case "SET": 64 tree.Set(i.k, i.v) 65 case "REMOVE": 66 tree.Remove(i.k) 67 case "SAVE": 68 tree.SaveVersion(false) 69 case "DELETE": 70 tree.DeleteVersion(i.version) 71 default: 72 panic("Unrecognized op: " + i.op) 73 } 74 } 75 76 func (i instruction) String() string { 77 if i.version > 0 { 78 return fmt.Sprintf("%-8s %-8s %-8s %-8d", i.op, i.k, i.v, i.version) 79 } 80 return fmt.Sprintf("%-8s %-8s %-8s", i.op, i.k, i.v) 81 } 82 83 // Generate a random program of the given size. 84 func genRandomProgram(size int) *program { 85 p := &program{} 86 nextVersion := 1 87 88 for p.size() < size { 89 k, v := []byte(cmn.RandStr(1)), []byte(cmn.RandStr(1)) 90 91 switch rand.Int() % 7 { 92 case 0, 1, 2: 93 p.addInstruction(instruction{op: "SET", k: k, v: v}) 94 case 3, 4: 95 p.addInstruction(instruction{op: "REMOVE", k: k}) 96 case 5: 97 p.addInstruction(instruction{op: "SAVE", version: int64(nextVersion)}) 98 nextVersion++ 99 case 6: 100 if rv := rand.Int() % nextVersion; rv < nextVersion && rv > 0 { 101 p.addInstruction(instruction{op: "DELETE", version: int64(rv)}) 102 } 103 } 104 } 105 return p 106 } 107 108 // Generate many programs and run them. 109 func TestMutableTreeFuzz(t *testing.T) { 110 maxIterations := testFuzzIterations 111 progsPerIteration := 100000 112 iterations := 0 113 114 for size := 5; iterations < maxIterations; size++ { 115 for i := 0; i < progsPerIteration/size; i++ { 116 tree, err := getTestTree(0) 117 require.NoError(t, err) 118 program := genRandomProgram(size) 119 err = program.Execute(tree) 120 if err != nil { 121 t.Fatalf("Error after %d iterations (size %d): %s\n%s", iterations, size, err.Error(), tree.String()) 122 } 123 iterations++ 124 } 125 } 126 }