mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-16 20:20:07 +00:00
A short topic pubkey panicked a lane worker and killed the process; keys must now be 32 bytes and lane workers recover. Also reject non-finite radio values before airtime, strip NULs from advert names and neighbour scopes, guard the MQTT client pointer, and never stream a broker copy of a hearing as a repeat.
127 lines
3.1 KiB
Go
127 lines
3.1 KiB
Go
// Copyright 2026 Beacon Contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package lora
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
)
|
|
|
|
func TestPreambleSymbols(t *testing.T) {
|
|
cases := []struct {
|
|
sf int
|
|
want int
|
|
}{
|
|
{7, 32},
|
|
{8, 32},
|
|
{9, 16},
|
|
{12, 16},
|
|
}
|
|
for _, tc := range cases {
|
|
if got := PreambleSymbols(tc.sf); got != tc.want {
|
|
t.Errorf("PreambleSymbols(%d) = %d, want %d", tc.sf, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFrameLength(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
transportCodes bool
|
|
pathLen int
|
|
payloadLen int
|
|
want int
|
|
}{
|
|
{"no transport codes, no path", false, 0, 10, 12},
|
|
{"transport codes with path", true, 3, 10, 19},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := FrameLength(tc.transportCodes, tc.pathLen, tc.payloadLen); got != tc.want {
|
|
t.Errorf("FrameLength(%v, %d, %d) = %d, want %d", tc.transportCodes, tc.pathLen, tc.payloadLen, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSymbols(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
frameLen int
|
|
sf int
|
|
cr int
|
|
de int
|
|
want int
|
|
}{
|
|
{"sf7 no ldro", 50, 7, 5, 0, 83},
|
|
{"sf10 ldro", 50, 10, 5, 1, 73},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := symbols(tc.frameLen, tc.sf, tc.cr, tc.de); got != tc.want {
|
|
t.Errorf("symbols(%d, %d, %d, %d) = %d, want %d", tc.frameLen, tc.sf, tc.cr, tc.de, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTimeOnAirMs(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
frameLen int
|
|
sf int
|
|
bwKHz float64
|
|
cr int
|
|
want float64
|
|
}{
|
|
{"sf7 bw62.5", 50, 7, 62.5, 5, 244.224},
|
|
{"sf10 bw62.5 ldro", 50, 10, 62.5, 5, 1527.808},
|
|
{"sf7 bw250", 50, 7, 250, 5, 61.056},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, ok := TimeOnAirMs(tc.frameLen, tc.sf, tc.bwKHz, tc.cr)
|
|
if !ok {
|
|
t.Fatalf("TimeOnAirMs(%d, %d, %v, %d) not costable, want ok", tc.frameLen, tc.sf, tc.bwKHz, tc.cr)
|
|
}
|
|
if math.Abs(got-tc.want) > 0.001 {
|
|
t.Errorf("TimeOnAirMs(%d, %d, %v, %d) = %v, want %v", tc.frameLen, tc.sf, tc.bwKHz, tc.cr, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTimeOnAirMsNotCostable(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
frameLen int
|
|
sf int
|
|
bwKHz float64
|
|
cr int
|
|
}{
|
|
{"sf zero", 50, 0, 62.5, 5},
|
|
{"sf below range", 50, 6, 62.5, 5},
|
|
{"sf above range", 50, 13, 62.5, 5},
|
|
{"bw zero", 50, 7, 0, 5},
|
|
{"bw NaN", 50, 7, math.NaN(), 5},
|
|
{"bw infinite", 50, 7, math.Inf(1), 5},
|
|
{"bw denormal", 50, 12, 1e-40, 5},
|
|
{"bw above range", 50, 7, 1e9, 5},
|
|
{"cr below range", 50, 7, 62.5, 4},
|
|
{"cr above range", 50, 7, 62.5, 9},
|
|
{"empty frame", 0, 7, 62.5, 5},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got, ok := TimeOnAirMs(tc.frameLen, tc.sf, tc.bwKHz, tc.cr)
|
|
if ok {
|
|
t.Errorf("TimeOnAirMs(%d, %d, %v, %d) = %v, ok; want not costable", tc.frameLen, tc.sf, tc.bwKHz, tc.cr, got)
|
|
}
|
|
if got != 0 {
|
|
t.Errorf("TimeOnAirMs(%d, %d, %v, %d) = %v, want 0 ms when not costable", tc.frameLen, tc.sf, tc.bwKHz, tc.cr, got)
|
|
}
|
|
})
|
|
}
|
|
}
|