Merge "compliance package policy and resolves" am: 45deca7ff7 am: e7d26816bb am: dfd9be96ce

Original change: https://android-review.googlesource.com/c/platform/build/+/1870078

Change-Id: I7dd037c6a97b9e5c778cc04feff1169571410d27
This commit is contained in:
Treehugger Robot
2021-12-04 04:18:22 +00:00
committed by Automerger Merge Worker
19 changed files with 3742 additions and 2 deletions

View File

@@ -24,6 +24,14 @@ bootstrap_go_package {
"condition.go", "condition.go",
"conditionset.go", "conditionset.go",
"graph.go", "graph.go",
"policy/policy.go",
"policy/resolve.go",
"policy/resolvenotices.go",
"policy/resolveshare.go",
"policy/resolveprivacy.go",
"policy/shareprivacyconflicts.go",
"policy/shipped.go",
"policy/walk.go",
"readgraph.go", "readgraph.go",
"resolution.go", "resolution.go",
"resolutionset.go", "resolutionset.go",
@@ -32,6 +40,14 @@ bootstrap_go_package {
"condition_test.go", "condition_test.go",
"conditionset_test.go", "conditionset_test.go",
"readgraph_test.go", "readgraph_test.go",
"policy/policy_test.go",
"policy/resolve_test.go",
"policy/resolvenotices_test.go",
"policy/resolveshare_test.go",
"policy/resolveprivacy_test.go",
"policy/shareprivacyconflicts_test.go",
"policy/shipped_test.go",
"policy/walk_test.go",
"resolutionset_test.go", "resolutionset_test.go",
"test_util.go", "test_util.go",
], ],

View File

@@ -0,0 +1,238 @@
// Copyright 2021 Google LLC
//
// 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 compliance
import (
"regexp"
"strings"
)
var (
// ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
ImpliesUnencumbered = ConditionNames{"unencumbered"}
// ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
ImpliesPermissive = ConditionNames{"permissive"}
// ImpliesNotice lists the condition names implying a notice or attribution policy.
ImpliesNotice = ConditionNames{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary", "by_exception_only"}
// ImpliesReciprocal lists the condition names implying a local source-sharing policy.
ImpliesReciprocal = ConditionNames{"reciprocal"}
// Restricted lists the condition names implying an infectious source-sharing policy.
ImpliesRestricted = ConditionNames{"restricted"}
// ImpliesProprietary lists the condition names implying a confidentiality policy.
ImpliesProprietary = ConditionNames{"proprietary"}
// ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
ImpliesByExceptionOnly = ConditionNames{"proprietary", "by_exception_only"}
// ImpliesPrivate lists the condition names implying a source-code privacy policy.
ImpliesPrivate = ConditionNames{"proprietary"}
// ImpliesShared lists the condition names implying a source-code sharing policy.
ImpliesShared = ConditionNames{"reciprocal", "restricted"}
)
var (
anyLgpl = regexp.MustCompile(`^SPDX-license-identifier-LGPL.*`)
versionedGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL-\p{N}.*`)
genericGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL$`)
ccBySa = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
)
// Resolution happens in two passes:
//
// 1. A bottom-up traversal propagates license conditions up to targets from
// dendencies as needed.
//
// 2. For each condition of interest, a top-down traversal adjusts the attached
// conditions pushing restricted down from targets into linked dependencies.
//
// The behavior of the 2 passes gets controlled by the 2 functions below.
//
// The first function controls what happens during the bottom-up traversal. In
// general conditions flow up through static links but not other dependencies;
// except, restricted sometimes flows up through dynamic links.
//
// In general, too, the originating target gets acted on to resolve the
// condition (e.g. providing notice), but again restricted is special in that
// it requires acting on (i.e. sharing source of) both the originating module
// and the target using the module.
//
// The latter function controls what happens during the top-down traversal. In
// general, only restricted conditions flow down at all, and only through
// static links.
//
// Not all restricted licenses are create equal. Some have special rules or
// exceptions. e.g. LGPL or "with classpath excption".
// depActionsApplicableToTarget returns the actions which propagate up an
// edge from dependency to target.
//
// This function sets the policy for the bottom-up traversal and how conditions
// flow up the graph from dependencies to targets.
//
// If a pure aggregation is built into a derivative work that is not a pure
// aggregation, per policy it ceases to be a pure aggregation in the context of
// that derivative work. The `treatAsAggregate` parameter will be false for
// non-aggregates and for aggregates in non-aggregate contexts.
func depActionsApplicableToTarget(e TargetEdge, depActions actionSet, treatAsAggregate bool) actionSet {
result := make(actionSet)
if edgeIsDerivation(e) {
result.addSet(depActions)
for _, cs := range depActions.byName(ImpliesRestricted) {
result.add(e.Target(), cs)
}
return result
}
if !edgeIsDynamicLink(e) {
return result
}
restricted := depActions.byName(ImpliesRestricted)
for actsOn, cs := range restricted {
for _, lc := range cs.AsList() {
hasGpl := false
hasLgpl := false
hasClasspath := false
hasGeneric := false
hasOther := false
for _, kind := range lc.origin.LicenseKinds() {
if strings.HasSuffix(kind, "-with-classpath-exception") {
hasClasspath = true
} else if anyLgpl.MatchString(kind) {
hasLgpl = true
} else if versionedGpl.MatchString(kind) {
hasGpl = true
} else if genericGpl.MatchString(kind) {
hasGeneric = true
} else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
hasOther = true
}
}
if hasOther || hasGpl {
result.addCondition(actsOn, lc)
result.addCondition(e.Target(), lc)
continue
}
if hasClasspath && !edgeNodesAreIndependentModules(e) {
result.addCondition(actsOn, lc)
result.addCondition(e.Target(), lc)
continue
}
if hasLgpl || hasClasspath {
continue
}
if !hasGeneric {
continue
}
result.addCondition(actsOn, lc)
result.addCondition(e.Target(), lc)
}
}
return result
}
// targetConditionsApplicableToDep returns the conditions which propagate down
// an edge from target to dependency.
//
// This function sets the policy for the top-down traversal and how conditions
// flow down the graph from targets to dependencies.
//
// If a pure aggregation is built into a derivative work that is not a pure
// aggregation, per policy it ceases to be a pure aggregation in the context of
// that derivative work. The `treatAsAggregate` parameter will be false for
// non-aggregates and for aggregates in non-aggregate contexts.
func targetConditionsApplicableToDep(e TargetEdge, targetConditions *LicenseConditionSet, treatAsAggregate bool) *LicenseConditionSet {
result := targetConditions.Copy()
// reverse direction -- none of these apply to things depended-on, only to targets depending-on.
result.RemoveAllByName(ConditionNames{"unencumbered", "permissive", "notice", "reciprocal", "proprietary", "by_exception_only"})
if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
// target is not a derivative work of dependency and is not linked to dependency
result.RemoveAllByName(ImpliesRestricted)
return result
}
if treatAsAggregate {
// If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
// Otherwise, restricted does not propagate back down to dependencies.
restricted := result.ByName(ImpliesRestricted).AsList()
for _, lc := range restricted {
if lc.origin.name != e.e.target {
result.Remove(lc)
}
}
return result
}
if edgeIsDerivation(e) {
return result
}
restricted := result.ByName(ImpliesRestricted).AsList()
for _, lc := range restricted {
hasGpl := false
hasLgpl := false
hasClasspath := false
hasGeneric := false
hasOther := false
for _, kind := range lc.origin.LicenseKinds() {
if strings.HasSuffix(kind, "-with-classpath-exception") {
hasClasspath = true
} else if anyLgpl.MatchString(kind) {
hasLgpl = true
} else if versionedGpl.MatchString(kind) {
hasGpl = true
} else if genericGpl.MatchString(kind) {
hasGeneric = true
} else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
hasOther = true
}
}
if hasOther || hasGpl {
continue
}
if hasClasspath && !edgeNodesAreIndependentModules(e) {
continue
}
if hasGeneric && !hasLgpl && !hasClasspath {
continue
}
result.Remove(lc)
}
return result
}
// edgeIsDynamicLink returns true for edges representing shared libraries
// linked dynamically at runtime.
func edgeIsDynamicLink(e TargetEdge) bool {
return e.e.annotations.HasAnnotation("dynamic")
}
// edgeIsDerivation returns true for edges where the target is a derivative
// work of dependency.
func edgeIsDerivation(e TargetEdge) bool {
isDynamic := e.e.annotations.HasAnnotation("dynamic")
isToolchain := e.e.annotations.HasAnnotation("toolchain")
return !isDynamic && !isToolchain
}
// edgeNodesAreIndependentModules returns true for edges where the target and
// dependency are independent modules.
func edgeNodesAreIndependentModules(e TargetEdge) bool {
return e.Target().PackageName() != e.Dependency().PackageName()
}

