github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/patch/patch_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 patch_test 21 22 import ( 23 "fmt" 24 "sort" 25 "testing" 26 27 . "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/overlord/patch" 30 "github.com/snapcore/snapd/overlord/state" 31 "github.com/snapcore/snapd/snap" 32 "github.com/snapcore/snapd/snapdtool" 33 ) 34 35 func Test(t *testing.T) { TestingT(t) } 36 37 type patchSuite struct { 38 restoreSanitize func() 39 } 40 41 var _ = Suite(&patchSuite{}) 42 43 func (s *patchSuite) SetUpTest(c *C) { 44 s.restoreSanitize = snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 45 } 46 47 func (s *patchSuite) TearDownTest(c *C) { 48 s.restoreSanitize() 49 } 50 51 func (s *patchSuite) TestInit(c *C) { 52 restore := patch.Mock(2, 1, nil) 53 defer restore() 54 55 st := state.New(nil) 56 patch.Init(st) 57 58 st.Lock() 59 defer st.Unlock() 60 var patchLevel int 61 err := st.Get("patch-level", &patchLevel) 62 c.Assert(err, IsNil) 63 c.Check(patchLevel, Equals, 2) 64 65 var patchSublevel int 66 c.Assert(st.Get("patch-sublevel", &patchSublevel), IsNil) 67 c.Check(patchSublevel, Equals, 1) 68 } 69 70 func (s *patchSuite) TestNothingToDo(c *C) { 71 restore := patch.Mock(2, 1, nil) 72 defer restore() 73 74 st := state.New(nil) 75 st.Lock() 76 st.Set("patch-level", 2) 77 st.Unlock() 78 err := patch.Apply(st) 79 c.Assert(err, IsNil) 80 } 81 82 func (s *patchSuite) TestNoDowngrade(c *C) { 83 restore := patch.Mock(2, 0, nil) 84 defer restore() 85 86 st := state.New(nil) 87 st.Lock() 88 st.Set("patch-level", 3) 89 st.Unlock() 90 err := patch.Apply(st) 91 c.Assert(err, ErrorMatches, `cannot downgrade: snapd is too old for the current system state \(patch level 3\)`) 92 } 93 94 func (s *patchSuite) TestApply(c *C) { 95 p12 := func(st *state.State) error { 96 var n int 97 st.Get("n", &n) 98 st.Set("n", n+1) 99 return nil 100 } 101 p121 := func(st *state.State) error { 102 var o int 103 st.Get("o", &o) 104 st.Set("o", o+1) 105 return nil 106 } 107 p23 := func(st *state.State) error { 108 var n int 109 st.Get("n", &n) 110 st.Set("n", n*10) 111 return nil 112 } 113 114 // patch level 3, sublevel 1 115 restore := patch.Mock(3, 1, map[int][]patch.PatchFunc{ 116 2: {p12, p121}, 117 3: {p23}, 118 }) 119 defer restore() 120 121 st := state.New(nil) 122 st.Lock() 123 st.Set("patch-level", 1) 124 st.Unlock() 125 err := patch.Apply(st) 126 c.Assert(err, IsNil) 127 128 st.Lock() 129 defer st.Unlock() 130 131 var level int 132 err = st.Get("patch-level", &level) 133 c.Assert(err, IsNil) 134 c.Check(level, Equals, 3) 135 136 var sublevel int 137 c.Assert(st.Get("patch-sublevel", &sublevel), IsNil) 138 c.Check(sublevel, Equals, 0) 139 140 var n, o int 141 err = st.Get("n", &n) 142 c.Assert(err, IsNil) 143 c.Check(n, Equals, 10) 144 145 c.Assert(st.Get("o", &o), IsNil) 146 c.Assert(o, Equals, 1) 147 } 148 149 func (s *patchSuite) TestApplyLevel6(c *C) { 150 var sequence []int 151 p50 := generatePatchFunc(50, &sequence) 152 p60 := generatePatchFunc(60, &sequence) 153 p61 := generatePatchFunc(61, &sequence) 154 155 restore := patch.Mock(6, 1, map[int][]patch.PatchFunc{ 156 5: {p50}, 157 6: {p60, p61}, 158 }) 159 defer restore() 160 161 // simulate the special case where sublevel is introduced for system that's already on patch level 6. 162 // only p61 patch should be applied. 163 st := state.New(nil) 164 st.Lock() 165 st.Set("patch-level", 6) 166 st.Unlock() 167 c.Assert(patch.Apply(st), IsNil) 168 169 st.Lock() 170 defer st.Unlock() 171 172 var level, sublevel int 173 c.Assert(sequence, DeepEquals, []int{61}) 174 c.Assert(st.Get("patch-level", &level), IsNil) 175 c.Assert(st.Get("patch-sublevel", &sublevel), IsNil) 176 c.Check(level, Equals, 6) 177 c.Check(sublevel, Equals, 1) 178 } 179 180 func (s *patchSuite) TestApplyFromSublevel(c *C) { 181 var sequence []int 182 p60 := generatePatchFunc(60, &sequence) 183 p61 := generatePatchFunc(61, &sequence) 184 p62 := generatePatchFunc(62, &sequence) 185 p70 := generatePatchFunc(70, &sequence) 186 p71 := generatePatchFunc(71, &sequence) 187 188 restore := patch.Mock(7, 1, map[int][]patch.PatchFunc{ 189 6: {p60, p61, p62}, 190 7: {p70, p71}, 191 }) 192 defer restore() 193 194 // we'll be patching from 6.0 -> 7.1 195 st := state.New(nil) 196 st.Lock() 197 st.Set("patch-level", 6) 198 st.Set("patch-sublevel", 0) 199 st.Unlock() 200 c.Assert(patch.Apply(st), IsNil) 201 202 st.Lock() 203 204 var level, sublevel int 205 c.Assert(st.Get("patch-level", &level), IsNil) 206 c.Assert(st.Get("patch-sublevel", &sublevel), IsNil) 207 c.Check(level, Equals, 7) 208 c.Check(sublevel, Equals, 1) 209 c.Assert(sequence, DeepEquals, []int{61, 62, 70, 71}) 210 211 // now patching from 7.1 -> 7.2 212 sequence = []int{} 213 p72 := generatePatchFunc(72, &sequence) 214 patch.Mock(7, 2, map[int][]patch.PatchFunc{ 215 6: {p60, p61, p62}, 216 7: {p70, p71, p72}, 217 }) 218 219 st.Unlock() 220 c.Assert(patch.Apply(st), IsNil) 221 c.Assert(sequence, DeepEquals, []int{72}) 222 223 st.Lock() 224 defer st.Unlock() 225 226 c.Assert(st.Get("patch-level", &level), IsNil) 227 c.Assert(st.Get("patch-sublevel", &sublevel), IsNil) 228 c.Check(level, Equals, 7) 229 c.Check(sublevel, Equals, 2) 230 } 231 232 func (s *patchSuite) TestMissing(c *C) { 233 restore := patch.Mock(3, 0, map[int][]patch.PatchFunc{ 234 3: {func(s *state.State) error { return nil }}, 235 }) 236 defer restore() 237 238 st := state.New(nil) 239 st.Lock() 240 st.Set("patch-level", 1) 241 st.Unlock() 242 err := patch.Apply(st) 243 c.Assert(err, ErrorMatches, `cannot upgrade: snapd is too new for the current system state \(patch level 1\)`) 244 } 245 246 func (s *patchSuite) TestDowngradeSublevel(c *C) { 247 restore := patch.Mock(3, 1, map[int][]patch.PatchFunc{ 248 3: {func(s *state.State) error { return nil }}, 249 }) 250 defer restore() 251 252 st := state.New(nil) 253 st.Lock() 254 st.Set("patch-level", 3) 255 st.Set("patch-sublevel", 6) 256 st.Unlock() 257 258 // we're at patch level 3, sublevel 6 according to state, but the implemented level is 3,1 259 c.Assert(patch.Apply(st), IsNil) 260 261 st.Lock() 262 defer st.Unlock() 263 var level, sublevel int 264 c.Assert(st.Get("patch-level", &level), IsNil) 265 c.Assert(st.Get("patch-sublevel", &sublevel), IsNil) 266 c.Check(level, Equals, 3) 267 c.Check(sublevel, Equals, 1) 268 } 269 270 func (s *patchSuite) TestError(c *C) { 271 p12 := func(st *state.State) error { 272 var n int 273 st.Get("n", &n) 274 st.Set("n", n+1) 275 return nil 276 } 277 p23 := func(st *state.State) error { 278 var n int 279 st.Get("n", &n) 280 st.Set("n", n*10) 281 return fmt.Errorf("boom") 282 } 283 p34 := func(st *state.State) error { 284 var n int 285 st.Get("n", &n) 286 st.Set("n", n*100) 287 return nil 288 } 289 restore := patch.Mock(3, 0, map[int][]patch.PatchFunc{ 290 2: {p12}, 291 3: {p23}, 292 4: {p34}, 293 }) 294 defer restore() 295 296 st := state.New(nil) 297 st.Lock() 298 st.Set("patch-level", 1) 299 st.Unlock() 300 err := patch.Apply(st) 301 c.Assert(err, ErrorMatches, `cannot patch system state to level 3, sublevel 0: boom`) 302 303 st.Lock() 304 defer st.Unlock() 305 306 var level int 307 err = st.Get("patch-level", &level) 308 c.Assert(err, IsNil) 309 c.Check(level, Equals, 2) 310 311 var n int 312 err = st.Get("n", &n) 313 c.Assert(err, IsNil) 314 c.Check(n, Equals, 10) 315 } 316 317 func (s *patchSuite) testMaybeResetPatchLevel6(c *C, snapdVersion, lastVersion string, expectedPatches []int) { 318 var sequence []int 319 320 snapdtool.MockVersion(snapdVersion) 321 322 p60 := generatePatchFunc(60, &sequence) 323 p61 := generatePatchFunc(61, &sequence) 324 p62 := generatePatchFunc(62, &sequence) 325 326 restore := patch.Mock(6, 2, map[int][]patch.PatchFunc{ 327 6: {p60, p61, p62}, 328 }) 329 330 defer restore() 331 332 st := state.New(nil) 333 st.Lock() 334 335 if lastVersion != "" { 336 st.Set("patch-sublevel-last-version", lastVersion) 337 } 338 st.Set("patch-level", 6) 339 st.Set("patch-sublevel", 2) 340 341 st.Unlock() 342 343 c.Assert(patch.Apply(st), IsNil) 344 c.Assert(sequence, DeepEquals, expectedPatches) 345 346 st.Lock() 347 defer st.Unlock() 348 var level, sublevel int 349 var ver string 350 var lastRefresh interface{} 351 c.Assert(st.Get("patch-level", &level), IsNil) 352 c.Assert(st.Get("patch-sublevel", &sublevel), IsNil) 353 c.Assert(st.Get("patch-sublevel-last-version", &ver), IsNil) 354 c.Assert(st.Get("patch-sublevel-reset", &lastRefresh), Equals, state.ErrNoState) 355 c.Check(ver, Equals, "snapd-version-1") 356 c.Check(level, Equals, 6) 357 c.Check(sublevel, Equals, 2) 358 } 359 360 func (s *patchSuite) TestSameSnapdVersionLvl60PatchesNotApplied(c *C) { 361 // sublevel patches not applied if snapd version is same 362 s.testMaybeResetPatchLevel6(c, "snapd-version-1", "snapd-version-1", nil) 363 } 364 365 func (s *patchSuite) TestDifferentSnapdVersionPatchLevel6NoLastVersion(c *C) { 366 s.testMaybeResetPatchLevel6(c, "snapd-version-1", "", []int{61, 62}) 367 } 368 369 func (s *patchSuite) TestDifferentSnapdVersionPatchLevel6(c *C) { 370 s.testMaybeResetPatchLevel6(c, "snapd-version-1", "snapd-version-2", []int{61, 62}) 371 } 372 373 func (s *patchSuite) TestSanity(c *C) { 374 patches := patch.PatchesForTest() 375 levels := make([]int, 0, len(patches)) 376 for l := range patches { 377 levels = append(levels, l) 378 } 379 sort.Ints(levels) 380 // all steps present 381 for i, level := range levels { 382 c.Check(level, Equals, i+1) 383 } 384 // ends at implemented patch level 385 c.Check(levels[len(levels)-1], Equals, patch.Level) 386 387 // Sublevel matches the number of patches for last Level. 388 c.Check(len(patches[patch.Level])-1, Equals, patch.Sublevel) 389 } 390 391 func generatePatchFunc(testValue int, sequence *[]int) patch.PatchFunc { 392 return func(st *state.State) error { 393 *sequence = append(*sequence, testValue) 394 return nil 395 } 396 }