leanovate / gopter

GOlang Property TestER
MIT License
598 stars 40 forks source link

map generators? #26

Closed andrewjstone closed 6 years ago

andrewjstone commented 6 years ago

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?

untoldwind commented 6 years ago

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.

untoldwind commented 6 years ago

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

andrewjstone commented 6 years ago

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