github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/snap/info_snap_yaml_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2021 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package snap_test
    21  
    22  import (
    23  	"fmt"
    24  	"regexp"
    25  	"testing"
    26  	"time"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/snap"
    31  	"github.com/snapcore/snapd/strutil"
    32  	"github.com/snapcore/snapd/testutil"
    33  	"github.com/snapcore/snapd/timeout"
    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.Type(), 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.Type(), 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.Type(), 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  func (s *YamlSuite) TestUnmarshalActivatesOn(c *C) {
  1354  	info, err := snap.InfoFromSnapYaml([]byte(`
  1355  name: snap
  1356  slots:
  1357      test-slot1:
  1358      test-slot2:
  1359  apps:
  1360      daemon:
  1361          activates-on: ["test-slot1", "test-slot2"]
  1362      foo:
  1363  `))
  1364  	c.Assert(err, IsNil)
  1365  	c.Check(info.InstanceName(), Equals, "snap")
  1366  	c.Check(info.Plugs, HasLen, 0)
  1367  	c.Check(info.Slots, HasLen, 2)
  1368  	c.Check(info.Apps, HasLen, 2)
  1369  	c.Check(info.Hooks, HasLen, 0)
  1370  
  1371  	app1 := info.Apps["daemon"]
  1372  	app2 := info.Apps["foo"]
  1373  	slot1 := info.Slots["test-slot1"]
  1374  	slot2 := info.Slots["test-slot2"]
  1375  
  1376  	c.Assert(app1, Not(IsNil))
  1377  	c.Check(app1.Name, Equals, "daemon")
  1378  	c.Check(app1.ActivatesOn, DeepEquals, []*snap.SlotInfo{slot1, slot2})
  1379  	// activates-on slots are implicitly added to the app
  1380  	c.Check(app1.Slots, DeepEquals, map[string]*snap.SlotInfo{
  1381  		slot1.Name: slot1, slot2.Name: slot2})
  1382  
  1383  	c.Assert(app2, Not(IsNil))
  1384  	c.Check(app2.Name, Equals, "foo")
  1385  	c.Check(app2.ActivatesOn, HasLen, 0)
  1386  	// As slot has been bound to app1, it isn't implicitly applied here
  1387  	c.Check(app2.Slots, HasLen, 0)
  1388  
  1389  	c.Assert(slot1, Not(IsNil))
  1390  	c.Check(slot1.Name, Equals, "test-slot1")
  1391  	c.Check(slot1.Apps, DeepEquals, map[string]*snap.AppInfo{
  1392  		app1.Name: app1})
  1393  
  1394  	c.Assert(slot2, Not(IsNil))
  1395  	c.Check(slot2.Name, Equals, "test-slot2")
  1396  	c.Check(slot2.Apps, DeepEquals, map[string]*snap.AppInfo{
  1397  		app1.Name: app1})
  1398  }
  1399  
  1400  func (s *YamlSuite) TestUnmarshalActivatesOnUnknownSlot(c *C) {
  1401  	info, err := snap.InfoFromSnapYaml([]byte(`
  1402  name: snap
  1403  apps:
  1404      daemon:
  1405          activates-on: ["test-slot"]
  1406  `))
  1407  	c.Check(info, IsNil)
  1408  	c.Check(err, ErrorMatches, `invalid activates-on value "test-slot" on app "daemon": slot not found`)
  1409  }
  1410  
  1411  // type and architectures
  1412  
  1413  func (s *YamlSuite) TestSnapYamlTypeDefault(c *C) {
  1414  	y := []byte(`name: binary
  1415  version: 1.0
  1416  `)
  1417  	info, err := snap.InfoFromSnapYaml(y)
  1418  	c.Assert(err, IsNil)
  1419  	c.Assert(info.Type(), Equals, snap.TypeApp)
  1420  }
  1421  
  1422  func (s *YamlSuite) TestSnapYamlEpochDefault(c *C) {
  1423  	y := []byte(`name: binary
  1424  version: 1.0
  1425  `)
  1426  	info, err := snap.InfoFromSnapYaml(y)
  1427  	c.Assert(err, IsNil)
  1428  	c.Assert(info.Epoch, DeepEquals, snap.E("0"))
  1429  }
  1430  
  1431  func (s *YamlSuite) TestSnapYamlConfinementDefault(c *C) {
  1432  	y := []byte(`name: binary
  1433  version: 1.0
  1434  `)
  1435  	info, err := snap.InfoFromSnapYaml(y)
  1436  	c.Assert(err, IsNil)
  1437  	c.Assert(info.Confinement, Equals, snap.StrictConfinement)
  1438  }
  1439  
  1440  func (s *YamlSuite) TestSnapYamlMultipleArchitecturesParsing(c *C) {
  1441  	y := []byte(`name: binary
  1442  version: 1.0
  1443  architectures: [i386, armhf]
  1444  `)
  1445  	info, err := snap.InfoFromSnapYaml(y)
  1446  	c.Assert(err, IsNil)
  1447  	c.Assert(info.Architectures, DeepEquals, []string{"i386", "armhf"})
  1448  }
  1449  
  1450  func (s *YamlSuite) TestSnapYamlSingleArchitecturesParsing(c *C) {
  1451  	y := []byte(`name: binary
  1452  version: 1.0
  1453  architectures: [i386]
  1454  `)
  1455  	info, err := snap.InfoFromSnapYaml(y)
  1456  	c.Assert(err, IsNil)
  1457  	c.Assert(info.Architectures, DeepEquals, []string{"i386"})
  1458  }
  1459  
  1460  func (s *YamlSuite) TestSnapYamlAssumesParsing(c *C) {
  1461  	y := []byte(`name: binary
  1462  version: 1.0
  1463  assumes: [feature2, feature1]
  1464  `)
  1465  	info, err := snap.InfoFromSnapYaml(y)
  1466  	c.Assert(err, IsNil)
  1467  	c.Assert(info.Assumes, DeepEquals, []string{"feature1", "feature2"})
  1468  }
  1469  
  1470  func (s *YamlSuite) TestSnapYamlNoArchitecturesParsing(c *C) {
  1471  	y := []byte(`name: binary
  1472  version: 1.0
  1473  `)
  1474  	info, err := snap.InfoFromSnapYaml(y)
  1475  	c.Assert(err, IsNil)
  1476  	c.Assert(info.Architectures, DeepEquals, []string{"all"})
  1477  }
  1478  
  1479  func (s *YamlSuite) TestSnapYamlBadArchitectureParsing(c *C) {
  1480  	y := []byte(`name: binary
  1481  version: 1.0
  1482  architectures:
  1483    armhf:
  1484      no
  1485  `)
  1486  	_, err := snap.InfoFromSnapYaml(y)
  1487  	c.Assert(err, NotNil)
  1488  }
  1489  
  1490  // apps
  1491  
  1492  func (s *YamlSuite) TestSimpleAppExample(c *C) {
  1493  	y := []byte(`name: wat
  1494  version: 42
  1495  apps:
  1496   cm:
  1497     command: cm0
  1498  `)
  1499  	info, err := snap.InfoFromSnapYaml(y)
  1500  	c.Assert(err, IsNil)
  1501  	c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{
  1502  		"cm": {
  1503  			Snap:    info,
  1504  			Name:    "cm",
  1505  			Command: "cm0",
  1506  		},
  1507  	})
  1508  }
  1509  
  1510  func (s *YamlSuite) TestDaemonEverythingExample(c *C) {
  1511  	y := []byte(`name: wat
  1512  version: 42
  1513  apps:
  1514   svc:
  1515     command: svc1
  1516     description: svc one
  1517     stop-timeout: 25s
  1518     start-timeout: 42m
  1519     daemon: forking
  1520     daemon-scope: system
  1521     stop-command: stop-cmd
  1522     post-stop-command: post-stop-cmd
  1523     restart-condition: on-abnormal
  1524     bus-name: busName
  1525     sockets:
  1526       sock1:
  1527         listen-stream: $SNAP_DATA/sock1.socket
  1528         socket-mode: 0666
  1529  `)
  1530  	info, err := snap.InfoFromSnapYaml(y)
  1531  	c.Assert(err, IsNil)
  1532  
  1533  	app := snap.AppInfo{
  1534  		Snap:            info,
  1535  		Name:            "svc",
  1536  		Command:         "svc1",
  1537  		Daemon:          "forking",
  1538  		DaemonScope:     snap.SystemDaemon,
  1539  		RestartCond:     snap.RestartOnAbnormal,
  1540  		StopTimeout:     timeout.Timeout(25 * time.Second),
  1541  		StartTimeout:    timeout.Timeout(42 * time.Minute),
  1542  		StopCommand:     "stop-cmd",
  1543  		PostStopCommand: "post-stop-cmd",
  1544  		BusName:         "busName",
  1545  		Sockets:         map[string]*snap.SocketInfo{},
  1546  	}
  1547  
  1548  	app.Sockets["sock1"] = &snap.SocketInfo{
  1549  		App:          &app,
  1550  		Name:         "sock1",
  1551  		ListenStream: "$SNAP_DATA/sock1.socket",
  1552  		SocketMode:   0666,
  1553  	}
  1554  
  1555  	c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{"svc": &app})
  1556  }
  1557  
  1558  func (s *YamlSuite) TestDaemonUserDaemon(c *C) {
  1559  	y := []byte(`name: wat
  1560  version: 42
  1561  apps:
  1562   svc:
  1563     command: svc1
  1564     daemon: simple
  1565     daemon-scope: user
  1566  `)
  1567  	info, err := snap.InfoFromSnapYaml(y)
  1568  	c.Assert(err, IsNil)
  1569  	c.Check(info.Apps["svc"].DaemonScope, Equals, snap.UserDaemon)
  1570  }
  1571  
  1572  func (s *YamlSuite) TestDaemonNoDaemonScope(c *C) {
  1573  	y := []byte(`name: wat
  1574  version: 42
  1575  apps:
  1576   svc:
  1577     command: svc1
  1578     daemon: simple
  1579  `)
  1580  	info, err := snap.InfoFromSnapYaml(y)
  1581  	c.Assert(err, IsNil)
  1582  
  1583  	// If daemon-scope is unset, default to system scope
  1584  	c.Check(info.Apps["svc"].DaemonScope, Equals, snap.SystemDaemon)
  1585  }
  1586  
  1587  func (s *YamlSuite) TestDaemonListenStreamAsInteger(c *C) {
  1588  	y := []byte(`name: wat
  1589  version: 42
  1590  apps:
  1591   svc:
  1592     command: svc
  1593     sockets:
  1594       sock:
  1595         listen-stream: 8080
  1596  `)
  1597  	info, err := snap.InfoFromSnapYaml(y)
  1598  	c.Assert(err, IsNil)
  1599  
  1600  	app := snap.AppInfo{
  1601  		Snap:    info,
  1602  		Name:    "svc",
  1603  		Command: "svc",
  1604  		Sockets: map[string]*snap.SocketInfo{},
  1605  	}
  1606  
  1607  	app.Sockets["sock"] = &snap.SocketInfo{
  1608  		App:          &app,
  1609  		Name:         "sock",
  1610  		ListenStream: "8080",
  1611  	}
  1612  
  1613  	c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{
  1614  		"svc": &app,
  1615  	})
  1616  }
  1617  
  1618  func (s *YamlSuite) TestDaemonInvalidSocketMode(c *C) {
  1619  	y := []byte(`name: wat
  1620  version: 42
  1621  apps:
  1622   svc:
  1623     command: svc
  1624     sockets:
  1625       sock:
  1626         listen-stream: 8080
  1627         socket-mode: asdfasdf
  1628  `)
  1629  	_, err := snap.InfoFromSnapYaml(y)
  1630  	c.Check(err.Error(), Matches, "cannot parse snap.yaml: yaml: unmarshal errors:\n"+
  1631  		"  line 9: cannot unmarshal !!str `asdfasdf` into (os|fs).FileMode")
  1632  }
  1633  
  1634  func (s *YamlSuite) TestDaemonInvalidDaemonScope(c *C) {
  1635  	y := []byte(`name: wat
  1636  version: 42
  1637  apps:
  1638   svc:
  1639     command: svc
  1640     daemon-scope: invalid
  1641  `)
  1642  	_, err := snap.InfoFromSnapYaml(y)
  1643  	c.Check(err.Error(), Equals, "cannot parse snap.yaml: invalid daemon scope: \"invalid\"")
  1644  }
  1645  
  1646  func (s *YamlSuite) TestSnapYamlGlobalEnvironment(c *C) {
  1647  	y := []byte(`
  1648  name: foo
  1649  version: 1.0
  1650  environment:
  1651   foo: bar
  1652   baz: boom
  1653  `)
  1654  	info, err := snap.InfoFromSnapYaml(y)
  1655  	c.Assert(err, IsNil)
  1656  	c.Assert(info.Environment, DeepEquals, *strutil.NewOrderedMap("foo", "bar", "baz", "boom"))
  1657  }
  1658  
  1659  func (s *YamlSuite) TestSnapYamlPerAppEnvironment(c *C) {
  1660  	y := []byte(`
  1661  name: foo
  1662  version: 1.0
  1663  apps:
  1664   foo:
  1665    environment:
  1666     k1: v1
  1667     k2: v2
  1668  `)
  1669  	info, err := snap.InfoFromSnapYaml(y)
  1670  	c.Assert(err, IsNil)
  1671  	c.Assert(info.Apps["foo"].Environment, DeepEquals, *strutil.NewOrderedMap("k1", "v1", "k2", "v2"))
  1672  }
  1673  
  1674  func (s *YamlSuite) TestSnapYamlPerHookEnvironment(c *C) {
  1675  	y := []byte(`
  1676  name: foo
  1677  version: 1.0
  1678  hooks:
  1679   foo:
  1680    environment:
  1681     k1: v1
  1682     k2: v2
  1683  `)
  1684  	info, err := snap.InfoFromSnapYaml(y)
  1685  	c.Assert(err, IsNil)
  1686  	c.Assert(info.Hooks["foo"].Environment, DeepEquals, *strutil.NewOrderedMap("k1", "v1", "k2", "v2"))
  1687  }
  1688  
  1689  // classic confinement
  1690  func (s *YamlSuite) TestClassicConfinement(c *C) {
  1691  	y := []byte(`
  1692  name: foo
  1693  confinement: classic
  1694  `)
  1695  	info, err := snap.InfoFromSnapYaml(y)
  1696  	c.Assert(err, IsNil)
  1697  	c.Assert(info.Confinement, Equals, snap.ClassicConfinement)
  1698  }
  1699  
  1700  func (s *YamlSuite) TestSnapYamlAliases(c *C) {
  1701  	y := []byte(`
  1702  name: foo
  1703  version: 1.0
  1704  apps:
  1705    foo:
  1706      aliases: [foo]
  1707    bar:
  1708      aliases: [bar, bar1]
  1709  `)
  1710  	info, err := snap.InfoFromSnapYaml(y)
  1711  	c.Assert(err, IsNil)
  1712  
  1713  	c.Check(info.Apps["foo"].LegacyAliases, DeepEquals, []string{"foo"})
  1714  	c.Check(info.Apps["bar"].LegacyAliases, DeepEquals, []string{"bar", "bar1"})
  1715  
  1716  	c.Check(info.LegacyAliases, DeepEquals, map[string]*snap.AppInfo{
  1717  		"foo":  info.Apps["foo"],
  1718  		"bar":  info.Apps["bar"],
  1719  		"bar1": info.Apps["bar"],
  1720  	})
  1721  }
  1722  
  1723  func (s *YamlSuite) TestSnapYamlAliasesConflict(c *C) {
  1724  	y := []byte(`
  1725  name: foo
  1726  version: 1.0
  1727  apps:
  1728    foo:
  1729      aliases: [bar]
  1730    bar:
  1731      aliases: [bar]
  1732  `)
  1733  	_, err := snap.InfoFromSnapYaml(y)
  1734  	c.Assert(err, ErrorMatches, `cannot set "bar" as alias for both ("foo" and "bar"|"bar" and "foo")`)
  1735  }
  1736  
  1737  func (s *YamlSuite) TestSnapYamlAppStartOrder(c *C) {
  1738  	y := []byte(`name: wat
  1739  version: 42
  1740  apps:
  1741   foo:
  1742     after: [bar, zed]
  1743   bar:
  1744     before: [foo]
  1745   baz:
  1746     after: [foo]
  1747   zed:
  1748  
  1749  `)
  1750  	info, err := snap.InfoFromSnapYaml(y)
  1751  	c.Assert(err, IsNil)
  1752  
  1753  	c.Check(info.Apps, DeepEquals, map[string]*snap.AppInfo{
  1754  		"foo": {
  1755  			Snap:  info,
  1756  			Name:  "foo",
  1757  			After: []string{"bar", "zed"},
  1758  		},
  1759  		"bar": {
  1760  			Snap:   info,
  1761  			Name:   "bar",
  1762  			Before: []string{"foo"},
  1763  		},
  1764  		"baz": {
  1765  			Snap:  info,
  1766  			Name:  "baz",
  1767  			After: []string{"foo"},
  1768  		},
  1769  		"zed": {
  1770  			Snap: info,
  1771  			Name: "zed",
  1772  		},
  1773  	})
  1774  }
  1775  
  1776  func (s *YamlSuite) TestSnapYamlWatchdog(c *C) {
  1777  	y := []byte(`
  1778  name: foo
  1779  version: 1.0
  1780  apps:
  1781    foo:
  1782      watchdog-timeout: 12s
  1783  `)
  1784  	info, err := snap.InfoFromSnapYaml(y)
  1785  	c.Assert(err, IsNil)
  1786  
  1787  	c.Check(info.Apps["foo"].WatchdogTimeout, Equals, timeout.Timeout(12*time.Second))
  1788  }
  1789  
  1790  func (s *YamlSuite) TestLayout(c *C) {
  1791  	y := []byte(`
  1792  name: foo
  1793  version: 1.0
  1794  layout:
  1795    /usr/share/foo:
  1796      bind: $SNAP/usr/share/foo
  1797    /usr/share/bar:
  1798      symlink: $SNAP/usr/share/bar
  1799    /etc/froz:
  1800      bind-file: $SNAP/etc/froz
  1801  `)
  1802  	info, err := snap.InfoFromSnapYaml(y)
  1803  	c.Assert(err, IsNil)
  1804  	c.Assert(info.Layout["/usr/share/foo"], DeepEquals, &snap.Layout{
  1805  		Snap:  info,
  1806  		Path:  "/usr/share/foo",
  1807  		Bind:  "$SNAP/usr/share/foo",
  1808  		User:  "root",
  1809  		Group: "root",
  1810  		Mode:  0755,
  1811  	})
  1812  	c.Assert(info.Layout["/usr/share/bar"], DeepEquals, &snap.Layout{
  1813  		Snap:    info,
  1814  		Path:    "/usr/share/bar",
  1815  		Symlink: "$SNAP/usr/share/bar",
  1816  		User:    "root",
  1817  		Group:   "root",
  1818  		Mode:    0755,
  1819  	})
  1820  	c.Assert(info.Layout["/etc/froz"], DeepEquals, &snap.Layout{
  1821  		Snap:     info,
  1822  		Path:     "/etc/froz",
  1823  		BindFile: "$SNAP/etc/froz",
  1824  		User:     "root",
  1825  		Group:    "root",
  1826  		Mode:     0755,
  1827  	})
  1828  }
  1829  
  1830  func (s *YamlSuite) TestLayoutsWithTypo(c *C) {
  1831  	y := []byte(`
  1832  name: foo
  1833  version: 1.0
  1834  layouts:
  1835    /usr/share/foo:
  1836      bind: $SNAP/usr/share/foo
  1837  `)
  1838  	info, err := snap.InfoFromSnapYaml(y)
  1839  	c.Assert(err, ErrorMatches, `cannot parse snap.yaml: typo detected: use singular "layout" instead of plural "layouts"`)
  1840  	c.Assert(info, IsNil)
  1841  }
  1842  
  1843  func (s *YamlSuite) TestSnapYamlAppTimer(c *C) {
  1844  	y := []byte(`name: wat
  1845  version: 42
  1846  apps:
  1847   foo:
  1848     daemon: oneshot
  1849     timer: mon,10:00-12:00
  1850  
  1851  `)
  1852  	info, err := snap.InfoFromSnapYaml(y)
  1853  	c.Assert(err, IsNil)
  1854  	app := info.Apps["foo"]
  1855  	c.Check(app.Timer, DeepEquals, &snap.TimerInfo{App: app, Timer: "mon,10:00-12:00"})
  1856  }
  1857  
  1858  func (s *YamlSuite) TestSnapYamlAppAutostart(c *C) {
  1859  	yAutostart := []byte(`name: wat
  1860  version: 42
  1861  apps:
  1862   foo:
  1863     command: bin/foo
  1864     autostart: foo.desktop
  1865  
  1866  `)
  1867  	info, err := snap.InfoFromSnapYaml(yAutostart)
  1868  	c.Assert(err, IsNil)
  1869  	app := info.Apps["foo"]
  1870  	c.Check(app.Autostart, Equals, "foo.desktop")
  1871  
  1872  	yNoAutostart := []byte(`name: wat
  1873  version: 42
  1874  apps:
  1875   foo:
  1876     command: bin/foo
  1877  
  1878  `)
  1879  	info, err = snap.InfoFromSnapYaml(yNoAutostart)
  1880  	c.Assert(err, IsNil)
  1881  	app = info.Apps["foo"]
  1882  	c.Check(app.Autostart, Equals, "")
  1883  }
  1884  
  1885  func (s *YamlSuite) TestSnapYamlAppCommonID(c *C) {
  1886  	yAutostart := []byte(`name: wat
  1887  version: 42
  1888  apps:
  1889   foo:
  1890     command: bin/foo
  1891     common-id: org.foo
  1892   bar:
  1893     command: bin/foo
  1894     common-id: org.bar
  1895   baz:
  1896     command: bin/foo
  1897  
  1898  `)
  1899  	info, err := snap.InfoFromSnapYaml(yAutostart)
  1900  	c.Assert(err, IsNil)
  1901  	c.Check(info.Apps["foo"].CommonID, Equals, "org.foo")
  1902  	c.Check(info.Apps["bar"].CommonID, Equals, "org.bar")
  1903  	c.Check(info.Apps["baz"].CommonID, Equals, "")
  1904  	c.Assert(info.CommonIDs, HasLen, 2)
  1905  	c.Assert((info.CommonIDs[0] == "org.foo" && info.CommonIDs[1] == "org.bar") ||
  1906  		(info.CommonIDs[1] == "org.foo" && info.CommonIDs[0] == "org.bar"),
  1907  		Equals,
  1908  		true)
  1909  }
  1910  
  1911  func (s *YamlSuite) TestSnapYamlCommandChain(c *C) {
  1912  	yAutostart := []byte(`name: wat
  1913  version: 42
  1914  apps:
  1915   foo:
  1916    command: bin/foo
  1917    command-chain: [chain1, chain2]
  1918  hooks:
  1919   configure:
  1920    command-chain: [hookchain1, hookchain2]
  1921  `)
  1922  	info, err := snap.InfoFromSnapYaml(yAutostart)
  1923  	c.Assert(err, IsNil)
  1924  	app := info.Apps["foo"]
  1925  	c.Check(app.CommandChain, DeepEquals, []string{"chain1", "chain2"})
  1926  	hook := info.Hooks["configure"]
  1927  	c.Check(hook.CommandChain, DeepEquals, []string{"hookchain1", "hookchain2"})
  1928  }
  1929  
  1930  func (s *YamlSuite) TestSnapYamlRestartDelay(c *C) {
  1931  	yAutostart := []byte(`name: wat
  1932  version: 42
  1933  apps:
  1934   foo:
  1935    command: bin/foo
  1936    daemon: simple
  1937    restart-delay: 12s
  1938  `)
  1939  	info, err := snap.InfoFromSnapYaml(yAutostart)
  1940  	c.Assert(err, IsNil)
  1941  	app := info.Apps["foo"]
  1942  	c.Assert(app, NotNil)
  1943  	c.Check(app.RestartDelay, Equals, timeout.Timeout(12*time.Second))
  1944  }
  1945  
  1946  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsing(c *C) {
  1947  	y := []byte(`name: binary
  1948  version: 1.0
  1949  system-usernames:
  1950    foo: shared
  1951    bar:
  1952      scope: external
  1953    baz:
  1954      scope: private
  1955      attr1: norf
  1956      attr2: corge
  1957      attr3: ""
  1958  `)
  1959  	info, err := snap.InfoFromSnapYaml(y)
  1960  	c.Assert(err, IsNil)
  1961  	c.Check(info.SystemUsernames, HasLen, 3)
  1962  	c.Assert(info.SystemUsernames["foo"], DeepEquals, &snap.SystemUsernameInfo{
  1963  		Name:  "foo",
  1964  		Scope: "shared",
  1965  	})
  1966  	c.Assert(info.SystemUsernames["bar"], DeepEquals, &snap.SystemUsernameInfo{
  1967  		Name:  "bar",
  1968  		Scope: "external",
  1969  	})
  1970  	c.Assert(info.SystemUsernames["baz"], DeepEquals, &snap.SystemUsernameInfo{
  1971  		Name:  "baz",
  1972  		Scope: "private",
  1973  		Attrs: map[string]interface{}{
  1974  			"attr1": "norf",
  1975  			"attr2": "corge",
  1976  			"attr3": "",
  1977  		},
  1978  	})
  1979  }
  1980  
  1981  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadType(c *C) {
  1982  	y := []byte(`name: binary
  1983  version: 1.0
  1984  system-usernames:
  1985    a: true
  1986  `)
  1987  	info, err := snap.InfoFromSnapYaml(y)
  1988  	c.Assert(err, ErrorMatches, `system username "a" has malformed definition \(found bool\)`)
  1989  	c.Assert(info, IsNil)
  1990  }
  1991  
  1992  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadValue(c *C) {
  1993  	y := []byte(`name: binary
  1994  version: 1.0
  1995  system-usernames:
  1996    a: [b, c]
  1997  `)
  1998  	info, err := snap.InfoFromSnapYaml(y)
  1999  	c.Assert(err, ErrorMatches, `system username "a" has malformed definition \(found \[\]interface {}\)`)
  2000  	c.Assert(info, IsNil)
  2001  }
  2002  
  2003  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadKeyEmpty(c *C) {
  2004  	y := []byte(`name: binary
  2005  version: 1.0
  2006  system-usernames:
  2007    "": shared
  2008  `)
  2009  	_, err := snap.InfoFromSnapYaml(y)
  2010  	c.Assert(err, ErrorMatches, `system username cannot be empty`)
  2011  }
  2012  
  2013  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadKeyList(c *C) {
  2014  	y := []byte(`name: binary
  2015  version: 1.0
  2016  system-usernames:
  2017  - foo: shared
  2018  `)
  2019  	_, err := snap.InfoFromSnapYaml(y)
  2020  	c.Assert(err, ErrorMatches, `(?m)cannot parse snap.yaml:.*`)
  2021  }
  2022  
  2023  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadValueEmpty(c *C) {
  2024  	y := []byte(`name: binary
  2025  version: 1.0
  2026  system-usernames:
  2027    a: ""
  2028  `)
  2029  	_, err := snap.InfoFromSnapYaml(y)
  2030  	c.Assert(err, ErrorMatches, `system username "a" does not specify a scope`)
  2031  }
  2032  
  2033  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadValueNull(c *C) {
  2034  	y := []byte(`name: binary
  2035  version: 1.0
  2036  system-usernames:
  2037    a: null
  2038  `)
  2039  	_, err := snap.InfoFromSnapYaml(y)
  2040  	c.Assert(err, ErrorMatches, `system username "a" does not specify a scope`)
  2041  }
  2042  
  2043  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadAttrKeyEmpty(c *C) {
  2044  	y := []byte(`name: binary
  2045  version: 1.0
  2046  system-usernames:
  2047    foo:
  2048      scope: shared
  2049      "": bar
  2050  `)
  2051  	_, err := snap.InfoFromSnapYaml(y)
  2052  	c.Assert(err, ErrorMatches, `system username "foo" has an empty attribute key`)
  2053  }
  2054  
  2055  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadAttrKeyNonString(c *C) {
  2056  	y := []byte(`name: binary
  2057  version: 1.0
  2058  system-usernames:
  2059    foo:
  2060      scope: shared
  2061      1: bar
  2062  `)
  2063  	_, err := snap.InfoFromSnapYaml(y)
  2064  	c.Assert(err, ErrorMatches, `system username "foo" has attribute key that is not a string \(found int\)`)
  2065  }
  2066  
  2067  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadAttrValue(c *C) {
  2068  	y := []byte(`name: binary
  2069  version: 1.0
  2070  system-usernames:
  2071    foo:
  2072      scope: shared
  2073      bar: null
  2074  `)
  2075  	_, err := snap.InfoFromSnapYaml(y)
  2076  	c.Assert(err, ErrorMatches, `attribute "bar" of system username "foo": invalid scalar:.*`)
  2077  }
  2078  
  2079  func (s *YamlSuite) TestSnapYamlSystemUsernamesParsingBadScopeNonString(c *C) {
  2080  	y := []byte(`name: binary
  2081  version: 1.0
  2082  system-usernames:
  2083    foo:
  2084      scope: 10
  2085  `)
  2086  	_, err := snap.InfoFromSnapYaml(y)
  2087  	c.Assert(err, ErrorMatches, `scope on system username "foo" is not a string \(found int\)`)
  2088  }
  2089  
  2090  func (s *YamlSuite) TestSnapYamlLinks(c *C) {
  2091  	yLinks := []byte(`name: my-snap
  2092  version: 1.0
  2093  
  2094  links:
  2095   donations:
  2096     - https://donate.me
  2097   contact:
  2098     - mailto:me@toto.space
  2099     - https://toto.space
  2100   bug-url:
  2101     - https://github.com/webteam-space/toto.space/issues
  2102   website:
  2103     - https://toto.space
  2104   source-code:
  2105     - https://github.com/webteam-space/toto.space
  2106  `)
  2107  	info, err := snap.InfoFromSnapYaml(yLinks)
  2108  	c.Assert(err, IsNil)
  2109  	c.Check(info.Links(), DeepEquals, map[string][]string{
  2110  		"bug-url":     {"https://github.com/webteam-space/toto.space/issues"},
  2111  		"contact":     {"mailto:me@toto.space", "https://toto.space"},
  2112  		"donations":   {"https://donate.me"},
  2113  		"source-code": {"https://github.com/webteam-space/toto.space"},
  2114  		"website":     {"https://toto.space"},
  2115  	})
  2116  	c.Check(info.Contact(), Equals, "mailto:me@toto.space")
  2117  }
  2118  
  2119  func (s *YamlSuite) TestSnapYamlEmptyLinksKey(c *C) {
  2120  	yLinks := []byte(`name: my-snap
  2121  version: 1.0
  2122  
  2123  links:
  2124   "":
  2125     - htps://toto.space
  2126  `)
  2127  	_, err := snap.InfoFromSnapYaml(yLinks)
  2128  	c.Check(err, ErrorMatches, `links key cannot be empty`)
  2129  }
  2130  
  2131  func (s *YamlSuite) TestSnapYamlInvalidLinksKey(c *C) {
  2132  	yLinks := `name: my-snap
  2133  version: 1.0
  2134  
  2135  links:
  2136   %s:
  2137      - link.website
  2138  `
  2139  
  2140  	invalid := []string{
  2141  		"--",
  2142  		"1-2",
  2143  		"aa-",
  2144  	}
  2145  
  2146  	for _, k := range invalid {
  2147  		_, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(yLinks, k)))
  2148  		c.Check(err, ErrorMatches, fmt.Sprintf(`links key is invalid: %s`, k))
  2149  	}
  2150  }