tests/GDBMIParserTests.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Mon, 28 Jan 2019 14:56:14 +0000
changeset 173 02546d4fbe6d
parent 126 fb73b0af430b
child 174 18ef81a3fee5
permissions -rw-r--r--
Fix frame of `GDBThreadSelectedEvent` if inferior is running When ifnferior is running at time we get `=thread-selected` event, we should at least make that frame kind of usable by fixing up it's debugger and thread. This allow clients to use (to some extent) event's frame without worring (too much) about these details.

"
jv:libgdbs - GNU Debugger Interface Library
Copyright (C) 2015-now Jan Vrany

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License. 

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
"
"{ Package: 'jv:libgdbs/tests' }"

"{ NameSpace: Smalltalk }"

TestCase subclass:#GDBMIParserTests
	instanceVariableNames:'properties prop1 prop2 prop3'
	classVariableNames:''
	poolDictionaries:'GDBCommandStatus'
	category:'GDB-Private-Tests'
!

!GDBMIParserTests class methodsFor:'documentation'!

copyright
"
jv:libgdbs - GNU Debugger Interface Library
Copyright (C) 2015-now Jan Vrany

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License. 

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
"
! !

!GDBMIParserTests methodsFor:'accessing'!

prop1
    ^ prop1
!

prop2
    ^ prop2
!

prop3
    ^ prop3
! !

!GDBMIParserTests methodsFor:'accessing-properties'!

properties
    ^ GDBObject getPropertiesOf: self.

    "Modified: / 20-06-2014 / 09:04:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

propertyAt: name
    ^ GDBObject getProperty: name of: self

    "Created: / 31-05-2014 / 00:00:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 20-06-2014 / 09:05:05 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

propertyAt: name put: value
    ^ GDBObject setProperty: name of: self to: value

    "Created: / 31-05-2014 / 00:01:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 20-06-2014 / 09:05:34 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!GDBMIParserTests methodsFor:'running'!

setUp
    Magritte::MADescriptionBuilder default flush.

    "Created: / 11-11-2017 / 12:02:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

tearDown
    properties := nil.

    "Created: / 18-06-2014 / 07:59:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!GDBMIParserTests methodsFor:'tests - C strings'!

test_c_string_01
    self assert:(GDBMIParser on:'"Hello" xxx') parseCString = 'Hello'.
    self assert:(GDBMIParser on:'"\"Hello\"" xxx') parseCString = '"Hello"'.
    self assert:(GDBMIParser on:'"\H\e\l\l\o" xxx') parseCString = 'Hello'.
    self assert:(GDBMIParser on:'"Hel\nlo" xxx') parseCString = 'Hel
lo'.
    self assert:(GDBMIParser on:'"X\xE1X" xxx') parseCString = ('X', (Character codePoint: 16rE1), 'X').
    self 
        assert:(GDBMIParser 
                on:'"warning: File \"/home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/stx/.gdbinit\" auto-loading has been declined by your `auto-load safe-path'' set to \"$debugdir:$datadir/auto-load\".\n"') 
                    parseCString 
                    = 'warning: File "/home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/stx/.gdbinit" auto-loading has been declined by your `auto-load safe-path'' set to "$debugdir:$datadir/auto-load".
'.

    "Created: / 28-05-2014 / 00:05:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 15-02-2018 / 08:55:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 04-02-2018 / 21:59:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!GDBMIParserTests methodsFor:'tests - commands'!

test_command_01
    | command |

    command := (GDBMIParser on:'b factorial') parseCommand.
    self assert:command isCLICommand.
    self assert:command token isNil.
    self assert:command value = 'b factorial'.

    "Created: / 24-06-2014 / 23:21:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_02
    | command |

    command := (GDBMIParser on:'-gdb-exit') parseCommand.
    self assert:command isMICommand.
    self assert:command class == GDBMI_gdb_exit.
    self assert:command arguments isEmpty

    "Created: / 24-06-2014 / 23:29:59 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!GDBMIParserTests methodsFor:'tests - commands reponses'!

test_command_break_created_01
    | parser  events |

    parser := GDBMIParser 
            on:'=breakpoint-created,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0xf3416ac0",func="__STX_AddTimeout",file="util.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/stx_8_0_0_x32_lin/build/stx/librun/util.c",line="143",thread-groups=["i1"],times="0",original-location="__STX_AddTimeout"}
'.
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:events first class == GDBBreakpointCreatedEvent.

    "Created: / 11-07-2017 / 10:07:06 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_break_created_02
    | parser  events |

    parser := GDBMIParser 
            on:'=breakpoint-created,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="<MULTIPLE>",times="0",original-location="stxThinLock"},{number="1.1",enabled="y",addr="0x00007fffeb6a0423",func="stxJ_MONITORENTER",file="../include/thinlocks.h",fullname="/home/jv/Private/Projects/SmalltalkX/sources/feature-94-revamp-thinlocks/build/stx/stc/thinlocks.h",line="127",thread-groups=["i1"]},{number="1.2",enabled="y",addr="0x00007ffff4c3b129",func="stxThinLock",file="../include/thinlocks.h",fullname="/home/jv/Private/Projects/SmalltalkX/sources/feature-94-revamp-thinlocks/build/stx/stc/thinlocks.h",line="128",thread-groups=["i1"]}
