test(02-03): add failing tests for MP3 encoder and RunSynthesis
- TestMP3Valid: synthetic snapshots through full pipeline, ffprobe validation - TestZeroPacketError: empty slice and zero-count snapshots return error, no file created - TestEncodeMP3DirectBytes: direct frame encoding validated by ffprobe
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
package encode
|
||||
|
||||
import (
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/netsynth/netsynth/classify"
|
||||
"github.com/netsynth/netsynth/synth"
|
||||
)
|
||||
|
||||
// TestMP3Valid synthesizes from 3 synthetic WindowSnapshots and validates the
|
||||
// output file is a valid MP3 using ffprobe.
|
||||
func TestMP3Valid(t *testing.T) {
|
||||
// Check ffprobe is available
|
||||
if _, err := exec.LookPath("ffprobe"); err != nil {
|
||||
t.Skip("ffprobe not found in PATH; skipping MP3 validation test")
|
||||
}
|
||||
|
||||
snaps := []classify.WindowSnapshot{
|
||||
{
|
||||
Counts: map[classify.TrafficClass]int64{
|
||||
classify.ClassICMP: 50,
|
||||
classify.ClassDNS: 30,
|
||||
},
|
||||
TotalPackets: 80,
|
||||
WindowIndex: 0,
|
||||
},
|
||||
{
|
||||
Counts: map[classify.TrafficClass]int64{
|
||||
classify.ClassHTTPS: 200,
|
||||
},
|
||||
TotalPackets: 200,
|
||||
WindowIndex: 1,
|
||||
},
|
||||
{
|
||||
Counts: map[classify.TrafficClass]int64{
|
||||
classify.ClassSSH: 10,
|
||||
classify.ClassHTTP: 80,
|
||||
},
|
||||
TotalPackets: 90,
|
||||
WindowIndex: 2,
|
||||
},
|
||||
}
|
||||
|
||||
tmpFile, err := os.CreateTemp("", "netsynth-test-*.mp3")
|
||||
if err != nil {
|
||||
t.Fatalf("create temp file: %v", err)
|
||||
}
|
||||
tmpPath := tmpFile.Name()
|
||||
tmpFile.Close()
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
if err := RunSynthesis(snaps, tmpPath); err != nil {
|
||||
t.Fatalf("RunSynthesis: %v", err)
|
||||
}
|
||||
|
||||
// Verify file exists and is non-empty
|
||||
info, err := os.Stat(tmpPath)
|
||||
if err != nil {
|
||||
t.Fatalf("stat output file: %v", err)
|
||||
}
|
||||
if info.Size() == 0 {
|
||||
t.Fatal("output file is empty")
|
||||
}
|
||||
|
||||
// Validate with ffprobe
|
||||
out, err := exec.Command("ffprobe",
|
||||
"-v", "error",
|
||||
"-show_entries", "format=format_name,duration,nb_streams",
|
||||
"-of", "csv=p=0",
|
||||
tmpPath,
|
||||
).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("ffprobe error: %v\noutput: %s", err, out)
|
||||
}
|
||||
|
||||
outStr := string(out)
|
||||
if !strings.Contains(outStr, "mp3") {
|
||||
t.Errorf("expected format_name to contain 'mp3', got: %q", outStr)
|
||||
}
|
||||
|
||||
// ffprobe csv=p=0 format: format_name,duration,nb_streams
|
||||
// Duration should be > 0; nb_streams should be 1
|
||||
// We check that the output is non-empty and contains expected fields
|
||||
fields := strings.Split(strings.TrimSpace(outStr), ",")
|
||||
if len(fields) < 3 {
|
||||
t.Errorf("unexpected ffprobe output format: %q", outStr)
|
||||
}
|
||||
}
|
||||
|
||||
// TestZeroPacketError verifies that RunSynthesis returns an error containing
|
||||
// "no packets" and does NOT create an output file when given zero-packet input.
|
||||
func TestZeroPacketError(t *testing.T) {
|
||||
// Test 1: empty snapshot slice
|
||||
tmpPath := "/tmp/netsynth-should-not-exist-" + t.Name() + ".mp3"
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
err := RunSynthesis([]classify.WindowSnapshot{}, tmpPath)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for empty snapshot slice, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "no packets") {
|
||||
t.Errorf("expected error to contain 'no packets', got: %q", err.Error())
|
||||
}
|
||||
if _, statErr := os.Stat(tmpPath); !os.IsNotExist(statErr) {
|
||||
t.Error("output file should NOT exist after zero-packet error")
|
||||
os.Remove(tmpPath)
|
||||
}
|
||||
|
||||
// Test 2: snapshots where all TotalPackets == 0
|
||||
tmpPath2 := "/tmp/netsynth-should-not-exist-zero-" + t.Name() + ".mp3"
|
||||
defer os.Remove(tmpPath2)
|
||||
|
||||
zeroSnaps := []classify.WindowSnapshot{
|
||||
{Counts: map[classify.TrafficClass]int64{}, TotalPackets: 0, WindowIndex: 0},
|
||||
{Counts: map[classify.TrafficClass]int64{}, TotalPackets: 0, WindowIndex: 1},
|
||||
}
|
||||
err2 := RunSynthesis(zeroSnaps, tmpPath2)
|
||||
if err2 == nil {
|
||||
t.Fatal("expected error for zero-packet snapshots, got nil")
|
||||
}
|
||||
if !strings.Contains(err2.Error(), "no packets") {
|
||||
t.Errorf("expected error to contain 'no packets', got: %q", err2.Error())
|
||||
}
|
||||
if _, statErr := os.Stat(tmpPath2); !os.IsNotExist(statErr) {
|
||||
t.Error("output file should NOT exist after zero-packet error")
|
||||
os.Remove(tmpPath2)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEncodeMP3DirectBytes encodes a 440 Hz sine wave directly via EncodeMP3
|
||||
// and validates the output as a valid MP3 file.
|
||||
func TestEncodeMP3DirectBytes(t *testing.T) {
|
||||
if _, err := exec.LookPath("ffprobe"); err != nil {
|
||||
t.Skip("ffprobe not found in PATH; skipping MP3 validation test")
|
||||
}
|
||||
|
||||
// Generate 1 second of 440 Hz stereo sine wave
|
||||
sampleRate := synth.SampleRate
|
||||
numFrames := sampleRate
|
||||
frames := make([][2]float64, numFrames)
|
||||
for i := range frames {
|
||||
v := 0.5 * math.Sin(2.0*math.Pi*440.0*float64(i)/float64(sampleRate))
|
||||
frames[i] = [2]float64{v, v}
|
||||
}
|
||||
|
||||
tmpFile, err := os.CreateTemp("", "netsynth-sine-test-*.mp3")
|
||||
if err != nil {
|
||||
t.Fatalf("create temp file: %v", err)
|
||||
}
|
||||
tmpPath := tmpFile.Name()
|
||||
tmpFile.Close()
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
if err := EncodeMP3(tmpPath, frames, sampleRate); err != nil {
|
||||
t.Fatalf("EncodeMP3: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(tmpPath)
|
||||
if err != nil {
|
||||
t.Fatalf("stat output file: %v", err)
|
||||
}
|
||||
if info.Size() == 0 {
|
||||
t.Fatal("output file is empty")
|
||||
}
|
||||
|
||||
out, err := exec.Command("ffprobe",
|
||||
"-v", "error",
|
||||
"-show_entries", "format=format_name",
|
||||
"-of", "csv=p=0",
|
||||
tmpPath,
|
||||
).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("ffprobe error: %v\noutput: %s", err, out)
|
||||
}
|
||||
if !strings.Contains(string(out), "mp3") {
|
||||
t.Errorf("expected MP3 format, got: %q", string(out))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user