gitee.com/mysnapcore/mysnapd@v0.1.0/snap/info_test.go (about)

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