github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/errtracker/errtracker_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 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 errtracker_test 21 22 import ( 23 "crypto/sha512" 24 "fmt" 25 "io/ioutil" 26 "net/http" 27 "net/http/httptest" 28 "os" 29 "path/filepath" 30 "sort" 31 "strings" 32 "testing" 33 "time" 34 35 "gopkg.in/mgo.v2/bson" 36 37 . "gopkg.in/check.v1" 38 39 "github.com/snapcore/snapd/arch" 40 "github.com/snapcore/snapd/dirs" 41 "github.com/snapcore/snapd/errtracker" 42 "github.com/snapcore/snapd/osutil" 43 "github.com/snapcore/snapd/release" 44 "github.com/snapcore/snapd/snapdenv" 45 "github.com/snapcore/snapd/testutil" 46 ) 47 48 // Hook up check.v1 into the "go test" runner 49 func Test(t *testing.T) { TestingT(t) } 50 51 type ErrtrackerTestSuite struct { 52 testutil.BaseTest 53 54 tmpdir string 55 56 hostBuildID string 57 coreBuildID string 58 distroRelease string 59 } 60 61 var _ = Suite(&ErrtrackerTestSuite{}) 62 63 var truePath = osutil.LookPathDefault("true", "/bin/true") 64 var falsePath = osutil.LookPathDefault("false", "/bin/false") 65 66 const someJournalEntry = "Mar 29 22:08:00 localhost kernel: [81B blob data]" 67 68 func (s *ErrtrackerTestSuite) SetUpTest(c *C) { 69 s.BaseTest.SetUpTest(c) 70 71 s.tmpdir = c.MkDir() 72 dirs.SetRootDir(s.tmpdir) 73 74 p := filepath.Join(s.tmpdir, "machine-id") 75 err := ioutil.WriteFile(p, []byte("bbb1a6a5bcdb418380056a2d759c3f7c"), 0644) 76 c.Assert(err, IsNil) 77 s.AddCleanup(errtracker.MockMachineIDPaths([]string{p})) 78 s.AddCleanup(errtracker.MockHostSnapd(truePath)) 79 s.AddCleanup(errtracker.MockCoreSnapd(falsePath)) 80 s.AddCleanup(errtracker.MockReExec(func() string { 81 return "yes" 82 })) 83 mockDetectVirt := testutil.MockCommand(c, "systemd-detect-virt", "echo none") 84 s.AddCleanup(mockDetectVirt.Restore) 85 86 s.hostBuildID, err = osutil.ReadBuildID(truePath) 87 c.Assert(err, IsNil) 88 s.coreBuildID, err = osutil.ReadBuildID(falsePath) 89 c.Assert(err, IsNil) 90 if release.ReleaseInfo.ID == "ubuntu" { 91 s.distroRelease = fmt.Sprintf("%s %s", strings.Title(release.ReleaseInfo.ID), release.ReleaseInfo.VersionID) 92 } else { 93 s.distroRelease = fmt.Sprintf("%s %s", release.ReleaseInfo.ID, release.ReleaseInfo.VersionID) 94 } 95 96 mockCpuinfo := filepath.Join(s.tmpdir, "cpuinfo") 97 mockSelfCmdline := filepath.Join(s.tmpdir, "self.cmdline") 98 mockSelfExe := filepath.Join(s.tmpdir, "self.exe") 99 mockSelfCwd := filepath.Join(s.tmpdir, "self.cwd") 100 101 c.Assert(ioutil.WriteFile(mockCpuinfo, []byte(` 102 processor : 0 103 bugs : very yes 104 etc : ... 105 106 processor : 42 107 bugs : very yes 108 `[1:]), 0644), IsNil) 109 c.Assert(ioutil.WriteFile(mockSelfCmdline, []byte("foo\x00bar\x00baz"), 0644), IsNil) 110 c.Assert(os.Symlink("target of /proc/self/exe", mockSelfExe), IsNil) 111 c.Assert(os.Symlink("target of /proc/self/cwd", mockSelfCwd), IsNil) 112 113 s.AddCleanup(errtracker.MockOsGetenv(func(s string) string { 114 switch s { 115 case "SHELL": 116 return "/bin/sh" 117 case "XDG_CURRENT_DESKTOP": 118 return "Unity" 119 } 120 return "" 121 })) 122 s.AddCleanup(errtracker.MockProcCpuinfo(mockCpuinfo)) 123 s.AddCleanup(errtracker.MockProcSelfCmdline(mockSelfCmdline)) 124 s.AddCleanup(errtracker.MockProcSelfExe(mockSelfExe)) 125 s.AddCleanup(errtracker.MockProcSelfCwd(mockSelfCwd)) 126 s.AddCleanup(testutil.MockCommand(c, "journalctl", "echo "+someJournalEntry).Restore) 127 128 mockCmd := testutil.MockCommand(c, "systemctl", "echo enabled; exit 0") 129 s.AddCleanup(mockCmd.Restore) 130 } 131 132 func (s *ErrtrackerTestSuite) TestReport(c *C) { 133 n := 0 134 identifier := "" 135 136 snapConfineProfile := filepath.Join(s.tmpdir, "/etc/apparmor.d/usr.lib.snapd.snap-confine") 137 err := os.MkdirAll(filepath.Dir(snapConfineProfile), 0755) 138 c.Assert(err, IsNil) 139 err = ioutil.WriteFile(snapConfineProfile, []byte("# fake profile of snap-confine"), 0644) 140 c.Assert(err, IsNil) 141 142 err = ioutil.WriteFile(snapConfineProfile+".dpkg-new", []byte{0}, 0644) 143 c.Assert(err, IsNil) 144 err = ioutil.WriteFile(snapConfineProfile+".real", []byte{0}, 0644) 145 c.Assert(err, IsNil) 146 err = ioutil.WriteFile(snapConfineProfile+".real.dpkg-new", []byte{0}, 0644) 147 c.Assert(err, IsNil) 148 149 prev := errtracker.SnapdVersion 150 defer func() { errtracker.SnapdVersion = prev }() 151 errtracker.SnapdVersion = "some-snapd-version" 152 153 handler := func(w http.ResponseWriter, r *http.Request) { 154 switch n { 155 case 0: 156 c.Check(r.Method, Equals, "POST") 157 c.Check(r.URL.Path, Matches, "/[a-z0-9]+") 158 identifier = r.URL.Path 159 b, err := ioutil.ReadAll(r.Body) 160 c.Assert(err, IsNil) 161 162 var data map[string]string 163 err = bson.Unmarshal(b, &data) 164 c.Assert(err, IsNil) 165 c.Check(data, DeepEquals, map[string]string{ 166 "DistroRelease": s.distroRelease, 167 "HostSnapdBuildID": s.hostBuildID, 168 "CoreSnapdBuildID": s.coreBuildID, 169 "SnapdVersion": "some-snapd-version", 170 "Date": "Fri Feb 17 09:51:00 2017", 171 "KernelVersion": osutil.KernelVersion(), 172 "ErrorMessage": "failed to do stuff", 173 "DuplicateSignature": "[failed to do stuff]", 174 "Architecture": arch.DpkgArchitecture(), 175 "DidSnapdReExec": "yes", 176 177 "ProblemType": "Snap", 178 "Snap": "some-snap", 179 "Channel": "beta", 180 181 "ProcCpuinfoMinimal": "processor\t: 42\nbugs\t\t: very yes\n", 182 "ExecutablePath": "target of /proc/self/exe", 183 "ProcCwd": "target of /proc/self/cwd", 184 "ProcCmdline": "foo\x00bar\x00baz", 185 "ProcEnviron": "SHELL=/bin/sh", 186 "JournalError": someJournalEntry + "\n", 187 "SourcePackage": "snapd", 188 "CurrentDesktop": "Unity", 189 "DetectedVirt": "none", 190 191 "MD5SumSnapConfineAppArmorProfile": "7a7aa5f21063170c1991b84eb8d86de1", 192 "MD5SumSnapConfineAppArmorProfileDpkgNew": "93b885adfe0da089cdf634904fd59f71", 193 "MD5SumSnapConfineAppArmorProfileReal": "93b885adfe0da089cdf634904fd59f71", 194 "MD5SumSnapConfineAppArmorProfileRealDpkgNew": "93b885adfe0da089cdf634904fd59f71", 195 }) 196 fmt.Fprintf(w, "c14388aa-f78d-11e6-8df0-fa163eaf9b83 OOPSID") 197 case 1: 198 c.Check(r.Method, Equals, "POST") 199 c.Check(r.URL.Path, Matches, identifier) 200 fmt.Fprintf(w, "xxxxx-f78d-11e6-8df0-fa163eaf9b83 OOPSID") 201 default: 202 c.Fatalf("expected one request, got %d", n+1) 203 } 204 205 n++ 206 } 207 208 server := httptest.NewServer(http.HandlerFunc(handler)) 209 defer server.Close() 210 restorer := errtracker.MockCrashDbURL(server.URL) 211 defer restorer() 212 restorer = errtracker.MockTimeNow(func() time.Time { return time.Date(2017, 2, 17, 9, 51, 0, 0, time.UTC) }) 213 defer restorer() 214 215 id, err := errtracker.Report("some-snap", "failed to do stuff", "[failed to do stuff]", map[string]string{ 216 "Channel": "beta", 217 }) 218 c.Check(err, IsNil) 219 c.Check(id, Equals, "c14388aa-f78d-11e6-8df0-fa163eaf9b83 OOPSID") 220 c.Check(n, Equals, 1) 221 222 // run again with the *same* dupSig and verify that it won't send 223 // that again 224 id, err = errtracker.Report("some-snap", "failed to do stuff", "[failed to do stuff]", map[string]string{ 225 "Channel": "beta", 226 }) 227 c.Check(err, IsNil) 228 c.Check(id, Equals, "already-reported") 229 c.Check(n, Equals, 1) 230 231 // run again with different data, verify identifier is unchanged 232 id, err = errtracker.Report("some-other-snap", "failed to do more stuff", "[failed to do more stuff]", nil) 233 c.Check(err, IsNil) 234 c.Check(id, Equals, "xxxxx-f78d-11e6-8df0-fa163eaf9b83 OOPSID") 235 c.Check(n, Equals, 2) 236 } 237 238 func (s *ErrtrackerTestSuite) TestReportUnderTesting(c *C) { 239 defer snapdenv.MockTesting(true)() 240 241 n := 0 242 prev := errtracker.SnapdVersion 243 defer func() { errtracker.SnapdVersion = prev }() 244 errtracker.SnapdVersion = "some-snapd-version" 245 246 handler := func(w http.ResponseWriter, r *http.Request) { 247 n++ 248 } 249 250 server := httptest.NewServer(http.HandlerFunc(handler)) 251 defer server.Close() 252 restorer := errtracker.MockCrashDbURL(server.URL) 253 defer restorer() 254 restorer = errtracker.MockTimeNow(func() time.Time { return time.Date(2017, 2, 17, 9, 51, 0, 0, time.UTC) }) 255 defer restorer() 256 257 id, err := errtracker.Report("some-snap", "failed to do stuff", "[failed to do stuff]", map[string]string{ 258 "Channel": "beta", 259 }) 260 c.Check(err, IsNil) 261 c.Check(id, Equals, "oops-not-sent") 262 c.Check(n, Equals, 0) 263 } 264 265 func (s *ErrtrackerTestSuite) TestTriesAllKnownMachineIDs(c *C) { 266 p := filepath.Join(c.MkDir(), "machine-id") 267 machineID := []byte("bbb1a6a5bcdb418380056a2d759c3f7c") 268 err := ioutil.WriteFile(p, machineID, 0644) 269 c.Assert(err, IsNil) 270 s.AddCleanup(errtracker.MockMachineIDPaths([]string{"/does/not/exist", p})) 271 272 n := 0 273 var identifiers []string 274 handler := func(w http.ResponseWriter, r *http.Request) { 275 identifiers = append(identifiers, r.URL.Path) 276 n++ 277 } 278 279 server := httptest.NewServer(http.HandlerFunc(handler)) 280 defer server.Close() 281 restorer := errtracker.MockCrashDbURL(server.URL) 282 defer restorer() 283 restorer = errtracker.MockTimeNow(func() time.Time { return time.Date(2017, 2, 17, 9, 51, 0, 0, time.UTC) }) 284 defer restorer() 285 286 _, err = errtracker.Report("some-snap", "failed to do stuff", "[failed to do stuff]", map[string]string{ 287 "Channel": "beta", 288 }) 289 c.Check(err, IsNil) 290 c.Check(n, Equals, 1) 291 c.Check(identifiers, DeepEquals, []string{fmt.Sprintf("/%x", sha512.Sum512(machineID))}) 292 } 293 294 func (s *ErrtrackerTestSuite) TestReportRepair(c *C) { 295 n := 0 296 prev := errtracker.SnapdVersion 297 defer func() { errtracker.SnapdVersion = prev }() 298 errtracker.SnapdVersion = "some-snapd-version" 299 300 handler := func(w http.ResponseWriter, r *http.Request) { 301 switch n { 302 case 0: 303 c.Check(r.Method, Equals, "POST") 304 c.Check(r.URL.Path, Matches, "/[a-z0-9]+") 305 b, err := ioutil.ReadAll(r.Body) 306 c.Assert(err, IsNil) 307 308 var data map[string]string 309 err = bson.Unmarshal(b, &data) 310 c.Assert(err, IsNil) 311 c.Check(data, DeepEquals, map[string]string{ 312 "DistroRelease": s.distroRelease, 313 "HostSnapdBuildID": s.hostBuildID, 314 "CoreSnapdBuildID": s.coreBuildID, 315 "SnapdVersion": "some-snapd-version", 316 "Date": "Fri Feb 17 09:51:00 2017", 317 "KernelVersion": osutil.KernelVersion(), 318 "Architecture": arch.DpkgArchitecture(), 319 "DidSnapdReExec": "yes", 320 321 "ProblemType": "Repair", 322 "Repair": `"repair (1; brand-id:canonical)"`, 323 "ErrorMessage": "failure in script", 324 "DuplicateSignature": "[dupSig]", 325 "BrandID": "canonical", 326 327 "ProcCpuinfoMinimal": "processor\t: 42\nbugs\t\t: very yes\n", 328 "ExecutablePath": "target of /proc/self/exe", 329 "ProcCwd": "target of /proc/self/cwd", 330 "ProcCmdline": "foo\x00bar\x00baz", 331 "ProcEnviron": "SHELL=/bin/sh", 332 "JournalError": someJournalEntry + "\n", 333 "SourcePackage": "snapd", 334 "CurrentDesktop": "Unity", 335 "DetectedVirt": "none", 336 }) 337 fmt.Fprintf(w, "c14388aa-f78d-11e6-8df0-fa163eaf9b83 OOPSID") 338 default: 339 c.Fatalf("expected one request, got %d", n+1) 340 } 341 342 n++ 343 } 344 345 server := httptest.NewServer(http.HandlerFunc(handler)) 346 defer server.Close() 347 restorer := errtracker.MockCrashDbURL(server.URL) 348 defer restorer() 349 restorer = errtracker.MockTimeNow(func() time.Time { return time.Date(2017, 2, 17, 9, 51, 0, 0, time.UTC) }) 350 defer restorer() 351 352 id, err := errtracker.ReportRepair(`"repair (1; brand-id:canonical)"`, "failure in script", "[dupSig]", map[string]string{ 353 "BrandID": "canonical", 354 }) 355 c.Check(err, IsNil) 356 c.Check(id, Equals, "c14388aa-f78d-11e6-8df0-fa163eaf9b83 OOPSID") 357 c.Check(n, Equals, 1) 358 } 359 360 func (s *ErrtrackerTestSuite) TestReportWithWhoopsieDisabled(c *C) { 361 mockCmd := testutil.MockCommand(c, "systemctl", "echo disabled; exit 1") 362 defer mockCmd.Restore() 363 364 handler := func(w http.ResponseWriter, r *http.Request) { 365 c.Fatalf("The server should not be hit from here") 366 } 367 server := httptest.NewServer(http.HandlerFunc(handler)) 368 defer server.Close() 369 restorer := errtracker.MockCrashDbURL(server.URL) 370 defer restorer() 371 372 id, err := errtracker.Report("some-snap", "failed to do stuff", "[failed to do stuff]", nil) 373 c.Check(err, IsNil) 374 c.Check(id, Equals, "") 375 } 376 377 func (s *ErrtrackerTestSuite) TestReportWithNoWhoopsieInstalled(c *C) { 378 mockCmd := testutil.MockCommand(c, "systemctl", "echo Failed to get unit file state for whoopsie.service; exit 1") 379 defer mockCmd.Restore() 380 381 n := 0 382 handler := func(w http.ResponseWriter, r *http.Request) { 383 fmt.Fprintf(w, "1234-oopsid") 384 n++ 385 } 386 server := httptest.NewServer(http.HandlerFunc(handler)) 387 defer server.Close() 388 restorer := errtracker.MockCrashDbURL(server.URL) 389 defer restorer() 390 391 id, err := errtracker.Report("some-snap", "failed to do stuff", "[failed to do stuff]", nil) 392 c.Check(err, IsNil) 393 c.Check(id, Equals, "1234-oopsid") 394 c.Check(n, Equals, 1) 395 } 396 397 func (s *ErrtrackerTestSuite) TestProcCpuinfo(c *C) { 398 fn := filepath.Join(s.tmpdir, "cpuinfo") 399 // sanity check 400 buf, err := ioutil.ReadFile(fn) 401 c.Assert(err, IsNil) 402 c.Check(string(buf), Equals, ` 403 processor : 0 404 bugs : very yes 405 etc : ... 406 407 processor : 42 408 bugs : very yes 409 `[1:]) 410 411 // just the last processor entry 412 c.Check(errtracker.ProcCpuinfoMinimal(), Equals, ` 413 processor : 42 414 bugs : very yes 415 `[1:]) 416 417 // if no processor line, just return the whole thing 418 c.Assert(ioutil.WriteFile(fn, []byte("yadda yadda\n"), 0644), IsNil) 419 c.Check(errtracker.ProcCpuinfoMinimal(), Equals, "yadda yadda\n") 420 421 c.Assert(os.Remove(fn), IsNil) 422 c.Check(errtracker.ProcCpuinfoMinimal(), Matches, "error: .* no such file or directory") 423 } 424 425 func (s *ErrtrackerTestSuite) TestProcExe(c *C) { 426 c.Check(errtracker.ProcExe(), Equals, "target of /proc/self/exe") 427 c.Assert(os.Remove(filepath.Join(s.tmpdir, "self.exe")), IsNil) 428 c.Check(errtracker.ProcExe(), Matches, "error: .* no such file or directory") 429 } 430 431 func (s *ErrtrackerTestSuite) TestProcCwd(c *C) { 432 c.Check(errtracker.ProcCwd(), Equals, "target of /proc/self/cwd") 433 c.Assert(os.Remove(filepath.Join(s.tmpdir, "self.cwd")), IsNil) 434 c.Check(errtracker.ProcCwd(), Matches, "error: .* no such file or directory") 435 } 436 437 func (s *ErrtrackerTestSuite) TestProcCmdline(c *C) { 438 c.Check(errtracker.ProcCmdline(), Equals, "foo\x00bar\x00baz") 439 c.Assert(os.Remove(filepath.Join(s.tmpdir, "self.cmdline")), IsNil) 440 c.Check(errtracker.ProcCmdline(), Matches, "error: .* no such file or directory") 441 } 442 443 func (s *ErrtrackerTestSuite) TestJournalError(c *C) { 444 jctl := testutil.MockCommand(c, "journalctl", "echo "+someJournalEntry) 445 defer jctl.Restore() 446 c.Check(errtracker.JournalError(), Equals, someJournalEntry+"\n") 447 c.Check(jctl.Calls(), DeepEquals, [][]string{ 448 {"journalctl", "-b", "--priority=warning..err", "--lines=1000"}, 449 }) 450 } 451 452 func (s *ErrtrackerTestSuite) TestJournalErrorSilentError(c *C) { 453 jctl := testutil.MockCommand(c, "journalctl", "kill $$") 454 defer jctl.Restore() 455 c.Check(errtracker.JournalError(), Matches, "error: signal: [Tt]erminated") 456 c.Check(jctl.Calls(), DeepEquals, [][]string{ 457 {"journalctl", "-b", "--priority=warning..err", "--lines=1000"}, 458 }) 459 } 460 461 func (s *ErrtrackerTestSuite) TestJournalErrorError(c *C) { 462 jctl := testutil.MockCommand(c, "journalctl", "echo OOPS; exit 1") 463 defer jctl.Restore() 464 c.Check(errtracker.JournalError(), Equals, "OOPS\n\nerror: exit status 1") 465 c.Check(jctl.Calls(), DeepEquals, [][]string{ 466 {"journalctl", "-b", "--priority=warning..err", "--lines=1000"}, 467 }) 468 } 469 470 func (s *ErrtrackerTestSuite) TestEnviron(c *C) { 471 defer errtracker.MockOsGetenv(func(s string) string { 472 switch s { 473 case "SHELL": 474 // marked as safe 475 return "/bin/sh" 476 case "GPG_AGENT_INFO": 477 // not marked as safe 478 return ".gpg-agent:0:1" 479 case "TERM": 480 // not really set 481 return "" 482 case "PATH": 483 // special handling from here down 484 return "/something/random:/sbin/:/home/ubuntu/bin:/bin:/snap/bin" 485 case "XDG_RUNTIME_DIR": 486 return "/some/thing" 487 case "LD_PRELOAD": 488 return "foo" 489 case "LD_LIBRARY_PATH": 490 return "bar" 491 } 492 return "" 493 })() 494 495 env := strings.Split(errtracker.Environ(), "\n") 496 sort.Strings(env) 497 498 c.Check(env, DeepEquals, []string{ 499 "LD_LIBRARY_PATH=<set>", 500 "LD_PRELOAD=<set>", 501 // note also /sbin/ -> /sbin 502 "PATH=(custom):/sbin:(user):/bin:/snap/bin", 503 "SHELL=/bin/sh", 504 "XDG_RUNTIME_DIR=<set>", 505 }) 506 } 507 508 func (s *ErrtrackerTestSuite) TestReportsDB(c *C) { 509 db, err := errtracker.NewReportsDB(filepath.Join(s.tmpdir, "foo.db")) 510 c.Assert(err, IsNil) 511 512 c.Check(db.AlreadyReported("some-dup-sig"), Equals, false) 513 514 err = db.MarkReported("some-dup-sig") 515 c.Check(err, IsNil) 516 517 c.Check(db.AlreadyReported("some-dup-sig"), Equals, true) 518 c.Check(db.AlreadyReported("other-dup-sig"), Equals, false) 519 } 520 521 func (s *ErrtrackerTestSuite) TestReportsDBCleanup(c *C) { 522 db, err := errtracker.NewReportsDB(filepath.Join(s.tmpdir, "foo.db")) 523 c.Assert(err, IsNil) 524 525 errtracker.SetReportDBCleanupTime(db, 1*time.Millisecond) 526 527 err = db.MarkReported("some-dup-sig") 528 c.Check(err, IsNil) 529 530 time.Sleep(10 * time.Millisecond) 531 err = db.MarkReported("other-dup-sig") 532 c.Check(err, IsNil) 533 534 // this one got cleaned out 535 c.Check(db.AlreadyReported("some-dup-sig"), Equals, false) 536 // this one is still fresh 537 c.Check(db.AlreadyReported("other-dup-sig"), Equals, true) 538 } 539 540 func (s *ErrtrackerTestSuite) TestReportsDBnilDoesNotCrash(c *C) { 541 db, err := errtracker.NewReportsDB("/proc/1/environ") 542 c.Assert(err, NotNil) 543 c.Check(db, IsNil) 544 545 c.Check(db.AlreadyReported("dupSig"), Equals, false) 546 c.Check(db.MarkReported("dupSig"), ErrorMatches, "cannot mark error report as reported with an uninitialized reports database") 547 } 548 549 func (s *ErrtrackerTestSuite) TestReportsDBnilDoesNotCrashOnReport(c *C) { 550 oldDbDir := dirs.ErrtrackerDbDir 551 dirs.ErrtrackerDbDir = "/proc/1/environ" 552 defer func() { 553 dirs.ErrtrackerDbDir = oldDbDir 554 }() 555 556 id, err := errtracker.Report("some-snap", "failed to do stuff", "[failed to do stuff]", nil) 557 c.Assert(err, ErrorMatches, "cannot open error reports database: open /proc/1/environ:.*") 558 c.Assert(id, Equals, "") 559 }