git/intake/main
internal/audio/audio_test.go
76 lines2 KB last change raw · history
package audio

import (
	"context"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
)

// makeFLAC writes a short tone, and a truncated copy of it. Truncation is the
// shape a half-finished download arrives in.
func makeFLAC(t *testing.T, dir string) (good, cut string) {
	t.Helper()
	if _, err := exec.LookPath("ffmpeg"); err != nil {
		t.Skip("ffmpeg is needed to build the fixture")
	}
	good = filepath.Join(dir, "good.flac")
	cmd := exec.Command("ffmpeg", "-hide_banner", "-loglevel", "error", "-y",
		"-f", "lavfi", "-i", "sine=frequency=440:duration=2", good)
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Skipf("could not build a flac: %v %s", err, out)
	}
	data, err := os.ReadFile(good)
	if err != nil {
		t.Fatal(err)
	}
	cut = filepath.Join(dir, "cut.flac")
	if err := os.WriteFile(cut, data[:len(data)/2], 0o600); err != nil {
		t.Fatal(err)
	}
	return good, cut
}

// ffmpeg decodes a truncated flac without complaint, so it cannot be what the
// archive relies on: a partial file would be promoted and only discovered later.
func TestTestFLACCatchesTruncation(t *testing.T) {
	if _, err := exec.LookPath("flac"); err != nil {
		t.Skip("flac is not installed")
	}
	good, cut := makeFLAC(t, t.TempDir())

	if err := TestFLAC(context.Background(), good); err != nil {
		t.Errorf("an intact flac should pass: %v", err)
	}
	if err := TestFLAC(context.Background(), cut); err == nil {
		t.Error("a truncated flac passed the integrity test")
	}

	// The same file that ffmpeg is happy with.
	if err := DecodeOK(context.Background(), cut); err != nil {
		t.Logf("ffmpeg also rejected it: %v", err)
	} else {
		t.Log("ffmpeg accepted the truncated file, which is why TestFLAC exists")
	}
}

// macOS writes an AppleDouble sidecar beside each file it touches on a foreign
// filesystem, named "._" plus the original. It keeps the audio extension, so by
// extension alone it looks like a track, and it was left behind in staging after
// everything else had been cleaned up.
func TestAppleDoubleFilesAreNotAudio(t *testing.T) {
	if IsAudio("._01 track.flac") {
		t.Error("an AppleDouble sidecar should not count as a track")
	}
	if IsAudio("._song.mp3") {
		t.Error("an AppleDouble sidecar should not count as a track")
	}
	if !IsAudio("01 track.flac") {
		t.Error("a real track should still count")
	}
	if !IsAudio("SONG.MP3") {
		t.Error("the extension check should stay case insensitive")
	}
}