This is not totally comprehensive, but at least it's a start for the core functionality. In the next commit, we'll also use this base to add some regression tests. --- Test/B11kill.ztst | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Test/B11kill.ztst diff --git a/Test/B11kill.ztst b/Test/B11kill.ztst new file mode 100644 index 000000000..6a548213b --- /dev/null +++ b/Test/B11kill.ztst @@ -0,0 +1,52 @@ +# Tests for the kill builtin. + +%test + +# Correct invocation + + if zmodload zsh/system &>/dev/null; then + ( + trap 'exit 19' TERM + kill $sysparams[pid] + ) + else + print -u $ZTST_fd 'Cannot zmodload zsh/system, skipping kill with no sigspec' + fi +19:kill with no sigspec + + + if zmodload zsh/system &>/dev/null; then + ( + trap 'exit 11' USR1 + kill -USR1 $sysparams[pid] + ) + else + print -u $ZTST_fd 'Cannot zmodload zsh/system, skipping kill with sigspec' + fi +11:kill with sigspec + +# Incorrect invocation + + ( + kill a b c + ) +3:kill with multiple wrong inputs should increment status +?(eval):kill:2: illegal pid: a +?(eval):kill:2: illegal pid: b +?(eval):kill:2: illegal pid: c + + ( + kill -INT a b c + ) +3:kill with sigspec and wrong inputs should increment status +?(eval):kill:2: illegal pid: a +?(eval):kill:2: illegal pid: b +?(eval):kill:2: illegal pid: c + + kill +1:kill with no arguments +?(eval):kill:1: not enough arguments + + kill -INT +1:kill with sigspec only +?(eval):kill:1: not enough arguments -- 2.25.0
The following case was encountered in the wild: % zsh; echo "$?" % trap 'exit 5' TERM % kill '' 5 This behaviour seems more likely to be the result of bugs in programs (eg. `kill -9 "$unsetvar") rather than being desirable behaviour to me. It also seems unintentional judging by the code and documentation, since it comes about as a result of the fact that: - `isanum` returns true for empty strings (since an empty string technically only consists of digits and minuses...); - `atoi`, when passed a pointer to an invalid number, returns 0; - `kill(0, signal)` sends the signal in question to all processes in the current process group. There are (at least) two ways to solve this issue: 1. Add special handling to `kill` to avoid this case. See this patch[0] for a version that does that. 2. Change how isanum behaves. Since the only two call sites that use it both seem like they should handle the case where the input char array is empty, that seems like a reasonable overall change to me.[1] After this patch: % trap 'exit 5' TERM % kill '' kill: illegal pid: 0: https://www.zsh.org/mla/workers/2020/msg00251.html 1: The other callsite using isanum() is the fg builtin, but in that case we just fail later since we can't find any job named '', so no big deal either way. It's the kill case which is more concerning. --- Src/jobs.c | 5 +++-- Test/B11kill.ztst | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Src/jobs.c b/Src/jobs.c index e7438251e..0485f2c7c 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1854,13 +1854,14 @@ scanjobs(void) /* This simple function indicates whether or not s may represent * * a number. It returns true iff s consists purely of digits and * - * minuses. Note that minus may appear more than once, and the empty * - * string will produce a `true' response. */ + * minuses. Note that minus may appear more than once. */ /**/ static int isanum(char *s) { + if (*s == '\0') + return 0; while (*s == '-' || idigit(*s)) s++; return *s == '\0'; diff --git a/Test/B11kill.ztst b/Test/B11kill.ztst index 6a548213b..69d4388c7 100644 --- a/Test/B11kill.ztst +++ b/Test/B11kill.ztst @@ -50,3 +50,15 @@ kill -INT 1:kill with sigspec only ?(eval):kill:1: not enough arguments + +# Regression tests: `kill ''` should not result in `kill 0`. + + trap 'exit 19' TERM + kill '' +1:Plain kill with empty pid should not send signal to current process group +?(eval):kill:2: illegal pid: + + trap 'exit 11' INT + kill -INT '' +1:kill with empty pid and sigspec should not send signal to current process group +?(eval):kill:2: illegal pid: -- 2.25.0
Chris Down wrote on Sat, Feb 15, 2020 at 20:20:55 -0400: > This is not totally comprehensive, but at least it's a start for the > core functionality. In the next commit, we'll also use this base to add > some regression tests. Thanks! I was only asking for regression tests, so unit tests are a pleasant surprise ☺ > + if zmodload zsh/system &>/dev/null; then > + ( > + trap 'exit 19' TERM > + kill $sysparams[pid] > + ) > + else > + print -u $ZTST_fd 'Cannot zmodload zsh/system, skipping kill with no sigspec' > + fi > +19:kill with no sigspec > + Shouldn't this set $ZTST_skip, rather than print? That variable is documented in Test/B01cd.ztst. +1 on the remainder of the file. Cheers, Daniel
Chris Down wrote on Sat, Feb 15, 2020 at 20:21:09 -0400: > 1. Add special handling to `kill` to avoid this case. See this patch[0] > for a version that does that. > 0: https://www.zsh.org/mla/workers/2020/msg00251.html Suggest to state "workers/45426" next to the link: that's how we customarily refer to mailing lists posts. The number comes from the X-Seq header and is shown in the archives too. > +++ b/Test/B11kill.ztst > @@ -50,3 +50,15 @@ > kill -INT > 1:kill with sigspec only > ?(eval):kill:1: not enough arguments > + > +# Regression tests: `kill ''` should not result in `kill 0`. > + > + trap 'exit 19' TERM > + kill '' > +1:Plain kill with empty pid should not send signal to current process group > +?(eval):kill:2: illegal pid: Should this test use a subshell so as not to kill the test suite process in case the bug regresses? Personally, I'm partial to first adding commits in "expected to fail" mode (using the «f» flag after the expected exit code) and then in a separate commit fix the bug, since doing so in effect validates the test. However, I see this as a nice-to-hvae, not a blocker. > + trap 'exit 11' INT > + kill -INT '' > +1:kill with empty pid and sigspec should not send signal to current process group > +?(eval):kill:2: illegal pid: Could you add a comment explaining the magic numbers 11 and 19? Cheers, Daniel
Daniel Shahaf writes:
>Shouldn't this set $ZTST_skip, rather than print? That variable is
>documented in Test/B01cd.ztst.
Ah yes, that looks better. Will send in v3. Thanks!
Daniel Shahaf writes: >Chris Down wrote on Sat, Feb 15, 2020 at 20:21:09 -0400: >> 1. Add special handling to `kill` to avoid this case. See this patch[0] >> for a version that does that. > >> 0: https://www.zsh.org/mla/workers/2020/msg00251.html > >Suggest to state "workers/45426" next to the link: that's how we customarily >refer to mailing lists posts. The number comes from the X-Seq header and is >shown in the archives too. Sure thing. :-) >> +++ b/Test/B11kill.ztst >> @@ -50,3 +50,15 @@ >> kill -INT >> 1:kill with sigspec only >> ?(eval):kill:1: not enough arguments >> + >> +# Regression tests: `kill ''` should not result in `kill 0`. >> + >> + trap 'exit 19' TERM >> + kill '' >> +1:Plain kill with empty pid should not send signal to current process group >> +?(eval):kill:2: illegal pid: > >Should this test use a subshell so as not to kill the test suite process in >case the bug regresses? > >Personally, I'm partial to first adding commits in "expected to fail" mode >(using the «f» flag after the expected exit code) and then in a separate commit >fix the bug, since doing so in effect validates the test. However, I see this >as a nice-to-hvae, not a blocker. For this particular one, the subshell won't help, since we're part of the same process group as make and the test runner regardless: make: *** [Makefile:263: check] User defined signal 1 make[1]: *** [Makefile:190: check] User defined signal 1 make[1]: Leaving directory '/home/cdown/git/zsh/Test' One possibility to avoid that is to run these few tests in a shell that's a new session leader, like in W02jobs, I suppose. What do you think about that? > >> + trap 'exit 11' INT >> + kill -INT '' >> +1:kill with empty pid and sigspec should not send signal to current process group >> +?(eval):kill:2: illegal pid: > >Could you add a comment explaining the magic numbers 11 and 19? Sure thing, will do in v3. Thanks! Chris
Chris Down wrote on Mon, 17 Feb 2020 14:11 +00:00: > Daniel Shahaf writes: > >Chris Down wrote on Sat, Feb 15, 2020 at 20:21:09 -0400: > >> + trap 'exit 19' TERM > >> + kill '' > >> +1:Plain kill with empty pid should not send signal to current process group > >> +?(eval):kill:2: illegal pid: > > > >Should this test use a subshell so as not to kill the test suite process in > >case the bug regresses? > > > >Personally, I'm partial to first adding commits in "expected to fail" mode > >(using the «f» flag after the expected exit code) and then in a separate commit > >fix the bug, since doing so in effect validates the test. However, I see this > >as a nice-to-hvae, not a blocker. > > For this particular one, the subshell won't help, since we're part of the same > process group as make and the test runner regardless: > > make: *** [Makefile:263: check] User defined signal 1 > make[1]: *** [Makefile:190: check] User defined signal 1 > make[1]: Leaving directory '/home/cdown/git/zsh/Test' Ah, right, of course. > One possibility to avoid that is to run these few tests in a shell that's a new > session leader, like in W02jobs, I suppose. What do you think about that? Making kill's tests use zpty would probably be too complicated for their purpose. Seeing as even now the bug would fail «make check» if it recurs, I think we can go with what you have. Thanks, Daniel