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  }