github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/wrappers/desktop_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2015 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 wrappers_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/dirs"
    32  	"github.com/snapcore/snapd/osutil"
    33  	"github.com/snapcore/snapd/snap"
    34  	"github.com/snapcore/snapd/snap/snaptest"
    35  	"github.com/snapcore/snapd/testutil"
    36  	"github.com/snapcore/snapd/wrappers"
    37  )
    38  
    39  type desktopSuite struct {
    40  	testutil.BaseTest
    41  	tempdir string
    42  
    43  	mockUpdateDesktopDatabase *testutil.MockCmd
    44  }
    45  
    46  var _ = Suite(&desktopSuite{})
    47  
    48  func (s *desktopSuite) SetUpTest(c *C) {
    49  	s.BaseTest.SetUpTest(c)
    50  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    51  	s.tempdir = c.MkDir()
    52  	dirs.SetRootDir(s.tempdir)
    53  
    54  	s.mockUpdateDesktopDatabase = testutil.MockCommand(c, "update-desktop-database", "")
    55  }
    56  
    57  func (s *desktopSuite) TearDownTest(c *C) {
    58  	s.BaseTest.TearDownTest(c)
    59  	s.mockUpdateDesktopDatabase.Restore()
    60  	dirs.SetRootDir("")
    61  }
    62  
    63  var desktopAppYaml = `
    64  name: foo
    65  version: 1.0
    66  apps:
    67      foobar:
    68  `
    69  
    70  var mockDesktopFile = []byte(`
    71  [Desktop Entry]
    72  Name=foo
    73  Icon=${SNAP}/foo.png`)
    74  var desktopContents = ""
    75  
    76  func (s *desktopSuite) TestAddPackageDesktopFiles(c *C) {
    77  	expectedDesktopFilePath := filepath.Join(dirs.SnapDesktopFilesDir, "foo_foobar.desktop")
    78  	c.Assert(osutil.FileExists(expectedDesktopFilePath), Equals, false)
    79  
    80  	info := snaptest.MockSnap(c, desktopAppYaml, &snap.SideInfo{Revision: snap.R(11)})
    81  
    82  	// generate .desktop file in the package baseDir
    83  	baseDir := info.MountDir()
    84  	err := os.MkdirAll(filepath.Join(baseDir, "meta", "gui"), 0755)
    85  	c.Assert(err, IsNil)
    86  
    87  	err = ioutil.WriteFile(filepath.Join(baseDir, "meta", "gui", "foobar.desktop"), mockDesktopFile, 0644)
    88  	c.Assert(err, IsNil)
    89  
    90  	err = wrappers.AddSnapDesktopFiles(info)
    91  	c.Assert(err, IsNil)
    92  	c.Assert(osutil.FileExists(expectedDesktopFilePath), Equals, true)
    93  	c.Assert(s.mockUpdateDesktopDatabase.Calls(), DeepEquals, [][]string{
    94  		{"update-desktop-database", dirs.SnapDesktopFilesDir},
    95  	})
    96  }
    97  
    98  func (s *desktopSuite) TestRemovePackageDesktopFiles(c *C) {
    99  	mockDesktopFilePath := filepath.Join(dirs.SnapDesktopFilesDir, "foo_foobar.desktop")
   100  
   101  	err := os.MkdirAll(dirs.SnapDesktopFilesDir, 0755)
   102  	c.Assert(err, IsNil)
   103  	err = ioutil.WriteFile(mockDesktopFilePath, mockDesktopFile, 0644)
   104  	c.Assert(err, IsNil)
   105  	info, err := snap.InfoFromSnapYaml([]byte(desktopAppYaml))
   106  	c.Assert(err, IsNil)
   107  
   108  	err = wrappers.RemoveSnapDesktopFiles(info)
   109  	c.Assert(err, IsNil)
   110  	c.Assert(osutil.FileExists(mockDesktopFilePath), Equals, false)
   111  	c.Assert(s.mockUpdateDesktopDatabase.Calls(), DeepEquals, [][]string{
   112  		{"update-desktop-database", dirs.SnapDesktopFilesDir},
   113  	})
   114  }
   115  
   116  func (s *desktopSuite) TestParallelInstancesRemovePackageDesktopFiles(c *C) {
   117  	mockDesktopFilePath := filepath.Join(dirs.SnapDesktopFilesDir, "foo_foobar.desktop")
   118  	mockDesktopInstanceFilePath := filepath.Join(dirs.SnapDesktopFilesDir, "foo+instance_foobar.desktop")
   119  
   120  	err := os.MkdirAll(dirs.SnapDesktopFilesDir, 0755)
   121  	c.Assert(err, IsNil)
   122  	err = ioutil.WriteFile(mockDesktopFilePath, mockDesktopFile, 0644)
   123  	c.Assert(err, IsNil)
   124  	err = ioutil.WriteFile(mockDesktopInstanceFilePath, mockDesktopFile, 0644)
   125  	c.Assert(err, IsNil)
   126  	info, err := snap.InfoFromSnapYaml([]byte(desktopAppYaml))
   127  	c.Assert(err, IsNil)
   128  
   129  	err = wrappers.RemoveSnapDesktopFiles(info)
   130  	c.Assert(err, IsNil)
   131  	c.Assert(osutil.FileExists(mockDesktopFilePath), Equals, false)
   132  	c.Assert(s.mockUpdateDesktopDatabase.Calls(), DeepEquals, [][]string{
   133  		{"update-desktop-database", dirs.SnapDesktopFilesDir},
   134  	})
   135  	// foo+instance file is still there
   136  	c.Assert(osutil.FileExists(mockDesktopInstanceFilePath), Equals, true)
   137  
   138  	// restore the non-instance file
   139  	err = ioutil.WriteFile(mockDesktopFilePath, mockDesktopFile, 0644)
   140  	c.Assert(err, IsNil)
   141  
   142  	s.mockUpdateDesktopDatabase.ForgetCalls()
   143  
   144  	info.InstanceKey = "instance"
   145  	err = wrappers.RemoveSnapDesktopFiles(info)
   146  	c.Assert(err, IsNil)
   147  	c.Assert(osutil.FileExists(mockDesktopInstanceFilePath), Equals, false)
   148  	c.Assert(s.mockUpdateDesktopDatabase.Calls(), DeepEquals, [][]string{
   149  		{"update-desktop-database", dirs.SnapDesktopFilesDir},
   150  	})
   151  	// foo file is still there
   152  	c.Assert(osutil.FileExists(mockDesktopFilePath), Equals, true)
   153  }
   154  
   155  func (s *desktopSuite) TestAddPackageDesktopFilesCleanup(c *C) {
   156  	mockDesktopFilePath := filepath.Join(dirs.SnapDesktopFilesDir, "foo_foobar1.desktop")
   157  	c.Assert(osutil.FileExists(mockDesktopFilePath), Equals, false)
   158  
   159  	err := os.MkdirAll(dirs.SnapDesktopFilesDir, 0755)
   160  	c.Assert(err, IsNil)
   161  
   162  	mockDesktopInstanceFilePath := filepath.Join(dirs.SnapDesktopFilesDir, "foo+instance_foobar.desktop")
   163  	err = ioutil.WriteFile(mockDesktopInstanceFilePath, mockDesktopFile, 0644)
   164  	c.Assert(err, IsNil)
   165  
   166  	err = os.MkdirAll(filepath.Join(dirs.SnapDesktopFilesDir, "foo_foobar2.desktop", "potato"), 0755)
   167  	c.Assert(err, IsNil)
   168  
   169  	info := snaptest.MockSnap(c, desktopAppYaml, &snap.SideInfo{Revision: snap.R(11)})
   170  
   171  	// generate .desktop file in the package baseDir
   172  	baseDir := info.MountDir()
   173  	err = os.MkdirAll(filepath.Join(baseDir, "meta", "gui"), 0755)
   174  	c.Assert(err, IsNil)
   175  
   176  	err = ioutil.WriteFile(filepath.Join(baseDir, "meta", "gui", "foobar1.desktop"), mockDesktopFile, 0644)
   177  	c.Assert(err, IsNil)
   178  	err = ioutil.WriteFile(filepath.Join(baseDir, "meta", "gui", "foobar2.desktop"), mockDesktopFile, 0644)
   179  	c.Assert(err, IsNil)
   180  
   181  	err = wrappers.AddSnapDesktopFiles(info)
   182  	c.Check(err, NotNil)
   183  	c.Check(osutil.FileExists(mockDesktopFilePath), Equals, false)
   184  	c.Check(s.mockUpdateDesktopDatabase.Calls(), HasLen, 0)
   185  	// foo+instance file was not removed by cleanup
   186  	c.Check(osutil.FileExists(mockDesktopInstanceFilePath), Equals, true)
   187  }
   188  
   189  // sanitize
   190  
   191  type sanitizeDesktopFileSuite struct {
   192  	testutil.BaseTest
   193  }
   194  
   195  var _ = Suite(&sanitizeDesktopFileSuite{})
   196  
   197  func (s *sanitizeDesktopFileSuite) SetUpTest(c *C) {
   198  	s.BaseTest.SetUpTest(c)
   199  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
   200  }
   201  
   202  func (s *sanitizeDesktopFileSuite) TearDownTest(c *C) {
   203  	s.BaseTest.TearDownTest(c)
   204  }
   205  
   206  func (s *sanitizeDesktopFileSuite) TestSanitizeIgnoreNotWhitelisted(c *C) {
   207  	snap := &snap.Info{SideInfo: snap.SideInfo{RealName: "foo", Revision: snap.R(12)}}
   208  	desktopContent := []byte(`[Desktop Entry]
   209  Name=foo
   210  UnknownKey=baz
   211  nonsense
   212  Icon=${SNAP}/meep
   213  
   214  # the empty line above is fine`)
   215  
   216  	e := wrappers.SanitizeDesktopFile(snap, "foo.desktop", desktopContent)
   217  	c.Assert(string(e), Equals, fmt.Sprintf(`[Desktop Entry]
   218  X-SnapInstanceName=foo
   219  Name=foo
   220  Icon=%s/foo/12/meep
   221  
   222  # the empty line above is fine
   223  `, dirs.SnapMountDir))
   224  }
   225  
   226  func (s *sanitizeDesktopFileSuite) TestSanitizeFiltersExec(c *C) {
   227  	snap, err := snap.InfoFromSnapYaml([]byte(`
   228  name: snap
   229  version: 1.0
   230  apps:
   231   app:
   232    command: cmd
   233  `))
   234  	c.Assert(err, IsNil)
   235  	desktopContent := []byte(`[Desktop Entry]
   236  Name=foo
   237  Exec=baz
   238  `)
   239  
   240  	e := wrappers.SanitizeDesktopFile(snap, "foo.desktop", desktopContent)
   241  	c.Assert(string(e), Equals, `[Desktop Entry]
   242  X-SnapInstanceName=snap
   243  Name=foo
   244  `)
   245  }
   246  
   247  func (s *sanitizeDesktopFileSuite) TestSanitizeFiltersExecPrefix(c *C) {
   248  	snap, err := snap.InfoFromSnapYaml([]byte(`
   249  name: snap
   250  version: 1.0
   251  apps:
   252   app:
   253    command: cmd
   254  `))
   255  	c.Assert(err, IsNil)
   256  	desktopContent := []byte(`[Desktop Entry]
   257  X-SnapInstanceName=snap
   258  Name=foo
   259  Exec=snap.app.evil.evil
   260  `)
   261  
   262  	e := wrappers.SanitizeDesktopFile(snap, "foo.desktop", desktopContent)
   263  	c.Assert(string(e), Equals, `[Desktop Entry]
   264  X-SnapInstanceName=snap
   265  Name=foo
   266  `)
   267  }
   268  
   269  func (s *sanitizeDesktopFileSuite) TestSanitizeFiltersExecRewriteFromDesktop(c *C) {
   270  	snap, err := snap.InfoFromSnapYaml([]byte(`
   271  name: snap
   272  version: 1.0
   273  apps:
   274   app:
   275    command: cmd
   276  `))
   277  	c.Assert(err, IsNil)
   278  	desktopContent := []byte(`[Desktop Entry]
   279  X-SnapInstanceName=snap
   280  Name=foo
   281  Exec=snap.app.evil.evil
   282  `)
   283  
   284  	e := wrappers.SanitizeDesktopFile(snap, "app.desktop", desktopContent)
   285  	c.Assert(string(e), Equals, fmt.Sprintf(`[Desktop Entry]
   286  X-SnapInstanceName=snap
   287  Name=foo
   288  Exec=env BAMF_DESKTOP_FILE_HINT=app.desktop %s/bin/snap.app
   289  `, dirs.SnapMountDir))
   290  }
   291  
   292  func (s *sanitizeDesktopFileSuite) TestSanitizeFiltersExecOk(c *C) {
   293  	snap, err := snap.InfoFromSnapYaml([]byte(`
   294  name: snap
   295  version: 1.0
   296  apps:
   297   app:
   298    command: cmd
   299  `))
   300  	c.Assert(err, IsNil)
   301  	desktopContent := []byte(`[Desktop Entry]
   302  Name=foo
   303  Exec=snap.app %U
   304  `)
   305  
   306  	e := wrappers.SanitizeDesktopFile(snap, "foo.desktop", desktopContent)
   307  	c.Assert(string(e), Equals, fmt.Sprintf(`[Desktop Entry]
   308  X-SnapInstanceName=snap
   309  Name=foo
   310  Exec=env BAMF_DESKTOP_FILE_HINT=foo.desktop %s/bin/snap.app %%U
   311  `, dirs.SnapMountDir))
   312  }
   313  
   314  // we do not support TryExec (even if its a valid line), this test ensures
   315  // we do not accidentally enable it
   316  func (s *sanitizeDesktopFileSuite) TestSanitizeFiltersTryExecIgnored(c *C) {
   317  	snap, err := snap.InfoFromSnapYaml([]byte(`
   318  name: snap
   319  version: 1.0
   320  apps:
   321   app:
   322    command: cmd
   323  `))
   324  	c.Assert(err, IsNil)
   325  	desktopContent := []byte(`[Desktop Entry]
   326  Name=foo
   327  TryExec=snap.app %U
   328  `)
   329  
   330  	e := wrappers.SanitizeDesktopFile(snap, "foo.desktop", desktopContent)
   331  	c.Assert(string(e), Equals, `[Desktop Entry]
   332  X-SnapInstanceName=snap
   333  Name=foo
   334  `)
   335  }
   336  
   337  func (s *sanitizeDesktopFileSuite) TestSanitizeWorthWithI18n(c *C) {
   338  	snap := &snap.Info{SideInfo: snap.SideInfo{RealName: "snap"}}
   339  	desktopContent := []byte(`[Desktop Entry]
   340  Name=foo
   341  GenericName=bar
   342  GenericName[de]=einsehrlangeszusammengesetzteswort
   343  GenericName[tlh_TLH]=Qapla'
   344  GenericName[ca@valencia]=Hola!
   345  Invalid=key
   346  Invalid[i18n]=key
   347  `)
   348  
   349  	e := wrappers.SanitizeDesktopFile(snap, "foo.desktop", desktopContent)
   350  	c.Assert(string(e), Equals, `[Desktop Entry]
   351  X-SnapInstanceName=snap
   352  Name=foo
   353  GenericName=bar
   354  GenericName[de]=einsehrlangeszusammengesetzteswort
   355  GenericName[tlh_TLH]=Qapla'
   356  GenericName[ca@valencia]=Hola!
   357  `)
   358  }
   359  
   360  func (s *sanitizeDesktopFileSuite) TestSanitizeDesktopActionsOk(c *C) {
   361  	snap := &snap.Info{}
   362  	desktopContent := []byte("[Desktop Action is-ok]\n")
   363  
   364  	e := wrappers.SanitizeDesktopFile(snap, "foo.desktop", desktopContent)
   365  	c.Assert(string(e), Equals, string(desktopContent))
   366  }
   367  
   368  func (s *sanitizeDesktopFileSuite) TestSanitizeDesktopFileAyatana(c *C) {
   369  	snap := &snap.Info{SideInfo: snap.SideInfo{RealName: "snap"}}
   370  
   371  	desktopContent := []byte(`[Desktop Entry]
   372  X-SnapInstanceName=snap
   373  Version=1.0
   374  Name=Firefox Web Browser
   375  X-Ayatana-Desktop-Shortcuts=NewWindow;Private
   376  
   377  [NewWindow Shortcut Group]
   378  Name=Open a New Window
   379  TargetEnvironment=Unity
   380  
   381  [Private Shortcut Group]
   382  Name=Private Mode
   383  TargetEnvironment=Unity
   384  `)
   385  
   386  	e := wrappers.SanitizeDesktopFile(snap, "foo.desktop", desktopContent)
   387  	c.Assert(string(e), Equals, string(desktopContent))
   388  }
   389  
   390  func (s *sanitizeDesktopFileSuite) TestSanitizeParallelInstancesPlain(c *C) {
   391  	snap, err := snap.InfoFromSnapYaml([]byte(`
   392  name: snap
   393  version: 1.0
   394  apps:
   395   app:
   396    command: cmd
   397  `))
   398  	snap.InstanceKey = "bar"
   399  	c.Assert(err, IsNil)
   400  	desktopContent := []byte(`[Desktop Entry]
   401  Name=foo
   402  Exec=snap.app
   403  `)
   404  	df := filepath.Base(snap.Apps["app"].DesktopFile())
   405  	e := wrappers.SanitizeDesktopFile(snap, df, desktopContent)
   406  	c.Assert(string(e), Equals, fmt.Sprintf(`[Desktop Entry]
   407  X-SnapInstanceName=snap_bar
   408  Name=foo
   409  Exec=env BAMF_DESKTOP_FILE_HINT=snap+bar_app.desktop %s/bin/snap_bar.app
   410  `, dirs.SnapMountDir))
   411  }
   412  
   413  func (s *sanitizeDesktopFileSuite) TestSanitizeParallelInstancesWithArgs(c *C) {
   414  	snap, err := snap.InfoFromSnapYaml([]byte(`
   415  name: snap
   416  version: 1.0
   417  apps:
   418   app:
   419    command: cmd
   420  `))
   421  	snap.InstanceKey = "bar"
   422  	c.Assert(err, IsNil)
   423  	desktopContent := []byte(`[Desktop Entry]
   424  Name=foo
   425  Exec=snap.app %U
   426  `)
   427  
   428  	df := filepath.Base(snap.Apps["app"].DesktopFile())
   429  	e := wrappers.SanitizeDesktopFile(snap, df, desktopContent)
   430  	c.Assert(string(e), Equals, fmt.Sprintf(`[Desktop Entry]
   431  X-SnapInstanceName=snap_bar
   432  Name=foo
   433  Exec=env BAMF_DESKTOP_FILE_HINT=snap+bar_app.desktop %s/bin/snap_bar.app %%U
   434  `, dirs.SnapMountDir))
   435  }
   436  
   437  func (s *sanitizeDesktopFileSuite) TestRewriteExecLineInvalid(c *C) {
   438  	snap := &snap.Info{}
   439  	_, err := wrappers.RewriteExecLine(snap, "foo.desktop", "Exec=invalid")
   440  	c.Assert(err, ErrorMatches, `invalid exec command: "invalid"`)
   441  }
   442  
   443  func (s *sanitizeDesktopFileSuite) TestRewriteExecLineOk(c *C) {
   444  	snap, err := snap.InfoFromSnapYaml([]byte(`
   445  name: snap
   446  version: 1.0
   447  apps:
   448   app:
   449    command: cmd
   450  `))
   451  	c.Assert(err, IsNil)
   452  
   453  	newl, err := wrappers.RewriteExecLine(snap, "foo.desktop", "Exec=snap.app")
   454  	c.Assert(err, IsNil)
   455  	c.Assert(newl, Equals, fmt.Sprintf("Exec=env BAMF_DESKTOP_FILE_HINT=foo.desktop %s/bin/snap.app", dirs.SnapMountDir))
   456  }
   457  
   458  func (s *sanitizeDesktopFileSuite) TestLangLang(c *C) {
   459  	langs := []struct {
   460  		line    string
   461  		isValid bool
   462  	}{
   463  		// langCodes
   464  		{"Name[lang]=lang-alone", true},
   465  		{"Name[_COUNTRY]=country-alone", false},
   466  		{"Name[.ENC-0DING]=encoding-alone", false},
   467  		{"Name[@modifier]=modifier-alone", false},
   468  		{"Name[lang_COUNTRY]=lang+country", true},
   469  		{"Name[lang.ENC-0DING]=lang+encoding", true},
   470  		{"Name[lang@modifier]=lang+modifier", true},
   471  		// could also test all bad combos of 2, and all combos of 3...
   472  		{"Name[lang_COUNTRY.ENC-0DING@modifier]=all", true},
   473  		// other localised entries
   474  		{"GenericName[xx]=a", true},
   475  		{"Comment[xx]=b", true},
   476  		{"Keywords[xx]=b", true},
   477  		// bad ones
   478  		{"Name[foo=bar", false},
   479  		{"Icon[xx]=bar", false},
   480  	}
   481  	for _, t := range langs {
   482  		c.Assert(wrappers.IsValidDesktopFileLine([]byte(t.line)), Equals, t.isValid)
   483  	}
   484  }
   485  
   486  func (s *sanitizeDesktopFileSuite) TestRewriteIconLine(c *C) {
   487  	snap, err := snap.InfoFromSnapYaml([]byte(`
   488  name: snap
   489  version: 1.0
   490  `))
   491  	c.Assert(err, IsNil)
   492  
   493  	newl, err := wrappers.RewriteIconLine(snap, "Icon=${SNAP}/icon.png")
   494  	c.Check(err, IsNil)
   495  	c.Check(newl, Equals, "Icon=${SNAP}/icon.png")
   496  
   497  	newl, err = wrappers.RewriteIconLine(snap, "Icon=snap.snap.icon")
   498  	c.Check(err, IsNil)
   499  	c.Check(newl, Equals, "Icon=snap.snap.icon")
   500  
   501  	newl, err = wrappers.RewriteIconLine(snap, "Icon=other-icon")
   502  	c.Check(err, IsNil)
   503  	c.Check(newl, Equals, "Icon=other-icon")
   504  
   505  	snap.InstanceKey = "bar"
   506  	newl, err = wrappers.RewriteIconLine(snap, "Icon=snap.snap.icon")
   507  	c.Check(err, IsNil)
   508  	c.Check(newl, Equals, "Icon=snap.snap_bar.icon")
   509  
   510  	_, err = wrappers.RewriteIconLine(snap, "Icon=snap.othersnap.icon")
   511  	c.Check(err, ErrorMatches, `invalid icon name: "snap.othersnap.icon", must start with "snap.snap."`)
   512  
   513  	_, err = wrappers.RewriteIconLine(snap, "Icon=/etc/passwd")
   514  	c.Check(err, ErrorMatches, `icon path "/etc/passwd" is not part of the snap`)
   515  
   516  	_, err = wrappers.RewriteIconLine(snap, "Icon=${SNAP}/./icon.png")
   517  	c.Check(err, ErrorMatches, `icon path "\${SNAP}/./icon.png" is not canonicalized, did you mean "\${SNAP}/icon.png"\?`)
   518  
   519  	_, err = wrappers.RewriteIconLine(snap, "Icon=${SNAP}/../outside/icon.png")
   520  	c.Check(err, ErrorMatches, `icon path "\${SNAP}/../outside/icon.png" is not canonicalized, did you mean "outside/icon.png"\?`)
   521  }
   522  
   523  func (s *sanitizeDesktopFileSuite) TestSanitizeParallelInstancesIconName(c *C) {
   524  	snap, err := snap.InfoFromSnapYaml([]byte(`
   525  name: snap
   526  version: 1.0
   527  apps:
   528   app:
   529    command: cmd
   530  `))
   531  	snap.InstanceKey = "bar"
   532  	c.Assert(err, IsNil)
   533  	desktopContent := []byte(`[Desktop Entry]
   534  Name=foo
   535  Icon=snap.snap.icon
   536  Exec=snap.app
   537  `)
   538  	df := filepath.Base(snap.Apps["app"].DesktopFile())
   539  	e := wrappers.SanitizeDesktopFile(snap, df, desktopContent)
   540  	c.Assert(string(e), Equals, fmt.Sprintf(`[Desktop Entry]
   541  X-SnapInstanceName=snap_bar
   542  Name=foo
   543  Icon=snap.snap_bar.icon
   544  Exec=env BAMF_DESKTOP_FILE_HINT=snap+bar_app.desktop %s/bin/snap_bar.app
   545  `, dirs.SnapMountDir))
   546  }
   547  
   548  func (s *desktopSuite) TestAddRemoveDesktopFiles(c *C) {
   549  	var tests = []struct {
   550  		instance                string
   551  		upstreamDesktopFileName string
   552  
   553  		expectedDesktopFileName string
   554  	}{
   555  		// normal cases
   556  		{"", "upstream.desktop", "foo_upstream.desktop"},
   557  		{"instance", "upstream.desktop", "foo+instance_upstream.desktop"},
   558  		// pathological cases are handled
   559  		{"", "instance.desktop", "foo_instance.desktop"},
   560  		{"instance", "instance.desktop", "foo+instance_instance.desktop"},
   561  	}
   562  
   563  	for _, t := range tests {
   564  		expectedDesktopFilePath := filepath.Join(dirs.SnapDesktopFilesDir, t.expectedDesktopFileName)
   565  		c.Assert(osutil.FileExists(expectedDesktopFilePath), Equals, false)
   566  
   567  		info := snaptest.MockSnap(c, desktopAppYaml, &snap.SideInfo{Revision: snap.R(11)})
   568  		info.InstanceKey = t.instance
   569  
   570  		// generate .desktop file in the package baseDir
   571  		baseDir := info.MountDir()
   572  		err := os.MkdirAll(filepath.Join(baseDir, "meta", "gui"), 0755)
   573  		c.Assert(err, IsNil)
   574  
   575  		err = ioutil.WriteFile(filepath.Join(baseDir, "meta", "gui", t.upstreamDesktopFileName), mockDesktopFile, 0644)
   576  		c.Assert(err, IsNil)
   577  
   578  		err = wrappers.AddSnapDesktopFiles(info)
   579  		c.Assert(err, IsNil)
   580  		c.Assert(osutil.FileExists(expectedDesktopFilePath), Equals, true)
   581  
   582  		// Ensure that the old-style parallel install desktop file was
   583  		// not created.
   584  		if t.instance != "" {
   585  			unexpectedOldStyleDesktopFilePath := strings.Replace(expectedDesktopFilePath, "+", "_", 1)
   586  			c.Assert(osutil.FileExists(unexpectedOldStyleDesktopFilePath), Equals, false)
   587  		}
   588  
   589  		// remove it again
   590  		err = wrappers.RemoveSnapDesktopFiles(info)
   591  		c.Assert(err, IsNil)
   592  		c.Assert(osutil.FileExists(expectedDesktopFilePath), Equals, false)
   593  	}
   594  }