blob: c9e02f2b1d21a07ba6de1f01c4dea1b21d35b1b2 [file]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package project_test
import (
"os"
"path/filepath"
"testing"
"go.fuchsia.dev/jiri/jiritest"
"go.fuchsia.dev/jiri/project"
)
func TestRestorePackageFromCache(t *testing.T) {
fake := jiritest.NewFakeJiriRoot(t)
pkg := project.Package{
Name: "test/pkg",
Path: "prebuilt/test",
Version: "v1",
}
destRelPath, err := pkg.ResolvePath()
if err != nil {
t.Fatal(err)
}
destDir := filepath.Join(fake.X.Root, destRelPath)
groups := map[string][]project.Package{
destRelPath: {pkg},
}
hash := project.InternalComputeGroupHash(groups[destRelPath])
cacheDir := filepath.Join(fake.X.PackageCacheDir(), hash)
// Case: Destination is not a symlink.
t.Run("NotASymlink", func(t *testing.T) {
if err := os.MkdirAll(destDir, 0755); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(destDir)
testFile := filepath.Join(destDir, "file")
if err := os.WriteFile(testFile, []byte("content"), 0644); err != nil {
t.Fatal(err)
}
if err := project.InternalRestorePackageFromCache(fake.X, pkg, groups); err != nil {
t.Errorf("expected no error, got %v", err)
}
// Verify destDir is still a directory and file exists.
fi, err := os.Lstat(destDir)
if err != nil {
t.Fatal(err)
}
if fi.Mode()&os.ModeSymlink != 0 {
t.Error("expected directory, got symlink")
}
if _, err := os.Stat(testFile); err != nil {
t.Errorf("expected file to exist, got %v", err)
}
})
// Case: Destination is a symlink, cache exists, restore successful.
t.Run("SymlinkAndCacheExists", func(t *testing.T) {
if err := os.MkdirAll(cacheDir, 0755); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(cacheDir)
cachedFile := filepath.Join(cacheDir, "cached_file")
if err := os.WriteFile(cachedFile, []byte("cached_content"), 0644); err != nil {
t.Fatal(err)
}
if err := os.Symlink(cacheDir, destDir); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(destDir)
if err := project.InternalRestorePackageFromCache(fake.X, pkg, groups); err != nil {
t.Errorf("expected no error, got %v", err)
}
// Verify destDir is now a real directory (not a symlink) and has the cached file.
fi, err := os.Lstat(destDir)
if err != nil {
t.Fatal(err)
}
if fi.Mode()&os.ModeSymlink != 0 {
t.Error("expected directory after restore, got symlink")
}
gotContent, err := os.ReadFile(filepath.Join(destDir, "cached_file"))
if err != nil {
t.Fatal(err)
}
if string(gotContent) != "cached_content" {
t.Errorf("expected cached_content, got %q", string(gotContent))
}
})
// Case: Destination is a symlink, but cache does not exist.
t.Run("SymlinkAndNoCache", func(t *testing.T) {
// Ensure cache dir does not exist
if err := os.RemoveAll(cacheDir); err != nil {
t.Fatal(err)
}
// Create destDir as a symlink to a non-existent cacheDir
if err := os.Symlink(cacheDir, destDir); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(destDir)
if err := project.InternalRestorePackageFromCache(fake.X, pkg, groups); err != nil {
t.Errorf("expected no error, got %v", err)
}
// Verify destDir (the symlink) is removed so that CIPD can download it.
_, err := os.Lstat(destDir)
if !os.IsNotExist(err) {
t.Errorf("expected destDir to be removed, got err: %v", err)
}
})
}
func TestIsPackageGroupCached(t *testing.T) {
fake := jiritest.NewFakeJiriRoot(t)
// Enable cache
fake.X.PackageCacheEnabled = true
pkg := project.Package{
Name: "test/pkg",
Path: "prebuilt/test",
Version: "v1",
}
groupPkgs := []project.Package{pkg}
hash := project.InternalComputeGroupHash(groupPkgs)
cacheDir := filepath.Join(fake.X.PackageCacheDir(), hash)
// Case 1: Cache is cold (doesn't exist)
t.Run("CacheCold", func(t *testing.T) {
if project.InternalIsPackageGroupCached(fake.X, groupPkgs) {
t.Error("expected false for cold cache, got true")
}
})
// Case 2: Cache is hot (exists and is directory)
t.Run("CacheHot", func(t *testing.T) {
if err := os.MkdirAll(cacheDir, 0755); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(cacheDir)
if !project.InternalIsPackageGroupCached(fake.X, groupPkgs) {
t.Error("expected true for hot cache, got false")
}
})
// Case 3: Cache exists but is a symlink (incorrect state)
t.Run("CacheIsSymlink", func(t *testing.T) {
// Create a target for symlink
targetDir := filepath.Join(t.TempDir(), "target")
if err := os.MkdirAll(targetDir, 0755); err != nil {
t.Fatal(err)
}
if err := os.Symlink(targetDir, cacheDir); err != nil {
t.Fatal(err)
}
defer os.Remove(cacheDir)
if project.InternalIsPackageGroupCached(fake.X, groupPkgs) {
t.Error("expected false when cache is a symlink, got true")
}
})
}
func TestPartitionPackages(t *testing.T) {
fake := jiritest.NewFakeJiriRoot(t)
// Define 3 packages:
// - pkg1 and pkg2 go to same destination (same group)
// - pkg3 goes to a different destination (different group)
pkg1 := project.Package{Name: "pkg1", Path: "dir1", Version: "v1"}
pkg2 := project.Package{Name: "pkg2", Path: "dir1", Version: "v1"} // same dest, same group
pkg3 := project.Package{Name: "pkg3", Path: "dir2", Version: "v1"} // diff dest, diff group
pkgs := project.Packages{
pkg1.Key(): pkg1,
pkg2.Key(): pkg2,
pkg3.Key(): pkg3,
}
// Case 1: Cache is disabled. All should be uncached.
t.Run("CacheDisabled", func(t *testing.T) {
fake.X.PackageCacheEnabled = false
cached, uncached, err := project.InternalPartitionPackages(fake.X, pkgs)
if err != nil {
t.Fatal(err)
}
if len(cached) != 0 {
t.Errorf("expected 0 cached packages, got %d", len(cached))
}
if len(uncached) != 3 {
t.Errorf("expected 3 uncached packages, got %d", len(uncached))
}
})
// Case 2: Cache is enabled.
t.Run("CacheEnabled", func(t *testing.T) {
fake.X.PackageCacheEnabled = true
cacheDir := fake.X.PackageCacheDir()
// Cache the group for pkg1 and pkg2, but NOT pkg3
group1 := []project.Package{pkg1, pkg2}
hash1 := project.InternalComputeGroupHash(group1)
pkg1And2CacheDir := filepath.Join(cacheDir, hash1)
if err := os.MkdirAll(pkg1And2CacheDir, 0755); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(pkg1And2CacheDir)
cached, uncached, err := project.InternalPartitionPackages(fake.X, pkgs)
if err != nil {
t.Fatal(err)
}
// pkg1 and pkg2 should be cached
if len(cached) != 2 {
t.Errorf("expected 2 cached packages, got %d", len(cached))
}
if _, ok := cached[pkg1.Key()]; !ok {
t.Errorf("expected pkg1 to be cached")
}
if _, ok := cached[pkg2.Key()]; !ok {
t.Errorf("expected pkg2 to be cached")
}
// pkg3 should be uncached
if len(uncached) != 1 {
t.Errorf("expected 1 uncached package, got %d", len(uncached))
}
if _, ok := uncached[pkg3.Key()]; !ok {
t.Errorf("expected pkg3 to be uncached")
}
})
}