github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/snap/info_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2020 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 "io/ioutil" 25 "os" 26 "path/filepath" 27 "regexp" 28 "sort" 29 "strings" 30 "testing" 31 32 . "gopkg.in/check.v1" 33 "gopkg.in/yaml.v2" 34 35 "github.com/snapcore/snapd/dirs" 36 "github.com/snapcore/snapd/osutil" 37 "github.com/snapcore/snapd/snap" 38 "github.com/snapcore/snapd/snap/snapfile" 39 "github.com/snapcore/snapd/snap/snaptest" 40 "github.com/snapcore/snapd/snap/squashfs" 41 "github.com/snapcore/snapd/testutil" 42 ) 43 44 type infoSuite struct { 45 testutil.BaseTest 46 } 47 48 type infoSimpleSuite struct{} 49 50 var _ = Suite(&infoSuite{}) 51 var _ = Suite(&infoSimpleSuite{}) 52 53 func (s *infoSimpleSuite) SetUpTest(c *C) { 54 dirs.SetRootDir(c.MkDir()) 55 } 56 57 func (s *infoSimpleSuite) TearDownTest(c *C) { 58 dirs.SetRootDir("") 59 } 60 61 func (s *infoSimpleSuite) TestReadInfoPanicsIfSanitizeUnset(c *C) { 62 si := &snap.SideInfo{Revision: snap.R(1)} 63 snaptest.MockSnap(c, sampleYaml, si) 64 c.Assert(func() { snap.ReadInfo("sample", si) }, Panics, `SanitizePlugsSlots function not set`) 65 } 66 67 func (s *infoSuite) SetUpTest(c *C) { 68 s.BaseTest.SetUpTest(c) 69 dirs.SetRootDir(c.MkDir()) 70 hookType := snap.NewHookType(regexp.MustCompile(".*")) 71 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 72 s.BaseTest.AddCleanup(snap.MockSupportedHookTypes([]*snap.HookType{hookType})) 73 } 74 75 func (s *infoSuite) TearDownTest(c *C) { 76 s.BaseTest.TearDownTest(c) 77 dirs.SetRootDir("") 78 } 79 80 func (s *infoSuite) TestSideInfoOverrides(c *C) { 81 info := &snap.Info{ 82 SuggestedName: "name", 83 OriginalSummary: "summary", 84 OriginalDescription: "desc", 85 } 86 87 info.SideInfo = snap.SideInfo{ 88 RealName: "newname", 89 EditedSummary: "fixed summary", 90 EditedDescription: "fixed desc", 91 Revision: snap.R(1), 92 SnapID: "snapidsnapidsnapidsnapidsnapidsn", 93 } 94 95 c.Check(info.InstanceName(), Equals, "newname") 96 c.Check(info.Summary(), Equals, "fixed summary") 97 c.Check(info.Description(), Equals, "fixed desc") 98 c.Check(info.Revision, Equals, snap.R(1)) 99 c.Check(info.SnapID, Equals, "snapidsnapidsnapidsnapidsnapidsn") 100 c.Check(info.ID(), Equals, "snapidsnapidsnapidsnapidsnapidsn") 101 } 102 103 func (s *infoSuite) TestAppInfoSecurityTag(c *C) { 104 appInfo := &snap.AppInfo{Snap: &snap.Info{SuggestedName: "http"}, Name: "GET"} 105 c.Check(appInfo.SecurityTag(), Equals, "snap.http.GET") 106 } 107 108 func (s *infoSuite) TestPlugSlotSecurityTags(c *C) { 109 info, err := snap.InfoFromSnapYaml([]byte(`name: name 110 apps: 111 app1: 112 app2: 113 hooks: 114 hook1: 115 plugs: 116 plug: 117 slots: 118 slot: 119 `)) 120 c.Assert(err, IsNil) 121 c.Assert(info.Plugs["plug"].SecurityTags(), DeepEquals, []string{ 122 "snap.name.app1", "snap.name.app2", "snap.name.hook.hook1"}) 123 c.Assert(info.Slots["slot"].SecurityTags(), DeepEquals, []string{ 124 "snap.name.app1", "snap.name.app2", "snap.name.hook.hook1"}) 125 } 126 127 func (s *infoSuite) TestAppInfoWrapperPath(c *C) { 128 info, err := snap.InfoFromSnapYaml([]byte(`name: foo 129 apps: 130 foo: 131 bar: 132 `)) 133 c.Assert(err, IsNil) 134 135 c.Check(info.Apps["bar"].WrapperPath(), Equals, filepath.Join(dirs.SnapBinariesDir, "foo.bar")) 136 c.Check(info.Apps["foo"].WrapperPath(), Equals, filepath.Join(dirs.SnapBinariesDir, "foo")) 137 } 138 139 func (s *infoSuite) TestAppInfoCompleterPath(c *C) { 140 info, err := snap.InfoFromSnapYaml([]byte(`name: foo 141 apps: 142 foo: 143 bar: 144 `)) 145 c.Assert(err, IsNil) 146 147 c.Check(info.Apps["bar"].CompleterPath(), Equals, filepath.Join(dirs.CompletersDir, "foo.bar")) 148 c.Check(info.Apps["foo"].CompleterPath(), Equals, filepath.Join(dirs.CompletersDir, "foo")) 149 } 150 151 func (s *infoSuite) TestAppInfoLauncherCommand(c *C) { 152 dirs.SetRootDir("") 153 154 info, err := snap.InfoFromSnapYaml([]byte(`name: foo 155 apps: 156 foo: 157 command: foo-bin 158 bar: 159 command: bar-bin -x 160 baz: 161 command: bar-bin -x 162 timer: 10:00-12:00,,mon,12:00~14:00 163 `)) 164 c.Assert(err, IsNil) 165 info.Revision = snap.R(42) 166 c.Check(info.Apps["bar"].LauncherCommand(), Equals, "/usr/bin/snap run foo.bar") 167 c.Check(info.Apps["bar"].LauncherStopCommand(), Equals, "/usr/bin/snap run --command=stop foo.bar") 168 c.Check(info.Apps["bar"].LauncherReloadCommand(), Equals, "/usr/bin/snap run --command=reload foo.bar") 169 c.Check(info.Apps["bar"].LauncherPostStopCommand(), Equals, "/usr/bin/snap run --command=post-stop foo.bar") 170 c.Check(info.Apps["foo"].LauncherCommand(), Equals, "/usr/bin/snap run foo") 171 c.Check(info.Apps["baz"].LauncherCommand(), Equals, `/usr/bin/snap run --timer="10:00-12:00,,mon,12:00~14:00" foo.baz`) 172 173 // snap with instance key 174 info.InstanceKey = "instance" 175 c.Check(info.Apps["bar"].LauncherCommand(), Equals, "/usr/bin/snap run foo_instance.bar") 176 c.Check(info.Apps["bar"].LauncherStopCommand(), Equals, "/usr/bin/snap run --command=stop foo_instance.bar") 177 c.Check(info.Apps["bar"].LauncherReloadCommand(), Equals, "/usr/bin/snap run --command=reload foo_instance.bar") 178 c.Check(info.Apps["bar"].LauncherPostStopCommand(), Equals, "/usr/bin/snap run --command=post-stop foo_instance.bar") 179 c.Check(info.Apps["foo"].LauncherCommand(), Equals, "/usr/bin/snap run foo_instance") 180 c.Check(info.Apps["baz"].LauncherCommand(), Equals, `/usr/bin/snap run --timer="10:00-12:00,,mon,12:00~14:00" foo_instance.baz`) 181 } 182 183 const sampleYaml = ` 184 name: sample 185 version: 1 186 apps: 187 app: 188 command: foo 189 app2: 190 command: bar 191 sample: 192 command: foobar 193 command-chain: [chain] 194 hooks: 195 configure: 196 command-chain: [hookchain] 197 ` 198 199 func (s *infoSuite) TestReadInfo(c *C) { 200 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 201 202 snapInfo1 := snaptest.MockSnap(c, sampleYaml, si) 203 204 snapInfo2, err := snap.ReadInfo("sample", si) 205 c.Assert(err, IsNil) 206 207 c.Check(snapInfo2.InstanceName(), Equals, "sample") 208 c.Check(snapInfo2.Revision, Equals, snap.R(42)) 209 c.Check(snapInfo2.Summary(), Equals, "esummary") 210 211 c.Check(snapInfo2.Apps["app"].Command, Equals, "foo") 212 c.Check(snapInfo2.Apps["sample"].CommandChain, DeepEquals, []string{"chain"}) 213 c.Check(snapInfo2.Hooks["configure"].CommandChain, DeepEquals, []string{"hookchain"}) 214 215 c.Check(snapInfo2, DeepEquals, snapInfo1) 216 } 217 218 func (s *infoSuite) TestReadInfoWithInstance(c *C) { 219 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "instance summary"} 220 221 snapInfo1 := snaptest.MockSnapInstance(c, "sample_instance", sampleYaml, si) 222 223 snapInfo2, err := snap.ReadInfo("sample_instance", si) 224 c.Assert(err, IsNil) 225 226 c.Check(snapInfo2.InstanceName(), Equals, "sample_instance") 227 c.Check(snapInfo2.SnapName(), Equals, "sample") 228 c.Check(snapInfo2.Revision, Equals, snap.R(42)) 229 c.Check(snapInfo2.Summary(), Equals, "instance summary") 230 231 c.Check(snapInfo2.Apps["app"].Command, Equals, "foo") 232 c.Check(snapInfo2.Apps["sample"].CommandChain, DeepEquals, []string{"chain"}) 233 c.Check(snapInfo2.Hooks["configure"].CommandChain, DeepEquals, []string{"hookchain"}) 234 235 c.Check(snapInfo2, DeepEquals, snapInfo1) 236 } 237 238 func (s *infoSuite) TestReadCurrentInfo(c *C) { 239 si := &snap.SideInfo{Revision: snap.R(42)} 240 241 snapInfo1 := snaptest.MockSnapCurrent(c, sampleYaml, si) 242 243 snapInfo2, err := snap.ReadCurrentInfo("sample") 244 c.Assert(err, IsNil) 245 246 c.Check(snapInfo2.InstanceName(), Equals, "sample") 247 c.Check(snapInfo2.Revision, Equals, snap.R(42)) 248 c.Check(snapInfo2, DeepEquals, snapInfo1) 249 250 snapInfo3, err := snap.ReadCurrentInfo("not-sample") 251 c.Check(snapInfo3, IsNil) 252 c.Assert(err, ErrorMatches, `cannot find current revision for snap not-sample:.*`) 253 } 254 255 func (s *infoSuite) TestReadCurrentInfoWithInstance(c *C) { 256 si := &snap.SideInfo{Revision: snap.R(42)} 257 258 snapInfo1 := snaptest.MockSnapInstanceCurrent(c, "sample_instance", sampleYaml, si) 259 260 snapInfo2, err := snap.ReadCurrentInfo("sample_instance") 261 c.Assert(err, IsNil) 262 263 c.Check(snapInfo2.InstanceName(), Equals, "sample_instance") 264 c.Check(snapInfo2.SnapName(), Equals, "sample") 265 c.Check(snapInfo2.Revision, Equals, snap.R(42)) 266 c.Check(snapInfo2, DeepEquals, snapInfo1) 267 268 snapInfo3, err := snap.ReadCurrentInfo("sample_other") 269 c.Check(snapInfo3, IsNil) 270 c.Assert(err, ErrorMatches, `cannot find current revision for snap sample_other:.*`) 271 } 272 273 func (s *infoSuite) TestInstallDate(c *C) { 274 si := &snap.SideInfo{Revision: snap.R(1)} 275 info := snaptest.MockSnap(c, sampleYaml, si) 276 // not current -> Zero 277 c.Check(info.InstallDate().IsZero(), Equals, true) 278 c.Check(snap.InstallDate(info.InstanceName()).IsZero(), Equals, true) 279 280 mountdir := info.MountDir() 281 dir, rev := filepath.Split(mountdir) 282 c.Assert(os.MkdirAll(dir, 0755), IsNil) 283 cur := filepath.Join(dir, "current") 284 c.Assert(os.Symlink(rev, cur), IsNil) 285 st, err := os.Lstat(cur) 286 c.Assert(err, IsNil) 287 instTime := st.ModTime() 288 // sanity 289 c.Check(instTime.IsZero(), Equals, false) 290 291 c.Check(info.InstallDate().Equal(instTime), Equals, true) 292 c.Check(snap.InstallDate(info.InstanceName()).Equal(instTime), Equals, true) 293 } 294 295 func (s *infoSuite) TestReadInfoNotFound(c *C) { 296 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 297 info, err := snap.ReadInfo("sample", si) 298 c.Check(info, IsNil) 299 c.Check(err, ErrorMatches, `cannot find installed snap "sample" at revision 42: missing file .*sample/42/meta/snap.yaml`) 300 bse, ok := err.(snap.BrokenSnapError) 301 c.Assert(ok, Equals, true) 302 c.Check(bse.Broken(), Equals, bse.Error()) 303 } 304 305 func (s *infoSuite) TestReadInfoUnreadable(c *C) { 306 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 307 c.Assert(os.MkdirAll(filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml"), 0755), IsNil) 308 309 info, err := snap.ReadInfo("sample", si) 310 c.Check(info, IsNil) 311 // TODO: maybe improve this error message 312 c.Check(err, ErrorMatches, ".* is a directory") 313 } 314 315 func (s *infoSuite) TestReadInfoUnparsable(c *C) { 316 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 317 p := filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml") 318 c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil) 319 c.Assert(ioutil.WriteFile(p, []byte(`- :`), 0644), IsNil) 320 321 info, err := snap.ReadInfo("sample", si) 322 c.Check(info, IsNil) 323 // TODO: maybe improve this error message 324 c.Check(err, ErrorMatches, `cannot use installed snap "sample" at revision 42: cannot parse snap.yaml: yaml: .*`) 325 bse, ok := err.(snap.BrokenSnapError) 326 c.Assert(ok, Equals, true) 327 c.Check(bse.Broken(), Equals, bse.Error()) 328 } 329 330 func (s *infoSuite) TestReadInfoUnfindable(c *C) { 331 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 332 p := filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml") 333 c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil) 334 c.Assert(ioutil.WriteFile(p, []byte(``), 0644), IsNil) 335 336 info, err := snap.ReadInfo("sample", si) 337 c.Check(err, ErrorMatches, `cannot find installed snap "sample" at revision 42: missing file .*var/lib/snapd/snaps/sample_42.snap`) 338 c.Check(info, IsNil) 339 } 340 341 func (s *infoSuite) TestReadInfoDanglingSymlink(c *C) { 342 si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"} 343 mpi := snap.MinimalPlaceInfo("sample", si.Revision) 344 p := filepath.Join(mpi.MountDir(), "meta", "snap.yaml") 345 c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil) 346 c.Assert(ioutil.WriteFile(p, []byte(`name: test`), 0644), IsNil) 347 c.Assert(os.MkdirAll(filepath.Dir(mpi.MountFile()), 0755), IsNil) 348 c.Assert(os.Symlink("/dangling", mpi.MountFile()), IsNil) 349 350 info, err := snap.ReadInfo("sample", si) 351 c.Check(err, IsNil) 352 c.Check(info.SnapName(), Equals, "test") 353 c.Check(info.Revision, Equals, snap.R(42)) 354 c.Check(info.Summary(), Equals, "esummary") 355 c.Check(info.Size, Equals, int64(0)) 356 } 357 358 // makeTestSnap here can also be used to produce broken snaps (differently from snaptest.MakeTestSnapWithFiles)! 359 func makeTestSnap(c *C, snapYaml string) string { 360 var m struct { 361 Type string `yaml:"type"` 362 } 363 yaml.Unmarshal([]byte(snapYaml), &m) // yes, ignore the error 364 365 tmp := c.MkDir() 366 snapSource := filepath.Join(tmp, "snapsrc") 367 368 err := os.MkdirAll(filepath.Join(snapSource, "meta"), 0755) 369 c.Assert(err, IsNil) 370 371 // our regular snap.yaml 372 err = ioutil.WriteFile(filepath.Join(snapSource, "meta", "snap.yaml"), []byte(snapYaml), 0644) 373 c.Assert(err, IsNil) 374 375 dest := filepath.Join(tmp, "foo.snap") 376 snap := squashfs.New(dest) 377 err = snap.Build(snapSource, &squashfs.BuildOpts{SnapType: m.Type}) 378 c.Assert(err, IsNil) 379 380 return dest 381 } 382 383 // produce descrs for empty hooks suitable for snaptest.PopulateDir 384 func emptyHooks(hookNames ...string) (emptyHooks [][]string) { 385 for _, hookName := range hookNames { 386 emptyHooks = append(emptyHooks, []string{filepath.Join("meta", "hooks", hookName), ""}) 387 } 388 return 389 } 390 391 func (s *infoSuite) TestReadInfoFromSnapFile(c *C) { 392 yaml := `name: foo 393 version: 1.0 394 type: app 395 epoch: 1* 396 confinement: devmode` 397 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil) 398 399 snapf, err := snapfile.Open(snapPath) 400 c.Assert(err, IsNil) 401 402 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 403 c.Assert(err, IsNil) 404 c.Check(info.InstanceName(), Equals, "foo") 405 c.Check(info.Version, Equals, "1.0") 406 c.Check(info.Type(), Equals, snap.TypeApp) 407 c.Check(info.Revision, Equals, snap.R(0)) 408 c.Check(info.Epoch.String(), Equals, "1*") 409 c.Check(info.Confinement, Equals, snap.DevModeConfinement) 410 c.Check(info.NeedsDevMode(), Equals, true) 411 c.Check(info.NeedsClassic(), Equals, false) 412 } 413 414 func (s *infoSuite) TestReadInfoFromClassicSnapFile(c *C) { 415 yaml := `name: foo 416 version: 1.0 417 type: app 418 confinement: classic` 419 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil) 420 421 snapf, err := snapfile.Open(snapPath) 422 c.Assert(err, IsNil) 423 424 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 425 c.Assert(err, IsNil) 426 c.Check(info.InstanceName(), Equals, "foo") 427 c.Check(info.Version, Equals, "1.0") 428 c.Check(info.Type(), Equals, snap.TypeApp) 429 c.Check(info.Revision, Equals, snap.R(0)) 430 c.Check(info.Confinement, Equals, snap.ClassicConfinement) 431 c.Check(info.NeedsDevMode(), Equals, false) 432 c.Check(info.NeedsClassic(), Equals, true) 433 } 434 435 func (s *infoSuite) TestReadInfoFromSnapFileMissingEpoch(c *C) { 436 yaml := `name: foo 437 version: 1.0 438 type: app` 439 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil) 440 441 snapf, err := snapfile.Open(snapPath) 442 c.Assert(err, IsNil) 443 444 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 445 c.Assert(err, IsNil) 446 c.Check(info.InstanceName(), Equals, "foo") 447 c.Check(info.Version, Equals, "1.0") 448 c.Check(info.Type(), Equals, snap.TypeApp) 449 c.Check(info.Revision, Equals, snap.R(0)) 450 c.Check(info.Epoch.String(), Equals, "0") // Defaults to 0 451 c.Check(info.Confinement, Equals, snap.StrictConfinement) 452 c.Check(info.NeedsDevMode(), Equals, false) 453 } 454 455 func (s *infoSuite) TestReadInfoFromSnapFileWithSideInfo(c *C) { 456 yaml := `name: foo 457 version: 1.0 458 type: app` 459 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil) 460 461 snapf, err := snapfile.Open(snapPath) 462 c.Assert(err, IsNil) 463 464 info, err := snap.ReadInfoFromSnapFile(snapf, &snap.SideInfo{ 465 RealName: "baz", 466 Revision: snap.R(42), 467 }) 468 c.Assert(err, IsNil) 469 c.Check(info.InstanceName(), Equals, "baz") 470 c.Check(info.Version, Equals, "1.0") 471 c.Check(info.Type(), Equals, snap.TypeApp) 472 c.Check(info.Revision, Equals, snap.R(42)) 473 } 474 475 func (s *infoSuite) TestReadInfoFromSnapFileValidates(c *C) { 476 yaml := `name: foo.bar 477 version: 1.0 478 type: app` 479 snapPath := makeTestSnap(c, yaml) 480 481 snapf, err := snapfile.Open(snapPath) 482 c.Assert(err, IsNil) 483 484 _, err = snap.ReadInfoFromSnapFile(snapf, nil) 485 c.Assert(err, ErrorMatches, `invalid snap name.*`) 486 } 487 488 func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidType(c *C) { 489 yaml := `name: foo 490 version: 1.0 491 type: foo` 492 snapPath := makeTestSnap(c, yaml) 493 494 snapf, err := snapfile.Open(snapPath) 495 c.Assert(err, IsNil) 496 497 _, err = snap.ReadInfoFromSnapFile(snapf, nil) 498 c.Assert(err, ErrorMatches, ".*invalid snap type.*") 499 } 500 501 func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidConfinement(c *C) { 502 yaml := `name: foo 503 version: 1.0 504 confinement: foo` 505 snapPath := makeTestSnap(c, yaml) 506 507 snapf, err := snapfile.Open(snapPath) 508 c.Assert(err, IsNil) 509 510 _, err = snap.ReadInfoFromSnapFile(snapf, nil) 511 c.Assert(err, ErrorMatches, ".*invalid confinement type.*") 512 } 513 514 func (s *infoSuite) TestAppEnvSimple(c *C) { 515 yaml := `name: foo 516 version: 1.0 517 type: app 518 environment: 519 global-k: global-v 520 apps: 521 foo: 522 environment: 523 app-k: app-v 524 ` 525 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 526 c.Assert(err, IsNil) 527 528 c.Check(info.Apps["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{ 529 osutil.NewExpandableEnv("global-k", "global-v"), 530 osutil.NewExpandableEnv("app-k", "app-v"), 531 }) 532 } 533 534 func (s *infoSuite) TestAppEnvOverrideGlobal(c *C) { 535 yaml := `name: foo 536 version: 1.0 537 type: app 538 environment: 539 global-k: global-v 540 global-and-local: global-v 541 apps: 542 foo: 543 environment: 544 app-k: app-v 545 global-and-local: local-v 546 ` 547 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 548 c.Assert(err, IsNil) 549 550 c.Check(info.Apps["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{ 551 osutil.NewExpandableEnv("global-k", "global-v", "global-and-local", "global-v"), 552 osutil.NewExpandableEnv("app-k", "app-v", "global-and-local", "local-v"), 553 }) 554 } 555 556 func (s *infoSuite) TestHookEnvSimple(c *C) { 557 yaml := `name: foo 558 version: 1.0 559 type: app 560 environment: 561 global-k: global-v 562 hooks: 563 foo: 564 environment: 565 app-k: app-v 566 ` 567 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 568 c.Assert(err, IsNil) 569 570 c.Check(info.Hooks["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{ 571 osutil.NewExpandableEnv("global-k", "global-v"), 572 osutil.NewExpandableEnv("app-k", "app-v"), 573 }) 574 } 575 576 func (s *infoSuite) TestHookEnvOverrideGlobal(c *C) { 577 yaml := `name: foo 578 version: 1.0 579 type: app 580 environment: 581 global-k: global-v 582 global-and-local: global-v 583 hooks: 584 foo: 585 environment: 586 app-k: app-v 587 global-and-local: local-v 588 ` 589 info, err := snap.InfoFromSnapYaml([]byte(yaml)) 590 c.Assert(err, IsNil) 591 592 c.Check(info.Hooks["foo"].EnvChain(), DeepEquals, []osutil.ExpandableEnv{ 593 osutil.NewExpandableEnv("global-k", "global-v", "global-and-local", "global-v"), 594 osutil.NewExpandableEnv("app-k", "app-v", "global-and-local", "local-v"), 595 }) 596 } 597 598 func (s *infoSuite) TestSplitSnapApp(c *C) { 599 for _, t := range []struct { 600 in string 601 out []string 602 }{ 603 // normal cases 604 {"foo.bar", []string{"foo", "bar"}}, 605 {"foo.bar.baz", []string{"foo", "bar.baz"}}, 606 // special case, snapName == appName 607 {"foo", []string{"foo", "foo"}}, 608 // snap instance names 609 {"foo_instance.bar", []string{"foo_instance", "bar"}}, 610 {"foo_instance.bar.baz", []string{"foo_instance", "bar.baz"}}, 611 {"foo_instance", []string{"foo_instance", "foo"}}, 612 } { 613 snap, app := snap.SplitSnapApp(t.in) 614 c.Check([]string{snap, app}, DeepEquals, t.out) 615 } 616 } 617 618 func (s *infoSuite) TestJoinSnapApp(c *C) { 619 for _, t := range []struct { 620 in []string 621 out string 622 }{ 623 // normal cases 624 {[]string{"foo", "bar"}, "foo.bar"}, 625 {[]string{"foo", "bar-baz"}, "foo.bar-baz"}, 626 // special case, snapName == appName 627 {[]string{"foo", "foo"}, "foo"}, 628 // snap instance names 629 {[]string{"foo_instance", "bar"}, "foo_instance.bar"}, 630 {[]string{"foo_instance", "bar-baz"}, "foo_instance.bar-baz"}, 631 {[]string{"foo_instance", "foo"}, "foo_instance"}, 632 } { 633 snapApp := snap.JoinSnapApp(t.in[0], t.in[1]) 634 c.Check(snapApp, Equals, t.out) 635 } 636 } 637 638 func ExampleSplitSnapApp() { 639 fmt.Println(snap.SplitSnapApp("hello-world.env")) 640 // Output: hello-world env 641 } 642 643 func ExampleSplitSnapApp_short() { 644 fmt.Println(snap.SplitSnapApp("hello-world")) 645 // Output: hello-world hello-world 646 } 647 648 func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidHook(c *C) { 649 yaml := `name: foo 650 version: 1.0 651 hooks: 652 123abc:` 653 snapPath := makeTestSnap(c, yaml) 654 655 snapf, err := snapfile.Open(snapPath) 656 c.Assert(err, IsNil) 657 658 _, err = snap.ReadInfoFromSnapFile(snapf, nil) 659 c.Assert(err, ErrorMatches, ".*invalid hook name.*") 660 } 661 662 func (s *infoSuite) TestReadInfoFromSnapFileCatchesInvalidImplicitHook(c *C) { 663 yaml := `name: foo 664 version: 1.0` 665 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, emptyHooks("123abc")) 666 667 snapf, err := snapfile.Open(snapPath) 668 c.Assert(err, IsNil) 669 670 _, err = snap.ReadInfoFromSnapFile(snapf, nil) 671 c.Assert(err, ErrorMatches, ".*invalid hook name.*") 672 } 673 674 func (s *infoSuite) checkInstalledSnapAndSnapFile(c *C, instanceName, yaml string, contents string, hooks []string, checker func(c *C, info *snap.Info)) { 675 // First check installed snap 676 sideInfo := &snap.SideInfo{Revision: snap.R(42)} 677 info0 := snaptest.MockSnapInstance(c, instanceName, yaml, sideInfo) 678 snaptest.PopulateDir(info0.MountDir(), emptyHooks(hooks...)) 679 info, err := snap.ReadInfo(info0.InstanceName(), sideInfo) 680 c.Check(err, IsNil) 681 checker(c, info) 682 683 // Now check snap file 684 snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, emptyHooks(hooks...)) 685 snapf, err := snapfile.Open(snapPath) 686 c.Assert(err, IsNil) 687 info, err = snap.ReadInfoFromSnapFile(snapf, nil) 688 c.Check(err, IsNil) 689 checker(c, info) 690 } 691 692 func (s *infoSuite) TestReadInfoNoHooks(c *C) { 693 yaml := `name: foo 694 version: 1.0` 695 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", nil, func(c *C, info *snap.Info) { 696 // Verify that no hooks were loaded for this snap 697 c.Check(info.Hooks, HasLen, 0) 698 }) 699 } 700 701 func (s *infoSuite) TestReadInfoSingleImplicitHook(c *C) { 702 yaml := `name: foo 703 version: 1.0` 704 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"test-hook"}, func(c *C, info *snap.Info) { 705 // Verify that the `test-hook` hook has now been loaded, and that it has 706 // no associated plugs. 707 c.Check(info.Hooks, HasLen, 1) 708 verifyImplicitHook(c, info, "test-hook", nil) 709 }) 710 } 711 712 func (s *infoSuite) TestReadInfoMultipleImplicitHooks(c *C) { 713 yaml := `name: foo 714 version: 1.0` 715 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"foo", "bar"}, func(c *C, info *snap.Info) { 716 // Verify that both hooks have now been loaded, and that neither have any 717 // associated plugs. 718 c.Check(info.Hooks, HasLen, 2) 719 verifyImplicitHook(c, info, "foo", nil) 720 verifyImplicitHook(c, info, "bar", nil) 721 }) 722 } 723 724 func (s *infoSuite) TestReadInfoInvalidImplicitHook(c *C) { 725 hookType := snap.NewHookType(regexp.MustCompile("foo")) 726 s.BaseTest.AddCleanup(snap.MockSupportedHookTypes([]*snap.HookType{hookType})) 727 728 yaml := `name: foo 729 version: 1.0` 730 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"foo", "bar"}, func(c *C, info *snap.Info) { 731 // Verify that only foo has been loaded, not bar 732 c.Check(info.Hooks, HasLen, 1) 733 verifyImplicitHook(c, info, "foo", nil) 734 }) 735 } 736 737 func (s *infoSuite) TestReadInfoImplicitAndExplicitHooks(c *C) { 738 yaml := `name: foo 739 version: 1.0 740 hooks: 741 explicit: 742 plugs: [test-plug] 743 slots: [test-slot]` 744 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"explicit", "implicit"}, func(c *C, info *snap.Info) { 745 // Verify that the `implicit` hook has now been loaded, and that it has 746 // no associated plugs. Also verify that the `explicit` hook is still 747 // valid. 748 c.Check(info.Hooks, HasLen, 2) 749 verifyImplicitHook(c, info, "implicit", nil) 750 verifyExplicitHook(c, info, "explicit", []string{"test-plug"}, []string{"test-slot"}) 751 }) 752 } 753 754 func (s *infoSuite) TestReadInfoExplicitHooks(c *C) { 755 yaml := `name: foo 756 version: 1.0 757 plugs: 758 test-plug: 759 slots: 760 test-slot: 761 hooks: 762 explicit: 763 ` 764 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"explicit"}, func(c *C, info *snap.Info) { 765 c.Check(info.Hooks, HasLen, 1) 766 verifyExplicitHook(c, info, "explicit", []string{"test-plug"}, []string{"test-slot"}) 767 }) 768 } 769 770 func (s *infoSuite) TestReadInfoImplicitHookPlugWhenImplicitlyBoundToApp(c *C) { 771 yaml := `name: foo 772 version: 1.0 773 plugs: 774 test-plug: 775 apps: 776 app: 777 ` 778 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) { 779 c.Check(info.Hooks, HasLen, 1) 780 verifyImplicitHook(c, info, "implicit", []string{"test-plug"}) 781 }) 782 } 783 784 func (s *infoSuite) TestReadInfoImplicitHookPlugWhenExplicitlyBoundToApp(c *C) { 785 yaml := `name: foo 786 version: 1.0 787 plugs: 788 test-plug: 789 apps: 790 app: 791 plugs: [test-plug] 792 ` 793 s.checkInstalledSnapAndSnapFile(c, "foo", yaml, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) { 794 c.Check(info.Hooks, HasLen, 1) 795 verifyImplicitHook(c, info, "implicit", nil) 796 }) 797 } 798 799 func (s *infoSuite) TestParallelInstanceReadInfoImplicitAndExplicitHooks(c *C) { 800 yaml := `name: foo 801 version: 1.0 802 hooks: 803 explicit: 804 plugs: [test-plug] 805 slots: [test-slot]` 806 s.checkInstalledSnapAndSnapFile(c, "foo_instance", yaml, "SNAP", []string{"explicit", "implicit"}, func(c *C, info *snap.Info) { 807 c.Check(info.Hooks, HasLen, 2) 808 verifyImplicitHook(c, info, "implicit", nil) 809 verifyExplicitHook(c, info, "explicit", []string{"test-plug"}, []string{"test-slot"}) 810 }) 811 } 812 813 func (s *infoSuite) TestReadInfoImplicitHookWithTopLevelPlugSlots(c *C) { 814 yaml1 := `name: snap-1 815 version: 1.0 816 plugs: 817 test-plug: 818 slots: 819 test-slot: 820 hooks: 821 explicit: 822 plugs: [test-plug,other-plug] 823 slots: [test-slot,other-slot] 824 ` 825 s.checkInstalledSnapAndSnapFile(c, "snap-1", yaml1, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) { 826 c.Check(info.Hooks, HasLen, 2) 827 implicitHook := info.Hooks["implicit"] 828 c.Assert(implicitHook, NotNil) 829 c.Assert(implicitHook.Explicit, Equals, false) 830 c.Assert(implicitHook.Plugs, HasLen, 0) 831 c.Assert(implicitHook.Slots, HasLen, 0) 832 833 c.Check(info.Plugs, HasLen, 2) 834 c.Check(info.Slots, HasLen, 2) 835 836 plug := info.Plugs["test-plug"] 837 c.Assert(plug, NotNil) 838 // implicit hook has not gained test-plug because it was already 839 // associated with an app or a hook (here with the hook called 840 // "explicit"). This is consistent with the hook called "implicit" 841 // having been defined in the YAML but devoid of any interface 842 // assignments. 843 c.Assert(implicitHook.Plugs["test-plug"], IsNil) 844 845 slot := info.Slots["test-slot"] 846 c.Assert(slot, NotNil) 847 c.Assert(implicitHook.Slots["test-slot"], IsNil) 848 849 explicitHook := info.Hooks["explicit"] 850 c.Assert(explicitHook, NotNil) 851 c.Assert(explicitHook.Explicit, Equals, true) 852 c.Assert(explicitHook.Plugs, HasLen, 2) 853 c.Assert(explicitHook.Slots, HasLen, 2) 854 855 plug = info.Plugs["test-plug"] 856 c.Assert(plug, NotNil) 857 c.Assert(explicitHook.Plugs["test-plug"], DeepEquals, plug) 858 859 slot = info.Slots["test-slot"] 860 c.Assert(slot, NotNil) 861 c.Assert(explicitHook.Slots["test-slot"], DeepEquals, slot) 862 }) 863 864 yaml2 := `name: snap-2 865 version: 1.0 866 plugs: 867 test-plug: 868 slots: 869 test-slot: 870 ` 871 s.checkInstalledSnapAndSnapFile(c, "snap-2", yaml2, "SNAP", []string{"implicit"}, func(c *C, info *snap.Info) { 872 c.Check(info.Hooks, HasLen, 1) 873 implicitHook := info.Hooks["implicit"] 874 c.Assert(implicitHook, NotNil) 875 c.Assert(implicitHook.Explicit, Equals, false) 876 c.Assert(implicitHook.Plugs, HasLen, 1) 877 c.Assert(implicitHook.Slots, HasLen, 1) 878 879 c.Check(info.Plugs, HasLen, 1) 880 c.Check(info.Slots, HasLen, 1) 881 882 plug := info.Plugs["test-plug"] 883 c.Assert(plug, NotNil) 884 c.Assert(implicitHook.Plugs["test-plug"], DeepEquals, plug) 885 886 slot := info.Slots["test-slot"] 887 c.Assert(slot, NotNil) 888 c.Assert(implicitHook.Slots["test-slot"], DeepEquals, slot) 889 }) 890 891 yaml3 := `name: snap-3 892 version: 1.0 893 plugs: 894 test-plug: 895 slots: 896 test-slot: 897 ` 898 s.checkInstalledSnapAndSnapFile(c, "snap-3", yaml3, "SNAP", []string{"implicit-1", "implicit-2"}, func(c *C, info *snap.Info) { 899 c.Check(info.Hooks, HasLen, 2) 900 implicit1Hook := info.Hooks["implicit-1"] 901 c.Assert(implicit1Hook, NotNil) 902 c.Assert(implicit1Hook.Explicit, Equals, false) 903 c.Assert(implicit1Hook.Plugs, HasLen, 1) 904 c.Assert(implicit1Hook.Slots, HasLen, 1) 905 906 implicit2Hook := info.Hooks["implicit-2"] 907 c.Assert(implicit2Hook, NotNil) 908 c.Assert(implicit2Hook.Explicit, Equals, false) 909 c.Assert(implicit2Hook.Plugs, HasLen, 1) 910 c.Assert(implicit2Hook.Slots, HasLen, 1) 911 912 c.Check(info.Plugs, HasLen, 1) 913 c.Check(info.Slots, HasLen, 1) 914 915 plug := info.Plugs["test-plug"] 916 c.Assert(plug, NotNil) 917 c.Assert(implicit1Hook.Plugs["test-plug"], DeepEquals, plug) 918 c.Assert(implicit2Hook.Plugs["test-plug"], DeepEquals, plug) 919 920 slot := info.Slots["test-slot"] 921 c.Assert(slot, NotNil) 922 c.Assert(implicit1Hook.Slots["test-slot"], DeepEquals, slot) 923 c.Assert(implicit2Hook.Slots["test-slot"], DeepEquals, slot) 924 }) 925 926 } 927 928 func verifyImplicitHook(c *C, info *snap.Info, hookName string, plugNames []string) { 929 hook := info.Hooks[hookName] 930 c.Assert(hook, NotNil, Commentf("Expected hooks to contain %q", hookName)) 931 c.Check(hook.Name, Equals, hookName) 932 933 if len(plugNames) == 0 { 934 c.Check(hook.Plugs, IsNil) 935 } 936 937 for _, plugName := range plugNames { 938 // Verify that the HookInfo and PlugInfo point to each other 939 plug := hook.Plugs[plugName] 940 c.Assert(plug, NotNil, Commentf("Expected hook plugs to contain %q", plugName)) 941 c.Check(plug.Name, Equals, plugName) 942 c.Check(plug.Hooks, HasLen, 1) 943 hook = plug.Hooks[hookName] 944 c.Assert(hook, NotNil, Commentf("Expected plug to be associated with hook %q", hookName)) 945 c.Check(hook.Name, Equals, hookName) 946 947 // Verify also that the hook plug made it into info.Plugs 948 c.Check(info.Plugs[plugName], DeepEquals, plug) 949 } 950 } 951 952 func verifyExplicitHook(c *C, info *snap.Info, hookName string, plugNames []string, slotNames []string) { 953 hook := info.Hooks[hookName] 954 c.Assert(hook, NotNil, Commentf("Expected hooks to contain %q", hookName)) 955 c.Check(hook.Name, Equals, hookName) 956 c.Check(hook.Plugs, HasLen, len(plugNames)) 957 c.Check(hook.Slots, HasLen, len(slotNames)) 958 959 for _, plugName := range plugNames { 960 // Verify that the HookInfo and PlugInfo point to each other 961 plug := hook.Plugs[plugName] 962 c.Assert(plug, NotNil, Commentf("Expected hook plugs to contain %q", plugName)) 963 c.Check(plug.Name, Equals, plugName) 964 c.Check(plug.Hooks, HasLen, 1) 965 hook = plug.Hooks[hookName] 966 c.Assert(hook, NotNil, Commentf("Expected plug to be associated with hook %q", hookName)) 967 c.Check(hook.Name, Equals, hookName) 968 969 // Verify also that the hook plug made it into info.Plugs 970 c.Check(info.Plugs[plugName], DeepEquals, plug) 971 } 972 973 for _, slotName := range slotNames { 974 // Verify that the HookInfo and SlotInfo point to each other 975 slot := hook.Slots[slotName] 976 c.Assert(slot, NotNil, Commentf("Expected hook slots to contain %q", slotName)) 977 c.Check(slot.Name, Equals, slotName) 978 c.Check(slot.Hooks, HasLen, 1) 979 hook = slot.Hooks[hookName] 980 c.Assert(hook, NotNil, Commentf("Expected slot to be associated with hook %q", hookName)) 981 c.Check(hook.Name, Equals, hookName) 982 983 // Verify also that the hook plug made it into info.Slots 984 c.Check(info.Slots[slotName], DeepEquals, slot) 985 } 986 987 } 988 989 func (s *infoSuite) TestPlaceInfoRevision(c *C) { 990 info := snap.MinimalPlaceInfo("name", snap.R("1")) 991 c.Check(info.SnapRevision(), Equals, snap.R("1")) 992 } 993 994 func (s *infoSuite) TestMinimalInfoDirAndFileMethods(c *C) { 995 dirs.SetRootDir("") 996 info := snap.MinimalPlaceInfo("name", snap.R("1")) 997 s.testDirAndFileMethods(c, info) 998 } 999 1000 func (s *infoSuite) TestDirAndFileMethods(c *C) { 1001 dirs.SetRootDir("") 1002 info := &snap.Info{SuggestedName: "name"} 1003 info.SideInfo = snap.SideInfo{Revision: snap.R(1)} 1004 s.testDirAndFileMethods(c, info) 1005 } 1006 1007 func (s *infoSuite) testDirAndFileMethods(c *C, info snap.PlaceInfo) { 1008 c.Check(info.MountDir(), Equals, fmt.Sprintf("%s/name/1", dirs.SnapMountDir)) 1009 c.Check(info.MountFile(), Equals, "/var/lib/snapd/snaps/name_1.snap") 1010 c.Check(info.HooksDir(), Equals, fmt.Sprintf("%s/name/1/meta/hooks", dirs.SnapMountDir)) 1011 c.Check(info.DataDir(), Equals, "/var/snap/name/1") 1012 c.Check(info.UserDataDir("/home/bob"), Equals, "/home/bob/snap/name/1") 1013 c.Check(info.UserCommonDataDir("/home/bob"), Equals, "/home/bob/snap/name/common") 1014 c.Check(info.CommonDataDir(), Equals, "/var/snap/name/common") 1015 c.Check(info.UserXdgRuntimeDir(12345), Equals, "/run/user/12345/snap.name") 1016 // XXX: Those are actually a globs, not directories 1017 c.Check(info.DataHomeDir(), Equals, "/home/*/snap/name/1") 1018 c.Check(info.CommonDataHomeDir(), Equals, "/home/*/snap/name/common") 1019 c.Check(info.XdgRuntimeDirs(), Equals, "/run/user/*/snap.name") 1020 } 1021 1022 func (s *infoSuite) TestMinimalInfoDirAndFileMethodsParallelInstall(c *C) { 1023 dirs.SetRootDir("") 1024 info := snap.MinimalPlaceInfo("name_instance", snap.R("1")) 1025 s.testInstanceDirAndFileMethods(c, info) 1026 } 1027 1028 func (s *infoSuite) TestDirAndFileMethodsParallelInstall(c *C) { 1029 dirs.SetRootDir("") 1030 info := &snap.Info{SuggestedName: "name", InstanceKey: "instance"} 1031 info.SideInfo = snap.SideInfo{Revision: snap.R(1)} 1032 s.testInstanceDirAndFileMethods(c, info) 1033 } 1034 1035 func (s *infoSuite) testInstanceDirAndFileMethods(c *C, info snap.PlaceInfo) { 1036 c.Check(info.MountDir(), Equals, fmt.Sprintf("%s/name_instance/1", dirs.SnapMountDir)) 1037 c.Check(info.MountFile(), Equals, "/var/lib/snapd/snaps/name_instance_1.snap") 1038 c.Check(info.HooksDir(), Equals, fmt.Sprintf("%s/name_instance/1/meta/hooks", dirs.SnapMountDir)) 1039 c.Check(info.DataDir(), Equals, "/var/snap/name_instance/1") 1040 c.Check(info.UserDataDir("/home/bob"), Equals, "/home/bob/snap/name_instance/1") 1041 c.Check(info.UserCommonDataDir("/home/bob"), Equals, "/home/bob/snap/name_instance/common") 1042 c.Check(info.CommonDataDir(), Equals, "/var/snap/name_instance/common") 1043 c.Check(info.UserXdgRuntimeDir(12345), Equals, "/run/user/12345/snap.name_instance") 1044 // XXX: Those are actually a globs, not directories 1045 c.Check(info.DataHomeDir(), Equals, "/home/*/snap/name_instance/1") 1046 c.Check(info.CommonDataHomeDir(), Equals, "/home/*/snap/name_instance/common") 1047 c.Check(info.XdgRuntimeDirs(), Equals, "/run/user/*/snap.name_instance") 1048 } 1049 1050 func BenchmarkTestParsePlaceInfoFromSnapFileName(b *testing.B) { 1051 for n := 0; n < b.N; n++ { 1052 for _, sn := range []string{ 1053 "core_21.snap", 1054 "kernel_41.snap", 1055 "some-long-kernel-name-kernel_82.snap", 1056 "what-is-this-core_111.snap", 1057 } { 1058 snap.ParsePlaceInfoFromSnapFileName(sn) 1059 } 1060 } 1061 } 1062 1063 func (s *infoSuite) TestParsePlaceInfoFromSnapFileName(c *C) { 1064 tt := []struct { 1065 sn string 1066 name string 1067 rev string 1068 expectErr string 1069 }{ 1070 {sn: "", expectErr: "empty snap file name"}, 1071 {sn: "name", expectErr: `snap file name "name" has invalid format \(missing '_'\)`}, 1072 {sn: "name_", expectErr: `cannot parse revision in snap file name "name_": invalid snap revision: ""`}, 1073 {sn: "name__", expectErr: "too many '_' in snap file name"}, 1074 {sn: "_name.snap", expectErr: `snap file name \"_name.snap\" has invalid format \(no snap name before '_'\)`}, 1075 {sn: "name_key.snap", expectErr: `cannot parse revision in snap file name "name_key.snap": invalid snap revision: "key"`}, 1076 {sn: "name.snap", expectErr: `snap file name "name.snap" has invalid format \(missing '_'\)`}, 1077 {sn: "name_12.snap", name: "name", rev: "12"}, 1078 {sn: "name_key_12.snap", expectErr: "too many '_' in snap file name"}, 1079 } 1080 for _, t := range tt { 1081 p, err := snap.ParsePlaceInfoFromSnapFileName(t.sn) 1082 if t.expectErr != "" { 1083 c.Check(err, ErrorMatches, t.expectErr) 1084 } else { 1085 c.Check(p.SnapName(), Equals, t.name) 1086 c.Check(p.SnapRevision(), Equals, snap.R(t.rev)) 1087 } 1088 } 1089 } 1090 1091 func makeFakeDesktopFile(c *C, name, content string) string { 1092 df := filepath.Join(dirs.SnapDesktopFilesDir, name) 1093 err := os.MkdirAll(filepath.Dir(df), 0755) 1094 c.Assert(err, IsNil) 1095 err = ioutil.WriteFile(df, []byte(content), 0644) 1096 c.Assert(err, IsNil) 1097 return df 1098 } 1099 1100 func (s *infoSuite) TestAppDesktopFile(c *C) { 1101 snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{}) 1102 snapInfo, err := snap.ReadInfo("sample", &snap.SideInfo{}) 1103 c.Assert(err, IsNil) 1104 1105 c.Check(snapInfo.InstanceName(), Equals, "sample") 1106 c.Check(snapInfo.Apps["app"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample_app.desktop`) 1107 c.Check(snapInfo.Apps["sample"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample_sample.desktop`) 1108 c.Check(snapInfo.DesktopPrefix(), Equals, "sample") 1109 1110 // snap with instance key 1111 snapInfo.InstanceKey = "instance" 1112 c.Check(snapInfo.InstanceName(), Equals, "sample_instance") 1113 c.Check(snapInfo.Apps["app"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample\+instance_app.desktop`) 1114 c.Check(snapInfo.Apps["sample"].DesktopFile(), Matches, `.*/var/lib/snapd/desktop/applications/sample\+instance_sample.desktop`) 1115 c.Check(snapInfo.DesktopPrefix(), Equals, "sample+instance") 1116 } 1117 1118 const coreSnapYaml = `name: core 1119 version: 0 1120 type: os 1121 plugs: 1122 network-bind: 1123 core-support: 1124 ` 1125 1126 // reading snap via ReadInfoFromSnapFile renames clashing core plugs 1127 func (s *infoSuite) TestReadInfoFromSnapFileRenamesCorePlus(c *C) { 1128 snapPath := snaptest.MakeTestSnapWithFiles(c, coreSnapYaml, nil) 1129 1130 snapf, err := snapfile.Open(snapPath) 1131 c.Assert(err, IsNil) 1132 1133 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 1134 c.Assert(err, IsNil) 1135 c.Check(info.Plugs["network-bind"], IsNil) 1136 c.Check(info.Plugs["core-support"], IsNil) 1137 c.Check(info.Plugs["network-bind-plug"], NotNil) 1138 c.Check(info.Plugs["core-support-plug"], NotNil) 1139 } 1140 1141 // reading snap via ReadInfo renames clashing core plugs 1142 func (s *infoSuite) TestReadInfoRenamesCorePlugs(c *C) { 1143 si := &snap.SideInfo{Revision: snap.R(42), RealName: "core"} 1144 snaptest.MockSnap(c, coreSnapYaml, si) 1145 info, err := snap.ReadInfo("core", si) 1146 c.Assert(err, IsNil) 1147 c.Check(info.Plugs["network-bind"], IsNil) 1148 c.Check(info.Plugs["core-support"], IsNil) 1149 c.Check(info.Plugs["network-bind-plug"], NotNil) 1150 c.Check(info.Plugs["core-support-plug"], NotNil) 1151 } 1152 1153 // reading snap via InfoFromSnapYaml renames clashing core plugs 1154 func (s *infoSuite) TestInfoFromSnapYamlRenamesCorePlugs(c *C) { 1155 info, err := snap.InfoFromSnapYaml([]byte(coreSnapYaml)) 1156 c.Assert(err, IsNil) 1157 c.Check(info.Plugs["network-bind"], IsNil) 1158 c.Check(info.Plugs["core-support"], IsNil) 1159 c.Check(info.Plugs["network-bind-plug"], NotNil) 1160 c.Check(info.Plugs["core-support-plug"], NotNil) 1161 } 1162 1163 func (s *infoSuite) TestInfoServices(c *C) { 1164 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1165 apps: 1166 svc1: 1167 daemon: potato 1168 svc2: 1169 daemon: no 1170 app1: 1171 app2: 1172 `)) 1173 c.Assert(err, IsNil) 1174 svcNames := []string{} 1175 svcs := info.Services() 1176 for i := range svcs { 1177 svcNames = append(svcNames, svcs[i].ServiceName()) 1178 } 1179 sort.Strings(svcNames) 1180 c.Check(svcNames, DeepEquals, []string{ 1181 "snap.pans.svc1.service", 1182 "snap.pans.svc2.service", 1183 }) 1184 1185 // snap with instance 1186 info.InstanceKey = "instance" 1187 svcNames = []string{} 1188 for i := range info.Services() { 1189 svcNames = append(svcNames, svcs[i].ServiceName()) 1190 } 1191 sort.Strings(svcNames) 1192 c.Check(svcNames, DeepEquals, []string{ 1193 "snap.pans_instance.svc1.service", 1194 "snap.pans_instance.svc2.service", 1195 }) 1196 } 1197 1198 func (s *infoSuite) TestAppInfoIsService(c *C) { 1199 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1200 apps: 1201 svc1: 1202 daemon: potato 1203 svc2: 1204 daemon: no 1205 svc3: 1206 daemon: simple 1207 daemon-scope: user 1208 app1: 1209 app2: 1210 `)) 1211 c.Assert(err, IsNil) 1212 1213 svc := info.Apps["svc1"] 1214 c.Check(svc.IsService(), Equals, true) 1215 c.Check(svc.DaemonScope, Equals, snap.SystemDaemon) 1216 c.Check(svc.ServiceName(), Equals, "snap.pans.svc1.service") 1217 c.Check(svc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.svc1.service") 1218 1219 c.Check(info.Apps["svc2"].IsService(), Equals, true) 1220 userSvc := info.Apps["svc3"] 1221 c.Check(userSvc.IsService(), Equals, true) 1222 c.Check(userSvc.DaemonScope, Equals, snap.UserDaemon) 1223 c.Check(userSvc.ServiceName(), Equals, "snap.pans.svc3.service") 1224 c.Check(userSvc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/user/snap.pans.svc3.service") 1225 c.Check(info.Apps["app1"].IsService(), Equals, false) 1226 c.Check(info.Apps["app1"].IsService(), Equals, false) 1227 1228 // snap with instance key 1229 info.InstanceKey = "instance" 1230 c.Check(svc.ServiceName(), Equals, "snap.pans_instance.svc1.service") 1231 c.Check(svc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.svc1.service") 1232 c.Check(userSvc.ServiceName(), Equals, "snap.pans_instance.svc3.service") 1233 c.Check(userSvc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/user/snap.pans_instance.svc3.service") 1234 } 1235 1236 func (s *infoSuite) TestAppInfoStringer(c *C) { 1237 info, err := snap.InfoFromSnapYaml([]byte(`name: asnap 1238 apps: 1239 one: 1240 daemon: simple 1241 `)) 1242 c.Assert(err, IsNil) 1243 c.Check(fmt.Sprintf("%q", info.Apps["one"].String()), Equals, `"asnap.one"`) 1244 } 1245 1246 func (s *infoSuite) TestSocketFile(c *C) { 1247 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1248 apps: 1249 app1: 1250 daemon: true 1251 sockets: 1252 sock1: 1253 listen-stream: /tmp/sock1.socket 1254 `)) 1255 1256 c.Assert(err, IsNil) 1257 1258 app := info.Apps["app1"] 1259 socket := app.Sockets["sock1"] 1260 c.Check(socket.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.app1.sock1.socket") 1261 1262 // snap with instance key 1263 info.InstanceKey = "instance" 1264 c.Check(socket.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.app1.sock1.socket") 1265 } 1266 1267 func (s *infoSuite) TestTimerFile(c *C) { 1268 info, err := snap.InfoFromSnapYaml([]byte(`name: pans 1269 apps: 1270 app1: 1271 daemon: true 1272 timer: mon,10:00-12:00 1273 `)) 1274 1275 c.Assert(err, IsNil) 1276 1277 app := info.Apps["app1"] 1278 timerFile := app.Timer.File() 1279 c.Check(timerFile, Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.app1.timer") 1280 c.Check(strings.TrimSuffix(app.ServiceFile(), ".service")+".timer", Equals, timerFile) 1281 1282 // snap with instance key 1283 info.InstanceKey = "instance" 1284 c.Check(app.Timer.File(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans_instance.app1.timer") 1285 } 1286 1287 func (s *infoSuite) TestLayoutParsing(c *C) { 1288 info, err := snap.InfoFromSnapYaml([]byte(`name: layout-demo 1289 layout: 1290 /usr: 1291 bind: $SNAP/usr 1292 /mytmp: 1293 type: tmpfs 1294 mode: 1777 1295 /mylink: 1296 symlink: /link/target 1297 `)) 1298 c.Assert(err, IsNil) 1299 1300 layout := info.Layout 1301 c.Assert(layout, NotNil) 1302 c.Check(layout["/usr"], DeepEquals, &snap.Layout{ 1303 Snap: info, 1304 Path: "/usr", 1305 User: "root", 1306 Group: "root", 1307 Mode: 0755, 1308 Bind: "$SNAP/usr", 1309 }) 1310 c.Check(layout["/mytmp"], DeepEquals, &snap.Layout{ 1311 Snap: info, 1312 Path: "/mytmp", 1313 Type: "tmpfs", 1314 User: "root", 1315 Group: "root", 1316 Mode: 01777, 1317 }) 1318 c.Check(layout["/mylink"], DeepEquals, &snap.Layout{ 1319 Snap: info, 1320 Path: "/mylink", 1321 User: "root", 1322 Group: "root", 1323 Mode: 0755, 1324 Symlink: "/link/target", 1325 }) 1326 } 1327 1328 func (s *infoSuite) TestPlugInfoString(c *C) { 1329 plug := &snap.PlugInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug"} 1330 c.Assert(plug.String(), Equals, "snap:plug") 1331 } 1332 1333 func (s *infoSuite) TestSlotInfoString(c *C) { 1334 slot := &snap.SlotInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "slot"} 1335 c.Assert(slot.String(), Equals, "snap:slot") 1336 } 1337 1338 func (s *infoSuite) TestPlugInfoAttr(c *C) { 1339 var val string 1340 var intVal int 1341 1342 plug := &snap.PlugInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug", Interface: "interface", Attrs: map[string]interface{}{"key": "value", "number": int(123)}} 1343 c.Assert(plug.Attr("key", &val), IsNil) 1344 c.Check(val, Equals, "value") 1345 1346 c.Assert(plug.Attr("number", &intVal), IsNil) 1347 c.Check(intVal, Equals, 123) 1348 1349 c.Check(plug.Attr("key", &intVal), ErrorMatches, `snap "snap" has interface "interface" with invalid value type for "key" attribute`) 1350 c.Check(plug.Attr("unknown", &val), ErrorMatches, `snap "snap" does not have attribute "unknown" for interface "interface"`) 1351 c.Check(plug.Attr("key", intVal), ErrorMatches, `internal error: cannot get "key" attribute of interface "interface" with non-pointer value`) 1352 } 1353 1354 func (s *infoSuite) TestSlotInfoAttr(c *C) { 1355 var val string 1356 var intVal int 1357 1358 slot := &snap.SlotInfo{Snap: &snap.Info{SuggestedName: "snap"}, Name: "plug", Interface: "interface", Attrs: map[string]interface{}{"key": "value", "number": int(123)}} 1359 1360 c.Assert(slot.Attr("key", &val), IsNil) 1361 c.Check(val, Equals, "value") 1362 1363 c.Assert(slot.Attr("number", &intVal), IsNil) 1364 c.Check(intVal, Equals, 123) 1365 1366 c.Check(slot.Attr("key", &intVal), ErrorMatches, `snap "snap" has interface "interface" with invalid value type for "key" attribute`) 1367 c.Check(slot.Attr("unknown", &val), ErrorMatches, `snap "snap" does not have attribute "unknown" for interface "interface"`) 1368 c.Check(slot.Attr("key", intVal), ErrorMatches, `internal error: cannot get "key" attribute of interface "interface" with non-pointer value`) 1369 } 1370 1371 func (s *infoSuite) TestDottedPathSlot(c *C) { 1372 attrs := map[string]interface{}{ 1373 "nested": map[string]interface{}{ 1374 "foo": "bar", 1375 }, 1376 } 1377 1378 slot := &snap.SlotInfo{Attrs: attrs} 1379 c.Assert(slot, NotNil) 1380 1381 v, ok := slot.Lookup("nested.foo") 1382 c.Assert(ok, Equals, true) 1383 c.Assert(v, Equals, "bar") 1384 1385 v, ok = slot.Lookup("nested") 1386 c.Assert(ok, Equals, true) 1387 c.Assert(v, DeepEquals, map[string]interface{}{ 1388 "foo": "bar", 1389 }) 1390 1391 _, ok = slot.Lookup("x") 1392 c.Assert(ok, Equals, false) 1393 1394 _, ok = slot.Lookup("..") 1395 c.Assert(ok, Equals, false) 1396 1397 _, ok = slot.Lookup("nested.foo.x") 1398 c.Assert(ok, Equals, false) 1399 1400 _, ok = slot.Lookup("nested.x") 1401 c.Assert(ok, Equals, false) 1402 } 1403 1404 func (s *infoSuite) TestDottedPathPlug(c *C) { 1405 attrs := map[string]interface{}{ 1406 "nested": map[string]interface{}{ 1407 "foo": "bar", 1408 }, 1409 } 1410 1411 plug := &snap.PlugInfo{Attrs: attrs} 1412 c.Assert(plug, NotNil) 1413 1414 v, ok := plug.Lookup("nested") 1415 c.Assert(ok, Equals, true) 1416 c.Assert(v, DeepEquals, map[string]interface{}{ 1417 "foo": "bar", 1418 }) 1419 1420 v, ok = plug.Lookup("nested.foo") 1421 c.Assert(ok, Equals, true) 1422 c.Assert(v, Equals, "bar") 1423 1424 _, ok = plug.Lookup("x") 1425 c.Assert(ok, Equals, false) 1426 1427 _, ok = plug.Lookup("..") 1428 c.Assert(ok, Equals, false) 1429 1430 _, ok = plug.Lookup("nested.foo.x") 1431 c.Assert(ok, Equals, false) 1432 } 1433 1434 func (s *infoSuite) TestDefaultContentProviders(c *C) { 1435 info, err := snap.InfoFromSnapYaml([]byte(yamlNeedDf)) 1436 c.Assert(err, IsNil) 1437 1438 plugs := make([]*snap.PlugInfo, 0, len(info.Plugs)) 1439 for _, plug := range info.Plugs { 1440 plugs = append(plugs, plug) 1441 } 1442 1443 dps := snap.DefaultContentProviders(plugs) 1444 c.Check(dps, DeepEquals, map[string][]string{"gtk-common-themes": {"gtk-3-themes", "icon-themes"}}) 1445 } 1446 1447 func (s *infoSuite) TestExpandSnapVariables(c *C) { 1448 dirs.SetRootDir("") 1449 info, err := snap.InfoFromSnapYaml([]byte(`name: foo`)) 1450 c.Assert(err, IsNil) 1451 info.Revision = snap.R(42) 1452 c.Assert(info.ExpandSnapVariables("$SNAP/stuff"), Equals, "/snap/foo/42/stuff") 1453 c.Assert(info.ExpandSnapVariables("$SNAP_DATA/stuff"), Equals, "/var/snap/foo/42/stuff") 1454 c.Assert(info.ExpandSnapVariables("$SNAP_COMMON/stuff"), Equals, "/var/snap/foo/common/stuff") 1455 c.Assert(info.ExpandSnapVariables("$GARBAGE/rocks"), Equals, "/rocks") 1456 1457 info.InstanceKey = "instance" 1458 // Despite setting the instance key the variables expand to the same 1459 // value as before. This is because they are used from inside the mount 1460 // namespace of the instantiated snap where the mount backend will 1461 // ensure that the regular (non-instance) paths contain 1462 // instance-specific code and data. 1463 c.Assert(info.ExpandSnapVariables("$SNAP/stuff"), Equals, "/snap/foo/42/stuff") 1464 c.Assert(info.ExpandSnapVariables("$SNAP_DATA/stuff"), Equals, "/var/snap/foo/42/stuff") 1465 c.Assert(info.ExpandSnapVariables("$SNAP_COMMON/stuff"), Equals, "/var/snap/foo/common/stuff") 1466 c.Assert(info.ExpandSnapVariables("$GARBAGE/rocks"), Equals, "/rocks") 1467 } 1468 1469 func (s *infoSuite) TestStopModeTypeKillMode(c *C) { 1470 for _, t := range []struct { 1471 stopMode string 1472 killall bool 1473 }{ 1474 {"", true}, 1475 {"sigterm", false}, 1476 {"sigterm-all", true}, 1477 {"sighup", false}, 1478 {"sighup-all", true}, 1479 {"sigusr1", false}, 1480 {"sigusr1-all", true}, 1481 {"sigusr2", false}, 1482 {"sigusr2-all", true}, 1483 } { 1484 c.Check(snap.StopModeType(t.stopMode).KillAll(), Equals, t.killall, Commentf("wrong KillAll for %v", t.stopMode)) 1485 } 1486 } 1487 1488 func (s *infoSuite) TestStopModeTypeKillSignal(c *C) { 1489 for _, t := range []struct { 1490 stopMode string 1491 killSig string 1492 }{ 1493 {"", ""}, 1494 {"sigterm", "SIGTERM"}, 1495 {"sigterm-all", "SIGTERM"}, 1496 {"sighup", "SIGHUP"}, 1497 {"sighup-all", "SIGHUP"}, 1498 {"sigusr1", "SIGUSR1"}, 1499 {"sigusr1-all", "SIGUSR1"}, 1500 {"sigusr2", "SIGUSR2"}, 1501 {"sigusr2-all", "SIGUSR2"}, 1502 } { 1503 c.Check(snap.StopModeType(t.stopMode).KillSignal(), Equals, t.killSig) 1504 } 1505 } 1506 1507 func (s *infoSuite) TestSplitInstanceName(c *C) { 1508 snapName, instanceKey := snap.SplitInstanceName("foo_bar") 1509 c.Check(snapName, Equals, "foo") 1510 c.Check(instanceKey, Equals, "bar") 1511 1512 snapName, instanceKey = snap.SplitInstanceName("foo") 1513 c.Check(snapName, Equals, "foo") 1514 c.Check(instanceKey, Equals, "") 1515 1516 // all following instance names are invalid 1517 1518 snapName, instanceKey = snap.SplitInstanceName("_bar") 1519 c.Check(snapName, Equals, "") 1520 c.Check(instanceKey, Equals, "bar") 1521 1522 snapName, instanceKey = snap.SplitInstanceName("foo___bar_bar") 1523 c.Check(snapName, Equals, "foo") 1524 c.Check(instanceKey, Equals, "__bar_bar") 1525 1526 snapName, instanceKey = snap.SplitInstanceName("") 1527 c.Check(snapName, Equals, "") 1528 c.Check(instanceKey, Equals, "") 1529 } 1530 1531 func (s *infoSuite) TestInstanceSnapName(c *C) { 1532 c.Check(snap.InstanceSnap("foo_bar"), Equals, "foo") 1533 c.Check(snap.InstanceSnap("foo"), Equals, "foo") 1534 1535 c.Check(snap.InstanceName("foo", "bar"), Equals, "foo_bar") 1536 c.Check(snap.InstanceName("foo", ""), Equals, "foo") 1537 } 1538 1539 func (s *infoSuite) TestInstanceNameInSnapInfo(c *C) { 1540 info := &snap.Info{ 1541 SuggestedName: "snap-name", 1542 InstanceKey: "foo", 1543 } 1544 1545 c.Check(info.InstanceName(), Equals, "snap-name_foo") 1546 c.Check(info.SnapName(), Equals, "snap-name") 1547 1548 info.InstanceKey = "" 1549 c.Check(info.InstanceName(), Equals, "snap-name") 1550 c.Check(info.SnapName(), Equals, "snap-name") 1551 } 1552 1553 func (s *infoSuite) TestIsActive(c *C) { 1554 info1 := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(1)}) 1555 info2 := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(2)}) 1556 // no current -> not active 1557 c.Check(info1.IsActive(), Equals, false) 1558 c.Check(info2.IsActive(), Equals, false) 1559 1560 mountdir := info1.MountDir() 1561 dir, rev := filepath.Split(mountdir) 1562 c.Assert(os.MkdirAll(dir, 0755), IsNil) 1563 cur := filepath.Join(dir, "current") 1564 c.Assert(os.Symlink(rev, cur), IsNil) 1565 1566 // is current -> is active 1567 c.Check(info1.IsActive(), Equals, true) 1568 c.Check(info2.IsActive(), Equals, false) 1569 } 1570 1571 func (s *infoSuite) TestInfoTypeSnapdBackwardCompatibility(c *C) { 1572 const snapdYaml = ` 1573 name: snapd 1574 type: app 1575 version: 1 1576 ` 1577 snapInfo := snaptest.MockSnap(c, sampleYaml, &snap.SideInfo{Revision: snap.R(1), SnapID: "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4"}) 1578 c.Check(snapInfo.Type(), Equals, snap.TypeSnapd) 1579 } 1580 1581 func (s *infoSuite) TestDirAndFileHelpers(c *C) { 1582 dirs.SetRootDir("") 1583 1584 c.Check(snap.MountDir("name", snap.R(1)), Equals, fmt.Sprintf("%s/name/1", dirs.SnapMountDir)) 1585 c.Check(snap.MountFile("name", snap.R(1)), Equals, "/var/lib/snapd/snaps/name_1.snap") 1586 c.Check(snap.HooksDir("name", snap.R(1)), Equals, fmt.Sprintf("%s/name/1/meta/hooks", dirs.SnapMountDir)) 1587 c.Check(snap.DataDir("name", snap.R(1)), Equals, "/var/snap/name/1") 1588 c.Check(snap.CommonDataDir("name"), Equals, "/var/snap/name/common") 1589 c.Check(snap.UserDataDir("/home/bob", "name", snap.R(1)), Equals, "/home/bob/snap/name/1") 1590 c.Check(snap.UserCommonDataDir("/home/bob", "name"), Equals, "/home/bob/snap/name/common") 1591 c.Check(snap.UserXdgRuntimeDir(12345, "name"), Equals, "/run/user/12345/snap.name") 1592 c.Check(snap.UserSnapDir("/home/bob", "name"), Equals, "/home/bob/snap/name") 1593 1594 c.Check(snap.MountDir("name_instance", snap.R(1)), Equals, fmt.Sprintf("%s/name_instance/1", dirs.SnapMountDir)) 1595 c.Check(snap.MountFile("name_instance", snap.R(1)), Equals, "/var/lib/snapd/snaps/name_instance_1.snap") 1596 c.Check(snap.HooksDir("name_instance", snap.R(1)), Equals, fmt.Sprintf("%s/name_instance/1/meta/hooks", dirs.SnapMountDir)) 1597 c.Check(snap.DataDir("name_instance", snap.R(1)), Equals, "/var/snap/name_instance/1") 1598 c.Check(snap.CommonDataDir("name_instance"), Equals, "/var/snap/name_instance/common") 1599 c.Check(snap.UserDataDir("/home/bob", "name_instance", snap.R(1)), Equals, "/home/bob/snap/name_instance/1") 1600 c.Check(snap.UserCommonDataDir("/home/bob", "name_instance"), Equals, "/home/bob/snap/name_instance/common") 1601 c.Check(snap.UserXdgRuntimeDir(12345, "name_instance"), Equals, "/run/user/12345/snap.name_instance") 1602 c.Check(snap.UserSnapDir("/home/bob", "name_instance"), Equals, "/home/bob/snap/name_instance") 1603 } 1604 1605 func (s *infoSuite) TestSortByType(c *C) { 1606 infos := []*snap.Info{ 1607 {SuggestedName: "app1", SnapType: "app"}, 1608 {SuggestedName: "os1", SnapType: "os"}, 1609 {SuggestedName: "base1", SnapType: "base"}, 1610 {SuggestedName: "gadget1", SnapType: "gadget"}, 1611 {SuggestedName: "kernel1", SnapType: "kernel"}, 1612 {SuggestedName: "app2", SnapType: "app"}, 1613 {SuggestedName: "os2", SnapType: "os"}, 1614 {SuggestedName: "snapd", SnapType: "snapd"}, 1615 {SuggestedName: "base2", SnapType: "base"}, 1616 {SuggestedName: "gadget2", SnapType: "gadget"}, 1617 {SuggestedName: "kernel2", SnapType: "kernel"}, 1618 } 1619 sort.Stable(snap.ByType(infos)) 1620 1621 c.Check(infos, DeepEquals, []*snap.Info{ 1622 {SuggestedName: "snapd", SnapType: "snapd"}, 1623 {SuggestedName: "os1", SnapType: "os"}, 1624 {SuggestedName: "os2", SnapType: "os"}, 1625 {SuggestedName: "kernel1", SnapType: "kernel"}, 1626 {SuggestedName: "kernel2", SnapType: "kernel"}, 1627 {SuggestedName: "base1", SnapType: "base"}, 1628 {SuggestedName: "base2", SnapType: "base"}, 1629 {SuggestedName: "gadget1", SnapType: "gadget"}, 1630 {SuggestedName: "gadget2", SnapType: "gadget"}, 1631 {SuggestedName: "app1", SnapType: "app"}, 1632 {SuggestedName: "app2", SnapType: "app"}, 1633 }) 1634 } 1635 1636 func (s *infoSuite) TestSortByTypeAgain(c *C) { 1637 core := &snap.Info{SnapType: snap.TypeOS} 1638 base := &snap.Info{SnapType: snap.TypeBase} 1639 app := &snap.Info{SnapType: snap.TypeApp} 1640 snapd := &snap.Info{} 1641 snapd.SideInfo = snap.SideInfo{RealName: "snapd"} 1642 1643 byType := func(snaps ...*snap.Info) []*snap.Info { 1644 sort.Stable(snap.ByType(snaps)) 1645 return snaps 1646 } 1647 1648 c.Check(byType(base, core), DeepEquals, []*snap.Info{core, base}) 1649 c.Check(byType(app, core), DeepEquals, []*snap.Info{core, app}) 1650 c.Check(byType(app, base), DeepEquals, []*snap.Info{base, app}) 1651 c.Check(byType(app, base, core), DeepEquals, []*snap.Info{core, base, app}) 1652 c.Check(byType(app, core, base), DeepEquals, []*snap.Info{core, base, app}) 1653 1654 c.Check(byType(app, core, base, snapd), DeepEquals, []*snap.Info{snapd, core, base, app}) 1655 c.Check(byType(app, snapd, core, base), DeepEquals, []*snap.Info{snapd, core, base, app}) 1656 } 1657 1658 func (s *infoSuite) TestMedia(c *C) { 1659 c.Check(snap.MediaInfos{}.IconURL(), Equals, "") 1660 1661 media := snap.MediaInfos{ 1662 { 1663 Type: "screenshot", 1664 URL: "https://example.com/shot1.svg", 1665 }, { 1666 Type: "icon", 1667 URL: "https://example.com/icon.png", 1668 }, { 1669 Type: "screenshot", 1670 URL: "https://example.com/shot2.svg", 1671 Width: 42, 1672 Height: 17, 1673 }, 1674 } 1675 1676 c.Check(media.IconURL(), Equals, "https://example.com/icon.png") 1677 } 1678 1679 func (s *infoSuite) TestSortApps(c *C) { 1680 tcs := []struct { 1681 err string 1682 apps []*snap.AppInfo 1683 sorted []string 1684 }{{ 1685 apps: []*snap.AppInfo{ 1686 {Name: "bar", Before: []string{"baz"}}, 1687 {Name: "baz", After: []string{"bar", "foo"}}, 1688 {Name: "foo"}, 1689 }, 1690 sorted: []string{"bar", "foo", "baz"}, 1691 }, { 1692 apps: []*snap.AppInfo{ 1693 {Name: "foo", After: []string{"bar", "zed"}}, 1694 {Name: "bar", Before: []string{"foo"}}, 1695 {Name: "baz", After: []string{"foo"}}, 1696 {Name: "zed"}, 1697 }, 1698 sorted: []string{"bar", "zed", "foo", "baz"}, 1699 }, { 1700 apps: []*snap.AppInfo{ 1701 {Name: "foo", After: []string{"baz"}}, 1702 {Name: "bar", Before: []string{"baz"}}, 1703 {Name: "baz"}, 1704 {Name: "zed", After: []string{"foo", "bar", "baz"}}, 1705 }, 1706 sorted: []string{"bar", "baz", "foo", "zed"}, 1707 }, { 1708 apps: []*snap.AppInfo{ 1709 {Name: "foo", Before: []string{"bar"}, After: []string{"zed"}}, 1710 {Name: "bar", Before: []string{"baz"}}, 1711 {Name: "baz", Before: []string{"zed"}}, 1712 {Name: "zed"}, 1713 }, 1714 err: `applications are part of a before/after cycle: ((foo|bar|baz|zed)(, )?){4}`, 1715 }, { 1716 apps: []*snap.AppInfo{ 1717 {Name: "foo", Before: []string{"bar"}}, 1718 {Name: "bar", Before: []string{"foo"}}, 1719 {Name: "baz", Before: []string{"foo"}, After: []string{"bar"}}, 1720 }, 1721 err: `applications are part of a before/after cycle: ((foo|bar|baz)(, )?){3}`, 1722 }, { 1723 apps: []*snap.AppInfo{ 1724 {Name: "baz", After: []string{"bar"}}, 1725 {Name: "foo"}, 1726 {Name: "bar", After: []string{"foo"}}, 1727 }, 1728 sorted: []string{"foo", "bar", "baz"}, 1729 }} 1730 for _, tc := range tcs { 1731 sorted, err := snap.SortServices(tc.apps) 1732 if tc.err != "" { 1733 c.Assert(err, ErrorMatches, tc.err) 1734 } else { 1735 c.Assert(err, IsNil) 1736 c.Assert(sorted, HasLen, len(tc.sorted)) 1737 sortedNames := make([]string, len(sorted)) 1738 for i, app := range sorted { 1739 sortedNames[i] = app.Name 1740 } 1741 c.Assert(sortedNames, DeepEquals, tc.sorted) 1742 } 1743 } 1744 } 1745 1746 func (s *infoSuite) TestSortAppInfoBySnapApp(c *C) { 1747 snap1 := &snap.Info{SuggestedName: "snapa"} 1748 snap2 := &snap.Info{SuggestedName: "snapb"} 1749 infos := []*snap.AppInfo{ 1750 {Snap: snap1, Name: "b"}, 1751 {Snap: snap2, Name: "b"}, 1752 {Snap: snap1, Name: "a"}, 1753 {Snap: snap2, Name: "a"}, 1754 } 1755 sort.Stable(snap.AppInfoBySnapApp(infos)) 1756 1757 c.Check(infos, DeepEquals, []*snap.AppInfo{ 1758 {Snap: snap1, Name: "a"}, 1759 {Snap: snap1, Name: "b"}, 1760 {Snap: snap2, Name: "a"}, 1761 {Snap: snap2, Name: "b"}, 1762 }) 1763 }