12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577 |
- /*
- * ECAL
- *
- * Copyright 2020 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the MIT
- * License, If a copy of the MIT License was not distributed with this
- * file, You can obtain one at https://opensource.org/licenses/MIT.
- */
- package interpreter
- import (
- "encoding/json"
- "fmt"
- "strconv"
- "strings"
- "sync"
- "testing"
- "time"
- "devt.de/krotik/common/errorutil"
- "devt.de/krotik/ecal/scope"
- "devt.de/krotik/ecal/util"
- )
- func TestSimpleDebugging(t *testing.T) {
- var err error
- defer func() {
- testDebugger = nil
- }()
- testDebugger = NewECALDebugger(nil)
- _, err = testDebugger.HandleInput("break ECALEvalTest:3")
- errorutil.AssertOk(err)
- _, err = testDebugger.HandleInput("break ECALEvalTest:4")
- errorutil.AssertOk(err)
- _, err = testDebugger.HandleInput("disablebreak ECALEvalTest:4")
- errorutil.AssertOk(err)
- wg := &sync.WaitGroup{}
- wg.Add(1)
- var tid uint64
- go func() {
- _, err = UnitTestEval(`
- log("test1")
- log("test2")
- log("test3")
- `, nil)
- if err != nil {
- t.Error(err)
- }
- testDebugger.RecordThreadFinished(tid)
- wg.Done()
- }()
- tid = waitForThreadSuspension(t)
- out, err := testDebugger.HandleInput(fmt.Sprintf("status"))
- outBytes, _ := json.MarshalIndent(out, "", " ")
- outString := string(outBytes)
- if err != nil || outString != `{
- "breakonstart": false,
- "breakpoints": {
- "ECALEvalTest:3": true,
- "ECALEvalTest:4": false
- },
- "sources": [
- "ECALEvalTest"
- ],
- "threads": {
- "1": {
- "callStack": [],
- "error": null,
- "threadRunning": false
- }
- }
- }` {
- t.Error("Unexpected result:", outString, err)
- return
- }
- out, err = testDebugger.HandleInput(fmt.Sprintf("describe %v", tid))
- outBytes, _ = json.MarshalIndent(out, "", " ")
- outString = string(outBytes)
- if err != nil || outString != `{
- "callStack": [],
- "callStackNode": [],
- "callStackVsSnapshot": [],
- "callStackVsSnapshotGlobal": [],
- "code": "log(\"test2\")",
- "error": null,
- "node": {
- "allowescapes": false,
- "children": [
- {
- "children": [
- {
- "allowescapes": true,
- "id": 5,
- "identifier": false,
- "line": 3,
- "linepos": 5,
- "name": "string",
- "pos": 18,
- "source": "ECALEvalTest",
- "value": "test2"
- }
- ],
- "name": "funccall"
- }
- ],
- "id": 7,
- "identifier": true,
- "line": 3,
- "linepos": 1,
- "name": "identifier",
- "pos": 14,
- "source": "ECALEvalTest",
- "value": "log"
- },
- "threadRunning": false,
- "vs": {},
- "vsGlobal": {}
- }` {
- t.Error("Unexpected result:", outString, err)
- return
- }
- // Continue until the end
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v Resume", tid))
- errorutil.AssertOk(err)
- wg.Wait()
- if err != nil || testlogger.String() != `
- test1
- test2
- test3`[1:] {
- t.Error("Unexpected result:", testlogger.String(), err)
- return
- }
- _, err = testDebugger.HandleInput("rmbreak ECALEvalTest:4")
- errorutil.AssertOk(err)
- out, err = testDebugger.HandleInput(fmt.Sprintf("status"))
- outBytes, _ = json.MarshalIndent(out, "", " ")
- outString = string(outBytes)
- if err != nil || outString != `{
- "breakonstart": false,
- "breakpoints": {
- "ECALEvalTest:3": true
- },
- "sources": [
- "ECALEvalTest"
- ],
- "threads": {}
- }` {
- t.Error("Unexpected result:", outString, err)
- return
- }
- _, err = testDebugger.HandleInput("break ECALEvalTest:4")
- errorutil.AssertOk(err)
- _, err = testDebugger.HandleInput("rmbreak ECALEvalTest")
- errorutil.AssertOk(err)
- out, err = testDebugger.HandleInput(fmt.Sprintf("status"))
- outBytes, _ = json.MarshalIndent(out, "", " ")
- outString = string(outBytes)
- if err != nil || outString != `{
- "breakonstart": false,
- "breakpoints": {},
- "sources": [
- "ECALEvalTest"
- ],
- "threads": {}
- }` {
- t.Error("Unexpected result:", outString, err)
- return
- }
- }
- func TestDebugReset(t *testing.T) {
- var err error
- defer func() {
- testDebugger = nil
- }()
- testDebugger = NewECALDebugger(nil)
- if _, err = testDebugger.HandleInput("break ECALEvalTest:3"); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- wg := &sync.WaitGroup{}
- wg.Add(1)
- go func() {
- defer wg.Done()
- _, err = UnitTestEval(`
- log("test1")
- log("test2")
- log("test3")
- `, nil)
- if err != nil {
- t.Error(err)
- }
- }()
- waitForThreadSuspension(t)
- out, err := testDebugger.HandleInput(fmt.Sprintf("status"))
- outBytes, _ := json.MarshalIndent(out, "", " ")
- outString := string(outBytes)
- if err != nil || outString != `{
- "breakonstart": false,
- "breakpoints": {
- "ECALEvalTest:3": true
- },
- "sources": [
- "ECALEvalTest"
- ],
- "threads": {
- "1": {
- "callStack": [],
- "error": null,
- "threadRunning": false
- }
- }
- }` {
- t.Error("Unexpected result:", outString, err)
- return
- }
- testDebugger.StopThreads(100 * time.Millisecond)
- wg.Wait()
- if err != nil || testlogger.String() != `
- test1
- test2`[1:] {
- t.Error("Unexpected result:", testlogger.String(), err)
- return
- }
- }
- func TestErrorStop(t *testing.T) {
- var err, evalError error
- defer func() {
- testDebugger = nil
- }()
- testDebugger = NewECALDebugger(nil)
- testDebugger.BreakOnError(true)
- if _, err = testDebugger.HandleInput("break ECALEvalTest:8"); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- wg := &sync.WaitGroup{}
- wg.Add(1)
- go func() {
- defer wg.Done()
- _, evalError = UnitTestEval(`
- func err () {
- raise("foo")
- }
- log("test1")
- log("test2")
- err()
- log("test3")
- `, nil)
- }()
- waitForThreadSuspension(t)
- out, err := testDebugger.HandleInput(fmt.Sprintf("status"))
- outBytes, _ := json.MarshalIndent(out, "", " ")
- outString := string(outBytes)
- if err != nil || outString != `{
- "breakonstart": false,
- "breakpoints": {
- "ECALEvalTest:8": true
- },
- "sources": [
- "ECALEvalTest"
- ],
- "threads": {
- "1": {
- "callStack": [
- "err() (ECALEvalTest:7)"
- ],
- "error": {
- "Data": null,
- "Detail": "",
- "Environment": {},
- "Node": {
- "Name": "identifier",
- "Token": {
- "ID": 7,
- "Pos": 16,
- "Val": "raise",
- "Identifier": true,
- "AllowEscapes": false,
- "PrefixNewlines": 1,
- "Lsource": "ECALEvalTest",
- "Lline": 3,
- "Lpos": 2
- },
- "Meta": null,
- "Children": [
- {
- "Name": "funccall",
- "Token": null,
- "Meta": null,
- "Children": [
- {
- "Name": "string",
- "Token": {
- "ID": 5,
- "Pos": 22,
- "Val": "foo",
- "Identifier": false,
- "AllowEscapes": true,
- "PrefixNewlines": 0,
- "Lsource": "ECALEvalTest",
- "Lline": 3,
- "Lpos": 8
- },
- "Meta": null,
- "Children": [],
- "Runtime": {}
- }
- ],
- "Runtime": {}
- }
- ],
- "Runtime": {}
- },
- "Source": "ECALTestRuntime",
- "Trace": null,
- "Type": "foo"
- },
- "threadRunning": false
- }
- }
- }` {
- t.Error("Unexpected result:", outString, err)
- return
- }
- if _, err = testDebugger.HandleInput(fmt.Sprintf("cont 1 Resume")); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- wg.Wait()
- if evalError == nil || testlogger.String() != `
- test1
- test2`[1:] || evalError.Error() != "ECAL error in ECALTestRuntime: foo () (Line:3 Pos:2)" {
- t.Error("Unexpected result:", testlogger.String(), err)
- return
- }
- }
- func TestConcurrentDebugging(t *testing.T) {
- var err error
- defer func() {
- testDebugger = nil
- }()
- testDebugger = NewECALDebugger(nil)
- if _, err = testDebugger.HandleInput("break ECALEvalTest:5"); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- wg := &sync.WaitGroup{}
- wg.Add(2)
- erp := NewECALRuntimeProvider("ECALTestRuntime", nil, nil)
- vs := scope.NewScope(scope.GlobalScope)
- go func() {
- _, err = UnitTestEvalWithRuntimeProvider(`
- a := 1
- b := 1
- func test1() {
- log("test3")
- b := a + 1
- }
- log("test1")
- log("test2")
- test1()
- log("test4")
- `, vs, erp)
- if err != nil {
- t.Error(err)
- }
- wg.Done()
- }()
- go func() {
- _, err = UnitTestEvalWithRuntimeProvider(`
- a := 1
- c := 1
- func test2() {
- log("test3")
- c := a + 1
- }
- log("test1")
- log("test2")
- test2()
- log("test4")
- `, vs, erp)
- if err != nil {
- t.Error(err)
- }
- wg.Done()
- }()
- waitForAllThreadSuspension(t)
- out, err := testDebugger.HandleInput(fmt.Sprintf("status"))
- outBytes, _ := json.MarshalIndent(out, "", " ")
- outString := string(outBytes)
- if err != nil || (outString != `{
- "breakonstart": false,
- "breakpoints": {
- "ECALEvalTest:5": true
- },
- "sources": [
- "ECALEvalTest"
- ],
- "threads": {
- "1": {
- "callStack": [
- "test1() (ECALEvalTest:10)"
- ],
- "error": null,
- "threadRunning": false
- },
- "2": {
- "callStack": [
- "test2() (ECALEvalTest:10)"
- ],
- "error": null,
- "threadRunning": false
- }
- }
- }` && outString != `{
- "breakonstart": false,
- "breakpoints": {
- "ECALEvalTest:5": true
- },
- "sources": [
- "ECALEvalTest"
- ],
- "threads": {
- "1": {
- "callStack": [
- "test2() (ECALEvalTest:10)"
- ],
- "error": null,
- "threadRunning": false
- },
- "2": {
- "callStack": [
- "test1() (ECALEvalTest:10)"
- ],
- "error": null,
- "threadRunning": false
- }
- }
- }`) {
- t.Error("Unexpected result:", outString, err)
- return
- }
- // Continue until the end
- if _, err := testDebugger.HandleInput(fmt.Sprintf("cont 1 Resume")); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err := testDebugger.HandleInput(fmt.Sprintf("cont 2 Resume")); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- wg.Wait()
- if vs.String() != `GlobalScope {
- a (float64) : 1
- b (float64) : 2
- c (float64) : 2
- test1 (*interpreter.function) : ecal.function: test1 (Line 4, Pos 1)
- test2 (*interpreter.function) : ecal.function: test2 (Line 4, Pos 1)
- }` {
- t.Error("Unexpected result:", vs)
- return
- }
- }
- func waitForThreadSuspension(t *testing.T) uint64 {
- var tid uint64
- for i := 0; i < 100; i++ {
- state, err := testDebugger.HandleInput("status")
- errorutil.AssertOk(err)
- threads := state.(map[string]interface{})["threads"].(map[string]map[string]interface{})
- if len(threads) > 0 {
- for threadID, status := range threads {
- if r, ok := status["threadRunning"]; ok && !r.(bool) {
- threadIDNum, _ := strconv.ParseInt(threadID, 10, 0)
- tid = uint64(threadIDNum)
- return tid
- }
- }
- }
- time.Sleep(1 * time.Millisecond)
- }
- panic("No suspended thread")
- }
- func waitForAllThreadSuspension(t *testing.T) uint64 {
- var tid uint64
- for i := 0; i < 100; i++ {
- state, err := testDebugger.HandleInput("status")
- errorutil.AssertOk(err)
- threads := state.(map[string]interface{})["threads"].(map[string]map[string]interface{})
- if len(threads) > 0 {
- allSuspended := true
- for _, status := range threads {
- if r, ok := status["threadRunning"]; ok && !r.(bool) {
- allSuspended = false
- break
- }
- }
- if allSuspended {
- break
- }
- }
- time.Sleep(1 * time.Millisecond)
- }
- return tid
- }
- func TestStepDebugging(t *testing.T) {
- var err error
- defer func() {
- testDebugger = nil
- }()
- testDebugger = NewECALDebugger(nil)
- code := `
- log("start")
- func fa(x) {
- a := 1
- log("a enter")
- fb(x)
- log("a exit")
- }
- func fb(x) {
- b := 2
- log("b enter")
- fc()
- fc(fc())
- log("b exit")
- }
- func fc() {
- c := 3
- log("c enter")
- log("c exit")
- }
- fa(1)
- func e() {
- log("e()")
- }
- func d() {
- e()
- }
- d(d())
- log("finish")
- `
- _, err = testDebugger.HandleInput("break ECALEvalTest:10")
- errorutil.AssertOk(err)
- _, err = testDebugger.HandleInput("breakonstart true")
- errorutil.AssertOk(err)
- wg := &sync.WaitGroup{}
- wg.Add(1)
- go func() {
- _, err = UnitTestEval(code, nil)
- if err != nil {
- t.Error(err)
- }
- wg.Done()
- }()
- tid := waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true
- },
- "code": "log(\"start\")",
- "threads": {
- "1": {
- "callStack": [],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {}
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v resume", tid))
- errorutil.AssertOk(err)
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true
- },
- "code": "b := 2",
- "threads": {
- "1": {
- "callStack": [
- "fa(1) (ECALEvalTest:21)",
- "fb(x) (ECALEvalTest:6)"
- ],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "x": 1
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Step in without a function
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v stepin", tid))
- errorutil.AssertOk(err)
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true
- },
- "code": "log(\"b enter\")",
- "threads": {
- "1": {
- "callStack": [
- "fa(1) (ECALEvalTest:21)",
- "fb(x) (ECALEvalTest:6)"
- ],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "b": 2,
- "x": 1
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Normal step over
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v stepover", tid))
- errorutil.AssertOk(err)
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true
- },
- "code": "fc()",
- "threads": {
- "1": {
- "callStack": [
- "fa(1) (ECALEvalTest:21)",
- "fb(x) (ECALEvalTest:6)"
- ],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "b": 2,
- "x": 1
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Normal step in
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v stepin", tid))
- errorutil.AssertOk(err)
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true
- },
- "code": "c := 3",
- "threads": {
- "1": {
- "callStack": [
- "fa(1) (ECALEvalTest:21)",
- "fb(x) (ECALEvalTest:6)",
- "fc() (ECALEvalTest:12)"
- ],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {}
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Normal step out
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v stepout", tid))
- errorutil.AssertOk(err)
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true
- },
- "code": "fc(fc())",
- "threads": {
- "1": {
- "callStack": [
- "fa(1) (ECALEvalTest:21)",
- "fb(x) (ECALEvalTest:6)"
- ],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "b": 2,
- "x": 1
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Step in and step out - we should end up on the same line as before
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v stepin", tid))
- errorutil.AssertOk(err)
- testStepDebugging2(t, testDebugger, wg)
- }
- func testStepDebugging2(t *testing.T, testDebugger util.ECALDebugger, wg *sync.WaitGroup) {
- tid := waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true
- },
- "code": "c := 3",
- "threads": {
- "1": {
- "callStack": [
- "fa(1) (ECALEvalTest:21)",
- "fb(x) (ECALEvalTest:6)",
- "fc() (ECALEvalTest:13)"
- ],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {}
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- _, err := testDebugger.HandleInput(fmt.Sprintf("cont %v stepout", tid))
- errorutil.AssertOk(err)
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true
- },
- "code": "fc(fc())",
- "threads": {
- "1": {
- "callStack": [
- "fa(1) (ECALEvalTest:21)",
- "fb(x) (ECALEvalTest:6)"
- ],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "b": 2,
- "x": 1
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Normal step out
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v stepout", tid))
- errorutil.AssertOk(err)
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true
- },
- "code": "log(\"a exit\")",
- "threads": {
- "1": {
- "callStack": [
- "fa(1) (ECALEvalTest:21)"
- ],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "a": 1,
- "x": 1
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Set a new breakpoint
- _, err = testDebugger.HandleInput("break ECALEvalTest:28")
- errorutil.AssertOk(err)
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v Resume", tid))
- errorutil.AssertOk(err)
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true,
- "ECALEvalTest:28": true
- },
- "code": "d(d())",
- "threads": {
- "1": {
- "callStack": [],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "d": "ecal.function: d (Line 25, Pos 1)",
- "e": "ecal.function: e (Line 22, Pos 1)",
- "fa": "ecal.function: fa (Line 3, Pos 1)",
- "fb": "ecal.function: fb (Line 9, Pos 1)",
- "fc": "ecal.function: fc (Line 16, Pos 1)"
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Normal step over
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v stepover", tid))
- errorutil.AssertOk(err)
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true,
- "ECALEvalTest:28": true
- },
- "code": "d(d())",
- "threads": {
- "1": {
- "callStack": [],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "d": "ecal.function: d (Line 25, Pos 1)",
- "e": "ecal.function: e (Line 22, Pos 1)",
- "fa": "ecal.function: fa (Line 3, Pos 1)",
- "fb": "ecal.function: fb (Line 9, Pos 1)",
- "fc": "ecal.function: fc (Line 16, Pos 1)"
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- if _, err := testDebugger.HandleInput(fmt.Sprintf("cont %v stepover", tid)); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:10": true,
- "ECALEvalTest:28": true
- },
- "code": "log(\"finish\")",
- "threads": {
- "1": {
- "callStack": [],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "d": "ecal.function: d (Line 25, Pos 1)",
- "e": "ecal.function: e (Line 22, Pos 1)",
- "fa": "ecal.function: fa (Line 3, Pos 1)",
- "fb": "ecal.function: fb (Line 9, Pos 1)",
- "fc": "ecal.function: fc (Line 16, Pos 1)"
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Continue until the end
- _, err = testDebugger.HandleInput(fmt.Sprintf("cont %v Resume", tid))
- errorutil.AssertOk(err)
- wg.Wait()
- if err != nil || testlogger.String() != `
- start
- a enter
- b enter
- c enter
- c exit
- c enter
- c exit
- c enter
- c exit
- b exit
- a exit
- e()
- e()
- finish`[1:] {
- t.Error("Unexpected result:", testlogger.String(), err)
- return
- }
- }
- func TestStepDebuggingWithImport(t *testing.T) {
- var err error
- defer func() {
- testDebugger = nil
- }()
- testDebugger = NewECALDebugger(nil)
- il := &util.MemoryImportLocator{Files: make(map[string]string)}
- il.Files["foo/bar"] = `
- func myfunc(n) {
- if (n <= 1) {
- return n
- }
- n := n + 1
- return n
- }
- `
- code := `
- a := 1
- import "foo/bar" as foobar
- log("start")
- a := foobar.myfunc(a)
- log("finish: ", a)
- `
- if _, err = testDebugger.HandleInput("break ECALEvalTest:4"); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput("break foo/bar:4"); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- wg := &sync.WaitGroup{}
- wg.Add(1)
- go func() {
- _, err = UnitTestEvalAndASTAndImport(code, nil, "", il)
- if err != nil {
- t.Error(err)
- }
- wg.Done()
- }()
- tid := waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:4": true,
- "foo/bar:4": true
- },
- "code": "log(\"start\")",
- "threads": {
- "1": {
- "callStack": [],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "a": 1,
- "foobar": {
- "myfunc": "ecal.function: myfunc (Line 2, Pos 1)"
- }
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Resume execution
- if _, err := testDebugger.HandleInput(fmt.Sprintf("cont %v resume", tid)); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- tid = waitForThreadSuspension(t)
- if state := getDebuggerState(tid, t); state != `{
- "breakpoints": {
- "ECALEvalTest:4": true,
- "foo/bar:4": true
- },
- "code": "return n",
- "threads": {
- "1": {
- "callStack": [
- "myfunc(a) (ECALEvalTest:5)"
- ],
- "error": null,
- "threadRunning": false
- }
- },
- "vs": {
- "n": 1
- }
- }` {
- t.Error("Unexpected state:", state)
- return
- }
- // Continue until the end
- if _, err := testDebugger.HandleInput(fmt.Sprintf("cont %v Resume", tid)); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- wg.Wait()
- if err != nil || testlogger.String() != `
- start
- finish: 1`[1:] {
- t.Error("Unexpected result:", testlogger.String(), err)
- return
- }
- }
- func getDebuggerState(tid uint64, t *testing.T) string {
- out, err := testDebugger.HandleInput(fmt.Sprintf("status"))
- if err != nil {
- t.Error(err)
- return ""
- }
- outMap := out.(map[string]interface{})
- out, err = testDebugger.HandleInput(fmt.Sprintf("describe %v", tid))
- if err != nil {
- t.Error(err)
- return ""
- }
- outMap2 := out.(map[string]interface{})
- outMap["vs"] = outMap2["vs"]
- outMap["code"] = outMap2["code"]
- delete(outMap, "breakonstart")
- delete(outMap, "sources")
- outBytes, _ := json.MarshalIndent(outMap, "", " ")
- return string(outBytes)
- }
- func TestInjectAndExtractDebugging(t *testing.T) {
- var err error
- defer func() {
- testDebugger = nil
- }()
- vs := scope.NewScope(scope.GlobalScope)
- testDebugger = NewECALDebugger(vs)
- if _, err = testDebugger.HandleInput("break ECALEvalTest:5"); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- wg := &sync.WaitGroup{}
- wg.Add(1)
- go func() {
- _, err = UnitTestEval(`
- b := 49
- func myfunc() {
- a := 56
- log("test2 a=", a)
- }
- log("test1")
- myfunc()
- log("test3 b=", b)
- `, vs)
- if err != nil {
- t.Error(err)
- }
- wg.Done()
- }()
- tid := waitForThreadSuspension(t)
- out, err := testDebugger.HandleInput(fmt.Sprintf("status"))
- outBytes, _ := json.MarshalIndent(out, "", " ")
- outString := string(outBytes)
- if err != nil || outString != `{
- "breakonstart": false,
- "breakpoints": {
- "ECALEvalTest:5": true
- },
- "sources": [
- "ECALEvalTest"
- ],
- "threads": {
- "1": {
- "callStack": [
- "myfunc() (ECALEvalTest:8)"
- ],
- "error": null,
- "threadRunning": false
- }
- }
- }` {
- t.Error("Unexpected result:", outString, err)
- return
- }
- out, err = testDebugger.HandleInput(fmt.Sprintf("describe %v", tid))
- outBytes, _ = json.MarshalIndent(out, "", " ")
- outString = string(outBytes)
- if err != nil || outString != `{
- "callStack": [
- "myfunc() (ECALEvalTest:8)"
- ],
- "callStackNode": [
- {
- "allowescapes": false,
- "children": [
- {
- "name": "funccall"
- }
- ],
- "id": 7,
- "identifier": true,
- "line": 8,
- "linepos": 1,
- "name": "identifier",
- "pos": 69,
- "source": "ECALEvalTest",
- "value": "myfunc"
- }
- ],
- "callStackVsSnapshot": [
- {
- "b": 49,
- "myfunc": "ecal.function: myfunc (Line 3, Pos 1)"
- }
- ],
- "callStackVsSnapshotGlobal": [
- {
- "b": 49,
- "myfunc": "ecal.function: myfunc (Line 3, Pos 1)"
- }
- ],
- "code": "log(\"test2 a=\", a)",
- "error": null,
- "node": {
- "allowescapes": false,
- "children": [
- {
- "children": [
- {
- "allowescapes": true,
- "id": 5,
- "identifier": false,
- "line": 5,
- "linepos": 6,
- "name": "string",
- "pos": 39,
- "source": "ECALEvalTest",
- "value": "test2 a="
- },
- {
- "allowescapes": false,
- "id": 7,
- "identifier": true,
- "line": 5,
- "linepos": 18,
- "name": "identifier",
- "pos": 51,
- "source": "ECALEvalTest",
- "value": "a"
- }
- ],
- "name": "funccall"
- }
- ],
- "id": 7,
- "identifier": true,
- "line": 5,
- "linepos": 2,
- "name": "identifier",
- "pos": 35,
- "source": "ECALEvalTest",
- "value": "log"
- },
- "threadRunning": false,
- "vs": {
- "a": 56
- },
- "vsGlobal": {
- "b": 49,
- "myfunc": "ecal.function: myfunc (Line 3, Pos 1)"
- }
- }` {
- t.Error("Unexpected result:", outString, err)
- return
- }
- if _, err := testDebugger.HandleInput(fmt.Sprintf("extract %v a foo", tid)); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err := testDebugger.HandleInput(fmt.Sprintf("inject %v a x := b + 1; x", tid)); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- // Continue until the end
- if _, err := testDebugger.HandleInput(fmt.Sprintf("cont %v Resume", tid)); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- wg.Wait()
- if vs.String() != `
- GlobalScope {
- b (float64) : 49
- foo (float64) : 56
- myfunc (*interpreter.function) : ecal.function: myfunc (Line 3, Pos 1)
- }`[1:] {
- t.Error("Unexpected result:", vs.String(), err)
- return
- }
- if testlogger.String() != `
- test1
- test2 a=50
- test3 b=49`[1:] {
- t.Error("Unexpected result:", testlogger.String(), err)
- return
- }
- }
- func TestSimpleStacktrace(t *testing.T) {
- res, err := UnitTestEval(`
- func a() {
- b()
- }
- func b() {
- c()
- }
- func c() {
- raise("testerror")
- }
- a()
- `, nil)
- if err == nil {
- t.Error("Unexpected result: ", res, err)
- return
- }
- ss := err.(util.TraceableRuntimeError)
- if out := fmt.Sprintf("%v\n %v", err.Error(), strings.Join(ss.GetTraceString(), "\n ")); out != `
- ECAL error in ECALTestRuntime: testerror () (Line:9 Pos:2)
- raise("testerror") (ECALEvalTest:9)
- c() (ECALEvalTest:6)
- b() (ECALEvalTest:3)
- a() (ECALEvalTest:11)`[1:] {
- t.Error("Unexpected output:", out)
- return
- }
- }
- func TestDebugDocstrings(t *testing.T) {
- for k, v := range DebugCommandsMap {
- if res := v.DocString(); res == "" {
- t.Error("Docstring missing for ", k)
- return
- }
- }
- }
- func TestDebuggingErrorInput(t *testing.T) {
- var err error
- defer func() {
- testDebugger = nil
- }()
- vs := scope.NewScope(scope.GlobalScope)
- testDebugger = NewECALDebugger(vs)
- if _, err = testDebugger.HandleInput("uuu"); err.Error() != `Unknown command: uuu` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput("break"); err.Error() != `Need a break target (<source>:<line>) as first parameter` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput("break foo"); err.Error() != `Invalid break target - should be <source>:<line>` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput("rmbreak"); err.Error() != `Need a break target (<source>[:<line>]) as first parameter` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput("disablebreak"); err.Error() != `Need a break target (<source>:<line>) as first parameter` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput("disablebreak foo"); err.Error() != `Invalid break target - should be <source>:<line>` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput("break ECALEvalTest:3"); err != nil {
- t.Error("Unexpected result:", err)
- return
- }
- wg := &sync.WaitGroup{}
- wg.Add(1)
- go func() {
- _, err = UnitTestEval(`
- a:=1
- log("test1")
- log("test2")
- log("test3")
- `, vs)
- if err != nil {
- t.Error(err)
- }
- wg.Done()
- }()
- tid := waitForThreadSuspension(t)
- out, err := testDebugger.HandleInput(fmt.Sprintf("status"))
- outBytes, _ := json.MarshalIndent(out, "", " ")
- outString := string(outBytes)
- if err != nil || outString != `{
- "breakonstart": false,
- "breakpoints": {
- "ECALEvalTest:3": true
- },
- "sources": [
- "ECALEvalTest"
- ],
- "threads": {
- "1": {
- "callStack": [],
- "error": null,
- "threadRunning": false
- }
- }
- }` {
- t.Error("Unexpected result:", outString, err)
- return
- }
- if _, err = testDebugger.HandleInput(fmt.Sprintf("extract %v foo foo", tid)); err.Error() != `No such value foo` {
- t.Error("Unexpected result:", err)
- return
- }
- }
- func TestDebuggingErrorInput2(t *testing.T) {
- var err error
- tid := 1
- defer func() {
- testDebugger = nil
- }()
- vs := scope.NewScope(scope.GlobalScope)
- testDebugger = NewECALDebugger(vs)
- if _, err = testDebugger.HandleInput("cont foo"); err.Error() != `Need a thread ID and a command Resume, StepIn, StepOver or StepOut` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput("cont foo bar"); err.Error() != `Parameter 1 should be a number` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput("cont 99 bar"); err.Error() != `Invalid command bar - must be resume, stepin, stepover or stepout` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput("describe"); err.Error() != `Need a thread ID` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput(fmt.Sprintf("extract %v foo", tid)); err.Error() != `Need a thread ID, a variable name and a destination variable name` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput(fmt.Sprintf("extract %v _foo foo", tid)); err.Error() != `Variable names may only contain [a-zA-Z] and [a-zA-Z0-9] from the second character` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput(fmt.Sprintf("inject %v", tid)); err.Error() != `Need a thread ID, a variable name and an expression` {
- t.Error("Unexpected result:", err)
- return
- }
- testDebugger.(*ecalDebugger).globalScope = nil
- if _, err = testDebugger.HandleInput(fmt.Sprintf("extract %v foo foo", tid)); err.Error() != `Cannot access global scope` {
- t.Error("Unexpected result:", err)
- return
- }
- if _, err = testDebugger.HandleInput(fmt.Sprintf("inject %v foo foo", tid)); err.Error() != `Cannot access global scope` {
- t.Error("Unexpected result:", err)
- return
- }
- }
|