t0061: adjust to test-tool transition
authorJunio C Hamano <gitster@pobox.com>
Thu, 25 Oct 2018 02:41:09 +0000 (11:41 +0900)
committerJunio C Hamano <gitster@pobox.com>
Thu, 25 Oct 2018 02:41:09 +0000 (11:41 +0900)
1  2 
run-command.c
t/t0061-run-command.sh
diff --combined run-command.c
index 84b883c2132bb07bc7fa21434368d64664587b8b,8d42a4f534f7ad0a1e06d5ce898b114253f06b18..d679cc267c39b9fb34f57078db5db12ed591b7e2
@@@ -1,12 -1,10 +1,12 @@@
  #include "cache.h"
  #include "run-command.h"
 -#include "exec_cmd.h"
 +#include "exec-cmd.h"
  #include "sigchain.h"
  #include "argv-array.h"
  #include "thread-utils.h"
  #include "strbuf.h"
 +#include "string-list.h"
 +#include "quote.h"
  
  void child_process_init(struct child_process *child)
  {
@@@ -245,7 -243,7 +245,7 @@@ int sane_execvp(const char *file, char 
  static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
  {
        if (!argv[0])
 -              die("BUG: shell command is empty");
 +              BUG("shell command is empty");
  
        if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
  #ifndef GIT_WINDOWS_NATIVE
@@@ -380,10 -378,10 +380,10 @@@ static void child_err_spew(struct child
        set_error_routine(old_errfn);
  }
  
- static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
+ static int prepare_cmd(struct argv_array *out, const struct child_process *cmd)
  {
        if (!cmd->argv[0])
 -              die("BUG: command is empty");
 +              BUG("command is empty");
  
        /*
         * Add SHELL_PATH so in the event exec fails with ENOEXEC we can
        /*
         * If there are no '/' characters in the command then perform a path
         * lookup and use the resolved path as the command to exec.  If there
-        * are no '/' characters or if the command wasn't found in the path,
-        * have exec attempt to invoke the command directly.
+        * are '/' characters, we have exec attempt to invoke the command
+        * directly.
         */
        if (!strchr(out->argv[1], '/')) {
                char *program = locate_in_PATH(out->argv[1]);
                if (program) {
                        free((char *)out->argv[1]);
                        out->argv[1] = program;
+               } else {
+                       argv_array_clear(out);
+                       errno = ENOENT;
+                       return -1;
                }
        }
+       return 0;
  }
  
  static char **prep_childenv(const char *const *deltaenv)
@@@ -471,12 -475,15 +477,12 @@@ struct atfork_state 
        sigset_t old;
  };
  
 -#ifndef NO_PTHREADS
 -static void bug_die(int err, const char *msg)
 -{
 -      if (err) {
 -              errno = err;
 -              die_errno("BUG: %s", msg);
 -      }
 -}
 -#endif
 +#define CHECK_BUG(err, msg) \
 +      do { \
 +              int e = (err); \
 +              if (e) \
 +                      BUG("%s: %s", msg, strerror(e)); \
 +      } while(0)
  
  static void atfork_prepare(struct atfork_state *as)
  {
        if (sigprocmask(SIG_SETMASK, &all, &as->old))
                die_errno("sigprocmask");
  #else
 -      bug_die(pthread_sigmask(SIG_SETMASK, &all, &as->old),
 +      CHECK_BUG(pthread_sigmask(SIG_SETMASK, &all, &as->old),
                "blocking all signals");
 -      bug_die(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs),
 +      CHECK_BUG(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs),
                "disabling cancellation");
  #endif
  }
@@@ -501,9 -508,9 +507,9 @@@ static void atfork_parent(struct atfork
        if (sigprocmask(SIG_SETMASK, &as->old, NULL))
                die_errno("sigprocmask");
  #else
 -      bug_die(pthread_setcancelstate(as->cs, NULL),
 +      CHECK_BUG(pthread_setcancelstate(as->cs, NULL),
                "re-enabling cancellation");
 -      bug_die(pthread_sigmask(SIG_SETMASK, &as->old, NULL),
 +      CHECK_BUG(pthread_sigmask(SIG_SETMASK, &as->old, NULL),
                "restoring signal mask");
  #endif
  }
@@@ -554,90 -561,6 +560,90 @@@ static int wait_or_whine(pid_t pid, con
        return code;
  }
  
 +static void trace_add_env(struct strbuf *dst, const char *const *deltaenv)
 +{
 +      struct string_list envs = STRING_LIST_INIT_DUP;
 +      const char *const *e;
 +      int i;
 +      int printed_unset = 0;
 +
 +      /* Last one wins, see run-command.c:prep_childenv() for context */
 +      for (e = deltaenv; e && *e; e++) {
 +              struct strbuf key = STRBUF_INIT;
 +              char *equals = strchr(*e, '=');
 +
 +              if (equals) {
 +                      strbuf_add(&key, *e, equals - *e);
 +                      string_list_insert(&envs, key.buf)->util = equals + 1;
 +              } else {
 +                      string_list_insert(&envs, *e)->util = NULL;
 +              }
 +              strbuf_release(&key);
 +      }
 +
 +      /* "unset X Y...;" */
 +      for (i = 0; i < envs.nr; i++) {
 +              const char *var = envs.items[i].string;
 +              const char *val = envs.items[i].util;
 +
 +              if (val || !getenv(var))
 +                      continue;
 +
 +              if (!printed_unset) {
 +                      strbuf_addstr(dst, " unset");
 +                      printed_unset = 1;
 +              }
 +              strbuf_addf(dst, " %s", var);
 +      }
 +      if (printed_unset)
 +              strbuf_addch(dst, ';');
 +
 +      /* ... followed by "A=B C=D ..." */
 +      for (i = 0; i < envs.nr; i++) {
 +              const char *var = envs.items[i].string;
 +              const char *val = envs.items[i].util;
 +              const char *oldval;
 +
 +              if (!val)
 +                      continue;
 +
 +              oldval = getenv(var);
 +              if (oldval && !strcmp(val, oldval))
 +                      continue;
 +
 +              strbuf_addf(dst, " %s=", var);
 +              sq_quote_buf_pretty(dst, val);
 +      }
 +      string_list_clear(&envs, 0);
 +}
 +
 +static void trace_run_command(const struct child_process *cp)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +
 +      if (!trace_want(&trace_default_key))
 +              return;
 +
 +      strbuf_addstr(&buf, "trace: run_command:");
 +      if (cp->dir) {
 +              strbuf_addstr(&buf, " cd ");
 +              sq_quote_buf_pretty(&buf, cp->dir);
 +              strbuf_addch(&buf, ';');
 +      }
 +      /*
 +       * The caller is responsible for initializing cp->env from
 +       * cp->env_array if needed. We only check one place.
 +       */
 +      if (cp->env)
 +              trace_add_env(&buf, cp->env);
 +      if (cp->git_cmd)
 +              strbuf_addstr(&buf, " git");
 +      sq_quote_argv_pretty(&buf, cp->argv);
 +
 +      trace_printf("%s", buf.buf);
 +      strbuf_release(&buf);
 +}
 +
  int start_command(struct child_process *cmd)
  {
        int need_in, need_out, need_err;
@@@ -706,8 -629,7 +712,8 @@@ fail_pipe
                cmd->err = fderr[0];
        }
  
 -      trace_argv_printf(cmd->argv, "trace: run_command:");
 +      trace_run_command(cmd);
 +
        fflush(NULL);
  
  #ifndef GIT_WINDOWS_NATIVE
        struct child_err cerr;
        struct atfork_state as;
  
+       if (prepare_cmd(&argv, cmd) < 0) {
+               failed_errno = errno;
+               cmd->pid = -1;
+               goto end_of_spawn;
+       }
        if (pipe(notify_pipe))
                notify_pipe[0] = notify_pipe[1] = -1;
  
                set_cloexec(null_fd);
        }
  
-       prepare_cmd(&argv, cmd);
        childenv = prep_childenv(cmd->env);
        atfork_prepare(&as);
  
        argv_array_clear(&argv);
        free(childenv);
  }
+ end_of_spawn:
  #else
  {
        int fhin = 0, fhout = 1, fherr = 2;
@@@ -964,7 -893,7 +977,7 @@@ int run_command(struct child_process *c
        int code;
  
        if (cmd->out < 0 || cmd->err < 0)
 -              die("BUG: run_command with a pipe can cause deadlock");
 +              BUG("run_command with a pipe can cause deadlock");
  
        code = start_command(cmd);
        if (code)
@@@ -1253,28 -1182,11 +1266,28 @@@ const char *find_hook(const char *name
        strbuf_reset(&path);
        strbuf_git_path(&path, "hooks/%s", name);
        if (access(path.buf, X_OK) < 0) {
 +              int err = errno;
 +
  #ifdef STRIP_EXTENSION
                strbuf_addstr(&path, STRIP_EXTENSION);
                if (access(path.buf, X_OK) >= 0)
                        return path.buf;
 +              if (errno == EACCES)
 +                      err = errno;
  #endif
 +
 +              if (err == EACCES && advice_ignored_hook) {
 +                      static struct string_list advise_given = STRING_LIST_INIT_DUP;
 +
 +                      if (!string_list_lookup(&advise_given, name)) {
 +                              string_list_insert(&advise_given, name);
 +                              advise(_("The '%s' hook was ignored because "
 +                                       "it's not set as executable.\n"
 +                                       "You can disable this warning with "
 +                                       "`git config advice.ignoredHook false`."),
 +                                     path.buf);
 +                      }
 +              }
                return NULL;
        }
        return path.buf;
@@@ -1554,7 -1466,7 +1567,7 @@@ static void pp_init(struct parallel_pro
  
        pp->data = data;
        if (!get_next_task)
 -              die("BUG: you need to specify a get_next_task function");
 +              BUG("you need to specify a get_next_task function");
        pp->get_next_task = get_next_task;
  
        pp->start_failure = start_failure ? start_failure : default_start_failure;
@@@ -1616,7 -1528,7 +1629,7 @@@ static int pp_start_one(struct parallel
                if (pp->children[i].state == GIT_CP_FREE)
                        break;
        if (i == pp->max_processes)
 -              die("BUG: bookkeeping is hard");
 +              BUG("bookkeeping is hard");
  
        code = pp->get_next_task(&pp->children[i].process,
                                 &pp->children[i].err,
diff --combined t/t0061-run-command.sh
index c887ed5b45e824d281343196c8781cbb6e85abed,0303ddbb6440f0f8ef8051f390447f9406694239..b9cfc03a53d9ef892b047df5a9d6a15fb0febf55
@@@ -13,25 -13,36 +13,36 @@@ cat >hello-script <<-EO
  EOF
  >empty
  
- test_expect_success 'start_command reports ENOENT' '
+ test_expect_success 'start_command reports ENOENT (slash)' '
 -      test-run-command start-command-ENOENT ./does-not-exist
 +      test-tool run-command start-command-ENOENT ./does-not-exist
  '
  
 -      test-run-command start-command-ENOENT does-not-exist
+ test_expect_success 'start_command reports ENOENT (no slash)' '
++      test-tool run-command start-command-ENOENT does-not-exist
+ '
  test_expect_success 'run_command can run a command' '
        cat hello-script >hello.sh &&
        chmod +x hello.sh &&
 -      test-run-command run-command ./hello.sh >actual 2>err &&
 +      test-tool run-command run-command ./hello.sh >actual 2>err &&
  
        test_cmp hello-script actual &&
        test_cmp empty err
  '
  
 -      test_must_fail test-run-command run-command should-not-run
+ test_expect_success 'run_command is restricted to PATH' '
+       write_script should-not-run <<-\EOF &&
+       echo yikes
+       EOF
++      test_must_fail test-tool run-command run-command should-not-run
+ '
  test_expect_success !MINGW 'run_command can run a script without a #! line' '
        cat >hello <<-\EOF &&
        cat hello-script
        EOF
        chmod +x hello &&
 -      test-run-command run-command ./hello >actual 2>err &&
 +      test-tool run-command run-command ./hello >actual 2>err &&
  
        test_cmp hello-script actual &&
        test_cmp empty err
@@@ -45,7 -56,7 +56,7 @@@ test_expect_success 'run_command does n
        EOF
  
        PATH=$PWD/bin1:$PWD/bin2:$PATH \
 -              test-run-command run-command greet >actual 2>err &&
 +              test-tool run-command run-command greet >actual 2>err &&
        test_cmp bin2/greet actual &&
        test_cmp empty err
  '
@@@ -62,7 -73,7 +73,7 @@@ test_expect_success POSIXPERM 'run_comm
        EOF
  
        PATH=$PWD/bin1:$PWD/bin2:$PATH \
 -              test-run-command run-command greet >actual 2>err &&
 +              test-tool run-command run-command greet >actual 2>err &&
        test_cmp bin2/greet actual &&
        test_cmp empty err
  '
@@@ -70,7 -81,7 +81,7 @@@
  test_expect_success POSIXPERM 'run_command reports EACCES' '
        cat hello-script >hello.sh &&
        chmod -x hello.sh &&
 -      test_must_fail test-run-command run-command ./hello.sh 2>err &&
 +      test_must_fail test-tool run-command run-command ./hello.sh 2>err &&
  
        grep "fatal: cannot exec.*hello.sh" err
  '
@@@ -104,17 -115,17 +115,17 @@@ Worl
  EOF
  
  test_expect_success 'run_command runs in parallel with more jobs available than tasks' '
 -      test-run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
 +      test-tool run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
        test_cmp expect actual
  '
  
  test_expect_success 'run_command runs in parallel with as many jobs as tasks' '
 -      test-run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
 +      test-tool run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
        test_cmp expect actual
  '
  
  test_expect_success 'run_command runs in parallel with more tasks than jobs available' '
 -      test-run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
 +      test-tool run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
        test_cmp expect actual
  '
  
@@@ -128,7 -139,7 +139,7 @@@ asking for a quick sto
  EOF
  
  test_expect_success 'run_command is asked to abort gracefully' '
 -      test-run-command run-command-abort 3 false 2>actual &&
 +      test-tool run-command run-command-abort 3 false 2>actual &&
        test_cmp expect actual
  '
  
@@@ -137,45 -148,8 +148,45 @@@ no further jobs availabl
  EOF
  
  test_expect_success 'run_command outputs ' '
 -      test-run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
 +      test-tool run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual &&
        test_cmp expect actual
  '
  
 +test_trace () {
 +      expect="$1"
 +      shift
 +      GIT_TRACE=1 test-tool run-command "$@" run-command true 2>&1 >/dev/null | \
 +              sed -e 's/.* run_command: //' -e '/trace: .*/d' >actual &&
 +      echo "$expect true" >expect &&
 +      test_cmp expect actual
 +}
 +
 +test_expect_success 'GIT_TRACE with environment variables' '
 +      test_trace "abc=1 def=2" env abc=1 env def=2 &&
 +      test_trace "abc=2" env abc env abc=1 env abc=2 &&
 +      test_trace "abc=2" env abc env abc=2 &&
 +      (
 +              abc=1 && export abc &&
 +              test_trace "def=1" env abc=1 env def=1
 +      ) &&
 +      (
 +              abc=1 && export abc &&
 +              test_trace "def=1" env abc env abc=1 env def=1
 +      ) &&
 +      test_trace "def=1" env non-exist env def=1 &&
 +      test_trace "abc=2" env abc=1 env abc env abc=2 &&
 +      (
 +              abc=1 def=2 && export abc def &&
 +              test_trace "unset abc def;" env abc env def
 +      ) &&
 +      (
 +              abc=1 def=2 && export abc def &&
 +              test_trace "unset def; abc=3" env abc env def env abc=3
 +      ) &&
 +      (
 +              abc=1 && export abc &&
 +              test_trace "unset abc;" env abc=2 env abc
 +      )
 +'
 +
  test_done