github.com/ethanhsieh/snapd@v0.0.0-20210615102523-3db9b8e4edc5/usersession/xdgopenproxy/portal_launcher_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package xdgopenproxy_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "syscall" 28 "time" 29 30 "github.com/godbus/dbus" 31 . "gopkg.in/check.v1" 32 33 "github.com/snapcore/snapd/testutil" 34 "github.com/snapcore/snapd/usersession/xdgopenproxy" 35 ) 36 37 type portalSuite struct { 38 testutil.DBusTest 39 40 portal *fakePortal 41 request *fakePortalRequest 42 43 openError *dbus.Error 44 sendResponse bool 45 openResponse uint32 46 calls []string 47 } 48 49 var _ = Suite(&portalSuite{}) 50 51 const portalRequestPath = "/org/freedesktop/portal/desktop/request/1" 52 53 func (s *portalSuite) SetUpSuite(c *C) { 54 s.DBusTest.SetUpSuite(c) 55 56 s.portal = &fakePortal{s} 57 err := s.SessionBus.Export(s.portal, xdgopenproxy.DesktopPortalObjectPath, xdgopenproxy.DesktopPortalOpenURIIface) 58 c.Assert(err, IsNil) 59 s.request = &fakePortalRequest{s} 60 err = s.SessionBus.Export(s.request, portalRequestPath, xdgopenproxy.DesktopPortalRequestIface) 61 c.Assert(err, IsNil) 62 63 _, err = s.SessionBus.RequestName(xdgopenproxy.DesktopPortalBusName, dbus.NameFlagAllowReplacement|dbus.NameFlagReplaceExisting) 64 c.Assert(err, IsNil) 65 } 66 67 func (s *portalSuite) TearDownSuite(c *C) { 68 if s.SessionBus != nil { 69 _, err := s.SessionBus.ReleaseName(xdgopenproxy.DesktopPortalBusName) 70 c.Check(err, IsNil) 71 } 72 73 s.DBusTest.TearDownSuite(c) 74 } 75 76 func (s *portalSuite) SetUpTest(c *C) { 77 s.DBusTest.SetUpTest(c) 78 79 s.openError = nil 80 s.openResponse = 0 81 s.sendResponse = true 82 s.calls = nil 83 } 84 85 func (s *portalSuite) TestOpenFile(c *C) { 86 launcher := &xdgopenproxy.PortalLauncher{} 87 88 path := filepath.Join(c.MkDir(), "test.txt") 89 c.Assert(ioutil.WriteFile(path, []byte("hello world"), 0644), IsNil) 90 91 err := launcher.OpenFile(s.SessionBus, path) 92 c.Check(err, IsNil) 93 c.Check(s.calls, DeepEquals, []string{ 94 "OpenFile", 95 }) 96 } 97 98 func (s *portalSuite) TestOpenFileCallError(c *C) { 99 s.openError = dbus.MakeFailedError(fmt.Errorf("failure")) 100 101 launcher := &xdgopenproxy.PortalLauncher{} 102 103 path := filepath.Join(c.MkDir(), "test.txt") 104 c.Assert(ioutil.WriteFile(path, []byte("hello world"), 0644), IsNil) 105 106 err := launcher.OpenFile(s.SessionBus, path) 107 c.Check(err, FitsTypeOf, dbus.Error{}) 108 c.Check(err, ErrorMatches, "failure") 109 c.Check(s.calls, DeepEquals, []string{ 110 "OpenFile", 111 }) 112 } 113 114 func (s *portalSuite) TestOpenFileResponseError(c *C) { 115 s.openResponse = 2 116 117 launcher := &xdgopenproxy.PortalLauncher{} 118 119 path := filepath.Join(c.MkDir(), "test.txt") 120 c.Assert(ioutil.WriteFile(path, []byte("hello world"), 0644), IsNil) 121 122 err := launcher.OpenFile(s.SessionBus, path) 123 c.Check(err, FitsTypeOf, (*xdgopenproxy.ResponseError)(nil)) 124 c.Check(err, ErrorMatches, `request declined by the user \(code 2\)`) 125 c.Check(s.calls, DeepEquals, []string{ 126 "OpenFile", 127 }) 128 } 129 130 func (s *portalSuite) TestOpenFileTimeout(c *C) { 131 s.sendResponse = false 132 restore := xdgopenproxy.MockPortalTimeout(5 * time.Millisecond) 133 defer restore() 134 135 launcher := &xdgopenproxy.PortalLauncher{} 136 137 file := filepath.Join(c.MkDir(), "test.txt") 138 c.Assert(ioutil.WriteFile(file, []byte("hello world"), 0644), IsNil) 139 140 err := launcher.OpenFile(s.SessionBus, file) 141 c.Check(err, FitsTypeOf, (*xdgopenproxy.ResponseError)(nil)) 142 c.Check(err, ErrorMatches, "timeout waiting for user response") 143 c.Check(s.calls, DeepEquals, []string{ 144 "OpenFile", 145 "Request.Close", 146 }) 147 } 148 149 func (s *portalSuite) TestOpenDir(c *C) { 150 launcher := &xdgopenproxy.PortalLauncher{} 151 152 dir := c.MkDir() 153 err := launcher.OpenFile(s.SessionBus, dir) 154 c.Check(err, IsNil) 155 c.Check(s.calls, DeepEquals, []string{ 156 "OpenFile", 157 }) 158 } 159 160 func (s *portalSuite) TestOpenMissingFile(c *C) { 161 launcher := &xdgopenproxy.PortalLauncher{} 162 163 path := filepath.Join(c.MkDir(), "no-such-file.txt") 164 err := launcher.OpenFile(s.SessionBus, path) 165 c.Check(err, ErrorMatches, "no such file or directory") 166 c.Check(s.calls, HasLen, 0) 167 } 168 169 func (s *portalSuite) TestOpenUnreadableFile(c *C) { 170 launcher := &xdgopenproxy.PortalLauncher{} 171 172 path := filepath.Join(c.MkDir(), "test.txt") 173 c.Assert(ioutil.WriteFile(path, []byte("hello world"), 0644), IsNil) 174 c.Assert(os.Chmod(path, 0), IsNil) 175 176 err := launcher.OpenFile(s.SessionBus, path) 177 c.Check(err, ErrorMatches, "permission denied") 178 c.Check(s.calls, HasLen, 0) 179 } 180 181 func (s *portalSuite) TestOpenURI(c *C) { 182 launcher := &xdgopenproxy.PortalLauncher{} 183 184 err := launcher.OpenURI(s.SessionBus, "http://example.com") 185 c.Check(err, IsNil) 186 c.Check(s.calls, DeepEquals, []string{ 187 "OpenURI http://example.com", 188 }) 189 } 190 191 func (s *portalSuite) TestOpenURICallError(c *C) { 192 s.openError = dbus.MakeFailedError(fmt.Errorf("failure")) 193 194 launcher := &xdgopenproxy.PortalLauncher{} 195 err := launcher.OpenURI(s.SessionBus, "http://example.com") 196 c.Check(err, FitsTypeOf, dbus.Error{}) 197 c.Check(err, ErrorMatches, "failure") 198 c.Check(s.calls, DeepEquals, []string{ 199 "OpenURI http://example.com", 200 }) 201 } 202 203 func (s *portalSuite) TestOpenURIResponseError(c *C) { 204 s.openResponse = 2 205 206 launcher := &xdgopenproxy.PortalLauncher{} 207 err := launcher.OpenURI(s.SessionBus, "http://example.com") 208 c.Check(err, FitsTypeOf, (*xdgopenproxy.ResponseError)(nil)) 209 c.Check(err, ErrorMatches, `request declined by the user \(code 2\)`) 210 c.Check(s.calls, DeepEquals, []string{ 211 "OpenURI http://example.com", 212 }) 213 } 214 215 func (s *portalSuite) TestOpenURITimeout(c *C) { 216 s.sendResponse = false 217 restore := xdgopenproxy.MockPortalTimeout(5 * time.Millisecond) 218 defer restore() 219 220 launcher := &xdgopenproxy.PortalLauncher{} 221 err := launcher.OpenURI(s.SessionBus, "http://example.com") 222 c.Check(err, FitsTypeOf, (*xdgopenproxy.ResponseError)(nil)) 223 c.Check(err, ErrorMatches, "timeout waiting for user response") 224 c.Check(s.calls, DeepEquals, []string{ 225 "OpenURI http://example.com", 226 "Request.Close", 227 }) 228 } 229 230 type fakePortal struct { 231 *portalSuite 232 } 233 234 func (p *fakePortal) OpenFile(parent string, clientFD dbus.UnixFD, options map[string]dbus.Variant) (dbus.ObjectPath, *dbus.Error) { 235 p.calls = append(p.calls, "OpenFile") 236 237 fd := int(clientFD) 238 defer syscall.Close(fd) 239 240 if p.sendResponse { 241 var results map[string]dbus.Variant 242 p.SessionBus.Emit(portalRequestPath, xdgopenproxy.DesktopPortalRequestIface+".Response", p.openResponse, results) 243 } 244 return portalRequestPath, p.openError 245 } 246 247 func (p *fakePortal) OpenURI(parent, uri string, options map[string]dbus.Variant) (dbus.ObjectPath, *dbus.Error) { 248 p.calls = append(p.calls, "OpenURI "+uri) 249 if p.sendResponse { 250 var results map[string]dbus.Variant 251 p.SessionBus.Emit(portalRequestPath, xdgopenproxy.DesktopPortalRequestIface+".Response", p.openResponse, results) 252 } 253 return portalRequestPath, p.openError 254 } 255 256 type fakePortalRequest struct { 257 *portalSuite 258 } 259 260 func (r *fakePortalRequest) Close() *dbus.Error { 261 r.calls = append(r.calls, "Request.Close") 262 return nil 263 }