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  }