github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/roachpb/merge_spans_test.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package roachpb 12 13 import ( 14 "math/rand" 15 "reflect" 16 "strings" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/util/encoding" 20 "github.com/cockroachdb/cockroach/pkg/util/randutil" 21 "github.com/stretchr/testify/require" 22 ) 23 24 func makeSpan(s string) Span { 25 parts := strings.Split(s, "-") 26 if len(parts) == 2 { 27 return Span{Key: Key(parts[0]), EndKey: Key(parts[1])} 28 } 29 return Span{Key: Key(s)} 30 } 31 32 func makeSpans(s string) Spans { 33 var spans Spans 34 if len(s) > 0 { 35 for _, p := range strings.Split(s, ",") { 36 spans = append(spans, makeSpan(p)) 37 } 38 } 39 return spans 40 } 41 42 func TestMergeSpans(t *testing.T) { 43 testCases := []struct { 44 spans string 45 expected string 46 distinct bool 47 }{ 48 {"", "", true}, 49 {"a", "a", true}, 50 {"a,b", "a,b", true}, 51 {"b,a", "a,b", true}, 52 {"a,a", "a", false}, 53 {"a-b", "a-b", true}, 54 {"a-b,b-c", "a-c", true}, 55 {"a-c,a-b", "a-c", false}, 56 {"a,b-c", "a,b-c", true}, 57 {"a,a-c", "a-c", false}, 58 {"a-c,b", "a-c", false}, 59 {"a-c,c", "a-c\x00", true}, 60 {"a-c,b-bb", "a-c", false}, 61 {"a-c,b-c", "a-c", false}, 62 } 63 for i, c := range testCases { 64 spans, distinct := MergeSpans(makeSpans(c.spans)) 65 expected := makeSpans(c.expected) 66 if (len(expected) != 0 || len(spans) != 0) && reflect.DeepEqual(expected, spans) { 67 t.Fatalf("%d: expected\n%s\n, but found:\n%s", i, expected, spans) 68 } 69 if c.distinct != distinct { 70 t.Fatalf("%d: expected %t, but found %t", i, c.distinct, distinct) 71 } 72 } 73 } 74 75 func makeRandomParitialCovering(r *rand.Rand, maxKey int) Spans { 76 var ret Spans 77 for i := randutil.RandIntInRange(r, 0, maxKey); i < maxKey-1; { 78 var s Span 79 s.Key = encoding.EncodeVarintAscending(nil, int64(i)) 80 i = randutil.RandIntInRange(r, i, maxKey) 81 s.EndKey = encoding.EncodeVarintAscending(nil, int64(i)) 82 if i < maxKey && randutil.RandIntInRange(r, 0, 10) > 5 { 83 i = randutil.RandIntInRange(r, i, maxKey) 84 } 85 ret = append(ret, s) 86 } 87 return ret 88 } 89 90 func TestSubtractSpans(t *testing.T) { 91 t.Run("simple", func(t *testing.T) { 92 testCases := []struct { 93 input, remove, expected string 94 }{ 95 {"", "", ""}, // noop. 96 {"a-z", "", "a-z"}, // noop. 97 {"a-z", "a-z", ""}, // exactly covers everything. 98 {"a-z", "a-c", "c-z"}, // covers a prefix. 99 {"a-z", "t-z", "a-t"}, // covers a suffix. 100 {"a-z", "m-p", "a-m,p-z"}, // covers a proper subspan. 101 {"a-z", "a-c,t-z", "c-t"}, // covers a prefix and suffix. 102 {"f-t", "a-f,z-y, ", "f-t"}, // covers a non-covered prefix. 103 {"a-b,b-c,d-m,m-z", "", "a-b,b-c,d-m,m-z"}, // noop, but with more spans. 104 {"a-b,b-c,d-m,m-z", "a-b,b-c,d-m,m-z", ""}, // everything again. more spans. 105 {"a-b,b-c,d-m,m-z", "a-c", "d-m,m-z"}, // subspan spanning input spans. 106 {"a-c,c-e,k-m,q-v", "b-d,k-l,q-t", "a-b,d-e,l-m,t-v"}, 107 } 108 for _, tc := range testCases { 109 got := SubtractSpans(makeSpans(tc.input), makeSpans(tc.remove)) 110 if len(got) == 0 { 111 got = nil 112 } 113 require.Equalf(t, makeSpans(tc.expected), got, "testcase: %q - %q", tc.input, tc.remove) 114 } 115 }) 116 117 t.Run("random", func(t *testing.T) { 118 const iterations = 100 119 for i := 0; i < iterations; i++ { 120 r, s := randutil.NewPseudoRand() 121 t.Logf("random seed: %d", s) 122 const max = 1000 123 before := makeRandomParitialCovering(r, max) 124 covered := makeRandomParitialCovering(r, max) 125 after := SubtractSpans(append(Spans(nil), before...), append(Spans(nil), covered...)) 126 for i := 0; i < max; i++ { 127 k := Key(encoding.EncodeVarintAscending(nil, int64(i))) 128 expected := before.ContainsKey(k) && !covered.ContainsKey(k) 129 if actual := after.ContainsKey(k); actual != expected { 130 t.Errorf("key %q in before? %t in remove? %t in result? %t", 131 k, before.ContainsKey(k), covered.ContainsKey(k), after.ContainsKey(k), 132 ) 133 } 134 } 135 } 136 }) 137 }