github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap/cmd_debug_timings_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 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 "net/http" 25 "strings" 26 "time" 27 28 . "gopkg.in/check.v1" 29 30 "github.com/snapcore/snapd/cmd/snap" 31 ) 32 33 type timingsCmdArgs struct { 34 args, stdout, stderr, error string 35 } 36 37 var timingsTests = []timingsCmdArgs{{ 38 args: "debug timings", 39 error: "please provide change ID or type with --last=<type>, or query for --ensure=<name> or --startup=<name>", 40 }, { 41 args: "debug timings --ensure=seed 9", 42 error: "cannot use change id, 'startup' or 'ensure' together", 43 }, { 44 args: "debug timings --ensure=seed --startup=ifacemgr", 45 error: "cannot use change id, 'startup' or 'ensure' together", 46 }, { 47 args: "debug timings --last=install --all", 48 error: "cannot use 'all' with change id or 'last'", 49 }, { 50 args: "debug timings --last=remove", 51 error: `no changes of type "remove" found`, 52 }, { 53 args: "debug timings --startup=load-state 9", 54 error: "cannot use change id, 'startup' or 'ensure' together", 55 }, { 56 args: "debug timings --all 9", 57 error: "cannot use 'all' with change id or 'last'", 58 }, { 59 args: "debug timings --last=install", 60 stdout: "ID Status Doing Undoing Summary\n" + 61 "40 Doing 910ms - lane 0 task bar summary\n" + 62 " ^ 1ms - foo summary\n" + 63 " ^ 1ms - bar summary\n" + 64 "41 Done 210ms - lane 1 task baz summary\n" + 65 "42 Done 310ms - lane 1 task boo summary\n" + 66 "43 Done 310ms - lane 0 task doh summary\n\n", 67 }, { 68 args: "debug timings 1", 69 stdout: "ID Status Doing Undoing Summary\n" + 70 "40 Doing 910ms - lane 0 task bar summary\n" + 71 " ^ 1ms - foo summary\n" + 72 " ^ 1ms - bar summary\n" + 73 "41 Done 210ms - lane 1 task baz summary\n" + 74 "42 Done 310ms - lane 1 task boo summary\n" + 75 "43 Done 310ms - lane 0 task doh summary\n\n", 76 }, { 77 args: "debug timings 1 --verbose", 78 stdout: "ID Status Doing Undoing Label Summary\n" + 79 "40 Doing 910ms - bar lane 0 task bar summary\n" + 80 " ^ 1ms - foo foo summary\n" + 81 " ^ 1ms - bar bar summary\n" + 82 "41 Done 210ms - baz lane 1 task baz summary\n" + 83 "42 Done 310ms - boo lane 1 task boo summary\n" + 84 "43 Done 310ms - doh lane 0 task doh summary\n\n", 85 }, { 86 args: "debug timings --ensure=seed", 87 stdout: "ID Status Doing Undoing Summary\n" + 88 "seed 8ms - \n" + 89 " ^ 8ms - baz summary\n" + 90 " ^ 8ms - booze summary\n" + 91 "40 Doing 910ms - task bar summary\n" + 92 " ^ 1ms - foo summary\n" + 93 " ^ 1ms - bar summary\n\n", 94 }, { 95 args: "debug timings --ensure=seed --all", 96 stdout: "ID Status Doing Undoing Summary\n" + 97 "seed 8ms - \n" + 98 " ^ 8ms - bar summary 1\n" + 99 " ^ 8ms - bar summary 2\n" + 100 "40 Doing 910ms - task bar summary\n" + 101 " ^ 1ms - foo summary\n" + 102 " ^ 1ms - bar summary\n" + 103 "seed 7ms - \n" + 104 " ^ 7ms - baz summary 2\n" + 105 "60 Doing 910ms - task bar summary\n" + 106 " ^ 1ms - foo summary\n" + 107 " ^ 1ms - bar summary\n\n", 108 }, { 109 args: "debug timings --ensure=seed --all --verbose", 110 stdout: "ID Status Doing Undoing Label Summary\n" + 111 "seed 8ms - \n" + 112 " ^ 8ms - abc bar summary 1\n" + 113 " ^ 8ms - abc bar summary 2\n" + 114 "40 Doing 910ms - bar task bar summary\n" + 115 " ^ 1ms - foo foo summary\n" + 116 " ^ 1ms - bar bar summary\n" + 117 "seed 7ms - \n" + 118 " ^ 7ms - ghi baz summary 2\n" + 119 "60 Doing 910ms - bar task bar summary\n" + 120 " ^ 1ms - foo foo summary\n" + 121 " ^ 1ms - bar bar summary\n\n", 122 }, { 123 args: "debug timings --startup=ifacemgr", 124 stdout: "ID Status Doing Undoing Summary\n" + 125 "ifacemgr 8ms - \n" + 126 " ^ 8ms - baz summary\n" + 127 " ^ 8ms - booze summary\n\n", 128 }, { 129 args: "debug timings --startup=ifacemgr --all", 130 stdout: "ID Status Doing Undoing Summary\n" + 131 "ifacemgr 8ms - \n" + 132 " ^ 8ms - baz summary\n" + 133 "ifacemgr 9ms - \n" + 134 " ^ 9ms - baz summary\n\n", 135 }} 136 137 func (s *SnapSuite) TestGetDebugTimings(c *C) { 138 s.mockCmdTimingsAPI(c) 139 140 restore := main.MockIsStdinTTY(true) 141 defer restore() 142 143 for _, test := range timingsTests { 144 s.stdout.Truncate(0) 145 s.stderr.Truncate(0) 146 147 c.Logf("Test: %s", test.args) 148 149 _, err := main.Parser(main.Client()).ParseArgs(strings.Fields(test.args)) 150 if test.error != "" { 151 c.Check(err, ErrorMatches, test.error) 152 } else { 153 c.Check(err, IsNil) 154 c.Check(s.Stderr(), Equals, test.stderr) 155 c.Check(s.Stdout(), Equals, test.stdout) 156 } 157 } 158 } 159 160 func (s *SnapSuite) mockCmdTimingsAPI(c *C) { 161 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 162 c.Assert(r.Method, Equals, "GET") 163 164 if r.URL.Path == "/v2/debug" { 165 q := r.URL.Query() 166 aspect := q.Get("aspect") 167 c.Assert(aspect, Equals, "change-timings") 168 169 changeID := q.Get("change-id") 170 ensure := q.Get("ensure") 171 startup := q.Get("startup") 172 all := q.Get("all") 173 174 switch { 175 case changeID == "1": 176 // lane 0 and lane 1 tasks, interleaved 177 fmt.Fprintln(w, `{"type":"sync","status-code":200,"status":"OK","result":[ 178 {"change-id":"1", "change-timings":{ 179 "41":{"doing-time":210000000, "status": "Done", "lane": 1, "ready-time": "2016-04-22T01:02:04Z", "kind": "baz", "summary": "lane 1 task baz summary"}, 180 "43":{"doing-time":310000000, "status": "Done", "ready-time": "2016-04-25T01:02:04Z", "kind": "doh", "summary": "lane 0 task doh summary"}, 181 "40":{"doing-time":910000000, "status": "Doing", "ready-time": "2016-04-20T00:00:00Z", "kind": "bar", "summary": "lane 0 task bar summary", 182 "doing-timings":[ 183 {"label":"foo", "summary": "foo summary", "duration": 1000001}, 184 {"level":1, "label":"bar", "summary": "bar summary", "duration": 1000002} 185 ]}, 186 "42":{"doing-time":310000000, "status": "Done", "lane": 1, "ready-time": "2016-04-23T01:02:04Z", "kind": "boo", "summary": "lane 1 task boo summary"} 187 }}]}`) 188 case ensure == "seed" && all == "false": 189 fmt.Fprintln(w, `{"type":"sync","status-code":200,"status":"OK","result":[ 190 {"change-id":"1", 191 "total-duration": 8000002, 192 "ensure-timings": [ 193 {"label":"baz", "summary": "baz summary", "duration": 8000001}, 194 {"level":1, "label":"booze", "summary": "booze summary", "duration": 8000002} 195 ], 196 "change-timings":{ 197 "40":{"doing-time":910000000, "status": "Doing", "kind": "bar", "summary": "task bar summary", 198 "doing-timings":[ 199 {"label":"foo", "summary": "foo summary", "duration": 1000001}, 200 {"level":1, "label":"bar", "summary": "bar summary", "duration": 1000002} 201 ]}}}]}`) 202 case ensure == "seed" && all == "true": 203 fmt.Fprintln(w, `{"type":"sync","status-code":200,"status":"OK","result":[ 204 {"change-id":"1", 205 "total-duration": 8000002, 206 "ensure-timings": [ 207 {"label":"abc", "summary": "bar summary 1", "duration": 8000001}, 208 {"label":"abc", "summary": "bar summary 2", "duration": 8000002} 209 ], 210 "change-timings":{ 211 "40":{"doing-time":910000000, "status": "Doing", "kind": "bar", "summary": "task bar summary", 212 "doing-timings":[ 213 {"label":"foo", "summary": "foo summary", "duration": 1000001}, 214 {"level":1, "label":"bar", "summary": "bar summary", "duration": 1000002} 215 ]}}}, 216 {"change-id":"2", 217 "total-duration": 7000002, 218 "ensure-timings": [{"label":"ghi", "summary": "baz summary 2", "duration": 7000002}], 219 "change-timings":{ 220 "60":{"doing-time":910000000, "status": "Doing", "kind": "bar", "summary": "task bar summary", 221 "doing-timings":[ 222 {"label":"foo", "summary": "foo summary", "duration": 1000001}, 223 {"level":1, "label":"bar", "summary": "bar summary", "duration": 1000002} 224 ]}}}]}`) 225 case startup == "ifacemgr" && all == "false": 226 fmt.Fprintln(w, `{"type":"sync","status-code":200,"status":"OK","result":[ 227 {"total-duration": 8000002, "startup-timings": [ 228 {"label":"baz", "summary": "baz summary", "duration": 8000001}, 229 {"level":1, "label":"booze", "summary": "booze summary", "duration": 8000002} 230 ]}]}`) 231 case startup == "ifacemgr" && all == "true": 232 fmt.Fprintln(w, `{"type":"sync","status-code":200,"status":"OK","result":[ 233 {"total-duration": 8000002, "startup-timings": [ 234 {"label":"baz", "summary": "baz summary", "duration": 8000001} 235 ]}, 236 {"total-duration": 9000002, "startup-timings": [ 237 {"label":"baz", "summary": "baz summary", "duration": 9000001} 238 ]}]}`) 239 default: 240 c.Errorf("unexpected request: %s, %s, %s", changeID, ensure, all) 241 } 242 return 243 } 244 245 // request for all changes on --last=... 246 if r.URL.Path == "/v2/changes" { 247 fmt.Fprintln(w, `{"type":"sync","status-code":200,"status":"OK","result":[{ 248 "id": "1", 249 "kind": "install-snap", 250 "summary": "a", 251 "status": "Doing", 252 "ready": false, 253 "spawn-time": "2016-04-21T01:02:03Z", 254 "ready-time": "2016-04-21T01:02:04Z", 255 "tasks": [{"id":"99", "kind": "bar", "summary": ".", "status": "Doing", "progress": {"done": 0, "total": 1}, "spawn-time": "2016-04-21T01:02:03Z", "ready-time": "2016-04-21T01:02:04Z"}] 256 }]}`) 257 return 258 } 259 c.Errorf("unexpected path %q", r.URL.Path) 260 }) 261 } 262 263 type TaskDef struct { 264 TaskID string 265 Lane int 266 ReadyTime time.Time 267 } 268 269 func (s *SnapSuite) TestSortTimingsTasks(c *C) { 270 mkTime := func(timeStr string) time.Time { 271 t, err := time.Parse(time.RFC3339, timeStr) 272 c.Assert(err, IsNil) 273 return t 274 } 275 276 testData := []struct { 277 ChangeTimings map[string]main.ChangeTimings 278 Expected []string 279 }{{ 280 // nothing to do 281 ChangeTimings: map[string]main.ChangeTimings{}, 282 Expected: []string{}, 283 }, { 284 ChangeTimings: map[string]main.ChangeTimings{ 285 // tasks in lane 0 only 286 "1": {ReadyTime: mkTime("2019-04-21T00:00:00Z")}, 287 "2": {ReadyTime: mkTime("2019-05-21T00:00:00Z")}, 288 "3": {ReadyTime: mkTime("2019-02-21T00:00:00Z")}, 289 "4": {ReadyTime: mkTime("2019-03-21T00:00:00Z")}, 290 "5": {ReadyTime: mkTime("2019-01-21T00:00:00Z")}, 291 }, 292 Expected: []string{"5", "3", "4", "1", "2"}, 293 }, { 294 // task in lane 1 with a task in lane 0 before and after it 295 ChangeTimings: map[string]main.ChangeTimings{ 296 "1": {Lane: 1, ReadyTime: mkTime("2019-01-21T00:00:00Z")}, 297 "2": {Lane: 0, ReadyTime: mkTime("2019-01-20T00:00:00Z")}, 298 "3": {Lane: 0, ReadyTime: mkTime("2019-01-22T00:00:00Z")}, 299 }, 300 Expected: []string{"2", "1", "3"}, 301 }, { 302 // tasks in lane 1 only 303 ChangeTimings: map[string]main.ChangeTimings{ 304 "1": {Lane: 1, ReadyTime: mkTime("2019-01-21T00:00:00Z")}, 305 "2": {Lane: 1, ReadyTime: mkTime("2019-01-20T00:00:00Z")}, 306 "3": {Lane: 1, ReadyTime: mkTime("2019-01-16T00:00:00Z")}, 307 }, 308 Expected: []string{"3", "2", "1"}, 309 }, { 310 // tasks in lanes 0, 1, 2 with tasks from line 0 before and after lanes 1, 2 311 ChangeTimings: map[string]main.ChangeTimings{ 312 "1": {Lane: 1, ReadyTime: mkTime("2019-01-21T00:00:00Z")}, 313 "2": {Lane: 0, ReadyTime: mkTime("2019-01-19T00:00:00Z")}, 314 "3": {Lane: 2, ReadyTime: mkTime("2019-01-20T00:00:00Z")}, 315 "4": {Lane: 0, ReadyTime: mkTime("2019-01-25T00:00:00Z")}, 316 "5": {Lane: 1, ReadyTime: mkTime("2019-01-20T00:00:00Z")}, 317 "6": {Lane: 2, ReadyTime: mkTime("2019-01-21T00:00:00Z")}, 318 "7": {Lane: 0, ReadyTime: mkTime("2019-01-18T00:00:00Z")}, 319 "8": {Lane: 0, ReadyTime: mkTime("2019-01-27T00:00:00Z")}, 320 }, 321 Expected: []string{"7", "2", "5", "1", "3", "6", "4", "8"}, 322 }, { 323 // pathological case: lane 0 tasks have ready-time between lane 1 tasks 324 ChangeTimings: map[string]main.ChangeTimings{ 325 "1": {Lane: 1, ReadyTime: mkTime("2019-01-20T00:00:00Z")}, 326 "2": {Lane: 1, ReadyTime: mkTime("2019-01-30T00:00:00Z")}, 327 "3": {Lane: 0, ReadyTime: mkTime("2019-01-27T00:00:00Z")}, 328 "4": {Lane: 0, ReadyTime: mkTime("2019-01-25T00:00:00Z")}, 329 }, 330 Expected: []string{"1", "2", "4", "3"}, 331 }} 332 333 for _, data := range testData { 334 tasks := main.SortTimingsTasks(data.ChangeTimings) 335 c.Check(tasks, DeepEquals, data.Expected) 336 } 337 }