aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2025-01-16 00:51:22 +0700
committerShulhan <ms@kilabit.info>2025-01-16 00:51:22 +0700
commit44479b6a3e2e80738e74a7daae6e72dfcee77502 (patch)
tree81ab0841c89f96d8036f09e9a798f65f23cb6f1c
parent224d28984050b04e60fe1228403535271568e80d (diff)
downloadpakakeh.go-44479b6a3e2e80738e74a7daae6e72dfcee77502.tar.xz
lib/play: refactor functions as methods of type Go
One of the major issue that we previously have is the Run and Test functions can write file in any unsafe path. Another issue is changing the default GoVersion and Timeout is set on the package level. This changes introduce new type Go as the top level type that can be instantiate with different Root, GoVersion, and Timeout. The instance of Go then can Format, Run, or Test the Go code in their own scope.
-rw-r--r--lib/play/command.go8
-rw-r--r--lib/play/go.go52
-rw-r--r--lib/play/http.go28
-rw-r--r--lib/play/http_example_test.go15
-rw-r--r--lib/play/http_test.go21
-rw-r--r--lib/play/play.go69
-rw-r--r--lib/play/play_example_test.go17
-rw-r--r--lib/play/play_test.go61
-rw-r--r--lib/play/request.go14
9 files changed, 179 insertions, 106 deletions
diff --git a/lib/play/command.go b/lib/play/command.go
index ada132d5..b7e7a93e 100644
--- a/lib/play/command.go
+++ b/lib/play/command.go
@@ -18,12 +18,12 @@ type command struct {
ctxCancel context.CancelFunc
}
-func newCommand(req *Request) (cmd *command) {
+func newCommand(opts GoOptions, req *Request) (cmd *command) {
cmd = &command{
buf: &bytes.Buffer{},
}
var ctxParent = context.Background()
- cmd.ctx, cmd.ctxCancel = context.WithTimeout(ctxParent, Timeout)
+ cmd.ctx, cmd.ctxCancel = context.WithTimeout(ctxParent, opts.Timeout)
var listArg = []string{`run`}
if !req.WithoutRace {
@@ -44,12 +44,12 @@ func newCommand(req *Request) (cmd *command) {
return cmd
}
-func newTestCommand(treq *Request) (cmd *command) {
+func newTestCommand(opts GoOptions, treq *Request) (cmd *command) {
cmd = &command{
buf: &bytes.Buffer{},
}
var ctxParent = context.Background()
- cmd.ctx, cmd.ctxCancel = context.WithTimeout(ctxParent, Timeout)
+ cmd.ctx, cmd.ctxCancel = context.WithTimeout(ctxParent, opts.Timeout)
var listArg = []string{`test`, `-count=1`}
if !treq.WithoutRace {
diff --git a/lib/play/go.go b/lib/play/go.go
new file mode 100644
index 00000000..835ac32a
--- /dev/null
+++ b/lib/play/go.go
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+package play
+
+import (
+ "time"
+)
+
+// GoOptions define the options for running and test [Go] code.
+type GoOptions struct {
+ // Root directory of where the Go code to be written, run, or test.
+ // Default to [os.UserCacheDir] if its not set.
+ Root string
+
+ // Version define the Go tool version in go.mod to be used to run the
+ // code.
+ // Default to package [GoVersion] if its not set.
+ Version string
+
+ // Timeout define the maximum time the program can be run until it
+ // gets terminated.
+ // Default to package [Timeout] if its not set.
+ Timeout time.Duration
+}
+
+func (opts *GoOptions) init() {
+ if len(opts.Root) == 0 {
+ opts.Root = userCacheDir
+ }
+ if len(opts.Version) == 0 {
+ opts.Version = GoVersion
+ }
+ if opts.Timeout <= 0 {
+ opts.Timeout = Timeout
+ }
+}
+
+// Go define the type that can format, run, and test Go code.
+type Go struct {
+ opts GoOptions
+}
+
+// NewGo create and initialize new Go tools.
+func NewGo(opts GoOptions) (playgo *Go) {
+ opts.init()
+ playgo = &Go{
+ opts: opts,
+ }
+ return playgo
+}
diff --git a/lib/play/http.go b/lib/play/http.go
index 3910a74d..65ae0202 100644
--- a/lib/play/http.go
+++ b/lib/play/http.go
@@ -14,8 +14,8 @@ import (
libhttp "git.sr.ht/~shulhan/pakakeh.go/lib/http"
)
-// HTTPHandleFormat define the HTTP handler for formating Go code.
-func HTTPHandleFormat(httpresw http.ResponseWriter, httpreq *http.Request) {
+// HTTPHandleFormat define the HTTP handler for [Go.Format].
+func (playgo *Go) HTTPHandleFormat(httpresw http.ResponseWriter, httpreq *http.Request) {
var (
logp = `HTTPHandleFormat`
resp = libhttp.EndpointResponse{}
@@ -48,7 +48,7 @@ func HTTPHandleFormat(httpresw http.ResponseWriter, httpreq *http.Request) {
goto out
}
- rawbody, err = Format(req)
+ rawbody, err = playgo.Format(req)
if err != nil {
resp.Code = http.StatusUnprocessableEntity
resp.Name = `ERR_CODE`
@@ -70,10 +70,12 @@ out:
httpresw.Write(rawbody)
}
-// HTTPHandleRun define the HTTP handler for running Go code.
-// Each client is identified by unique cookie, so if two Run requests come
-// from the same client, the previous Run will be cancelled.
-func HTTPHandleRun(httpresw http.ResponseWriter, httpreq *http.Request) {
+// HTTPHandleRun define the HTTP handler for [Go.Run].
+// Each client is identified by unique cookie, so if two requests come from
+// the same client, the previous run will be cancelled.
+func (playgo *Go) HTTPHandleRun(
+ httpresw http.ResponseWriter, httpreq *http.Request,
+) {
var (
logp = `HTTPHandleRun`
@@ -88,7 +90,7 @@ func HTTPHandleRun(httpresw http.ResponseWriter, httpreq *http.Request) {
goto out
}
- rawb, err = Run(req)
+ rawb, err = playgo.Run(req)
if err != nil {
resp = &libhttp.EndpointResponse{
E: liberrors.E{
@@ -169,10 +171,12 @@ func readRequest(httpreq *http.Request) (
return req, nil
}
-// HTTPHandleTest define the HTTP handler for testing Go code.
-// Each client is identified by unique cookie, so if two Run requests come
+// HTTPHandleTest define the HTTP handler for testing [Go.Test].
+// Each client is identified by unique cookie, so if two requests come
// from the same client, the previous Test will be cancelled.
-func HTTPHandleTest(httpresw http.ResponseWriter, httpreq *http.Request) {
+func (playgo *Go) HTTPHandleTest(
+ httpresw http.ResponseWriter, httpreq *http.Request,
+) {
var (
logp = `HTTPHandleTest`
@@ -187,7 +191,7 @@ func HTTPHandleTest(httpresw http.ResponseWriter, httpreq *http.Request) {
goto out
}
- rawb, err = Test(treq)
+ rawb, err = playgo.Test(treq)
if err != nil {
resp = &libhttp.EndpointResponse{
E: liberrors.E{
diff --git a/lib/play/http_example_test.go b/lib/play/http_example_test.go
index a25bf8c7..03f2aa85 100644
--- a/lib/play/http_example_test.go
+++ b/lib/play/http_example_test.go
@@ -15,7 +15,7 @@ import (
"regexp"
)
-func ExampleHTTPHandleFormat() {
+func ExampleGo_HTTPHandleFormat() {
const codeIndentMissingImport = `
package main
func main() {
@@ -39,8 +39,9 @@ func main() {
bytes.NewReader(rawbody))
httpreq.Header.Set(`Content-Type`, `application/json`)
+ var playgo = NewGo(GoOptions{})
var mux = http.NewServeMux()
- mux.HandleFunc(`POST /api/play/format`, HTTPHandleFormat)
+ mux.HandleFunc(`POST /api/play/format`, playgo.HTTPHandleFormat)
mux.ServeHTTP(resprec, httpreq)
var resp = resprec.Result()
@@ -55,7 +56,7 @@ func main() {
// {"data":"package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello, world\")\n}\n","code":200}
}
-func ExampleHTTPHandleRun() {
+func ExampleGo_HTTPHandleRun() {
const code = `
package main
import "fmt"
@@ -81,9 +82,10 @@ func main() {
bytes.NewReader(rawbody))
httpreq.Header.Set(`Content-Type`, `application/json`)
+ var playgo = NewGo(GoOptions{})
var mux = http.NewServeMux()
- mux.HandleFunc(`POST /api/play/run`, HTTPHandleRun)
+ mux.HandleFunc(`POST /api/play/run`, playgo.HTTPHandleRun)
mux.ServeHTTP(resprec, httpreq)
var resp = resprec.Result()
@@ -98,7 +100,7 @@ func main() {
// {"data":"Hello, world\n","code":200}
}
-func ExampleHTTPHandleTest() {
+func ExampleGo_HTTPHandleTest() {
const code = `
package test
import "testing"
@@ -121,9 +123,10 @@ func TestSum(t *testing.T) {
log.Fatal(err)
}
+ var playgo = NewGo(GoOptions{})
var mux = http.NewServeMux()
- mux.HandleFunc(`POST /api/play/test`, HTTPHandleTest)
+ mux.HandleFunc(`POST /api/play/test`, playgo.HTTPHandleTest)
var resprec = httptest.NewRecorder()
var httpreq = httptest.NewRequest(`POST`, `/api/play/test`,
diff --git a/lib/play/http_test.go b/lib/play/http_test.go
index f3bdc3d6..47ce1377 100644
--- a/lib/play/http_test.go
+++ b/lib/play/http_test.go
@@ -16,7 +16,7 @@ import (
"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)
-func TestHTTPHandleFormat(t *testing.T) {
+func TestGo_HTTPHandleFormat(t *testing.T) {
type testCase struct {
tag string
contentType string
@@ -42,6 +42,8 @@ func TestHTTPHandleFormat(t *testing.T) {
}}
var (
+ playgo = NewGo(GoOptions{})
+
req Request
tcase testCase
rawb []byte
@@ -54,13 +56,13 @@ func TestHTTPHandleFormat(t *testing.T) {
t.Fatal(err)
}
- var req = httptest.NewRequest(`POST`, `/`,
+ var httpReq = httptest.NewRequest(`POST`, `/`,
bytes.NewReader(rawb))
- req.Header.Set(libhttp.HeaderContentType, tcase.contentType)
+ httpReq.Header.Set(libhttp.HeaderContentType, tcase.contentType)
var httpWriter = httptest.NewRecorder()
- HTTPHandleFormat(httpWriter, req)
+ playgo.HTTPHandleFormat(httpWriter, httpReq)
var result = httpWriter.Result()
rawb, err = httputil.DumpResponse(result, true)
@@ -74,7 +76,7 @@ func TestHTTPHandleFormat(t *testing.T) {
}
}
-func TestHTTPHandleRun(t *testing.T) {
+func TestGo_HTTPHandleRun(t *testing.T) {
type testCase struct {
tag string
contentType string
@@ -111,6 +113,8 @@ func TestHTTPHandleRun(t *testing.T) {
}}
var (
+ playgo = NewGo(GoOptions{})
+
tcase testCase
rawb []byte
)
@@ -129,7 +133,7 @@ func TestHTTPHandleRun(t *testing.T) {
var httpWriter = httptest.NewRecorder()
- HTTPHandleRun(httpWriter, httpReq)
+ playgo.HTTPHandleRun(httpWriter, httpReq)
var result = httpWriter.Result()
rawb, err = httputil.DumpResponse(result, true)
@@ -143,7 +147,7 @@ func TestHTTPHandleRun(t *testing.T) {
}
}
-func TestHTTPHandleTest(t *testing.T) {
+func TestGo_HTTPHandleTest(t *testing.T) {
type testCase struct {
tag string
contentType string
@@ -176,6 +180,7 @@ func TestHTTPHandleTest(t *testing.T) {
}}
var (
+ playgo = NewGo(GoOptions{})
rexDuration = regexp.MustCompile(`(?m)\\t(\d+\.\d+)s`)
tcase testCase
rawb []byte
@@ -195,7 +200,7 @@ func TestHTTPHandleTest(t *testing.T) {
var httpWriter = httptest.NewRecorder()
- HTTPHandleTest(httpWriter, httpReq)
+ playgo.HTTPHandleTest(httpWriter, httpReq)
var httpResp = httpWriter.Result()
rawb, err = httputil.DumpResponse(httpResp, true)
diff --git a/lib/play/play.go b/lib/play/play.go
index c4d05f12..81a5ae79 100644
--- a/lib/play/play.go
+++ b/lib/play/play.go
@@ -11,8 +11,8 @@
//
// # Formatting and running Go code
//
-// HTTP APIs for formatting and running Go code accept JSON content type,
-// with the following request format,
+// The HTTP APIs for formatting and running Go code accept the following JSON
+// request format,
//
// {
// "goversion": <string>, // For run only.
@@ -22,13 +22,13 @@
//
// 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
+// The default "goversion" is defined in global variable [GoVersion] in this
// package.
// If "without_race" is true, the Run command will not run with "-race"
// option.
// The "body" field contains the Go code to be formatted or run.
//
-// Both return the following JSON response format,
+// Both request return the following JSON response format,
//
// {
// "code": <integer, HTTP status code>,
@@ -37,33 +37,34 @@
// "data": <string>
// }
//
-// For the [HTTPHandleFormat], the response "data" contains the formatted Go
+// For the [Go.HTTPHandleFormat], the response "data" contains the formatted Go
// code.
-// For the [HTTPHandleRun], the response "data" contains the output from
-// running the Go code, the "message" contains an error pre-Run, like bad
-// request or file system related error.
+// For the [Go.HTTPHandleRun], the response "data" contains the output from
+// running the Go code.
+// The "message" field contains an error on pre-Run, like bad request or file
+// system related error.
//
// # Unsafe run
//
-// As exceptional, the [Run] and [HTTPHandleRun] accept the following
+// As exceptional, the [Go.Run] and [Go.HTTPHandleRun] accept the following
// request for running program inside custom "go.mod",
//
// {
// "unsafe_run": <path>
// }
//
-// The "unsafe_run" define the path to directory relative to HTTP server
-// working directory.
-// Once request accepted it will change the directory into "unsafe_run" first
-// and then run "go run ." directly.
+// The "unsafe_run" define the path to directory relative to
+// [play.GoOptions.Root] working directory.
+// Once the request is accepted it will change the directory into "unsafe_run"
+// first and then execute "go run ." directly.
// Go code that executed inside "unsafe_run" should be not modifiable and
// safe from mallicious execution.
//
// # Testing
//
-// For testing, since the test must run inside the directory that contains
-// the Go file to be tested, the [HTTPHandleTest] API accept the following
-// request format,
+// The [Go.Test] or [Go.HTTPHandleTest] must run inside the directory that
+// contains the Go code file to be tested.
+// The [Go.HTTPHandleTest] API accept the following request format,
//
// {
// "goversion": <string>,
@@ -74,11 +75,10 @@
//
// The "file" field define the path to the "_test.go" file, default to
// "test_test.go" if its empty.
-// The "body" field contains the Go code that will be saved to
-// "file".
+// The "body" field contains the Go code that will be saved to that "file".
// The test will run, by default, with "go test -count=1 -race $dirname"
-// where "$dirname" is the path directory to the "file" relative to where
-// the program is running.
+// where "$dirname" is the path to directory where "file" located, must be
+// under the [play.GoOptions.Root] directory.
// If "without_race" is true, the test command will not run with "-race"
// option.
package play
@@ -93,17 +93,16 @@ import (
"golang.org/x/tools/imports"
)
-// ErrEmptyFile error when running [Test] with empty File field in the
-// [Request].
+// ErrEmptyFile error when running [Go.Test] with empty [play.Request.File].
var ErrEmptyFile = errors.New(`empty File`)
// GoVersion define the Go tool version for go.mod to be used to run the
// code.
-var GoVersion = `1.23.2`
+const GoVersion = `1.23.2`
// Timeout define the maximum time the program can be run until it get
// terminated.
-var Timeout = 10 * time.Second
+const Timeout = 10 * time.Second
var now = func() int64 {
return time.Now().Unix()
@@ -131,9 +130,9 @@ func init() {
}
}
-// Format the Go code in the [Request.Body] and return the result to out.
+// Format the Go code in the [play.Request.Body] and return the result to out.
// Any syntax error on the code will be returned as error.
-func Format(req Request) (out []byte, err error) {
+func (playgo *Go) Format(req Request) (out []byte, err error) {
var logp = `Format`
var fmtbody []byte
@@ -145,11 +144,11 @@ func Format(req Request) (out []byte, err error) {
return fmtbody, nil
}
-// Run the Go code in the [Request.Body].
-func Run(req *Request) (out []byte, err error) {
+// Run the Go code in the [play.Request.Body].
+func (playgo *Go) Run(req *Request) (out []byte, err error) {
var logp = `Run`
- req.init()
+ req.init(playgo.opts)
var cmd *command = runningCmd.get(req.cookieSid.Value)
if cmd != nil {
@@ -161,24 +160,24 @@ func Run(req *Request) (out []byte, err error) {
if len(req.Body) == 0 {
return nil, nil
}
- err = req.writes()
+ err = req.writes(playgo.opts)
if err != nil {
return nil, fmt.Errorf(`%s: %w`, logp, err)
}
}
- cmd = newCommand(req)
+ cmd = newCommand(playgo.opts, req)
runningCmd.store(req.cookieSid.Value, cmd)
out = cmd.run()
return out, nil
}
-// Test the Go code in the [Request.Body].
-func Test(req *Request) (out []byte, err error) {
+// Test the Go code in the [play.Request.Body].
+func (playgo *Go) Test(req *Request) (out []byte, err error) {
var logp = `Test`
- req.init()
+ req.init(playgo.opts)
var cmd *command = runningCmd.get(req.cookieSid.Value)
if cmd != nil {
@@ -198,7 +197,7 @@ func Test(req *Request) (out []byte, err error) {
return nil, fmt.Errorf(`%s: %w`, logp, err)
}
- cmd = newTestCommand(req)
+ cmd = newTestCommand(playgo.opts, req)
runningCmd.store(req.cookieSid.Value, cmd)
out = cmd.run()
diff --git a/lib/play/play_example_test.go b/lib/play/play_example_test.go
index 46310e05..d87c54ff 100644
--- a/lib/play/play_example_test.go
+++ b/lib/play/play_example_test.go
@@ -10,7 +10,7 @@ import (
"regexp"
)
-func ExampleFormat() {
+func ExampleGo_Format() {
const codeIndentMissingImport = `
package main
func main() {
@@ -21,10 +21,12 @@ func main() {
Body: codeIndentMissingImport,
}
var (
+ playgo = NewGo(GoOptions{})
+
out []byte
err error
)
- out, err = Format(req)
+ out, err = playgo.Format(req)
if err != nil {
log.Fatal(err)
}
@@ -40,7 +42,7 @@ func main() {
//}
}
-func ExampleRun() {
+func ExampleGo_Run() {
const codeRun = `
package main
import "fmt"
@@ -52,10 +54,12 @@ func main() {
Body: codeRun,
}
var (
+ playgo = NewGo(GoOptions{})
+
out []byte
err error
)
- out, err = Run(&req)
+ out, err = playgo.Run(&req)
if err != nil {
fmt.Printf(`error: %s`, err)
}
@@ -65,7 +69,7 @@ func main() {
//Hello, world
}
-func ExampleTest() {
+func ExampleGo_Test() {
const codeTest = `
package test
import "testing"
@@ -80,11 +84,12 @@ func TestSum(t *testing.T) {
File: `testdata/test_test.go`,
}
var (
+ playgo = NewGo(GoOptions{})
rexDuration = regexp.MustCompile(`(?m)\s+(\d+\.\d+)s$`)
out []byte
err error
)
- out, err = Test(&req)
+ out, err = playgo.Test(&req)
if err != nil {
fmt.Printf(`error: %s`, err)
}
diff --git a/lib/play/play_test.go b/lib/play/play_test.go
index 39971597..110ffca3 100644
--- a/lib/play/play_test.go
+++ b/lib/play/play_test.go
@@ -24,29 +24,30 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
-func TestFormat(t *testing.T) {
+func TestGo_Format(t *testing.T) {
var (
tdata *test.Data
err error
)
-
tdata, err = test.LoadData(`testdata/format_test.txt`)
if err != nil {
t.Fatal(err)
}
var (
- req Request
- name string
- exp string
- input []byte
- got []byte
+ playgo = Go{}
+ name string
+ input []byte
)
for name, input = range tdata.Input {
+ var req Request
+
req.Body = string(input)
- exp = string(tdata.Output[name])
- got, err = Format(req)
+ var exp = string(tdata.Output[name])
+ var got []byte
+
+ got, err = playgo.Format(req)
if err != nil {
test.Assert(t, name, exp, string(got))
exp = string(tdata.Output[name+`:error`])
@@ -58,7 +59,7 @@ func TestFormat(t *testing.T) {
}
}
-func TestRun(t *testing.T) {
+func TestGo_Run(t *testing.T) {
type testCase struct {
tag string
req Request
@@ -79,14 +80,15 @@ func TestRun(t *testing.T) {
tag: `noimport`,
}}
var (
- tcase testCase
- exp string
- got []byte
+ playgo = NewGo(GoOptions{})
+ tcase testCase
+ exp string
+ got []byte
)
for _, tcase = range listCase {
tcase.req.Body = string(tdata.Input[tcase.tag])
- got, err = Run(&tcase.req)
+ got, err = playgo.Run(&tcase.req)
if err != nil {
var tagError = tcase.tag + `Error`
exp = string(tdata.Output[tagError])
@@ -97,11 +99,11 @@ func TestRun(t *testing.T) {
}
}
-// TestRunOverlap execute Run multiple times.
+// TestGo_Run_Overlap execute Run multiple times.
// The first Run, run the code with infinite loop.
// The second Run, run normal code.
// On the second Run, the first Run should be cancelled or killed.
-func TestRunOverlap(t *testing.T) {
+func TestGo_Run_Overlap(t *testing.T) {
var (
tdata *test.Data
err error
@@ -156,7 +158,8 @@ func testRunOverlap(t *testing.T, runwg *sync.WaitGroup, tdata *test.Data,
defer runwg.Done()
var (
- req = &Request{
+ playgo = NewGo(GoOptions{})
+ req = &Request{
cookieSid: &http.Cookie{
Value: sid,
},
@@ -167,7 +170,7 @@ func testRunOverlap(t *testing.T, runwg *sync.WaitGroup, tdata *test.Data,
err error
)
- out, err = Run(req)
+ out, err = playgo.Run(req)
if err != nil {
exp = string(tdata.Output[runName+`-error`])
test.Assert(t, runName+` error`, exp, err.Error())
@@ -187,16 +190,17 @@ func testRunOverlap(t *testing.T, runwg *sync.WaitGroup, tdata *test.Data,
}
}
-func TestRunUnsafeRun(t *testing.T) {
+func TestGo_Run_UnsafeRun(t *testing.T) {
var req = &Request{
UnsafeRun: `testdata/unsafe_run/cmd/forum`,
}
var (
- out []byte
- err error
+ playgo = NewGo(GoOptions{})
+ out []byte
+ err error
)
- out, err = Run(req)
+ out, err = playgo.Run(req)
if err != nil {
t.Fatal(err)
}
@@ -205,7 +209,7 @@ func TestRunUnsafeRun(t *testing.T) {
test.Assert(t, `unsafeRun`, exp, string(out))
}
-func TestTest(t *testing.T) {
+func TestGo_Test(t *testing.T) {
type testCase struct {
tag string
exp string
@@ -245,15 +249,16 @@ func TestTest(t *testing.T) {
var rexDuration = regexp.MustCompile(`(?m)\s+(\d+\.\d+)s$`)
var (
- tcase testCase
- exp string
- got []byte
+ playgo = NewGo(GoOptions{})
+ tcase testCase
+ exp string
+ got []byte
)
for _, tcase = range listCase {
tcase.treq.Body = string(tdata.Input[tcase.tag])
- tcase.treq.init()
+ tcase.treq.init(playgo.opts)
- got, err = Test(&tcase.treq)
+ got, err = playgo.Test(&tcase.treq)
if err != nil {
test.Assert(t, tcase.tag, tcase.expError, err.Error())
}
diff --git a/lib/play/request.go b/lib/play/request.go
index 5afffaaf..94e5f04b 100644
--- a/lib/play/request.go
+++ b/lib/play/request.go
@@ -17,7 +17,7 @@ import (
const cookieNameSid = `sid`
const defTestFile = `test_test.go`
-// Request for calling [Format] and [Run].
+// Request for calling [Go.Format], [Go.Run], or [Go.Test].
type Request struct {
// cookieSid contains unique session ID between request.
// A single session can only run one command at a time, otherwise
@@ -40,11 +40,12 @@ type Request struct {
// executed directly.
UnsafeRun string `json:"unsafe_run"`
- // WithoutRace define option opt out "-race" when running Go code.
+ // WithoutRace define the field to opt out the "-race" option when
+ // running Go code.
WithoutRace bool `json:"without_race"`
}
-func (req *Request) init() {
+func (req *Request) init(opts GoOptions) {
if req.cookieSid == nil {
req.cookieSid = &http.Cookie{
Name: cookieNameSid,
@@ -56,7 +57,7 @@ func (req *Request) init() {
req.cookieSid.SameSite = http.SameSiteStrictMode
if req.GoVersion == `` {
- req.GoVersion = GoVersion
+ req.GoVersion = opts.Version
}
}
@@ -76,9 +77,8 @@ func (req *Request) generateSid() string {
}
// writes write the go.mod and main.go files inside the unsafe directory.
-func (req *Request) writes() (err error) {
- req.UnsafeRun = filepath.Join(userCacheDir, `goplay`,
- req.cookieSid.Value)
+func (req *Request) writes(opts GoOptions) (err error) {
+ req.UnsafeRun = filepath.Join(opts.Root, `goplay`, req.cookieSid.Value)
err = os.MkdirAll(req.UnsafeRun, 0700)
if err != nil {