1package xxhash 2 3import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "strings" 8 "testing" 9) 10 11func TestAll(t *testing.T) { 12 for _, tt := range []struct { 13 name string 14 input string 15 want uint64 16 }{ 17 {"empty", "", 0xef46db3751d8e999}, 18 {"a", "a", 0xd24ec4f1a98c6e5b}, 19 {"as", "as", 0x1c330fb2d66be179}, 20 {"asd", "asd", 0x631c37ce72a97393}, 21 {"asdf", "asdf", 0x415872f599cea71e}, 22 { 23 "len=63", 24 // Exactly 63 characters, which exercises all code paths. 25 "Call me Ishmael. Some years ago--never mind how long precisely-", 26 0x02a2e85470d6fd96, 27 }, 28 } { 29 for chunkSize := 1; chunkSize <= len(tt.input); chunkSize++ { 30 name := fmt.Sprintf("%s,chunkSize=%d", tt.name, chunkSize) 31 t.Run(name, func(t *testing.T) { 32 testDigest(t, tt.input, chunkSize, tt.want) 33 }) 34 } 35 t.Run(tt.name, func(t *testing.T) { testSum(t, tt.input, tt.want) }) 36 } 37} 38 39func testDigest(t *testing.T, input string, chunkSize int, want uint64) { 40 d := New() 41 ds := New() // uses WriteString 42 for i := 0; i < len(input); i += chunkSize { 43 chunk := input[i:] 44 if len(chunk) > chunkSize { 45 chunk = chunk[:chunkSize] 46 } 47 n, err := d.Write([]byte(chunk)) 48 if err != nil || n != len(chunk) { 49 t.Fatalf("Digest.Write: got (%d, %v); want (%d, nil)", n, err, len(chunk)) 50 } 51 n, err = ds.WriteString(chunk) 52 if err != nil || n != len(chunk) { 53 t.Fatalf("Digest.WriteString: got (%d, %v); want (%d, nil)", n, err, len(chunk)) 54 } 55 } 56 if got := d.Sum64(); got != want { 57 t.Fatalf("Digest.Sum64: got 0x%x; want 0x%x", got, want) 58 } 59 if got := ds.Sum64(); got != want { 60 t.Fatalf("Digest.Sum64 (WriteString): got 0x%x; want 0x%x", got, want) 61 } 62 var b [8]byte 63 binary.BigEndian.PutUint64(b[:], want) 64 if got := d.Sum(nil); !bytes.Equal(got, b[:]) { 65 t.Fatalf("Sum: got %v; want %v", got, b[:]) 66 } 67} 68 69func testSum(t *testing.T, input string, want uint64) { 70 if got := Sum64([]byte(input)); got != want { 71 t.Fatalf("Sum64: got 0x%x; want 0x%x", got, want) 72 } 73 if got := Sum64String(input); got != want { 74 t.Fatalf("Sum64String: got 0x%x; want 0x%x", got, want) 75 } 76} 77 78func TestReset(t *testing.T) { 79 parts := []string{"The quic", "k br", "o", "wn fox jumps", " ov", "er the lazy ", "dog."} 80 d := New() 81 for _, part := range parts { 82 d.Write([]byte(part)) 83 } 84 h0 := d.Sum64() 85 86 d.Reset() 87 d.Write([]byte(strings.Join(parts, ""))) 88 h1 := d.Sum64() 89 90 if h0 != h1 { 91 t.Errorf("0x%x != 0x%x", h0, h1) 92 } 93} 94 95func TestBinaryMarshaling(t *testing.T) { 96 d := New() 97 d.WriteString("abc") 98 b, err := d.MarshalBinary() 99 if err != nil { 100 t.Fatal(err) 101 } 102 d = New() 103 d.WriteString("junk") 104 if err := d.UnmarshalBinary(b); err != nil { 105 t.Fatal(err) 106 } 107 d.WriteString("def") 108 if got, want := d.Sum64(), Sum64String("abcdef"); got != want { 109 t.Fatalf("after MarshalBinary+UnmarshalBinary, got 0x%x; want 0x%x", got, want) 110 } 111 112 d0 := New() 113 d1 := New() 114 for i := 0; i < 64; i++ { 115 b, err := d0.MarshalBinary() 116 if err != nil { 117 t.Fatal(err) 118 } 119 d0 = new(Digest) 120 if err := d0.UnmarshalBinary(b); err != nil { 121 t.Fatal(err) 122 } 123 if got, want := d0.Sum64(), d1.Sum64(); got != want { 124 t.Fatalf("after %d Writes, unmarshaled Digest gave sum 0x%x; want 0x%x", i, got, want) 125 } 126 127 d0.Write([]byte{'a'}) 128 d1.Write([]byte{'a'}) 129 } 130} 131 132var sink uint64 133 134func TestAllocs(t *testing.T) { 135 const shortStr = "abcdefghijklmnop" 136 // Sum64([]byte(shortString)) shouldn't allocate because the 137 // intermediate []byte ought not to escape. 138 // (See https://github.com/cespare/xxhash/pull/2.) 139 t.Run("Sum64", func(t *testing.T) { 140 testAllocs(t, func() { 141 sink = Sum64([]byte(shortStr)) 142 }) 143 }) 144 // Creating and using a Digest shouldn't allocate because its methods 145 // shouldn't make it escape. (A previous version of New returned a 146 // hash.Hash64 which forces an allocation.) 147 t.Run("Digest", func(t *testing.T) { 148 b := []byte("asdf") 149 testAllocs(t, func() { 150 d := New() 151 d.Write(b) 152 sink = d.Sum64() 153 }) 154 }) 155} 156 157func testAllocs(t *testing.T, fn func()) { 158 t.Helper() 159 if allocs := int(testing.AllocsPerRun(10, fn)); allocs > 0 { 160 t.Fatalf("got %d allocation(s) (want zero)", allocs) 161 } 162} 163