blob: cc6200e071005cf98b698a12d26e437136f02025 [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
import (
"errors"
"reflect"
"sync"
"testing"
"go.fuchsia.dev/jiri"
)
func TestGroupProjectsByDepth(t *testing.T) {
rootPath := "/root"
projects := []Project{
{Name: "p1", Path: "/root/p1"},
{Name: "p2", Path: "/root/p1/p2"},
{Name: "p3", Path: "/root/p3"},
{Name: "p4", Path: "/root/p3/p4/p5"},
}
groups, depths, err := groupProjectsByDepth(projects, rootPath)
if err != nil {
t.Fatalf("groupProjectsByDepth failed: %v", err)
}
expectedDepths := []int{1, 2, 3}
if !reflect.DeepEqual(depths, expectedDepths) {
t.Errorf("Expected depths %v, got %v", expectedDepths, depths)
}
expectedGroups := map[int][]Project{
1: {
{Name: "p1", Path: "/root/p1"},
{Name: "p3", Path: "/root/p3"},
},
2: {
{Name: "p2", Path: "/root/p1/p2"},
},
3: {
{Name: "p4", Path: "/root/p3/p4/p5"},
},
}
if len(groups) != len(expectedGroups) {
t.Errorf("Expected %d groups, got %d", len(expectedGroups), len(groups))
}
for depth, expectedProjs := range expectedGroups {
projs, ok := groups[depth]
if !ok {
t.Errorf("Expected group for depth %d, but none found", depth)
continue
}
if len(projs) != len(expectedProjs) {
t.Errorf("Expected %d projects for depth %d, got %d", len(expectedProjs), depth, len(projs))
continue
}
for i, expectedProj := range expectedProjs {
if projs[i].Name != expectedProj.Name || projs[i].Path != expectedProj.Path {
t.Errorf("Expected project %v at index %d of depth %d, got %v", expectedProj, i, depth, projs[i])
}
}
}
}
func TestRunProjectsParallel(t *testing.T) {
jirix := &jiri.X{}
jirix.Jobs = 2
projects := []Project{
{Name: "p1"},
{Name: "p2"},
{Name: "p3"},
}
// Test successful run
var mu sync.Mutex
var processed []string
err := runProjectsParallel(jirix, projects, func(p Project) error {
mu.Lock()
processed = append(processed, p.Name)
mu.Unlock()
return nil
})
if err != nil {
t.Fatalf("runProjectsParallel failed: %v", err)
}
if len(processed) != 3 {
t.Errorf("Expected 3 projects to be processed, got %v", processed)
}
// Test error collection
err = runProjectsParallel(jirix, projects, func(p Project) error {
if p.Name == "p2" || p.Name == "p3" {
return errors.New("error: " + p.Name)
}
return nil
})
if err == nil {
t.Fatalf("Expected runProjectsParallel to fail, but it succeeded")
}
errMsg := err.Error()
if errMsg != "error: p2\nerror: p3" && errMsg != "error: p3\nerror: p2" {
t.Errorf("Unexpected error message: %q", errMsg)
}
}
func TestRunProjectsParallelWithDepth(t *testing.T) {
jirix := &jiri.X{}
jirix.Jobs = 2
rootPath := "/root"
projects := []Project{
{Name: "p1", Path: "/root/p1"},
{Name: "p2", Path: "/root/p1/p2"},
{Name: "p3", Path: "/root/p3"},
{Name: "p4", Path: "/root/p3/p4/p5"},
}
// Test normal order (shallower to deeper)
var mu sync.Mutex
var processed []string
err := runProjectsParallelWithDepth(jirix, projects, rootPath, false /*reverse*/, func(p Project) error {
mu.Lock()
processed = append(processed, p.Name)
mu.Unlock()
return nil
})
if err != nil {
t.Fatalf("runProjectsParallelWithDepth failed: %v", err)
}
// We expect p1 and p3 (depth 1) to be processed before p2 (depth 2), which is processed before p4 (depth 3).
p1Index, p2Index, p3Index, p4Index := -1, -1, -1, -1
for i, name := range processed {
switch name {
case "p1":
p1Index = i
case "p2":
p2Index = i
case "p3":
p3Index = i
case "p4":
p4Index = i
}
}
if p1Index > p2Index || p3Index > p2Index {
t.Errorf("Expected p1/p3 to be processed before p2. Order: %v", processed)
}
if p2Index > p4Index {
t.Errorf("Expected p2 to be processed before p4. Order: %v", processed)
}
// Test reverse order (deeper to shallower)
processed = nil
err = runProjectsParallelWithDepth(jirix, projects, rootPath, true /*reverse*/, func(p Project) error {
mu.Lock()
processed = append(processed, p.Name)
mu.Unlock()
return nil
})
if err != nil {
t.Fatalf("runProjectsParallelWithDepth failed: %v", err)
}
// We expect p4 (depth 3) to be processed before p2 (depth 2), which is processed before p1/p3 (depth 1).
for i, name := range processed {
switch name {
case "p1":
p1Index = i
case "p2":
p2Index = i
case "p3":
p3Index = i
case "p4":
p4Index = i
}
}
if p4Index > p2Index {
t.Errorf("Expected p4 to be processed before p2 (reverse). Order: %v", processed)
}
if p2Index > p1Index || p2Index > p3Index {
t.Errorf("Expected p2 to be processed before p1/p3 (reverse). Order: %v", processed)
}
}