123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605 |
- /*
- * 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 (ECALEvalTest)",
- "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 (ECALEvalTest): foo () (Line:3 Pos:2)" {
- t.Error("Unexpected result:", testlogger.String(), evalError, 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")
- mutex a { mutex a { log("test5") } }
- `, 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
- }
- ls, err := testDebugger.HandleInput(fmt.Sprintf("lockstate"))
- lsBytes, _ := json.MarshalIndent(ls, "", " ")
- lsString := string(lsBytes)
- if lsString != `{
- "log": [],
- "owners": {}
- }` {
- t.Error("Unexpected result:", lsString)
- 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)
- block: mutex (Line:12 Pos:1) {
- block: mutex (Line:12 Pos:11) {
- }
- }
- }` {
- t.Error("Unexpected result:", vs)
- return
- }
- ls, err = testDebugger.HandleInput(fmt.Sprintf("lockstate"))
- lsBytes, _ = json.MarshalIndent(ls, "", " ")
- lsString = string(lsBytes)
- if !strings.Contains(lsString, "took lock a with owner") || !strings.Contains(lsString, "attempted to take lock a twice") {
- t.Error("Unexpected result:", lsString)
- 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 (ECALEvalTest): 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
- }
- }
|