github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/state/state_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2020 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  	"bytes"
    24  	"errors"
    25  	"fmt"
    26  	"testing"
    27  	"time"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/overlord/state"
    32  )
    33  
    34  func TestState(t *testing.T) { TestingT(t) }
    35  
    36  type stateSuite struct{}
    37  
    38  var _ = Suite(&stateSuite{})
    39  
    40  type mgrState1 struct {
    41  	A string
    42  }
    43  
    44  type Count2 struct {
    45  	B int
    46  }
    47  
    48  type mgrState2 struct {
    49  	C *Count2
    50  }
    51  
    52  func (ss *stateSuite) TestLockUnlock(c *C) {
    53  	st := state.New(nil)
    54  	st.Lock()
    55  	st.Unlock()
    56  }
    57  
    58  func (ss *stateSuite) TestGetAndSet(c *C) {
    59  	st := state.New(nil)
    60  	st.Lock()
    61  	defer st.Unlock()
    62  
    63  	mSt1 := &mgrState1{A: "foo"}
    64  	st.Set("mgr1", mSt1)
    65  	mSt2 := &mgrState2{C: &Count2{B: 42}}
    66  	st.Set("mgr2", mSt2)
    67  
    68  	var mSt1B mgrState1
    69  	err := st.Get("mgr1", &mSt1B)
    70  	c.Assert(err, IsNil)
    71  	c.Check(&mSt1B, DeepEquals, mSt1)
    72  
    73  	var mSt2B mgrState2
    74  	err = st.Get("mgr2", &mSt2B)
    75  	c.Assert(err, IsNil)
    76  	c.Check(&mSt2B, DeepEquals, mSt2)
    77  }
    78  
    79  func (ss *stateSuite) TestSetPanic(c *C) {
    80  	st := state.New(nil)
    81  	st.Lock()
    82  	defer st.Unlock()
    83  
    84  	unsupported := struct {
    85  		Ch chan bool
    86  	}{}
    87  	c.Check(func() { st.Set("mgr9", unsupported) }, PanicMatches, `internal error: could not marshal value for state entry "mgr9": json: unsupported type:.*`)
    88  }
    89  
    90  func (ss *stateSuite) TestGetNoState(c *C) {
    91  	st := state.New(nil)
    92  	st.Lock()
    93  	defer st.Unlock()
    94  
    95  	var mSt1B mgrState1
    96  	err := st.Get("mgr9", &mSt1B)
    97  	c.Check(err, Equals, state.ErrNoState)
    98  }
    99  
   100  func (ss *stateSuite) TestSetToNilDeletes(c *C) {
   101  	st := state.New(nil)
   102  	st.Lock()
   103  	defer st.Unlock()
   104  
   105  	st.Set("a", map[string]int{"a": 1})
   106  	var v map[string]int
   107  	err := st.Get("a", &v)
   108  	c.Assert(err, IsNil)
   109  	c.Check(v, HasLen, 1)
   110  
   111  	st.Set("a", nil)
   112  
   113  	var v1 map[string]int
   114  	err = st.Get("a", &v1)
   115  	c.Check(err, Equals, state.ErrNoState)
   116  	c.Check(v1, HasLen, 0)
   117  }
   118  
   119  func (ss *stateSuite) TestNullMeansNoState(c *C) {
   120  	buf := bytes.NewBufferString(`{"data": {"a": null}}`)
   121  	st, err := state.ReadState(nil, buf)
   122  	c.Assert(err, IsNil)
   123  
   124  	st.Lock()
   125  	defer st.Unlock()
   126  
   127  	var v1 map[string]int
   128  	err = st.Get("a", &v1)
   129  	c.Check(err, Equals, state.ErrNoState)
   130  	c.Check(v1, HasLen, 0)
   131  }
   132  
   133  func (ss *stateSuite) TestGetUnmarshalProblem(c *C) {
   134  	st := state.New(nil)
   135  	st.Lock()
   136  	defer st.Unlock()
   137  
   138  	mismatched := struct {
   139  		A int
   140  	}{A: 22}
   141  	st.Set("mgr9", &mismatched)
   142  
   143  	var mSt1B mgrState1
   144  	err := st.Get("mgr9", &mSt1B)
   145  	c.Check(err, ErrorMatches, `internal error: could not unmarshal state entry "mgr9": json: cannot unmarshal .*`)
   146  }
   147  
   148  func (ss *stateSuite) TestCache(c *C) {
   149  	st := state.New(nil)
   150  	st.Lock()
   151  	defer st.Unlock()
   152  
   153  	type key1 struct{}
   154  	type key2 struct{}
   155  
   156  	c.Assert(st.Cached(key1{}), Equals, nil)
   157  
   158  	st.Cache(key1{}, "value1")
   159  	st.Cache(key2{}, "value2")
   160  	c.Assert(st.Cached(key1{}), Equals, "value1")
   161  	c.Assert(st.Cached(key2{}), Equals, "value2")
   162  
   163  	st.Cache(key1{}, nil)
   164  	c.Assert(st.Cached(key1{}), Equals, nil)
   165  
   166  	_, ok := st.Cached("key3").(string)
   167  	c.Assert(ok, Equals, false)
   168  }
   169  
   170  type fakeStateBackend struct {
   171  	checkpoints      [][]byte
   172  	error            func() error
   173  	ensureBefore     time.Duration
   174  	restartRequested bool
   175  }
   176  
   177  func (b *fakeStateBackend) Checkpoint(data []byte) error {
   178  	b.checkpoints = append(b.checkpoints, data)
   179  	if b.error != nil {
   180  		return b.error()
   181  	}
   182  	return nil
   183  }
   184  
   185  func (b *fakeStateBackend) EnsureBefore(d time.Duration) {
   186  	b.ensureBefore = d
   187  }
   188  
   189  func (b *fakeStateBackend) RequestRestart(t state.RestartType) {
   190  	b.restartRequested = true
   191  }
   192  
   193  func (ss *stateSuite) TestImplicitCheckpointAndRead(c *C) {
   194  	b := new(fakeStateBackend)
   195  	st := state.New(b)
   196  	st.Lock()
   197  
   198  	st.Set("v", 1)
   199  	mSt1 := &mgrState1{A: "foo"}
   200  	st.Set("mgr1", mSt1)
   201  	mSt2 := &mgrState2{C: &Count2{B: 42}}
   202  	st.Set("mgr2", mSt2)
   203  
   204  	// implicit checkpoint
   205  	st.Unlock()
   206  
   207  	c.Assert(b.checkpoints, HasLen, 1)
   208  
   209  	buf := bytes.NewBuffer(b.checkpoints[0])
   210  
   211  	st2, err := state.ReadState(nil, buf)
   212  	c.Assert(err, IsNil)
   213  	c.Assert(st2.Modified(), Equals, false)
   214  
   215  	st2.Lock()
   216  	defer st2.Unlock()
   217  
   218  	var v int
   219  	err = st2.Get("v", &v)
   220  	c.Assert(err, IsNil)
   221  	c.Check(v, Equals, 1)
   222  
   223  	var mSt1B mgrState1
   224  	err = st2.Get("mgr1", &mSt1B)
   225  	c.Assert(err, IsNil)
   226  	c.Check(&mSt1B, DeepEquals, mSt1)
   227  
   228  	var mSt2B mgrState2
   229  	err = st2.Get("mgr2", &mSt2B)
   230  	c.Assert(err, IsNil)
   231  	c.Check(&mSt2B, DeepEquals, mSt2)
   232  }
   233  
   234  func (ss *stateSuite) TestImplicitCheckpointRetry(c *C) {
   235  	restore := state.MockCheckpointRetryDelay(2*time.Millisecond, 1*time.Second)
   236  	defer restore()
   237  
   238  	retries := 0
   239  	boom := errors.New("boom")
   240  	error := func() error {
   241  		retries++
   242  		if retries == 2 {
   243  			return nil
   244  		}
   245  		return boom
   246  	}
   247  	b := &fakeStateBackend{error: error}
   248  	st := state.New(b)
   249  	st.Lock()
   250  
   251  	// implicit checkpoint will retry
   252  	st.Unlock()
   253  
   254  	c.Check(retries, Equals, 2)
   255  }
   256  
   257  func (ss *stateSuite) TestImplicitCheckpointPanicsAfterFailedRetries(c *C) {
   258  	restore := state.MockCheckpointRetryDelay(2*time.Millisecond, 80*time.Millisecond)
   259  	defer restore()
   260  
   261  	boom := errors.New("boom")
   262  	retries := 0
   263  	errFn := func() error {
   264  		retries++
   265  		return boom
   266  	}
   267  	b := &fakeStateBackend{error: errFn}
   268  	st := state.New(b)
   269  	st.Lock()
   270  
   271  	// implicit checkpoint will panic after all failed retries
   272  	t0 := time.Now()
   273  	c.Check(func() { st.Unlock() }, PanicMatches, "cannot checkpoint even after 80ms of retries every 2ms: boom")
   274  	// we did at least a couple
   275  	c.Check(retries > 2, Equals, true, Commentf("expected more than 2 retries got %v", retries))
   276  	c.Check(time.Since(t0) > 80*time.Millisecond, Equals, true)
   277  }
   278  
   279  func (ss *stateSuite) TestImplicitCheckpointModifiedOnly(c *C) {
   280  	restore := state.MockCheckpointRetryDelay(2*time.Millisecond, 1*time.Second)
   281  	defer restore()
   282  
   283  	b := &fakeStateBackend{}
   284  	st := state.New(b)
   285  	st.Lock()
   286  	st.Unlock()
   287  	st.Lock()
   288  	st.Unlock()
   289  
   290  	c.Assert(b.checkpoints, HasLen, 1)
   291  
   292  	st.Lock()
   293  	st.Set("foo", "bar")
   294  	st.Unlock()
   295  
   296  	c.Assert(b.checkpoints, HasLen, 2)
   297  }
   298  
   299  func (ss *stateSuite) TestNewChangeAndChanges(c *C) {
   300  	st := state.New(nil)
   301  	st.Lock()
   302  	defer st.Unlock()
   303  
   304  	chg1 := st.NewChange("install", "...")
   305  	chg2 := st.NewChange("remove", "...")
   306  
   307  	chgs := st.Changes()
   308  	c.Check(chgs, HasLen, 2)
   309  
   310  	expected := map[string]*state.Change{
   311  		chg1.ID(): chg1,
   312  		chg2.ID(): chg2,
   313  	}
   314  
   315  	for _, chg := range chgs {
   316  		c.Check(chg, Equals, expected[chg.ID()])
   317  		c.Check(st.Change(chg.ID()), Equals, chg)
   318  	}
   319  
   320  	c.Check(st.Change("no-such-id"), IsNil)
   321  }
   322  
   323  func (ss *stateSuite) TestNewChangeAndCheckpoint(c *C) {
   324  	b := new(fakeStateBackend)
   325  	st := state.New(b)
   326  	st.Lock()
   327  
   328  	chg := st.NewChange("install", "summary")
   329  	c.Assert(chg, NotNil)
   330  	chgID := chg.ID()
   331  	chg.Set("a", 1)
   332  	chg.SetStatus(state.ErrorStatus)
   333  
   334  	spawnTime := chg.SpawnTime()
   335  	readyTime := chg.ReadyTime()
   336  
   337  	// implicit checkpoint
   338  	st.Unlock()
   339  
   340  	c.Assert(b.checkpoints, HasLen, 1)
   341  
   342  	buf := bytes.NewBuffer(b.checkpoints[0])
   343  
   344  	st2, err := state.ReadState(nil, buf)
   345  	c.Assert(err, IsNil)
   346  	c.Assert(st2, NotNil)
   347  
   348  	st2.Lock()
   349  	defer st2.Unlock()
   350  
   351  	chgs := st2.Changes()
   352  
   353  	c.Assert(chgs, HasLen, 1)
   354  
   355  	chg0 := chgs[0]
   356  	c.Check(chg0.ID(), Equals, chgID)
   357  	c.Check(chg0.Kind(), Equals, "install")
   358  	c.Check(chg0.Summary(), Equals, "summary")
   359  	c.Check(chg0.SpawnTime().Equal(spawnTime), Equals, true)
   360  	c.Check(chg0.ReadyTime().Equal(readyTime), Equals, true)
   361  
   362  	var v int
   363  	err = chg0.Get("a", &v)
   364  	c.Check(err, IsNil)
   365  	c.Check(v, Equals, 1)
   366  
   367  	c.Check(chg0.Status(), Equals, state.ErrorStatus)
   368  
   369  	select {
   370  	case <-chg0.Ready():
   371  	default:
   372  		c.Errorf("Change didn't preserve Ready channel closed after deserialization")
   373  	}
   374  }
   375  
   376  func (ss *stateSuite) TestNewChangeAndCheckpointTaskDerivedStatus(c *C) {
   377  	b := new(fakeStateBackend)
   378  	st := state.New(b)
   379  	st.Lock()
   380  
   381  	chg := st.NewChange("install", "summary")
   382  	c.Assert(chg, NotNil)
   383  	chgID := chg.ID()
   384  
   385  	t1 := st.NewTask("download", "1...")
   386  	t1.SetStatus(state.DoneStatus)
   387  	chg.AddTask(t1)
   388  
   389  	// implicit checkpoint
   390  	st.Unlock()
   391  
   392  	c.Assert(b.checkpoints, HasLen, 1)
   393  	buf := bytes.NewBuffer(b.checkpoints[0])
   394  
   395  	st2, err := state.ReadState(nil, buf)
   396  	c.Assert(err, IsNil)
   397  
   398  	st2.Lock()
   399  	defer st2.Unlock()
   400  
   401  	chgs := st2.Changes()
   402  
   403  	c.Assert(chgs, HasLen, 1)
   404  
   405  	chg0 := chgs[0]
   406  	c.Check(chg0.ID(), Equals, chgID)
   407  	c.Check(chg0.Status(), Equals, state.DoneStatus)
   408  
   409  	select {
   410  	case <-chg0.Ready():
   411  	default:
   412  		c.Errorf("Change didn't preserve Ready channel closed after deserialization")
   413  	}
   414  }
   415  
   416  func (ss *stateSuite) TestNewTaskAndCheckpoint(c *C) {
   417  	b := new(fakeStateBackend)
   418  	st := state.New(b)
   419  	st.Lock()
   420  
   421  	chg := st.NewChange("install", "summary")
   422  	c.Assert(chg, NotNil)
   423  
   424  	t1 := st.NewTask("download", "1...")
   425  	chg.AddTask(t1)
   426  	t1ID := t1.ID()
   427  	t1.Set("a", 1)
   428  	t1.SetStatus(state.DoneStatus)
   429  	t1.SetProgress("snap", 5, 10)
   430  	t1.JoinLane(42)
   431  	t1.JoinLane(43)
   432  
   433  	t2 := st.NewTask("inst", "2...")
   434  	chg.AddTask(t2)
   435  	t2ID := t2.ID()
   436  	t2.WaitFor(t1)
   437  	schedule := time.Now().Add(time.Hour)
   438  	t2.At(schedule)
   439  
   440  	// implicit checkpoint
   441  	st.Unlock()
   442  
   443  	c.Assert(b.checkpoints, HasLen, 1)
   444  
   445  	buf := bytes.NewBuffer(b.checkpoints[0])
   446  
   447  	st2, err := state.ReadState(nil, buf)
   448  	c.Assert(err, IsNil)
   449  	c.Assert(st2, NotNil)
   450  
   451  	st2.Lock()
   452  	defer st2.Unlock()
   453  
   454  	chgs := st2.Changes()
   455  	c.Assert(chgs, HasLen, 1)
   456  	chg0 := chgs[0]
   457  
   458  	tasks0 := make(map[string]*state.Task)
   459  	for _, t := range chg0.Tasks() {
   460  		tasks0[t.ID()] = t
   461  	}
   462  	c.Assert(tasks0, HasLen, 2)
   463  
   464  	task0_1 := tasks0[t1ID]
   465  	c.Check(task0_1.ID(), Equals, t1ID)
   466  	c.Check(task0_1.Kind(), Equals, "download")
   467  	c.Check(task0_1.Summary(), Equals, "1...")
   468  	c.Check(task0_1.Change(), Equals, chg0)
   469  
   470  	var v int
   471  	err = task0_1.Get("a", &v)
   472  	c.Check(err, IsNil)
   473  	c.Check(v, Equals, 1)
   474  
   475  	c.Check(task0_1.Status(), Equals, state.DoneStatus)
   476  
   477  	_, cur, tot := task0_1.Progress()
   478  	c.Check(cur, Equals, 5)
   479  	c.Check(tot, Equals, 10)
   480  
   481  	c.Assert(task0_1.Lanes(), DeepEquals, []int{42, 43})
   482  
   483  	task0_2 := tasks0[t2ID]
   484  	c.Check(task0_2.WaitTasks(), DeepEquals, []*state.Task{task0_1})
   485  
   486  	c.Check(task0_1.HaltTasks(), DeepEquals, []*state.Task{task0_2})
   487  
   488  	tasks2 := make(map[string]*state.Task)
   489  	for _, t := range st2.Tasks() {
   490  		tasks2[t.ID()] = t
   491  	}
   492  	c.Assert(tasks2, HasLen, 2)
   493  
   494  	c.Check(task0_1.AtTime().IsZero(), Equals, true)
   495  	c.Check(task0_2.AtTime().Equal(schedule), Equals, true)
   496  }
   497  
   498  func (ss *stateSuite) TestEmptyStateDataAndCheckpointReadAndSet(c *C) {
   499  	b := new(fakeStateBackend)
   500  	st := state.New(b)
   501  	st.Lock()
   502  
   503  	chg := st.NewChange("install", "summary")
   504  	c.Assert(chg, NotNil)
   505  
   506  	// implicit checkpoint
   507  	st.Unlock()
   508  
   509  	c.Assert(b.checkpoints, HasLen, 1)
   510  
   511  	buf := bytes.NewBuffer(b.checkpoints[0])
   512  
   513  	st2, err := state.ReadState(nil, buf)
   514  	c.Assert(err, IsNil)
   515  	c.Assert(st2, NotNil)
   516  
   517  	st2.Lock()
   518  	defer st2.Unlock()
   519  
   520  	// no crash
   521  	st2.Set("a", 1)
   522  }
   523  
   524  func (ss *stateSuite) TestEmptyTaskAndChangeDataAndCheckpointReadAndSet(c *C) {
   525  	b := new(fakeStateBackend)
   526  	st := state.New(b)
   527  	st.Lock()
   528  
   529  	t1 := st.NewTask("1...", "...")
   530  	t1ID := t1.ID()
   531  	chg := st.NewChange("chg", "...")
   532  	chgID := chg.ID()
   533  	chg.AddTask(t1)
   534  
   535  	// implicit checkpoint
   536  	st.Unlock()
   537  
   538  	c.Assert(b.checkpoints, HasLen, 1)
   539  
   540  	buf := bytes.NewBuffer(b.checkpoints[0])
   541  
   542  	st2, err := state.ReadState(nil, buf)
   543  	c.Assert(err, IsNil)
   544  	c.Assert(st2, NotNil)
   545  
   546  	st2.Lock()
   547  	defer st2.Unlock()
   548  
   549  	chg2 := st2.Change(chgID)
   550  	t1_2 := st2.Task(t1ID)
   551  	c.Assert(t1_2, NotNil)
   552  
   553  	// no crash
   554  	chg2.Set("c", 1)
   555  	// no crash either
   556  	t1_2.Set("t", 1)
   557  }
   558  
   559  func (ss *stateSuite) TestEnsureBefore(c *C) {
   560  	b := new(fakeStateBackend)
   561  	st := state.New(b)
   562  
   563  	st.EnsureBefore(10 * time.Second)
   564  
   565  	c.Check(b.ensureBefore, Equals, 10*time.Second)
   566  }
   567  
   568  func (ss *stateSuite) TestCheckpointPreserveLastIds(c *C) {
   569  	b := new(fakeStateBackend)
   570  	st := state.New(b)
   571  	st.Lock()
   572  
   573  	st.NewChange("install", "...")
   574  	st.NewTask("download", "...")
   575  	st.NewTask("download", "...")
   576  
   577  	c.Assert(st.NewLane(), Equals, 1)
   578  
   579  	// implicit checkpoint
   580  	st.Unlock()
   581  
   582  	c.Assert(b.checkpoints, HasLen, 1)
   583  
   584  	buf := bytes.NewBuffer(b.checkpoints[0])
   585  
   586  	st2, err := state.ReadState(nil, buf)
   587  	c.Assert(err, IsNil)
   588  
   589  	st2.Lock()
   590  	defer st2.Unlock()
   591  
   592  	c.Assert(st2.NewTask("download", "...").ID(), Equals, "3")
   593  	c.Assert(st2.NewChange("install", "...").ID(), Equals, "2")
   594  
   595  	c.Assert(st2.NewLane(), Equals, 2)
   596  
   597  }
   598  
   599  func (ss *stateSuite) TestCheckpointPreserveCleanStatus(c *C) {
   600  	b := new(fakeStateBackend)
   601  	st := state.New(b)
   602  	st.Lock()
   603  
   604  	chg := st.NewChange("install", "...")
   605  	t := st.NewTask("download", "...")
   606  	chg.AddTask(t)
   607  	t.SetStatus(state.DoneStatus)
   608  	t.SetClean()
   609  
   610  	// implicit checkpoint
   611  	st.Unlock()
   612  
   613  	c.Assert(b.checkpoints, HasLen, 1)
   614  
   615  	buf := bytes.NewBuffer(b.checkpoints[0])
   616  
   617  	st2, err := state.ReadState(nil, buf)
   618  	c.Assert(err, IsNil)
   619  
   620  	st2.Lock()
   621  	defer st2.Unlock()
   622  
   623  	chg2 := st2.Change(chg.ID())
   624  	t2 := st2.Task(t.ID())
   625  
   626  	c.Assert(chg2.IsClean(), Equals, true)
   627  	c.Assert(t2.IsClean(), Equals, true)
   628  }
   629  
   630  func (ss *stateSuite) TestNewTaskAndTasks(c *C) {
   631  	st := state.New(nil)
   632  	st.Lock()
   633  	defer st.Unlock()
   634  
   635  	chg1 := st.NewChange("install", "...")
   636  	t11 := st.NewTask("check", "...")
   637  	chg1.AddTask(t11)
   638  	t12 := st.NewTask("inst", "...")
   639  	chg1.AddTask(t12)
   640  
   641  	chg2 := st.NewChange("remove", "...")
   642  	t21 := st.NewTask("check", "...")
   643  	t22 := st.NewTask("rm", "...")
   644  	chg2.AddTask(t21)
   645  	chg2.AddTask(t22)
   646  
   647  	tasks := st.Tasks()
   648  	c.Check(tasks, HasLen, 4)
   649  
   650  	expected := map[string]*state.Task{
   651  		t11.ID(): t11,
   652  		t12.ID(): t12,
   653  		t21.ID(): t21,
   654  		t22.ID(): t22,
   655  	}
   656  
   657  	for _, t := range tasks {
   658  		c.Check(t, Equals, expected[t.ID()])
   659  	}
   660  }
   661  
   662  func (ss *stateSuite) TestTaskNoTask(c *C) {
   663  	st := state.New(nil)
   664  	st.Lock()
   665  	defer st.Unlock()
   666  
   667  	c.Check(st.Task("1"), IsNil)
   668  }
   669  
   670  func (ss *stateSuite) TestNewTaskHiddenUntilLinked(c *C) {
   671  	st := state.New(nil)
   672  	st.Lock()
   673  	defer st.Unlock()
   674  
   675  	t1 := st.NewTask("check", "...")
   676  
   677  	tasks := st.Tasks()
   678  	c.Check(tasks, HasLen, 0)
   679  
   680  	c.Check(st.Task(t1.ID()), IsNil)
   681  }
   682  
   683  func (ss *stateSuite) TestMethodEntrance(c *C) {
   684  	st := state.New(&fakeStateBackend{})
   685  
   686  	// Reset modified flag.
   687  	st.Lock()
   688  	st.Unlock()
   689  
   690  	writes := []func(){
   691  		func() { st.Set("foo", 1) },
   692  		func() { st.NewChange("install", "...") },
   693  		func() { st.NewTask("download", "...") },
   694  		func() { st.UnmarshalJSON(nil) },
   695  		func() { st.NewLane() },
   696  		func() { st.Warnf("hello") },
   697  		func() { st.OkayWarnings(time.Time{}) },
   698  		func() { st.UnshowAllWarnings() },
   699  	}
   700  
   701  	reads := []func(){
   702  		func() { st.Get("foo", nil) },
   703  		func() { st.Cached("foo") },
   704  		func() { st.Cache("foo", 1) },
   705  		func() { st.Changes() },
   706  		func() { st.Change("foo") },
   707  		func() { st.Tasks() },
   708  		func() { st.Task("foo") },
   709  		func() { st.MarshalJSON() },
   710  		func() { st.Prune(time.Now(), time.Hour, time.Hour, 100) },
   711  		func() { st.TaskCount() },
   712  		func() { st.AllWarnings() },
   713  		func() { st.PendingWarnings() },
   714  		func() { st.WarningsSummary() },
   715  	}
   716  
   717  	for i, f := range reads {
   718  		c.Logf("Testing read function #%d", i)
   719  		c.Assert(f, PanicMatches, "internal error: accessing state without lock")
   720  		c.Assert(st.Modified(), Equals, false)
   721  	}
   722  
   723  	for i, f := range writes {
   724  		st.Lock()
   725  		st.Unlock()
   726  		c.Assert(st.Modified(), Equals, false)
   727  
   728  		c.Logf("Testing write function #%d", i)
   729  		c.Assert(f, PanicMatches, "internal error: accessing state without lock")
   730  		c.Assert(st.Modified(), Equals, true)
   731  	}
   732  }
   733  
   734  func (ss *stateSuite) TestPrune(c *C) {
   735  	st := state.New(&fakeStateBackend{})
   736  	st.Lock()
   737  	defer st.Unlock()
   738  
   739  	now := time.Now()
   740  	pruneWait := 1 * time.Hour
   741  	abortWait := 3 * time.Hour
   742  
   743  	unset := time.Time{}
   744  
   745  	t1 := st.NewTask("foo", "...")
   746  	t2 := st.NewTask("foo", "...")
   747  	t3 := st.NewTask("foo", "...")
   748  	t4 := st.NewTask("foo", "...")
   749  
   750  	chg1 := st.NewChange("abort", "...")
   751  	chg1.AddTask(t1)
   752  	state.MockChangeTimes(chg1, now.Add(-abortWait), unset)
   753  
   754  	chg2 := st.NewChange("prune", "...")
   755  	chg2.AddTask(t2)
   756  	c.Assert(chg2.Status(), Equals, state.DoStatus)
   757  	state.MockChangeTimes(chg2, now.Add(-pruneWait), now.Add(-pruneWait))
   758  
   759  	chg3 := st.NewChange("ready-but-recent", "...")
   760  	chg3.AddTask(t3)
   761  	state.MockChangeTimes(chg3, now.Add(-pruneWait), now.Add(-pruneWait/2))
   762  
   763  	chg4 := st.NewChange("old-but-not-ready", "...")
   764  	chg4.AddTask(t4)
   765  	state.MockChangeTimes(chg4, now.Add(-pruneWait/2), unset)
   766  
   767  	// unlinked task
   768  	t5 := st.NewTask("unliked", "...")
   769  	c.Check(st.Task(t5.ID()), IsNil)
   770  	state.MockTaskTimes(t5, now.Add(-pruneWait), now.Add(-pruneWait))
   771  
   772  	// two warnings, one expired
   773  	st.AddWarning("hello", now, never, time.Nanosecond, state.DefaultRepeatAfter)
   774  	st.Warnf("hello again")
   775  
   776  	past := time.Now().AddDate(-1, 0, 0)
   777  	st.Prune(past, pruneWait, abortWait, 100)
   778  
   779  	c.Assert(st.Change(chg1.ID()), Equals, chg1)
   780  	c.Assert(st.Change(chg2.ID()), IsNil)
   781  	c.Assert(st.Change(chg3.ID()), Equals, chg3)
   782  	c.Assert(st.Change(chg4.ID()), Equals, chg4)
   783  
   784  	c.Assert(st.Task(t1.ID()), Equals, t1)
   785  	c.Assert(st.Task(t2.ID()), IsNil)
   786  	c.Assert(st.Task(t3.ID()), Equals, t3)
   787  	c.Assert(st.Task(t4.ID()), Equals, t4)
   788  
   789  	c.Assert(chg1.Status(), Equals, state.HoldStatus)
   790  	c.Assert(chg3.Status(), Equals, state.DoStatus)
   791  	c.Assert(chg4.Status(), Equals, state.DoStatus)
   792  
   793  	c.Assert(t1.Status(), Equals, state.HoldStatus)
   794  	c.Assert(t3.Status(), Equals, state.DoStatus)
   795  	c.Assert(t4.Status(), Equals, state.DoStatus)
   796  
   797  	c.Check(st.TaskCount(), Equals, 3)
   798  
   799  	c.Check(st.AllWarnings(), HasLen, 1)
   800  }
   801  
   802  func (ss *stateSuite) TestPruneEmptyChange(c *C) {
   803  	// Empty changes are a bit special because they start out on Hold
   804  	// which is a Ready status, but the change itself is not considered Ready
   805  	// explicitly because that's how every change that will have tasks added
   806  	// to it starts their life.
   807  	st := state.New(&fakeStateBackend{})
   808  	st.Lock()
   809  	defer st.Unlock()
   810  
   811  	now := time.Now()
   812  	pruneWait := 1 * time.Hour
   813  	abortWait := 3 * time.Hour
   814  
   815  	chg := st.NewChange("abort", "...")
   816  	state.MockChangeTimes(chg, now.Add(-pruneWait), time.Time{})
   817  
   818  	past := time.Now().AddDate(-1, 0, 0)
   819  	st.Prune(past, pruneWait, abortWait, 100)
   820  	c.Assert(st.Change(chg.ID()), IsNil)
   821  }
   822  
   823  func (ss *stateSuite) TestPruneMaxChangesHappy(c *C) {
   824  	st := state.New(&fakeStateBackend{})
   825  	st.Lock()
   826  	defer st.Unlock()
   827  
   828  	now := time.Now()
   829  	pruneWait := 1 * time.Hour
   830  	abortWait := 3 * time.Hour
   831  
   832  	// create 10 changes, chg0 is freshest, chg9 is oldest, but
   833  	// all changes are not old enough for pruneWait
   834  	for i := 0; i < 10; i++ {
   835  		chg := st.NewChange(fmt.Sprintf("chg%d", i), "...")
   836  		t := st.NewTask("foo", "...")
   837  		chg.AddTask(t)
   838  		t.SetStatus(state.DoneStatus)
   839  
   840  		when := time.Duration(i) * time.Second
   841  		state.MockChangeTimes(chg, now.Add(-when), now.Add(-when))
   842  	}
   843  	c.Assert(st.Changes(), HasLen, 10)
   844  
   845  	// and 5 more, all not ready
   846  	for i := 10; i < 15; i++ {
   847  		chg := st.NewChange(fmt.Sprintf("chg%d", i), "...")
   848  		t := st.NewTask("foo", "...")
   849  		chg.AddTask(t)
   850  	}
   851  
   852  	// test that nothing is done when we are within pruneWait and
   853  	// maxReadyChanges
   854  	past := time.Now().AddDate(-1, 0, 0)
   855  	maxReadyChanges := 100
   856  	st.Prune(past, pruneWait, abortWait, maxReadyChanges)
   857  	c.Assert(st.Changes(), HasLen, 15)
   858  
   859  	// but with maxReadyChanges we remove the ready ones
   860  	maxReadyChanges = 5
   861  	st.Prune(past, pruneWait, abortWait, maxReadyChanges)
   862  	c.Assert(st.Changes(), HasLen, 10)
   863  	remaining := map[string]bool{}
   864  	for _, chg := range st.Changes() {
   865  		remaining[chg.Kind()] = true
   866  	}
   867  	c.Check(remaining, DeepEquals, map[string]bool{
   868  		// ready and fresh
   869  		"chg0": true,
   870  		"chg1": true,
   871  		"chg2": true,
   872  		"chg3": true,
   873  		"chg4": true,
   874  		// not ready
   875  		"chg10": true,
   876  		"chg11": true,
   877  		"chg12": true,
   878  		"chg13": true,
   879  		"chg14": true,
   880  	})
   881  }
   882  
   883  func (ss *stateSuite) TestPruneMaxChangesSomeNotReady(c *C) {
   884  	st := state.New(&fakeStateBackend{})
   885  	st.Lock()
   886  	defer st.Unlock()
   887  
   888  	// 10 changes, none ready
   889  	for i := 0; i < 10; i++ {
   890  		chg := st.NewChange(fmt.Sprintf("chg%d", i), "...")
   891  		t := st.NewTask("foo", "...")
   892  		chg.AddTask(t)
   893  	}
   894  	c.Assert(st.Changes(), HasLen, 10)
   895  
   896  	// nothing can be pruned
   897  	past := time.Now().AddDate(-1, 0, 0)
   898  	maxChanges := 5
   899  	st.Prune(past, 1*time.Hour, 3*time.Hour, maxChanges)
   900  	c.Assert(st.Changes(), HasLen, 10)
   901  }
   902  
   903  func (ss *stateSuite) TestPruneMaxChangesHonored(c *C) {
   904  	st := state.New(&fakeStateBackend{})
   905  	st.Lock()
   906  	defer st.Unlock()
   907  
   908  	// 10 changes, none ready
   909  	for i := 0; i < 10; i++ {
   910  		chg := st.NewChange(fmt.Sprintf("chg%d", i), "not-ready")
   911  		t := st.NewTask("foo", "not-readly")
   912  		chg.AddTask(t)
   913  	}
   914  	c.Assert(st.Changes(), HasLen, 10)
   915  
   916  	// one extra change that just now entered ready state
   917  	chg := st.NewChange(fmt.Sprintf("chg99"), "so-ready")
   918  	t := st.NewTask("foo", "so-ready")
   919  	when := 1 * time.Second
   920  	state.MockChangeTimes(chg, time.Now().Add(-when), time.Now().Add(-when))
   921  	t.SetStatus(state.DoneStatus)
   922  	chg.AddTask(t)
   923  
   924  	// we have 11 changes in total, 10 not-ready, 1 ready
   925  	//
   926  	// this test we do not purge the freshly ready change
   927  	maxChanges := 10
   928  	past := time.Now().AddDate(-1, 0, 0)
   929  	st.Prune(past, 1*time.Hour, 3*time.Hour, maxChanges)
   930  	c.Assert(st.Changes(), HasLen, 11)
   931  }
   932  
   933  func (ss *stateSuite) TestPruneHonorsStartOperationTime(c *C) {
   934  	st := state.New(&fakeStateBackend{})
   935  	st.Lock()
   936  	defer st.Unlock()
   937  
   938  	now := time.Now()
   939  
   940  	startTime := 2 * time.Hour
   941  	spawnTime := 10 * time.Hour
   942  	pruneWait := 1 * time.Hour
   943  	abortWait := 3 * time.Hour
   944  
   945  	chg := st.NewChange("change", "...")
   946  	t := st.NewTask("foo", "")
   947  	chg.AddTask(t)
   948  	// change spawned 10h ago
   949  	state.MockChangeTimes(chg, now.Add(-spawnTime), time.Time{})
   950  
   951  	// start operation time is 2h ago, change is not aborted because
   952  	// it's less than abortWait limit.
   953  	opTime := now.Add(-startTime)
   954  	st.Prune(opTime, pruneWait, abortWait, 100)
   955  	c.Assert(st.Changes(), HasLen, 1)
   956  	c.Check(chg.Status(), Equals, state.DoStatus)
   957  
   958  	// start operation time is 9h ago, change is aborted.
   959  	startTime = 9 * time.Hour
   960  	opTime = time.Now().Add(-startTime)
   961  	st.Prune(opTime, pruneWait, abortWait, 100)
   962  	c.Assert(st.Changes(), HasLen, 1)
   963  	c.Check(chg.Status(), Equals, state.HoldStatus)
   964  }
   965  
   966  func (ss *stateSuite) TestRequestRestart(c *C) {
   967  	b := new(fakeStateBackend)
   968  	st := state.New(b)
   969  
   970  	ok, t := st.Restarting()
   971  	c.Check(ok, Equals, false)
   972  	c.Check(t, Equals, state.RestartUnset)
   973  
   974  	st.RequestRestart(state.RestartDaemon)
   975  
   976  	c.Check(b.restartRequested, Equals, true)
   977  
   978  	ok, t = st.Restarting()
   979  	c.Check(ok, Equals, true)
   980  	c.Check(t, Equals, state.RestartDaemon)
   981  }
   982  
   983  func (ss *stateSuite) TestRequestRestartSystemAndVerifyReboot(c *C) {
   984  	b := new(fakeStateBackend)
   985  	st := state.New(b)
   986  
   987  	st.Lock()
   988  	err := st.VerifyReboot("boot-id-1")
   989  	st.Unlock()
   990  	c.Assert(err, IsNil)
   991  
   992  	ok, t := st.Restarting()
   993  	c.Check(ok, Equals, false)
   994  	c.Check(t, Equals, state.RestartUnset)
   995  
   996  	st.Lock()
   997  	st.RequestRestart(state.RestartSystem)
   998  	st.Unlock()
   999  
  1000  	c.Check(b.restartRequested, Equals, true)
  1001  
  1002  	ok, t = st.Restarting()
  1003  	c.Check(ok, Equals, true)
  1004  	c.Check(t, Equals, state.RestartSystem)
  1005  
  1006  	var fromBootID string
  1007  	st.Lock()
  1008  	c.Check(st.Get("system-restart-from-boot-id", &fromBootID), IsNil)
  1009  	st.Unlock()
  1010  	c.Check(fromBootID, Equals, "boot-id-1")
  1011  
  1012  	st.Lock()
  1013  	err = st.VerifyReboot("boot-id-1")
  1014  	st.Unlock()
  1015  	c.Check(err, Equals, state.ErrExpectedReboot)
  1016  
  1017  	st.Lock()
  1018  	err = st.VerifyReboot("boot-id-2")
  1019  	st.Unlock()
  1020  	c.Assert(err, IsNil)
  1021  	st.Lock()
  1022  	c.Check(st.Get("system-restart-from-boot-id", &fromBootID), Equals, state.ErrNoState)
  1023  	st.Unlock()
  1024  }
  1025  
  1026  func (ss *stateSuite) TestReadStateInitsCache(c *C) {
  1027  	st, err := state.ReadState(nil, bytes.NewBufferString("{}"))
  1028  	c.Assert(err, IsNil)
  1029  	st.Lock()
  1030  	defer st.Unlock()
  1031  
  1032  	st.Cache("key", "value")
  1033  	c.Assert(st.Cached("key"), Equals, "value")
  1034  }
  1035  
  1036  func (ss *stateSuite) TestTimingsSupport(c *C) {
  1037  	st := state.New(nil)
  1038  	st.Lock()
  1039  	defer st.Unlock()
  1040  
  1041  	var tims []int
  1042  
  1043  	err := st.GetMaybeTimings(&tims)
  1044  	c.Assert(err, IsNil)
  1045  	c.Check(tims, IsNil)
  1046  
  1047  	st.SaveTimings([]int{1, 2, 3})
  1048  
  1049  	err = st.GetMaybeTimings(&tims)
  1050  	c.Assert(err, IsNil)
  1051  	c.Check(tims, DeepEquals, []int{1, 2, 3})
  1052  }