diff options
-rw-r--r-- | pkg/sentry/context/contexttest/contexttest.go | 32 | ||||
-rw-r--r-- | pkg/sentry/fs/BUILD | 2 | ||||
-rw-r--r-- | pkg/sentry/fs/ashmem/area.go | 8 | ||||
-rw-r--r-- | pkg/sentry/fs/copy_up_test.go | 2 | ||||
-rw-r--r-- | pkg/sentry/fs/dev/BUILD | 1 | ||||
-rw-r--r-- | pkg/sentry/fs/dev/dev.go | 4 | ||||
-rw-r--r-- | pkg/sentry/fs/file_overlay_test.go | 2 | ||||
-rw-r--r-- | pkg/sentry/fs/inode_overlay_test.go | 2 | ||||
-rw-r--r-- | pkg/sentry/fs/mounts_test.go | 2 | ||||
-rw-r--r-- | pkg/sentry/fs/tmpfs/BUILD | 6 | ||||
-rw-r--r-- | pkg/sentry/fs/tmpfs/file_test.go | 6 | ||||
-rw-r--r-- | pkg/sentry/fs/tmpfs/fs.go | 4 | ||||
-rw-r--r-- | pkg/sentry/fs/tmpfs/inode_file.go | 20 | ||||
-rw-r--r-- | pkg/sentry/fs/tmpfs/tmpfs.go | 14 | ||||
-rw-r--r-- | pkg/sentry/kernel/contexttest/BUILD | 17 | ||||
-rw-r--r-- | pkg/sentry/kernel/contexttest/contexttest.go | 38 |
16 files changed, 116 insertions, 44 deletions
diff --git a/pkg/sentry/context/contexttest/contexttest.go b/pkg/sentry/context/contexttest/contexttest.go index d2f084ed7..d5fd9f165 100644 --- a/pkg/sentry/context/contexttest/contexttest.go +++ b/pkg/sentry/context/contexttest/contexttest.go @@ -31,23 +31,30 @@ import ( // Context returns a Context that may be used in tests. Uses ptrace as the // platform.Platform. +// +// Note that some filesystems may require a minimal kernel for testing, which +// this test context does not provide. For such tests, see kernel/contexttest. func Context(tb testing.TB) context.Context { p, err := ptrace.New() if err != nil { tb.Fatal(err) } // Test usage of context.Background is fine. - return &testContext{ - Context: context.Background(), - l: limits.NewLimitSet(), - platform: p, + return &TestContext{ + Context: context.Background(), + l: limits.NewLimitSet(), + platform: p, + otherValues: make(map[interface{}]interface{}), } } -type testContext struct { +// TestContext represents a context with minimal functionality suitable for +// running tests. +type TestContext struct { context.Context - l *limits.LimitSet - platform platform.Platform + l *limits.LimitSet + platform platform.Platform + otherValues map[interface{}]interface{} } // globalUniqueID tracks incremental unique identifiers for tests. @@ -76,8 +83,14 @@ func (hostClock) Now() ktime.Time { return ktime.FromNanoseconds(time.Now().UnixNano()) } +// RegisterValue registers additional values with this test context. Useful for +// providing values from external packages that contexttest can't depend on. +func (t *TestContext) RegisterValue(key, value interface{}) { + t.otherValues[key] = value +} + // Value implements context.Context. -func (t *testContext) Value(key interface{}) interface{} { +func (t *TestContext) Value(key interface{}) interface{} { switch key { case limits.CtxLimits: return t.l @@ -92,6 +105,9 @@ func (t *testContext) Value(key interface{}) interface{} { case ktime.CtxRealtimeClock: return hostClock{} default: + if val, ok := t.otherValues[key]; ok { + return val + } return t.Context.Value(key) } } diff --git a/pkg/sentry/fs/BUILD b/pkg/sentry/fs/BUILD index 548898aaa..0fe2b14bf 100644 --- a/pkg/sentry/fs/BUILD +++ b/pkg/sentry/fs/BUILD @@ -94,9 +94,9 @@ go_test( deps = [ ":fs", "//pkg/sentry/context", - "//pkg/sentry/context/contexttest", "//pkg/sentry/fs/ramfs/test", "//pkg/sentry/fs/tmpfs", + "//pkg/sentry/kernel/contexttest", "//pkg/sentry/usermem", "//pkg/syserror", ], diff --git a/pkg/sentry/fs/ashmem/area.go b/pkg/sentry/fs/ashmem/area.go index 5372875ac..d7dd2c084 100644 --- a/pkg/sentry/fs/ashmem/area.go +++ b/pkg/sentry/fs/ashmem/area.go @@ -23,8 +23,8 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/fs" "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil" "gvisor.googlesource.com/gvisor/pkg/sentry/fs/tmpfs" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/sentry/memmap" - "gvisor.googlesource.com/gvisor/pkg/sentry/platform" "gvisor.googlesource.com/gvisor/pkg/sentry/usage" "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" "gvisor.googlesource.com/gvisor/pkg/syserror" @@ -117,11 +117,11 @@ func (a *Area) ConfigureMMap(ctx context.Context, file *fs.File, opts *memmap.MM opts.MaxPerms = opts.MaxPerms.Intersect(a.perms) if a.tmpfsFile == nil { - p := platform.FromContext(ctx) - if p == nil { + k := kernel.KernelFromContext(ctx) + if k == nil { return syserror.ENOMEM } - tmpfsInodeOps := tmpfs.NewInMemoryFile(ctx, usage.Tmpfs, fs.UnstableAttr{}, p) + tmpfsInodeOps := tmpfs.NewInMemoryFile(ctx, usage.Tmpfs, fs.UnstableAttr{}, k) // This is not backed by a real filesystem, so we pass in nil. tmpfsInode := fs.NewInode(tmpfsInodeOps, fs.NewNonCachingMountSource(nil, fs.MountSourceFlags{}), fs.StableAttr{}) dirent := fs.NewDirent(tmpfsInode, namePrefix+"/"+a.name) diff --git a/pkg/sentry/fs/copy_up_test.go b/pkg/sentry/fs/copy_up_test.go index fcba14ed4..2b2f4bb8f 100644 --- a/pkg/sentry/fs/copy_up_test.go +++ b/pkg/sentry/fs/copy_up_test.go @@ -22,9 +22,9 @@ import ( "sync" "testing" - "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" _ "gvisor.googlesource.com/gvisor/pkg/sentry/fs/tmpfs" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/contexttest" "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" ) diff --git a/pkg/sentry/fs/dev/BUILD b/pkg/sentry/fs/dev/BUILD index fc069bb5f..b17b5202c 100644 --- a/pkg/sentry/fs/dev/BUILD +++ b/pkg/sentry/fs/dev/BUILD @@ -25,6 +25,7 @@ go_library( "//pkg/sentry/fs/fsutil", "//pkg/sentry/fs/ramfs", "//pkg/sentry/fs/tmpfs", + "//pkg/sentry/kernel", "//pkg/sentry/memmap", "//pkg/sentry/mm", "//pkg/sentry/platform", diff --git a/pkg/sentry/fs/dev/dev.go b/pkg/sentry/fs/dev/dev.go index 05a5005ad..3e127bf04 100644 --- a/pkg/sentry/fs/dev/dev.go +++ b/pkg/sentry/fs/dev/dev.go @@ -22,7 +22,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/fs/binder" "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs" "gvisor.googlesource.com/gvisor/pkg/sentry/fs/tmpfs" - "gvisor.googlesource.com/gvisor/pkg/sentry/platform" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" ) @@ -86,7 +86,7 @@ func New(ctx context.Context, msrc *fs.MountSource, binderEnabled bool, ashmemEn "random": newCharacterDevice(newRandomDevice(ctx, fs.RootOwner, 0444), msrc), "urandom": newCharacterDevice(newRandomDevice(ctx, fs.RootOwner, 0444), msrc), - "shm": tmpfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0777), msrc, platform.FromContext(ctx)), + "shm": tmpfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0777), msrc, kernel.KernelFromContext(ctx)), // A devpts is typically mounted at /dev/pts to provide // pseudoterminal support. Place an empty directory there for diff --git a/pkg/sentry/fs/file_overlay_test.go b/pkg/sentry/fs/file_overlay_test.go index 11e4f7203..f121cbdda 100644 --- a/pkg/sentry/fs/file_overlay_test.go +++ b/pkg/sentry/fs/file_overlay_test.go @@ -19,9 +19,9 @@ import ( "testing" "gvisor.googlesource.com/gvisor/pkg/sentry/context" - "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" ramfstest "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs/test" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/contexttest" ) func TestReaddir(t *testing.T) { diff --git a/pkg/sentry/fs/inode_overlay_test.go b/pkg/sentry/fs/inode_overlay_test.go index acdb2b4f8..9e922d008 100644 --- a/pkg/sentry/fs/inode_overlay_test.go +++ b/pkg/sentry/fs/inode_overlay_test.go @@ -18,9 +18,9 @@ import ( "testing" "gvisor.googlesource.com/gvisor/pkg/sentry/context" - "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" ramfstest "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs/test" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/contexttest" "gvisor.googlesource.com/gvisor/pkg/syserror" ) diff --git a/pkg/sentry/fs/mounts_test.go b/pkg/sentry/fs/mounts_test.go index 2f7a1710f..7d682d99b 100644 --- a/pkg/sentry/fs/mounts_test.go +++ b/pkg/sentry/fs/mounts_test.go @@ -18,9 +18,9 @@ import ( "testing" "gvisor.googlesource.com/gvisor/pkg/sentry/context" - "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" ramfstest "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs/test" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/contexttest" ) // Creates a new MountNamespace with filesystem: diff --git a/pkg/sentry/fs/tmpfs/BUILD b/pkg/sentry/fs/tmpfs/BUILD index 277583113..9065cdd5d 100644 --- a/pkg/sentry/fs/tmpfs/BUILD +++ b/pkg/sentry/fs/tmpfs/BUILD @@ -20,10 +20,10 @@ go_library( "//pkg/sentry/fs", "//pkg/sentry/fs/fsutil", "//pkg/sentry/fs/ramfs", + "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/pipe", "//pkg/sentry/memmap", - "//pkg/sentry/platform", "//pkg/sentry/safemem", "//pkg/sentry/socket/unix/transport", "//pkg/sentry/usage", @@ -39,9 +39,9 @@ go_test( embed = [":tmpfs"], deps = [ "//pkg/sentry/context", - "//pkg/sentry/context/contexttest", "//pkg/sentry/fs", - "//pkg/sentry/platform", + "//pkg/sentry/kernel", + "//pkg/sentry/kernel/contexttest", "//pkg/sentry/usage", "//pkg/sentry/usermem", ], diff --git a/pkg/sentry/fs/tmpfs/file_test.go b/pkg/sentry/fs/tmpfs/file_test.go index b5830d3df..02da9af82 100644 --- a/pkg/sentry/fs/tmpfs/file_test.go +++ b/pkg/sentry/fs/tmpfs/file_test.go @@ -19,16 +19,16 @@ import ( "testing" "gvisor.googlesource.com/gvisor/pkg/sentry/context" - "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" - "gvisor.googlesource.com/gvisor/pkg/sentry/platform" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/contexttest" "gvisor.googlesource.com/gvisor/pkg/sentry/usage" "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" ) func newFileInode(ctx context.Context) *fs.Inode { m := fs.NewCachingMountSource(&Filesystem{}, fs.MountSourceFlags{}) - iops := NewInMemoryFile(ctx, usage.Tmpfs, fs.WithCurrentTime(ctx, fs.UnstableAttr{}), platform.FromContext(ctx)) + iops := NewInMemoryFile(ctx, usage.Tmpfs, fs.WithCurrentTime(ctx, fs.UnstableAttr{}), kernel.KernelFromContext(ctx)) return fs.NewInode(iops, m, fs.StableAttr{ DeviceID: tmpfsDevice.DeviceID(), InodeID: tmpfsDevice.NextIno(), diff --git a/pkg/sentry/fs/tmpfs/fs.go b/pkg/sentry/fs/tmpfs/fs.go index 3ac0c4dd4..88f85b85a 100644 --- a/pkg/sentry/fs/tmpfs/fs.go +++ b/pkg/sentry/fs/tmpfs/fs.go @@ -21,8 +21,8 @@ import ( "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/sentry/context" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth" - "gvisor.googlesource.com/gvisor/pkg/sentry/platform" ) const ( @@ -131,5 +131,5 @@ func (f *Filesystem) Mount(ctx context.Context, device string, flags fs.MountSou msrc := fs.NewCachingMountSource(f, flags) // Construct the tmpfs root. - return NewDir(ctx, nil, owner, perms, msrc, platform.FromContext(ctx)), nil + return NewDir(ctx, nil, owner, perms, msrc, kernel.KernelFromContext(ctx)), nil } diff --git a/pkg/sentry/fs/tmpfs/inode_file.go b/pkg/sentry/fs/tmpfs/inode_file.go index e0181c52c..ca2b4aabb 100644 --- a/pkg/sentry/fs/tmpfs/inode_file.go +++ b/pkg/sentry/fs/tmpfs/inode_file.go @@ -21,8 +21,8 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/context" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/sentry/memmap" - "gvisor.googlesource.com/gvisor/pkg/sentry/platform" "gvisor.googlesource.com/gvisor/pkg/sentry/safemem" "gvisor.googlesource.com/gvisor/pkg/sentry/usage" "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" @@ -52,8 +52,8 @@ type fileInodeOperations struct { fsutil.InodeNotSymlink `state:"nosave"` fsutil.NoopWriteOut `state:"nosave"` - // platform is used to allocate memory that stores the file's contents. - platform platform.Platform + // kernel is used to allocate platform memory that stores the file's contents. + kernel *kernel.Kernel // memUsage is the default memory usage that will be reported by this file. memUsage usage.MemoryKind @@ -84,12 +84,12 @@ type fileInodeOperations struct { } // NewInMemoryFile returns a new file backed by p.Memory(). -func NewInMemoryFile(ctx context.Context, usage usage.MemoryKind, uattr fs.UnstableAttr, p platform.Platform) fs.InodeOperations { +func NewInMemoryFile(ctx context.Context, usage usage.MemoryKind, uattr fs.UnstableAttr, k *kernel.Kernel) fs.InodeOperations { return &fileInodeOperations{ attr: fsutil.InMemoryAttributes{ Unstable: uattr, }, - platform: p, + kernel: k, memUsage: usage, } } @@ -98,7 +98,7 @@ func NewInMemoryFile(ctx context.Context, usage usage.MemoryKind, uattr fs.Unsta func (f *fileInodeOperations) Release(context.Context) { f.dataMu.Lock() defer f.dataMu.Unlock() - f.data.DropAll(f.platform.Memory()) + f.data.DropAll(f.kernel.Platform.Memory()) } // Mappable implements fs.InodeOperations.Mappable. @@ -212,7 +212,7 @@ func (f *fileInodeOperations) Truncate(ctx context.Context, inode *fs.Inode, siz // and can remove them. f.dataMu.Lock() defer f.dataMu.Unlock() - f.data.Truncate(uint64(size), f.platform.Memory()) + f.data.Truncate(uint64(size), f.kernel.Platform.Memory()) return nil } @@ -310,7 +310,7 @@ func (rw *fileReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) { return 0, nil } - mem := rw.f.platform.Memory() + mem := rw.f.kernel.Platform.Memory() var done uint64 seg, gap := rw.f.data.Find(uint64(rw.offset)) for rw.offset < end { @@ -376,7 +376,7 @@ func (rw *fileReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error) } }() - mem := rw.f.platform.Memory() + mem := rw.f.kernel.Platform.Memory() // Page-aligned mr for when we need to allocate memory. RoundUp can't // overflow since end is an int64. pgstartaddr := usermem.Addr(rw.offset).RoundDown() @@ -465,7 +465,7 @@ func (f *fileInodeOperations) Translate(ctx context.Context, required, optional optional.End = pgend } - mem := f.platform.Memory() + mem := f.kernel.Platform.Memory() cerr := f.data.Fill(ctx, required, optional, mem, f.memUsage, func(_ context.Context, dsts safemem.BlockSeq, _ uint64) (uint64, error) { // Newly-allocated pages are zeroed, so we don't need to do anything. return dsts.NumBytes(), nil diff --git a/pkg/sentry/fs/tmpfs/tmpfs.go b/pkg/sentry/fs/tmpfs/tmpfs.go index 91b782540..40a8c4b1e 100644 --- a/pkg/sentry/fs/tmpfs/tmpfs.go +++ b/pkg/sentry/fs/tmpfs/tmpfs.go @@ -20,8 +20,8 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/context" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/pipe" - "gvisor.googlesource.com/gvisor/pkg/sentry/platform" "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport" "gvisor.googlesource.com/gvisor/pkg/sentry/usage" "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" @@ -54,13 +54,13 @@ func rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent type Dir struct { ramfs.Dir - // platform is used to allocate storage for tmpfs Files. - platform platform.Platform + // kernel is used to allocate platform memory as storage for tmpfs Files. + kernel *kernel.Kernel } // NewDir returns a new directory. -func NewDir(ctx context.Context, contents map[string]*fs.Inode, owner fs.FileOwner, perms fs.FilePermissions, msrc *fs.MountSource, platform platform.Platform) *fs.Inode { - d := &Dir{platform: platform} +func NewDir(ctx context.Context, contents map[string]*fs.Inode, owner fs.FileOwner, perms fs.FilePermissions, msrc *fs.MountSource, kernel *kernel.Kernel) *fs.Inode { + d := &Dir{kernel: kernel} d.InitDir(ctx, contents, owner, perms) // Manually set the CreateOps. @@ -84,7 +84,7 @@ func (d *Dir) afterLoad() { func (d *Dir) newCreateOps() *ramfs.CreateOps { return &ramfs.CreateOps{ NewDir: func(ctx context.Context, dir *fs.Inode, perms fs.FilePermissions) (*fs.Inode, error) { - return NewDir(ctx, nil, fs.FileOwnerFromContext(ctx), perms, dir.MountSource, d.platform), nil + return NewDir(ctx, nil, fs.FileOwnerFromContext(ctx), perms, dir.MountSource, d.kernel), nil }, NewFile: func(ctx context.Context, dir *fs.Inode, perms fs.FilePermissions) (*fs.Inode, error) { uattr := fs.WithCurrentTime(ctx, fs.UnstableAttr{ @@ -93,7 +93,7 @@ func (d *Dir) newCreateOps() *ramfs.CreateOps { // Always start unlinked. Links: 0, }) - iops := NewInMemoryFile(ctx, usage.Tmpfs, uattr, d.platform) + iops := NewInMemoryFile(ctx, usage.Tmpfs, uattr, d.kernel) return fs.NewInode(iops, dir.MountSource, fs.StableAttr{ DeviceID: tmpfsDevice.DeviceID(), InodeID: tmpfsDevice.NextIno(), diff --git a/pkg/sentry/kernel/contexttest/BUILD b/pkg/sentry/kernel/contexttest/BUILD new file mode 100644 index 000000000..391986291 --- /dev/null +++ b/pkg/sentry/kernel/contexttest/BUILD @@ -0,0 +1,17 @@ +package(licenses = ["notice"]) # Apache 2.0 + +load("//tools/go_stateify:defs.bzl", "go_library") + +go_library( + name = "contexttest", + testonly = 1, + srcs = ["contexttest.go"], + importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/contexttest", + visibility = ["//pkg/sentry:internal"], + deps = [ + "//pkg/sentry/context", + "//pkg/sentry/context/contexttest", + "//pkg/sentry/kernel", + "//pkg/sentry/platform", + ], +) diff --git a/pkg/sentry/kernel/contexttest/contexttest.go b/pkg/sentry/kernel/contexttest/contexttest.go new file mode 100644 index 000000000..9eb18e7e8 --- /dev/null +++ b/pkg/sentry/kernel/contexttest/contexttest.go @@ -0,0 +1,38 @@ +// Copyright 2018 Google LLC +// +// 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 contexttest provides a test context.Context which includes +// a dummy kernel pointing to a valid platform. +package contexttest + +import ( + "testing" + + "gvisor.googlesource.com/gvisor/pkg/sentry/context" + "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/platform" +) + +// Context returns a Context that may be used in tests. Uses ptrace as the +// platform.Platform, and provides a stub kernel that only serves to point to +// the platform. +func Context(tb testing.TB) context.Context { + ctx := contexttest.Context(tb) + k := &kernel.Kernel{ + Platform: platform.FromContext(ctx), + } + ctx.(*contexttest.TestContext).RegisterValue(kernel.CtxKernel, k) + return ctx +} |