323 lines
8.8 KiB
Go
323 lines
8.8 KiB
Go
package project
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestTopDataAssetsDirPrefersExplicitConfig(t *testing.T) {
|
|
root := t.TempDir()
|
|
mkdirAll(t, filepath.Join(root, "src"))
|
|
mkdirAll(t, filepath.Join(root, "assets"))
|
|
mkdirAll(t, filepath.Join(root, "build"))
|
|
mkdirAll(t, filepath.Join(root, "custom-assets"))
|
|
|
|
proj := &Project{
|
|
Root: root,
|
|
Config: Config{
|
|
Module: ModuleConfig{Name: "Test", ResRef: "test"},
|
|
Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"},
|
|
TopData: TopDataConfig{
|
|
Assets: "custom-assets",
|
|
},
|
|
},
|
|
}
|
|
|
|
result := proj.TopDataAssetsDir()
|
|
expected := filepath.Join(root, "custom-assets")
|
|
if result != expected {
|
|
t.Errorf("expected %s, got %s", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestTopDataAssetsDirDoesNotAssumeSiblingRepo(t *testing.T) {
|
|
root := t.TempDir()
|
|
mkdirAll(t, filepath.Join(root, "src"))
|
|
mkdirAll(t, filepath.Join(root, "assets"))
|
|
mkdirAll(t, filepath.Join(root, "build"))
|
|
|
|
parentDir := filepath.Dir(root)
|
|
siblingPath := filepath.Join(parentDir, "sow-assets")
|
|
mkdirAll(t, siblingPath)
|
|
mkdirAll(t, filepath.Join(siblingPath, "assets", "part", "belt"))
|
|
|
|
proj := &Project{
|
|
Root: root,
|
|
Config: Config{
|
|
Module: ModuleConfig{Name: "Test", ResRef: "test"},
|
|
Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"},
|
|
TopData: TopDataConfig{
|
|
Assets: "sow-assets",
|
|
},
|
|
},
|
|
}
|
|
|
|
result := proj.TopDataAssetsDir()
|
|
expected := filepath.Join(root, "sow-assets")
|
|
if result != expected {
|
|
t.Errorf("expected unresolved local path %s, got %s", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestTopDataAssetsDirReturnsEmptyForNoConfig(t *testing.T) {
|
|
root := t.TempDir()
|
|
mkdirAll(t, filepath.Join(root, "src"))
|
|
mkdirAll(t, filepath.Join(root, "assets"))
|
|
mkdirAll(t, filepath.Join(root, "build"))
|
|
|
|
proj := &Project{
|
|
Root: root,
|
|
Config: Config{
|
|
Module: ModuleConfig{Name: "Test", ResRef: "test"},
|
|
Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"},
|
|
TopData: TopDataConfig{},
|
|
},
|
|
}
|
|
|
|
result := proj.TopDataAssetsDir()
|
|
if result != "" {
|
|
t.Errorf("expected empty string, got %s", result)
|
|
}
|
|
}
|
|
|
|
func TestModuleArchivePathUsesConfiguredBuildDirAndResRef(t *testing.T) {
|
|
root := t.TempDir()
|
|
mkdirAll(t, filepath.Join(root, "output"))
|
|
|
|
proj := &Project{
|
|
Root: root,
|
|
Config: Config{
|
|
Module: ModuleConfig{Name: "Test", ResRef: "testmod"},
|
|
Paths: PathConfig{Build: "output"},
|
|
},
|
|
}
|
|
|
|
got := proj.ModuleArchivePath()
|
|
want := filepath.Join(root, "output", "testmod.mod")
|
|
if got != want {
|
|
t.Fatalf("expected %s, got %s", want, got)
|
|
}
|
|
}
|
|
|
|
func TestHAKPathsUseConfiguredBuildDir(t *testing.T) {
|
|
root := t.TempDir()
|
|
mkdirAll(t, filepath.Join(root, "output"))
|
|
|
|
proj := &Project{
|
|
Root: root,
|
|
Config: Config{
|
|
Paths: PathConfig{Build: "output"},
|
|
},
|
|
}
|
|
|
|
if got, want := proj.HAKManifestPath(), filepath.Join(root, "output", "haks.json"); got != want {
|
|
t.Fatalf("expected manifest path %s, got %s", want, got)
|
|
}
|
|
if got, want := proj.HAKArchivePath("core_01"), filepath.Join(root, "output", "core_01.hak"); got != want {
|
|
t.Fatalf("expected hak path %s, got %s", want, got)
|
|
}
|
|
}
|
|
|
|
func TestTopDataPackagePathsUseConfiguredNamesAndBuildDir(t *testing.T) {
|
|
root := t.TempDir()
|
|
mkdirAll(t, filepath.Join(root, "output"))
|
|
|
|
proj := &Project{
|
|
Root: root,
|
|
Config: Config{
|
|
Paths: PathConfig{Build: "output"},
|
|
TopData: TopDataConfig{
|
|
PackageHAK: "custom_top.hak",
|
|
PackageTLK: "custom_dialog.tlk",
|
|
},
|
|
},
|
|
}
|
|
|
|
if got, want := proj.TopDataPackageHAKPath(), filepath.Join(root, "output", "custom_top.hak"); got != want {
|
|
t.Fatalf("expected top hak path %s, got %s", want, got)
|
|
}
|
|
if got, want := proj.TopDataPackageTLKPath(), filepath.Join(root, "output", "custom_dialog.tlk"); got != want {
|
|
t.Fatalf("expected top tlk path %s, got %s", want, got)
|
|
}
|
|
}
|
|
|
|
func TestValidateLayoutAllowsMissingAssetsDir(t *testing.T) {
|
|
root := t.TempDir()
|
|
mkdirAll(t, filepath.Join(root, "src"))
|
|
mkdirAll(t, filepath.Join(root, "build"))
|
|
|
|
proj := &Project{
|
|
Root: root,
|
|
Config: Config{
|
|
Module: ModuleConfig{Name: "Test", ResRef: "test"},
|
|
Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"},
|
|
},
|
|
}
|
|
|
|
if err := proj.ValidateLayout(); err != nil {
|
|
t.Fatalf("ValidateLayout returned error: %v", err)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(root, "assets")); !errors.Is(err, os.ErrNotExist) {
|
|
t.Fatalf("expected missing assets dir to remain absent, got err=%v", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateLayoutRejectsInvalidTopDataPackageOutputNames(t *testing.T) {
|
|
root := t.TempDir()
|
|
mkdirAll(t, filepath.Join(root, "src"))
|
|
mkdirAll(t, filepath.Join(root, "build"))
|
|
|
|
proj := &Project{
|
|
Root: root,
|
|
Config: Config{
|
|
Module: ModuleConfig{Name: "Test", ResRef: "test"},
|
|
Paths: PathConfig{Source: "src", Build: "build"},
|
|
TopData: TopDataConfig{
|
|
Source: "topdata",
|
|
PackageHAK: "nested/custom_top.hak",
|
|
PackageTLK: "custom_dialog.txt",
|
|
},
|
|
},
|
|
}
|
|
|
|
err := proj.ValidateLayout()
|
|
if err == nil {
|
|
t.Fatal("expected topdata output validation error")
|
|
}
|
|
if !strings.Contains(err.Error(), "topdata.package_hak") {
|
|
t.Fatalf("expected package_hak validation error, got %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "topdata.package_tlk") {
|
|
t.Fatalf("expected package_tlk validation error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateLayoutRejectsInvalidAutogenConsumerConfig(t *testing.T) {
|
|
root := t.TempDir()
|
|
mkdirAll(t, filepath.Join(root, "src"))
|
|
mkdirAll(t, filepath.Join(root, "build"))
|
|
|
|
proj := &Project{
|
|
Root: root,
|
|
Config: Config{
|
|
Module: ModuleConfig{Name: "Test", ResRef: "test"},
|
|
Paths: PathConfig{Source: "src", Build: "build"},
|
|
Autogen: AutogenConfig{
|
|
Consumers: []AutogenConsumerConfig{
|
|
{
|
|
ID: "head_visualeffects",
|
|
Producer: "head_visualeffects",
|
|
Dataset: "visualeffects",
|
|
Mode: "unsupported",
|
|
Root: "vfxs",
|
|
Include: []string{"head_accessories/**/*.mdl"},
|
|
Derive: AutogenDeriveConfig{Kind: "model_stem", GroupFrom: "first_path_segment"},
|
|
Manifest: AutogenManifestConfig{
|
|
ReleaseTag: "head-vfx-manifest-current",
|
|
AssetName: "nested/sow-head-vfx-manifest.json",
|
|
CacheName: "sow-head-vfx-manifest.txt",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
err := proj.ValidateLayout()
|
|
if err == nil {
|
|
t.Fatal("expected autogen validation error")
|
|
}
|
|
if !strings.Contains(err.Error(), "autogen.consumers[0].mode") {
|
|
t.Fatalf("expected autogen mode validation error, got %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "autogen.consumers[0].manifest.asset_name") {
|
|
t.Fatalf("expected autogen asset_name validation error, got %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "autogen.consumers[0].manifest.cache_name") {
|
|
t.Fatalf("expected autogen cache_name validation error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestScanAllowsMissingAssetsDir(t *testing.T) {
|
|
root := t.TempDir()
|
|
mkdirAll(t, filepath.Join(root, "src"))
|
|
mkdirAll(t, filepath.Join(root, "build"))
|
|
|
|
proj := &Project{
|
|
Root: root,
|
|
Config: Config{
|
|
Module: ModuleConfig{Name: "Test", ResRef: "test"},
|
|
Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"},
|
|
},
|
|
}
|
|
|
|
if err := proj.Scan(); err != nil {
|
|
t.Fatalf("Scan returned error: %v", err)
|
|
}
|
|
if len(proj.Inventory.AssetFiles) != 0 {
|
|
t.Fatalf("expected no asset files, got %#v", proj.Inventory.AssetFiles)
|
|
}
|
|
}
|
|
|
|
func mkdirAll(t *testing.T, path string) {
|
|
t.Helper()
|
|
if err := os.MkdirAll(path, 0o755); err != nil {
|
|
t.Fatalf("mkdir %s: %v", path, err)
|
|
}
|
|
}
|
|
|
|
func TestCloneWithHAKNamesFiltersConfiguredHAKs(t *testing.T) {
|
|
proj := &Project{
|
|
Root: "/tmp/test",
|
|
Config: Config{
|
|
Module: ModuleConfig{Name: "Test", ResRef: "test"},
|
|
Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"},
|
|
HAKs: []HAKConfig{
|
|
{Name: "sow_over", Priority: 1, Include: []string{"over/**"}},
|
|
{Name: "sow_core", Priority: 2, Include: []string{"core/**"}},
|
|
{Name: "sow_item", Priority: 3, Include: []string{"item/**"}},
|
|
},
|
|
},
|
|
}
|
|
|
|
filtered, err := proj.CloneWithHAKNames([]string{"sow_core", "sow_item"})
|
|
if err != nil {
|
|
t.Fatalf("CloneWithHAKNames returned error: %v", err)
|
|
}
|
|
|
|
if filtered == proj {
|
|
t.Fatalf("expected filtered clone, got original pointer")
|
|
}
|
|
|
|
got := make([]string, 0, len(filtered.Config.HAKs))
|
|
for _, hak := range filtered.Config.HAKs {
|
|
got = append(got, hak.Name)
|
|
}
|
|
want := []string{"sow_core", "sow_item"}
|
|
if !slices.Equal(got, want) {
|
|
t.Fatalf("unexpected filtered haks: got %v want %v", got, want)
|
|
}
|
|
|
|
if len(proj.Config.HAKs) != 3 {
|
|
t.Fatalf("original project config should remain unchanged, got %d haks", len(proj.Config.HAKs))
|
|
}
|
|
}
|
|
|
|
func TestCloneWithHAKNamesRejectsUnknownHAKs(t *testing.T) {
|
|
proj := &Project{
|
|
Config: Config{
|
|
HAKs: []HAKConfig{
|
|
{Name: "sow_core", Priority: 1, Include: []string{"core/**"}},
|
|
},
|
|
},
|
|
}
|
|
|
|
if _, err := proj.CloneWithHAKNames([]string{"missing_hak"}); err == nil {
|
|
t.Fatal("expected error for unknown hak name")
|
|
}
|
|
}
|