@renault-digital/bash-base 2.4.1 → 2.5.0

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.
@@ -9,38 +9,101 @@ AfterRun preserve
9
9
  Describe 'prc_filter_by_port'
10
10
  It 'with port number'
11
11
  nc -lk 127.0.0.1 9999 &
12
+ listener_pid=$!
12
13
  sleep 1
13
14
  echo "=== Debug: check listener ==="
14
15
  lsof -iTCP:9999 -sTCP:LISTEN -n -P
15
16
  When call prc_filter_by_port 9999
16
17
  The status should be success
17
18
  The output should include ":9999"
18
- kill $(lsof -iTCP:9999 -sTCP:LISTEN -n -P -t)
19
+ kill "$listener_pid" 2>/dev/null || true
20
+ wait "$listener_pid" 2>/dev/null || true
19
21
  End
20
22
 
21
23
  It 'without port number'
22
24
  nc -l 9999 &
25
+ listener_pid=$!
26
+ sleep 1
23
27
  When call prc_filter_by_port
24
28
  The status should be success
25
29
  The output should include ":9999"
26
- kill $(lsof -iTCP:9999 -sTCP:LISTEN -n -P -t)
30
+ kill "$listener_pid" 2>/dev/null || true
31
+ wait "$listener_pid" 2>/dev/null || true
27
32
  End
28
33
  End
29
34
 
30
35
 
36
+ # Use an owned listener and wait until it is actually listening before
37
+ # exercising the public API. Redirect all background descriptors so an
38
+ # unsuccessful kill cannot hold the command-substitution output pipe open.
39
+ prc_test_kill_listener() {
40
+ local port="$1" signal="$2"
41
+ local listener_pid="" supervisor_pid attempt ready=0 rc=1 pid_file
42
+
43
+ pid_file="$(mktemp "${TMPDIR:-/tmp}/bash-base-listener.XXXXXX")" || return 1
44
+
45
+ # A SIGKILL-ed direct background job causes Bash to write "Killed: 9"
46
+ # on ShellSpec's stderr, even if nc itself redirects stderr. Instead,
47
+ # the direct child is a POSIX sh supervisor that exits normally after
48
+ # waiting for nc. Keep nc's PID to verify we are testing our listener.
49
+ sh -c '
50
+ nc -l "$1" </dev/null >/dev/null 2>&1 &
51
+ child=$!
52
+ trap "kill $child 2>/dev/null || :" 0 1 2 15
53
+ printf "%s\n" "$child" > "$2"
54
+ wait "$child" 2>/dev/null || :
55
+ ' sh "$port" "$pid_file" </dev/null >/dev/null 2>&1 &
56
+ supervisor_pid=$!
57
+
58
+ for ((attempt = 0; attempt < 50; attempt++)); do
59
+ if [[ -s "$pid_file" ]]; then
60
+ IFS= read -r listener_pid < "$pid_file" || :
61
+ fi
62
+ if [[ -n "$listener_pid" ]] &&
63
+ lsof -nP -iTCP:"$port" -sTCP:LISTEN -t 2>/dev/null | grep -qx "$listener_pid"; then
64
+ ready=1
65
+ break
66
+ fi
67
+ if ! kill -0 "$supervisor_pid" 2>/dev/null; then
68
+ break
69
+ fi
70
+ sleep 0.1
71
+ done
72
+
73
+ if ((ready)); then
74
+ actual="$(prc_kill_by_port "$port" "$signal")"
75
+ rc=$?
76
+ else
77
+ printf 'Test listener failed to bind on port %s\n' "$port" >&2
78
+ fi
79
+
80
+ # Cleanup only the processes owned by this test. Reap the normal-exiting
81
+ # supervisor, not the signal-terminated nc child it manages.
82
+ if [[ -n "$listener_pid" ]]; then
83
+ kill "$listener_pid" 2>/dev/null || :
84
+ fi
85
+ wait "$supervisor_pid" 2>/dev/null || :
86
+ rm -f "$pid_file"
87
+ return "$rc"
88
+ }
89
+
31
90
  Describe 'prc_kill_by_port'
32
91
  It 'signal term'
