xref: /aosp_15_r20/tools/treble/build/treble_build/report/report_test.go (revision 105f628577ac4ba0e277a494fbb614ed8c12a994)
1// Copyright 2022 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package report
16
17import (
18	"context"
19	"errors"
20	"fmt"
21	"reflect"
22	"strconv"
23	"testing"
24
25	"tools/treble/build/report/app"
26)
27
28type reportTest struct {
29	manifest   *app.RepoManifest
30	commands   map[string]*app.BuildCommand
31	inputs     map[string]*app.BuildInput
32	queries    map[string]*app.BuildQuery
33	paths      map[string]map[string]*app.BuildPath
34	multipaths map[string]map[string][]*app.BuildPath
35	projects   map[string]*app.GitProject
36	commits    map[*app.GitProject]map[string]*app.GitCommit
37
38	deps           *app.BuildDeps
39	projectCommits map[string]int
40}
41
42func (r *reportTest) Manifest(filename string) (*app.RepoManifest, error) {
43	var err error
44	out := r.manifest
45	if out == nil {
46		err = errors.New(fmt.Sprintf("No manifest named %s", filename))
47	}
48	return r.manifest, err
49}
50func (r *reportTest) Command(ctx context.Context, target string) (*app.BuildCommand, error) {
51	var err error
52	out := r.commands[target]
53	if out == nil {
54		err = errors.New(fmt.Sprintf("No command for target %s", target))
55	}
56	return out, err
57}
58
59func (r *reportTest) Input(ctx context.Context, target string) (*app.BuildInput, error) {
60	var err error
61	out := r.inputs[target]
62	if out == nil {
63		err = errors.New(fmt.Sprintf("No inputs for target %s", target))
64	}
65	return out, err
66}
67
68func (r *reportTest) Query(ctx context.Context, target string) (*app.BuildQuery, error) {
69	var err error
70	out := r.queries[target]
71	if out == nil {
72		err = errors.New(fmt.Sprintf("No queries for target %s", target))
73	}
74	return out, err
75}
76
77func (r *reportTest) Path(ctx context.Context, target string, dependency string) (*app.BuildPath, error) {
78	return r.paths[target][dependency], nil
79}
80
81func (r *reportTest) Paths(ctx context.Context, target string, dependency string) ([]*app.BuildPath, error) {
82	return r.multipaths[target][dependency], nil
83}
84
85func (r *reportTest) Deps(ctx context.Context) (*app.BuildDeps, error) {
86	return r.deps, nil
87}
88func (r *reportTest) Project(ctx context.Context, path string, gitDir string, remote string, revision string) (*app.GitProject, error) {
89	var err error
90	out := r.projects[path]
91	if out == nil {
92		err = errors.New(fmt.Sprintf("No projects for target %s", path))
93	}
94	return out, err
95}
96func (r *reportTest) PopulateFiles(ctx context.Context, proj *app.GitProject, upstream string) error {
97	return nil
98}
99func (r *reportTest) CommitInfo(ctx context.Context, proj *app.GitProject, sha string) (*app.GitCommit, error) {
100	var err error
101	out := r.commits[proj][sha]
102	if out == nil {
103		err = errors.New(fmt.Sprintf("No commit for sha %s", sha))
104	}
105	return out, err
106}
107
108// Helper routine used in test function to create array of unique names
109func createStrings(name string, count int) []string {
110	var out []string
111	for i := 0; i < count; i++ {
112		out = append(out, name+strconv.Itoa(i))
113	}
114	return out
115}
116
117// Project names used in tests
118func projName(i int) string {
119	return "proj." + strconv.Itoa(i)
120}
121
122func fileName(i int) (filename string, sha string) {
123	iString := strconv.Itoa(i)
124	return "source." + iString, "sha." + iString
125}
126func createFile(i int) *app.GitTreeObj {
127	fname, sha := fileName(i)
128	return &app.GitTreeObj{Permissions: "100644", Type: "blob", Filename: fname, Sha: sha}
129}
130func createProject(name string) *app.GitProject {
131	return &app.GitProject{
132		RepoDir: name, WorkDir: name, GitDir: ".git", Remote: "origin",
133		RemoteUrl: "origin_url", Revision: name + "_sha",
134		Files: make(map[string]*app.GitTreeObj)}
135
136}
137
138// Create basic test data for given inputs
139func createTest(projCount int, fileCount int) *reportTest {
140	test := &reportTest{
141		manifest: &app.RepoManifest{
142			Remotes:  []app.RepoRemote{{Name: "remote1", Revision: "revision_1"}},
143			Default:  app.RepoDefault{Remote: "remote1", Revision: "revision_2"},
144			Projects: []app.RepoProject{},
145		},
146		commands: map[string]*app.BuildCommand{},
147		inputs:   map[string]*app.BuildInput{},
148		queries:  map[string]*app.BuildQuery{},
149		projects: map[string]*app.GitProject{},
150		commits:  map[*app.GitProject]map[string]*app.GitCommit{},
151	}
152
153	// Create projects with files
154	for i := 0; i <= projCount; i++ {
155		name := projName(i)
156
157		proj := createProject(name)
158
159		for i := 0; i <= fileCount; i++ {
160			treeObj := createFile(i)
161			proj.Files[treeObj.Filename] = treeObj
162
163		}
164		test.projects[name] = proj
165		test.manifest.Projects = append(test.manifest.Projects,
166			app.RepoProject{Groups: "group", Name: name, Revision: "sha", Path: name})
167
168	}
169	return test
170}
171
172func Test_report(t *testing.T) {
173
174	test := createTest(10, 20)
175
176	// Test cases will specify input file by project and file index
177	type inputFile struct {
178		proj int
179		file int
180	}
181
182	targetDefs := []struct {
183		name          string      // Target name
184		cmds          int         // Number of build steps
185		inputTargets  int         // Number of input targets
186		outputTargets int         // Number of output targets
187		inputFiles    []inputFile // Input files for target
188	}{
189		{
190			name:          "target",
191			cmds:          7,
192			inputTargets:  4,
193			outputTargets: 7,
194			inputFiles:    []inputFile{{proj: 0, file: 1}, {proj: 1, file: 0}},
195		},
196		{
197			name:          "target2",
198			cmds:          0,
199			inputTargets:  0,
200			outputTargets: 0,
201			inputFiles:    []inputFile{{proj: 0, file: 1}, {proj: 0, file: 2}, {proj: 1, file: 0}},
202		},
203		{
204			name:          "null_target",
205			cmds:          0,
206			inputTargets:  0,
207			outputTargets: 0,
208			inputFiles:    []inputFile{},
209		},
210	}
211
212	// Create target data based on definitions
213	var targets []string
214
215	// Build expected output while creating the targets
216	resTargets := make(map[string]*app.BuildTarget)
217
218	for _, target := range targetDefs {
219
220		res := &app.BuildTarget{Name: target.name,
221			Steps:     target.cmds,
222			FileCount: len(target.inputFiles),
223			Projects:  make(map[string]*app.GitProject),
224		}
225
226		// Add files to the build target
227		var inputFiles []string
228		for _, in := range target.inputFiles {
229			// Get project by name
230			pName := projName(in.proj)
231			bf := createFile(in.file)
232			p := test.projects[pName]
233
234			inputFiles = append(inputFiles,
235				fmt.Sprintf("%s/%s", p.WorkDir, bf.Filename))
236
237			if _, exists := res.Projects[pName]; !exists {
238				res.Projects[pName] = createProject(pName)
239			}
240			res.Projects[pName].Files[bf.Filename] = bf
241		}
242
243		// Create test data
244		test.commands[target.name] = &app.BuildCommand{Target: target.name, Cmds: createStrings("cmd.", target.cmds)}
245		test.inputs[target.name] = &app.BuildInput{Target: target.name, Files: inputFiles}
246		test.queries[target.name] = &app.BuildQuery{
247			Target:  target.name,
248			Inputs:  createStrings("target.in.", target.inputTargets),
249			Outputs: createStrings("target.out.", target.outputTargets)}
250
251		targets = append(targets, target.name)
252		resTargets[res.Name] = res
253	}
254
255	rtx := &Context{RepoBase: "/src", Repo: test, Build: test, Project: test, WorkerCount: 1, BuildWorkerCount: 1}
256	rtx.ResolveProjectMap(nil, "test_file", "")
257	req := &app.ReportRequest{Targets: targets}
258	rsp, err := RunReport(nil, rtx, req)
259	if err != nil {
260		t.Errorf("Failed to run report for request %+v", req)
261	} else {
262		if !reflect.DeepEqual(rsp.Targets, resTargets) {
263			t.Errorf("Got targets %+v, expected %+v", rsp.Targets, resTargets)
264		}
265	}
266}
267