mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
yangerd: add missing files
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package ethmonitor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildEthernetContainerCopper1G(t *testing.T) {
|
||||
data := ethtoolJSON{
|
||||
Speed: 1000,
|
||||
Duplex: "Full",
|
||||
Port: "Twisted Pair",
|
||||
AutoNegotiation: true,
|
||||
SupportedLinkModes: []string{
|
||||
"10baseT/Half", "10baseT/Full",
|
||||
"100baseT/Half", "100baseT/Full",
|
||||
"1000baseT/Full",
|
||||
},
|
||||
AdvertisedLinkModes: []string{
|
||||
"10baseT/Half", "10baseT/Full",
|
||||
"100baseT/Half", "100baseT/Full",
|
||||
"1000baseT/Full",
|
||||
},
|
||||
}
|
||||
|
||||
eth, speedBPS := buildEthernetContainer(data)
|
||||
|
||||
if speedBPS != 1_000_000_000 {
|
||||
t.Fatalf("speed = %d, want 1000000000", speedBPS)
|
||||
}
|
||||
if eth["phy-type"] != "ieee802-ethernet-phy-type:phy-type-1000BASE-T" {
|
||||
t.Fatalf("phy-type = %v", eth["phy-type"])
|
||||
}
|
||||
if eth["pmd-type"] != "ieee802-ethernet-phy-type:pmd-type-1000BASE-T" {
|
||||
t.Fatalf("pmd-type = %v", eth["pmd-type"])
|
||||
}
|
||||
if eth["duplex"] != "full" {
|
||||
t.Fatalf("duplex = %v", eth["duplex"])
|
||||
}
|
||||
autoneg := eth["auto-negotiation"].(map[string]any)
|
||||
if autoneg["enable"] != true {
|
||||
t.Fatal("autoneg should be true")
|
||||
}
|
||||
// advertised == supported → no advertised-pmd-types key
|
||||
if _, ok := autoneg["infix-ethernet-interface:advertised-pmd-types"]; ok {
|
||||
t.Fatal("advertised-pmd-types should be suppressed when equal to supported")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildEthernetContainerFibre10G(t *testing.T) {
|
||||
data := ethtoolJSON{
|
||||
Speed: 10000,
|
||||
Duplex: "Full",
|
||||
Port: "FIBRE",
|
||||
AutoNegotiation: false,
|
||||
SupportedLinkModes: []string{"10000baseSR/Full"},
|
||||
AdvertisedLinkModes: []string{"10000baseSR/Full"},
|
||||
}
|
||||
|
||||
eth, speedBPS := buildEthernetContainer(data)
|
||||
|
||||
if speedBPS != 10_000_000_000 {
|
||||
t.Fatalf("speed = %d, want 10000000000", speedBPS)
|
||||
}
|
||||
// Fibre 10G → phy-type 10GBASE-R, no pmd-type from lookup table
|
||||
// But exactly one supported mode → pmd-type refined from supported list
|
||||
if eth["pmd-type"] != "ieee802-ethernet-phy-type:pmd-type-10GBASE-SR" {
|
||||
t.Fatalf("pmd-type = %v, want refined from single supported mode", eth["pmd-type"])
|
||||
}
|
||||
if eth["phy-type"] != "ieee802-ethernet-phy-type:phy-type-10GBASE-R" {
|
||||
t.Fatalf("phy-type = %v", eth["phy-type"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildEthernetContainerSpeedUnknown(t *testing.T) {
|
||||
data := ethtoolJSON{
|
||||
Speed: ethtoolSpeedUnknown,
|
||||
Duplex: "Unknown! (255)",
|
||||
Port: "Twisted Pair",
|
||||
AutoNegotiation: true,
|
||||
}
|
||||
|
||||
eth, speedBPS := buildEthernetContainer(data)
|
||||
|
||||
if speedBPS != 0 {
|
||||
t.Fatalf("speed = %d, want 0 for unknown", speedBPS)
|
||||
}
|
||||
if _, ok := eth["speed"]; ok {
|
||||
t.Fatal("speed should not be set when unknown")
|
||||
}
|
||||
if _, ok := eth["phy-type"]; ok {
|
||||
t.Fatal("phy-type should not be set when speed unknown")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildEthernetContainerAdvertisedDiffers(t *testing.T) {
|
||||
data := ethtoolJSON{
|
||||
Speed: 1000,
|
||||
Duplex: "Full",
|
||||
Port: "Twisted Pair",
|
||||
SupportedLinkModes: []string{
|
||||
"10baseT/Full", "100baseT/Full", "1000baseT/Full",
|
||||
},
|
||||
AdvertisedLinkModes: []string{"1000baseT/Full"},
|
||||
}
|
||||
|
||||
eth, _ := buildEthernetContainer(data)
|
||||
|
||||
autoneg := eth["auto-negotiation"].(map[string]any)
|
||||
adv, ok := autoneg["infix-ethernet-interface:advertised-pmd-types"]
|
||||
if !ok {
|
||||
t.Fatal("advertised-pmd-types should be present when != supported")
|
||||
}
|
||||
advList := adv.([]string)
|
||||
if len(advList) != 1 || advList[0] != "ieee802-ethernet-phy-type:pmd-type-1000BASE-T" {
|
||||
t.Fatalf("advertised = %v", advList)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEthtoolModesToPMD(t *testing.T) {
|
||||
modes := []string{
|
||||
"10baseT/Half", "10baseT/Full",
|
||||
"1000baseT/Full",
|
||||
"Autoneg", "TP",
|
||||
}
|
||||
got := ethtoolModesToPMD(modes)
|
||||
want := []string{
|
||||
"ieee802-ethernet-phy-type:pmd-type-10BASE-T",
|
||||
"ieee802-ethernet-phy-type:pmd-type-1000BASE-T",
|
||||
}
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("got %v, want %v", got, want)
|
||||
}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Fatalf("got[%d] = %q, want %q", i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Package frrvty is a minimal in-process client for an FRR daemon's vty
|
||||
// Unix socket.
|
||||
//
|
||||
// It speaks the same protocol vtysh uses, so yangerd can run "show ..."
|
||||
// commands (e.g. "show ip route json") against zebra without forking
|
||||
// vtysh. The command is written NUL-terminated; the daemon streams the
|
||||
// command output followed by a four-byte trailer of three NUL bytes and a
|
||||
// one-byte CLI return code (\0\0\0<ret>).
|
||||
package frrvty
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ZebraVtySocket is the default path to zebra's vty socket. It lives in
|
||||
// the same runstatedir as the zserv API socket.
|
||||
const ZebraVtySocket = "/var/run/frr/zebra.vty"
|
||||
|
||||
// Client runs commands against a single FRR daemon vty socket. A fresh
|
||||
// connection is opened per query, matching vtysh's behaviour.
|
||||
type Client struct {
|
||||
socket string
|
||||
}
|
||||
|
||||
// New returns a Client for the given vty socket path. An empty path
|
||||
// selects the zebra socket.
|
||||
func New(socket string) *Client {
|
||||
if socket == "" {
|
||||
socket = ZebraVtySocket
|
||||
}
|
||||
return &Client{socket: socket}
|
||||
}
|
||||
|
||||
// Query connects to the vty socket, runs one command, and returns its raw
|
||||
// output with the protocol trailer stripped. A non-zero CLI return code
|
||||
// is reported as an error (the partial output is still returned).
|
||||
func (c *Client) Query(ctx context.Context, command string) ([]byte, error) {
|
||||
var d net.Dialer
|
||||
conn, err := d.DialContext(ctx, "unix", c.socket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial %s: %w", c.socket, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
_ = conn.SetDeadline(deadline)
|
||||
}
|
||||
|
||||
// vtysh writes the command including its trailing NUL terminator.
|
||||
if _, err := conn.Write(append([]byte(command), 0)); err != nil {
|
||||
return nil, fmt.Errorf("write %q: %w", command, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
tmp := make([]byte, 4096)
|
||||
for {
|
||||
n, rerr := conn.Read(tmp)
|
||||
if n > 0 {
|
||||
buf.Write(tmp[:n])
|
||||
// The response ends with \0\0\0<ret>. The payload is
|
||||
// text/JSON and never contains NUL, so testing the last
|
||||
// four accumulated bytes is unambiguous.
|
||||
if b := buf.Bytes(); len(b) >= 4 &&
|
||||
b[len(b)-4] == 0 && b[len(b)-3] == 0 && b[len(b)-2] == 0 {
|
||||
payload := b[:len(b)-4]
|
||||
if ret := b[len(b)-1]; ret != 0 {
|
||||
return payload, fmt.Errorf("vty command %q: status %d", command, ret)
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
}
|
||||
if rerr != nil {
|
||||
if rerr == io.EOF {
|
||||
return nil, fmt.Errorf("vty command %q: closed before trailer", command)
|
||||
}
|
||||
return nil, fmt.Errorf("vty command %q: read: %w", command, rerr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package frrvty
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// fakeZebra serves a single vty connection: it reads the NUL-terminated
|
||||
// command, then writes the configured reply followed by the \0\0\0<ret>
|
||||
// trailer.
|
||||
func fakeZebra(t *testing.T, reply string, ret byte) string {
|
||||
t.Helper()
|
||||
|
||||
sock := filepath.Join(t.TempDir(), "zebra.vty")
|
||||
ln, err := net.Listen("unix", sock)
|
||||
if err != nil {
|
||||
t.Fatalf("listen: %v", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Read the NUL-terminated command.
|
||||
buf := make([]byte, 256)
|
||||
for {
|
||||
n, err := conn.Read(buf)
|
||||
if n > 0 && buf[n-1] == 0 {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
out := append([]byte(reply), 0, 0, 0, ret)
|
||||
_, _ = conn.Write(out)
|
||||
}()
|
||||
|
||||
t.Cleanup(func() {
|
||||
ln.Close()
|
||||
wg.Wait()
|
||||
})
|
||||
return sock
|
||||
}
|
||||
|
||||
func TestQueryStripsTrailer(t *testing.T) {
|
||||
sock := fakeZebra(t, `{"a":1}`, 0)
|
||||
c := New(sock)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
out, err := c.Query(ctx, "show ip route json")
|
||||
if err != nil {
|
||||
t.Fatalf("Query: %v", err)
|
||||
}
|
||||
if string(out) != `{"a":1}` {
|
||||
t.Errorf("output = %q, want %q", out, `{"a":1}`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryNonZeroStatus(t *testing.T) {
|
||||
sock := fakeZebra(t, "Unknown command", 1)
|
||||
c := New(sock)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
out, err := c.Query(ctx, "bogus")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for non-zero status")
|
||||
}
|
||||
if string(out) != "Unknown command" {
|
||||
t.Errorf("partial output = %q, want %q", out, "Unknown command")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryDialError(t *testing.T) {
|
||||
c := New(filepath.Join(t.TempDir(), "does-not-exist.vty"))
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if _, err := c.Query(ctx, "show ip route json"); err == nil {
|
||||
t.Fatal("expected dial error")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package iwmonitor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/kernelkit/infix/src/yangerd/internal/wpactrl"
|
||||
)
|
||||
|
||||
func TestFormatStations(t *testing.T) {
|
||||
m := &IWMonitor{}
|
||||
stas := []map[string]string{
|
||||
{
|
||||
"addr": "02:00:00:00:00:01",
|
||||
"signal": "-57",
|
||||
"connected_time": "120",
|
||||
"rx_packets": "1500",
|
||||
"tx_packets": "2500",
|
||||
"rx_bytes": "4825331939",
|
||||
"tx_bytes": "216392802676",
|
||||
"rx_rate_info": "1560 vhtmcs 8 vhtnss 2",
|
||||
"tx_rate_info": "1733 vhtmcs 9 vhtnss 2",
|
||||
},
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(m.formatStations(stas))
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
|
||||
var parsed struct {
|
||||
Station []struct {
|
||||
MAC string `json:"mac-address"`
|
||||
Signal int16 `json:"signal-strength"`
|
||||
ConnectedTime uint32 `json:"connected-time"`
|
||||
RxPackets string `json:"rx-packets"`
|
||||
TxPackets string `json:"tx-packets"`
|
||||
RxBytes string `json:"rx-bytes"`
|
||||
TxBytes string `json:"tx-bytes"`
|
||||
RxSpeed uint32 `json:"rx-speed"`
|
||||
TxSpeed uint32 `json:"tx-speed"`
|
||||
} `json:"station"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &parsed); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if len(parsed.Station) != 1 {
|
||||
t.Fatalf("got %d stations, want 1", len(parsed.Station))
|
||||
}
|
||||
|
||||
s := parsed.Station[0]
|
||||
if s.MAC != "02:00:00:00:00:01" {
|
||||
t.Errorf("mac-address = %q", s.MAC)
|
||||
}
|
||||
if s.Signal != -57 {
|
||||
t.Errorf("signal-strength = %d, want -57", s.Signal)
|
||||
}
|
||||
if s.ConnectedTime != 120 {
|
||||
t.Errorf("connected-time = %d, want 120", s.ConnectedTime)
|
||||
}
|
||||
if s.RxBytes != "4825331939" || s.TxBytes != "216392802676" {
|
||||
t.Errorf("bytes = %q/%q", s.RxBytes, s.TxBytes)
|
||||
}
|
||||
if s.RxPackets != "1500" || s.TxPackets != "2500" {
|
||||
t.Errorf("packets = %q/%q", s.RxPackets, s.TxPackets)
|
||||
}
|
||||
// hostapd rate info is already in 100kbps units
|
||||
if s.RxSpeed != 1560 || s.TxSpeed != 1733 {
|
||||
t.Errorf("speed = %d/%d, want 1560/1733", s.RxSpeed, s.TxSpeed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterAuthorized(t *testing.T) {
|
||||
stas := []map[string]string{
|
||||
{"addr": "02:00:00:00:00:01", "flags": "[AUTH][ASSOC][AUTHORIZED]"},
|
||||
{"addr": "02:00:00:00:00:02", "flags": "[AUTH][ASSOC]"}, // mid-handshake
|
||||
{"addr": "02:00:00:00:00:03", "flags": "[AUTH][ASSOC][AUTHORIZED][SHORT_PREAMBLE]"},
|
||||
}
|
||||
|
||||
out := filterAuthorized(stas)
|
||||
if len(out) != 2 {
|
||||
t.Fatalf("got %d stations, want 2", len(out))
|
||||
}
|
||||
if out[0]["addr"] != "02:00:00:00:00:01" || out[1]["addr"] != "02:00:00:00:00:03" {
|
||||
t.Errorf("addrs = %q, %q", out[0]["addr"], out[1]["addr"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveSSIDHostapd(t *testing.T) {
|
||||
// hostapd STATUS reports bss[N]=<ifname> / ssid[N]=<ssid> pairs;
|
||||
// multi-BSS setups must resolve by interface name.
|
||||
status := map[string]string{
|
||||
"state": "ENABLED",
|
||||
"bss[0]": "wlan0",
|
||||
"ssid[0]": "Lobby",
|
||||
"bss[1]": "wlan0_1",
|
||||
"ssid[1]": "Office",
|
||||
}
|
||||
|
||||
si := wpactrl.SocketInfo{Iface: "wlan0_1", Daemon: "hostapd"}
|
||||
if got := resolveSSID("wlan0_1", si, status); got != "Office" {
|
||||
t.Errorf("resolveSSID(wlan0_1) = %q, want Office", got)
|
||||
}
|
||||
si = wpactrl.SocketInfo{Iface: "wlan0", Daemon: "hostapd"}
|
||||
if got := resolveSSID("wlan0", si, status); got != "Lobby" {
|
||||
t.Errorf("resolveSSID(wlan0) = %q, want Lobby", got)
|
||||
}
|
||||
if got := resolveSSID("wlan9", si, status); got != "" {
|
||||
t.Errorf("resolveSSID(wlan9) = %q, want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBitrate(t *testing.T) {
|
||||
cases := map[string]uint32{
|
||||
"1560 vhtmcs 8 vhtnss 2": 1560, // hostapd: 100kbps units
|
||||
"866.7 MBit/s VHT-MCS 9": 8667, // iw: MBit/s -> 100kbps
|
||||
"54.0 MBit/s": 540,
|
||||
"": 0,
|
||||
"garbage rate": 0,
|
||||
}
|
||||
for in, want := range cases {
|
||||
if got := parseBitrate(in); got != want {
|
||||
t.Errorf("parseBitrate(%q) = %d, want %d", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package wpactrl
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// fakeHostapd serves the hostapd control protocol for station
|
||||
// enumeration: STA-FIRST returns the first station block, STA-NEXT <addr>
|
||||
// the one after it, and an empty datagram past the last station.
|
||||
func fakeHostapd(t *testing.T, stations []string) string {
|
||||
t.Helper()
|
||||
|
||||
dir := t.TempDir()
|
||||
serverPath := dir + "/wlan0"
|
||||
|
||||
serverAddr := &net.UnixAddr{Name: serverPath, Net: "unixgram"}
|
||||
server, err := net.ListenUnixgram("unixgram", serverAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("listen: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { server.Close() })
|
||||
|
||||
addrOf := func(block string) string {
|
||||
return strings.SplitN(block, "\n", 2)[0]
|
||||
}
|
||||
|
||||
go func() {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
n, raddr, err := server.ReadFromUnix(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cmd := string(buf[:n])
|
||||
|
||||
var resp string
|
||||
switch {
|
||||
case cmd == "STA-FIRST":
|
||||
if len(stations) > 0 {
|
||||
resp = stations[0]
|
||||
}
|
||||
case strings.HasPrefix(cmd, "STA-NEXT "):
|
||||
prev := strings.TrimPrefix(cmd, "STA-NEXT ")
|
||||
for i, st := range stations {
|
||||
if addrOf(st) == prev && i+1 < len(stations) {
|
||||
resp = stations[i+1]
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
resp = "UNKNOWN COMMAND\n"
|
||||
}
|
||||
server.WriteToUnix([]byte(resp), raddr)
|
||||
}
|
||||
}()
|
||||
|
||||
return serverPath
|
||||
}
|
||||
|
||||
func TestAllStations(t *testing.T) {
|
||||
sta1 := "02:00:00:00:00:01\nflags=[AUTH][ASSOC][AUTHORIZED]\n" +
|
||||
"signal=-57\nconnected_time=120\nrx_bytes=1000\ntx_bytes=2000\n"
|
||||
sta2 := "02:00:00:00:00:02\nflags=[AUTH][ASSOC][AUTHORIZED]\n" +
|
||||
"signal=-78\nconnected_time=60\nrx_bytes=300\ntx_bytes=400\n"
|
||||
|
||||
path := fakeHostapd(t, []string{sta1, sta2})
|
||||
|
||||
conn, err := DialTimeout(path, 2*time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("dial: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
stas, err := conn.AllStations()
|
||||
if err != nil {
|
||||
t.Fatalf("AllStations: %v", err)
|
||||
}
|
||||
if len(stas) != 2 {
|
||||
t.Fatalf("got %d stations, want 2", len(stas))
|
||||
}
|
||||
if stas[0]["addr"] != "02:00:00:00:00:01" || stas[1]["addr"] != "02:00:00:00:00:02" {
|
||||
t.Errorf("addrs = %q, %q", stas[0]["addr"], stas[1]["addr"])
|
||||
}
|
||||
if stas[0]["signal"] != "-57" {
|
||||
t.Errorf("sta[0] signal = %q", stas[0]["signal"])
|
||||
}
|
||||
if stas[1]["connected_time"] != "60" {
|
||||
t.Errorf("sta[1] connected_time = %q", stas[1]["connected_time"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllStationsNone(t *testing.T) {
|
||||
path := fakeHostapd(t, nil)
|
||||
|
||||
conn, err := DialTimeout(path, 2*time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("dial: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
stas, err := conn.AllStations()
|
||||
if err != nil {
|
||||
t.Fatalf("AllStations: %v", err)
|
||||
}
|
||||
if len(stas) != 0 {
|
||||
t.Fatalf("got %d stations, want 0", len(stas))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllStationsUnsupported(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
serverPath := dir + "/wlan0"
|
||||
|
||||
serverAddr := &net.UnixAddr{Name: serverPath, Net: "unixgram"}
|
||||
server, err := net.ListenUnixgram("unixgram", serverAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("listen: %v", err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
go func() {
|
||||
buf := make([]byte, 4096)
|
||||
n, raddr, err := server.ReadFromUnix(buf)
|
||||
if err != nil || n == 0 {
|
||||
return
|
||||
}
|
||||
server.WriteToUnix([]byte("UNKNOWN COMMAND\n"), raddr)
|
||||
}()
|
||||
|
||||
conn, err := DialTimeout(serverPath, 2*time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("dial: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if _, err := conn.AllStations(); err == nil {
|
||||
t.Fatal("expected error for UNKNOWN COMMAND")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user