gvisor/pkg/p9/messages_test.go

469 lines
8.8 KiB
Go

// 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{},
&Treaddir{
Directory: 1,
Offset: 2,
Count: 3,
},
&Rreaddir{
// Count must be sufficient to encode a dirent.
Count: 0x18,
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)
}
}