diff options
Diffstat (limited to 'gdb/testsuite')
-rw-r--r-- | gdb/testsuite/ChangeLog | 18 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/max-depth.c | 232 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/max-depth.exp | 165 | ||||
-rw-r--r-- | gdb/testsuite/gdb.fortran/max-depth.exp | 41 | ||||
-rw-r--r-- | gdb/testsuite/gdb.fortran/max-depth.f90 | 42 | ||||
-rw-r--r-- | gdb/testsuite/gdb.go/max-depth.exp | 40 | ||||
-rw-r--r-- | gdb/testsuite/gdb.go/max-depth.go | 41 | ||||
-rw-r--r-- | gdb/testsuite/gdb.modula2/max-depth.c | 45 | ||||
-rw-r--r-- | gdb/testsuite/gdb.modula2/max-depth.exp | 37 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-format-string.exp | 21 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-nested-maps.c | 130 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-nested-maps.exp | 238 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-nested-maps.py | 135 | ||||
-rw-r--r-- | gdb/testsuite/lib/gdb.exp | 30 |
14 files changed, 1215 insertions, 0 deletions
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index b6c3618..6d9ac5a 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,21 @@ +2019-04-29 Andrew Burgess <andrew.burgess@embecosm.com> + Richard Bunt <richard.bunt@arm.com> + + * gdb.base/max-depth.c: New file. + * gdb.base/max-depth.exp: New file. + * gdb.python/py-nested-maps.c: New file. + * gdb.python/py-nested-maps.exp: New file. + * gdb.python/py-nested-maps.py: New file. + * gdb.python/py-format-string.exp (test_max_depth): New proc. + (test_all_common): Call test_max_depth. + * gdb.fortran/max-depth.exp: New file. + * gdb.fortran/max-depth.f90: New file. + * gdb.go/max-depth.exp: New file. + * gdb.go/max-depth.go: New file. + * gdb.modula2/max-depth.exp: New file. + * gdb.modula2/max-depth.c: New file. + * lib/gdb.exp (get_print_expr_at_depths): New proc. + 2019-04-29 Tom de Vries <tdevries@suse.de> * lib/opencl.exp (skip_opencl_tests): Add missing "with" in regexp. diff --git a/gdb/testsuite/gdb.base/max-depth.c b/gdb/testsuite/gdb.base/max-depth.c new file mode 100644 index 0000000..3c57249 --- /dev/null +++ b/gdb/testsuite/gdb.base/max-depth.c @@ -0,0 +1,232 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +struct s1 +{ + int x; + int y; +} s1; + +struct s2 +{ + int x; + int y; + struct + { + int z; + int a; + }; +} s2; + +struct s3 +{ + int x; + int y; + struct + { + int z; + int a; + struct + { + int b; + int c; + }; + }; +} s3; + +struct s4 +{ + int x; + int y; + struct + { + int x; + int y; + struct + { + int x; + int y; + } l2; + } l1; +} s4; + +struct s5 +{ + union + { + int raw[3]; + struct + { + int x; + int y; + int z; + }; + }; +} s5; + +typedef struct +{ + union + { + int raw[3]; + struct + { + int x; + int y; + int z; + }; + }; +} s6_t; + +s6_t s6; + +struct s7 +{ + struct + { + int x; + int y; + }; + struct + { + int z; + int a; + }; + struct + { + int b; + int c; + }; +} s7; + +struct s8 +{ + int x; + int y; + struct + { + int z; + int a; + struct + { + int b; + int c; + }; + } d1; +} s8; + +struct s9 +{ + int x; + int y; + struct + { + int k; + int j; + struct + { + int z; + int a; + struct + { + int b; + int c; + }; + } d1; + }; + struct + { + int z; + int a; + struct + { + int b; + int c; + }; + } d2; +} s9; + +struct s10 +{ + int x[10]; + int y; + struct + { + int k[10]; + int j; + struct + { + int z; + int a; + struct + { + int b[10]; + int c; + }; + } d1; + }; + struct + { + int z; + int a; + struct + { + int b[10]; + int c; + }; + } d2; +} s10; + +struct s11 +{ + int x; + char s[10]; + struct + { + int z; + int a; + }; +} s11; + +/* The following are C++ inheritance testing. */ +#ifdef __cplusplus + +/* This is non-virtual inheritance. */ +struct C1 { int c1 = 1; } c1; +struct C2 { int c2 = 2; } c2; +struct C3 : C2 { int c3 = 3; } c3; +struct C4 { int c4 = 4; } c4; +struct C5 : C4 { int c5 = 5; } c5; +struct C6 : C5 { int c6 = 6; } c6; +struct C7 : C1, C3, C6 { int c7 = 7; } c7; + +/* This is virtual inheritance. */ +struct V1 { int v1 = 1; } v1; +struct V2 : virtual V1 { int v2 = 2; } v2; +struct V3 : virtual V1 { int v3 = 3; } v3; +struct V4 : virtual V2 { int v4 = 4; } v4; +struct V5 : virtual V2 { int v5 = 1; } v5; +struct V6 : virtual V2, virtual V3 { int v6 = 1; } v6; +struct V7 : virtual V4, virtual V5, virtual V6 { int v7 = 1; } v7; + +#endif /* __cplusplus */ + +int +main () +{ + return 0; +} diff --git a/gdb/testsuite/gdb.base/max-depth.exp b/gdb/testsuite/gdb.base/max-depth.exp new file mode 100644 index 0000000..b3b7876 --- /dev/null +++ b/gdb/testsuite/gdb.base/max-depth.exp @@ -0,0 +1,165 @@ +# Copyright 2019 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Tests GDB's handling of 'set print max-depth'. + +# Only test C++ if we are able. Always use C. +if { [skip_cplus_tests] || [get_compiler_info "c++"] } { + set lang {c} +} else { + set lang {c c++} +} + +foreach l $lang { + set dir "$l" + remote_exec host "rm -rf [standard_output_file ${dir}]" + remote_exec host "mkdir -p [standard_output_file ${dir}]" +} + +proc compile_and_run_tests { lang } { + global testfile + global srcfile + global binfile + global hex + + standard_testfile .c + + # Create the additional flags. + set flags "debug" + lappend flags $lang + + set dir "$lang" + set binfile [standard_output_file ${dir}/${testfile}] + if { [prepare_for_testing "failed to prepare" "${binfile}" "${srcfile}" "${flags}"] } { + return 0 + } + + # Advance to main. + if { ![runto_main] } then { + fail "can't run to main" + return 0 + } + + # The max-depth setting has no effect as the anonymous scopes are + # ignored and the members are aggregated into the parent scope. + gdb_print_expr_at_depths "s1" {"{...}" \ + "{x = 0, y = 0}"\ + "{x = 0, y = 0}"} + + gdb_print_expr_at_depths "s2" {"{...}" \ + "{x = 0, y = 0, {z = 0, a = 0}}" \ + "{x = 0, y = 0, {z = 0, a = 0}}"} + + gdb_print_expr_at_depths "s3" {"{...}" \ + "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}" \ + "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}" \ + "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}"} + + # Increasing max-depth unfurls more of the object. + gdb_print_expr_at_depths "s4" {"{...}" \ + "{x = 0, y = 0, l1 = {...}}" \ + "{x = 0, y = 0, l1 = {x = 0, y = 0, l2 = {...}}}" \ + "{x = 0, y = 0, l1 = {x = 0, y = 0, l2 = {x = 0, y = 0}}}"} + + # Check handling of unions, in this case 'raw' is printed instead of + # just {...} as this is not useful. + gdb_print_expr_at_depths "s5" {"{...}" \ + "{{raw = {...}, {x = 0, y = 0, z = 0}}}" \ + "{{raw = \\{0, 0, 0\\}, {x = 0, y = 0, z = 0}}}"} + + # Check handling of typedefs. + gdb_print_expr_at_depths "s6" {"{...}" \ + "{{raw = {...}, {x = 0, y = 0, z = 0}}}" \ + "{{raw = \\{0, 0, 0\\}, {x = 0, y = 0, z = 0}}}"} + + # Multiple anonymous structures in parallel. + gdb_print_expr_at_depths "s7" {"{...}" \ + "{{x = 0, y = 0}, {z = 0, a = 0}, {b = 0, c = 0}}" \ + "{{x = 0, y = 0}, {z = 0, a = 0}, {b = 0, c = 0}}"} + + # Flip flop between named and anonymous. Expected to unfurl to the + # first non-anonymous type. + gdb_print_expr_at_depths "s8" {"{...}" \ + "{x = 0, y = 0, d1 = {...}}" \ + "{x = 0, y = 0, d1 = {z = 0, a = 0, {b = 0, c = 0}}}"} + + # Imbalanced tree, this will unfurl one size more than the other as + # one side has more anonymous levels. + gdb_print_expr_at_depths "s9" {"{...}" \ + "{x = 0, y = 0, {k = 0, j = 0, d1 = {...}}, d2 = {...}}" \ + "{x = 0, y = 0, {k = 0, j = 0, d1 = {z = 0, a = 0, {b = 0, c = 0}}}, d2 = {z = 0, a = 0, {b = 0, c = 0}}}"} + + # Arrays are treated as an extra level, while scalars are not. + gdb_print_expr_at_depths "s10" {"{...}" \ + "{x = {...}, y = 0, {k = {...}, j = 0, d1 = {...}}, d2 = {...}}" \ + "{x = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, y = 0, {k = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, j = 0, d1 = {z = 0, a = 0, {b = {...}, c = 0}}}, d2 = {z = 0, a = 0, {b = {...}, c = 0}}}" \ + "{x = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, y = 0, {k = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, j = 0, d1 = {z = 0, a = 0, {b = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, c = 0}}}, d2 = {z = 0, a = 0, {b = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, c = 0}}}"} + + # Strings are treated as scalars. + gdb_print_expr_at_depths "s11" {"{...}" \ + "{x = 0, s = \"\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\", {z = 0, a = 0}}"} + + + if { $lang == "c++" } { + gdb_print_expr_at_depths "c1" {"{...}" \ + "{c1 = 1}" } + gdb_print_expr_at_depths "c2" { "{...}" "{c2 = 2}" } + gdb_print_expr_at_depths "c3" { "{...}" \ + "{<C2> = {...}, c3 = 3}" \ + "{<C2> = {c2 = 2}, c3 = 3}" } + gdb_print_expr_at_depths "c4" { "{...}" "{c4 = 4}" } + gdb_print_expr_at_depths "c5" { "{...}" \ + "{<C4> = {...}, c5 = 5}" \ + "{<C4> = {c4 = 4}, c5 = 5}" } + gdb_print_expr_at_depths "c6" { "{...}" \ + "{<C5> = {...}, c6 = 6}" \ + "{<C5> = {<C4> = {...}, c5 = 5}, c6 = 6}" \ + "{<C5> = {<C4> = {c4 = 4}, c5 = 5}, c6 = 6}" } + gdb_print_expr_at_depths "c7" { "{...}" \ + "{<C1> = {...}, <C3> = {...}, <C6> = {...}, c7 = 7}" \ + "{<C1> = {c1 = 1}, <C3> = {<C2> = {...}, c3 = 3}, <C6> = {<C5> = {...}, c6 = 6}, c7 = 7}" \ + "{<C1> = {c1 = 1}, <C3> = {<C2> = {c2 = 2}, c3 = 3}, <C6> = {<C5> = {<C4> = {...}, c5 = 5}, c6 = 6}, c7 = 7}" \ + "{<C1> = {c1 = 1}, <C3> = {<C2> = {c2 = 2}, c3 = 3}, <C6> = {<C5> = {<C4> = {c4 = 4}, c5 = 5}, c6 = 6}, c7 = 7}" } + + gdb_print_expr_at_depths "v1" [list "{...}" "{v1 = 1}" ] + gdb_print_expr_at_depths "v2" [list "{...}" \ + "{<V1> = {...}, _vptr.V2 = $hex <VTT for V2>, v2 = 2}" \ + "{<V1> = {v1 = 1}, _vptr.V2 = $hex <VTT for V2>, v2 = 2}" ] + gdb_print_expr_at_depths "v3" [list "{...}" \ + "{<V1> = {...}, _vptr.V3 = $hex <VTT for V3>, v3 = 3}" \ + "{<V1> = {v1 = 1}, _vptr.V3 = $hex <VTT for V3>, v3 = 3}" ] + gdb_print_expr_at_depths "v4" [list "{...}" \ + "{<V2> = {...}, _vptr.V4 = $hex <vtable for V4\[^>\]+>, v4 = 4}" \ + "{<V2> = {<V1> = {...}, _vptr.V2 = $hex <VTT for V4>, v2 = 2}, _vptr.V4 = $hex <vtable for V4\[^>\]+>, v4 = 4}" \ + "{<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <VTT for V4>, v2 = 2}, _vptr.V4 = $hex <vtable for V4\[^>\]+>, v4 = 4}" ] + gdb_print_expr_at_depths "v5" [list "{...}" \ + "{<V2> = {...}, _vptr.V5 = $hex <vtable for V5\[^>\]+>, v5 = 1}" \ + "{<V2> = {<V1> = {...}, _vptr.V2 = $hex <VTT for V5>, v2 = 2}, _vptr.V5 = $hex <vtable for V5\[^>\]+>, v5 = 1}" \ + "{<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <VTT for V5>, v2 = 2}, _vptr.V5 = $hex <vtable for V5\[^>\]+>, v5 = 1}" ] + gdb_print_expr_at_depths "v6" [list "{...}" \ + "{<V2> = {...}, <V3> = {...}, _vptr.V6 = $hex <vtable for V6\[^>\]+>, v6 = 1}" \ + "{<V2> = {<V1> = {...}, _vptr.V2 = $hex <vtable for V6\[^>\]+>, v2 = 2}, <V3> = {_vptr.V3 = $hex <VTT for V6>, v3 = 3}, _vptr.V6 = $hex <vtable for V6\[^>\]+>, v6 = 1}" \ + "{<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <vtable for V6\[^>\]+>, v2 = 2}, <V3> = {_vptr.V3 = $hex <VTT for V6>, v3 = 3}, _vptr.V6 = $hex <vtable for V6\[^>\]+>, v6 = 1}" ] + gdb_print_expr_at_depths "v7" [list "{...}" \ + "{<V4> = {...}, <V5> = {...}, <V6> = {...}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" \ + "{<V4> = {<V2> = {...}, _vptr.V4 = $hex <vtable for V7\[^>\]+>, v4 = 4}, <V5> = {_vptr.V5 = $hex <vtable for V7\[^>\]+>, v5 = 1}, <V6> = {<V3> = {...}, _vptr.V6 = $hex <vtable for V7\[^>\]+>, v6 = 1}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" \ + "{<V4> = {<V2> = {<V1> = {...}, _vptr.V2 = $hex <vtable for V7\[^>\]+>, v2 = 2}, _vptr.V4 = $hex <vtable for V7\[^>\]+>, v4 = 4}, <V5> = {_vptr.V5 = $hex <vtable for V7\[^>\]+>, v5 = 1}, <V6> = {<V3> = {_vptr.V3 = $hex <VTT for V7>, v3 = 3}, _vptr.V6 = $hex <vtable for V7\[^>\]+>, v6 = 1}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" \ + "{<V4> = {<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <vtable for V7\[^>\]+>, v2 = 2}, _vptr.V4 = $hex <vtable for V7\[^>\]+>, v4 = 4}, <V5> = {_vptr.V5 = $hex <vtable for V7\[^>\]+>, v5 = 1}, <V6> = {<V3> = {_vptr.V3 = $hex <VTT for V7>, v3 = 3}, _vptr.V6 = $hex <vtable for V7\[^>\]+>, v6 = 1}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" ] + } +} + +foreach_with_prefix l $lang { + compile_and_run_tests $l +} diff --git a/gdb/testsuite/gdb.fortran/max-depth.exp b/gdb/testsuite/gdb.fortran/max-depth.exp new file mode 100644 index 0000000..8b0bb80 --- /dev/null +++ b/gdb/testsuite/gdb.fortran/max-depth.exp @@ -0,0 +1,41 @@ +# Copyright 2019 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This file tests GDB's handling of 'set print max-depth' for nested +# fortran types. + +load_lib "fortran.exp" + +if { [skip_fortran_tests] } { continue } + +standard_testfile .f90 + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug f90}] } { + return -1 +} + +if { ![runto MAIN__] } { + perror "Could not run to breakpoint `MAIN__'." + continue +} + +gdb_breakpoint [gdb_get_line_number "stop-here"] +gdb_continue_to_breakpoint "stop-here" ".*stop-here.*" + +gdb_print_expr_at_depths "var" {"\\(\\.\\.\\.\\)" \ + "\\( d = \\(\\.\\.\\.\\) \\)" \ + "\\( d = \\( string = 'abcde', c = \\(\\.\\.\\.\\) \\) \\)" \ + "\\( d = \\( string = 'abcde', c = \\( b = \\(\\.\\.\\.\\), array = \\(\\.\\.\\.\\) \\) \\) \\)" \ + "\\( d = \\( string = 'abcde', c = \\( b = \\( a = 1 \\), array = \\(0, 0, 0, 0, 0\\) \\) \\) \\)" } diff --git a/gdb/testsuite/gdb.fortran/max-depth.f90 b/gdb/testsuite/gdb.fortran/max-depth.f90 new file mode 100644 index 0000000..9b6f90c --- /dev/null +++ b/gdb/testsuite/gdb.fortran/max-depth.f90 @@ -0,0 +1,42 @@ +! Copyright 2019 Free Software Foundation, Inc. +! +! This program is free software; you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation; either version 2 of the License, or +! (at your option) any later version. +! +! This program 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 General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with this program. If not, see <http://www.gnu.org/licenses/>. + +program max_depth_test + type :: s1 + integer :: a + end type s1 + + type :: s2 + type (s1) :: b + integer :: array (5) + end type s2 + + type :: s3 + character(5) :: string + type (s2) :: c + end type s3 + + type :: s4 + type (s3) :: d + end type s4 + + logical :: l + type (s4) :: var + + var%d%c%b%a = 1 + var%d%c%array = 0 + var%d%string = "abcde" + l = .FALSE. ! stop-here +end program max_depth_test diff --git a/gdb/testsuite/gdb.go/max-depth.exp b/gdb/testsuite/gdb.go/max-depth.exp new file mode 100644 index 0000000..0f8ba9a --- /dev/null +++ b/gdb/testsuite/gdb.go/max-depth.exp @@ -0,0 +1,40 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2019 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Test the use of 'set prinnt max-depth' when printing go structures. + +load_lib "go.exp" + +if { [skip_go_tests] } { continue } + +standard_testfile .go + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} {debug go}] } { + return -1 +} + +if { [go_runto_main] < 0 } { + untested "could not run to main" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "Break here"] +gdb_continue_to_breakpoint "run to breakpoint" ".*Break here.*" + +gdb_print_expr_at_depths "s1" [list "{...}" \ + "{m_struct = {...}, m_string = $hex \"hello world\"}" \ + "{m_struct = {m_a = 3, m_b = 7}, m_string = $hex \"hello world\"}"] diff --git a/gdb/testsuite/gdb.go/max-depth.go b/gdb/testsuite/gdb.go/max-depth.go new file mode 100644 index 0000000..6dbfc22 --- /dev/null +++ b/gdb/testsuite/gdb.go/max-depth.go @@ -0,0 +1,41 @@ +// Copyright 2019 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package main + +import "fmt" + +type S1 struct { + m_a int + m_b int +} + +type S2 struct { + m_struct S1 + m_string string +} + +func main () { + var s1 S2 + var i int + + s1.m_string = "hello world" + s1.m_struct.m_a = 3 + s1.m_struct.m_b = 7 + + i = 0 // Break here. + fmt.Println ("Got: %d, %d, %d, %s", i, s1.m_struct.m_a, + s1.m_struct.m_b, s1.m_string) +} diff --git a/gdb/testsuite/gdb.modula2/max-depth.c b/gdb/testsuite/gdb.modula2/max-depth.c new file mode 100644 index 0000000..c2fd70c --- /dev/null +++ b/gdb/testsuite/gdb.modula2/max-depth.c @@ -0,0 +1,45 @@ +/* Copyright 2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <string.h> + +struct S1 +{ + int a; + int b; +}; + +struct S2 +{ + char str[10]; + struct S1 s1; +}; + +struct S3 +{ + struct S2 s2; +}; + +int +main () +{ + struct S3 s3; + + memcpy (s3.s2.str, "abcde\0fghi", 10); + s3.s2.s1.a = 3; + s3.s2.s1.b = 4; + + return 0; /* Break here. */ +} diff --git a/gdb/testsuite/gdb.modula2/max-depth.exp b/gdb/testsuite/gdb.modula2/max-depth.exp new file mode 100644 index 0000000..ac7edfe --- /dev/null +++ b/gdb/testsuite/gdb.modula2/max-depth.exp @@ -0,0 +1,37 @@ +# Copyright 2019 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Test the use of 'set prinnt max-depth' when printing modula2 structures. + +standard_testfile + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug quiet}]} { + return -1 +} + +if { ![runto_main] } then { + fail "can't run to main" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "Break here"] +gdb_continue_to_breakpoint "breakpoint" + +gdb_test "set lang modula-2" ".*does not match.*" "switch to modula-2" + +gdb_print_expr_at_depths "s3" [list "{...}" \ + "{s2 = {...}}" \ + "{s2 = {str = \"abcde\\\\000fghi\", s1 = {...}}}" \ + "{s2 = {str = \"abcde\\\\000fghi\", s1 = {a = 3, b = 4}}}"] diff --git a/gdb/testsuite/gdb.python/py-format-string.exp b/gdb/testsuite/gdb.python/py-format-string.exp index 2f574fb..ebb2074 100644 --- a/gdb/testsuite/gdb.python/py-format-string.exp +++ b/gdb/testsuite/gdb.python/py-format-string.exp @@ -672,6 +672,26 @@ proc test_max_elements {} { } } +# Test the max_depth option for gdb.Value.format_string. +proc test_max_depth {} { + set opts "max_depth=-1" + with_test_prefix $opts { + check_format_string "a_struct_with_union" $opts + } + set opts "max_depth=0" + with_test_prefix $opts { + check_format_string "a_struct_with_union" $opts "\\{\.\.\.\\}" + } + set opts "max_depth=1" + with_test_prefix $opts { + check_format_string "a_struct_with_union" $opts "\\{the_union = \\{\.\.\.\\}\\}" + } + set opts "max_depth=2" + with_test_prefix $opts { + check_format_string "a_struct_with_union" $opts + } +} + # Test the repeat_threshold option for gdb.Value.format_string. proc test_repeat_threshold {} { global current_lang @@ -925,6 +945,7 @@ proc test_all_common {} { test_actual_objects test_static_members test_max_elements + test_max_depth test_repeat_threshold test_format # Multiple options mixed together. diff --git a/gdb/testsuite/gdb.python/py-nested-maps.c b/gdb/testsuite/gdb.python/py-nested-maps.c new file mode 100644 index 0000000..94107c0 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-nested-maps.c @@ -0,0 +1,130 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stdlib.h> +#include <string.h> + +#define FIXED_MAP_SIZE 10 + +struct key_t +{ + int a; + int b; +}; + +struct value_t +{ + int x; + int y; + int z; +}; + +struct map_t +{ + const char *name; + int length; + struct key_t *keys; + struct value_t *values; + + /* This field is used only by the pretty printer. */ + int show_header; +}; + +struct map_map_t +{ + int length; + struct map_t **values; + + /* This field is used only by the pretty printer. */ + int show_header; +}; + +struct map_t * +create_map (const char *name) +{ + struct map_t *m = malloc (sizeof (struct map_t)); + m->name = strdup (name); + m->length = 0; + m->keys = NULL; + m->values = NULL; + m->show_header = 0; +} + +void +add_map_element (struct map_t *m, struct key_t k, struct value_t v) +{ + if (m->length == 0) + { + m->keys = malloc (sizeof (struct key_t) * FIXED_MAP_SIZE); + m->values = malloc (sizeof (struct value_t) * FIXED_MAP_SIZE); + } + + m->keys[m->length] = k; + m->values[m->length] = v; + m->length++; +} + +struct map_map_t * +create_map_map (void) +{ + struct map_map_t *mm = malloc (sizeof (struct map_map_t)); + mm->length = 0; + mm->values = NULL; + mm->show_header = 0; +} + +void +add_map_map_element (struct map_map_t *mm, struct map_t *map) +{ + if (mm->length == 0) + mm->values = malloc (sizeof (struct map_t *) * FIXED_MAP_SIZE); + + mm->values[mm->length] = map; + mm->length++; +} + +int +main (void) +{ + struct map_t *m1 = create_map ("m1"); + struct key_t k1 = {3, 4}; + struct key_t k2 = {4, 5}; + struct key_t k3 = {5, 6}; + struct key_t k4 = {6, 7}; + struct key_t k5 = {7, 8}; + struct key_t k6 = {8, 9}; + struct value_t v1 = {0, 1, 2}; + struct value_t v2 = {3, 4, 5}; + struct value_t v3 = {6, 7, 8}; + struct value_t v4 = {9, 0, 1}; + struct value_t v5 = {2, 3, 4}; + struct value_t v6 = {5, 6, 7}; + add_map_element (m1, k1, v1); + add_map_element (m1, k2, v2); + add_map_element (m1, k3, v3); + + struct map_t *m2 = create_map ("m2"); + add_map_element (m2, k4, v4); + add_map_element (m2, k5, v5); + add_map_element (m2, k6, v6); + + struct map_map_t *mm = create_map_map (); + add_map_map_element (mm, m1); + add_map_map_element (mm, m2); + + return 0; /* Break here. */ +} diff --git a/gdb/testsuite/gdb.python/py-nested-maps.exp b/gdb/testsuite/gdb.python/py-nested-maps.exp new file mode 100644 index 0000000..81dd99c --- /dev/null +++ b/gdb/testsuite/gdb.python/py-nested-maps.exp @@ -0,0 +1,238 @@ +# Copyright (C) 2019 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This tests GDB's python pretty printing of nested map like +# structures using structures as keys and values, it then tests how +# 'set print max-depth' affects this printing. + +load_lib gdb-python.exp + +standard_testfile + +# Start with a fresh gdb. +gdb_exit +gdb_start + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +standard_testfile + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug}] } { + return -1 +} + +if ![runto_main ] then { + return -1 +} + +gdb_breakpoint [gdb_get_line_number "Break here"] +gdb_continue_to_breakpoint "run to testing point" ".*Break here.*" + +set remote_python_file [gdb_remote_download host \ + ${srcdir}/${subdir}/${testfile}.py] +gdb_test_no_output "source ${remote_python_file}" "load python file" + +# Test printing with 'set print pretty off'. +gdb_test_no_output "set print pretty off" +with_test_prefix "pretty=off" { + gdb_print_expr_at_depths "*m1" \ + [list \ + "\{\\.\\.\\.\}" \ + "\{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}" \ + "\{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}" \ + ] + + gdb_print_expr_at_depths "*mm" \ + [list \ + "\{\\.\\.\\.\}" \ + "\{\\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}, \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}\}" \ + "\{\\\[$hex \"m1\"\\\] = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}, \\\[$hex \"m2\"\\\] = \{\\\[\{a = 6, b = 7\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 7, b = 8\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 8, b = 9\}\\\] = \{\\.\\.\\.\}\}\}" \ + "\{\\\[$hex \"m1\"\\\] = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}, \\\[$hex \"m2\"\\\] = \{\\\[\{a = 6, b = 7\}\\\] = \{x = 9, y = 0, z = 1\}, \\\[\{a = 7, b = 8\}\\\] = \{x = 2, y = 3, z = 4\}, \\\[\{a = 8, b = 9\}\\\] = \{x = 5, y = 6, z = 7\}\}\}" \ + ] +} + +# Now again, but with 'set print pretty on'. +gdb_test_no_output "set print pretty on" +with_test_prefix "pretty=on" { + gdb_print_expr_at_depths "*m1" \ + [list \ + "\{\\.\\.\\.\}" \ + [multi_line \ + " = \{" \ + " \\\[\{" \ + " a = 3," \ + " b = 4" \ + " \}\\\] = \{\\.\\.\\.\}," \ + " \\\[\{" \ + " a = 4," \ + " b = 5" \ + " \}\\\] = \{\\.\\.\\.\}," \ + " \\\[\{" \ + " a = 5," \ + " b = 6" \ + " \}\\\] = \{\\.\\.\\.\}" \ + "\}" ] \ + [multi_line \ + " = \{" \ + " \\\[\{" \ + " a = 3," \ + " b = 4" \ + " \}\\\] = \{" \ + " x = 0," \ + " y = 1," \ + " z = 2" \ + " \}," \ + " \\\[\{" \ + " a = 4," \ + " b = 5" \ + " \}\\\] = \{" \ + " x = 3," \ + " y = 4," \ + " z = 5" \ + " \}," \ + " \\\[\{" \ + " a = 5," \ + " b = 6" \ + " \}\\\] = \{" \ + " x = 6," \ + " y = 7," \ + " z = 8" \ + " \}" \ + "\}" ] \ + ] + + gdb_print_expr_at_depths "*mm" \ + [list \ + "\{\\.\\.\\.\}" \ + [multi_line \ + " = \{" \ + " \\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}," \ + " \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}" \ + "\}" ] \ + [multi_line \ + " = \{" \ + " \\\[$hex \"m1\"\\\] = \{" \ + " \\\[\{" \ + " a = 3," \ + " b = 4" \ + " \}\\\] = \{\\.\\.\\.\}," \ + " \\\[\{" \ + " a = 4," \ + " b = 5" \ + " \}\\\] = \{\\.\\.\\.\}," \ + " \\\[\{" \ + " a = 5," \ + " b = 6" \ + " \}\\\] = \{\\.\\.\\.\}" \ + " \}," \ + " \\\[$hex \"m2\"\\\] = \{" \ + " \\\[\{" \ + " a = 6," \ + " b = 7" \ + " \}\\\] = \{\\.\\.\\.\}," \ + " \\\[\{" \ + " a = 7," \ + " b = 8" \ + " \}\\\] = \{\\.\\.\\.\}," \ + " \\\[\{" \ + " a = 8," \ + " b = 9" \ + " \}\\\] = \{\\.\\.\\.\}" \ + " \}" \ + "\}" ] \ + [multi_line \ + " = \{" \ + " \\\[$hex \"m1\"\\\] = \{" \ + " \\\[\{" \ + " a = 3," \ + " b = 4" \ + " \}\\\] = \{" \ + " x = 0," \ + " y = 1," \ + " z = 2" \ + " \}," \ + " \\\[\{" \ + " a = 4," \ + " b = 5" \ + " \}\\\] = \{" \ + " x = 3," \ + " y = 4," \ + " z = 5" \ + " \}," \ + " \\\[\{" \ + " a = 5," \ + " b = 6" \ + " \}\\\] = \{" \ + " x = 6," \ + " y = 7," \ + " z = 8" \ + " \}" \ + " \}," \ + " \\\[$hex \"m2\"\\\] = \{" \ + " \\\[\{" \ + " a = 6," \ + " b = 7" \ + " \}\\\] = \{" \ + " x = 9," \ + " y = 0," \ + " z = 1" \ + " \}," \ + " \\\[\{" \ + " a = 7," \ + " b = 8" \ + " \}\\\] = \{" \ + " x = 2," \ + " y = 3," \ + " z = 4" \ + " \}," \ + " \\\[\{" \ + " a = 8," \ + " b = 9" \ + " \}\\\] = \{" \ + " x = 5," \ + " y = 6," \ + " z = 7" \ + " \}" \ + " \}" \ + "\}" ] \ + ] +} + +# Test printing with 'set print pretty off', but this time display a +# top level string (returned from the to_string method) as part of the +# printed value. +gdb_test_no_output "set mm->show_header=1" +gdb_test_no_output "set m1->show_header=1" +gdb_test_no_output "set m2->show_header=1" +with_test_prefix "headers=on" { + gdb_test_no_output "set print pretty off" + with_test_prefix "pretty=off" { + gdb_print_expr_at_depths "*m1" \ + [list \ + "\{\\.\\.\\.\}" \ + "pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}" \ + "pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}" \ + ] + + gdb_print_expr_at_depths "*mm" \ + [list \ + "\{\\.\\.\\.\}" \ + "pp_map_map = \{\\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}, \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}\}" \ + "pp_map_map = \{\\\[$hex \"m1\"\\\] = pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}, \\\[$hex \"m2\"\\\] = pp_map = \{\\\[\{a = 6, b = 7\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 7, b = 8\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 8, b = 9\}\\\] = \{\\.\\.\\.\}\}\}" \ + "pp_map_map = \{\\\[$hex \"m1\"\\\] = pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}, \\\[$hex \"m2\"\\\] = pp_map = \{\\\[\{a = 6, b = 7\}\\\] = \{x = 9, y = 0, z = 1\}, \\\[\{a = 7, b = 8\}\\\] = \{x = 2, y = 3, z = 4\}, \\\[\{a = 8, b = 9\}\\\] = \{x = 5, y = 6, z = 7\}\}\}" \ + ] + } +} diff --git a/gdb/testsuite/gdb.python/py-nested-maps.py b/gdb/testsuite/gdb.python/py-nested-maps.py new file mode 100644 index 0000000..d3fdf59 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-nested-maps.py @@ -0,0 +1,135 @@ +# Copyright (C) 2019 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This file is part of the GDB testsuite. It tests GDB's printing of +# nested map like structures. + +import re +import gdb + +def _iterator1 (pointer, len): + while len > 0: + map = pointer.dereference() + yield ('', map['name']) + yield ('', map.dereference()) + pointer += 1 + len -= 1 + +def _iterator2 (pointer1, pointer2, len): + while len > 0: + yield ("", pointer1.dereference()) + yield ("", pointer2.dereference()) + pointer1 += 1 + pointer2 += 1 + len -= 1 + +class pp_map (object): + def __init__(self, val): + self.val = val + + def to_string(self): + if (self.val['show_header'] == 0): + return None + else: + return "pp_map" + + def children(self): + return _iterator2(self.val['keys'], + self.val['values'], + self.val['length']) + + def display_hint (self): + return 'map' + +class pp_map_map (object): + def __init__(self, val): + self.val = val + + def to_string(self): + if (self.val['show_header'] == 0): + return None + else: + return "pp_map_map" + + def children(self): + return _iterator1(self.val['values'], + self.val['length']) + + def display_hint (self): + return 'map' + +def lookup_function (val): + "Look-up and return a pretty-printer that can print val." + + # Get the type. + type = val.type + + # If it points to a reference, get the reference. + if type.code == gdb.TYPE_CODE_REF: + type = type.target () + + # Get the unqualified type, stripped of typedefs. + type = type.unqualified ().strip_typedefs () + + # Get the type name. + typename = type.tag + + if typename == None: + return None + + # Iterate over local dictionary of types to determine + # if a printer is registered for that type. Return an + # instantiation of the printer if found. + for function in pretty_printers_dict: + if function.match (typename): + return pretty_printers_dict[function] (val) + + # Cannot find a pretty printer. Return None. + return None + +# Lookup a printer for VAL in the typedefs dict. +def lookup_typedefs_function (val): + "Look-up and return a pretty-printer that can print val (typedefs)." + + # Get the type. + type = val.type + + if type == None or type.name == None or type.code != gdb.TYPE_CODE_TYPEDEF: + return None + + # Iterate over local dictionary of typedef types to determine if a + # printer is registered for that type. Return an instantiation of + # the printer if found. + for function in typedefs_pretty_printers_dict: + if function.match (type.name): + return typedefs_pretty_printers_dict[function] (val) + + # Cannot find a pretty printer. + return None + +def register_pretty_printers (): + pretty_printers_dict[re.compile ('^struct map_t$')] = pp_map + pretty_printers_dict[re.compile ('^map_t$')] = pp_map + pretty_printers_dict[re.compile ('^struct map_map_t$')] = pp_map_map + pretty_printers_dict[re.compile ('^map_map_t$')] = pp_map_map + +# Dict for struct types with typedefs fully stripped. +pretty_printers_dict = {} +# Dict for typedef types. +typedefs_pretty_printers_dict = {} + +register_pretty_printers () +gdb.pretty_printers.append (lookup_function) +gdb.pretty_printers.append (lookup_typedefs_function) diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index f0f4334..25d370e 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -1390,6 +1390,36 @@ proc gdb_test_stdio {command inferior_pattern {gdb_pattern ""} {message ""}} { return $res } +# get_print_expr_at_depths EXP OUTPUTS +# +# Used for testing 'set print max-depth'. Prints the expression EXP +# with 'set print max-depth' set to various depths. OUTPUTS is a list +# of `n` different patterns to match at each of the depths from 0 to +# (`n` - 1). +# +# This proc does one final check with the max-depth set to 'unlimited' +# which is tested against the last pattern in the OUTPUTS list. The +# OUTPUTS list is therefore required to match every depth from 0 to a +# depth where the whole of EXP is printed with no ellipsis. +# +# This proc leaves the 'set print max-depth' set to 'unlimited'. +proc gdb_print_expr_at_depths {exp outputs} { + for { set depth 0 } { $depth <= [llength $outputs] } { incr depth } { + if { $depth == [llength $outputs] } { + set expected_result [lindex $outputs [expr [llength $outputs] - 1]] + set depth_string "unlimited" + } else { + set expected_result [lindex $outputs $depth] + set depth_string $depth + } + + with_test_prefix "exp='$exp': depth=${depth_string}" { + gdb_test_no_output "set print max-depth ${depth_string}" + gdb_test "p $exp" "$expected_result" + } + } +} + # Issue a PASS and return true if evaluating CONDITION in the caller's |