2023-01-11 01:31:38 +03:00
|
|
|
/*
|
|
|
|
DESCRIPTION
|
|
|
|
file_test.go provides testing for functionality contained in file.go.
|
|
|
|
|
|
|
|
AUTHORS
|
|
|
|
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
|
|
|
|
|
|
|
LICENSE
|
|
|
|
Copyright (C) 2022-2023 the Australian Ocean Lab (AusOcean)
|
|
|
|
|
|
|
|
It is free software: you can redistribute it and/or modify them
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
option) any later version.
|
|
|
|
|
|
|
|
It is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with revid in gpl.txt. If not, see http://www.gnu.org/licenses.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"bitbucket.org/ausocean/av/cmd/vidforward/global"
|
|
|
|
"bitbucket.org/ausocean/av/revid"
|
|
|
|
"bitbucket.org/ausocean/av/revid/config"
|
|
|
|
"bitbucket.org/ausocean/utils/logging"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
testURL = "rtmp://some-random-url.abcdef-12345"
|
|
|
|
testMAC = "78:90:AE:7B:2C:76"
|
|
|
|
)
|
|
|
|
|
2023-02-08 08:49:05 +03:00
|
|
|
func init() {
|
2023-01-11 01:31:38 +03:00
|
|
|
inTest = true
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBroadcastMarshal(t *testing.T) {
|
|
|
|
logger := (*logging.TestLogger)(t)
|
|
|
|
|
|
|
|
// Marshalling functionality uses this.
|
|
|
|
global.SetLogger(logger)
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
in Broadcast
|
|
|
|
expect []byte
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
in: Broadcast{
|
|
|
|
mac: testMAC,
|
2023-03-05 01:09:39 +03:00
|
|
|
urls: []string{testURL},
|
2023-01-11 01:31:38 +03:00
|
|
|
status: statusActive,
|
|
|
|
rv: newRevidForTest((*logging.TestLogger)(t), testURL, t),
|
|
|
|
},
|
2023-03-05 01:09:39 +03:00
|
|
|
expect: []byte("{\"MAC\":\"" + testMAC + "\",\"URLs\":[\"" + testURL + "\"],\"Status\":\"" + statusActive + "\"}"),
|
2023-01-11 01:31:38 +03:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
got, err := test.in.MarshalJSON()
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("could not marshal json for test no. %d: %v", i, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !bytes.Equal(got, test.expect) {
|
|
|
|
t.Errorf("did not get expected result.\nGot: %v\nWnt: %v\n", string(got), string(test.expect))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBroadcastUnmarshal(t *testing.T) {
|
|
|
|
logger := (*logging.TestLogger)(t)
|
|
|
|
|
|
|
|
// Marshalling functionality uses this.
|
|
|
|
global.SetLogger(logger)
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
in []byte
|
2023-06-15 00:09:31 +03:00
|
|
|
expect *Broadcast
|
2023-01-11 01:31:38 +03:00
|
|
|
}{
|
|
|
|
{
|
2023-06-15 00:09:31 +03:00
|
|
|
expect: &Broadcast{
|
2023-01-11 01:31:38 +03:00
|
|
|
mac: testMAC,
|
2023-03-05 01:09:39 +03:00
|
|
|
urls: []string{testURL},
|
2023-01-11 01:31:38 +03:00
|
|
|
status: statusActive,
|
|
|
|
rv: newRevidForTest(logger, testURL, t),
|
|
|
|
},
|
2023-03-05 01:09:39 +03:00
|
|
|
in: []byte("{\"MAC\":\"" + testMAC + "\",\"URLs\":[\"" + testURL + "\"],\"Status\":\"" + statusActive + "\"}"),
|
2023-01-11 01:31:38 +03:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
var got Broadcast
|
|
|
|
err := got.UnmarshalJSON(test.in)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("could not marshal json for test no. %d: %v", i, err)
|
|
|
|
continue
|
|
|
|
}
|
2023-06-15 00:09:31 +03:00
|
|
|
if !broadcastsEqual(&got, test.expect) {
|
2023-01-11 01:31:38 +03:00
|
|
|
t.Errorf("did not get expected result.\nGot: %v\nWnt: %v\n", got, test.expect)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBroadcastManagerMarshal(t *testing.T) {
|
|
|
|
logger := (*logging.TestLogger)(t)
|
|
|
|
|
|
|
|
// Marshalling functionality uses this.
|
|
|
|
global.SetLogger(logger)
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
in broadcastManager
|
|
|
|
expect []byte
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
in: broadcastManager{
|
2023-06-15 00:09:31 +03:00
|
|
|
broadcasts: map[MAC]*Broadcast{
|
|
|
|
testMAC: &Broadcast{
|
2023-01-11 01:31:38 +03:00
|
|
|
testMAC,
|
2023-03-05 01:09:39 +03:00
|
|
|
[]string{testURL},
|
2023-01-11 01:31:38 +03:00
|
|
|
statusSlate,
|
|
|
|
newRevidForTest((*logging.TestLogger)(t), testURL, t),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
slateExitSignals: newExitSignalsForTest(t, testMAC),
|
|
|
|
log: logger,
|
|
|
|
dogNotifier: newWatchdogNotifierForTest(t, logger),
|
|
|
|
},
|
2023-03-05 01:09:39 +03:00
|
|
|
expect: []byte("{\"Broadcasts\":{\"" + testMAC + "\":{\"MAC\":\"" + testMAC + "\",\"URLs\":[\"" + testURL + "\"],\"Status\":\"" + statusSlate + "\"}},\"SlateExitSignals\":[\"" + testMAC + "\"]}"),
|
2023-01-11 01:31:38 +03:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
got, err := test.in.MarshalJSON()
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("could not marshal json for test no. %d: %v", i, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !bytes.Equal(got, test.expect) {
|
|
|
|
t.Errorf("did not get expected result.\nGot: %v\nWnt: %v\n", string(got), string(test.expect))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBroadcastManagerUnmarshal(t *testing.T) {
|
|
|
|
logger := (*logging.TestLogger)(t)
|
|
|
|
|
|
|
|
// Marshalling functionality uses this.
|
|
|
|
global.SetLogger(logger)
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
in []byte
|
|
|
|
expect broadcastManager
|
|
|
|
}{
|
|
|
|
{
|
2023-03-05 01:09:39 +03:00
|
|
|
in: []byte("{\"Broadcasts\":{\"" + testMAC + "\":{\"MAC\":\"" + testMAC + "\",\"URLs\":[\"" + testURL + "\"],\"Status\":\"" + statusSlate + "\"}},\"SlateExitSignals\":[\"" + testMAC + "\"]}"),
|
2023-01-11 01:31:38 +03:00
|
|
|
expect: broadcastManager{
|
2023-06-15 00:09:31 +03:00
|
|
|
broadcasts: map[MAC]*Broadcast{
|
|
|
|
testMAC: &Broadcast{
|
2023-01-11 01:31:38 +03:00
|
|
|
testMAC,
|
2023-03-05 01:09:39 +03:00
|
|
|
[]string{testURL},
|
2023-01-11 01:31:38 +03:00
|
|
|
statusSlate,
|
|
|
|
newRevidForTest((*logging.TestLogger)(t), testURL, t),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
slateExitSignals: newExitSignalsForTest(t, testMAC),
|
|
|
|
log: logger,
|
|
|
|
dogNotifier: newWatchdogNotifierForTest(t, logger),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
var got broadcastManager
|
|
|
|
if err := json.Unmarshal(test.in, &got); err != nil {
|
|
|
|
t.Errorf("could not unmarshal json for test no. %d: %v", i, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !broadcastManagersEqual(got, test.expect) {
|
|
|
|
t.Errorf("did not get expected result.\nGot: %+v\nWnt: %+v\n", got, test.expect)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func broadcastManagersEqual(m1, m2 broadcastManager) bool {
|
|
|
|
if !broadcastMapsEqual(m1.broadcasts, m2.broadcasts) ||
|
|
|
|
!slateExitSignalMapsEqual(m1.slateExitSignals, m2.slateExitSignals) ||
|
|
|
|
!watchdogNotifiersEqual(*m1.dogNotifier, *m2.dogNotifier) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2023-06-15 00:09:31 +03:00
|
|
|
func broadcastMapsEqual(m1, m2 map[MAC]*Broadcast) bool {
|
2023-02-08 08:49:05 +03:00
|
|
|
return mapsEqual(m1, m2, broadcastsEqual)
|
2023-01-11 01:31:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func slateExitSignalMapsEqual(m1, m2 map[MAC]chan struct{}) bool {
|
2023-02-08 08:49:05 +03:00
|
|
|
return mapsEqual(m1, m2, func(v1, v2 chan struct{}) bool {
|
|
|
|
return ((v1 == nil || v2 == nil) && v1 == v2) || (v1 != nil && v2 != nil)
|
2023-01-11 01:31:38 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func activeHandlersMapEqual(m1, m2 map[int]handlerInfo) bool {
|
2023-02-08 08:49:05 +03:00
|
|
|
return mapsEqual(m1, m2, func(v1, v2 handlerInfo) bool { return v1.name == v2.name })
|
2023-01-11 01:31:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// mapsEqual is a generic function to check that any two maps are equal based on
|
|
|
|
// the provided value compare function cmp.
|
|
|
|
func mapsEqual[K comparable, V any](m1, m2 map[K]V, cmp func(v1, v2 V) bool) bool {
|
|
|
|
if len(m1) != len(m2) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for k, v1 := range m1 {
|
|
|
|
v2, ok := m2[k]
|
2023-02-08 08:49:05 +03:00
|
|
|
if !ok || !cmp(v1, v2) {
|
2023-01-11 01:31:38 +03:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func watchdogNotifiersEqual(w1, w2 watchdogNotifier) bool {
|
|
|
|
if w1.watchdogInterval != w2.watchdogInterval ||
|
|
|
|
!activeHandlersMapEqual(w1.activeHandlers, w2.activeHandlers) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2023-06-15 00:09:31 +03:00
|
|
|
func broadcastsEqual(b1, b2 *Broadcast) bool {
|
|
|
|
if b1 == nil {
|
|
|
|
panic("b1 is nil")
|
|
|
|
}
|
|
|
|
if b2 == nil {
|
|
|
|
panic("b2 is nil")
|
|
|
|
}
|
2023-03-05 01:09:39 +03:00
|
|
|
if b1.mac != b2.mac || !reflect.DeepEqual(b1.urls, b2.urls) || b1.status != b2.status ||
|
2023-01-11 01:31:38 +03:00
|
|
|
((b1.rv == nil || b2.rv == nil) && b1.rv != b2.rv) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if b1.rv != nil && !configsEqual(b1.rv.Config(), b2.rv.Config()) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// configsEqual returns true if the provided config.Config values are equal. The
|
|
|
|
// comparison is shallow given that only fields of basic types are compared, not
|
|
|
|
// structs or interfaces.
|
|
|
|
func configsEqual(cfg1, cfg2 config.Config) bool {
|
|
|
|
cfg1ValOf := reflect.ValueOf(cfg1)
|
|
|
|
cfg2ValOf := reflect.ValueOf(cfg2)
|
|
|
|
for i := 0; i < cfg1ValOf.NumField(); i++ {
|
|
|
|
if cfg1ValOf.Field(i).Kind() == reflect.Struct || cfg1ValOf.Field(i).Kind() == reflect.Interface {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(cfg1ValOf.Field(i).Interface(), cfg2ValOf.Field(i).Interface()) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// newRevidForTest allows us to create revid in table driven test entry.
|
|
|
|
func newRevidForTest(log logging.Logger, url string, t *testing.T) *revid.Revid {
|
2023-03-05 01:09:39 +03:00
|
|
|
r, err := newRevid(log, []string{url})
|
2023-01-11 01:31:38 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("could not create revid pipeline: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
// newExitSignalsForTest creates a map of chan struct{} for the provided MACs.
|
|
|
|
// This is used to populate the slateExitSignals field in the broadcastManager.
|
|
|
|
func newExitSignalsForTest(t *testing.T, macs ...MAC) map[MAC]chan struct{} {
|
|
|
|
sigMap := make(map[MAC]chan struct{})
|
|
|
|
for _, m := range macs {
|
|
|
|
sigMap[m] = make(chan struct{})
|
|
|
|
}
|
|
|
|
return sigMap
|
|
|
|
}
|
|
|
|
|
|
|
|
// newWatchdogNotifierForTest allows us to create watchdog notifier in test table.
|
|
|
|
func newWatchdogNotifierForTest(t *testing.T, l logging.Logger) *watchdogNotifier {
|
|
|
|
n, err := newWatchdogNotifier(l, func() {})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("could not create new watchdog notifier: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|