| @ -0,0 +1,38 @@ | |||||
| package web | |||||
| import ( | |||||
| "reflect" | |||||
| ) | |||||
| func isFunc(fn interface{}) bool { | |||||
| return reflect.ValueOf(fn).Kind() == reflect.Func | |||||
| } | |||||
| /* | |||||
| This is more than a little sketchtacular. Go's rules for function pointer | |||||
| equality are pretty restrictive: nil function pointers always compare equal, and | |||||
| all other pointer types never do. However, this is pretty limiting: it means | |||||
| that we can't let people reference the middleware they've given us since we have | |||||
| no idea which function they're referring to. | |||||
| To get better data out of Go, we sketch on the representation of interfaces. We | |||||
| happen to know that interfaces are pairs of pointers: one to the real data, one | |||||
| to data about the type. Therefore, two interfaces, including two function | |||||
| interface{}'s, point to exactly the same objects iff their interface | |||||
| representations are identical. And it turns out this is sufficient for our | |||||
| purposes. | |||||
| If you're curious, you can read more about the representation of functions here: | |||||
| http://golang.org/s/go11func | |||||
| We're in effect comparing the pointers of the indirect layer. | |||||
| */ | |||||
| func funcEqual(a, b interface{}) bool { | |||||
| if !isFunc(a) || !isFunc(b) { | |||||
| panic("funcEqual: type error!") | |||||
| } | |||||
| av := reflect.ValueOf(&a).Elem() | |||||
| bv := reflect.ValueOf(&b).Elem() | |||||
| return av.InterfaceData() == bv.InterfaceData() | |||||
| } | |||||
| @ -0,0 +1,84 @@ | |||||
| package web | |||||
| import ( | |||||
| "testing" | |||||
| ) | |||||
| // To tell you the truth, I'm not actually sure how many of these cases are | |||||
| // needed. Presumably someone with more patience than I could comb through | |||||
| // http://golang.org/s/go11func and figure out what all the different cases I | |||||
| // ought to test are, but I think this test includes all the cases I care about | |||||
| // and is at least reasonably thorough. | |||||
| func a() string { | |||||
| return "A" | |||||
| } | |||||
| func b() string { | |||||
| return "B" | |||||
| } | |||||
| func mkFn(s string) func() string { | |||||
| return func() string { | |||||
| return s | |||||
| } | |||||
| } | |||||
| var c = mkFn("C") | |||||
| var d = mkFn("D") | |||||
| var e = a | |||||
| var f = c | |||||
| var g = mkFn("D") | |||||
| type Type string | |||||
| func (t *Type) String() string { | |||||
| return string(*t) | |||||
| } | |||||
| var t1 = Type("hi") | |||||
| var t2 = Type("bye") | |||||
| var t1f = t1.String | |||||
| var t2f = t2.String | |||||
| var funcEqualTests = []struct { | |||||
| a, b func() string | |||||
| result bool | |||||
| }{ | |||||
| {a, a, true}, | |||||
| {a, b, false}, | |||||
| {b, b, true}, | |||||
| {a, c, false}, | |||||
| {c, c, true}, | |||||
| {c, d, false}, | |||||
| {a, e, true}, | |||||
| {a, f, false}, | |||||
| {c, f, true}, | |||||
| {e, f, false}, | |||||
| {d, g, false}, | |||||
| {t1f, t1f, true}, | |||||
| {t1f, t2f, false}, | |||||
| } | |||||
| func TestFuncEqual(t *testing.T) { | |||||
| t.Parallel() | |||||
| for _, test := range funcEqualTests { | |||||
| r := funcEqual(test.a, test.b) | |||||
| if r != test.result { | |||||
| t.Errorf("funcEqual(%v, %v) should have been %v", | |||||
| test.a, test.b, test.result) | |||||
| } | |||||
| } | |||||
| h := mkFn("H") | |||||
| i := h | |||||
| j := mkFn("H") | |||||
| k := a | |||||
| if !funcEqual(h, i) { | |||||
| t.Errorf("h and i should have been equal") | |||||
| } | |||||
| if funcEqual(h, j) { | |||||
| t.Errorf("h and j should not have been equal") | |||||
| } | |||||
| if !funcEqual(a, k) { | |||||
| t.Errorf("a and k should not have been equal") | |||||
| } | |||||
| } | |||||