blob: 7aa3891e7e34c047699f4cf00337f9c764685007 [file] [log] [blame]
// Copyright 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package report
import (
"context"
"errors"
"fmt"
"reflect"
"strconv"
"testing"
"tools/treble/build/report/app"
)
type reportTest struct {
manifest *app.RepoManifest
commands map[string]*app.BuildCommand
inputs map[string]*app.BuildInput
queries map[string]*app.BuildQuery
paths map[string]map[string]*app.BuildPath
multipaths map[string]map[string][]*app.BuildPath
projects map[string]*app.GitProject
commits map[*app.GitProject]map[string]*app.GitCommit
deps *app.BuildDeps
projectCommits map[string]int
}
func (r *reportTest) Manifest(filename string) (*app.RepoManifest, error) {
var err error
out := r.manifest
if out == nil {
err = errors.New(fmt.Sprintf("No manifest named %s", filename))
}
return r.manifest, err
}
func (r *reportTest) Command(ctx context.Context, target string) (*app.BuildCommand, error) {
var err error
out := r.commands[target]
if out == nil {
err = errors.New(fmt.Sprintf("No command for target %s", target))
}
return out, err
}
func (r *reportTest) Input(ctx context.Context, target string) (*app.BuildInput, error) {
var err error
out := r.inputs[target]
if out == nil {
err = errors.New(fmt.Sprintf("No inputs for target %s", target))
}
return out, err
}
func (r *reportTest) Query(ctx context.Context, target string) (*app.BuildQuery, error) {
var err error
out := r.queries[target]
if out == nil {
err = errors.New(fmt.Sprintf("No queries for target %s", target))
}
return out, err
}
func (r *reportTest) Path(ctx context.Context, target string, dependency string) (*app.BuildPath, error) {
return r.paths[target][dependency], nil
}
func (r *reportTest) Paths(ctx context.Context, target string, dependency string) ([]*app.BuildPath, error) {
return r.multipaths[target][dependency], nil
}
func (r *reportTest) Deps(ctx context.Context) (*app.BuildDeps, error) {
return r.deps, nil
}
func (r *reportTest) Project(ctx context.Context, path string, gitDir string, remote string, revision string) (*app.GitProject, error) {
var err error
out := r.projects[path]
if out == nil {
err = errors.New(fmt.Sprintf("No projects for target %s", path))
}
return out, err
}
func (r *reportTest) PopulateFiles(ctx context.Context, proj *app.GitProject, upstream string) error {
return nil
}
func (r *reportTest) CommitInfo(ctx context.Context, proj *app.GitProject, sha string) (*app.GitCommit, error) {
var err error
out := r.commits[proj][sha]
if out == nil {
err = errors.New(fmt.Sprintf("No commit for sha %s", sha))
}
return out, err
}
// Helper routine used in test function to create array of unique names
func createStrings(name string, count int) []string {
var out []string
for i := 0; i < count; i++ {
out = append(out, name+strconv.Itoa(i))
}
return out
}
// Project names used in tests
func projName(i int) string {
return "proj." + strconv.Itoa(i)
}
func fileName(i int) (filename string, sha string) {
iString := strconv.Itoa(i)
return "source." + iString, "sha." + iString
}
func createFile(i int) *app.GitTreeObj {
fname, sha := fileName(i)
return &app.GitTreeObj{Permissions: "100644", Type: "blob", Filename: fname, Sha: sha}
}
func createProject(name string) *app.GitProject {
return &app.GitProject{
RepoDir: name, WorkDir: name, GitDir: ".git", Remote: "origin",
RemoteUrl: "origin_url", Revision: name + "_sha",
Files: make(map[string]*app.GitTreeObj)}
}
// Create basic test data for given inputs
func createTest(projCount int, fileCount int) *reportTest {
test := &reportTest{
manifest: &app.RepoManifest{
Remotes: []app.RepoRemote{{Name: "remote1", Revision: "revision_1"}},
Default: app.RepoDefault{Remote: "remote1", Revision: "revision_2"},
Projects: []app.RepoProject{},
},
commands: map[string]*app.BuildCommand{},
inputs: map[string]*app.BuildInput{},
queries: map[string]*app.BuildQuery{},
projects: map[string]*app.GitProject{},
commits: map[*app.GitProject]map[string]*app.GitCommit{},
}
// Create projects with files
for i := 0; i <= projCount; i++ {
name := projName(i)
proj := createProject(name)
for i := 0; i <= fileCount; i++ {
treeObj := createFile(i)
proj.Files[treeObj.Filename] = treeObj
}
test.projects[name] = proj
test.manifest.Projects = append(test.manifest.Projects,
app.RepoProject{Groups: "group", Name: name, Revision: "sha", Path: name})
}
return test
}
func Test_report(t *testing.T) {
test := createTest(10, 20)
// Test cases will specify input file by project and file index
type inputFile struct {
proj int
file int
}
targetDefs := []struct {
name string // Target name
cmds int // Number of build steps
inputTargets int // Number of input targets
outputTargets int // Number of output targets
inputFiles []inputFile // Input files for target
}{
{
name: "target",
cmds: 7,
inputTargets: 4,
outputTargets: 7,
inputFiles: []inputFile{{proj: 0, file: 1}, {proj: 1, file: 0}},
},
{
name: "target2",
cmds: 0,
inputTargets: 0,
outputTargets: 0,
inputFiles: []inputFile{{proj: 0, file: 1}, {proj: 0, file: 2}, {proj: 1, file: 0}},
},
{
name: "null_target",
cmds: 0,
inputTargets: 0,
outputTargets: 0,
inputFiles: []inputFile{},
},
}
// Create target data based on definitions
var targets []string
// Build expected output while creating the targets
resTargets := make(map[string]*app.BuildTarget)
for _, target := range targetDefs {
res := &app.BuildTarget{Name: target.name,
Steps: target.cmds,
FileCount: len(target.inputFiles),
Projects: make(map[string]*app.GitProject),
}
// Add files to the build target
var inputFiles []string
for _, in := range target.inputFiles {
// Get project by name
pName := projName(in.proj)
bf := createFile(in.file)
p := test.projects[pName]
inputFiles = append(inputFiles,
fmt.Sprintf("%s/%s", p.WorkDir, bf.Filename))
if _, exists := res.Projects[pName]; !exists {
res.Projects[pName] = createProject(pName)
}
res.Projects[pName].Files[bf.Filename] = bf
}
// Create test data
test.commands[target.name] = &app.BuildCommand{Target: target.name, Cmds: createStrings("cmd.", target.cmds)}
test.inputs[target.name] = &app.BuildInput{Target: target.name, Files: inputFiles}
test.queries[target.name] = &app.BuildQuery{
Target: target.name,
Inputs: createStrings("target.in.", target.inputTargets),
Outputs: createStrings("target.out.", target.outputTargets)}
targets = append(targets, target.name)
resTargets[res.Name] = res
}
rtx := &Context{RepoBase: "/src", Repo: test, Build: test, Project: test, WorkerCount: 1, BuildWorkerCount: 1}
rtx.ResolveProjectMap(nil, "test_file", "")
req := &app.ReportRequest{Targets: targets}
rsp, err := RunReport(nil, rtx, req)
if err != nil {
t.Errorf("Failed to run report for request %+v", req)
} else {
if !reflect.DeepEqual(rsp.Targets, resTargets) {
t.Errorf("Got targets %+v, expected %+v", rsp.Targets, resTargets)
}
}
}