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  }