Nested structs with Ptr of usage panics
jhedev opened this issue · 6 comments
The following code panics sometimes:
package main
import (
"github.com/leanovate/gopter"
"github.com/leanovate/gopter/gen"
"github.com/leanovate/gopter/prop"
)
type A struct {
Value *float64
}
func genA() gopter.Gen {
return gopter.DeriveGen(
func(f *float64) A {
return A{Value: f}
},
func(a A) *float64 {
return a.Value
},
gen.PtrOf(gen.Float64()),
)
}
type B struct {
A1 A
}
func genB() gopter.Gen {
return gopter.DeriveGen(
func(a1 A) B {
return B{a1}
}, func(b B) A {
return b.A1
},
genA(),
)
}
func main() {
properties := gopter.NewProperties(nil)
properties.Property("should not panic", prop.ForAll(
func(b B) bool {
return false
},
genB(),
))
properties.Run(gopter.ConsoleReporter(false))
}
with the output being:
! should not panic: Error on property evaluation after 0 passed tests:
Check paniced: reflect: call of reflect.Value.Interface on zero Value
goroutine 1 [running]:
runtime/debug.Stack(0xc4200556b0, 0x10f6840, 0xc42000aa80)
/usr/local/Cellar/go/1.10.3/libexec/src/runtime/debug/stack.go:24 +0xa7
github.com/leanovate/gopter.SaveProp.func1.1(0xc420055d30)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop.go:19 +0x6e
panic(0x10f6840, 0xc42000aa80)
/usr/local/Cellar/go/1.10.3/libexec/src/runtime/panic.go:502 +0x229
reflect.valueInterface(0x0, 0x0, 0x0, 0x1, 0x10e49e0, 0x0)
/usr/local/Cellar/go/1.10.3/libexec/src/reflect/value.go:953 +0x1a3
reflect.Value.Interface(0x0, 0x0, 0x0, 0x0, 0x0)
/usr/local/Cellar/go/1.10.3/libexec/src/reflect/value.go:948 +0x44
github.com/leanovate/gopter/gen.PtrShrinker.func1(0x10e49e0, 0x0, 0x1)
/Users/joel/code/go/src/github.com/leanovate/gopter/gen/ptr_shrink.go:28
+0xc1
github.com/leanovate/gopter.CombineShrinker.func1(0x10e8c80, 0xc42000aa60,
0x10e8c80)
/Users/joel/code/go/src/github.com/leanovate/gopter/shrink.go:168 +0x139
github.com/leanovate/gopter.(*derivedGen).Shrinker(0xc420094500,
0x10fc000, 0x0, 0xc42000c0e0)
/Users/joel/code/go/src/github.com/leanovate/gopter/derived_gen.go:81
+0xe3
github.com/leanovate/gopter.(*derivedGen).Shrinker-fm(0x10fc000, 0x0, 0x1)
/Users/joel/code/go/src/github.com/leanovate/gopter/derived_gen.go:31
+0x3e
github.com/leanovate/gopter.CombineShrinker.func1(0x10e8c80, 0xc42000aa00,
0x10e8c80)
/Users/joel/code/go/src/github.com/leanovate/gopter/shrink.go:168 +0x139
github.com/leanovate/gopter.(*derivedGen).Shrinker(0xc420094780,
0x10fc080, 0x0, 0xc420055b90)
/Users/joel/code/go/src/github.com/leanovate/gopter/derived_gen.go:81
+0xe3
github.com/leanovate/gopter.(*derivedGen).Shrinker-fm(0x10fc080, 0x0,
0x10fc080)
/Users/joel/code/go/src/github.com/leanovate/gopter/derived_gen.go:31
+0x3e
github.com/leanovate/gopter/prop.shrinkValue(0x3e8, 0xc420094a00,
0x10fc080, 0x0, 0xc420094a50, 0xc420055cb8, 0x10c4dbc, 0x10fc980,
0xc42000e480)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop/forall.go:99
+0x4e
github.com/leanovate/gopter/prop.ForAll.func1(0xc42000a620, 0x1122320)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop/forall.go:47
+0x532
github.com/leanovate/gopter.SaveProp.func1(0xc42000a620, 0x0)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop.go:24 +0x6c
github.com/leanovate/gopter.Prop.Check.func1(0x0, 0xc42000e4c0, 0x11c69a0)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop.go:52 +0x15c
github.com/leanovate/gopter.(*runner).runWorkers(0xc420088570, 0x0)
/Users/joel/code/go/src/github.com/leanovate/gopter/runner.go:49 +0x2d9
github.com/leanovate/gopter.Prop.Check(0xc42000e4a0, 0xc42001e6c0,
0x111b6c4)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop.go:110 +0x1af
github.com/leanovate/gopter.(*Properties).Run(0xc4200882d0, 0x1131560,
0xc42000a5e0, 0x10)
/Users/joel/code/go/src/github.com/leanovate/gopter/properties.go:37 +0xc9
main.main()
/Users/joel/code/gopter_sample.go:50 +0x1b0
It seems to only happen if the shrinker is used. If I change, for example, the return false
in the property to return true
it does never happen.
Somehow it requires these two nested structs. Using just struct A does not result in a panic... Not sure why or if I was just unlucky when reproducing...
It looks like the problem is in the Ptr shrink implementation where the call to Interface
panics on a nil value.
I'm happy to help to fix this but I need a bit of guidance :)
I've now found a similar problem when using slices: reflect: call of reflect.Value.Set on zero Value
Just patched the ptr shrinker to avoid this kind of problem, please check if it really solves it as it seems to be pretty erratic.
Can you also give me a hint about the problem with slices (i.e. stacktrack or example)
Thanks so much for the quick fix!
The slice thing happens when I adapt the example above like this:
package main
import (
"github.com/leanovate/gopter"
"github.com/leanovate/gopter/gen"
"github.com/leanovate/gopter/prop"
)
type A struct {
Value *float64
}
func genA() gopter.Gen {
return gopter.DeriveGen(
func(f *float64) A {
return A{Value: f}
},
func(a A) *float64 {
return a.Value
},
gen.PtrOf(gen.Float64()),
)
}
type B struct {
A1 A
}
func genB() gopter.Gen {
return gopter.DeriveGen(
func(a1 A) B {
return B{a1}
}, func(b B) A {
return b.A1
},
genA(),
)
}
func main() {
properties := gopter.NewProperties(nil)
properties.Property("should not panic", prop.ForAll(
func(b []*B) bool {
if len(b) == 0 {
return true
}
return false
},
gen.SliceOf(gen.PtrOf(genB())),
))
properties.Run(gopter.ConsoleReporter(false))
}
Output is:
! should not panic: Error on property evaluation after 2 passed tests:
Check paniced: reflect: call of reflect.Value.Set on zero Value goroutine
1 [running]:
runtime/debug.Stack(0xc420055800, 0x10f7100, 0xc42000b600)
/usr/local/Cellar/go/1.10.3/libexec/src/runtime/debug/stack.go:24 +0xa7
github.com/leanovate/gopter.SaveProp.func1.1(0xc420055d30)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop.go:19 +0x6e
panic(0x10f7100, 0xc42000b600)
/usr/local/Cellar/go/1.10.3/libexec/src/runtime/panic.go:502 +0x229
reflect.flag.mustBeExported(0x0)
/usr/local/Cellar/go/1.10.3/libexec/src/reflect/value.go:215 +0xae
reflect.Value.Set(0x10e5920, 0xc42000c1a8, 0x196, 0x0, 0x0, 0x0)
/usr/local/Cellar/go/1.10.3/libexec/src/reflect/value.go:1368 +0x40
github.com/leanovate/gopter/gen.(*sliceShrinkOne).Next(0xc4200889c0, 0x0,
0x0, 0x100e800)
/Users/joel/code/go/src/github.com/leanovate/gopter/gen/slice_shrink.go:23
+0x21a
github.com/leanovate/gopter/gen.(*sliceShrinkOne).Next-fm(0x0, 0x0,
0xc42000c100)
/Users/joel/code/go/src/github.com/leanovate/gopter/gen/slice_shrink.go:45
+0x2a
github.com/leanovate/gopter.(*concatedShrink).Next(0xc42000b5a0,
0xc42000b5c0, 0x0, 0x11c80e0)
/Users/joel/code/go/src/github.com/leanovate/gopter/shrink.go:76 +0x4b
github.com/leanovate/gopter.(*concatedShrink).Next-fm(0x0, 0xc4200889c0,
0xc4200888a0)
/Users/joel/code/go/src/github.com/leanovate/gopter/shrink.go:91 +0x2a
github.com/leanovate/gopter.Shrink.Filter.func1(0xc42000b5c0,
0xc420055b48, 0x10cb17b)
/Users/joel/code/go/src/github.com/leanovate/gopter/shrink.go:24 +0x3b
github.com/leanovate/gopter/prop.firstFailure(0xc42000b5c0, 0xc420055cb8,
0xc42000b5c0, 0xc42000b140, 0xc420055bd0)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop/forall.go:114
+0x2b
github.com/leanovate/gopter/prop.shrinkValue(0x3e8, 0xc420095450,
0x10e8c40, 0xc42000b140, 0xc4200954a0, 0xc420055cb8, 0xc42001e740,
0x10fd240, 0xc42000e4a0)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop/forall.go:100
+0x88
github.com/leanovate/gopter/prop.ForAll.func1(0xc42000ada0, 0x1122c18)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop/forall.go:47
+0x532
github.com/leanovate/gopter.SaveProp.func1(0xc42000ada0, 0x0)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop.go:24 +0x6c
github.com/leanovate/gopter.Prop.Check.func1(0x0, 0xc42000e500, 0x11c79a0)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop.go:52 +0x15c
github.com/leanovate/gopter.(*runner).runWorkers(0xc420088570, 0x0)
/Users/joel/code/go/src/github.com/leanovate/gopter/runner.go:49 +0x2d9
github.com/leanovate/gopter.Prop.Check(0xc42000e4e0, 0xc42001e6c0,
0x111bf84)
/Users/joel/code/go/src/github.com/leanovate/gopter/prop.go:110 +0x1af
github.com/leanovate/gopter.(*Properties).Run(0xc4200882d0, 0x1131ee0,
0xc42000a620, 0x10)
/Users/joel/code/go/src/github.com/leanovate/gopter/properties.go:37 +0xc9
main.main()
/Users/joel/code/gopter_sample.go:53 +0x1c4
The panic message was a bit misleading in this case.
The patch should fix this problem (at least I was not able to reproduce the panic any more)
@untoldwind Thanks again for your quick help. I'll try it with our test suite asap
I ran the test suite again and the error did not occur anymore.