lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/xnet/virtnet/virtnet_test.go (about) 1 // Copyright (C) 2018-2020 Nexedi SA and Contributors. 2 // Kirill Smelkov <kirr@nexedi.com> 3 // 4 // This program is free software: you can Use, Study, Modify and Redistribute 5 // it under the terms of the GNU General Public License version 3, or (at your 6 // option) any later version, as published by the Free Software Foundation. 7 // 8 // You can also Link and Combine this program with other software covered by 9 // the terms of any of the Free Software licenses or any of the Open Source 10 // Initiative approved licenses and Convey the resulting work. Corresponding 11 // source of such a combination shall include the source code for all other 12 // software used. 13 // 14 // This program is distributed WITHOUT ANY WARRANTY; without even the implied 15 // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 // 17 // See COPYING file for full licensing terms. 18 // See https://www.nexedi.com/licensing for rationale and options. 19 20 package virtnet_test 21 22 import ( 23 "context" 24 "io" 25 "net" 26 "strings" 27 "testing" 28 "time" 29 30 "golang.org/x/sync/errgroup" 31 32 "lab.nexedi.com/kirr/go123/exc" 33 "lab.nexedi.com/kirr/go123/internal/xtesting" 34 "lab.nexedi.com/kirr/go123/xnet" 35 "lab.nexedi.com/kirr/go123/xnet/pipenet" 36 . "lab.nexedi.com/kirr/go123/xnet/virtnet" 37 38 "github.com/pkg/errors" 39 ) 40 41 // testNet is testing network environment. 42 // 43 // It consists of a subnetwork backed by pipenet with 2 hosts: hα and hβ. On 44 // both hosts a listener is started at "" (i.e. it will have ":1" address). 45 // There is a connection established in between α:2-β:2. 46 type testNet struct { 47 testing.TB 48 49 net *SubNetwork 50 hα, hβ *Host 51 lα, lβ xnet.Listener 52 cαβ, cβα net.Conn 53 } 54 55 // newTestNet creates new testing network environment. 56 func newTestNet(t0 testing.TB) *testNet { 57 t := &testNet{TB: t0} 58 t.Helper() 59 60 var err error 61 t.net = pipenet.AsVirtNet(pipenet.New("t")) 62 t.hα, err = t.net.NewHost(context.Background(), "α") 63 if err != nil { 64 t.Fatal(err) 65 } 66 t.hβ, err = t.net.NewHost(context.Background(), "β") 67 if err != nil { 68 t.Fatal(err) 69 } 70 t.lα, err = t.hα.Listen(context.Background(), "") 71 if err != nil { 72 t.Fatal(err) 73 } 74 t.lβ, err = t.hβ.Listen(context.Background(), "") 75 if err != nil { 76 t.Fatal(err) 77 } 78 79 // preestablish α:2-β:2 connection 80 wg := &errgroup.Group{} 81 defer func() { 82 err := wg.Wait() 83 if err != nil { 84 t.Fatal(err) 85 } 86 }() 87 88 wg.Go(func() error { 89 c, err := t.lβ.Accept(context.Background()) 90 if err != nil { 91 return err 92 } 93 t.cβα = c 94 return nil 95 }) 96 97 c, err := t.hα.Dial(context.Background(), "β:1") 98 if err != nil { 99 t.Fatal(err) 100 } 101 t.cαβ = c 102 103 return t 104 } 105 106 // xneterr constructs net.OpError for testNet network. 107 // 108 // if addr is of form "α:1" - only .Addr is set. 109 // if addr is of form "α:1->β:1" - both .Source and .Addr are set. 110 func xneterr(op, addr string, err error) *net.OpError { 111 addrv := strings.Split(addr, "->") 112 if len(addrv) > 2 { 113 exc.Raisef("xneterr: invalid addr %q", addr) 114 } 115 116 operr := &net.OpError{ 117 Op: op, 118 Net: "pipet", // matches newTestNet 119 Err: err, 120 } 121 122 for i, addr := range addrv { 123 a, e := ParseAddr("pipet", addr) 124 exc.Raiseif(e) 125 126 if i == len(addrv)-1 { 127 operr.Addr = a 128 } else { 129 operr.Source = a 130 } 131 } 132 133 return operr 134 } 135 136 // xobject lookups testNet object by name. 137 func (t *testNet) xobject(name string) io.Closer { 138 switch name { 139 case "subnet": return t.net 140 case "hα": return t.hα 141 case "hβ": return t.hβ 142 case "lα": return t.lα 143 case "lβ": return t.lβ 144 case "cαβ": return t.cαβ 145 case "cβα": return t.cβα 146 } 147 148 exc.Raisef("invalid object: %q", name) 149 panic(0) 150 } 151 152 type testFlag int 153 const serialOnly testFlag = 1 154 155 // testClose verifies object.Close vs test func. 156 // 157 // object to close is specified by name, e.g. "hβ". test func should try to do 158 // an action and verify it gets expected error given object is closed. 159 // 160 // two scenarios are verified: 161 // 162 // - serial case: first close, then test, and 163 // - concurrent case: close is run in parallel to test. 164 // 165 // if concurrent case is not applicable for test (e.g. it tries to run a 166 // function that does not block, like e.g. NewHost in pipenet case), it can be 167 // disabled via passing optional serialOnly flag. 168 func testClose(t0 testing.TB, object string, test func(*testNet), flagv ...testFlag) { 169 t0.Helper() 170 171 // serial case 172 t := newTestNet(t0) 173 obj := t.xobject(object) 174 175 err := obj.Close() 176 if err != nil { 177 t.Fatal(err) 178 } 179 test(t) 180 181 if len(flagv) > 0 && flagv[0] == serialOnly { 182 return 183 } 184 185 // concurrent case 186 t = newTestNet(t0) 187 obj = t.xobject(object) 188 189 wg := &errgroup.Group{} 190 wg.Go(func() error { 191 tdelay() 192 return obj.Close() 193 }) 194 195 test(t) 196 err = wg.Wait() 197 if err != nil { 198 t.Fatal(err) 199 } 200 } 201 202 // tdelay delays a bit. 203 // 204 // needed e.g. to test Close interaction with waiting read or write 205 // (we cannot easily sync and make sure e.g. read is started and became asleep) 206 func tdelay() { 207 time.Sleep(1 * time.Millisecond) 208 } 209 210 // TestClose verifies that for all virtnet objects Close properly interrupt / 211 // errors all corresponding operations. 212 func TestClose(t *testing.T) { 213 bg := context.Background() 214 assert := xtesting.Assert(t) 215 216 // Subnet Host listener conn 217 // NewHost x 218 // Dial x x x 219 // Listen x x 220 // Accept x x x 221 // Read/Write x x x 222 223 // ---- NewHost ---- 224 225 // subnet.NewHost vs subnet.Close 226 testClose(t, "subnet", func(t *testNet) { 227 h, err := t.net.NewHost(bg, "γ") 228 assert.Eq(h, (*Host)(nil)) 229 assert.Eq(errors.Cause(err), ErrNetDown) 230 assert.Eq(err.Error(), "virtnet \"pipet\": new host \"γ\": network is down") 231 }, serialOnly) 232 233 // ---- Dial ---- 234 235 // host.Dial vs subnet.Close 236 testClose(t, "subnet", func(t *testNet) { 237 c, err := t.hα.Dial(bg, "β:1") 238 assert.Eq(c, nil) 239 assert.Eq(err, xneterr("dial", "α:3->β:1", ErrNetDown)) 240 }) 241 242 // host1.Dial vs host1.Close 243 testClose(t, "hα", func(t *testNet) { 244 c, err := t.hα.Dial(bg, "β:1") 245 assert.Eq(c, nil) 246 assert.Eq(err, xneterr("dial", "α:3->β:1", ErrHostDown)) 247 }) 248 249 // host1.Dial vs host2.Close 250 testClose(t, "hβ", func(t *testNet) { 251 c, err := t.hα.Dial(bg, "β:1") 252 assert.Eq(c, nil) 253 assert.Eq(err, xneterr("dial", "α:3->β:1", ErrConnRefused)) 254 }) 255 256 // host1.Dial vs host2.listener.Close 257 testClose(t, "lβ", func(t *testNet) { 258 c, err := t.hα.Dial(bg, "β:1") 259 assert.Eq(c, nil) 260 assert.Eq(err, xneterr("dial", "α:3->β:1", ErrConnRefused)) 261 }) 262 263 // ---- Listen ---- 264 265 // host.Listen vs subnet.Close 266 testClose(t, "subnet", func(t *testNet) { 267 l, err := t.hα.Listen(bg, "") 268 assert.Eq(l, nil) 269 assert.Eq(err, xneterr("listen", "α:0", ErrNetDown)) 270 }, serialOnly) 271 272 // host.Listen vs host.Close 273 testClose(t, "hα", func(t *testNet) { 274 l, err := t.hα.Listen(bg, "") 275 assert.Eq(l, nil) 276 assert.Eq(err, xneterr("listen", "α:0", ErrHostDown)) 277 }, serialOnly) 278 279 // ---- Accept ---- 280 281 // listener.Accept vs subnet.Close 282 testClose(t, "subnet", func(t *testNet) { 283 c, err := t.lα.Accept(bg) 284 assert.Eq(c, nil) 285 assert.Eq(err, xneterr("accept", "α:1", ErrNetDown)) 286 }) 287 288 // listener.Accept vs host.Close 289 testClose(t, "hα", func(t *testNet) { 290 c, err := t.lα.Accept(bg) 291 assert.Eq(c, nil) 292 assert.Eq(err, xneterr("accept", "α:1", ErrHostDown)) 293 }) 294 295 // listener.Accept vs listener.Close 296 testClose(t, "lα", func(t *testNet) { 297 c, err := t.lα.Accept(bg) 298 assert.Eq(c, nil) 299 assert.Eq(err, xneterr("accept", "α:1", ErrSockDown)) 300 }) 301 302 // ---- Read/Write ---- 303 304 buf := []byte("hello world!") 305 306 // conn.{Read,Write} vs subnet.Close 307 testClose(t, "subnet", func(t *testNet) { 308 n, err := t.cαβ.Read(buf) 309 assert.Eq(n, 0) 310 // err can be also EOF because subnet.Close closes cβα too and 311 // depending on scheduling we might first get EOF on our end. 312 if err != io.EOF { 313 assert.Eq(err, xneterr("read", "β:2->α:2", ErrNetDown)) 314 } 315 }) 316 testClose(t, "subnet", func(t *testNet) { 317 n, err := t.cαβ.Write(buf) 318 assert.Eq(n, 0) 319 assert.Eq(err, xneterr("write", "α:2->β:2", ErrNetDown)) 320 }) 321 322 // conn1.{Read,Write} vs host1.Close 323 testClose(t, "hα", func(t *testNet) { 324 n, err := t.cαβ.Read(buf) 325 assert.Eq(n, 0) 326 assert.Eq(err, xneterr("read", "β:2->α:2", ErrHostDown)) 327 }) 328 testClose(t, "hα", func(t *testNet) { 329 n, err := t.cαβ.Write(buf) 330 assert.Eq(n, 0) 331 assert.Eq(err, xneterr("write", "α:2->β:2", ErrHostDown)) 332 }) 333 334 // conn1.{Read,Write} vs host2.Close 335 testClose(t, "hβ", func(t *testNet) { 336 n, err := t.cαβ.Read(buf) 337 assert.Eq(n, 0) 338 assert.Eq(err, io.EOF) 339 }) 340 testClose(t, "hβ", func(t *testNet) { 341 n, err := t.cαβ.Write(buf) 342 assert.Eq(n, 0) 343 assert.Eq(err, xneterr("write", "α:2->β:2", io.ErrClosedPipe)) 344 }) 345 346 // conn1.{Read,Write} vs conn1.Close 347 testClose(t, "cαβ", func(t *testNet) { 348 n, err := t.cαβ.Read(buf) 349 assert.Eq(n, 0) 350 assert.Eq(err, xneterr("read", "β:2->α:2", ErrSockDown)) 351 }) 352 testClose(t, "cαβ", func(t *testNet) { 353 n, err := t.cαβ.Write(buf) 354 assert.Eq(n, 0) 355 assert.Eq(err, xneterr("write", "α:2->β:2", ErrSockDown)) 356 }) 357 358 // conn1.{Read,Write} vs conn2.Close 359 testClose(t, "cβα", func(t *testNet) { 360 n, err := t.cαβ.Read(buf) 361 assert.Eq(n, 0) 362 assert.Eq(err, io.EOF) 363 }) 364 testClose(t, "cβα", func(t *testNet) { 365 n, err := t.cαβ.Write(buf) 366 assert.Eq(n, 0) 367 assert.Eq(err, xneterr("write", "α:2->β:2", io.ErrClosedPipe)) 368 }) 369 } 370 371 // TestVNetDown verifies that engine shutdown error signal is properly handled. 372 func TestVNetDown(t0 *testing.T) { 373 assert := xtesting.Assert(t0) 374 375 t := newTestNet(t0) 376 errSomeProblem := errors.New("some problem") 377 SubnetShutdown(t.net, errSomeProblem) // notifier.VNetDown does this 378 379 // SubNetwork.Close = shutdown(nil) and all that interactions were 380 // verified in TestClose. Here lets check only that we get proper Close error. 381 err := t.net.Close() 382 assert.Eq(errors.Cause(err), errSomeProblem) 383 assert.Eq(err.Error(), "virtnet \"pipet\": close: some problem") 384 } 385 386 // TestAutoClose verifies that subnet.AutoClose() leads to subnet.Close() after 387 // its last host is closed. 388 func TestAutoClose(t0 *testing.T) { 389 t := newTestNet(t0) 390 X := exc.Raiseif 391 assert := xtesting.Assert(t0) 392 393 t.net.AutoClose() 394 395 hγ, err := t.net.NewHost(context.Background(), "γ"); X(err) 396 err = t.hα.Close(); X(err) 397 err = t.hβ.Close(); X(err) 398 err = hγ.Close(); X(err) 399 400 hδ, err := t.net.NewHost(context.Background(), "δ") 401 assert.Eq(hδ, (*Host)(nil)) 402 assert.Eq(errors.Cause(err), ErrNetDown) 403 assert.Eq(err.Error(), "virtnet \"pipet\": new host \"δ\": network is down") 404 }