bash-dap 0.1__tar.gz → 0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bash_dap-0.1 → bash_dap-0.3}/PKG-INFO +13 -5
- {bash_dap-0.1 → bash_dap-0.3}/README.md +12 -4
- {bash_dap-0.1 → bash_dap-0.3}/bash_dap/__init__.py +1 -1
- {bash_dap-0.1 → bash_dap-0.3}/bash_dap/adapter.py +35 -8
- {bash_dap-0.1 → bash_dap-0.3}/bash_dap/harness.sh +47 -10
- {bash_dap-0.1 → bash_dap-0.3}/bash_dap.egg-info/PKG-INFO +13 -5
- {bash_dap-0.1 → bash_dap-0.3}/pyproject.toml +1 -1
- {bash_dap-0.1 → bash_dap-0.3}/tests/test_adapter.py +46 -0
- {bash_dap-0.1 → bash_dap-0.3}/LICENSE +0 -0
- {bash_dap-0.1 → bash_dap-0.3}/bash_dap/__main__.py +0 -0
- {bash_dap-0.1 → bash_dap-0.3}/bash_dap.egg-info/SOURCES.txt +0 -0
- {bash_dap-0.1 → bash_dap-0.3}/bash_dap.egg-info/dependency_links.txt +0 -0
- {bash_dap-0.1 → bash_dap-0.3}/bash_dap.egg-info/entry_points.txt +0 -0
- {bash_dap-0.1 → bash_dap-0.3}/bash_dap.egg-info/top_level.txt +0 -0
- {bash_dap-0.1 → bash_dap-0.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bash-dap
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3
|
|
4
4
|
Summary: A debug adapter (Debug Adapter Protocol) for bash scripts, written in bash and Python
|
|
5
5
|
Author-email: Ilia Maslakov <il.smind@gmail.com>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -18,8 +18,9 @@ Dynamic: license-file
|
|
|
18
18
|
# bash-dap
|
|
19
19
|
|
|
20
20
|
A debug adapter of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)
|
|
21
|
-
for bash scripts: breakpoints,
|
|
22
|
-
variables (arrays and associative arrays as trees),
|
|
21
|
+
for bash scripts: breakpoints on lines, with conditions, and on functions, steps into, over and
|
|
22
|
+
out of functions, the call stack, the variables (arrays and associative arrays as trees),
|
|
23
|
+
expressions, pause.
|
|
23
24
|
|
|
24
25
|
It needs nothing but bash 4.1 or newer and Python 3.8 or newer: no bashdb, no Node.js. (The bash
|
|
25
26
|
of macOS is 3.2: a newer one, from Homebrew say, is given to the launch as *bash*.) The debugger itself is a script
|
|
@@ -75,13 +76,20 @@ subshell is a process of its own, so what is said to one, a step, a breakpoint,
|
|
|
75
76
|
others through files of that directory, which every process looks at, at a test of two files a
|
|
76
77
|
command. A script under the debugger runs some 25 times slower than by itself.
|
|
77
78
|
|
|
79
|
+
A breakpoint on a function stops at its first command, at each call, in a subshell too;
|
|
80
|
+
what the script defines later, or a file it sources, is found as well, as the name is all
|
|
81
|
+
the debugger looks for.
|
|
82
|
+
|
|
83
|
+
The condition of a breakpoint is a command of bash, `[[ $f == report.pdf ]]` or
|
|
84
|
+
`(( count > 2 ))`: the script stops there when its status is 0. It runs in a subshell, so it
|
|
85
|
+
sees the variables of the script and changes none of them.
|
|
86
|
+
|
|
78
87
|
When the session ends the adapter ends the script by its process group, the session it has to
|
|
79
88
|
itself; never by a parent, never anything else.
|
|
80
89
|
|
|
81
90
|
## Not yet
|
|
82
91
|
|
|
83
|
-
|
|
84
|
-
new bash.
|
|
92
|
+
A script that another script runs with a new bash.
|
|
85
93
|
|
|
86
94
|
## License
|
|
87
95
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# bash-dap
|
|
2
2
|
|
|
3
3
|
A debug adapter of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)
|
|
4
|
-
for bash scripts: breakpoints,
|
|
5
|
-
variables (arrays and associative arrays as trees),
|
|
4
|
+
for bash scripts: breakpoints on lines, with conditions, and on functions, steps into, over and
|
|
5
|
+
out of functions, the call stack, the variables (arrays and associative arrays as trees),
|
|
6
|
+
expressions, pause.
|
|
6
7
|
|
|
7
8
|
It needs nothing but bash 4.1 or newer and Python 3.8 or newer: no bashdb, no Node.js. (The bash
|
|
8
9
|
of macOS is 3.2: a newer one, from Homebrew say, is given to the launch as *bash*.) The debugger itself is a script
|
|
@@ -58,13 +59,20 @@ subshell is a process of its own, so what is said to one, a step, a breakpoint,
|
|
|
58
59
|
others through files of that directory, which every process looks at, at a test of two files a
|
|
59
60
|
command. A script under the debugger runs some 25 times slower than by itself.
|
|
60
61
|
|
|
62
|
+
A breakpoint on a function stops at its first command, at each call, in a subshell too;
|
|
63
|
+
what the script defines later, or a file it sources, is found as well, as the name is all
|
|
64
|
+
the debugger looks for.
|
|
65
|
+
|
|
66
|
+
The condition of a breakpoint is a command of bash, `[[ $f == report.pdf ]]` or
|
|
67
|
+
`(( count > 2 ))`: the script stops there when its status is 0. It runs in a subshell, so it
|
|
68
|
+
sees the variables of the script and changes none of them.
|
|
69
|
+
|
|
61
70
|
When the session ends the adapter ends the script by its process group, the session it has to
|
|
62
71
|
itself; never by a parent, never anything else.
|
|
63
72
|
|
|
64
73
|
## Not yet
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
new bash.
|
|
75
|
+
A script that another script runs with a new bash.
|
|
68
76
|
|
|
69
77
|
## License
|
|
70
78
|
|
|
@@ -15,7 +15,11 @@ import tempfile
|
|
|
15
15
|
import threading
|
|
16
16
|
import time
|
|
17
17
|
|
|
18
|
+
from . import __version__
|
|
19
|
+
|
|
18
20
|
HARNESS = os.path.join(os.path.dirname(os.path.abspath(__file__)), "harness.sh")
|
|
21
|
+
# a name of a function of bash, as a breakpoint takes it: no space, no quote, no parenthesis
|
|
22
|
+
FUNCTION_NAME = re.compile(r"^[^\s()$`'\"\\]+$")
|
|
19
23
|
VARIABLES_REF = 1 # the reference of the variables of a stop
|
|
20
24
|
|
|
21
25
|
|
|
@@ -108,6 +112,7 @@ class Adapter:
|
|
|
108
112
|
self.alive = False
|
|
109
113
|
self.pause_asked = False
|
|
110
114
|
self.breaks = {}
|
|
115
|
+
self.functions = []
|
|
111
116
|
self.gen = 0
|
|
112
117
|
self.refs = {}
|
|
113
118
|
self.next_ref = VARIABLES_REF + 1
|
|
@@ -194,8 +199,13 @@ class Adapter:
|
|
|
194
199
|
return
|
|
195
200
|
with open(os.path.join(self.dir, "breaks"), "w") as f:
|
|
196
201
|
for path, lines in self.breaks.items():
|
|
197
|
-
for line in lines:
|
|
198
|
-
|
|
202
|
+
for line, condition in lines:
|
|
203
|
+
if condition:
|
|
204
|
+
f.write("%s:%d\t%s\n" % (path, line, condition))
|
|
205
|
+
else:
|
|
206
|
+
f.write("%s:%d\n" % (path, line))
|
|
207
|
+
for name in self.functions:
|
|
208
|
+
f.write("%s()\n" % name)
|
|
199
209
|
self.gen += 1
|
|
200
210
|
open(os.path.join(self.dir, "gen.%d" % self.gen), "w").close()
|
|
201
211
|
|
|
@@ -255,6 +265,8 @@ class Adapter:
|
|
|
255
265
|
return
|
|
256
266
|
if self.pause_asked and reason == "step":
|
|
257
267
|
reason = "pause"
|
|
268
|
+
elif reason == "function":
|
|
269
|
+
reason = "function breakpoint"
|
|
258
270
|
self.pause_asked = False
|
|
259
271
|
self.event("stopped", {"reason": reason, "threadId": 1, "allThreadsStopped": True})
|
|
260
272
|
|
|
@@ -287,6 +299,8 @@ class Adapter:
|
|
|
287
299
|
self.client = request.get("arguments", {})
|
|
288
300
|
self.respond(request, {
|
|
289
301
|
"supportsConfigurationDoneRequest": True,
|
|
302
|
+
"supportsFunctionBreakpoints": True,
|
|
303
|
+
"supportsConditionalBreakpoints": True,
|
|
290
304
|
"supportsEvaluateForHovers": True,
|
|
291
305
|
"supportsTerminateRequest": True,
|
|
292
306
|
})
|
|
@@ -345,17 +359,30 @@ class Adapter:
|
|
|
345
359
|
def on_setBreakpoints(self, request):
|
|
346
360
|
args = request.get("arguments", {})
|
|
347
361
|
path = os.path.realpath(args.get("source", {}).get("path", ""))
|
|
348
|
-
|
|
362
|
+
# a line and its condition, a command of bash whose status says whether to stop
|
|
363
|
+
lines = [(b["line"], " ".join(b.get("condition", "").split()))
|
|
364
|
+
for b in args.get("breakpoints", []) if "line" in b]
|
|
349
365
|
self.breaks[path] = lines
|
|
350
366
|
self.save_breaks()
|
|
351
367
|
self.respond(request, {"breakpoints": [
|
|
352
368
|
{"id": i + 1, "verified": True, "line": line, "source": {"path": path}}
|
|
353
|
-
for i, line in enumerate(lines)]})
|
|
369
|
+
for i, (line, _) in enumerate(lines)]})
|
|
354
370
|
|
|
355
371
|
def on_setFunctionBreakpoints(self, request):
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
372
|
+
"""The functions to stop in at their first command; a name that is no name of a
|
|
373
|
+
function of bash is not taken"""
|
|
374
|
+
answers = []
|
|
375
|
+
self.functions = []
|
|
376
|
+
for b in request.get("arguments", {}).get("breakpoints", []):
|
|
377
|
+
name = b.get("name", "")
|
|
378
|
+
if FUNCTION_NAME.match(name):
|
|
379
|
+
self.functions.append(name)
|
|
380
|
+
answers.append({"id": 1000 + len(self.functions), "verified": True})
|
|
381
|
+
else:
|
|
382
|
+
answers.append({"verified": False,
|
|
383
|
+
"message": "no name of a function of bash: %s" % name})
|
|
384
|
+
self.save_breaks()
|
|
385
|
+
self.respond(request, {"breakpoints": answers})
|
|
359
386
|
|
|
360
387
|
def on_setExceptionBreakpoints(self, request):
|
|
361
388
|
self.respond(request, {"breakpoints": []})
|
|
@@ -527,7 +554,7 @@ def main():
|
|
|
527
554
|
"on stdin and stdout")
|
|
528
555
|
return 0
|
|
529
556
|
if len(sys.argv) > 1 and sys.argv[1] == "--version":
|
|
530
|
-
print("bash-dap
|
|
557
|
+
print("bash-dap %s" % __version__)
|
|
531
558
|
return 0
|
|
532
559
|
Adapter(sys.stdin.buffer, sys.stdout.buffer).run()
|
|
533
560
|
return 0
|
|
@@ -9,14 +9,17 @@
|
|
|
9
9
|
# "replies", which it reads what the debugger says from, a line each:
|
|
10
10
|
#
|
|
11
11
|
# commands: run | step | next | finish | print EXPRESSION | vars | stack
|
|
12
|
-
# replies: stopped REASON LINE FUNCTION FILE (REASON: entry, breakpoint, step)
|
|
12
|
+
# replies: stopped REASON LINE FUNCTION FILE (REASON: entry, breakpoint, function, step)
|
|
13
13
|
# value TEXT | var NAME TEXT | frame LEVEL FUNCTION LINE FILE | error TEXT
|
|
14
14
|
# done, after the reply to a command | exited CODE
|
|
15
15
|
#
|
|
16
16
|
# TEXT is quoted as printf %q does, so that a reply is one line. A command is read only while
|
|
17
17
|
# the script is stopped. At any time the adapter may write the breakpoints to the file "breaks",
|
|
18
18
|
# FILE:LINE a line, then make the empty file "gen.N" of the next generation N; and stop the
|
|
19
|
-
# script at its next command by writing "step 0" to the file "state".
|
|
19
|
+
# script at its next command by writing "step 0" to the file "state". A line NAME() of
|
|
20
|
+
# "breaks" is a breakpoint on the function NAME: the script stops at its first command. After
|
|
21
|
+
# FILE:LINE and a tab, a command of bash is the condition of the breakpoint: the script stops
|
|
22
|
+
# there only when its status is 0.
|
|
20
23
|
#
|
|
21
24
|
# A subshell is a process of its own: what it is told, a step, a breakpoint, goes to the others
|
|
22
25
|
# through these files. The names of the debugger begin with __bdap_.
|
|
@@ -33,11 +36,14 @@ __bdap_state=$__bdap_dir/state
|
|
|
33
36
|
__bdap_mode=run
|
|
34
37
|
__bdap_depth=0
|
|
35
38
|
# the breakpoints by FILE:LINE, and the generation of the file they came from; __bdap_names has
|
|
36
|
-
# NAME:LINE, the name of the file only, that the trap tests at each command
|
|
37
|
-
|
|
39
|
+
# NAME:LINE, the name of the file only, that the trap tests at each command; __bdap_funcs has
|
|
40
|
+
# NAME() of the functions to stop in; __bdap_conds the conditions of FILE:LINE
|
|
41
|
+
declare -A __bdap_breaks=() __bdap_names=() __bdap_funcs=() __bdap_conds=()
|
|
38
42
|
__bdap_gen=0
|
|
39
43
|
__bdap_inside=
|
|
40
44
|
__bdap_prev= # the place of the command before: a line stops when it is come to, not again
|
|
45
|
+
__bdap_fdepth=0 # the depth of the calls at the command before: more is a function entered
|
|
46
|
+
__bdap_fenter= # a function to stop in is entered: its first command is next
|
|
41
47
|
|
|
42
48
|
# the variables of bash itself, and those it makes for a function: not shown
|
|
43
49
|
__bdap_known=" $(compgen -v | tr '\n' ' ') FUNCNAME BASH_ARGC BASH_ARGV BASH_LINENO BASH_SOURCE COLUMNS LINES "
|
|
@@ -93,8 +99,18 @@ __bdap_breaks_load () {
|
|
|
93
99
|
done
|
|
94
100
|
__bdap_breaks=()
|
|
95
101
|
__bdap_names=()
|
|
96
|
-
|
|
102
|
+
__bdap_funcs=()
|
|
103
|
+
__bdap_conds=()
|
|
104
|
+
while IFS= read -r __bdap_place; do
|
|
97
105
|
[[ -z $__bdap_place ]] && continue
|
|
106
|
+
if [[ $__bdap_place == *$'\t'* ]]; then
|
|
107
|
+
__bdap_conds["${__bdap_place%%$'\t'*}"]=${__bdap_place#*$'\t'}
|
|
108
|
+
__bdap_place=${__bdap_place%%$'\t'*}
|
|
109
|
+
fi
|
|
110
|
+
if [[ $__bdap_place == *"()" ]]; then
|
|
111
|
+
__bdap_funcs["$__bdap_place"]=1
|
|
112
|
+
continue
|
|
113
|
+
fi
|
|
98
114
|
__bdap_breaks["$__bdap_place"]=1
|
|
99
115
|
__bdap_names["${__bdap_place##*/}"]=1
|
|
100
116
|
done <"$__bdap_dir/breaks"
|
|
@@ -126,6 +142,7 @@ __bdap_wait () {
|
|
|
126
142
|
__bdap_resume run 0
|
|
127
143
|
__bdap_breaks=()
|
|
128
144
|
__bdap_names=()
|
|
145
|
+
__bdap_funcs=()
|
|
129
146
|
trap - DEBUG
|
|
130
147
|
}
|
|
131
148
|
|
|
@@ -138,6 +155,13 @@ __bdap_on_line () {
|
|
|
138
155
|
[[ -z $__bdap_word || ${__bdap_text[0]-} == *"$__bdap_word"* ]]
|
|
139
156
|
}
|
|
140
157
|
|
|
158
|
+
# Whether the condition of a breakpoint holds, none always: run in a subshell, it sees the
|
|
159
|
+
# variables of the script and changes none of them
|
|
160
|
+
__bdap_condition () {
|
|
161
|
+
[[ -z $1 ]] && return 0
|
|
162
|
+
( eval "$1" ) </dev/null >/dev/null 2>&1
|
|
163
|
+
}
|
|
164
|
+
|
|
141
165
|
# Before a command of the script that may stop: what the script has set, -e, -u, -x, is not
|
|
142
166
|
# the debugger's, which runs without
|
|
143
167
|
__bdap_hook () {
|
|
@@ -169,9 +193,20 @@ __bdap_check () {
|
|
|
169
193
|
next) ((__bdap_d <= __bdap_depth)) && __bdap_reason=step ;;
|
|
170
194
|
finish) ((__bdap_d < __bdap_depth)) && __bdap_reason=step ;;
|
|
171
195
|
esac
|
|
172
|
-
if [[ -z $__bdap_reason && -n ${__bdap_breaks["$__bdap_file:$__bdap_line"]-} ]] && __bdap_on_line
|
|
196
|
+
if [[ -z $__bdap_reason && -n ${__bdap_breaks["$__bdap_file:$__bdap_line"]-} ]] && __bdap_on_line \
|
|
197
|
+
&& __bdap_condition "${__bdap_conds["$__bdap_file:$__bdap_line"]-}"; then
|
|
173
198
|
__bdap_reason=breakpoint
|
|
174
199
|
fi
|
|
200
|
+
# the first command of a function to stop in: the calls are deeper than at the command
|
|
201
|
+
# before; bash comes first to the line that defines it, with the call as the command
|
|
202
|
+
if [[ -z $__bdap_reason && -n ${__bdap_funcs["${FUNCNAME[2]-}()"]-} ]]; then
|
|
203
|
+
if ((__bdap_d - 1 > __bdap_fdepth)) && [[ $__bdap_command == "${FUNCNAME[2]}"* ]]; then
|
|
204
|
+
__bdap_fenter=1
|
|
205
|
+
elif ((__bdap_d - 1 > __bdap_fdepth)) || [[ -n $__bdap_fenter ]]; then
|
|
206
|
+
__bdap_reason=function
|
|
207
|
+
fi
|
|
208
|
+
fi
|
|
209
|
+
[[ -n $__bdap_reason ]] && __bdap_fenter=
|
|
175
210
|
# the next command of a line is no new place to stop at
|
|
176
211
|
[[ -z $__bdap_reason || $__bdap_prev == "$__bdap_line:${BASH_SOURCE[2]}" ]] && return 0
|
|
177
212
|
# the lines of the frames, the one stopped in first
|
|
@@ -195,12 +230,14 @@ trap '__bdap_say "exited $?"' EXIT
|
|
|
195
230
|
shopt -s extdebug
|
|
196
231
|
set -o functrace
|
|
197
232
|
# the hook only when there may be something to do: a step, a breakpoint in a file of that name
|
|
198
|
-
# on the line, what another process has said; else a command costs a test
|
|
199
|
-
# line: a second one would have LINENO one more
|
|
200
|
-
# (with set -u of the script it runs too: nothing unset is read, BASH_SOURCE is empty at the end
|
|
233
|
+
# on the line or on the function, what another process has said; else a command costs a test
|
|
234
|
+
# of two files. One line: a second one would have LINENO one more
|
|
235
|
+
# (with set -u of the script it runs too: nothing unset is read, BASH_SOURCE is empty at the end,
|
|
236
|
+
# FUNCNAME unset out of any function)
|
|
201
237
|
__bdap_trap='[[ -n $__bdap_inside || ( $__bdap_mode == run'
|
|
202
238
|
__bdap_trap+=' && -z ${__bdap_names["${BASH_SOURCE[0]:+${BASH_SOURCE[0]##*/}}:$LINENO"]-}'
|
|
239
|
+
__bdap_trap+=' && -z ${__bdap_funcs["${FUNCNAME[0]-}()"]-}'
|
|
203
240
|
__bdap_trap+=' && ! -e $__bdap_state && ! -e $__bdap_dir/gen.$((__bdap_gen + 1)) ) ]] || __bdap_hook "$BASH_COMMAND";'
|
|
204
|
-
__bdap_trap+=' __bdap_prev=$LINENO:${BASH_SOURCE[0]-}'
|
|
241
|
+
__bdap_trap+=' __bdap_prev=$LINENO:${BASH_SOURCE[0]-} __bdap_fdepth=${FUNCNAME[0]+${#FUNCNAME[@]}}'
|
|
205
242
|
trap "$__bdap_trap" DEBUG
|
|
206
243
|
. "$0" "$@"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bash-dap
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3
|
|
4
4
|
Summary: A debug adapter (Debug Adapter Protocol) for bash scripts, written in bash and Python
|
|
5
5
|
Author-email: Ilia Maslakov <il.smind@gmail.com>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -18,8 +18,9 @@ Dynamic: license-file
|
|
|
18
18
|
# bash-dap
|
|
19
19
|
|
|
20
20
|
A debug adapter of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)
|
|
21
|
-
for bash scripts: breakpoints,
|
|
22
|
-
variables (arrays and associative arrays as trees),
|
|
21
|
+
for bash scripts: breakpoints on lines, with conditions, and on functions, steps into, over and
|
|
22
|
+
out of functions, the call stack, the variables (arrays and associative arrays as trees),
|
|
23
|
+
expressions, pause.
|
|
23
24
|
|
|
24
25
|
It needs nothing but bash 4.1 or newer and Python 3.8 or newer: no bashdb, no Node.js. (The bash
|
|
25
26
|
of macOS is 3.2: a newer one, from Homebrew say, is given to the launch as *bash*.) The debugger itself is a script
|
|
@@ -75,13 +76,20 @@ subshell is a process of its own, so what is said to one, a step, a breakpoint,
|
|
|
75
76
|
others through files of that directory, which every process looks at, at a test of two files a
|
|
76
77
|
command. A script under the debugger runs some 25 times slower than by itself.
|
|
77
78
|
|
|
79
|
+
A breakpoint on a function stops at its first command, at each call, in a subshell too;
|
|
80
|
+
what the script defines later, or a file it sources, is found as well, as the name is all
|
|
81
|
+
the debugger looks for.
|
|
82
|
+
|
|
83
|
+
The condition of a breakpoint is a command of bash, `[[ $f == report.pdf ]]` or
|
|
84
|
+
`(( count > 2 ))`: the script stops there when its status is 0. It runs in a subshell, so it
|
|
85
|
+
sees the variables of the script and changes none of them.
|
|
86
|
+
|
|
78
87
|
When the session ends the adapter ends the script by its process group, the session it has to
|
|
79
88
|
itself; never by a parent, never anything else.
|
|
80
89
|
|
|
81
90
|
## Not yet
|
|
82
91
|
|
|
83
|
-
|
|
84
|
-
new bash.
|
|
92
|
+
A script that another script runs with a new bash.
|
|
85
93
|
|
|
86
94
|
## License
|
|
87
95
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bash-dap"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3"
|
|
8
8
|
description = "A debug adapter (Debug Adapter Protocol) for bash scripts, written in bash and Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "GPL-3.0-or-later" }
|
|
@@ -198,6 +198,52 @@ class SessionTest(unittest.TestCase):
|
|
|
198
198
|
c.wait_event("terminated")
|
|
199
199
|
self.assertIn("3 files, 2800 bytes", c.output())
|
|
200
200
|
|
|
201
|
+
def test_function_breakpoints(self):
|
|
202
|
+
c = self.client = Client()
|
|
203
|
+
c.request("initialize", {"adapterID": "bash-dap", "linesStartAt1": True})
|
|
204
|
+
c.request("launch", {"program": script("backup.sh"), "cwd": SCRIPTS})
|
|
205
|
+
c.wait_event("initialized")
|
|
206
|
+
response = c.request("setFunctionBreakpoints", {"breakpoints": [
|
|
207
|
+
{"name": "size_of"}, {"name": "not a name"}]})
|
|
208
|
+
verified = [b["verified"] for b in response["body"]["breakpoints"]]
|
|
209
|
+
self.assertEqual(verified, [True, False])
|
|
210
|
+
c.request("configurationDone")
|
|
211
|
+
# at the first command of the function, in the subshell of $(...)
|
|
212
|
+
self.assertEqual(c.wait_event("stopped")["body"]["reason"], "function breakpoint")
|
|
213
|
+
self.assertEqual(c.stop_place(), [("size_of", "backup.sh", 7), ("main", "backup.sh", 15)])
|
|
214
|
+
# once a call, not at each of its commands
|
|
215
|
+
c.request("next", {"threadId": 1})
|
|
216
|
+
c.wait_event("stopped")
|
|
217
|
+
self.assertEqual(c.stop_place()[0], ("size_of", "backup.sh", 8))
|
|
218
|
+
c.request("continue", {"threadId": 1})
|
|
219
|
+
c.wait_event("stopped")
|
|
220
|
+
self.assertEqual(c.evaluate("$f")["body"]["result"], "photo.jpg")
|
|
221
|
+
# taken off, the script runs to its end
|
|
222
|
+
c.request("setFunctionBreakpoints", {"breakpoints": []})
|
|
223
|
+
c.request("continue", {"threadId": 1})
|
|
224
|
+
self.assertEqual(c.wait_event("exited")["body"]["exitCode"], 3)
|
|
225
|
+
|
|
226
|
+
def test_conditional_breakpoint(self):
|
|
227
|
+
c = self.client = Client()
|
|
228
|
+
c.request("initialize", {"adapterID": "bash-dap", "linesStartAt1": True})
|
|
229
|
+
c.request("launch", {"program": script("backup.sh"), "cwd": SCRIPTS})
|
|
230
|
+
c.wait_event("initialized")
|
|
231
|
+
response = c.request("setBreakpoints", {"source": {"path": script("backup.sh")},
|
|
232
|
+
"breakpoints": [
|
|
233
|
+
{"line": 15, "condition": "[[ $f == photo.jpg ]]"},
|
|
234
|
+
# an assignment in a condition changes nothing of the script
|
|
235
|
+
{"line": 16, "condition": "total=1000; false"}]})
|
|
236
|
+
self.assertTrue(all(b["verified"] for b in response["body"]["breakpoints"]))
|
|
237
|
+
c.request("configurationDone")
|
|
238
|
+
self.assertEqual(c.wait_event("stopped")["body"]["reason"], "breakpoint")
|
|
239
|
+
self.assertEqual(c.stop_place(), [("main", "backup.sh", 15)])
|
|
240
|
+
self.assertEqual(c.evaluate("$f")["body"]["result"], "photo.jpg")
|
|
241
|
+
self.assertEqual(c.evaluate("$total")["body"]["result"], "900")
|
|
242
|
+
# no other round stops, and the total is the script's own
|
|
243
|
+
c.request("continue", {"threadId": 1})
|
|
244
|
+
self.assertEqual(c.wait_event("exited")["body"]["exitCode"], 3)
|
|
245
|
+
self.assertIn("3 files, 2800 bytes", c.output())
|
|
246
|
+
|
|
201
247
|
def test_pipe_sourced_file_and_dollar_zero(self):
|
|
202
248
|
c = self.client = Client()
|
|
203
249
|
c.start(script("pipe.sh"), [(script("lib.sh"), 3), (script("pipe.sh"), 6)])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|