lib9p

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

commit b9e7dc94845876ecafd12a15b1d00c6fa9bded3f
parent 90ec08d4677d4739a49f018cc1947480c46f7000
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Wed,  3 Jan 2024 12:31:37 +0900

add TestSWstat

Diffstat:
Mfs_test.go | 23+++++++++++++++++++++++
Mserver_test.go | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 219 insertions(+), 21 deletions(-)

diff --git a/fs_test.go b/fs_test.go @@ -86,6 +86,12 @@ func (f *testFile) WriteAt(p []byte, off int64) (int, error) { return len(p), nil } +// permission check is intentionally skipped. +func (f *testFile) WStat(stat *Stat) error { + f.stat = *stat + return nil +} + type testFS struct { // root is the root testFile of this file system. root *testFile @@ -223,6 +229,7 @@ func init() { {"a", 0644, "glenda", "glenda", "glenda", "a\n"}, {"b", 0600, "ken", "ken", "ken", "b\n"}, {"c", 0644, "glenda", "glenda", "glenda", ""}, + {"d", fs.ModeDir | 0770, "glenda", "bell", "glenda", ""}, {"dir", fs.ModeDir | 0755, "rob", "rob", "rob", ""}, {"dir/file", 0666, "brian", "brian", "dennis", "unko\n"}, } @@ -262,6 +269,22 @@ func init() { } testfs.waitc = make(chan struct{}) testfs.groups = map[string]group{ + "glenda": group{ + leader: "glenda", + members: map[string]bool{"glenda": true}, + }, + "ken": group{ + leader: "ken", + members: map[string]bool{"ken": true}, + }, + "brian": group{ + leader: "brian", + members: map[string]bool{"brian": true}, + }, + "rob": group{ + leader: "rob", + members: map[string]bool{"rob": true}, + }, "bell": group{ leader: "glenda", members: map[string]bool{ diff --git a/server_test.go b/server_test.go @@ -5,6 +5,7 @@ import ( "context" "errors" "io" + "io/fs" "os" "path" "reflect" @@ -509,7 +510,7 @@ func TestSCreate(t *testing.T) { t.Errorf("%d: qid mismatch: want: %v, got: %v", i, ff.stat.Qid, qid) goto remove } -remove: + remove: dd, err := testfs.walkPath(test.dir) if err != nil { t.Fatal(i, err) @@ -572,9 +573,9 @@ func testSRead(t *testing.T, pathname string, c *conn, tc, rc chan *request) { } func TestSWrite(t *testing.T) { - tests := []struct{ + tests := []struct { pathname string - data []byte + data []byte }{ {"c", []byte("unko")}, } @@ -582,7 +583,7 @@ func TestSWrite(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() go sWrite(ctx, c, tc) - for i, test := range tests{ + for i, test := range tests { func() { c.fPool.delete(0) fid, err := c.fPool.add(0) @@ -619,10 +620,10 @@ func TestSWrite(t *testing.T) { } func TestSClunk(t *testing.T) { - tests := []struct{ - fid uint32 + tests := []struct { + fid uint32 ifcall *TClunk - want Msg + want Msg }{ {0, &TClunk{Fid: 0}, &RClunk{}}, {0, &TClunk{Fid: 1}, &RError{}}, @@ -672,15 +673,15 @@ func TestSClunk(t *testing.T) { } func TestSRemove(t *testing.T) { - tests := []struct{ - fid uint32 + tests := []struct { + fid uint32 pathname string - fuid string // uid of the file to be removed. - mode OpenMode - perm FileMode - ruid string // uid of the user who evoke remove. - ifcall *TRemove - want Msg + fuid string // uid of the file to be removed. + mode OpenMode + perm FileMode + ruid string // uid of the user who evoke remove. + ifcall *TRemove + want Msg }{ // ok. {0, "unko", "glenda", OREAD, 0777, "glenda", &TRemove{Fid: 0}, &RRemove{}}, @@ -721,7 +722,7 @@ func TestSRemove(t *testing.T) { if _, ok := test.want.(*RRemove); !ok { t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v", i, test.want, ofcall) - return + return } if _, err := testfs.OpenFile(test.pathname, OREAD); err == nil { t.Errorf("%d: file not removed", i) @@ -733,7 +734,7 @@ func TestSRemove(t *testing.T) { if _, ok := test.want.(*RError); !ok { t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v", i, test.want, ofcall) - return + return } default: t.Fatalf("%d: unexpected message: %v", i, ofcall) @@ -768,8 +769,8 @@ func testSStat(t *testing.T, pathname string, c *conn, tc, rc chan *request) { c.fPool.delete(0) switch ofcall := ofcall.(type) { case *RStat: - if !reflect.DeepEqual(st, *ofcall.Stat) { - t.Errorf("stat doesn't match:\n\twant: %v\n\tgot: %v", st, ofcall.Stat) + if !reflect.DeepEqual(&st, ofcall.Stat) { + t.Errorf("stat doesn't match:\n\twant: %v\n\tgot: %v", &st, ofcall.Stat) } case *RError: t.Errorf("unexpected message: %v", ofcall) @@ -780,4 +781,179 @@ func testSStat(t *testing.T, pathname string, c *conn, tc, rc chan *request) { cpath := path.Join(pathname, child.stat.Name) testSStat(t, cpath, c, tc, rc) } -} -\ No newline at end of file +} + +func TestSWstat(t *testing.T) { + tests := []struct { + fid uint32 + path string + uid string + stat *Stat + ifcall *TWStat + want Msg + }{ + // 0 ok: rename by owner of the directory. + {0, "./d/unko", "glenda", &Stat{Name: "unko"}, + &TWStat{ + Fid: 0, + Stat: &Stat{Name: "oppai"}, + }, &RWStat{}}, + // 1 ok: rename by group member of the directory. + {0, "./d/unko", "ken", &Stat{Name: "unko"}, + &TWStat{ + Fid: 0, + Stat: &Stat{Name: "oppai"}, + }, &RWStat{}}, + // 2 permission denied: rename by other. + {0, "./d/unko", "kenji", &Stat{Name: "unko"}, + &TWStat{ + Fid: 0, + Stat: &Stat{Name: "oppai"}, + }, &RError{Ename: ErrPerm}}, + // 3 ok: length change by owner with write permission + {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: 0700, Length: 10}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "glenda", Mode: 0700, Length: 20}, + }, &RWStat{}}, + // 4 permission denied: length change by owner without write permission + {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: 0000, Length: 10}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "glenda", Mode: 0700, Length: 20}, + }, &RError{Ename: ErrPerm}}, + // 5 operation not permitted: length change of a directory to other than 0 + {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 10}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 20}, + }, &RError{}}, + /* + // TODO: change directory length to 0. + {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length: 10}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700, Length:0}, + }, &RWStat{}}, + */ + // 6 ok: change mode by owner + {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir | 0700}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "glenda", Mode: fs.ModeDir | 0755}, + }, &RWStat{}}, + // 7 ok: change mode by the leader + {0, "unko", "glenda", &Stat{Gid: "bell", Mode: fs.ModeDir | 0700}, + &TWStat{ + Fid: 0, + Stat: &Stat{Gid: "glenda", Mode: fs.ModeDir | 0755}, + }, &RWStat{}}, + // 8 permission denied: change mode by a non-leader member + {0, "unko", "ken", &Stat{Gid: "bell", Mode: fs.ModeDir | 0700}, + &TWStat{ + Fid: 0, + Stat: &Stat{Gid: "glenda", Mode: fs.ModeDir | 0755}, + }, &RError{}}, + // 9 ok: change mtime by owner + {0, "unko", "glenda", &Stat{Uid: "glenda", Mtime: 1}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "glenda", Mtime: 0}, + }, &RWStat{}}, + // 10 ok: change mtime by the leader + {0, "unko", "glenda", &Stat{Gid: "bell", Mtime: 1}, + &TWStat{ + Fid: 0, + Stat: &Stat{Gid: "bell", Mtime: 0}, + }, &RWStat{}}, + // 11 permission denied: change mtime by a non-leader member + {0, "unko", "ken", &Stat{Gid: "bell", Mtime: 0}, + &TWStat{ + Fid: 0, + Stat: &Stat{Gid: "bel", Mtime: 1}, + }, &RError{}}, + // 12 operation not permitted: change directory bit. + {0, "unko", "glenda", &Stat{Uid: "glenda", Mode: fs.ModeDir}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "glenda"}, + }, &RError{}}, + // 13 ok: change gid by the owner, also a member of new group. + {0, "unko", "glenda", &Stat{Uid: "glenda", Gid: "kessoku"}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "glenda", Gid: "bell"}, + }, &RWStat{}}, + // 14 ok: change gid by the leader, also the leader of new group. + {0, "unko", "glenda", &Stat{Uid: "ken", Gid: "glenda"}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "ken", Gid: "bell"}, + }, &RWStat{}}, + // 15 permission denied: change gid by the leader, non-member of new group. + {0, "unko", "ken", &Stat{Uid: "glenda", Gid: "ken"}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "glenda", Gid: "kessoku"}, + }, &RError{}}, + // 16 permission denied: change gid by the owner, non-member of new group. + {0, "unko", "ken", &Stat{Uid: "glenda", Gid: "ken"}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "glenda", Gid: "kessoku"}, + }, &RError{}}, + // 17 operation not permitted: change uid + {0, "unko", "ken", &Stat{Uid: "glenda"}, + &TWStat{ + Fid: 0, + Stat: &Stat{Uid: "ken"}, + }, &RError{}}, + // 18 ok: change mode and gid. + {0, "unko", "glenda", &Stat{Gid: "bell", Mode: 0777}, + &TWStat{ + Fid: 0, + Stat: &Stat{Gid: "glenda", Mode: 0755}, + }, &RWStat{}}, + } + c, tc, rc := setupConn(testfs) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go sWStat(ctx, c, tc) + for i, test := range tests { + func() { + fid, err := c.fPool.add(test.fid) + if err != nil { + t.Error(err) + return + } + defer c.fPool.delete(test.fid) + fid.omode = OREAD + f := &testFile{stat: *test.stat} + fid.file = f + fid.uid = test.uid + fid.path = test.path + tc <- &request{ifcall: test.ifcall} + ofcall := (<-rc).ofcall + switch ofcall := ofcall.(type) { + case *RWStat: + if _, ok := test.want.(*RWStat); !ok { + t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v", + i, test.want, ofcall) + return + } + if !reflect.DeepEqual(&f.stat, test.ifcall.Stat) { + t.Errorf("%d: stat doesn't match:\n\twant: %v\n\tgot: %v", + i, test.ifcall.Stat, &f.stat) + } + case *RError: + if _, ok := test.want.(*RError); !ok { + t.Errorf("%d: unexpected message:\n\twant: %v\n\tgot: %v", + i, test.want, ofcall) + } + default: + t.Fatalf("%d: unexpected message: %v", i, ofcall) + } + }() + + } +}