github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/osutil/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 osutil_test
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"io"
    26  	"os"
    27  	"os/exec"
    28  	"syscall"
    29  	"time"
    30  
    31  	. "gopkg.in/check.v1"
    32  	"gopkg.in/tomb.v2"
    33  
    34  	"github.com/snapcore/snapd/dirs"
    35  	"github.com/snapcore/snapd/osutil"
    36  )
    37  
    38  type execSuite struct{}
    39  
    40  var _ = Suite(&execSuite{})
    41  
    42  func (s *execSuite) SetUpTest(c *C) {
    43  	dirs.SetRootDir(c.MkDir())
    44  }
    45  
    46  func (s *execSuite) TearDownTest(c *C) {
    47  	dirs.SetRootDir("")
    48  }
    49  
    50  func (s *execSuite) TestRunAndWaitRunsAndWaits(c *C) {
    51  	buf, err := osutil.RunAndWait([]string{"sh", "-c", "echo hello; sleep .1"}, nil, time.Second, &tomb.Tomb{})
    52  	c.Assert(err, IsNil)
    53  	c.Check(string(buf), Equals, "hello\n")
    54  }
    55  
    56  func (s *execSuite) TestRunAndWaitRunsSetsEnviron(c *C) {
    57  	buf, err := osutil.RunAndWait([]string{"sh", "-c", "echo $FOO"}, []string{"FOO=42"}, time.Second, &tomb.Tomb{})
    58  	c.Assert(err, IsNil)
    59  	c.Check(string(buf), Equals, "42\n")
    60  }
    61  
    62  func (s *execSuite) TestRunAndWaitRunsAndKillsOnTimeout(c *C) {
    63  	buf, err := osutil.RunAndWait([]string{"sleep", "1s"}, nil, time.Millisecond, &tomb.Tomb{})
    64  	c.Check(err, ErrorMatches, "exceeded maximum runtime.*")
    65  	c.Check(string(buf), Matches, "(?s).*exceeded maximum runtime.*")
    66  }
    67  
    68  func (s *execSuite) TestRunAndWaitRunsAndKillsOnAbort(c *C) {
    69  	tmb := &tomb.Tomb{}
    70  	go func() {
    71  		time.Sleep(10 * time.Millisecond)
    72  		tmb.Kill(nil)
    73  	}()
    74  	buf, err := osutil.RunAndWait([]string{"sleep", "1s"}, nil, time.Second, tmb)
    75  	c.Check(err, ErrorMatches, "aborted.*")
    76  	c.Check(string(buf), Matches, "(?s).*aborted.*")
    77  }
    78  
    79  func (s *execSuite) TestRunAndWaitKillImpatient(c *C) {
    80  	defer osutil.MockSyscallKill(func(int, syscall.Signal) error { return nil })()
    81  	defer osutil.MockCmdWaitTimeout(time.Millisecond)()
    82  
    83  	buf, err := osutil.RunAndWait([]string{"sleep", "1s"}, nil, time.Millisecond, &tomb.Tomb{})
    84  	c.Check(err, ErrorMatches, ".* did not stop")
    85  	c.Check(string(buf), Equals, "")
    86  }
    87  
    88  func (s *execSuite) TestRunAndWaitExposesKillallError(c *C) {
    89  	defer osutil.MockSyscallKill(func(p int, s syscall.Signal) error {
    90  		syscall.Kill(p, s)
    91  		return fmt.Errorf("xyzzy")
    92  	})()
    93  	defer osutil.MockCmdWaitTimeout(time.Millisecond)()
    94  
    95  	_, err := osutil.RunAndWait([]string{"sleep", "1s"}, nil, time.Millisecond, &tomb.Tomb{})
    96  	c.Check(err, ErrorMatches, "cannot abort: xyzzy")
    97  }
    98  
    99  func (s *execSuite) TestKillProcessGroupKillsProcessGroup(c *C) {
   100  	pid := 0
   101  	ppid := 0
   102  	defer osutil.MockSyscallGetpgid(func(p int) (int, error) {
   103  		ppid = p
   104  		return syscall.Getpgid(p)
   105  	})()
   106  	defer osutil.MockSyscallKill(func(p int, s syscall.Signal) error {
   107  		pid = p
   108  		return syscall.Kill(p, s)
   109  	})()
   110  
   111  	cmd := exec.Command("sleep", "1m")
   112  	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
   113  	cmd.Start()
   114  	defer cmd.Process.Kill()
   115  
   116  	err := osutil.KillProcessGroup(cmd)
   117  	c.Assert(err, IsNil)
   118  	// process groups are passed to kill as negative numbers
   119  	c.Check(pid, Equals, -ppid)
   120  }
   121  
   122  func (s *execSuite) TestKillProcessGroupShyOfInit(c *C) {
   123  	defer osutil.MockSyscallGetpgid(func(int) (int, error) { return 1, nil })()
   124  
   125  	cmd := exec.Command("sleep", "1m")
   126  	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
   127  	cmd.Start()
   128  	defer cmd.Process.Kill()
   129  
   130  	err := osutil.KillProcessGroup(cmd)
   131  	c.Assert(err, ErrorMatches, "cannot kill pgid 1")
   132  }
   133  
   134  func (s *execSuite) TestStreamCommandHappy(c *C) {
   135  	var buf bytes.Buffer
   136  	stdout, err := osutil.StreamCommand("sh", "-c", "echo hello; sleep .1; echo bye")
   137  	c.Assert(err, IsNil)
   138  	_, err = io.Copy(&buf, stdout)
   139  	c.Assert(err, IsNil)
   140  	c.Check(buf.String(), Equals, "hello\nbye\n")
   141  
   142  	wrf, wrc := osutil.WaitingReaderGuts(stdout)
   143  	c.Assert(wrf, FitsTypeOf, &os.File{})
   144  	// Depending on golang version the error is one of the two.
   145  	c.Check(wrf.(*os.File).Close(), ErrorMatches, "invalid argument|file already closed")
   146  	c.Check(wrc.ProcessState, NotNil) // i.e. already waited for
   147  }
   148  
   149  func (s *execSuite) TestStreamCommandSad(c *C) {
   150  	var buf bytes.Buffer
   151  	stdout, err := osutil.StreamCommand("false")
   152  	c.Assert(err, IsNil)
   153  	_, err = io.Copy(&buf, stdout)
   154  	c.Assert(err, ErrorMatches, "exit status 1")
   155  	c.Check(buf.String(), Equals, "")
   156  
   157  	wrf, wrc := osutil.WaitingReaderGuts(stdout)
   158  	c.Assert(wrf, FitsTypeOf, &os.File{})
   159  	// Depending on golang version the error is one of the two.
   160  	c.Check(wrf.(*os.File).Close(), ErrorMatches, "invalid argument|file already closed")
   161  	c.Check(wrc.ProcessState, NotNil) // i.e. already waited for
   162  }