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 }