33
- func() { actual=$( nc -l 9999 & prc_kill_by_port 9999 ); }
92
+ test_port=$((22000 + RANDOM % 20000))
93
+ func() { prc_test_kill_listener "$test_port" 15; }
34
94
  When run func
35
95
  The status should be success
36
- The value "${actual}" should include ":9999"
96
+ The value "${actual}" should include ":${test_port}"
97
+ The value "${actual}" should include "Start to kill port listener by signal 15..."
37
98
  End
38
99
 
39
100
  It 'signal kill'
40
- func() { actual=$( nc -l 9999 & prc_kill_by_port 9999 9); }
101
+ test_port=$((22000 + RANDOM % 20000))
102
+ func() { prc_test_kill_listener "$test_port" 9; }
41
103
  When run func
42
104
  The status should be success
43
- The value "${actual}" should include ":9999"
105
+ The value "${actual}" should include ":${test_port}"
106
+ The value "${actual}" should include "Start to kill port listener by signal 9..."
44
107
  End
45
108
 
46
109
  It 'port not listened'
@@ -52,39 +115,70 @@ Describe 'prc_kill_by_port'
52
115
  End
53
116
 
54
117
 
118
+ # The default macOS "ps" output depends on the controlling terminal.
119
+ # GitHub Actions has no such terminal, so these unit tests provide an explicit
120
+ # process table. Do not change the public functions' existing `ps` behavior.
55
121
  Describe 'prc_filter_by_cmd'
122
+ ps() {
123
+ printf '%s\n' \
124
+ ' PID TTY TIME CMD' \
125
+ '999999997 ttys001 0:00.00 /bin/bash -c fixture' \
126
+ '999999998 ttys001 0:00.00 sleep 10'
127
+ }
128
+
56
129
  It 'with command'
57
130
  When call prc_filter_by_cmd bash
58
131
  The status should be success
59
- The output should include "bash"
132
+ The output should include '/bin/bash -c fixture'
133
+ The output should not include 'sleep 10'
60
134
  End
61
135
 
62
136
  It 'without command'
63
137
  When call prc_filter_by_cmd
64
138
  The status should be success
65
- The output should include "bash"
139
+ The output should include '/bin/bash -c fixture'
140
+ The output should include 'sleep 10'
66
141
  End
67
142
  End
68
143
 
69
144
 
70
145
  Describe 'prc_kill_by_cmd'
146
+ # ps emits a deterministic table; fake PIDs cannot refer to real processes.
147
+ ps() {
148
+ printf '%s\n' \
149
+ ' PID TTY TIME CMD' \
150
+ '999999997 ttys001 0:00.00 /bin/bash -c fixture' \
151
+ '999999998 ttys001 0:00.00 sleep 10'
152
+ }
153
+
154
+ # xargs invokes an external `kill`, not a shell function. Use ShellSpec's
155
+ # command-based Mock so signals are checked without terminating processes.
156
+ Mock kill
157
+ printf 'MOCK_KILL:'
158
+ printf ' <%s>' "$@"
159
+ printf '\n'
160
+ End
161
+
71
162
  It 'signal term'
72
- func() { actual=$( sleep 10 & prc_kill_by_cmd sleep); }
73
- When run func
163
+ When call prc_kill_by_cmd sleep
74
164
  The status should be success
75
- The value "${actual}" should include "sleep"
165
+ The output should include 'Start to kill command by signal 15...'
166
+ The output should include 'MOCK_KILL: <-15> <999999998>'
167
+ The output should not include 'MOCK_KILL: <-15> <999999997>'
76
168
  End
77
169
 
78
170
  It 'signal kill'
79
- func() { actual=$( sleep 10 & prc_kill_by_cmd sleep 9); }
80
- When run func
171
+ When call prc_kill_by_cmd sleep 9
81
172
  The status should be success
82
- The value "${actual}" should include "sleep"
173
+ The output should include 'Start to kill command by signal 9...'
174
+ The output should include 'MOCK_KILL: <-9> <999999998>'
175
+ The output should not include 'MOCK_KILL: <-9> <999999997>'
83
176
  End
84
177
 
85
178
  It 'not found command'
86
179
  When call prc_kill_by_cmd xxxx
87
180
  The status should be success
88
- The output should include "No command to kill"
181
+ The output should include 'No command to kill'
182
+ The output should not include 'MOCK_KILL:'
89
183
  End