View File

@@ -0,0 +1,300 @@
// Copyright 2021 Google LLC
//
// 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 compliance
import (
"bytes"
"fmt"
"sort"
"strings"
"testing"
)
func TestPolicy_edgeConditions(t *testing.T) {
tests := []struct {
name string
edge annotated
treatAsAggregate bool
otherCondition string
expectedDepActions []string
expectedTargetConditions []string
}{
{
name: "firstparty",
edge: annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
expectedTargetConditions: []string{},
},
{
name: "notice",
edge: annotated{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
expectedTargetConditions: []string{},
},
{
name: "fponlgpl",
edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
expectedDepActions: []string{
"apacheBin.meta_lic:lgplLib.meta_lic:restricted",
"lgplLib.meta_lic:lgplLib.meta_lic:restricted",
},
expectedTargetConditions: []string{},
},
{
name: "fponlgpldynamic",
edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
expectedDepActions: []string{},
expectedTargetConditions: []string{},
},
{
name: "fpongpl",
edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
expectedDepActions: []string{
"apacheBin.meta_lic:gplLib.meta_lic:restricted",
"gplLib.meta_lic:gplLib.meta_lic:restricted",
},
expectedTargetConditions: []string{},
},
{
name: "fpongpldynamic",
edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
expectedDepActions: []string{
"apacheBin.meta_lic:gplLib.meta_lic:restricted",
"gplLib.meta_lic:gplLib.meta_lic:restricted",
},
expectedTargetConditions: []string{},
},
{
name: "independentmodule",
edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
expectedDepActions: []string{},
expectedTargetConditions: []string{},
},
{
name: "independentmodulestatic",
edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
expectedDepActions: []string{
"apacheBin.meta_lic:gplWithClasspathException.meta_lic:restricted",
"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted",
},
expectedTargetConditions: []string{},
},
{
name: "dependentmodule",
edge: annotated{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
expectedDepActions: []string{
"dependentModule.meta_lic:gplWithClasspathException.meta_lic:restricted",
"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted",
},
expectedTargetConditions: []string{},
},
{
name: "lgplonfp",
edge: annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
expectedTargetConditions: []string{"lgplBin.meta_lic:restricted"},
},
{
name: "lgplonfpdynamic",
edge: annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
expectedDepActions: []string{},
expectedTargetConditions: []string{},
},
{
name: "gplonfp",
edge: annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
},
{
name: "gplcontainer",
edge: annotated{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
treatAsAggregate: true,
expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
expectedTargetConditions: []string{"gplContainer.meta_lic:restricted"},
},
{
name: "gploncontainer",
edge: annotated{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
treatAsAggregate: true,
otherCondition: "gplLib.meta_lic:restricted",
expectedDepActions: []string{
"apacheContainer.meta_lic:gplLib.meta_lic:restricted",
"apacheLib.meta_lic:apacheLib.meta_lic:notice",
"apacheLib.meta_lic:gplLib.meta_lic:restricted",
"gplLib.meta_lic:gplLib.meta_lic:restricted",
},
expectedTargetConditions: []string{},
},
{
name: "gplonbin",
edge: annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
treatAsAggregate: false,
otherCondition: "gplLib.meta_lic:restricted",
expectedDepActions: []string{
"apacheBin.meta_lic:gplLib.meta_lic:restricted",
"apacheLib.meta_lic:apacheLib.meta_lic:notice",
"apacheLib.meta_lic:gplLib.meta_lic:restricted",
"gplLib.meta_lic:gplLib.meta_lic:restricted",
},
expectedTargetConditions: []string{"gplLib.meta_lic:restricted"},
},
{
name: "gplonfpdynamic",
edge: annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
expectedDepActions: []string{},
expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
},
{
name: "independentmodulereverse",
edge: annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
expectedDepActions: []string{},
expectedTargetConditions: []string{},
},
{
name: "independentmodulereversestatic",
edge: annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
expectedDepActions: []string{"apacheBin.meta_lic:apacheBin.meta_lic:notice"},
expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted"},
},
{
name: "dependentmodulereverse",
edge: annotated{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
expectedDepActions: []string{},
expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted"},
},
{
name: "ponr",
edge: annotated{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
expectedDepActions: []string{
"proprietary.meta_lic:gplLib.meta_lic:restricted",
"gplLib.meta_lic:gplLib.meta_lic:restricted",
},
expectedTargetConditions: []string{},
},
{
name: "ronp",
edge: annotated{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
expectedDepActions: []string{"proprietary.meta_lic:proprietary.meta_lic:proprietary"},
expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
},
{
name: "noticeonb_e_o",
edge: annotated{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
expectedDepActions: []string{"by_exception.meta_lic:by_exception.meta_lic:by_exception_only"},
expectedTargetConditions: []string{},
},
{
name: "b_e_oonnotice",
edge: annotated{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
expectedTargetConditions: []string{},
},
{
name: "noticeonrecip",
edge: annotated{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
expectedDepActions: []string{"mplLib.meta_lic:mplLib.meta_lic:reciprocal"},
expectedTargetConditions: []string{},
},
{
name: "reciponnotice",
edge: annotated{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
expectedTargetConditions: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fs := make(testFS)
stderr := &bytes.Buffer{}
target := meta[tt.edge.target] + fmt.Sprintf("deps: {\n file: \"%s\"\n", tt.edge.dep)
for _, ann := range tt.edge.annotations {
target += fmt.Sprintf(" annotations: \"%s\"\n", ann)
}
fs[tt.edge.target] = []byte(target + "}\n")
fs[tt.edge.dep] = []byte(meta[tt.edge.dep])
lg, err := ReadLicenseGraph(&fs, stderr, []string{tt.edge.target})
if err != nil {
t.Errorf("unexpected error reading graph: %w", err)
return
}
// simulate a condition inherited from another edge/dependency.
otherTarget := ""
otherCondition := ""
if len(tt.otherCondition) > 0 {
fields := strings.Split(tt.otherCondition, ":")
otherTarget = fields[0]
otherCondition = fields[1]
// other target must exist in graph
lg.targets[otherTarget] = &TargetNode{name: otherTarget}
lg.targets[otherTarget].proto.LicenseConditions = append(lg.targets[otherTarget].proto.LicenseConditions, otherCondition)
}
if tt.expectedDepActions != nil {
depActions := make(actionSet)
depActions[lg.targets[tt.edge.dep]] = lg.targets[tt.edge.dep].LicenseConditions()
if otherTarget != "" {
// simulate a sub-dependency's condition having already propagated up to dep and about to go to target
otherCs := lg.targets[otherTarget].LicenseConditions()
depActions[lg.targets[tt.edge.dep]].AddSet(otherCs)
depActions[lg.targets[otherTarget]] = otherCs
}
asActual := depActionsApplicableToTarget(lg.Edges()[0], depActions, tt.treatAsAggregate)
asExpected := make(actionSet)
for _, triple := range tt.expectedDepActions {
fields := strings.Split(triple, ":")
actsOn := lg.targets[fields[0]]
origin := lg.targets[fields[1]]
expectedConditions := newLicenseConditionSet()
expectedConditions.add(origin, fields[2:]...)
if _, ok := asExpected[actsOn]; ok {
asExpected[actsOn].AddSet(expectedConditions)
} else {
asExpected[actsOn] = expectedConditions
}
}
checkSameActions(lg, asActual, asExpected, t)
}
if tt.expectedTargetConditions != nil {
targetConditions := lg.TargetNode(tt.edge.target).LicenseConditions()
if otherTarget != "" {
targetConditions.add(lg.targets[otherTarget], otherCondition)
}
cs := targetConditionsApplicableToDep(
lg.Edges()[0],
targetConditions,
tt.treatAsAggregate)
actual := make([]string, 0, cs.Count())
for _, lc := range cs.AsList() {
actual = append(actual, lc.asString(":"))
}
sort.Strings(actual)
sort.Strings(tt.expectedTargetConditions)
if len(actual) != len(tt.expectedTargetConditions) {
t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions",
actual, len(actual), tt.expectedTargetConditions, len(tt.expectedTargetConditions))
} else {
for i := 0; i < len(actual); i++ {
if actual[i] != tt.expectedTargetConditions[i] {
t.Errorf("unexpected target condition at element %d: got %q, want %q",
i, actual[i], tt.expectedTargetConditions[i])
}
}
}
}
})
}
}

View File

@@ -0,0 +1,217 @@
// Copyright 2021 Google LLC
//
// 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 compliance
// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
// propagating conditions up the graph as necessary according to the properties
// of each edge and according to each license condition in question.
//
// Subsequent top-down walks of the graph will filter some resolutions and may
// introduce new resolutions.
//
// e.g. if a "restricted" condition applies to a binary, it also applies to all
// of the statically-linked libraries and the transitive closure of their static
// dependencies; even if neither they nor the transitive closure of their
// dependencies originate any "restricted" conditions. The bottom-up walk will
// not resolve the library and its transitive closure, but the later top-down
// walk will.
func ResolveBottomUpConditions(lg *LicenseGraph) *ResolutionSet {
// short-cut if already walked and cached
lg.mu.Lock()
rs := lg.rsBU
lg.mu.Unlock()
if rs != nil {
return rs
}
// must be indexed for fast lookup
lg.indexForward()
rs = newResolutionSet()
// cmap contains an entry for every target that was previously walked as a pure aggregate only.
cmap := make(map[string]bool)
var walk func(f string, treatAsAggregate bool) actionSet
walk = func(f string, treatAsAggregate bool) actionSet {
target := lg.targets[f]
result := make(actionSet)
result[target] = newLicenseConditionSet()
result[target].add(target, target.proto.LicenseConditions...)
if preresolved, ok := rs.resolutions[target]; ok {
if treatAsAggregate {
result.addSet(preresolved)
return result
}
if _, asAggregate := cmap[f]; !asAggregate {
result.addSet(preresolved)
return result
}
// previously walked in a pure aggregate context,
// needs to walk again in non-aggregate context
delete(cmap, f)
}
if treatAsAggregate {
cmap[f] = true
}
// add all the conditions from all the dependencies
for _, edge := range lg.index[f] {
// walk dependency to get its conditions
as := walk(edge.dependency, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
// turn those into the conditions that apply to the target
as = depActionsApplicableToTarget(TargetEdge{lg, edge}, as, treatAsAggregate)
// add them to the result
result.addSet(as)
}
// record these conditions as applicable to the target
rs.addConditions(target, result)
rs.addSelf(target, result.byName(ImpliesRestricted))
// return this up the tree
return result
}
// walk each of the roots
for _, r := range lg.rootFiles {
_ = walk(r, lg.targets[r].IsContainer())
}
// if not yet cached, save the result
lg.mu.Lock()
if lg.rsBU == nil {
lg.rsBU = rs
} else {
// if we end up with 2, release the later for garbage collection
rs = lg.rsBU
}
lg.mu.Unlock()
return rs
}
// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
// resolving all reachable nodes for `condition`. Policy establishes the rules
// for transforming and propagating resolutions down the graph.
//
// e.g. For current policy, none of the conditions propagate from target to
// dependency except restricted. For restricted, the policy is to share the
// source of any libraries linked to restricted code and to provide notice.
func ResolveTopDownConditions(lg *LicenseGraph) *ResolutionSet {
// short-cut if already walked and cached
lg.mu.Lock()
rs := lg.rsTD
lg.mu.Unlock()
if rs != nil {
return rs
}
// start with the conditions propagated up the graph
rs = ResolveBottomUpConditions(lg)
// rmap maps 'appliesTo' targets to their applicable conditions
//
// rmap is the resulting ResolutionSet
rmap := make(map[*TargetNode]actionSet)
for attachesTo, as := range rs.resolutions {
rmap[attachesTo] = as.copy()
}
path := make([]*dependencyEdge, 0, 32)
var walk func(f string, cs *LicenseConditionSet, treatAsAggregate bool)
walk = func(f string, cs *LicenseConditionSet, treatAsAggregate bool) {
fnode := lg.targets[f]
if !cs.IsEmpty() {
parentsAllAggregate := true
for _, e := range path {
target := lg.targets[e.target]
if _, ok := rmap[target]; !ok {
rmap[target] = make(actionSet)
}
rmap[target].add(fnode, cs)
if !target.IsContainer() {
parentsAllAggregate = false
break
}
}
if parentsAllAggregate {
if _, ok := rmap[fnode]; !ok {
rmap[fnode] = make(actionSet)
}
rmap[fnode].add(fnode, cs)
}
}
// add conditions attached to `f`
cs = cs.Copy()
for _, fcs := range rs.resolutions[fnode] {
cs.AddSet(fcs)
}
// for each dependency
for _, edge := range lg.index[f] {
e := TargetEdge{lg, edge}
// dcs holds the dpendency conditions inherited from the target
dcs := targetConditionsApplicableToDep(e, cs, treatAsAggregate)
if dcs.IsEmpty() {
if !treatAsAggregate || (!edgeIsDerivation(e) && !edgeIsDynamicLink(e)) {
continue
}
}
path = append(path, edge)
// add the conditions to the dependency
walk(edge.dependency, dcs, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
path = path[:len(path)-1]
}
}
// walk each of the roots
for _, r := range lg.rootFiles {
as, ok := rs.resolutions[lg.targets[r]]
if !ok {
// no conditions in root or transitive closure of dependencies
continue
}
if as.isEmpty() {
continue
}
path = path[:0]
// add the conditions to the root and its transitive closure
walk(r, newLicenseConditionSet(), lg.targets[r].IsContainer())
}
rs = &ResolutionSet{rmap}
// if not yet cached, save the result
lg.mu.Lock()
if lg.rsTD == nil {
lg.rsTD = rs
} else {
// if we end up with 2, release the later for garbage collection
rs = lg.rsTD
}
lg.mu.Unlock()
return rs
}

View File

@@ -0,0 +1,755 @@
// Copyright 2021 Google LLC
//
// 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 compliance
import (
"bytes"
"testing"
)
func TestResolveBottomUpConditions(t *testing.T) {
tests := []struct {
name string
roots []string
edges []annotated
expectedResolutions []res
}{
{
name: "firstparty",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "firstpartytool",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"toolchain"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "firstpartydeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "firstpartywide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "firstpartydynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "firstpartydynamicdeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "firstpartydynamicwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "restricted",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "restrictedtool",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"toolchain"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "restricteddeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "restrictedwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "restricteddynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "restricteddynamicdeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "restricteddynamicwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricted",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestrictedtool",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"toolchain"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricteddeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestrictedwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricteddynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricteddynamicdeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricteddynamicwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "classpath",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "classpathdependent",
roots: []string{"dependentModule.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "classpathdynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "classpathdependentdynamic",
roots: []string{"dependentModule.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stderr := &bytes.Buffer{}
lg, err := toGraph(stderr, tt.roots, tt.edges)
if err != nil {
t.Errorf("unexpected test data error: got %w, want no error", err)
return
}
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
actualRs := ResolveBottomUpConditions(lg)
checkSame(actualRs, expectedRs, t)
})
}
}
func TestResolveTopDownConditions(t *testing.T) {
tests := []struct {
name string
roots []string
edges []annotated
expectedResolutions []res
}{
{
name: "firstparty",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "firstpartydynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "restricted",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "restrictedtool",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "restricteddeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
{"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"mplLib.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
},
},
{
name: "restrictedwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "restricteddynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "restricteddynamicdeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"mplLib.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
},
},
{
name: "restricteddynamicwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricted",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "weakrestrictedtool",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "weakrestricteddeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"apacheContainer.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "weakrestrictedwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricteddynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "weakrestricteddynamicdeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricteddynamicwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "classpath",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "classpathdependent",
roots: []string{"dependentModule.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "classpathdynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "classpathdependentdynamic",
roots: []string{"dependentModule.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stderr := &bytes.Buffer{}
lg, err := toGraph(stderr, tt.roots, tt.edges)
if err != nil {
t.Errorf("unexpected test data error: got %w, want no error", err)
return
}
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
actualRs := ResolveTopDownConditions(lg)
checkSame(actualRs, expectedRs, t)
})
}
}

View File

@@ -0,0 +1,21 @@
// Copyright 2021 Google LLC
//
// 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 compliance
// ResolveNotices implements the policy for notices.
func ResolveNotices(lg *LicenseGraph) *ResolutionSet {
rs := ResolveTopDownConditions(lg)
return WalkResolutionsForCondition(lg, rs, ImpliesNotice)
}

View File

@@ -0,0 +1,467 @@
// Copyright 2021 Google LLC
//
// 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 compliance
import (
"bytes"
"testing"
)
func TestResolveNotices(t *testing.T) {
tests := []struct {
name string
roots []string
edges []annotated
expectedResolutions []res
}{
{
name: "firstparty",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "firstpartydynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
},
},
{
name: "firstpartydynamicshipped",
roots: []string{"apacheBin.meta_lic", "apacheLib.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "restricted",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "restrictedtool",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "restricteddeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
{"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "restrictedwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "restricteddynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "restricteddynamicshipped",
roots: []string{"apacheBin.meta_lic", "mitLib.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "restricteddynamicdeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
},
},
{
name: "restricteddynamicwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
},
},
{
name: "restricteddynamicwideshipped",
roots: []string{"apacheContainer.meta_lic", "gplLib.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricted",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestrictedtool",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "weakrestrictedtoolshipped",
roots: []string{"apacheBin.meta_lic", "lgplBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
},
},
{
name: "weakrestricteddeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"apacheContainer.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestrictedwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricteddynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "weakrestricteddynamicshipped",
roots: []string{"apacheBin.meta_lic", "lgplLib.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricteddynamicdeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
},
},
{
name: "weakrestricteddynamicdeepshipped",
roots: []string{"apacheContainer.meta_lic", "lgplLib.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "weakrestricteddynamicwide",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
},
},
{
name: "weakrestricteddynamicwideshipped",
roots: []string{"apacheContainer.meta_lic", "lgplLib.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "classpath",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "classpathdependent",
roots: []string{"dependentModule.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "classpathdynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "classpathdynamicshipped",
roots: []string{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "classpathdependentdynamic",
roots: []string{"dependentModule.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "classpathdependentdynamicshipped",
roots: []string{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
{"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stderr := &bytes.Buffer{}
lg, err := toGraph(stderr, tt.roots, tt.edges)
if err != nil {
t.Errorf("unexpected test data error: got %w, want no error", err)
return
}
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
actualRs := ResolveNotices(lg)
checkSame(actualRs, expectedRs, t)
})
}
}

View File

@@ -0,0 +1,21 @@
// Copyright 2021 Google LLC
//
// 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 compliance
// ResolveSourcePrivacy implements the policy for source privacy.
func ResolveSourcePrivacy(lg *LicenseGraph) *ResolutionSet {
rs := ResolveTopDownConditions(lg)
return WalkResolutionsForCondition(lg, rs, ImpliesPrivate)
}

View File

@@ -0,0 +1,87 @@
// Copyright 2021 Google LLC
//
// 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 compliance
import (
"bytes"
"testing"
)
func TestResolveSourcePrivacy(t *testing.T) {
tests := []struct {
name string
roots []string
edges []annotated
expectedResolutions []res
}{
{
name: "firstparty",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{},
},
{
name: "notice",
roots: []string{"mitBin.meta_lic"},
edges: []annotated{
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{},
},
{
name: "lgpl",
roots: []string{"lgplBin.meta_lic"},
edges: []annotated{
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{},
},
{
name: "proprietaryonresricted",
roots: []string{"proprietary.meta_lic"},
edges: []annotated{
{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
},
},
{
name: "restrictedonproprietary",
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stderr := &bytes.Buffer{}
lg, err := toGraph(stderr, tt.roots, tt.edges)
if err != nil {
t.Errorf("unexpected test data error: got %w, want no error", err)
return
}
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
actualRs := ResolveSourcePrivacy(lg)
checkSame(actualRs, expectedRs, t)
})
}
}

View File

@@ -0,0 +1,21 @@
// Copyright 2021 Google LLC
//
// 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 compliance
// ResolveSourceSharing implements the policy for source-sharing.
func ResolveSourceSharing(lg *LicenseGraph) *ResolutionSet {
rs := ResolveTopDownConditions(lg)
return WalkResolutionsForCondition(lg, rs, ImpliesShared)
}

View File

@@ -0,0 +1,295 @@
// Copyright 2021 Google LLC
//
// 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 compliance
import (
"bytes"
"testing"
)
func TestResolveSourceSharing(t *testing.T) {
tests := []struct {
name string
roots []string
edges []annotated
expectedResolutions []res
}{
{
name: "independentmodulerestricted",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{},
},
{
name: "independentmodulerestrictedshipped",
roots: []string{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "independentmodulestaticrestricted",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "dependentmodulerestricted",
roots: []string{"dependentModule.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "dependentmodulerestrictedshipclasspath",
roots: []string{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "lgplonfprestricted",
roots: []string{"lgplBin.meta_lic"},
edges: []annotated{
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
{"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
},
},
{
name: "lgplonfpdynamicrestricted",
roots: []string{"lgplBin.meta_lic"},
edges: []annotated{
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
},
},
{
name: "lgplonfpdynamicrestrictedshiplib",
roots: []string{"lgplBin.meta_lic", "apacheLib.meta_lic"},
edges: []annotated{
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
},
},
{
name: "gplonfprestricted",
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
{"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "gplcontainerrestricted",
roots: []string{"gplContainer.meta_lic"},
edges: []annotated{
{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
{"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
},
},
{
name: "gploncontainerrestricted",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "gplonbinrestricted",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "gplonfpdynamicrestricted",
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "gplonfpdynamicrestrictedshiplib",
roots: []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
{"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "independentmodulereverserestricted",
roots: []string{"gplWithClasspathException.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "independentmodulereversestaticrestricted",
roots: []string{"gplWithClasspathException.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "dependentmodulereverserestricted",
roots: []string{"gplWithClasspathException.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "dependentmodulereverserestrictedshipdependent",
roots: []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "ponrrestricted",
roots: []string{"proprietary.meta_lic"},
edges: []annotated{
{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
{"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "ronprestricted",
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
{"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "noticeonb_e_orestricted",
roots: []string{"mitBin.meta_lic"},
edges: []annotated{
{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
},
expectedResolutions: []res{},
},
{
name: "b_e_oonnoticerestricted",
roots: []string{"by_exception.meta_lic"},
edges: []annotated{
{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{},
},
{
name: "noticeonreciprecip",
roots: []string{"mitBin.meta_lic"},
edges: []annotated{
{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
},
},
{
name: "reciponnoticerecip",
roots: []string{"mplBin.meta_lic"},
edges: []annotated{
{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stderr := &bytes.Buffer{}
lg, err := toGraph(stderr, tt.roots, tt.edges)
if err != nil {
t.Errorf("unexpected test data error: got %w, want no error", err)
return
}
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
actualRs := ResolveSourceSharing(lg)
checkSame(actualRs, expectedRs, t)
})
}
}

View File

@@ -0,0 +1,91 @@
// Copyright 2021 Google LLC
//
// 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 compliance
import (
"fmt"
)
// SourceSharePrivacyConflict describes an individual conflict between a source-sharing
// condition and a source privacy condition
type SourceSharePrivacyConflict struct {
SourceNode *TargetNode
ShareCondition LicenseCondition
PrivacyCondition LicenseCondition
}
// Error returns a string describing the conflict.
func (conflict SourceSharePrivacyConflict) Error() string {
return fmt.Sprintf("%s %s from %s and must share from %s %s\n",
conflict.SourceNode.name,
conflict.PrivacyCondition.name, conflict.PrivacyCondition.origin.name,
conflict.ShareCondition.name, conflict.ShareCondition.origin.name)
}
// IsEqualTo returns true when `conflict` and `other` describe the same conflict.
func (conflict SourceSharePrivacyConflict) IsEqualTo(other SourceSharePrivacyConflict) bool {
return conflict.SourceNode.name == other.SourceNode.name &&
conflict.ShareCondition.name == other.ShareCondition.name &&
conflict.ShareCondition.origin.name == other.ShareCondition.origin.name &&
conflict.PrivacyCondition.name == other.PrivacyCondition.name &&
conflict.PrivacyCondition.origin.name == other.PrivacyCondition.origin.name
}
// ConflictingSharedPrivateSource lists all of the targets where conflicting conditions to
// share the source and to keep the source private apply to the target.
func ConflictingSharedPrivateSource(lg *LicenseGraph) []SourceSharePrivacyConflict {
// shareSource is the set of all source-sharing resolutions.
shareSource := ResolveSourceSharing(lg)
if shareSource.IsEmpty() {
return []SourceSharePrivacyConflict{}
}
// privateSource is the set of all source privacy resolutions.
privateSource := ResolveSourcePrivacy(lg)
if privateSource.IsEmpty() {
return []SourceSharePrivacyConflict{}
}
// combined is the combination of source-sharing and source privacy.
combined := JoinResolutionSets(shareSource, privateSource)
// size is the size of the result
size := 0
for _, actsOn := range combined.ActsOn() {
rl := combined.ResolutionsByActsOn(actsOn)
size += rl.CountConditionsByName(ImpliesShared) * rl.CountConditionsByName(ImpliesPrivate)
}
if size == 0 {
return []SourceSharePrivacyConflict{}
}
result := make([]SourceSharePrivacyConflict, 0, size)
for _, actsOn := range combined.ActsOn() {
rl := combined.ResolutionsByActsOn(actsOn)
if len(rl) == 0 {
continue
}
pconditions := rl.ByName(ImpliesPrivate).AllConditions().AsList()
ssconditions := rl.ByName(ImpliesShared).AllConditions().AsList()
// report all conflicting condition combinations
for _, p := range pconditions {
for _, ss := range ssconditions {
result = append(result, SourceSharePrivacyConflict{actsOn, ss, p})
}
}
}
return result
}

View File

@@ -0,0 +1,129 @@
// Copyright 2021 Google LLC
//
// 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 compliance
import (
"bytes"
"sort"
"testing"
)
// byConflict orders conflicts by target then share then privacy
type byConflict []SourceSharePrivacyConflict
// Len returns the count of elements in the slice.
func (l byConflict) Len() int { return len(l) }
// Swap rearranged 2 elements so that each occupies the other's former
// position.
func (l byConflict) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
// Less returns true when the `i`th element is lexicographically less than
// the `j`th element.
func (l byConflict) Less(i, j int) bool {
if l[i].SourceNode.name == l[j].SourceNode.name {
if l[i].ShareCondition.origin.name == l[j].ShareCondition.origin.name {
if l[i].ShareCondition.name == l[j].ShareCondition.name {
if l[i].PrivacyCondition.origin.name == l[j].PrivacyCondition.origin.name {
return l[i].PrivacyCondition.name < l[j].PrivacyCondition.name
}
return l[i].PrivacyCondition.origin.name < l[j].PrivacyCondition.origin.name
}
return l[i].ShareCondition.name < l[j].ShareCondition.name
}
return l[i].ShareCondition.origin.name < l[j].ShareCondition.origin.name
}
return l[i].SourceNode.name < l[j].SourceNode.name
}
func TestConflictingSharedPrivateSource(t *testing.T) {
tests := []struct {
name string
roots []string
edges []annotated
expectedConflicts []confl
}{
{
name: "firstparty",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedConflicts: []confl{},
},
{
name: "notice",
roots: []string{"mitBin.meta_lic"},
edges: []annotated{
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedConflicts: []confl{},
},
{
name: "lgpl",
roots: []string{"lgplBin.meta_lic"},
edges: []annotated{
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedConflicts: []confl{},
},
{
name: "proprietaryonrestricted",
roots: []string{"proprietary.meta_lic"},
edges: []annotated{
{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedConflicts: []confl{
{"proprietary.meta_lic", "gplLib.meta_lic:restricted", "proprietary.meta_lic:proprietary"},
},
},
{
name: "restrictedonproprietary",
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
},
expectedConflicts: []confl{
{"proprietary.meta_lic", "gplBin.meta_lic:restricted", "proprietary.meta_lic:proprietary"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stderr := &bytes.Buffer{}
lg, err := toGraph(stderr, tt.roots, tt.edges)
if err != nil {
t.Errorf("unexpected test data error: got %w, want no error", err)
return
}
expectedConflicts := toConflictList(lg, tt.expectedConflicts)
actualConflicts := ConflictingSharedPrivateSource(lg)
sort.Sort(byConflict(expectedConflicts))
sort.Sort(byConflict(actualConflicts))
if len(expectedConflicts) != len(actualConflicts) {
t.Errorf("unexpected number of share/privacy conflicts: got %v with %d conflicts, want %v with %d conflicts",
actualConflicts, len(actualConflicts), expectedConflicts, len(expectedConflicts))
} else {
for i := 0; i < len(actualConflicts); i++ {
if !actualConflicts[i].IsEqualTo(expectedConflicts[i]) {
t.Errorf("unexpected share/privacy conflict at element %d: got %q, want %q",
i, actualConflicts[i].Error(), expectedConflicts[i].Error())
}
}
}
})
}
}

View File

@@ -0,0 +1,54 @@
// Copyright 2021 Google LLC
//
// 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 compliance
// ShippedNodes returns the set of nodes in a license graph where the target or
// a derivative work gets distributed. (caches result)
func ShippedNodes(lg *LicenseGraph) *TargetNodeSet {
lg.mu.Lock()
shipped := lg.shippedNodes
lg.mu.Unlock()
if shipped != nil {
return shipped
}
tset := make(map[*TargetNode]bool)
WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
if _, alreadyWalked := tset[tn]; alreadyWalked {
return false
}
if len(path) > 0 {
if !edgeIsDerivation(path[len(path)-1]) {
return false
}
}
tset[tn] = true
return true
})
shipped = &TargetNodeSet{tset}
lg.mu.Lock()
if lg.shippedNodes == nil {
lg.shippedNodes = shipped
} else {
// if we end up with 2, release the later for garbage collection.
shipped = lg.shippedNodes
}
lg.mu.Unlock()
return shipped
}

View File

@@ -0,0 +1,130 @@
// Copyright 2021 Google LLC
//
// 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 compliance
import (
"bytes"
"sort"
"testing"
)
func TestShippedNodes(t *testing.T) {
tests := []struct {
name string
roots []string
edges []annotated
expectedNodes []string
}{
{
name: "singleton",
roots: []string{"apacheLib.meta_lic"},
edges: []annotated{},
expectedNodes: []string{"apacheLib.meta_lic"},
},
{
name: "simplebinary",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedNodes: []string{"apacheBin.meta_lic", "apacheLib.meta_lic"},
},
{
name: "simpledynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedNodes: []string{"apacheBin.meta_lic"},
},
{
name: "container",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedNodes: []string{
"apacheContainer.meta_lic",
"apacheLib.meta_lic",
"gplLib.meta_lic",
},
},
{
name: "binary",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedNodes: []string{
"apacheBin.meta_lic",
"apacheLib.meta_lic",
"gplLib.meta_lic",
},
},
{
name: "binarydynamic",
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
},
expectedNodes: []string{
"apacheBin.meta_lic",
"apacheLib.meta_lic",
},
},
{
name: "containerdeep",
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
{"apacheLib.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
},
expectedNodes: []string{
"apacheContainer.meta_lic",
"apacheBin.meta_lic",
"apacheLib.meta_lic",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stderr := &bytes.Buffer{}
lg, err := toGraph(stderr, tt.roots, tt.edges)
if err != nil {
t.Errorf("unexpected test data error: got %w, want no error", err)
return
}
expectedNodes := append([]string{}, tt.expectedNodes...)
actualNodes := ShippedNodes(lg).Names()
sort.Strings(expectedNodes)
sort.Strings(actualNodes)
if len(expectedNodes) != len(actualNodes) {
t.Errorf("unexpected number of shipped nodes: got %v with %d nodes, want %v with %d nodes",
actualNodes, len(actualNodes), expectedNodes, len(expectedNodes))
return
}
for i := 0; i < len(actualNodes); i++ {
if expectedNodes[i] != actualNodes[i] {
t.Errorf("unexpected node at index %d: got %q in %v, want %q in %v",
i, actualNodes[i], actualNodes, expectedNodes[i], expectedNodes)
}
}
})
}
}

View File

@@ -0,0 +1,76 @@
// Copyright 2021 Google LLC
//
// 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 compliance
// VisitNode is called for each root and for each walked dependency node by
// WalkTopDown. When VisitNode returns true, WalkTopDown will proceed to walk
// down the dependences of the node
type VisitNode func(*LicenseGraph, *TargetNode, TargetEdgePath) bool
// WalkTopDown does a top-down walk of `lg` calling `visit` and descending
// into depenencies when `visit` returns true.
func WalkTopDown(lg *LicenseGraph, visit VisitNode) {
path := NewTargetEdgePath(32)
// must be indexed for fast lookup
lg.indexForward()
var walk func(f string)
walk = func(f string) {
visitChildren := visit(lg, lg.targets[f], *path)
if !visitChildren {
return
}
for _, edge := range lg.index[f] {
path.Push(TargetEdge{lg, edge})
walk(edge.dependency)
path.Pop()
}
}
for _, r := range lg.rootFiles {
path.Clear()
walk(r)
}
}
// WalkResolutionsForCondition performs a top-down walk of the LicenseGraph
// resolving all distributed works for condition `names`.
func WalkResolutionsForCondition(lg *LicenseGraph, rs *ResolutionSet, names ConditionNames) *ResolutionSet {
shipped := ShippedNodes(lg)
// rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions
//
// rmap is the resulting ResolutionSet
rmap := make(map[*TargetNode]actionSet)
WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, _ TargetEdgePath) bool {
if _, ok := rmap[tn]; ok {
return false
}
if !shipped.Contains(tn) {
return false
}
if as, ok := rs.resolutions[tn]; ok {
fas := as.byActsOn(shipped).byName(names)
if !fas.isEmpty() {
rmap[tn] = fas
}
}
return tn.IsContainer() // descend into containers
})
return &ResolutionSet{rmap}
}

View File

@@ -0,0 +1,629 @@
// Copyright 2021 Google LLC
//
// 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 compliance
import (
"bytes"
"testing"
)
func TestWalkResolutionsForCondition(t *testing.T) {
tests := []struct {
name string
condition ConditionNames
roots []string
edges []annotated
expectedResolutions []res
}{
{
name: "firstparty",
condition: ImpliesNotice,
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
},
},
{
name: "notice",
condition: ImpliesNotice,
roots: []string{"mitBin.meta_lic"},
edges: []annotated{
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
{"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "fponlgplnotice",
condition: ImpliesNotice,
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
},
},
{
name: "fponlgpldynamicnotice",
condition: ImpliesNotice,
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
},
},
{
name: "independentmodulenotice",
condition: ImpliesNotice,
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
},
},
{
name: "independentmodulerestricted",
condition: ImpliesRestricted,
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{},
},
{
name: "independentmodulestaticnotice",
condition: ImpliesNotice,
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "independentmodulestaticrestricted",
condition: ImpliesRestricted,
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "dependentmodulenotice",
condition: ImpliesNotice,
roots: []string{"dependentModule.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "dependentmodulerestricted",
condition: ImpliesRestricted,
roots: []string{"dependentModule.meta_lic"},
edges: []annotated{
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "lgplonfpnotice",
condition: ImpliesNotice,
roots: []string{"lgplBin.meta_lic"},
edges: []annotated{
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
{"lgplBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
},
},
{
name: "lgplonfprestricted",
condition: ImpliesRestricted,
roots: []string{"lgplBin.meta_lic"},
edges: []annotated{
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
{"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
},
},
{
name: "lgplonfpdynamicnotice",
condition: ImpliesNotice,
roots: []string{"lgplBin.meta_lic"},
edges: []annotated{
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
},
},
{
name: "lgplonfpdynamicrestricted",
condition: ImpliesRestricted,
roots: []string{"lgplBin.meta_lic"},
edges: []annotated{
{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
},
},
{
name: "gplonfpnotice",
condition: ImpliesNotice,
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
{"gplBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "gplonfprestricted",
condition: ImpliesRestricted,
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
{"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "gplcontainernotice",
condition: ImpliesNotice,
roots: []string{"gplContainer.meta_lic"},
edges: []annotated{
{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
{"gplContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
},
},
{
name: "gplcontainerrestricted",
condition: ImpliesRestricted,
roots: []string{"gplContainer.meta_lic"},
edges: []annotated{
{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
{"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
},
},
{
name: "gploncontainernotice",
condition: ImpliesNotice,
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "gploncontainerrestricted",
condition: ImpliesRestricted,
roots: []string{"apacheContainer.meta_lic"},
edges: []annotated{
{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "gplonbinnotice",
condition: ImpliesNotice,
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
{"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "gplonbinrestricted",
condition: ImpliesRestricted,
roots: []string{"apacheBin.meta_lic"},
edges: []annotated{
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "gplonfpdynamicnotice",
condition: ImpliesNotice,
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "gplonfpdynamicrestricted",
condition: ImpliesRestricted,
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "gplonfpdynamicrestrictedshipped",
condition: ImpliesRestricted,
roots: []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
{"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "independentmodulereversenotice",
condition: ImpliesNotice,
roots: []string{"gplWithClasspathException.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "independentmodulereverserestricted",
condition: ImpliesRestricted,
roots: []string{"gplWithClasspathException.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "independentmodulereverserestrictedshipped",
condition: ImpliesRestricted,
roots: []string{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "independentmodulereversestaticnotice",
condition: ImpliesNotice,
roots: []string{"gplWithClasspathException.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "independentmodulereversestaticrestricted",
condition: ImpliesRestricted,
roots: []string{"gplWithClasspathException.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "dependentmodulereversenotice",
condition: ImpliesNotice,
roots: []string{"gplWithClasspathException.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "dependentmodulereverserestricted",
condition: ImpliesRestricted,
roots: []string{"gplWithClasspathException.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "dependentmodulereverserestrictedshipped",
condition: ImpliesRestricted,
roots: []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
edges: []annotated{
{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
},
expectedResolutions: []res{
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
},
},
{
name: "ponrnotice",
condition: ImpliesNotice,
roots: []string{"proprietary.meta_lic"},
edges: []annotated{
{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
{"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
{"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "ponrrestricted",
condition: ImpliesRestricted,
roots: []string{"proprietary.meta_lic"},
edges: []annotated{
{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
{"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
},
},
{
name: "ponrproprietary",
condition: ImpliesProprietary,
roots: []string{"proprietary.meta_lic"},
edges: []annotated{
{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
},
},
{
name: "ronpnotice",
condition: ImpliesNotice,
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
{"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
{"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "ronprestricted",
condition: ImpliesRestricted,
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
{"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
},
},
{
name: "ronpproprietary",
condition: ImpliesProprietary,
roots: []string{"gplBin.meta_lic"},
edges: []annotated{
{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
},
},
{
name: "noticeonb_e_onotice",
condition: ImpliesNotice,
roots: []string{"mitBin.meta_lic"},
edges: []annotated{
{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
{"mitBin.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
},
},
{
name: "noticeonb_e_orestricted",
condition: ImpliesRestricted,
roots: []string{"mitBin.meta_lic"},
edges: []annotated{
{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
},
expectedResolutions: []res{},
},
{
name: "noticeonb_e_ob_e_o",
condition: ImpliesByExceptionOnly,
roots: []string{"mitBin.meta_lic"},
edges: []annotated{
{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"mitBin.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
},
},
{
name: "b_e_oonnoticenotice",
condition: ImpliesNotice,
roots: []string{"by_exception.meta_lic"},
edges: []annotated{
{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
{"by_exception.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "b_e_oonnoticerestricted",
condition: ImpliesRestricted,
roots: []string{"by_exception.meta_lic"},
edges: []annotated{
{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{},
},
{
name: "b_e_oonnoticeb_e_o",
condition: ImpliesByExceptionOnly,
roots: []string{"by_exception.meta_lic"},
edges: []annotated{
{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
},
},
{
name: "noticeonrecipnotice",
condition: ImpliesNotice,
roots: []string{"mitBin.meta_lic"},
edges: []annotated{
{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
{"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
},
},
{
name: "noticeonreciprecip",
condition: ImpliesReciprocal,
roots: []string{"mitBin.meta_lic"},
edges: []annotated{
{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
},
},
{
name: "reciponnoticenotice",
condition: ImpliesNotice,
roots: []string{"mplBin.meta_lic"},
edges: []annotated{
{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
{"mplBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
},
},
{
name: "reciponnoticerecip",
condition: ImpliesReciprocal,
roots: []string{"mplBin.meta_lic"},
edges: []annotated{
{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
},
expectedResolutions: []res{
{"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stderr := &bytes.Buffer{}
lg, err := toGraph(stderr, tt.roots, tt.edges)
if err != nil {
t.Errorf("unexpected test data error: got %w, want no error", err)
return
}
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
actualRs := WalkResolutionsForCondition(lg, ResolveTopDownConditions(lg), tt.condition)
checkSame(actualRs, expectedRs, t)
})
}
}

View File

@@ -125,10 +125,10 @@ func TestResolutionSet_AttachedToTarget(t *testing.T) {
rsShare := toResolutionSet(lg, share) rsShare := toResolutionSet(lg, share)
if rsShare.AttachedToTarget(newTestNode(lg, "binc")) { if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
t.Errorf("unexpected AttachedToTarget(\"binc\"): got true, want false") t.Errorf("unexpected AttachedToTarget(\"binc\"): got true, want false")
} }
if !rsShare.AttachedToTarget(newTestNode(lg, "image")) { if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
t.Errorf("unexpected AttachedToTarget(\"image\"): got false want true") t.Errorf("unexpected AttachedToTarget(\"image\"): got false want true")
} }
} }

View File

@@ -30,6 +30,85 @@ const (
license_kinds: "SPDX-license-identifier-Apache-2.0" license_kinds: "SPDX-license-identifier-Apache-2.0"
license_conditions: "notice" license_conditions: "notice"
` `
// GPL starts a test metadata file for GPL 2.0 licensing.
GPL = `` +
`package_name: "Free Software"
license_kinds: "SPDX-license-identifier-GPL-2.0"
license_conditions: "restricted"
`
// Classpath starts a test metadata file for GPL 2.0 with classpath exception licensing.
Classpath = `` +
`package_name: "Free Software"
license_kinds: "SPDX-license-identifier-GPL-2.0-with-classpath-exception"
license_conditions: "restricted"
`
// DependentModule starts a test metadata file for a module in the same package as `Classpath`.
DependentModule = `` +
`package_name: "Free Software"
license_kinds: "SPDX-license-identifier-MIT"
license_conditions: "notice"
`
// LGPL starts a test metadata file for a module with LGPL 2.0 licensing.
LGPL = `` +
`package_name: "Free Library"
license_kinds: "SPDX-license-identifier-LGPL-2.0"
license_conditions: "restricted"
`
// MPL starts a test metadata file for a module with MPL 2.0 reciprical licensing.
MPL = `` +
`package_name: "Reciprocal"
license_kinds: "SPDX-license-identifier-MPL-2.0"
license_conditions: "reciprocal"
`
// MIT starts a test metadata file for a module with generic notice (MIT) licensing.
MIT = `` +
`package_name: "Android"
license_kinds: "SPDX-license-identifier-MIT"
license_conditions: "notice"
`
// Proprietary starts a test metadata file for a module with proprietary licensing.
Proprietary = `` +
`package_name: "Android"
license_kinds: "legacy_proprietary"
license_conditions: "proprietary"
`
// ByException starts a test metadata file for a module with by_exception_only licensing.
ByException = `` +
`package_name: "Special"
license_kinds: "legacy_by_exception_only"
license_conditions: "by_exception_only"
`
)
var (
// meta maps test file names to metadata file content without dependencies.
meta = map[string]string{
"apacheBin.meta_lic": AOSP,
"apacheLib.meta_lic": AOSP,
"apacheContainer.meta_lic": AOSP + "is_container: true\n",
"dependentModule.meta_lic": DependentModule,
"gplWithClasspathException.meta_lic": Classpath,
"gplBin.meta_lic": GPL,
"gplLib.meta_lic": GPL,
"gplContainer.meta_lic": GPL + "is_container: true\n",
"lgplBin.meta_lic": LGPL,
"lgplLib.meta_lic": LGPL,
"mitBin.meta_lic": MIT,
"mitLib.meta_lic": MIT,
"mplBin.meta_lic": MPL,
"mplLib.meta_lic": MPL,
"proprietary.meta_lic": Proprietary,
"by_exception.meta_lic": ByException,
}
) )
// toConditionList converts a test data map of condition name to origin names into a ConditionList. // toConditionList converts a test data map of condition name to origin names into a ConditionList.
@@ -125,6 +204,98 @@ func (l byEdge) Less(i, j int) bool {
return l[i].target < l[j].target return l[i].target < l[j].target
} }
// annotated describes annotated test data edges to define test graphs.
type annotated struct {
target, dep string
annotations []string
}
func (e annotated) String() string {
if e.annotations != nil {
return e.target + " -> " + e.dep + " [" + strings.Join(e.annotations, ", ") + "]"
}
return e.target + " -> " + e.dep
}
func (e annotated) IsEqualTo(other annotated) bool {
if e.target != other.target {
return false
}
if e.dep != other.dep {
return false
}
if len(e.annotations) != len(other.annotations) {
return false
}
a1 := append([]string{}, e.annotations...)
a2 := append([]string{}, other.annotations...)
for i := 0; i < len(a1); i++ {
if a1[i] != a2[i] {
return false
}
}
return true
}
// toGraph converts a list of roots and a list of annotated edges into a test license graph.
func toGraph(stderr io.Writer, roots []string, edges []annotated) (*LicenseGraph, error) {
deps := make(map[string][]annotated)
for _, root := range roots {
deps[root] = []annotated{}
}
for _, edge := range edges {
if prev, ok := deps[edge.target]; ok {
deps[edge.target] = append(prev, edge)
} else {
deps[edge.target] = []annotated{edge}
}
if _, ok := deps[edge.dep]; !ok {
deps[edge.dep] = []annotated{}
}
}
fs := make(testFS)
for file, edges := range deps {
body := meta[file]
for _, edge := range edges {
body += fmt.Sprintf("deps: {\n file: %q\n", edge.dep)
for _, ann := range edge.annotations {
body += fmt.Sprintf(" annotations: %q\n", ann)
}
body += "}\n"
}
fs[file] = []byte(body)
}
return ReadLicenseGraph(&fs, stderr, roots)
}
// byAnnotatedEdge orders edges by target then dep name then annotations.
type byAnnotatedEdge []annotated
func (l byAnnotatedEdge) Len() int { return len(l) }
func (l byAnnotatedEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l byAnnotatedEdge) Less(i, j int) bool {
if l[i].target == l[j].target {
if l[i].dep == l[j].dep {
ai := append([]string{}, l[i].annotations...)
aj := append([]string{}, l[j].annotations...)
sort.Strings(ai)
sort.Strings(aj)
for k := 0; k < len(ai) && k < len(aj); k++ {
if ai[k] == aj[k] {
continue
}
return ai[k] < aj[k]
}
return len(ai) < len(aj)
}
return l[i].dep < l[j].dep
}
return l[i].target < l[j].target
}
// res describes test data resolutions to define test resolution sets. // res describes test data resolutions to define test resolution sets.
type res struct { type res struct {
attachesTo, actsOn, origin, condition string attachesTo, actsOn, origin, condition string
@@ -148,6 +319,28 @@ func toResolutionSet(lg *LicenseGraph, data []res) *ResolutionSet {
return &ResolutionSet{rmap} return &ResolutionSet{rmap}
} }
type confl struct {
sourceNode, share, privacy string
}
func toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict {
result := make([]SourceSharePrivacyConflict, 0, len(data))
for _, c := range data {
fields := strings.Split(c.share, ":")
oshare := fields[0]
cshare := fields[1]
fields = strings.Split(c.privacy, ":")
oprivacy := fields[0]
cprivacy := fields[1]
result = append(result, SourceSharePrivacyConflict{
newTestNode(lg, c.sourceNode),
LicenseCondition{cshare, newTestNode(lg, oshare)},
LicenseCondition{cprivacy, newTestNode(lg, oprivacy)},
})
}
return result
}
// checkSameActions compares an actual action set to an expected action set for a test. // checkSameActions compares an actual action set to an expected action set for a test.
func checkSameActions(lg *LicenseGraph, asActual, asExpected actionSet, t *testing.T) { func checkSameActions(lg *LicenseGraph, asActual, asExpected actionSet, t *testing.T) {
rsActual := ResolutionSet{make(map[*TargetNode]actionSet)} rsActual := ResolutionSet{make(map[*TargetNode]actionSet)}