diff --git a/go.mod b/go.mod index 5fdf609c..c87c414b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/go-python/gpython go 1.17 require ( + github.com/google/go-cmp v0.5.7 github.com/gopherjs/gopherwasm v1.1.0 github.com/peterh/liner v1.2.2 ) diff --git a/go.sum b/go.sum index 42793c4f..34e6510b 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw= github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherwasm v1.1.0 h1:fA2uLoctU5+T3OhOn2vYP0DVT6pxc7xhTlBB1paATqQ= @@ -12,3 +14,5 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pytest/pytest.go b/pytest/pytest.go index 7cadadb1..232f7525 100644 --- a/pytest/pytest.go +++ b/pytest/pytest.go @@ -5,14 +5,17 @@ package pytest import ( + "bytes" "io" "os" "path" + "path/filepath" "strings" "testing" "github.com/go-python/gpython/compile" "github.com/go-python/gpython/py" + "github.com/google/go-cmp/cmp" _ "github.com/go-python/gpython/stdlib" ) @@ -126,3 +129,57 @@ func RunBenchmarks(b *testing.B, testDir string) { }) } } + +// RunScript runs the provided path to a script. +// RunScript captures the stdout and stderr while executing the script +// and compares it to a golden file: +// RunScript("./testdata/foo.py") +// will compare the output with "./testdata/foo_golden.txt". +func RunScript(t *testing.T, fname string) { + opts := py.DefaultContextOpts() + opts.SysArgs = []string{fname} + ctx := py.NewContext(opts) + defer ctx.Close() + + sys := ctx.Store().MustGetModule("sys") + tmp, err := os.MkdirTemp("", "gpython-pytest-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmp) + + out, err := os.Create(filepath.Join(tmp, "combined")) + if err != nil { + t.Fatalf("could not create stdout/stderr: %+v", err) + } + defer out.Close() + + sys.Globals["stdout"] = &py.File{File: out, FileMode: py.FileWrite} + sys.Globals["stderr"] = &py.File{File: out, FileMode: py.FileWrite} + + _, err = py.RunFile(ctx, fname, py.CompileOpts{}, nil) + if err != nil { + t.Fatalf("could not run script %q: %+v", fname, err) + } + + err = out.Close() + if err != nil { + t.Fatalf("could not close stdout/stderr: %+v", err) + } + + got, err := os.ReadFile(out.Name()) + if err != nil { + t.Fatalf("could not read script output: %+v", err) + } + + ref := fname[:len(fname)-len(".py")] + "_golden.txt" + want, err := os.ReadFile(ref) + if err != nil { + t.Fatalf("could not read golden output %q: %+v", ref, err) + } + + diff := cmp.Diff(string(want), string(got)) + if !bytes.Equal(got, want) { + t.Fatalf("output differ: -- (-ref +got)\n%s", diff) + } +} diff --git a/pytest/pytest_test.go b/pytest/pytest_test.go new file mode 100644 index 00000000..15e136bf --- /dev/null +++ b/pytest/pytest_test.go @@ -0,0 +1,33 @@ +// Copyright 2018 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pytest + +import ( + "testing" +) + +func TestCompileSrc(t *testing.T) { + for _, tc := range []struct { + name string + code string + }{ + { + name: "hello", + code: `print("hello")`, + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, _ = CompileSrc(t, gContext, tc.code, tc.name) + }) + } +} + +func TestRunTests(t *testing.T) { + RunTests(t, "./testdata/tests") +} + +func TestRunScript(t *testing.T) { + RunScript(t, "./testdata/hello.py") +} diff --git a/pytest/testdata/hello.py b/pytest/testdata/hello.py new file mode 100644 index 00000000..fdbccfc1 --- /dev/null +++ b/pytest/testdata/hello.py @@ -0,0 +1,7 @@ +# Copyright 2022 The go-python Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +print("hello") +print("world") +print("bye.") diff --git a/pytest/testdata/hello_golden.txt b/pytest/testdata/hello_golden.txt new file mode 100644 index 00000000..e4226353 --- /dev/null +++ b/pytest/testdata/hello_golden.txt @@ -0,0 +1,3 @@ +hello +world +bye. diff --git a/pytest/testdata/tests/libtest.py b/pytest/testdata/tests/libtest.py new file mode 100644 index 00000000..003eb3db --- /dev/null +++ b/pytest/testdata/tests/libtest.py @@ -0,0 +1,11 @@ +# Copyright 2022 The go-python Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +""" +Simple test harness +""" + +def testFunc(): + return + diff --git a/pytest/testdata/tests/module.py b/pytest/testdata/tests/module.py new file mode 100644 index 00000000..4151c996 --- /dev/null +++ b/pytest/testdata/tests/module.py @@ -0,0 +1,12 @@ +# Copyright 2022 The go-python Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +from libtest import testFunc + +doc="module" +assert True +assert not False +assert testFunc() is None + +doc="finished" diff --git a/stdlib/time/testdata/test.py b/stdlib/time/testdata/test.py new file mode 100644 index 00000000..1f8536cd --- /dev/null +++ b/stdlib/time/testdata/test.py @@ -0,0 +1,49 @@ +# Copyright 2022 The go-python Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +import time + +now = time.time() +now = time.time_ns() +now = time.clock() + +def notimplemented(fn, *args, **kwargs): + try: + fn(*args, **kwargs) + print("error for %s(%s, %s)" % (fn,args,kwargs)) + except NotImplementedError: + pass + +notimplemented(time.clock_gettime) +notimplemented(time.clock_settime) + +print("# sleep") +time.sleep(0.1) +try: + time.sleep(-1) + print("no error sleep(-1)") +except ValueError as e: + print("caught error: %s" % (e,)) + pass +try: + time.sleep("1") + print("no error sleep('1')") +except TypeError as e: + print("caught error: %s" % (e,)) + pass + +notimplemented(time.gmtime) +notimplemented(time.localtime) +notimplemented(time.asctime) +notimplemented(time.ctime) +notimplemented(time.mktime, 1) +notimplemented(time.strftime) +notimplemented(time.strptime) +notimplemented(time.tzset) +notimplemented(time.monotonic) +notimplemented(time.process_time) +notimplemented(time.perf_counter) +notimplemented(time.get_clock_info) + +print("OK") diff --git a/stdlib/time/testdata/test_golden.txt b/stdlib/time/testdata/test_golden.txt new file mode 100644 index 00000000..cb12313f --- /dev/null +++ b/stdlib/time/testdata/test_golden.txt @@ -0,0 +1,4 @@ +# sleep +caught error: ValueError: 'sleep length must be non-negative' +caught error: TypeError: 'sleep() argument 1 must be float, not str' +OK diff --git a/stdlib/time/time.go b/stdlib/time/time.go index d783ae8f..81d50271 100644 --- a/stdlib/time/time.go +++ b/stdlib/time/time.go @@ -150,7 +150,7 @@ func time_sleep(self py.Object, args py.Tuple) (py.Object, error) { if secs < 0 { return nil, py.ExceptionNewf(py.ValueError, "sleep length must be non-negative") } - time.Sleep(time.Duration(secs * 1e9)) + time.Sleep(time.Duration(secs * py.Float(time.Second))) return py.None, nil } @@ -1007,17 +1007,15 @@ func init() { py.MustNewMethod("perf_counter", time_perf_counter, 0, perf_counter_doc), py.MustNewMethod("get_clock_info", time_get_clock_info, 0, get_clock_info_doc), } - + py.RegisterModule(&py.ModuleImpl{ Info: py.ModuleInfo{ - Name: "time", - Doc: module_doc, + Name: "time", + Doc: module_doc, }, Methods: methods, - Globals: py.StringDict{ - }, + Globals: py.StringDict{}, }) - } const module_doc = `This module provides various functions to manipulate time values. diff --git a/stdlib/time/time_test.go b/stdlib/time/time_test.go new file mode 100644 index 00000000..0afb30c0 --- /dev/null +++ b/stdlib/time/time_test.go @@ -0,0 +1,15 @@ +// Copyright 2022 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package time_test + +import ( + "testing" + + "github.com/go-python/gpython/pytest" +) + +func TestTime(t *testing.T) { + pytest.RunScript(t, "./testdata/test.py") +}