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