github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/client/clientutil/snapinfo_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2018-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 clientutil_test
    21  
    22  import (
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  	"reflect"
    27  	"testing"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/client"
    32  	"github.com/snapcore/snapd/client/clientutil"
    33  	"github.com/snapcore/snapd/dirs"
    34  	"github.com/snapcore/snapd/snap"
    35  	"github.com/snapcore/snapd/testutil"
    36  )
    37  
    38  func Test(t *testing.T) { TestingT(t) }
    39  
    40  type cmdSuite struct {
    41  	testutil.BaseTest
    42  }
    43  
    44  func (s *cmdSuite) SetUpTest(c *C) {
    45  	s.BaseTest.SetUpTest(c)
    46  	dirs.SetRootDir(c.MkDir())
    47  	s.AddCleanup(func() { dirs.SetRootDir("/") })
    48  }
    49  
    50  var _ = Suite(&cmdSuite{})
    51  
    52  func (*cmdSuite) TestClientSnapFromSnapInfo(c *C) {
    53  	si := &snap.Info{
    54  		SnapType:      snap.TypeApp,
    55  		SuggestedName: "",
    56  		InstanceKey:   "insta",
    57  		Version:       "v1",
    58  		Confinement:   snap.StrictConfinement,
    59  		License:       "Proprietary",
    60  		Publisher: snap.StoreAccount{
    61  			ID:          "ZvtzsxbsHivZLdvzrt0iqW529riGLfXJ",
    62  			Username:    "thingyinc",
    63  			DisplayName: "Thingy Inc.",
    64  			Validation:  "unproven",
    65  		},
    66  		Base: "core18",
    67  		SideInfo: snap.SideInfo{
    68  			RealName:          "the-snap",
    69  			SnapID:            "snapidid",
    70  			Revision:          snap.R(99),
    71  			EditedTitle:       "the-title",
    72  			EditedSummary:     "the-summary",
    73  			EditedDescription: "the-description",
    74  			Channel:           "latest/stable",
    75  			Contact:           "https://thingy.com",
    76  			Private:           true,
    77  		},
    78  		Channels: map[string]*snap.ChannelSnapInfo{},
    79  		Tracks:   []string{},
    80  		Prices:   map[string]float64{},
    81  		Media: []snap.MediaInfo{
    82  			{Type: "icon", URL: "https://dashboard.snapcraft.io/site_media/appmedia/2017/12/Thingy.png"},
    83  			{Type: "screenshot", URL: "https://dashboard.snapcraft.io/site_media/appmedia/2018/01/Thingy_01.png"},
    84  			{Type: "screenshot", URL: "https://dashboard.snapcraft.io/site_media/appmedia/2018/01/Thingy_02.png", Width: 600, Height: 200},
    85  		},
    86  		CommonIDs: []string{"org.thingy"},
    87  		Website:   "http://example.com/thingy",
    88  		StoreURL:  "https://snapcraft.io/thingy",
    89  		Broken:    "broken",
    90  	}
    91  	// valid InstallDate
    92  	err := os.MkdirAll(si.MountDir(), 0755)
    93  	c.Assert(err, IsNil)
    94  	err = os.Symlink(si.Revision.String(), filepath.Join(filepath.Dir(si.MountDir()), "current"))
    95  	c.Assert(err, IsNil)
    96  
    97  	ci, err := clientutil.ClientSnapFromSnapInfo(si, nil)
    98  	c.Check(err, IsNil)
    99  
   100  	// check that fields are filled
   101  	// see daemon/snap.go for fields filled after this
   102  	expectedZeroFields := []string{
   103  		"Screenshots", // unused nowadays
   104  		"DownloadSize",
   105  		"InstalledSize",
   106  		"Health",
   107  		"Status",
   108  		"TrackingChannel",
   109  		"IgnoreValidation",
   110  		"CohortKey",
   111  		"DevMode",
   112  		"TryMode",
   113  		"JailMode",
   114  		"MountedFrom",
   115  	}
   116  	var checker func(string, reflect.Value)
   117  	checker = func(pfx string, x reflect.Value) {
   118  		t := x.Type()
   119  		for i := 0; i < x.NumField(); i++ {
   120  			f := t.Field(i)
   121  			if f.PkgPath != "" {
   122  				// not exported, ignore
   123  				continue
   124  			}
   125  			v := x.Field(i)
   126  			if f.Anonymous {
   127  				checker(pfx+f.Name+".", v)
   128  				continue
   129  			}
   130  			if reflect.DeepEqual(v.Interface(), reflect.Zero(f.Type).Interface()) {
   131  				name := pfx + f.Name
   132  				c.Check(expectedZeroFields, testutil.Contains, name, Commentf("%s not set", name))
   133  			}
   134  		}
   135  	}
   136  	x := reflect.ValueOf(ci).Elem()
   137  	checker("", x)
   138  
   139  	// check some values
   140  	c.Check(ci.Name, Equals, "the-snap_insta")
   141  	c.Check(ci.Type, Equals, "app")
   142  	c.Check(ci.ID, Equals, si.ID())
   143  	c.Check(ci.Revision, Equals, snap.R(99))
   144  	c.Check(ci.Version, Equals, "v1")
   145  	c.Check(ci.Title, Equals, "the-title")
   146  	c.Check(ci.Summary, Equals, "the-summary")
   147  	c.Check(ci.Description, Equals, "the-description")
   148  	c.Check(ci.Icon, Equals, si.Media.IconURL())
   149  	c.Check(ci.Website, Equals, si.Website)
   150  	c.Check(ci.StoreURL, Equals, si.StoreURL)
   151  	c.Check(ci.Developer, Equals, "thingyinc")
   152  	c.Check(ci.Publisher, DeepEquals, &si.Publisher)
   153  }
   154  
   155  type testStatusDecorator struct {
   156  	calls int
   157  }
   158  
   159  func (sd *testStatusDecorator) DecorateWithStatus(appInfo *client.AppInfo, app *snap.AppInfo) error {
   160  	sd.calls++
   161  	if appInfo.Snap != app.Snap.InstanceName() || appInfo.Name != app.Name {
   162  		panic("mismatched")
   163  	}
   164  	appInfo.Enabled = true
   165  	appInfo.Active = true
   166  	return nil
   167  }
   168  
   169  func (*cmdSuite) TestClientSnapFromSnapInfoAppsInactive(c *C) {
   170  	si := &snap.Info{
   171  		SnapType:      snap.TypeApp,
   172  		SuggestedName: "",
   173  		InstanceKey:   "insta",
   174  		SideInfo: snap.SideInfo{
   175  			RealName: "the-snap",
   176  			SnapID:   "snapidid",
   177  			Revision: snap.R(99),
   178  		},
   179  	}
   180  	si.Apps = map[string]*snap.AppInfo{
   181  		"svc": {Snap: si, Name: "svc", Daemon: "simple"},
   182  		"app": {Snap: si, Name: "app", CommonID: "common.id"},
   183  	}
   184  	// sanity
   185  	c.Check(si.IsActive(), Equals, false)
   186  	// desktop file
   187  	df := si.Apps["app"].DesktopFile()
   188  	err := os.MkdirAll(filepath.Dir(df), 0755)
   189  	c.Assert(err, IsNil)
   190  	err = ioutil.WriteFile(df, nil, 0644)
   191  	c.Assert(err, IsNil)
   192  
   193  	sd := &testStatusDecorator{}
   194  	ci, err := clientutil.ClientSnapFromSnapInfo(si, sd)
   195  	c.Check(err, IsNil)
   196  
   197  	c.Check(ci.Name, Equals, "the-snap_insta")
   198  	c.Check(ci.Apps, DeepEquals, []client.AppInfo{
   199  		{
   200  			Snap:        "the-snap_insta",
   201  			Name:        "app",
   202  			CommonID:    "common.id",
   203  			DesktopFile: df,
   204  		},
   205  		{Snap: "the-snap_insta", Name: "svc", Daemon: "simple"},
   206  	})
   207  	// not called on inactive snaps
   208  	c.Check(sd.calls, Equals, 0)
   209  }
   210  
   211  func (*cmdSuite) TestClientSnapFromSnapInfoAppsActive(c *C) {
   212  	si := &snap.Info{
   213  		SnapType:      snap.TypeApp,
   214  		SuggestedName: "",
   215  		InstanceKey:   "insta",
   216  		SideInfo: snap.SideInfo{
   217  			RealName: "the-snap",
   218  			SnapID:   "snapidid",
   219  			Revision: snap.R(99),
   220  		},
   221  	}
   222  	si.Apps = map[string]*snap.AppInfo{
   223  		"svc": {Snap: si, Name: "svc", Daemon: "simple"},
   224  	}
   225  	// make it active
   226  	err := os.MkdirAll(si.MountDir(), 0755)
   227  	c.Assert(err, IsNil)
   228  	err = os.Symlink(si.Revision.String(), filepath.Join(filepath.Dir(si.MountDir()), "current"))
   229  	c.Assert(err, IsNil)
   230  	c.Check(si.IsActive(), Equals, true)
   231  
   232  	sd := &testStatusDecorator{}
   233  	ci, err := clientutil.ClientSnapFromSnapInfo(si, sd)
   234  	c.Check(err, IsNil)
   235  	// ... service status
   236  	c.Check(ci.Name, Equals, "the-snap_insta")
   237  	c.Check(ci.Apps, DeepEquals, []client.AppInfo{
   238  		{Snap: "the-snap_insta", Name: "svc", Daemon: "simple", Enabled: true, Active: true},
   239  	})
   240  
   241  	c.Check(sd.calls, Equals, 1)
   242  }
   243  
   244  func (*cmdSuite) TestAppStatusNotes(c *C) {
   245  	ai := client.AppInfo{}
   246  	c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "-")
   247  
   248  	ai = client.AppInfo{
   249  		Daemon: "oneshot",
   250  	}
   251  	c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "-")
   252  
   253  	ai = client.AppInfo{
   254  		Daemon: "oneshot",
   255  		Activators: []client.AppActivator{
   256  			{Type: "timer"},
   257  		},
   258  	}
   259  	c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "timer-activated")
   260  
   261  	ai = client.AppInfo{
   262  		Daemon: "oneshot",
   263  		Activators: []client.AppActivator{
   264  			{Type: "socket"},
   265  		},
   266  	}
   267  	c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "socket-activated")
   268  
   269  	// check that the output is stable regardless of the order of activators
   270  	ai = client.AppInfo{
   271  		Daemon: "oneshot",
   272  		Activators: []client.AppActivator{
   273  			{Type: "timer"},
   274  			{Type: "socket"},
   275  		},
   276  	}
   277  	c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "timer-activated,socket-activated")
   278  	ai = client.AppInfo{
   279  		Daemon: "oneshot",
   280  		Activators: []client.AppActivator{
   281  			{Type: "socket"},
   282  			{Type: "timer"},
   283  		},
   284  	}
   285  	c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "timer-activated,socket-activated")
   286  }