github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/osutil/context_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 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 "context" 24 "io" 25 "os/exec" 26 "strings" 27 "testing" 28 "time" 29 30 "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/osutil" 33 ) 34 35 type ctxSuite struct{} 36 37 type dumbReader struct{} 38 39 func (dumbReader) Read([]byte) (int, error) { 40 return 1, nil 41 } 42 43 var _ = check.Suite(&ctxSuite{}) 44 45 func (ctxSuite) TestWriter(c *check.C) { 46 ctx, cancel := context.WithTimeout(context.Background(), time.Second/100) 47 defer cancel() 48 n, err := io.Copy(osutil.ContextWriter(ctx), dumbReader{}) 49 c.Assert(err, check.Equals, context.DeadlineExceeded) 50 // but we copied things until the deadline hit 51 c.Check(n, check.Not(check.Equals), int64(0)) 52 } 53 54 func (ctxSuite) TestWriterDone(c *check.C) { 55 ctx, cancel := context.WithCancel(context.Background()) 56 cancel() 57 n, err := io.Copy(osutil.ContextWriter(ctx), dumbReader{}) 58 c.Assert(err, check.Equals, context.Canceled) 59 // and nothing was copied 60 c.Check(n, check.Equals, int64(0)) 61 } 62 63 func (ctxSuite) TestWriterSuccess(c *check.C) { 64 ctx, cancel := context.WithTimeout(context.Background(), time.Second/100) 65 defer cancel() 66 // check we can copy if we're quick 67 n, err := io.Copy(osutil.ContextWriter(ctx), strings.NewReader("hello")) 68 c.Check(err, check.IsNil) 69 c.Check(n, check.Equals, int64(len("hello"))) 70 } 71 72 func (ctxSuite) TestRun(c *check.C) { 73 ctx, cancel := context.WithTimeout(context.Background(), time.Second/100) 74 defer cancel() 75 cmd := exec.Command("/bin/sleep", "1") 76 err := osutil.RunWithContext(ctx, cmd) 77 c.Check(err, check.Equals, context.DeadlineExceeded) 78 } 79 80 func (ctxSuite) TestRunRace(c *check.C) { 81 if testing.Short() { 82 c.Skip("skippinng non-short test") 83 } 84 85 // first, time how long /bin/false takes 86 t0 := time.Now() 87 cmderr := exec.Command("/bin/false").Run() 88 dt := time.Since(t0) 89 90 // note in particular the error is not "killed" 91 c.Assert(cmderr, check.ErrorMatches, "exit status 1") 92 failedstr := cmderr.Error() 93 killedstr := context.DeadlineExceeded.Error() 94 95 // now run it in a loop with a deadline of exactly that 96 nkilled := 0 97 nfailed := 0 98 for nfailed == 0 || nkilled == 0 { 99 cmd := exec.Command("/bin/false") 100 ctx, cancel := context.WithTimeout(context.Background(), dt) 101 err := osutil.RunWithContext(ctx, cmd) 102 cancel() 103 switch err.Error() { 104 case killedstr: 105 nkilled++ 106 case failedstr: 107 nfailed++ 108 default: 109 // if the error is anything other than due to the context 110 // being done, or the command failing, there's a bug. 111 c.Fatalf("expected %q or %q, got %q", failedstr, killedstr, err) 112 } 113 } 114 } 115 116 func (ctxSuite) TestRunDone(c *check.C) { 117 ctx, cancel := context.WithCancel(context.Background()) 118 cancel() 119 cmd := exec.Command("/bin/sleep", "1") 120 err := osutil.RunWithContext(ctx, cmd) 121 c.Check(err, check.Equals, context.Canceled) 122 } 123 124 func (ctxSuite) TestRunSuccess(c *check.C) { 125 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 126 defer cancel() 127 cmd := exec.Command("/bin/sleep", "0.01") 128 err := osutil.RunWithContext(ctx, cmd) 129 c.Check(err, check.IsNil) 130 } 131 132 func (ctxSuite) TestRunSuccessfulFailure(c *check.C) { 133 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 134 defer cancel() 135 cmd := exec.Command("not/something/you/can/run") 136 err := osutil.RunWithContext(ctx, cmd) 137 c.Check(err, check.ErrorMatches, `fork/exec \S+: no such file or directory`) 138 }