// Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // Package testutil is a collection of testing helper methods. package testutil import ( "bytes" "encoding/hex" "fmt" "io" "io/ioutil" "strings" ) // ResizeData resizes the input. If n < 0, then the original input will be // returned as is. If n <= len(input), then the input slice will be truncated. // However, if n > len(input), then the input will be replicated to fill in // the missing bytes, but each replicated string will be XORed by some byte // mask to avoid favoring algorithms with large LZ77 windows. // // If n > len(input), then len(input) must be > 0. func ResizeData(input []byte, n int) []byte { if n < 0 { return input } if len(input) >= n { return input[:n] } if len(input) == 0 { panic("unable to replicate an empty string") } var mask byte output := make([]byte, n) for i := range output { idx := i % len(input) output[i] = input[idx] ^ mask if idx == len(input)-1 { mask++ } } return output } // MustLoadFile must load a file or else panics. func MustLoadFile(file string) []byte { b, err := ioutil.ReadFile(file) if err != nil { panic(err) } return b } // MustDecodeHex must decode a hexadecimal string or else panics. func MustDecodeHex(s string) []byte { b, err := hex.DecodeString(s) if err != nil { panic(err) } return b } // MustDecodeBitGen must decode a BitGen formatted string or else panics. func MustDecodeBitGen(s string) []byte { b, err := DecodeBitGen(s) if err != nil { panic(err) } return b } // BytesCompare compares inputs a and b and reports whether they are equal. // // If they are not equal, it returns two one-line strings that are // representative of the differences between the two strings. // The output will be quoted strings if it seems like the data is text, // otherwise, it will use hexadecimal strings. // // Example usage: // // if got, want, ok := testutil.BytesCompare(output, v.output); !ok { // t.Errorf("output mismatch:\ngot %s\nwant %s", got, want) // } // func BytesCompare(a, b []byte) (sa, sb string, ok bool) { if ok = bytes.Equal(a, b); ok { return } commonPrefix := func(a, b []byte) int { if len(a) > len(b) { a, b = b, a } for i := range a { if a[i] != b[i] { return i } } return len(a) } formatter := func(a, b []byte, format string, trimHead, maxLen int) (sa, sb string) { trimHead -= maxLen / 2 // Always provide context of equal bytes if trimHead < 0 { trimHead = 0 } if trimHead > (len(a) - maxLen) { trimHead = (len(a) - maxLen) } if trimHead > (len(b) - maxLen) { trimHead = (len(b) - maxLen) } var head, atail, btail string if trimHead > 0 { a = a[trimHead:] b = b[trimHead:] head = fmt.Sprintf("(%d bytes)...", trimHead) } if len(a) > maxLen { atail = fmt.Sprintf("...(%d bytes)", len(a)-maxLen) a = a[:maxLen] } if len(b) > maxLen { btail = fmt.Sprintf("...(%d bytes)", len(b)-maxLen) b = b[:maxLen] } sa = fmt.Sprintf("%s"+format+"%s", head, a, atail) sb = fmt.Sprintf("%s"+format+"%s", head, b, btail) return sa, sb } const maxLen = 64 n := commonPrefix(a, b) sa, sb = formatter(a, b, "%q", n, maxLen) // Favor quoted output, first if s := sa + sb; strings.Count(s, `\u`)+strings.Count(s, `\x`) > maxLen/8 { sa, sb = formatter(a, b, "%x", n, maxLen/2) // Fallback to hex, next } return sa, sb, false } // BuggyReader returns Err after N bytes have been read from R. type BuggyReader struct { R io.Reader N int64 // Number of valid bytes to read Err error // Return this error after N bytes } func (br *BuggyReader) Read(buf []byte) (int, error) { if int64(len(buf)) > br.N { buf = buf[:br.N] } n, err := br.R.Read(buf) br.N -= int64(n) if err == nil && br.N <= 0 { return n, br.Err } return n, err } // BuggyWriter returns Err after N bytes have been written to W. type BuggyWriter struct { W io.Writer N int64 // Number of valid bytes to write Err error // Return this error after N bytes } func (bw *BuggyWriter) Write(buf []byte) (int, error) { if int64(len(buf)) > bw.N { buf = buf[:bw.N] } n, err := bw.W.Write(buf) bw.N -= int64(n) if err == nil && bw.N <= 0 { return n, bw.Err } return n, err }