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

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 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 interfaces_test
    21  
    22  import (
    23  	. "gopkg.in/check.v1"
    24  
    25  	"github.com/snapcore/snapd/interfaces"
    26  	"github.com/snapcore/snapd/interfaces/utils"
    27  	"github.com/snapcore/snapd/snap"
    28  	"github.com/snapcore/snapd/snap/snaptest"
    29  	"github.com/snapcore/snapd/testutil"
    30  )
    31  
    32  type connSuite struct {
    33  	testutil.BaseTest
    34  	plug *snap.PlugInfo
    35  	slot *snap.SlotInfo
    36  }
    37  
    38  var _ = Suite(&connSuite{})
    39  
    40  func (s *connSuite) SetUpTest(c *C) {
    41  	s.BaseTest.SetUpTest(c)
    42  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    43  	consumer := snaptest.MockInfo(c, `
    44  name: consumer
    45  version: 0
    46  apps:
    47      app:
    48  plugs:
    49      plug:
    50          interface: interface
    51          attr: value
    52          complex:
    53              c: d
    54  `, nil)
    55  	s.plug = consumer.Plugs["plug"]
    56  	producer := snaptest.MockInfo(c, `
    57  name: producer
    58  version: 0
    59  apps:
    60      app:
    61  slots:
    62      slot:
    63          interface: interface
    64          attr: value
    65          number: 100
    66          complex:
    67              a: b
    68  `, nil)
    69  	s.slot = producer.Slots["slot"]
    70  }
    71  
    72  func (s *connSuite) TearDownTest(c *C) {
    73  	s.BaseTest.TearDownTest(c)
    74  }
    75  
    76  // Make sure ConnectedPlug,ConnectedSlot, PlugInfo, SlotInfo implement Attrer.
    77  var _ interfaces.Attrer = (*interfaces.ConnectedPlug)(nil)
    78  var _ interfaces.Attrer = (*interfaces.ConnectedSlot)(nil)
    79  var _ interfaces.Attrer = (*snap.PlugInfo)(nil)
    80  var _ interfaces.Attrer = (*snap.SlotInfo)(nil)
    81  
    82  func (s *connSuite) TestStaticSlotAttrs(c *C) {
    83  	slot := interfaces.NewConnectedSlot(s.slot, nil, nil)
    84  	c.Assert(slot, NotNil)
    85  
    86  	var val string
    87  	var intVal int
    88  	c.Assert(slot.StaticAttr("unknown", &val), ErrorMatches, `snap "producer" does not have attribute "unknown" for interface "interface"`)
    89  
    90  	attrs := slot.StaticAttrs()
    91  	c.Assert(attrs, DeepEquals, map[string]interface{}{
    92  		"attr":    "value",
    93  		"number":  int64(100),
    94  		"complex": map[string]interface{}{"a": "b"},
    95  	})
    96  	slot.StaticAttr("attr", &val)
    97  	c.Assert(val, Equals, "value")
    98  
    99  	c.Assert(slot.StaticAttr("unknown", &val), ErrorMatches, `snap "producer" does not have attribute "unknown" for interface "interface"`)
   100  	c.Check(slot.StaticAttr("attr", &intVal), ErrorMatches, `snap "producer" has interface "interface" with invalid value type string for "attr" attribute: \*int`)
   101  	c.Check(slot.StaticAttr("attr", val), ErrorMatches, `internal error: cannot get "attr" attribute of interface "interface" with non-pointer value`)
   102  
   103  	// static attributes passed via args take precedence over slot.Attrs
   104  	slot2 := interfaces.NewConnectedSlot(s.slot, map[string]interface{}{"foo": "bar"}, nil)
   105  	slot2.StaticAttr("foo", &val)
   106  	c.Assert(val, Equals, "bar")
   107  }
   108  
   109  func (s *connSuite) TestSlotRef(c *C) {
   110  	slot := interfaces.NewConnectedSlot(s.slot, nil, nil)
   111  	c.Assert(slot, NotNil)
   112  	c.Assert(*slot.Ref(), DeepEquals, interfaces.SlotRef{Snap: "producer", Name: "slot"})
   113  }
   114  
   115  func (s *connSuite) TestPlugRef(c *C) {
   116  	plug := interfaces.NewConnectedPlug(s.plug, nil, nil)
   117  	c.Assert(plug, NotNil)
   118  	c.Assert(*plug.Ref(), DeepEquals, interfaces.PlugRef{Snap: "consumer", Name: "plug"})
   119  }
   120  
   121  func (s *connSuite) TestStaticPlugAttrs(c *C) {
   122  	plug := interfaces.NewConnectedPlug(s.plug, nil, nil)
   123  	c.Assert(plug, NotNil)
   124  
   125  	var val string
   126  	var intVal int
   127  	c.Assert(plug.StaticAttr("unknown", &val), ErrorMatches, `snap "consumer" does not have attribute "unknown" for interface "interface"`)
   128  
   129  	attrs := plug.StaticAttrs()
   130  	c.Assert(attrs, DeepEquals, map[string]interface{}{
   131  		"attr":    "value",
   132  		"complex": map[string]interface{}{"c": "d"},
   133  	})
   134  	plug.StaticAttr("attr", &val)
   135  	c.Assert(val, Equals, "value")
   136  
   137  	c.Assert(plug.StaticAttr("unknown", &val), ErrorMatches, `snap "consumer" does not have attribute "unknown" for interface "interface"`)
   138  	c.Check(plug.StaticAttr("attr", &intVal), ErrorMatches, `snap "consumer" has interface "interface" with invalid value type string for "attr" attribute: \*int`)
   139  	c.Check(plug.StaticAttr("attr", val), ErrorMatches, `internal error: cannot get "attr" attribute of interface "interface" with non-pointer value`)
   140  
   141  	// static attributes passed via args take precedence over plug.Attrs
   142  	plug2 := interfaces.NewConnectedPlug(s.plug, map[string]interface{}{"foo": "bar"}, nil)
   143  	plug2.StaticAttr("foo", &val)
   144  	c.Assert(val, Equals, "bar")
   145  }
   146  
   147  func (s *connSuite) TestDynamicSlotAttrs(c *C) {
   148  	attrs := map[string]interface{}{
   149  		"foo":    "bar",
   150  		"number": int(100),
   151  	}
   152  	slot := interfaces.NewConnectedSlot(s.slot, nil, attrs)
   153  	c.Assert(slot, NotNil)
   154  
   155  	var strVal string
   156  	var intVal int64
   157  	var mapVal map[string]interface{}
   158  
   159  	c.Assert(slot.Attr("foo", &strVal), IsNil)
   160  	c.Assert(strVal, Equals, "bar")
   161  
   162  	c.Assert(slot.Attr("attr", &strVal), IsNil)
   163  	c.Assert(strVal, Equals, "value")
   164  
   165  	c.Assert(slot.Attr("number", &intVal), IsNil)
   166  	c.Assert(intVal, Equals, int64(100))
   167  
   168  	err := slot.SetAttr("other", map[string]interface{}{"number-two": int(222)})
   169  	c.Assert(err, IsNil)
   170  	c.Assert(slot.Attr("other", &mapVal), IsNil)
   171  	num := mapVal["number-two"]
   172  	c.Assert(num, Equals, int64(222))
   173  
   174  	c.Check(slot.Attr("unknown", &strVal), ErrorMatches, `snap "producer" does not have attribute "unknown" for interface "interface"`)
   175  	c.Check(slot.Attr("foo", &intVal), ErrorMatches, `snap "producer" has interface "interface" with invalid value type string for "foo" attribute: \*int64`)
   176  	c.Check(slot.Attr("number", intVal), ErrorMatches, `internal error: cannot get "number" attribute of interface "interface" with non-pointer value`)
   177  }
   178  
   179  func (s *connSuite) TestDottedPathSlot(c *C) {
   180  	attrs := map[string]interface{}{
   181  		"nested": map[string]interface{}{
   182  			"foo": "bar",
   183  		},
   184  	}
   185  	var strVal string
   186  
   187  	slot := interfaces.NewConnectedSlot(s.slot, nil, attrs)
   188  	c.Assert(slot, NotNil)
   189  
   190  	// static attribute complex.a
   191  	c.Assert(slot.Attr("complex.a", &strVal), IsNil)
   192  	c.Assert(strVal, Equals, "b")
   193  
   194  	v, ok := slot.Lookup("complex.a")
   195  	c.Assert(ok, Equals, true)
   196  	c.Assert(v, Equals, "b")
   197  
   198  	// dynamic attribute nested.foo
   199  	c.Assert(slot.Attr("nested.foo", &strVal), IsNil)
   200  	c.Assert(strVal, Equals, "bar")
   201  
   202  	v, ok = slot.Lookup("nested.foo")
   203  	c.Assert(ok, Equals, true)
   204  	c.Assert(v, Equals, "bar")
   205  
   206  	_, ok = slot.Lookup("..")
   207  	c.Assert(ok, Equals, false)
   208  }
   209  
   210  func (s *connSuite) TestDottedPathPlug(c *C) {
   211  	attrs := map[string]interface{}{
   212  		"a": "b",
   213  		"nested": map[string]interface{}{
   214  			"foo": "bar",
   215  		},
   216  	}
   217  	var strVal string
   218  
   219  	plug := interfaces.NewConnectedPlug(s.plug, nil, attrs)
   220  	c.Assert(plug, NotNil)
   221  
   222  	v, ok := plug.Lookup("a")
   223  	c.Assert(ok, Equals, true)
   224  	c.Assert(v, Equals, "b")
   225  
   226  	// static attribute complex.c
   227  	c.Assert(plug.Attr("complex.c", &strVal), IsNil)
   228  	c.Assert(strVal, Equals, "d")
   229  
   230  	v, ok = plug.Lookup("complex.c")
   231  	c.Assert(ok, Equals, true)
   232  	c.Assert(v, Equals, "d")
   233  
   234  	// dynamic attribute nested.foo
   235  	c.Assert(plug.Attr("nested.foo", &strVal), IsNil)
   236  	c.Assert(strVal, Equals, "bar")
   237  
   238  	v, ok = plug.Lookup("nested.foo")
   239  	c.Assert(ok, Equals, true)
   240  	c.Assert(v, Equals, "bar")
   241  
   242  	_, ok = plug.Lookup("nested.x")
   243  	c.Assert(ok, Equals, false)
   244  
   245  	_, ok = plug.Lookup("nested.foo.y")
   246  	c.Assert(ok, Equals, false)
   247  
   248  	_, ok = plug.Lookup("..")
   249  	c.Assert(ok, Equals, false)
   250  }
   251  
   252  func (s *connSuite) TestLookupFailure(c *C) {
   253  	attrs := map[string]interface{}{}
   254  
   255  	slot := interfaces.NewConnectedSlot(s.slot, nil, attrs)
   256  	c.Assert(slot, NotNil)
   257  	plug := interfaces.NewConnectedPlug(s.plug, nil, attrs)
   258  	c.Assert(plug, NotNil)
   259  
   260  	v, ok := slot.Lookup("a")
   261  	c.Assert(ok, Equals, false)
   262  	c.Assert(v, IsNil)
   263  
   264  	v, ok = plug.Lookup("a")
   265  	c.Assert(ok, Equals, false)
   266  	c.Assert(v, IsNil)
   267  }
   268  
   269  func (s *connSuite) TestDynamicPlugAttrs(c *C) {
   270  	attrs := map[string]interface{}{
   271  		"foo":    "bar",
   272  		"number": int(100),
   273  	}
   274  	plug := interfaces.NewConnectedPlug(s.plug, nil, attrs)
   275  	c.Assert(plug, NotNil)
   276  
   277  	var strVal string
   278  	var intVal int64
   279  	var mapVal map[string]interface{}
   280  
   281  	c.Assert(plug.Attr("foo", &strVal), IsNil)
   282  	c.Assert(strVal, Equals, "bar")
   283  
   284  	c.Assert(plug.Attr("attr", &strVal), IsNil)
   285  	c.Assert(strVal, Equals, "value")
   286  
   287  	c.Assert(plug.Attr("number", &intVal), IsNil)
   288  	c.Assert(intVal, Equals, int64(100))
   289  
   290  	err := plug.SetAttr("other", map[string]interface{}{"number-two": int(222)})
   291  	c.Assert(err, IsNil)
   292  	c.Assert(plug.Attr("other", &mapVal), IsNil)
   293  	num := mapVal["number-two"]
   294  	c.Assert(num, Equals, int64(222))
   295  
   296  	c.Check(plug.Attr("unknown", &strVal), ErrorMatches, `snap "consumer" does not have attribute "unknown" for interface "interface"`)
   297  	c.Check(plug.Attr("foo", &intVal), ErrorMatches, `snap "consumer" has interface "interface" with invalid value type string for "foo" attribute: \*int64`)
   298  	c.Check(plug.Attr("number", intVal), ErrorMatches, `internal error: cannot get "number" attribute of interface "interface" with non-pointer value`)
   299  }
   300  
   301  func (s *connSuite) TestOverwriteStaticAttrError(c *C) {
   302  	attrs := map[string]interface{}{}
   303  
   304  	plug := interfaces.NewConnectedPlug(s.plug, nil, attrs)
   305  	c.Assert(plug, NotNil)
   306  	slot := interfaces.NewConnectedSlot(s.slot, nil, attrs)
   307  	c.Assert(slot, NotNil)
   308  
   309  	err := plug.SetAttr("attr", "overwrite")
   310  	c.Assert(err, NotNil)
   311  	c.Assert(err, ErrorMatches, `cannot change attribute "attr" as it was statically specified in the snap details`)
   312  
   313  	err = slot.SetAttr("attr", "overwrite")
   314  	c.Assert(err, NotNil)
   315  	c.Assert(err, ErrorMatches, `cannot change attribute "attr" as it was statically specified in the snap details`)
   316  }
   317  
   318  func (s *connSuite) TestCopyAttributes(c *C) {
   319  	orig := map[string]interface{}{
   320  		"a": "A",
   321  		"b": true,
   322  		"c": int(100),
   323  		"d": []interface{}{"x", "y", true},
   324  		"e": map[string]interface{}{"e1": "E1"},
   325  	}
   326  
   327  	cpy := utils.CopyAttributes(orig)
   328  	c.Check(cpy, DeepEquals, orig)
   329  
   330  	cpy["d"].([]interface{})[0] = 999
   331  	c.Check(orig["d"].([]interface{})[0], Equals, "x")
   332  	cpy["e"].(map[string]interface{})["e1"] = "x"
   333  	c.Check(orig["e"].(map[string]interface{})["e1"], Equals, "E1")
   334  }
   335  
   336  func (s *connSuite) TestNewConnectedPlugExplicitStaticAttrs(c *C) {
   337  	staticAttrs := map[string]interface{}{
   338  		"baz": "boom",
   339  	}
   340  	dynAttrs := map[string]interface{}{
   341  		"foo": "bar",
   342  	}
   343  	plug := interfaces.NewConnectedPlug(s.plug, staticAttrs, dynAttrs)
   344  	c.Assert(plug, NotNil)
   345  	c.Assert(plug.StaticAttrs(), DeepEquals, map[string]interface{}{"baz": "boom"})
   346  	c.Assert(plug.DynamicAttrs(), DeepEquals, map[string]interface{}{"foo": "bar"})
   347  }
   348  
   349  func (s *connSuite) TestNewConnectedSlotExplicitStaticAttrs(c *C) {
   350  	staticAttrs := map[string]interface{}{
   351  		"baz": "boom",
   352  	}
   353  	dynAttrs := map[string]interface{}{
   354  		"foo": "bar",
   355  	}
   356  	slot := interfaces.NewConnectedSlot(s.slot, staticAttrs, dynAttrs)
   357  	c.Assert(slot, NotNil)
   358  	c.Assert(slot.StaticAttrs(), DeepEquals, map[string]interface{}{"baz": "boom"})
   359  	c.Assert(slot.DynamicAttrs(), DeepEquals, map[string]interface{}{"foo": "bar"})
   360  }