github.com/rigado/snapd@v2.42.5-go-mod+incompatible/cmd/snap-exec/main_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 main_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "testing" 29 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/release" 34 "github.com/snapcore/snapd/snap" 35 "github.com/snapcore/snapd/snap/snaptest" 36 "github.com/snapcore/snapd/testutil" 37 38 snapExec "github.com/snapcore/snapd/cmd/snap-exec" 39 ) 40 41 // Hook up check.v1 into the "go test" runner 42 func Test(t *testing.T) { TestingT(t) } 43 44 type snapExecSuite struct{} 45 46 var _ = Suite(&snapExecSuite{}) 47 48 func (s *snapExecSuite) SetUpTest(c *C) { 49 // clean previous parse runs 50 snapExec.SetOptsCommand("") 51 snapExec.SetOptsHook("") 52 } 53 54 func (s *snapExecSuite) TearDown(c *C) { 55 dirs.SetRootDir("/") 56 } 57 58 var mockYaml = []byte(`name: snapname 59 version: 1.0 60 apps: 61 app: 62 command: run-app cmd-arg1 $SNAP_DATA 63 stop-command: stop-app 64 post-stop-command: post-stop-app 65 completer: you/complete/me 66 environment: 67 BASE_PATH: /some/path 68 LD_LIBRARY_PATH: ${BASE_PATH}/lib 69 MY_PATH: $PATH 70 app2: 71 command: run-app2 72 stop-command: stop-app2 73 post-stop-command: post-stop-app2 74 command-chain: [chain1, chain2] 75 nostop: 76 command: nostop 77 `) 78 79 var mockClassicYaml = append([]byte("confinement: classic\n"), mockYaml...) 80 81 var mockHookYaml = []byte(`name: snapname 82 version: 1.0 83 hooks: 84 configure: 85 `) 86 87 var mockHookCommandChainYaml = []byte(`name: snapname 88 version: 1.0 89 hooks: 90 configure: 91 command-chain: [chain1, chain2] 92 `) 93 94 var binaryTemplate = `#!/bin/sh 95 echo "$(basename $0)" >> %[1]q 96 for arg in "$@"; do 97 echo "$arg" >> %[1]q 98 done 99 printf "\n" >> %[1]q` 100 101 func (s *snapExecSuite) TestInvalidCombinedParameters(c *C) { 102 invalidParameters := []string{"--hook=hook-name", "--command=command-name", "snap-name"} 103 _, _, err := snapExec.ParseArgs(invalidParameters) 104 c.Check(err, ErrorMatches, ".*cannot use --hook and --command together.*") 105 } 106 107 func (s *snapExecSuite) TestInvalidExtraParameters(c *C) { 108 invalidParameters := []string{"--hook=hook-name", "snap-name", "foo", "bar"} 109 _, _, err := snapExec.ParseArgs(invalidParameters) 110 c.Check(err, ErrorMatches, ".*too many arguments for hook \"hook-name\": snap-name foo bar.*") 111 } 112 113 func (s *snapExecSuite) TestFindCommand(c *C) { 114 info, err := snap.InfoFromSnapYaml(mockYaml) 115 c.Assert(err, IsNil) 116 117 for _, t := range []struct { 118 cmd string 119 expected string 120 }{ 121 {cmd: "", expected: `run-app cmd-arg1 $SNAP_DATA`}, 122 {cmd: "stop", expected: "stop-app"}, 123 {cmd: "post-stop", expected: "post-stop-app"}, 124 } { 125 cmd, err := snapExec.FindCommand(info.Apps["app"], t.cmd) 126 c.Check(err, IsNil) 127 c.Check(cmd, Equals, t.expected) 128 } 129 } 130 131 func (s *snapExecSuite) TestFindCommandInvalidCommand(c *C) { 132 info, err := snap.InfoFromSnapYaml(mockYaml) 133 c.Assert(err, IsNil) 134 135 _, err = snapExec.FindCommand(info.Apps["app"], "xxx") 136 c.Check(err, ErrorMatches, `cannot use "xxx" command`) 137 } 138 139 func (s *snapExecSuite) TestFindCommandNoCommand(c *C) { 140 info, err := snap.InfoFromSnapYaml(mockYaml) 141 c.Assert(err, IsNil) 142 143 _, err = snapExec.FindCommand(info.Apps["nostop"], "stop") 144 c.Check(err, ErrorMatches, `no "stop" command found for "nostop"`) 145 } 146 147 func (s *snapExecSuite) TestSnapExecAppIntegration(c *C) { 148 dirs.SetRootDir(c.MkDir()) 149 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 150 Revision: snap.R("42"), 151 }) 152 153 execArgv0 := "" 154 execArgs := []string{} 155 execEnv := []string{} 156 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 157 execArgv0 = argv0 158 execArgs = argv 159 execEnv = env 160 return nil 161 }) 162 defer restore() 163 164 // launch and verify its run the right way 165 err := snapExec.ExecApp("snapname.app", "42", "stop", []string{"arg1", "arg2"}) 166 c.Assert(err, IsNil) 167 c.Check(execArgv0, Equals, fmt.Sprintf("%s/snapname/42/stop-app", dirs.SnapMountDir)) 168 c.Check(execArgs, DeepEquals, []string{execArgv0, "arg1", "arg2"}) 169 c.Check(execEnv, testutil.Contains, "BASE_PATH=/some/path") 170 c.Check(execEnv, testutil.Contains, "LD_LIBRARY_PATH=/some/path/lib") 171 c.Check(execEnv, testutil.Contains, fmt.Sprintf("MY_PATH=%s", os.Getenv("PATH"))) 172 } 173 174 func (s *snapExecSuite) TestSnapExecAppCommandChainIntegration(c *C) { 175 dirs.SetRootDir(c.MkDir()) 176 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 177 Revision: snap.R("42"), 178 }) 179 180 execArgv0 := "" 181 execArgs := []string{} 182 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 183 execArgv0 = argv0 184 execArgs = argv 185 return nil 186 }) 187 defer restore() 188 189 chain1_path := fmt.Sprintf("%s/snapname/42/chain1", dirs.SnapMountDir) 190 chain2_path := fmt.Sprintf("%s/snapname/42/chain2", dirs.SnapMountDir) 191 app_path := fmt.Sprintf("%s/snapname/42/run-app2", dirs.SnapMountDir) 192 stop_path := fmt.Sprintf("%s/snapname/42/stop-app2", dirs.SnapMountDir) 193 post_stop_path := fmt.Sprintf("%s/snapname/42/post-stop-app2", dirs.SnapMountDir) 194 195 for _, t := range []struct { 196 cmd string 197 args []string 198 expected []string 199 }{ 200 // Normal command 201 {expected: []string{chain1_path, chain2_path, app_path}}, 202 {args: []string{"arg1", "arg2"}, expected: []string{chain1_path, chain2_path, app_path, "arg1", "arg2"}}, 203 204 // Stop command 205 {cmd: "stop", expected: []string{chain1_path, chain2_path, stop_path}}, 206 {cmd: "stop", args: []string{"arg1", "arg2"}, expected: []string{chain1_path, chain2_path, stop_path, "arg1", "arg2"}}, 207 208 // Post-stop command 209 {cmd: "post-stop", expected: []string{chain1_path, chain2_path, post_stop_path}}, 210 {cmd: "post-stop", args: []string{"arg1", "arg2"}, expected: []string{chain1_path, chain2_path, post_stop_path, "arg1", "arg2"}}, 211 } { 212 err := snapExec.ExecApp("snapname.app2", "42", t.cmd, t.args) 213 c.Assert(err, IsNil) 214 c.Check(execArgv0, Equals, t.expected[0]) 215 c.Check(execArgs, DeepEquals, t.expected) 216 } 217 } 218 219 func (s *snapExecSuite) TestSnapExecHookIntegration(c *C) { 220 dirs.SetRootDir(c.MkDir()) 221 snaptest.MockSnap(c, string(mockHookYaml), &snap.SideInfo{ 222 Revision: snap.R("42"), 223 }) 224 225 execArgv0 := "" 226 execArgs := []string{} 227 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 228 execArgv0 = argv0 229 execArgs = argv 230 return nil 231 }) 232 defer restore() 233 234 // launch and verify it ran correctly 235 err := snapExec.ExecHook("snapname", "42", "configure") 236 c.Assert(err, IsNil) 237 c.Check(execArgv0, Equals, fmt.Sprintf("%s/snapname/42/meta/hooks/configure", dirs.SnapMountDir)) 238 c.Check(execArgs, DeepEquals, []string{execArgv0}) 239 } 240 241 func (s *snapExecSuite) TestSnapExecHookCommandChainIntegration(c *C) { 242 dirs.SetRootDir(c.MkDir()) 243 snaptest.MockSnap(c, string(mockHookCommandChainYaml), &snap.SideInfo{ 244 Revision: snap.R("42"), 245 }) 246 247 execArgv0 := "" 248 execArgs := []string{} 249 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 250 execArgv0 = argv0 251 execArgs = argv 252 return nil 253 }) 254 defer restore() 255 256 chain1_path := fmt.Sprintf("%s/snapname/42/chain1", dirs.SnapMountDir) 257 chain2_path := fmt.Sprintf("%s/snapname/42/chain2", dirs.SnapMountDir) 258 hook_path := fmt.Sprintf("%s/snapname/42/meta/hooks/configure", dirs.SnapMountDir) 259 260 err := snapExec.ExecHook("snapname", "42", "configure") 261 c.Assert(err, IsNil) 262 c.Check(execArgv0, Equals, chain1_path) 263 c.Check(execArgs, DeepEquals, []string{chain1_path, chain2_path, hook_path}) 264 } 265 266 func (s *snapExecSuite) TestSnapExecHookMissingHookIntegration(c *C) { 267 dirs.SetRootDir(c.MkDir()) 268 snaptest.MockSnap(c, string(mockHookYaml), &snap.SideInfo{ 269 Revision: snap.R("42"), 270 }) 271 272 err := snapExec.ExecHook("snapname", "42", "missing-hook") 273 c.Assert(err, NotNil) 274 c.Assert(err, ErrorMatches, "cannot find hook \"missing-hook\" in \"snapname\"") 275 } 276 277 func (s *snapExecSuite) TestSnapExecIgnoresUnknownArgs(c *C) { 278 snapApp, rest, err := snapExec.ParseArgs([]string{"--command=shell", "snapname.app", "--arg1", "arg2"}) 279 c.Assert(err, IsNil) 280 c.Assert(snapExec.GetOptsCommand(), Equals, "shell") 281 c.Assert(snapApp, DeepEquals, "snapname.app") 282 c.Assert(rest, DeepEquals, []string{"--arg1", "arg2"}) 283 } 284 285 func (s *snapExecSuite) TestSnapExecErrorsOnUnknown(c *C) { 286 _, _, err := snapExec.ParseArgs([]string{"--command=shell", "--unknown", "snapname.app", "--arg1", "arg2"}) 287 c.Check(err, ErrorMatches, "unknown flag `unknown'") 288 } 289 290 func (s *snapExecSuite) TestSnapExecErrorsOnMissingSnapApp(c *C) { 291 _, _, err := snapExec.ParseArgs([]string{"--command=shell"}) 292 c.Check(err, ErrorMatches, "need the application to run as argument") 293 } 294 295 func (s *snapExecSuite) TestSnapExecAppRealIntegration(c *C) { 296 // we need a lot of mocks 297 dirs.SetRootDir(c.MkDir()) 298 299 oldOsArgs := os.Args 300 defer func() { os.Args = oldOsArgs }() 301 302 os.Setenv("SNAP_REVISION", "42") 303 defer os.Unsetenv("SNAP_REVISION") 304 305 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 306 Revision: snap.R("42"), 307 }) 308 309 canaryFile := filepath.Join(c.MkDir(), "canary.txt") 310 script := fmt.Sprintf("%s/snapname/42/run-app", dirs.SnapMountDir) 311 err := ioutil.WriteFile(script, []byte(fmt.Sprintf(binaryTemplate, canaryFile)), 0755) 312 c.Assert(err, IsNil) 313 314 // we can not use the real syscall.execv here because it would 315 // replace the entire test :) 316 restore := snapExec.MockSyscallExec(actuallyExec) 317 defer restore() 318 319 // run it 320 os.Args = []string{"snap-exec", "snapname.app", "foo", "--bar=baz", "foobar"} 321 err = snapExec.Run() 322 c.Assert(err, IsNil) 323 324 c.Assert(canaryFile, testutil.FileEquals, `run-app 325 cmd-arg1 326 foo 327 --bar=baz 328 foobar 329 330 `) 331 } 332 333 func (s *snapExecSuite) TestSnapExecHookRealIntegration(c *C) { 334 // we need a lot of mocks 335 dirs.SetRootDir(c.MkDir()) 336 337 oldOsArgs := os.Args 338 defer func() { os.Args = oldOsArgs }() 339 340 os.Setenv("SNAP_REVISION", "42") 341 defer os.Unsetenv("SNAP_REVISION") 342 343 canaryFile := filepath.Join(c.MkDir(), "canary.txt") 344 345 testSnap := snaptest.MockSnap(c, string(mockHookYaml), &snap.SideInfo{ 346 Revision: snap.R("42"), 347 }) 348 hookPath := filepath.Join("meta", "hooks", "configure") 349 hookPathAndContents := []string{hookPath, fmt.Sprintf(binaryTemplate, canaryFile)} 350 snaptest.PopulateDir(testSnap.MountDir(), [][]string{hookPathAndContents}) 351 hookPath = filepath.Join(testSnap.MountDir(), hookPath) 352 c.Assert(os.Chmod(hookPath, 0755), IsNil) 353 354 // we can not use the real syscall.execv here because it would 355 // replace the entire test :) 356 restore := snapExec.MockSyscallExec(actuallyExec) 357 defer restore() 358 359 // run it 360 os.Args = []string{"snap-exec", "--hook=configure", "snapname"} 361 err := snapExec.Run() 362 c.Assert(err, IsNil) 363 364 c.Assert(canaryFile, testutil.FileEquals, "configure\n\n") 365 } 366 367 func actuallyExec(argv0 string, argv []string, env []string) error { 368 cmd := exec.Command(argv[0], argv[1:]...) 369 cmd.Env = env 370 output, err := cmd.CombinedOutput() 371 if len(output) > 0 { 372 return fmt.Errorf("Expected output length to be 0, it was %d", len(output)) 373 } 374 return err 375 } 376 377 func (s *snapExecSuite) TestSnapExecShellIntegration(c *C) { 378 dirs.SetRootDir(c.MkDir()) 379 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 380 Revision: snap.R("42"), 381 }) 382 383 execArgv0 := "" 384 execArgs := []string{} 385 execEnv := []string{} 386 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 387 execArgv0 = argv0 388 execArgs = argv 389 execEnv = env 390 return nil 391 }) 392 defer restore() 393 394 // launch and verify its run the right way 395 err := snapExec.ExecApp("snapname.app", "42", "shell", []string{"-c", "echo foo"}) 396 c.Assert(err, IsNil) 397 c.Check(execArgv0, Equals, "/bin/bash") 398 c.Check(execArgs, DeepEquals, []string{execArgv0, "-c", "echo foo"}) 399 c.Check(execEnv, testutil.Contains, "LD_LIBRARY_PATH=/some/path/lib") 400 401 // launch and verify shell still runs the command chain 402 err = snapExec.ExecApp("snapname.app2", "42", "shell", []string{"-c", "echo foo"}) 403 c.Assert(err, IsNil) 404 chain1 := fmt.Sprintf("%s/snapname/42/chain1", dirs.SnapMountDir) 405 chain2 := fmt.Sprintf("%s/snapname/42/chain2", dirs.SnapMountDir) 406 c.Check(execArgv0, Equals, chain1) 407 c.Check(execArgs, DeepEquals, []string{chain1, chain2, "/bin/bash", "-c", "echo foo"}) 408 } 409 410 func (s *snapExecSuite) TestSnapExecAppIntegrationWithVars(c *C) { 411 dirs.SetRootDir(c.MkDir()) 412 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 413 Revision: snap.R("42"), 414 }) 415 416 execArgv0 := "" 417 execArgs := []string{} 418 execEnv := []string{} 419 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 420 execArgv0 = argv0 421 execArgs = argv 422 execEnv = env 423 return nil 424 }) 425 defer restore() 426 427 // setup env 428 os.Setenv("SNAP_DATA", "/var/snap/snapname/42") 429 defer os.Unsetenv("SNAP_DATA") 430 431 // launch and verify its run the right way 432 err := snapExec.ExecApp("snapname.app", "42", "", []string{"user-arg1"}) 433 c.Assert(err, IsNil) 434 c.Check(execArgv0, Equals, fmt.Sprintf("%s/snapname/42/run-app", dirs.SnapMountDir)) 435 c.Check(execArgs, DeepEquals, []string{execArgv0, "cmd-arg1", "/var/snap/snapname/42", "user-arg1"}) 436 c.Check(execEnv, testutil.Contains, "BASE_PATH=/some/path") 437 c.Check(execEnv, testutil.Contains, "LD_LIBRARY_PATH=/some/path/lib") 438 c.Check(execEnv, testutil.Contains, fmt.Sprintf("MY_PATH=%s", os.Getenv("PATH"))) 439 } 440 441 func (s *snapExecSuite) TestSnapExecExpandEnvCmdArgs(c *C) { 442 for _, t := range []struct { 443 args []string 444 env map[string]string 445 expected []string 446 }{ 447 { 448 args: []string{"foo"}, 449 env: nil, 450 expected: []string{"foo"}, 451 }, 452 { 453 args: []string{"$var"}, 454 env: map[string]string{"var": "value"}, 455 expected: []string{"value"}, 456 }, 457 { 458 args: []string{"foo", "$not_existing"}, 459 env: nil, 460 expected: []string{"foo"}, 461 }, 462 { 463 args: []string{"foo", "$var", "baz"}, 464 env: map[string]string{"var": "bar", "unrelated": "env"}, 465 expected: []string{"foo", "bar", "baz"}, 466 }, 467 } { 468 c.Check(snapExec.ExpandEnvCmdArgs(t.args, t.env), DeepEquals, t.expected) 469 470 } 471 } 472 473 func (s *snapExecSuite) TestSnapExecCompleteError(c *C) { 474 dirs.SetRootDir(c.MkDir()) 475 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 476 Revision: snap.R("42"), 477 }) 478 479 restore := snapExec.MockOsReadlink(func(p string) (string, error) { 480 c.Assert(p, Equals, "/proc/self/exe") 481 return "", fmt.Errorf("fail") 482 }) 483 defer restore() 484 485 // setup env 486 os.Setenv("SNAP_DATA", "/var/snap/snapname/42") 487 defer os.Unsetenv("SNAP_DATA") 488 489 // launch and verify its run the right way 490 err := snapExec.ExecApp("snapname.app", "42", "complete", []string{"foo"}) 491 c.Assert(err, ErrorMatches, "cannot find completion helper: fail") 492 } 493 494 func (s *snapExecSuite) TestSnapExecCompleteConfined(c *C) { 495 dirs.SetRootDir(c.MkDir()) 496 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 497 Revision: snap.R("42"), 498 }) 499 500 execArgv0 := "" 501 execArgs := []string{} 502 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 503 execArgv0 = argv0 504 execArgs = argv 505 return nil 506 }) 507 defer restore() 508 509 restore = snapExec.MockOsReadlink(func(p string) (string, error) { 510 c.Assert(p, Equals, "/proc/self/exe") 511 // as if running inside the snap mount namespace 512 return "/usr/lib/snapd/snap-exec", nil 513 }) 514 defer restore() 515 516 // setup env 517 os.Setenv("SNAP_DATA", "/var/snap/snapname/42") 518 defer os.Unsetenv("SNAP_DATA") 519 520 // launch and verify its run the right way 521 err := snapExec.ExecApp("snapname.app", "42", "complete", []string{"foo"}) 522 c.Assert(err, IsNil) 523 c.Check(execArgv0, Equals, "/bin/bash") 524 c.Check(execArgs, DeepEquals, []string{execArgv0, 525 dirs.CompletionHelperInCore, 526 filepath.Join(dirs.SnapMountDir, "snapname/42/you/complete/me"), 527 "foo"}) 528 } 529 530 func (s *snapExecSuite) TestSnapExecCompleteClassicReexec(c *C) { 531 restore := release.MockReleaseInfo(&release.OS{ID: "ubuntu"}) 532 defer restore() 533 dirs.SetRootDir(c.MkDir()) 534 snaptest.MockSnap(c, string(mockClassicYaml), &snap.SideInfo{ 535 Revision: snap.R("42"), 536 }) 537 538 execArgv0 := "" 539 execArgs := []string{} 540 restore = snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 541 execArgv0 = argv0 542 execArgs = argv 543 return nil 544 }) 545 defer restore() 546 547 restore = snapExec.MockOsReadlink(func(p string) (string, error) { 548 c.Assert(p, Equals, "/proc/self/exe") 549 // as if it's reexeced from the snap 550 return filepath.Join(dirs.SnapMountDir, "core/current", dirs.CoreLibExecDir, "snap-exec"), nil 551 }) 552 defer restore() 553 554 // setup env 555 os.Setenv("SNAP_DATA", "/var/snap/snapname/42") 556 defer os.Unsetenv("SNAP_DATA") 557 558 // launch and verify its run the right way 559 err := snapExec.ExecApp("snapname.app", "42", "complete", []string{"foo"}) 560 c.Assert(err, IsNil) 561 c.Check(execArgv0, Equals, "/bin/bash") 562 c.Check(execArgs, DeepEquals, []string{execArgv0, 563 filepath.Join(dirs.SnapMountDir, "core/current", dirs.CompletionHelperInCore), 564 filepath.Join(dirs.SnapMountDir, "snapname/42/you/complete/me"), 565 "foo"}) 566 } 567 568 func (s *snapExecSuite) TestSnapExecCompleteClassicNoReexec(c *C) { 569 restore := release.MockReleaseInfo(&release.OS{ID: "centos"}) 570 defer restore() 571 dirs.SetRootDir(c.MkDir()) 572 snaptest.MockSnap(c, string(mockClassicYaml), &snap.SideInfo{ 573 Revision: snap.R("42"), 574 }) 575 576 execArgv0 := "" 577 execArgs := []string{} 578 restore = snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 579 execArgv0 = argv0 580 execArgs = argv 581 return nil 582 }) 583 defer restore() 584 585 restore = snapExec.MockOsReadlink(func(p string) (string, error) { 586 c.Assert(p, Equals, "/proc/self/exe") 587 // running from distro libexecdir 588 return filepath.Join(dirs.DistroLibExecDir, "snap-exec"), nil 589 }) 590 defer restore() 591 592 // setup env 593 os.Setenv("SNAP_DATA", "/var/snap/snapname/42") 594 defer os.Unsetenv("SNAP_DATA") 595 596 // launch and verify its run the right way 597 err := snapExec.ExecApp("snapname.app", "42", "complete", []string{"foo"}) 598 c.Assert(err, IsNil) 599 c.Check(execArgv0, Equals, "/bin/bash") 600 c.Check(execArgs, DeepEquals, []string{execArgv0, 601 filepath.Join(dirs.DistroLibExecDir, "etelpmoc.sh"), 602 filepath.Join(dirs.SnapMountDir, "snapname/42/you/complete/me"), 603 "foo"}) 604 }