github.com/stulluk/snapd@v0.0.0-20210611110309-f6d5d5bd24b0/snap/info_test.go (about)

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