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 }