Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,12 @@ func initializeAudioSystem(cmd *cobra.Command, cli *CLI, cfg *config.Config) err
slog.Warn("failed to load embedded platform soundpack from config",
"identifier", cfg.DefaultSoundpack, "error", err)
}
} else if embeddedIdentifier, ok := embeddedPlatformSoundpackIdentifier(cfg.DefaultSoundpack); ok {
mapper, err = loadEmbeddedPlatformSoundpack(embeddedIdentifier)
if err != nil {
slog.Warn("failed to load embedded platform soundpack from config name",
"name", cfg.DefaultSoundpack, "identifier", embeddedIdentifier, "error", err)
}
} else {
// Resolve soundpack name to path: if default_soundpack is a name (not a path),
// search soundpack_paths for a matching entry
Expand Down
65 changes: 64 additions & 1 deletion internal/cli/embedded_linux_soundpack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,77 @@ import (
"context"
"encoding/json"
"os"
"path/filepath"
"testing"

"claudio.click/internal/cli/testenv"
"claudio.click/internal/config"
"claudio.click/internal/hooks"
"claudio.click/internal/sounds"
"claudio.click/internal/soundpack"
"claudio.click/internal/sounds"
)

func TestEmbeddedPlatformSoundpackIdentifierRecognizesBarePlatformNames(t *testing.T) {
tests := []struct {
name string
want string
}{
{name: "windows", want: "embedded:windows.json"},
{name: "wsl", want: "embedded:wsl.json"},
{name: "darwin", want: "embedded:darwin.json"},
{name: "linux", want: "embedded:linux.json"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, ok := embeddedPlatformSoundpackIdentifier(tt.name)
if !ok {
t.Fatalf("expected %q to be recognized as a bare embedded soundpack name", tt.name)
}
if got != tt.want {
t.Fatalf("expected %q, got %q", tt.want, got)
}
})
}
}

func TestInitializeAudioSystemUsesBareEmbeddedLinuxSoundpack(t *testing.T) {
testenv.IsolateXDG(t)

tempDir := t.TempDir()
if err := os.Mkdir(filepath.Join(tempDir, "linux"), 0755); err != nil {
t.Fatalf("failed to create same-name directory: %v", err)
}
t.Chdir(tempDir)

cli := NewCLI()
cfg := &config.Config{
DefaultSoundpack: "linux",
Enabled: false,
}

if err := initializeAudioSystem(cli.rootCmd, cli, cfg); err != nil {
t.Fatalf("initializeAudioSystem returned error: %v", err)
}
if cli.soundpackResolver == nil {
t.Fatal("soundpack resolver was not initialized")
}
if got := cli.soundpackResolver.GetType(); got != "json" {
t.Fatalf("expected bare linux to initialize embedded JSON resolver, got type %q", got)
}
if got := cli.soundpackResolver.GetName(); got != "linux-default" {
t.Fatalf("expected embedded linux resolver name %q, got %q", "linux-default", got)
}

resolved, err := cli.soundpackResolver.ResolveSound("default.wav")
if err != nil {
t.Fatalf("expected embedded linux resolver to resolve default.wav: %v", err)
}
if _, err := os.Stat(resolved); err != nil {
t.Fatalf("resolved default.wav path %q does not exist: %v", resolved, err)
}
}

// TestEmbeddedLinuxSoundpackResolves proves the native-Linux default pack
// resolves its default tones out of the box on any host.
//
Expand Down
23 changes: 23 additions & 0 deletions internal/cli/embedded_soundpack_discovery_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package cli

import "testing"

func TestDiscoverEmbeddedSoundpacksIncludesLinux(t *testing.T) {
packs, err := discoverEmbeddedSoundpacks()
if err != nil {
t.Fatalf("discoverEmbeddedSoundpacks returned error: %v", err)
}

found := map[string]bool{}
for _, pack := range packs {
if pack.Type == "embedded" {
found[pack.Name] = true
}
}

for _, name := range []string{"windows", "wsl", "darwin", "linux"} {
if !found[name] {
t.Errorf("expected embedded soundpack discovery to include %q", name)
}
}
}
31 changes: 25 additions & 6 deletions internal/cli/soundpack_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,27 @@ type soundpackInfo struct {
Path string
}

var embeddedPlatformSoundpackFiles = []string{"windows.json", "wsl.json", "darwin.json", "linux.json"}

func embeddedPlatformSoundpackIdentifier(name string) (string, bool) {
if name == "" || strings.ContainsAny(name, `/\`) || filepath.Ext(name) != "" {
return "", false
}

filename := name + ".json"
for _, embedded := range embeddedPlatformSoundpackFiles {
if filename == embedded {
return "embedded:" + filename, true
}
}

if _, err := config.GetEmbeddedPlatformSoundpackData(filename); err == nil {
return "embedded:" + filename, true
}

return "", false
}

// discoverSoundpacks finds all available soundpacks from embedded, XDG, and config sources.
// Returns a deduplicated list of soundpack info structs.
func discoverSoundpacks() ([]soundpackInfo, error) {
Expand Down Expand Up @@ -80,12 +101,11 @@ func discoverSoundpacks() ([]soundpackInfo, error) {
return packs, nil
}

// discoverEmbeddedSoundpacks returns info for the 3 embedded platform packs
// discoverEmbeddedSoundpacks returns info for embedded platform packs.
func discoverEmbeddedSoundpacks() ([]soundpackInfo, error) {
platformFiles := []string{"windows.json", "wsl.json", "darwin.json"}
var packs []soundpackInfo

for _, file := range platformFiles {
for _, file := range embeddedPlatformSoundpackFiles {
data, err := config.GetEmbeddedPlatformSoundpackData(file)
if err != nil {
slog.Warn("failed to read embedded platform soundpack", "file", file, "error", err)
Expand Down Expand Up @@ -292,15 +312,14 @@ func countNonEmptyMappings(mappings map[string]string) int {
return count
}

// ExtractAllSoundKeys reads all 3 embedded platform JSONs and returns the sorted
// ExtractAllSoundKeys reads all embedded platform JSONs and returns the sorted
// union of all mapping keys. It uses PeekJSONSoundpackFromBytes (which
// applies the size and mappings-count caps but skips path/existence
// checks) since we only need the keys.
func ExtractAllSoundKeys() ([]string, error) {
platformFiles := []string{"windows.json", "wsl.json", "darwin.json"}
keySet := make(map[string]struct{})

for _, file := range platformFiles {
for _, file := range embeddedPlatformSoundpackFiles {
data, err := config.GetEmbeddedPlatformSoundpackData(file)
if err != nil {
slog.Warn("failed to read embedded platform soundpack", "file", file, "error", err)
Expand Down
Loading