'.
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:events first class == GDBBreakpointCreatedEvent.

    "Created: / 03-10-2017 / 11:20:01 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_break_list_01
    | parser  events |

    parser := GDBMIParser 
            on:'1^done,BreakpointTable={nr_rows="1",nr_cols="6",hdr=[{width="3",alignment="-1",col_name="number",colhdr="Num"},{width="14",alignment="-1",col_name="type",colhdr="Type"},{width="4",alignment="-1",col_name="disp",colhdr="Disp"},{width="3",alignment="-1",col_name="enabled",colhdr="Enb"},{width="10",alignment="-1",col_name="addr",colhdr="Address"},{width="40",alignment="2",col_name="what",colhdr="What"}],body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x000100d0",func="main",file="hello.c",fullname="/home/foo/hello.c",line="5",thread-groups=["i1"],times="0",ignore="3"}]}
'.
    parser token2CommandMappingBlock:[:token | GDBMI_break_list new ].
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:(events first result propertyAt:'BreakpointTable') notNil.
    self 
        assert:((events first result propertyAt:'BreakpointTable') at:'body') 
                notNil.

    "Created: / 20-06-2014 / 09:08:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_break_modified_01
    | parser  events |

    parser := GDBMIParser 
            on:'=breakpoint-modified,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0xf33f4daf",func="_mark",file="new.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/stx_8_0_0_x32_lin/build/stx/librun/new.c",line="18538",thread-groups=["i1"],times="0",script={"set $x = $x + 1","c"},original-location="_mark"}
