diff --git a/db/db.go b/db/db.go index ecfeb3f2..b1bb02eb 100644 --- a/db/db.go +++ b/db/db.go @@ -1405,18 +1405,6 @@ func isTextType(t string) bool { strings.HasPrefix(t, "clob") } -func randomString() string { - var output strings.Builder - chars := "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP" - - for i := 0; i < 20; i++ { - random := rand.Intn(len(chars)) - randomChar := chars[random] - output.WriteByte(randomChar) - } - return output.String() -} - func containsEmptyType(slice []string) bool { for _, str := range slice { if str == "" { diff --git a/random/random.go b/random/random.go new file mode 100644 index 00000000..3f34431f --- /dev/null +++ b/random/random.go @@ -0,0 +1,29 @@ +package random + +import ( + "math/rand" + "strings" + "sync" + "time" +) + +var r *rand.Rand +var mu sync.Mutex + +func init() { + r = rand.New(rand.NewSource(time.Now().UnixNano())) +} + +// RandomString returns a random string of 20 characters +func RandomString() string { + mu.Lock() + defer mu.Unlock() + var output strings.Builder + chars := "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP" + for i := 0; i < 20; i++ { + random := r.Intn(len(chars)) + randomChar := chars[random] + output.WriteString(string(randomChar)) + } + return output.String() +} diff --git a/random/random_test.go b/random/random_test.go new file mode 100644 index 00000000..795f5651 --- /dev/null +++ b/random/random_test.go @@ -0,0 +1,25 @@ +package random + +import ( + "testing" +) + +func Test_RandomStringLength(t *testing.T) { + str := RandomString() + if len(str) != 20 { + t.Errorf("RandomString() returned a string of length %d; want 20", len(str)) + } +} + +func Test_RandomStringUniqueness(t *testing.T) { + const numStrings = 100 + strs := make(map[string]bool, numStrings) + + for i := 0; i < numStrings; i++ { + str := RandomString() + if strs[str] { + t.Errorf("RandomString() returned a non-unique string: %s", str) + } + strs[str] = true + } +} diff --git a/store/store_test.go b/store/store_test.go index 6671c6de..dba14c43 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -5,18 +5,17 @@ import ( "errors" "expvar" "fmt" - "math/rand" "net" "os" "path/filepath" "sort" - "strings" "testing" "time" "github.com/rqlite/rqlite/command" "github.com/rqlite/rqlite/command/encoding" "github.com/rqlite/rqlite/db" + "github.com/rqlite/rqlite/random" "github.com/rqlite/rqlite/testdata/chinook" ) @@ -2867,18 +2866,18 @@ func mustNewStoreAtPathsLn(id, dataPath, sqlitePath string, fk bool) (*Store, ne } func mustNewStore(t *testing.T) (*Store, net.Listener) { - return mustNewStoreAtPathsLn(randomString(), t.TempDir(), "", false) + return mustNewStoreAtPathsLn(random.RandomString(), t.TempDir(), "", false) } func mustNewStoreFK(t *testing.T) (*Store, net.Listener) { - return mustNewStoreAtPathsLn(randomString(), t.TempDir(), "", true) + return mustNewStoreAtPathsLn(random.RandomString(), t.TempDir(), "", true) } func mustNewStoreSQLitePath(t *testing.T) (*Store, net.Listener, string) { dataDir := t.TempDir() sqliteDir := t.TempDir() sqlitePath := filepath.Join(sqliteDir, "explicit-path.db") - s, ln := mustNewStoreAtPathsLn(randomString(), dataDir, sqlitePath, true) + s, ln := mustNewStoreAtPathsLn(random.RandomString(), dataDir, sqlitePath, true) return s, ln, sqlitePath } @@ -3103,19 +3102,6 @@ func asJSONAssociative(v interface{}) string { return string(b) } -func randomString() string { - var output strings.Builder - r := rand.New(rand.NewSource(time.Now().UnixNano())) - chars := "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP" - - for i := 0; i < 20; i++ { - random := r.Intn(len(chars)) - randomChar := chars[random] - output.WriteString(string(randomChar)) - } - return output.String() -} - func testPoll(t *testing.T, f func() bool, p time.Duration, d time.Duration) { tck := time.NewTicker(p) defer tck.Stop()