github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/overlord/snapstate/handlers_prereq_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017-2018 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 snapstate_test
    21  
    22  import (
    23  	"fmt"
    24  	"os"
    25  	"time"
    26  
    27  	. "gopkg.in/check.v1"
    28  	"gopkg.in/tomb.v2"
    29  
    30  	"github.com/snapcore/snapd/overlord/snapstate"
    31  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    32  	"github.com/snapcore/snapd/overlord/state"
    33  	"github.com/snapcore/snapd/release"
    34  	"github.com/snapcore/snapd/snap"
    35  	"github.com/snapcore/snapd/store"
    36  )
    37  
    38  type prereqSuite struct {
    39  	baseHandlerSuite
    40  
    41  	fakeStore *fakeStore
    42  }
    43  
    44  var _ = Suite(&prereqSuite{})
    45  
    46  func (s *prereqSuite) SetUpTest(c *C) {
    47  	s.setup(c, nil)
    48  
    49  	s.fakeStore = &fakeStore{
    50  		state:       s.state,
    51  		fakeBackend: s.fakeBackend,
    52  	}
    53  	s.state.Lock()
    54  	defer s.state.Unlock()
    55  	snapstate.ReplaceStore(s.state, s.fakeStore)
    56  
    57  	s.state.Set("seeded", true)
    58  	s.state.Set("refresh-privacy-key", "privacy-key")
    59  	s.AddCleanup(snapstatetest.MockDeviceModel(DefaultModel()))
    60  
    61  	restoreCheckFreeSpace := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return nil })
    62  	s.AddCleanup(restoreCheckFreeSpace)
    63  
    64  	restoreInstallSize := snapstate.MockInstallSize(func(st *state.State, snaps []*snap.Info, userID int) (uint64, error) {
    65  		return 0, nil
    66  	})
    67  	s.AddCleanup(restoreInstallSize)
    68  }
    69  
    70  func (s *prereqSuite) TestDoPrereqNothingToDo(c *C) {
    71  	s.state.Lock()
    72  
    73  	si1 := &snap.SideInfo{
    74  		RealName: "core",
    75  		Revision: snap.R(1),
    76  	}
    77  	snapstate.Set(s.state, "core", &snapstate.SnapState{
    78  		Sequence: []*snap.SideInfo{si1},
    79  		Current:  si1.Revision,
    80  	})
    81  
    82  	t := s.state.NewTask("prerequisites", "test")
    83  	t.Set("snap-setup", &snapstate.SnapSetup{
    84  		SideInfo: &snap.SideInfo{
    85  			RealName: "foo",
    86  			Revision: snap.R(33),
    87  		},
    88  	})
    89  	s.state.NewChange("dummy", "...").AddTask(t)
    90  	s.state.Unlock()
    91  
    92  	s.se.Ensure()
    93  	s.se.Wait()
    94  
    95  	s.state.Lock()
    96  	defer s.state.Unlock()
    97  	c.Assert(s.fakeBackend.ops, HasLen, 0)
    98  	c.Check(t.Status(), Equals, state.DoneStatus)
    99  }
   100  
   101  func (s *prereqSuite) TestDoPrereqWithBaseNone(c *C) {
   102  	s.state.Lock()
   103  
   104  	t := s.state.NewTask("prerequisites", "test")
   105  	t.Set("snap-setup", &snapstate.SnapSetup{
   106  		SideInfo: &snap.SideInfo{
   107  			RealName: "foo",
   108  			Revision: snap.R(33),
   109  		},
   110  		Base:   "none",
   111  		Prereq: []string{"prereq1"},
   112  	})
   113  	chg := s.state.NewChange("dummy", "...")
   114  	chg.AddTask(t)
   115  	s.state.Unlock()
   116  
   117  	s.se.Ensure()
   118  	s.se.Wait()
   119  
   120  	s.state.Lock()
   121  	defer s.state.Unlock()
   122  	c.Check(t.Status(), Equals, state.DoneStatus)
   123  
   124  	// check that the do-prereq task added all needed prereqs
   125  	expectedLinkedSnaps := []string{"prereq1", "snapd"}
   126  	linkedSnaps := make([]string, 0, len(expectedLinkedSnaps))
   127  	for _, t := range chg.Tasks() {
   128  		if t.Kind() == "link-snap" {
   129  			snapsup, err := snapstate.TaskSnapSetup(t)
   130  			c.Assert(err, IsNil)
   131  			linkedSnaps = append(linkedSnaps, snapsup.InstanceName())
   132  		}
   133  	}
   134  	c.Check(linkedSnaps, DeepEquals, expectedLinkedSnaps)
   135  }
   136  
   137  func (s *prereqSuite) TestDoPrereqTalksToStoreAndQueues(c *C) {
   138  	s.state.Lock()
   139  
   140  	snapstate.Set(s.state, "core", &snapstate.SnapState{
   141  		Active: true,
   142  		Sequence: []*snap.SideInfo{
   143  			{RealName: "core", Revision: snap.R(1)},
   144  		},
   145  		Current:  snap.R(1),
   146  		SnapType: "os",
   147  	})
   148  
   149  	t := s.state.NewTask("prerequisites", "test")
   150  	t.Set("snap-setup", &snapstate.SnapSetup{
   151  		SideInfo: &snap.SideInfo{
   152  			RealName: "foo",
   153  			Revision: snap.R(33),
   154  		},
   155  		Channel: "beta",
   156  		Base:    "some-base",
   157  		Prereq:  []string{"prereq1", "prereq2"},
   158  	})
   159  	chg := s.state.NewChange("dummy", "...")
   160  	chg.AddTask(t)
   161  	s.state.Unlock()
   162  
   163  	s.se.Ensure()
   164  	s.se.Wait()
   165  
   166  	s.state.Lock()
   167  	defer s.state.Unlock()
   168  	c.Assert(s.fakeBackend.ops, DeepEquals, fakeOps{
   169  		{
   170  			op: "storesvc-snap-action",
   171  		},
   172  		{
   173  			op: "storesvc-snap-action:action",
   174  			action: store.SnapAction{
   175  				Action:       "install",
   176  				InstanceName: "prereq1",
   177  				Channel:      "stable",
   178  			},
   179  			revno: snap.R(11),
   180  		},
   181  		{
   182  			op: "storesvc-snap-action",
   183  		},
   184  		{
   185  			op: "storesvc-snap-action:action",
   186  			action: store.SnapAction{
   187  				Action:       "install",
   188  				InstanceName: "prereq2",
   189  				Channel:      "stable",
   190  			},
   191  			revno: snap.R(11),
   192  		},
   193  		{
   194  			op: "storesvc-snap-action",
   195  		},
   196  		{
   197  			op: "storesvc-snap-action:action",
   198  			action: store.SnapAction{
   199  				Action:       "install",
   200  				InstanceName: "some-base",
   201  				Channel:      "stable",
   202  			},
   203  			revno: snap.R(11),
   204  		},
   205  	})
   206  	c.Check(t.Status(), Equals, state.DoneStatus)
   207  
   208  	// check that the do-prereq task added all needed prereqs
   209  	expectedLinkedSnaps := []string{"prereq1", "prereq2", "some-base"}
   210  	linkedSnaps := make([]string, 0, len(expectedLinkedSnaps))
   211  	for _, t := range chg.Tasks() {
   212  		if t.Kind() == "link-snap" {
   213  			snapsup, err := snapstate.TaskSnapSetup(t)
   214  			c.Assert(err, IsNil)
   215  			linkedSnaps = append(linkedSnaps, snapsup.InstanceName())
   216  		}
   217  	}
   218  	c.Check(linkedSnaps, DeepEquals, expectedLinkedSnaps)
   219  }
   220  
   221  func (s *prereqSuite) TestDoPrereqRetryWhenBaseInFlight(c *C) {
   222  	restore := snapstate.MockPrerequisitesRetryTimeout(1 * time.Millisecond)
   223  	defer restore()
   224  
   225  	var prereqTask *state.Task
   226  
   227  	calls := 0
   228  	s.runner.AddHandler("link-snap",
   229  		func(task *state.Task, _ *tomb.Tomb) error {
   230  			st := task.State()
   231  			st.Lock()
   232  			defer st.Unlock()
   233  
   234  			calls += 1
   235  			if calls == 1 {
   236  				// retry again later, this forces taskrunner
   237  				// to pick prequisites task.
   238  				return &state.Retry{After: 1 * time.Millisecond}
   239  			}
   240  
   241  			// setup everything as if the snap is installed
   242  
   243  			snapsup, _ := snapstate.TaskSnapSetup(task)
   244  			var snapst snapstate.SnapState
   245  			snapstate.Get(st, snapsup.InstanceName(), &snapst)
   246  			snapst.Current = snapsup.Revision()
   247  			snapst.Sequence = append(snapst.Sequence, snapsup.SideInfo)
   248  			snapstate.Set(st, snapsup.InstanceName(), &snapst)
   249  
   250  			// check that prerequisites task is not done yet, it must wait for core.
   251  			// This check guarantees that prerequisites task found link-snap snap
   252  			// task in flight, and returned a retry error, resulting in DoingStatus.
   253  			c.Check(prereqTask.Status(), Equals, state.DoingStatus)
   254  
   255  			return nil
   256  		}, nil)
   257  	s.state.Lock()
   258  	tCore := s.state.NewTask("link-snap", "Pretend core gets installed")
   259  	tCore.Set("snap-setup", &snapstate.SnapSetup{
   260  		SideInfo: &snap.SideInfo{
   261  			RealName: "core",
   262  			Revision: snap.R(11),
   263  		},
   264  	})
   265  
   266  	// pretend foo gets installed and needs core (which is in progress)
   267  	prereqTask = s.state.NewTask("prerequisites", "foo")
   268  	prereqTask.Set("snap-setup", &snapstate.SnapSetup{
   269  		SideInfo: &snap.SideInfo{
   270  			RealName: "foo",
   271  		},
   272  	})
   273  
   274  	chg := s.state.NewChange("dummy", "...")
   275  	chg.AddTask(prereqTask)
   276  	chg.AddTask(tCore)
   277  
   278  	// NOTE: tasks are iterated on in undefined order, we have fixed the
   279  	// link-snap handler to return a 'fake' retry what results
   280  	// 'prerequisites' task handler observing the state of the world we
   281  	// want, even if 'link-snap' ran first
   282  
   283  	// wait, we will hit prereq-retry-timeout eventually
   284  	// (this can take a while on very slow machines)
   285  	for i := 0; i < 500; i++ {
   286  		time.Sleep(1 * time.Millisecond)
   287  		s.state.Unlock()
   288  		s.se.Ensure()
   289  		s.se.Wait()
   290  		s.state.Lock()
   291  		if prereqTask.Status() == state.DoneStatus {
   292  			break
   293  		}
   294  	}
   295  
   296  	// sanity check, exactly two calls to link-snap due to retry error on 1st call
   297  	c.Check(calls, Equals, 2)
   298  
   299  	c.Check(tCore.Status(), Equals, state.DoneStatus)
   300  	c.Check(prereqTask.Status(), Equals, state.DoneStatus)
   301  
   302  	// sanity
   303  	c.Check(chg.Status(), Equals, state.DoneStatus)
   304  }
   305  
   306  func (s *prereqSuite) TestDoPrereqChannelEnvvars(c *C) {
   307  	os.Setenv("SNAPD_BASES_CHANNEL", "edge")
   308  	defer os.Unsetenv("SNAPD_BASES_CHANNEL")
   309  	os.Setenv("SNAPD_PREREQS_CHANNEL", "candidate")
   310  	defer os.Unsetenv("SNAPD_PREREQS_CHANNEL")
   311  	s.state.Lock()
   312  
   313  	snapstate.Set(s.state, "core", &snapstate.SnapState{
   314  		Active: true,
   315  		Sequence: []*snap.SideInfo{
   316  			{RealName: "core", Revision: snap.R(1)},
   317  		},
   318  		Current:  snap.R(1),
   319  		SnapType: "os",
   320  	})
   321  
   322  	t := s.state.NewTask("prerequisites", "test")
   323  	t.Set("snap-setup", &snapstate.SnapSetup{
   324  		SideInfo: &snap.SideInfo{
   325  			RealName: "foo",
   326  			Revision: snap.R(33),
   327  		},
   328  		Channel: "beta",
   329  		Base:    "some-base",
   330  		Prereq:  []string{"prereq1", "prereq2"},
   331  	})
   332  	chg := s.state.NewChange("dummy", "...")
   333  	chg.AddTask(t)
   334  	s.state.Unlock()
   335  
   336  	s.se.Ensure()
   337  	s.se.Wait()
   338  
   339  	s.state.Lock()
   340  	defer s.state.Unlock()
   341  	c.Assert(s.fakeBackend.ops, DeepEquals, fakeOps{
   342  		{
   343  			op: "storesvc-snap-action",
   344  		},
   345  		{
   346  			op: "storesvc-snap-action:action",
   347  			action: store.SnapAction{
   348  				Action:       "install",
   349  				InstanceName: "prereq1",
   350  				Channel:      "candidate",
   351  			},
   352  			revno: snap.R(11),
   353  		},
   354  		{
   355  			op: "storesvc-snap-action",
   356  		},
   357  		{
   358  			op: "storesvc-snap-action:action",
   359  			action: store.SnapAction{
   360  				Action:       "install",
   361  				InstanceName: "prereq2",
   362  				Channel:      "candidate",
   363  			},
   364  			revno: snap.R(11),
   365  		},
   366  		{
   367  			op: "storesvc-snap-action",
   368  		},
   369  		{
   370  			op: "storesvc-snap-action:action",
   371  			action: store.SnapAction{
   372  				Action:       "install",
   373  				InstanceName: "some-base",
   374  				Channel:      "edge",
   375  			},
   376  			revno: snap.R(11),
   377  		},
   378  	})
   379  	c.Check(t.Status(), Equals, state.DoneStatus)
   380  }
   381  
   382  func (s *prereqSuite) TestDoPrereqNothingToDoForBase(c *C) {
   383  	for _, typ := range []snap.Type{
   384  		snap.TypeOS,
   385  		snap.TypeGadget,
   386  		snap.TypeKernel,
   387  		snap.TypeBase,
   388  	} {
   389  
   390  		s.state.Lock()
   391  		t := s.state.NewTask("prerequisites", "test")
   392  		t.Set("snap-setup", &snapstate.SnapSetup{
   393  			SideInfo: &snap.SideInfo{
   394  				RealName: fmt.Sprintf("foo-%s", typ),
   395  				Revision: snap.R(1),
   396  			},
   397  			Type: typ,
   398  		})
   399  		s.state.NewChange("dummy", "...").AddTask(t)
   400  		s.state.Unlock()
   401  
   402  		s.se.Ensure()
   403  		s.se.Wait()
   404  
   405  		s.state.Lock()
   406  		c.Assert(s.fakeBackend.ops, HasLen, 0)
   407  		c.Check(t.Status(), Equals, state.DoneStatus)
   408  		s.state.Unlock()
   409  	}
   410  }
   411  
   412  func (s *prereqSuite) TestDoPrereqNothingToDoForSnapdSnap(c *C) {
   413  	s.state.Lock()
   414  	t := s.state.NewTask("prerequisites", "test")
   415  	t.Set("snap-setup", &snapstate.SnapSetup{
   416  		// type is normally set from snap info at install time
   417  		Type: snap.TypeSnapd,
   418  		SideInfo: &snap.SideInfo{
   419  			RealName: "snapd",
   420  			Revision: snap.R(1),
   421  		},
   422  	})
   423  	s.state.NewChange("dummy", "...").AddTask(t)
   424  	s.state.Unlock()
   425  
   426  	s.se.Ensure()
   427  	s.se.Wait()
   428  
   429  	s.state.Lock()
   430  	c.Assert(s.fakeBackend.ops, HasLen, 0)
   431  	c.Check(t.Status(), Equals, state.DoneStatus)
   432  	s.state.Unlock()
   433  }
   434  
   435  func (s *prereqSuite) TestDoPrereqCore16wCoreNothingToDo(c *C) {
   436  	s.state.Lock()
   437  
   438  	si1 := &snap.SideInfo{
   439  		RealName: "core",
   440  		Revision: snap.R(1),
   441  	}
   442  	snapstate.Set(s.state, "core", &snapstate.SnapState{
   443  		Sequence: []*snap.SideInfo{si1},
   444  		Current:  si1.Revision,
   445  	})
   446  
   447  	t := s.state.NewTask("prerequisites", "test")
   448  	t.Set("snap-setup", &snapstate.SnapSetup{
   449  		SideInfo: &snap.SideInfo{
   450  			RealName: "foo",
   451  			Revision: snap.R(33),
   452  		},
   453  		Base: "core16",
   454  	})
   455  	s.state.NewChange("dummy", "...").AddTask(t)
   456  	s.state.Unlock()
   457  
   458  	s.se.Ensure()
   459  	s.se.Wait()
   460  
   461  	s.state.Lock()
   462  	defer s.state.Unlock()
   463  	c.Assert(s.fakeBackend.ops, HasLen, 0)
   464  	c.Check(t.Status(), Equals, state.DoneStatus)
   465  }
   466  
   467  func (s *prereqSuite) testDoPrereqNoCorePullsInSnaps(c *C, base string) {
   468  	restore := release.MockOnClassic(true)
   469  	defer restore()
   470  
   471  	s.state.Lock()
   472  
   473  	t := s.state.NewTask("prerequisites", "test")
   474  	t.Set("snap-setup", &snapstate.SnapSetup{
   475  		SideInfo: &snap.SideInfo{
   476  			RealName: "foo",
   477  			Revision: snap.R(33),
   478  		},
   479  		Base: base,
   480  	})
   481  	s.state.NewChange("dummy", "...").AddTask(t)
   482  	s.state.Unlock()
   483  
   484  	s.se.Ensure()
   485  	s.se.Wait()
   486  
   487  	s.state.Lock()
   488  	defer s.state.Unlock()
   489  	c.Assert(s.fakeBackend.ops, DeepEquals, fakeOps{
   490  		{
   491  			op: "storesvc-snap-action",
   492  		},
   493  		{
   494  			op: "storesvc-snap-action:action",
   495  			action: store.SnapAction{
   496  				Action:       "install",
   497  				InstanceName: base,
   498  				Channel:      "stable",
   499  			},
   500  			revno: snap.R(11),
   501  		},
   502  		{
   503  			op: "storesvc-snap-action",
   504  		},
   505  		{
   506  			op: "storesvc-snap-action:action",
   507  			action: store.SnapAction{
   508  				Action:       "install",
   509  				InstanceName: "snapd",
   510  				Channel:      "stable",
   511  			},
   512  			revno: snap.R(11),
   513  		},
   514  	})
   515  
   516  	c.Check(t.Change().Err(), IsNil)
   517  	c.Check(t.Status(), Equals, state.DoneStatus)
   518  }
   519  
   520  func (s *prereqSuite) TestDoPrereqCore16noCore(c *C) {
   521  	s.testDoPrereqNoCorePullsInSnaps(c, "core16")
   522  }
   523  
   524  func (s *prereqSuite) TestDoPrereqCore18NoCorePullsInSnapd(c *C) {
   525  	s.testDoPrereqNoCorePullsInSnaps(c, "core18")
   526  }
   527  
   528  func (s *prereqSuite) TestDoPrereqOtherBaseNoCorePullsInSnapd(c *C) {
   529  	s.testDoPrereqNoCorePullsInSnaps(c, "some-base")
   530  }
   531  
   532  func (s *prereqSuite) TestDoPrereqBaseIsNotBase(c *C) {
   533  	s.state.Lock()
   534  
   535  	t := s.state.NewTask("prerequisites", "test")
   536  	t.Set("snap-setup", &snapstate.SnapSetup{
   537  		SideInfo: &snap.SideInfo{
   538  			RealName: "foo",
   539  			Revision: snap.R(33),
   540  		},
   541  		Channel: "beta",
   542  		Base:    "app-snap",
   543  		Prereq:  []string{"prereq1"},
   544  	})
   545  	chg := s.state.NewChange("dummy", "...")
   546  	chg.AddTask(t)
   547  	s.state.Unlock()
   548  
   549  	s.se.Ensure()
   550  	s.se.Wait()
   551  
   552  	s.state.Lock()
   553  	defer s.state.Unlock()
   554  	c.Check(chg.Status(), Equals, state.ErrorStatus)
   555  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*- test \(cannot install snap base "app-snap": unexpected snap type "app", instead of 'base'\)`)
   556  }
   557  
   558  func (s *prereqSuite) TestDoPrereqBaseNoRevision(c *C) {
   559  	os.Setenv("SNAPD_BASES_CHANNEL", "channel-no-revision")
   560  	defer os.Unsetenv("SNAPD_BASES_CHANNEL")
   561  
   562  	s.state.Lock()
   563  
   564  	t := s.state.NewTask("prerequisites", "test")
   565  	t.Set("snap-setup", &snapstate.SnapSetup{
   566  		SideInfo: &snap.SideInfo{
   567  			RealName: "foo",
   568  			Revision: snap.R(33),
   569  		},
   570  		Channel: "beta",
   571  		Base:    "some-base",
   572  		Prereq:  []string{"prereq1"},
   573  	})
   574  	chg := s.state.NewChange("dummy", "...")
   575  	chg.AddTask(t)
   576  	s.state.Unlock()
   577  
   578  	s.se.Ensure()
   579  	s.se.Wait()
   580  
   581  	s.state.Lock()
   582  	defer s.state.Unlock()
   583  	c.Check(chg.Status(), Equals, state.ErrorStatus)
   584  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*- test \(cannot install snap base "some-base": no snap revision available as specified\)`)
   585  }
   586  
   587  func (s *prereqSuite) TestDoPrereqNoRevision(c *C) {
   588  	os.Setenv("SNAPD_PREREQS_CHANNEL", "channel-no-revision")
   589  	defer os.Unsetenv("SNAPD_PREREQS_CHANNEL")
   590  
   591  	s.state.Lock()
   592  
   593  	t := s.state.NewTask("prerequisites", "test")
   594  	t.Set("snap-setup", &snapstate.SnapSetup{
   595  		SideInfo: &snap.SideInfo{
   596  			RealName: "foo",
   597  			Revision: snap.R(33),
   598  		},
   599  		Channel: "beta",
   600  		Prereq:  []string{"prereq1"},
   601  	})
   602  	chg := s.state.NewChange("dummy", "...")
   603  	chg.AddTask(t)
   604  	s.state.Unlock()
   605  
   606  	s.se.Ensure()
   607  	s.se.Wait()
   608  
   609  	s.state.Lock()
   610  	defer s.state.Unlock()
   611  	c.Check(chg.Status(), Equals, state.ErrorStatus)
   612  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*- test \(cannot install prerequisite "prereq1": no snap revision available as specified\)`)
   613  }
   614  
   615  func (s *prereqSuite) TestDoPrereqSnapdNoRevision(c *C) {
   616  	os.Setenv("SNAPD_SNAPD_CHANNEL", "channel-no-revision")
   617  	defer os.Unsetenv("SNAPD_SNAPD_CHANNEL")
   618  
   619  	s.state.Lock()
   620  
   621  	t := s.state.NewTask("prerequisites", "test")
   622  	t.Set("snap-setup", &snapstate.SnapSetup{
   623  		SideInfo: &snap.SideInfo{
   624  			RealName: "foo",
   625  			Revision: snap.R(33),
   626  		},
   627  		Base:    "core18",
   628  		Channel: "beta",
   629  	})
   630  	chg := s.state.NewChange("dummy", "...")
   631  	chg.AddTask(t)
   632  	s.state.Unlock()
   633  
   634  	s.se.Ensure()
   635  	s.se.Wait()
   636  
   637  	s.state.Lock()
   638  	defer s.state.Unlock()
   639  	c.Check(chg.Status(), Equals, state.ErrorStatus)
   640  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*- test \(cannot install system snap "snapd": no snap revision available as specified\)`)
   641  }