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  }