• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2024 Google Inc. All rights reserved.
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 find_input_delta_lib
16
17import (
18	"errors"
19	"io/fs"
20	"testing"
21	"testing/fstest"
22	"time"
23
24	// For Assert*.
25	"android/soong/android"
26
27	fid_proto "android/soong/cmd/find_input_delta/find_input_delta_proto_internal"
28	"google.golang.org/protobuf/proto"
29)
30
31// Various state files
32
33func marshalProto(t *testing.T, message proto.Message) []byte {
34	data, err := proto.Marshal(message)
35	if err != nil {
36		t.Errorf("%v", err)
37	}
38	return data
39}
40
41func protoFile(name string, mtime_nsec int64, hash string, contents []*fid_proto.PartialCompileInput) (pci *fid_proto.PartialCompileInput) {
42	pci = &fid_proto.PartialCompileInput{
43		Name: proto.String(name),
44	}
45	if mtime_nsec != 0 {
46		pci.MtimeNsec = proto.Int64(mtime_nsec)
47	}
48	if len(hash) > 0 {
49		pci.Hash = proto.String(hash)
50	}
51	if contents != nil {
52		pci.Contents = contents
53	}
54	return
55}
56
57func TestLoadState(t *testing.T) {
58	testCases := []struct {
59		Name     string
60		Filename string
61		Mapfs    fs.ReadFileFS
62		Expected *fid_proto.PartialCompileInputs
63		Err      error
64	}{
65		{
66			Name:     "missing file",
67			Filename: "missing",
68			Mapfs:    fstest.MapFS{},
69			Expected: &fid_proto.PartialCompileInputs{},
70			Err:      nil,
71		},
72		{
73			Name:     "bad file",
74			Filename: ".",
75			Mapfs:    OsFs,
76			Expected: &fid_proto.PartialCompileInputs{},
77			Err:      errors.New("read failed"),
78		},
79		{
80			Name:     "file with mtime",
81			Filename: "state.old",
82			Mapfs: fstest.MapFS{
83				"state.old": &fstest.MapFile{
84					Data: marshalProto(t, &fid_proto.PartialCompileInputs{
85						InputFiles: []*fid_proto.PartialCompileInput{
86							protoFile("input1", 100, "", nil),
87						},
88					}),
89				},
90			},
91			Expected: &fid_proto.PartialCompileInputs{
92				InputFiles: []*fid_proto.PartialCompileInput{
93					protoFile("input1", 100, "", nil),
94				},
95			},
96			Err: nil,
97		},
98		{
99			Name:     "file with mtime and hash",
100			Filename: "state.old",
101			Mapfs: fstest.MapFS{
102				"state.old": &fstest.MapFile{
103					Data: marshalProto(t, &fid_proto.PartialCompileInputs{
104						InputFiles: []*fid_proto.PartialCompileInput{
105							protoFile("input1", 100, "crc:crc_value", nil),
106						},
107					}),
108				},
109			},
110			Expected: &fid_proto.PartialCompileInputs{
111				InputFiles: []*fid_proto.PartialCompileInput{
112					protoFile("input1", 100, "crc:crc_value", nil),
113				},
114			},
115			Err: nil,
116		},
117	}
118	for _, tc := range testCases {
119		actual, err := LoadState(tc.Filename, tc.Mapfs)
120		if tc.Err == nil {
121			android.AssertSame(t, tc.Name, tc.Err, err)
122		} else if err == nil {
123			t.Errorf("%s: expected error, did not get one", tc.Name)
124		}
125		if !proto.Equal(tc.Expected, actual) {
126			t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
127		}
128	}
129}
130
131func TestCreateState(t *testing.T) {
132	testCases := []struct {
133		Name     string
134		Inputs   []string
135		Inspect  bool
136		Mapfs    StatReadFileFS
137		Expected *fid_proto.PartialCompileInputs
138		Err      error
139	}{
140		{
141			Name:     "no inputs",
142			Inputs:   []string{},
143			Mapfs:    fstest.MapFS{},
144			Expected: &fid_proto.PartialCompileInputs{},
145			Err:      nil,
146		},
147		{
148			Name:   "files found",
149			Inputs: []string{"baz", "foo", "bar"},
150			Mapfs: fstest.MapFS{
151				"foo": &fstest.MapFile{ModTime: time.Unix(0, 100).UTC()},
152				"baz": &fstest.MapFile{ModTime: time.Unix(0, 300).UTC()},
153				"bar": &fstest.MapFile{ModTime: time.Unix(0, 200).UTC()},
154			},
155			Expected: &fid_proto.PartialCompileInputs{
156				InputFiles: []*fid_proto.PartialCompileInput{
157					// Files are always sorted.
158					protoFile("bar", 200, "", nil),
159					protoFile("baz", 300, "", nil),
160					protoFile("foo", 100, "", nil),
161				},
162			},
163			Err: nil,
164		},
165	}
166	for _, tc := range testCases {
167		actual, err := CreateState(tc.Inputs, tc.Inspect, tc.Mapfs)
168		if tc.Err == nil {
169			android.AssertSame(t, tc.Name, tc.Err, err)
170		} else if err == nil {
171			t.Errorf("%s: expected error, did not get one", tc.Name)
172		}
173		if !proto.Equal(tc.Expected, actual) {
174			t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
175		}
176	}
177}
178
179func TestCompareInternalState(t *testing.T) {
180	testCases := []struct {
181		Name     string
182		Target   string
183		Prior    *fid_proto.PartialCompileInputs
184		New      *fid_proto.PartialCompileInputs
185		Expected *FileList
186	}{
187		{
188			Name:   "prior is empty",
189			Target: "foo",
190			Prior:  &fid_proto.PartialCompileInputs{},
191			New: &fid_proto.PartialCompileInputs{
192				InputFiles: []*fid_proto.PartialCompileInput{
193					protoFile("file1", 100, "", nil),
194				},
195			},
196			Expected: &FileList{
197				Name:      "foo",
198				Additions: []string{"file1"},
199			},
200		},
201		{
202			Name:   "one each add modify delete",
203			Target: "foo",
204			Prior: &fid_proto.PartialCompileInputs{
205				InputFiles: []*fid_proto.PartialCompileInput{
206					protoFile("file0", 100, "", nil),
207					protoFile("file1", 100, "", nil),
208					protoFile("file2", 200, "", nil),
209				},
210			},
211			New: &fid_proto.PartialCompileInputs{
212				InputFiles: []*fid_proto.PartialCompileInput{
213					protoFile("file0", 100, "", nil),
214					protoFile("file1", 200, "", nil),
215					protoFile("file3", 300, "", nil),
216				},
217			},
218			Expected: &FileList{
219				Name:      "foo",
220				Additions: []string{"file3"},
221				Changes:   []FileList{FileList{Name: "file1"}},
222				Deletions: []string{"file2"},
223			},
224		},
225		{
226			Name:   "interior one each add modify delete",
227			Target: "bar",
228			Prior: &fid_proto.PartialCompileInputs{
229				InputFiles: []*fid_proto.PartialCompileInput{
230					protoFile("file1", 405, "", []*fid_proto.PartialCompileInput{
231						protoFile("innerC", 400, "crc32:11111111", nil),
232						protoFile("innerD", 400, "crc32:44444444", nil),
233					}),
234				},
235			},
236			New: &fid_proto.PartialCompileInputs{
237				InputFiles: []*fid_proto.PartialCompileInput{
238					protoFile("file1", 505, "", []*fid_proto.PartialCompileInput{
239						protoFile("innerA", 400, "crc32:55555555", nil),
240						protoFile("innerC", 500, "crc32:66666666", nil),
241					}),
242				},
243			},
244			Expected: &FileList{
245				Name: "bar",
246				Changes: []FileList{FileList{
247					Name:      "file1",
248					Additions: []string{"innerA"},
249					Changes:   []FileList{FileList{Name: "innerC"}},
250					Deletions: []string{"innerD"},
251				}},
252			},
253		},
254	}
255	for _, tc := range testCases {
256		actual := CompareInternalState(tc.Prior, tc.New, tc.Target)
257		if !tc.Expected.Equal(actual) {
258			t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
259		}
260	}
261}
262
263func TestCompareInspectExtsZipRegexp(t *testing.T) {
264	testCases := []struct {
265		Name     string
266		Expected bool
267	}{
268		{Name: ".jar", Expected: true},
269		{Name: ".jar5", Expected: true},
270		{Name: ".apex", Expected: true},
271		{Name: ".apex9", Expected: true},
272		{Name: ".apexx", Expected: false},
273		{Name: ".apk", Expected: true},
274		{Name: ".apk3", Expected: true},
275		{Name: ".go", Expected: false},
276	}
277	for _, tc := range testCases {
278		actual := InspectExtsZipRegexp.Match([]byte(tc.Name))
279		if tc.Expected != actual {
280			t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
281		}
282	}
283}
284