278 lines
7.1 KiB
Go
278 lines
7.1 KiB
Go
package whisper
|
|
|
|
import (
|
|
"context"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestGetModelsDir(t *testing.T) {
|
|
dir, err := GetModelsDir()
|
|
if err != nil {
|
|
t.Fatalf("GetModelsDir() error = %v", err)
|
|
}
|
|
|
|
// should not contain ~ (should be expanded)
|
|
if strings.Contains(dir, "~") {
|
|
t.Errorf("GetModelsDir() contains ~, got %s", dir)
|
|
}
|
|
|
|
// should end with expected path
|
|
if !strings.HasSuffix(dir, filepath.Join(".local", "share", "hyprvoice", "models", "whisper")) {
|
|
t.Errorf("GetModelsDir() = %s, want path ending with .local/share/hyprvoice/models/whisper", dir)
|
|
}
|
|
}
|
|
|
|
func TestGetModelPath(t *testing.T) {
|
|
tests := []struct {
|
|
modelID string
|
|
wantEnd string
|
|
}{
|
|
{"base.en", "ggml-base.en.bin"},
|
|
{"tiny", "ggml-tiny.bin"},
|
|
{"large-v3", "ggml-large-v3.bin"},
|
|
{"unknown", ""},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.modelID, func(t *testing.T) {
|
|
got := GetModelPath(tt.modelID)
|
|
if tt.wantEnd == "" {
|
|
if got != "" {
|
|
t.Errorf("GetModelPath(%q) = %s, want empty", tt.modelID, got)
|
|
}
|
|
return
|
|
}
|
|
if !strings.HasSuffix(got, tt.wantEnd) {
|
|
t.Errorf("GetModelPath(%q) = %s, want ending with %s", tt.modelID, got, tt.wantEnd)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetDownloadURL(t *testing.T) {
|
|
tests := []struct {
|
|
modelID string
|
|
wantURL string
|
|
}{
|
|
{"base.en", "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin"},
|
|
{"tiny", "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.bin"},
|
|
{"unknown", ""},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.modelID, func(t *testing.T) {
|
|
got := GetDownloadURL(tt.modelID)
|
|
if got != tt.wantURL {
|
|
t.Errorf("GetDownloadURL(%q) = %s, want %s", tt.modelID, got, tt.wantURL)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetModel(t *testing.T) {
|
|
t.Run("known model", func(t *testing.T) {
|
|
info := GetModel("base.en")
|
|
if info == nil {
|
|
t.Fatal("GetModel(base.en) = nil, want non-nil")
|
|
}
|
|
if info.ID != "base.en" {
|
|
t.Errorf("info.ID = %s, want base.en", info.ID)
|
|
}
|
|
if info.Filename != "ggml-base.en.bin" {
|
|
t.Errorf("info.Filename = %s, want ggml-base.en.bin", info.Filename)
|
|
}
|
|
if info.Multilingual {
|
|
t.Error("base.en should not be multilingual")
|
|
}
|
|
})
|
|
|
|
t.Run("multilingual model", func(t *testing.T) {
|
|
info := GetModel("base")
|
|
if info == nil {
|
|
t.Fatal("GetModel(base) = nil, want non-nil")
|
|
}
|
|
if !info.Multilingual {
|
|
t.Error("base should be multilingual")
|
|
}
|
|
})
|
|
|
|
t.Run("unknown model", func(t *testing.T) {
|
|
info := GetModel("unknown")
|
|
if info != nil {
|
|
t.Errorf("GetModel(unknown) = %v, want nil", info)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestListModels(t *testing.T) {
|
|
models := ListModels()
|
|
if len(models) != 9 {
|
|
t.Errorf("ListModels() returned %d models, want 9", len(models))
|
|
}
|
|
|
|
// verify known models exist
|
|
ids := make(map[string]bool)
|
|
for _, m := range models {
|
|
ids[m.ID] = true
|
|
}
|
|
|
|
expected := []string{"tiny.en", "base.en", "small.en", "medium.en", "tiny", "base", "small", "medium", "large-v3"}
|
|
for _, id := range expected {
|
|
if !ids[id] {
|
|
t.Errorf("ListModels() missing model %s", id)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestListMultilingualModels(t *testing.T) {
|
|
models := ListMultilingualModels()
|
|
if len(models) != 5 {
|
|
t.Errorf("ListMultilingualModels() returned %d models, want 5", len(models))
|
|
}
|
|
|
|
for _, m := range models {
|
|
if !m.Multilingual {
|
|
t.Errorf("ListMultilingualModels() returned non-multilingual model %s", m.ID)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestListEnglishOnlyModels(t *testing.T) {
|
|
models := ListEnglishOnlyModels()
|
|
if len(models) != 4 {
|
|
t.Errorf("ListEnglishOnlyModels() returned %d models, want 4", len(models))
|
|
}
|
|
|
|
for _, m := range models {
|
|
if m.Multilingual {
|
|
t.Errorf("ListEnglishOnlyModels() returned multilingual model %s", m.ID)
|
|
}
|
|
if !strings.HasSuffix(m.ID, ".en") {
|
|
t.Errorf("ListEnglishOnlyModels() returned model without .en suffix: %s", m.ID)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIsInstalled(t *testing.T) {
|
|
// should return false for non-existent model
|
|
if IsInstalled("base.en") {
|
|
// this might actually be true if the user has it installed
|
|
// just skip this test if model exists
|
|
t.Skip("base.en is installed, skipping test")
|
|
}
|
|
|
|
// should return false for unknown model
|
|
if IsInstalled("unknown-model") {
|
|
t.Error("IsInstalled(unknown-model) = true, want false")
|
|
}
|
|
}
|
|
|
|
func TestListInstalled(t *testing.T) {
|
|
// just verify it doesn't crash
|
|
installed := ListInstalled()
|
|
t.Logf("Installed models: %v", installed)
|
|
}
|
|
|
|
func TestDownload_UnknownModel(t *testing.T) {
|
|
err := Download(context.Background(), "unknown-model", nil)
|
|
if err == nil {
|
|
t.Error("Download(unknown-model) = nil, want error")
|
|
}
|
|
if !strings.Contains(err.Error(), "unknown model") {
|
|
t.Errorf("Download error = %v, want error containing 'unknown model'", err)
|
|
}
|
|
}
|
|
|
|
func TestDownload_Cancelled(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel() // cancel immediately
|
|
|
|
err := Download(ctx, "tiny.en", nil)
|
|
if err == nil {
|
|
t.Error("Download with cancelled context = nil, want error")
|
|
}
|
|
}
|
|
|
|
func TestRemove_NotInstalled(t *testing.T) {
|
|
// use a model that's unlikely to be installed
|
|
err := Remove("large-v3")
|
|
if err == nil {
|
|
t.Skip("large-v3 is installed, skipping test")
|
|
}
|
|
if !strings.Contains(err.Error(), "not installed") {
|
|
t.Errorf("Remove error = %v, want error containing 'not installed'", err)
|
|
}
|
|
}
|
|
|
|
func TestRemove_UnknownModel(t *testing.T) {
|
|
err := Remove("unknown-model")
|
|
if err == nil {
|
|
t.Error("Remove(unknown-model) = nil, want error")
|
|
}
|
|
if !strings.Contains(err.Error(), "unknown model") {
|
|
t.Errorf("Remove error = %v, want error containing 'unknown model'", err)
|
|
}
|
|
}
|
|
|
|
func TestGetInstalledPath_NotInstalled(t *testing.T) {
|
|
// use a model that's unlikely to be installed
|
|
_, err := GetInstalledPath("large-v3")
|
|
if err == nil {
|
|
t.Skip("large-v3 is installed, skipping test")
|
|
}
|
|
if !strings.Contains(err.Error(), "not installed") {
|
|
t.Errorf("GetInstalledPath error = %v, want error containing 'not installed'", err)
|
|
}
|
|
}
|
|
|
|
func TestDownloadAndRemove_Integration(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test in short mode")
|
|
}
|
|
|
|
// create a temp directory for this test
|
|
tempDir := t.TempDir()
|
|
|
|
// override GetModelsDir for this test
|
|
origGetModelsDir := GetModelsDir
|
|
_ = origGetModelsDir // acknowledge we're shadowing
|
|
|
|
// we can't easily override GetModelsDir since it's a function not a var
|
|
// so we'll just check the download flow works conceptually
|
|
// actual download testing would need network and is slow
|
|
|
|
t.Log("Integration test would download a model here")
|
|
t.Log("Temp dir:", tempDir)
|
|
}
|
|
|
|
// TestModelInfo_SizeBytes verifies size bytes are reasonable
|
|
func TestModelInfo_SizeBytes(t *testing.T) {
|
|
models := ListModels()
|
|
for _, m := range models {
|
|
if m.SizeBytes <= 0 {
|
|
t.Errorf("Model %s has invalid SizeBytes: %d", m.ID, m.SizeBytes)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestModelInfo_HasAllFields verifies all models have required fields
|
|
func TestModelInfo_HasAllFields(t *testing.T) {
|
|
models := ListModels()
|
|
for _, m := range models {
|
|
if m.ID == "" {
|
|
t.Error("Model has empty ID")
|
|
}
|
|
if m.Name == "" {
|
|
t.Errorf("Model %s has empty Name", m.ID)
|
|
}
|
|
if m.Filename == "" {
|
|
t.Errorf("Model %s has empty Filename", m.ID)
|
|
}
|
|
if m.Size == "" {
|
|
t.Errorf("Model %s has empty Size", m.ID)
|
|
}
|
|
}
|
|
}
|