Skip to content
Open
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
8 changes: 8 additions & 0 deletions ui/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,14 @@ func (s *DownloadScreen) buildDownloads(config internal.Config, host romm.Host,
downloadLocation = filepath.Join(tmpDir, fmt.Sprintf("grout_multirom_%d.zip", g.ID))
sourceURL, _ = url.JoinPath(host.URL(), "/api/roms/", strconv.Itoa(g.ID), "content", g.FsName)
} else {
// Skip games with no file metadata to avoid an out-of-range panic.
// This can happen when the cached row was written without a
// `files` array (e.g. after an incremental cache update).
if len(g.Files) == 0 {
gaba.GetLogger().Warn("Skipping ROM with no file metadata; refresh the library to repopulate it",
"game", g.Name, "id", g.ID, "fs_name", g.FsName)
continue
}
// Find the file to download - use selected file if specified, otherwise first file
fileToDownload := g.Files[0]
if selectedFileID > 0 {
Expand Down
91 changes: 91 additions & 0 deletions ui/download_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package ui

import (
"os"
"testing"

"grout/internal"
"grout/romm"
)

func TestMain(m *testing.M) {
// cfw.GetCFW() (called from buildDownloads) requires the CFW env var to
// be set to a recognised value, otherwise it terminates the process.
if os.Getenv("CFW") == "" {
os.Setenv("CFW", "ROCKNIX")
}
os.Exit(m.Run())
}

// TestBuildDownloads_EmptyFiles is a regression test for issue #223:
// a cached ROM with HasMultipleFiles == false and an empty Files slice
// must not panic. The entry should be skipped instead.
func TestBuildDownloads_EmptyFiles(t *testing.T) {
s := NewDownloadScreen()
config := internal.Config{}
host := romm.Host{RootURI: "http://example.invalid"}
platform := romm.Platform{ID: 1, FSSlug: "nds", Name: "Nintendo DS"}

games := []romm.Rom{
{
ID: 60,
Name: "Professor Layton and the Curious Village",
FsName: "Professor Layton and the Curious Village (USA).nds",
FsNameNoExt: "Professor Layton and the Curious Village (USA)",
HasMultipleFiles: false,
Files: nil,
},
}

defer func() {
if r := recover(); r != nil {
t.Fatalf("buildDownloads panicked on empty Files slice: %v", r)
}
}()

downloads, artDownloads, gamelistEntries := s.buildDownloads(config, host, platform, games, 0)

if len(downloads) != 0 {
t.Errorf("expected 0 downloads when Files is empty, got %d", len(downloads))
}
if len(artDownloads) != 0 {
t.Errorf("expected 0 art downloads when Files is empty, got %d", len(artDownloads))
}
if len(gamelistEntries) != 0 {
t.Errorf("expected 0 gamelist entries when Files is empty, got %d", len(gamelistEntries))
}
}

// TestBuildDownloads_SingleFile_HappyPath sanity-checks that a normal
// single-file ROM still produces a download URL after the empty-Files guard.
func TestBuildDownloads_SingleFile_HappyPath(t *testing.T) {
s := NewDownloadScreen()
config := internal.Config{}
host := romm.Host{RootURI: "http://example.invalid"}
platform := romm.Platform{ID: 1, FSSlug: "nds", Name: "Nintendo DS"}

games := []romm.Rom{
{
ID: 42,
Name: "Test Game",
FsName: "test.nds",
FsNameNoExt: "test",
HasMultipleFiles: false,
Files: []romm.RomFile{
{ID: 100, FileName: "test.nds"},
},
},
}

downloads, _, gamelistEntries := s.buildDownloads(config, host, platform, games, 0)

if len(downloads) != 1 {
t.Fatalf("expected 1 download, got %d", len(downloads))
}
if len(gamelistEntries) != 1 {
t.Fatalf("expected 1 gamelist entry, got %d", len(gamelistEntries))
}
if downloads[0].URL == "" {
t.Error("expected non-empty download URL")
}
}