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