package ttslaunch import ( "context" "os" "path/filepath" "runtime" "strings" "testing" "time" ) // TestLaunchScript_MissingFile 钉住"脚本不存在 ⇒ 返回可读错误"(调用方只 WARN)。 func TestLaunchScript_MissingFile(t *testing.T) { missing := filepath.Join(t.TempDir(), "一键启动.bat") err := LaunchScript(context.Background(), missing) if err == nil { t.Fatal("脚本不存在却没报错") } if !strings.Contains(err.Error(), "一键启动.bat") { t.Fatalf("错误里没有脚本路径: %v", err) } } // TestLaunchScript_Directory 钉住"路径是目录 ⇒ 报错"(配置写错时别去 cmd /c 一个目录)。 func TestLaunchScript_Directory(t *testing.T) { if err := LaunchScript(context.Background(), t.TempDir()); err == nil { t.Fatal("目录被当成脚本起起来了") } } // TestLaunchScript_EmptyPath 钉住空路径不会退化成"起当前目录里的东西"。 func TestLaunchScript_EmptyPath(t *testing.T) { if err := LaunchScript(context.Background(), " "); err == nil { t.Fatal("空路径没报错") } } // TestLaunchScript_StartsBatchFile 是本切片唯一真起进程的用例(只在 Windows 上有意义): // 它同时钉住三件容易踩的事 —— // // 1. `.bat` 必须经解释器起得来(裸 CreateProcess 不行); // 2. 路径含**中文**(主人的 `一键启动.bat` 就是这个形状)时编码/引号不出岔子; // 3. 工作目录按脚本所在目录解析(脚本里的相对路径是相对**它自己**)。 func TestLaunchScript_StartsBatchFile(t *testing.T) { if runtime.GOOS != "windows" { t.Skip("一键启动.bat 只在 Windows 上有意义") } // 不用 t.TempDir:脚本进程的**工作目录**就是脚本所在目录,Windows 在它退出前 // 不允许删那个目录,t.TempDir 的自动清理会误报失败。这里自己建、自己尽力清。 base, err := os.MkdirTemp("", "ttslaunch-test-") if err != nil { t.Fatalf("mkdtemp: %v", err) } defer cleanupEventually(t, base) dir := filepath.Join(base, "emotionTTS_语音 服务") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatalf("mkdir: %v", err) } script := filepath.Join(dir, "一键启动.bat") const body = "@echo off\r\necho started> \"%~dp0marker.txt\"\r\n" if err := os.WriteFile(script, []byte(body), 0o600); err != nil { t.Fatalf("write script: %v", err) } if err := LaunchScript(context.Background(), script); err != nil { t.Fatalf("起脚本失败: %v", err) } waitForFile(t, filepath.Join(dir, "marker.txt"), 10*time.Second) } // cleanupEventually 尽力删掉临时目录:给脚本进程一点退出时间,删不掉只记日志 // (绝不让清理动作把"脚本确实起来了"这个结论打成失败)。 func cleanupEventually(t *testing.T, dir string) { t.Helper() for i := 0; i < 60; i++ { if err := os.RemoveAll(dir); err == nil { return } time.Sleep(100 * time.Millisecond) } t.Logf("临时目录还没删掉(脚本进程可能仍在运行): %s", dir) } // waitForFile 等一个文件出现(轮询,带 deadline)。 func waitForFile(t *testing.T, path string, timeout time.Duration) { t.Helper() deadline := time.Now().Add(timeout) for time.Now().Before(deadline) { if _, err := os.Stat(path); err == nil { return } time.Sleep(50 * time.Millisecond) } t.Fatalf("等 %v 也没等到 %s(脚本没跑起来)", timeout, path) }