lib9p

Go 9P library.
Log | Files | Refs | LICENSE

server_test.go (27735B)


      1 package lib9p
      2 
      3 import (
      4 	"bytes"
      5 	"context"
      6 	"errors"
      7 	"io"
      8 	"io/fs"
      9 	"os"
     10 	"path"
     11 	"reflect"
     12 	"testing"
     13 )
     14 
     15 // TestRunListener tests if the listener goroutine receives 9P messages
     16 // and send them through the server's listenChan channel.
     17 func TestRunListener(t *testing.T) {
     18 	tFile, err := os.Open("testdata/test_Tmsg.dat")
     19 	if err != nil {
     20 		t.Fatalf("open file: %v", err)
     21 	}
     22 	defer tFile.Close()
     23 	tFile2, err := os.Open("testdata/test_Tmsg.dat")
     24 	if err != nil {
     25 		t.Fatalf("open file: %v", err)
     26 	}
     27 	defer tFile2.Close()
     28 	c := &conn{
     29 		s: &Server{chatty9P: false},
     30 		r: tFile,
     31 	}
     32 	oldReqPoolAdd := reqPoolAdd
     33 	defer func() { reqPoolAdd = oldReqPoolAdd }()
     34 	reqPoolAdd = func(*reqPool, uint16) (*request, error) { return &request{}, nil }
     35 	ctx, cancel := context.WithCancel(context.Background())
     36 	defer cancel()
     37 	c.runListener(ctx, newReqPool())
     38 	for {
     39 		want, err := RecvMsg(tFile2)
     40 		if err == io.EOF {
     41 			break
     42 		} else if err != nil {
     43 			t.Fatalf("recvmsg: %v", err)
     44 		}
     45 		r := <-c.listenChan
     46 		if r.listenErr != nil {
     47 			t.Fatalf("listenErr: %v", r.listenErr)
     48 		}
     49 		got := r.ifcall
     50 		if !reflect.DeepEqual(want, got) {
     51 			t.Errorf("listener modified message:\n\twant: %v\n\tgot: %v",
     52 				want, got)
     53 		}
     54 	}
     55 }
     56 
     57 // TestRunSpeaker tests if the speaker goroutine receives 9P messages
     58 // and send them through the server's speakChan channel.
     59 func TestRunResponder(t *testing.T) {
     60 	rFile, err := os.Open("testdata/test_Rmsg.dat")
     61 	if err != nil {
     62 		t.Fatalf("open file: %v", err)
     63 	}
     64 	defer rFile.Close()
     65 	r, w := io.Pipe()
     66 	c := &conn{s: &Server{chatty9P: false}, w: w}
     67 	rp := newReqPool()
     68 	ctx, cancel := context.WithCancel(context.Background())
     69 	defer cancel()
     70 	c.runResponder(ctx, rp)
     71 	for {
     72 		want, err := readMsg(rFile)
     73 		if err == io.EOF {
     74 			break
     75 		} else if err != nil {
     76 			t.Fatalf("readmsg: %v", err)
     77 		}
     78 		msg, err := unmarshal(want)
     79 		if err != nil {
     80 			t.Fatalf("unmarshal %v", err)
     81 		}
     82 		c.respChan <- &request{
     83 			tag:    msg.GetTag(),
     84 			ofcall: msg,
     85 		}
     86 		got := make([]byte, len(want))
     87 		_, err = io.ReadFull(r, got)
     88 		if err != nil {
     89 			t.Fatalf("readfull: %v", err)
     90 		}
     91 		if !bytes.Equal(want, got) {
     92 			t.Errorf("responder modified message:\n\twant: %v\n\tgot: %v",
     93 				want, got)
     94 		}
     95 	}
     96 }
     97 
     98 // TestGetReq tests if getReq returns the same Msg as RecvMsg and
     99 // registers the Msg to the ReqPool.
    100 func TestGetReq(t *testing.T) {
    101 	tFile, err := os.Open("testdata/test_Tmsg.dat")
    102 	if err != nil {
    103 		t.Fatalf("open file: %v", err)
    104 	}
    105 	defer tFile.Close()
    106 	tFile2, err := os.Open("testdata/test_Tmsg.dat")
    107 	if err != nil {
    108 		t.Fatalf("open file: %v", err)
    109 	}
    110 	defer tFile2.Close()
    111 	rp := newReqPool()
    112 	for {
    113 		got := getReq(tFile, rp, false)
    114 		if got.listenErr == io.EOF {
    115 			break
    116 		} else if got.listenErr != nil {
    117 			t.Fatalf("getReq: %v", got.listenErr)
    118 		}
    119 		wantMsg, err := RecvMsg(tFile2)
    120 		if err != nil {
    121 			t.Fatalf("recvmsg: %v", err)
    122 		}
    123 		if got.tag != wantMsg.GetTag() {
    124 			t.Errorf("r.tag: want: %v, got: %v", wantMsg.GetTag(), got.tag)
    125 		}
    126 		if !reflect.DeepEqual(got.ifcall, wantMsg) {
    127 			t.Errorf("r.ifcall:\n\twant: %v,\n\tgot:  %v", wantMsg, got.ifcall)
    128 		}
    129 		got2, ok := rp.lookup(wantMsg.GetTag())
    130 		if !ok {
    131 			t.Errorf("request not registered to the pool")
    132 		}
    133 		if got != got2 {
    134 			t.Errorf("wrong message in pool:\n\twant: %p,\n\tgot:  %p", got, got2)
    135 		}
    136 		rp.delete(wantMsg.GetTag())
    137 	}
    138 }
    139 
    140 func TestSVersion(t *testing.T) {
    141 	tests := []struct {
    142 		input *TVersion
    143 		want  Msg
    144 	}{
    145 		{&TVersion{Msize: 1024, Version: "9P2000"},
    146 			&RVersion{Msize: 1024, Version: "9P2000"}},
    147 		{&TVersion{Msize: 564, Version: "9P2000"},
    148 			&RVersion{Msize: 564, Version: "9P2000"}},
    149 		{&TVersion{Msize: 8 * 1024, Version: "9P2000"},
    150 			&RVersion{Msize: 1024, Version: "9P2000"}},
    151 		{&TVersion{Msize: 1024, Version: "hoge"},
    152 			&RVersion{Msize: 1024, Version: "unknown"}},
    153 	}
    154 	c, rc := setupConn(nil)
    155 	tc := make(chan *request)
    156 	ctx, cancel := context.WithCancel(context.Background())
    157 	defer cancel()
    158 	go sVersion(ctx, c, tc)
    159 	for i, test := range tests {
    160 		oldMsize := c.msize
    161 		tc <- &request{ifcall: test.input}
    162 		got := (<-rc).ofcall.(*RVersion)
    163 		if !reflect.DeepEqual(test.want, got) {
    164 			t.Errorf("%d: want: %v,\n\tgot:  %v", i, test.want, got)
    165 		}
    166 		if test.input.Msize < oldMsize && c.msize != test.input.Msize {
    167 			t.Errorf("%d: msize not changed", i)
    168 		}
    169 		if test.input.Msize >= oldMsize && c.msize != oldMsize {
    170 			t.Errorf("%d: msize changed unexpectedly", i)
    171 		}
    172 		c.msize = oldMsize
    173 	}
    174 }
    175 
    176 func TestSAuth(t *testing.T) {
    177 	tests := []struct {
    178 		input    *TAuth
    179 		want     Msg
    180 		authFunc func(context.Context, *request)
    181 	}{
    182 		{&TAuth{Afid: NOFID, Uname: "kenji", Aname: ""},
    183 			&RError{Ename: errors.New("authentication not required")}, nil},
    184 		{&TAuth{Afid: NOFID, Uname: "kenji", Aname: ""},
    185 			&RError{Ename: errors.New("NOFID can't be used for afid")},
    186 			func(ctx context.Context, r *request) {}},
    187 		{&TAuth{Afid: 0, Uname: "kenji", Aname: ""},
    188 			&RAuth{Tag: 0, Aqid: Qid{0, 1, 2}},
    189 			func(ctx context.Context, r *request) {
    190 				r.ofcall = &RAuth{Tag: 0, Aqid: Qid{0, 1, 2}}
    191 			}},
    192 		{&TAuth{Afid: 0, Uname: "kenji", Aname: "fs"},
    193 			&RError{Ename: errors.New("no such file system")},
    194 			func(ctx context.Context, r *request) {
    195 				r.ofcall = &RAuth{Tag: 0, Aqid: Qid{0, 1, 2}}
    196 			}},
    197 	}
    198 	for i, test := range tests {
    199 		func() {
    200 			c, rc := setupConn(nil)
    201 			tc := make(chan *request)
    202 			c.s.Auth = test.authFunc
    203 			ctx, cancel := context.WithCancel(context.Background())
    204 			defer cancel()
    205 			go sAuth(ctx, c, tc)
    206 			tc <- &request{ifcall: test.input}
    207 			ofcall := (<-rc).ofcall
    208 			switch wantmsg := test.want.(type) {
    209 			case *RAuth:
    210 				gotmsg, ok := ofcall.(*RAuth)
    211 				if !ok {
    212 					t.Errorf("%d: unexpected message: %v", i, ofcall)
    213 					return
    214 				}
    215 				if !reflect.DeepEqual(wantmsg, gotmsg) {
    216 					t.Errorf("%d: want: %v,\n\tgot:  %v", i, wantmsg, gotmsg)
    217 					return
    218 				}
    219 			case *RError:
    220 				_, ok := ofcall.(*RError)
    221 				if !ok {
    222 					t.Errorf("%d: unexpected message: %v", i, ofcall)
    223 					return
    224 				}
    225 			default:
    226 				t.Fatalf("%d: unexpected message: %v", i, wantmsg)
    227 			}
    228 		}()
    229 	}
    230 }
    231 
    232 // TestSAttach tests sAttach.
    233 // It prepares a conn and adds an AuthFile to the FidPool with fid 0.
    234 // It also sets c.fsmap["fs"] to test a Tattach message with Aname other than "".
    235 func TestSAttach(t *testing.T) {
    236 	tests := []struct {
    237 		input  *TAttach
    238 		needAuth   bool
    239 		authOK bool
    240 		want   Msg
    241 	}{
    242 		{&TAttach{Fid: 0, Afid: NOFID, Uname: "kenji", Aname: ""},
    243 			false, false,
    244 			&RError{}}, // duplicate fid
    245 		{&TAttach{Fid: 1, Afid: NOFID, Uname: "kenji", Aname: ""},
    246 			false, true,
    247 			&RAttach{}}, // ok
    248 		{&TAttach{Fid: 2, Afid: NOFID, Uname: "kenji", Aname: ""},
    249 			true, false,
    250 			&RError{}}, // afid is not set
    251 		{&TAttach{Fid: 2, Afid: 0, Uname: "kenji", Aname: ""},
    252 			true, false,
    253 			&RError{}}, // not authOK
    254 		{&TAttach{Fid: 2, Afid: 0, Uname: "glenda", Aname: ""},
    255 			true, true,
    256 			&RError{}}, // wrong user
    257 		{&TAttach{Fid: 2, Afid: 0, Uname: "kenji", Aname: ""},
    258 			true, true,
    259 			&RAttach{}}, // ok
    260 		{&TAttach{Fid: 3, Afid: 0, Uname: "kenji", Aname: "fs"},
    261 			true, true,
    262 			&RAttach{}}, // ok
    263 		{&TAttach{Fid: 4, Afid: 0, Uname: "kenji", Aname: "fss"},
    264 			true, true,
    265 			&RError{}}, // no such file system
    266 	}
    267 	c, rc := setupConn(testfs)
    268 	c.s.fsmap["fs"] = testfs
    269 	tc := make(chan *request)
    270 	dammyAuth := func(context.Context, *request) {}
    271 	af := &AuthFile{
    272 		Qid:   Qid{Type: QTAUTH},
    273 		Uname: "kenji",
    274 		Aname: "",
    275 	}
    276 	fid, err := c.fPool.add(0)
    277 	if err != nil {
    278 		t.Fatal(err)
    279 	}
    280 	fid.file = af
    281 	ctx, cancel := context.WithCancel(context.Background())
    282 	defer cancel()
    283 	go sAttach(ctx, c, tc)
    284 	for i, test := range tests {
    285 		af.AuthOK = test.authOK
    286 		af.Aname = test.input.Aname
    287 		if test.needAuth {
    288 			c.s.Auth = dammyAuth
    289 		} else {
    290 			c.s.Auth = nil
    291 		}
    292 		req := &request{ifcall: test.input}
    293 		tc <- req
    294 		ofcall := (<-rc).ofcall
    295 		switch wantmsg := test.want.(type) {
    296 		case *RAttach:
    297 			_, ok := ofcall.(*RAttach)
    298 			if !ok {
    299 				t.Errorf("%d: unexpected message: %v", i, ofcall)
    300 				return
    301 			}
    302 		case *RError:
    303 			_, ok := ofcall.(*RError)
    304 			if !ok {
    305 				t.Errorf("%d: unexpected message: %v", i, ofcall)
    306 				return
    307 			}
    308 		default:
    309 			t.Fatalf("%d: unexpected message: %v", i, wantmsg)
    310 		}
    311 	}
    312 }
    313 
    314 func TestSFlush(t *testing.T) {
    315 	rp := newReqPool()
    316 	tests := []struct {
    317 		input *request
    318 	}{
    319 		{&request{ifcall: &TFlush{},
    320 			oldreq: &request{pool: rp, done: make(chan struct{})}}},
    321 	}
    322 	c, rc := setupConn(nil)
    323 	tc := make(chan *request)
    324 	ctx, cancel := context.WithCancel(context.Background())
    325 	defer cancel()
    326 	go sFlush(ctx, c, tc)
    327 	for i, test := range tests {
    328 		tc <- test.input
    329 		if gotmsg, ok := (<-rc).ofcall.(*RFlush); !ok {
    330 			t.Errorf("%d: unexpected message: %v", i, gotmsg)
    331 		}
    332 		if _, ok := <-test.input.oldreq.done; ok {
    333 			t.Errorf("%d: done channel not closed", i)
    334 		}
    335 	}
    336 }
    337 
    338 func TestSWalk(t *testing.T) {
    339 	tests := []struct {
    340 		input   Msg
    341 		wantLen int // len(Ofcall.(*RWalk).Qids)
    342 		wantErr error
    343 	}{
    344 		{&TWalk{Fid: 0, Newfid: 1, Wnames: []string{"a"}},
    345 			1, nil},
    346 		{&TWalk{Fid: 0, Newfid: 2, Wnames: []string{"b"}},
    347 			1, nil},
    348 		{&TWalk{Fid: 0, Newfid: 3, Wnames: []string{"dir", "file"}},
    349 			2, nil},
    350 		{&TWalk{Fid: 0, Newfid: 4, Wnames: []string{"dir", "hoge"}},
    351 			1, nil}, // short walk
    352 		// 9P document says:
    353 		// 	If the first element cant be walked for any reason,
    354 		// 	RError is returned.
    355 		{&TWalk{Fid: 0, Newfid: 5, Wnames: []string{"hoge", "hoge"}},
    356 			0, errors.New("not found")},
    357 	}
    358 	c, rc := setupConn(testfs)
    359 	tc := make(chan *request)
    360 	fid, err := c.fPool.add(0)
    361 	if err != nil {
    362 		t.Fatal(err)
    363 	}
    364 	fid.omode = -1
    365 	fid.path = "."
    366 	fid.fs = testfs
    367 	ctx, cancel := context.WithCancel(context.Background())
    368 	defer cancel()
    369 	go sWalk(ctx, c, tc)
    370 	for i, test := range tests {
    371 		req := &request{ifcall: test.input}
    372 		tc <- req
    373 		ofcall := (<-rc).ofcall
    374 		if test.wantErr == nil {
    375 			gotMsg, ok := ofcall.(*RWalk)
    376 			if !ok {
    377 				t.Errorf("%d: unexpected message: %v", i, ofcall)
    378 				continue
    379 			}
    380 			if test.wantLen != len(gotMsg.Qids) {
    381 				t.Errorf("%d: walk length mismatch: want: %d, got: %d",
    382 					i, test.wantLen, len(gotMsg.Qids))
    383 				continue
    384 			}
    385 		} else {
    386 			_, ok := ofcall.(*RError)
    387 			if !ok {
    388 				t.Errorf("%d: unexpected message: %v", i, ofcall)
    389 				continue
    390 			}
    391 			// TODO: check error message.
    392 		}
    393 	}
    394 }
    395 
    396 func TestSOpen(t *testing.T) {
    397 	tests := []struct {
    398 		filePath string // file to open.
    399 		uid      string
    400 		input    Msg
    401 		wantMsg  Msg
    402 	}{
    403 		// ok
    404 		{"a", "glenda", &TOpen{Fid: 0, Mode: OREAD}, &ROpen{}},
    405 		// fid not found
    406 		{"a", "glenda", &TOpen{Fid: 1, Mode: OREAD}, &RError{}},
    407 		// permission denied
    408 		{"b", "glenda", &TOpen{Fid: 0, Mode: OREAD}, &RError{}},
    409 		{"b", "ken", &TOpen{Fid: 0, Mode: ORDWR}, &ROpen{}},
    410 		{"dir", "glenda", &TOpen{Fid: 0, Mode: OREAD}, &ROpen{}},
    411 		// A directory can't be opened with OWRITE.
    412 		{"dir", "glenda", &TOpen{Fid: 0, Mode: OWRITE}, &RError{}},
    413 	}
    414 	c, rc := setupConn(testfs)
    415 	tc := make(chan *request)
    416 	ctx, cancel := context.WithCancel(context.Background())
    417 	defer cancel()
    418 	go sOpen(ctx, c, tc)
    419 	for i, test := range tests {
    420 		c.fPool.delete(0)
    421 		// add the file to open to c.fPool.
    422 		fid, err := c.fPool.add(0)
    423 		if err != nil {
    424 			t.Error(i, err)
    425 			continue
    426 		}
    427 		fid.omode = -1
    428 		fid.path = test.filePath
    429 		fid.uid = test.uid
    430 		fid.fs = testfs
    431 		f, err := testfs.walkPath(test.filePath)
    432 		if err != nil {
    433 			t.Error(i, err)
    434 			continue
    435 		}
    436 		fid.file = f
    437 		fid.qidpath = f.stat.Qid.Path
    438 		tc <- &request{ifcall: test.input}
    439 		ofcall := (<-rc).ofcall
    440 		switch test.wantMsg.(type) {
    441 		case *ROpen:
    442 			if _, ok := ofcall.(*ROpen); !ok {
    443 				t.Errorf("%d: unexpected message: %v", i, ofcall)
    444 			}
    445 		case *RError:
    446 			if _, ok := ofcall.(*RError); !ok {
    447 				t.Errorf("%d: unexpected message: %v", i, ofcall)
    448 			}
    449 		default:
    450 			t.Errorf("%d: unexpected message: %v", i, ofcall)
    451 		}
    452 	}
    453 }
    454 
    455 func TestSCreate(t *testing.T) {
    456 	tests := []struct {
    457 		dir    string // pathname in which a file is to be created.
    458 		uid    string
    459 		ifcall *TCreate
    460 		want   Msg
    461 	}{
    462 		{".", "glenda",
    463 			&TCreate{Name: "test", Perm: 0777, Mode: OREAD},
    464 			&RCreate{}},
    465 		{"dir", "glenda",
    466 			&TCreate{Name: "test", Perm: 0777, Mode: OREAD},
    467 			&RError{Ename: ErrPerm}},
    468 		{"dir", "rob",
    469 			&TCreate{Name: "test", Perm: 0777, Mode: OREAD},
    470 			&RCreate{}},
    471 	}
    472 	c, rc := setupConn(testfs)
    473 	tc := make(chan *request)
    474 	ctx, cancel := context.WithCancel(context.Background())
    475 	defer cancel()
    476 	go sCreate(ctx, c, tc)
    477 	for i, test := range tests {
    478 		c.fPool.delete(0)
    479 		fid, err := c.fPool.add(0)
    480 		if err != nil {
    481 			t.Error(i, err)
    482 			continue
    483 		}
    484 		f, err := testfs.walkPath(test.dir)
    485 		if err != nil {
    486 			t.Error(i, err)
    487 			continue
    488 		}
    489 		fid.file = f
    490 		fid.omode = -1
    491 		fid.path = test.dir
    492 		fid.uid = test.uid
    493 		fid.fs = testfs
    494 		tc <- &request{ifcall: test.ifcall}
    495 		ofcall := (<-rc).ofcall
    496 		var qid Qid
    497 		switch test.want.(type) {
    498 		case *RCreate:
    499 			got, ok := ofcall.(*RCreate)
    500 			if !ok {
    501 				t.Errorf("%d:\n\twant: %v\n\tgot:  %v", i, test.want, ofcall)
    502 				continue
    503 			}
    504 			qid = got.Qid
    505 		case *RError:
    506 			if _, ok := ofcall.(*RError); !ok {
    507 				t.Errorf("%d:\n\twant: %v\n\tgot:  %v", i, test.want, ofcall)
    508 				continue
    509 			}
    510 			continue
    511 		default:
    512 			t.Errorf("%d: unexpected message: %v", i, test.want)
    513 			continue
    514 		}
    515 		fname := path.Join(test.dir, test.ifcall.Name)
    516 		ff, err := testfs.walkPath(fname)
    517 		if err != nil {
    518 			t.Error(i, err)
    519 			goto remove
    520 		}
    521 		if !reflect.DeepEqual(ff.stat.Qid, qid) {
    522 			t.Errorf("%d: qid mismatch: want: %v, got: %v", i, ff.stat.Qid, qid)
    523 			goto remove
    524 		}
    525 	remove:
    526 		dd, err := testfs.walkPath(test.dir)
    527 		if err != nil {
    528 			t.Fatal(i, err)
    529 			continue
    530 		}
    531 		for i, c := range dd.children {
    532 			if c.stat.Name == test.ifcall.Name {
    533 				dd.children = append(dd.children[:i], dd.children[i+1:]...)
    534 				break
    535 			}
    536 		}
    537 	}
    538 }
    539 
    540 func TestSRead(t *testing.T) {
    541 	c, rc := setupConn(testfs)
    542 	tc := make(chan *request)
    543 	ctx, cancel := context.WithCancel(context.Background())
    544 	defer cancel()
    545 	go sRead(ctx, c, tc)
    546 	testSRead(t, ".", c, tc, rc)
    547 }
    548 
    549 func testSRead(t *testing.T, pathname string, c *conn, tc, rc chan *request) {
    550 	ff, err := testfs.OpenFile(pathname, O_RDONLY)
    551 	if err != nil {
    552 		t.Fatal(err)
    553 	}
    554 	defer ff.Close()
    555 	f := ff.(*testFile)
    556 	fi, err := f.Stat()
    557 	if err != nil {
    558 		t.Fatal(err)
    559 	}
    560 	if fi.IsDir() {
    561 		fid, err := c.fPool.add(0)
    562 		if err != nil {
    563 			t.Fatal(err)
    564 		}
    565 		fid.omode = OREAD
    566 		fid.file = f
    567 		fid.path = pathname
    568 		tc <- &request{ifcall: &TRead{Fid: 0, Offset: 0, Count: 1000}}
    569 		ofcall := (<-rc).ofcall
    570 		rread, ok := ofcall.(*RRead)
    571 		if !ok {
    572 			t.Errorf("unexpected message: %v", ofcall)
    573 			return
    574 		}
    575 		c.fPool.delete(0)
    576 		b := rread.Data
    577 		cstats := make(map[string]*Stat, len(f.children))
    578 		for len(b) != 0 {
    579 			st := NewStat(b)
    580 			b = b[2+st.Size():]
    581 			cstats[st.Name] = st
    582 		}
    583 		for _, child := range f.children {
    584 			if !reflect.DeepEqual(cstats[child.stat.Name], &child.stat) {
    585 				t.Errorf("readdir returned wrong stat:\n\twant: %v\n\tgot:  %v",
    586 					child.stat, cstats[child.stat.Name])
    587 			}
    588 			delete(cstats, child.stat.Name)
    589 			cpath := path.Join(pathname, child.stat.Name)
    590 			testSRead(t, cpath, c, tc, rc)
    591 		}
    592 		if len(cstats) != 0 {
    593 			t.Errorf("readdir returned unexistent stats: %v", cstats)
    594 		}
    595 	} else {
    596 		fid, err := c.fPool.add(0)
    597 		defer c.fPool.delete(0)
    598 		if err != nil {
    599 			t.Fatal(err)
    600 		}
    601 		fid.omode = OREAD
    602 		fid.file = f
    603 		fid.path = pathname
    604 		tc <- &request{ifcall: &TRead{Fid: 0, Offset: 0, Count: 1000}}
    605 		ofcall := (<-rc).ofcall
    606 		rread, ok := ofcall.(*RRead)
    607 		if !ok {
    608 			t.Errorf("unexpected message: %v", ofcall)
    609 			return
    610 		}
    611 		if !bytes.Equal(rread.Data, f.content) {
    612 			t.Errorf("content mismatch: want: %v, got: %v", f.content, rread.Data)
    613 			return
    614 		}
    615 	}
    616 }
    617 
    618 func TestSWrite(t *testing.T) {
    619 	tests := []struct {
    620 		omode    OpenMode
    621 		data     []byte
    622 		err      error
    623 	}{
    624 		{ORDWR, []byte("hoge"), nil},
    625 		{OREAD, []byte("hoge"), errors.New("omode")},
    626 	}
    627 	c, rc := setupConn(testfs)
    628 	tc := make(chan *request)
    629 	ctx, cancel := context.WithCancel(context.Background())
    630 	defer cancel()
    631 	go sWrite(ctx, c, tc)
    632 	for i, test := range tests {
    633 		func() {
    634 			c.fPool.delete(0)
    635 			fid, err := c.fPool.add(0)
    636 			if err != nil {
    637 				t.Error(err)
    638 				return
    639 			}
    640 			fid.omode = test.omode
    641 			f := &testFile{fsys: testfs}
    642 			fid.file = f
    643 			tc <- &request{ifcall: &TWrite{Count: uint32(len(test.data)), Data: test.data}}
    644 			ofcall := (<-rc).ofcall
    645 			switch ofcall := ofcall.(type) {
    646 			case *RWrite:
    647 				if test.err != nil {
    648 					t.Errorf("%d: unexpected message: %v", i, ofcall)
    649 					return
    650 				}
    651 				if !bytes.Equal(f.content, test.data) {
    652 					t.Errorf("%d: content mismatch: want: %v, got: %v",
    653 						i, test.data, f.content)
    654 				}
    655 			case *RError:
    656 				if test.err == nil {
    657 					t.Errorf("%d: unexpected err: %v", i, ofcall.Ename)
    658 				}
    659 			default:
    660 				t.Errorf("%d: unexpected message: %v", i, ofcall)
    661 			}
    662 		}()
    663 	}
    664 }
    665 
    666 func TestSClunk(t *testing.T) {
    667 	tests := []struct {
    668 		fid    uint32
    669 		ifcall *TClunk
    670 		want   Msg
    671 	}{
    672 		{0, &TClunk{Fid: 0}, &RClunk{}},
    673 		{0, &TClunk{Fid: 1}, &RError{}},
    674 		{1, &TClunk{Fid: 1}, &RClunk{}},
    675 	}
    676 	c, rc := setupConn(testfs)
    677 	tc := make(chan *request)
    678 	ctx, cancel := context.WithCancel(context.Background())
    679 	defer cancel()
    680 	go sClunk(ctx, c, tc)
    681 	for i, test := range tests {
    682 		func() {
    683 			_, err := c.fPool.add(test.fid)
    684 			if err != nil {
    685 				t.Error(err)
    686 				return
    687 			}
    688 			defer c.fPool.delete(test.fid)
    689 			tc <- &request{ifcall: test.ifcall}
    690 			ofcall := (<-rc).ofcall
    691 			switch ofcall := ofcall.(type) {
    692 			case *RClunk:
    693 				if _, ok := test.want.(*RClunk); !ok {
    694 					t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot:  %v",
    695 						i, test.want, ofcall)
    696 					return
    697 				}
    698 				if _, ok := c.fPool.lookup(test.fid); ok {
    699 					if test.fid == test.ifcall.Fid {
    700 						t.Errorf("%d: fid not flushed", i)
    701 					}
    702 				} else {
    703 					if test.fid != test.ifcall.Fid {
    704 						t.Errorf("%d: wrong fid flushed", i)
    705 					}
    706 				}
    707 			case *RError:
    708 				if _, ok := test.want.(*RError); !ok {
    709 					t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot:  %v",
    710 						i, test.want, ofcall)
    711 					return
    712 				}
    713 			default:
    714 				t.Errorf("%d: unexpected message %v", i, ofcall)
    715 			}
    716 		}()
    717 	}
    718 }
    719 
    720 // TestORCLOSE tests whether a file created with ORCLOSE bit set is correctly
    721 // removed when clunked.
    722 func TestORCLOSE(t *testing.T) {
    723 	c, rc := setupConn(testfs)
    724 	tc0, tc1 := make(chan *request), make(chan *request)
    725 	ctx, cancel := context.WithCancel(context.Background())
    726 	defer cancel()
    727 	go sCreate(ctx, c, tc0)
    728 	go sClunk(ctx, c, tc1)
    729 	fid, err := c.fPool.add(0)
    730 	if err != nil {
    731 		t.Fatal(err)
    732 	}
    733 	fid.path = "."
    734 	fid.fs = testfs
    735 	fid.uid = "glenda"
    736 	tc0 <- &request{
    737 		ifcall: &TCreate{Fid: 0, Name: "tmp", Perm: 0777, Mode: ORCLOSE | OREAD},
    738 	}
    739 	ofcall := (<-rc).ofcall
    740 	if rerr, ok := ofcall.(*RError); ok {
    741 		t.Fatal(rerr.Ename)
    742 	}
    743 	if _, err = testfs.walkPath("tmp"); err != nil {
    744 		t.Fatal(err)
    745 	}
    746 	tc1 <- &request{ifcall: &TClunk{Fid: 0}}
    747 	ofcall = (<-rc).ofcall
    748 	if rerr, ok := ofcall.(*RError); ok {
    749 		t.Fatal(rerr.Ename)
    750 	}
    751 	if _, err = testfs.walkPath("tmp"); err == nil {
    752 		t.Fatal("file not removed after closing it")
    753 	}
    754 }
    755 
    756 func TestSRemove(t *testing.T) {
    757 	tests := []struct {
    758 		fid      uint32
    759 		pathname string
    760 		fuid     string // uid of the file to be removed.
    761 		mode     OpenMode
    762 		perm     FileMode
    763 		ruid     string // uid of the user who evoke remove.
    764 		ifcall   *TRemove
    765 		want     Msg
    766 	}{
    767 		// ok.
    768 		{0, "hoge", "glenda", OREAD, 0777, "glenda", &TRemove{Fid: 0}, &RRemove{}},
    769 		// unknown fid.
    770 		{0, "hoge", "glenda", OREAD, 0777, "glenda", &TRemove{Fid: 1}, &RError{}},
    771 		// permission denied.
    772 		{0, "hoge", "glenda", OREAD, 0777, "kenji", &TRemove{Fid: 0}, &RError{}},
    773 		// ok.
    774 		{0, "dir/hoge", "glenda", OREAD, 0000, "rob", &TRemove{Fid: 0}, &RRemove{}},
    775 		// permission denied.
    776 		{0, "dir/hoge", "glenda", OREAD, 0777, "glenda", &TRemove{Fid: 0}, &RError{}},
    777 	}
    778 	c, rc := setupConn(testfs)
    779 	tc := make(chan *request)
    780 	ctx, cancel := context.WithCancel(context.Background())
    781 	defer cancel()
    782 	go sRemove(ctx, c, tc)
    783 	for i, test := range tests {
    784 		func() {
    785 			fid, err := c.fPool.add(test.fid)
    786 			if err != nil {
    787 				t.Error(i, err)
    788 				return
    789 			}
    790 			defer c.fPool.delete(test.fid)
    791 			fid.omode = -1
    792 			fid.path = test.pathname
    793 			fid.file = &testFile{stat: Stat{Name:path.Base(test.pathname)}}
    794 			fid.uid = test.ruid
    795 			fid.fs = testfs
    796 			dir, err := testfs.walkPath(path.Dir(test.pathname))
    797 			if err != nil {
    798 				t.Error(i, err)
    799 				return
    800 			}
    801 			dir.children = append(dir.children, fid.file.(*testFile))
    802 			defer func() {
    803 				if 0 < len(dir.children) &&
    804 					dir.children[len(dir.children)-1].stat.Name ==
    805 						path.Base(test.pathname) {
    806 					dir.children = dir.children[:len(dir.children)-1]
    807 				}
    808 			}()
    809 			tc <- &request{ifcall: test.ifcall}
    810 			ofcall := (<-rc).ofcall
    811 			switch ofcall := ofcall.(type) {
    812 			case *RRemove:
    813 				if _, ok := test.want.(*RRemove); !ok {
    814 					t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot:  %v",
    815 						i, test.want, ofcall)
    816 					return
    817 				}
    818 				if _, err := testfs.OpenFile(test.pathname, O_RDONLY); err == nil {
    819 					t.Errorf("%d: file not removed", i)
    820 					testfs.Remove(test.pathname)
    821 					return
    822 				}
    823 			case *RError:
    824 				defer testfs.Remove(test.pathname)
    825 				if _, ok := test.want.(*RError); !ok {
    826 					t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot:  %v",
    827 						i, test.want, ofcall)
    828 					return
    829 				}
    830 			default:
    831 				t.Fatalf("%d: unexpected message: %v", i, ofcall)
    832 			}
    833 		}()
    834 	}
    835 }
    836 
    837 func TestSStat(t *testing.T) {
    838 	c, rc := setupConn(testfs)
    839 	tc := make(chan *request)
    840 	ctx, cancel := context.WithCancel(context.Background())
    841 	defer cancel()
    842 	go sStat(ctx, c, tc)
    843 	testSStat(t, ".", c, tc, rc)
    844 }
    845 
    846 func testSStat(t *testing.T, pathname string, c *conn, tc, rc chan *request) {
    847 	f, err := testfs.walkPath(pathname)
    848 	if err != nil {
    849 		t.Fatal(err)
    850 	}
    851 	st := f.stat
    852 	fid, err := c.fPool.add(0)
    853 	if err != nil {
    854 		t.Fatal(err)
    855 	}
    856 	fid.omode = -1
    857 	fid.path = pathname
    858 	fid.file = f
    859 	fid.fs = testfs
    860 	tc <- &request{ifcall: &TStat{Fid: 0}}
    861 	ofcall := (<-rc).ofcall
    862 	c.fPool.delete(0)
    863 	switch ofcall := ofcall.(type) {
    864 	case *RStat:
    865 		if !reflect.DeepEqual(&st, ofcall.Stat) {
    866 			t.Errorf("stat doesn't match:\n\twant: %v\n\tgot:  %v", &st, ofcall.Stat)
    867 		}
    868 	case *RError:
    869 		t.Errorf("unexpected message: %v", ofcall)
    870 	default:
    871 		t.Fatalf("unexpected message: %v", ofcall)
    872 	}
    873 	for _, child := range f.children {
    874 		cpath := path.Join(pathname, child.stat.Name)
    875 		testSStat(t, cpath, c, tc, rc)
    876 	}
    877 }
    878 
    879 func TestSWstat(t *testing.T) {
    880 	tests := []struct {
    881 		fid    uint32
    882 		path   string
    883 		uid    string
    884 		stat   *Stat
    885 		ifcall *TWstat
    886 		want   Msg
    887 	}{
    888 		// 0 ok: rename by owner of the directory.
    889 		{0, "./d/hoge", "glenda", &Stat{Name: "hoge"},
    890 			&TWstat{
    891 				Fid:  0,
    892 				Stat: &Stat{Name: "oppai"},
    893 			}, &RWstat{}},
    894 		// 1 ok: rename by group member of the directory.
    895 		{0, "./d/hoge", "ken", &Stat{Name: "hoge"},
    896 			&TWstat{
    897 				Fid:  0,
    898 				Stat: &Stat{Name: "oppai"},
    899 			}, &RWstat{}},
    900 		// 2 permission denied: rename by other.
    901 		{0, "./d/hoge", "kenji", &Stat{Name: "hoge"},
    902 			&TWstat{
    903 				Fid:  0,
    904 				Stat: &Stat{Name: "oppai"},
    905 			}, &RError{Ename: ErrPerm}},
    906 		// 3 ok: length change by owner with write permission
    907 		{0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: 0700, Length: 10},
    908 			&TWstat{
    909 				Fid:  0,
    910 				Stat: &Stat{Uid: "glenda", Mode: 0700, Length: 20},
    911 			}, &RWstat{}},
    912 		// 4 permission denied: length change by owner without write permission
    913 		{0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: 0000, Length: 10},
    914 			&TWstat{
    915 				Fid:  0,
    916 				Stat: &Stat{Uid: "glenda", Mode: 0700, Length: 20},
    917 			}, &RError{Ename: ErrPerm}},
    918 		// 5 operation not permitted: length change of a directory to other than 0
    919 		{0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 10},
    920 			&TWstat{
    921 				Fid:  0,
    922 				Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 20},
    923 			}, &RError{}},
    924 		/*
    925 			// TODO: change directory length to 0.
    926 			{0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 10},
    927 				&TWstat{
    928 					Fid: 0,
    929 					Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length:0},
    930 				}, &RWstat{}},
    931 		*/
    932 		// 6 ok: change mode by owner
    933 		{0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700},
    934 			&TWstat{
    935 				Fid:  0,
    936 				Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0755},
    937 			}, &RWstat{}},
    938 		// 7 ok: change mode by the leader
    939 		{0, "hoge", "glenda", &Stat{Gid: "bell", Mode: fs.ModeDir | 0700},
    940 			&TWstat{
    941 				Fid:  0,
    942 				Stat: &Stat{Gid: "glenda", Mode: fs.ModeDir | 0755},
    943 			}, &RWstat{}},
    944 		// 8 permission denied: change mode by a non-leader member
    945 		{0, "hoge", "ken", &Stat{Gid: "bell", Mode: fs.ModeDir | 0700},
    946 			&TWstat{
    947 				Fid:  0,
    948 				Stat: &Stat{Gid: "glenda", Mode: fs.ModeDir | 0755},
    949 			}, &RError{}},
    950 		// 9 ok: change mtime by owner
    951 		{0, "hoge", "glenda", &Stat{Uid: "glenda", Mtime: 1},
    952 			&TWstat{
    953 				Fid:  0,
    954 				Stat: &Stat{Uid: "glenda", Mtime: 0},
    955 			}, &RWstat{}},
    956 		// 10 ok: change mtime by the leader
    957 		{0, "hoge", "glenda", &Stat{Gid: "bell", Mtime: 1},
    958 			&TWstat{
    959 				Fid:  0,
    960 				Stat: &Stat{Gid: "bell", Mtime: 0},
    961 			}, &RWstat{}},
    962 		// 11 permission denied: change mtime by a non-leader member
    963 		{0, "hoge", "ken", &Stat{Gid: "bell", Mtime: 0},
    964 			&TWstat{
    965 				Fid:  0,
    966 				Stat: &Stat{Gid: "bel", Mtime: 1},
    967 			}, &RError{}},
    968 		// 12 operation not permitted: change directory bit.
    969 		{0, "hoge", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir},
    970 			&TWstat{
    971 				Fid:  0,
    972 				Stat: &Stat{Uid: "glenda"},
    973 			}, &RError{}},
    974 		// 13 ok: change gid by the owner, also a member of new group.
    975 		{0, "hoge", "glenda", &Stat{Uid: "glenda", Gid: "kessoku"},
    976 			&TWstat{
    977 				Fid:  0,
    978 				Stat: &Stat{Uid: "glenda", Gid: "bell"},
    979 			}, &RWstat{}},
    980 		// 14 ok: change gid by the leader, also the leader of new group.
    981 		{0, "hoge", "glenda", &Stat{Uid: "ken", Gid: "glenda"},
    982 			&TWstat{
    983 				Fid:  0,
    984 				Stat: &Stat{Uid: "ken", Gid: "bell"},
    985 			}, &RWstat{}},
    986 		// 15 permission denied: change gid by the leader, non-member of new group.
    987 		{0, "hoge", "ken", &Stat{Uid: "glenda", Gid: "ken"},
    988 			&TWstat{
    989 				Fid:  0,
    990 				Stat: &Stat{Uid: "glenda", Gid: "kessoku"},
    991 			}, &RError{}},
    992 		// 16 permission denied: change gid by the owner, non-member of new group.
    993 		{0, "hoge", "ken", &Stat{Uid: "glenda", Gid: "ken"},
    994 			&TWstat{
    995 				Fid:  0,
    996 				Stat: &Stat{Uid: "glenda", Gid: "kessoku"},
    997 			}, &RError{}},
    998 		// 17 operation not permitted: change uid
    999 		{0, "hoge", "ken", &Stat{Uid: "glenda"},
   1000 			&TWstat{
   1001 				Fid:  0,
   1002 				Stat: &Stat{Uid: "ken"},
   1003 			}, &RError{}},
   1004 		// 18 ok: change mode and gid.
   1005 		{0, "hoge", "glenda", &Stat{Gid: "bell", Mode: 0777},
   1006 			&TWstat{
   1007 				Fid:  0,
   1008 				Stat: &Stat{Gid: "glenda", Mode: 0755},
   1009 			}, &RWstat{}},
   1010 	}
   1011 	c, rc := setupConn(testfs)
   1012 	tc := make(chan *request)
   1013 	ctx, cancel := context.WithCancel(context.Background())
   1014 	defer cancel()
   1015 	go sWStat(ctx, c, tc)
   1016 	for i, test := range tests {
   1017 		func() {
   1018 			fid, err := c.fPool.add(test.fid)
   1019 			if err != nil {
   1020 				t.Error(err)
   1021 				return
   1022 			}
   1023 			defer c.fPool.delete(test.fid)
   1024 			fid.omode = OREAD
   1025 			f := &testFile{stat: *test.stat}
   1026 			fid.file = f
   1027 			fid.uid = test.uid
   1028 			fid.path = test.path
   1029 			fid.fs = testfs
   1030 			tc <- &request{ifcall: test.ifcall}
   1031 			ofcall := (<-rc).ofcall
   1032 			switch ofcall := ofcall.(type) {
   1033 			case *RWstat:
   1034 				if _, ok := test.want.(*RWstat); !ok {
   1035 					t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot:  %v",
   1036 						i, test.want, ofcall)
   1037 					return
   1038 				}
   1039 				if !reflect.DeepEqual(&f.stat, test.ifcall.Stat) {
   1040 					t.Errorf("%d: stat doesn't match:\n\twant: %v\n\tgot:  %v",
   1041 						i, test.ifcall.Stat, &f.stat)
   1042 				}
   1043 			case *RError:
   1044 				if _, ok := test.want.(*RError); !ok {
   1045 					t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot:  %v",
   1046 						i, test.want, ofcall)
   1047 				}
   1048 			default:
   1049 				t.Fatalf("%d: unexpected message: %v", i, ofcall)
   1050 			}
   1051 		}()
   1052 
   1053 	}
   1054 }