diff options
Diffstat (limited to 'pkg/p9/messages_test.go')
-rw-r--r-- | pkg/p9/messages_test.go | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/pkg/p9/messages_test.go b/pkg/p9/messages_test.go new file mode 100644 index 000000000..7facc9f5e --- /dev/null +++ b/pkg/p9/messages_test.go @@ -0,0 +1,483 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package p9 + +import ( + "fmt" + "reflect" + "testing" +) + +func TestEncodeDecode(t *testing.T) { + objs := []encoder{ + &QID{ + Type: 1, + Version: 2, + Path: 3, + }, + &FSStat{ + Type: 1, + BlockSize: 2, + Blocks: 3, + BlocksFree: 4, + BlocksAvailable: 5, + Files: 6, + FilesFree: 7, + FSID: 8, + NameLength: 9, + }, + &AttrMask{ + Mode: true, + NLink: true, + UID: true, + GID: true, + RDev: true, + ATime: true, + MTime: true, + CTime: true, + INo: true, + Size: true, + Blocks: true, + BTime: true, + Gen: true, + DataVersion: true, + }, + &Attr{ + Mode: Exec, + UID: 2, + GID: 3, + NLink: 4, + RDev: 5, + Size: 6, + BlockSize: 7, + Blocks: 8, + ATimeSeconds: 9, + ATimeNanoSeconds: 10, + MTimeSeconds: 11, + MTimeNanoSeconds: 12, + CTimeSeconds: 13, + CTimeNanoSeconds: 14, + BTimeSeconds: 15, + BTimeNanoSeconds: 16, + Gen: 17, + DataVersion: 18, + }, + &SetAttrMask{ + Permissions: true, + UID: true, + GID: true, + Size: true, + ATime: true, + MTime: true, + CTime: true, + ATimeNotSystemTime: true, + MTimeNotSystemTime: true, + }, + &SetAttr{ + Permissions: 1, + UID: 2, + GID: 3, + Size: 4, + ATimeSeconds: 5, + ATimeNanoSeconds: 6, + MTimeSeconds: 7, + MTimeNanoSeconds: 8, + }, + &Dirent{ + QID: QID{Type: 1}, + Offset: 2, + Type: 3, + Name: "a", + }, + &Rlerror{ + Error: 1, + }, + &Tstatfs{ + FID: 1, + }, + &Rstatfs{ + FSStat: FSStat{Type: 1}, + }, + &Tlopen{ + FID: 1, + Flags: WriteOnly, + }, + &Rlopen{ + QID: QID{Type: 1}, + IoUnit: 2, + }, + &Tlconnect{ + FID: 1, + }, + &Rlconnect{}, + &Tlcreate{ + FID: 1, + Name: "a", + OpenFlags: 2, + Permissions: 3, + GID: 4, + }, + &Rlcreate{ + Rlopen{QID: QID{Type: 1}}, + }, + &Tsymlink{ + Directory: 1, + Name: "a", + Target: "b", + GID: 2, + }, + &Rsymlink{ + QID: QID{Type: 1}, + }, + &Tmknod{ + Directory: 1, + Name: "a", + Mode: 2, + Major: 3, + Minor: 4, + GID: 5, + }, + &Rmknod{ + QID: QID{Type: 1}, + }, + &Trename{ + FID: 1, + Directory: 2, + Name: "a", + }, + &Rrename{}, + &Treadlink{ + FID: 1, + }, + &Rreadlink{ + Target: "a", + }, + &Tgetattr{ + FID: 1, + AttrMask: AttrMask{Mode: true}, + }, + &Rgetattr{ + Valid: AttrMask{Mode: true}, + QID: QID{Type: 1}, + Attr: Attr{Mode: Write}, + }, + &Tsetattr{ + FID: 1, + Valid: SetAttrMask{Permissions: true}, + SetAttr: SetAttr{Permissions: Write}, + }, + &Rsetattr{}, + &Txattrwalk{ + FID: 1, + NewFID: 2, + Name: "a", + }, + &Rxattrwalk{ + Size: 1, + }, + &Txattrcreate{ + FID: 1, + Name: "a", + AttrSize: 2, + Flags: 3, + }, + &Rxattrcreate{}, + &Tgetxattr{ + FID: 1, + Name: "abc", + Size: 2, + }, + &Rgetxattr{ + Value: "xyz", + }, + &Tsetxattr{ + FID: 1, + Name: "abc", + Value: "xyz", + Flags: 2, + }, + &Rsetxattr{}, + &Treaddir{ + Directory: 1, + Offset: 2, + Count: 3, + }, + &Rreaddir{ + // Count must be sufficient to encode a dirent. + Count: 0x1a, + Entries: []Dirent{{QID: QID{Type: 2}}}, + }, + &Tfsync{ + FID: 1, + }, + &Rfsync{}, + &Tlink{ + Directory: 1, + Target: 2, + Name: "a", + }, + &Rlink{}, + &Tmkdir{ + Directory: 1, + Name: "a", + Permissions: 2, + GID: 3, + }, + &Rmkdir{ + QID: QID{Type: 1}, + }, + &Trenameat{ + OldDirectory: 1, + OldName: "a", + NewDirectory: 2, + NewName: "b", + }, + &Rrenameat{}, + &Tunlinkat{ + Directory: 1, + Name: "a", + Flags: 2, + }, + &Runlinkat{}, + &Tversion{ + MSize: 1, + Version: "a", + }, + &Rversion{ + MSize: 1, + Version: "a", + }, + &Tauth{ + AuthenticationFID: 1, + UserName: "a", + AttachName: "b", + UID: 2, + }, + &Rauth{ + QID: QID{Type: 1}, + }, + &Tattach{ + FID: 1, + Auth: Tauth{AuthenticationFID: 2}, + }, + &Rattach{ + QID: QID{Type: 1}, + }, + &Tflush{ + OldTag: 1, + }, + &Rflush{}, + &Twalk{ + FID: 1, + NewFID: 2, + Names: []string{"a"}, + }, + &Rwalk{ + QIDs: []QID{{Type: 1}}, + }, + &Tread{ + FID: 1, + Offset: 2, + Count: 3, + }, + &Rread{ + Data: []byte{'a'}, + }, + &Twrite{ + FID: 1, + Offset: 2, + Data: []byte{'a'}, + }, + &Rwrite{ + Count: 1, + }, + &Tclunk{ + FID: 1, + }, + &Rclunk{}, + &Tremove{ + FID: 1, + }, + &Rremove{}, + &Tflushf{ + FID: 1, + }, + &Rflushf{}, + &Twalkgetattr{ + FID: 1, + NewFID: 2, + Names: []string{"a"}, + }, + &Rwalkgetattr{ + QIDs: []QID{{Type: 1}}, + Valid: AttrMask{Mode: true}, + Attr: Attr{Mode: Write}, + }, + &Tucreate{ + Tlcreate: Tlcreate{ + FID: 1, + Name: "a", + OpenFlags: 2, + Permissions: 3, + GID: 4, + }, + UID: 5, + }, + &Rucreate{ + Rlcreate{Rlopen{QID: QID{Type: 1}}}, + }, + &Tumkdir{ + Tmkdir: Tmkdir{ + Directory: 1, + Name: "a", + Permissions: 2, + GID: 3, + }, + UID: 4, + }, + &Rumkdir{ + Rmkdir{QID: QID{Type: 1}}, + }, + &Tusymlink{ + Tsymlink: Tsymlink{ + Directory: 1, + Name: "a", + Target: "b", + GID: 2, + }, + UID: 3, + }, + &Rusymlink{ + Rsymlink{QID: QID{Type: 1}}, + }, + &Tumknod{ + Tmknod: Tmknod{ + Directory: 1, + Name: "a", + Mode: 2, + Major: 3, + Minor: 4, + GID: 5, + }, + UID: 6, + }, + &Rumknod{ + Rmknod{QID: QID{Type: 1}}, + }, + } + + for _, enc := range objs { + // Encode the original. + data := make([]byte, initialBufferLength) + buf := buffer{data: data[:0]} + enc.encode(&buf) + + // Create a new object, same as the first. + enc2 := reflect.New(reflect.ValueOf(enc).Elem().Type()).Interface().(encoder) + buf2 := buffer{data: buf.data} + + // To be fair, we need to add any payloads (directly). + if pl, ok := enc.(payloader); ok { + enc2.(payloader).SetPayload(pl.Payload()) + } + + // And any file payloads (directly). + if fl, ok := enc.(filer); ok { + enc2.(filer).SetFilePayload(fl.FilePayload()) + } + + // Mark sure it was okay. + enc2.decode(&buf2) + if buf2.isOverrun() { + t.Errorf("object %#v->%#v got overrun on decode", enc, enc2) + continue + } + + // Check that they are equal. + if !reflect.DeepEqual(enc, enc2) { + t.Errorf("object %#v and %#v differ", enc, enc2) + continue + } + } +} + +func TestMessageStrings(t *testing.T) { + for typ := range msgRegistry.factories { + entry := &msgRegistry.factories[typ] + if entry.create != nil { + name := fmt.Sprintf("%+v", typ) + t.Run(name, func(t *testing.T) { + defer func() { // Ensure no panic. + if r := recover(); r != nil { + t.Errorf("printing %s failed: %v", name, r) + } + }() + m := entry.create() + _ = fmt.Sprintf("%v", m) + err := ErrInvalidMsgType{MsgType(typ)} + _ = err.Error() + }) + } + } +} + +func TestRegisterDuplicate(t *testing.T) { + defer func() { + if r := recover(); r == nil { + // We expect a panic. + t.FailNow() + } + }() + + // Register a duplicate. + msgRegistry.register(MsgRlerror, func() message { return &Rlerror{} }) +} + +func TestMsgCache(t *testing.T) { + // Cache starts empty. + if got, want := len(msgRegistry.factories[MsgRlerror].cache), 0; got != want { + t.Errorf("Wrong cache size, got: %d, want: %d", got, want) + } + + // Message can be created with an empty cache. + msg, err := msgRegistry.get(0, MsgRlerror) + if err != nil { + t.Errorf("msgRegistry.get(): %v", err) + } + if got, want := len(msgRegistry.factories[MsgRlerror].cache), 0; got != want { + t.Errorf("Wrong cache size, got: %d, want: %d", got, want) + } + + // Check that message is added to the cache when returned. + msgRegistry.put(msg) + if got, want := len(msgRegistry.factories[MsgRlerror].cache), 1; got != want { + t.Errorf("Wrong cache size, got: %d, want: %d", got, want) + } + + // Check that returned message is reused. + if got, err := msgRegistry.get(0, MsgRlerror); err != nil { + t.Errorf("msgRegistry.get(): %v", err) + } else if msg != got { + t.Errorf("Message not reused, got: %d, want: %d", got, msg) + } + + // Check that cache doesn't grow beyond max size. + for i := 0; i < maxCacheSize+1; i++ { + msgRegistry.put(&Rlerror{}) + } + if got, want := len(msgRegistry.factories[MsgRlerror].cache), maxCacheSize; got != want { + t.Errorf("Wrong cache size, got: %d, want: %d", got, want) + } +} |