[
  {
    "path": ".gitignore",
    "content": "# Created by .ignore support plugin (hsz.mobi)\n### Go template\n# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n.idea\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n*.test\n*.prof\n\n\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: go\nsudo: false\ngo:\n  - \"1.14\"\nbefore_install:\n  - go get github.com/mattn/goveralls\n  - export PATH=$PATH:$HOME/gopath/bin\n  - go install github.com/vrecan/death/./...\nscript:\n  - $GOPATH/bin/goveralls -service=travis-ci -race -v\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Ben Aldrich\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# Death [![Build Status](https://travis-ci.org/vrecan/death.svg?branch=master)](https://travis-ci.org/vrecan/death) [![Coverage Status](https://coveralls.io/repos/github/vrecan/death/badge.svg?branch=master)](https://coveralls.io/github/vrecan/death?branch=master) [![Go Reference](https://pkg.go.dev/badge/github.com/vrecan/death/v3.svg)](https://pkg.go.dev/github.com/vrecan/death/v3) [![Join the chat at https://gitter.im/vrecan/death](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/vrecan/death?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n\n<p>Simple library to make it easier to manage the death of your application.</p>\n\n## Get The Library\n\nUse gopkg.in to import death based on your logger.\n\n| Version | Go Get URL                                                                           | source                                                           | doc                                                  | Notes                                                                                                                                                                                                                                                                                                |\n| ------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| 3.x     | [github.com/vrecan/death/v3](https://github.com/vrecan/death/tree/release-branch.v3) | [source](https://github.com/vrecan/death/tree/release-branch.v3) | [doc](https://pkg.go.dev/github.com/vrecan/death/v3) | This removes the need for an independent logger. By default death will not log but will return an error if all the closers do not properly close. If you want to provide a logger just satisfy the deathlog.Logger interface. This also uses go modules so import it as `github.com/vrecan/death/v3` |\n| 2.x     | [gopkg.in/vrecan/death.v2](https://gopkg.in/vrecan/death.v2)                         | [source](https://github.com/vrecan/death/tree/v2.0)              | [doc](https://godoc.org/gopkg.in/vrecan/death.v2)    | This supports loggers who _do not_ return an error from their `Error` and `Warn` functions like [logrus](https://github.com/sirupsen/logrus)                                                                                                                                                         |\n| 1.x     | [gopkg.in/vrecan/death.v1](https://gopkg.in/vrecan/death.v1)                         | [souce](https://github.com/vrecan/death/tree/v1.0)               | [doc](https://godoc.org/gopkg.in/vrecan/death.v1)    | This supports loggers who _do_ return an error from their `Error` and `Warn` functions like [seelog](https://github.com/cihub/seelog)                                                                                                                                                                |\n\nExample\n\n```bash\ngo get github.com/vrecan/death/v3\n```\n\n## Use The Library\n\n```go\npackage main\n\nimport (\n\tDEATH \"github.com/vrecan/death/v3\"\n\tSYS \"syscall\"\n)\n\nfunc main() {\n\tdeath := DEATH.NewDeath(SYS.SIGINT, SYS.SIGTERM) //pass the signals you want to end your application\n\t//when you want to block for shutdown signals\n\tdeath.WaitForDeath() // this will finish when a signal of your type is sent to your application\n}\n```\n\n### Close Other Objects On Shutdown\n\n<p>One simple feature of death is that it can also close other objects when shutdown starts</p>\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\tDEATH \"github.com/vrecan/death/v3\"\n\tSYS \"syscall\"\n\t\"io\"\n)\n\nfunc main() {\n\tdeath := DEATH.NewDeath(SYS.SIGINT, SYS.SIGTERM) //pass the signals you want to end your application\n\tobjects := make([]io.Closer, 0)\n\n\tobjects = append(objects, &NewType{}) // this will work as long as the type implements a Close method\n\n\t//when you want to block for shutdown signals\n\terr := death.WaitForDeath(objects...) // this will finish when a signal of your type is sent to your application\n\tif err != nil {\n\t\tlog.Println(err)\n\t\tos.Exit(1)\n\t}\n}\n\ntype NewType struct {\n}\n\nfunc (c *NewType) Close() error {\n\treturn nil\n}\n\n```\n\n### Or close using an anonymous function\n\n```go\npackage main\n\nimport (\n\tDEATH \"github.com/vrecan/death/v3\"\n\tSYS \"syscall\"\n)\n\nfunc main() {\n\tdeath := DEATH.NewDeath(SYS.SIGINT, SYS.SIGTERM) //pass the signals you want to end your application\n\t//when you want to block for shutdown signals\n\tdeath.WaitForDeathWithFunc(func(){\n\t\t//do whatever you want on death\n\t})\n}\n```\n\n# Release Process\n\n## Rules for release branches:\n\n- If you are releasing a new major version you need to branch off of master into a branch `release-branch.v#` (example `release-branch.v2` for a 2.x release)\n- If you are releasing a minor or patch update to an existing major release make sure to merge master into the release branch\n\n## Rules for tagging and publishing the release\n\nWhen you are ready to publish the release make sure you...\n\n1. Merge your changes into the correct release branch.\n2. Check out the release branch locally (example: `git pull origin release-branch.v3`)\n3. Create a new tag for the specific release version you will publish (example: `git tag v3.0.1`)\n4. Push the tag up to github (example: `git push origin v3.0.1`)\n5. Go to the release tab in github\n6. Select the target branch as the release branch and type in the tag name (tagname should include `v` so example: `v3.0.1`)\n7. Write a title and a well worded description on exactly what is in this change\n8. Click publish release\n"
  },
  {
    "path": "death.go",
    "content": "package death\n\n//Manage the death of your application.\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/signal\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Death manages the death of your application.\ntype Death struct {\n\twg          *sync.WaitGroup\n\tsigChannel  chan os.Signal\n\tcallChannel chan struct{}\n\ttimeout     time.Duration\n\tlog         Logger\n}\n\n// closer is a wrapper to the struct we are going to close with metadata\n// to help with debuging close.\ntype closer struct {\n\tIndex   int\n\tC       io.Closer\n\tName    string\n\tPKGPath string\n\tErr     error\n}\n\n// NewDeath Create Death with the signals you want to die from.\nfunc NewDeath(signals ...os.Signal) (death *Death) {\n\tdeath = &Death{timeout: 10 * time.Second,\n\t\tsigChannel:  make(chan os.Signal, 1),\n\t\tcallChannel: make(chan struct{}, 1),\n\t\twg:          &sync.WaitGroup{},\n\t\tlog:         DefaultLogger()}\n\tsignal.Notify(death.sigChannel, signals...)\n\tdeath.wg.Add(1)\n\tgo death.listenForSignal()\n\treturn death\n}\n\n// SetTimeout Overrides the time death is willing to wait for a objects to be closed.\nfunc (d *Death) SetTimeout(t time.Duration) *Death {\n\td.timeout = t\n\treturn d\n}\n\n// SetLogger Overrides the default logger (seelog)\nfunc (d *Death) SetLogger(l Logger) *Death {\n\td.log = l\n\treturn d\n}\n\n// WaitForDeath wait for signal and then kill all items that need to die. If they fail to\n// die when instructed we return an error\nfunc (d *Death) WaitForDeath(closable ...io.Closer) (err error) {\n\td.wg.Wait()\n\td.log.Info(\"Shutdown started...\")\n\tcount := len(closable)\n\td.log.Debug(\"Closing \", count, \" objects\")\n\tif count > 0 {\n\t\treturn d.closeInMass(closable...)\n\t}\n\treturn nil\n}\n\n// WaitForDeathWithFunc allows you to have a single function get called when it's time to\n// kill your application.\nfunc (d *Death) WaitForDeathWithFunc(f func()) {\n\td.wg.Wait()\n\td.log.Info(\"Shutdown started...\")\n\tf()\n}\n\n// getPkgPath for an io closer.\nfunc getPkgPath(c io.Closer) (name string, pkgPath string) {\n\tt := reflect.TypeOf(c)\n\tif t.Kind() == reflect.Ptr {\n\t\tt = t.Elem()\n\t}\n\treturn t.Name(), t.PkgPath()\n}\n\n// closeInMass Close all the objects at once and wait for them to finish with a channel. Return an\n// error if you fail to close all the objects\nfunc (d *Death) closeInMass(closable ...io.Closer) (err error) {\n\n\tcount := len(closable)\n\tsentToClose := make(map[int]closer)\n\t//call close async\n\tdoneClosers := make(chan closer, count)\n\tfor i, c := range closable {\n\t\tname, pkgPath := getPkgPath(c)\n\t\tcloser := closer{Index: i, C: c, Name: name, PKGPath: pkgPath}\n\t\tgo d.closeObjects(closer, doneClosers)\n\t\tsentToClose[i] = closer\n\t}\n\n\t// wait on channel for notifications.\n\ttimer := time.NewTimer(d.timeout)\n\tfailedClosers := []closer{}\n\tfor {\n\t\tselect {\n\t\tcase <-timer.C:\n\t\t\ts := \"failed to close: \"\n\t\t\tpkgs := []string{}\n\t\t\tfor _, c := range sentToClose {\n\t\t\t\tpkgs = append(pkgs, fmt.Sprintf(\"%s/%s\", c.PKGPath, c.Name))\n\t\t\t\td.log.Error(\"Failed to close: \", c.PKGPath, \"/\", c.Name)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"%s\", fmt.Sprintf(\"%s %s\", s, strings.Join(pkgs, \", \")))\n\t\tcase closer := <-doneClosers:\n\t\t\tdelete(sentToClose, closer.Index)\n\t\t\tcount--\n\t\t\tif closer.Err != nil {\n\t\t\t\tfailedClosers = append(failedClosers, closer)\n\t\t\t}\n\n\t\t\td.log.Debug(count, \" object(s) left\")\n\t\t\tif count != 0 || len(sentToClose) != 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif len(failedClosers) != 0 {\n\t\t\t\terrString := generateErrString(failedClosers)\n\t\t\t\treturn fmt.Errorf(\"errors from closers: %s\", errString)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\n// closeObjects and return a bool when finished on a channel.\nfunc (d *Death) closeObjects(closer closer, done chan<- closer) {\n\terr := closer.C.Close()\n\tif err != nil {\n\t\td.log.Error(err)\n\t\tcloser.Err = err\n\t}\n\tdone <- closer\n}\n\n// FallOnSword manually initiates the death process.\nfunc (d *Death) FallOnSword() {\n\tselect {\n\tcase d.callChannel <- struct{}{}:\n\tdefault:\n\t}\n}\n\n// ListenForSignal Manage death of application by signal.\nfunc (d *Death) listenForSignal() {\n\tdefer d.wg.Done()\n\tfor {\n\t\tselect {\n\t\tcase <-d.sigChannel:\n\t\t\treturn\n\t\tcase <-d.callChannel:\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// generateErrString generates a string containing a list of tuples of pkgname to error message\nfunc generateErrString(failedClosers []closer) (errString string) {\n\tfor i, fc := range failedClosers {\n\t\tif i == 0 {\n\t\t\terrString = fmt.Sprintf(\"%s/%s: %s\", fc.PKGPath, fc.Name, fc.Err)\n\t\t\tcontinue\n\t\t}\n\t\terrString = fmt.Sprintf(\"%s, %s/%s: %s\", errString, fc.PKGPath, fc.Name, fc.Err)\n\t}\n\n\treturn errString\n}\n"
  },
  {
    "path": "death_unix_test.go",
    "content": "// +build linux bsd darwin\n\npackage death\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"syscall\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/smartystreets/goconvey/convey\"\n)\n\ntype Unhashable map[string]interface{}\n\nfunc (u Unhashable) Close() error {\n\treturn nil\n}\n\nfunc TestDeath(t *testing.T) {\n\n\tConvey(\"Validate death handles unhashable types\", t, func() {\n\t\tu := make(Unhashable)\n\t\tdeath := NewDeath(syscall.SIGTERM)\n\t\tsyscall.Kill(os.Getpid(), syscall.SIGTERM)\n\t\terr := death.WaitForDeath(u)\n\t\tSo(err, ShouldBeNil)\n\t})\n\n\tConvey(\"Validate death happens cleanly\", t, func() {\n\t\tdeath := NewDeath(syscall.SIGTERM)\n\t\tsyscall.Kill(os.Getpid(), syscall.SIGTERM)\n\t\terr := death.WaitForDeath()\n\t\tSo(err, ShouldBeNil)\n\t})\n\n\tConvey(\"Validate death happens with other signals\", t, func() {\n\t\tdeath := NewDeath(syscall.SIGHUP)\n\t\tcloseMe := &CloseMe{}\n\t\tsyscall.Kill(os.Getpid(), syscall.SIGHUP)\n\t\terr := death.WaitForDeath(closeMe)\n\t\tSo(err, ShouldBeNil)\n\t\tSo(closeMe.Closed, ShouldEqual, 1)\n\t})\n\n\tConvey(\"Validate death happens with a manual call\", t, func() {\n\t\tdeath := NewDeath(syscall.SIGHUP)\n\t\tcloseMe := &CloseMe{}\n\t\tdeath.FallOnSword()\n\t\terr := death.WaitForDeath(closeMe)\n\t\tSo(err, ShouldBeNil)\n\t\tSo(closeMe.Closed, ShouldEqual, 1)\n\t})\n\n\tConvey(\"Validate multiple sword falls do not block\", t, func() {\n\t\tdeath := NewDeath(syscall.SIGHUP)\n\t\tcloseMe := &CloseMe{}\n\t\tdeath.FallOnSword()\n\t\tdeath.FallOnSword()\n\t\terr := death.WaitForDeath(closeMe)\n\t\tSo(err, ShouldBeNil)\n\t\tSo(closeMe.Closed, ShouldEqual, 1)\n\t})\n\n\tConvey(\"Validate multiple sword falls do not block even after we have exited waitForDeath\", t, func() {\n\t\tdeath := NewDeath(syscall.SIGHUP)\n\t\tcloseMe := &CloseMe{}\n\t\tdeath.FallOnSword()\n\t\tdeath.FallOnSword()\n\t\terr := death.WaitForDeath(closeMe)\n\t\tSo(err, ShouldBeNil)\n\t\tdeath.FallOnSword()\n\t\tdeath.FallOnSword()\n\t\tSo(closeMe.Closed, ShouldEqual, 1)\n\t})\n\n\tConvey(\"Validate death gives up after timeout\", t, func() {\n\t\tdeath := NewDeath(syscall.SIGHUP)\n\t\tdeath.SetTimeout(10 * time.Millisecond)\n\t\tneverClose := &neverClose{}\n\t\tsyscall.Kill(os.Getpid(), syscall.SIGHUP)\n\t\terr := death.WaitForDeath(neverClose)\n\t\tSo(err, ShouldNotBeNil)\n\t})\n\n\tConvey(\"Validate death uses new logger\", t, func() {\n\t\tdeath := NewDeath(syscall.SIGHUP)\n\t\tcloseMe := &CloseMe{}\n\t\tlogger := &MockLogger{}\n\t\tdeath.SetLogger(logger)\n\n\t\tsyscall.Kill(os.Getpid(), syscall.SIGHUP)\n\t\terr := death.WaitForDeath(closeMe)\n\t\tSo(err, ShouldBeNil)\n\t\tSo(closeMe.Closed, ShouldEqual, 1)\n\t\tSo(logger.Logs, ShouldNotBeEmpty)\n\t})\n\n\tConvey(\"Close multiple things with one that fails the timer\", t, func() {\n\t\tdeath := NewDeath(syscall.SIGHUP)\n\t\tdeath.SetTimeout(10 * time.Millisecond)\n\t\tneverClose := &neverClose{}\n\t\tcloseMe := &CloseMe{}\n\t\tsyscall.Kill(os.Getpid(), syscall.SIGHUP)\n\t\terr := death.WaitForDeath(neverClose, closeMe)\n\t\tSo(err, ShouldNotBeNil)\n\t\tSo(closeMe.Closed, ShouldEqual, 1)\n\t})\n\n\tConvey(\"Close with anonymous function\", t, func() {\n\t\tdeath := NewDeath(syscall.SIGHUP)\n\t\tdeath.SetTimeout(5 * time.Millisecond)\n\t\tcloseMe := &CloseMe{}\n\t\tsyscall.Kill(os.Getpid(), syscall.SIGHUP)\n\t\tdeath.WaitForDeathWithFunc(func() {\n\t\t\tcloseMe.Close()\n\t\t\tSo(true, ShouldBeTrue)\n\t\t})\n\t\tSo(closeMe.Closed, ShouldEqual, 1)\n\t})\n\n\tConvey(\"Validate death errors when closer returns error\", t, func() {\n\t\tdeath := NewDeath(syscall.SIGHUP)\n\t\tkillMe := &KillMe{}\n\t\tdeath.FallOnSword()\n\t\terr := death.WaitForDeath(killMe)\n\t\tSo(err, ShouldNotBeNil)\n\t})\n\n}\n\nfunc TestGenerateErrString(t *testing.T) {\n\tConvey(\"Generate for multiple errors\", t, func() {\n\t\tclosers := []closer{\n\t\t\tcloser{\n\t\t\t\tErr:     fmt.Errorf(\"error 1\"),\n\t\t\t\tName:    \"foo\",\n\t\t\t\tPKGPath: \"my/pkg\",\n\t\t\t},\n\t\t\tcloser{\n\t\t\t\tErr:     fmt.Errorf(\"error 2\"),\n\t\t\t\tName:    \"bar\",\n\t\t\t\tPKGPath: \"my/otherpkg\",\n\t\t\t},\n\t\t}\n\n\t\texpected := \"my/pkg/foo: error 1, my/otherpkg/bar: error 2\"\n\t\tactual := generateErrString(closers)\n\n\t\tSo(actual, ShouldEqual, expected)\n\t})\n\n\tConvey(\"Generate for single error\", t, func() {\n\t\tclosers := []closer{\n\t\t\tcloser{\n\t\t\t\tErr:     fmt.Errorf(\"error 1\"),\n\t\t\t\tName:    \"foo\",\n\t\t\t\tPKGPath: \"my/pkg\",\n\t\t\t},\n\t\t}\n\n\t\texpected := \"my/pkg/foo: error 1\"\n\t\tactual := generateErrString(closers)\n\n\t\tSo(actual, ShouldEqual, expected)\n\t})\n}\n\ntype MockLogger struct {\n\tLogs []interface{}\n}\n\nfunc (l *MockLogger) Info(v ...interface{}) {\n\tfor _, log := range v {\n\t\tl.Logs = append(l.Logs, log)\n\t}\n}\n\nfunc (l *MockLogger) Debug(v ...interface{}) {\n\tfor _, log := range v {\n\t\tl.Logs = append(l.Logs, log)\n\t}\n}\n\nfunc (l *MockLogger) Error(v ...interface{}) {\n\tfor _, log := range v {\n\t\tl.Logs = append(l.Logs, log)\n\t}\n}\n\nfunc (l *MockLogger) Warn(v ...interface{}) {\n\tfor _, log := range v {\n\t\tl.Logs = append(l.Logs, log)\n\t}\n}\n\ntype neverClose struct {\n}\n\nfunc (n *neverClose) Close() error {\n\ttime.Sleep(2 * time.Minute)\n\treturn nil\n}\n\n// CloseMe returns nil from close\ntype CloseMe struct {\n\tClosed int\n}\n\nfunc (c *CloseMe) Close() error {\n\tc.Closed++\n\treturn nil\n}\n\n// KillMe returns an error from close\ntype KillMe struct{}\n\nfunc (c *KillMe) Close() error {\n\treturn fmt.Errorf(\"error from closer\")\n}\n"
  },
  {
    "path": "death_windows_test.go",
    "content": "package death\n\nimport (\n\t\"bytes\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"syscall\"\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/smartystreets/goconvey/convey\"\n)\n\nfunc TestDeath(t *testing.T) {\n\n\tConvey(\"Validate death happens cleanly on windows with ctrl-c event\", t, func() {\n\t\t// create source file\n\t\tconst source = `\npackage main\nimport (\n\t\"syscall\"\n\t\"github.com/vrecan/death/v3\"\n)\nfunc main() {\n\tdeath := death.NewDeath(syscall.SIGINT)\n\tdeath.WaitForDeath()\n}\n`\n\t\ttmp, err := ioutil.TempDir(\"\", \"TestCtrlBreak\")\n\t\tif err != nil {\n\t\t\tt.Fatal(\"TempDir failed: \", err)\n\t\t}\n\t\tdefer os.RemoveAll(tmp)\n\n\t\t// write ctrlbreak.go\n\t\tname := filepath.Join(tmp, \"ctlbreak\")\n\t\tsrc := name + \".go\"\n\t\tf, err := os.Create(src)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Failed to create %v: %v\", src, err)\n\t\t}\n\t\tdefer f.Close()\n\t\tf.Write([]byte(source))\n\n\t\t// compile it\n\t\texe := name + \".exe\"\n\t\tdefer os.Remove(exe)\n\t\to, err := exec.Command(\"go\", \"build\", \"-o\", exe, src).CombinedOutput()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Failed to compile: %v\\n%v\", err, string(o))\n\t\t}\n\n\t\t// run it\n\t\tcmd := exec.Command(exe)\n\t\tvar b bytes.Buffer\n\t\tcmd.Stdout = &b\n\t\tcmd.Stderr = &b\n\t\tcmd.SysProcAttr = &syscall.SysProcAttr{\n\t\t\tCreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,\n\t\t}\n\t\terr = cmd.Start()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Start failed: %v\", err)\n\t\t}\n\t\tgo func() {\n\t\t\ttime.Sleep(1 * time.Second)\n\t\t\tsendCtrlBreak(t, cmd.Process.Pid)\n\t\t}()\n\t\terr = cmd.Wait()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Program exited with error: %v\\n%v\", err, string(b.Bytes()))\n\t\t}\n\t})\n}\n\nfunc sendCtrlBreak(t *testing.T, pid int) {\n\td, e := syscall.LoadDLL(\"kernel32.dll\")\n\tif e != nil {\n\t\tt.Fatalf(\"LoadDLL: %v\\n\", e)\n\t}\n\tp, e := d.FindProc(\"GenerateConsoleCtrlEvent\")\n\tif e != nil {\n\t\tt.Fatalf(\"FindProc: %v\\n\", e)\n\t}\n\tr, _, e := p.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid))\n\tif r == 0 {\n\t\tt.Fatalf(\"GenerateConsoleCtrlEvent: %v\\n\", e)\n\t}\n}\n"
  },
  {
    "path": "deathlog.go",
    "content": "package death\n\n// Logger interface to log.\ntype Logger interface {\n\tError(v ...interface{})\n\tDebug(v ...interface{})\n\tInfo(v ...interface{})\n}\n\ntype defaultLogger struct{}\n\nvar logger = defaultLogger{}\n\n// DefaultLogger returns a logger that does nothing\nfunc DefaultLogger() Logger {\n\treturn logger\n}\n\nfunc (d defaultLogger) Error(v ...interface{}) {}\nfunc (d defaultLogger) Debug(v ...interface{}) {}\nfunc (d defaultLogger) Info(v ...interface{})  {}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/vrecan/death/v3\n\ngo 1.18\n\nrequire github.com/smartystreets/goconvey v1.7.2\n\nrequire (\n\tgithub.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60 // indirect\n\tgithub.com/jtolds/gls v4.20.0+incompatible // indirect\n\tgithub.com/smartystreets/assertions v1.2.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60 h1:btMZK5oA0NpSAOOE7zRfq2UEuPC9apJ2UBackURyaTU=\ngithub.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60/go.mod h1:cz9oNYuRUWGdHmLF2IodMLkAhcPtXeULvcBNagUrxTI=\ngithub.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=\ngithub.com/smartystreets/assertions v1.2.1 h1:bKNHfEv7tSIjZ8JbKaFjzFINljxG4lzZvmHUnElzOIg=\ngithub.com/smartystreets/assertions v1.2.1/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8=\ngithub.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=\ngithub.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\n"
  },
  {
    "path": "pkgPath_test.go",
    "content": "package death\n\nimport (\n\t\"testing\"\n\n\t. \"github.com/smartystreets/goconvey/convey\"\n)\n\nfunc TestGetPkgPath(t *testing.T) {\n\n\tConvey(\"Give pkgPath a ptr\", t, func() {\n\t\tc := &Closer{}\n\t\tname, pkgPath := getPkgPath(c)\n\t\tSo(name, ShouldEqual, \"Closer\")\n\t\tSo(pkgPath, ShouldEqual, \"github.com/vrecan/death/v3\")\n\n\t})\n\n\tConvey(\"Give pkgPath a interface\", t, func() {\n\t\tvar closable Closable\n\t\tclosable = Closer{}\n\t\tname, pkgPath := getPkgPath(closable)\n\t\tSo(name, ShouldEqual, \"Closer\")\n\t\tSo(pkgPath, ShouldEqual, \"github.com/vrecan/death/v3\")\n\t})\n\n\tConvey(\"Give pkgPath a copy\", t, func() {\n\t\tc := Closer{}\n\t\tname, pkgPath := getPkgPath(c)\n\t\tSo(name, ShouldEqual, \"Closer\")\n\t\tSo(pkgPath, ShouldEqual, \"github.com/vrecan/death/v3\")\n\t})\n}\n\ntype Closable interface {\n\tClose() error\n}\ntype Closer struct {\n}\n\nfunc (c Closer) Close() error {\n\treturn nil\n}\n"
  }
]