github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/standby/standby_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 /* 3 * Copyright (C) 2018 Canonical Ltd 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 3 as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * 17 */ 18 package standby_test 19 20 import ( 21 "testing" 22 "time" 23 24 . "gopkg.in/check.v1" 25 26 "github.com/snapcore/snapd/overlord/standby" 27 "github.com/snapcore/snapd/overlord/state" 28 ) 29 30 // Hook up v1 into the "go test" runner 31 func Test(t *testing.T) { TestingT(t) } 32 33 type standbySuite struct { 34 state *state.State 35 36 canStandby bool 37 } 38 39 var _ = Suite(&standbySuite{}) 40 41 func (s *standbySuite) SetUpTest(c *C) { 42 s.state = state.New(nil) 43 } 44 45 func (s *standbySuite) TestCanStandbyNoChanges(c *C) { 46 m := standby.New(s.state) 47 c.Check(m.CanStandby(), Equals, false) 48 49 m.SetStartTime(time.Time{}) 50 c.Check(m.CanStandby(), Equals, true) 51 } 52 53 func (s *standbySuite) TestCanStandbyPendingChanges(c *C) { 54 st := s.state 55 st.Lock() 56 chg := st.NewChange("foo", "fake change") 57 chg.AddTask(st.NewTask("bar", "fake task")) 58 c.Assert(chg.Status(), Equals, state.DoStatus) 59 st.Unlock() 60 61 m := standby.New(s.state) 62 m.SetStartTime(time.Time{}) 63 c.Check(m.CanStandby(), Equals, false) 64 } 65 66 func (s *standbySuite) TestCanStandbyPendingClean(c *C) { 67 st := s.state 68 st.Lock() 69 t := st.NewTask("bar", "fake task") 70 chg := st.NewChange("foo", "fake change") 71 chg.AddTask(t) 72 t.SetStatus(state.DoneStatus) 73 c.Assert(chg.Status(), Equals, state.DoneStatus) 74 c.Assert(t.IsClean(), Equals, false) 75 st.Unlock() 76 77 m := standby.New(s.state) 78 m.SetStartTime(time.Time{}) 79 c.Check(m.CanStandby(), Equals, false) 80 } 81 82 func (s *standbySuite) TestCanStandbyOnlyDonePendingChanges(c *C) { 83 st := s.state 84 st.Lock() 85 t := st.NewTask("bar", "fake task") 86 chg := st.NewChange("foo", "fake change") 87 chg.AddTask(t) 88 t.SetStatus(state.DoneStatus) 89 t.SetClean() 90 c.Assert(chg.Status(), Equals, state.DoneStatus) 91 c.Assert(t.IsClean(), Equals, true) 92 st.Unlock() 93 94 m := standby.New(s.state) 95 m.SetStartTime(time.Time{}) 96 c.Check(m.CanStandby(), Equals, true) 97 } 98 99 func (s *standbySuite) CanStandby() bool { 100 return s.canStandby 101 } 102 103 func (s *standbySuite) TestCanStandbyWithOpinion(c *C) { 104 m := standby.New(s.state) 105 m.AddOpinion(s) 106 m.SetStartTime(time.Time{}) 107 108 s.canStandby = true 109 c.Check(m.CanStandby(), Equals, true) 110 111 s.canStandby = false 112 c.Check(m.CanStandby(), Equals, false) 113 } 114 115 type opine func() bool 116 117 func (f opine) CanStandby() bool { 118 return f() 119 } 120 121 func (s *standbySuite) TestStartChecks(c *C) { 122 n := 0 123 // opinions 124 ch1 := make(chan bool, 1) 125 // sync with request restart 126 ch2 := make(chan struct{}) 127 128 defer standby.MockStandbyWait(time.Millisecond)() 129 defer standby.MockStateRequestRestart(func(_ *state.State, t state.RestartType) { 130 c.Check(t, Equals, state.RestartSocket) 131 n++ 132 ch2 <- struct{}{} 133 })() 134 135 m := standby.New(s.state) 136 m.AddOpinion(opine(func() bool { 137 opinion := <-ch1 138 return opinion 139 })) 140 141 m.Start() 142 ch1 <- false 143 c.Check(n, Equals, 0) 144 ch1 <- false 145 c.Check(n, Equals, 0) 146 147 ch1 <- true 148 <-ch2 149 c.Check(n, Equals, 1) 150 // no more opinions 151 close(ch1) 152 153 m.Stop() 154 close(ch2) 155 } 156 157 func (s *standbySuite) TestStopWaits(c *C) { 158 defer standby.MockStandbyWait(time.Millisecond)() 159 defer standby.MockStateRequestRestart(func(*state.State, state.RestartType) { 160 c.Fatal("request restart should have not been called") 161 })() 162 163 ch := make(chan struct{}) 164 opineReady := make(chan struct{}) 165 done := make(chan struct{}) 166 m := standby.New(s.state) 167 synced := false 168 m.AddOpinion(opine(func() bool { 169 if !synced { 170 // synchronize with the main goroutine only at the 171 // beginning 172 close(opineReady) 173 synced = true 174 } 175 select { 176 case <-time.After(200 * time.Millisecond): 177 case <-done: 178 } 179 return false 180 })) 181 182 m.Start() 183 184 // let the opinionator start its delay 185 <-opineReady 186 go func() { 187 // this will block until standby stops 188 m.Stop() 189 close(ch) 190 }() 191 192 select { 193 case <-time.After(100 * time.Millisecond): 194 // wheee 195 case <-ch: 196 c.Fatal("stop should have blocked and didn't") 197 } 198 199 close(done) 200 201 // wait for Stop to complete now 202 select { 203 case <-ch: 204 // nothing to do here 205 case <-time.After(10 * time.Second): 206 c.Fatal("stop did not complete") 207 } 208 }