|
| 1 | +package natsort |
| 2 | + |
| 3 | +import ( |
| 4 | + "math/rand" |
| 5 | + "reflect" |
| 6 | + "sort" |
| 7 | + "strconv" |
| 8 | + "testing" |
| 9 | +) |
| 10 | + |
| 11 | +func TestStrings(t *testing.T) { |
| 12 | + golden := []struct { |
| 13 | + in []string |
| 14 | + want []string |
| 15 | + }{ |
| 16 | + { |
| 17 | + in: []string{"abc5", "abc1", "abc01", "ab", "abc10", "abc2"}, |
| 18 | + want: []string{ |
| 19 | + "ab", |
| 20 | + "abc1", |
| 21 | + "abc01", |
| 22 | + "abc2", |
| 23 | + "abc5", |
| 24 | + "abc10", |
| 25 | + }, |
| 26 | + }, |
| 27 | + { |
| 28 | + in: []string{"foo20", "foo.bar", "foo2", "foo.10", "foo.1", "foo.20", "foo.11", "foo1", "foobar", "foo21", "foo10", "foo11", "foo.21", "foo.2"}, |
| 29 | + want: []string{ |
| 30 | + "foo.1", |
| 31 | + "foo.2", |
| 32 | + "foo.10", |
| 33 | + "foo.11", |
| 34 | + "foo.20", |
| 35 | + "foo.21", |
| 36 | + "foo.bar", |
| 37 | + "foo1", |
| 38 | + "foo2", |
| 39 | + "foo10", |
| 40 | + "foo11", |
| 41 | + "foo20", |
| 42 | + "foo21", |
| 43 | + "foobar", |
| 44 | + }, |
| 45 | + }, |
| 46 | + } |
| 47 | + for _, g := range golden { |
| 48 | + Strings(g.in) |
| 49 | + if !reflect.DeepEqual(g.want, g.in) { |
| 50 | + t.Errorf("Error: sort failed, expected: %#q, got: %#q", g.want, g.in) |
| 51 | + } |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +func TestLess(t *testing.T) { |
| 56 | + testset := []struct { |
| 57 | + s1, s2 string |
| 58 | + less bool |
| 59 | + }{ |
| 60 | + {"0", "00", true}, |
| 61 | + {"00", "0", false}, |
| 62 | + {"aa", "ab", true}, |
| 63 | + {"ab", "abc", true}, |
| 64 | + {"abc", "ad", true}, |
| 65 | + {"ab1", "ab2", true}, |
| 66 | + {"ab1c", "ab1c", false}, |
| 67 | + {"ab12", "abc", true}, |
| 68 | + {"ab2a", "ab10", true}, |
| 69 | + {"a0001", "a0000001", true}, |
| 70 | + {"a10", "abcdefgh2", true}, |
| 71 | + {"аб2аб", "аб10аб", true}, |
| 72 | + {"2аб", "3аб", true}, |
| 73 | + // |
| 74 | + {"a1b", "a01b", true}, |
| 75 | + {"a01b", "a1b", false}, |
| 76 | + {"ab01b", "ab010b", true}, |
| 77 | + {"ab010b", "ab01b", false}, |
| 78 | + {"a01b001", "a001b01", true}, |
| 79 | + {"a001b01", "a01b001", false}, |
| 80 | + {"a1", "a1x", true}, |
| 81 | + {"1ax", "1b", true}, |
| 82 | + {"1b", "1ax", false}, |
| 83 | + // |
| 84 | + {"082", "83", true}, |
| 85 | + // |
| 86 | + {"083a", "9a", false}, |
| 87 | + {"9a", "083a", true}, |
| 88 | + // |
| 89 | + {"foo.bar", "foo123", true}, |
| 90 | + {"foo123", "foo.bar", false}, |
| 91 | + } |
| 92 | + for _, v := range testset { |
| 93 | + if res := Less(v.s1, v.s2); res != v.less { |
| 94 | + t.Errorf("Compared %#q to %#q: expected %v, got %v", |
| 95 | + v.s1, v.s2, v.less, res) |
| 96 | + } |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +func BenchmarkStdStrings(b *testing.B) { |
| 101 | + set := testSet(300) |
| 102 | + arr := make([]string, len(set[0])) |
| 103 | + b.ResetTimer() |
| 104 | + for i := 0; i < b.N; i++ { |
| 105 | + for _, list := range set { |
| 106 | + b.StopTimer() |
| 107 | + copy(arr, list) |
| 108 | + b.StartTimer() |
| 109 | + |
| 110 | + sort.Strings(arr) |
| 111 | + } |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +func BenchmarkStrings(b *testing.B) { |
| 116 | + set := testSet(300) |
| 117 | + arr := make([]string, len(set[0])) |
| 118 | + b.ResetTimer() |
| 119 | + for i := 0; i < b.N; i++ { |
| 120 | + for _, list := range set { |
| 121 | + b.StopTimer() |
| 122 | + copy(arr, list) |
| 123 | + b.StartTimer() |
| 124 | + |
| 125 | + Strings(arr) |
| 126 | + } |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | +func BenchmarkStdLess(b *testing.B) { |
| 131 | + set := testSet(300) |
| 132 | + b.ResetTimer() |
| 133 | + for i := 0; i < b.N; i++ { |
| 134 | + for j := range set[0] { |
| 135 | + k := (j + 1) % len(set[0]) |
| 136 | + _ = set[0][j] < set[0][k] |
| 137 | + } |
| 138 | + } |
| 139 | +} |
| 140 | + |
| 141 | +func BenchmarkLess(b *testing.B) { |
| 142 | + set := testSet(300) |
| 143 | + b.ResetTimer() |
| 144 | + for i := 0; i < b.N; i++ { |
| 145 | + for j := range set[0] { |
| 146 | + k := (j + 1) % len(set[0]) |
| 147 | + _ = Less(set[0][j], set[0][k]) |
| 148 | + } |
| 149 | + } |
| 150 | +} |
| 151 | + |
| 152 | +// Get 1000 arrays of 10000-string-arrays (less if -short is specified). |
| 153 | +func testSet(seed int) [][]string { |
| 154 | + gen := &generator{ |
| 155 | + src: rand.New(rand.NewSource( |
| 156 | + int64(seed), |
| 157 | + )), |
| 158 | + } |
| 159 | + n := 1000 |
| 160 | + if testing.Short() { |
| 161 | + n = 1 |
| 162 | + } |
| 163 | + set := make([][]string, n) |
| 164 | + for i := range set { |
| 165 | + strings := make([]string, 10000) |
| 166 | + for idx := range strings { |
| 167 | + // Generate a random string |
| 168 | + strings[idx] = gen.NextString() |
| 169 | + } |
| 170 | + set[i] = strings |
| 171 | + } |
| 172 | + return set |
| 173 | +} |
| 174 | + |
| 175 | +type generator struct { |
| 176 | + src *rand.Rand |
| 177 | +} |
| 178 | + |
| 179 | +func (g *generator) NextInt(max int) int { |
| 180 | + return g.src.Intn(max) |
| 181 | +} |
| 182 | + |
| 183 | +// Gets random random-length alphanumeric string. |
| 184 | +func (g *generator) NextString() (str string) { |
| 185 | + // Random-length 3-8 chars part |
| 186 | + strlen := g.src.Intn(6) + 3 |
| 187 | + // Random-length 1-3 num |
| 188 | + numlen := g.src.Intn(3) + 1 |
| 189 | + // Random position for num in string |
| 190 | + numpos := g.src.Intn(strlen + 1) |
| 191 | + // Generate the number |
| 192 | + var num string |
| 193 | + for i := 0; i < numlen; i++ { |
| 194 | + num += strconv.Itoa(g.src.Intn(10)) |
| 195 | + } |
| 196 | + // Put it all together |
| 197 | + for i := 0; i < strlen+1; i++ { |
| 198 | + if i == numpos { |
| 199 | + str += num |
| 200 | + } else { |
| 201 | + str += string('a' + g.src.Intn(16)) |
| 202 | + } |
| 203 | + } |
| 204 | + return str |
| 205 | +} |
0 commit comments