File: testio.go

package info (click to toggle)
golang-github-kisom-goutils 0.0~git20161101.0.858c9cb-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 396 kB
  • sloc: makefile: 6
file content (364 lines) | stat: -rw-r--r-- 10,019 bytes parent folder | download | duplicates (4)
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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
// Package testio implements various io utility types. Included are
// BrokenWriter, which fails after writing a certain number of bytes;
// a BufCloser, which wraps a bytes.Buffer in a Close method; a
// BrokenReadWriter, which fails after writing a certain number of
// bytes and/or reading a certain number of bytes; a LoggingBuffer
// that logs all reads and writes; and a BufferConn, that is designed
// to simulate net.Conn.
package testio

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
)

// BrokenWriter implements an io.Writer that fails after a certain
// number of bytes. This can be used to simulate a network connection
// that breaks during write or a file on a filesystem that becomes
// full, for example. A BrokenWriter doesn't actually store any data.
type BrokenWriter struct {
	current, limit int
}

// NewBrokenWriter creates a new BrokenWriter that can store only
// limit bytes.
func NewBrokenWriter(limit int) *BrokenWriter {
	return &BrokenWriter{limit: limit}
}

// Write will write the byte slice to the BrokenWriter, failing if the
// maximum number of bytes has been reached.
func (w *BrokenWriter) Write(p []byte) (int, error) {
	if (len(p) + w.current) <= w.limit {
		w.current += len(p)
	} else {
		spill := (len(p) + w.current) - w.limit
		w.current = w.limit
		return len(p) - spill, errors.New("testio: write failed")
	}

	return len(p), nil
}

// Extend increases the byte limit to allow more data to be written.
func (w *BrokenWriter) Extend(n int) {
	w.limit += n
}

// Reset clears the limit and bytes in the BrokenWriter. Extend needs
// to be called to allow data to be written.
func (w *BrokenWriter) Reset() {
	w.limit = 0
	w.current = 0
}

// Close is provided to satisfy the Closer interface.
func (w *BrokenWriter) Close() error {
	w.Reset()
	return nil
}

// SilentBrokenWriter implements an io.Writer that fails after a
// certain number of bytes. However, this failure is silent: it just
// reports fewer bytes written than p.  It doesn't actually store any
// data, and is used to verify that io.Writer implementations properly
// return errors on short writes.
type SilentBrokenWriter struct {
	current, limit int
}

// NewSilentBrokenWriter creates a new SilentBrokenWriter that can store only
// limit bytes.
func NewSilentBrokenWriter(limit int) *SilentBrokenWriter {
	return &SilentBrokenWriter{limit: limit}
}

// Write will write the byte slice to the SilentBrokenWriter, failing if the
// maximum number of bytes has been reached.
func (w *SilentBrokenWriter) Write(p []byte) (int, error) {
	if (len(p) + w.current) <= w.limit {
		w.current += len(p)
	} else {
		spill := (len(p) + w.current) - w.limit
		w.current = w.limit
		return len(p) - spill, nil
	}

	return len(p), nil
}

// Extend increases the byte limit to allow more data to be written.
func (w *SilentBrokenWriter) Extend(n int) {
	w.limit += n
}

// Reset clears the limit and bytes in the SilentBrokenWriter. Extend needs
// to be called to allow data to be written.
func (w *SilentBrokenWriter) Reset() {
	w.limit = 0
	w.current = 0
}

// Close is provided to satisfy the Closer interface.
func (w *SilentBrokenWriter) Close() error {
	w.Reset()
	return nil
}

// BrokenReadWriter implements a broken reader and writer, backed by a
// bytes.Buffer.
type BrokenReadWriter struct {
	rlimit, wlimit int
	buf            *bytes.Buffer
}

// NewBrokenReadWriter initialises a new BrokerReadWriter with an empty
// reader and the specified limits.
func NewBrokenReadWriter(wlimit, rlimit int) *BrokenReadWriter {
	return &BrokenReadWriter{
		wlimit: wlimit,
		rlimit: rlimit,
		buf:    &bytes.Buffer{},
	}
}

// Write satisfies the Writer interface.
func (brw *BrokenReadWriter) Write(p []byte) (int, error) {
	if (len(p) + brw.buf.Len()) > brw.wlimit {
		remain := brw.wlimit - brw.buf.Len()
		if remain > 0 {
			brw.buf.Write(p[:remain])
			return remain, errors.New("testio: write failed")
		}
		return 0, errors.New("testio: write failed")
	}
	return brw.buf.Write(p)
}

// Read satisfies the Reader interface.
func (brw *BrokenReadWriter) Read(p []byte) (int, error) {
	remain := brw.rlimit - brw.buf.Len()
	if len(p) > remain {
		tmp := make([]byte, len(p)-remain)
		n, err := brw.buf.Read(tmp)
		if err == nil {
			err = io.EOF
		}
		copy(p, tmp)
		return n, err
	}
	return brw.buf.Read(p)
}

// Extend increases the BrokenReadWriter limit.
func (brw *BrokenReadWriter) Extend(w, r int) {
	brw.rlimit += r
	brw.wlimit += w
}

// Reset clears the internal buffer. It retains its original limit.
func (brw *BrokenReadWriter) Reset() {
	brw.buf.Reset()
}

// BufCloser is a buffer wrapped with a Close method.
type BufCloser struct {
	buf *bytes.Buffer
}

// Write writes the data to the BufCloser.
func (buf *BufCloser) Write(p []byte) (int, error) {
	return buf.buf.Write(p)
}

// Read reads data from the BufCloser.
func (buf *BufCloser) Read(p []byte) (int, error) {
	return buf.buf.Read(p)
}

