diff --git a/gop.mod b/gop.mod index e20a7a1..f37d009 100644 --- a/gop.mod +++ b/gop.mod @@ -4,3 +4,6 @@ project _yap.gox App github.com/goplus/yap project _yunit.gox App github.com/goplus/yap/ytest class _yunit.gox Case + +project _ytest.gox App github.com/goplus/yap/ytest +class _ytest.gox Case diff --git a/ytest/case.go b/ytest/case.go index 7b4ffb6..7d0c349 100644 --- a/ytest/case.go +++ b/ytest/case.go @@ -18,12 +18,133 @@ package ytest import ( "net/http" + "testing" + "time" ) +// ----------------------------------------------------------------------------- + +type caseT interface { + // Name returns the name of the running (sub-) test or benchmark. + // + // The name will include the name of the test along with the names of + // any nested sub-tests. If two sibling sub-tests have the same name, + // Name will append a suffix to guarantee the returned name is unique. + Name() string + + // Fail marks the function as having failed but continues execution. + Fail() + + // Failed reports whether the function has failed. + Failed() bool + + // FailNow marks the function as having failed and stops its execution + // by calling runtime.Goexit (which then runs all deferred calls in the + // current goroutine). + // Execution will continue at the next test or benchmark. + // FailNow must be called from the goroutine running the + // test or benchmark function, not from other goroutines + // created during the test. Calling FailNow does not stop + // those other goroutines. + FailNow() + + // Log formats its arguments using default formatting, analogous to Println, + // and records the text in the error log. For tests, the text will be printed only if + // the test fails or the -test.v flag is set. For benchmarks, the text is always + // printed to avoid having performance depend on the value of the -test.v flag. + Log(args ...any) + + // Logf formats its arguments according to the format, analogous to Printf, and + // records the text in the error log. A final newline is added if not provided. For + // tests, the text will be printed only if the test fails or the -test.v flag is + // set. For benchmarks, the text is always printed to avoid having performance + // depend on the value of the -test.v flag. + Logf(format string, args ...any) + + // Errorln is equivalent to Log followed by Fail. + Errorln(args ...any) + + // Errorf is equivalent to Logf followed by Fail. + Errorf(format string, args ...any) + + // Fatal is equivalent to Log followed by FailNow. + Fatal(args ...any) + + // Fatalf is equivalent to Logf followed by FailNow. + Fatalf(format string, args ...any) + + // Skip is equivalent to Log followed by SkipNow. + Skip(args ...any) + + // Skipf is equivalent to Logf followed by SkipNow. + Skipf(format string, args ...any) + + // SkipNow marks the test as having been skipped and stops its execution + // by calling runtime.Goexit. + // If a test fails (see Error, Errorf, Fail) and is then skipped, + // it is still considered to have failed. + // Execution will continue at the next test or benchmark. See also FailNow. + // SkipNow must be called from the goroutine running the test, not from + // other goroutines created during the test. Calling SkipNow does not stop + // those other goroutines. + SkipNow() + + // Skipped reports whether the test was skipped. + Skipped() bool + + // Helper marks the calling function as a test helper function. + // When printing file and line information, that function will be skipped. + // Helper may be called simultaneously from multiple goroutines. + Helper() + + // Cleanup registers a function to be called when the test (or subtest) and all its + // subtests complete. Cleanup functions will be called in last added, + // first called order. + Cleanup(f func()) + + // TempDir returns a temporary directory for the test to use. + // The directory is automatically removed by Cleanup when the test and + // all its subtests complete. + // Each subsequent call to t.TempDir returns a unique directory; + // if the directory creation fails, TempDir terminates the test by calling Fatal. + TempDir() string + + // Run runs f as a subtest of t called name. + // + // Run may be called simultaneously from multiple goroutines, but all such calls + // must return before the outer test function for t returns. + Run(name string, f func()) bool + + // Deadline reports the time at which the test binary will have + // exceeded the timeout specified by the -timeout flag. + // + // The ok result is false if the -timeout flag indicates “no timeout” (0). + Deadline() (deadline time.Time, ok bool) +} + +type testingT struct { + *testing.T +} + +// Errorln is equivalent to Log followed by Fail. +func (p testingT) Errorln(args ...any) { + p.T.Error(args...) +} + +// Run runs f as a subtest of t called name. +// +// Run may be called simultaneously from multiple goroutines, but all such calls +// must return before the outer test function for t returns. +func (p testingT) Run(name string, f func()) bool { + return p.T.Run(name, func(t *testing.T) { f() }) +} + +// ----------------------------------------------------------------------------- + type Case struct { *Request *App - name string + caseT DefaultHeader http.Header } @@ -32,14 +153,17 @@ func New() *Case { return &Case{} } -func (p *Case) initCase(app *App) { - p.App = app - p.DefaultHeader = make(http.Header) +// Gopt_Case_TestMain is required by Go+ compiler as the entry of a YAP test case. +func Gopt_Case_TestMain(c interface{ initCase(*App, caseT) }, t *testing.T) { + app := new(App).initApp() + c.initCase(app, testingT{t}) + c.(interface{ Main() }).Main() } -func (p *Case) Run(name string, doSth func()) { - p.name = name - doSth() +func (p *Case) initCase(app *App, t caseT) { + p.App = app + p.caseT = t + p.DefaultHeader = make(http.Header) } func (p *Case) Req(method, url string) *Request { @@ -55,3 +179,5 @@ func (p *Case) Get(url string) *Request { func (p *Case) Post(url string) *Request { return p.Req(http.MethodPost, url) } + +// ----------------------------------------------------------------------------- diff --git a/ytest/classfile.go b/ytest/classfile.go index f62f48a..19cae97 100644 --- a/ytest/classfile.go +++ b/ytest/classfile.go @@ -19,7 +19,9 @@ package ytest import ( "io" "net/http" + "os" "strings" + "testing" ) const ( @@ -39,14 +41,23 @@ func (p *App) initApp() *App { return p } -// Gopt_App_Main is required by Go+ compiler as the entry of a YAP testing project. -func Gopt_App_Main(app interface{ initApp() *App }, workers ...interface{ initCase(*App) }) { +// Gopt_App_TestMain is required by Go+ compiler as the TestMain entry of a YAP testing project. +func Gopt_App_TestMain(app interface{ initApp() *App }, m *testing.M) { + app.initApp() + if me, ok := app.(interface{ MainEntry() }); ok { + me.MainEntry() + } + os.Exit(m.Run()) +} + +// Gopt_App_Main is required by Go+ compiler as the Main entry of a YAP testing project. +func Gopt_App_Main(app interface{ initApp() *App }, workers ...interface{ initCase(*App, caseT) }) { a := app.initApp() if me, ok := app.(interface{ MainEntry() }); ok { me.MainEntry() } for _, worker := range workers { - worker.initCase(a) + worker.initCase(a, nil) worker.(interface{ Main() }).Main() } } diff --git a/ytest/demo/example/example_yunit.gox b/ytest/demo/_example/example_ytest.gox similarity index 100% rename from ytest/demo/example/example_yunit.gox rename to ytest/demo/_example/example_ytest.gox diff --git a/ytest/demo/_example/gop_autogen.go b/ytest/demo/_example/gop_autogen.go new file mode 100644 index 0000000..8b83229 --- /dev/null +++ b/ytest/demo/_example/gop_autogen.go @@ -0,0 +1,6 @@ +package main + +const _ = true + +func main() { +} diff --git a/ytest/demo/_example/gop_autogen_test.go b/ytest/demo/_example/gop_autogen_test.go new file mode 100644 index 0000000..5e9fd52 --- /dev/null +++ b/ytest/demo/_example/gop_autogen_test.go @@ -0,0 +1,89 @@ +package main + +import ( + "fmt" + "github.com/goplus/yap/ytest" + "testing" +) + +type case_example struct { + ytest.Case +} +//line ytest/demo/example/example_ytest.gox:1 +func (this *case_example) Main() { +//line ytest/demo/example/example_ytest.gox:1:1 + this.Host("https://example.com", "http://localhost:8080") +//line ytest/demo/example/example_ytest.gox:2:1 + testauth := ytest.Oauth2("...") +//line ytest/demo/example/example_ytest.gox:4:1 + this.DefaultHeader.Set("User-Agent", "yaptest/0.7") +//line ytest/demo/example/example_ytest.gox:6:1 + this.Run("urlWithVar", func() { +//line ytest/demo/example/example_ytest.gox:7:1 + id := "123" +//line ytest/demo/example/example_ytest.gox:8:1 + this.Get("https://example.com/p/" + id) +//line ytest/demo/example/example_ytest.gox:9:1 + this.Send() +//line ytest/demo/example/example_ytest.gox:10:1 + fmt.Println("code:", this.Resp().Code()) +//line ytest/demo/example/example_ytest.gox:11:1 + fmt.Println("body:", this.Resp().Body()) + }) +//line ytest/demo/example/example_ytest.gox:14:1 + this.Run("matchWithVar", func() { +//line ytest/demo/example/example_ytest.gox:15:1 + code := ytest.Gopx_Var_Cast__0[int]() +//line ytest/demo/example/example_ytest.gox:16:1 + id := "123" +//line ytest/demo/example/example_ytest.gox:17:1 + this.Get("https://example.com/p/" + id) +//line ytest/demo/example/example_ytest.gox:18:1 + this.RetWith(code) +//line ytest/demo/example/example_ytest.gox:19:1 + fmt.Println("code:", code) +//line ytest/demo/example/example_ytest.gox:20:1 + ytest.Match__4(code, 200) + }) +//line ytest/demo/example/example_ytest.gox:23:1 + this.Run("postWithAuth", func() { +//line ytest/demo/example/example_ytest.gox:24:1 + id := "123" +//line ytest/demo/example/example_ytest.gox:25:1 + title := "title" +//line ytest/demo/example/example_ytest.gox:26:1 + author := "author" +//line ytest/demo/example/example_ytest.gox:27:1 + this.Post("https://example.com/p/" + id) +//line ytest/demo/example/example_ytest.gox:28:1 + this.Auth(testauth) +//line ytest/demo/example/example_ytest.gox:29:1 + this.Json(map[string]string{"title": title, "author": author}) +//line ytest/demo/example/example_ytest.gox:33:1 + this.RetWith(200) +//line ytest/demo/example/example_ytest.gox:34:1 + fmt.Println("body:", this.Resp().Body()) + }) +//line ytest/demo/example/example_ytest.gox:37:1 + this.Run("mathJsonObject", func() { +//line ytest/demo/example/example_ytest.gox:38:1 + title := ytest.Gopx_Var_Cast__0[string]() +//line ytest/demo/example/example_ytest.gox:39:1 + author := ytest.Gopx_Var_Cast__0[string]() +//line ytest/demo/example/example_ytest.gox:40:1 + id := "123" +//line ytest/demo/example/example_ytest.gox:41:1 + this.Get("https://example.com/p/" + id) +//line ytest/demo/example/example_ytest.gox:42:1 + this.RetWith(200) +//line ytest/demo/example/example_ytest.gox:43:1 + this.Json(map[string]*ytest.Var__0[string]{"title": title, "author": author}) +//line ytest/demo/example/example_ytest.gox:47:1 + fmt.Println("title:", title) +//line ytest/demo/example/example_ytest.gox:48:1 + fmt.Println("author:", author) + }) +} +func Test_example(t *testing.T) { + ytest.Gopt_Case_TestMain(new(case_example), t) +} diff --git a/ytest/demo/example/gop_autogen.go b/ytest/demo/example/gop_autogen.go deleted file mode 100644 index 0e0d935..0000000 --- a/ytest/demo/example/gop_autogen.go +++ /dev/null @@ -1,92 +0,0 @@ -package main - -import ( - "fmt" - "github.com/goplus/yap/ytest" -) - -const _ = true - -func main() { -//line ytest/demo/example/example_yunit.gox:37:1 - ytest.Gopt_App_Main(new(ytest.App), new(example)) -} - -type example struct { - ytest.Case -} -//line ytest/demo/example/example_yunit.gox:1 -func (this *example) Main() { -//line ytest/demo/example/example_yunit.gox:1:1 - this.Host("https://example.com", "http://localhost:8080") -//line ytest/demo/example/example_yunit.gox:2:1 - testauth := ytest.Oauth2("...") -//line ytest/demo/example/example_yunit.gox:4:1 - this.DefaultHeader.Set("User-Agent", "yaptest/0.7") -//line ytest/demo/example/example_yunit.gox:6:1 - this.Run("urlWithVar", func() { -//line ytest/demo/example/example_yunit.gox:7:1 - id := "123" -//line ytest/demo/example/example_yunit.gox:8:1 - this.Get("https://example.com/p/" + id) -//line ytest/demo/example/example_yunit.gox:9:1 - this.Send() -//line ytest/demo/example/example_yunit.gox:10:1 - fmt.Println("code:", this.Resp().Code()) -//line ytest/demo/example/example_yunit.gox:11:1 - fmt.Println("body:", this.Resp().Body()) - }) -//line ytest/demo/example/example_yunit.gox:14:1 - this.Run("matchWithVar", func() { -//line ytest/demo/example/example_yunit.gox:15:1 - code := ytest.Gopx_Var_Cast__0[int]() -//line ytest/demo/example/example_yunit.gox:16:1 - id := "123" -//line ytest/demo/example/example_yunit.gox:17:1 - this.Get("https://example.com/p/" + id) -//line ytest/demo/example/example_yunit.gox:18:1 - this.RetWith(code) -//line ytest/demo/example/example_yunit.gox:19:1 - fmt.Println("code:", code) -//line ytest/demo/example/example_yunit.gox:20:1 - ytest.Match__4(code, 200) - }) -//line ytest/demo/example/example_yunit.gox:23:1 - this.Run("postWithAuth", func() { -//line ytest/demo/example/example_yunit.gox:24:1 - id := "123" -//line ytest/demo/example/example_yunit.gox:25:1 - title := "title" -//line ytest/demo/example/example_yunit.gox:26:1 - author := "author" -//line ytest/demo/example/example_yunit.gox:27:1 - this.Post("https://example.com/p/" + id) -//line ytest/demo/example/example_yunit.gox:28:1 - this.Auth(testauth) -//line ytest/demo/example/example_yunit.gox:29:1 - this.Json(map[string]string{"title": title, "author": author}) -//line ytest/demo/example/example_yunit.gox:33:1 - this.RetWith(200) -//line ytest/demo/example/example_yunit.gox:34:1 - fmt.Println("body:", this.Resp().Body()) - }) -//line ytest/demo/example/example_yunit.gox:37:1 - this.Run("mathJsonObject", func() { -//line ytest/demo/example/example_yunit.gox:38:1 - title := ytest.Gopx_Var_Cast__0[string]() -//line ytest/demo/example/example_yunit.gox:39:1 - author := ytest.Gopx_Var_Cast__0[string]() -//line ytest/demo/example/example_yunit.gox:40:1 - id := "123" -//line ytest/demo/example/example_yunit.gox:41:1 - this.Get("https://example.com/p/" + id) -//line ytest/demo/example/example_yunit.gox:42:1 - this.RetWith(200) -//line ytest/demo/example/example_yunit.gox:43:1 - this.Json(map[string]*ytest.Var__0[string]{"title": title, "author": author}) -//line ytest/demo/example/example_yunit.gox:47:1 - fmt.Println("title:", title) -//line ytest/demo/example/example_yunit.gox:48:1 - fmt.Println("author:", author) - }) -}