github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/state/task_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 state_test 21 22 import ( 23 "encoding/json" 24 "fmt" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/overlord/state" 29 "github.com/snapcore/snapd/testutil" 30 "time" 31 ) 32 33 type taskSuite struct{} 34 35 var _ = Suite(&taskSuite{}) 36 37 func (ts *taskSuite) TestNewTask(c *C) { 38 st := state.New(nil) 39 st.Lock() 40 defer st.Unlock() 41 42 t := st.NewTask("download", "1...") 43 44 c.Check(t.Kind(), Equals, "download") 45 c.Check(t.Summary(), Equals, "1...") 46 } 47 48 func (cs *taskSuite) TestReadyTime(c *C) { 49 st := state.New(nil) 50 st.Lock() 51 defer st.Unlock() 52 53 task := st.NewTask("download", "summary...") 54 55 now := time.Now() 56 57 t := task.SpawnTime() 58 c.Check(t.After(now.Add(-5*time.Second)), Equals, true) 59 c.Check(t.Before(now.Add(5*time.Second)), Equals, true) 60 61 c.Check(task.ReadyTime().IsZero(), Equals, true) 62 63 task.SetStatus(state.DoneStatus) 64 65 t = task.ReadyTime() 66 c.Check(t.After(now.Add(-5*time.Second)), Equals, true) 67 c.Check(t.Before(now.Add(5*time.Second)), Equals, true) 68 } 69 70 func (cs *taskSuite) TestDoingUndoingTime(c *C) { 71 st := state.New(nil) 72 st.Lock() 73 defer st.Unlock() 74 75 task := st.NewTask("download", "summary...") 76 77 task.AccumulateDoingTime(123456) 78 c.Assert(task.DoingTime(), Equals, time.Duration(123456)) 79 80 task.AccumulateUndoingTime(654321) 81 c.Assert(task.UndoingTime(), Equals, time.Duration(654321)) 82 } 83 84 func (ts *taskSuite) TestGetSet(c *C) { 85 st := state.New(nil) 86 st.Lock() 87 defer st.Unlock() 88 89 t := st.NewTask("download", "1...") 90 91 t.Set("a", 1) 92 93 var v int 94 err := t.Get("a", &v) 95 c.Assert(err, IsNil) 96 c.Check(v, Equals, 1) 97 } 98 99 func (ts *taskSuite) TestHas(c *C) { 100 st := state.New(nil) 101 st.Lock() 102 defer st.Unlock() 103 104 t := st.NewTask("download", "1...") 105 c.Check(t.Has("a"), Equals, false) 106 107 t.Set("a", 1) 108 c.Check(t.Has("a"), Equals, true) 109 110 t.Set("a", nil) 111 c.Check(t.Has("a"), Equals, false) 112 } 113 114 func (ts *taskSuite) TestClear(c *C) { 115 st := state.New(nil) 116 st.Lock() 117 defer st.Unlock() 118 119 t := st.NewTask("download", "1...") 120 121 t.Set("a", 1) 122 123 var v int 124 err := t.Get("a", &v) 125 c.Assert(err, IsNil) 126 c.Check(v, Equals, 1) 127 128 t.Clear("a") 129 130 c.Check(t.Get("a", &v), Equals, state.ErrNoState) 131 } 132 133 func (ts *taskSuite) TestStatusAndSetStatus(c *C) { 134 st := state.New(nil) 135 st.Lock() 136 defer st.Unlock() 137 138 t := st.NewTask("download", "1...") 139 140 c.Check(t.Status(), Equals, state.DoStatus) 141 142 t.SetStatus(state.DoneStatus) 143 144 c.Check(t.Status(), Equals, state.DoneStatus) 145 } 146 147 func (ts *taskSuite) TestIsCleanAndSetClean(c *C) { 148 st := state.New(nil) 149 st.Lock() 150 defer st.Unlock() 151 152 t := st.NewTask("download", "1...") 153 154 c.Check(t.IsClean(), Equals, false) 155 156 t.SetStatus(state.DoneStatus) 157 t.SetClean() 158 159 c.Check(t.IsClean(), Equals, true) 160 } 161 162 func jsonStr(m json.Marshaler) string { 163 data, err := m.MarshalJSON() 164 if err != nil { 165 panic(err) 166 } 167 return string(data) 168 } 169 170 func (ts *taskSuite) TestProgressAndSetProgress(c *C) { 171 st := state.New(nil) 172 st.Lock() 173 defer st.Unlock() 174 175 t := st.NewTask("download", "1...") 176 177 t.SetProgress("snap", 2, 99) 178 label, cur, tot := t.Progress() 179 c.Check(label, Equals, "snap") 180 c.Check(cur, Equals, 2) 181 c.Check(tot, Equals, 99) 182 183 t.SetProgress("", 0, 0) 184 label, cur, tot = t.Progress() 185 c.Check(label, Equals, "") 186 c.Check(cur, Equals, 0) 187 c.Check(tot, Equals, 1) 188 c.Check(jsonStr(t), Not(testutil.Contains), "progress") 189 190 t.SetProgress("", 0, -1) 191 _, cur, tot = t.Progress() 192 c.Check(cur, Equals, 0) 193 c.Check(tot, Equals, 1) 194 c.Check(jsonStr(t), Not(testutil.Contains), "progress") 195 196 t.SetProgress("", 0, -1) 197 _, cur, tot = t.Progress() 198 c.Check(cur, Equals, 0) 199 c.Check(tot, Equals, 1) 200 c.Check(jsonStr(t), Not(testutil.Contains), "progress") 201 202 t.SetProgress("", 2, 1) 203 _, cur, tot = t.Progress() 204 c.Check(cur, Equals, 0) 205 c.Check(tot, Equals, 1) 206 c.Check(jsonStr(t), Not(testutil.Contains), "progress") 207 208 t.SetProgress("", 42, 42) 209 _, cur, tot = t.Progress() 210 c.Check(cur, Equals, 42) 211 c.Check(tot, Equals, 42) 212 } 213 214 func (ts *taskSuite) TestProgressDefaults(c *C) { 215 st := state.New(nil) 216 st.Lock() 217 defer st.Unlock() 218 219 t := st.NewTask("download", "1...") 220 221 c.Check(t.Status(), Equals, state.DoStatus) 222 _, cur, tot := t.Progress() 223 c.Check(cur, Equals, 0) 224 c.Check(tot, Equals, 1) 225 226 t.SetStatus(state.DoStatus) 227 _, cur, tot = t.Progress() 228 c.Check(cur, Equals, 0) 229 c.Check(tot, Equals, 1) 230 231 t.SetStatus(state.DoneStatus) 232 _, cur, tot = t.Progress() 233 c.Check(cur, Equals, 1) 234 c.Check(tot, Equals, 1) 235 236 t.SetStatus(state.ErrorStatus) 237 _, cur, tot = t.Progress() 238 c.Check(cur, Equals, 1) 239 c.Check(tot, Equals, 1) 240 } 241 242 func (ts *taskSuite) TestState(c *C) { 243 st := state.New(nil) 244 st.Lock() 245 t := st.NewTask("download", "1...") 246 st.Unlock() 247 248 c.Assert(t.State(), Equals, st) 249 } 250 251 func (ts *taskSuite) TestTaskMarshalsWaitFor(c *C) { 252 st := state.New(nil) 253 st.Lock() 254 defer st.Unlock() 255 256 t1 := st.NewTask("download", "1...") 257 t2 := st.NewTask("install", "2...") 258 t2.WaitFor(t1) 259 260 d, err := t2.MarshalJSON() 261 c.Assert(err, IsNil) 262 263 needle := fmt.Sprintf(`"wait-tasks":["%s"`, t1.ID()) 264 c.Assert(string(d), testutil.Contains, needle) 265 } 266 267 func (ts *taskSuite) TestTaskMarshalsDoingUndoingTime(c *C) { 268 st := state.New(nil) 269 st.Lock() 270 defer st.Unlock() 271 272 t := st.NewTask("download", "1...") 273 t.AccumulateDoingTime(123456) 274 t.AccumulateUndoingTime(654321) 275 276 d, err := t.MarshalJSON() 277 c.Assert(err, IsNil) 278 279 c.Assert(string(d), testutil.Contains, `"doing-time":123456`) 280 c.Assert(string(d), testutil.Contains, `"undoing-time":654321`) 281 } 282 283 func (ts *taskSuite) TestTaskWaitFor(c *C) { 284 st := state.New(nil) 285 st.Lock() 286 defer st.Unlock() 287 288 t1 := st.NewTask("download", "1...") 289 t2 := st.NewTask("install", "2...") 290 t2.WaitFor(t1) 291 292 c.Assert(t2.WaitTasks(), DeepEquals, []*state.Task{t1}) 293 c.Assert(t1.HaltTasks(), DeepEquals, []*state.Task{t2}) 294 c.Assert(t1.NumHaltTasks(), Equals, 1) 295 c.Assert(t2.NumHaltTasks(), Equals, 0) 296 } 297 298 func (ts *taskSuite) TestAt(c *C) { 299 b := new(fakeStateBackend) 300 b.ensureBefore = time.Hour 301 st := state.New(b) 302 st.Lock() 303 defer st.Unlock() 304 305 t := st.NewTask("download", "1...") 306 307 now := time.Now() 308 restore := state.MockTime(now) 309 defer restore() 310 when := now.Add(10 * time.Second) 311 t.At(when) 312 313 c.Check(t.AtTime().Equal(when), Equals, true) 314 c.Check(b.ensureBefore, Equals, 10*time.Second) 315 } 316 317 func (ts *taskSuite) TestAtPast(c *C) { 318 b := new(fakeStateBackend) 319 b.ensureBefore = time.Hour 320 st := state.New(b) 321 st.Lock() 322 defer st.Unlock() 323 324 t := st.NewTask("download", "1...") 325 326 when := time.Now().Add(-10 * time.Second) 327 t.At(when) 328 329 c.Check(t.AtTime().Equal(when), Equals, true) 330 c.Check(b.ensureBefore, Equals, time.Duration(0)) 331 } 332 333 func (ts *taskSuite) TestAtReadyNop(c *C) { 334 b := new(fakeStateBackend) 335 b.ensureBefore = time.Hour 336 st := state.New(b) 337 st.Lock() 338 defer st.Unlock() 339 340 t := st.NewTask("download", "1...") 341 t.SetStatus(state.DoneStatus) 342 343 when := time.Now().Add(10 * time.Second) 344 t.At(when) 345 346 c.Check(t.AtTime().IsZero(), Equals, true) 347 c.Check(b.ensureBefore, Equals, time.Hour) 348 } 349 350 func (cs *taskSuite) TestLogf(c *C) { 351 st := state.New(nil) 352 st.Lock() 353 defer st.Unlock() 354 355 t := st.NewTask("download", "1...") 356 357 for i := 0; i < 20; i++ { 358 t.Logf("Message #%d", i) 359 } 360 361 log := t.Log() 362 c.Assert(log, HasLen, 10) 363 for i := 0; i < 10; i++ { 364 c.Assert(log[i], Matches, fmt.Sprintf("....-..-..T.* INFO Message #%d", i+10)) 365 } 366 } 367 368 func (cs *taskSuite) TestErrorf(c *C) { 369 st := state.New(nil) 370 st.Lock() 371 defer st.Unlock() 372 373 t := st.NewTask("download", "1...") 374 375 t.Errorf("Some %s", "error") 376 c.Assert(t.Log()[0], Matches, "....-..-..T.* ERROR Some error") 377 } 378 379 func (ts *taskSuite) TestTaskMarshalsLog(c *C) { 380 st := state.New(nil) 381 st.Lock() 382 defer st.Unlock() 383 384 t := st.NewTask("download", "1...") 385 t.Logf("foo") 386 387 d, err := t.MarshalJSON() 388 c.Assert(err, IsNil) 389 390 c.Assert(string(d), Matches, `.*"log":\["....-..-..T.* INFO foo"\].*`) 391 } 392 393 // TODO: Better testing of full task roundtripping via JSON. 394 395 func (cs *taskSuite) TestMethodEntrance(c *C) { 396 st := state.New(&fakeStateBackend{}) 397 st.Lock() 398 t1 := st.NewTask("download", "1...") 399 t2 := st.NewTask("install", "2...") 400 st.Unlock() 401 402 writes := []func(){ 403 func() { t1.SetStatus(state.DoneStatus) }, 404 func() { t1.SetClean() }, 405 func() { t1.Set("a", 1) }, 406 func() { t2.WaitFor(t1) }, 407 func() { t1.SetProgress("", 2, 2) }, 408 func() { t1.Logf("") }, 409 func() { t1.Errorf("") }, 410 func() { t1.UnmarshalJSON(nil) }, 411 func() { t1.SetProgress("", 1, 1) }, 412 func() { t1.JoinLane(1) }, 413 func() { t1.AccumulateDoingTime(1) }, 414 func() { t1.AccumulateUndoingTime(2) }, 415 } 416 417 reads := []func(){ 418 func() { t1.Status() }, 419 func() { t1.IsClean() }, 420 func() { t1.Get("a", nil) }, 421 func() { t1.WaitTasks() }, 422 func() { t1.HaltTasks() }, 423 func() { t1.Progress() }, 424 func() { t1.Log() }, 425 func() { t1.MarshalJSON() }, 426 func() { t1.Progress() }, 427 func() { t1.SetProgress("", 0, 1) }, 428 func() { t1.Lanes() }, 429 func() { t1.DoingTime() }, 430 func() { t1.UndoingTime() }, 431 } 432 433 for i, f := range reads { 434 c.Logf("Testing read function #%d", i) 435 c.Assert(f, PanicMatches, "internal error: accessing state without lock") 436 c.Assert(st.Modified(), Equals, false) 437 } 438 439 for i, f := range writes { 440 st.Lock() 441 st.Unlock() 442 c.Assert(st.Modified(), Equals, false) 443 444 c.Logf("Testing write function #%d", i) 445 c.Assert(f, PanicMatches, "internal error: accessing state without lock") 446 c.Assert(st.Modified(), Equals, true) 447 } 448 } 449 450 func (cs *taskSuite) TestNewTaskSet(c *C) { 451 ts0 := state.NewTaskSet() 452 c.Check(ts0.Tasks(), HasLen, 0) 453 454 st := state.New(nil) 455 st.Lock() 456 t1 := st.NewTask("download", "1...") 457 t2 := st.NewTask("install", "2...") 458 ts2 := state.NewTaskSet(t1, t2) 459 st.Unlock() 460 461 c.Assert(ts2.Tasks(), DeepEquals, []*state.Task{t1, t2}) 462 } 463 464 func (ts *taskSuite) TestTaskWaitAll(c *C) { 465 st := state.New(nil) 466 st.Lock() 467 defer st.Unlock() 468 469 t1 := st.NewTask("download", "1...") 470 t2 := st.NewTask("install", "2...") 471 t3 := st.NewTask("setup", "3...") 472 t3.WaitAll(state.NewTaskSet(t1, t2)) 473 474 c.Assert(t3.WaitTasks(), HasLen, 2) 475 c.Assert(t1.HaltTasks(), DeepEquals, []*state.Task{t3}) 476 c.Assert(t2.HaltTasks(), DeepEquals, []*state.Task{t3}) 477 } 478 479 func (ts *taskSuite) TestTaskSetWaitFor(c *C) { 480 st := state.New(nil) 481 st.Lock() 482 defer st.Unlock() 483 484 t1 := st.NewTask("download", "1...") 485 t2 := st.NewTask("install", "2...") 486 t3 := st.NewTask("setup", "3...") 487 ts23 := state.NewTaskSet(t2, t3) 488 ts23.WaitFor(t1) 489 490 c.Assert(t2.WaitTasks(), DeepEquals, []*state.Task{t1}) 491 c.Assert(t3.WaitTasks(), DeepEquals, []*state.Task{t1}) 492 c.Assert(t1.NumHaltTasks(), Equals, 2) 493 } 494 495 func (ts *taskSuite) TestTaskSetWaitAll(c *C) { 496 st := state.New(nil) 497 st.Lock() 498 defer st.Unlock() 499 500 t1 := st.NewTask("download", "1...") 501 t2 := st.NewTask("check", "2...") 502 t3 := st.NewTask("setup", "3...") 503 t4 := st.NewTask("link", "4...") 504 ts12 := state.NewTaskSet(t1, t2) 505 ts34 := state.NewTaskSet(t3, t4) 506 ts34.WaitAll(ts12) 507 508 c.Assert(t3.WaitTasks(), DeepEquals, []*state.Task{t1, t2}) 509 c.Assert(t4.WaitTasks(), DeepEquals, []*state.Task{t1, t2}) 510 c.Assert(t1.NumHaltTasks(), Equals, 2) 511 c.Assert(t2.NumHaltTasks(), Equals, 2) 512 } 513 514 func (ts *taskSuite) TestTaskSetAddTaskAndAddAll(c *C) { 515 st := state.New(nil) 516 st.Lock() 517 defer st.Unlock() 518 519 t1 := st.NewTask("download", "1...") 520 t2 := st.NewTask("check", "2...") 521 t3 := st.NewTask("setup", "3...") 522 t4 := st.NewTask("link", "4...") 523 524 ts0 := state.NewTaskSet(t1) 525 526 ts0.AddTask(t2) 527 ts0.AddAll(state.NewTaskSet(t3, t4)) 528 529 // these do nothing 530 ts0.AddTask(t2) 531 ts0.AddAll(state.NewTaskSet(t3, t4)) 532 533 c.Check(ts0.Tasks(), DeepEquals, []*state.Task{t1, t2, t3, t4}) 534 } 535 536 func (ts *taskSuite) TestLanes(c *C) { 537 st := state.New(nil) 538 st.Lock() 539 defer st.Unlock() 540 541 t := st.NewTask("download", "1...") 542 543 c.Assert(t.Lanes(), DeepEquals, []int{0}) 544 t.JoinLane(1) 545 c.Assert(t.Lanes(), DeepEquals, []int{1}) 546 t.JoinLane(2) 547 c.Assert(t.Lanes(), DeepEquals, []int{1, 2}) 548 } 549 550 func (cs *taskSuite) TestTaskSetEdge(c *C) { 551 st := state.New(nil) 552 st.Lock() 553 defer st.Unlock() 554 555 // setup an example taskset 556 t1 := st.NewTask("download", "1...") 557 t2 := st.NewTask("verify", "2...") 558 t3 := st.NewTask("install", "3...") 559 ts := state.NewTaskSet(t1, t2, t3) 560 561 // edges are just typed strings 562 edge1 := state.TaskSetEdge("on-edge") 563 edge2 := state.TaskSetEdge("eddie") 564 565 // nil task causes panic 566 c.Check(func() { ts.MarkEdge(nil, edge1) }, PanicMatches, `cannot set edge "on-edge" with nil task`) 567 568 // no edge marked yet 569 t, err := ts.Edge(edge1) 570 c.Assert(t, IsNil) 571 c.Assert(err, ErrorMatches, `internal error: missing "on-edge" edge in task set`) 572 t, err = ts.Edge(edge2) 573 c.Assert(t, IsNil) 574 c.Assert(err, ErrorMatches, `internal error: missing "eddie" edge in task set`) 575 576 // one edge 577 ts.MarkEdge(t1, edge1) 578 t, err = ts.Edge(edge1) 579 c.Assert(t, Equals, t1) 580 c.Assert(err, IsNil) 581 582 // two edges 583 ts.MarkEdge(t2, edge2) 584 t, err = ts.Edge(edge1) 585 c.Assert(t, Equals, t1) 586 c.Assert(err, IsNil) 587 t, err = ts.Edge(edge2) 588 c.Assert(t, Equals, t2) 589 c.Assert(err, IsNil) 590 591 // edges can be reassigned 592 ts.MarkEdge(t3, edge1) 593 t, err = ts.Edge(edge1) 594 c.Assert(t, Equals, t3) 595 c.Assert(err, IsNil) 596 } 597 598 func (cs *taskSuite) TestTaskAddAllWithEdges(c *C) { 599 st := state.New(nil) 600 st.Lock() 601 defer st.Unlock() 602 603 edge1 := state.TaskSetEdge("install") 604 605 t1 := st.NewTask("download", "1...") 606 t2 := st.NewTask("verify", "2...") 607 t3 := st.NewTask("install", "3...") 608 ts := state.NewTaskSet(t1, t2, t3) 609 610 ts.MarkEdge(t1, edge1) 611 t, err := ts.Edge(edge1) 612 c.Assert(t, Equals, t1) 613 c.Assert(err, IsNil) 614 615 ts2 := state.NewTaskSet() 616 err = ts2.AddAllWithEdges(ts) 617 c.Assert(err, IsNil) 618 t, err = ts2.Edge(edge1) 619 c.Assert(t, Equals, t1) 620 c.Assert(err, IsNil) 621 622 // doing it again is no harm 623 err = ts2.AddAllWithEdges(ts) 624 c.Assert(err, IsNil) 625 t, err = ts2.Edge(edge1) 626 c.Assert(t, Equals, t1) 627 c.Assert(err, IsNil) 628 629 // but conflicting edges are an error 630 t4 := st.NewTask("another-kind", "4...") 631 tsWithDuplicatedEdge := state.NewTaskSet(t4) 632 tsWithDuplicatedEdge.MarkEdge(t4, edge1) 633 err = ts2.AddAllWithEdges(tsWithDuplicatedEdge) 634 c.Assert(err, ErrorMatches, `cannot add taskset: duplicated edge "install"`) 635 }