From 5f8b55d1dc7042549720e2ec78e436e0cfd147e8 Mon Sep 17 00:00:00 2001 From: "Kwon, Yeong Eon" Date: Thu, 14 Nov 2024 10:59:14 +0900 Subject: [PATCH] redirect console output --- console.go | 17 +++++++++++++++-- otto.go | 14 +++++++++++++- otto_test.go | 29 +++++++++++++++++++++++++++++ runtime.go | 2 ++ 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/console.go b/console.go index e8d99cf7..8a02a348 100644 --- a/console.go +++ b/console.go @@ -2,6 +2,7 @@ package otto import ( "fmt" + "io" "os" "strings" ) @@ -15,12 +16,24 @@ func formatForConsole(argumentList []Value) string { } func builtinConsoleLog(call FunctionCall) Value { - fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList)) //nolint:errcheck // Nothing we can do if this fails. + var w io.Writer + if call.runtime.stdoutWriter != nil { + w = call.runtime.stdoutWriter + } else { + w = os.Stdout + } + fmt.Fprintln(w, formatForConsole(call.ArgumentList)) //nolint:errcheck // Nothing we can do if this fails. return Value{} } func builtinConsoleError(call FunctionCall) Value { - fmt.Fprintln(os.Stdout, formatForConsole(call.ArgumentList)) //nolint:errcheck // Nothing we can do if this fails. + var w io.Writer + if call.runtime.stdoutWriter != nil { + w = call.runtime.stdoutWriter + } else { + w = os.Stdout + } + fmt.Fprintln(w, formatForConsole(call.ArgumentList)) //nolint:errcheck // Nothing we can do if this fails. return Value{} } diff --git a/otto.go b/otto.go index 31b7e301..faebe188 100644 --- a/otto.go +++ b/otto.go @@ -222,6 +222,7 @@ package otto import ( "encoding/json" "errors" + "io" "strings" "github.com/robertkrimen/otto/file" @@ -238,12 +239,15 @@ type Otto struct { } // New will allocate a new JavaScript runtime. -func New() *Otto { +func New(opts ...Option) *Otto { o := &Otto{ runtime: newContext(), } o.runtime.otto = o o.runtime.traceLimit = 10 + for _, opt := range opts { + opt(o) + } if err := o.Set("console", o.runtime.newConsole()); err != nil { panic(err) } @@ -775,3 +779,11 @@ func (o Object) MarshalJSON() ([]byte, error) { } return json.Marshal(goValue) } + +type Option func(*Otto) + +func WithStdoutWriter(w io.Writer) Option { + return func(o *Otto) { + o.runtime.stdoutWriter = w + } +} diff --git a/otto_test.go b/otto_test.go index 2ad71569..27557d47 100644 --- a/otto_test.go +++ b/otto_test.go @@ -1803,6 +1803,35 @@ func TestOttoInterrupt(t *testing.T) { } } +func TestOtterStdoutWriter(t *testing.T) { + tests := []struct { + name string + script string + expect string + }{ + { + name: "console.log", + script: "console.log('hello', 'world')", + expect: "hello world\n", + }, + { + name: "console.err", + script: "console.error('error', 'world')", + expect: "error world\n", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var buf = &bytes.Buffer{} + vm := New(WithStdoutWriter(buf)) + _, err := vm.Run(tc.script) + require.NoError(t, err) + require.Equal(t, tc.expect, buf.String()) + }) + } +} + func BenchmarkNew(b *testing.B) { for i := 0; i < b.N; i++ { New() diff --git a/runtime.go b/runtime.go index 70262367..21533e19 100644 --- a/runtime.go +++ b/runtime.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "math" "path" "reflect" @@ -66,6 +67,7 @@ type runtime struct { stackLimit int traceLimit int lck sync.Mutex + stdoutWriter io.Writer } func (rt *runtime) enterScope(scop *scope) {