blob: 0e53f87c47a93d049d1532d36357b475ecadb710 [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 cli
import (
"context"
"flag"
"os"
"path/filepath"
"testing"
"go.fuchsia.dev/fuchsia/src/testing/host-target-testing/artifacts"
)
func TestRepeatableBuildVarParsing(t *testing.T) {
for _, tc := range []struct {
name string
kind repeatableBuildKind
input string
expectedValue string
expectedVersion string
expectError bool
}{
{
name: "build id plain",
kind: buildIdKind,
input: "12345",
expectedValue: "12345",
expectedVersion: "latest",
},
{
name: "build id with latest",
kind: buildIdKind,
input: "12345:latest",
expectedValue: "12345",
expectedVersion: "latest",
},
{
name: "build id with fromApiLevel",
kind: buildIdKind,
input: "12345:fromApiLevel",
expectedValue: "12345",
expectedVersion: "fromApiLevel",
},
{
name: "builder name",
kind: builderNameKind,
input: "core.x64-release",
expectedValue: "core.x64-release",
},
{
name: "fuchsia build dir",
kind: fuchsiaBuildDirKind,
input: "/path/to/out/default",
expectedValue: "/path/to/out/default",
},
{
name: "product bundle dir",
kind: productBundleDirKind,
input: "/path/to/pb",
expectedValue: "/path/to/pb",
},
{
name: "empty string error",
kind: buildIdKind,
input: "",
expectError: true,
},
{
name: "invalid version policy error",
kind: buildIdKind,
input: "12345:invalid_policy",
expectError: true,
},
{
name: "blob fetch mode not allowed in version specifier",
kind: buildIdKind,
input: "12345:lazy",
expectError: true,
},
{
name: "blob fetch mode prefetch not allowed in version specifier",
kind: buildIdKind,
input: "12345:prefetch",
expectError: true,
},
{
name: "too many parts in specifier error",
kind: buildIdKind,
input: "12345:latest:lazy",
expectError: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
c := &RepeatableBuildConfig{}
rbVar := repeatableBuildVar{c: c, kind: tc.kind}
err := rbVar.Set(tc.input)
if tc.expectError {
if err == nil {
t.Fatalf("expected error for input %q, got nil", tc.input)
}
return
}
if err != nil {
t.Fatalf("unexpected error for input %q: %v", tc.input, err)
}
if len(c.builds) != 1 {
t.Fatalf("expected 1 build, got %d", len(c.builds))
}
b := c.builds[0]
if b.kind != tc.kind {
t.Fatalf("expected kind %v, got %v", tc.kind, b.kind)
}
if b.value != tc.expectedValue {
t.Fatalf("expected value %q, got %q", tc.expectedValue, b.value)
}
if tc.expectedVersion != "" && b.versionPolicy != tc.expectedVersion {
t.Fatalf("expected versionPolicy %q, got %q", tc.expectedVersion, b.versionPolicy)
}
})
}
}
func TestRepeatableBuildConfigBlobFetchModeDefault(t *testing.T) {
fs := flag.NewFlagSet("test", flag.ContinueOnError)
c := NewRepeatableBuildConfig(fs, nil, nil, "", "")
args := []string{
"-fuchsia-build-dir", "/tmp/dir1",
"-product-bundle-dir", "/tmp/dir2",
}
if err := fs.Parse(args); err != nil {
t.Fatalf("failed to parse flags: %v", err)
}
if c.blobFetchMode != artifacts.Unspecified {
t.Fatalf("expected default blobFetchMode Unspecified, got %v", c.blobFetchMode)
}
builds, err := c.GetBuilds(context.Background(), nil, "")
if err != nil {
t.Fatalf("GetBuilds failed: %v", err)
}
if len(builds) != 2 {
t.Fatalf("expected 2 builds, got %d", len(builds))
}
for i, b := range builds {
if b.BlobFetchMode != artifacts.Unspecified {
t.Fatalf("build %d: expected Unspecified, got %v", i, b.BlobFetchMode)
}
}
}
func TestRepeatableBuildConfigBlobFetchModeConfigured(t *testing.T) {
for _, modeStr := range []string{"lazy", "prefetch"} {
t.Run(modeStr, func(t *testing.T) {
fs := flag.NewFlagSet("test", flag.ContinueOnError)
c := NewRepeatableBuildConfig(fs, nil, nil, "", "")
args := []string{
"-blob-fetch-mode", modeStr,
"-fuchsia-build-dir", "/tmp/dir1",
}
if err := fs.Parse(args); err != nil {
t.Fatalf("failed to parse flags: %v", err)
}
expectedMode, err := artifacts.ParseBlobFetchMode(modeStr)
if err != nil {
t.Fatalf("failed to parse mode: %v", err)
}
if c.blobFetchMode != expectedMode {
t.Fatalf("expected blobFetchMode %v, got %v", expectedMode, c.blobFetchMode)
}
builds, err := c.GetBuilds(context.Background(), nil, "")
if err != nil {
t.Fatalf("GetBuilds failed: %v", err)
}
if len(builds) != 1 {
t.Fatalf("expected 1 build, got %d", len(builds))
}
if builds[0].BlobFetchMode != expectedMode {
t.Fatalf("expected %v, got %v", expectedMode, builds[0].BlobFetchMode)
}
if builds[0].ProductBundle == nil {
t.Fatalf("expected ProductBundle to be populated, got nil")
}
})
}
}
func TestRepeatableBuildConfigProductBundleName(t *testing.T) {
ctx := context.Background()
tmpDir := t.TempDir()
pbJSON := `[
{"name": "core.arm64", "path": "obj/build/images/fuchsia/product_bundle"},
{"name": "workbench_eng.arm64", "path": "obj/build/images/fuchsia/workbench_eng.arm64/product_bundle"}
]`
if err := os.WriteFile(filepath.Join(tmpDir, "product_bundles.json"), []byte(pbJSON), 0o644); err != nil {
t.Fatal(err)
}
// Multiple product bundles without -product-bundle-name should fail.
fs := flag.NewFlagSet("test", flag.ContinueOnError)
c := NewRepeatableBuildConfig(fs, nil, nil, "", "")
if err := fs.Parse([]string{"-fuchsia-build-dir", tmpDir}); err != nil {
t.Fatalf("failed to parse flags: %v", err)
}
if _, err := c.GetBuilds(ctx, nil, ""); err == nil {
t.Fatal("expected error for multiple product bundles without -product-bundle-name, got nil")
}
// Specifying -product-bundle-name resolves the matching ProductBundle.
fs = flag.NewFlagSet("test", flag.ContinueOnError)
c = NewRepeatableBuildConfig(fs, nil, nil, "", "")
if err := fs.Parse([]string{
"-fuchsia-build-dir", tmpDir,
"-product-bundle-name", "workbench_eng.arm64",
}); err != nil {
t.Fatalf("failed to parse flags: %v", err)
}
builds, err := c.GetBuilds(ctx, nil, "")
if err != nil {
t.Fatalf("GetBuilds failed: %v", err)
}
if len(builds) != 1 {
t.Fatalf("expected 1 build, got %d", len(builds))
}
pbDir, err := builds[0].ProductBundle.GetProductBundleDir(ctx)
if err != nil {
t.Fatalf("GetProductBundleDir failed: %v", err)
}
wantDir := filepath.Join(tmpDir, "obj/build/images/fuchsia/workbench_eng.arm64/product_bundle")
if pbDir != wantDir {
t.Errorf("got %q, want %q", pbDir, wantDir)
}
}