blob: 968438213e0af540030e36aea39fdf23a489fc2e [file] [log] [blame]
// Copyright 2021 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 fint
import (
"context"
"errors"
"fmt"
"io"
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
"go.fuchsia.dev/fuchsia/tools/lib/jsonutil"
)
// gnAnalyzeRunner conforms to the subprocessRunner interface but is
// specifically intended for testing functions that are only intended to use it
// to make a single `gn analyze` call.
type gnAnalyzeRunner struct {
output gnAnalyzeOutput
// Remaining fields are to be set by `Run()`.
// The exact command that was run.
cmd []string
// The JSON input that was passed to the gn analyze command.
input gnAnalyzeInput
}
func (r *gnAnalyzeRunner) Run(_ context.Context, cmd []string, _, _ io.Writer) error {
if len(r.cmd) != 0 {
return errors.New("Run() can only be called once per runner")
}
r.cmd = cmd
// Relying on index is somewhat fragile but easier than the alternatives.
inputPath := cmd[3]
outputPath := cmd[4]
if err := jsonutil.ReadFromFile(inputPath, &r.input); err != nil {
return fmt.Errorf("failed to read GN analyze input: %w", err)
}
return jsonutil.WriteToFile(outputPath, r.output)
}
func (r *gnAnalyzeRunner) RunWithStdin(_ context.Context, _ []string, _, _ io.Writer, _ io.Reader) error {
return nil
}
func TestShouldBuild(t *testing.T) {
ctx := context.Background()
testCases := []struct {
name string
files []string
mockOutput gnAnalyzeOutput
// Whether we expect `gn analyze` to be run in this test case.
expectNoCommand bool
// Whether this test case should determine that the build graph is
// affected.
expectShouldBuild bool
// Whether this test case is expected to cause the function to return an
// error.
expectErr bool
}{
{
name: "unknown file type",
files: []string{"foo.invalid"},
expectNoCommand: true,
expectShouldBuild: true,
},
{
name: "affected",
mockOutput: gnAnalyzeOutput{
Status: buildGraphAffectedStatus,
},
expectShouldBuild: true,
},
{
name: "unaffected",
mockOutput: gnAnalyzeOutput{
Status: buildGraphNotAffectedStatus,
},
expectShouldBuild: false,
},
{
name: "unknown status",
mockOutput: gnAnalyzeOutput{
Status: unknownAffectedStatus,
},
expectShouldBuild: true,
},
{
name: "invalid status",
mockOutput: gnAnalyzeOutput{
Status: "invalid",
},
expectErr: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if len(tc.files) == 0 {
tc.files = []string{"foo/bar.cc", "baz.py"}
}
runner := &gnAnalyzeRunner{
output: tc.mockOutput,
}
platform := "linux-x64"
buildDir := filepath.Join(t.TempDir(), "build")
checkoutDir := filepath.Join(t.TempDir(), "checkout")
gotShouldBuild, err := shouldBuild(ctx, runner, buildDir, checkoutDir, platform, tc.files)
if tc.expectErr && err == nil {
t.Fatalf("Expected error but analysis succeeded")
} else if !tc.expectErr && err != nil {
t.Fatalf("Unexpected analysis error: %s", err)
}
if gotShouldBuild != tc.expectShouldBuild {
t.Errorf("Wrong affected result: wanted %v, got %v", tc.expectShouldBuild, gotShouldBuild)
}
if tc.expectNoCommand {
if len(runner.cmd) != 0 {
t.Fatalf("Expected no command to be run, but this command was run: %v", runner.cmd)
}
// The remaining assertions deal only with the case where gn
// analyze is expected to be run.
return
}
expectedCmd := []string{thirdPartyPrebuilt(checkoutDir, platform, "gn"), "analyze", buildDir}
if diff := cmp.Diff(runner.cmd[:len(expectedCmd)], expectedCmd); diff != "" {
t.Fatalf("Wrong gn command run (-want +got):\n%s", diff)
}
if diff := cmp.Diff(formatFilePaths(tc.files), runner.input.Files); diff != "" {
t.Fatalf("gn analyze received the wrong list of files (-want +got):\n%s", diff)
}
})
}
}
func TestCanAnalyzeFiles(t *testing.T) {
ctx := context.Background()
testCases := []struct {
name string
files []string
expectedCanAnalyze bool
}{
{
name: "no files",
files: []string{},
expectedCanAnalyze: true,
},
{
name: "all file types supported",
files: []string{"foo.py", "baz.go", "abc/def.cc", "abc/OWNERS"},
expectedCanAnalyze: true,
},
{
name: "some file types unsupported",
files: []string{"foo.py", "bar/baz.invalid"},
expectedCanAnalyze: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
canAnalyze := canAnalyzeFiles(ctx, tc.files)
if canAnalyze != tc.expectedCanAnalyze {
t.Errorf("Wrong canAnalyze result: wanted %v, got %v", tc.expectedCanAnalyze, canAnalyze)
}
})
}
}