Skip to content

Commit

Permalink
pkg/mock: add a mock of os.Exit for testing purposes
Browse files Browse the repository at this point in the history
  • Loading branch information
tav committed Jan 27, 2020
1 parent b845eaf commit 915fce2
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 0 deletions.
85 changes: 85 additions & 0 deletions pkg/mock/osexit/osexit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Public Domain (-) 2018-present, The Core Authors.
// See the Core UNLICENSE file for details.

// Package osexit mocks the os.Exit function.
//
// To use, first set a package-specific osExit function, e.g.
//
// var osExit = os.Exit
//
// Then use that when you want to call os.Exit, e.g.
//
// if somethingFatal {
// osExit(1)
// return
// }
//
// Make sure to return immediately after the call to osExit, so that testing
// code will match real code as closely as possible.
//
// You can now use the utility functions provided by this package to override
// osExit for testing purposes, e.g.
//
// osExit = osexit.Set()
// invokeCodeCallingExit()
// if !osexit.Called() {
// t.Fatalf("os.Exit was not called as expected")
// }
//
package osexit

import (
"sync"
)

var (
called bool
mu sync.RWMutex // protects called, status
status int
)

// Called returns whether the mock os.Exit function was called.
func Called() bool {
mu.RLock()
c := called
mu.RUnlock()
return c
}

// Func provides a mock for the os.Exit function. Special care must be taken
// when testing os.Exit to make sure no code runs after the call to Exit. It's
// recommended to put a return statement after Exit calls so that the behaviour
// of the mock matches that of the real function as much as possible.
func Func(code int) {
mu.Lock()
if called {
mu.Unlock()
return
}
called = true
status = code
mu.Unlock()
}

// Reset resets the state of the mock function.
func Reset() {
mu.Lock()
called = false
status = 0
mu.Unlock()
}

// Set returns the mock os.Exit function after calling Reset.
func Set() func(int) {
Reset()
return Func
}

// Status returns the status code that the mock os.Exit function was called
// with.
func Status() int {
mu.RLock()
s := status
mu.RUnlock()
return s
}
38 changes: 38 additions & 0 deletions pkg/mock/osexit/osexit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Public Domain (-) 2018-present, The Core Authors.
// See the Core UNLICENSE file for details.

package osexit_test

import (
"os"
"testing"

"espra.com/pkg/mock/osexit"
)

var osExit = os.Exit

func TestOsExit(t *testing.T) {
osExit = osexit.Set()
osExit(2)
if !osexit.Called() {
t.Fatalf("mock exit function was not called")
}
status := osexit.Status()
if status != 2 {
t.Fatalf("mock exit function did not set the right status code: got %d, want 2", status)
}
osExit(3)
status = osexit.Status()
if status != 2 {
t.Fatalf("mock exit function overrode the status set by a previous call: got %d, want 2", status)
}
osexit.Reset()
if osexit.Called() {
t.Fatalf("the reset mock exit function claims to have been called")
}
status = osexit.Status()
if status != 0 {
t.Fatalf("the reset mock exit function returned a non-zero status code: got %d, want 0", status)
}
}

0 comments on commit 915fce2

Please sign in to comment.