File: errors_test.go

package info (click to toggle)
sia 1.3.0-4
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 6,340 kB
  • sloc: makefile: 80; sh: 52
file content (328 lines) | stat: -rw-r--r-- 8,698 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
package host

import (
	"bufio"
	"errors"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/NebulousLabs/Sia/modules"
)

// countFileLines is a helper function that will count the number of lines in a
// file, based on the number of '\n' characters. countFileLines will load the
// file into memory using ioutil.ReadAll.
//
// countFileLines will ignore all lines with the string 'DEBUG' in it.
func countFileLines(filepath string) (uint64, error) {
	file, err := os.Open(filepath)
	if err != nil {
		return 0, err
	}
	scanner := bufio.NewScanner(file)
	lines := uint64(0)
	for scanner.Scan() {
		line := scanner.Text()
		if !strings.Contains(line, "[DEBUG]") {
			lines++
		}
	}
	return lines, nil
}

// TestComposeErrors checks that composeErrors is correctly composing errors
// and handling edge cases.
func TestComposeErrors(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	t.Parallel()

	trials := []struct {
		inputErrors           []error
		nilReturn             bool
		expectedComposedError string
	}{
		{
			nil,
			true,
			"",
		},
		{
			make([]error, 0),
			true,
			"",
		},
		{
			[]error{errors.New("single error")},
			false,
			"single error",
		},
		{
			[]error{
				errors.New("first error"),
				errors.New("second error"),
			},
			false,
			"first error; second error",
		},
		{
			[]error{
				errors.New("first error"),
				errors.New("second error"),
				errors.New("third error"),
			},
			false,
			"first error; second error; third error",
		},
		{
			[]error{
				nil,
				errors.New("second error"),
				errors.New("third error"),
			},
			false,
			"second error; third error",
		},
		{
			[]error{
				errors.New("first error"),
				nil,
				nil,
			},
			false,
			"first error",
		},
		{
			[]error{
				nil,
				nil,
				nil,
			},
			true,
			"",
		},
	}
	for _, trial := range trials {
		err := composeErrors(trial.inputErrors...)
		if trial.nilReturn {
			if err != nil {
				t.Error("composeError failed a test, expecting nil, got", err)
			}
		} else {
			if err == nil {
				t.Error("not expecting a nil error when doing composition")
			}
			if err.Error() != trial.expectedComposedError {
				t.Error("composeError failed a test, expecting", trial.expectedComposedError, "got", err.Error())
			}
		}
	}
}

// TestExtendErr checks that extendErr works as described - preserving the
// error type within the package and adding a string. Also returning nil if the
// input error is nil.
func TestExtendErr(t *testing.T) {
	// Try extending a nil error.
	var err error
	err2 := extendErr("extend: ", err)
	if err2 != nil {
		t.Error("providing a nil error to extendErr does not return nil")
	}

	// Try extending a normal error.
	err = errors.New("extend me")
	err2 = extendErr("extend: ", err)
	if err2.Error() != "extend: extend me" {
		t.Error("normal error not extended correctly")
	}

	// Try extending ErrorCommunication.
	err = ErrorCommunication("err")
	err2 = extendErr("extend: ", err)
	if err2.Error() != "communication error: extend: err" {
		t.Error("extending ErrorCommunication did not occur correctly:", err2.Error())
	}
	if _, ok := err2.(ErrorCommunication); !ok {
		t.Error("extended error did not preserve error type")
	}

	// Try extending ErrorConnection.
	err = ErrorConnection("err")
	err2 = extendErr("extend: ", err)
	if err2.Error() != "connection error: extend: err" {
		t.Error("extending ErrorConnection did not occur correctly:", err2.Error())
	}
	switch err2.(type) {
	case ErrorConnection:
	default:
		t.Error("extended error did not preserve error type")
	}

	// Try extending ErrorConsensus.
	err = ErrorConsensus("err")
	err2 = extendErr("extend: ", err)
	if err2.Error() != "consensus error: extend: err" {
		t.Error("extending ErrorConsensus did not occur correctly:", err2.Error())
	}
	switch err2.(type) {
	case ErrorConsensus:
	default:
		t.Error("extended error did not preserve error type")
	}

	// Try extending ErrorInternal.
	err = ErrorInternal("err")
	err2 = extendErr("extend: ", err)
	if err2.Error() != "internal error: extend: err" {
		t.Error("extending ErrorInternal did not occur correctly:", err2.Error())
	}
	switch err2.(type) {
	case ErrorInternal:
	default:
		t.Error("extended error did not preserve error type")
	}
}

