github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/snap/info_test.go (about)

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