xref: /aosp_15_r20/build/make/tools/compliance/policy_policy_test.go (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1// Copyright 2021 Google LLC
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 compliance
16
17import (
18	"bytes"
19	"fmt"
20	"sort"
21	"strings"
22	"testing"
23
24	"android/soong/tools/compliance/testfs"
25)
26
27func TestPolicy_edgeConditions(t *testing.T) {
28	tests := []struct {
29		name                     string
30		edge                     annotated
31		treatAsAggregate         bool
32		otherCondition           string
33		expectedDepActions       []string
34		expectedTargetConditions []string
35	}{
36		{
37			name:                     "firstparty",
38			edge:                     annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
39			expectedDepActions:       []string{},
40			expectedTargetConditions: []string{},
41		},
42		{
43			name:                     "notice",
44			edge:                     annotated{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
45			expectedDepActions:       []string{},
46			expectedTargetConditions: []string{},
47		},
48		{
49			name: "fponlgpl",
50			edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
51			expectedDepActions: []string{
52				"apacheBin.meta_lic:lgplLib.meta_lic:restricted_if_statically_linked",
53				"lgplLib.meta_lic:lgplLib.meta_lic:restricted_if_statically_linked",
54			},
55			expectedTargetConditions: []string{},
56		},
57		{
58			name:                     "fponlgpldynamic",
59			edge:                     annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
60			expectedDepActions:       []string{},
61			expectedTargetConditions: []string{},
62		},
63		{
64			name: "fpongpl",
65			edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
66			expectedDepActions: []string{
67				"apacheBin.meta_lic:gplLib.meta_lic:restricted",
68				"gplLib.meta_lic:gplLib.meta_lic:restricted",
69			},
70			expectedTargetConditions: []string{},
71		},
72		{
73			name: "fpongpldynamic",
74			edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
75			expectedDepActions: []string{
76				"apacheBin.meta_lic:gplLib.meta_lic:restricted",
77				"gplLib.meta_lic:gplLib.meta_lic:restricted",
78			},
79			expectedTargetConditions: []string{},
80		},
81		{
82			name:                     "independentmodule",
83			edge:                     annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
84			expectedDepActions:       []string{},
85			expectedTargetConditions: []string{},
86		},
87		{
88			name:                     "independentmodulestatic",
89			edge:                     annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
90			expectedDepActions:       []string{},
91			expectedTargetConditions: []string{},
92		},
93		{
94			name:                     "dependentmodule",
95			edge:                     annotated{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
96			expectedDepActions:       []string{},
97			expectedTargetConditions: []string{},
98		},
99
100		{
101			name:                     "lgplonfp",
102			edge:                     annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
103			expectedDepActions:       []string{},
104			expectedTargetConditions: []string{"lgplBin.meta_lic:restricted_if_statically_linked"},
105		},
106		{
107			name:                     "lgplonfpdynamic",
108			edge:                     annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
109			expectedDepActions:       []string{},
110			expectedTargetConditions: []string{},
111		},
112		{
113			name:                     "gplonfp",
114			edge:                     annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
115			expectedDepActions:       []string{},
116			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
117		},
118		{
119			name:                     "gplcontainer",
120			edge:                     annotated{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
121			treatAsAggregate:         true,
122			expectedDepActions:       []string{},
123			expectedTargetConditions: []string{"gplContainer.meta_lic:restricted"},
124		},
125		{
126			name:             "gploncontainer",
127			edge:             annotated{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
128			treatAsAggregate: true,
129			otherCondition:   "gplLib.meta_lic:restricted",
130			expectedDepActions: []string{
131				"apacheContainer.meta_lic:gplLib.meta_lic:restricted",
132				"apacheLib.meta_lic:gplLib.meta_lic:restricted",
133				"gplLib.meta_lic:gplLib.meta_lic:restricted",
134			},
135			expectedTargetConditions: []string{},
136		},
137		{
138			name:             "gplonbin",
139			edge:             annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
140			treatAsAggregate: false,
141			otherCondition:   "gplLib.meta_lic:restricted",
142			expectedDepActions: []string{
143				"apacheBin.meta_lic:gplLib.meta_lic:restricted",
144				"apacheLib.meta_lic:gplLib.meta_lic:restricted",
145				"gplLib.meta_lic:gplLib.meta_lic:restricted",
146			},
147			expectedTargetConditions: []string{"gplLib.meta_lic:restricted"},
148		},
149		{
150			name:                     "gplonfpdynamic",
151			edge:                     annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
152			expectedDepActions:       []string{},
153			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
154		},
155		{
156			name:                     "independentmodulereverse",
157			edge:                     annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
158			expectedDepActions:       []string{},
159			expectedTargetConditions: []string{},
160		},
161		{
162			name:                     "independentmodulereversestatic",
163			edge:                     annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
164			expectedDepActions:       []string{},
165			expectedTargetConditions: []string{},
166		},
167		{
168			name:                     "dependentmodulereverse",
169			edge:                     annotated{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
170			expectedDepActions:       []string{},
171			expectedTargetConditions: []string{},
172		},
173		{
174			name: "ponr",
175			edge: annotated{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
176			expectedDepActions: []string{
177				"proprietary.meta_lic:gplLib.meta_lic:restricted",
178				"gplLib.meta_lic:gplLib.meta_lic:restricted",
179			},
180			expectedTargetConditions: []string{},
181		},
182		{
183			name:                     "ronp",
184			edge:                     annotated{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
185			expectedDepActions:       []string{},
186			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
187		},
188		{
189			name:                     "noticeonb_e_o",
190			edge:                     annotated{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
191			expectedDepActions:       []string{},
192			expectedTargetConditions: []string{},
193		},
194		{
195			name:                     "b_e_oonnotice",
196			edge:                     annotated{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
197			expectedDepActions:       []string{},
198			expectedTargetConditions: []string{},
199		},
200		{
201			name:                     "noticeonrecip",
202			edge:                     annotated{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
203			expectedDepActions:       []string{},
204			expectedTargetConditions: []string{},
205		},
206		{
207			name:                     "reciponnotice",
208			edge:                     annotated{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
209			expectedDepActions:       []string{},
210			expectedTargetConditions: []string{},
211		},
212	}
213	for _, tt := range tests {
214		t.Run(tt.name, func(t *testing.T) {
215			fs := make(testfs.TestFS)
216			stderr := &bytes.Buffer{}
217			target := meta[tt.edge.target] + fmt.Sprintf("deps: {\n  file: \"%s\"\n", tt.edge.dep)
218			for _, ann := range tt.edge.annotations {
219				target += fmt.Sprintf("  annotations: \"%s\"\n", ann)
220			}
221			fs[tt.edge.target] = []byte(target + "}\n")
222			fs[tt.edge.dep] = []byte(meta[tt.edge.dep])
223			lg, err := ReadLicenseGraph(&fs, stderr, []string{tt.edge.target})
224			if err != nil {
225				t.Errorf("unexpected error reading graph: %s", err)
226				return
227			}
228			edge := lg.Edges()[0]
229			// simulate a condition inherited from another edge/dependency.
230			otherTarget := ""
231			otherCondition := ""
232			var otn *TargetNode
233			if len(tt.otherCondition) > 0 {
234				fields := strings.Split(tt.otherCondition, ":")
235				otherTarget = fields[0]
236				otherCondition = fields[1]
237				otn = &TargetNode{name: otherTarget}
238				// other target must exist in graph
239				lg.targets[otherTarget] = otn
240				otn.licenseConditions = LicenseConditionSet(RecognizedConditionNames[otherCondition])
241			}
242			targets := make(map[string]*TargetNode)
243			targets[edge.target.name] = edge.target
244			targets[edge.dependency.name] = edge.dependency
245			if otn != nil {
246				targets[otn.name] = otn
247			}
248			if tt.expectedDepActions != nil {
249				t.Run("depConditionsPropagatingToTarget", func(t *testing.T) {
250					depConditions := edge.dependency.LicenseConditions()
251					if otherTarget != "" {
252						// simulate a sub-dependency's condition having already propagated up to dep and about to go to target
253						otherCs := otn.LicenseConditions()
254						depConditions |= otherCs
255					}
256					t.Logf("calculate target actions for edge=%s, dep conditions=%#v %s, treatAsAggregate=%v", edge.String(), depConditions, depConditions, tt.treatAsAggregate)
257					csActual := depConditionsPropagatingToTarget(lg, edge, depConditions, tt.treatAsAggregate)
258					t.Logf("calculated target conditions as %#v %s", csActual, csActual)
259					csExpected := NewLicenseConditionSet()
260					for _, triple := range tt.expectedDepActions {
261						fields := strings.Split(triple, ":")
262						expectedConditions := NewLicenseConditionSet()
263						for _, cname := range fields[2:] {
264							expectedConditions = expectedConditions.Plus(RecognizedConditionNames[cname])
265						}
266						csExpected |= expectedConditions
267					}
268					t.Logf("expected target conditions as %#v %s", csExpected, csExpected)
269					if csActual != csExpected {
270						t.Errorf("unexpected license conditions: got %#v, want %#v", csActual, csExpected)
271					}
272				})
273			}
274			if tt.expectedTargetConditions != nil {
275				t.Run("targetConditionsPropagatingToDep", func(t *testing.T) {
276					targetConditions := edge.target.LicenseConditions()
277					if otherTarget != "" {
278						targetConditions = targetConditions.Union(otn.licenseConditions)
279					}
280					t.Logf("calculate dep conditions for edge=%s, target conditions=%v, treatAsAggregate=%v", edge.String(), targetConditions.Names(), tt.treatAsAggregate)
281					cs := targetConditionsPropagatingToDep(lg, edge, targetConditions, tt.treatAsAggregate, AllResolutions)
282					t.Logf("calculated dep conditions as %v", cs.Names())
283					actual := cs.Names()
284					sort.Strings(actual)
285					expected := make([]string, 0)
286					for _, expectedDepCondition := range tt.expectedTargetConditions {
287						expected = append(expected, strings.Split(expectedDepCondition, ":")[1])
288					}
289					sort.Strings(expected)
290					if len(actual) != len(expected) {
291						t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions",
292							actual, len(actual), expected, len(expected))
293					} else {
294						for i := 0; i < len(actual); i++ {
295							if actual[i] != expected[i] {
296								t.Errorf("unexpected target condition at element %d: got %q, want %q",
297									i, actual[i], expected[i])
298							}
299						}
300					}
301				})
302			}
303		})
304	}
305}
306