github.com/tompreston/snapd@v0.0.0-20210817193607-954edfcb9611/snap/info_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2021 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 snap_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"regexp"
    28  	"sort"
    29  	"strings"
    30  	"testing"
    31  
    32  	. "gopkg.in/check.v1"
    33  	"gopkg.in/yaml.v2"
    34  
    35  	"github.com/snapcore/snapd/dirs"
    36  	"github.com/snapcore/snapd/osutil"
    37  	"github.com/snapcore/snapd/snap"
    38  	"github.com/snapcore/snapd/snap/snapfile"
    39  	"github.com/snapcore/snapd/snap/snaptest"
    40  	"github.com/snapcore/snapd/snap/squashfs"
    41  	"github.com/snapcore/snapd/testutil"
    42  )
    43  
    44  type infoSuite struct {
    45  	testutil.BaseTest
    46  }
    47  
    48  type infoSimpleSuite struct{}
    49  
    50  var _ = Suite(&infoSuite{})
    51  var _ = Suite(&infoSimpleSuite{})
    52  
    53  func (s *infoSimpleSuite) SetUpTest(c *C) {
    54  	dirs.SetRootDir(c.MkDir())
    55  }
    56  
    57  func (s *infoSimpleSuite) TearDownTest(c *C) {
    58  	dirs.SetRootDir("")
    59  }
    60  
    61  func (s *infoSimpleSuite) TestReadInfoPanicsIfSanitizeUnset(c *C) {
    62  	si := &snap.SideInfo{Revision: snap.R(1)}
    63  	snaptest.MockSnap(c, sampleYaml, si)
    64  	c.Assert(func() { snap.ReadInfo("sample", si) }, Panics, `SanitizePlugsSlots function not set`)
    65  }
    66  
    67  func (s *infoSuite) SetUpTest(c *C) {
    68  	s.BaseTest.SetUpTest(c)
    69  	dirs.SetRootDir(c.MkDir())
    70  	hookType := snap.NewHookType(regexp.MustCompile(".*"))
    71  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    72  	s.BaseTest.AddCleanup(snap.MockSupportedHookTypes([]*snap.HookType{hookType}))
    73  }
    74  
    75  func (s *infoSuite) TearDownTest(c *C) {
    76  	s.BaseTest.TearDownTest(c)
    77  	dirs.SetRootDir("")
    78  }
    79  
    80  func (s *infoSuite) TestSideInfoOverrides(c *C) {
    81  	info := &snap.Info{
    82  		SuggestedName:       "name",
    83  		OriginalSummary:     "summary",
    84  		OriginalDescription: "desc",
    85  	}
    86  
    87  	info.SideInfo = snap.SideInfo{
    88  		RealName:          "newname",
    89  		EditedSummary:     "fixed summary",
    90  		EditedDescription: "fixed desc",
    91  		Revision:          snap.R(1),
    92  		SnapID:            "snapidsnapidsnapidsnapidsnapidsn",
    93  	}
    94  
    95  	c.Check(info.InstanceName(), Equals, "newname")
    96  	c.Check(info.Summary(), Equals, "fixed summary")
    97  	c.Check(info.Description(), Equals, "fixed desc")
    98  	c.Check(info.Revision, Equals, snap.R(1))
    99  	c.Check(info.SnapID, Equals, "snapidsnapidsnapidsnapidsnapidsn")
   100  	c.Check(info.ID(), Equals, "snapidsnapidsnapidsnapidsnapidsn")
   101  }
   102  
   103  func (s *infoSuite) TestContactFromEdited(c *C) {
   104  	info := &snap.Info{
   105  		OriginalLinks: nil,
   106  	}
   107  
   108  	info.SideInfo = snap.SideInfo{
   109  		EditedContact: "mailto:econtact",
   110  	}
   111  
   112  	c.Check(info.Contact(), Equals, "mailto:econtact")
   113  }
   114  
   115  func (s *infoSuite) TestNoContact(c *C) {
   116  	info := &snap.Info{}
   117  
   118  	c.Check(info.Contact(), Equals, "")
   119  }
   120  
   121  func (s *infoSuite) TestContactFromLinks(c *C) {
   122  	info := &snap.Info{
   123  		OriginalLinks: map[string][]string{
   124  			"contact": {"ocontact1", "ocontact2"},
   125  		},
   126  	}
   127  
   128  	c.Check(info.Contact(), Equals, "mailto:ocontact1")
   129  }
   130  
   131  func (s *infoSuite) TestContactFromLinksMailtoAlready(c *C) {
   132  	info := &snap.Info{
   133  		OriginalLinks: map[string][]string{
   134  			"contact": {"mailto:ocontact1", "ocontact2"},
   135  		},
   136  	}
   137  
   138  	c.Check(info.Contact(), Equals, "mailto:ocontact1")
   139  }
   140  
   141  func (s *infoSuite) TestContactFromLinksNotEmail(c *C) {
   142  	info := &snap.Info{
   143  		OriginalLinks: map[string][]string{
   144  			"contact": {"https://ocontact1", "ocontact2"},
   145  		},
   146  	}
   147  
   148  	c.Check(info.Contact(), Equals, "https://ocontact1")
   149  }
   150  
   151  func (s *infoSuite) TestLinks(c *C) {
   152  	info := &snap.Info{
   153  		OriginalLinks: map[string][]string{
   154  			"contact": {"ocontact"},
   155  			"website": {"owebsite"},
   156  		},
   157  	}
   158  
   159  	info.SideInfo = snap.SideInfo{
   160  		EditedLinks: map[string][]string{
   161  			"contact": {"econtact"},
   162  			"website": {"ewebsite"},
   163  		},
   164  	}
   165  
   166  	c.Check(info.Links(), DeepEquals, map[string][]string{
   167  		"contact": {"econtact"},
   168  		"website": {"ewebsite"},
   169  	})
   170  
   171  	info.EditedLinks = nil
   172  	c.Check(info.Links(), DeepEquals, map[string][]string{
   173  		"contact": {"ocontact"},
   174  		"website": {"owebsite"},
   175  	})
   176  }
   177  
   178  func (s *infoSuite) TestAppInfoSecurityTag(c *C) {
   179  	appInfo := &snap.AppInfo{Snap: &snap.Info{SuggestedName: "http"}, Name: "GET"}
   180  	c.Check(appInfo.SecurityTag(), Equals, "snap.http.GET")
   181  }
   182  
   183  func (s *infoSuite) TestPlugSlotSecurityTags(c *C) {
   184  	info, err := snap.InfoFromSnapYaml([]byte(`name: name
   185  apps:
   186      app1:
   187      app2:
   188  hooks:
   189      hook1:
   190  plugs:
   191      plug:
   192  slots:
   193      slot:
   194  `))
   195  	c.Assert(err, IsNil)
   196  	c.Assert(info.Plugs["plug"].SecurityTags(), DeepEquals, []string{
   197  		"snap.name.app1", "snap.name.app2", "snap.name.hook.hook1"})
   198  	c.Assert(info.Slots["slot"].SecurityTags(), DeepEquals, []string{
   199  		"snap.name.app1", "snap.name.app2", "snap.name.hook.hook1"})
   200  }
   201  
   202  func (s *infoSuite) TestAppInfoWrapperPath(c *C) {
   203  	info, err := snap.InfoFromSnapYaml([]byte(`name: foo
   204  apps:
   205     foo:
   206     bar:
   207  `))
   208  	c.Assert(err, IsNil)
   209  
   210  	c.Check(info.Apps["bar"].WrapperPath(), Equals, filepath.Join(dirs.SnapBinariesDir, "foo.bar"))
   211  	c.Check(info.Apps["foo"].WrapperPath(), Equals, filepath.Join(dirs.SnapBinariesDir, "foo"))
   212  }
   213  
   214  func (s *infoSuite) TestAppInfoCompleterPath(c *C) {
   215  	info, err := snap.InfoFromSnapYaml([]byte(`name: foo
   216  apps:
   217     foo:
   218     bar:
   219  `))
   220  	c.Assert(err, IsNil)
   221  
   222  	c.Check(info.Apps["bar"].CompleterPath(), Equals, filepath.Join(dirs.CompletersDir, "foo.bar"))
   223  	c.Check(info.Apps["foo"].CompleterPath(), Equals, filepath.Join(dirs.CompletersDir, "foo"))
   224  }
   225  
   226  func (s *infoSuite) TestAppInfoLauncherCommand(c *C) {
   227  	dirs.SetRootDir("")
   228  
   229  	info, err := snap.InfoFromSnapYaml([]byte(`name: foo
   230  apps:
   231     foo:
   232       command: foo-bin
   233     bar:
   234       command: bar-bin -x
   235     baz:
   236       command: bar-bin -x
   237       timer: 10:00-12:00,,mon,12:00~14:00
   238  `))
   239  	c.Assert(err, IsNil)
   240  	info.Revision = snap.R(42)
   241  	c.Check(info.Apps["bar"].LauncherCommand(), Equals, "/usr/bin/snap run foo.bar")
   242  	c.Check(info.Apps["bar"].LauncherStopCommand(), Equals, "/usr/bin/snap run --command=stop foo.bar")
   243  	c.Check(info.Apps["bar"].LauncherReloadCommand(), Equals, "/usr/bin/snap run --command=reload foo.bar")
   244  	c.Check(info.Apps["bar"].LauncherPostStopCommand(), Equals, "/usr/bin/snap run --command=post-stop foo.bar")
   245  	c.Check(info.Apps["foo"].LauncherCommand(), Equals, "/usr/bin/snap run foo")
   246  	c.Check(info.Apps["baz"].LauncherCommand(), Equals, `/usr/bin/snap run --timer="10:00-12:00,,mon,12:00~14:00" foo.baz`)
   247  
   248  	// snap with instance key
   249  	info.InstanceKey = "instance"
   250  	c.Check(info.Apps["bar"].LauncherCommand(), Equals, "/usr/bin/snap run foo_instance.bar")
   251  	c.Check(info.Apps["bar"].LauncherStopCommand(), Equals, "/usr/bin/snap run --command=stop foo_instance.bar")
   252  	c.Check(info.Apps["bar"].LauncherReloadCommand(), Equals, "/usr/bin/snap run --command=reload foo_instance.bar")
   253  	c.Check(info.Apps["bar"].LauncherPostStopCommand(), Equals, "/usr/bin/snap run --command=post-stop foo_instance.bar")
   254  	c.Check(info.Apps["foo"].LauncherCommand(), Equals, "/usr/bin/snap run foo_instance")
   255  	c.Check(info.Apps["baz"].LauncherCommand(), Equals, `/usr/bin/snap run --timer="10:00-12:00,,mon,12:00~14:00" foo_instance.baz`)
   256  }
   257  
   258  const sampleYaml = `
   259  name: sample
   260  version: 1
   261  apps:
   262   app:
   263     command: foo
   264   app2:
   265     command: bar
   266   sample:
   267     command: foobar
   268     command-chain: [chain]
   269  hooks:
   270   configure:
   271    command-chain: [hookchain]
   272  `
   273  
   274  func (s *infoSuite) TestReadInfo(c *C) {
   275  	si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"}
   276  
   277  	snapInfo1 := snaptest.MockSnap(c, sampleYaml, si)
   278  
   279  	snapInfo2, err := snap.ReadInfo("sample", si)
   280  	c.Assert(err, IsNil)
   281  
   282  	c.Check(snapInfo2.InstanceName(), Equals, "sample")
   283  	c.Check(snapInfo2.Revision, Equals, snap.R(42))
   284  	c.Check(snapInfo2.Summary(), Equals, "esummary")
   285  
   286  	c.Check(snapInfo2.Apps["app"].Command, Equals, "foo")
   287  	c.Check(snapInfo2.Apps["sample"].CommandChain, DeepEquals, []string{"chain"})
   288  	c.Check(snapInfo2.Hooks["configure"].CommandChain, DeepEquals, []string{"hookchain"})
   289  
   290  	c.Check(snapInfo2, DeepEquals, snapInfo1)
   291  }
   292  
   293  func (s *infoSuite) TestReadInfoWithInstance(c *C) {
   294  	si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "instance summary"}
   295  
   296  	snapInfo1 := snaptest.MockSnapInstance(c, "sample_instance", sampleYaml, si)
   297  
   298  	snapInfo2, err := snap.ReadInfo("sample_instance", si)
   299  	c.Assert(err, IsNil)
   300  
   301  	c.Check(snapInfo2.InstanceName(), Equals, "sample_instance")
   302  	c.Check(snapInfo2.SnapName(), Equals, "sample")
   303  	c.Check(snapInfo2.Revision, Equals, snap.R(42))
   304  	c.Check(snapInfo2.Summary(), Equals, "instance summary")
   305  
   306  	c.Check(snapInfo2.Apps["app"].Command, Equals, "foo")
   307  	c.Check(snapInfo2.Apps["sample"].CommandChain, DeepEquals, []string{"chain"})
   308  	c.Check(snapInfo2.Hooks["configure"].CommandChain, DeepEquals, []string{"hookchain"})
   309  
   310  	c.Check(snapInfo2, DeepEquals, snapInfo1)
   311  }
   312  
   313  func (s *infoSuite) TestReadCurrentInfo(c *C) {
   314  	si := &snap.SideInfo{Revision: snap.R(42)}
   315  
   316  	snapInfo1 := snaptest.MockSnapCurrent(c, sampleYaml, si)
   317  
   318  	snapInfo2, err := snap.ReadCurrentInfo("sample")
   319  	c.Assert(err, IsNil)
   320  
   321  	c.Check(snapInfo2.InstanceName(), Equals, "sample")
   322  	c.Check(snapInfo2.Revision, Equals, snap.R(42))
   323  	c.Check(snapInfo2, DeepEquals, snapInfo1)
   324  
   325  	snapInfo3, err := snap.ReadCurrentInfo("not-sample")
   326  	c.Check(snapInfo3, IsNil)
   327  	c.Assert(err, ErrorMatches, `cannot find current revision for snap not-sample:.*`)
   328  }
   329  
   330  func (s *infoSuite) TestReadCurrentInfoWithInstance(c *C) {
   331  	si := &snap.SideInfo{Revision: snap.R(42)}
   332  
   333  	snapInfo1 := snaptest.MockSnapInstanceCurrent(c, "sample_instance", sampleYaml, si)
   334  
   335  	snapInfo2, err := snap.ReadCurrentInfo("sample_instance")
   336  	c.Assert(err, IsNil)
   337  
   338  	c.Check(snapInfo2.InstanceName(), Equals, "sample_instance")
   339  	c.Check(snapInfo2.SnapName(), Equals, "sample")
   340  	c.Check(snapInfo2.Revision, Equals, snap.R(42))
   341  	c.Check(snapInfo2, DeepEquals, snapInfo1)
   342  
   343  	snapInfo3, err := snap.ReadCurrentInfo("sample_other")
   344  	c.Check(snapInfo3, IsNil)
   345  	c.Assert(err, ErrorMatches, `cannot find current revision for snap sample_other:.*`)
   346  }
   347  
   348  func (s *infoSuite) TestInstallDate(c *C) {
   349  	si := &snap.SideInfo{Revision: snap.R(1)}
   350  	info := snaptest.MockSnap(c, sampleYaml, si)
   351  	// not current -> Zero
   352  	c.Check(info.InstallDate().IsZero(), Equals, true)
   353  	c.Check(snap.InstallDate(info.InstanceName()).IsZero(), Equals, true)
   354  
   355  	mountdir := info.MountDir()
   356  	dir, rev := filepath.Split(mountdir)
   357  	c.Assert(os.MkdirAll(dir, 0755), IsNil)
   358  	cur := filepath.Join(dir, "current")
   359  	c.Assert(os.Symlink(rev, cur), IsNil)
   360  	st, err := os.Lstat(cur)
   361  	c.Assert(err, IsNil)
   362  	instTime := st.ModTime()
   363  	// sanity
   364  	c.Check(instTime.IsZero(), Equals, false)
   365  
   366  	c.Check(info.InstallDate().Equal(instTime), Equals, true)
   367  	c.Check(snap.InstallDate(info.InstanceName()).Equal(instTime), Equals, true)
   368  }
   369  
   370  func (s *infoSuite) TestReadInfoNotFound(c *C) {
   371  	si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"}
   372  	info, err := snap.ReadInfo("sample", si)
   373  	c.Check(info, IsNil)
   374  	c.Check(err, ErrorMatches, `cannot find installed snap "sample" at revision 42: missing file .*sample/42/meta/snap.yaml`)
   375  	bse, ok := err.(snap.BrokenSnapError)
   376  	c.Assert(ok, Equals, true)
   377  	c.Check(bse.Broken(), Equals, bse.Error())
   378  }
   379  
   380  func (s *infoSuite) TestReadInfoUnreadable(c *C) {
   381  	si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"}
   382  	c.Assert(os.MkdirAll(filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml"), 0755), IsNil)
   383  
   384  	info, err := snap.ReadInfo("sample", si)
   385  	c.Check(info, IsNil)
   386  	// TODO: maybe improve this error message
   387  	c.Check(err, ErrorMatches, ".* is a directory")
   388  }
   389  
   390  func (s *infoSuite) TestReadInfoUnparsable(c *C) {
   391  	si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"}
   392  	p := filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml")
   393  	c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
   394  	c.Assert(ioutil.WriteFile(p, []byte(`- :`), 0644), IsNil)
   395  
   396  	info, err := snap.ReadInfo("sample", si)
   397  	c.Check(info, IsNil)
   398  	// TODO: maybe improve this error message
   399  	c.Check(err, ErrorMatches, `cannot use installed snap "sample" at revision 42: cannot parse snap.yaml: yaml: .*`)
   400  	bse, ok := err.(snap.BrokenSnapError)
   401  	c.Assert(ok, Equals, true)
   402  	c.Check(bse.Broken(), Equals, bse.Error())
   403  }
   404  
   405  func (s *infoSuite) TestReadInfoUnfindable(c *C) {
   406  	si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"}
   407  	p := filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml")
   408  	c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
   409  	c.Assert(ioutil.WriteFile(p, []byte(``), 0644), IsNil)
   410  
   411  	info, err := snap.ReadInfo("sample", si)
   412  	c.Check(err, ErrorMatches, `cannot find installed snap "sample" at revision 42: missing file .*var/lib/snapd/snaps/sample_42.snap`)
   413  	c.Check(info, IsNil)
   414  }
   415  
   416  func (s *infoSuite) TestReadInfoDanglingSymlink(c *C) {
   417  	si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"}
   418  	mpi := snap.MinimalPlaceInfo("sample", si.Revision)
   419  	p := filepath.Join(mpi.MountDir(), "meta", "snap.yaml")
   420  	c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
   421  	c.Assert(ioutil.WriteFile(p, []byte(`name: test`), 0644), IsNil)
   422  	c.Assert(os.MkdirAll(filepath.Dir(mpi.MountFile()), 0755), IsNil)
   423  	c.Assert(os.Symlink("/dangling", mpi.MountFile()), IsNil)
   424  
   425  	info, err := snap.ReadInfo("sample", si)
   426  	c.Check(err, IsNil)
   427  	c.Check(info.SnapName(), Equals, "test")
   428  	c.Check(info.Revision, Equals, snap.R(42))
   429  	c.Check(info.Summary(), Equals, "esummary")
   430  	c.Check(info.Size, Equals, int64(0))
   431  }
   432  
   433  // makeTestSnap here can also be used to produce broken snaps (differently from snaptest.MakeTestSnapWithFiles)!
   434  func makeTestSnap(c *C, snapYaml string) string {
   435  	var m struct {
   436  		Type string `yaml:"type"`
   437  	}
   438  	yaml.Unmarshal([]byte(snapYaml), &m) // yes, ignore the error
   439  
   440  	tmp := c.MkDir()
   441  	snapSource := filepath.Join(tmp, "snapsrc")
   442  
   443  	err := os.MkdirAll(filepath.Join(snapSource, "meta"), 0755)
   444  	c.Assert(err, IsNil)
   445  
   446  	// our regular snap.yaml
   447  	err = ioutil.WriteFile(filepath.Join(snapSource, "meta", "snap.yaml"), []byte(snapYaml), 0644)
   448  	c.Assert(err, IsNil)
   449  
   450  	dest := filepath.Join(tmp, "foo.snap")
   451  	snap := squashfs.New(dest)
   452  	err = snap.Build(snapSource, &squashfs.BuildOpts{SnapType: m.Type})
   453  	c.Assert(err, IsNil)
   454  
   455  	return dest
   456  }
   457  
   458  // produce descrs for empty hooks suitable for snaptest.PopulateDir
   459  func emptyHooks(hookNames ...string) (emptyHooks [][]string) {
   460  	for _, hookName := range hookNames {
   461  		emptyHooks = append(emptyHooks, []string{filepath.Join("meta", "hooks", hookName), ""})
   462  	}
   463  	return
   464  }
   465  
   466  func (s *infoSuite) TestReadInfoFromSnapFile(c *C) {
   467  	yaml := `name: foo
   468  version: 1.0
   469  type: app
   470  epoch: 1*
   471  confinement: devmode`
   472  	snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil)
   473  
   474  	snapf, err := snapfile.Open(snapPath)
   475  	c.Assert(err, IsNil)
   476  
   477  	info, err := snap.ReadInfoFromSnapFile(snapf, nil)
   478  	c.Assert(err, IsNil)
   479  	c.Check(info.InstanceName(), Equals, "foo")
   480  	c.Check(info.Version, Equals, "1.0")
   481  	c.Check(info.Type(), Equals, snap.TypeApp)
   482  	c.Check(info.Revision, Equals, snap.R(0))
   483  	c.Check(info.Epoch.String(), Equals, "1*")
   484  	c.Check(info.Confinement, Equals, snap.DevModeConfinement)
   485  	c.Check(info.NeedsDevMode(), Equals, true)
   486  	c.Check(info.NeedsClassic(), Equals, false)
   487  }
   488  
   489  func (s *infoSuite) TestReadInfoFromClassicSnapFile(c *C) {
   490  	yaml := `name: foo
   491  version: 1.0
   492  type: app
   493  confinement: classic`
   494  	snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil)
   495  
   496  	snapf, err := snapfile.Open(snapPath)
   497  	c.Assert(err, IsNil)
   498  
   499  	info, err := snap.ReadInfoFromSnapFile(snapf, nil)
   500  	c.Assert(err, IsNil)
   501  	c.Check(info.InstanceName(), Equals, "foo")
   502  	c.Check(info.Version, Equals, "1.0")
   503  	c.Check(info.Type(), Equals, snap.TypeApp)
   504  	c.Check(info.Revision, Equals, snap.R(0))
   505  	c.Check(info.Confinement, Equals, snap.ClassicConfinement)
   506  	c.Check(info.NeedsDevMode(), Equals, false)
   507  	c.Check(info.NeedsClassic(), Equals, true)
   508  }
   509  
   510  func (s *infoSuite) TestReadInfoFromSnapFileMissingEpoch(c *C) {
   511  	yaml := `name: foo
   512  version: 1.0
   513  type: app`
   514  	snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil)
   515  
   516  	snapf, err := snapfile.Open(snapPath)
   517  	c.Assert(err, IsNil)
   518  
   519  	info, err := snap.ReadInfoFromSnapFile(snapf, nil)
   520  	c.Assert(err, IsNil)
   521  	c.Check(info.InstanceName(), Equals, "foo")
   522  	c.Check(info.Version, Equals, "1.0")
   523  	c.Check(info.Type(), Equals, snap.TypeApp)
   524  	c.Check(info.Revision, Equals, snap.R(0))
   525  	c.Check(info.Epoch.String(), Equals, "0") // Defaults to 0
   526  	c.Check(info.Confinement, Equals, snap.StrictConfinement)
   527  	c.Check(info.NeedsDevMode(), Equals, false)
   528  }
   529  
   530  func (s *infoSuite) TestReadInfoFromSnapFileWithSideInfo(c *C) {
   531  	yaml := `name: foo
   532  version: 1.0
   533  type: app`
   534  	snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil)
   535  
   536  	snapf, err := snapfile.Open(snapPath)
   537  	c.Assert(err, IsNil)
   538  
   539  	info, err := snap.ReadInfoFromSnapFile(snapf, &snap.SideInfo{
   540  		RealName: "baz",
   541  		Revision: snap.R(42),
   542  	})
   543  	c.Assert(err, IsNil)
   544  	c.Check(info.InstanceName(), Equals, "baz")
   545  	c.Check(info.Version, Equals, "1.0")
   546  	c.Check(info.Type(), Equals, snap.TypeApp)
   547  	c.Check(info.Revision, Equals, snap.R(42))
   548  }
   549  
   550  func (s *infoSuite) TestReadInfoFromSnapFileValidates(c *C) {
   551  	yaml := `name: foo.bar
   552  version: 1.0
   553  type: app`
   554  	snapPath := makeTestSnap(c, yaml)
   555  
   556  	snapf, err := snapfile.Open(snapPath)
   557  	c.Assert(err, IsNil)
   558  
   559  	_, err = snap.ReadInfoFromSnapFile(snapf, nil)
   560  	c.Assert(err, ErrorMatches, `invalid snap name.*`)
   561  }
   562  
   563  func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidType(c *C) {
   564  	yaml := `name: foo
   565  version: 1.0
   566  type: foo`
   567  	snapPath := makeTestSnap(c, yaml)
   568  
   569  	snapf, err := snapfile.Open(snapPath)
   570  	c.Assert(err, IsNil)
   571  
   572  	_, err = snap.ReadInfoFromSnapFile(snapf, nil)
   573  	c.Assert(err, ErrorMatches, ".*invalid snap type.*")
   574  }
   575  
   576  func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidConfinement(c *C) {
   577  	yaml := `name: foo
   578  version: 1.0
   579  confinement: foo`
   580  	snapPath := makeTestSnap(c, yaml)
   581  
   582  	snapf, err := snapfile.Open(snapPath)
   583  	c.Assert(err, IsNil)
   584  
   585  	_, err = snap.ReadInfoFromSnapFile(snapf, nil)
   586  	c.Assert(err, ErrorMatches, ".*invalid confinement type.*")
   587  }
   588  
   589  func (s *infoSuite) TestAppEnvSimple(c *C) {
   590  	yaml := `name: foo
   591  version: 1.0
   592  type: app
   593  environment:
   594   global-k: global-v
   595  apps:
   596   foo:
   597    environment:
   598     app-k: app-v
   599  `
   600  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   601  	c.Assert(err, IsNil)
   602  
   603  	c.Check(info.Apps["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{
   604  		osutil.NewExpandableEnv("global-k", "global-v"),
   605  		osutil.NewExpandableEnv("app-k", "app-v"),
   606  	})
   607  }
   608  
   609  func (s *infoSuite) TestAppEnvOverrideGlobal(c *C) {
   610  	yaml := `name: foo
   611  version: 1.0
   612  type: app
   613  environment:
   614   global-k: global-v
   615   global-and-local: global-v
   616  apps:
   617   foo:
   618    environment:
   619     app-k: app-v
   620     global-and-local: local-v
   621  `
   622  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   623  	c.Assert(err, IsNil)
   624  
   625  	c.Check(info.Apps["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{
   626  		osutil.NewExpandableEnv("global-k", "global-v", "global-and-local", "global-v"),
   627  		osutil.NewExpandableEnv("app-k", "app-v", "global-and-local", "local-v"),
   628  	})
   629  }
   630  
   631  func (s *infoSuite) TestHookEnvSimple(c *C) {
   632  	yaml := `name: foo
   633  version: 1.0
   634  type: app
   635  environment:
   636   global-k: global-v
   637  hooks:
   638   foo:
   639    environment:
   640     app-k: app-v
   641  `
   642  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   643  	c.Assert(err, IsNil)
   644  
   645  	c.Check(info.Hooks["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{
   646  		osutil.NewExpandableEnv("global-k", "global-v"),
   647  		osutil.NewExpandableEnv("app-k", "app-v"),
   648  	})
   649  }
   650  
   651  func (s *infoSuite) TestHookEnvOverrideGlobal(c *C) {
   652  	yaml := `name: foo
   653  version: 1.0
   654  type: app
   655  environment:
   656   global-k: global-v
   657   global-and-local: global-v
   658  hooks:
   659   foo:
   660    environment:
   661     app-k: app-v
   662     global-and-local: local-v
   663  `
   664  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   665  	c.Assert(err, IsNil)
   666  
   667  	c.Check(info.Hooks["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{
   668  		osutil.NewExpandableEnv("global-k", "global-v", "global-and-local", "global-v"),
   669  		osutil.NewExpandableEnv("app-k", "app-v", "global-and-local", "local-v"),
   670  	})
   671  }
   672  
   673  func (s *infoSuite) TestSplitSnapApp(c *C) {
   674  	for _, t := range []struct {
   675  		in  string
   676  		out []string
   677  	}{
   678  		// normal cases
   679  		{"foo.bar", []string{"foo", "bar"}},
   680  		{"foo.bar.baz", []string{"foo", "bar.baz"}},
   681  		// special case, snapName == appName
   682  		{"foo", []string{"foo", "foo"}},
   683  		// snap instance names
   684  		{"foo_instance.bar", []string{"foo_instance", "bar"}},
   685  		{"foo_instance.bar.baz", []string{"foo_instance", "bar.baz"}},
   686  		{"foo_instance", []string{"foo_instance", "foo"}},
   687  	} {
   688  		snap, app := snap.SplitSnapApp(t.in)
   689  		c.Check([]string{snap, app}, DeepEquals, t.out)
   690  	}
   691  }
   692  
   693  func (s *infoSuite) TestJoinSnapApp(c *C) {
   694  	for _, t := range []struct {
   695  		in  []string
   696  		out string
   697  	}{
   698  		// normal cases
   699  		{[]string{"foo", "bar"}, "foo.bar"},
   700  		{[]string{"foo", "bar-baz"}, "foo.bar-baz"},
   701  		// special case, snapName == appName
   702  		{[]string{"foo", "foo"}, "foo"},
   703  		// snap instance names
   704  		{[]string{"foo_instance", "bar"}, "foo_instance.bar"},
   705  		{[]string{"foo_instance", "bar-baz"}, "foo_instance.bar-baz"},
   706  		{[]string{"foo_instance", "foo"}, "foo_instance"},
   707  	} {
   708  		snapApp := snap.JoinSnapApp(t.in[0], t.in[1])
   709  		c.Check(snapApp, Equals, t.out)
   710  	}
   711  }
   712  
   713  func ExampleSplitSnapApp() {
   714  	fmt.Println(snap.SplitSnapApp("hello-world.env"))
   715  	// Output: hello-world env
   716  }
   717  
   718  func ExampleSplitSnapApp_short() {
   719  	fmt.Println(snap.SplitSnapApp("hello-world"))
   720  	// Output: hello-world hello-world
   721  }
   722  
   723  func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidHook(c *C) {
   724  	yaml := `name: foo
   725  version: 1.0
   726  hooks:
   727    123abc:`
   728  	snapPath := makeTestSnap(c, yaml)
   729  
   730  	snapf, err := snapfile.Open(snapPath)
   731  	c.Assert(err, IsNil)
   732  
   733  	_, err = snap.ReadInfoFromSnapFile(snapf, nil)
   734  	c.Assert(err, ErrorMatches, ".*invalid hook name.*")
   735  }
   736  
   737  func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidImplicitHook(c *C) {
   738  	yaml := `name: foo
   739  version: 1.0`
   740  	snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, emptyHooks("123abc"))
   741  
   742  	snapf, err := snapfile.Open(snapPath)
   743  	c.Assert(err, IsNil)
   744  
   745  	_, err = snap.ReadInfoFromSnapFile(snapf, nil)
   746  	c.Assert(err, ErrorMatches, ".*invalid hook name.*")
   747  }
   748  
   749  func (s *infoSuite) checkInstalledSnapAndSnapFile(c *C, instanceName, yaml string, contents string, hooks []string, checker func(c *C, info *snap.Info)) {
   750  	// First check installed snap
   751  	sideInfo := &snap.SideInfo{Revision: snap.R(42)}
   752  	info0 := snaptest.MockSnapInstance(c, instanceName, yaml, sideInfo)
   753  	snaptest.PopulateDir(info0.MountDir(), emptyHooks(hooks...))
   754  	info, err := snap.ReadInfo(info0.InstanceName(), sideInfo)
   755  	c.Check(err, IsNil)
   756  	checker(c, info)
   757  
   758  	// Now check snap file
   759  	snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, emptyHooks(hooks...))
   760  	snapf, err := snapfile.Open(snapPath)
   761  	c.Assert(err, IsNil)
   762  	info, err = snap.ReadInfoFromSnapFile(snapf, nil)
   763  	c.Check(err, IsNil)
   764  	checker(c, info)
   765  }
   766  
   767  func (s *infoSuite) TestReadInfoNoHooks(c *C) {
   768  	yaml := `name: foo
   769  version: 1.0`
   770  	s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", nil, func(c *C, info *snap.Info) {
   771  		// Verify that no hooks were loaded for this snap
   772  		c.Check(info.Hooks, HasLen, 0)
   773  	})
   774  }
   775  
   776  func (s *infoSuite) TestReadInfoSingleImplicitHook(c *C) {
   777  	yaml := `name: foo
   778  version: 1.0`
   779  	s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"test-hook"}, func(c *C, info *snap.Info) {
   780  		// Verify that the `test-hook` hook has now been loaded, and that it has
   781  		// no associated plugs.
   782  		c.Check(info.Hooks, HasLen, 1)
   783  		verifyImplicitHook(c, info, "test-hook", nil)
   784  	})
   785  }
   786  
   787  func (s *infoSuite) TestReadInfoMultipleImplicitHooks(c *C) {
   788  	yaml := `name: foo
   789  version: 1.0`
   790  	s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"foo", "bar"}, func(c *C, info *snap.Info) {
   791  		// Verify that both hooks have now been loaded, and that neither have any
   792  		// associated plugs.
   793  		c.Check(info.Hooks, HasLen, 2)
   794  		verifyImplicitHook(c, info, "foo", nil)
   795  		verifyImplicitHook(c, info, "bar", nil)
   796  	})
   797  }
   798  
   799  func (s *infoSuite) TestReadInfoInvalidImplicitHook(c *C) {
   800  	hookType := snap.NewHookType(regexp.MustCompile("foo"))
   801  	s.BaseTest.AddCleanup(snap.MockSupportedHookTypes([]*snap.HookType{hookType}))
   802  
   803  	yaml := `name: foo
   804  version: 1.0`
   805  	s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"foo", "bar"}, func(c *C, info *snap.Info) {
   806  		// Verify that only foo has been loaded, not bar
   807  		c.Check(info.Hooks, HasLen, 1)
   808  		verifyImplicitHook(c, info, "foo", nil)
   809  	})
   810  }
   811  
   812  func (s *infoSuite) TestReadInfoImplicitAndExplicitHooks(c *C) {
   813  	yaml := `name: foo
   814  version: 1.0
   815  hooks:
   816    explicit:
   817      plugs: [test-plug]
   818      slots: [test-slot]`
   819  	s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"explicit", "implicit"}, func(c *C, info *snap.Info) {
   820  		// Verify that the `implicit` hook has now been loaded, and that it has
   821  		// no associated plugs. Also verify that the `explicit` hook is still
   822  		// valid.
   823  		c.Check(info.Hooks, HasLen, 2)
   824  		verifyImplicitHook(c, info, "implicit", nil)
   825  		verifyExplicitHook(c, info, "explicit", []string{"test-plug"}, []string{"test-slot"})
   826  	})
   827  }
   828  
   829  func (s *infoSuite) TestReadInfoExplicitHooks(c *C) {
   830  	yaml := `name: foo
   831  version: 1.0
   832  plugs:
   833    test-plug:
   834  slots:
   835    test-slot:
   836  hooks:
   837    explicit:
   838  `
   839  	s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"explicit"}, func(c *C, info *snap.Info) {
   840  		c.Check(info.Hooks, HasLen, 1)
   841  		verifyExplicitHook(c, info, "explicit", []string{"test-plug"}, []string{"test-slot"})
   842  	})
   843  }
   844  
   845  func (s *infoSuite) TestReadInfoImplicitHookPlugWhenImplicitlyBoundToApp(c *C) {
   846  	yaml := `name: foo
   847  version: 1.0
   848  plugs:
   849    test-plug:
   850  apps:
   851    app:
   852  `
   853  	s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) {
   854  		c.Check(info.Hooks, HasLen, 1)
   855  		verifyImplicitHook(c, info, "implicit", []string{"test-plug"})
   856  	})
   857  }
   858  
   859  func (s *infoSuite) TestReadInfoImplicitHookPlugWhenExplicitlyBoundToApp(c *C) {
   860  	yaml := `name: foo
   861  version: 1.0
   862  plugs:
   863    test-plug:
   864  apps:
   865    app:
   866      plugs: [test-plug]
   867  `
   868  	s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) {
   869  		c.Check(info.Hooks, HasLen, 1)
   870  		verifyImplicitHook(c, info, "implicit", nil)
   871  	})
   872  }
   873  
   874  func (s *infoSuite) TestParallelInstanceReadInfoImplicitAndExplicitHooks(c *C) {
   875  	yaml := `name: foo
   876  version: 1.0
   877  hooks:
   878    explicit:
   879      plugs: [test-plug]
   880      slots: [test-slot]`
   881  	s.checkInstalledSnapAndSnapFile(c, "foo_instance", yaml, "SNAP", []string{"explicit", "implicit"}, func(c *C, info *snap.Info) {
   882  		c.Check(info.Hooks, HasLen, 2)
   883  		verifyImplicitHook(c, info, "implicit", nil)
   884  		verifyExplicitHook(c, info, "explicit", []string{"test-plug"}, []string{"test-slot"})
   885  	})
   886  }
   887  
   888  func (s *infoSuite) TestReadInfoImplicitHookWithTopLevelPlugSlots(c *C) {
   889  	yaml1 := `name: snap-1
   890  version: 1.0
   891  plugs:
   892    test-plug:
   893  slots:
   894    test-slot:
   895  hooks:
   896    explicit:
   897      plugs: [test-plug,other-plug]
   898      slots: [test-slot,other-slot]
   899  `
   900  	s.checkInstalledSnapAndSnapFile(c, "snap-1", yaml1, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) {
   901  		c.Check(info.Hooks, HasLen, 2)
   902  		implicitHook := info.Hooks["implicit"]
   903  		c.Assert(implicitHook, NotNil)
   904  		c.Assert(implicitHook.Explicit, Equals, false)
   905  		c.Assert(implicitHook.Plugs, HasLen, 0)
   906  		c.Assert(implicitHook.Slots, HasLen, 0)
   907  
   908  		c.Check(info.Plugs, HasLen, 2)
   909  		c.Check(info.Slots, HasLen, 2)
   910  
   911  		plug := info.Plugs["test-plug"]
   912  		c.Assert(plug, NotNil)
   913  		// implicit hook has not gained test-plug because it was already
   914  		// associated with an app or a hook (here with the hook called
   915  		// "explicit"). This is consistent with the hook called "implicit"
   916  		// having been defined in the YAML but devoid of any interface
   917  		// assignments.
   918  		c.Assert(implicitHook.Plugs["test-plug"], IsNil)
   919  
   920  		slot := info.Slots["test-slot"]
   921  		c.Assert(slot, NotNil)
   922  		c.Assert(implicitHook.Slots["test-slot"], IsNil)
   923  
   924  		explicitHook := info.Hooks["explicit"]
   925  		c.Assert(explicitHook, NotNil)
   926  		c.Assert(explicitHook.Explicit, Equals, true)
   927  		c.Assert(explicitHook.Plugs, HasLen, 2)
   928  		c.Assert(explicitHook.Slots, HasLen, 2)
   929  
   930  		plug = info.Plugs["test-plug"]
   931  		c.Assert(plug, NotNil)
   932  		c.Assert(explicitHook.Plugs["test-plug"], DeepEquals, plug)
   933  
   934  		slot = info.Slots["test-slot"]
   935  		c.Assert(slot, NotNil)
   936  		c.Assert(explicitHook.Slots["test-slot"], DeepEquals, slot)
   937  	})
   938  
   939  	yaml2 := `name: snap-2
   940  version: 1.0
   941  plugs:
   942    test-plug:
   943  slots:
   944    test-slot:
   945  `
   946  	s.checkInstalledSnapAndSnapFile(c, "snap-2", yaml2, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) {
   947  		c.Check(info.Hooks, HasLen, 1)
   948  		implicitHook := info.Hooks["implicit"]
   949  		c.Assert(implicitHook, NotNil)
   950  		c.Assert(implicitHook.Explicit, Equals, false)
   951  		c.Assert(implicitHook.Plugs, HasLen, 1)
   952  		c.Assert(implicitHook.Slots, HasLen, 1)
   953  
   954  		c.Check(info.Plugs, HasLen, 1)
   955  		c.Check(info.Slots, HasLen, 1)
   956  
   957  		plug := info.Plugs["test-plug"]
   958  		c.Assert(plug, NotNil)
   959  		c.Assert(implicitHook.Plugs["test-plug"], DeepEquals, plug)
   960  
   961  		slot := info.Slots["test-slot"]
   962  		c.Assert(slot, NotNil)
   963  		c.Assert(implicitHook.Slots["test-slot"], DeepEquals, slot)
   964  	})
   965  
   966  	yaml3 := `name: snap-3
   967  version: 1.0
   968  plugs:
   969    test-plug:
   970  slots:
   971    test-slot:
   972  `
   973  	s.checkInstalledSnapAndSnapFile(c, "snap-3", yaml3, "SNAP", []string{"implicit-1", "implicit-2"}, func(c *C, info *snap.Info) {
   974  		c.Check(info.Hooks, HasLen, 2)
   975  		implicit1Hook := info.Hooks["implicit-1"]
   976  		c.Assert(implicit1Hook, NotNil)
   977  		c.Assert(implicit1Hook.Explicit, Equals, false)
   978  		c.Assert(implicit1Hook.Plugs, HasLen, 1)
   979  		c.Assert(implicit1Hook.Slots, HasLen, 1)
   980  
   981  		implicit2Hook := info.Hooks["implicit-2"]
   982  		c.Assert(implicit2Hook, NotNil)
   983  		c.Assert(implicit2Hook.Explicit, Equals, false)
   984  		c.Assert(implicit2Hook.Plugs, HasLen, 1)
   985  		c.Assert(implicit2Hook.Slots, HasLen, 1)
   986  
   987  		c.Check(info.Plugs, HasLen, 1)
   988  		c.Check(info.Slots, HasLen, 1)
   989  
   990  		plug := info.Plugs["test-plug"]
   991  		c.Assert(plug, NotNil)
   992  		c.Assert(implicit1Hook.Plugs["test-plug"], DeepEquals, plug)
   993  		c.Assert(implicit2Hook.Plugs["test-plug"], DeepEquals, plug)
   994  
   995  		slot := info.Slots["test-slot"]
   996  		c.Assert(slot, NotNil)
   997  		c.Assert(implicit1Hook.Slots["test-slot"], DeepEquals, slot)
   998  		c.Assert(implicit2Hook.Slots["test-slot"], DeepEquals, slot)
   999  	})
  1000  
  1001  }
  1002  
  1003  func verifyImplicitHook(c *C, info *snap.Info, hookName string, plugNames []string) {
  1004  	hook := info.Hooks[hookName]
  1005  	c.Assert(hook, NotNil, Commentf("Expected hooks to contain %q", hookName))
  1006  	c.Check(hook.Name, Equals, hookName)
  1007  
  1008  	if len(plugNames) == 0 {
  1009  		c.Check(hook.Plugs, IsNil)
  1010  	}
  1011  
  1012  	for _, plugName := range plugNames {
  1013  		// Verify that the HookInfo and PlugInfo point to each other
  1014  		plug := hook.Plugs[plugName]
  1015  		c.Assert(plug, NotNil, Commentf("Expected hook plugs to contain %q", plugName))
  1016  		c.Check(plug.Name, Equals, plugName)
  1017  		c.Check(plug.Hooks, HasLen, 1)
  1018  		hook = plug.Hooks[hookName]
  1019  		c.Assert(hook, NotNil, Commentf("Expected plug to be associated with hook %q", hookName))
  1020  		c.Check(hook.Name, Equals, hookName)
  1021  
  1022  		// Verify also that the hook plug made it into info.Plugs
  1023  		c.Check(info.Plugs[plugName], DeepEquals, plug)
  1024  	}
  1025  }
  1026  
  1027  func verifyExplicitHook(c *C, info *snap.Info, hookName string, plugNames []string, slotNames []string) {
  1028  	hook := info.Hooks[hookName]
  1029  	c.Assert(hook, NotNil, Commentf("Expected hooks to contain %q", hookName))
  1030  	c.Check(hook.Name, Equals, hookName)
  1031  	c.Check(hook.Plugs, HasLen, len(plugNames))
  1032  	c.Check(hook.Slots, HasLen, len(slotNames))
  1033  
  1034  	for _, plugName := range plugNames {
  1035  		// Verify that the HookInfo and PlugInfo point to each other
  1036  		plug := hook.Plugs[plugName]
  1037  		c.Assert(plug, NotNil, Commentf("Expected hook plugs to contain %q", plugName))
  1038  		c.Check(plug.Name, Equals, plugName)
  1039  		c.Check(plug.Hooks, HasLen, 1)
  1040  		hook = plug.Hooks[hookName]
  1041  		c.Assert(hook, NotNil, Commentf("Expected plug to be associated with hook %q", hookName))
  1042  		c.Check(hook.Name, Equals, hookName)
  1043  
  1044  		// Verify also that the hook plug made it into info.Plugs
  1045  		c.Check(info.Plugs[plugName], DeepEquals, plug)
  1046  	}
  1047  
  1048  	for _, slotName := range slotNames {
  1049  		// Verify that the HookInfo and SlotInfo point to each other
  1050  		slot := hook.Slots[slotName]
  1051  		c.Assert(slot, NotNil, Commentf("Expected hook slots to contain %q", slotName))
  1052  		c.Check(slot.Name, Equals, slotName)
  1053  		c.Check(slot.Hooks, HasLen, 1)
  1054  		hook = slot.Hooks[hookName]
  1055  		c.Assert(hook, NotNil, Commentf("Expected slot to be associated with hook %q", hookName))
  1056  		c.Check(hook.Name, Equals, hookName)
  1057  
  1058  		// Verify also that the hook plug made it into info.Slots
  1059  		c.Check(info.Slots[slotName], DeepEquals, slot)
  1060  	}
  1061  
  1062  }
  1063  
  1064  func (s *infoSuite) TestPlaceInfoRevision(c *C) {
  1065  	info := snap.MinimalPlaceInfo("name", snap.R("1"))
  1066  	c.Check(info.SnapRevision(), Equals, snap.R("1"))
  1067  }
  1068  
  1069  func (s *infoSuite) TestMinimalInfoDirAndFileMethods(c *C) {
  1070  	dirs.SetRootDir("")
  1071  	info := snap.MinimalPlaceInfo("name", snap.R("1"))
  1072  	s.testDirAndFileMethods(c, info)
  1073  }
  1074  
  1075  func (s *infoSuite) TestDirAndFileMethods(c *C) {
  1076  	dirs.SetRootDir("")
  1077  	info := &snap.Info{SuggestedName: "name"}
  1078  	info.SideInfo = snap.SideInfo{Revision: snap.R(1)}
  1079  	s.testDirAndFileMethods(c, info)
  1080  }
  1081  
  1082  func (s *infoSuite) testDirAndFileMethods(c *C, info snap.PlaceInfo) {
  1083  	c.Check(info.MountDir(), Equals, fmt.Sprintf("%s/name/1", dirs.SnapMountDir))
  1084  	c.Check(info.MountFile(), Equals, "/var/lib/snapd/snaps/name_1.snap")
  1085  	c.Check(info.HooksDir(), Equals, fmt.Sprintf("%s/name/1/meta/hooks", dirs.SnapMountDir))
  1086  	c.Check(info.DataDir(), Equals, "/var/snap/name/1")
  1087  	c.Check(info.UserDataDir("/home/bob"), Equals, "/home/bob/snap/name/1")
  1088  	c.Check(info.UserCommonDataDir("/home/bob"), Equals, "/home/bob/snap/name/common")
  1089  	c.Check(info.CommonDataDir(), Equals, "/var/snap/name/common")
  1090  	c.Check(info.UserXdgRuntimeDir(12345), Equals, "/run/user/12345/snap.name")
  1091  	// XXX: Those are actually a globs, not directories
  1092  	c.Check(info.DataHomeDir(), Equals, "/home/*/snap/name/1")
  1093  	c.Check(info.CommonDataHomeDir(), Equals, "/home/*/snap/name/common")
  1094  	c.Check(info.XdgRuntimeDirs(), Equals, "/run/user/*/snap.name")
  1095  }
  1096  
  1097  func (s *infoSuite) TestMinimalInfoDirAndFileMethodsParallelInstall(c *C) {
  1098  	dirs.SetRootDir("")
  1099  	info := snap.MinimalPlaceInfo("name_instance", snap.R("1"))
  1100  	s.testInstanceDirAndFileMethods(c, info)
  1101  }
  1102  
  1103  func (s *infoSuite) TestDirAndFileMethodsParallelInstall(c *C) {
  1104  	dirs.SetRootDir("")
  1105  	info := &snap.Info{SuggestedName: "name", InstanceKey: "instance"}
  1106  	info.SideInfo = snap.SideInfo{Revision: snap.R(1)}
  1107  	s.testInstanceDirAndFileMethods(c, info)
  1108  }
  1109  
  1110  func (s *infoSuite) testInstanceDirAndFileMethods(c *C, info snap.PlaceInfo) {
  1111  	c.Check(info.MountDir(), Equals, fmt.Sprintf("%s/name_instance/1", dirs.SnapMountDir))
  1112  	c.Check(info.MountFile(), Equals, "/var/lib/snapd/snaps/name_instance_1.snap")
  1113  	c.Check(info.HooksDir(), Equals, fmt.Sprintf("%s/name_instance/1/meta/hooks", dirs.SnapMountDir))
  1114  	c.Check(info.DataDir(), Equals, "/var/snap/name_instance/1")
  1115  	c.Check(info.UserDataDir("/home/bob"), Equals, "/home/bob/snap/name_instance/1")
  1116  	c.Check(info.UserCommonDataDir("/home/bob"), Equals, "/home/bob/snap/name_instance/common")
  1117  	c.Check(info.CommonDataDir(), Equals, "/var/snap/name_instance/common")
  1118  	c.Check(info.UserXdgRuntimeDir(12345), Equals, "/run/user/12345/snap.name_instance")
  1119  	// XXX: Those are actually a globs, not directories
  1120  	c.Check(info.DataHomeDir(), Equals, "/home/*/snap/name_instance/1")
  1121  	c.Check(info.CommonDataHomeDir(), Equals, "/home/*/snap/name_instance/common")
  1122  	c.Check(info.XdgRuntimeDirs(), Equals, "/run/user/*/snap.name_instance")
  1123  }
  1124  
  1125  func BenchmarkTestParsePlaceInfoFromSnapFileName(b *testing.B) {
  1126  	for n := 0; n < b.N; n++ {
  1127  		for _, sn := range []string{
  1128  			"core_21.snap",
  1129  			"kernel_41.snap",
  1130  			"some-long-kernel-name-kernel_82.snap",
  1131  			"what-is-this-core_111.snap",
  1132  		} {
  1133  			snap.ParsePlaceInfoFromSnapFileName(sn)
  1134  		}
  1135  	}
  1136  }
  1137  
  1138  func (s *infoSuite) TestParsePlaceInfoFromSnapFileName(c *C) {
  1139  	tt := []struct {
  1140  		sn        string
  1141  		name      string
  1142  		rev       string
  1143  		expectErr string
  1144  	}{
  1145  		{sn: "", expectErr: "empty snap file name"},
  1146  		{sn: "name", expectErr: `snap file name "name" has invalid format \(missing '_'\)`},
  1147  		{sn: "name_", expectErr: `cannot parse revision in snap file name "name_": invalid snap revision: ""`},
  1148  		{sn: "name__", expectErr: "too many '_' in snap file name"},
  1149  		{sn: "_name.snap", expectErr: `snap file name \"_name.snap\" has invalid format \(no snap name before '_'\)`},
  1150  		{sn: "name_key.snap", expectErr: `cannot parse revision in snap file name "name_key.snap": invalid snap revision: "key"`},
  1151  		{sn: "name.snap", expectErr: `snap file name "name.snap" has invalid format \(missing '_'\)`},
  1152  		{sn: "name_12.snap", name: "name", rev: "12"},
  1153  		{sn: "name_key_12.snap", expectErr: "too many '_' in snap file name"},
  1154  	}
  1155  	for _, t := range tt {
  1156  		p, err := snap.ParsePlaceInfoFromSnapFileName(t.sn)
  1157  		if t.expectErr != "" {
  1158  			c.Check(err, ErrorMatches, t.expectErr)
  1159  		} else {
  1160  			c.Check(p.SnapName(), Equals, t.name)
  1161  			c.Check(p.SnapRevision(), Equals, snap.R(t.rev))
  1162  		}
  1163  	}
  1164  }
  1165  
  1166  func makeFakeDesktopFile(c *C, name, content string) string {
  1167  	df := filepath.Join(dirs.SnapDesktopFilesDir, name)
  1168  	err := os.MkdirAll(filepath.Dir(df), 0755)
  1169  	c.Assert(err, IsNil)
  1170  	err = ioutil.WriteFile(df, []byte(content), 0644)
  1171  	c.Assert(err, IsNil)
  1172  	return df
  1173  }
  1174  
  1175  func (s *infoSuite) TestAppDesktopFile(c *C) {
  1176  	snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{})
  1177  	snapInfo, err := snap.ReadInfo("sample", &snap.SideInfo{})
  1178  	c.Assert(err, IsNil)
  1179  
  1180  	c.Check(snapInfo.InstanceName(), Equals, "sample")
  1181  	c.Check(snapInfo.Apps["app"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample_app.desktop`)
  1182  	c.Check(snapInfo.Apps["sample"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample_sample.desktop`)
  1183  	c.Check(snapInfo.DesktopPrefix(), Equals, "sample")
  1184  
  1185  	// snap with instance key
  1186  	snapInfo.InstanceKey = "instance"
  1187  	c.Check(snapInfo.InstanceName(), Equals, "sample_instance")
  1188  	c.Check(snapInfo.Apps["app"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample\+instance_app.desktop`)
  1189  	c.Check(snapInfo.Apps["sample"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample\+instance_sample.desktop`)
  1190  	c.Check(snapInfo.DesktopPrefix(), Equals, "sample+instance")
  1191  }
  1192  
  1193  const coreSnapYaml = `name: core
  1194  version: 0
  1195  type: os
  1196  plugs:
  1197    network-bind:
  1198    core-support:
  1199  `
  1200  
  1201  // reading snap via ReadInfoFromSnapFile renames clashing core plugs
  1202  func (s *infoSuite) TestReadInfoFromSnapFileRenamesCorePlus(c *C) {
  1203  	snapPath := snaptest.MakeTestSnapWithFiles(c, coreSnapYaml, nil)
  1204  
  1205  	snapf, err := snapfile.Open(snapPath)
  1206  	c.Assert(err, IsNil)
  1207  
  1208  	info, err := snap.ReadInfoFromSnapFile(snapf, nil)
  1209  	c.Assert(err, IsNil)
  1210  	c.Check(info.Plugs["network-bind"], IsNil)
  1211  	c.Check(info.Plugs["core-support"], IsNil)
  1212  	c.Check(info.Plugs["network-bind-plug"], NotNil)
  1213  	c.Check(info.Plugs["core-support-plug"], NotNil)
  1214  }
  1215  
  1216  // reading snap via ReadInfo renames clashing core plugs
  1217  func (s *infoSuite) TestReadInfoRenamesCorePlugs(c *C) {
  1218  	si := &snap.SideInfo{Revision: snap.R(42), RealName: "core"}
  1219  	snaptest.MockSnap(c, coreSnapYaml, si)
  1220  	info, err := snap.ReadInfo("core", si)
  1221  	c.Assert(err, IsNil)
  1222  	c.Check(info.Plugs["network-bind"], IsNil)
  1223  	c.Check(info.Plugs["core-support"], IsNil)
  1224  	c.Check(info.Plugs["network-bind-plug"], NotNil)
  1225  	c.Check(info.Plugs["core-support-plug"], NotNil)
  1226  }
  1227  
  1228  // reading snap via InfoFromSnapYaml renames clashing core plugs
  1229  func (s *infoSuite) TestInfoFromSnapYamlRenamesCorePlugs(c *C) {
  1230  	info, err := snap.InfoFromSnapYaml([]byte(coreSnapYaml))
  1231  	c.Assert(err, IsNil)
  1232  	c.Check(info.Plugs["network-bind"], IsNil)
  1233  	c.Check(info.Plugs["core-support"], IsNil)
  1234  	c.Check(info.Plugs["network-bind-plug"], NotNil)
  1235  	c.Check(info.Plugs["core-support-plug"], NotNil)
  1236  }
  1237  
  1238  func (s *infoSuite) TestInfoServices(c *C) {
  1239  	info, err := snap.InfoFromSnapYaml([]byte(`name: pans
  1240  apps:
  1241    svc1:
  1242      daemon: potato
  1243    svc2:
  1244      daemon: no
  1245    app1:
  1246    app2:
  1247  `))
  1248  	c.Assert(err, IsNil)
  1249  	svcNames := []string{}
  1250  	svcs := info.Services()
  1251  	for i := range svcs {
  1252  		svcNames = append(svcNames, svcs[i].ServiceName())
  1253  	}
  1254  	sort.Strings(svcNames)
  1255  	c.Check(svcNames, DeepEquals, []string{
  1256  		"snap.pans.svc1.service",
  1257  		"snap.pans.svc2.service",
  1258  	})
  1259  
  1260  	// snap with instance
  1261  	info.InstanceKey = "instance"
  1262  	svcNames = []string{}
  1263  	for i := range info.Services() {
  1264  		svcNames = append(svcNames, svcs[i].ServiceName())
  1265  	}
  1266  	sort.Strings(svcNames)
  1267  	c.Check(svcNames, DeepEquals, []string{
  1268  		"snap.pans_instance.svc1.service",
  1269  		"snap.pans_instance.svc2.service",
  1270  	})
  1271  }
  1272  
  1273  func (s *infoSuite) TestAppInfoIsService(c *C) {
  1274  	info, err := snap.InfoFromSnapYaml([]byte(`name: pans
  1275  apps:
  1276    svc1:
  1277      daemon: potato
  1278    svc2:
  1279      daemon: no
  1280    svc3:
  1281      daemon: simple
  1282      daemon-scope: user
  1283    app1:
  1284    app2:
  1285  `))
  1286  	c.Assert(err, IsNil)
  1287  
  1288  	svc := info.Apps["svc1"]
  1289  	c.Check(svc.IsService(), Equals, true)
  1290  	c.Check(svc.DaemonScope, Equals, snap.SystemDaemon)
  1291  	c.Check(svc.ServiceName(), Equals, "snap.pans.svc1.service")
  1292  	c.Check(svc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.svc1.service")
  1293  
  1294  	c.Check(info.Apps["svc2"].IsService(), Equals, true)
  1295  	userSvc := info.Apps["svc3"]
  1296  	c.Check(userSvc.IsService(), Equals, true)
  1297  	c.Check(userSvc.DaemonScope, Equals, snap.UserDaemon)
  1298  	c.Check(userSvc.ServiceName(), Equals, "snap.pans.svc3.service")
  1299  	c.Check(userSvc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/user/snap.pans.svc3.service")
  1300  	c.Check(info.Apps["app1"].IsService(), Equals, false)
  1301  	c.Check(info.Apps["app1"].IsService(), Equals, false)
  1302  
  1303  	// snap with instance key
  1304  	info.InstanceKey = "instance"
  1305  	c.Check(svc.ServiceName(), Equals, "snap.pans_instance.svc1.service")
  1306  	c.Check(svc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.svc1.service")
  1307  	c.Check(userSvc.ServiceName(), Equals, "snap.pans_instance.svc3.service")
  1308  	c.Check(userSvc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/user/snap.pans_instance.svc3.service")
  1309  }
  1310  
  1311  func (s *infoSuite) TestAppInfoStringer(c *C) {
  1312  	info, err := snap.InfoFromSnapYaml([]byte(`name: asnap
  1313  apps:
  1314    one:
  1315     daemon: simple
  1316  `))
  1317  	c.Assert(err, IsNil)
  1318  	c.Check(fmt.Sprintf("%q", info.Apps["one"].String()), Equals, `"asnap.one"`)
  1319  }
  1320  
  1321  func (s *infoSuite) TestSocketFile(c *C) {
  1322  	info, err := snap.InfoFromSnapYaml([]byte(`name: pans
  1323  apps:
  1324    app1:
  1325      daemon: true
  1326      sockets:
  1327        sock1:
  1328          listen-stream: /tmp/sock1.socket
  1329  `))
  1330  
  1331  	c.Assert(err, IsNil)
  1332  
  1333  	app := info.Apps["app1"]
  1334  	socket := app.Sockets["sock1"]
  1335  	c.Check(socket.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.app1.sock1.socket")
  1336  
  1337  	// snap with instance key
  1338  	info.InstanceKey = "instance"
  1339  	c.Check(socket.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.app1.sock1.socket")
  1340  }
  1341  
  1342  func (s *infoSuite) TestTimerFile(c *C) {
  1343  	info, err := snap.InfoFromSnapYaml([]byte(`name: pans
  1344  apps:
  1345    app1:
  1346      daemon: true
  1347      timer: mon,10:00-12:00
  1348  `))
  1349  
  1350  	c.Assert(err, IsNil)
  1351  
  1352  	app := info.Apps["app1"]
  1353  	timerFile := app.Timer.File()
  1354  	c.Check(timerFile, Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.app1.timer")
  1355  	c.Check(strings.TrimSuffix(app.ServiceFile(), ".service")+".timer", Equals, timerFile)
  1356  
  1357  	// snap with instance key
  1358  	info.InstanceKey = "instance"
  1359  	c.Check(app.Timer.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.app1.timer")
  1360  }
  1361  
  1362  func (s *infoSuite) TestLayoutParsing(c *C) {
  1363  	info, err := snap.InfoFromSnapYaml([]byte(`name: layout-demo
  1364  layout:
  1365    /usr:
  1366      bind: $SNAP/usr
  1367    /mytmp:
  1368      type: tmpfs
  1369      mode: 1777
  1370    /mylink:
  1371      symlink: /link/target
  1372  `))
  1373  	c.Assert(err, IsNil)
  1374  
  1375  	layout := info.Layout
  1376  	c.Assert(layout, NotNil)
  1377  	c.Check(layout["/usr"], DeepEquals, &snap.Layout{
  1378  		Snap:  info,
  1379  		Path:  "/usr",
  1380  		User:  "root",
  1381  		Group: "root",
  1382  		Mode:  0755,
  1383  		Bind:  "$SNAP/usr",
  1384  	})
  1385  	c.Check(layout["/mytmp"], DeepEquals, &snap.Layout{
  1386  		Snap:  info,
  1387  		Path:  "/mytmp",
  1388  		Type:  "tmpfs",
  1389  		User:  "root",
  1390  		Group: "root",
  1391  		Mode:  01777,
  1392  	})
  1393  	c.Check(layout["/mylink"], DeepEquals, &snap.Layout{
  1394  		Snap:    info,
  1395  		Path:    "/mylink",
  1396  		User:    "root",
  1397  		Group:   "root",
  1398  		Mode:    0755,
  1399  		Symlink: "/link/target",
  1400  	})
  1401  }
  1402  
  1403  func (s *infoSuite) TestPlugInfoString(c *C) {
  1404  	plug := &snap.PlugInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug"}
  1405  	c.Assert(plug.String(), Equals, "snap:plug")
  1406  }
  1407  
  1408  func (s *infoSuite) TestSlotInfoString(c *C) {
  1409  	slot := &snap.SlotInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "slot"}
  1410  	c.Assert(slot.String(), Equals, "snap:slot")
  1411  }
  1412  
  1413  func (s *infoSuite) TestPlugInfoAttr(c *C) {
  1414  	var val string
  1415  	var intVal int
  1416  
  1417  	plug := &snap.PlugInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug", Interface: "interface", Attrs: map[string]interface{}{"key": "value", "number": int(123)}}
  1418  	c.Assert(plug.Attr("key", &val), IsNil)
  1419  	c.Check(val, Equals, "value")
  1420  
  1421  	c.Assert(plug.Attr("number", &intVal), IsNil)
  1422  	c.Check(intVal, Equals, 123)
  1423  
  1424  	c.Check(plug.Attr("key", &intVal), ErrorMatches, `snap "snap" has interface "interface" with invalid value type for "key" attribute`)
  1425  	c.Check(plug.Attr("unknown", &val), ErrorMatches, `snap "snap" does not have attribute "unknown" for interface "interface"`)
  1426  	c.Check(plug.Attr("key", intVal), ErrorMatches, `internal error: cannot get "key" attribute of interface "interface" with non-pointer value`)
  1427  }
  1428  
  1429  func (s *infoSuite) TestSlotInfoAttr(c *C) {
  1430  	var val string
  1431  	var intVal int
  1432  
  1433  	slot := &snap.SlotInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug", Interface: "interface", Attrs: map[string]interface{}{"key": "value", "number": int(123)}}
  1434  
  1435  	c.Assert(slot.Attr("key", &val), IsNil)
  1436  	c.Check(val, Equals, "value")
  1437  
  1438  	c.Assert(slot.Attr("number", &intVal), IsNil)
  1439  	c.Check(intVal, Equals, 123)
  1440  
  1441  	c.Check(slot.Attr("key", &intVal), ErrorMatches, `snap "snap" has interface "interface" with invalid value type for "key" attribute`)
  1442  	c.Check(slot.Attr("unknown", &val), ErrorMatches, `snap "snap" does not have attribute "unknown" for interface "interface"`)
  1443  	c.Check(slot.Attr("key", intVal), ErrorMatches, `internal error: cannot get "key" attribute of interface "interface" with non-pointer value`)
  1444  }
  1445  
  1446  func (s *infoSuite) TestDottedPathSlot(c *C) {
  1447  	attrs := map[string]interface{}{
  1448  		"nested": map[string]interface{}{
  1449  			"foo": "bar",
  1450  		},
  1451  	}
  1452  
  1453  	slot := &snap.SlotInfo{Attrs: attrs}
  1454  	c.Assert(slot, NotNil)
  1455  
  1456  	v, ok := slot.Lookup("nested.foo")
  1457  	c.Assert(ok, Equals, true)
  1458  	c.Assert(v, Equals, "bar")
  1459  
  1460  	v, ok = slot.Lookup("nested")
  1461  	c.Assert(ok, Equals, true)
  1462  	c.Assert(v, DeepEquals, map[string]interface{}{
  1463  		"foo": "bar",
  1464  	})
  1465  
  1466  	_, ok = slot.Lookup("x")
  1467  	c.Assert(ok, Equals, false)
  1468  
  1469  	_, ok = slot.Lookup("..")
  1470  	c.Assert(ok, Equals, false)
  1471  
  1472  	_, ok = slot.Lookup("nested.foo.x")
  1473  	c.Assert(ok, Equals, false)
  1474  
  1475  	_, ok = slot.Lookup("nested.x")
  1476  	c.Assert(ok, Equals, false)
  1477  }
  1478  
  1479  func (s *infoSuite) TestDottedPathPlug(c *C) {
  1480  	attrs := map[string]interface{}{
  1481  		"nested": map[string]interface{}{
  1482  			"foo": "bar",
  1483  		},
  1484  	}
  1485  
  1486  	plug := &snap.PlugInfo{Attrs: attrs}
  1487  	c.Assert(plug, NotNil)
  1488  
  1489  	v, ok := plug.Lookup("nested")
  1490  	c.Assert(ok, Equals, true)
  1491  	c.Assert(v, DeepEquals, map[string]interface{}{
  1492  		"foo": "bar",
  1493  	})
  1494  
  1495  	v, ok = plug.Lookup("nested.foo")
  1496  	c.Assert(ok, Equals, true)
  1497  	c.Assert(v, Equals, "bar")
  1498  
  1499  	_, ok = plug.Lookup("x")
  1500  	c.Assert(ok, Equals, false)
  1501  
  1502  	_, ok = plug.Lookup("..")
  1503  	c.Assert(ok, Equals, false)
  1504  
  1505  	_, ok = plug.Lookup("nested.foo.x")
  1506  	c.Assert(ok, Equals, false)
  1507  }
  1508  
  1509  func (s *infoSuite) TestDefaultContentProviders(c *C) {
  1510  	info, err := snap.InfoFromSnapYaml([]byte(yamlNeedDf))
  1511  	c.Assert(err, IsNil)
  1512  
  1513  	plugs := make([]*snap.PlugInfo, 0, len(info.Plugs))
  1514  	for _, plug := range info.Plugs {
  1515  		plugs = append(plugs, plug)
  1516  	}
  1517  
  1518  	dps := snap.DefaultContentProviders(plugs)
  1519  	c.Check(dps, DeepEquals, map[string][]string{"gtk-common-themes": {"gtk-3-themes", "icon-themes"}})
  1520  }
  1521  
  1522  func (s *infoSuite) TestExpandSnapVariables(c *C) {
  1523  	dirs.SetRootDir("")
  1524  	info, err := snap.InfoFromSnapYaml([]byte(`name: foo`))
  1525  	c.Assert(err, IsNil)
  1526  	info.Revision = snap.R(42)
  1527  	c.Assert(info.ExpandSnapVariables("$SNAP/stuff"), Equals, "/snap/foo/42/stuff")
  1528  	c.Assert(info.ExpandSnapVariables("$SNAP_DATA/stuff"), Equals, "/var/snap/foo/42/stuff")
  1529  	c.Assert(info.ExpandSnapVariables("$SNAP_COMMON/stuff"), Equals, "/var/snap/foo/common/stuff")
  1530  	c.Assert(info.ExpandSnapVariables("$GARBAGE/rocks"), Equals, "/rocks")
  1531  
  1532  	info.InstanceKey = "instance"
  1533  	// Despite setting the instance key the variables expand to the same
  1534  	// value as before. This is because they are used from inside the mount
  1535  	// namespace of the instantiated snap where the mount backend will
  1536  	// ensure that the regular (non-instance) paths contain
  1537  	// instance-specific code and data.
  1538  	c.Assert(info.ExpandSnapVariables("$SNAP/stuff"), Equals, "/snap/foo/42/stuff")
  1539  	c.Assert(info.ExpandSnapVariables("$SNAP_DATA/stuff"), Equals, "/var/snap/foo/42/stuff")
  1540  	c.Assert(info.ExpandSnapVariables("$SNAP_COMMON/stuff"), Equals, "/var/snap/foo/common/stuff")
  1541  	c.Assert(info.ExpandSnapVariables("$GARBAGE/rocks"), Equals, "/rocks")
  1542  }
  1543  
  1544  func (s *infoSuite) TestStopModeTypeKillMode(c *C) {
  1545  	for _, t := range []struct {
  1546  		stopMode string
  1547  		killall  bool
  1548  	}{
  1549  		{"", true},
  1550  		{"sigterm", false},
  1551  		{"sigterm-all", true},
  1552  		{"sighup", false},
  1553  		{"sighup-all", true},
  1554  		{"sigusr1", false},
  1555  		{"sigusr1-all", true},
  1556  		{"sigusr2", false},
  1557  		{"sigusr2-all", true},
  1558  	} {
  1559  		c.Check(snap.StopModeType(t.stopMode).KillAll(), Equals, t.killall, Commentf("wrong KillAll for %v", t.stopMode))
  1560  	}
  1561  }
  1562  
  1563  func (s *infoSuite) TestStopModeTypeKillSignal(c *C) {
  1564  	for _, t := range []struct {
  1565  		stopMode string
  1566  		killSig  string
  1567  	}{
  1568  		{"", ""},
  1569  		{"sigterm", "SIGTERM"},
  1570  		{"sigterm-all", "SIGTERM"},
  1571  		{"sighup", "SIGHUP"},
  1572  		{"sighup-all", "SIGHUP"},
  1573  		{"sigusr1", "SIGUSR1"},
  1574  		{"sigusr1-all", "SIGUSR1"},
  1575  		{"sigusr2", "SIGUSR2"},
  1576  		{"sigusr2-all", "SIGUSR2"},
  1577  	} {
  1578  		c.Check(snap.StopModeType(t.stopMode).KillSignal(), Equals, t.killSig)
  1579  	}
  1580  }
  1581  
  1582  func (s *infoSuite) TestSplitInstanceName(c *C) {
  1583  	snapName, instanceKey := snap.SplitInstanceName("foo_bar")
  1584  	c.Check(snapName, Equals, "foo")
  1585  	c.Check(instanceKey, Equals, "bar")
  1586  
  1587  	snapName, instanceKey = snap.SplitInstanceName("foo")
  1588  	c.Check(snapName, Equals, "foo")
  1589  	c.Check(instanceKey, Equals, "")
  1590  
  1591  	// all following instance names are invalid
  1592  
  1593  	snapName, instanceKey = snap.SplitInstanceName("_bar")
  1594  	c.Check(snapName, Equals, "")
  1595  	c.Check(instanceKey, Equals, "bar")
  1596  
  1597  	snapName, instanceKey = snap.SplitInstanceName("foo___bar_bar")
  1598  	c.Check(snapName, Equals, "foo")
  1599  	c.Check(instanceKey, Equals, "__bar_bar")
  1600  
  1601  	snapName, instanceKey = snap.SplitInstanceName("")
  1602  	c.Check(snapName, Equals, "")
  1603  	c.Check(instanceKey, Equals, "")
  1604  }
  1605  
  1606  func (s *infoSuite) TestInstanceSnapName(c *C) {
  1607  	c.Check(snap.InstanceSnap("foo_bar"), Equals, "foo")
  1608  	c.Check(snap.InstanceSnap("foo"), Equals, "foo")
  1609  
  1610  	c.Check(snap.InstanceName("foo", "bar"), Equals, "foo_bar")
  1611  	c.Check(snap.InstanceName("foo", ""), Equals, "foo")
  1612  }
  1613  
  1614  func (s *infoSuite) TestInstanceNameInSnapInfo(c *C) {
  1615  	info := &snap.Info{
  1616  		SuggestedName: "snap-name",
  1617  		InstanceKey:   "foo",
  1618  	}
  1619  
  1620  	c.Check(info.InstanceName(), Equals, "snap-name_foo")
  1621  	c.Check(info.SnapName(), Equals, "snap-name")
  1622  
  1623  	info.InstanceKey = ""
  1624  	c.Check(info.InstanceName(), Equals, "snap-name")
  1625  	c.Check(info.SnapName(), Equals, "snap-name")
  1626  }
  1627  
  1628  func (s *infoSuite) TestIsActive(c *C) {
  1629  	info1 := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(1)})
  1630  	info2 := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(2)})
  1631  	// no current -> not active
  1632  	c.Check(info1.IsActive(), Equals, false)
  1633  	c.Check(info2.IsActive(), Equals, false)
  1634  
  1635  	mountdir := info1.MountDir()
  1636  	dir, rev := filepath.Split(mountdir)
  1637  	c.Assert(os.MkdirAll(dir, 0755), IsNil)
  1638  	cur := filepath.Join(dir, "current")
  1639  	c.Assert(os.Symlink(rev, cur), IsNil)
  1640  
  1641  	// is current -> is active
  1642  	c.Check(info1.IsActive(), Equals, true)
  1643  	c.Check(info2.IsActive(), Equals, false)
  1644  }
  1645  
  1646  func (s *infoSuite) TestInfoTypeSnapdBackwardCompatibility(c *C) {
  1647  	const snapdYaml = `
  1648  name: snapd
  1649  type: app
  1650  version: 1
  1651  `
  1652  	snapInfo := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(1), SnapID: "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4"})
  1653  	c.Check(snapInfo.Type(), Equals, snap.TypeSnapd)
  1654  }
  1655  
  1656  func (s *infoSuite) TestDirAndFileHelpers(c *C) {
  1657  	dirs.SetRootDir("")
  1658  
  1659  	c.Check(snap.MountDir("name", snap.R(1)), Equals, fmt.Sprintf("%s/name/1", dirs.SnapMountDir))
  1660  	c.Check(snap.MountFile("name", snap.R(1)), Equals, "/var/lib/snapd/snaps/name_1.snap")
  1661  	c.Check(snap.HooksDir("name", snap.R(1)), Equals, fmt.Sprintf("%s/name/1/meta/hooks", dirs.SnapMountDir))
  1662  	c.Check(snap.DataDir("name", snap.R(1)), Equals, "/var/snap/name/1")
  1663  	c.Check(snap.CommonDataDir("name"), Equals, "/var/snap/name/common")
  1664  	c.Check(snap.UserDataDir("/home/bob", "name", snap.R(1)), Equals, "/home/bob/snap/name/1")
  1665  	c.Check(snap.UserCommonDataDir("/home/bob", "name"), Equals, "/home/bob/snap/name/common")
  1666  	c.Check(snap.UserXdgRuntimeDir(12345, "name"), Equals, "/run/user/12345/snap.name")
  1667  	c.Check(snap.UserSnapDir("/home/bob", "name"), Equals, "/home/bob/snap/name")
  1668  
  1669  	c.Check(snap.MountDir("name_instance", snap.R(1)), Equals, fmt.Sprintf("%s/name_instance/1", dirs.SnapMountDir))
  1670  	c.Check(snap.MountFile("name_instance", snap.R(1)), Equals, "/var/lib/snapd/snaps/name_instance_1.snap")
  1671  	c.Check(snap.HooksDir("name_instance", snap.R(1)), Equals, fmt.Sprintf("%s/name_instance/1/meta/hooks", dirs.SnapMountDir))
  1672  	c.Check(snap.DataDir("name_instance", snap.R(1)), Equals, "/var/snap/name_instance/1")
  1673  	c.Check(snap.CommonDataDir("name_instance"), Equals, "/var/snap/name_instance/common")
  1674  	c.Check(snap.UserDataDir("/home/bob", "name_instance", snap.R(1)), Equals, "/home/bob/snap/name_instance/1")
  1675  	c.Check(snap.UserCommonDataDir("/home/bob", "name_instance"), Equals, "/home/bob/snap/name_instance/common")
  1676  	c.Check(snap.UserXdgRuntimeDir(12345, "name_instance"), Equals, "/run/user/12345/snap.name_instance")
  1677  	c.Check(snap.UserSnapDir("/home/bob", "name_instance"), Equals, "/home/bob/snap/name_instance")
  1678  }
  1679  
  1680  func (s *infoSuite) TestSortByType(c *C) {
  1681  	infos := []*snap.Info{
  1682  		{SuggestedName: "app1", SnapType: "app"},
  1683  		{SuggestedName: "os1", SnapType: "os"},
  1684  		{SuggestedName: "base1", SnapType: "base"},
  1685  		{SuggestedName: "gadget1", SnapType: "gadget"},
  1686  		{SuggestedName: "kernel1", SnapType: "kernel"},
  1687  		{SuggestedName: "app2", SnapType: "app"},
  1688  		{SuggestedName: "os2", SnapType: "os"},
  1689  		{SuggestedName: "snapd", SnapType: "snapd"},
  1690  		{SuggestedName: "base2", SnapType: "base"},
  1691  		{SuggestedName: "gadget2", SnapType: "gadget"},
  1692  		{SuggestedName: "kernel2", SnapType: "kernel"},
  1693  	}
  1694  	sort.Stable(snap.ByType(infos))
  1695  
  1696  	c.Check(infos, DeepEquals, []*snap.Info{
  1697  		{SuggestedName: "snapd", SnapType: "snapd"},
  1698  		{SuggestedName: "os1", SnapType: "os"},
  1699  		{SuggestedName: "os2", SnapType: "os"},
  1700  		{SuggestedName: "kernel1", SnapType: "kernel"},
  1701  		{SuggestedName: "kernel2", SnapType: "kernel"},
  1702  		{SuggestedName: "base1", SnapType: "base"},
  1703  		{SuggestedName: "base2", SnapType: "base"},
  1704  		{SuggestedName: "gadget1", SnapType: "gadget"},
  1705  		{SuggestedName: "gadget2", SnapType: "gadget"},
  1706  		{SuggestedName: "app1", SnapType: "app"},
  1707  		{SuggestedName: "app2", SnapType: "app"},
  1708  	})
  1709  }
  1710  
  1711  func (s *infoSuite) TestSortByTypeAgain(c *C) {
  1712  	core := &snap.Info{SnapType: snap.TypeOS}
  1713  	base := &snap.Info{SnapType: snap.TypeBase}
  1714  	app := &snap.Info{SnapType: snap.TypeApp}
  1715  	snapd := &snap.Info{}
  1716  	snapd.SideInfo = snap.SideInfo{RealName: "snapd"}
  1717  
  1718  	byType := func(snaps ...*snap.Info) []*snap.Info {
  1719  		sort.Stable(snap.ByType(snaps))
  1720  		return snaps
  1721  	}
  1722  
  1723  	c.Check(byType(base, core), DeepEquals, []*snap.Info{core, base})
  1724  	c.Check(byType(app, core), DeepEquals, []*snap.Info{core, app})
  1725  	c.Check(byType(app, base), DeepEquals, []*snap.Info{base, app})
  1726  	c.Check(byType(app, base, core), DeepEquals, []*snap.Info{core, base, app})
  1727  	c.Check(byType(app, core, base), DeepEquals, []*snap.Info{core, base, app})
  1728  
  1729  	c.Check(byType(app, core, base, snapd), DeepEquals, []*snap.Info{snapd, core, base, app})
  1730  	c.Check(byType(app, snapd, core, base), DeepEquals, []*snap.Info{snapd, core, base, app})
  1731  }
  1732  
  1733  func (s *infoSuite) TestMedia(c *C) {
  1734  	c.Check(snap.MediaInfos{}.IconURL(), Equals, "")
  1735  
  1736  	media := snap.MediaInfos{
  1737  		{
  1738  			Type: "screenshot",
  1739  			URL:  "https://example.com/shot1.svg",
  1740  		}, {
  1741  			Type: "icon",
  1742  			URL:  "https://example.com/icon.png",
  1743  		}, {
  1744  			Type:   "screenshot",
  1745  			URL:    "https://example.com/shot2.svg",
  1746  			Width:  42,
  1747  			Height: 17,
  1748  		},
  1749  	}
  1750  
  1751  	c.Check(media.IconURL(), Equals, "https://example.com/icon.png")
  1752  }
  1753  
  1754  func (s *infoSuite) TestSortApps(c *C) {
  1755  	tcs := []struct {
  1756  		err    string
  1757  		apps   []*snap.AppInfo
  1758  		sorted []string
  1759  	}{{
  1760  		apps: []*snap.AppInfo{
  1761  			{Name: "bar", Before: []string{"baz"}},
  1762  			{Name: "foo"},
  1763  		},
  1764  		sorted: []string{"bar", "foo"},
  1765  	}, {
  1766  		apps: []*snap.AppInfo{
  1767  			{Name: "bar", Before: []string{"foo"}},
  1768  			{Name: "foo", Before: []string{"baz"}},
  1769  		},
  1770  		sorted: []string{"bar", "foo"},
  1771  	}, {
  1772  		apps: []*snap.AppInfo{
  1773  			{Name: "bar", Before: []string{"foo"}},
  1774  		},
  1775  		sorted: []string{"bar"},
  1776  	}, {
  1777  		apps: []*snap.AppInfo{
  1778  			{Name: "bar", After: []string{"foo"}},
  1779  		},
  1780  		sorted: []string{"bar"},
  1781  	}, {
  1782  		apps: []*snap.AppInfo{
  1783  			{Name: "bar", Before: []string{"baz"}},
  1784  			{Name: "baz", After: []string{"bar", "foo"}},
  1785  			{Name: "foo"},
  1786  		},
  1787  		sorted: []string{"bar", "foo", "baz"},
  1788  	}, {
  1789  		apps: []*snap.AppInfo{
  1790  			{Name: "foo", After: []string{"bar", "zed"}},
  1791  			{Name: "bar", Before: []string{"foo"}},
  1792  			{Name: "baz", After: []string{"foo"}},
  1793  			{Name: "zed"},
  1794  		},
  1795  		sorted: []string{"bar", "zed", "foo", "baz"},
  1796  	}, {
  1797  		apps: []*snap.AppInfo{
  1798  			{Name: "foo", After: []string{"baz"}},
  1799  			{Name: "bar", Before: []string{"baz"}},
  1800  			{Name: "baz"},
  1801  			{Name: "zed", After: []string{"foo", "bar", "baz"}},
  1802  		},
  1803  		sorted: []string{"bar", "baz", "foo", "zed"},
  1804  	}, {
  1805  		apps: []*snap.AppInfo{
  1806  			{Name: "foo", Before: []string{"bar"}, After: []string{"zed"}},
  1807  			{Name: "bar", Before: []string{"baz"}},
  1808  			{Name: "baz", Before: []string{"zed"}},
  1809  			{Name: "zed"},
  1810  		},
  1811  		err: `applications are part of a before/after cycle: ((foo|bar|baz|zed)(, )?){4}`,
  1812  	}, {
  1813  		apps: []*snap.AppInfo{
  1814  			{Name: "foo", Before: []string{"bar"}},
  1815  			{Name: "bar", Before: []string{"foo"}},
  1816  			{Name: "baz", Before: []string{"foo"}, After: []string{"bar"}},
  1817  		},
  1818  		err: `applications are part of a before/after cycle: ((foo|bar|baz)(, )?){3}`,
  1819  	}, {
  1820  		apps: []*snap.AppInfo{
  1821  			{Name: "baz", After: []string{"bar"}},
  1822  			{Name: "foo"},
  1823  			{Name: "bar", After: []string{"foo"}},
  1824  		},
  1825  		sorted: []string{"foo", "bar", "baz"},
  1826  	}}
  1827  	for _, tc := range tcs {
  1828  		sorted, err := snap.SortServices(tc.apps)
  1829  		if tc.err != "" {
  1830  			c.Assert(err, ErrorMatches, tc.err)
  1831  		} else {
  1832  			c.Assert(err, IsNil)
  1833  			c.Assert(sorted, HasLen, len(tc.sorted))
  1834  			sortedNames := make([]string, len(sorted))
  1835  			for i, app := range sorted {
  1836  				sortedNames[i] = app.Name
  1837  			}
  1838  			c.Assert(sortedNames, DeepEquals, tc.sorted)
  1839  		}
  1840  	}
  1841  }
  1842  
  1843  func (s *infoSuite) TestSortAppInfoBySnapApp(c *C) {
  1844  	snap1 := &snap.Info{SuggestedName: "snapa"}
  1845  	snap2 := &snap.Info{SuggestedName: "snapb"}
  1846  	infos := []*snap.AppInfo{
  1847  		{Snap: snap1, Name: "b"},
  1848  		{Snap: snap2, Name: "b"},
  1849  		{Snap: snap1, Name: "a"},
  1850  		{Snap: snap2, Name: "a"},
  1851  	}
  1852  	sort.Stable(snap.AppInfoBySnapApp(infos))
  1853  
  1854  	c.Check(infos, DeepEquals, []*snap.AppInfo{
  1855  		{Snap: snap1, Name: "a"},
  1856  		{Snap: snap1, Name: "b"},
  1857  		{Snap: snap2, Name: "a"},
  1858  		{Snap: snap2, Name: "b"},
  1859  	})
  1860  }