Issue
I’m using crypto/ssh package and I’m trying to write a unit test for a method that constructs the ClientConfig.
One of the assertions in the unit is that the returned ClientConfig
is deeply equal to the expected.
The assertion fails because both Auth
and HostKeyCallback
fields of ClientConfig
are not deeply equal.
The HostKeyCallback
is hardcoded to be ssh.InsecureIgnoreHostKey()
.
The only authentication method I’m testing right now is with password and I have verified that the password string is picked up correctly.
I tried to mess around in playground (see here) and I don’t understand why there is no deep equality in these cases.
package main
import (
"fmt"
"reflect"
"golang.org/x/crypto/ssh"
)
func main() {
pass := "bar"
auth := []ssh.AuthMethod{ssh.Password(pass)}
authLiteral := []ssh.AuthMethod{ssh.Password("bar")}
if reflect.DeepEqual(authLiteral, auth) {
fmt.Println("authentication methods are equal")
} else {
fmt.Println("authentication methods are not equal")
}
callback1 := ssh.InsecureIgnoreHostKey()
callback2 := ssh.InsecureIgnoreHostKey()
if reflect.DeepEqual(callback1, callback2) {
fmt.Println("callbacks are equal")
} else {
fmt.Println("callbacks are not equal")
}
}
authentication methods are not equal
callbacks are not equal
Could someone please explain these results?
I would also be grateful if you could suggest how I could unit test this case.
Solution
As mkopriva notes in a comment, this is formally disallowed. (Still, it might be nice if reflect.DeepEqual
, which comes with the Go implementation, did something like I did below. Because it comes with the implementation, the implementor can use special knowledge to write it.)
Ultimately, of course, there’s some machine-level implementation for pointer to function taking arguments of types T1, T2, … Tna and returning values value of types R1, R2, … Rnr . But we’re not told what that representation is. It might be a single unsafe.Pointer
value, or it might be some kind of inline expansion of a thunk. So we don’t know how many bytes to compare here.
If you’re willing to make rather dangerous / non-portable assumptions, and update them if and when they prove inadequate, there is a way to "cheat" here. I made this example on the Go Playground, which produces the desired output. Is this just by chance, or does this really work on your Go implementation?
The text is also below. Use at your own risk!
package main
import (
"fmt"
"unsafe"
)
func dummy1() {}
func dummy2() {}
func main() {
fmt.Println("dummy1 == dummy2 =>", qe(dummy1, dummy2))
fmt.Println("dummy1 == dummy1 =>", qe(dummy1, dummy1))
}
// "questionably equal", for function pointers
func qe(a, b func()) bool {
pp1 := (*unsafe.Pointer)(unsafe.Pointer(&a))
pp2 := (*unsafe.Pointer)(unsafe.Pointer(&b))
p1 := *pp1
p2 := *pp2
return p1 == p2
}
Answered By – torek
Answer Checked By – Marilyn (GoLangFix Volunteer)