github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/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 EditedContact: "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", DaemonScope: snap.SystemDaemon}, 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 { 206 Snap: "the-snap_insta", 207 Name: "svc", 208 Daemon: "simple", 209 DaemonScope: snap.SystemDaemon, 210 }, 211 }) 212 // not called on inactive snaps 213 c.Check(sd.calls, Equals, 0) 214 } 215 216 func (*cmdSuite) TestClientSnapFromSnapInfoAppsActive(c *C) { 217 si := &snap.Info{ 218 SnapType: snap.TypeApp, 219 SuggestedName: "", 220 InstanceKey: "insta", 221 SideInfo: snap.SideInfo{ 222 RealName: "the-snap", 223 SnapID: "snapidid", 224 Revision: snap.R(99), 225 }, 226 } 227 si.Apps = map[string]*snap.AppInfo{ 228 "svc": {Snap: si, Name: "svc", Daemon: "simple", DaemonScope: snap.SystemDaemon}, 229 } 230 // make it active 231 err := os.MkdirAll(si.MountDir(), 0755) 232 c.Assert(err, IsNil) 233 err = os.Symlink(si.Revision.String(), filepath.Join(filepath.Dir(si.MountDir()), "current")) 234 c.Assert(err, IsNil) 235 c.Check(si.IsActive(), Equals, true) 236 237 sd := &testStatusDecorator{} 238 ci, err := clientutil.ClientSnapFromSnapInfo(si, sd) 239 c.Check(err, IsNil) 240 // ... service status 241 c.Check(ci.Name, Equals, "the-snap_insta") 242 c.Check(ci.Apps, DeepEquals, []client.AppInfo{ 243 { 244 Snap: "the-snap_insta", 245 Name: "svc", 246 Daemon: "simple", 247 DaemonScope: snap.SystemDaemon, 248 Enabled: true, 249 Active: true, 250 }, 251 }) 252 253 c.Check(sd.calls, Equals, 1) 254 } 255 256 func (*cmdSuite) TestAppStatusNotes(c *C) { 257 ai := client.AppInfo{} 258 c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "-") 259 260 ai = client.AppInfo{ 261 Daemon: "oneshot", 262 } 263 c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "-") 264 265 ai = client.AppInfo{ 266 Daemon: "simple", 267 DaemonScope: snap.UserDaemon, 268 } 269 c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "user") 270 271 ai = client.AppInfo{ 272 Daemon: "oneshot", 273 Activators: []client.AppActivator{ 274 {Type: "timer"}, 275 }, 276 } 277 c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "timer-activated") 278 279 ai = client.AppInfo{ 280 Daemon: "oneshot", 281 Activators: []client.AppActivator{ 282 {Type: "socket"}, 283 }, 284 } 285 c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "socket-activated") 286 287 ai = client.AppInfo{ 288 Daemon: "oneshot", 289 Activators: []client.AppActivator{ 290 {Type: "dbus"}, 291 }, 292 } 293 c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "dbus-activated") 294 295 // check that the output is stable regardless of the order of activators 296 ai = client.AppInfo{ 297 Daemon: "oneshot", 298 Activators: []client.AppActivator{ 299 {Type: "timer"}, 300 {Type: "socket"}, 301 {Type: "dbus"}, 302 }, 303 } 304 c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "timer-activated,socket-activated,dbus-activated") 305 ai = client.AppInfo{ 306 Daemon: "oneshot", 307 DaemonScope: snap.UserDaemon, 308 Activators: []client.AppActivator{ 309 {Type: "dbus"}, 310 {Type: "socket"}, 311 {Type: "timer"}, 312 }, 313 } 314 c.Check(clientutil.ClientAppInfoNotes(&ai), Equals, "user,timer-activated,socket-activated,dbus-activated") 315 }