github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/syz-hub/state/state_test.go (about) 1 // Copyright 2016 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package state 5 6 import ( 7 "sort" 8 "testing" 9 10 "github.com/google/go-cmp/cmp" 11 "github.com/google/syzkaller/pkg/rpctype" 12 ) 13 14 type TestState struct { 15 t *testing.T 16 dir string 17 state *State 18 } 19 20 func MakeTestState(t *testing.T) *TestState { 21 t.Parallel() 22 dir := t.TempDir() 23 state, err := Make(dir) 24 if err != nil { 25 t.Fatalf("failed to make state: %v", err) 26 } 27 return &TestState{t, dir, state} 28 } 29 30 func (ts *TestState) Reload() { 31 ts.state.Flush() 32 state, err := Make(ts.dir) 33 if err != nil { 34 ts.t.Fatalf("failed to make state: %v", err) 35 } 36 ts.state = state 37 } 38 39 func (ts *TestState) Connect(name, domain string, fresh bool, calls []string, corpus [][]byte) { 40 ts.t.Helper() 41 if err := ts.state.Connect(name, domain, fresh, calls, corpus); err != nil { 42 ts.t.Fatalf("Connect failed: %v", err) 43 } 44 } 45 46 func (ts *TestState) Sync(name string, add [][]byte, del []string) (string, []rpctype.HubInput, int) { 47 ts.t.Helper() 48 domain, inputs, pending, err := ts.state.Sync(name, add, del) 49 if err != nil { 50 ts.t.Fatalf("Sync failed: %v", err) 51 } 52 sort.Slice(inputs, func(i, j int) bool { 53 if inputs[i].Domain != inputs[j].Domain { 54 return inputs[i].Domain < inputs[j].Domain 55 } 56 return string(inputs[i].Prog) < string(inputs[j].Prog) 57 }) 58 return domain, inputs, pending 59 } 60 61 func (ts *TestState) AddRepro(name string, repro []byte) { 62 ts.t.Helper() 63 if err := ts.state.AddRepro(name, repro); err != nil { 64 ts.t.Fatalf("AddRepro failed: %v", err) 65 } 66 } 67 68 func (ts *TestState) PendingRepro(name string) []byte { 69 ts.t.Helper() 70 repro, err := ts.state.PendingRepro(name) 71 if err != nil { 72 ts.t.Fatalf("PendingRepro failed: %v", err) 73 } 74 return repro 75 } 76 77 func TestBasic(t *testing.T) { 78 st := MakeTestState(t) 79 80 if _, _, _, err := st.state.Sync("foo", nil, nil); err == nil { 81 t.Fatalf("synced with unconnected manager") 82 } 83 calls := []string{"read", "write"} 84 st.Connect("foo", "", false, calls, nil) 85 st.Sync("foo", nil, nil) 86 } 87 88 func TestRepro(t *testing.T) { 89 st := MakeTestState(t) 90 91 st.Connect("foo", "", false, []string{"open", "read", "write"}, nil) 92 st.Connect("bar", "", false, []string{"open", "read", "close"}, nil) 93 94 expectPendingRepro := func(name, result string) { 95 t.Helper() 96 repro := st.PendingRepro(name) 97 if string(repro) != result { 98 t.Fatalf("got %q, want %q", string(repro), result) 99 } 100 } 101 expectPendingRepro("foo", "") 102 expectPendingRepro("bar", "") 103 st.AddRepro("foo", []byte("open()")) 104 expectPendingRepro("foo", "") 105 expectPendingRepro("bar", "open()") 106 expectPendingRepro("bar", "") 107 108 // This repro is already present. 109 st.AddRepro("bar", []byte("open()")) 110 st.AddRepro("bar", []byte("read()")) 111 st.AddRepro("bar", []byte("open()\nread()")) 112 // This does not satisfy foo's call set. 113 st.AddRepro("bar", []byte("close()")) 114 expectPendingRepro("bar", "") 115 116 // Check how persistence works. 117 st.Reload() 118 st.Connect("foo", "", false, []string{"open", "read", "write"}, nil) 119 st.Connect("bar", "", false, []string{"open", "read", "close"}, nil) 120 expectPendingRepro("bar", "") 121 expectPendingRepro("foo", "read()") 122 expectPendingRepro("foo", "open()\nread()") 123 expectPendingRepro("foo", "") 124 } 125 126 func TestDomain(t *testing.T) { 127 st := MakeTestState(t) 128 129 st.Connect("client0", "", false, []string{"open"}, nil) 130 st.Connect("client1", "domain1", false, []string{"open"}, nil) 131 st.Connect("client2", "domain2", false, []string{"open"}, nil) 132 st.Connect("client3", "domain3", false, []string{"open"}, nil) 133 { 134 domain, inputs, pending := st.Sync("client0", [][]byte{[]byte("open(0x0)")}, nil) 135 if domain != "" || len(inputs) != 0 || pending != 0 { 136 t.Fatalf("bad sync result: %v, %v, %v", domain, inputs, pending) 137 } 138 } 139 { 140 domain, inputs, pending := st.Sync("client0", [][]byte{[]byte("open(0x1)")}, nil) 141 if domain != "" || len(inputs) != 0 || pending != 0 { 142 t.Fatalf("bad sync result: %v, %v, %v", domain, inputs, pending) 143 } 144 } 145 { 146 domain, inputs, pending := st.Sync("client1", [][]byte{[]byte("open(0x2)"), []byte("open(0x1)")}, nil) 147 if domain != "domain1" || pending != 0 { 148 t.Fatalf("bad sync result: %v, %v, %v", domain, inputs, pending) 149 } 150 if diff := cmp.Diff(inputs, []rpctype.HubInput{ 151 {Domain: "", Prog: []byte("open(0x0)")}, 152 }); diff != "" { 153 t.Fatal(diff) 154 } 155 } 156 { 157 _, inputs, _ := st.Sync("client2", [][]byte{[]byte("open(0x3)")}, nil) 158 if diff := cmp.Diff(inputs, []rpctype.HubInput{ 159 {Domain: "", Prog: []byte("open(0x0)")}, 160 {Domain: "domain1", Prog: []byte("open(0x1)")}, 161 {Domain: "domain1", Prog: []byte("open(0x2)")}, 162 }); diff != "" { 163 t.Fatal(diff) 164 } 165 } 166 { 167 _, inputs, _ := st.Sync("client0", nil, nil) 168 if diff := cmp.Diff(inputs, []rpctype.HubInput{ 169 {Domain: "domain1", Prog: []byte("open(0x2)")}, 170 {Domain: "domain2", Prog: []byte("open(0x3)")}, 171 }); diff != "" { 172 t.Fatal(diff) 173 } 174 } 175 st.Reload() 176 st.Connect("client3", "domain3", false, []string{"open"}, nil) 177 { 178 _, inputs, _ := st.Sync("client3", nil, nil) 179 if diff := cmp.Diff(inputs, []rpctype.HubInput{ 180 {Domain: "", Prog: []byte("open(0x0)")}, 181 {Domain: "domain1", Prog: []byte("open(0x1)")}, 182 {Domain: "domain1", Prog: []byte("open(0x2)")}, 183 {Domain: "domain2", Prog: []byte("open(0x3)")}, 184 }); diff != "" { 185 t.Fatal(diff) 186 } 187 } 188 }