package app import ( "bytes" "compress/gzip" "context" "crypto/sha256" "encoding/hex" "errors" "fmt" "io" "net/http" "os" "os/exec" "path/filepath" "runtime" "strings" "sync" "sync/atomic" "testing" "time" ) func TestMain(m *testing.M) { if os.Getenv("JUKU_TEST_FFMPEG_HELPER") == "1" { ffmpegTestHelper() return } os.Exit(m.Run()) } func ffmpegTestHelper() { mode, _ := os.ReadFile(os.Args[0] + ".mode") if log, err := os.OpenFile(os.Args[0]+".calls", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600); err == nil { fmt.Fprintln(log, strings.Join(os.Args[1:], " ")) _ = log.Close() } if string(mode) == "hang" { time.Sleep(time.Minute) } if string(mode) == "slow" { time.Sleep(100 * time.Millisecond) } if strings.Contains(strings.Join(os.Args[1:], " "), "-encoders") { fmt.Println(" A..... aac AAC") if string(mode) != "missing" { fmt.Println(" V..... libx264 H.264") } return } if string(mode) == "preset" { fmt.Fprintln(os.Stderr, "Unrecognized option 'preset'.\r\nError splitting the argument list: Option not found") os.Exit(8) } fmt.Print("synthetic MP4 output") } func ffmpegTestEnvironment(t *testing.T) string { t.Helper() previous, err := os.Getwd() if err != nil { t.Fatal(err) } root := t.TempDir() if err := os.Chdir(root); err != nil { t.Fatal(err) } t.Cleanup(func() { _ = os.Chdir(previous) }) t.Setenv("PATH", "") t.Setenv("JUKU_TEST_FFMPEG_HELPER", "1") return root } func ffmpegTestProgram(t *testing.T, directory, mode string) string { t.Helper() if err := os.MkdirAll(directory, 0755); err != nil { t.Fatal(err) } program, err := os.Executable() if err != nil { t.Fatal(err) } path := filepath.Join(directory, ffmpegExecutableName()) if err := os.Link(program, path); err != nil { body, err := os.ReadFile(program) if err != nil { t.Fatal(err) } if err := os.WriteFile(path, body, 0755); err != nil { t.Fatal(err) } } if err := os.WriteFile(path+".mode", []byte(mode), 0600); err != nil { t.Fatal(err) } return path } func ffmpegTestInstaller(t *testing.T, configured string) *ffmpegInstaller { t.Helper() return &ffmpegInstaller{ configured: configured, name: ffmpegExecutableName(), directory: ffmpegManagedDirectory(), client: &http.Client{Transport: rankingTransport(func(*http.Request) (*http.Response, error) { t.Error("unexpected FFmpeg download") return nil, errors.New("network forbidden") })}, } } func TestFFmpegPATHPrecedesPortableAndKeepsAutomaticSetting(t *testing.T) { root := ffmpegTestEnvironment(t) ffmpegTestProgram(t, filepath.Join(root, "bin"), "preset") ffmpegTestProgram(t, root, "preset") full := ffmpegTestProgram(t, filepath.Join(root, "系统 PATH with spaces"), "good") t.Setenv("PATH", filepath.Dir(full)) d := NewDownloader(Config{FFmpeg: "ffmpeg", OutputDir: filepath.Join(root, "out"), dataDir: filepath.Join(root, "data"), settingsLoaded: true}) if d.cfg.FFmpeg != "ffmpeg" { t.Fatal("automatic discovery was frozen as an explicit portable path", d.cfg.FFmpeg) } if d.ffmpegInstallation().snapshot().Status == "ready" { t.Fatal("unverified executable advertised as ready") } path, err := d.ensureFFmpeg(context.Background()) if err != nil || path != full { t.Fatal("PATH must take precedence over current-directory and bundled programs", path, err) } if !strings.Contains(d.ffmpegInstallation().snapshot().Detail, full) { t.Fatal("the selected executable must be visible in status") } } func TestFFmpegSkipsIncompatiblePATHCandidates(t *testing.T) { root := ffmpegTestEnvironment(t) first := ffmpegTestProgram(t, filepath.Join(root, "minimal"), "missing") second := ffmpegTestProgram(t, filepath.Join(root, "advertised-only"), "preset") full := ffmpegTestProgram(t, filepath.Join(root, "full"), "good") t.Setenv("PATH", strings.Join([]string{filepath.Dir(first), filepath.Dir(second), filepath.Dir(first), filepath.Dir(full)}, string(os.PathListSeparator))) installer := ffmpegTestInstaller(t, "ffmpeg") var events []diagnosticEvent installer.record = func(event diagnosticEvent) { events = append(events, event) } path, err := installer.ensure(context.Background()) if err != nil || path != full { t.Fatal("a PATH hit without working encoders must not mask a usable fallback", path, err) } if len(events) != 3 || events[0].Event != "ffmpeg.rejected" || !strings.Contains(events[1].Message, "preset") || events[2].Event != "ffmpeg.ready" || !strings.Contains(events[2].Message, full) { t.Fatal("selection diagnostics must explain rejected candidates and the final path", events) } if candidates := ffmpegCandidates("ffmpeg", installer.directory); len(candidates) != 3 { t.Fatal("duplicate PATH entries were not deduplicated", candidates) } } func TestFFmpegExplicitPathsRemainAuthoritative(t *testing.T) { root := ffmpegTestEnvironment(t) full := ffmpegTestProgram(t, filepath.Join(root, "full"), "good") bad := ffmpegTestProgram(t, filepath.Join(root, "explicit"), "preset") t.Setenv("PATH", filepath.Dir(full)) installer := ffmpegTestInstaller(t, bad) path, err := installer.ensure(context.Background()) if err == nil || path != "" || !strings.Contains(err.Error(), "preset") || !strings.Contains(err.Error(), bad) { t.Fatal("an invalid explicit path must report its failure without switching programs", path, err) } if installer.snapshot().Status != "failed" { t.Fatal("invalid explicit program was marked ready") } if err := os.WriteFile(bad+".mode", []byte("good"), 0600); err != nil { t.Fatal(err) } installer.retry() path, err = installer.ensure(context.Background()) if err != nil || path != bad { t.Fatal("retry must probe a corrected explicit program", path, err) } installer = ffmpegTestInstaller(t, " \""+full+"\" ") path, err = installer.ensure(context.Background()) if err != nil || path != full { t.Fatal("quoted paths with spaces were not resolved", path, err) } installer = ffmpegTestInstaller(t, filepath.Join(root, "missing", "ffmpeg.exe")) if _, err := installer.ensure(context.Background()); err == nil || !strings.Contains(err.Error(), "找不到指定") { t.Fatal("missing explicit path must not silently download", err) } } func TestFFmpegSharedProbeIsCachedAndCancelable(t *testing.T) { root := ffmpegTestEnvironment(t) program := ffmpegTestProgram(t, filepath.Join(root, "slow"), "slow") installer := ffmpegTestInstaller(t, program) var wait sync.WaitGroup for index := 0; index < 8; index++ { wait.Add(1) go func() { defer wait.Done() if path, err := installer.ensure(context.Background()); err != nil || path != program { t.Error(path, err) } }() } ctx, cancel := context.WithCancel(context.Background()) cancel() if _, err := installer.ensure(ctx); !errors.Is(err, context.Canceled) { t.Fatal("canceled waiter did not exit", err) } wait.Wait() if _, err := installer.ensure(context.Background()); err != nil { t.Fatal(err) } log, err := os.ReadFile(program + ".calls") if err != nil || len(strings.Split(strings.TrimSpace(string(log)), "\n")) != 2 { t.Fatal("concurrent users must share one encoder query and one real probe", string(log), err) } hanging := ffmpegTestProgram(t, filepath.Join(root, "hang"), "hang") ctx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() start := time.Now() if err := validateFFmpeg(ctx, hanging); err == nil || time.Since(start) > 3*time.Second { t.Fatal("capability probe must honor cancellation", err, time.Since(start)) } } func ffmpegTestPackage(t *testing.T, installer *ffmpegInstaller) *atomic.Int32 { t.Helper() program, err := os.Executable() if err != nil { t.Fatal(err) } binary, err := os.ReadFile(program) if err != nil { t.Fatal(err) } var archive bytes.Buffer writer, _ := gzip.NewWriterLevel(&archive, gzip.BestSpeed) _, _ = writer.Write(binary) if err := writer.Close(); err != nil { t.Fatal(err) } digest := func(body []byte) string { value := sha256.Sum256(body); return hex.EncodeToString(value[:]) } document := []byte("synthetic test package documentation") installer.pack = ffmpegPackage{platform: "fixture", archiveSize: int64(archive.Len()), binarySize: int64(len(binary)), archiveSHA256: digest(archive.Bytes()), binarySHA256: digest(binary), licenseSHA256: digest(document), readmeSHA256: digest(document)} var requests atomic.Int32 installer.client = &http.Client{Transport: rankingTransport(func(request *http.Request) (*http.Response, error) { requests.Add(1) body := document if strings.HasSuffix(request.URL.Path, ".gz") { body = archive.Bytes() } return &http.Response{StatusCode: http.StatusOK, Header: make(http.Header), Body: io.NopCloser(bytes.NewReader(body)), ContentLength: int64(len(body)), Request: request}, nil })} return &requests } func TestFFmpegAutomaticFallbackPreservesExistingPortableProgram(t *testing.T) { root := ffmpegTestEnvironment(t) old := ffmpegTestProgram(t, filepath.Join(root, "bin", runtime.GOOS+"-"+runtime.GOARCH), "missing") info, err := os.Stat(old) if err != nil { t.Fatal(err) } installer := ffmpegTestInstaller(t, "ffmpeg") requests := ffmpegTestPackage(t, installer) path, err := installer.ensure(context.Background()) if err != nil || path != filepath.Join(installer.directory, installer.name) || path == old { t.Fatal("verified auto-download must be selected after rejecting the old portable program", path, err) } if requests.Load() != 3 { t.Fatal("archive, license and build details must all be fetched", requests.Load()) } after, err := os.Stat(old) if err != nil || !os.SameFile(info, after) { t.Fatal("existing portable program was replaced", err) } restarted := ffmpegTestInstaller(t, "ffmpeg") if next, err := restarted.ensure(context.Background()); err != nil || next != path { t.Fatal("restart must reuse the verified installation without downloading", next, err) } } func TestFFmpegInstallationRejectsCorruptionAndInvalidDestination(t *testing.T) { root := ffmpegTestEnvironment(t) installer := ffmpegTestInstaller(t, "ffmpeg") ffmpegTestPackage(t, installer) installer.pack.archiveSHA256 = strings.Repeat("0", 64) if _, err := installer.install(context.Background()); err == nil || !strings.Contains(err.Error(), "SHA-256") { t.Fatal("corrupt download was accepted", err) } if _, err := os.Stat(installer.directory); !errors.Is(err, os.ErrNotExist) { t.Fatal("a corrupt download must not create a usable installation", err) } ffmpegTestPackage(t, installer) bad := ffmpegTestProgram(t, installer.directory, "preset") if path, err := installer.install(context.Background()); err == nil || path != "" { t.Fatal("an existing invalid destination must not turn an install failure into success", path, err) } if mode, err := os.ReadFile(bad + ".mode"); err != nil || string(mode) != "preset" { t.Fatal("existing installation contents were changed", root, err) } } func TestFFmpegLocalPlaybackProbe(t *testing.T) { program, err := exec.LookPath("ffmpeg") if err != nil { t.Skip("local FFmpeg unavailable") } ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second) defer cancel() if err := validateFFmpeg(ctx, program); err != nil { t.Fatal(err) } }