leanovate/gopter

map generators?

andrewjstone opened this issue · 3 comments

There are generators for slices, but none for maps. What I have been doing to work around this is generating helper structs containing slices and then computing the maps after generation. This seems to work fine, but is a bit ugly.

Is there a fundamental reason why generating maps is hard, or is it just a matter of work to build it?

If I remember correctly I skipped this because until recently there have been some issues with a generic "map[interface{}]interface{}". But I think this has been fixed.

Here is an (early) example that seems to work with go 1.9:

func MapOf(keyGen, elementGen gopter.Gen) gopter.Gen {
	return func(genParams *gopter.GenParameters) *gopter.GenResult {
		len := 0
		if genParams.MaxSize > 0 || genParams.MinSize > 0 {
			if genParams.MinSize > genParams.MaxSize {
				panic("GenParameters.MinSize must be <= GenParameters.MaxSize")
			}

			if genParams.MaxSize == genParams.MinSize {
				len = genParams.MaxSize
			} else {
				len = genParams.Rng.Intn(genParams.MaxSize-genParams.MinSize) + genParams.MinSize
			}
		}

		result := map[interface{}]interface{}{}
		for i := 0; i < len; i++ {
			element, elementOk := elementGen(genParams).Retrieve()
			key, keyOk := keyGen(genParams).Retrieve()

			if elementOk && keyOk {
				result[key] = element
			}
		}

		genResult := gopter.NewGenResult(result, gopter.NoShrinker)
		return genResult
	}
}

Of course, the shrinker will require further work.

I've added a more sophisticated MapOf version with shrinker support and better typing.
Let me know if that version meets your needs.

Looks great. I wasn't able to evaluate the shrinking code, but the interface looks like what I'd expect. Thanks!