summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2024-11-16 15:12:48 +0700
committerShulhan <ms@kilabit.info>2024-11-16 16:50:51 +0700
commita465d718b8ab7d8530e59586a60b11349f1ccc04 (patch)
tree6ba3beeb5fec02cd7a9a060052022f671fb37c76
parent20fdd17d5202a356f1581345e73c15eb8ce1a42a (diff)
downloadpakakeh.go-a465d718b8ab7d8530e59586a60b11349f1ccc04.tar.xz
lib/play: add option to Run with specific Go version and without race
The idea is to allow testing Go code on specific Go version. For example, before Go 1.22, the for loop with variable is shared among block statements, which cause every use of that variable is run with the last value.
-rw-r--r--lib/play/command.go8
-rw-r--r--lib/play/play.go21
-rw-r--r--lib/play/play_test.go19
-rw-r--r--lib/play/request.go10
-rw-r--r--lib/play/testdata/httpHandleRun_test.txt29
5 files changed, 72 insertions, 15 deletions
diff --git a/lib/play/command.go b/lib/play/command.go
index f3812dbe..4413b9ff 100644
--- a/lib/play/command.go
+++ b/lib/play/command.go
@@ -37,7 +37,13 @@ func newCommand(req *Request, workingDir string) (cmd *command, err error) {
return nil, fmt.Errorf(`newCommand: %w`, err)
}
- cmd.execGoRun = exec.CommandContext(cmd.ctx, `go`, `run`, `-race`, `.`)
+ var listArg = []string{`run`}
+ if !req.WithoutRace {
+ listArg = append(listArg, `-race`)
+ }
+ listArg = append(listArg, `.`)
+
+ cmd.execGoRun = exec.CommandContext(cmd.ctx, `go`, listArg...)
cmd.execGoRun.Env = append(cmd.execGoRun.Env, `CGO_ENABLED=1`)
cmd.execGoRun.Env = append(cmd.execGoRun.Env, `HOME=`+userHomeDir)
cmd.execGoRun.Env = append(cmd.execGoRun.Env, `PATH=/usr/bin:/usr/local/bin`)
diff --git a/lib/play/play.go b/lib/play/play.go
index 6abd79a8..068a52c2 100644
--- a/lib/play/play.go
+++ b/lib/play/play.go
@@ -11,10 +11,19 @@
// format,
//
// {
-// "body":<string>
+// "goversion": <string>, // For run only.
+// "without_race": <boolean>, // For run only.
+// "body": <string>
// }
//
-// where "body" field contains the Go code to be formatted or run.
+// The "goversion" field define the Go tools and toolchain version to be
+// used to compile the code.
+// The default "goversion" is defined as global variable [GoVersion] in this
+// package.
+// If "without_race" is false, the Run command will not run with "-race"
+// option.
+// The "body" field contains the Go code to be formatted or run.
+//
// Both have the following response format,
//
// {
@@ -54,12 +63,6 @@ var GoVersion = `1.23.2`
// terminated.
var Timeout = 10 * time.Second
-var gomodTemplate = `
-module play.local
-
-go ` + GoVersion + `
-`
-
var now = func() int64 {
return time.Now().Unix()
}
@@ -269,6 +272,8 @@ func Run(req *Request) (out []byte, err error) {
var gomod = filepath.Join(tempdir, `go.mod`)
+ var gomodTemplate = "module play.local\n\ngo " + req.GoVersion + "\n"
+
err = os.WriteFile(gomod, []byte(gomodTemplate), 0600)
if err != nil {
return nil, fmt.Errorf(`%s: WriteFile %q: %w`, logp, gomod, err)
diff --git a/lib/play/play_test.go b/lib/play/play_test.go
index 4e95fb44..4172c506 100644
--- a/lib/play/play_test.go
+++ b/lib/play/play_test.go
@@ -128,6 +128,7 @@ func TestHTTPHandleRun(t *testing.T) {
type testCase struct {
tag string
contentType string
+ req Request
}
var (
@@ -150,32 +151,38 @@ func TestHTTPHandleRun(t *testing.T) {
}, {
tag: `nopackage`,
contentType: libhttp.ContentTypeJSON,
+ }, {
+ tag: `go121_for`,
+ contentType: libhttp.ContentTypeJSON,
+ req: Request{
+ GoVersion: `1.21.13`,
+ WithoutRace: true,
+ },
}}
var (
withBody = true
- req Request
tcase testCase
rawb []byte
body bytes.Buffer
)
for _, tcase = range listCase {
- req.Body = string(tdata.Input[tcase.tag])
+ tcase.req.Body = string(tdata.Input[tcase.tag])
- rawb, err = json.Marshal(&req)
+ rawb, err = json.Marshal(&tcase.req)
if err != nil {
t.Fatal(err)
}
body.Reset()
body.Write(rawb)
- var req *http.Request = httptest.NewRequest(`POST`, `/`, &body)
- req.Header.Set(libhttp.HeaderContentType, tcase.contentType)
+ var httpReq *http.Request = httptest.NewRequest(`POST`, `/`, &body)
+ httpReq.Header.Set(libhttp.HeaderContentType, tcase.contentType)
var writer *httptest.ResponseRecorder = httptest.NewRecorder()
- HTTPHandleRun(writer, req)
+ HTTPHandleRun(writer, httpReq)
var result *http.Response = writer.Result()
rawb, err = httputil.DumpResponse(result, withBody)
diff --git a/lib/play/request.go b/lib/play/request.go
index 2b479325..18c210bd 100644
--- a/lib/play/request.go
+++ b/lib/play/request.go
@@ -23,8 +23,14 @@ type Request struct {
// In the HTTP request, the sid is read from cookie named "sid".
cookieSid *http.Cookie
+ // The Go version that will be used in go.mod.
+ GoVersion string `json:"goversion"`
+
// Body contains the Go code to be Format-ed or Run.
Body string `json:"body"`
+
+ // WithoutRace define option opt out "-race" when running Go code.
+ WithoutRace bool `json:"without_race"`
}
func (req *Request) init() {
@@ -37,6 +43,10 @@ func (req *Request) init() {
req.cookieSid.Path = `/`
req.cookieSid.MaxAge = 604800 // Seven days.
req.cookieSid.SameSite = http.SameSiteStrictMode
+
+ if req.GoVersion == `` {
+ req.GoVersion = GoVersion
+ }
}
// generateSid generate session ID from the first 16 hex of SHA256 hash of
diff --git a/lib/play/testdata/httpHandleRun_test.txt b/lib/play/testdata/httpHandleRun_test.txt
index 10997d47..bbea0f91 100644
--- a/lib/play/testdata/httpHandleRun_test.txt
+++ b/lib/play/testdata/httpHandleRun_test.txt
@@ -59,3 +59,32 @@ func main() {
<<< noimport
+>>> go121_for
+package main
+
+import (
+ "fmt"
+ "sync"
+)
+
+func main() {
+ var wg sync.WaitGroup
+ for x := 1; x < 5; x++ {
+ wg.Add(1)
+ go func() {
+ fmt.Println(`x =`, x)
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+<<< go121_for
+HTTP/1.1 200 OK
+Connection: close
+Content-Type: application/json
+Set-Cookie: sid=d964b22e8f2e75d9; Path=/; Max-Age=604800; SameSite=Strict
+
+{"data":"x = 5\nx = 5\nx = 5\nx = 5\n","code":200}
+
+>>> noop