github.com/freetocompute/snapd@v0.0.0-20210618182524-2fb355d72fd9/testutil/exec_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package testutil
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"os/exec"
    27  	"path/filepath"
    28  
    29  	"gopkg.in/check.v1"
    30  )
    31  
    32  type mockCommandSuite struct{}
    33  
    34  var _ = check.Suite(&mockCommandSuite{})
    35  
    36  const (
    37  	UmountNoFollow = umountNoFollow
    38  )
    39  
    40  func (s *mockCommandSuite) TestMockCommand(c *check.C) {
    41  	mock := MockCommand(c, "cmd", "true")
    42  	defer mock.Restore()
    43  	err := exec.Command("cmd", "first-run", "--arg1", "arg2", "a space").Run()
    44  	c.Assert(err, check.IsNil)
    45  	err = exec.Command("cmd", "second-run", "--arg1", "arg2", "a %s").Run()
    46  	c.Assert(err, check.IsNil)
    47  	c.Assert(mock.Calls(), check.DeepEquals, [][]string{
    48  		{"cmd", "first-run", "--arg1", "arg2", "a space"},
    49  		{"cmd", "second-run", "--arg1", "arg2", "a %s"},
    50  	})
    51  }
    52  
    53  func (s *mockCommandSuite) TestMockCommandAlso(c *check.C) {
    54  	mock := MockCommand(c, "fst", "")
    55  	also := mock.Also("snd", "")
    56  	defer mock.Restore()
    57  
    58  	c.Assert(exec.Command("fst").Run(), check.IsNil)
    59  	c.Assert(exec.Command("snd").Run(), check.IsNil)
    60  	c.Check(mock.Calls(), check.DeepEquals, [][]string{{"fst"}, {"snd"}})
    61  	c.Check(mock.Calls(), check.DeepEquals, also.Calls())
    62  }
    63  
    64  func (s *mockCommandSuite) TestMockCommandConflictEcho(c *check.C) {
    65  	mock := MockCommand(c, "do-not-swallow-echo-args", "")
    66  	defer mock.Restore()
    67  
    68  	c.Assert(exec.Command("do-not-swallow-echo-args", "-E", "-n", "-e").Run(), check.IsNil)
    69  	c.Assert(mock.Calls(), check.DeepEquals, [][]string{
    70  		{"do-not-swallow-echo-args", "-E", "-n", "-e"},
    71  	})
    72  }
    73  
    74  func (s *mockCommandSuite) TestMockShellchecksWhenAvailable(c *check.C) {
    75  	tmpDir := c.MkDir()
    76  	mockShellcheck := MockCommand(c, "shellcheck", fmt.Sprintf(`cat > %s/input`, tmpDir))
    77  	defer mockShellcheck.Restore()
    78  
    79  	restore := MockShellcheckPath(mockShellcheck.Exe())
    80  	defer restore()
    81  
    82  	mock := MockCommand(c, "some-command", "echo some-command")
    83  
    84  	c.Assert(exec.Command("some-command").Run(), check.IsNil)
    85  
    86  	c.Assert(mock.Calls(), check.DeepEquals, [][]string{
    87  		{"some-command"},
    88  	})
    89  	c.Assert(mockShellcheck.Calls(), check.DeepEquals, [][]string{
    90  		{"shellcheck", "-s", "bash", "-"},
    91  	})
    92  
    93  	scriptData, err := ioutil.ReadFile(mock.Exe())
    94  	c.Assert(err, check.IsNil)
    95  	c.Assert(string(scriptData), Contains, "\necho some-command\n")
    96  
    97  	data, err := ioutil.ReadFile(filepath.Join(tmpDir, "input"))
    98  	c.Assert(err, check.IsNil)
    99  	c.Assert(data, check.DeepEquals, scriptData)
   100  }
   101  
   102  func (s *mockCommandSuite) TestMockNoShellchecksWhenNotAvailable(c *check.C) {
   103  	mockShellcheck := MockCommand(c, "shellcheck", `echo "i am not called"; exit 1`)
   104  	defer mockShellcheck.Restore()
   105  
   106  	restore := MockShellcheckPath("")
   107  	defer restore()
   108  
   109  	// This would fail with proper shellcheck due to SC2086: Double quote to
   110  	// prevent globbing and word splitting.
   111  	mock := MockCommand(c, "some-command", "echo $1")
   112  
   113  	c.Assert(exec.Command("some-command").Run(), check.IsNil)
   114  
   115  	c.Assert(mock.Calls(), check.DeepEquals, [][]string{
   116  		{"some-command"},
   117  	})
   118  	c.Assert(mockShellcheck.Calls(), check.HasLen, 0)
   119  }
   120  
   121  func (s *mockCommandSuite) TestMockCreateAbsPathDir(c *check.C) {
   122  	// this is an absolute path
   123  	dir := c.MkDir()
   124  
   125  	absPath := filepath.Join(dir, "this/is/nested/command")
   126  	mock := MockCommand(c, absPath, "")
   127  
   128  	c.Assert(exec.Command(absPath).Run(), check.IsNil)
   129  	c.Assert(mock.Calls(), check.DeepEquals, [][]string{
   130  		{"command"},
   131  	})
   132  
   133  	binDirRo := filepath.Join(dir, "ro")
   134  	err := os.MkdirAll(binDirRo, 0000)
   135  	c.Assert(err, check.IsNil)
   136  	absPathBad := filepath.Join(binDirRo, "this/fails/command")
   137  	exp := fmt.Sprintf(`cannot create the directory for mocked command "%[1]s/ro/this/fails/command": mkdir %[1]s/ro/this: permission denied`, dir)
   138  	c.Assert(func() { MockCommand(c, absPathBad, "") }, check.Panics, exp)
   139  }