tests/GDBMIParserTests.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Tue, 18 Jun 2019 11:04:46 +0100
changeset 193 2aa0074479d9
parent 174 18ef81a3fee5
child 208 b0d2028189fa
permissions -rw-r--r--
Add (utility) `GDBProcess >> gdbCommandParseAndValidate:` to parse and validate given GDB command. This is used both by `GDBLocalProcess` and settings UI.

"
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'.
    self assert:command operation = 'break'.
    self assert:command runOnBackground not.

    command := (GDBMIParser on:'cont   &   ') parseCommand.
    self assert:command isCLICommand.
    self assert:command token isNil.
    self assert:command value = 'cont   '.
    self assert:command operation = 'continue'.
    self assert:command runOnBackground.

    "Created: / 24-06-2014 / 23:21:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 28-01-2019 / 23:05: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> $'
! !