├── .travis.yml ├── .gitignore ├── README.md ├── LICENSE ├── gobberish.go └── gobberish_test.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.3 5 | - 1.4 6 | - tip 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Gobberish 2 | ========= 3 | Building random unicode strings for fuzz testing. 4 | 5 | [![Build Status](https://travis-ci.org/chrismcguire/gobberish.svg?branch=master)](https://travis-ci.org/chrismcguire/gobberish) 6 | [![GoDoc](https://godoc.org/github.com/chrismcguire/gobberish?status.svg)](https://godoc.org/github.com/chrismcguire/gobberish) 7 | 8 | Usage 9 | ----- 10 | Importing 11 | ```Go 12 | import "github.com/chrismcguire/gobberish" 13 | import "fmt" 14 | ``` 15 | 16 | Generating a random utf-8 string of a specified length 17 | ```Go 18 | testString := gobberish.GenerateString(15) 19 | fmt.Println(testString) 20 | 21 | >> "티냮絥䯩얻橸禸䨃ȲСᣫ흳乃!*" 22 | ``` 23 | 24 | You can also specify unicode ranges. For instance, to generate a 5 character string composed of Greek and Latin code points 25 | ```Go 26 | greekAndLatin := gobberish.GenerateStringInRange(5, unicode.Greek, unicode.Latin) 27 | fmt.Println(greekAndLatin) 28 | 29 | >> "ƈꝨĶῈΊ" 30 | ``` 31 | A full list of character ranges can be found at http://golang.org/pkg/unicode/#pkg-variables 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Chris McGuire 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /gobberish.go: -------------------------------------------------------------------------------- 1 | // Package gobberish provides helper functions for generating random strings 2 | // for testing. 3 | package gobberish 4 | 5 | import "errors" 6 | import "math/rand" 7 | import "time" 8 | import "unicode" 9 | 10 | // Generate a random utf-8 string of a given character (not byte) length. 11 | // The range of the random characters is the entire printable unicode range. 12 | func GenerateString(length int) string { 13 | var s []rune 14 | for i := 0; i < length; i++ { 15 | s = append(s, CreateRandomRune()) 16 | } 17 | 18 | return string(s) 19 | } 20 | 21 | // Generate a random utf-8 string of a given character length that exists on the 22 | // given RangeTables. For a list of valid RangeTables, see 23 | // http://golang.org/pkg/unicode/#pkg-variables 24 | func GenerateStringInRange(length int, tables ...*unicode.RangeTable) string { 25 | var s []rune 26 | for i := 0; i < length; i++ { 27 | s = append(s, CreateRandomRuneInRange(tables)) 28 | } 29 | 30 | return string(s) 31 | } 32 | 33 | // Generates a random rune in the printable range. 34 | func CreateRandomRune() rune { 35 | return CreateRandomRuneInRange(unicode.GraphicRanges) 36 | } 37 | 38 | // Generates a random rune in the given RangeTable. 39 | func CreateRandomRuneInRange(tables []*unicode.RangeTable) rune { 40 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 41 | i := r.Intn(totalInRange(tables)) 42 | x, _ := getItemInRangeTable(i, tables) 43 | 44 | return rune(x) 45 | } 46 | 47 | // Returns the nth item contained in the array of ranges. 48 | func getItemInRangeTable(n int, tables []*unicode.RangeTable) (int, error) { 49 | nPointer := n 50 | var picked int 51 | found := false 52 | 53 | for _, table := range tables { 54 | if found == false { 55 | for _, r16 := range table.R16 { 56 | countInRange := int((r16.Hi-r16.Lo)/r16.Stride) + 1 57 | if nPointer <= countInRange-1 { 58 | picked = int(r16.Lo) + (int(r16.Stride) * nPointer) 59 | found = true 60 | break 61 | } else { 62 | nPointer -= countInRange 63 | } 64 | } 65 | 66 | if found == false { 67 | for _, r32 := range table.R32 { 68 | countInRange := int((r32.Hi-r32.Lo)/r32.Stride) + 1 69 | if nPointer <= countInRange-1 { 70 | picked = int(r32.Lo) + (int(r32.Stride) * nPointer) 71 | found = true 72 | break 73 | } else { 74 | nPointer -= countInRange 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | if found == true { 82 | return picked, nil 83 | } else { 84 | return -1, errors.New("Value not found in range") 85 | } 86 | } 87 | 88 | // Counts the total number of items contained in the array of ranges. 89 | func totalInRange(tables []*unicode.RangeTable) int { 90 | total := 0 91 | for _, table := range tables { 92 | for _, r16 := range table.R16 { 93 | total += int((r16.Hi-r16.Lo)/r16.Stride) + 1 94 | } 95 | for _, r32 := range table.R32 { 96 | total += int((r32.Hi-r32.Lo)/r32.Stride) + 1 97 | } 98 | } 99 | return total 100 | } 101 | -------------------------------------------------------------------------------- /gobberish_test.go: -------------------------------------------------------------------------------- 1 | package gobberish 2 | 3 | import ( 4 | "errors" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | "unicode" 8 | ) 9 | 10 | // getItemInRangeTable 11 | 12 | func TestGetItemInSingle16RangeTable(t *testing.T) { 13 | ranges := [][]int{{5, 15, 2}} 14 | table := createRangeTable(ranges) 15 | tables := []*unicode.RangeTable{&table} 16 | 17 | returned, _ := getItemInRangeTable(4, tables) 18 | 19 | assert.Equal( 20 | t, 21 | 13, 22 | returned, 23 | "Retreived value from range did not match expected", 24 | ) 25 | } 26 | 27 | func TestGetItemInSingle32RangeTable(t *testing.T) { 28 | ranges := [][]int{{100000, 100010, 1}} 29 | table := createRangeTable(ranges) 30 | tables := []*unicode.RangeTable{&table} 31 | 32 | returned, _ := getItemInRangeTable(7, tables) 33 | 34 | assert.Equal( 35 | t, 36 | 100007, 37 | returned, 38 | "Retreived value from range did not match expected", 39 | ) 40 | } 41 | 42 | func TestGetItemWithFirstItemInRange(t *testing.T) { 43 | ranges := [][]int{{7, 10, 1}} 44 | table := createRangeTable(ranges) 45 | tables := []*unicode.RangeTable{&table} 46 | 47 | returned, _ := getItemInRangeTable(0, tables) 48 | 49 | assert.Equal( 50 | t, 51 | 7, 52 | returned, 53 | "Retreived value from range did not match expected", 54 | ) 55 | } 56 | 57 | func TestGetItemWithLastItemInRange(t *testing.T) { 58 | ranges := [][]int{{0, 5, 1}} 59 | table := createRangeTable(ranges) 60 | tables := []*unicode.RangeTable{&table} 61 | 62 | returned, _ := getItemInRangeTable(5, tables) 63 | 64 | assert.Equal( 65 | t, 66 | 5, 67 | returned, 68 | "Retreived value from range did not match expected", 69 | ) 70 | } 71 | 72 | func TestGetItemInMultiRangeRangeTable(t *testing.T) { 73 | ranges := [][]int{{0, 5, 1}, {100000, 100010, 1}} 74 | table := createRangeTable(ranges) 75 | tables := []*unicode.RangeTable{&table} 76 | 77 | returned, _ := getItemInRangeTable(9, tables) 78 | 79 | assert.Equal( 80 | t, 81 | 100003, 82 | returned, 83 | "Retreived value from range did not match expected", 84 | ) 85 | } 86 | 87 | func TestGetItemWithFirstItemInSecondRange(t *testing.T) { 88 | ranges := [][]int{{10, 20, 1}, {100100, 100200, 20}} 89 | table := createRangeTable(ranges) 90 | tables := []*unicode.RangeTable{&table} 91 | 92 | returned, _ := getItemInRangeTable(11, tables) 93 | 94 | assert.Equal( 95 | t, 96 | 100100, 97 | returned, 98 | "Retreived value from range did not match expected", 99 | ) 100 | } 101 | 102 | func TestGetItemWithLastItemInSecondRange(t *testing.T) { 103 | ranges := [][]int{{10, 20, 1}, {100100, 100200, 20}} 104 | table := createRangeTable(ranges) 105 | tables := []*unicode.RangeTable{&table} 106 | 107 | returned, _ := getItemInRangeTable(16, tables) 108 | 109 | assert.Equal( 110 | t, 111 | 100200, 112 | returned, 113 | "Retreived value from range did not match expected", 114 | ) 115 | } 116 | 117 | func TestGetItemWithMultipleRangeTables(t *testing.T) { 118 | ranges := [][]int{{0, 10, 2}, {100010, 100020, 2}} 119 | table := createRangeTable(ranges) 120 | tables := []*unicode.RangeTable{&table} 121 | secondRanges := [][]int{{10, 13, 1}, {100000, 100021, 7}} 122 | secondTable := createRangeTable(secondRanges) 123 | tables = append(tables, &secondTable) 124 | 125 | returned, _ := getItemInRangeTable(14, tables) 126 | 127 | assert.Equal( 128 | t, 129 | 12, 130 | returned, 131 | "Retreived value from range did not match expected", 132 | ) 133 | } 134 | 135 | func TestGetItemWithLastItemInMultipleRangeTables(t *testing.T) { 136 | ranges := [][]int{{0, 10, 2}, {100010, 100020, 2}} 137 | table := createRangeTable(ranges) 138 | tables := []*unicode.RangeTable{&table} 139 | secondRanges := [][]int{{10, 13, 1}, {100000, 100021, 7}} 140 | secondTable := createRangeTable(secondRanges) 141 | tables = append(tables, &secondTable) 142 | 143 | returned, _ := getItemInRangeTable(19, tables) 144 | 145 | assert.Equal( 146 | t, 147 | 100021, 148 | returned, 149 | "Retreived value from range did not match expected", 150 | ) 151 | } 152 | 153 | func TestGetItemWhenOutsideRange(t *testing.T) { 154 | ranges := [][]int{{0, 10, 1}} 155 | table := createRangeTable(ranges) 156 | tables := []*unicode.RangeTable{&table} 157 | 158 | returned, err := getItemInRangeTable(42, tables) 159 | 160 | assert.Equal( 161 | t, 162 | -1, 163 | returned, 164 | "Retreive value from range when error did not match expected", 165 | ) 166 | assert.Equal( 167 | t, 168 | errors.New("Value not found in range"), 169 | err, 170 | "Error not raised when getting outside of range", 171 | ) 172 | } 173 | 174 | // totalInRange 175 | 176 | func TestTotalInRangeWithEmptySlice(t *testing.T) { 177 | var tables []*unicode.RangeTable 178 | 179 | returned := totalInRange(tables) 180 | assert.Equal(t, 0, returned, "Returned non-zero value") 181 | } 182 | 183 | func TestTotalInRangeWithSingleRange(t *testing.T) { 184 | ranges := [][]int{{5, 7, 1}} 185 | table := createRangeTable(ranges) 186 | tables := []*unicode.RangeTable{&table} 187 | 188 | returned := totalInRange(tables) 189 | 190 | assert.Equal(t, 3, returned, "Incorrectly counted range") 191 | } 192 | 193 | func TestTotalInRangeWithSingleRangeTable(t *testing.T) { 194 | ranges := [][]int{{0, 10, 2}, {100000, 100010, 1}} 195 | table := createRangeTable(ranges) 196 | tables := []*unicode.RangeTable{&table} 197 | 198 | returned := totalInRange(tables) 199 | 200 | assert.Equal(t, 17, returned, "Incorrectly counted range") 201 | } 202 | 203 | func TestTotalInRangeWithMultipleRangeTables(t *testing.T) { 204 | ranges := [][]int{{7, 14, 7}, {1000000, 1000001, 1}} 205 | table := createRangeTable(ranges) 206 | tables := []*unicode.RangeTable{&table} 207 | secondRanges := [][]int{{8, 10, 1}, {100000, 100021, 7}} 208 | secondTable := createRangeTable(secondRanges) 209 | tables = append(tables, &secondTable) 210 | 211 | returned := totalInRange(tables) 212 | 213 | assert.Equal(t, 11, returned, "Incorrectly counted range.") 214 | } 215 | 216 | func TestTotalInRangeWithSingleValueRange(t *testing.T) { 217 | ranges := [][]int{{5, 5, 1}} 218 | table := createRangeTable(ranges) 219 | tables := []*unicode.RangeTable{&table} 220 | 221 | returned := totalInRange(tables) 222 | 223 | assert.Equal(t, 1, returned, "Incorrectly counted range") 224 | } 225 | 226 | func createRangeTable(ranges [][]int) unicode.RangeTable { 227 | var sixteens []unicode.Range16 228 | var thirtyTwos []unicode.Range32 229 | 230 | for _, numRange := range ranges { 231 | if numRange[1] <= 65535 { 232 | sixteen := unicode.Range16{ 233 | Lo: uint16(numRange[0]), 234 | Hi: uint16(numRange[1]), 235 | Stride: uint16(numRange[2]), 236 | } 237 | sixteens = append(sixteens, sixteen) 238 | } else { 239 | thirtyTwo := unicode.Range32{ 240 | Lo: uint32(numRange[0]), 241 | Hi: uint32(numRange[1]), 242 | Stride: uint32(numRange[2]), 243 | } 244 | thirtyTwos = append(thirtyTwos, thirtyTwo) 245 | } 246 | } 247 | 248 | table := unicode.RangeTable{R16: sixteens, R32: thirtyTwos} 249 | 250 | return table 251 | } 252 | --------------------------------------------------------------------------------