github.com/rigado/snapd@v2.42.5-go-mod+incompatible/snap/info_snap_yaml_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2016 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  	"regexp"
    24  	"testing"
    25  	"time"
    26  
    27  	. "gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/snap"
    30  	"github.com/snapcore/snapd/strutil"
    31  	"github.com/snapcore/snapd/timeout"
    32  
    33  	"github.com/snapcore/snapd/testutil"
    34  )
    35  
    36  // Hook up check.v1 into the "go test" runner
    37  func Test(t *testing.T) { TestingT(t) }
    38  
    39  type InfoSnapYamlTestSuite struct {
    40  	testutil.BaseTest
    41  }
    42  
    43  var _ = Suite(&InfoSnapYamlTestSuite{})
    44  
    45  var mockYaml = []byte(`name: foo
    46  version: 1.0
    47  type: app
    48  `)
    49  
    50  func (s *InfoSnapYamlTestSuite) SetUpTest(c *C) {
    51  	s.BaseTest.SetUpTest(c)
    52  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    53  }
    54  
    55  func (s *InfoSnapYamlTestSuite) TearDownTest(c *C) {
    56  	s.BaseTest.TearDownTest(c)
    57  }
    58  
    59  func (s *InfoSnapYamlTestSuite) TestSimple(c *C) {
    60  	info, err := snap.InfoFromSnapYaml(mockYaml)
    61  	c.Assert(err, IsNil)
    62  	c.Assert(info.InstanceName(), Equals, "foo")
    63  	c.Assert(info.Version, Equals, "1.0")
    64  	c.Assert(info.GetType(), Equals, snap.TypeApp)
    65  	c.Assert(info.Epoch, DeepEquals, snap.E("0"))
    66  }
    67  
    68  func (s *InfoSnapYamlTestSuite) TestSnapdTypeAddedByMagic(c *C) {
    69  	info, err := snap.InfoFromSnapYaml([]byte(`name: snapd
    70  version: 1.0`))
    71  	c.Assert(err, IsNil)
    72  	c.Assert(info.InstanceName(), Equals, "snapd")
    73  	c.Assert(info.Version, Equals, "1.0")
    74  	c.Assert(info.GetType(), Equals, snap.TypeSnapd)
    75  }
    76  
    77  func (s *InfoSnapYamlTestSuite) TestFail(c *C) {
    78  	_, err := snap.InfoFromSnapYaml([]byte("random-crap"))
    79  	c.Assert(err, ErrorMatches, "(?m)cannot parse snap.yaml:.*")
    80  }
    81  
    82  type YamlSuite struct {
    83  	restore func()
    84  	testutil.BaseTest
    85  }
    86  
    87  var _ = Suite(&YamlSuite{})
    88  
    89  func (s *YamlSuite) SetUpTest(c *C) {
    90  	s.BaseTest.SetUpTest(c)
    91  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    92  	hookType := snap.NewHookType(regexp.MustCompile(".*"))
    93  	s.restore = snap.MockSupportedHookTypes([]*snap.HookType{hookType})
    94  }
    95  
    96  func (s *YamlSuite) TearDownTest(c *C) {
    97  	s.BaseTest.TearDownTest(c)
    98  	s.restore()
    99  }
   100  
   101  func (s *YamlSuite) TestUnmarshalGarbage(c *C) {
   102  	_, err := snap.InfoFromSnapYaml([]byte(`"`))
   103  	c.Assert(err, ErrorMatches, ".*: yaml: found unexpected end of stream")
   104  }
   105  
   106  func (s *YamlSuite) TestUnmarshalEmpty(c *C) {
   107  	info, err := snap.InfoFromSnapYaml([]byte(``))
   108  	c.Assert(err, IsNil)
   109  	c.Assert(info.Plugs, HasLen, 0)
   110  	c.Assert(info.Slots, HasLen, 0)
   111  	c.Assert(info.Apps, HasLen, 0)
   112  }
   113  
   114  // Tests focusing on plugs
   115  
   116  func (s *YamlSuite) TestUnmarshalStandaloneImplicitPlug(c *C) {
   117  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   118  	info, err := snap.InfoFromSnapYaml([]byte(`
   119  name: snap
   120  plugs:
   121      network-client:
   122  `))
   123  	c.Assert(err, IsNil)
   124  	c.Check(info.InstanceName(), Equals, "snap")
   125  	c.Check(info.Plugs, HasLen, 1)
   126  	c.Check(info.Slots, HasLen, 0)
   127  	c.Assert(info.Plugs["network-client"], DeepEquals, &snap.PlugInfo{
   128  		Snap:      info,
   129  		Name:      "network-client",
   130  		Interface: "network-client",
   131  	})
   132  }
   133  
   134  func (s *YamlSuite) TestUnmarshalStandaloneAbbreviatedPlug(c *C) {
   135  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   136  	info, err := snap.InfoFromSnapYaml([]byte(`
   137  name: snap
   138  plugs:
   139      net: network-client
   140  `))
   141  	c.Assert(err, IsNil)
   142  	c.Check(info.InstanceName(), Equals, "snap")
   143  	c.Check(info.Plugs, HasLen, 1)
   144  	c.Check(info.Slots, HasLen, 0)
   145  	c.Assert(info.Plugs["net"], DeepEquals, &snap.PlugInfo{
   146  		Snap:      info,
   147  		Name:      "net",
   148  		Interface: "network-client",
   149  	})
   150  }
   151  
   152  func (s *YamlSuite) TestUnmarshalStandaloneMinimalisticPlug(c *C) {
   153  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   154  	info, err := snap.InfoFromSnapYaml([]byte(`
   155  name: snap
   156  plugs:
   157      net:
   158          interface: network-client
   159  `))
   160  	c.Assert(err, IsNil)
   161  	c.Check(info.InstanceName(), Equals, "snap")
   162  	c.Check(info.Plugs, HasLen, 1)
   163  	c.Check(info.Slots, HasLen, 0)
   164  	c.Assert(info.Plugs["net"], DeepEquals, &snap.PlugInfo{
   165  		Snap:      info,
   166  		Name:      "net",
   167  		Interface: "network-client",
   168  	})
   169  }
   170  
   171  func (s *YamlSuite) TestUnmarshalStandaloneCompletePlug(c *C) {
   172  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   173  	info, err := snap.InfoFromSnapYaml([]byte(`
   174  name: snap
   175  plugs:
   176      net:
   177          interface: network-client
   178          ipv6-aware: true
   179  `))
   180  	c.Assert(err, IsNil)
   181  	c.Check(info.InstanceName(), Equals, "snap")
   182  	c.Check(info.Plugs, HasLen, 1)
   183  	c.Check(info.Slots, HasLen, 0)
   184  	c.Assert(info.Plugs["net"], DeepEquals, &snap.PlugInfo{
   185  		Snap:      info,
   186  		Name:      "net",
   187  		Interface: "network-client",
   188  		Attrs:     map[string]interface{}{"ipv6-aware": true},
   189  	})
   190  }
   191  
   192  func (s *YamlSuite) TestUnmarshalStandalonePlugWithIntAndListAndMap(c *C) {
   193  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   194  	info, err := snap.InfoFromSnapYaml([]byte(`
   195  name: snap
   196  plugs:
   197      iface:
   198          interface: complex
   199          i: 3
   200          l: [1,2,3]
   201          m:
   202            a: A
   203            b: B
   204  `))
   205  	c.Assert(err, IsNil)
   206  	c.Check(info.InstanceName(), Equals, "snap")
   207  	c.Check(info.Plugs, HasLen, 1)
   208  	c.Check(info.Slots, HasLen, 0)
   209  	c.Assert(info.Plugs["iface"], DeepEquals, &snap.PlugInfo{
   210  		Snap:      info,
   211  		Name:      "iface",
   212  		Interface: "complex",
   213  		Attrs: map[string]interface{}{
   214  			"i": int64(3),
   215  			"l": []interface{}{int64(1), int64(2), int64(3)},
   216  			"m": map[string]interface{}{"a": "A", "b": "B"},
   217  		},
   218  	})
   219  }
   220  
   221  func (s *YamlSuite) TestUnmarshalLastPlugDefinitionWins(c *C) {
   222  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   223  	info, err := snap.InfoFromSnapYaml([]byte(`
   224  name: snap
   225  plugs:
   226      net:
   227          interface: network-client
   228          attr: 1
   229      net:
   230          interface: network-client
   231          attr: 2
   232  `))
   233  	c.Assert(err, IsNil)
   234  	c.Check(info.InstanceName(), Equals, "snap")
   235  	c.Check(info.Plugs, HasLen, 1)
   236  	c.Check(info.Slots, HasLen, 0)
   237  	c.Assert(info.Plugs["net"], DeepEquals, &snap.PlugInfo{
   238  		Snap:      info,
   239  		Name:      "net",
   240  		Interface: "network-client",
   241  		Attrs:     map[string]interface{}{"attr": int64(2)},
   242  	})
   243  }
   244  
   245  func (s *YamlSuite) TestUnmarshalPlugsExplicitlyDefinedImplicitlyBoundToApps(c *C) {
   246  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   247  	info, err := snap.InfoFromSnapYaml([]byte(`
   248  name: snap
   249  plugs:
   250      network-client:
   251  apps:
   252      app:
   253  `))
   254  	c.Assert(err, IsNil)
   255  	c.Check(info.InstanceName(), Equals, "snap")
   256  	c.Check(info.Plugs, HasLen, 1)
   257  	c.Check(info.Slots, HasLen, 0)
   258  	c.Check(info.Apps, HasLen, 1)
   259  
   260  	plug := info.Plugs["network-client"]
   261  	app := info.Apps["app"]
   262  	c.Assert(plug, DeepEquals, &snap.PlugInfo{
   263  		Snap:      info,
   264  		Name:      "network-client",
   265  		Interface: "network-client",
   266  		Apps:      map[string]*snap.AppInfo{app.Name: app},
   267  	})
   268  	c.Assert(app, DeepEquals, &snap.AppInfo{
   269  		Snap:  info,
   270  		Name:  "app",
   271  		Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
   272  	})
   273  }
   274  
   275  func (s *YamlSuite) TestUnmarshalGlobalPlugBoundToOneApp(c *C) {
   276  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   277  	info, err := snap.InfoFromSnapYaml([]byte(`
   278  name: snap
   279  plugs:
   280      network-client:
   281  apps:
   282      with-plug:
   283          plugs: [network-client]
   284      without-plug:
   285  `))
   286  	c.Assert(err, IsNil)
   287  	c.Check(info.InstanceName(), Equals, "snap")
   288  	c.Check(info.Plugs, HasLen, 1)
   289  	c.Check(info.Slots, HasLen, 0)
   290  	c.Check(info.Apps, HasLen, 2)
   291  
   292  	plug := info.Plugs["network-client"]
   293  	withPlugApp := info.Apps["with-plug"]
   294  	withoutPlugApp := info.Apps["without-plug"]
   295  	c.Assert(plug, DeepEquals, &snap.PlugInfo{
   296  		Snap:      info,
   297  		Name:      "network-client",
   298  		Interface: "network-client",
   299  		Apps:      map[string]*snap.AppInfo{withPlugApp.Name: withPlugApp},
   300  	})
   301  	c.Assert(withPlugApp, DeepEquals, &snap.AppInfo{
   302  		Snap:  info,
   303  		Name:  "with-plug",
   304  		Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
   305  	})
   306  	c.Assert(withoutPlugApp, DeepEquals, &snap.AppInfo{
   307  		Snap:  info,
   308  		Name:  "without-plug",
   309  		Plugs: map[string]*snap.PlugInfo{},
   310  	})
   311  }
   312  
   313  func (s *YamlSuite) TestUnmarshalPlugsExplicitlyDefinedExplicitlyBoundToApps(c *C) {
   314  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   315  	info, err := snap.InfoFromSnapYaml([]byte(`
   316  name: snap
   317  plugs:
   318      net: network-client
   319  apps:
   320      app:
   321          plugs: ["net"]
   322  `))
   323  	c.Assert(err, IsNil)
   324  	c.Check(info.InstanceName(), Equals, "snap")
   325  	c.Check(info.Plugs, HasLen, 1)
   326  	c.Check(info.Slots, HasLen, 0)
   327  	c.Check(info.Apps, HasLen, 1)
   328  	plug := info.Plugs["net"]
   329  	app := info.Apps["app"]
   330  	c.Assert(plug, DeepEquals, &snap.PlugInfo{
   331  		Snap:      info,
   332  		Name:      "net",
   333  		Interface: "network-client",
   334  		Apps:      map[string]*snap.AppInfo{app.Name: app},
   335  	})
   336  	c.Assert(app, DeepEquals, &snap.AppInfo{
   337  		Snap:  info,
   338  		Name:  "app",
   339  		Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
   340  	})
   341  }
   342  
   343  func (s *YamlSuite) TestUnmarshalPlugsImplicitlyDefinedExplicitlyBoundToApps(c *C) {
   344  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   345  	info, err := snap.InfoFromSnapYaml([]byte(`
   346  name: snap
   347  apps:
   348      app:
   349          plugs: ["network-client"]
   350  `))
   351  	c.Assert(err, IsNil)
   352  	c.Check(info.InstanceName(), Equals, "snap")
   353  	c.Check(info.Plugs, HasLen, 1)
   354  	c.Check(info.Slots, HasLen, 0)
   355  	c.Check(info.Apps, HasLen, 1)
   356  	plug := info.Plugs["network-client"]
   357  	app := info.Apps["app"]
   358  	c.Assert(plug, DeepEquals, &snap.PlugInfo{
   359  		Snap:      info,
   360  		Name:      "network-client",
   361  		Interface: "network-client",
   362  		Apps:      map[string]*snap.AppInfo{app.Name: app},
   363  	})
   364  	c.Assert(app, DeepEquals, &snap.AppInfo{
   365  		Snap:  info,
   366  		Name:  "app",
   367  		Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
   368  	})
   369  }
   370  
   371  func (s *YamlSuite) TestUnmarshalPlugWithoutInterfaceName(c *C) {
   372  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   373  	info, err := snap.InfoFromSnapYaml([]byte(`
   374  name: snap
   375  plugs:
   376      network-client:
   377          ipv6-aware: true
   378  `))
   379  	c.Assert(err, IsNil)
   380  	c.Check(info.InstanceName(), Equals, "snap")
   381  	c.Check(info.Plugs, HasLen, 1)
   382  	c.Check(info.Slots, HasLen, 0)
   383  	c.Check(info.Apps, HasLen, 0)
   384  	c.Assert(info.Plugs["network-client"], DeepEquals, &snap.PlugInfo{
   385  		Snap:      info,
   386  		Name:      "network-client",
   387  		Interface: "network-client",
   388  		Attrs:     map[string]interface{}{"ipv6-aware": true},
   389  	})
   390  }
   391  
   392  func (s *YamlSuite) TestUnmarshalPlugWithLabel(c *C) {
   393  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   394  	info, err := snap.InfoFromSnapYaml([]byte(`
   395  name: snap
   396  plugs:
   397      bool-file:
   398          label: Disk I/O indicator
   399  `))
   400  	c.Assert(err, IsNil)
   401  	c.Check(info.InstanceName(), Equals, "snap")
   402  	c.Check(info.Plugs, HasLen, 1)
   403  	c.Check(info.Slots, HasLen, 0)
   404  	c.Check(info.Apps, HasLen, 0)
   405  	c.Assert(info.Plugs["bool-file"], DeepEquals, &snap.PlugInfo{
   406  		Snap:      info,
   407  		Name:      "bool-file",
   408  		Interface: "bool-file",
   409  		Label:     "Disk I/O indicator",
   410  	})
   411  }
   412  
   413  func (s *YamlSuite) TestUnmarshalCorruptedPlugWithNonStringInterfaceName(c *C) {
   414  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   415  	_, err := snap.InfoFromSnapYaml([]byte(`
   416  name: snap
   417  plugs:
   418      net:
   419          interface: 1.0
   420          ipv6-aware: true
   421  `))
   422  	c.Assert(err, ErrorMatches, `interface name on plug "net" is not a string \(found float64\)`)
   423  }
   424  
   425  func (s *YamlSuite) TestUnmarshalCorruptedPlugWithNonStringLabel(c *C) {
   426  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   427  	_, err := snap.InfoFromSnapYaml([]byte(`
   428  name: snap
   429  plugs:
   430      bool-file:
   431          label: 1.0
   432  `))
   433  	c.Assert(err, ErrorMatches, `label of plug "bool-file" is not a string \(found float64\)`)
   434  }
   435  
   436  func (s *YamlSuite) TestUnmarshalCorruptedPlugWithNonStringAttributes(c *C) {
   437  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   438  	_, err := snap.InfoFromSnapYaml([]byte(`
   439  name: snap
   440  plugs:
   441      net:
   442          1: ok
   443  `))
   444  	c.Assert(err, ErrorMatches, `plug "net" has attribute key that is not a string \(found int\)`)
   445  }
   446  
   447  func (s *YamlSuite) TestUnmarshalCorruptedPlugWithEmptyAttributeKey(c *C) {
   448  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   449  	_, err := snap.InfoFromSnapYaml([]byte(`
   450  name: snap
   451  plugs:
   452      net:
   453          "": ok
   454  `))
   455  	c.Assert(err, ErrorMatches, `plug "net" has an empty attribute key`)
   456  }
   457  
   458  func (s *YamlSuite) TestUnmarshalCorruptedPlugWithUnexpectedType(c *C) {
   459  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   460  	_, err := snap.InfoFromSnapYaml([]byte(`
   461  name: snap
   462  plugs:
   463      net: 5
   464  `))
   465  	c.Assert(err, ErrorMatches, `plug "net" has malformed definition \(found int\)`)
   466  }
   467  
   468  func (s *YamlSuite) TestUnmarshalReservedPlugAttribute(c *C) {
   469  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   470  	_, err := snap.InfoFromSnapYaml([]byte(`
   471  name: snap
   472  plugs:
   473      serial:
   474          interface: serial-port
   475          $baud-rate: [9600]
   476  `))
   477  	c.Assert(err, ErrorMatches, `plug "serial" uses reserved attribute "\$baud-rate"`)
   478  }
   479  
   480  func (s *YamlSuite) TestUnmarshalInvalidPlugAttribute(c *C) {
   481  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   482  	_, err := snap.InfoFromSnapYaml([]byte(`
   483  name: snap
   484  plugs:
   485      serial:
   486          interface: serial-port
   487          foo: null
   488  `))
   489  	c.Assert(err, ErrorMatches, `attribute "foo" of plug \"serial\": invalid scalar:.*`)
   490  }
   491  
   492  func (s *YamlSuite) TestUnmarshalInvalidAttributeMapKey(c *C) {
   493  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   494  	_, err := snap.InfoFromSnapYaml([]byte(`
   495  name: snap
   496  plugs:
   497      serial:
   498          interface: serial-port
   499          bar:
   500            baz:
   501            - 1: A
   502  `))
   503  	c.Assert(err, ErrorMatches, `attribute "bar" of plug \"serial\": non-string key: 1`)
   504  }
   505  
   506  // Tests focusing on slots
   507  
   508  func (s *YamlSuite) TestUnmarshalStandaloneImplicitSlot(c *C) {
   509  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   510  	info, err := snap.InfoFromSnapYaml([]byte(`
   511  name: snap
   512  slots:
   513      network-client:
   514  `))
   515  	c.Assert(err, IsNil)
   516  	c.Check(info.InstanceName(), Equals, "snap")
   517  	c.Check(info.Plugs, HasLen, 0)
   518  	c.Check(info.Slots, HasLen, 1)
   519  	c.Assert(info.Slots["network-client"], DeepEquals, &snap.SlotInfo{
   520  		Snap:      info,
   521  		Name:      "network-client",
   522  		Interface: "network-client",
   523  	})
   524  }
   525  
   526  func (s *YamlSuite) TestUnmarshalStandaloneAbbreviatedSlot(c *C) {
   527  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   528  	info, err := snap.InfoFromSnapYaml([]byte(`
   529  name: snap
   530  slots:
   531      net: network-client
   532  `))
   533  	c.Assert(err, IsNil)
   534  	c.Check(info.InstanceName(), Equals, "snap")
   535  	c.Check(info.Plugs, HasLen, 0)
   536  	c.Check(info.Slots, HasLen, 1)
   537  	c.Assert(info.Slots["net"], DeepEquals, &snap.SlotInfo{
   538  		Snap:      info,
   539  		Name:      "net",
   540  		Interface: "network-client",
   541  	})
   542  }
   543  
   544  func (s *YamlSuite) TestUnmarshalStandaloneMinimalisticSlot(c *C) {
   545  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   546  	info, err := snap.InfoFromSnapYaml([]byte(`
   547  name: snap
   548  slots:
   549      net:
   550          interface: network-client
   551  `))
   552  	c.Assert(err, IsNil)
   553  	c.Check(info.InstanceName(), Equals, "snap")
   554  	c.Check(info.Plugs, HasLen, 0)
   555  	c.Check(info.Slots, HasLen, 1)
   556  	c.Assert(info.Slots["net"], DeepEquals, &snap.SlotInfo{
   557  		Snap:      info,
   558  		Name:      "net",
   559  		Interface: "network-client",
   560  	})
   561  }
   562  
   563  func (s *YamlSuite) TestUnmarshalStandaloneCompleteSlot(c *C) {
   564  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   565  	info, err := snap.InfoFromSnapYaml([]byte(`
   566  name: snap
   567  slots:
   568      net:
   569          interface: network-client
   570          ipv6-aware: true
   571  `))
   572  	c.Assert(err, IsNil)
   573  	c.Check(info.InstanceName(), Equals, "snap")
   574  	c.Check(info.Plugs, HasLen, 0)
   575  	c.Check(info.Slots, HasLen, 1)
   576  	c.Assert(info.Slots["net"], DeepEquals, &snap.SlotInfo{
   577  		Snap:      info,
   578  		Name:      "net",
   579  		Interface: "network-client",
   580  		Attrs:     map[string]interface{}{"ipv6-aware": true},
   581  	})
   582  }
   583  
   584  func (s *YamlSuite) TestUnmarshalStandaloneSlotWithIntAndListAndMap(c *C) {
   585  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   586  	info, err := snap.InfoFromSnapYaml([]byte(`
   587  name: snap
   588  slots:
   589      iface:
   590          interface: complex
   591          i: 3
   592          l: [1,2]
   593          m:
   594            a: "A"
   595  `))
   596  	c.Assert(err, IsNil)
   597  	c.Check(info.InstanceName(), Equals, "snap")
   598  	c.Check(info.Plugs, HasLen, 0)
   599  	c.Check(info.Slots, HasLen, 1)
   600  	c.Assert(info.Slots["iface"], DeepEquals, &snap.SlotInfo{
   601  		Snap:      info,
   602  		Name:      "iface",
   603  		Interface: "complex",
   604  		Attrs: map[string]interface{}{
   605  			"i": int64(3),
   606  			"l": []interface{}{int64(1), int64(2)},
   607  			"m": map[string]interface{}{"a": "A"},
   608  		},
   609  	})
   610  }
   611  
   612  func (s *YamlSuite) TestUnmarshalLastSlotDefinitionWins(c *C) {
   613  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   614  	info, err := snap.InfoFromSnapYaml([]byte(`
   615  name: snap
   616  slots:
   617      net:
   618          interface: network-client
   619          attr: 1
   620      net:
   621          interface: network-client
   622          attr: 2
   623  `))
   624  	c.Assert(err, IsNil)
   625  	c.Check(info.InstanceName(), Equals, "snap")
   626  	c.Check(info.Plugs, HasLen, 0)
   627  	c.Check(info.Slots, HasLen, 1)
   628  	c.Assert(info.Slots["net"], DeepEquals, &snap.SlotInfo{
   629  		Snap:      info,
   630  		Name:      "net",
   631  		Interface: "network-client",
   632  		Attrs:     map[string]interface{}{"attr": int64(2)},
   633  	})
   634  }
   635  
   636  func (s *YamlSuite) TestUnmarshalSlotsExplicitlyDefinedImplicitlyBoundToApps(c *C) {
   637  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   638  	info, err := snap.InfoFromSnapYaml([]byte(`
   639  name: snap
   640  slots:
   641      network-client:
   642  apps:
   643      app:
   644  `))
   645  	c.Assert(err, IsNil)
   646  	c.Check(info.InstanceName(), Equals, "snap")
   647  	c.Check(info.Plugs, HasLen, 0)
   648  	c.Check(info.Slots, HasLen, 1)
   649  	c.Check(info.Apps, HasLen, 1)
   650  	slot := info.Slots["network-client"]
   651  	app := info.Apps["app"]
   652  	c.Assert(slot, DeepEquals, &snap.SlotInfo{
   653  		Snap:      info,
   654  		Name:      "network-client",
   655  		Interface: "network-client",
   656  		Apps:      map[string]*snap.AppInfo{app.Name: app},
   657  	})
   658  	c.Assert(app, DeepEquals, &snap.AppInfo{
   659  		Snap:  info,
   660  		Name:  "app",
   661  		Slots: map[string]*snap.SlotInfo{slot.Name: slot},
   662  	})
   663  }
   664  
   665  func (s *YamlSuite) TestUnmarshalSlotsExplicitlyDefinedExplicitlyBoundToApps(c *C) {
   666  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   667  	info, err := snap.InfoFromSnapYaml([]byte(`
   668  name: snap
   669  slots:
   670      net: network-client
   671  apps:
   672      app:
   673          slots: ["net"]
   674  `))
   675  	c.Assert(err, IsNil)
   676  	c.Check(info.InstanceName(), Equals, "snap")
   677  	c.Check(info.Plugs, HasLen, 0)
   678  	c.Check(info.Slots, HasLen, 1)
   679  	c.Check(info.Apps, HasLen, 1)
   680  	slot := info.Slots["net"]
   681  	app := info.Apps["app"]
   682  	c.Assert(slot, DeepEquals, &snap.SlotInfo{
   683  		Snap:      info,
   684  		Name:      "net",
   685  		Interface: "network-client",
   686  		Apps:      map[string]*snap.AppInfo{app.Name: app},
   687  	})
   688  	c.Assert(app, DeepEquals, &snap.AppInfo{
   689  		Snap:  info,
   690  		Name:  "app",
   691  		Slots: map[string]*snap.SlotInfo{slot.Name: slot},
   692  	})
   693  }
   694  
   695  func (s *YamlSuite) TestUnmarshalSlotsImplicitlyDefinedExplicitlyBoundToApps(c *C) {
   696  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   697  	info, err := snap.InfoFromSnapYaml([]byte(`
   698  name: snap
   699  apps:
   700      app:
   701          slots: ["network-client"]
   702  `))
   703  	c.Assert(err, IsNil)
   704  	c.Check(info.InstanceName(), Equals, "snap")
   705  	c.Check(info.Plugs, HasLen, 0)
   706  	c.Check(info.Slots, HasLen, 1)
   707  	c.Check(info.Apps, HasLen, 1)
   708  	slot := info.Slots["network-client"]
   709  	app := info.Apps["app"]
   710  	c.Assert(slot, DeepEquals, &snap.SlotInfo{
   711  		Snap:      info,
   712  		Name:      "network-client",
   713  		Interface: "network-client",
   714  		Apps:      map[string]*snap.AppInfo{app.Name: app},
   715  	})
   716  	c.Assert(app, DeepEquals, &snap.AppInfo{
   717  		Snap:  info,
   718  		Name:  "app",
   719  		Slots: map[string]*snap.SlotInfo{slot.Name: slot},
   720  	})
   721  }
   722  
   723  func (s *YamlSuite) TestUnmarshalSlotWithoutInterfaceName(c *C) {
   724  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   725  	info, err := snap.InfoFromSnapYaml([]byte(`
   726  name: snap
   727  slots:
   728      network-client:
   729          ipv6-aware: true
   730  `))
   731  	c.Assert(err, IsNil)
   732  	c.Check(info.InstanceName(), Equals, "snap")
   733  	c.Check(info.Plugs, HasLen, 0)
   734  	c.Check(info.Slots, HasLen, 1)
   735  	c.Check(info.Apps, HasLen, 0)
   736  	c.Assert(info.Slots["network-client"], DeepEquals, &snap.SlotInfo{
   737  		Snap:      info,
   738  		Name:      "network-client",
   739  		Interface: "network-client",
   740  		Attrs:     map[string]interface{}{"ipv6-aware": true},
   741  	})
   742  }
   743  
   744  func (s *YamlSuite) TestUnmarshalSlotWithLabel(c *C) {
   745  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   746  	info, err := snap.InfoFromSnapYaml([]byte(`
   747  name: snap
   748  slots:
   749      led0:
   750          interface: bool-file
   751          label: Front panel LED (red)
   752  `))
   753  	c.Assert(err, IsNil)
   754  	c.Check(info.InstanceName(), Equals, "snap")
   755  	c.Check(info.Plugs, HasLen, 0)
   756  	c.Check(info.Slots, HasLen, 1)
   757  	c.Check(info.Apps, HasLen, 0)
   758  	c.Assert(info.Slots["led0"], DeepEquals, &snap.SlotInfo{
   759  		Snap:      info,
   760  		Name:      "led0",
   761  		Interface: "bool-file",
   762  		Label:     "Front panel LED (red)",
   763  	})
   764  }
   765  
   766  func (s *YamlSuite) TestUnmarshalGlobalSlotsBindToHooks(c *C) {
   767  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   768  	info, err := snap.InfoFromSnapYaml([]byte(`
   769  name: snap
   770  slots:
   771      test-slot:
   772  hooks:
   773      test-hook:
   774  `))
   775  	c.Assert(err, IsNil)
   776  	c.Check(info.InstanceName(), Equals, "snap")
   777  	c.Check(info.Plugs, HasLen, 0)
   778  	c.Check(info.Slots, HasLen, 1)
   779  	c.Check(info.Apps, HasLen, 0)
   780  	c.Check(info.Hooks, HasLen, 1)
   781  
   782  	slot, ok := info.Slots["test-slot"]
   783  	c.Assert(ok, Equals, true, Commentf("Expected slots to include 'test-slot'"))
   784  	hook, ok := info.Hooks["test-hook"]
   785  	c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
   786  
   787  	c.Check(slot, DeepEquals, &snap.SlotInfo{
   788  		Snap:      info,
   789  		Name:      "test-slot",
   790  		Interface: "test-slot",
   791  		Hooks:     map[string]*snap.HookInfo{hook.Name: hook},
   792  	})
   793  	c.Check(hook, DeepEquals, &snap.HookInfo{
   794  		Snap:  info,
   795  		Name:  "test-hook",
   796  		Slots: map[string]*snap.SlotInfo{slot.Name: slot},
   797  
   798  		Explicit: true,
   799  	})
   800  }
   801  
   802  func (s *YamlSuite) TestUnmarshalHookWithSlot(c *C) {
   803  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   804  	info, err := snap.InfoFromSnapYaml([]byte(`
   805  name: snap
   806  hooks:
   807      test-hook:
   808          slots: [test-slot]
   809  `))
   810  	c.Assert(err, IsNil)
   811  	c.Check(info.InstanceName(), Equals, "snap")
   812  	c.Check(info.Plugs, HasLen, 0)
   813  	c.Check(info.Slots, HasLen, 1)
   814  	c.Check(info.Apps, HasLen, 0)
   815  	c.Check(info.Hooks, HasLen, 1)
   816  
   817  	slot, ok := info.Slots["test-slot"]
   818  	c.Assert(ok, Equals, true, Commentf("Expected slots to include 'test-slot'"))
   819  	hook, ok := info.Hooks["test-hook"]
   820  	c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
   821  
   822  	c.Check(slot, DeepEquals, &snap.SlotInfo{
   823  		Snap:      info,
   824  		Name:      "test-slot",
   825  		Interface: "test-slot",
   826  		Hooks:     map[string]*snap.HookInfo{hook.Name: hook},
   827  	})
   828  	c.Check(hook, DeepEquals, &snap.HookInfo{
   829  		Snap:  info,
   830  		Name:  "test-hook",
   831  		Slots: map[string]*snap.SlotInfo{slot.Name: slot},
   832  
   833  		Explicit: true,
   834  	})
   835  }
   836  
   837  func (s *YamlSuite) TestUnmarshalCorruptedSlotWithNonStringInterfaceName(c *C) {
   838  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   839  	_, err := snap.InfoFromSnapYaml([]byte(`
   840  name: snap
   841  slots:
   842      net:
   843          interface: 1.0
   844          ipv6-aware: true
   845  `))
   846  	c.Assert(err, ErrorMatches, `interface name on slot "net" is not a string \(found float64\)`)
   847  }
   848  
   849  func (s *YamlSuite) TestUnmarshalCorruptedSlotWithNonStringLabel(c *C) {
   850  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   851  	_, err := snap.InfoFromSnapYaml([]byte(`
   852  name: snap
   853  slots:
   854      bool-file:
   855          label: 1.0
   856  `))
   857  	c.Assert(err, ErrorMatches, `label of slot "bool-file" is not a string \(found float64\)`)
   858  }
   859  
   860  func (s *YamlSuite) TestUnmarshalCorruptedSlotWithNonStringAttributes(c *C) {
   861  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   862  	_, err := snap.InfoFromSnapYaml([]byte(`
   863  name: snap
   864  slots:
   865      net:
   866          1: ok
   867  `))
   868  	c.Assert(err, ErrorMatches, `slot "net" has attribute key that is not a string \(found int\)`)
   869  }
   870  
   871  func (s *YamlSuite) TestUnmarshalCorruptedSlotWithEmptyAttributeKey(c *C) {
   872  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   873  	_, err := snap.InfoFromSnapYaml([]byte(`
   874  name: snap
   875  slots:
   876      net:
   877          "": ok
   878  `))
   879  	c.Assert(err, ErrorMatches, `slot "net" has an empty attribute key`)
   880  }
   881  
   882  func (s *YamlSuite) TestUnmarshalCorruptedSlotWithUnexpectedType(c *C) {
   883  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   884  	_, err := snap.InfoFromSnapYaml([]byte(`
   885  name: snap
   886  slots:
   887      net: 5
   888  `))
   889  	c.Assert(err, ErrorMatches, `slot "net" has malformed definition \(found int\)`)
   890  }
   891  
   892  func (s *YamlSuite) TestUnmarshalReservedSlotAttribute(c *C) {
   893  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   894  	_, err := snap.InfoFromSnapYaml([]byte(`
   895  name: snap
   896  slots:
   897      serial:
   898          interface: serial-port
   899          $baud-rate: [9600]
   900  `))
   901  	c.Assert(err, ErrorMatches, `slot "serial" uses reserved attribute "\$baud-rate"`)
   902  }
   903  
   904  func (s *YamlSuite) TestUnmarshalInvalidSlotAttribute(c *C) {
   905  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   906  	_, err := snap.InfoFromSnapYaml([]byte(`
   907  name: snap
   908  slots:
   909      serial:
   910          interface: serial-port
   911          foo: null
   912  `))
   913  	c.Assert(err, ErrorMatches, `attribute "foo" of slot \"serial\": invalid scalar:.*`)
   914  }
   915  
   916  func (s *YamlSuite) TestUnmarshalHook(c *C) {
   917  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   918  	info, err := snap.InfoFromSnapYaml([]byte(`
   919  name: snap
   920  hooks:
   921      test-hook:
   922  `))
   923  	c.Assert(err, IsNil)
   924  	c.Check(info.InstanceName(), Equals, "snap")
   925  	c.Check(info.Plugs, HasLen, 0)
   926  	c.Check(info.Slots, HasLen, 0)
   927  	c.Check(info.Apps, HasLen, 0)
   928  	c.Check(info.Hooks, HasLen, 1)
   929  
   930  	hook, ok := info.Hooks["test-hook"]
   931  	c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
   932  
   933  	c.Check(hook, DeepEquals, &snap.HookInfo{
   934  		Snap:  info,
   935  		Name:  "test-hook",
   936  		Plugs: nil,
   937  
   938  		Explicit: true,
   939  	})
   940  }
   941  
   942  func (s *YamlSuite) TestUnmarshalUnsupportedHook(c *C) {
   943  	s.restore()
   944  	hookType := snap.NewHookType(regexp.MustCompile("not-test-hook"))
   945  	s.restore = snap.MockSupportedHookTypes([]*snap.HookType{hookType})
   946  
   947  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   948  	info, err := snap.InfoFromSnapYaml([]byte(`
   949  name: snap
   950  hooks:
   951      test-hook:
   952  `))
   953  	c.Assert(err, IsNil)
   954  	c.Check(info.InstanceName(), Equals, "snap")
   955  	c.Check(info.Plugs, HasLen, 0)
   956  	c.Check(info.Slots, HasLen, 0)
   957  	c.Check(info.Apps, HasLen, 0)
   958  	c.Check(info.Hooks, HasLen, 0, Commentf("Expected no hooks to be loaded"))
   959  }
   960  
   961  func (s *YamlSuite) TestUnmarshalHookFiltersOutUnsupportedHooks(c *C) {
   962  	s.restore()
   963  	hookType := snap.NewHookType(regexp.MustCompile("test-.*"))
   964  	s.restore = snap.MockSupportedHookTypes([]*snap.HookType{hookType})
   965  
   966  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   967  	info, err := snap.InfoFromSnapYaml([]byte(`
   968  name: snap
   969  hooks:
   970      test-hook:
   971      foo-hook:
   972  `))
   973  	c.Assert(err, IsNil)
   974  	c.Check(info.InstanceName(), Equals, "snap")
   975  	c.Check(info.Plugs, HasLen, 0)
   976  	c.Check(info.Slots, HasLen, 0)
   977  	c.Check(info.Apps, HasLen, 0)
   978  	c.Check(info.Hooks, HasLen, 1)
   979  
   980  	hook, ok := info.Hooks["test-hook"]
   981  	c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
   982  
   983  	c.Check(hook, DeepEquals, &snap.HookInfo{
   984  		Snap:  info,
   985  		Name:  "test-hook",
   986  		Plugs: nil,
   987  
   988  		Explicit: true,
   989  	})
   990  }
   991  
   992  func (s *YamlSuite) TestUnmarshalHookWithPlug(c *C) {
   993  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
   994  	info, err := snap.InfoFromSnapYaml([]byte(`
   995  name: snap
   996  hooks:
   997      test-hook:
   998          plugs: [test-plug]
   999  `))
  1000  	c.Assert(err, IsNil)
  1001  	c.Check(info.InstanceName(), Equals, "snap")
  1002  	c.Check(info.Plugs, HasLen, 1)
  1003  	c.Check(info.Slots, HasLen, 0)
  1004  	c.Check(info.Apps, HasLen, 0)
  1005  	c.Check(info.Hooks, HasLen, 1)
  1006  
  1007  	plug, ok := info.Plugs["test-plug"]
  1008  	c.Assert(ok, Equals, true, Commentf("Expected plugs to include 'test-plug'"))
  1009  	hook, ok := info.Hooks["test-hook"]
  1010  	c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
  1011  
  1012  	c.Check(plug, DeepEquals, &snap.PlugInfo{
  1013  		Snap:      info,
  1014  		Name:      "test-plug",
  1015  		Interface: "test-plug",
  1016  		Hooks:     map[string]*snap.HookInfo{hook.Name: hook},
  1017  	})
  1018  	c.Check(hook, DeepEquals, &snap.HookInfo{
  1019  		Snap:  info,
  1020  		Name:  "test-hook",
  1021  		Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
  1022  
  1023  		Explicit: true,
  1024  	})
  1025  }
  1026  
  1027  func (s *YamlSuite) TestUnmarshalGlobalPlugsBindToHooks(c *C) {
  1028  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
  1029  	info, err := snap.InfoFromSnapYaml([]byte(`
  1030  name: snap
  1031  plugs:
  1032      test-plug:
  1033  hooks:
  1034      test-hook:
  1035  `))
  1036  	c.Assert(err, IsNil)
  1037  	c.Check(info.InstanceName(), Equals, "snap")
  1038  	c.Check(info.Plugs, HasLen, 1)
  1039  	c.Check(info.Slots, HasLen, 0)
  1040  	c.Check(info.Apps, HasLen, 0)
  1041  	c.Check(info.Hooks, HasLen, 1)
  1042  
  1043  	plug, ok := info.Plugs["test-plug"]
  1044  	c.Assert(ok, Equals, true, Commentf("Expected plugs to include 'test-plug'"))
  1045  	hook, ok := info.Hooks["test-hook"]
  1046  	c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
  1047  
  1048  	c.Check(plug, DeepEquals, &snap.PlugInfo{
  1049  		Snap:      info,
  1050  		Name:      "test-plug",
  1051  		Interface: "test-plug",
  1052  		Hooks:     map[string]*snap.HookInfo{hook.Name: hook},
  1053  	})
  1054  	c.Check(hook, DeepEquals, &snap.HookInfo{
  1055  		Snap:  info,
  1056  		Name:  "test-hook",
  1057  		Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
  1058  
  1059  		Explicit: true,
  1060  	})
  1061  }
  1062  
  1063  func (s *YamlSuite) TestUnmarshalGlobalPlugBoundToOneHook(c *C) {
  1064  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
  1065  	info, err := snap.InfoFromSnapYaml([]byte(`
  1066  name: snap
  1067  plugs:
  1068      test-plug:
  1069  hooks:
  1070      with-plug:
  1071          plugs: [test-plug]
  1072      without-plug:
  1073  `))
  1074  	c.Assert(err, IsNil)
  1075  	c.Check(info.InstanceName(), Equals, "snap")
  1076  	c.Check(info.Plugs, HasLen, 1)
  1077  	c.Check(info.Slots, HasLen, 0)
  1078  	c.Check(info.Apps, HasLen, 0)
  1079  	c.Check(info.Hooks, HasLen, 2)
  1080  
  1081  	plug := info.Plugs["test-plug"]
  1082  	withPlugHook := info.Hooks["with-plug"]
  1083  	withoutPlugHook := info.Hooks["without-plug"]
  1084  	c.Assert(plug, DeepEquals, &snap.PlugInfo{
  1085  		Snap:      info,
  1086  		Name:      "test-plug",
  1087  		Interface: "test-plug",
  1088  		Hooks:     map[string]*snap.HookInfo{withPlugHook.Name: withPlugHook},
  1089  	})
  1090  	c.Assert(withPlugHook, DeepEquals, &snap.HookInfo{
  1091  		Snap:  info,
  1092  		Name:  "with-plug",
  1093  		Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
  1094  
  1095  		Explicit: true,
  1096  	})
  1097  	c.Assert(withoutPlugHook, DeepEquals, &snap.HookInfo{
  1098  		Snap:  info,
  1099  		Name:  "without-plug",
  1100  		Plugs: map[string]*snap.PlugInfo{},
  1101  
  1102  		Explicit: true,
  1103  	})
  1104  }
  1105  
  1106  func (s *YamlSuite) TestUnmarshalExplicitGlobalPlugBoundToHook(c *C) {
  1107  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
  1108  	info, err := snap.InfoFromSnapYaml([]byte(`
  1109  name: snap
  1110  plugs:
  1111      test-plug: test-interface
  1112  hooks:
  1113      test-hook:
  1114          plugs: ["test-plug"]
  1115  `))
  1116  	c.Assert(err, IsNil)
  1117  	c.Check(info.InstanceName(), Equals, "snap")
  1118  	c.Check(info.Plugs, HasLen, 1)
  1119  	c.Check(info.Slots, HasLen, 0)
  1120  	c.Check(info.Apps, HasLen, 0)
  1121  	c.Check(info.Hooks, HasLen, 1)
  1122  
  1123  	plug, ok := info.Plugs["test-plug"]
  1124  	c.Assert(ok, Equals, true, Commentf("Expected plugs to include 'test-plug'"))
  1125  	hook, ok := info.Hooks["test-hook"]
  1126  	c.Assert(ok, Equals, true, Commentf("Expected hooks to include 'test-hook'"))
  1127  
  1128  	c.Check(plug, DeepEquals, &snap.PlugInfo{
  1129  		Snap:      info,
  1130  		Name:      "test-plug",
  1131  		Interface: "test-interface",
  1132  		Hooks:     map[string]*snap.HookInfo{hook.Name: hook},
  1133  	})
  1134  	c.Check(hook, DeepEquals, &snap.HookInfo{
  1135  		Snap:  info,
  1136  		Name:  "test-hook",
  1137  		Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
  1138  
  1139  		Explicit: true,
  1140  	})
  1141  }
  1142  
  1143  func (s *YamlSuite) TestUnmarshalGlobalPlugBoundToHookNotApp(c *C) {
  1144  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
  1145  	info, err := snap.InfoFromSnapYaml([]byte(`
  1146  name: snap
  1147  plugs:
  1148      test-plug:
  1149  hooks:
  1150      test-hook:
  1151          plugs: [test-plug]
  1152  apps:
  1153      test-app:
  1154  `))
  1155  	c.Assert(err, IsNil)
  1156  	c.Check(info.InstanceName(), Equals, "snap")
  1157  	c.Check(info.Plugs, HasLen, 1)
  1158  	c.Check(info.Slots, HasLen, 0)
  1159  	c.Check(info.Apps, HasLen, 1)
  1160  	c.Check(info.Hooks, HasLen, 1)
  1161  
  1162  	plug := info.Plugs["test-plug"]
  1163  	hook := info.Hooks["test-hook"]
  1164  	app := info.Apps["test-app"]
  1165  	c.Assert(plug, DeepEquals, &snap.PlugInfo{
  1166  		Snap:      info,
  1167  		Name:      "test-plug",
  1168  		Interface: "test-plug",
  1169  		Apps:      map[string]*snap.AppInfo{},
  1170  		Hooks:     map[string]*snap.HookInfo{hook.Name: hook},
  1171  	})
  1172  	c.Assert(hook, DeepEquals, &snap.HookInfo{
  1173  		Snap:  info,
  1174  		Name:  "test-hook",
  1175  		Plugs: map[string]*snap.PlugInfo{plug.Name: plug},
  1176  
  1177  		Explicit: true,
  1178  	})
  1179  	c.Assert(app, DeepEquals, &snap.AppInfo{
  1180  		Snap:  info,
  1181  		Name:  "test-app",
  1182  		Plugs: map[string]*snap.PlugInfo{},
  1183  	})
  1184  }
  1185  
  1186  func (s *YamlSuite) TestUnmarshalComplexExample(c *C) {
  1187  	// NOTE: yaml content cannot use tabs, indent the section with spaces.
  1188  	info, err := snap.InfoFromSnapYaml([]byte(`
  1189  name: foo
  1190  version: 1.2
  1191  title: Foo
  1192  summary: foo app
  1193  type: app
  1194  epoch: 1*
  1195  confinement: devmode
  1196  license: GPL-3.0
  1197  description: |
  1198      Foo provides useful services
  1199  apps:
  1200      daemon:
  1201         command: foo --daemon
  1202         plugs: [network, network-bind]
  1203         slots: [foo-socket-slot]
  1204      foo:
  1205         command: fooctl
  1206         plugs: [foo-socket-plug]
  1207  hooks:
  1208      test-hook:
  1209         plugs: [foo-socket-plug]
  1210         slots: [foo-socket-slot]
  1211  plugs:
  1212      foo-socket-plug:
  1213          interface: socket
  1214          # $protocol: foo
  1215      logging:
  1216          interface: syslog
  1217  slots:
  1218      foo-socket-slot:
  1219          interface: socket
  1220          path: $SNAP_DATA/socket
  1221          protocol: foo
  1222      tracing:
  1223          interface: ptrace
  1224  `))
  1225  	c.Assert(err, IsNil)
  1226  	c.Check(info.InstanceName(), Equals, "foo")
  1227  	c.Check(info.Version, Equals, "1.2")
  1228  	c.Check(info.GetType(), Equals, snap.TypeApp)
  1229  	c.Check(info.Epoch, DeepEquals, snap.E("1*"))
  1230  	c.Check(info.Confinement, Equals, snap.DevModeConfinement)
  1231  	c.Check(info.Title(), Equals, "Foo")
  1232  	c.Check(info.Summary(), Equals, "foo app")
  1233  	c.Check(info.Description(), Equals, "Foo provides useful services\n")
  1234  	c.Check(info.Apps, HasLen, 2)
  1235  	c.Check(info.Plugs, HasLen, 4)
  1236  	c.Check(info.Slots, HasLen, 2)
  1237  	// these don't come from snap.yaml
  1238  	c.Check(info.Publisher, Equals, snap.StoreAccount{})
  1239  	c.Check(info.Channel, Equals, "")
  1240  	c.Check(info.License, Equals, "GPL-3.0")
  1241  
  1242  	app1 := info.Apps["daemon"]
  1243  	app2 := info.Apps["foo"]
  1244  	hook := info.Hooks["test-hook"]
  1245  	plug1 := info.Plugs["network"]
  1246  	plug2 := info.Plugs["network-bind"]
  1247  	plug3 := info.Plugs["foo-socket-plug"]
  1248  	plug4 := info.Plugs["logging"]
  1249  	slot1 := info.Slots["foo-socket-slot"]
  1250  	slot2 := info.Slots["tracing"]
  1251  
  1252  	// app1 ("daemon") has three plugs ("network", "network-bind", "logging")
  1253  	// and two slots ("foo-socket", "tracing"). The slot "tracing" and plug
  1254  	// "logging" are global, everything else is app-bound.
  1255  
  1256  	c.Assert(app1, Not(IsNil))
  1257  	c.Check(app1.Snap, Equals, info)
  1258  	c.Check(app1.Name, Equals, "daemon")
  1259  	c.Check(app1.Command, Equals, "foo --daemon")
  1260  	c.Check(app1.Plugs, DeepEquals, map[string]*snap.PlugInfo{
  1261  		plug1.Name: plug1, plug2.Name: plug2, plug4.Name: plug4})
  1262  	c.Check(app1.Slots, DeepEquals, map[string]*snap.SlotInfo{
  1263  		slot1.Name: slot1, slot2.Name: slot2})
  1264  
  1265  	// app2 ("foo") has two plugs ("foo-socket", "logging") and one slot
  1266  	// ("tracing"). The slot "tracing" and plug "logging" are  global while
  1267  	// "foo-socket" is app-bound.
  1268  
  1269  	c.Assert(app2, Not(IsNil))
  1270  	c.Check(app2.Snap, Equals, info)
  1271  	c.Check(app2.Name, Equals, "foo")
  1272  	c.Check(app2.Command, Equals, "fooctl")
  1273  	c.Check(app2.Plugs, DeepEquals, map[string]*snap.PlugInfo{
  1274  		plug3.Name: plug3, plug4.Name: plug4})
  1275  	c.Check(app2.Slots, DeepEquals, map[string]*snap.SlotInfo{
  1276  		slot2.Name: slot2})
  1277  
  1278  	// hook1 has two plugs ("foo-socket", "logging") and two slots ("foo-socket", "tracing").
  1279  	// The plug "logging" and slot "tracing" are global while "foo-socket" is hook-bound.
  1280  
  1281  	c.Assert(hook, NotNil)
  1282  	c.Check(hook.Snap, Equals, info)
  1283  	c.Check(hook.Name, Equals, "test-hook")
  1284  	c.Check(hook.Plugs, DeepEquals, map[string]*snap.PlugInfo{
  1285  		plug3.Name: plug3, plug4.Name: plug4})
  1286  	c.Check(hook.Slots, DeepEquals, map[string]*snap.SlotInfo{
  1287  		slot1.Name: slot1, slot2.Name: slot2})
  1288  
  1289  	// plug1 ("network") is implicitly defined and app-bound to "daemon"
  1290  
  1291  	c.Assert(plug1, Not(IsNil))
  1292  	c.Check(plug1.Snap, Equals, info)
  1293  	c.Check(plug1.Name, Equals, "network")
  1294  	c.Check(plug1.Interface, Equals, "network")
  1295  	c.Check(plug1.Attrs, HasLen, 0)
  1296  	c.Check(plug1.Label, Equals, "")
  1297  	c.Check(plug1.Apps, DeepEquals, map[string]*snap.AppInfo{app1.Name: app1})
  1298  
  1299  	// plug2 ("network-bind") is implicitly defined and app-bound to "daemon"
  1300  
  1301  	c.Assert(plug2, Not(IsNil))
  1302  	c.Check(plug2.Snap, Equals, info)
  1303  	c.Check(plug2.Name, Equals, "network-bind")
  1304  	c.Check(plug2.Interface, Equals, "network-bind")
  1305  	c.Check(plug2.Attrs, HasLen, 0)
  1306  	c.Check(plug2.Label, Equals, "")
  1307  	c.Check(plug2.Apps, DeepEquals, map[string]*snap.AppInfo{app1.Name: app1})
  1308  
  1309  	// plug3 ("foo-socket") is app-bound to "foo"
  1310  
  1311  	c.Assert(plug3, Not(IsNil))
  1312  	c.Check(plug3.Snap, Equals, info)
  1313  	c.Check(plug3.Name, Equals, "foo-socket-plug")
  1314  	c.Check(plug3.Interface, Equals, "socket")
  1315  	c.Check(plug3.Attrs, HasLen, 0)
  1316  	c.Check(plug3.Label, Equals, "")
  1317  	c.Check(plug3.Apps, DeepEquals, map[string]*snap.AppInfo{app2.Name: app2})
  1318  
  1319  	// plug4 ("logging") is global so it is bound to all apps
  1320  
  1321  	c.Assert(plug4, Not(IsNil))
  1322  	c.Check(plug4.Snap, Equals, info)
  1323  	c.Check(plug4.Name, Equals, "logging")
  1324  	c.Check(plug4.Interface, Equals, "syslog")
  1325  	c.Check(plug4.Attrs, HasLen, 0)
  1326  	c.Check(plug4.Label, Equals, "")
  1327  	c.Check(plug4.Apps, DeepEquals, map[string]*snap.AppInfo{
  1328  		app1.Name: app1, app2.Name: app2})
  1329  
  1330  	// slot1 ("foo-socket") is app-bound to "daemon"
  1331  
  1332  	c.Assert(slot1, Not(IsNil))
  1333  	c.Check(slot1.Snap, Equals, info)
  1334  	c.Check(slot1.Name, Equals, "foo-socket-slot")
  1335  	c.Check(slot1.Interface, Equals, "socket")
  1336  	c.Check(slot1.Attrs, DeepEquals, map[string]interface{}{
  1337  		"protocol": "foo", "path": "$SNAP_DATA/socket"})
  1338  	c.Check(slot1.Label, Equals, "")
  1339  	c.Check(slot1.Apps, DeepEquals, map[string]*snap.AppInfo{app1.Name: app1})
  1340  
  1341  	// slot2 ("tracing") is global so it is bound to all apps
  1342  
  1343  	c.Assert(slot2, Not(IsNil))
  1344  	c.Check(slot2.Snap, Equals, info)
  1345  	c.Check(slot2.Name, Equals, "tracing")
  1346  	c.Check(slot2.Interface, Equals, "ptrace")
  1347  	c.Check(slot2.Attrs, HasLen, 0)
  1348  	c.Check(slot2.Label, Equals, "")
  1349  	c.Check(slot2.Apps, DeepEquals, map[string]*snap.AppInfo{
  1350  		app1.Name: app1, app2.Name: app2})
  1351  }
  1352  
  1353  // type and architectures
  1354  
  1355  func (s *YamlSuite) TestSnapYamlTypeDefault(c *C) {
  1356  	y := []byte(`name: binary
  1357  version: 1.0
  1358  `)
  1359  	info, err := snap.InfoFromSnapYaml(y)
  1360  	c.Assert(err, IsNil)
  1361  	c.Assert(info.GetType(), Equals, snap.TypeApp)
  1362  }
  1363  
  1364  func (s *YamlSuite) TestSnapYamlEpochDefault(c *C) {
  1365  	y := []byte(`name: binary
  1366  version: 1.0
  1367  `)
  1368  	info, err := snap.InfoFromSnapYaml(y)
  1369  	c.Assert(err, IsNil)
  1370  	c.Assert(info.Epoch, DeepEquals, snap.E("0"))
  1371  }
  1372  
  1373  func (s *YamlSuite) TestSnapYamlConfinementDefault(c *C) {
  1374  	y := []byte(`name: binary
  1375  version: 1.0
  1376  `)
  1377  	info, err := snap.InfoFromSnapYaml(y)
  1378  	c.Assert(err, IsNil)
  1379  	c.Assert(info.Confinement, Equals, snap.StrictConfinement)
  1380  }
  1381  
  1382  func (s *YamlSuite) TestSnapYamlMultipleArchitecturesParsing(c *C) {
  1383  	y := []byte(`name: binary
  1384  version: 1.0
  1385  architectures: [i386, armhf]
  1386  `)
  1387  	info, err := snap.InfoFromSnapYaml(y)
  1388  	c.Assert(err, IsNil)
  1389  	c.Assert(info.Architectures, DeepEquals, []string{"i386", "armhf"})
  1390  }
  1391  
  1392  func (s *YamlSuite) TestSnapYamlSingleArchitecturesParsing(c *C) {
  1393  	y := []byte(`name: binary
  1394  version: 1.0
  1395  architectures: [i386]
  1396  `)
  1397  	info, err := snap.InfoFromSnapYaml(y)
  1398  	c.Assert(err, IsNil)
  1399  	c.Assert(info.Architectures, DeepEquals, []string{"i386"})
  1400  }
  1401  
  1402  func (s *YamlSuite) TestSnapYamlAssumesParsing(c *C) {
  1403  	y := []byte(`name: binary
  1404  version: 1.0
  1405  assumes: [feature2, feature1]
  1406  `)
  1407  	info, err := snap.InfoFromSnapYaml(y)
  1408  	c.Assert(err, IsNil)
  1409  	c.Assert(info.Assumes, DeepEquals, []string{"feature1", "feature2"})
  1410  }
  1411  
  1412  func (s *YamlSuite) TestSnapYamlNoArchitecturesParsing(c *C) {
  1413  	y := []byte(`name: binary
  1414  version: 1.0
  1415  `)
  1416  	info, err := snap.InfoFromSnapYaml(y)
  1417  	c.Assert(err, IsNil)
  1418  	c.Assert(info.Architectures, DeepEquals, []string{"all"})
  1419  }
  1420  
  1421  func (s *YamlSuite) TestSnapYamlBadArchitectureParsing(c *C) {
  1422  	y := []byte(`name: binary
  1423  version: 1.0
  1424  architectures:
  1425    armhf:
  1426      no
  1427  `)
  1428  	_, err := snap.InfoFromSnapYaml(y)
  1429  	c.Assert(err, NotNil)
  1430  }
  1431  
  1432  // apps
  1433  
  1434  func (s *YamlSuite) TestSimpleAppExample(c *C) {
  1435  	y := []byte(`name: wat
  1436  version: 42
  1437  apps:
  1438   cm:
  1439     command: cm0
  1440  `)
  1441  	info, err := snap.InfoFromSnapYaml(y)
  1442  	c.Assert(err, IsNil)
  1443  	c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{
  1444  		"cm": {
  1445  			Snap:    info,
  1446  			Name:    "cm",
  1447  			Command: "cm0",
  1448  		},
  1449  	})
  1450  }
  1451  
  1452  func (s *YamlSuite) TestDaemonEverythingExample(c *C) {
  1453  	y := []byte(`name: wat
  1454  version: 42
  1455  apps:
  1456   svc:
  1457     command: svc1
  1458     description: svc one
  1459     stop-timeout: 25s
  1460     start-timeout: 42m
  1461     daemon: forking
  1462     stop-command: stop-cmd
  1463     post-stop-command: post-stop-cmd
  1464     restart-condition: on-abnormal
  1465     bus-name: busName
  1466     sockets:
  1467       sock1:
  1468         listen-stream: $SNAP_DATA/sock1.socket
  1469         socket-mode: 0666
  1470  `)
  1471  	info, err := snap.InfoFromSnapYaml(y)
  1472  	c.Assert(err, IsNil)
  1473  
  1474  	app := snap.AppInfo{
  1475  		Snap:            info,
  1476  		Name:            "svc",
  1477  		Command:         "svc1",
  1478  		Daemon:          "forking",
  1479  		RestartCond:     snap.RestartOnAbnormal,
  1480  		StopTimeout:     timeout.Timeout(25 * time.Second),
  1481  		StartTimeout:    timeout.Timeout(42 * time.Minute),
  1482  		StopCommand:     "stop-cmd",
  1483  		PostStopCommand: "post-stop-cmd",
  1484  		BusName:         "busName",
  1485  		Sockets:         map[string]*snap.SocketInfo{},
  1486  	}
  1487  
  1488  	app.Sockets["sock1"] = &snap.SocketInfo{
  1489  		App:          &app,
  1490  		Name:         "sock1",
  1491  		ListenStream: "$SNAP_DATA/sock1.socket",
  1492  		SocketMode:   0666,
  1493  	}
  1494  
  1495  	c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{"svc": &app})
  1496  }
  1497  
  1498  func (s *YamlSuite) TestDaemonListenStreamAsInteger(c *C) {
  1499  	y := []byte(`name: wat
  1500  version: 42
  1501  apps:
  1502   svc:
  1503     command: svc
  1504     sockets:
  1505       sock:
  1506         listen-stream: 8080
  1507  `)
  1508  	info, err := snap.InfoFromSnapYaml(y)
  1509  	c.Assert(err, IsNil)
  1510  
  1511  	app := snap.AppInfo{
  1512  		Snap:    info,
  1513  		Name:    "svc",
  1514  		Command: "svc",
  1515  		Sockets: map[string]*snap.SocketInfo{},
  1516  	}
  1517  
  1518  	app.Sockets["sock"] = &snap.SocketInfo{
  1519  		App:          &app,
  1520  		Name:         "sock",
  1521  		ListenStream: "8080",
  1522  	}
  1523  
  1524  	c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{
  1525  		"svc": &app,
  1526  	})
  1527  }
  1528  
  1529  func (s *YamlSuite) TestDaemonInvalidSocketMode(c *C) {
  1530  	y := []byte(`name: wat
  1531  version: 42
  1532  apps:
  1533   svc:
  1534     command: svc
  1535     sockets:
  1536       sock:
  1537         listen-stream: 8080
  1538         socket-mode: asdfasdf
  1539  `)
  1540  	_, err := snap.InfoFromSnapYaml(y)
  1541  	c.Check(err.Error(), Equals, "cannot parse snap.yaml: yaml: unmarshal errors:\n"+
  1542  		"  line 9: cannot unmarshal !!str `asdfasdf` into os.FileMode")
  1543  }
  1544  
  1545  func (s *YamlSuite) TestSnapYamlGlobalEnvironment(c *C) {
  1546  	y := []byte(`
  1547  name: foo
  1548  version: 1.0
  1549  environment:
  1550   foo: bar
  1551   baz: boom
  1552  `)
  1553  	info, err := snap.InfoFromSnapYaml(y)
  1554  	c.Assert(err, IsNil)
  1555  	c.Assert(info.Environment, DeepEquals, *strutil.NewOrderedMap("foo", "bar", "baz", "boom"))
  1556  }
  1557  
  1558  func (s *YamlSuite) TestSnapYamlPerAppEnvironment(c *C) {
  1559  	y := []byte(`
  1560  name: foo
  1561  version: 1.0
  1562  apps:
  1563   foo:
  1564    environment:
  1565     k1: v1
  1566     k2: v2
  1567  `)
  1568  	info, err := snap.InfoFromSnapYaml(y)
  1569  	c.Assert(err, IsNil)
  1570  	c.Assert(info.Apps["foo"].Environment, DeepEquals, *strutil.NewOrderedMap("k1", "v1", "k2", "v2"))
  1571  }
  1572  
  1573  func (s *YamlSuite) TestSnapYamlPerHookEnvironment(c *C) {
  1574  	y := []byte(`
  1575  name: foo
  1576  version: 1.0
  1577  hooks:
  1578   foo:
  1579    environment:
  1580     k1: v1
  1581     k2: v2
  1582  `)
  1583  	info, err := snap.InfoFromSnapYaml(y)
  1584  	c.Assert(err, IsNil)
  1585  	c.Assert(info.Hooks["foo"].Environment, DeepEquals, *strutil.NewOrderedMap("k1", "v1", "k2", "v2"))
  1586  }
  1587  
  1588  // classic confinement
  1589  func (s *YamlSuite) TestClassicConfinement(c *C) {
  1590  	y := []byte(`
  1591  name: foo
  1592  confinement: classic
  1593  `)
  1594  	info, err := snap.InfoFromSnapYaml(y)
  1595  	c.Assert(err, IsNil)
  1596  	c.Assert(info.Confinement, Equals, snap.ClassicConfinement)
  1597  }
  1598  
  1599  func (s *YamlSuite) TestSnapYamlAliases(c *C) {
  1600  	y := []byte(`
  1601  name: foo
  1602  version: 1.0
  1603  apps:
  1604    foo:
  1605      aliases: [foo]
  1606    bar:
  1607      aliases: [bar, bar1]
  1608  `)
  1609  	info, err := snap.InfoFromSnapYaml(y)
  1610  	c.Assert(err, IsNil)
  1611  
  1612  	c.Check(info.Apps["foo"].LegacyAliases, DeepEquals, []string{"foo"})
  1613  	c.Check(info.Apps["bar"].LegacyAliases, DeepEquals, []string{"bar", "bar1"})
  1614  
  1615  	c.Check(info.LegacyAliases, DeepEquals, map[string]*snap.AppInfo{
  1616  		"foo":  info.Apps["foo"],
  1617  		"bar":  info.Apps["bar"],
  1618  		"bar1": info.Apps["bar"],
  1619  	})
  1620  }
  1621  
  1622  func (s *YamlSuite) TestSnapYamlAliasesConflict(c *C) {
  1623  	y := []byte(`
  1624  name: foo
  1625  version: 1.0
  1626  apps:
  1627    foo:
  1628      aliases: [bar]
  1629    bar:
  1630      aliases: [bar]
  1631  `)
  1632  	_, err := snap.InfoFromSnapYaml(y)
  1633  	c.Assert(err, ErrorMatches, `cannot set "bar" as alias for both ("foo" and "bar"|"bar" and "foo")`)
  1634  }
  1635  
  1636  func (s *YamlSuite) TestSnapYamlAppStartOrder(c *C) {
  1637  	y := []byte(`name: wat
  1638  version: 42
  1639  apps:
  1640   foo:
  1641     after: [bar, zed]
  1642   bar:
  1643     before: [foo]
  1644   baz:
  1645     after: [foo]
  1646   zed:
  1647  
  1648  `)
  1649  	info, err := snap.InfoFromSnapYaml(y)
  1650  	c.Assert(err, IsNil)
  1651  
  1652  	c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{
  1653  		"foo": {
  1654  			Snap:  info,
  1655  			Name:  "foo",
  1656  			After: []string{"bar", "zed"},
  1657  		},
  1658  		"bar": {
  1659  			Snap:   info,
  1660  			Name:   "bar",
  1661  			Before: []string{"foo"},
  1662  		},
  1663  		"baz": {
  1664  			Snap:  info,
  1665  			Name:  "baz",
  1666  			After: []string{"foo"},
  1667  		},
  1668  		"zed": {
  1669  			Snap: info,
  1670  			Name: "zed",
  1671  		},
  1672  	})
  1673  }
  1674  
  1675  func (s *YamlSuite) TestSnapYamlWatchdog(c *C) {
  1676  	y := []byte(`
  1677  name: foo
  1678  version: 1.0
  1679  apps:
  1680    foo:
  1681      watchdog-timeout: 12s
  1682  `)
  1683  	info, err := snap.InfoFromSnapYaml(y)
  1684  	c.Assert(err, IsNil)
  1685  
  1686  	c.Check(info.Apps["foo"].WatchdogTimeout, Equals, timeout.Timeout(12*time.Second))
  1687  }
  1688  
  1689  func (s *YamlSuite) TestLayout(c *C) {
  1690  	y := []byte(`
  1691  name: foo
  1692  version: 1.0
  1693  layout:
  1694    /usr/share/foo:
  1695      bind: $SNAP/usr/share/foo
  1696    /usr/share/bar:
  1697      symlink: $SNAP/usr/share/bar
  1698    /etc/froz:
  1699      bind-file: $SNAP/etc/froz
  1700  `)
  1701  	info, err := snap.InfoFromSnapYaml(y)
  1702  	c.Assert(err, IsNil)
  1703  	c.Assert(info.Layout["/usr/share/foo"], DeepEquals, &snap.Layout{
  1704  		Snap:  info,
  1705  		Path:  "/usr/share/foo",
  1706  		Bind:  "$SNAP/usr/share/foo",
  1707  		User:  "root",
  1708  		Group: "root",
  1709  		Mode:  0755,
  1710  	})
  1711  	c.Assert(info.Layout["/usr/share/bar"], DeepEquals, &snap.Layout{
  1712  		Snap:    info,
  1713  		Path:    "/usr/share/bar",
  1714  		Symlink: "$SNAP/usr/share/bar",
  1715  		User:    "root",
  1716  		Group:   "root",
  1717  		Mode:    0755,
  1718  	})
  1719  	c.Assert(info.Layout["/etc/froz"], DeepEquals, &snap.Layout{
  1720  		Snap:     info,
  1721  		Path:     "/etc/froz",
  1722  		BindFile: "$SNAP/etc/froz",
  1723  		User:     "root",
  1724  		Group:    "root",
  1725  		Mode:     0755,
  1726  	})
  1727  }
  1728  
  1729  func (s *YamlSuite) TestLayoutsWithTypo(c *C) {
  1730  	y := []byte(`
  1731  name: foo
  1732  version: 1.0
  1733  layouts:
  1734    /usr/share/foo:
  1735      bind: $SNAP/usr/share/foo
  1736  `)
  1737  	info, err := snap.InfoFromSnapYaml(y)
  1738  	c.Assert(err, ErrorMatches, `cannot parse snap.yaml: typo detected: use singular "layout" instead of plural "layouts"`)
  1739  	c.Assert(info, IsNil)
  1740  }
  1741  
  1742  func (s *YamlSuite) TestSnapYamlAppTimer(c *C) {
  1743  	y := []byte(`name: wat
  1744  version: 42
  1745  apps:
  1746   foo:
  1747     daemon: oneshot
  1748     timer: mon,10:00-12:00
  1749  
  1750  `)
  1751  	info, err := snap.InfoFromSnapYaml(y)
  1752  	c.Assert(err, IsNil)
  1753  	app := info.Apps["foo"]
  1754  	c.Check(app.Timer, DeepEquals, &snap.TimerInfo{App: app, Timer: "mon,10:00-12:00"})
  1755  }
  1756  
  1757  func (s *YamlSuite) TestSnapYamlAppAutostart(c *C) {
  1758  	yAutostart := []byte(`name: wat
  1759  version: 42
  1760  apps:
  1761   foo:
  1762     command: bin/foo
  1763     autostart: foo.desktop
  1764  
  1765  `)
  1766  	info, err := snap.InfoFromSnapYaml(yAutostart)
  1767  	c.Assert(err, IsNil)
  1768  	app := info.Apps["foo"]
  1769  	c.Check(app.Autostart, Equals, "foo.desktop")
  1770  
  1771  	yNoAutostart := []byte(`name: wat
  1772  version: 42
  1773  apps:
  1774   foo:
  1775     command: bin/foo
  1776  
  1777  `)
  1778  	info, err = snap.InfoFromSnapYaml(yNoAutostart)
  1779  	c.Assert(err, IsNil)
  1780  	app = info.Apps["foo"]
  1781  	c.Check(app.Autostart, Equals, "")
  1782  }
  1783  
  1784  func (s *YamlSuite) TestSnapYamlAppCommonID(c *C) {
  1785  	yAutostart := []byte(`name: wat
  1786  version: 42
  1787  apps:
  1788   foo:
  1789     command: bin/foo
  1790     common-id: org.foo
  1791   bar:
  1792     command: bin/foo
  1793     common-id: org.bar
  1794   baz:
  1795     command: bin/foo
  1796  
  1797  `)
  1798  	info, err := snap.InfoFromSnapYaml(yAutostart)
  1799  	c.Assert(err, IsNil)
  1800  	c.Check(info.Apps["foo"].CommonID, Equals, "org.foo")
  1801  	c.Check(info.Apps["bar"].CommonID, Equals, "org.bar")
  1802  	c.Check(info.Apps["baz"].CommonID, Equals, "")
  1803  	c.Assert(info.CommonIDs, HasLen, 2)
  1804  	c.Assert((info.CommonIDs[0] == "org.foo" && info.CommonIDs[1] == "org.bar") ||
  1805  		(info.CommonIDs[1] == "org.foo" && info.CommonIDs[0] == "org.bar"),
  1806  		Equals,
  1807  		true)
  1808  }
  1809  
  1810  func (s *YamlSuite) TestSnapYamlCommandChain(c *C) {
  1811  	yAutostart := []byte(`name: wat
  1812  version: 42
  1813  apps:
  1814   foo:
  1815    command: bin/foo
  1816    command-chain: [chain1, chain2]
  1817  hooks:
  1818   configure:
  1819    command-chain: [hookchain1, hookchain2]
  1820  `)
  1821  	info, err := snap.InfoFromSnapYaml(yAutostart)
  1822  	c.Assert(err, IsNil)
  1823  	app := info.Apps["foo"]
  1824  	c.Check(app.CommandChain, DeepEquals, []string{"chain1", "chain2"})
  1825  	hook := info.Hooks["configure"]
  1826  	c.Check(hook.CommandChain, DeepEquals, []string{"hookchain1", "hookchain2"})
  1827  }
  1828  
  1829  func (s *YamlSuite) TestSnapYamlRestartDelay(c *C) {
  1830  	yAutostart := []byte(`name: wat
  1831  version: 42
  1832  apps:
  1833   foo:
  1834    command: bin/foo
  1835    daemon: simple
  1836    restart-delay: 12s
  1837  `)
  1838  	info, err := snap.InfoFromSnapYaml(yAutostart)
  1839  	c.Assert(err, IsNil)
  1840  	app := info.Apps["foo"]
  1841  	c.Assert(app, NotNil)
  1842  	c.Check(app.RestartDelay, Equals, timeout.Timeout(12*time.Second))
  1843  }
  1844  
  1845  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsing(c *C) {
  1846  	y := []byte(`name: binary
  1847  version: 1.0
  1848  system-usernames:
  1849    foo: shared
  1850    bar:
  1851      scope: external
  1852    baz:
  1853      scope: private
  1854      attr1: norf
  1855      attr2: corge
  1856      attr3: ""
  1857  `)
  1858  	info, err := snap.InfoFromSnapYaml(y)
  1859  	c.Assert(err, IsNil)
  1860  	c.Check(info.SystemUsernames, HasLen, 3)
  1861  	c.Assert(info.SystemUsernames["foo"], DeepEquals, &snap.SystemUsernameInfo{
  1862  		Name:  "foo",
  1863  		Scope: "shared",
  1864  	})
  1865  	c.Assert(info.SystemUsernames["bar"], DeepEquals, &snap.SystemUsernameInfo{
  1866  		Name:  "bar",
  1867  		Scope: "external",
  1868  	})
  1869  	c.Assert(info.SystemUsernames["baz"], DeepEquals, &snap.SystemUsernameInfo{
  1870  		Name:  "baz",
  1871  		Scope: "private",
  1872  		Attrs: map[string]interface{}{
  1873  			"attr1": "norf",
  1874  			"attr2": "corge",
  1875  			"attr3": "",
  1876  		},
  1877  	})
  1878  }
  1879  
  1880  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadType(c *C) {
  1881  	y := []byte(`name: binary
  1882  version: 1.0
  1883  system-usernames:
  1884    a: true
  1885  `)
  1886  	info, err := snap.InfoFromSnapYaml(y)
  1887  	c.Assert(err, ErrorMatches, `system username "a" has malformed definition \(found bool\)`)
  1888  	c.Assert(info, IsNil)
  1889  }
  1890  
  1891  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadValue(c *C) {
  1892  	y := []byte(`name: binary
  1893  version: 1.0
  1894  system-usernames:
  1895    a: [b, c]
  1896  `)
  1897  	info, err := snap.InfoFromSnapYaml(y)
  1898  	c.Assert(err, ErrorMatches, `system username "a" has malformed definition \(found \[\]interface {}\)`)
  1899  	c.Assert(info, IsNil)
  1900  }
  1901  
  1902  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadKeyEmpty(c *C) {
  1903  	y := []byte(`name: binary
  1904  version: 1.0
  1905  system-usernames:
  1906    "": shared
  1907  `)
  1908  	_, err := snap.InfoFromSnapYaml(y)
  1909  	c.Assert(err, ErrorMatches, `system username cannot be empty`)
  1910  }
  1911  
  1912  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadKeyList(c *C) {
  1913  	y := []byte(`name: binary
  1914  version: 1.0
  1915  system-usernames:
  1916  - foo: shared
  1917  `)
  1918  	_, err := snap.InfoFromSnapYaml(y)
  1919  	c.Assert(err, ErrorMatches, `(?m)cannot parse snap.yaml:.*`)
  1920  }
  1921  
  1922  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadValueEmpty(c *C) {
  1923  	y := []byte(`name: binary
  1924  version: 1.0
  1925  system-usernames:
  1926    a: ""
  1927  `)
  1928  	_, err := snap.InfoFromSnapYaml(y)
  1929  	c.Assert(err, ErrorMatches, `system username "a" does not specify a scope`)
  1930  }
  1931  
  1932  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadValueNull(c *C) {
  1933  	y := []byte(`name: binary
  1934  version: 1.0
  1935  system-usernames:
  1936    a: null
  1937  `)
  1938  	_, err := snap.InfoFromSnapYaml(y)
  1939  	c.Assert(err, ErrorMatches, `system username "a" does not specify a scope`)
  1940  }
  1941  
  1942  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadAttrKeyEmpty(c *C) {
  1943  	y := []byte(`name: binary
  1944  version: 1.0
  1945  system-usernames:
  1946    foo:
  1947      scope: shared
  1948      "": bar
  1949  `)
  1950  	_, err := snap.InfoFromSnapYaml(y)
  1951  	c.Assert(err, ErrorMatches, `system username "foo" has an empty attribute key`)
  1952  }
  1953  
  1954  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadAttrKeyNonString(c *C) {
  1955  	y := []byte(`name: binary
  1956  version: 1.0
  1957  system-usernames:
  1958    foo:
  1959      scope: shared
  1960      1: bar
  1961  `)
  1962  	_, err := snap.InfoFromSnapYaml(y)
  1963  	c.Assert(err, ErrorMatches, `system username "foo" has attribute key that is not a string \(found int\)`)
  1964  }
  1965  
  1966  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadAttrValue(c *C) {
  1967  	y := []byte(`name: binary
  1968  version: 1.0
  1969  system-usernames:
  1970    foo:
  1971      scope: shared
  1972      bar: null
  1973  `)
  1974  	_, err := snap.InfoFromSnapYaml(y)
  1975  	c.Assert(err, ErrorMatches, `attribute "bar" of system username "foo": invalid scalar:.*`)
  1976  }
  1977  
  1978  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadScopeNonString(c *C) {
  1979  	y := []byte(`name: binary
  1980  version: 1.0
  1981  system-usernames:
  1982    foo:
  1983      scope: 10
  1984  `)
  1985  	_, err := snap.InfoFromSnapYaml(y)
  1986  	c.Assert(err, ErrorMatches, `scope on system username "foo" is not a string \(found int\)`)
  1987  }