github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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 }