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