597 lines
14 KiB
Go
597 lines
14 KiB
Go
package frame
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"bufio"
|
||
|
|
"bytes"
|
||
|
|
"fmt"
|
||
|
|
"image"
|
||
|
|
"net"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"gocv.io/x/gocv"
|
||
|
|
)
|
||
|
|
|
||
|
|
func ptr[T any](v T) *T { return &v }
|
||
|
|
|
||
|
|
func testFrame(t *testing.T, width, height int, matType gocv.MatType, fillVal float64) Frame {
|
||
|
|
t.Helper()
|
||
|
|
mat := gocv.NewMatWithSize(height, width, matType)
|
||
|
|
mat.SetTo(gocv.NewScalar(fillVal, fillVal, fillVal, 0))
|
||
|
|
return Frame{
|
||
|
|
PixelBytes: mat.ToBytes(),
|
||
|
|
Width: uint(mat.Cols()),
|
||
|
|
Height: uint(mat.Rows()),
|
||
|
|
GocvImageType: mat.Type(),
|
||
|
|
Channels: mat.Channels(),
|
||
|
|
Guid: []byte("test-guid"),
|
||
|
|
SourceData: "test--cam0",
|
||
|
|
Timestamp: uint64(time.Now().UnixNano()),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestToMat_Roundtrip(t *testing.T) {
|
||
|
|
mat := gocv.NewMatWithSize(4, 4, gocv.MatTypeCV8UC1)
|
||
|
|
mat.SetTo(gocv.NewScalar(128, 0, 0, 0))
|
||
|
|
original := mat.ToBytes()
|
||
|
|
|
||
|
|
f := Frame{
|
||
|
|
PixelBytes: original,
|
||
|
|
Width: 4,
|
||
|
|
Height: 4,
|
||
|
|
GocvImageType: gocv.MatTypeCV8UC1,
|
||
|
|
Channels: 1,
|
||
|
|
}
|
||
|
|
|
||
|
|
result, err := f.ToMat()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("ToMat() returned error: %v", err)
|
||
|
|
}
|
||
|
|
defer result.Close()
|
||
|
|
|
||
|
|
if result.Cols() != 4 || result.Rows() != 4 {
|
||
|
|
t.Errorf("got size %dx%d, want 4x4", result.Cols(), result.Rows())
|
||
|
|
}
|
||
|
|
|
||
|
|
got := result.ToBytes()
|
||
|
|
if !bytes.Equal(got, original) {
|
||
|
|
t.Errorf("pixel bytes differ after roundtrip")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestToMat_NonNilResult(t *testing.T) {
|
||
|
|
f := Frame{
|
||
|
|
PixelBytes: []byte{0, 1, 2, 3},
|
||
|
|
Width: 2,
|
||
|
|
Height: 2,
|
||
|
|
GocvImageType: gocv.MatTypeCV8UC1,
|
||
|
|
Channels: 1,
|
||
|
|
}
|
||
|
|
|
||
|
|
mat, err := f.ToMat()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("ToMat() error: %v", err)
|
||
|
|
}
|
||
|
|
defer mat.Close()
|
||
|
|
if mat.Empty() {
|
||
|
|
t.Error("expected non-empty Mat")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestToMat_EmptyBytes(t *testing.T) {
|
||
|
|
f := Frame{
|
||
|
|
PixelBytes: nil,
|
||
|
|
Width: 0,
|
||
|
|
Height: 0,
|
||
|
|
GocvImageType: gocv.MatTypeCV8UC1,
|
||
|
|
Channels: 1,
|
||
|
|
}
|
||
|
|
|
||
|
|
_, err := f.ToMat()
|
||
|
|
if err == nil {
|
||
|
|
t.Error("expected error for empty pixel data, got nil")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestEncryptDecrypt_Roundtrip(t *testing.T) {
|
||
|
|
original := []byte("these are my pixel bytes, there are many like them but these are mine")
|
||
|
|
f := Frame{
|
||
|
|
PixelBytes: original,
|
||
|
|
Width: 8,
|
||
|
|
Height: 2,
|
||
|
|
GocvImageType: gocv.MatTypeCV8UC3,
|
||
|
|
Channels: 3,
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := f.Encrypt("secret"); err != nil {
|
||
|
|
t.Fatalf("Encrypt() error: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if bytes.Equal(f.PixelBytes, original) {
|
||
|
|
t.Error("PixelBytes unchanged after encryption")
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := f.Decrypt("secret"); err != nil {
|
||
|
|
t.Fatalf("Decrypt() error: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if !bytes.Equal(f.PixelBytes, original) {
|
||
|
|
t.Error("PixelBytes differ after decrypt roundtrip")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestEncryptDecrypt_WrongPassphrase(t *testing.T) {
|
||
|
|
original := []byte("sensitive pixel data")
|
||
|
|
f := Frame{
|
||
|
|
PixelBytes: original,
|
||
|
|
Width: 4,
|
||
|
|
Height: 1,
|
||
|
|
GocvImageType: gocv.MatTypeCV8UC3,
|
||
|
|
Channels: 3,
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := f.Encrypt("correct-horse-battery-staple"); err != nil {
|
||
|
|
t.Fatalf("Encrypt() error: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := f.Decrypt("wrong-passphrase"); err == nil {
|
||
|
|
t.Error("expected error for wrong passphrase, got nil")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestEncryptDecrypt_EmptyBytes(t *testing.T) {
|
||
|
|
f := Frame{PixelBytes: nil}
|
||
|
|
if err := f.Encrypt("pass"); err != nil {
|
||
|
|
t.Errorf("Encrypt on empty bytes should succeed: %v", err)
|
||
|
|
}
|
||
|
|
if err := f.Decrypt("pass"); err != nil {
|
||
|
|
t.Errorf("Decrypt on empty bytes should succeed: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestDecrypt_TooShort(t *testing.T) {
|
||
|
|
f := Frame{PixelBytes: []byte{0, 1, 2}}
|
||
|
|
if err := f.Decrypt("pass"); err == nil {
|
||
|
|
t.Error("expected error for too-short data, got nil")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestEncrypt_UniqueSaltPerCall(t *testing.T) {
|
||
|
|
data := []byte("same data every time")
|
||
|
|
f1 := Frame{PixelBytes: bytes.Clone(data)}
|
||
|
|
f2 := Frame{PixelBytes: bytes.Clone(data)}
|
||
|
|
|
||
|
|
f1.Encrypt("pass")
|
||
|
|
f2.Encrypt("pass")
|
||
|
|
|
||
|
|
if bytes.Equal(f1.PixelBytes, f2.PixelBytes) {
|
||
|
|
t.Error("two encryptions of same data should differ (salt)")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func testClip() Clip {
|
||
|
|
return Clip{
|
||
|
|
PixelMats: [][]byte{{1, 2, 3}, {4, 5, 6}},
|
||
|
|
Width: 1,
|
||
|
|
Height: 1,
|
||
|
|
Types: gocv.MatTypeCV8UC3,
|
||
|
|
Channels: 3,
|
||
|
|
Guids: [][]byte{{0x01}, {0x02}},
|
||
|
|
Timestamps: []uint64{100, 200},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClipEncryptDecrypt_Roundtrip(t *testing.T) {
|
||
|
|
clp := testClip()
|
||
|
|
if err := clp.Encrypt("secret"); err != nil {
|
||
|
|
t.Fatalf("Encrypt() error: %v", err)
|
||
|
|
}
|
||
|
|
if len(clp.Salt) != saltSize {
|
||
|
|
t.Fatalf("clip salt is %d bytes, want %d", len(clp.Salt), saltSize)
|
||
|
|
}
|
||
|
|
for i, px := range clp.PixelMats {
|
||
|
|
if len(px) != 3+clipEncryptedOverhead {
|
||
|
|
t.Fatalf("frame %d is %d bytes, want %d encrypted", i, len(px), 3+clipEncryptedOverhead)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := clp.Decrypt("secret"); err != nil {
|
||
|
|
t.Fatalf("Decrypt() error: %v", err)
|
||
|
|
}
|
||
|
|
want := testClip()
|
||
|
|
for i := range clp.PixelMats {
|
||
|
|
if !bytes.Equal(clp.PixelMats[i], want.PixelMats[i]) {
|
||
|
|
t.Errorf("frame %d differs after clip decrypt roundtrip", i)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClipEncryptDecrypt_WrongPassphrase(t *testing.T) {
|
||
|
|
clp := testClip()
|
||
|
|
if err := clp.Encrypt("correct-horse"); err != nil {
|
||
|
|
t.Fatalf("Encrypt() error: %v", err)
|
||
|
|
}
|
||
|
|
if err := clp.Decrypt("wrong-passphrase"); err == nil {
|
||
|
|
t.Error("expected error for wrong passphrase, got nil")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClipDecrypt_LegacyPerFrameFallback(t *testing.T) {
|
||
|
|
want := testClip()
|
||
|
|
legacy := testClip()
|
||
|
|
for i := range legacy.PixelMats {
|
||
|
|
f := Frame{PixelBytes: legacy.PixelMats[i]}
|
||
|
|
if err := f.Encrypt("secret"); err != nil {
|
||
|
|
t.Fatalf("frame %d Encrypt() error: %v", i, err)
|
||
|
|
}
|
||
|
|
legacy.PixelMats[i] = f.PixelBytes
|
||
|
|
}
|
||
|
|
if len(legacy.Salt) != 0 {
|
||
|
|
t.Fatal("legacy clip must not carry a clip-level salt")
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := legacy.Decrypt("secret"); err != nil {
|
||
|
|
t.Fatalf("Decrypt() error: %v", err)
|
||
|
|
}
|
||
|
|
for i := range legacy.PixelMats {
|
||
|
|
if !bytes.Equal(legacy.PixelMats[i], want.PixelMats[i]) {
|
||
|
|
t.Errorf("frame %d differs after legacy per-frame decrypt", i)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClipEncrypt_RecordsIterationCount(t *testing.T) {
|
||
|
|
orig := DefaultPBKDF2Iter
|
||
|
|
defer SetDefaultPBKDF2Iter(orig)
|
||
|
|
|
||
|
|
SetDefaultPBKDF2Iter(12345)
|
||
|
|
clp := testClip()
|
||
|
|
if err := clp.Encrypt("secret"); err != nil {
|
||
|
|
t.Fatalf("Encrypt() error: %v", err)
|
||
|
|
}
|
||
|
|
if clp.PBKDF2Iter != 12345 {
|
||
|
|
t.Fatalf("clip records PBKDF2Iter=%d, want 12345", clp.PBKDF2Iter)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClipDecrypt_HonorsStoredIterationCount(t *testing.T) {
|
||
|
|
orig := DefaultPBKDF2Iter
|
||
|
|
defer SetDefaultPBKDF2Iter(orig)
|
||
|
|
|
||
|
|
SetDefaultPBKDF2Iter(12345)
|
||
|
|
want := testClip()
|
||
|
|
clp := testClip()
|
||
|
|
if err := clp.Encrypt("secret"); err != nil {
|
||
|
|
t.Fatalf("Encrypt() error: %v", err)
|
||
|
|
}
|
||
|
|
if clp.PBKDF2Iter != 12345 {
|
||
|
|
t.Fatalf("clip PBKDF2Iter=%d, want 12345", clp.PBKDF2Iter)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Decrypting node is configured with a different default; it must still
|
||
|
|
// use the count recorded on the clip.
|
||
|
|
SetDefaultPBKDF2Iter(600000)
|
||
|
|
if err := clp.Decrypt("secret"); err != nil {
|
||
|
|
t.Fatalf("Decrypt with mismatched default: %v", err)
|
||
|
|
}
|
||
|
|
for i := range clp.PixelMats {
|
||
|
|
if !bytes.Equal(clp.PixelMats[i], want.PixelMats[i]) {
|
||
|
|
t.Errorf("frame %d differs after decrypt honoring stored iterations", i)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClipDecrypt_TamperedIterationCountFails(t *testing.T) {
|
||
|
|
orig := DefaultPBKDF2Iter
|
||
|
|
defer SetDefaultPBKDF2Iter(orig)
|
||
|
|
|
||
|
|
clp := testClip()
|
||
|
|
if err := clp.Encrypt("secret"); err != nil {
|
||
|
|
t.Fatalf("Encrypt() error: %v", err)
|
||
|
|
}
|
||
|
|
// Changing the count invalidates the derived key.
|
||
|
|
clp.PBKDF2Iter = 600001
|
||
|
|
if err := clp.Decrypt("secret"); err == nil {
|
||
|
|
t.Error("expected decrypt error after tampering with PBKDF2Iter, got nil")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClipEncrypt_Empty(t *testing.T) {
|
||
|
|
clp := Clip{}
|
||
|
|
if err := clp.Encrypt("secret"); err != nil {
|
||
|
|
t.Errorf("Encrypt on empty clip should succeed: %v", err)
|
||
|
|
}
|
||
|
|
if len(clp.Salt) != 0 {
|
||
|
|
t.Error("empty clip should not be assigned a salt")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAuthHandshake_Match(t *testing.T) {
|
||
|
|
SetAuthToken("s3cret")
|
||
|
|
t.Cleanup(func() { SetAuthToken("") })
|
||
|
|
|
||
|
|
server, client := net.Pipe()
|
||
|
|
defer server.Close()
|
||
|
|
defer client.Close()
|
||
|
|
|
||
|
|
delivered := make(chan Clip, 1)
|
||
|
|
go HandleClipConn(server, NewMalformedAttempts(), func(clp Clip) {
|
||
|
|
delivered <- clp
|
||
|
|
})
|
||
|
|
|
||
|
|
if err := writeAuthToken(client); err != nil {
|
||
|
|
t.Fatalf("writeAuthToken: %v", err)
|
||
|
|
}
|
||
|
|
clp := validTestClip()
|
||
|
|
if _, err := writeWireClip(client, &clp); err != nil {
|
||
|
|
t.Fatalf("encode: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
select {
|
||
|
|
case <-delivered:
|
||
|
|
case <-time.After(3 * time.Second):
|
||
|
|
t.Fatal("timed out waiting for authenticated clip")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAuthHandshake_Mismatch(t *testing.T) {
|
||
|
|
SetAuthToken("s3cret")
|
||
|
|
t.Cleanup(func() { SetAuthToken("") })
|
||
|
|
|
||
|
|
server, client := net.Pipe()
|
||
|
|
defer server.Close()
|
||
|
|
defer client.Close()
|
||
|
|
|
||
|
|
done := make(chan struct{})
|
||
|
|
go func() {
|
||
|
|
HandleClipConn(server, NewMalformedAttempts(), func(Clip) {
|
||
|
|
t.Error("callback must not run for a bad token")
|
||
|
|
})
|
||
|
|
close(done)
|
||
|
|
}()
|
||
|
|
|
||
|
|
if _, err := fmt.Fprintf(client, "wrong-token\n"); err != nil {
|
||
|
|
t.Fatalf("write: %v", err)
|
||
|
|
}
|
||
|
|
client.Close()
|
||
|
|
|
||
|
|
select {
|
||
|
|
case <-done:
|
||
|
|
case <-time.After(3 * time.Second):
|
||
|
|
t.Fatal("HandleClipConn did not return for bad token")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAuthSend_EndToEnd(t *testing.T) {
|
||
|
|
SetAuthToken("s3cret")
|
||
|
|
t.Cleanup(func() { SetAuthToken("") })
|
||
|
|
|
||
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("listen: %v", err)
|
||
|
|
}
|
||
|
|
defer ln.Close()
|
||
|
|
|
||
|
|
received := make(chan Clip, 1)
|
||
|
|
go func() {
|
||
|
|
conn, err := ln.Accept()
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
HandleClipConn(conn, NewMalformedAttempts(), func(clp Clip) {
|
||
|
|
received <- clp
|
||
|
|
})
|
||
|
|
}()
|
||
|
|
|
||
|
|
clp := testClip()
|
||
|
|
if err := clp.Send(ln.Addr().String()); err != nil {
|
||
|
|
t.Fatalf("Send() error: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
select {
|
||
|
|
case got := <-received:
|
||
|
|
if !bytes.Equal(got.PixelMats[0], []byte{1, 2, 3}) {
|
||
|
|
t.Error("received wrong pixel data")
|
||
|
|
}
|
||
|
|
case <-time.After(3 * time.Second):
|
||
|
|
t.Fatal("timed out waiting for clip")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSublimate_Basic(t *testing.T) {
|
||
|
|
clp := Clip{
|
||
|
|
PixelMats: [][]byte{
|
||
|
|
{1, 2, 3},
|
||
|
|
{4, 5, 6},
|
||
|
|
},
|
||
|
|
Width: 1,
|
||
|
|
Height: 1,
|
||
|
|
Types: gocv.MatTypeCV8UC3,
|
||
|
|
Channels: 3,
|
||
|
|
Guids: [][]byte{{0x01}, {0x02}},
|
||
|
|
Timestamps: []uint64{100, 200},
|
||
|
|
SourceData: "test--cam0",
|
||
|
|
}
|
||
|
|
|
||
|
|
frames := clp.Sublimate()
|
||
|
|
if len(frames) != 2 {
|
||
|
|
t.Fatalf("got %d frames, want 2", len(frames))
|
||
|
|
}
|
||
|
|
|
||
|
|
for i, f := range frames {
|
||
|
|
if f.Width != 1 || f.Height != 1 {
|
||
|
|
t.Errorf("frame %d: got %dx%d, want 1x1", i, f.Width, f.Height)
|
||
|
|
}
|
||
|
|
if f.Channels != 3 {
|
||
|
|
t.Errorf("frame %d: got %d channels, want 3", i, f.Channels)
|
||
|
|
}
|
||
|
|
if f.Guid[0] != byte(i+1) {
|
||
|
|
t.Errorf("frame %d: guid = %x, want %x", i, f.Guid[0], byte(i+1))
|
||
|
|
}
|
||
|
|
if f.Timestamp != uint64(100*(i+1)) {
|
||
|
|
t.Errorf("frame %d: timestamp = %d, want %d", i, f.Timestamp, uint64(100*(i+1)))
|
||
|
|
}
|
||
|
|
if f.SourceData != "test--cam0" {
|
||
|
|
t.Errorf("frame %d: sourceData = %q", i, f.SourceData)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSublimate_EmptyClip(t *testing.T) {
|
||
|
|
clp := Clip{}
|
||
|
|
frames := clp.Sublimate()
|
||
|
|
if len(frames) != 0 {
|
||
|
|
t.Errorf("got %d frames, want 0", len(frames))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSublimate_DetectionsCarryOver(t *testing.T) {
|
||
|
|
dets := []map[string][]Detection{
|
||
|
|
{"human": {{DetectionTitle: "human", DetectionRegion: image.Rect(0, 0, 10, 10)}}},
|
||
|
|
nil,
|
||
|
|
}
|
||
|
|
|
||
|
|
clp := Clip{
|
||
|
|
PixelMats: [][]byte{{0}, {0}},
|
||
|
|
Guids: [][]byte{{1}, {2}},
|
||
|
|
Timestamps: []uint64{0, 0},
|
||
|
|
Detections: dets,
|
||
|
|
}
|
||
|
|
|
||
|
|
frames := clp.Sublimate()
|
||
|
|
if len(frames[0].Detections["human"]) != 1 {
|
||
|
|
t.Error("detections not carried over to frame 0")
|
||
|
|
}
|
||
|
|
if frames[1].Detections != nil {
|
||
|
|
t.Error("expected nil detections for frame 1")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestCheckLenCorrelations_Match(t *testing.T) {
|
||
|
|
clp := Clip{
|
||
|
|
PixelMats: [][]byte{{0}, {0}, {0}},
|
||
|
|
Guids: [][]byte{{1}, {2}, {3}},
|
||
|
|
Timestamps: []uint64{10, 20, 30},
|
||
|
|
}
|
||
|
|
ok, err := clp.CheckLenCorrelations()
|
||
|
|
if !ok {
|
||
|
|
t.Errorf("expected match, got error: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestCheckLenCorrelations_PixelMatsGuidsMismatch(t *testing.T) {
|
||
|
|
clp := Clip{
|
||
|
|
PixelMats: [][]byte{{0}, {0}},
|
||
|
|
Guids: [][]byte{{1}},
|
||
|
|
Timestamps: []uint64{10, 20},
|
||
|
|
}
|
||
|
|
ok, _ := clp.CheckLenCorrelations()
|
||
|
|
if ok {
|
||
|
|
t.Error("expected mismatch")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestCheckLenCorrelations_TimestampsMismatch(t *testing.T) {
|
||
|
|
clp := Clip{
|
||
|
|
PixelMats: [][]byte{{0}, {0}},
|
||
|
|
Guids: [][]byte{{1}, {2}},
|
||
|
|
Timestamps: []uint64{10},
|
||
|
|
}
|
||
|
|
ok, _ := clp.CheckLenCorrelations()
|
||
|
|
if ok {
|
||
|
|
t.Error("expected mismatch")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestCheckLenCorrelations_Empty(t *testing.T) {
|
||
|
|
clp := Clip{}
|
||
|
|
ok, err := clp.CheckLenCorrelations()
|
||
|
|
if !ok {
|
||
|
|
t.Errorf("empty clip should match: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClipSend(t *testing.T) {
|
||
|
|
SetAuthToken("test-token")
|
||
|
|
t.Cleanup(func() { SetAuthToken("") })
|
||
|
|
|
||
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("listen: %v", err)
|
||
|
|
}
|
||
|
|
defer ln.Close()
|
||
|
|
|
||
|
|
addr := ln.Addr().String()
|
||
|
|
|
||
|
|
received := make(chan Clip, 1)
|
||
|
|
go func() {
|
||
|
|
conn, err := ln.Accept()
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
defer conn.Close()
|
||
|
|
br := bufio.NewReader(conn)
|
||
|
|
line, err := br.ReadString('\n')
|
||
|
|
if err != nil || strings.TrimSpace(line) != "test-token" {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
clp, err := DecodeWireClip(br)
|
||
|
|
if err == nil {
|
||
|
|
received <- clp
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
sent := Clip{
|
||
|
|
PixelMats: [][]byte{{1, 2, 3}},
|
||
|
|
Width: 1,
|
||
|
|
Height: 1,
|
||
|
|
Types: gocv.MatTypeCV8UC3,
|
||
|
|
Channels: 3,
|
||
|
|
Guids: [][]byte{{42}},
|
||
|
|
Timestamps: []uint64{99},
|
||
|
|
SourceData: "test--cam0",
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := sent.Send(addr); err != nil {
|
||
|
|
t.Fatalf("Send() error: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
select {
|
||
|
|
case got := <-received:
|
||
|
|
if !bytes.Equal(got.PixelMats[0], []byte{1, 2, 3}) {
|
||
|
|
t.Error("received wrong pixel data")
|
||
|
|
}
|
||
|
|
if got.Guids[0][0] != 42 {
|
||
|
|
t.Error("received wrong guid")
|
||
|
|
}
|
||
|
|
if got.Timestamps[0] != 99 {
|
||
|
|
t.Error("received wrong timestamp")
|
||
|
|
}
|
||
|
|
case <-time.After(3 * time.Second):
|
||
|
|
t.Fatal("timed out waiting for clip")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClipSend_InvalidAddress(t *testing.T) {
|
||
|
|
clp := Clip{}
|
||
|
|
err := clp.Send("256.0.0.1:1")
|
||
|
|
if err == nil {
|
||
|
|
t.Error("expected error for unreachable address, got nil")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFrameFields_ZeroValues(t *testing.T) {
|
||
|
|
f := Frame{}
|
||
|
|
if f.Detections != nil {
|
||
|
|
t.Error("expected nil Detections for zero-value Frame")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClipFields_ZeroValues(t *testing.T) {
|
||
|
|
c := Clip{}
|
||
|
|
if c.Comparisons != nil {
|
||
|
|
t.Error("expected nil Comparisons for zero-value Clip")
|
||
|
|
}
|
||
|
|
}
|