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 }