mirror of https://bitbucket.org/ausocean/av.git
299 lines
8.0 KiB
Go
299 lines
8.0 KiB
Go
|
/*
|
||
|
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"
|
||
|
)
|
||
|
|
||
|
func init(){
|
||
|
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,
|
||
|
url: testURL,
|
||
|
status: statusActive,
|
||
|
rv: newRevidForTest((*logging.TestLogger)(t), testURL, t),
|
||
|
},
|
||
|
expect: []byte("{\"MAC\":\"" + testMAC + "\",\"URL\":\"" + testURL + "\",\"Status\":\"" + statusActive + "\"}"),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
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
|
||
|
expect Broadcast
|
||
|
}{
|
||
|
{
|
||
|
expect: Broadcast{
|
||
|
mac: testMAC,
|
||
|
url: testURL,
|
||
|
status: statusActive,
|
||
|
rv: newRevidForTest(logger, testURL, t),
|
||
|
},
|
||
|
in: []byte("{\"MAC\":\"" + testMAC + "\",\"URL\":\"" + testURL + "\",\"Status\":\"" + statusActive + "\"}"),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
if !broadcastsEqual(got, test.expect) {
|
||
|
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{
|
||
|
broadcasts: map[MAC]Broadcast{
|
||
|
testMAC: Broadcast{
|
||
|
testMAC,
|
||
|
testURL,
|
||
|
statusSlate,
|
||
|
newRevidForTest((*logging.TestLogger)(t), testURL, t),
|
||
|
},
|
||
|
},
|
||
|
slateExitSignals: newExitSignalsForTest(t, testMAC),
|
||
|
log: logger,
|
||
|
dogNotifier: newWatchdogNotifierForTest(t, logger),
|
||
|
},
|
||
|
expect: []byte("{\"Broadcasts\":{\"" + testMAC + "\":{\"MAC\":\"" + testMAC + "\",\"URL\":\"" + testURL + "\",\"Status\":\"" + statusSlate + "\"}},\"SlateExitSignals\":[\"" + testMAC + "\"]}"),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}{
|
||
|
{
|
||
|
in: []byte("{\"Broadcasts\":{\"" + testMAC + "\":{\"MAC\":\"" + testMAC + "\",\"URL\":\"" + testURL + "\",\"Status\":\"" + statusSlate + "\"}},\"SlateExitSignals\":[\"" + testMAC + "\"]}"),
|
||
|
expect: broadcastManager{
|
||
|
broadcasts: map[MAC]Broadcast{
|
||
|
testMAC: Broadcast{
|
||
|
testMAC,
|
||
|
testURL,
|
||
|
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
|
||
|
}
|
||
|
|
||
|
func broadcastMapsEqual(m1, m2 map[MAC]Broadcast) bool {
|
||
|
return mapsEqual(m1,m2,broadcastsEqual)
|
||
|
}
|
||
|
|
||
|
func slateExitSignalMapsEqual(m1, m2 map[MAC]chan struct{}) bool {
|
||
|
return mapsEqual(m1,m2,func(v1, v2 chan struct{}) bool {
|
||
|
return ((v1 == nil || v2 == nil ) && v1 == v2) || (v1 != nil && v2 != nil)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func activeHandlersMapEqual(m1, m2 map[int]handlerInfo) bool {
|
||
|
return mapsEqual(m1,m2, func(v1, v2 handlerInfo) bool { return v1.name == v2.name })
|
||
|
}
|
||
|
|
||
|
// 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]
|
||
|
if !ok || !cmp(v1,v2) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func watchdogNotifiersEqual(w1, w2 watchdogNotifier) bool {
|
||
|
if w1.watchdogInterval != w2.watchdogInterval ||
|
||
|
!activeHandlersMapEqual(w1.activeHandlers, w2.activeHandlers) {
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func broadcastsEqual(b1, b2 Broadcast) bool {
|
||
|
if b1.mac != b2.mac || b1.url != b2.url || b1.status != b2.status ||
|
||
|
((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 {
|
||
|
r, err := newRevid(log, url)
|
||
|
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
|
||
|
}
|