lib9p

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

server_test.go (27815B)


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