package pkglint import ( "gopkg.in/check.v1" "math/rand" "time" ) // Fuzzer generates random strings. // The structure of the strings is configurable. type Fuzzer struct { seed int64 rnd *rand.Rand stock []struct { r rune weight int } total int // The sum of all stock weights last string // ok bool // Whether the last string was processed correctly } // NewFuzzer returns a fuzzer. // If no seed is passed, a random seed is chosen. // To reproduce a previous run, pass the seed from that run as the parameter. func NewFuzzer(seed ...int64) *Fuzzer { var actualSeed int64 if len(seed) > 0 { actualSeed = seed[0] } else { actualSeed = time.Now().UnixNano() } return &Fuzzer{seed: actualSeed, rnd: rand.New(rand.NewSource(actualSeed))} } // Char randomly generates a character from the given set. // Each character has the given weight. func (f *Fuzzer) Char(set string, weight int) { for _, r := range set { f.addChar(r, weight) } } // Range randomly generates a character from the given range. // Each character has the given weight. func (f *Fuzzer) Range(minIncl, maxIncl rune, weight int) { for r := minIncl; r <= maxIncl; r++ { f.addChar(r, weight) } } func (f *Fuzzer) addChar(r rune, weight int) { f.stock = append(f.stock, struct { r rune weight int }{r, weight}) f.total += weight } func (f *Fuzzer) Generate(length int) string { rs := make([]rune, length) for i := 0; i < length; i++ { rs[i] = f.randomChar() } f.last = string(rs) return f.last } func (f *Fuzzer) randomChar() rune { i := int(f.rnd.Int31n(int32(f.total))) for _, entry := range f.stock { i -= entry.weight if i < 0 { return entry.r } } panic("Out of stock") } // CheckOk is typically used in a defer statement and is run after all // the tests to check whether they have been marked as ok. func (f *Fuzzer) CheckOk() { if !f.ok { panic(sprintf("Fuzzing failed with seed %d, last generated value: %s", f.seed, f.last)) } } // Ok marks the current string as processed correctly. func (f *Fuzzer) Ok() { f.ok = true } func (s *Suite) Test_Fuzzer__out_of_stock(c *check.C) { fuzzer := NewFuzzer(0) fuzzer.total = 1 // Intentionally damage the fuzzer to achieve full code coverage. c.Check( func() { fuzzer.Generate(1) }, check.Panics, "Out of stock") }