PageRenderTime 63ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/sort/utils_test.go

https://code.google.com/p/sortingo/
Go | 295 lines | 205 code | 29 blank | 61 comment | 33 complexity | bc80b97cbc84f8aa2ef28cefad784e27 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. //
  2. // Copyright 2011 Nathan Fiedler. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. //
  6. package sort
  7. // This file contains utility functions for the unit tests.
  8. import (
  9. "bytes"
  10. "fmt"
  11. "math/rand"
  12. "sort"
  13. "strings"
  14. "testing"
  15. )
  16. // smallDataSize is the recommended test size for the slower sorting
  17. // algorithms (e.g. insertion, gnome, selection).
  18. const smallDataSize = 512
  19. // mediumDataSize is the recommended test size for the sorting algorithms
  20. // that are faster than the typical slow sorts, but not necessarily as
  21. // fast as the memory efficient sorts.
  22. const mediumDataSize = 16384
  23. // largeDataSize is the recommended test size for the faster sorting
  24. // algorithms (e.g. burstsort, funnelsort). This is also the largest
  25. // allowable size for testing.
  26. const largeDataSize = 65536
  27. // repeatedStrings contains a sequence of repeated strings.
  28. var repeatedStrings []string
  29. // repeatedCycleStrings contains a repeating sequence of strings.
  30. var repeatedCycleStrings []string
  31. // randomStrings consists of strings of 100 mixed-case letters and numbers.
  32. var randomStrings []string
  33. // uniqueWords consists of unique pseudo words, similar to a dictionary.
  34. var uniqueWords []string
  35. // nonUniqueWords consists of pseudo words that may repeat numerous times.
  36. var nonUniqueWords []string
  37. // init sets up the test data one time to avoid regenerating repeatedly.
  38. func init() {
  39. // Generate the repeated strings test data.
  40. repeatedStrings = make([]string, largeDataSize)
  41. a100 := strings.Repeat("A", 100)
  42. for idx := range repeatedStrings {
  43. repeatedStrings[idx] = a100
  44. }
  45. // Generate a repeating cycle of strings.
  46. strs := make([]string, len(a100))
  47. for i := range strs {
  48. strs[i] = a100[0 : i+1]
  49. }
  50. repeatedCycleStrings = make([]string, largeDataSize)
  51. c := 0
  52. for i := range repeatedCycleStrings {
  53. repeatedCycleStrings[i] = strs[c]
  54. if c++; c >= len(strs) {
  55. c = 0
  56. }
  57. }
  58. // Generate a set of random strings, each of length 100.
  59. randomStrings = make([]string, largeDataSize)
  60. for i := range randomStrings {
  61. bb := bytes.NewBuffer(make([]byte, 0, 100))
  62. for j := 0; j < 100; j++ {
  63. d := rand.Int31n(62)
  64. if d < 10 {
  65. bb.WriteRune('0' + d)
  66. } else if d < 36 {
  67. bb.WriteRune('A' + (d - 10))
  68. } else {
  69. bb.WriteRune('a' + (d - 36))
  70. }
  71. }
  72. randomStrings[i] = string(bb.Bytes())
  73. }
  74. // Generate a set of unique pseudo words.
  75. uniqueWords = make([]string, largeDataSize)
  76. wordExists := make(map[string]bool)
  77. for i := range uniqueWords {
  78. var s string
  79. // Loop until a unique random word is generated.
  80. for {
  81. // Each word is from 1 to 28 characters long.
  82. l := 1 + rand.Intn(27)
  83. bb := bytes.NewBuffer(make([]byte, 0, l))
  84. // Each word consists only of the lowercase letters.
  85. for j := 0; j < l; j++ {
  86. d := rand.Int31n(26)
  87. bb.WriteRune('a' + d)
  88. }
  89. s = string(bb.Bytes())
  90. if !wordExists[s] {
  91. break
  92. }
  93. }
  94. uniqueWords[i] = s
  95. wordExists[s] = true
  96. }
  97. // Generate a set of pseudo words that may be repeated.
  98. nonUniqueWords = make([]string, largeDataSize)
  99. n := len(nonUniqueWords)
  100. for i := 0; i < n; {
  101. // Each word is 1 to 28 characters long.
  102. l := 1 + rand.Intn(27)
  103. bb := bytes.NewBuffer(make([]byte, 0, l))
  104. // Each word consists only of the lowercase letters.
  105. for j := 0; j < l; j++ {
  106. d := rand.Int31n(26)
  107. bb.WriteRune('a' + d)
  108. }
  109. // Repeat the word some number of times.
  110. c := rand.Intn(100)
  111. if c > (n - i) {
  112. c = n - i
  113. }
  114. s := string(bb.Bytes())
  115. for j := 0; j < c; j++ {
  116. nonUniqueWords[i] = s
  117. i++
  118. }
  119. }
  120. shuffle(nonUniqueWords)
  121. }
  122. // testSortArguments runs a given sort function with the most
  123. // basic of input sequences in order to test its robustness.
  124. func testSortArguments(t *testing.T, f func([]string)) {
  125. // these should silently do nothing
  126. f(nil)
  127. f(make([]string, 0))
  128. f([]string{"a"})
  129. // test the bare minimum input, two elements
  130. input := []string{"b", "a"}
  131. f(input)
  132. if !sort.StringsAreSorted(input) {
  133. t.Error("two inputs not sorted")
  134. }
  135. // now try three elements
  136. input = []string{"c", "b", "a"}
  137. f(input)
  138. if !sort.StringsAreSorted(input) {
  139. t.Error("three inputs not sorted")
  140. }
  141. // test with all empty input
  142. input = []string{"", "", "", "", "", "", "", "", "", ""}
  143. f(input)
  144. // test with peculiar input
  145. input = []string{"z", "m", "", "a", "d", "tt", "tt", "tt", "foo", "bar"}
  146. f(input)
  147. if !sort.StringsAreSorted(input) {
  148. t.Error("peculiar inputs not sorted")
  149. }
  150. }
  151. // testRepeated runs a given sort function over a sequence of
  152. // repeated strings.
  153. func testSortRepeated(t *testing.T, f func([]string), size int) {
  154. checkTestSize(t, size)
  155. input := make([]string, size)
  156. copy(input, repeatedStrings)
  157. f(input)
  158. if !isRepeated(input) {
  159. t.Error("repeated input not repeating")
  160. }
  161. }
  162. // testSortRepeatedCycle generates a repeating cycle of strings and
  163. // runs the given sort on that data. The size is the number of elements
  164. // to generate for the test.
  165. func testSortRepeatedCycle(t *testing.T, f func([]string), size int) {
  166. checkTestSize(t, size)
  167. input := make([]string, size)
  168. copy(input, repeatedCycleStrings)
  169. f(input)
  170. if !sort.StringsAreSorted(input) {
  171. t.Error("repeated cycle input not sorted")
  172. }
  173. }
  174. // testSortRandom runs the given sort on a randomly generated data set
  175. // consisting of strings of 100 letters and upper and lower case letters.
  176. func testSortRandom(t *testing.T, f func([]string), size int) {
  177. checkTestSize(t, size)
  178. input := make([]string, size)
  179. copy(input, randomStrings)
  180. f(input)
  181. if !sort.StringsAreSorted(input) {
  182. t.Error("random input not sorted")
  183. }
  184. }
  185. // testSortDictWords generates a set of random pseudo-words and runs the
  186. // given sort function on that set.
  187. func testSortDictWords(t *testing.T, f func([]string), size int) {
  188. checkTestSize(t, size)
  189. input := make([]string, size)
  190. copy(input, uniqueWords)
  191. f(input)
  192. if !sort.StringsAreSorted(input) {
  193. t.Error("dictwords input not sorted")
  194. }
  195. }
  196. // testSortSorted runs the given sort function on an input set that
  197. // is already in sorted order.
  198. func testSortSorted(t *testing.T, f func([]string), size int) {
  199. checkTestSize(t, size)
  200. input := make([]string, size)
  201. copy(input, uniqueWords)
  202. sort.Strings(input)
  203. f(input)
  204. if !sort.StringsAreSorted(input) {
  205. t.Error("sorted dictwords input not sorted")
  206. }
  207. }
  208. // ReverseStringArray is identical to sort.StringArray except that the
  209. // contents are sorted in reverse order.
  210. type ReverseStringArray []string
  211. func (p ReverseStringArray) Len() int { return len(p) }
  212. func (p ReverseStringArray) Less(i, j int) bool { return p[i] > p[j] }
  213. func (p ReverseStringArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
  214. // testSortReversed runs the given sort function on an input set that
  215. // is in reverse sorted order.
  216. func testSortReversed(t *testing.T, f func([]string), size int) {
  217. checkTestSize(t, size)
  218. input := make([]string, size)
  219. copy(input, uniqueWords)
  220. ri := ReverseStringArray(input)
  221. sort.Sort(ri)
  222. f(input)
  223. if !sort.StringsAreSorted(input) {
  224. t.Error("reversed dictwords input not sorted")
  225. }
  226. }
  227. // testSortNonUnique runs the given sort function on a set of words
  228. // that are not necessarily unique (many will repeat a random number
  229. // of times).
  230. func testSortNonUnique(t *testing.T, f func([]string), size int) {
  231. checkTestSize(t, size)
  232. input := make([]string, size)
  233. copy(input, nonUniqueWords)
  234. f(input)
  235. if !sort.StringsAreSorted(input) {
  236. t.Error("non-unique words input not sorted")
  237. }
  238. }
  239. // checkTestSize compares the given size argument to the maximum
  240. // allowable value, logging an error and failing the test if the
  241. // value is too large.
  242. func checkTestSize(t *testing.T, size int) {
  243. if size > largeDataSize {
  244. t.Error("size is larger than", largeDataSize)
  245. }
  246. }
  247. // shuffle randomly shuffles the elements in the string slice.
  248. func shuffle(input []string) {
  249. n := len(input)
  250. indices := rand.Perm(n)
  251. for i := 0; i < n; i++ {
  252. j := indices[i]
  253. input[i], input[j] = input[j], input[i]
  254. }
  255. }
  256. // isRepeated tests if the array consists only of repeated strings.
  257. func isRepeated(arr []string) bool {
  258. s := arr[0]
  259. for i, a := range arr {
  260. if a != s {
  261. fmt.Printf("%s != %s @ %d\n", a, s, i)
  262. return false
  263. }
  264. }
  265. return true
  266. }