90
184
  End
@@ -120,7 +120,7 @@ Describe 'reflect_function_names_of_file'
120
120
  When call reflect_function_names_of_file "${shellScriptFile}"
121
121
  The status should eq "0"
122
122
  The output should include "reflect_nth_arg"
123
- The lines of output should eq 65
123
+ The lines of output should eq 67
124
124
 
125
125
  rm -fr "${shellScriptFile}"
126
126
  End
@@ -16,3 +16,20 @@ array_describe_equals() {
16
16
 
17
17
 
18
18
 
19
+
20
+ # Compare ordered array values across Bash and zsh without assuming native
21
+ # indexed-array numbering or typeset/declare output formatting.
22
+ array_values_equal() {
23
+ local array_name="$1"
24
+ shift
25
+ local -a __bb_test_values
26
+ local element
27
+
28
+ eval "__bb_test_values=( \"\${${array_name}[@]}\" )" || return 1
29
+ [[ ${#__bb_test_values[@]} -eq $# ]] || return 1
30
+ for element in "${__bb_test_values[@]}"; do
31
+ [[ "$element" == "$1" ]] || return 1
32
+ shift
33
+ done
34
+ return 0
35
+ }
@@ -11,7 +11,7 @@ Describe 'string_split_to_array'
11
11
  It 'with variable'
12
12
  str="a b c"
13
13
  When call string_split_to_array ' ' actual "$str"
14
- The variable actual should satisfy array_describe_equals actual "([0]='a' [1]='b' [2]='c')"
14
+ The variable actual should satisfy array_values_equal actual 'a' 'b' 'c'
15
15
  End
16
16
 
17
17
  It 'with pipe and print to stdout'
@@ -23,7 +23,7 @@ Describe 'string_split_to_array'
23
23
  It 'with token string including space'
24
24
  str="amy delimiterbmy delimiterc"
25
25
  When call string_split_to_array 'my delimiter' actual "$str"
26
- The variable actual should satisfy array_describe_equals actual "([0]='a' [1]='b' [2]='c')"
26
+ The variable actual should satisfy array_values_equal actual 'a' 'b' 'c'
27
27
  End
28
28
 
29
29
  It 'with stdin'
@@ -33,7 +33,7 @@ Describe 'string_split_to_array'
33
33
  * origin/feature/52-new-feature
34
34
  EOF
35
35
  When call string_split_to_array $'\n' actual "${lines}"
36
- The variable actual should satisfy array_describe_equals actual "([0]=' origin/develop' [1]=' origin/integration' [2]='* origin/feature/52-new-feature')"
36
+ The variable actual should satisfy array_values_equal actual ' origin/develop' ' origin/integration' '* origin/feature/52-new-feature'
37
37
  End
38
38
 
39
39
  It 'with file'
@@ -43,7 +43,7 @@ Describe 'string_split_to_array'
43
43
  3. C
44
44
  EOF
45
45
  When call eval "string_split_to_array $'\n' actual < temp_file.txt"
46
- The variable actual should satisfy array_describe_equals actual "([0]=' 1. A D' [1]=' 2. B' [2]=' 3. C')"
46
+ The variable actual should satisfy array_values_equal actual ' 1. A D' ' 2. B' ' 3. C'
47
47
 
48
48
  rm -fr temp_file.txt
49
49
  End
@@ -54,12 +54,12 @@ Describe 'string_pick_to_array'
54
54
  It 'with variable'
55
55
  str="[{age:12},{age:15},{age:16}]"
56
56
  When call string_pick_to_array '{age:' '}' actual "$str"
57
- The variable actual should satisfy array_describe_equals actual "([0]='12' [1]='15' [2]='16')"
57
+ The variable actual should satisfy array_values_equal actual '12' '15' '16'
58
58
  End
59
59
 
60
60
  It 'with pipe and print to stdout'
61
61
  str="[{age:12},{age:15},{age:16}]"
62
- When call eval "echo $str | string_pick_to_array '{age:' '}'"
62
+ When call eval 'printf "%s\n" "$str" | string_pick_to_array "{age:" "}"'
63
63
  The output should eq $'12\n15\n16'
64
64
  End
65
65
 
@@ -78,7 +78,7 @@ Describe 'string_pick_to_array'
78
78
  ]
79
79
  EOF
80
80
  When call string_pick_to_array 'age:' $'\n' actual "${lines}"
81
- The variable actual should satisfy array_describe_equals actual "([0]='12' [1]='15' [2]='16')"
81
+ The variable actual should satisfy array_values_equal actual '12' '15' '16'
82
82
  End
83
83
 
84
84
  It 'with file'
@@ -96,7 +96,7 @@ Describe 'string_pick_to_array'
96
96
  ]
97
97
  EOF
98
98
  When call eval "string_pick_to_array 'age:' $'\n' actual < temp_file.txt"
99
- The variable actual should satisfy array_describe_equals actual "([0]='12' [1]='15' [2]='16')"
99
+ The variable actual should satisfy array_values_equal actual '12' '15' '16'
100
100
 
101
101
  rm -fr temp_file.txt
102
102
  End
@@ -435,9 +435,8 @@ Describe 'string_before_first'
435
435
  EFGH
436
436
  IJKL
437
437
  EOF
438
- func() { actual=$(string_before_first 'FGH' < temp_file.txt); }
439
- When run func
440
- The variable actual should eq $'ABCD\nE'
438
+ When call eval 'string_before_first "FGH" < temp_file.txt'
439
+ The output should eq $'ABCD\nE'
441
440
  rm -fr temp_file.txt
442
441
  End
443
442
  End
@@ -465,9 +464,8 @@ Describe 'string_after_first'
465
464
  EFGH
466
465
  IJKL
467
466
  EOF
468
- func() { actual=$(string_after_first "FGH" < temp_file.txt); }
469
- When run func
470
- The variable actual should eq $'\nIJKL'
467
+ When call eval 'string_after_first "FGH" < temp_file.txt'
468
+ The output should eq $'\nIJKL'
471
469
  rm -fr temp_file.txt
472
470
  End
473
471
  End
@@ -548,9 +546,8 @@ Describe 'string_index_first'
548
546
  EFGH
549
547
  IJKL
550
548
  EOF
551
- func() { actual=$(string_index_first "FGH" < temp_file.txt); }
552
- When run func
553
- The variable actual should eq "6"
549
+ When call eval 'string_index_first "FGH" < temp_file.txt'
550
+ The output should eq "6"
554
551
  rm -fr temp_file.txt
555
552
  End
556
553
  End
package/src/alias.sh CHANGED
@@ -1,9 +1,40 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- shopt -s expand_aliases 2>/dev/null # This file is for compatibility with previous versions
4
-
5
3
  source src/out.sh
6
4
  source src/tool.sh
7
5
 
8
- alias args_confirm='confirm_to_continue'
9
- alias args_print='print_args'
6
+ # Backward-compatible command names. Use wrapper functions rather than aliases:
7
+ # aliases need different non-interactive expansion rules in Bash and zsh, while
8
+ # functions work identically when this library is sourced by either shell.
9
+
10
+ # @NAME
11
+ # args_confirm -- show the name and value of variables, and continue execute if args_confirm by user, or exit if not
12
+ # @SYNOPSIS
13
+ # args_confirm variableName...
14
+ # @DESCRIPTION
15
+ # **variableName...** some existed variable names to show its value
16
+ # @EXAMPLES
17
+ # a="correct value"
18
+ # b="wrong value"
19
+ # args_confirm a b
20
+ # @SEE_ALSO
21
+ # args_print, stop_if_failed
22
+ args_confirm() {
23
+ confirm_to_continue "$@"
24
+ }
25
+
26
+ # @NAME
27
+ # args_print -- if LOG_LEVEL<=$LOG_LEVEL_WARN, show the name and value of variables
28
+ # @SYNOPSIS
29
+ # args_print variableName...
30
+ # @DESCRIPTION
31
+ # **variableName...** some existed variable names to show its value
32
+ # @EXAMPLES
33
+ # var1="value 1"
34
+ # var2="value 2"
35
+ # args_print var1 var2
36
+ # @SEE_ALSO
37
+ # print_header, print_error, print_success, print_warn, print_info, print_debug
38
+ args_print() {
39
+ print_args "$@"
40
+ }