'.
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:events first class == GDBBreakpointModifiedEvent.
    self assert:(events first breakpoints first propertyAt:'script') = ('set $x = $x + 1', Character cr ,  'c')

    "Created: / 06-07-2017 / 07:47:05 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 12-11-2017 / 20:18:49 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_data_read_memory_01
    | parser events result |

    parser := GDBMIParser 
            on:('9^done,addr="0x00001390",nr-bytes="6",total-bytes="6",
next-row="0x00001396",prev-row="0x0000138e",next-page="0x00001396",
prev-page="0x0000138a",memory=[
{addr="0x00001390",data=["0x00","0x01"]},
{addr="0x00001392",data=["0x02","0x03"]},
{addr="0x00001394",data=["0x04","0x05"]}]' asStringCollection asStringWith:'').
    parser token2CommandMappingBlock:[:token | GDBMI_data_read_memory new ].
    events := parser parseOutput.

    self assert:events size == 1.
    result := events first result.

    self assert:((result value) isKindOf: GDBMemoryDump).
    self assert: (result value) addr = '0x00001390'.
    self assert: (result value) memory size = 3.

    "Created: / 24-01-2018 / 08:55:09 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 25-01-2018 / 09:07:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_data_read_memory_02
    | parser events result |

    parser := GDBMIParser 
            on:('4^done,addr="0x000013a0",nr-bytes="32",total-bytes="32",
next-row="0x000013c0",prev-row="0x0000139c",
next-page="0x000013c0",prev-page="0x00001380",memory=[
{addr="0x000013a0",data=["0x10","0x11","0x12","0x13"],ascii="xxxx"},
{addr="0x000013a4",data=["0x14","0x15","0x16","0x17"],ascii="xxxx"},
{addr="0x000013a8",data=["0x18","0x19","0x1a","0x1b"],ascii="xxxx"},
{addr="0x000013ac",data=["0x1c","0x1d","0x1e","0x1f"],ascii="xxxx"},
{addr="0x000013b0",data=["0x20","0x21","0x22","0x23"],ascii=" !!\"#"},
{addr="0x000013b4",data=["0x24","0x25","0x26","0x27"],ascii="$%&''"},
{addr="0x000013b8",data=["0x28","0x29","0x2a","0x2b"],ascii="()*+"},
{addr="0x000013bc",data=["0x2c","0x2d","0x2e","0x2f"],ascii=",-./"}]' asStringCollection asStringWith:'').
    parser token2CommandMappingBlock:[:token | GDBMI_data_read_memory new ].
    events := parser parseOutput.

    self assert:events size == 1.
    result := events first result.

    self assert:((result value) isKindOf: GDBMemoryDump).
    self assert: (result value) addr = '0x000013a0'.
    self assert: (result value) memory size = 8.

    "Created: / 25-01-2018 / 22:53:50 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_gdb_exit
    | parser events result |

    parser := GDBMIParser 
            on:'1^exit'.
    parser token2CommandMappingBlock:[:token | GDBMI_gdb_exit new ].
    events := parser parseOutput.

    self assert:events size == 1.
    result := events first result.

    self assert:result value isNil.

    "Created: / 31-05-2017 / 21:19:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_stack_list_frames_01
    | parser  events |

    parser := GDBMIParser 
            on:'1^done,stack=[frame={level="0",addr="0x00010734",func="callee4",file="../../../devo/gdb/testsuite/gdb.mi/basics.c",fullname="/home/foo/bar/devo/gdb/testsuite/gdb.mi/basics.c",line="8"},frame={level="1",addr="0x0001076c",func="callee3",file="../../../devo/gdb/testsuite/gdb.mi/basics.c",fullname="/home/foo/bar/devo/gdb/testsuite/gdb.mi/basics.c",line="17"},frame={level="2",addr="0x0001078c",func="callee2",file="../../../devo/gdb/testsuite/gdb.mi/basics.c",fullname="/home/foo/bar/devo/gdb/testsuite/gdb.mi/basics.c",line="22"},frame={level="3",addr="0x000107b4",func="callee1",file="../../../devo/gdb/testsuite/gdb.mi/basics.c",fullname="/home/foo/bar/devo/gdb/testsuite/gdb.mi/basics.c",line="27"},frame={level="4",addr="0x000107e0",func="main",file="../../../devo/gdb/testsuite/gdb.mi/basics.c",fullname="/home/foo/bar/devo/gdb/testsuite/gdb.mi/basics.c",line="32"}]
'.
    parser token2CommandMappingBlock:[:token | GDBMI_stack_list_frames new ].
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:(events first result propertyAt:'stack') size == 5.

    "Created: / 19-06-2014 / 22:00:15 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 20-06-2014 / 09:18:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_thread_info_01
    | parser events result |

    parser := GDBMIParser 
            on:'1^done,threads=[{id="2",target-id="Thread 0xb7e14b90 (LWP 21257)",frame={level="0",addr="0xffffe410",func="__kernel_vsyscall",args=[]},state="running"},{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",frame={level="0",addr="0x0804891f",func="foo",args=[{name="i",value="10"}],file="/tmp/a.c",fullname="/tmp/a.c",line="158"},state="running"}],current-thread-id="1"'.        ''.
    parser token2CommandMappingBlock:[:token | GDBMI_thread_info new ].
    events := parser parseOutput.

    self assert:events size == 1.
    result := events first result.

    self assert: (result propertyAt: 'current-thread-id') = '1'.
    self assert: (result propertyAt:'threads') size == 2.
    self assert:((result propertyAt:'threads') first isKindOf: GDBThreadInfo).
    self assert: (result propertyAt:'threads') first id = 2.
    self assert: (result propertyAt:'threads') first targetId = 'Thread 0xb7e14b90 (LWP 21257)'.
    self assert: (result propertyAt:'threads') first state == GDBThreadStateRunning theOneAndOnlyInstance.

    "Created: / 08-03-2015 / 08:13:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_var_create_01
    | parser events result |

    parser := GDBMIParser 
            on:'14^done,name="var1",numchild="0",value="5",type="int",thread-id="1",has_more="0"'.
    parser token2CommandMappingBlock:[:token | GDBMI_var_create new ].
    events := parser parseOutput.

    self assert:events size == 1.
    result := events first result.

    self assert:((result value) isKindOf: GDBVariableObject).
    self assert: (result value) id = 'var1'.
    self assert:((result value) instVarNamed: #value) = '5'.

    "Created: / 19-03-2015 / 07:46:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 15-02-2018 / 09:18:55 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_command_var_create_02
    | parser events result |

    parser := GDBMIParser 
            on:'212^error,msg="Cannot instantiate printer for default visualizer"'.
    parser token2CommandMappingBlock:[:token | GDBMI_var_create new ].
    events := parser parseOutput.

    self assert:events size == 1.
    result := events first result.

    self assert: result isError.
    self assert: (result propertyAt: #msg) = 'Cannot instantiate printer for default visualizer'.
    self assert: result value isNil.

    "Created: / 02-02-2018 / 09:27:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"

!

test_command_var_update_01
    | parser events result changelist |

    parser := GDBMIParser 
            on:('3^done,changelist=[{name="var1",value="3",in_scope="true",
type_changed="false"}]' asStringCollection asStringWith:'').
    parser token2CommandMappingBlock:[:token | GDBMI_var_update new ].
    events := parser parseOutput.

    self assert:events size == 1.
    result := events first result.
    changelist := result propertyAt: #changelist.  

    self assert: changelist size == 1.
    self assert:(changelist first isKindOf: GDBVariableObjectChange).
    self assert:(changelist first id = 'var1').
    self assert:(changelist first value = '3').
    self assert:(changelist first isInvalid = false).
    self assert:(changelist first inScope = true)

    "Created: / 29-01-2018 / 20:21:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 12-02-2018 / 22:15:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_data_disassemble_01
    | parser  events  result  lines |

    parser := GDBMIParser 
            on:('8^done,asm_insns=[src_and_asm_line={line="3",file="factorial1.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/stx_8_0_0/build/jv/libgdbs/tests/c/factorial1.c",line_asm_insn=[{address="0x000000000000064a",func-name="factorial",offset="0",opcodes="55",inst="push   %rbp"},{address="0x000000000000064b",func-name="factorial",offset="1",opcodes="48 89 e5",inst="mov    %rsp,%rbp"},{address="0x000000000000064e",func-name="factorial",offset="4",opcodes="48 83 ec 10",inst="sub    $0x10,%rsp"},{address="0x0000000000000652",func-name="factorial",offset="8",opcodes="89 7d fc",inst="mov    %edi,-0x4(%rbp)"}]},src_and_asm_line={line="4",file="factorial1.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/stx_8_0_0/build/jv/libgdbs/tests/c/factorial1.c",line_asm_insn=[{address="0x0000000000000655",func-name="factorial",offset="11",opcodes="83 7d fc 01",inst="cmpl   $0x1,-0x4(%rbp)"},{address="0x0000000000000659",func-name="factorial",offset="15",opcodes="75 07",inst="jne    0x662 <factorial+24>"}]},src_and_asm_line={line="5",file="factorial1.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/stx_8_0_0/build/jv/libgdbs/tests/c/factorial1.c",line_asm_insn=[{address="0x000000000000065b",func-name="factorial",offset="17",opcodes="b8 01 00 00 00",inst="mov    $0x1,%eax"},{address="0x0000000000000660",func-name="factorial",offset="22",opcodes="eb 11",inst="jmp    0x673 <factorial+41>"}]},src_and_asm_line={line="6",file="factorial1.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/stx_8_0_0/build/jv/libgdbs/tests/c/factorial1.c",line_asm_insn=[]},src_and_asm_line={line="7",file="factorial1.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/stx_8_0_0/build/jv/libgdbs/tests/c/factorial1.c",line_asm_insn=[{address="0x0000000000000662",func-name="factorial",offset="24",opcodes="8b 45 fc",inst="mov    -0x4(%rbp),%eax"},{address="0x0000000000000665",func-name="factorial",offset="27",opcodes="83 e8 01",inst="sub    $0x1,%eax"},{address="0x0000000000000668",func-name="factorial",offset="30",opcodes="89 c7",inst="mov    %eax,%edi"},{address="0x000000000000066a",func-name="factorial",offset="32",opcodes="e8 db ff ff ff",inst="callq  0x64a <factorial>"},{address="0x000000000000066f",func-name="factorial",offset="37",opcodes="0f af 45 fc",inst="imul   -0x4(%rbp),%eax"}]},src_and_asm_line={line="8",file="factorial1.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/stx_8_0_0/build/jv/libgdbs/tests/c/factorial1.c",line_asm_insn=[]},src_and_asm_line={line="9",file="factorial1.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/stx_8_0_0/build/jv/libgdbs/tests/c/factorial1.c",line_asm_insn=[{address="0x0000000000000673",func-name="factorial",offset="41",opcodes="c9",inst="leaveq "},{address="0x0000000000000674",func-name="factorial",offset="42",opcodes="c3",inst="retq   "}]}]').
    parser token2CommandMappingBlock:[:token | GDBMI_data_disassemble new ].
    events := parser parseOutput.
    self assert:events size == 1.
    result := events first result.
    lines := result propertyAt:#'asm_insns'.
    self assert: lines isSequenceable.
    self assert:(lines first isKindOf:GDBInstructionsAndSourceLine).
    self assert:(lines first instructions isSequenceable).
    self assert:(lines first instructions first isKindOf: GDBInstruction).
    self assert:(lines first instructions first opcodes isByteArray).
    self assert:(lines first instructions first address isInteger).

    "Modified: / 03-07-2018 / 14:44:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_data_disassemble_02
    | parser  events  result  lines |

    parser := GDBMIParser 
            on:('67^done,asm_insns=[{address="0x000000000070b748",func-name="bee!!JITObjectReference>>#sourceOffset:",offset="0",opcodes="48 89 c6",inst="mov    %rax,%rsi"},{address="0x000000000070b74b",func-name="bee!!JITObjectReference>>#sourceOffset:",offset="3",opcodes="48 8b 44 24 08",inst="mov    0x8(%rsp),%rax"}]').
    parser token2CommandMappingBlock:[:token | GDBMI_data_disassemble new ].
    events := parser parseOutput.
    self assert:events size == 1.
    result := events first result.
    lines := result propertyAt:#'asm_insns'.
    self assert: lines isSequenceable.
    self assert:(lines first isKindOf:GDBInstruction).
    self assert:(lines first opcodes isByteArray).
    self assert:(lines first address isInteger).

    "Created: / 03-07-2018 / 17:12:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!GDBMIParserTests methodsFor:'tests - examples'!

test_simple_example_01
    | parser  events |

    parser := GDBMIParser 
            on:'^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x08048564",func="main",file="myprog.c",fullname="/home/nickrob/myprog.c",line="68",thread-groups=["i1"],times="0"}
(gdb)
'.
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:events first isCommandResultEvent.
    self assert:events first result status == CommandStatusDone.
    self 
        assert:((events first result propertyAt:'bkpt') at:'addr') = '0x08048564'

    "Created: / 30-05-2014 / 23:53:22 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 19-06-2014 / 21:55:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_simple_example_02
    | input parser events |


    input := '1234*running
1234^done
(gdb)
' readStream.
    parser := GDBMIParser on: input.
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:events first token == 1234.

    parser := GDBMIParser on: input.
    events := parser parseOutput.  
    self assert:events size == 1.
    self assert:events first isCommandResultEvent.
    self assert:events first result status == CommandStatusDone.
    self assert:events first token == 1234

    "Created: / 03-06-2014 / 00:50:55 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 27-02-2015 / 09:24:19 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_simple_session_01
    | parser  events |

    parser := GDBMIParser 
            on:'~"GNU gdb (GDB) 7.5-ubuntu\n"
~"Copyright (C) 2012 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.  Type \"show copying\"\nand \"show warranty\" for details.\n"
~"This GDB was configured as \"x86_64-linux-gnu\".\nFor bug reporting instructions, please see:\n"
~"<http://www.gnu.org/software/gdb/bugs/>...\n"
~"Reading symbols from /home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/jv/libgdbs/tests/c/factorial..."
~"done.\n"
(gdb) 
'.
    events := parser parseOutput.
    self assert:events size == 7.
    self assert:events first isConsoleOutputEvent.
    self assert:events first value = 'GNU gdb (GDB) 7.5-ubuntu
'.
    self assert:events last isConsoleOutputEvent.
    self assert:events last value = 'done.
'.
    parser := GDBMIParser 
            on:'^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400556",func="main",file="factorial.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/jv/libgdbs/tests/c/factorial.c",line="12",times="0",original-location="main"}
(gdb) 
'.
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:events first result status == CommandStatusDone.
    self assert:((events first result propertyAt:'bkpt') at:'addr') 
                = '0x0000000000400556'.
    parser := GDBMIParser 
            on:'~"Breakpoint 2 at 0x400527: file factorial.c, line 4.\n"
=breakpoint-created,bkpt={number="2",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400527",func="factorial",file="factorial.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/jv/libgdbs/tests/c/factorial.c",line="4",times="0",original-location="factorial"}
^done
(gdb)
'.
    events := parser parseOutput.
    self assert:events size == 2.
    self assert:events first isConsoleOutputEvent.
    self assert:events first value 
                = 'Breakpoint 2 at 0x400527: file factorial.c, line 4.
'.
    self assert:events second isNotificationEvent.
    self assert:events second class == GDBBreakpointCreatedEvent.
    self assert:events second type = 'breakpoint-created'.
    self assert:events second breakpoints size == 1.
    self assert:events second breakpoints first class == GDBBreakpoint.
    self assert:events second breakpoints first number = '2'.
    self assert:events second breakpoints first file = 'factorial.c'.
    parser := GDBMIParser 
            on:'&"info break\n"
~"Num     Type           Disp Enb Address            What\n"
~"1       breakpoint     keep y   0x0000000000400556 in main at factorial.c:12\n"
~"2       breakpoint     keep y   0x0000000000400527 in factorial at factorial.c:4\n"
^done
(gdb)
'.
    events := parser parseOutput.
    self assert:events size == 5.
    self assert:events first isLogOutputEvent.
    self assert:events first value = 'info break
'.
    self assert:events second isConsoleOutputEvent.
    self assert:events third isConsoleOutputEvent.
    self assert:events fourth isConsoleOutputEvent.
    self assert:events fifth isCommandResultEvent.
    parser := GDBMIParser 
            on:'&"run\n"
~"Starting program: /home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/jv/libgdbs/tests/c/factorial \n"
=thread-group-started,id="i1",pid="17240"
=thread-created,id="1",group-id="i1"
^running
*running,thread-id="all"
(gdb)
'.
    "/ Note that parser yields anb output after first async event,
    "/ so to parse all events in the above input we have to call
    "/ parseOutput couple times
    events := parser parseOutput.
    self assert:events size == 3.
    self assert:events first isLogOutputEvent.
    self assert:events second isConsoleOutputEvent.
    self assert:events third type = 'thread-group-started'.
    self assert:events third threadGroupId = 'i1'
.
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:events first type = 'thread-created'.
    self assert:events first threadId = 1.

    events := parser parseOutput.
    self assert:events size == 2.
    self assert:events first isCommandResultEvent.
    self assert:events second type = 'running'.

    "Created: / 01-06-2014 / 22:57:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 27-11-2017 / 20:29:34 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_simple_session_01_crlf
    | parser  events |

    parser := GDBMIParser 
            on:('~"GNU gdb (GDB) 7.5-ubuntu\n"
~"Copyright (C) 2012 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.  Type \"show copying\"\nand \"show warranty\" for details.\n"
~"This GDB was configured as \"x86_64-linux-gnu\".\nFor bug reporting instructions, please see:\n"
~"<http://www.gnu.org/software/gdb/bugs/>...\n"
~"Reading symbols from /home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/jv/libgdbs/tests/c/factorial..."
~"done.\n"
(gdb) 
' asStringCollection asStringWith: (Character return, Character lf)).
    events := parser parseOutput.
    self assert:events size == 7.
    self assert:events first isConsoleOutputEvent.
    self assert:events first value = 'GNU gdb (GDB) 7.5-ubuntu
'.
    self assert:events last isConsoleOutputEvent.
    self assert:events last value = 'done.
'.
    parser := GDBMIParser 
            on:'^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400556",func="main",file="factorial.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/jv/libgdbs/tests/c/factorial.c",line="12",times="0",original-location="main"}
(gdb) 
'.
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:events first result status == CommandStatusDone.
    self assert:((events first result propertyAt:'bkpt') at:'addr') 
                = '0x0000000000400556'.
    parser := GDBMIParser 
            on:'~"Breakpoint 2 at 0x400527: file factorial.c, line 4.\n"
=breakpoint-created,bkpt={number="2",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400527",func="factorial",file="factorial.c",fullname="/home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/jv/libgdbs/tests/c/factorial.c",line="4",times="0",original-location="factorial"}
^done
(gdb)
'.
    events := parser parseOutput.
    self assert:events size == 2.
    self assert:events first isConsoleOutputEvent.
    self assert:events first value 
                = 'Breakpoint 2 at 0x400527: file factorial.c, line 4.
'.
    self assert:events second isNotificationEvent.
    self assert:events second class == GDBBreakpointCreatedEvent.
    self assert:events second type = 'breakpoint-created'.
    self assert:events second breakpoints size == 1.
    self assert:events second breakpoints first class == GDBBreakpoint.
    self assert:events second breakpoints first number = '2'.
    self assert:events second breakpoints first file = 'factorial.c'.
    parser := GDBMIParser 
            on:'&"info break\n"
~"Num     Type           Disp Enb Address            What\n"
~"1       breakpoint     keep y   0x0000000000400556 in main at factorial.c:12\n"
~"2       breakpoint     keep y   0x0000000000400527 in factorial at factorial.c:4\n"
^done
(gdb)
'.
    events := parser parseOutput.
    self assert:events size == 5.
    self assert:events first isLogOutputEvent.
    self assert:events first value = 'info break
'.
    self assert:events second isConsoleOutputEvent.
    self assert:events third isConsoleOutputEvent.
    self assert:events fourth isConsoleOutputEvent.
    self assert:events fifth isCommandResultEvent.
    parser := GDBMIParser 
            on:'&"run\n"
~"Starting program: /home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/jv/libgdbs/tests/c/factorial \n"
=thread-group-started,id="i1",pid="17240"
=thread-created,id="1",group-id="i1"
^running
*running,thread-id="all"
(gdb)
'.
    "/ Note that parser yields anb output after first async event,
    "/ so to parse all events in the above input we have to call
    "/ parseOutput couple times
    events := parser parseOutput.
    self assert:events size == 3.
    self assert:events first isLogOutputEvent.
    self assert:events second isConsoleOutputEvent.
    self assert:events third type = 'thread-group-started'.
    self assert:events third threadGroupId = 'i1'
.
    events := parser parseOutput.
    self assert:events size == 1.
    self assert:events first type = 'thread-created'.
    self assert:events first threadId = 1.

    events := parser parseOutput.
    self assert:events size == 2.
    self assert:events first isCommandResultEvent.
    self assert:events second type = 'running'.

    "Created: / 12-01-2018 / 08:42:15 / jv"
! !

!GDBMIParserTests methodsFor:'tests - values'!

test_bytearray_01
    self assert:(GDBMIParser on:'"48 89 e5"') parseByteArray =#[16r48 16r89 16re5].

    "Created: / 22-06-2018 / 11:22:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_list_01
    | list |

    list := (GDBMIParser on:'[]') parseList.
    self assert:list class == Array.
    self assert:list isEmpty.

    "Created: / 18-06-2014 / 07:13:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_list_02
    | list |

    list := (GDBMIParser on:'["i1","i2"]') parseList.
    self assert:list size == 2.
    self assert:list first = 'i1'.
    self assert:list second = 'i2'.

    "Created: / 19-06-2014 / 09:24:38 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_list_03
    | list |

    list := (GDBMIParser on:'[["1","2"],"i2"]') parseList.
    self assert:list size == 2.
    self assert:list first size == 2.
    self assert:list first first = '1'.
    self assert:list first second = '2'.
    self assert:list second = 'i2'.

    "Created: / 19-06-2014 / 09:25:44 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_list_04
    | list |

    list := (GDBMIParser 
            on:'[bkpt={number="1",line="2"},bkpt={number="2",line="10"}]') parseList.
    self assert:list size == 2.
    self assert:list first size == 2.
    self assert:(list first at:'number') = '1'.
    self assert:(list first at:'line') = '2'.
    self assert:list second size == 2.
    self assert:(list second at:'number') = '2'.
    self assert:(list second at:'line') = '10'.

    "Created: / 19-06-2014 / 09:29:09 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_tuple_01
    | tuple |

    tuple := (GDBMIParser on:'{}') parseTuple.
    self assert:tuple class == Dictionary.
    self assert:tuple isEmpty.

    "Created: / 14-06-2014 / 02:24:18 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 18-06-2014 / 07:03:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_tuple_02a
    | tuple |

    tuple := (GDBMIParser on:'{p1="XXX"}') parseTuple.
    self assert:tuple class == Dictionary.
    self assert:tuple size == 1.
    self assert:(tuple at:'p1') = 'XXX'.

    "Created: / 18-06-2014 / 07:12:15 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_tuple_02b
    | tuple |

    tuple := (GDBMIParser on:'{p1=["i1","i2"]}') parseTuple.
    self assert:tuple class == Dictionary.
    self assert:tuple size == 1.
    self assert:(tuple at:'p1') asArray = #( 'i1' 'i2' )

    "Created: / 18-06-2014 / 07:09:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_tuple_02c
    | tuple |

    tuple := (GDBMIParser on:'{p1={x="10",y="10"}}') parseTuple.
    self assert:tuple class == Dictionary.
    self assert:tuple size == 1.
    self assert:(tuple at:'p1') class == Dictionary.
    self assert:((tuple at:'p1') at:'x') = '10'.
    self assert:((tuple at:'p1') at:'y') = '10'.

    "Created: / 18-06-2014 / 07:12:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_tuple_03a
    | tuple |

    tuple := (GDBMIParser on:'{"c"}') parseTuple.
    self assert:tuple = #('c').

    "Created: / 06-07-2017 / 07:45:19 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_tuple_03b
    | tuple |

    tuple := (GDBMIParser on:'{"line1","line2"}') parseTuple.
    self assert:tuple = #('line1' 'line2').

    "Created: / 06-07-2017 / 08:21:39 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!GDBMIParserTests methodsFor:'tests - values - typed'!

test_typed_boolean
    self assert:(GDBMIParser on:'"y"') parseValueAsBoolean = true.
    self assert:(GDBMIParser on:'"n"') parseValueAsBoolean = false.
    self assert:(GDBMIParser on:'"yX"') parseValueAsBoolean = 'yX'.
    self assert:(GDBMIParser on:'"nM"') parseValueAsBoolean = 'nM'.
    self assert:(GDBMIParser on:'"1"') parseValueAsBoolean = true.
    self assert:(GDBMIParser on:'"0"') parseValueAsBoolean = false.
    self assert:(GDBMIParser on:'"1X"') parseValueAsBoolean = '1X'.
    self assert:(GDBMIParser on:'"0M"') parseValueAsBoolean = '0M'.
    self assert:(GDBMIParser on:'"no"') parseValueAsBoolean = 'no'.
    self assert:(GDBMIParser on:'"12xyz"') parseValueAsBoolean = '12xyz'.
    self assert:(GDBMIParser on:'"true"') parseValueAsBoolean = true.
    self assert:(GDBMIParser on:'"false"') parseValueAsBoolean = false.
    self assert:(GDBMIParser on:'"invalid"') parseValueAsBoolean = 'invalid'.
    self assert:(GDBMIParser on:'"truelike"') parseValueAsBoolean = 'truelike'.
    self assert:(GDBMIParser on:'"falselike"') parseValueAsBoolean = 'falselike'.

    "Created: / 18-06-2014 / 23:38:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 12-02-2018 / 22:21:13 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_typed_integer
    self assert:(GDBMIParser on:'"123"') parseValueAsInteger == 123.
    self assert:(GDBMIParser on:'"0"') parseValueAsInteger == 0.
    self assert:(GDBMIParser on:'"no"') parseValueAsInteger = 'no'.
    self assert:(GDBMIParser on:'"12xyz"') parseValueAsInteger = '12xyz'.
    self assert:(GDBMIParser on:'"0xFaB"') parseValueAsInteger == 16rFaB. 
    self assert:(GDBMIParser on:'"0x123"') parseValueAsInteger == 16r123.

    "Created: / 19-06-2014 / 09:39:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 03-07-2018 / 14:50:22 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_typed_list_01a
    | object |

    object := (GDBMIParser on:'["123","456"]') parseValueAsListOf:Integer.
    self assert:object size == 2.
    self assert:object first == 123.
    self assert:object second == 456.

    "Created: / 19-06-2014 / 09:40:55 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_typed_list_02a
    | object |

    object := (GDBMIParser on:'["123","456"]') parseValueAsListOf:String.
    self assert:object size == 2.
    self assert:object first = '123'.
    self assert:object second = '456'.

    "Created: / 19-06-2014 / 09:41:15 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_typed_list_03a
    | objectDesc stringDesc switchDesc list |

    objectDesc := GDBMAContainer forClass:self class.
    objectDesc define:#prop1 as:Integer.
    objectDesc define:#prop2 as:Boolean.        

    stringDesc := Magritte::MAStringDescription new.

    switchDesc := GDBMADescriptionSwitch forTag: 'object'  use: objectDesc forTag: nil  use: stringDesc.

    list := (GDBMIParser on:'[object={prop1="123",prop2="n"},"xxx"]') parseValueAsListOf: nil describedBy: switchDesc.
    self assert: list size == 2.
    self assert: (list first isKindOf: self class).
    self assert: (list first prop1 = 123).
    self assert: (list first prop2 = false).
    self assert: (list second = 'xxx').

    "Created: / 03-07-2018 / 16:35:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_typed_object_01a
    | descriptor  object |

    descriptor := GDBMAContainer forClass:self class.
    descriptor define:#prop1 as:Integer.
    descriptor define:#prop2 as:Boolean.
    object := (GDBMIParser on:'{prop1="123",prop2="y"}') 
            parseValueAsInstanceOf:self class
            describedBy:descriptor.
    self assert:object class == self class.
    self assert:object prop1 == 123.
    self assert:object prop2.
    self assert:object properties isEmptyOrNil

    "Created: / 18-06-2014 / 09:01:14 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 23-09-2014 / 22:37:54 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_typed_object_01b
    | descriptor  object |

    descriptor := GDBMAContainer forClass:self class.
    descriptor 
        define:#prop1
        as:Array
        of:Integer.
    object := (GDBMIParser on:'{prop1=["123"]}') 
            parseValueAsInstanceOf:self class
            describedBy:descriptor.
    self assert:object class == self class.
    self assert:object prop1 size == 1.
    self assert:object prop1 first = 123.
    self assert:object properties isEmptyOrNil

    "Created: / 19-06-2014 / 09:42:52 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 23-09-2014 / 23:01:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_typed_object_02a
    | descriptor  object |

    descriptor := GDBMAContainer forClass:self class.
    descriptor define:#prop1 as:Integer.
    descriptor define:#prop2 as:Boolean.
    object := (GDBMIParser 
            on:'[{prop1="123",prop2="y"},{prop1="789",prop2="n"}]') 
                parseValueAsListOf:self class
                describedBy:descriptor.
    self assert:object size == 2.
    self assert:object first class == self class.
    self assert:object first prop1 == 123.
    self assert:object first prop2.
    self assert:object first properties isEmptyOrNil.
    self assert:object second class == self class.
    self assert:object second prop1 == 789.
    self assert:object second prop2 not.
    self assert:object second properties isEmptyOrNil

    "Created: / 17-09-2014 / 07:42:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 23-09-2014 / 23:01:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_typed_object_02b
    | descriptor  object |

    descriptor := GDBMAContainer forClass:self class.
    descriptor define:#prop1 as:Integer.
    descriptor define:#prop2 as:Boolean.
    object := (GDBMIParser 
            on:'[object={prop1="123",prop2="y"},object={prop1="789",prop2="n"}]') 
                parseValueAsListOf:self class
                describedBy:descriptor.
    self assert:object size == 2.
    self assert:object first class == self class.
    self assert:object first prop1 == 123.
    self assert:object first prop2.
    self assert:object first properties isEmptyOrNil.
    self assert:object second class == self class.
    self assert:object second prop1 == 789.
    self assert:object second prop2 not.
    self assert:object second properties isEmptyOrNil

    "Created: / 17-09-2014 / 07:43:01 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 23-09-2014 / 23:01:40 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

test_typed_object_03a
    | descriptor  object |

    descriptor := GDBMAContainer forClass:self class.
    descriptor add: 
        (Magritte::MASingleOptionDescription new
            optionsAndLabels: (Array with: 1 -> 'one' with: 2 -> 'two' with: #more -> 'more');
            accessor: (GDBMAPropertyAccessor forPropertyNamed: 'prop1')).

    object := (GDBMIParser on:'{prop1="one"}') 
                parseValueAsInstanceOf:self class
                describedBy:descriptor.
    self assert:object class == self class.
    self assert:object prop1 == 1.

    object := (GDBMIParser on:'{prop1="more"}') 
                parseValueAsInstanceOf:self class
                describedBy:descriptor.
    self assert:object class == self class.
    self assert:object prop1 == #more.         

    self should: [ 
    object := (GDBMIParser on:'{prop1="fail"}') 
                parseValueAsInstanceOf:self class
                describedBy:descriptor.
    ] raise: Magritte::MAReadError

    "Created: / 25-09-2014 / 08:33:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 27-11-2017 / 20:30:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!GDBMIParserTests class methodsFor:'documentation'!

version_HG

    ^ '$Changeset: <not expanded> $'
! !