Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gnovm/tests/stdlibs): add fmt #3847

Merged
merged 21 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions gnovm/cmd/gno/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ func execRun(cfg *runCfg, args []string, io commands.IO) error {
stderr := io.Err()

// init store and machine
output := test.OutputWithError(stdout, stderr)
_, testStore := test.Store(
cfg.rootDir,
stdin, stdout, stderr)
cfg.rootDir, output)
if cfg.verbose {
testStore.SetLogStoreOps(true)
}
Expand All @@ -118,7 +118,7 @@ func execRun(cfg *runCfg, args []string, io commands.IO) error {
ctx := test.Context(pkgPath, send)
m := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: pkgPath,
Output: stdout,
Output: output,
Input: stdin,
Store: testStore,
Context: ctx,
Expand Down
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ func execTest(cfg *testCfg, args []string, io commands.IO) error {
if cfg.verbose {
stdout = io.Out()
}
opts := test.NewTestOptions(cfg.rootDir, io.In(), stdout, io.Err())
opts := test.NewTestOptions(cfg.rootDir, stdout, io.Err())
opts.RunFlag = cfg.run
opts.Sync = cfg.updateGoldenTests
opts.Verbose = cfg.verbose
Expand Down
3 changes: 2 additions & 1 deletion gnovm/cmd/gno/testdata/test/pkg_underscore_test.txtar
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Test a pkg name with _test as suffix

# Set up GNOROOT in the current directory.
mkdir $WORK/gnovm
mkdir $WORK/gnovm/tests
symlink $WORK/gnovm/stdlibs -> $GNOROOT/gnovm/stdlibs
symlink $WORK/gnovm/tests/stdlibs -> $GNOROOT/gnovm/tests/stdlibs
env GNOROOT=$WORK

gno test -v ./examples/gno.land/p/demo/hello
Expand Down
3 changes: 2 additions & 1 deletion gnovm/cmd/gno/testdata/test/realm_boundmethod.txtar
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Set up GNOROOT in the current directory.
mkdir $WORK/gnovm
mkdir $WORK/gnovm/tests
symlink $WORK/gnovm/stdlibs -> $GNOROOT/gnovm/stdlibs
symlink $WORK/gnovm/tests/stdlibs -> $GNOROOT/gnovm/tests/stdlibs
env GNOROOT=$WORK

gno test -v ./examples/gno.land/r/demo/realm2
Expand Down
6 changes: 1 addition & 5 deletions gnovm/cmd/gno/tool_lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func execLint(cfg *lintCfg, args []string, io commands.IO) error {
hasError := false

bs, ts := test.StoreWithOptions(
rootDir, nopReader{}, goio.Discard, goio.Discard,
rootDir, goio.Discard,
test.StoreOptions{PreprocessOnly: true},
)

Expand Down Expand Up @@ -317,7 +317,3 @@ func issueFromError(pkgPath string, err error) lintIssue {
}
return issue
}

type nopReader struct{}

func (nopReader) Read(p []byte) (int, error) { return 0, goio.EOF }
7 changes: 3 additions & 4 deletions gnovm/pkg/gnolang/debugger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ func evalTest(debugAddr, in, file string) (out, err string) {
bout := bytes.NewBufferString("")
berr := bytes.NewBufferString("")
stdin := bytes.NewBufferString(in)
stdout := writeNopCloser{bout}
stderr := writeNopCloser{berr}
output := test.OutputWithError(writeNopCloser{bout}, writeNopCloser{berr})

defer func() {
if r := recover(); r != nil {
Expand All @@ -39,14 +38,14 @@ func evalTest(debugAddr, in, file string) (out, err string) {
err = strings.TrimSpace(strings.ReplaceAll(err, "../../tests/files/", "files/"))
}()

_, testStore := test.Store(gnoenv.RootDir(), stdin, stdout, stderr)
_, testStore := test.Store(gnoenv.RootDir(), output)

f := gnolang.MustReadFile(file)

m := gnolang.NewMachineWithOptions(gnolang.MachineOptions{
PkgPath: string(f.PkgName),
Input: stdin,
Output: stdout,
Output: output,
Store: testStore,
Context: test.Context(string(f.PkgName), nil),
Debug: true,
Expand Down
41 changes: 39 additions & 2 deletions gnovm/pkg/gnolang/files_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func TestFiles(t *testing.T) {
Sync: *withSync,
}
o.BaseStore, o.TestStore = test.StoreWithOptions(
rootDir, nopReader{}, o.WriterForStore(), io.Discard,
rootDir, o.WriterForStore(),
test.StoreOptions{WithExtern: true},
)
return o
Expand Down Expand Up @@ -121,7 +121,7 @@ func TestStdlibs(t *testing.T) {
capture = new(bytes.Buffer)
out = capture
}
opts = test.NewTestOptions(rootDir, nopReader{}, out, out)
opts = test.NewTestOptions(rootDir, out, out)
opts.Verbose = true
return
}
Expand Down Expand Up @@ -179,4 +179,41 @@ func TestStdlibs(t *testing.T) {
if err != nil {
t.Fatal(err)
}

testDir := "../../tests/stdlibs/"
testFs := os.DirFS(testDir)
err = fs.WalkDir(testFs, ".", func(path string, de fs.DirEntry, err error) error {
switch {
case err != nil:
return err
case !de.IsDir() || path == ".":
return nil
}
if _, err := os.Stat(filepath.Join(dir, path)); err == nil {
// skip; this dir exists already in the normal stdlibs and we
// currently don't support testing these "mixed stdlibs".
return nil
}

fp := filepath.Join(testDir, path)
memPkg := gnolang.MustReadMemPackage(fp, path)
t.Run("test-"+strings.ReplaceAll(memPkg.Path, "/", "-"), func(t *testing.T) {
if sharedCapture != nil {
sharedCapture.Reset()
}

err := test.Test(memPkg, "", sharedOpts)
if !testing.Verbose() {
t.Log(sharedCapture.String())
}
if err != nil {
t.Error(err)
}
})

return nil
})
if err != nil {
t.Fatal(err)
}
}
2 changes: 1 addition & 1 deletion gnovm/pkg/gnolang/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,7 @@ func (it *InterfaceType) String() string {
it.Generic,
FieldTypeList(it.Methods).String())
} else {
return fmt.Sprintf("interface{%s}",
return fmt.Sprintf("interface {%s}",
FieldTypeList(it.Methods).String())
}
}
Expand Down
4 changes: 2 additions & 2 deletions gnovm/pkg/repl/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,15 @@ func NewRepl(opts ...ReplOption) *Repl {
r.stderr = &b

r.storeFunc = func() gno.Store {
_, st := test.Store(gnoenv.RootDir(), r.stdin, r.stdout, r.stderr)
_, st := test.Store(gnoenv.RootDir(), test.OutputWithError(r.stdout, r.stderr))
return st
}

for _, o := range opts {
o(r)
}

r.state = newState(r.stdout, r.storeFunc)
r.state = newState(test.OutputWithError(r.stdout, r.stderr), r.storeFunc)

br := bufio.NewReader(r.stdin)
bw := bufio.NewWriter(io.MultiWriter(r.stderr, r.stdout))
Expand Down
1 change: 1 addition & 0 deletions gnovm/pkg/test/filetest.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
// the first string is set to the new generated content of the file.
func (opts *TestOptions) RunFiletest(filename string, source []byte) (string, error) {
opts.outWriter.w = opts.Output
opts.outWriter.errW = opts.Error

return opts.runFiletest(filename, source)
}
Expand Down
45 changes: 6 additions & 39 deletions gnovm/pkg/test/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,19 @@ type StoreOptions struct {
// NOTE: this isn't safe, should only be used for testing.
func Store(
rootDir string,
stdin io.Reader,
stdout, stderr io.Writer,
output io.Writer,
) (
baseStore storetypes.CommitStore,
resStore gno.Store,
) {
return StoreWithOptions(rootDir, stdin, stdout, stderr, StoreOptions{})
return StoreWithOptions(rootDir, output, StoreOptions{})
}

// StoreWithOptions is a variant of [Store] which additionally accepts a
// [StoreOptions] argument.
func StoreWithOptions(
rootDir string,
stdin io.Reader,
stdout, stderr io.Writer,
output io.Writer,
opts StoreOptions,
) (
baseStore storetypes.CommitStore,
Expand Down Expand Up @@ -84,7 +82,7 @@ func StoreWithOptions(
ctx := Context(pkgPath, send)
m2 := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "test",
Output: stdout,
Output: output,
Store: store,
Context: ctx,
})
Expand All @@ -95,37 +93,6 @@ func StoreWithOptions(
// gonative exceptions.
// These are values available using gonative; eventually they should all be removed.
switch pkgPath {
case "os":
pkg := gno.NewPackageNode("os", pkgPath, nil)
pkg.DefineGoNativeValue("Stdin", stdin)
pkg.DefineGoNativeValue("Stdout", stdout)
pkg.DefineGoNativeValue("Stderr", stderr)
return pkg, pkg.NewPackage()
case "fmt":
pkg := gno.NewPackageNode("fmt", pkgPath, nil)
pkg.DefineGoNativeValue("Println", func(a ...interface{}) (n int, err error) {
// NOTE: uncomment to debug long running tests
// fmt.Println(a...)
res := fmt.Sprintln(a...)
return stdout.Write([]byte(res))
})
pkg.DefineGoNativeValue("Printf", func(format string, a ...interface{}) (n int, err error) {
res := fmt.Sprintf(format, a...)
return stdout.Write([]byte(res))
})
pkg.DefineGoNativeValue("Print", func(a ...interface{}) (n int, err error) {
res := fmt.Sprint(a...)
return stdout.Write([]byte(res))
})
pkg.DefineGoNativeValue("Sprint", fmt.Sprint)
pkg.DefineGoNativeValue("Sprintf", fmt.Sprintf)
pkg.DefineGoNativeValue("Sprintln", fmt.Sprintln)
pkg.DefineGoNativeValue("Sscanf", fmt.Sscanf)
pkg.DefineGoNativeValue("Errorf", fmt.Errorf)
pkg.DefineGoNativeValue("Fprintln", fmt.Fprintln)
pkg.DefineGoNativeValue("Fprintf", fmt.Fprintf)
pkg.DefineGoNativeValue("Fprint", fmt.Fprint)
return pkg, pkg.NewPackage()
case "encoding/json":
pkg := gno.NewPackageNode("json", pkgPath, nil)
pkg.DefineGoNativeValue("Unmarshal", json.Unmarshal)
Expand Down Expand Up @@ -163,7 +130,7 @@ func StoreWithOptions(
}

// Load normal stdlib.
pn, pv = loadStdlib(rootDir, pkgPath, store, stdout, opts.PreprocessOnly)
pn, pv = loadStdlib(rootDir, pkgPath, store, output, opts.PreprocessOnly)
if pn != nil {
return
}
Expand All @@ -180,7 +147,7 @@ func StoreWithOptions(
ctx := Context(pkgPath, send)
m2 := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "test",
Output: stdout,
Output: output,
Store: store,
Context: ctx,
})
Expand Down
48 changes: 40 additions & 8 deletions gnovm/pkg/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,21 @@
})
}

// OutputWithError returns an io.Writer that can be used as a [gno.Machine.Output],
// where the test standard libraries will write to errWriter when using
// os.Stderr.
func OutputWithError(output, errWriter io.Writer) io.Writer {
return &outputWithError{output, errWriter}
}

type outputWithError struct {
w io.Writer
errW io.Writer
}

func (o outputWithError) Write(p []byte) (int, error) { return o.w.Write(p) }
func (o outputWithError) StderrWrite(p []byte) (int, error) { return o.errW.Write(p) }

Check warning on line 95 in gnovm/pkg/test/test.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/test/test.go#L95

Added line #L95 was not covered by tests

// ----------------------------------------
// testParams

Expand Down Expand Up @@ -136,37 +151,53 @@
}

// NewTestOptions sets up TestOptions, filling out all "required" parameters.
func NewTestOptions(rootDir string, stdin io.Reader, stdout, stderr io.Writer) *TestOptions {
func NewTestOptions(rootDir string, stdout, stderr io.Writer) *TestOptions {
opts := &TestOptions{
RootDir: rootDir,
Output: stdout,
Error: stderr,
}
opts.BaseStore, opts.TestStore = Store(rootDir, stdin, opts.WriterForStore(), stderr)
opts.BaseStore, opts.TestStore = Store(rootDir, opts.WriterForStore())
return opts
}

// proxyWriter is a simple wrapper around a io.Writer, it exists so that the
// underlying writer can be swapped with another when necessary.
type proxyWriter struct {
w io.Writer
w io.Writer
errW io.Writer
}

func (p *proxyWriter) Write(b []byte) (int, error) {
return p.w.Write(b)
}

// StderrWrite implements the interface specified in tests/stdlibs/os/os.go,
// which if found in Machine.Output allows to write to stderr from Gno.
func (p *proxyWriter) StderrWrite(b []byte) (int, error) {
return p.errW.Write(b)
}

// tee temporarily appends the writer w to an underlying MultiWriter, which
// should then be reverted using revert().
func (p *proxyWriter) tee(w io.Writer) (revert func()) {
save := p.w
rev := tee(&p.w, w)
revErr := tee(&p.errW, w)
return func() {
rev()
revErr()
}
}

func tee(ptr *io.Writer, dst io.Writer) (revert func()) {
save := *ptr
if save == io.Discard {
p.w = w
*ptr = dst
} else {
p.w = io.MultiWriter(save, w)
*ptr = io.MultiWriter(save, dst)
}
return func() {
p.w = save
*ptr = save
}
}

Expand All @@ -178,6 +209,7 @@
// tests; you can use [NewTestOptions] for a common base configuration.
func Test(memPkg *gnovm.MemPackage, fsDir string, opts *TestOptions) error {
opts.outWriter.w = opts.Output
opts.outWriter.errW = opts.Error

var errs error

Expand Down Expand Up @@ -313,7 +345,7 @@
// - Run the test files before this for loop (but persist it to store;
// RunFiles doesn't do that currently)
// - Wrap here.
m = Machine(gs, opts.Output, memPkg.Path, opts.Debug)
m = Machine(gs, opts.WriterForStore(), memPkg.Path, opts.Debug)
m.Alloc = alloc.Reset()
m.SetActivePackage(pv)

Expand Down
4 changes: 2 additions & 2 deletions gnovm/stdlibs/bufio/example_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ func ExampleWriter() {

// The simplest use of a Scanner, to read standard input as a set of lines.
func ExampleScanner_lines() {
scanner := bufio.NewScanner(os.Stdin)
scanner := bufio.NewScanner(strings.NewReader("hello\nworld\n"))
for scanner.Scan() {
fmt.Println(scanner.Text()) // Println will add back the final '\n'
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
fmt.Fprintln(os.Stderr, "reading buffer:", err)
}
}

Expand Down
2 changes: 1 addition & 1 deletion gnovm/tests/files/addressable_8a_err.gno
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ func main() {
}

// Error:
// main/files/addressable_8a_err.gno:4:6: cannot take address of func func(){ (const (println func(...interface{})))((const ("Hello, World!" string))) }
// main/files/addressable_8a_err.gno:4:6: cannot take address of func func(){ (const (println func(...interface {})))((const ("Hello, World!" string))) }
Loading
Loading