// Close is a stub function to satisfy the io.Closer interface.
func (buf *BufCloser) Close() error {
	return nil
}

// Reset clears the internal buffer.
func (buf *BufCloser) Reset() {
	buf.buf.Reset()
}

// Bytes returns the contents of the buffer as a byte slice.
func (buf *BufCloser) Bytes() []byte {
	return buf.buf.Bytes()
}

// NewBufCloser creates and initializes a new BufCloser using buf as
// its initial contents. It is intended to prepare a BufCloser to read
// existing data. It can also be used to size the internal buffer for
// writing. To do that, buf should have the desired capacity but a
// length of zero.
func NewBufCloser(buf []byte) *BufCloser {
	bc := new(BufCloser)
	bc.buf = bytes.NewBuffer(buf)
	return bc
}

// NewBufCloserString creates and initializes a new Buffer using
// string s as its initial contents. It is intended to prepare a
// buffer to read an existing string.
func NewBufCloserString(s string) *BufCloser {
	buf := new(BufCloser)
	buf.buf = bytes.NewBufferString(s)
	return buf
}

// A LoggingBuffer is an io.ReadWriter that prints the hex value of
// the data for all reads and writes.
type LoggingBuffer struct {
	rw   io.ReadWriter
	w    io.Writer
	name string
}

// NewLoggingBuffer creates a logging buffer from an existing
// io.ReadWriter. By default, it will log to standard error.
func NewLoggingBuffer(rw io.ReadWriter) *LoggingBuffer {
	return &LoggingBuffer{
		rw: rw,
		w:  os.Stderr,
	}
}

// LogTo sets the io.Writer that the buffer will write logs to.
func (lb *LoggingBuffer) LogTo(w io.Writer) {
	lb.w = w
}

// SetName gives a name to the logging buffer to help distinguish
// output from this buffer.
func (lb *LoggingBuffer) SetName(name string) {
	lb.name = name
}

// Write writes the data to the logging buffer and writes the data to
// the logging writer.
func (lb *LoggingBuffer) Write(p []byte) (int, error) {
	if lb.name != "" {
		fmt.Fprintf(lb.w, "[%s] ", lb.name)
	}

	fmt.Fprintf(lb.w, "[WRITE] %x\n", p)
	return lb.rw.Write(p)
}

// Read reads the data from the logging buffer and writes the data to
// the logging writer.
func (lb *LoggingBuffer) Read(p []byte) (int, error) {
	n, err := lb.rw.Read(p)
	if err != nil {
		return n, err
	}
	if lb.name != "" {
		fmt.Fprintf(lb.w, "[%s] ", lb.name)
	}

	fmt.Fprintf(lb.w, "[READ] %x\n", p)
	return n, err
}

// BufferConn is a type that can be used to simulate network
// connections between a "client" (the code that uses the BufferConn)
// and some simulated "peer". Writes go to a "client" buffer, which is
// used to record the data sent by the caller, which may be read with
// ReadPeer. The peer's responses may be simulated by calling
// WritePeer; when the client reads from the BufferConn, they will see
// this data.
type BufferConn struct {
	client, peer *bytes.Buffer
}

// NewBufferConn initialises a new simulated network connection.
func NewBufferConn() *BufferConn {
	return &BufferConn{
		client: &bytes.Buffer{},
		peer:   &bytes.Buffer{},
	}
}

// Write writes to the client buffer.
func (bc *BufferConn) Write(p []byte) (int, error) {
	return bc.client.Write(p)
}

// Read reads from the peer buffer.
func (bc *BufferConn) Read(p []byte) (int, error) {
	return bc.peer.Read(p)
}

// WritePeer writes data to the peer buffer.
func (bc *BufferConn) WritePeer(p []byte) (int, error) {
	return bc.peer.Write(p)
}

// ReadClient reads data from the client buffer.
func (bc *BufferConn) ReadClient(p []byte) (int, error) {
	return bc.client.Read(p)
}

// Close is a dummy operation that allows the BufferConn to be used as
// an io.Closer.
func (bc *BufferConn) Close() error {
	return nil
}

// BrokenCloser is a BufCloser that fails to close.
type BrokenCloser struct {
	buf *bytes.Buffer
}

// Write writes the data to the BrokenCloser.
func (buf *BrokenCloser) Write(p []byte) (int, error) {
	return buf.buf.Write(p)
}

// Read reads data from the BrokenCloser.
func (buf *BrokenCloser) Read(p []byte) (int, error) {
	return buf.buf.Read(p)
}

// Close is a stub function to satisfy the io.Closer interface.
func (buf *BrokenCloser) Close() error {
	return errors.New("testio: broken closer is broken")
}

// Reset clears the internal buffer.
func (buf *BrokenCloser) Reset() {
	buf.buf.Reset()
}

// Bytes returns the contents of the buffer as a byte slice.
func (buf *BrokenCloser) Bytes() []byte {
	return buf.buf.Bytes()
}

// NewBrokenCloser creates and initializes a new BrokenCloser using buf as
// its initial contents. It is intended to prepare a BrokenCloser to read
// existing data. It can also be used to size the internal buffer for
// writing. To do that, buf should have the desired capacity but a
// length of zero.
func NewBrokenCloser(buf []byte) *BrokenCloser {
	bc := new(BrokenCloser)
	bc.buf = bytes.NewBuffer(buf)
	return bc
}

// NewBrokenCloserString creates and initializes a new Buffer using
// string s as its initial contents. It is intended to prepare a
// buffer to read an existing string.
func NewBrokenCloserString(s string) *BrokenCloser {
	buf := new(BrokenCloser)
	buf.buf = bytes.NewBufferString(s)
	return buf
}