clone

a desktop tv and program guide for over-the-air broadcasts: what's on air, what your tuner gets, live tv, recordings

main
airwaves / internal / hdhr / find_test.go
find_test.go 4.2K
1package hdhr23import (4 "bytes"5 "fmt"6 "net"7 "testing"8)910func TestBroadcastAddr(t *testing.T) {11 for _, tc := range []struct {12 cidr string13 want string // "" for none14 }{15 {"169.254.93.183/16", "169.254.255.255"}, // a tuner's link-local port16 {"192.168.1.20/24", "192.168.1.255"},17 {"10.1.2.3/8", "10.255.255.255"},18 {"100.64.1.2/32", ""}, // a tailnet address19 {"10.0.0.1/31", ""},20 {"127.0.0.1/8", ""},21 {"fe80::1/64", ""},22 } {23 ip, n, err := net.ParseCIDR(tc.cidr)24 if err != nil {25 t.Fatal(err)26 }27 n.IP = ip28 got, ok := broadcastAddr(n)29 if tc.want == "" {30 if ok {31 t.Errorf("%s: broadcast %s, want none", tc.cidr, got)32 }33 continue34 }35 if !ok || got.String() != tc.want {36 t.Errorf("%s: broadcast %v %v, want %s", tc.cidr, got, ok, tc.want)37 }38 }39 // Interface addresses can come as 16-byte IPs with 16-byte masks.40 n := &net.IPNet{IP: net.ParseIP("192.168.1.20"), Mask: net.CIDRMask(120, 128)}41 if got, ok := broadcastAddr(n); !ok || got.String() != "192.168.1.255" {42 t.Errorf("16-byte form: %v %v", got, ok)43 }44 if targets := broadcastTargets(); len(targets) == 0 || !targets[0].Equal(net.IPv4bcast) {45 t.Errorf("targets %v, want 255.255.255.255 first", targets)46 }47}4849func TestDiscoveryClient(t *testing.T) {50 // The emulated device answers the search the client sends.51 if !parseRequest(searchPacket(), DeviceIDFor("nas")) {52 t.Error("the search isn't one a device answers")53 }5455 id := DeviceIDFor("tuner")56 from := net.ParseIP("169.254.88.116")57 f, ok := parseReply(reply(id, 2, "http://169.254.88.116"), from)58 if !ok || f.ID != id || f.Tuners != 2 || f.BaseURL != "http://169.254.88.116" {59 t.Errorf("reply: %+v %v", f, ok)60 }61 // A name the network may not resolve gives way to the address it62 // answered from; a port stays.63 f, _ = parseReply(reply(id, 4, "http://hdhr-10afffff.local"), from)64 if f.BaseURL != "http://169.254.88.116" {65 t.Errorf("named base URL: %q", f.BaseURL)66 }67 f, _ = parseReply(reply(id, 2, "http://airwaves.local:5004"), from)68 if f.BaseURL != "http://169.254.88.116:5004" {69 t.Errorf("named base URL with a port: %q", f.BaseURL)70 }7172 bad := reply(id, 2, "http://169.254.88.116")73 bad[len(bad)-1] ^= 0xFF74 if _, ok := parseReply(bad, from); ok {75 t.Error("a reply with a bad CRC was read")76 }77 if _, ok := parseReply(searchPacket(), from); ok {78 t.Error("a request was read as a reply")79 }80 // Other kinds of device (a record engine) aren't tuners.81 var b bytes.Buffer82 tlv(&b, tagDeviceType, u32(5))83 tlv(&b, tagDeviceID, u32(id))84 if _, ok := parseReply(packet(typeDiscoverRp, b.Bytes()), from); ok {85 t.Error("a storage device was taken for a tuner")86 }87}8889func TestLongTag(t *testing.T) {90 long := bytes.Repeat([]byte("x"), 300)91 var b bytes.Buffer92 tlv(&b, tagBaseURL, long)93 tlv(&b, tagTunerCount, []byte{4})94 var got [][]byte95 if !eachTag(b.Bytes(), func(_ byte, v []byte) { got = append(got, v) }) {96 t.Fatal("not well formed")97 }98 if len(got) != 2 || !bytes.Equal(got[0], long) || !bytes.Equal(got[1], []byte{4}) {99 t.Errorf("values %q", got)100 }101 if eachTag(b.Bytes()[:b.Len()-1], func(byte, []byte) {}) {102 t.Error("a truncated payload was well formed")103 }104}105106func TestDeviceIDOrAddress(t *testing.T) {107 id := DeviceIDFor("tuner")108 if got, ok := deviceID(fmt.Sprintf("%08X", id)); !ok || got != id {109 t.Errorf("device ID: %08X %v", got, ok)110 }111 for _, s := range []string{"", "nas.local", "192.168.1.30", fmt.Sprintf("%08X", id^1)} {112 if _, ok := deviceID(s); ok {113 t.Errorf("%q taken for a device ID", s)114 }115 }116}117118func TestURLs(t *testing.T) {119 for _, tc := range []struct{ in, api, stream string }{120 {"192.168.1.30", "http://192.168.1.30", "http://192.168.1.30:5004"},121 {"http://hdhomerun.local/", "http://hdhomerun.local", "http://hdhomerun.local:5004"},122 {" http://127.0.0.1:8080/discover.json", "http://127.0.0.1:8080", "http://127.0.0.1:8080"},123 {"[fe80::1]", "http://[fe80::1]", "http://[fe80::1]:5004"},124 // As a discovery reply gives it.125 {"http://169.254.1.2:80", "http://169.254.1.2", "http://169.254.1.2:5004"},126 } {127 api, err := apiURL(tc.in)128 if err != nil || api != tc.api || streamURL(api) != tc.stream {129 t.Errorf("%q: %q %q %v, want %q %q", tc.in, api, streamURL(api), err, tc.api, tc.stream)130 }131 }132 if _, err := apiURL(" "); err == nil {133 t.Error("an empty address was accepted")134 }135}