unix: add SockaddrUnix tests on linux

Follow the existing examples of the SockaddrTIPC and SockaddrL2TPIP{,6}
tests.

Handling of abstract socket addresses is different on linux than on
other platforms, thus these are currently linux-specific.

Change-Id: I66686d979e1dd317ab7a8a6dd85f98e670ad89b6
Reviewed-on: https://go-review.googlesource.com/c/sys/+/228764
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/unix/syscall_internal_linux_test.go b/unix/syscall_internal_linux_test.go
index 4dafac8..22199b2 100644
--- a/unix/syscall_internal_linux_test.go
+++ b/unix/syscall_internal_linux_test.go
@@ -8,6 +8,7 @@
 
 import (
 	"reflect"
+	"strings"
 	"testing"
 	"unsafe"
 )
@@ -137,6 +138,25 @@
 			skt: SocketSpec{domain: AF_INET6, typ: SOCK_DGRAM, protocol: IPPROTO_L2TP},
 		},
 		{
+			name: "AF_UNIX unnamed/abstract",
+			rsa: sockaddrUnixToAny(RawSockaddrUnix{
+				Family: AF_UNIX,
+			}),
+			sa: &SockaddrUnix{
+				Name: "@",
+			},
+		},
+		{
+			name: "AF_UNIX named",
+			rsa: sockaddrUnixToAny(RawSockaddrUnix{
+				Family: AF_UNIX,
+				Path:   [108]int8{'g', 'o', 'p', 'h', 'e', 'r'},
+			}),
+			sa: &SockaddrUnix{
+				Name: "gopher",
+			},
+		},
+		{
 			name: "AF_MAX EAFNOSUPPORT",
 			rsa: &RawSockaddrAny{
 				Addr: RawSockaddr{
@@ -373,6 +393,76 @@
 	}
 }
 
+func TestSockaddrUnix_sockaddr(t *testing.T) {
+	tests := []struct {
+		name string
+		sa   *SockaddrUnix
+		raw  *RawSockaddrUnix
+		slen _Socklen
+		err  error
+	}{
+		{
+			name: "unnamed",
+			sa:   &SockaddrUnix{},
+			raw: &RawSockaddrUnix{
+				Family: AF_UNIX,
+			},
+			slen: 2, // family (uint16)
+		},
+		{
+			name: "abstract",
+			sa: &SockaddrUnix{
+				Name: "@",
+			},
+			raw: &RawSockaddrUnix{
+				Family: AF_UNIX,
+			},
+			slen: 3, // family (uint16) + NULL
+		},
+		{
+			name: "named",
+			sa: &SockaddrUnix{
+				Name: "gopher",
+			},
+			raw: &RawSockaddrUnix{
+				Family: AF_UNIX,
+				Path:   [108]int8{'g', 'o', 'p', 'h', 'e', 'r'},
+			},
+			slen: _Socklen(3 + len("gopher")), // family (uint16) + len(gopher)
+		},
+		{
+			name: "named too long",
+			sa: &SockaddrUnix{
+				Name: strings.Repeat("A", 108),
+			},
+			err: EINVAL,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			out, l, err := tt.sa.sockaddr()
+			if err != tt.err {
+				t.Fatalf("unexpected error: %v, want: %v", err, tt.err)
+			}
+
+			if l != tt.slen {
+				t.Fatalf("unexpected Socklen: %d, want %d", l, tt.slen)
+			}
+			if out == nil {
+				// No pointer to cast, return early.
+				return
+			}
+
+			raw := (*RawSockaddrUnix)(out)
+			if !reflect.DeepEqual(raw, tt.raw) {
+				t.Fatalf("unexpected RawSockaddrUnix:\n got: %#v\nwant: %#v", raw, tt.raw)
+			}
+		})
+	}
+
+}
+
 // These helpers explicitly copy the contents of in into out to produce
 // the correct sockaddr structure, without relying on unsafe casting to
 // a type of a larger size.
@@ -402,3 +492,17 @@
 	)
 	return &out
 }
+
+func sockaddrUnixToAny(in RawSockaddrUnix) *RawSockaddrAny {
+	var out RawSockaddrAny
+
+	// Explicitly copy the contents of in into out to produce the correct
+	// sockaddr structure, without relying on unsafe casting to a type of a
+	// larger size.
+	copy(
+		(*(*[SizeofSockaddrAny]byte)(unsafe.Pointer(&out)))[:],
+		(*(*[SizeofSockaddrUnix]byte)(unsafe.Pointer(&in)))[:],
+	)
+
+	return &out
+}