tests: disallow the use of abbreviated options (by default)

Git's command-line parsers support uniquely abbreviated options, e.g. `git init --ba` would automatically expand `--ba` to `--bare`. This is a very convenient feature in every day life for Git users, in particular when tab completion is not available. However, it is not a good idea to rely on that in Git's test suite, as something that is a unique abbreviation of a command line option today might no longer be a unique abbreviation tomorrow. For example, if a future contribution added a new mode `git init --babyproofing` and a previously-introduced test case used the fact that `git init --ba` expanded to `git init --bare`, that future contribution would now have to touch seemingly unrelated tests just to keep the test suite from failing. So let's disallow abbreviated options in the test suite by default. Note: for ease of implementation, this patch really only touches the `parse-options` machinery: more and more hand-rolled option parsers are converted to use that internal API, and more and more scripts are converted to built-ins (naturally using the parse-options API, too), so in practice this catches most issues, and is definitely the biggest bang for the buck. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Apr 12, 2019 at 02:37 UTC b02e7d5d701a6217a3a522d9169b483b25c262dc
4 files changed +33 -1
parse-options.c
+9
@@ -6,6 +6,8 @@
6 #include "color.h"
7 #include "utf8.h"
8
9 +static int disallow_abbreviated_options;
10 +
11 #define OPT_SHORT 1
12 #define OPT_UNSET 2
13
@@ -344,6 +346,10 @@ is_abbreviated:
346 return get_value(p, options, all_opts, flags ^ opt_flags);
347 }
348
349 + if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
350 + die("disallowed abbreviated or ambiguous option '%.*s'",
351 + (int)(arg_end - arg), arg);
352 +
353 if (ambiguous_option) {
354 error(_("ambiguous option: %s "
355 "(could be --%s%s or --%s%s)"),
@@ -708,6 +714,9 @@ int parse_options(int argc, const char **argv, const char *prefix,
714 {
715 struct parse_opt_ctx_t ctx;
716
717 + disallow_abbreviated_options =
718 + git_env_bool("GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS", 0);
719 +
720 parse_options_start(&ctx, argc, argv, prefix, options, flags);
721 switch (parse_options_step(&ctx, options, usagestr)) {
722 case PARSE_OPT_HELP:
t/README
+4
@@ -399,6 +399,10 @@ GIT_TEST_SIDEBAND_ALL=<boolean>, when true, overrides the
399 fetch-pack to not request sideband-all (even if the server advertises
400 sideband-all).
401
402 +GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=<boolean>, when true (which is
403 +the default when running tests), errors out when an abbreviated option
404 +is used.
405 +
406 Naming Tests
407 ------------
408
t/t0040-parse-options.sh
+13 -1
@@ -203,20 +203,24 @@ file: (not set)
203 EOF
204
205 test_expect_success 'unambiguously abbreviated option' '
206 + GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
207 test-tool parse-options --int 2 --boolean --no-bo >output 2>output.err &&
208 test_must_be_empty output.err &&
209 test_cmp expect output
210 '
211
212 test_expect_success 'unambiguously abbreviated option with "="' '
213 + GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
214 test-tool parse-options --expect="integer: 2" --int=2
215 '
216
217 test_expect_success 'ambiguously abbreviated option' '
216 - test_expect_code 129 test-tool parse-options --strin 123
218 + test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
219 + test-tool parse-options --strin 123
220 '
221
222 test_expect_success 'non ambiguous option (after two options it abbreviates)' '
223 + GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
224 test-tool parse-options --expect="string: 123" --st 123
225 '
226
@@ -325,6 +329,7 @@ file: (not set)
329 EOF
330
331 test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
332 + GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
333 test-tool parse-options --no-ambig >output 2>output.err &&
334 test_must_be_empty output.err &&
335 test_cmp expect output
@@ -370,4 +375,11 @@ test_expect_success '--no-verbose resets multiple verbose to 0' '
375 test-tool parse-options --expect="verbose: 0" -v -v -v --no-verbose
376 '
377
378 +test_expect_success 'GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS works' '
379 + GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
380 + test-tool parse-options --ye &&
381 + test_must_fail env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=true \
382 + test-tool parse-options --ye
383 +'
384 +
385 test_done
t/test-lib.sh
+7
@@ -57,6 +57,13 @@ fi
57 . "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
58 export PERL_PATH SHELL_PATH
59
60 +# Disallow the use of abbreviated options in the test suite by default
61 +if test -z "${GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS}"
62 +then
63 + GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=true
64 + export GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS
65 +fi
66 +
67 ################################################################
68 # It appears that people try to run tests without building...
69 "${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X" >/dev/null