Stop using pointer of function pointers in sys.Context (#1301)
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
16
config.go
16
config.go
@@ -609,12 +609,12 @@ type moduleConfig struct {
|
|||||||
stdout io.Writer
|
stdout io.Writer
|
||||||
stderr io.Writer
|
stderr io.Writer
|
||||||
randSource io.Reader
|
randSource io.Reader
|
||||||
walltime *sys.Walltime
|
walltime sys.Walltime
|
||||||
walltimeResolution sys.ClockResolution
|
walltimeResolution sys.ClockResolution
|
||||||
nanotime *sys.Nanotime
|
nanotime sys.Nanotime
|
||||||
nanotimeResolution sys.ClockResolution
|
nanotimeResolution sys.ClockResolution
|
||||||
nanosleep *sys.Nanosleep
|
nanosleep sys.Nanosleep
|
||||||
osyield *sys.Osyield
|
osyield sys.Osyield
|
||||||
args [][]byte
|
args [][]byte
|
||||||
// environ is pair-indexed to retain order similar to os.Environ.
|
// environ is pair-indexed to retain order similar to os.Environ.
|
||||||
environ [][]byte
|
environ [][]byte
|
||||||
@@ -728,7 +728,7 @@ func (c *moduleConfig) WithStdout(stdout io.Writer) ModuleConfig {
|
|||||||
// WithWalltime implements ModuleConfig.WithWalltime
|
// WithWalltime implements ModuleConfig.WithWalltime
|
||||||
func (c *moduleConfig) WithWalltime(walltime sys.Walltime, resolution sys.ClockResolution) ModuleConfig {
|
func (c *moduleConfig) WithWalltime(walltime sys.Walltime, resolution sys.ClockResolution) ModuleConfig {
|
||||||
ret := c.clone()
|
ret := c.clone()
|
||||||
ret.walltime = &walltime
|
ret.walltime = walltime
|
||||||
ret.walltimeResolution = resolution
|
ret.walltimeResolution = resolution
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@@ -745,7 +745,7 @@ func (c *moduleConfig) WithSysWalltime() ModuleConfig {
|
|||||||
// WithNanotime implements ModuleConfig.WithNanotime
|
// WithNanotime implements ModuleConfig.WithNanotime
|
||||||
func (c *moduleConfig) WithNanotime(nanotime sys.Nanotime, resolution sys.ClockResolution) ModuleConfig {
|
func (c *moduleConfig) WithNanotime(nanotime sys.Nanotime, resolution sys.ClockResolution) ModuleConfig {
|
||||||
ret := c.clone()
|
ret := c.clone()
|
||||||
ret.nanotime = &nanotime
|
ret.nanotime = nanotime
|
||||||
ret.nanotimeResolution = resolution
|
ret.nanotimeResolution = resolution
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@@ -758,14 +758,14 @@ func (c *moduleConfig) WithSysNanotime() ModuleConfig {
|
|||||||
// WithNanosleep implements ModuleConfig.WithNanosleep
|
// WithNanosleep implements ModuleConfig.WithNanosleep
|
||||||
func (c *moduleConfig) WithNanosleep(nanosleep sys.Nanosleep) ModuleConfig {
|
func (c *moduleConfig) WithNanosleep(nanosleep sys.Nanosleep) ModuleConfig {
|
||||||
ret := *c // copy
|
ret := *c // copy
|
||||||
ret.nanosleep = &nanosleep
|
ret.nanosleep = nanosleep
|
||||||
return &ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithOsyield implements ModuleConfig.WithOsyield
|
// WithOsyield implements ModuleConfig.WithOsyield
|
||||||
func (c *moduleConfig) WithOsyield(osyield sys.Osyield) ModuleConfig {
|
func (c *moduleConfig) WithOsyield(osyield sys.Osyield) ModuleConfig {
|
||||||
ret := *c // copy
|
ret := *c // copy
|
||||||
ret.osyield = &osyield
|
ret.osyield = osyield
|
||||||
return &ret
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
445
config_test.go
445
config_test.go
@@ -1,12 +1,11 @@
|
|||||||
package wazero
|
package wazero
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
"github.com/tetratelabs/wazero/internal/fstest"
|
"github.com/tetratelabs/wazero/internal/fstest"
|
||||||
@@ -163,260 +162,214 @@ func TestModuleConfig(t *testing.T) {
|
|||||||
// TestModuleConfig_toSysContext only tests the cases that change the inputs to
|
// TestModuleConfig_toSysContext only tests the cases that change the inputs to
|
||||||
// sys.NewContext.
|
// sys.NewContext.
|
||||||
func TestModuleConfig_toSysContext(t *testing.T) {
|
func TestModuleConfig_toSysContext(t *testing.T) {
|
||||||
// Always assigns clocks so that pointers are constant.
|
|
||||||
var wt sys.Walltime = func() (int64, int32) {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
var nt sys.Nanotime = func() int64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
base := NewModuleConfig()
|
base := NewModuleConfig()
|
||||||
base.(*moduleConfig).walltime = &wt
|
|
||||||
base.(*moduleConfig).walltimeResolution = 1
|
|
||||||
base.(*moduleConfig).nanotime = &nt
|
|
||||||
base.(*moduleConfig).nanotimeResolution = 1
|
|
||||||
|
|
||||||
testFS := testfs.FS{}
|
|
||||||
testFS2 := testfs.FS{"/": &testfs.File{}}
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input ModuleConfig
|
input func() (mc ModuleConfig, verify func(t *testing.T, sys *internalsys.Context))
|
||||||
expected *internalsys.Context
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
input: base,
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
return base, func(t *testing.T, sys *internalsys.Context) { require.NotNil(t, sys) }
|
||||||
math.MaxUint32, // max
|
},
|
||||||
nil, // args
|
|
||||||
nil, // environ
|
|
||||||
nil, // stdin
|
|
||||||
nil, // stdout
|
|
||||||
nil, // stderr
|
|
||||||
nil, // randSource
|
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
nil, // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithArgs",
|
name: "WithNanotime",
|
||||||
input: base.WithArgs("a", "bc"),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithNanotime(func() int64 { return 1234567 }, 54321)
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
[]string{"a", "bc"}, // args
|
require.Equal(t, 1234567, int(sys.Nanotime()))
|
||||||
nil, // environ
|
require.Equal(t, 54321, int(sys.NanotimeResolution()))
|
||||||
nil, // stdin
|
}
|
||||||
nil, // stdout
|
},
|
||||||
nil, // stderr
|
|
||||||
nil, // randSource
|
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
nil, // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithArgs empty ok", // Particularly argv[0] can be empty, and we have no rules about others.
|
name: "WithSysNanotime",
|
||||||
input: base.WithArgs("", "bc"),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithSysNanotime()
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
[]string{"", "bc"}, // args
|
require.Equal(t, int(1), int(sys.NanotimeResolution()))
|
||||||
nil, // environ
|
}
|
||||||
nil, // stdin
|
},
|
||||||
nil, // stdout
|
|
||||||
nil, // stderr
|
|
||||||
nil, // randSource
|
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
nil, // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithArgs second call overwrites",
|
name: "WithWalltime",
|
||||||
input: base.WithArgs("a", "bc").WithArgs("bc", "a"),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithWalltime(func() (sec int64, nsec int32) { return 5, 10 }, 54321)
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
[]string{"bc", "a"}, // args
|
actualSec, actualNano := sys.Walltime()
|
||||||
nil, // environ
|
require.Equal(t, 5, int(actualSec))
|
||||||
nil, // stdin
|
require.Equal(t, 10, int(actualNano))
|
||||||
nil, // stdout
|
require.Equal(t, 54321, int(sys.WalltimeResolution()))
|
||||||
nil, // stderr
|
}
|
||||||
nil, // randSource
|
},
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
nil, // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithEnv",
|
name: "WithSysWalltime",
|
||||||
input: base.WithEnv("a", "b"),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithSysWalltime()
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
nil, // args
|
require.Equal(t, int(time.Microsecond.Nanoseconds()), int(sys.WalltimeResolution()))
|
||||||
[]string{"a=b"}, // environ
|
}
|
||||||
nil, // stdin
|
},
|
||||||
nil, // stdout
|
|
||||||
nil, // stderr
|
|
||||||
nil, // randSource
|
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
nil, // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithEnv empty value",
|
name: "WithArgs empty",
|
||||||
input: base.WithEnv("a", ""),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithArgs()
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
nil, // args
|
args := sys.Args()
|
||||||
[]string{"a="}, // environ
|
require.Equal(t, 0, len(args))
|
||||||
nil, // stdin
|
}
|
||||||
nil, // stdout
|
},
|
||||||
nil, // stderr
|
|
||||||
nil, // randSource
|
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
nil, // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithEnv twice",
|
name: "WithArgs",
|
||||||
input: base.WithEnv("a", "b").WithEnv("c", "de"),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithArgs("a", "bc")
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
nil, // args
|
args := sys.Args()
|
||||||
[]string{"a=b", "c=de"}, // environ
|
require.Equal(t, 2, len(args))
|
||||||
nil, // stdin
|
require.Equal(t, "a", string(args[0]))
|
||||||
nil, // stdout
|
require.Equal(t, "bc", string(args[1]))
|
||||||
nil, // stderr
|
}
|
||||||
nil, // randSource
|
},
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
nil, // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithEnv overwrites",
|
name: "WithArgs empty ok", // Particularly argv[0] can be empty, and we have no rules about others.
|
||||||
input: base.WithEnv("a", "bc").WithEnv("c", "de").WithEnv("a", "de"),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithArgs("", "bc")
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
nil, // args
|
args := sys.Args()
|
||||||
[]string{"a=de", "c=de"}, // environ
|
require.Equal(t, 2, len(args))
|
||||||
nil, // stdin
|
require.Equal(t, "", string(args[0]))
|
||||||
nil, // stdout
|
require.Equal(t, "bc", string(args[1]))
|
||||||
nil, // stderr
|
}
|
||||||
nil, // randSource
|
},
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
nil, // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithEnv twice",
|
name: "WithArgs second call overwrites",
|
||||||
input: base.WithEnv("a", "b").WithEnv("c", "de"),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithArgs("a", "bc").WithArgs("bc", "a")
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
nil, // args
|
args := sys.Args()
|
||||||
[]string{"a=b", "c=de"}, // environ
|
require.Equal(t, 2, len(args))
|
||||||
nil, // stdin
|
require.Equal(t, "bc", string(args[0]))
|
||||||
nil, // stdout
|
require.Equal(t, "a", string(args[1]))
|
||||||
nil, // stderr
|
}
|
||||||
nil, // randSource
|
},
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
nil, // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithFS",
|
name: "WithEnv",
|
||||||
input: base.WithFS(testFS),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithEnv("a", "b")
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
nil, // args
|
envs := sys.Environ()
|
||||||
nil, // environ
|
require.Equal(t, 1, len(envs))
|
||||||
nil, // stdin
|
require.Equal(t, "a=b", string(envs[0]))
|
||||||
nil, // stdout
|
}
|
||||||
nil, // stderr
|
},
|
||||||
nil, // randSource
|
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
sysfs.Adapt(testFS),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithFS overwrites",
|
name: "WithEnv empty value",
|
||||||
input: base.WithFS(testFS).WithFS(testFS2),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithEnv("a", "")
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
nil, // args
|
envs := sys.Environ()
|
||||||
nil, // environ
|
require.Equal(t, 1, len(envs))
|
||||||
nil, // stdin
|
require.Equal(t, "a=", string(envs[0]))
|
||||||
nil, // stdout
|
}
|
||||||
nil, // stderr
|
},
|
||||||
nil, // randSource
|
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
sysfs.Adapt(testFS2), // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithFS nil",
|
name: "WithEnv twice",
|
||||||
input: base.WithFS(nil),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithEnv("a", "b").WithEnv("c", "de")
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
nil, // args
|
envs := sys.Environ()
|
||||||
nil, // environ
|
require.Equal(t, 2, len(envs))
|
||||||
nil, // stdin
|
require.Equal(t, "a=b", string(envs[0]))
|
||||||
nil, // stdout
|
require.Equal(t, "c=de", string(envs[1]))
|
||||||
nil, // stderr
|
}
|
||||||
nil, // randSource
|
},
|
||||||
&wt, 1, // walltime, walltimeResolution
|
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
|
||||||
nil, // nanosleep
|
|
||||||
nil, // osyield
|
|
||||||
nil, // fs
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "WithRandSource",
|
name: "WithEnv overwrites",
|
||||||
input: base.WithRandSource(rand.Reader),
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
expected: requireSysContext(t,
|
config := base.WithEnv("a", "bc").WithEnv("c", "de").WithEnv("a", "ff")
|
||||||
math.MaxUint32, // max
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
nil, // args
|
envs := sys.Environ()
|
||||||
nil, // environ
|
require.Equal(t, 2, len(envs))
|
||||||
nil, // stdin
|
require.Equal(t, "a=ff", string(envs[0]))
|
||||||
nil, // stdout
|
require.Equal(t, "c=de", string(envs[1]))
|
||||||
nil, // stderr
|
}
|
||||||
rand.Reader, // randSource
|
},
|
||||||
&wt, 1, // walltime, walltimeResolution
|
},
|
||||||
&nt, 1, // nanotime, nanotimeResolution
|
{
|
||||||
nil, // nanosleep
|
name: "WithEnv twice",
|
||||||
nil, // osyield
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
nil, // fs
|
config := base.WithEnv("a", "b").WithEnv("c", "de")
|
||||||
),
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
|
envs := sys.Environ()
|
||||||
|
require.Equal(t, 2, len(envs))
|
||||||
|
require.Equal(t, "a=b", string(envs[0]))
|
||||||
|
require.Equal(t, "c=de", string(envs[1]))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WithFS",
|
||||||
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
|
testFS := &testfs.FS{}
|
||||||
|
config := base.WithFS(testFS)
|
||||||
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
|
rootfs := sys.FS().RootFS()
|
||||||
|
require.Equal(t, sysfs.Adapt(testFS), rootfs)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WithFS overwrites",
|
||||||
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
|
testFS, testFS2 := &testfs.FS{}, &testfs.FS{}
|
||||||
|
config := base.WithFS(testFS).WithFS(testFS2)
|
||||||
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
|
rootfs := sys.FS().RootFS()
|
||||||
|
require.Equal(t, sysfs.Adapt(testFS2), rootfs)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WithFS nil",
|
||||||
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
|
config := base.WithFS(nil)
|
||||||
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
|
rootfs := sys.FS().RootFS()
|
||||||
|
require.Equal(t, sysfs.Adapt(nil), rootfs)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WithRandSource",
|
||||||
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
|
r := bytes.NewReader([]byte{1, 2, 3, 4})
|
||||||
|
config := base.WithRandSource(r)
|
||||||
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
|
actual := sys.RandSource()
|
||||||
|
require.Equal(t, r, actual)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WithRandSource nil",
|
||||||
|
input: func() (ModuleConfig, func(t *testing.T, sys *internalsys.Context)) {
|
||||||
|
config := base.WithRandSource(nil)
|
||||||
|
return config, func(t *testing.T, sys *internalsys.Context) {
|
||||||
|
actual := sys.RandSource()
|
||||||
|
require.Equal(t, platform.NewFakeRandSource(), actual)
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,9 +377,10 @@ func TestModuleConfig_toSysContext(t *testing.T) {
|
|||||||
tc := tt
|
tc := tt
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
sysCtx, err := tc.input.(*moduleConfig).toSysContext()
|
config, verify := tc.input()
|
||||||
|
actual, err := config.(*moduleConfig).toSysContext()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, tc.expected, sysCtx)
|
verify(t, actual)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -756,34 +710,3 @@ func TestNewRuntimeConfig(t *testing.T) {
|
|||||||
require.Equal(t, engineKindInterpreter, c.engineKind)
|
require.Equal(t, engineKindInterpreter, c.engineKind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requireSysContext ensures wasm.NewContext doesn't return an error, which makes it usable in test matrices.
|
|
||||||
func requireSysContext(
|
|
||||||
t *testing.T,
|
|
||||||
max uint32,
|
|
||||||
args, environ []string,
|
|
||||||
stdin io.Reader,
|
|
||||||
stdout, stderr io.Writer,
|
|
||||||
randSource io.Reader,
|
|
||||||
walltime *sys.Walltime, walltimeResolution sys.ClockResolution,
|
|
||||||
nanotime *sys.Nanotime, nanotimeResolution sys.ClockResolution,
|
|
||||||
nanosleep *sys.Nanosleep,
|
|
||||||
osyield *sys.Osyield,
|
|
||||||
fs sysfs.FS,
|
|
||||||
) *internalsys.Context {
|
|
||||||
sysCtx, err := internalsys.NewContext(
|
|
||||||
max,
|
|
||||||
toByteSlices(args),
|
|
||||||
toByteSlices(environ),
|
|
||||||
stdin,
|
|
||||||
stdout,
|
|
||||||
stderr,
|
|
||||||
randSource,
|
|
||||||
walltime, walltimeResolution,
|
|
||||||
nanotime, nanotimeResolution,
|
|
||||||
nanosleep, osyield,
|
|
||||||
fs,
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return sysCtx
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,32 +15,30 @@ const (
|
|||||||
|
|
||||||
// NewFakeWalltime implements sys.Walltime with FakeEpochNanos that increases by 1ms each reading.
|
// NewFakeWalltime implements sys.Walltime with FakeEpochNanos that increases by 1ms each reading.
|
||||||
// See /RATIONALE.md
|
// See /RATIONALE.md
|
||||||
func NewFakeWalltime() *sys.Walltime {
|
func NewFakeWalltime() sys.Walltime {
|
||||||
// AddInt64 returns the new value. Adjust so the first reading will be FakeEpochNanos
|
// AddInt64 returns the new value. Adjust so the first reading will be FakeEpochNanos
|
||||||
t := FakeEpochNanos - ms
|
t := FakeEpochNanos - ms
|
||||||
var wt sys.Walltime = func() (sec int64, nsec int32) {
|
return func() (sec int64, nsec int32) {
|
||||||
wt := atomic.AddInt64(&t, ms)
|
wt := atomic.AddInt64(&t, ms)
|
||||||
return wt / 1e9, int32(wt % 1e9)
|
return wt / 1e9, int32(wt % 1e9)
|
||||||
}
|
}
|
||||||
return &wt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFakeNanotime implements sys.Nanotime that increases by 1ms each reading.
|
// NewFakeNanotime implements sys.Nanotime that increases by 1ms each reading.
|
||||||
// See /RATIONALE.md
|
// See /RATIONALE.md
|
||||||
func NewFakeNanotime() *sys.Nanotime {
|
func NewFakeNanotime() sys.Nanotime {
|
||||||
// AddInt64 returns the new value. Adjust so the first reading will be zero.
|
// AddInt64 returns the new value. Adjust so the first reading will be zero.
|
||||||
t := int64(0) - ms
|
t := int64(0) - ms
|
||||||
var nt sys.Nanotime = func() int64 {
|
return func() int64 {
|
||||||
return atomic.AddInt64(&t, ms)
|
return atomic.AddInt64(&t, ms)
|
||||||
}
|
}
|
||||||
return &nt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FakeNanosleep implements sys.Nanosleep by returning without sleeping.
|
// FakeNanosleep implements sys.Nanosleep by returning without sleeping.
|
||||||
func FakeNanosleep(int64) {}
|
var FakeNanosleep = sys.Nanosleep(func(int64) {})
|
||||||
|
|
||||||
// FakeOsyield implements sys.Osyield by returning without yielding.
|
// FakeOsyield implements sys.Osyield by returning without yielding.
|
||||||
func FakeOsyield() {}
|
var FakeOsyield = sys.Osyield(func() {})
|
||||||
|
|
||||||
// Walltime implements sys.Walltime with time.Now.
|
// Walltime implements sys.Walltime with time.Now.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -12,22 +12,22 @@ func Test_NewFakeWalltime(t *testing.T) {
|
|||||||
wt := NewFakeWalltime()
|
wt := NewFakeWalltime()
|
||||||
|
|
||||||
// Base should be the same as FakeEpochNanos
|
// Base should be the same as FakeEpochNanos
|
||||||
sec, nsec := (*wt)()
|
sec, nsec := wt()
|
||||||
ft := time.UnixMicro(FakeEpochNanos / time.Microsecond.Nanoseconds()).UTC()
|
ft := time.UnixMicro(FakeEpochNanos / time.Microsecond.Nanoseconds()).UTC()
|
||||||
require.Equal(t, ft, time.Unix(sec, int64(nsec)).UTC())
|
require.Equal(t, ft, time.Unix(sec, int64(nsec)).UTC())
|
||||||
|
|
||||||
// next reading should increase by 1ms
|
// next reading should increase by 1ms
|
||||||
sec, nsec = (*wt)()
|
sec, nsec = wt()
|
||||||
require.Equal(t, ft.Add(time.Millisecond), time.Unix(sec, int64(nsec)).UTC())
|
require.Equal(t, ft.Add(time.Millisecond), time.Unix(sec, int64(nsec)).UTC())
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_NewFakeNanotime(t *testing.T) {
|
func Test_NewFakeNanotime(t *testing.T) {
|
||||||
nt := NewFakeNanotime()
|
nt := NewFakeNanotime()
|
||||||
|
|
||||||
require.Equal(t, int64(0), (*nt)())
|
require.Equal(t, int64(0), nt())
|
||||||
|
|
||||||
// next reading should increase by 1ms
|
// next reading should increase by 1ms
|
||||||
require.Equal(t, int64(time.Millisecond), (*nt)())
|
require.Equal(t, int64(time.Millisecond), nt())
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Walltime(t *testing.T) {
|
func Test_Walltime(t *testing.T) {
|
||||||
|
|||||||
@@ -252,32 +252,32 @@ type FileTable = descriptor.Table[uint32, *FileEntry]
|
|||||||
//
|
//
|
||||||
// If `preopened` is not sysfs.UnimplementedFS, it is inserted into
|
// If `preopened` is not sysfs.UnimplementedFS, it is inserted into
|
||||||
// the file descriptor table as FdPreopen.
|
// the file descriptor table as FdPreopen.
|
||||||
func NewFSContext(stdin io.Reader, stdout, stderr io.Writer, rootFS sysfs.FS) (fsc *FSContext, err error) {
|
func (c *Context) NewFSContext(stdin io.Reader, stdout, stderr io.Writer, rootFS sysfs.FS) (err error) {
|
||||||
fsc = &FSContext{rootFS: rootFS}
|
c.fsc.rootFS = rootFS
|
||||||
inReader, err := stdinReader(stdin)
|
inReader, err := stdinReader(stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
fsc.openedFiles.Insert(inReader)
|
c.fsc.openedFiles.Insert(inReader)
|
||||||
outWriter, err := stdioWriter(stdout, noopStdoutStat)
|
outWriter, err := stdioWriter(stdout, noopStdoutStat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
fsc.openedFiles.Insert(outWriter)
|
c.fsc.openedFiles.Insert(outWriter)
|
||||||
errWriter, err := stdioWriter(stderr, noopStderrStat)
|
errWriter, err := stdioWriter(stderr, noopStderrStat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
fsc.openedFiles.Insert(errWriter)
|
c.fsc.openedFiles.Insert(errWriter)
|
||||||
|
|
||||||
if _, ok := rootFS.(sysfs.UnimplementedFS); ok {
|
if _, ok := rootFS.(sysfs.UnimplementedFS); ok {
|
||||||
return fsc, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if comp, ok := rootFS.(*sysfs.CompositeFS); ok {
|
if comp, ok := rootFS.(*sysfs.CompositeFS); ok {
|
||||||
preopens := comp.FS()
|
preopens := comp.FS()
|
||||||
for i, p := range comp.GuestPaths() {
|
for i, p := range comp.GuestPaths() {
|
||||||
fsc.openedFiles.Insert(&FileEntry{
|
c.fsc.openedFiles.Insert(&FileEntry{
|
||||||
FS: preopens[i],
|
FS: preopens[i],
|
||||||
Name: p,
|
Name: p,
|
||||||
IsPreopen: true,
|
IsPreopen: true,
|
||||||
@@ -285,7 +285,7 @@ func NewFSContext(stdin io.Reader, stdout, stderr io.Writer, rootFS sysfs.FS) (f
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fsc.openedFiles.Insert(&FileEntry{
|
c.fsc.openedFiles.Insert(&FileEntry{
|
||||||
FS: rootFS,
|
FS: rootFS,
|
||||||
Name: "/",
|
Name: "/",
|
||||||
IsPreopen: true,
|
IsPreopen: true,
|
||||||
@@ -293,7 +293,7 @@ func NewFSContext(stdin io.Reader, stdout, stderr io.Writer, rootFS sysfs.FS) (f
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return fsc, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func stdinReader(r io.Reader) (*FileEntry, error) {
|
func stdinReader(r io.Reader) (*FileEntry, error) {
|
||||||
|
|||||||
@@ -66,8 +66,10 @@ func TestNewFSContext(t *testing.T) {
|
|||||||
tc := tt
|
tc := tt
|
||||||
|
|
||||||
t.Run(tc.name, func(b *testing.T) {
|
t.Run(tc.name, func(b *testing.T) {
|
||||||
fsc, err := NewFSContext(nil, nil, nil, tc.fs)
|
c := Context{}
|
||||||
|
err := c.NewFSContext(nil, nil, nil, tc.fs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
fsc := c.fsc
|
||||||
defer fsc.Close(testCtx)
|
defer fsc.Close(testCtx)
|
||||||
|
|
||||||
preopenedDir, _ := fsc.openedFiles.Lookup(FdPreopen)
|
preopenedDir, _ := fsc.openedFiles.Lookup(FdPreopen)
|
||||||
@@ -102,8 +104,10 @@ func TestFSContext_CloseFile(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testFS := sysfs.Adapt(embedFS)
|
testFS := sysfs.Adapt(embedFS)
|
||||||
|
|
||||||
fsc, err := NewFSContext(nil, nil, nil, testFS)
|
c := Context{}
|
||||||
|
err = c.NewFSContext(nil, nil, nil, testFS)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
fsc := c.fsc
|
||||||
defer fsc.Close(testCtx)
|
defer fsc.Close(testCtx)
|
||||||
|
|
||||||
fdToClose, errno := fsc.OpenFile(testFS, "empty.txt", os.O_RDONLY, 0)
|
fdToClose, errno := fsc.OpenFile(testFS, "empty.txt", os.O_RDONLY, 0)
|
||||||
@@ -132,7 +136,10 @@ func TestFSContext_CloseFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnimplementedFSContext(t *testing.T) {
|
func TestUnimplementedFSContext(t *testing.T) {
|
||||||
testFS, err := NewFSContext(nil, nil, nil, sysfs.UnimplementedFS{})
|
c := Context{}
|
||||||
|
err := c.NewFSContext(nil, nil, nil, sysfs.UnimplementedFS{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
testFS := &c.fsc
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected := &FSContext{rootFS: sysfs.UnimplementedFS{}}
|
expected := &FSContext{rootFS: sysfs.UnimplementedFS{}}
|
||||||
@@ -159,8 +166,10 @@ func TestCompositeFSContext(t *testing.T) {
|
|||||||
rootFS, err := sysfs.NewRootFS([]sysfs.FS{testFS2, testFS1}, []string{"/tmp", "/"})
|
rootFS, err := sysfs.NewRootFS([]sysfs.FS{testFS2, testFS1}, []string{"/tmp", "/"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testFS, err := NewFSContext(nil, nil, nil, rootFS)
|
c := Context{}
|
||||||
|
err = c.NewFSContext(nil, nil, nil, rootFS)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
testFS := &c.fsc
|
||||||
|
|
||||||
// Ensure the pre-opens have exactly the name specified, and are in order.
|
// Ensure the pre-opens have exactly the name specified, and are in order.
|
||||||
preopen3, ok := testFS.openedFiles.Lookup(3)
|
preopen3, ok := testFS.openedFiles.Lookup(3)
|
||||||
@@ -182,8 +191,10 @@ func TestCompositeFSContext(t *testing.T) {
|
|||||||
func TestContext_Close(t *testing.T) {
|
func TestContext_Close(t *testing.T) {
|
||||||
testFS := sysfs.Adapt(testfs.FS{"foo": &testfs.File{}})
|
testFS := sysfs.Adapt(testfs.FS{"foo": &testfs.File{}})
|
||||||
|
|
||||||
fsc, err := NewFSContext(nil, nil, nil, testFS)
|
c := Context{}
|
||||||
|
err := c.NewFSContext(nil, nil, nil, testFS)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
fsc := c.fsc
|
||||||
|
|
||||||
// Verify base case
|
// Verify base case
|
||||||
require.Equal(t, 1+FdPreopen, uint32(fsc.openedFiles.Len()))
|
require.Equal(t, 1+FdPreopen, uint32(fsc.openedFiles.Len()))
|
||||||
@@ -207,8 +218,10 @@ func TestContext_Close_Error(t *testing.T) {
|
|||||||
|
|
||||||
testFS := sysfs.Adapt(testfs.FS{"foo": file})
|
testFS := sysfs.Adapt(testfs.FS{"foo": file})
|
||||||
|
|
||||||
fsc, err := NewFSContext(nil, nil, nil, testFS)
|
c := Context{}
|
||||||
|
err := c.NewFSContext(nil, nil, nil, testFS)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
fsc := c.fsc
|
||||||
|
|
||||||
// open another file
|
// open another file
|
||||||
_, errno := fsc.OpenFile(testFS, "foo", os.O_RDONLY, 0)
|
_, errno := fsc.OpenFile(testFS, "foo", os.O_RDONLY, 0)
|
||||||
@@ -228,7 +241,11 @@ func TestFSContext_ReOpenDir(t *testing.T) {
|
|||||||
errno := dirFs.Mkdir(dirName, 0o700)
|
errno := dirFs.Mkdir(dirName, 0o700)
|
||||||
require.Zero(t, errno)
|
require.Zero(t, errno)
|
||||||
|
|
||||||
fsc, err := NewFSContext(nil, nil, nil, dirFs)
|
c := Context{}
|
||||||
|
err := c.NewFSContext(nil, nil, nil, dirFs)
|
||||||
|
require.NoError(t, err)
|
||||||
|
fsc := c.fsc
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer func() {
|
defer func() {
|
||||||
require.NoError(t, fsc.Close(context.Background()))
|
require.NoError(t, fsc.Close(context.Background()))
|
||||||
@@ -275,45 +292,48 @@ func TestFSContext_Renumber(t *testing.T) {
|
|||||||
errno := dirFs.Mkdir(dirName, 0o700)
|
errno := dirFs.Mkdir(dirName, 0o700)
|
||||||
require.Zero(t, errno)
|
require.Zero(t, errno)
|
||||||
|
|
||||||
c, err := NewFSContext(nil, nil, nil, dirFs)
|
c := Context{}
|
||||||
|
err := c.NewFSContext(nil, nil, nil, dirFs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
fsc := c.fsc
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
require.NoError(t, c.Close(context.Background()))
|
require.NoError(t, fsc.Close(context.Background()))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for _, toFd := range []uint32{10, 100, 100} {
|
for _, toFd := range []uint32{10, 100, 100} {
|
||||||
fromFd, errno := c.OpenFile(dirFs, dirName, os.O_RDONLY, 0)
|
fromFd, errno := fsc.OpenFile(dirFs, dirName, os.O_RDONLY, 0)
|
||||||
require.Zero(t, errno)
|
require.Zero(t, errno)
|
||||||
|
|
||||||
prevDirFile, ok := c.LookupFile(fromFd)
|
prevDirFile, ok := fsc.LookupFile(fromFd)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
require.Zero(t, c.Renumber(fromFd, toFd))
|
require.Zero(t, fsc.Renumber(fromFd, toFd))
|
||||||
|
|
||||||
renumberedDirFile, ok := c.LookupFile(toFd)
|
renumberedDirFile, ok := fsc.LookupFile(toFd)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
require.Equal(t, prevDirFile, renumberedDirFile)
|
require.Equal(t, prevDirFile, renumberedDirFile)
|
||||||
|
|
||||||
// Previous file descriptor shouldn't be used.
|
// Previous file descriptor shouldn't be used.
|
||||||
_, ok = c.LookupFile(fromFd)
|
_, ok = fsc.LookupFile(fromFd)
|
||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("errors", func(t *testing.T) {
|
t.Run("errors", func(t *testing.T) {
|
||||||
// Sanity check for 3 being preopen.
|
// Sanity check for 3 being preopen.
|
||||||
preopen, ok := c.LookupFile(3)
|
preopen, ok := fsc.LookupFile(3)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.True(t, preopen.IsPreopen)
|
require.True(t, preopen.IsPreopen)
|
||||||
|
|
||||||
// From is preopen.
|
// From is preopen.
|
||||||
require.Equal(t, syscall.ENOTSUP, c.Renumber(3, 100))
|
require.Equal(t, syscall.ENOTSUP, fsc.Renumber(3, 100))
|
||||||
|
|
||||||
// From does not exist.
|
// From does not exist.
|
||||||
require.Equal(t, syscall.EBADF, c.Renumber(12345, 3))
|
require.Equal(t, syscall.EBADF, fsc.Renumber(12345, 3))
|
||||||
|
|
||||||
// Both are preopen.
|
// Both are preopen.
|
||||||
require.Equal(t, syscall.ENOTSUP, c.Renumber(3, 3))
|
require.Equal(t, syscall.ENOTSUP, fsc.Renumber(3, 3))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,36 +344,41 @@ func TestFSContext_ChangeOpenFlag(t *testing.T) {
|
|||||||
const fileName = "dir"
|
const fileName = "dir"
|
||||||
require.NoError(t, os.WriteFile(path.Join(tmpDir, fileName), []byte("0123456789"), 0o600))
|
require.NoError(t, os.WriteFile(path.Join(tmpDir, fileName), []byte("0123456789"), 0o600))
|
||||||
|
|
||||||
c, errno := NewFSContext(nil, nil, nil, dirFs)
|
c := Context{}
|
||||||
require.NoError(t, errno)
|
err := c.NewFSContext(nil, nil, nil, dirFs)
|
||||||
|
require.NoError(t, err)
|
||||||
|
fsc := c.fsc
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
require.NoError(t, c.Close(context.Background()))
|
require.NoError(t, fsc.Close(context.Background()))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Without APPEND.
|
// Without APPEND.
|
||||||
fd, errno := c.OpenFile(dirFs, fileName, os.O_RDWR, 0o600)
|
fd, errno := fsc.OpenFile(dirFs, fileName, os.O_RDWR, 0o600)
|
||||||
require.Zero(t, errno)
|
require.Zero(t, errno)
|
||||||
|
|
||||||
f0, ok := c.openedFiles.Lookup(fd)
|
f0, ok := fsc.openedFiles.Lookup(fd)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, f0.openFlag&syscall.O_APPEND, 0)
|
require.Equal(t, f0.openFlag&syscall.O_APPEND, 0)
|
||||||
|
|
||||||
// Set the APPEND flag.
|
// Set the APPEND flag.
|
||||||
require.Zero(t, c.ChangeOpenFlag(fd, syscall.O_APPEND))
|
require.Zero(t, fsc.ChangeOpenFlag(fd, syscall.O_APPEND))
|
||||||
f1, ok := c.openedFiles.Lookup(fd)
|
f1, ok := fsc.openedFiles.Lookup(fd)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, f1.openFlag&syscall.O_APPEND, syscall.O_APPEND)
|
require.Equal(t, f1.openFlag&syscall.O_APPEND, syscall.O_APPEND)
|
||||||
|
|
||||||
// Remove the APPEND flag.
|
// Remove the APPEND flag.
|
||||||
require.Zero(t, c.ChangeOpenFlag(fd, 0))
|
require.Zero(t, fsc.ChangeOpenFlag(fd, 0))
|
||||||
f2, ok := c.openedFiles.Lookup(fd)
|
f2, ok := fsc.openedFiles.Lookup(fd)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, f2.openFlag&syscall.O_APPEND, 0)
|
require.Equal(t, f2.openFlag&syscall.O_APPEND, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriterForFile(t *testing.T) {
|
func TestWriterForFile(t *testing.T) {
|
||||||
testFS, err := NewFSContext(nil, nil, nil, sysfs.UnimplementedFS{})
|
c := Context{}
|
||||||
|
err := c.NewFSContext(nil, nil, nil, sysfs.UnimplementedFS{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
testFS := &c.fsc
|
||||||
|
|
||||||
require.Nil(t, WriterForFile(testFS, FdStdin))
|
require.Nil(t, WriterForFile(testFS, FdStdin))
|
||||||
require.Equal(t, noopStdout.File, WriterForFile(testFS, FdStdout))
|
require.Equal(t, noopStdout.File, WriterForFile(testFS, FdStdout))
|
||||||
|
|||||||
@@ -17,16 +17,14 @@ type Context struct {
|
|||||||
args, environ [][]byte
|
args, environ [][]byte
|
||||||
argsSize, environSize uint32
|
argsSize, environSize uint32
|
||||||
|
|
||||||
// Note: Using function pointers here keeps them stable for tests.
|
walltime sys.Walltime
|
||||||
|
|
||||||
walltime *sys.Walltime
|
|
||||||
walltimeResolution sys.ClockResolution
|
walltimeResolution sys.ClockResolution
|
||||||
nanotime *sys.Nanotime
|
nanotime sys.Nanotime
|
||||||
nanotimeResolution sys.ClockResolution
|
nanotimeResolution sys.ClockResolution
|
||||||
nanosleep *sys.Nanosleep
|
nanosleep sys.Nanosleep
|
||||||
osyield *sys.Osyield
|
osyield sys.Osyield
|
||||||
randSource io.Reader
|
randSource io.Reader
|
||||||
fsc *FSContext
|
fsc FSContext
|
||||||
}
|
}
|
||||||
|
|
||||||
// Args is like os.Args and defaults to nil.
|
// Args is like os.Args and defaults to nil.
|
||||||
@@ -65,7 +63,7 @@ func (c *Context) EnvironSize() uint32 {
|
|||||||
|
|
||||||
// Walltime implements platform.Walltime.
|
// Walltime implements platform.Walltime.
|
||||||
func (c *Context) Walltime() (sec int64, nsec int32) {
|
func (c *Context) Walltime() (sec int64, nsec int32) {
|
||||||
return (*(c.walltime))()
|
return c.walltime()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WalltimeNanos returns platform.Walltime as epoch nanoseconds.
|
// WalltimeNanos returns platform.Walltime as epoch nanoseconds.
|
||||||
@@ -81,7 +79,7 @@ func (c *Context) WalltimeResolution() sys.ClockResolution {
|
|||||||
|
|
||||||
// Nanotime implements sys.Nanotime.
|
// Nanotime implements sys.Nanotime.
|
||||||
func (c *Context) Nanotime() int64 {
|
func (c *Context) Nanotime() int64 {
|
||||||
return (*(c.nanotime))()
|
return c.nanotime()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NanotimeResolution returns resolution of Nanotime.
|
// NanotimeResolution returns resolution of Nanotime.
|
||||||
@@ -91,17 +89,17 @@ func (c *Context) NanotimeResolution() sys.ClockResolution {
|
|||||||
|
|
||||||
// Nanosleep implements sys.Nanosleep.
|
// Nanosleep implements sys.Nanosleep.
|
||||||
func (c *Context) Nanosleep(ns int64) {
|
func (c *Context) Nanosleep(ns int64) {
|
||||||
(*(c.nanosleep))(ns)
|
c.nanosleep(ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Osyield implements sys.Osyield.
|
// Osyield implements sys.Osyield.
|
||||||
func (c *Context) Osyield() {
|
func (c *Context) Osyield() {
|
||||||
(*(c.osyield))()
|
c.osyield()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FS returns the possibly empty (sysfs.UnimplementedFS) file system context.
|
// FS returns the possibly empty (sysfs.UnimplementedFS) file system context.
|
||||||
func (c *Context) FS() *FSContext {
|
func (c *Context) FS() *FSContext {
|
||||||
return c.fsc
|
return &c.fsc
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandSource is a source of random bytes and defaults to a deterministic source.
|
// RandSource is a source of random bytes and defaults to a deterministic source.
|
||||||
@@ -120,6 +118,8 @@ func (eofReader) Read([]byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DefaultContext returns Context with no values set except a possible nil fs.FS
|
// DefaultContext returns Context with no values set except a possible nil fs.FS
|
||||||
|
//
|
||||||
|
// This is only used for testing.
|
||||||
func DefaultContext(fs sysfs.FS) *Context {
|
func DefaultContext(fs sysfs.FS) *Context {
|
||||||
if sysCtx, err := NewContext(0, nil, nil, nil, nil, nil, nil, nil, 0, nil, 0, nil, nil, fs); err != nil {
|
if sysCtx, err := NewContext(0, nil, nil, nil, nil, nil, nil, nil, 0, nil, 0, nil, nil, fs); err != nil {
|
||||||
panic(fmt.Errorf("BUG: DefaultContext should never error: %w", err))
|
panic(fmt.Errorf("BUG: DefaultContext should never error: %w", err))
|
||||||
@@ -128,12 +128,6 @@ func DefaultContext(fs sysfs.FS) *Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
_ = DefaultContext(nil) // Force panic on bug.
|
|
||||||
ns sys.Nanosleep = platform.FakeNanosleep
|
|
||||||
oy sys.Osyield = platform.FakeOsyield
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewContext is a factory function which helps avoid needing to know defaults or exporting all fields.
|
// NewContext is a factory function which helps avoid needing to know defaults or exporting all fields.
|
||||||
// Note: max is exposed for testing. max is only used for env/args validation.
|
// Note: max is exposed for testing. max is only used for env/args validation.
|
||||||
func NewContext(
|
func NewContext(
|
||||||
@@ -142,12 +136,12 @@ func NewContext(
|
|||||||
stdin io.Reader,
|
stdin io.Reader,
|
||||||
stdout, stderr io.Writer,
|
stdout, stderr io.Writer,
|
||||||
randSource io.Reader,
|
randSource io.Reader,
|
||||||
walltime *sys.Walltime,
|
walltime sys.Walltime,
|
||||||
walltimeResolution sys.ClockResolution,
|
walltimeResolution sys.ClockResolution,
|
||||||
nanotime *sys.Nanotime,
|
nanotime sys.Nanotime,
|
||||||
nanotimeResolution sys.ClockResolution,
|
nanotimeResolution sys.ClockResolution,
|
||||||
nanosleep *sys.Nanosleep,
|
nanosleep sys.Nanosleep,
|
||||||
osyield *sys.Osyield,
|
osyield sys.Osyield,
|
||||||
rootFS sysfs.FS,
|
rootFS sysfs.FS,
|
||||||
) (sysCtx *Context, err error) {
|
) (sysCtx *Context, err error) {
|
||||||
sysCtx = &Context{args: args, environ: environ}
|
sysCtx = &Context{args: args, environ: environ}
|
||||||
@@ -191,19 +185,19 @@ func NewContext(
|
|||||||
if nanosleep != nil {
|
if nanosleep != nil {
|
||||||
sysCtx.nanosleep = nanosleep
|
sysCtx.nanosleep = nanosleep
|
||||||
} else {
|
} else {
|
||||||
sysCtx.nanosleep = &ns
|
sysCtx.nanosleep = platform.FakeNanosleep
|
||||||
}
|
}
|
||||||
|
|
||||||
if osyield != nil {
|
if osyield != nil {
|
||||||
sysCtx.osyield = osyield
|
sysCtx.osyield = osyield
|
||||||
} else {
|
} else {
|
||||||
sysCtx.osyield = &oy
|
sysCtx.osyield = platform.FakeOsyield
|
||||||
}
|
}
|
||||||
|
|
||||||
if rootFS != nil {
|
if rootFS != nil {
|
||||||
sysCtx.fsc, err = NewFSContext(stdin, stdout, stderr, rootFS)
|
err = sysCtx.NewFSContext(stdin, stdout, stderr, rootFS)
|
||||||
} else {
|
} else {
|
||||||
sysCtx.fsc, err = NewFSContext(stdin, stdout, stderr, sysfs.UnimplementedFS{})
|
err = sysCtx.NewFSContext(stdin, stdout, stderr, sysfs.UnimplementedFS{})
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -15,15 +15,6 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/sys"
|
"github.com/tetratelabs/wazero/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContext_FS(t *testing.T) {
|
|
||||||
sysCtx := DefaultContext(nil)
|
|
||||||
|
|
||||||
fsc, err := NewFSContext(nil, nil, nil, sysfs.UnimplementedFS{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, fsc, sysCtx.FS())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_WalltimeNanos(t *testing.T) {
|
func TestContext_WalltimeNanos(t *testing.T) {
|
||||||
sysCtx := DefaultContext(nil)
|
sysCtx := DefaultContext(nil)
|
||||||
|
|
||||||
@@ -60,11 +51,9 @@ func TestDefaultSysContext(t *testing.T) {
|
|||||||
require.Equal(t, sys.ClockResolution(1_000), sysCtx.WalltimeResolution())
|
require.Equal(t, sys.ClockResolution(1_000), sysCtx.WalltimeResolution())
|
||||||
require.Zero(t, sysCtx.Nanotime()) // See above on functions.
|
require.Zero(t, sysCtx.Nanotime()) // See above on functions.
|
||||||
require.Equal(t, sys.ClockResolution(1), sysCtx.NanotimeResolution())
|
require.Equal(t, sys.ClockResolution(1), sysCtx.NanotimeResolution())
|
||||||
require.Equal(t, &ns, sysCtx.nanosleep)
|
require.Equal(t, platform.FakeNanosleep, sysCtx.nanosleep)
|
||||||
require.Equal(t, platform.NewFakeRandSource(), sysCtx.RandSource())
|
require.Equal(t, platform.NewFakeRandSource(), sysCtx.RandSource())
|
||||||
|
|
||||||
expectedFS, _ := NewFSContext(nil, nil, nil, testFS)
|
|
||||||
|
|
||||||
expectedOpenedFiles := FileTable{}
|
expectedOpenedFiles := FileTable{}
|
||||||
expectedOpenedFiles.Insert(noopStdin)
|
expectedOpenedFiles.Insert(noopStdin)
|
||||||
expectedOpenedFiles.Insert(noopStdout)
|
expectedOpenedFiles.Insert(noopStdout)
|
||||||
@@ -75,9 +64,7 @@ func TestDefaultSysContext(t *testing.T) {
|
|||||||
FS: testFS,
|
FS: testFS,
|
||||||
File: &lazyDir{fs: testFS},
|
File: &lazyDir{fs: testFS},
|
||||||
})
|
})
|
||||||
|
require.Equal(t, expectedOpenedFiles, sysCtx.FS().openedFiles)
|
||||||
require.Equal(t, expectedOpenedFiles, expectedFS.openedFiles)
|
|
||||||
require.Equal(t, expectedFS, sysCtx.FS())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileEntry_cachedStat(t *testing.T) {
|
func TestFileEntry_cachedStat(t *testing.T) {
|
||||||
@@ -104,7 +91,9 @@ func TestFileEntry_cachedStat(t *testing.T) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
fsc, _ := NewFSContext(nil, nil, nil, tc.fs)
|
c := Context{}
|
||||||
|
_ = c.NewFSContext(nil, nil, nil, tc.fs)
|
||||||
|
fsc := c.fsc
|
||||||
defer fsc.Close(testCtx)
|
defer fsc.Close(testCtx)
|
||||||
|
|
||||||
f, ok := fsc.LookupFile(FdPreopen)
|
f, ok := fsc.LookupFile(FdPreopen)
|
||||||
@@ -258,7 +247,7 @@ func TestNewContext_Environ(t *testing.T) {
|
|||||||
func TestNewContext_Walltime(t *testing.T) {
|
func TestNewContext_Walltime(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
time *sys.Walltime
|
time sys.Walltime
|
||||||
resolution sys.ClockResolution
|
resolution sys.ClockResolution
|
||||||
expectedErr string
|
expectedErr string
|
||||||
}{
|
}{
|
||||||
@@ -307,7 +296,7 @@ func TestNewContext_Walltime(t *testing.T) {
|
|||||||
func TestNewContext_Nanotime(t *testing.T) {
|
func TestNewContext_Nanotime(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
time *sys.Nanotime
|
time sys.Nanotime
|
||||||
resolution sys.ClockResolution
|
resolution sys.ClockResolution
|
||||||
expectedErr string
|
expectedErr string
|
||||||
}{
|
}{
|
||||||
@@ -396,12 +385,12 @@ func TestNewContext_Nanosleep(t *testing.T) {
|
|||||||
nil, // randSource
|
nil, // randSource
|
||||||
nil, 0, // Nanosleep, NanosleepResolution
|
nil, 0, // Nanosleep, NanosleepResolution
|
||||||
nil, 0, // Nanosleep, NanosleepResolution
|
nil, 0, // Nanosleep, NanosleepResolution
|
||||||
&aNs, // nanosleep
|
aNs, // nanosleep
|
||||||
nil, // osyield
|
nil, // osyield
|
||||||
nil, // rootFS
|
nil, // rootFS
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, &aNs, sysCtx.nanosleep)
|
require.Equal(t, aNs, sysCtx.nanosleep)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewContext_Osyield(t *testing.T) {
|
func TestNewContext_Osyield(t *testing.T) {
|
||||||
@@ -417,9 +406,9 @@ func TestNewContext_Osyield(t *testing.T) {
|
|||||||
nil, 0, // Nanosleep, NanosleepResolution
|
nil, 0, // Nanosleep, NanosleepResolution
|
||||||
nil, 0, // Nanosleep, NanosleepResolution
|
nil, 0, // Nanosleep, NanosleepResolution
|
||||||
nil, // nanosleep
|
nil, // nanosleep
|
||||||
&oy, // osyield
|
oy, // osyield
|
||||||
nil, // rootFS
|
nil, // rootFS
|
||||||
)
|
)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, &oy, sysCtx.osyield)
|
require.Equal(t, oy, sysCtx.osyield)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user