github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/tests/runtime_test.go (about) 1 //go:build js && gopherjs 2 3 package tests 4 5 import ( 6 "fmt" 7 "runtime" 8 "testing" 9 10 "github.com/google/go-cmp/cmp" 11 "github.com/gopherjs/gopherjs/js" 12 ) 13 14 func Test_parseCallFrame(t *testing.T) { 15 tests := []struct { 16 name string 17 input string 18 want string 19 }{ 20 { 21 name: "Chrome 96.0.4664.110 on Linux #1", 22 input: "at foo (eval at $b (https://gopherjs.github.io/playground/playground.js:102:11836), <anonymous>:25887:60)", 23 want: "foo https://gopherjs.github.io/playground/playground.js 102 11836", 24 }, 25 { 26 name: "Chrome 96, anonymous eval", 27 input: " at eval (<anonymous>)", 28 want: "eval <anonymous> 0 0", 29 }, 30 { 31 name: "Chrome 96, anonymous Array.forEach", 32 input: " at Array.forEach (<anonymous>)", 33 want: "Array.forEach <anonymous> 0 0", 34 }, 35 { 36 name: "Chrome 96, file location only", 37 input: "at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js:31:225", 38 want: "<none> https://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js 31 225", 39 }, 40 { 41 name: "Chrome 96, aliased function", 42 input: "at k.e.$externalizeWrapper.e.$externalizeWrapper [as run] (https://gopherjs.github.io/playground/playground.js:5:30547)", 43 want: "run https://gopherjs.github.io/playground/playground.js 5 30547", 44 }, 45 { 46 name: "Node.js v12.22.5", 47 input: " at Script.runInThisContext (vm.js:120:18)", 48 want: "Script.runInThisContext vm.js 120 18", 49 }, 50 { 51 name: "Node.js v12.22.5, aliased function", 52 input: "at REPLServer.runBound [as eval] (domain.js:440:12)", 53 want: "eval domain.js 440 12", 54 }, 55 { 56 name: "Firefox 78.15.0esr Linux", 57 input: "getEvalResult@resource://devtools/server/actors/webconsole/eval-with-debugger.js:231:24", 58 want: "getEvalResult resource://devtools/server/actors/webconsole/eval-with-debugger.js 231 24", 59 }, 60 } 61 62 for _, tt := range tests { 63 t.Run(tt.name, func(t *testing.T) { 64 lines := js.Global.Get("String").New(tt.input) 65 frame := runtime.ParseCallFrame(lines) 66 got := fmt.Sprintf("%v %v %v %v", frame.FuncName, frame.File, frame.Line, frame.Col) 67 if tt.want != got { 68 t.Errorf("Unexpected result: %s", got) 69 } 70 }) 71 } 72 } 73 74 func TestBuildPlatform(t *testing.T) { 75 if runtime.GOOS != "js" { 76 t.Errorf("Got runtime.GOOS=%q. Want: %q.", runtime.GOOS, "js") 77 } 78 if runtime.GOARCH != "ecmascript" { 79 t.Errorf("Got runtime.GOARCH=%q. Want: %q.", runtime.GOARCH, "ecmascript") 80 } 81 } 82 83 type funcName string 84 85 func masked(_ funcName) funcName { return "<MASKED>" } 86 87 type callStack []funcName 88 89 func (c *callStack) capture() { 90 *c = nil 91 pc := [100]uintptr{} 92 depth := runtime.Callers(0, pc[:]) 93 frames := runtime.CallersFrames(pc[:depth]) 94 for true { 95 frame, more := frames.Next() 96 *c = append(*c, funcName(frame.Function)) 97 if !more { 98 break 99 } 100 } 101 } 102 103 func TestCallers(t *testing.T) { 104 got := callStack{} 105 106 // Some of the GopherJS function names don't match upstream Go, or even the 107 // function names in the Go source when minified. 108 // Until https://github.com/gopherjs/gopherjs/issues/1085 is resolved, the 109 // mismatch is difficult to avoid, but we can at least use "masked" frames to 110 // make sure the number of frames matches expected. 111 want := callStack{ 112 masked("runtime.Callers"), 113 masked("github.com/gopherjs/gopherjs/tests.(*callerNames).capture"), 114 masked("github.com/gopherjs/gopherjs/tests.TestCallers.func{1,2}"), 115 masked("testing.tRunner"), 116 "runtime.goexit", 117 } 118 119 opts := cmp.Comparer(func(a, b funcName) bool { 120 if a == masked("") || b == masked("") { 121 return true 122 } 123 return a == b 124 }) 125 126 t.Run("Normal", func(t *testing.T) { 127 got.capture() 128 if diff := cmp.Diff(want, got, opts); diff != "" { 129 t.Errorf("runtime.Callers() returned a diff (-want,+got):\n%s", diff) 130 } 131 }) 132 133 t.Run("Deferred", func(t *testing.T) { 134 defer func() { 135 if diff := cmp.Diff(want, got, opts); diff != "" { 136 t.Errorf("runtime.Callers() returned a diff (-want,+got):\n%s", diff) 137 } 138 }() 139 defer got.capture() 140 }) 141 142 t.Run("Recover", func(t *testing.T) { 143 defer func() { 144 recover() 145 got.capture() 146 147 want := callStack{ 148 masked("runtime.Callers"), 149 masked("github.com/gopherjs/gopherjs/tests.(*callerNames).capture"), 150 masked("github.com/gopherjs/gopherjs/tests.TestCallers.func3.1"), 151 "runtime.gopanic", 152 masked("github.com/gopherjs/gopherjs/tests.TestCallers.func{1,2}"), 153 masked("testing.tRunner"), 154 "runtime.goexit", 155 } 156 if diff := cmp.Diff(want, got, opts); diff != "" { 157 t.Errorf("runtime.Callers() returned a diff (-want,+got):\n%s", diff) 158 } 159 }() 160 panic("panic") 161 }) 162 }