// TestManagedLogError will check that errors are being logged correctly based
// on the logAllLimit, the probabilities, and the logFewLimit.
func TestManagedLogError(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	ht, err := newHostTester("TestManagedLogError")
	if err != nil {
		t.Fatal(err)
	}
	defer ht.Close()
	logFilepath := filepath.Join(ht.persistDir, modules.HostDir, logFile)

	// Count the number of lines in the log file.
	baseLines, err := countFileLines(logFilepath)
	if err != nil {
		t.Fatal(err)
	}

	// Log 'logAllLimit' for ErrorCommunication.
	for i := uint64(0); i < logAllLimit; i++ {
		ht.host.managedLogError(ErrorCommunication("comm error"))
	}
	logLines, err := countFileLines(logFilepath)
	if err != nil {
		t.Fatal(err)
	}
	if logLines != baseLines+logAllLimit {
		t.Error("does not seem that all communication errors were logged")
	}
	baseLines = logLines

	// Log 'logAllLimit' for ErrorConnection.
	for i := uint64(0); i < logAllLimit; i++ {
		ht.host.managedLogError(ErrorConnection("conn error"))
	}
	logLines, err = countFileLines(logFilepath)
	if err != nil {
		t.Fatal(err)
	}
	if logLines != baseLines+logAllLimit {
		t.Error("does not seem that all connection errors were logged")
	}
	baseLines = logLines

	// Log 'logAllLimit' for ErrorConsensus.
	for i := uint64(0); i < logAllLimit; i++ {
		ht.host.managedLogError(ErrorConsensus("consensus error"))
	}
	logLines, err = countFileLines(logFilepath)
	if err != nil {
		t.Fatal(err)
	}
	if logLines != baseLines+logAllLimit {
		t.Error("does not seem that all consensus errors were logged")
	}
	baseLines = logLines

	// Log 'logAllLimit' for ErrorInternal.
	for i := uint64(0); i < logAllLimit; i++ {
		ht.host.managedLogError(ErrorInternal("internal error"))
	}
	logLines, err = countFileLines(logFilepath)
	if err != nil {
		t.Fatal(err)
	}
	if logLines != baseLines+logAllLimit {
		t.Error("does not seem that all internal errors were logged")
	}
	baseLines = logLines

	// Log 'logAllLimit' for normal errors.
	for i := uint64(0); i < logAllLimit; i++ {
		ht.host.managedLogError(errors.New("normal error"))
	}
	logLines, err = countFileLines(logFilepath)
	if err != nil {
		t.Fatal(err)
	}
	if logLines != baseLines+logAllLimit {
		t.Error("does not seem that all normal errors were logged", logLines, baseLines, logAllLimit)
	}
	baseLines = logLines

	// Log enough ErrorInternal errors to bring ErrorInternal close, but not
	// all the way, to the 'logFewLimit'.
	remaining := logFewLimit - logAllLimit
	logsNeeded := remaining * errorInternalProbability
	for i := uint64(0); i < logsNeeded/3; i++ {
		ht.host.managedLogError(ErrorInternal("internal err"))
	}
	logLines, err = countFileLines(logFilepath)
	if err != nil {
		t.Fatal(err)
	}
	if logLines < baseLines+remaining/6 || logLines > baseLines+remaining {
		t.Error("probabilistic logging is not logging with the correct probability:", logLines, baseLines, remaining)
	}
	// Log enough ErrorInternal errors to bring it all the way to
	// 'logFewLimit'.
	for i := uint64(0); i < logsNeeded*5; i++ {
		ht.host.managedLogError(ErrorInternal("internal err"))
	}
	logLines, err = countFileLines(logFilepath)
	if err != nil {
		t.Fatal(err)
	}
	if logLines < baseLines+remaining || logLines > baseLines+logsNeeded*2 {
		t.Error("probabilisitic logging is not clamping correctly:", baseLines, logLines, logsNeeded)
	}
	baseLines = logLines

	// Log enough ErrorCommunication errors to bring ErrorCommunication close, but not
	// all the way, to the 'logFewLimit'.
	remaining = logFewLimit - logAllLimit
	logsNeeded = remaining * errorCommunicationProbability
	for i := uint64(0); i < logsNeeded/3; i++ {
		ht.host.managedLogError(ErrorCommunication("comm err"))
	}
	logLines, err = countFileLines(logFilepath)
	if err != nil {
		t.Fatal(err)
	}
	if logLines < baseLines+remaining/6 || logLines > baseLines+remaining {
		t.Error("probabilistic logging is not logging with the correct probability:", baseLines, logLines, logsNeeded, remaining)
	}
	// Log enough ErrorCommunication errors to bring it all the way to
	// 'logFewLimit'.
	for i := uint64(0); i < logsNeeded*5; i++ {
		ht.host.managedLogError(ErrorCommunication("comm err"))
	}
	logLines, err = countFileLines(logFilepath)
	if err != nil {
		t.Fatal(err)
	}
	if logLines < baseLines+remaining || logLines > baseLines+logsNeeded*2 {
		t.Error("probabilisitic logging is not clamping correctly:", baseLines, logLines, logsNeeded, remaining)
	}
}