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