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 }