github.com/fluhus/gostuff@v0.4.1-0.20240331134726-be71864f2b5d/rhash/common_test.go (about)

     1  // A generic test suite for rolling hashes.
     2  
     3  package rhash
     4  
     5  import (
     6  	"crypto/rand"
     7  	"hash"
     8  	"testing"
     9  
    10  	"github.com/fluhus/gostuff/sets"
    11  )
    12  
    13  // Runs the test suite for a hash64.
    14  func test64(t *testing.T, f func(n int) hash.Hash64) {
    15  	t.Run("basic", func(t *testing.T) { test64basic(t, f) })
    16  	t.Run("cyclic", func(t *testing.T) { test64cyclic(t, f) })
    17  	t.Run("big-n", func(t *testing.T) { test64bigN(t, f) })
    18  }
    19  
    20  func test64basic(t *testing.T, f func(n int) hash.Hash64) {
    21  	data := []byte("amitamit")
    22  	tests := []struct {
    23  		n        int
    24  		wantSize []int
    25  		wantEq   []int
    26  	}{
    27  		{2, []int{1, 2, 3, 4, 5, 5, 5, 5}, []int{-1, -1, -1, -1, -1, 1, 2, 3}},
    28  		{3, []int{1, 2, 3, 4, 5, 6, 6, 6}, []int{-1, -1, -1, -1, -1, -1, 2, 3}},
    29  	}
    30  	for _, test := range tests {
    31  		slice := []uint64{}
    32  		set := sets.Set[uint64]{}
    33  		h := f(test.n)
    34  		for i := range data {
    35  			h.Write(data[i : i+1])
    36  			slice = append(slice, h.Sum64())
    37  			set.Add(h.Sum64())
    38  			if len(set) != test.wantSize[i] {
    39  				t.Fatalf("n=%d #%d: set size=%v, want %v",
    40  					test.n, i, len(set), test.wantSize[i])
    41  			}
    42  			if test.wantEq[i] != -1 && h.Sum64() != slice[test.wantEq[i]] {
    43  				t.Fatalf("n=%d #%d: Sum64()=%d, want %d",
    44  					test.n, i, h.Sum64(), slice[test.wantEq[i]])
    45  			}
    46  		}
    47  	}
    48  }
    49  
    50  func test64cyclic(t *testing.T, f func(n int) hash.Hash64) {
    51  	inputs := []string{
    52  		"asdjadasdk",
    53  		"uioewrmnoc",
    54  		"wiewuwikxa",
    55  		"mfhddl/lcc",
    56  		"28n9789dkd",
    57  	}
    58  	h := f(10)
    59  	for _, input := range inputs {
    60  		h.Write([]byte(input))
    61  		h2 := f(10)
    62  		h2.Write([]byte(input))
    63  		got, want := h.Sum64(), h2.Sum64()
    64  		if got != want {
    65  			t.Fatalf("Sum64(%q)=%v, want %v", input, got, want)
    66  		}
    67  	}
    68  }
    69  
    70  func test64bigN(t *testing.T, f func(n int) hash.Hash64) {
    71  	const n = 100
    72  
    73  	// Create random input.
    74  	buf := make([]byte, n)
    75  	_, err := rand.Read(buf)
    76  	if err != nil {
    77  		t.Fatalf("rand.Read() failed: %v", err)
    78  	}
    79  
    80  	// Repeat 3 times.
    81  	buf = append(buf, buf...)
    82  	buf = append(buf, buf...)
    83  
    84  	h := f(n)
    85  	hashes := sets.Set[uint64]{}
    86  	for i := range buf {
    87  		h.Write(buf[i : i+1])
    88  		hashes.Add(h.Sum64())
    89  		want := min(i+1, n*2-1)
    90  		if len(hashes) != want {
    91  			t.Fatalf("got %d unique hashes, want %d", len(hashes), want)
    92  		}
    93  	}
    94  }
    95  
    96  // Runs the test suite for a hash32.
    97  func test32(t *testing.T, f func(n int) hash.Hash32) {
    98  	t.Run("basic", func(t *testing.T) { test32basic(t, f) })
    99  	t.Run("cyclic", func(t *testing.T) { test32cyclic(t, f) })
   100  	t.Run("big-n", func(t *testing.T) { test32bigN(t, f) })
   101  }
   102  
   103  func test32basic(t *testing.T, f func(n int) hash.Hash32) {
   104  	data := []byte("amitamit")
   105  	tests := []struct {
   106  		n        int
   107  		wantSize []int
   108  		wantEq   []int
   109  	}{
   110  		{2, []int{1, 2, 3, 4, 5, 5, 5, 5}, []int{-1, -1, -1, -1, -1, 1, 2, 3}},
   111  		{3, []int{1, 2, 3, 4, 5, 6, 6, 6}, []int{-1, -1, -1, -1, -1, -1, 2, 3}},
   112  	}
   113  	for _, test := range tests {
   114  		slice := []uint32{}
   115  		set := sets.Set[uint32]{}
   116  		h := f(test.n)
   117  		for i := range data {
   118  			h.Write(data[i : i+1])
   119  			slice = append(slice, h.Sum32())
   120  			set.Add(h.Sum32())
   121  			if len(set) != test.wantSize[i] {
   122  				t.Fatalf("n=%d #%d: set size=%v, want %v",
   123  					test.n, i, len(set), test.wantSize[i])
   124  			}
   125  			if test.wantEq[i] != -1 && h.Sum32() != slice[test.wantEq[i]] {
   126  				t.Fatalf("n=%d #%d: Sum32()=%d, want %d",
   127  					test.n, i, h.Sum32(), slice[test.wantEq[i]])
   128  			}
   129  		}
   130  	}
   131  }
   132  
   133  func test32cyclic(t *testing.T, f func(n int) hash.Hash32) {
   134  	inputs := []string{
   135  		"asdjadasdk",
   136  		"uioewrmnoc",
   137  		"wiewuwikxa",
   138  		"mfhddl/lcc",
   139  		"28n9789dkd",
   140  	}
   141  	h := f(10)
   142  	for _, input := range inputs {
   143  		h.Write([]byte(input))
   144  		h2 := f(10)
   145  		h2.Write([]byte(input))
   146  		got, want := h.Sum32(), h2.Sum32()
   147  		if got != want {
   148  			t.Fatalf("Sum32(%q)=%v, want %v", input, got, want)
   149  		}
   150  	}
   151  }
   152  
   153  func test32bigN(t *testing.T, f func(n int) hash.Hash32) {
   154  	const n = 100
   155  
   156  	// Create random input.
   157  	buf := make([]byte, n)
   158  	_, err := rand.Read(buf)
   159  	if err != nil {
   160  		t.Fatalf("rand.Read() failed: %v", err)
   161  	}
   162  
   163  	// Repeat 3 times.
   164  	buf = append(buf, buf...)
   165  	buf = append(buf, buf...)
   166  
   167  	h := f(n)
   168  	hashes := sets.Set[uint32]{}
   169  	for i := range buf {
   170  		h.Write(buf[i : i+1])
   171  		hashes.Add(h.Sum32())
   172  		want := min(i+1, n*2-1)
   173  		if len(hashes) != want {
   174  			t.Fatalf("got %d unique hashes, want %d", len(hashes), want)
   175  		}
   176  	}
   177  }