gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/host/errors_test.go (about)

     1  package host
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"gitlab.com/SiaPrime/SiaPrime/modules"
    12  )
    13  
    14  // countFileLines is a helper function that will count the number of lines in a
    15  // file, based on the number of '\n' characters. countFileLines will load the
    16  // file into memory using ioutil.ReadAll.
    17  //
    18  // countFileLines will ignore all lines with the string 'DEBUG' in it.
    19  func countFileLines(filepath string) (uint64, error) {
    20  	file, err := os.Open(filepath)
    21  	if err != nil {
    22  		return 0, err
    23  	}
    24  	scanner := bufio.NewScanner(file)
    25  	lines := uint64(0)
    26  	for scanner.Scan() {
    27  		line := scanner.Text()
    28  		if !strings.Contains(line, "[DEBUG]") {
    29  			lines++
    30  		}
    31  	}
    32  	return lines, nil
    33  }
    34  
    35  // TestComposeErrors checks that composeErrors is correctly composing errors
    36  // and handling edge cases.
    37  func TestComposeErrors(t *testing.T) {
    38  	if testing.Short() {
    39  		t.SkipNow()
    40  	}
    41  	t.Parallel()
    42  
    43  	trials := []struct {
    44  		inputErrors           []error
    45  		nilReturn             bool
    46  		expectedComposedError string
    47  	}{
    48  		{
    49  			nil,
    50  			true,
    51  			"",
    52  		},
    53  		{
    54  			make([]error, 0),
    55  			true,
    56  			"",
    57  		},
    58  		{
    59  			[]error{errors.New("single error")},
    60  			false,
    61  			"single error",
    62  		},
    63  		{
    64  			[]error{
    65  				errors.New("first error"),
    66  				errors.New("second error"),
    67  			},
    68  			false,
    69  			"first error; second error",
    70  		},
    71  		{
    72  			[]error{
    73  				errors.New("first error"),
    74  				errors.New("second error"),
    75  				errors.New("third error"),
    76  			},
    77  			false,
    78  			"first error; second error; third error",
    79  		},
    80  		{
    81  			[]error{
    82  				nil,
    83  				errors.New("second error"),
    84  				errors.New("third error"),
    85  			},
    86  			false,
    87  			"second error; third error",
    88  		},
    89  		{
    90  			[]error{
    91  				errors.New("first error"),
    92  				nil,
    93  				nil,
    94  			},
    95  			false,
    96  			"first error",
    97  		},
    98  		{
    99  			[]error{
   100  				nil,
   101  				nil,
   102  				nil,
   103  			},
   104  			true,
   105  			"",
   106  		},
   107  	}
   108  	for _, trial := range trials {
   109  		err := composeErrors(trial.inputErrors...)
   110  		if trial.nilReturn {
   111  			if err != nil {
   112  				t.Error("composeError failed a test, expecting nil, got", err)
   113  			}
   114  		} else {
   115  			if err == nil {
   116  				t.Error("not expecting a nil error when doing composition")
   117  			}
   118  			if err.Error() != trial.expectedComposedError {
   119  				t.Error("composeError failed a test, expecting", trial.expectedComposedError, "got", err.Error())
   120  			}
   121  		}
   122  	}
   123  }
   124  
   125  // TestExtendErr checks that extendErr works as described - preserving the
   126  // error type within the package and adding a string. Also returning nil if the
   127  // input error is nil.
   128  func TestExtendErr(t *testing.T) {
   129  	// Try extending a nil error.
   130  	var err error
   131  	err2 := extendErr("extend: ", err)
   132  	if err2 != nil {
   133  		t.Error("providing a nil error to extendErr does not return nil")
   134  	}
   135  
   136  	// Try extending a normal error.
   137  	err = errors.New("extend me")
   138  	err2 = extendErr("extend: ", err)
   139  	if err2.Error() != "extend: extend me" {
   140  		t.Error("normal error not extended correctly")
   141  	}
   142  
   143  	// Try extending ErrorCommunication.
   144  	err = ErrorCommunication("err")
   145  	err2 = extendErr("extend: ", err)
   146  	if err2.Error() != "communication error: extend: err" {
   147  		t.Error("extending ErrorCommunication did not occur correctly:", err2.Error())
   148  	}
   149  	if _, ok := err2.(ErrorCommunication); !ok {
   150  		t.Error("extended error did not preserve error type")
   151  	}
   152  
   153  	// Try extending ErrorConnection.
   154  	err = ErrorConnection("err")
   155  	err2 = extendErr("extend: ", err)
   156  	if err2.Error() != "connection error: extend: err" {
   157  		t.Error("extending ErrorConnection did not occur correctly:", err2.Error())
   158  	}
   159  	switch err2.(type) {
   160  	case ErrorConnection:
   161  	default:
   162  		t.Error("extended error did not preserve error type")
   163  	}
   164  
   165  	// Try extending ErrorConsensus.
   166  	err = ErrorConsensus("err")
   167  	err2 = extendErr("extend: ", err)
   168  	if err2.Error() != "consensus error: extend: err" {
   169  		t.Error("extending ErrorConsensus did not occur correctly:", err2.Error())
   170  	}
   171  	switch err2.(type) {
   172  	case ErrorConsensus:
   173  	default:
   174  		t.Error("extended error did not preserve error type")
   175  	}
   176  
   177  	// Try extending ErrorInternal.
   178  	err = ErrorInternal("err")
   179  	err2 = extendErr("extend: ", err)
   180  	if err2.Error() != "internal error: extend: err" {
   181  		t.Error("extending ErrorInternal did not occur correctly:", err2.Error())
   182  	}
   183  	switch err2.(type) {
   184  	case ErrorInternal:
   185  	default:
   186  		t.Error("extended error did not preserve error type")
   187  	}
   188  }
   189  
   190  // TestManagedLogError will check that errors are being logged correctly based
   191  // on the logAllLimit, the probabilities, and the logFewLimit.
   192  func TestManagedLogError(t *testing.T) {
   193  	if testing.Short() {
   194  		t.SkipNow()
   195  	}
   196  	ht, err := newHostTester("TestManagedLogError")
   197  	if err != nil {
   198  		t.Fatal(err)
   199  	}
   200  	defer ht.Close()
   201  	logFilepath := filepath.Join(ht.persistDir, modules.HostDir, logFile)
   202  
   203  	// Count the number of lines in the log file.
   204  	baseLines, err := countFileLines(logFilepath)
   205  	if err != nil {
   206  		t.Fatal(err)
   207  	}
   208  
   209  	// Log 'logAllLimit' for ErrorCommunication.
   210  	for i := uint64(0); i < logAllLimit; i++ {
   211  		ht.host.managedLogError(ErrorCommunication("comm error"))
   212  	}
   213  	logLines, err := countFileLines(logFilepath)
   214  	if err != nil {
   215  		t.Fatal(err)
   216  	}
   217  	if logLines != baseLines+logAllLimit {
   218  		t.Error("does not seem that all communication errors were logged")
   219  	}
   220  	baseLines = logLines
   221  
   222  	// Log 'logAllLimit' for ErrorConnection.
   223  	for i := uint64(0); i < logAllLimit; i++ {
   224  		ht.host.managedLogError(ErrorConnection("conn error"))
   225  	}
   226  	logLines, err = countFileLines(logFilepath)
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	if logLines != baseLines+logAllLimit {
   231  		t.Error("does not seem that all connection errors were logged")
   232  	}
   233  	baseLines = logLines
   234  
   235  	// Log 'logAllLimit' for ErrorConsensus.
   236  	for i := uint64(0); i < logAllLimit; i++ {
   237  		ht.host.managedLogError(ErrorConsensus("consensus error"))
   238  	}
   239  	logLines, err = countFileLines(logFilepath)
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	}
   243  	if logLines != baseLines+logAllLimit {
   244  		t.Error("does not seem that all consensus errors were logged")
   245  	}
   246  	baseLines = logLines
   247  
   248  	// Log 'logAllLimit' for ErrorInternal.
   249  	for i := uint64(0); i < logAllLimit; i++ {
   250  		ht.host.managedLogError(ErrorInternal("internal error"))
   251  	}
   252  	logLines, err = countFileLines(logFilepath)
   253  	if err != nil {
   254  		t.Fatal(err)
   255  	}
   256  	if logLines != baseLines+logAllLimit {
   257  		t.Error("does not seem that all internal errors were logged")
   258  	}
   259  	baseLines = logLines
   260  
   261  	// Log 'logAllLimit' for normal errors.
   262  	for i := uint64(0); i < logAllLimit; i++ {
   263  		ht.host.managedLogError(errors.New("normal error"))
   264  	}
   265  	logLines, err = countFileLines(logFilepath)
   266  	if err != nil {
   267  		t.Fatal(err)
   268  	}
   269  	if logLines != baseLines+logAllLimit {
   270  		t.Error("does not seem that all normal errors were logged", logLines, baseLines, logAllLimit)
   271  	}
   272  	baseLines = logLines
   273  
   274  	// Log enough ErrorInternal errors to bring ErrorInternal close, but not
   275  	// all the way, to the 'logFewLimit'.
   276  	remaining := logFewLimit - logAllLimit
   277  	logsNeeded := remaining * errorInternalProbability
   278  	for i := uint64(0); i < logsNeeded/3; i++ {
   279  		ht.host.managedLogError(ErrorInternal("internal err"))
   280  	}
   281  	logLines, err = countFileLines(logFilepath)
   282  	if err != nil {
   283  		t.Fatal(err)
   284  	}
   285  	if logLines < baseLines+remaining/6 || logLines > baseLines+remaining {
   286  		t.Error("probabilistic logging is not logging with the correct probability:", logLines, baseLines, remaining)
   287  	}
   288  	// Log enough ErrorInternal errors to bring it all the way to
   289  	// 'logFewLimit'.
   290  	for i := uint64(0); i < logsNeeded*5; i++ {
   291  		ht.host.managedLogError(ErrorInternal("internal err"))
   292  	}
   293  	logLines, err = countFileLines(logFilepath)
   294  	if err != nil {
   295  		t.Fatal(err)
   296  	}
   297  	if logLines < baseLines+remaining || logLines > baseLines+logsNeeded*2 {
   298  		t.Error("probabilisitic logging is not clamping correctly:", baseLines, logLines, logsNeeded)
   299  	}
   300  	baseLines = logLines
   301  
   302  	// Log enough ErrorCommunication errors to bring ErrorCommunication close, but not
   303  	// all the way, to the 'logFewLimit'.
   304  	remaining = logFewLimit - logAllLimit
   305  	logsNeeded = remaining * errorCommunicationProbability
   306  	for i := uint64(0); i < logsNeeded/3; i++ {
   307  		ht.host.managedLogError(ErrorCommunication("comm err"))
   308  	}
   309  	logLines, err = countFileLines(logFilepath)
   310  	if err != nil {
   311  		t.Fatal(err)
   312  	}
   313  	if logLines < baseLines+remaining/6 || logLines > baseLines+remaining {
   314  		t.Error("probabilistic logging is not logging with the correct probability:", baseLines, logLines, logsNeeded, remaining)
   315  	}
   316  	// Log enough ErrorCommunication errors to bring it all the way to
   317  	// 'logFewLimit'.
   318  	for i := uint64(0); i < logsNeeded*5; i++ {
   319  		ht.host.managedLogError(ErrorCommunication("comm err"))
   320  	}
   321  	logLines, err = countFileLines(logFilepath)
   322  	if err != nil {
   323  		t.Fatal(err)
   324  	}
   325  	if logLines < baseLines+remaining || logLines > baseLines+logsNeeded*2 {
   326  		t.Error("probabilisitic logging is not clamping correctly:", baseLines, logLines, logsNeeded, remaining)
   327  	}
   328  }