github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap/cmd_connections_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2018 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  	"net/http"
    26  	"net/url"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/client"
    31  	. "github.com/snapcore/snapd/cmd/snap"
    32  )
    33  
    34  func (s *SnapSuite) TestConnectionsNoneConnected(c *C) {
    35  	result := client.Connections{}
    36  	query := url.Values{}
    37  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
    38  		c.Check(r.Method, Equals, "GET")
    39  		c.Check(r.URL.Path, Equals, "/v2/connections")
    40  		c.Check(r.URL.Query(), DeepEquals, query)
    41  		body, err := ioutil.ReadAll(r.Body)
    42  		c.Check(err, IsNil)
    43  		c.Check(body, DeepEquals, []byte{})
    44  		EncodeResponseBody(c, w, map[string]interface{}{
    45  			"type":   "sync",
    46  			"result": result,
    47  		})
    48  	})
    49  	_, err := Parser(Client()).ParseArgs([]string{"connections"})
    50  	c.Check(err, IsNil)
    51  	c.Assert(s.Stdout(), Equals, "")
    52  	c.Assert(s.Stderr(), Equals, "")
    53  
    54  	s.ResetStdStreams()
    55  
    56  	query = url.Values{
    57  		"select": []string{"all"},
    58  	}
    59  	_, err = Parser(Client()).ParseArgs([]string{"connections", "--all"})
    60  	c.Check(err, IsNil)
    61  	c.Assert(s.Stdout(), Equals, "")
    62  	c.Assert(s.Stderr(), Equals, "")
    63  }
    64  
    65  func (s *SnapSuite) TestConnectionsNotInstalled(c *C) {
    66  	query := url.Values{
    67  		"snap":   []string{"foo"},
    68  		"select": []string{"all"},
    69  	}
    70  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
    71  		c.Check(r.Method, Equals, "GET")
    72  		c.Check(r.URL.Path, Equals, "/v2/connections")
    73  		c.Check(r.URL.Query(), DeepEquals, query)
    74  		body, err := ioutil.ReadAll(r.Body)
    75  		c.Check(err, IsNil)
    76  		c.Check(body, DeepEquals, []byte{})
    77  		fmt.Fprintln(w, `{"type": "error", "result": {"message": "not found", "value": "foo", "kind": "snap-not-found"}, "status-code": 404}`)
    78  	})
    79  	_, err := Parser(Client()).ParseArgs([]string{"connections", "foo"})
    80  	c.Check(err, ErrorMatches, `not found`)
    81  	c.Assert(s.Stdout(), Equals, "")
    82  	c.Assert(s.Stderr(), Equals, "")
    83  }
    84  
    85  func (s *SnapSuite) TestConnectionsNoneConnectedPlugs(c *C) {
    86  	query := url.Values{
    87  		"select": []string{"all"},
    88  	}
    89  	result := client.Connections{
    90  		Plugs: []client.Plug{
    91  			{
    92  				Snap:      "keyboard-lights",
    93  				Name:      "capslock-led",
    94  				Interface: "leds",
    95  			},
    96  		},
    97  	}
    98  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
    99  		c.Check(r.Method, Equals, "GET")
   100  		c.Check(r.URL.Path, Equals, "/v2/connections")
   101  		c.Check(r.URL.Query(), DeepEquals, query)
   102  		body, err := ioutil.ReadAll(r.Body)
   103  		c.Check(err, IsNil)
   104  		c.Check(body, DeepEquals, []byte{})
   105  		EncodeResponseBody(c, w, map[string]interface{}{
   106  			"type":   "sync",
   107  			"result": result,
   108  		})
   109  	})
   110  
   111  	rest, err := Parser(Client()).ParseArgs([]string{"connections", "--all"})
   112  	c.Assert(err, IsNil)
   113  	c.Assert(rest, DeepEquals, []string{})
   114  	expectedStdout := "" +
   115  		"Interface  Plug                          Slot  Notes\n" +
   116  		"leds       keyboard-lights:capslock-led  -     -\n"
   117  	c.Assert(s.Stdout(), Equals, expectedStdout)
   118  	c.Assert(s.Stderr(), Equals, "")
   119  
   120  	s.ResetStdStreams()
   121  
   122  	query = url.Values{
   123  		"select": []string{"all"},
   124  		"snap":   []string{"keyboard-lights"},
   125  	}
   126  
   127  	rest, err = Parser(Client()).ParseArgs([]string{"connections", "keyboard-lights"})
   128  	c.Assert(err, IsNil)
   129  	c.Assert(rest, DeepEquals, []string{})
   130  	expectedStdout = "" +
   131  		"Interface  Plug                          Slot  Notes\n" +
   132  		"leds       keyboard-lights:capslock-led  -     -\n"
   133  	c.Assert(s.Stdout(), Equals, expectedStdout)
   134  	c.Assert(s.Stderr(), Equals, "")
   135  }
   136  
   137  func (s *SnapSuite) TestConnectionsNoneConnectedSlots(c *C) {
   138  	result := client.Connections{}
   139  	query := url.Values{}
   140  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   141  		c.Check(r.Method, Equals, "GET")
   142  		c.Check(r.URL.Path, Equals, "/v2/connections")
   143  		c.Check(r.URL.Query(), DeepEquals, query)
   144  		body, err := ioutil.ReadAll(r.Body)
   145  		c.Check(err, IsNil)
   146  		c.Check(body, DeepEquals, []byte{})
   147  		EncodeResponseBody(c, w, map[string]interface{}{
   148  			"type":   "sync",
   149  			"result": result,
   150  		})
   151  	})
   152  	_, err := Parser(Client()).ParseArgs([]string{"connections"})
   153  	c.Check(err, IsNil)
   154  	c.Assert(s.Stdout(), Equals, "")
   155  	c.Assert(s.Stderr(), Equals, "")
   156  
   157  	s.ResetStdStreams()
   158  
   159  	query = url.Values{
   160  		"select": []string{"all"},
   161  	}
   162  	result = client.Connections{
   163  		Slots: []client.Slot{
   164  			{
   165  				Snap:      "leds-provider",
   166  				Name:      "capslock-led",
   167  				Interface: "leds",
   168  			},
   169  		},
   170  	}
   171  	rest, err := Parser(Client()).ParseArgs([]string{"connections", "--all"})
   172  	c.Assert(err, IsNil)
   173  	c.Assert(rest, DeepEquals, []string{})
   174  	expectedStdout := "" +
   175  		"Interface  Plug  Slot                        Notes\n" +
   176  		"leds       -     leds-provider:capslock-led  -\n"
   177  	c.Assert(s.Stdout(), Equals, expectedStdout)
   178  	c.Assert(s.Stderr(), Equals, "")
   179  }
   180  
   181  func (s *SnapSuite) TestConnectionsSomeConnected(c *C) {
   182  	result := client.Connections{
   183  		Established: []client.Connection{
   184  			{
   185  				Plug:      client.PlugRef{Snap: "keyboard-lights", Name: "capslock"},
   186  				Slot:      client.SlotRef{Snap: "leds-provider", Name: "capslock-led"},
   187  				Interface: "leds",
   188  				Gadget:    true,
   189  			}, {
   190  				Plug:      client.PlugRef{Snap: "keyboard-lights", Name: "numlock"},
   191  				Slot:      client.SlotRef{Snap: "core", Name: "numlock-led"},
   192  				Interface: "leds",
   193  				Manual:    true,
   194  			}, {
   195  				Plug:      client.PlugRef{Snap: "keyboard-lights", Name: "scrollock"},
   196  				Slot:      client.SlotRef{Snap: "core", Name: "scrollock-led"},
   197  				Interface: "leds",
   198  			},
   199  		},
   200  		Plugs: []client.Plug{
   201  			{
   202  				Snap:      "keyboard-lights",
   203  				Name:      "capslock",
   204  				Interface: "leds",
   205  				Connections: []client.SlotRef{{
   206  					Snap: "leds-provider",
   207  					Name: "capslock-led",
   208  				}},
   209  			}, {
   210  				Snap:      "keyboard-lights",
   211  				Name:      "numlock",
   212  				Interface: "leds",
   213  				Connections: []client.SlotRef{{
   214  					Snap: "core",
   215  					Name: "numlock-led",
   216  				}},
   217  			}, {
   218  				Snap:      "keyboard-lights",
   219  				Name:      "scrollock",
   220  				Interface: "leds",
   221  				Connections: []client.SlotRef{{
   222  					Snap: "core",
   223  					Name: "scrollock-led",
   224  				}},
   225  			},
   226  		},
   227  		Slots: []client.Slot{
   228  			{
   229  				Snap:      "core",
   230  				Name:      "numlock-led",
   231  				Interface: "leds",
   232  				Connections: []client.PlugRef{{
   233  					Snap: "keyuboard-lights",
   234  					Name: "numlock",
   235  				}},
   236  			}, {
   237  				Snap:      "core",
   238  				Name:      "scrollock-led",
   239  				Interface: "leds",
   240  				Connections: []client.PlugRef{{
   241  					Snap: "keyuboard-lights",
   242  					Name: "scrollock",
   243  				}},
   244  			}, {
   245  				Snap:      "leds-provider",
   246  				Name:      "capslock-led",
   247  				Interface: "leds",
   248  				Connections: []client.PlugRef{{
   249  					Snap: "keyuboard-lights",
   250  					Name: "capslock",
   251  				}},
   252  			},
   253  		},
   254  	}
   255  	query := url.Values{}
   256  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   257  		c.Check(r.Method, Equals, "GET")
   258  		c.Check(r.URL.Path, Equals, "/v2/connections")
   259  		c.Check(r.URL.Query(), DeepEquals, query)
   260  		body, err := ioutil.ReadAll(r.Body)
   261  		c.Check(err, IsNil)
   262  		c.Check(body, DeepEquals, []byte{})
   263  		EncodeResponseBody(c, w, map[string]interface{}{
   264  			"type":   "sync",
   265  			"result": result,
   266  		})
   267  	})
   268  	rest, err := Parser(Client()).ParseArgs([]string{"connections"})
   269  	c.Assert(err, IsNil)
   270  	c.Assert(rest, DeepEquals, []string{})
   271  	expectedStdout := "" +
   272  		"Interface  Plug                       Slot                        Notes\n" +
   273  		"leds       keyboard-lights:capslock   leds-provider:capslock-led  gadget\n" +
   274  		"leds       keyboard-lights:numlock    :numlock-led                manual\n" +
   275  		"leds       keyboard-lights:scrollock  :scrollock-led              -\n"
   276  	c.Assert(s.Stdout(), Equals, expectedStdout)
   277  	c.Assert(s.Stderr(), Equals, "")
   278  }
   279  
   280  func (s *SnapSuite) TestConnectionsSomeDisconnected(c *C) {
   281  	result := client.Connections{
   282  		Established: []client.Connection{
   283  			{
   284  				Plug:      client.PlugRef{Snap: "keyboard-lights", Name: "scrollock"},
   285  				Slot:      client.SlotRef{Snap: "core", Name: "scrollock-led"},
   286  				Interface: "leds",
   287  			}, {
   288  				Plug:      client.PlugRef{Snap: "keyboard-lights", Name: "capslock"},
   289  				Slot:      client.SlotRef{Snap: "leds-provider", Name: "capslock-led"},
   290  				Interface: "leds",
   291  			},
   292  		},
   293  		Undesired: []client.Connection{
   294  			{
   295  				Plug:      client.PlugRef{Snap: "keyboard-lights", Name: "numlock"},
   296  				Slot:      client.SlotRef{Snap: "core", Name: "numlock-led"},
   297  				Interface: "leds",
   298  				Manual:    true,
   299  			},
   300  		},
   301  		Plugs: []client.Plug{
   302  			{
   303  				Snap:      "keyboard-lights",
   304  				Name:      "capslock",
   305  				Interface: "leds",
   306  				Connections: []client.SlotRef{{
   307  					Snap: "leds-provider",
   308  					Name: "capslock-led",
   309  				}},
   310  			}, {
   311  				Snap:      "keyboard-lights",
   312  				Name:      "numlock",
   313  				Interface: "leds",
   314  			}, {
   315  				Snap:      "keyboard-lights",
   316  				Name:      "scrollock",
   317  				Interface: "leds",
   318  				Connections: []client.SlotRef{{
   319  					Snap: "core",
   320  					Name: "scrollock-led",
   321  				}},
   322  			},
   323  		},
   324  		Slots: []client.Slot{
   325  			{
   326  				Snap:      "core",
   327  				Name:      "capslock-led",
   328  				Interface: "leds",
   329  			}, {
   330  				Snap:      "core",
   331  				Name:      "numlock-led",
   332  				Interface: "leds",
   333  			}, {
   334  				Snap:      "core",
   335  				Name:      "scrollock-led",
   336  				Interface: "leds",
   337  				Connections: []client.PlugRef{{
   338  					Snap: "keyuboard-lights",
   339  					Name: "scrollock",
   340  				}},
   341  			}, {
   342  				Snap:      "leds-provider",
   343  				Name:      "capslock-led",
   344  				Interface: "leds",
   345  				Connections: []client.PlugRef{{
   346  					Snap: "keyuboard-lights",
   347  					Name: "capslock",
   348  				}},
   349  			}, {
   350  				Snap:      "leds-provider",
   351  				Name:      "numlock-led",
   352  				Interface: "leds",
   353  			},
   354  		},
   355  	}
   356  	query := url.Values{
   357  		"select": []string{"all"},
   358  	}
   359  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   360  		c.Check(r.Method, Equals, "GET")
   361  		c.Check(r.URL.Path, Equals, "/v2/connections")
   362  		c.Check(r.URL.Query(), DeepEquals, query)
   363  		body, err := ioutil.ReadAll(r.Body)
   364  		c.Check(err, IsNil)
   365  		c.Check(body, DeepEquals, []byte{})
   366  		EncodeResponseBody(c, w, map[string]interface{}{
   367  			"type":   "sync",
   368  			"result": result,
   369  		})
   370  	})
   371  
   372  	rest, err := Parser(Client()).ParseArgs([]string{"connections", "--all"})
   373  	c.Assert(err, IsNil)
   374  	c.Assert(rest, DeepEquals, []string{})
   375  	expectedStdout := "" +
   376  		"Interface  Plug                       Slot                        Notes\n" +
   377  		"leds       -                          leds-provider:numlock-led   -\n" +
   378  		"leds       keyboard-lights:capslock   leds-provider:capslock-led  -\n" +
   379  		"leds       keyboard-lights:numlock    -                           -\n" +
   380  		"leds       keyboard-lights:scrollock  :scrollock-led              -\n"
   381  	c.Assert(s.Stdout(), Equals, expectedStdout)
   382  	c.Assert(s.Stderr(), Equals, "")
   383  }
   384  
   385  func (s *SnapSuite) TestConnectionsOnlyDisconnected(c *C) {
   386  	result := client.Connections{
   387  		Undesired: []client.Connection{
   388  			{
   389  				Plug:      client.PlugRef{Snap: "keyboard-lights", Name: "numlock"},
   390  				Slot:      client.SlotRef{Snap: "leds-provider", Name: "numlock-led"},
   391  				Interface: "leds",
   392  				Manual:    true,
   393  			},
   394  		},
   395  		Slots: []client.Slot{
   396  			{
   397  				Snap:      "leds-provider",
   398  				Name:      "capslock-led",
   399  				Interface: "leds",
   400  			}, {
   401  				Snap:      "leds-provider",
   402  				Name:      "numlock-led",
   403  				Interface: "leds",
   404  			},
   405  		},
   406  	}
   407  	query := url.Values{
   408  		"snap":   []string{"leds-provider"},
   409  		"select": []string{"all"},
   410  	}
   411  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   412  		c.Check(r.Method, Equals, "GET")
   413  		c.Check(r.URL.Path, Equals, "/v2/connections")
   414  		c.Check(r.URL.Query(), DeepEquals, query)
   415  		body, err := ioutil.ReadAll(r.Body)
   416  		c.Check(err, IsNil)
   417  		c.Check(body, DeepEquals, []byte{})
   418  		EncodeResponseBody(c, w, map[string]interface{}{
   419  			"type":   "sync",
   420  			"result": result,
   421  		})
   422  	})
   423  
   424  	rest, err := Parser(Client()).ParseArgs([]string{"connections", "leds-provider"})
   425  	c.Assert(err, IsNil)
   426  	c.Assert(rest, DeepEquals, []string{})
   427  	expectedStdout := "" +
   428  		"Interface  Plug  Slot                        Notes\n" +
   429  		"leds       -     leds-provider:capslock-led  -\n" +
   430  		"leds       -     leds-provider:numlock-led   -\n"
   431  	c.Assert(s.Stdout(), Equals, expectedStdout)
   432  	c.Assert(s.Stderr(), Equals, "")
   433  }
   434  
   435  func (s *SnapSuite) TestConnectionsFiltering(c *C) {
   436  	result := client.Connections{}
   437  	query := url.Values{
   438  		"select": []string{"all"},
   439  	}
   440  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   441  		c.Check(r.Method, Equals, "GET")
   442  		c.Check(r.URL.Path, Equals, "/v2/connections")
   443  		c.Check(r.URL.Query(), DeepEquals, query)
   444  		body, err := ioutil.ReadAll(r.Body)
   445  		c.Check(err, IsNil)
   446  		c.Check(body, DeepEquals, []byte{})
   447  		EncodeResponseBody(c, w, map[string]interface{}{
   448  			"type":   "sync",
   449  			"result": result,
   450  		})
   451  	})
   452  
   453  	query = url.Values{
   454  		"select": []string{"all"},
   455  		"snap":   []string{"mouse-buttons"},
   456  	}
   457  	rest, err := Parser(Client()).ParseArgs([]string{"connections", "mouse-buttons"})
   458  	c.Assert(err, IsNil)
   459  	c.Assert(rest, DeepEquals, []string{})
   460  
   461  	rest, err = Parser(Client()).ParseArgs([]string{"connections", "mouse-buttons", "--all"})
   462  	c.Assert(err, ErrorMatches, "cannot use --all with snap name")
   463  	c.Assert(rest, DeepEquals, []string{"--all"})
   464  }
   465  
   466  func (s *SnapSuite) TestConnectionsSorting(c *C) {
   467  	result := client.Connections{
   468  		Established: []client.Connection{
   469  			{
   470  				Plug:      client.PlugRef{Snap: "foo", Name: "plug"},
   471  				Slot:      client.SlotRef{Snap: "a-content-provider", Name: "data"},
   472  				Interface: "content",
   473  			}, {
   474  				Plug:      client.PlugRef{Snap: "foo", Name: "plug"},
   475  				Slot:      client.SlotRef{Snap: "b-content-provider", Name: "data"},
   476  				Interface: "content",
   477  			}, {
   478  				Plug:      client.PlugRef{Snap: "foo", Name: "desktop-plug"},
   479  				Slot:      client.SlotRef{Snap: "core", Name: "desktop"},
   480  				Interface: "desktop",
   481  			}, {
   482  				Plug:      client.PlugRef{Snap: "foo", Name: "x11-plug"},
   483  				Slot:      client.SlotRef{Snap: "core", Name: "x11"},
   484  				Interface: "x11",
   485  			}, {
   486  				Plug:      client.PlugRef{Snap: "foo", Name: "a-x11-plug"},
   487  				Slot:      client.SlotRef{Snap: "core", Name: "x11"},
   488  				Interface: "x11",
   489  			}, {
   490  				Plug:      client.PlugRef{Snap: "a-foo", Name: "plug"},
   491  				Slot:      client.SlotRef{Snap: "a-content-provider", Name: "data"},
   492  				Interface: "content",
   493  			}, {
   494  				Plug:      client.PlugRef{Snap: "keyboard-app", Name: "x11"},
   495  				Slot:      client.SlotRef{Snap: "core", Name: "x11"},
   496  				Interface: "x11",
   497  				Manual:    true,
   498  			},
   499  		},
   500  		Undesired: []client.Connection{
   501  			{
   502  				Plug:      client.PlugRef{Snap: "foo", Name: "plug"},
   503  				Slot:      client.SlotRef{Snap: "c-content-provider", Name: "data"},
   504  				Interface: "content",
   505  				Manual:    true,
   506  			},
   507  		},
   508  		Plugs: []client.Plug{
   509  			{
   510  				Snap:      "foo",
   511  				Name:      "plug",
   512  				Interface: "content",
   513  				Connections: []client.SlotRef{{
   514  					Snap: "a-content-provider",
   515  					Name: "data",
   516  				}, {
   517  					Snap: "b-content-provider",
   518  					Name: "data",
   519  				}},
   520  			}, {
   521  				Snap:      "foo",
   522  				Name:      "desktop-plug",
   523  				Interface: "desktop",
   524  				Connections: []client.SlotRef{{
   525  					Snap: "core",
   526  					Name: "desktop",
   527  				}},
   528  			}, {
   529  				Snap:      "foo",
   530  				Name:      "x11-plug",
   531  				Interface: "x11",
   532  				Connections: []client.SlotRef{{
   533  					Snap: "core",
   534  					Name: "x11",
   535  				}},
   536  			}, {
   537  				Snap:      "foo",
   538  				Name:      "a-x11-plug",
   539  				Interface: "x11",
   540  				Connections: []client.SlotRef{{
   541  					Snap: "core",
   542  					Name: "x11",
   543  				}},
   544  			}, {
   545  				Snap:      "a-foo",
   546  				Name:      "plug",
   547  				Interface: "content",
   548  				Connections: []client.SlotRef{{
   549  					Snap: "a-content-provider",
   550  					Name: "data",
   551  				}},
   552  			}, {
   553  				Snap:      "keyboard-app",
   554  				Name:      "x11",
   555  				Interface: "x11",
   556  				Connections: []client.SlotRef{{
   557  					Snap: "core",
   558  					Name: "x11",
   559  				}},
   560  			}, {
   561  				Snap:      "keyboard-lights",
   562  				Name:      "numlock",
   563  				Interface: "leds",
   564  			},
   565  		},
   566  		Slots: []client.Slot{
   567  			{
   568  				Snap:      "c-content-provider",
   569  				Name:      "data",
   570  				Interface: "content",
   571  			}, {
   572  				Snap:      "a-content-provider",
   573  				Name:      "data",
   574  				Interface: "content",
   575  				Connections: []client.PlugRef{{
   576  					Snap: "foo",
   577  					Name: "plug",
   578  				}, {
   579  					Snap: "a-foo",
   580  					Name: "plug",
   581  				}},
   582  			}, {
   583  				Snap:      "b-content-provider",
   584  				Name:      "data",
   585  				Interface: "content",
   586  				Connections: []client.PlugRef{{
   587  					Snap: "foo",
   588  					Name: "plug",
   589  				}},
   590  			}, {
   591  				Snap:      "core",
   592  				Name:      "x11",
   593  				Interface: "x11",
   594  				Connections: []client.PlugRef{{
   595  					Snap: "foo",
   596  					Name: "a-x11-plug",
   597  				}, {
   598  					Snap: "foo",
   599  					Name: "x11-plug",
   600  				}, {
   601  					Snap: "keyboard-app",
   602  					Name: "x11",
   603  				}},
   604  			}, {
   605  				Snap:      "leds-provider",
   606  				Name:      "numlock-led",
   607  				Interface: "leds",
   608  			},
   609  		},
   610  	}
   611  	query := url.Values{
   612  		"select": []string{"all"},
   613  	}
   614  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   615  		c.Check(r.Method, Equals, "GET")
   616  		c.Check(r.URL.Path, Equals, "/v2/connections")
   617  		c.Check(r.URL.Query(), DeepEquals, query)
   618  		body, err := ioutil.ReadAll(r.Body)
   619  		c.Check(err, IsNil)
   620  		c.Check(body, DeepEquals, []byte{})
   621  		EncodeResponseBody(c, w, map[string]interface{}{
   622  			"type":   "sync",
   623  			"result": result,
   624  		})
   625  	})
   626  
   627  	rest, err := Parser(Client()).ParseArgs([]string{"connections", "--all"})
   628  	c.Assert(err, IsNil)
   629  	c.Assert(rest, DeepEquals, []string{})
   630  	expectedStdout := "" +
   631  		"Interface  Plug                     Slot                       Notes\n" +
   632  		"content    -                        c-content-provider:data    -\n" +
   633  		"content    a-foo:plug               a-content-provider:data    -\n" +
   634  		"content    foo:plug                 a-content-provider:data    -\n" +
   635  		"content    foo:plug                 b-content-provider:data    -\n" +
   636  		"desktop    foo:desktop-plug         :desktop                   -\n" +
   637  		"leds       -                        leds-provider:numlock-led  -\n" +
   638  		"leds       keyboard-lights:numlock  -                          -\n" +
   639  		"x11        foo:a-x11-plug           :x11                       -\n" +
   640  		"x11        foo:x11-plug             :x11                       -\n" +
   641  		"x11        keyboard-app:x11         :x11                       manual\n"
   642  	c.Assert(s.Stdout(), Equals, expectedStdout)
   643  	c.Assert(s.Stderr(), Equals, "")
   644  }
   645  
   646  func (s *SnapSuite) TestConnectionsDefiningAttribute(c *C) {
   647  	result := client.Connections{
   648  		Established: []client.Connection{
   649  			{
   650  				Plug:      client.PlugRef{Snap: "foo", Name: "a-plug"},
   651  				Slot:      client.SlotRef{Snap: "a-content-provider", Name: "data"},
   652  				Interface: "content",
   653  				PlugAttrs: map[string]interface{}{
   654  					"content": "plug-some-data",
   655  					"target":  "$SNAP/foo",
   656  				},
   657  				SlotAttrs: map[string]interface{}{
   658  					"content": "slot-some-data",
   659  					"source": map[string]interface{}{
   660  						"read": []string{"$SNAP/bar"},
   661  					},
   662  				},
   663  			}, {
   664  				Plug:      client.PlugRef{Snap: "foo", Name: "b-plug"},
   665  				Slot:      client.SlotRef{Snap: "b-content-provider", Name: "data"},
   666  				Interface: "content",
   667  				PlugAttrs: map[string]interface{}{
   668  					// no content attribute for plug, falls back to slot
   669  					"target": "$SNAP/foo",
   670  				},
   671  				SlotAttrs: map[string]interface{}{
   672  					"content": "slot-some-data",
   673  					"source": map[string]interface{}{
   674  						"read": []string{"$SNAP/bar"},
   675  					},
   676  				},
   677  			}, {
   678  				Plug:      client.PlugRef{Snap: "foo", Name: "c-plug"},
   679  				Slot:      client.SlotRef{Snap: "c-content-provider", Name: "data"},
   680  				Interface: "content",
   681  				PlugAttrs: map[string]interface{}{
   682  					// no content attribute for plug
   683  					"target": "$SNAP/foo",
   684  				},
   685  				SlotAttrs: map[string]interface{}{
   686  					// no content attribute for slot either
   687  					"source": map[string]interface{}{
   688  						"read": []string{"$SNAP/bar"},
   689  					},
   690  				},
   691  			}, {
   692  				Plug:      client.PlugRef{Snap: "foo", Name: "d-plug"},
   693  				Slot:      client.SlotRef{Snap: "d-content-provider", Name: "data"},
   694  				Interface: "content",
   695  				// no attributes at all
   696  			}, {
   697  				Plug: client.PlugRef{Snap: "foo", Name: "desktop-plug"},
   698  				Slot: client.SlotRef{Snap: "core", Name: "desktop"},
   699  				// desktop interface does not have any defining attributes
   700  				Interface: "desktop",
   701  				PlugAttrs: map[string]interface{}{
   702  					"this-is-ignored": "foo",
   703  				},
   704  				SlotAttrs: map[string]interface{}{
   705  					"this-is-ignored-too": "foo",
   706  				},
   707  			},
   708  		},
   709  		Plugs: []client.Plug{
   710  			{
   711  				Snap:      "foo",
   712  				Name:      "a-plug",
   713  				Interface: "content",
   714  				Connections: []client.SlotRef{{
   715  					Snap: "a-content-provider",
   716  					Name: "data",
   717  				}},
   718  				Attrs: map[string]interface{}{
   719  					"content": "plug-some-data",
   720  					"target":  "$SNAP/foo",
   721  				},
   722  			}, {
   723  				Snap:      "foo",
   724  				Name:      "b-plug",
   725  				Interface: "content",
   726  				Connections: []client.SlotRef{{
   727  					Snap: "b-content-provider",
   728  					Name: "data",
   729  				}},
   730  				Attrs: map[string]interface{}{
   731  					// no content attribute for plug, falls back to slot
   732  					"target": "$SNAP/foo",
   733  				},
   734  			}, {
   735  				Snap:      "foo",
   736  				Name:      "c-plug",
   737  				Interface: "content",
   738  				Connections: []client.SlotRef{{
   739  					Snap: "c-content-provider",
   740  					Name: "data",
   741  				}},
   742  				Attrs: map[string]interface{}{
   743  					// no content attribute for plug
   744  					"target": "$SNAP/foo",
   745  				},
   746  			}, {
   747  				Snap:      "foo",
   748  				Name:      "d-plug",
   749  				Interface: "content",
   750  				Connections: []client.SlotRef{{
   751  					Snap: "d-content-provider",
   752  					Name: "data",
   753  				}},
   754  			}, {
   755  				Snap:      "foo",
   756  				Name:      "desktop-plug",
   757  				Interface: "desktop",
   758  				Connections: []client.SlotRef{{
   759  					Snap: "core",
   760  					Name: "desktop",
   761  				}},
   762  			},
   763  		},
   764  		Slots: []client.Slot{
   765  			{
   766  				Snap:      "a-content-provider",
   767  				Name:      "data",
   768  				Interface: "content",
   769  				Connections: []client.PlugRef{{
   770  					Snap: "foo",
   771  					Name: "a-plug",
   772  				}},
   773  				Attrs: map[string]interface{}{
   774  					"content": "slot-some-data",
   775  					"source": map[string]interface{}{
   776  						"read": []string{"$SNAP/bar"},
   777  					},
   778  				},
   779  			}, {
   780  				Snap:      "b-content-provider",
   781  				Name:      "data",
   782  				Interface: "content",
   783  				Connections: []client.PlugRef{{
   784  					Snap: "foo",
   785  					Name: "a-plug",
   786  				}},
   787  				Attrs: map[string]interface{}{
   788  					"content": "slot-some-data",
   789  					"source": map[string]interface{}{
   790  						"read": []string{"$SNAP/bar"},
   791  					},
   792  				},
   793  			}, {
   794  				Snap:      "c-content-provider",
   795  				Name:      "data",
   796  				Interface: "content",
   797  				Connections: []client.PlugRef{{
   798  					Snap: "foo",
   799  					Name: "a-plug",
   800  				}},
   801  				Attrs: map[string]interface{}{
   802  					"source": map[string]interface{}{
   803  						"read": []string{"$SNAP/bar"},
   804  					},
   805  				},
   806  			}, {
   807  				Snap:      "a-content-provider",
   808  				Name:      "data",
   809  				Interface: "content",
   810  				Connections: []client.PlugRef{{
   811  					Snap: "foo",
   812  					Name: "a-plug",
   813  				}},
   814  			}, {
   815  				Snap:      "core",
   816  				Name:      "desktop",
   817  				Interface: "desktop",
   818  				Connections: []client.PlugRef{{
   819  					Snap: "foo",
   820  					Name: "desktop-plug",
   821  				}},
   822  			},
   823  		},
   824  	}
   825  	query := url.Values{
   826  		"select": []string{"all"},
   827  	}
   828  	s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) {
   829  		c.Check(r.Method, Equals, "GET")
   830  		c.Check(r.URL.Path, Equals, "/v2/connections")
   831  		c.Check(r.URL.Query(), DeepEquals, query)
   832  		body, err := ioutil.ReadAll(r.Body)
   833  		c.Check(err, IsNil)
   834  		c.Check(body, DeepEquals, []byte{})
   835  		EncodeResponseBody(c, w, map[string]interface{}{
   836  			"type":   "sync",
   837  			"result": result,
   838  		})
   839  	})
   840  
   841  	rest, err := Parser(Client()).ParseArgs([]string{"connections", "--all"})
   842  	c.Assert(err, IsNil)
   843  	c.Assert(rest, DeepEquals, []string{})
   844  	expectedStdout := "" +
   845  		"Interface                Plug              Slot                     Notes\n" +
   846  		"content[plug-some-data]  foo:a-plug        a-content-provider:data  -\n" +
   847  		"content[slot-some-data]  foo:b-plug        b-content-provider:data  -\n" +
   848  		"content                  foo:c-plug        c-content-provider:data  -\n" +
   849  		"content                  foo:d-plug        d-content-provider:data  -\n" +
   850  		"desktop                  foo:desktop-plug  :desktop                 -\n"
   851  	c.Assert(s.Stdout(), Equals, expectedStdout)
   852  	c.Assert(s.Stderr(), Equals, "")
   853  }