go-i3/byteorder_test.go
2023-10-22 16:19:10 +02:00

125 lines
3.1 KiB
Go

package i3
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/sync/errgroup"
)
func msgBytes(order binary.ByteOrder, t messageType, payload string) []byte {
var buf bytes.Buffer
if err := binary.Write(&buf, order, &header{magic, uint32(len(payload)), t}); err != nil {
panic(err)
}
_, err := buf.WriteString(payload)
if err != nil {
panic(err)
}
return buf.Bytes()
}
func TestDetectByteOrder(t *testing.T) {
t.Parallel()
for _, i3order := range []binary.ByteOrder{binary.BigEndian, binary.LittleEndian} {
i3order := i3order // copy
t.Run(fmt.Sprintf("%T", i3order), func(t *testing.T) {
t.Parallel()
var (
subscribeRequest = msgBytes(i3order, messageTypeSubscribe, "[]"+strings.Repeat(" ", 65536+256-2))
subscribeReply = msgBytes(i3order, messageReplyTypeSubscribe, `{"success": true}`)
nopPrefix = "nop byte-order detection. padding: "
runCommandRequest = msgBytes(i3order, messageTypeRunCommand, nopPrefix+strings.Repeat("a", 65536+256-len(nopPrefix)))
runCommandReply = msgBytes(i3order, messageReplyTypeCommand, `[{"success": true}]`)
protocol = map[string][]byte{
string(subscribeRequest): subscribeReply,
string(runCommandRequest): runCommandReply,
}
)
// Abstract socket addresses are a linux-only feature, so we must
// use file system paths for listening/dialing:
dir, err := ioutil.TempDir("", "i3test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
path := filepath.Join(dir, fmt.Sprintf("i3test-%T.sock", i3order))
i3addr, err := net.ResolveUnixAddr("unix", path)
if err != nil {
t.Fatal(err)
}
i3ln, err := net.ListenUnix("unix", i3addr)
if err != nil {
t.Fatal(err)
}
var (
eg errgroup.Group
order binary.ByteOrder
orderErr error
)
eg.Go(func() error {
addr, err := net.ResolveUnixAddr("unix", path)
if err != nil {
return err
}
conn, err := net.DialUnix("unix", nil, addr)
if err != nil {
return err
}
order, orderErr = detectByteOrder(conn)
conn.Close()
i3ln.Close() // unblock Accept and return an error
return orderErr
})
eg.Go(func() error {
for {
conn, err := i3ln.Accept()
if err != nil {
return err
}
eg.Go(func() error {
defer conn.Close()
for {
var request [14 + 65536 + 256]byte
if _, err := io.ReadFull(conn, request[:]); err != nil {
return err
}
if reply := protocol[string(request[:])]; reply != nil {
if _, err := io.Copy(conn, bytes.NewReader(reply)); err != nil {
return err
}
continue
}
// silently drop unexpected messages like i3
}
})
}
})
if err := eg.Wait(); err != nil {
// If order != nil && orderErr == nil, the test succeeded and any
// returned errors are from teardown.
if order == nil || orderErr != nil {
t.Fatal(err)
}
}
if got, want := order, i3order; got != want {
t.Fatalf("unexpected byte order: got %v, want %v", got, want)
}
})
}
}