Wake: a target may list several broadcast addresses
Config keeps broadcast (one) and adds broadcasts (a list); exactly one must be present. Wake.Addresses() returns Broadcast then Broadcasts; checkWake errors on both-set, neither-or-empty, and non-host:port entries. Waker sends to every address in order, logging/past a failure so one dead address does not stop the others, and returns false only when none could be sent. Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package wake_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/crossbar/internal/wake"
|
||||
)
|
||||
|
||||
// listener returns a UDP socket on 127.0.0.1 and a channel that gets one value per datagram.
|
||||
func listener(t *testing.T) (string, <-chan []byte) {
|
||||
t.Helper()
|
||||
pc, err := net.ListenPacket("udp4", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { pc.Close() })
|
||||
got := make(chan []byte, 4)
|
||||
go func() {
|
||||
buf := make([]byte, 256)
|
||||
for {
|
||||
n, _, err := pc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
b := make([]byte, n)
|
||||
copy(b, buf[:n])
|
||||
got <- b
|
||||
}
|
||||
}()
|
||||
return pc.LocalAddr().String(), got
|
||||
}
|
||||
|
||||
func expectPacket(t *testing.T, name string, got <-chan []byte) {
|
||||
t.Helper()
|
||||
select {
|
||||
case b := <-got:
|
||||
if len(b) != 102 {
|
||||
t.Errorf("%s: got %d bytes, want a 102-byte magic packet", name, len(b))
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Errorf("%s: no packet within two seconds", name)
|
||||
}
|
||||
}
|
||||
|
||||
// A target may name several broadcast addresses (a host that roams between two networks): the
|
||||
// packet goes to every one of them, and one address that cannot be resolved does not stop the
|
||||
// others.
|
||||
func TestWakeSendsToEveryBroadcast(t *testing.T) {
|
||||
a, gotA := listener(t)
|
||||
b, gotB := listener(t)
|
||||
h := &fakeHealth{after: 1 << 30} // never healthy
|
||||
w := wake.New(map[string]wake.Target{"titan": {MAC: "aa:bb:cc:dd:ee:ff", Broadcasts: []string{a, "256.1.1.1:9", b}, Wait: 300 * time.Millisecond}}, h)
|
||||
w.PollEvery(20 * time.Millisecond)
|
||||
if w.Wake(t.Context(), "titan") {
|
||||
t.Errorf("Wake must report false when the host never comes up")
|
||||
}
|
||||
expectPacket(t, "first address", gotA)
|
||||
expectPacket(t, "third address, after an unresolvable second", gotB)
|
||||
}
|
||||
|
||||
// The single-address form keeps working, alone or together with the list.
|
||||
func TestWakeBroadcastAndBroadcastsCombine(t *testing.T) {
|
||||
a, gotA := listener(t)
|
||||
b, gotB := listener(t)
|
||||
h := &fakeHealth{after: 1 << 30} // never healthy
|
||||
w := wake.New(map[string]wake.Target{"titan": {MAC: "aa:bb:cc:dd:ee:ff", Broadcast: a, Broadcasts: []string{b}, Wait: 300 * time.Millisecond}}, h)
|
||||
w.PollEvery(20 * time.Millisecond)
|
||||
w.Wake(t.Context(), "titan")
|
||||
expectPacket(t, "Broadcast", gotA)
|
||||
expectPacket(t, "Broadcasts[0]", gotB)
|
||||
}
|
||||
Reference in New Issue
Block a user