vitess.io/vitess@v0.16.2/go/vt/key/key_test.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package key
    18  
    19  import (
    20  	"encoding/hex"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  	"google.golang.org/protobuf/proto"
    27  
    28  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    29  )
    30  
    31  func TestKey(t *testing.T) {
    32  	k0 := Uint64Key(0)
    33  	k1 := Uint64Key(1)
    34  	k2 := Uint64Key(0x7FFFFFFFFFFFFFFF)
    35  	k3 := Uint64Key(0x8000000000000000)
    36  	k4 := Uint64Key(0xFFFFFFFFFFFFFFFF)
    37  
    38  	f := func(k Uint64Key, x string) {
    39  		hexK := hex.EncodeToString(k.Bytes())
    40  		if x != hexK {
    41  			t.Errorf("byte mismatch %#v != %#v", k, x)
    42  		}
    43  	}
    44  
    45  	f(k0, "0000000000000000")
    46  	f(k1, "0000000000000001")
    47  	f(k2, "7fffffffffffffff")
    48  	f(k3, "8000000000000000")
    49  	f(k4, "ffffffffffffffff")
    50  }
    51  
    52  func TestEvenShardsKeyRange(t *testing.T) {
    53  	testCases := []struct {
    54  		i, n     int
    55  		wantSpec string
    56  		want     *topodatapb.KeyRange
    57  	}{
    58  		{0, 1,
    59  			"-",
    60  			&topodatapb.KeyRange{},
    61  		},
    62  		{0, 2,
    63  			"-80",
    64  			&topodatapb.KeyRange{
    65  				End: []byte{0x80},
    66  			},
    67  		},
    68  		{1, 2,
    69  			"80-",
    70  			&topodatapb.KeyRange{
    71  				Start: []byte{0x80},
    72  			},
    73  		},
    74  		{1, 4,
    75  			"40-80",
    76  			&topodatapb.KeyRange{
    77  				Start: []byte{0x40},
    78  				End:   []byte{0x80},
    79  			},
    80  		},
    81  		{2, 4,
    82  			"80-c0",
    83  			&topodatapb.KeyRange{
    84  				Start: []byte{0x80},
    85  				End:   []byte{0xc0},
    86  			},
    87  		},
    88  		{1, 256,
    89  			"01-02",
    90  			&topodatapb.KeyRange{
    91  				Start: []byte{0x01},
    92  				End:   []byte{0x02},
    93  			},
    94  		},
    95  		{256, 512,
    96  			"8000-8080",
    97  			&topodatapb.KeyRange{
    98  				Start: []byte{0x80, 0x00},
    99  				End:   []byte{0x80, 0x80},
   100  			},
   101  		},
   102  		// Second to last shard out of 512.
   103  		{510, 512,
   104  			"ff00-ff80",
   105  			&topodatapb.KeyRange{
   106  				Start: []byte{0xff, 0x00},
   107  				End:   []byte{0xff, 0x80},
   108  			},
   109  		},
   110  		// Last out of 512 shards.
   111  		{511, 512,
   112  			"ff80-",
   113  			&topodatapb.KeyRange{
   114  				Start: []byte{0xff, 0x80},
   115  			},
   116  		},
   117  	}
   118  
   119  	for _, tc := range testCases {
   120  		got, err := EvenShardsKeyRange(tc.i, tc.n)
   121  		if err != nil {
   122  			t.Fatalf("EvenShardsKeyRange(%v, %v) returned unexpected error: %v", tc.i, tc.n, err)
   123  		}
   124  		if !proto.Equal(got, tc.want) {
   125  			t.Errorf("EvenShardsKeyRange(%v, %v) = (%x, %x), want = (%x, %x)", tc.i, tc.n, got.Start, got.End, tc.want.Start, tc.want.End)
   126  		}
   127  
   128  		// Check if the string representation is equal as well.
   129  		if gotStr, want := KeyRangeString(got), tc.wantSpec; gotStr != want {
   130  			t.Errorf("EvenShardsKeyRange(%v) = %v, want = %v", got, gotStr, want)
   131  		}
   132  
   133  		// Now verify that ParseKeyRangeParts() produces the same KeyRange object as
   134  		// we do.
   135  		parts := strings.Split(tc.wantSpec, "-")
   136  		kr, _ := ParseKeyRangeParts(parts[0], parts[1])
   137  		if !proto.Equal(got, kr) {
   138  			t.Errorf("EvenShardsKeyRange(%v, %v) != ParseKeyRangeParts(%v, %v): (%x, %x) != (%x, %x)", tc.i, tc.n, parts[0], parts[1], got.Start, got.End, kr.Start, kr.End)
   139  		}
   140  	}
   141  }
   142  
   143  func TestKeyRangeAdd(t *testing.T) {
   144  	testcases := []struct {
   145  		first  string
   146  		second string
   147  		out    string
   148  		ok     bool
   149  	}{{
   150  		first:  "",
   151  		second: "",
   152  		out:    "",
   153  		ok:     false,
   154  	}, {
   155  		first:  "",
   156  		second: "-80",
   157  		out:    "",
   158  		ok:     false,
   159  	}, {
   160  		first:  "-80",
   161  		second: "",
   162  		out:    "",
   163  		ok:     false,
   164  	}, {
   165  		first:  "",
   166  		second: "80-",
   167  		out:    "",
   168  		ok:     false,
   169  	}, {
   170  		first:  "80-",
   171  		second: "",
   172  		out:    "",
   173  		ok:     false,
   174  	}, {
   175  		first:  "80-",
   176  		second: "-40",
   177  		out:    "",
   178  		ok:     false,
   179  	}, {
   180  		first:  "-40",
   181  		second: "80-",
   182  		out:    "",
   183  		ok:     false,
   184  	}, {
   185  		first:  "-80",
   186  		second: "80-",
   187  		out:    "-",
   188  		ok:     true,
   189  	}, {
   190  		first:  "80-",
   191  		second: "-80",
   192  		out:    "-",
   193  		ok:     true,
   194  	}, {
   195  		first:  "-40",
   196  		second: "40-80",
   197  		out:    "-80",
   198  		ok:     true,
   199  	}, {
   200  		first:  "40-80",
   201  		second: "-40",
   202  		out:    "-80",
   203  		ok:     true,
   204  	}, {
   205  		first:  "40-80",
   206  		second: "80-c0",
   207  		out:    "40-c0",
   208  		ok:     true,
   209  	}, {
   210  		first:  "80-c0",
   211  		second: "40-80",
   212  		out:    "40-c0",
   213  		ok:     true,
   214  	}}
   215  	keyRangeToString := func(kr *topodatapb.KeyRange) string {
   216  		if kr == nil {
   217  			return ""
   218  		}
   219  		return KeyRangeString(kr)
   220  	}
   221  	for _, tcase := range testcases {
   222  		first := stringToKeyRange(tcase.first)
   223  		second := stringToKeyRange(tcase.second)
   224  		out, ok := KeyRangeAdd(first, second)
   225  		assert.Equal(t, tcase.out, keyRangeToString(out))
   226  		assert.Equal(t, tcase.ok, ok)
   227  	}
   228  }
   229  
   230  func TestKeyRangeEndEqual(t *testing.T) {
   231  	testcases := []struct {
   232  		first  string
   233  		second string
   234  		out    bool
   235  	}{{
   236  		first:  "",
   237  		second: "",
   238  		out:    true,
   239  	}, {
   240  		first:  "",
   241  		second: "-80",
   242  		out:    false,
   243  	}, {
   244  		first:  "40-",
   245  		second: "10-",
   246  		out:    true,
   247  	}, {
   248  		first:  "-8000",
   249  		second: "-80",
   250  		out:    true,
   251  	}, {
   252  		first:  "-8000",
   253  		second: "-8000000000000000",
   254  		out:    true,
   255  	}, {
   256  		first:  "-80",
   257  		second: "-8000",
   258  		out:    true,
   259  	}}
   260  
   261  	for _, tcase := range testcases {
   262  		first := stringToKeyRange(tcase.first)
   263  		second := stringToKeyRange(tcase.second)
   264  		out := KeyRangeEndEqual(first, second)
   265  		if out != tcase.out {
   266  			t.Fatalf("KeyRangeEndEqual(%q, %q) expected %t, got %t", tcase.first, tcase.second, tcase.out, out)
   267  		}
   268  	}
   269  }
   270  
   271  func TestKeyRangeStartEqual(t *testing.T) {
   272  	testcases := []struct {
   273  		first  string
   274  		second string
   275  		out    bool
   276  	}{{
   277  		first:  "",
   278  		second: "",
   279  		out:    true,
   280  	}, {
   281  		first:  "",
   282  		second: "-80",
   283  		out:    true,
   284  	}, {
   285  		first:  "40-",
   286  		second: "20-",
   287  		out:    false,
   288  	}, {
   289  		first:  "-8000",
   290  		second: "-80",
   291  		out:    true,
   292  	}, {
   293  		first:  "-8000",
   294  		second: "-8000000000000000",
   295  		out:    true,
   296  	}, {
   297  		first:  "-80",
   298  		second: "-8000",
   299  		out:    true,
   300  	}}
   301  
   302  	for _, tcase := range testcases {
   303  		first := stringToKeyRange(tcase.first)
   304  		second := stringToKeyRange(tcase.second)
   305  		out := KeyRangeStartEqual(first, second)
   306  		if out != tcase.out {
   307  			t.Fatalf("KeyRangeStartEqual(%q, %q) expected %t, got %t", tcase.first, tcase.second, tcase.out, out)
   308  		}
   309  	}
   310  }
   311  
   312  func TestKeyRangeEqual(t *testing.T) {
   313  	testcases := []struct {
   314  		first  string
   315  		second string
   316  		out    bool
   317  	}{{
   318  		first:  "",
   319  		second: "",
   320  		out:    true,
   321  	}, {
   322  		first:  "",
   323  		second: "-80",
   324  		out:    false,
   325  	}, {
   326  		first:  "-8000",
   327  		second: "-80",
   328  		out:    true,
   329  	}, {
   330  		first:  "-8000",
   331  		second: "-8000000000000000",
   332  		out:    true,
   333  	}, {
   334  		first:  "-80",
   335  		second: "-8000",
   336  		out:    true,
   337  	}}
   338  
   339  	for _, tcase := range testcases {
   340  		first := stringToKeyRange(tcase.first)
   341  		second := stringToKeyRange(tcase.second)
   342  		out := KeyRangeEqual(first, second)
   343  		if out != tcase.out {
   344  			t.Fatalf("KeyRangeEqual(%q, %q) expected %t, got %t", tcase.first, tcase.second, tcase.out, out)
   345  		}
   346  	}
   347  }
   348  
   349  func TestKeyRangeContiguous(t *testing.T) {
   350  	testcases := []struct {
   351  		first  string
   352  		second string
   353  		out    bool
   354  	}{{
   355  		first:  "-40",
   356  		second: "40-80",
   357  		out:    true,
   358  	}, {
   359  		first:  "40-80",
   360  		second: "-40",
   361  		out:    false,
   362  	}, {
   363  		first:  "-",
   364  		second: "-40",
   365  		out:    true,
   366  	}, {
   367  		first:  "40-80",
   368  		second: "c0-",
   369  		out:    false,
   370  	}, {
   371  		first:  "40-80",
   372  		second: "80-c0",
   373  		out:    true,
   374  	}, {
   375  		first:  "40-80",
   376  		second: "8000000000000000-c000000000000000",
   377  		out:    true,
   378  	}, {
   379  		first:  "4000000000000000-8000000000000000",
   380  		second: "80-c0",
   381  		out:    true,
   382  	}}
   383  
   384  	for _, tcase := range testcases {
   385  		first := stringToKeyRange(tcase.first)
   386  		second := stringToKeyRange(tcase.second)
   387  		out := KeyRangeContiguous(first, second)
   388  		if out != tcase.out {
   389  			t.Fatalf("KeyRangeContiguous(%q, %q) expected %t, got %t", tcase.first, tcase.second, tcase.out, out)
   390  		}
   391  	}
   392  }
   393  
   394  func TestEvenShardsKeyRange_Error(t *testing.T) {
   395  	testCases := []struct {
   396  		i, n      int
   397  		wantError string
   398  	}{
   399  		{
   400  			-1, 0,
   401  			"the shard count must be > 0",
   402  		},
   403  		{
   404  			32, 8,
   405  			"must be less than",
   406  		},
   407  		{
   408  			1, 6,
   409  			"must be a power of two",
   410  		},
   411  	}
   412  
   413  	for _, tc := range testCases {
   414  		kr, err := EvenShardsKeyRange(tc.i, tc.n)
   415  		if err == nil || !strings.Contains(err.Error(), tc.wantError) {
   416  			t.Fatalf("EvenShardsKeyRange(%v, %v) = (%v, %v) want error = %v", tc.i, tc.n, kr, err, tc.wantError)
   417  		}
   418  	}
   419  }
   420  
   421  func TestParseShardingSpec(t *testing.T) {
   422  	x40 := []byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
   423  	x80 := []byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
   424  	goodTable := map[string][]*topodatapb.KeyRange{
   425  		"-": {{}},
   426  		"0": {{}},
   427  		"-4000000000000000-8000000000000000-": {
   428  			{End: x40},
   429  			{Start: x40, End: x80},
   430  			{Start: x80},
   431  		},
   432  	}
   433  	badTable := []string{
   434  		"4000000000000000",
   435  		"---",
   436  		"4000000000000000--8000000000000000",
   437  		"4000000000000000-3000000000000000", // not in order
   438  	}
   439  	for key, wanted := range goodTable {
   440  		r, err := ParseShardingSpec(key)
   441  		if err != nil {
   442  			t.Errorf("Unexpected error: %v.", err)
   443  		}
   444  		if len(r) != len(wanted) {
   445  			t.Errorf("Wrong result: wanted %v, got %v", wanted, r)
   446  			continue
   447  		}
   448  		for i, w := range wanted {
   449  			if !proto.Equal(r[i], w) {
   450  				t.Errorf("Wrong result: wanted %v, got %v", w, r[i])
   451  				break
   452  			}
   453  		}
   454  	}
   455  	for _, bad := range badTable {
   456  		_, err := ParseShardingSpec(bad)
   457  		if err == nil {
   458  			t.Errorf("Didn't get expected error for %v.", bad)
   459  		}
   460  	}
   461  }
   462  
   463  func TestContains(t *testing.T) {
   464  	var table = []struct {
   465  		kid       string
   466  		start     string
   467  		end       string
   468  		contained bool
   469  	}{
   470  		{kid: "3000000000000000", start: "3000000000000000", end: "", contained: true},
   471  		{kid: "3000000000000000", start: "", end: "3000000000000000", contained: false},
   472  		{kid: "4000000000000000", start: "3000000000000000", end: "", contained: true},
   473  		{kid: "2000000000000000", start: "3000000000000000", end: "", contained: false},
   474  	}
   475  
   476  	for _, el := range table {
   477  		s, err := hex.DecodeString(el.start)
   478  		if err != nil {
   479  			t.Errorf("Unexpected error: %v", err)
   480  		}
   481  		e, err := hex.DecodeString(el.end)
   482  		if err != nil {
   483  			t.Errorf("Unexpected error: %v", err)
   484  		}
   485  		kr := &topodatapb.KeyRange{
   486  			Start: s,
   487  			End:   e,
   488  		}
   489  		k, err := hex.DecodeString(el.kid)
   490  		if err != nil {
   491  			t.Errorf("Unexpected error: %v", err)
   492  		}
   493  		if c := KeyRangeContains(kr, k); c != el.contained {
   494  			t.Errorf("Unexpected result: contains for %v and (%v-%v) yields %v.", el.kid, el.start, el.end, c)
   495  		}
   496  		if !KeyRangeContains(nil, k) {
   497  			t.Errorf("KeyRangeContains(nil, x) should always be true")
   498  		}
   499  	}
   500  }
   501  
   502  func TestIntersectOverlap(t *testing.T) {
   503  	var table = []struct {
   504  		a          string
   505  		b          string
   506  		c          string
   507  		d          string
   508  		intersects bool
   509  		overlap    string
   510  	}{
   511  		{a: "40", b: "80", c: "c0", d: "d0", intersects: false},
   512  		{a: "", b: "80", c: "80", d: "", intersects: false},
   513  		{a: "", b: "80", c: "", d: "40", intersects: true, overlap: "-40"},
   514  		{a: "80", b: "", c: "c0", d: "", intersects: true, overlap: "c0-"},
   515  		{a: "", b: "80", c: "40", d: "80", intersects: true, overlap: "40-80"},
   516  		{a: "40", b: "80", c: "60", d: "a0", intersects: true, overlap: "60-80"},
   517  		{a: "40", b: "80", c: "50", d: "60", intersects: true, overlap: "50-60"},
   518  		{a: "40", b: "80", c: "10", d: "50", intersects: true, overlap: "40-50"},
   519  		{a: "40", b: "80", c: "40", d: "80", intersects: true, overlap: "40-80"},
   520  		{a: "", b: "80", c: "", d: "80", intersects: true, overlap: "-80"},
   521  		{a: "40", b: "", c: "40", d: "", intersects: true, overlap: "40-"},
   522  		{a: "40", b: "80", c: "20", d: "40", intersects: false},
   523  		{a: "80", b: "", c: "80", d: "c0", intersects: true, overlap: "80-c0"},
   524  		{a: "", b: "", c: "c0", d: "d0", intersects: true, overlap: "c0-d0"},
   525  	}
   526  
   527  	for _, el := range table {
   528  		a, err := hex.DecodeString(el.a)
   529  		if err != nil {
   530  			t.Errorf("Unexpected error: %v", err)
   531  		}
   532  		b, err := hex.DecodeString(el.b)
   533  		if err != nil {
   534  			t.Errorf("Unexpected error: %v", err)
   535  		}
   536  		left := &topodatapb.KeyRange{Start: a, End: b}
   537  		c, err := hex.DecodeString(el.c)
   538  		if err != nil {
   539  			t.Errorf("Unexpected error: %v", err)
   540  		}
   541  		d, err := hex.DecodeString(el.d)
   542  		if err != nil {
   543  			t.Errorf("Unexpected error: %v", err)
   544  		}
   545  		right := &topodatapb.KeyRange{Start: c, End: d}
   546  		if c := KeyRangesIntersect(left, right); c != el.intersects {
   547  			t.Errorf("Unexpected result: KeyRangesIntersect for %v and %v yields %v.", left, right, c)
   548  		}
   549  		overlap, err := KeyRangesOverlap(left, right)
   550  		if el.intersects {
   551  			if err != nil {
   552  				t.Errorf("Unexpected result: KeyRangesOverlap for overlapping %v and %v returned an error: %v", left, right, err)
   553  			} else {
   554  				got := hex.EncodeToString(overlap.Start) + "-" + hex.EncodeToString(overlap.End)
   555  				if got != el.overlap {
   556  					t.Errorf("Unexpected result: KeyRangesOverlap for overlapping %v and %v should have returned: %v but got: %v", left, right, el.overlap, got)
   557  				}
   558  			}
   559  		} else {
   560  			if err == nil {
   561  				t.Errorf("Unexpected result: KeyRangesOverlap for non-overlapping %v and %v should have returned an error", left, right)
   562  			}
   563  		}
   564  	}
   565  }
   566  
   567  func TestKeyRangeIncludes(t *testing.T) {
   568  	var table = []struct {
   569  		name     string
   570  		big      string
   571  		small    string
   572  		expected bool
   573  	}{
   574  		{"big nil, small nil", "nil", "nil", true},
   575  		{"big nil, small non nil, fully partial", "nil", "80-c0", true},
   576  		{"big nil, small non nil, full start", "nil", "-c0", true},
   577  		{"big nil, small non nil, full end", "nil", "80-", true},
   578  		{"big non-nil, fully partial, small nil", "80-c0", "nil", false},
   579  		{"big non-nil, full start, small nil", "-c0", "nil", false},
   580  		{"big non-nil, full end, small nil", "80-", "nil", false},
   581  		{"big full, small full", "-", "-", true},
   582  		{"big full, small partial", "-", "40-60", true},
   583  		{"big partial, small full", "40-60", "-", false},
   584  
   585  		{"big partial, small to the end", "40-60", "40-", false},
   586  		{"big partial, small bigger to the right", "40-60", "40-80", false},
   587  		{"big partial, small equal", "40-60", "40-60", true},
   588  		{"big partial, small smaller right", "40-60", "40-50", true},
   589  
   590  		{"big partial, small to the beginning", "40-60", "-60", false},
   591  		{"big partial, small smaller to the left", "40-60", "20-60", false},
   592  		{"big partial, small bigger left", "40-60", "50-60", true},
   593  	}
   594  
   595  	var err error
   596  	for _, tc := range table {
   597  		var big, small *topodatapb.KeyRange
   598  		if tc.big != "nil" {
   599  			parts := strings.Split(tc.big, "-")
   600  			big, err = ParseKeyRangeParts(parts[0], parts[1])
   601  			if err != nil {
   602  				t.Fatalf("test data error in %v: %v", tc.big, err)
   603  			}
   604  		}
   605  		if tc.small != "nil" {
   606  			parts := strings.Split(tc.small, "-")
   607  			small, err = ParseKeyRangeParts(parts[0], parts[1])
   608  			if err != nil {
   609  				t.Fatalf("test data error in %v: %v", tc.small, err)
   610  			}
   611  		}
   612  		got := KeyRangeIncludes(big, small)
   613  		if got != tc.expected {
   614  			t.Errorf("KeyRangeIncludes for test case '%v' returned %v but expected %v", tc.name, got, tc.expected)
   615  		}
   616  	}
   617  }
   618  
   619  func BenchmarkUint64KeyBytes(b *testing.B) {
   620  	keys := []Uint64Key{
   621  		0, 1, 0x7FFFFFFFFFFFFFFF, 0x8000000000000000, 0xFFFFFFFFFFFFFFFF,
   622  	}
   623  
   624  	for i := 0; i < b.N; i++ {
   625  		for _, key := range keys {
   626  			key.Bytes()
   627  		}
   628  	}
   629  }
   630  
   631  func BenchmarkUint64KeyString(b *testing.B) {
   632  	keys := []Uint64Key{
   633  		0, 1, 0x7FFFFFFFFFFFFFFF, 0x8000000000000000, 0xFFFFFFFFFFFFFFFF,
   634  	}
   635  
   636  	for i := 0; i < b.N; i++ {
   637  		for _, key := range keys {
   638  			_ = key.String()
   639  		}
   640  	}
   641  }
   642  
   643  func BenchmarkKeyRangeContains(b *testing.B) {
   644  	kr := &topodatapb.KeyRange{
   645  		Start: []byte{0x40, 0, 0, 0, 0, 0, 0, 0},
   646  		End:   []byte{0x80, 0, 0, 0, 0, 0, 0, 0},
   647  	}
   648  	keys := [][]byte{
   649  		{0x30, 0, 0, 0, 0, 0, 0, 0},
   650  		{0x40, 0, 0, 0, 0, 0, 0, 0},
   651  		{0x50, 0, 0, 0, 0, 0, 0, 0},
   652  		{0x80, 0, 0, 0, 0, 0, 0, 0},
   653  		{0x90, 0, 0, 0, 0, 0, 0, 0},
   654  	}
   655  
   656  	for i := 0; i < b.N; i++ {
   657  		for _, key := range keys {
   658  			KeyRangeContains(kr, key)
   659  		}
   660  	}
   661  }
   662  
   663  func BenchmarkKeyRangesIntersect(b *testing.B) {
   664  	kr1 := &topodatapb.KeyRange{
   665  		Start: []byte{0x40, 0, 0, 0, 0, 0, 0, 0},
   666  		End:   []byte{0x80, 0, 0, 0, 0, 0, 0, 0},
   667  	}
   668  	kr2 := &topodatapb.KeyRange{
   669  		Start: []byte{0x30, 0, 0, 0, 0, 0, 0, 0},
   670  		End:   []byte{0x50, 0, 0, 0, 0, 0, 0, 0},
   671  	}
   672  
   673  	for i := 0; i < b.N; i++ {
   674  		KeyRangesIntersect(kr1, kr2)
   675  	}
   676  }
   677  
   678  func BenchmarkKeyRangesOverlap(b *testing.B) {
   679  	kr1 := &topodatapb.KeyRange{
   680  		Start: []byte{0x40, 0, 0, 0, 0, 0, 0, 0},
   681  		End:   []byte{0x80, 0, 0, 0, 0, 0, 0, 0},
   682  	}
   683  	kr2 := &topodatapb.KeyRange{
   684  		Start: []byte{0x30, 0, 0, 0, 0, 0, 0, 0},
   685  		End:   []byte{0x50, 0, 0, 0, 0, 0, 0, 0},
   686  	}
   687  
   688  	for i := 0; i < b.N; i++ {
   689  		if _, err := KeyRangesOverlap(kr1, kr2); err != nil {
   690  			b.Fatal(err)
   691  		}
   692  	}
   693  }
   694  
   695  func TestIsKeyRange(t *testing.T) {
   696  	testcases := []struct {
   697  		in  string
   698  		out bool
   699  	}{{
   700  		in:  "-",
   701  		out: true,
   702  	}, {
   703  		in:  "-80",
   704  		out: true,
   705  	}, {
   706  		in:  "40-80",
   707  		out: true,
   708  	}, {
   709  		in:  "80-",
   710  		out: true,
   711  	}, {
   712  		in:  "a0-",
   713  		out: true,
   714  	}, {
   715  		in:  "-A0",
   716  		out: true,
   717  	}, {
   718  		in:  "",
   719  		out: false,
   720  	}, {
   721  		in:  "x-80",
   722  		out: false,
   723  	}, {
   724  		in:  "-80x",
   725  		out: false,
   726  	}, {
   727  		in:  "select",
   728  		out: false,
   729  	}}
   730  
   731  	for _, tcase := range testcases {
   732  		assert.Equal(t, IsKeyRange(tcase.in), tcase.out, tcase.in)
   733  	}
   734  }
   735  
   736  func TestGenerateShardRanges(t *testing.T) {
   737  	type args struct {
   738  		shards int
   739  	}
   740  
   741  	tests := []struct {
   742  		name    string
   743  		args    args
   744  		want    []string
   745  		wantErr bool
   746  	}{
   747  		{
   748  			"errors for shards less than 0",
   749  			args{0},
   750  			nil,
   751  			true,
   752  		},
   753  		{
   754  			"errors for shards more than 65536",
   755  			args{65537},
   756  			nil,
   757  			true,
   758  		},
   759  		{
   760  			"works for a single shard",
   761  			args{1},
   762  			[]string{"-"},
   763  			false,
   764  		},
   765  		{
   766  			"works for more than one shard",
   767  			args{2},
   768  			[]string{"-80", "80-"},
   769  			false,
   770  		},
   771  		{
   772  			"works for an odd number of shards",
   773  			args{7},
   774  			[]string{"-24", "24-49", "49-6d", "6d-92", "92-b6", "b6-db", "db-"},
   775  			false,
   776  		},
   777  		{
   778  			"works for large number of shards",
   779  			args{256},
   780  			[]string{"-01", "01-02", "02-03", "03-04", "04-05", "05-06", "06-07", "07-08", "08-09", "09-0a", "0a-0b", "0b-0c", "0c-0d", "0d-0e", "0e-0f", "0f-10", "10-11", "11-12", "12-13", "13-14", "14-15", "15-16", "16-17", "17-18", "18-19", "19-1a", "1a-1b", "1b-1c", "1c-1d", "1d-1e", "1e-1f", "1f-20", "20-21", "21-22", "22-23", "23-24", "24-25", "25-26", "26-27", "27-28", "28-29", "29-2a", "2a-2b", "2b-2c", "2c-2d", "2d-2e", "2e-2f", "2f-30", "30-31", "31-32", "32-33", "33-34", "34-35", "35-36", "36-37", "37-38", "38-39", "39-3a", "3a-3b", "3b-3c", "3c-3d", "3d-3e", "3e-3f", "3f-40", "40-41", "41-42", "42-43", "43-44", "44-45", "45-46", "46-47", "47-48", "48-49", "49-4a", "4a-4b", "4b-4c", "4c-4d", "4d-4e", "4e-4f", "4f-50", "50-51", "51-52", "52-53", "53-54", "54-55", "55-56", "56-57", "57-58", "58-59", "59-5a", "5a-5b", "5b-5c", "5c-5d", "5d-5e", "5e-5f", "5f-60", "60-61", "61-62", "62-63", "63-64", "64-65", "65-66", "66-67", "67-68", "68-69", "69-6a", "6a-6b", "6b-6c", "6c-6d", "6d-6e", "6e-6f", "6f-70", "70-71", "71-72", "72-73", "73-74", "74-75", "75-76", "76-77", "77-78", "78-79", "79-7a", "7a-7b", "7b-7c", "7c-7d", "7d-7e", "7e-7f", "7f-80", "80-81", "81-82", "82-83", "83-84", "84-85", "85-86", "86-87", "87-88", "88-89", "89-8a", "8a-8b", "8b-8c", "8c-8d", "8d-8e", "8e-8f", "8f-90", "90-91", "91-92", "92-93", "93-94", "94-95", "95-96", "96-97", "97-98", "98-99", "99-9a", "9a-9b", "9b-9c", "9c-9d", "9d-9e", "9e-9f", "9f-a0", "a0-a1", "a1-a2", "a2-a3", "a3-a4", "a4-a5", "a5-a6", "a6-a7", "a7-a8", "a8-a9", "a9-aa", "aa-ab", "ab-ac", "ac-ad", "ad-ae", "ae-af", "af-b0", "b0-b1", "b1-b2", "b2-b3", "b3-b4", "b4-b5", "b5-b6", "b6-b7", "b7-b8", "b8-b9", "b9-ba", "ba-bb", "bb-bc", "bc-bd", "bd-be", "be-bf", "bf-c0", "c0-c1", "c1-c2", "c2-c3", "c3-c4", "c4-c5", "c5-c6", "c6-c7", "c7-c8", "c8-c9", "c9-ca", "ca-cb", "cb-cc", "cc-cd", "cd-ce", "ce-cf", "cf-d0", "d0-d1", "d1-d2", "d2-d3", "d3-d4", "d4-d5", "d5-d6", "d6-d7", "d7-d8", "d8-d9", "d9-da", "da-db", "db-dc", "dc-dd", "dd-de", "de-df", "df-e0", "e0-e1", "e1-e2", "e2-e3", "e3-e4", "e4-e5", "e5-e6", "e6-e7", "e7-e8", "e8-e9", "e9-ea", "ea-eb", "eb-ec", "ec-ed", "ed-ee", "ee-ef", "ef-f0", "f0-f1", "f1-f2", "f2-f3", "f3-f4", "f4-f5", "f5-f6", "f6-f7", "f7-f8", "f8-f9", "f9-fa", "fa-fb", "fb-fc", "fc-fd", "fd-fe", "fe-ff", "ff-"},
   781  			false,
   782  		},
   783  	}
   784  
   785  	for _, tt := range tests {
   786  		t.Run(tt.name, func(t *testing.T) {
   787  			got, err := GenerateShardRanges(tt.args.shards)
   788  			if tt.wantErr {
   789  				assert.Error(t, err)
   790  				return
   791  			}
   792  
   793  			require.NoError(t, err)
   794  			assert.Equal(t, got, tt.want)
   795  		})
   796  	}
   797  }
   798  
   799  func TestShardCalculatorForShardsGreaterThan512(t *testing.T) {
   800  	got, err := GenerateShardRanges(512)
   801  	assert.NoError(t, err)
   802  
   803  	want := "ff80-"
   804  
   805  	assert.Equal(t, want, got[511], "Invalid mapping for a 512-shard keyspace. Expected %v, got %v", want, got[511])
   806  }
   807  
   808  func stringToKeyRange(spec string) *topodatapb.KeyRange {
   809  	if spec == "" {
   810  		return nil
   811  	}
   812  	parts := strings.Split(spec, "-")
   813  	if len(parts) != 2 {
   814  		panic("invalid spec")
   815  	}
   816  	kr, err := ParseKeyRangeParts(parts[0], parts[1])
   817  	if err != nil {
   818  		panic(err)
   819  	}
   820  	return kr
   821  }