github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/cmdstate/cmdstate_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 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 cmdstate_test
    21  
    22  import (
    23  	"path/filepath"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/dirs"
    31  	"github.com/snapcore/snapd/osutil"
    32  	"github.com/snapcore/snapd/overlord"
    33  	"github.com/snapcore/snapd/overlord/cmdstate"
    34  	"github.com/snapcore/snapd/overlord/state"
    35  )
    36  
    37  // hook up gocheck to testing
    38  func TestCommand(t *testing.T) { check.TestingT(t) }
    39  
    40  type cmdSuite struct {
    41  	rootdir string
    42  	state   *state.State
    43  	se      *overlord.StateEngine
    44  	manager overlord.StateManager
    45  	restore func()
    46  }
    47  
    48  var _ = check.Suite(&cmdSuite{})
    49  
    50  type statr interface {
    51  	Status() state.Status
    52  }
    53  
    54  func (s *cmdSuite) waitfor(thing statr) {
    55  	s.state.Unlock()
    56  	for i := 0; i < 5; i++ {
    57  		s.se.Ensure()
    58  		s.se.Wait()
    59  		s.state.Lock()
    60  		if thing.Status().Ready() {
    61  			return
    62  		}
    63  		s.state.Unlock()
    64  	}
    65  	s.state.Lock()
    66  }
    67  
    68  func (s *cmdSuite) SetUpTest(c *check.C) {
    69  	d := c.MkDir()
    70  	dirs.SetRootDir(d)
    71  	s.rootdir = d
    72  	s.state = state.New(nil)
    73  	s.se = overlord.NewStateEngine(s.state)
    74  	runner := state.NewTaskRunner(s.state)
    75  	s.manager = cmdstate.Manager(s.state, runner)
    76  	s.se.AddManager(s.manager)
    77  	s.se.AddManager(runner)
    78  	c.Assert(s.se.StartUp(), check.IsNil)
    79  	s.restore = cmdstate.MockDefaultExecTimeout(time.Second / 10)
    80  }
    81  
    82  func (s *cmdSuite) TearDownTest(c *check.C) {
    83  	s.restore()
    84  }
    85  
    86  func (s *cmdSuite) TestExecTask(c *check.C) {
    87  	s.state.Lock()
    88  	defer s.state.Unlock()
    89  	argvIn := []string{"/bin/echo", "hello"}
    90  	tasks := cmdstate.ExecWithTimeout(s.state, "this is the summary", argvIn, time.Second/10).Tasks()
    91  	c.Assert(tasks, check.HasLen, 1)
    92  	task := tasks[0]
    93  	c.Check(task.Kind(), check.Equals, "exec-command")
    94  
    95  	var argvOut []string
    96  	c.Check(task.Get("argv", &argvOut), check.IsNil)
    97  	c.Check(argvOut, check.DeepEquals, argvIn)
    98  }
    99  
   100  func (s *cmdSuite) TestExecHappy(c *check.C) {
   101  	s.state.Lock()
   102  	defer s.state.Unlock()
   103  
   104  	fn := filepath.Join(s.rootdir, "flag")
   105  	ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"touch", fn}, time.Second/10)
   106  	chg := s.state.NewChange("do-the-thing", "Doing the thing")
   107  	chg.AddAll(ts)
   108  
   109  	s.waitfor(chg)
   110  
   111  	c.Check(osutil.FileExists(fn), check.Equals, true)
   112  	c.Check(chg.Status(), check.Equals, state.DoneStatus)
   113  }
   114  
   115  func (s *cmdSuite) TestExecSad(c *check.C) {
   116  	s.state.Lock()
   117  	defer s.state.Unlock()
   118  
   119  	ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"sh", "-c", "echo hello; false"}, time.Second/10)
   120  	chg := s.state.NewChange("do-the-thing", "Doing the thing")
   121  	chg.AddAll(ts)
   122  
   123  	s.waitfor(chg)
   124  
   125  	c.Check(chg.Status(), check.Equals, state.ErrorStatus)
   126  }
   127  
   128  func (s *cmdSuite) TestExecAbort(c *check.C) {
   129  	s.state.Lock()
   130  	defer s.state.Unlock()
   131  
   132  	ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"sleep", "1h"}, time.Second/10)
   133  	chg := s.state.NewChange("do-the-thing", "Doing the thing")
   134  	chg.AddAll(ts)
   135  
   136  	s.state.Unlock()
   137  	s.se.Ensure()
   138  	s.state.Lock()
   139  
   140  	c.Assert(chg.Status(), check.Equals, state.DoingStatus)
   141  
   142  	chg.Abort()
   143  
   144  	s.waitfor(chg)
   145  
   146  	c.Check(chg.Status(), check.Equals, state.ErrorStatus)
   147  	c.Check(strings.Join(chg.Tasks()[0].Log(), "\n"), check.Matches, `(?s).*ERROR aborted`)
   148  }
   149  
   150  func (s *cmdSuite) TestExecStop(c *check.C) {
   151  	s.state.Lock()
   152  	defer s.state.Unlock()
   153  
   154  	ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"sleep", "1h"}, time.Second/10)
   155  	chg := s.state.NewChange("do-the-thing", "Doing the thing")
   156  	chg.AddAll(ts)
   157  
   158  	c.Assert(chg.Status(), check.Equals, state.DoStatus)
   159  
   160  	s.state.Unlock()
   161  	s.se.Stop()
   162  	s.state.Lock()
   163  
   164  	c.Check(chg.Status(), check.Equals, state.DoStatus)
   165  	chg.Abort()
   166  }
   167  
   168  func (s *cmdSuite) TestExecTimesOut(c *check.C) {
   169  	s.state.Lock()
   170  	defer s.state.Unlock()
   171  
   172  	ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"sleep", "1m"}, time.Second/10)
   173  	chg := s.state.NewChange("do-the-thing", "Doing the thing")
   174  	chg.AddAll(ts)
   175  
   176  	s.waitfor(chg)
   177  
   178  	c.Check(chg.Status(), check.Equals, state.ErrorStatus)
   179  	c.Check(strings.Join(chg.Tasks()[0].Log(), "\n"), check.Matches, `(?s).*ERROR exceeded maximum runtime.*`)
   180  }
   181  
   182  func (s *cmdSuite) TestExecTimeoutMissing(c *check.C) {
   183  	s.state.Lock()
   184  	defer s.state.Unlock()
   185  
   186  	restore := cmdstate.MockDefaultExecTimeout(1 * time.Second)
   187  	defer restore()
   188  
   189  	ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"sleep", "0.3"}, time.Second/10)
   190  	c.Assert(len(ts.Tasks()), check.Equals, 1)
   191  	t := ts.Tasks()[0]
   192  	// no timeout means the default timeout will be used
   193  	t.Clear("timeout")
   194  	chg := s.state.NewChange("do-the-thing", "Doing the thing")
   195  	chg.AddAll(ts)
   196  
   197  	s.waitfor(chg)
   198  
   199  	// slept for
   200  	c.Check(chg.Status(), check.Equals, state.DoneStatus)
   201  }