Merge changes Ia2ec1b81,Ibc831ae8
* changes: Performance and scale. Use struct{}
This commit is contained in:
@@ -48,7 +48,6 @@ blueprint_go_binary {
|
|||||||
bootstrap_go_package {
|
bootstrap_go_package {
|
||||||
name: "compliance-module",
|
name: "compliance-module",
|
||||||
srcs: [
|
srcs: [
|
||||||
"actionset.go",
|
|
||||||
"condition.go",
|
"condition.go",
|
||||||
"conditionset.go",
|
"conditionset.go",
|
||||||
"doc.go",
|
"doc.go",
|
||||||
|
@@ -1,119 +0,0 @@
|
|||||||
// 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"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// actionSet maps `actOn` target nodes to the license conditions the actions resolve.
|
|
||||||
type actionSet map[*TargetNode]*LicenseConditionSet
|
|
||||||
|
|
||||||
// String returns a string representation of the set.
|
|
||||||
func (as actionSet) String() string {
|
|
||||||
var sb strings.Builder
|
|
||||||
fmt.Fprintf(&sb, "{")
|
|
||||||
osep := ""
|
|
||||||
for actsOn, cs := range as {
|
|
||||||
cl := cs.AsList()
|
|
||||||
sort.Sort(cl)
|
|
||||||
fmt.Fprintf(&sb, "%s%s -> %s", osep, actsOn.name, cl.String())
|
|
||||||
osep = ", "
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&sb, "}")
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// byName returns the subset of `as` actions where the condition name is in `names`.
|
|
||||||
func (as actionSet) byName(names ConditionNames) actionSet {
|
|
||||||
result := make(actionSet)
|
|
||||||
for actsOn, cs := range as {
|
|
||||||
bn := cs.ByName(names)
|
|
||||||
if bn.IsEmpty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result[actsOn] = bn
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// byActsOn returns the subset of `as` where `actsOn` is in the `reachable` target node set.
|
|
||||||
func (as actionSet) byActsOn(reachable *TargetNodeSet) actionSet {
|
|
||||||
result := make(actionSet)
|
|
||||||
for actsOn, cs := range as {
|
|
||||||
if !reachable.Contains(actsOn) || cs.IsEmpty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result[actsOn] = cs.Copy()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy returns another actionSet with the same value as `as`
|
|
||||||
func (as actionSet) copy() actionSet {
|
|
||||||
result := make(actionSet)
|
|
||||||
for actsOn, cs := range as {
|
|
||||||
if cs.IsEmpty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result[actsOn] = cs.Copy()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// addSet adds all of the actions of `other` if not already present.
|
|
||||||
func (as actionSet) addSet(other actionSet) {
|
|
||||||
for actsOn, cs := range other {
|
|
||||||
as.add(actsOn, cs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add makes the action on `actsOn` to resolve the conditions in `cs` a member of the set.
|
|
||||||
func (as actionSet) add(actsOn *TargetNode, cs *LicenseConditionSet) {
|
|
||||||
if acs, ok := as[actsOn]; ok {
|
|
||||||
acs.AddSet(cs)
|
|
||||||
} else {
|
|
||||||
as[actsOn] = cs.Copy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addCondition makes the action on `actsOn` to resolve `lc` a member of the set.
|
|
||||||
func (as actionSet) addCondition(actsOn *TargetNode, lc LicenseCondition) {
|
|
||||||
if _, ok := as[actsOn]; !ok {
|
|
||||||
as[actsOn] = newLicenseConditionSet()
|
|
||||||
}
|
|
||||||
as[actsOn].Add(lc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isEmpty returns true if no action to resolve a condition exists.
|
|
||||||
func (as actionSet) isEmpty() bool {
|
|
||||||
for _, cs := range as {
|
|
||||||
if !cs.IsEmpty() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// conditions returns the set of conditions resolved by the action set.
|
|
||||||
func (as actionSet) conditions() *LicenseConditionSet {
|
|
||||||
result := newLicenseConditionSet()
|
|
||||||
for _, cs := range as {
|
|
||||||
result.AddSet(cs)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
@@ -30,8 +30,8 @@ func init() {
|
|||||||
|
|
||||||
Reports on stderr any targets where policy says that the source both
|
Reports on stderr any targets where policy says that the source both
|
||||||
must and must not be shared. The error report indicates the target, the
|
must and must not be shared. The error report indicates the target, the
|
||||||
license condition with origin that has a source privacy policy, and the
|
license condition that has a source privacy policy, and the license
|
||||||
license condition with origin that has a source sharing policy.
|
condition that has a source sharing policy.
|
||||||
|
|
||||||
Any given target may appear multiple times with different combinations
|
Any given target may appear multiple times with different combinations
|
||||||
of conflicting license conditions.
|
of conflicting license conditions.
|
||||||
|
@@ -23,15 +23,12 @@ import (
|
|||||||
|
|
||||||
type outcome struct {
|
type outcome struct {
|
||||||
target string
|
target string
|
||||||
privacyOrigin string
|
|
||||||
privacyCondition string
|
privacyCondition string
|
||||||
shareOrigin string
|
|
||||||
shareCondition string
|
shareCondition string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *outcome) String() string {
|
func (o *outcome) String() string {
|
||||||
return fmt.Sprintf("%s %s from %s and must share from %s %s",
|
return fmt.Sprintf("%s %s and must share from %s", o.target, o.privacyCondition, o.shareCondition)
|
||||||
o.target, o.privacyCondition, o.privacyOrigin, o.shareCondition, o.shareOrigin)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type outcomeList []*outcome
|
type outcomeList []*outcome
|
||||||
@@ -180,9 +177,7 @@ func Test(t *testing.T) {
|
|||||||
expectedOutcomes: outcomeList{
|
expectedOutcomes: outcomeList{
|
||||||
&outcome{
|
&outcome{
|
||||||
target: "testdata/proprietary/bin/bin2.meta_lic",
|
target: "testdata/proprietary/bin/bin2.meta_lic",
|
||||||
privacyOrigin: "testdata/proprietary/bin/bin2.meta_lic",
|
|
||||||
privacyCondition: "proprietary",
|
privacyCondition: "proprietary",
|
||||||
shareOrigin: "testdata/proprietary/lib/libb.so.meta_lic",
|
|
||||||
shareCondition: "restricted",
|
shareCondition: "restricted",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -195,9 +190,7 @@ func Test(t *testing.T) {
|
|||||||
expectedOutcomes: outcomeList{
|
expectedOutcomes: outcomeList{
|
||||||
&outcome{
|
&outcome{
|
||||||
target: "testdata/proprietary/bin/bin2.meta_lic",
|
target: "testdata/proprietary/bin/bin2.meta_lic",
|
||||||
privacyOrigin: "testdata/proprietary/bin/bin2.meta_lic",
|
|
||||||
privacyCondition: "proprietary",
|
privacyCondition: "proprietary",
|
||||||
shareOrigin: "testdata/proprietary/lib/libb.so.meta_lic",
|
|
||||||
shareCondition: "restricted",
|
shareCondition: "restricted",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -210,9 +203,7 @@ func Test(t *testing.T) {
|
|||||||
expectedOutcomes: outcomeList{
|
expectedOutcomes: outcomeList{
|
||||||
&outcome{
|
&outcome{
|
||||||
target: "testdata/proprietary/lib/liba.so.meta_lic",
|
target: "testdata/proprietary/lib/liba.so.meta_lic",
|
||||||
privacyOrigin: "testdata/proprietary/lib/liba.so.meta_lic",
|
|
||||||
privacyCondition: "proprietary",
|
privacyCondition: "proprietary",
|
||||||
shareOrigin: "testdata/proprietary/lib/libb.so.meta_lic",
|
|
||||||
shareCondition: "restricted",
|
shareCondition: "restricted",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -225,9 +216,7 @@ func Test(t *testing.T) {
|
|||||||
expectedOutcomes: outcomeList{
|
expectedOutcomes: outcomeList{
|
||||||
&outcome{
|
&outcome{
|
||||||
target: "testdata/proprietary/bin/bin2.meta_lic",
|
target: "testdata/proprietary/bin/bin2.meta_lic",
|
||||||
privacyOrigin: "testdata/proprietary/bin/bin2.meta_lic",
|
|
||||||
privacyCondition: "proprietary",
|
privacyCondition: "proprietary",
|
||||||
shareOrigin: "testdata/proprietary/lib/libb.so.meta_lic",
|
|
||||||
shareCondition: "restricted",
|
shareCondition: "restricted",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -277,10 +266,8 @@ func Test(t *testing.T) {
|
|||||||
cFields := strings.Split(ts, " ")
|
cFields := strings.Split(ts, " ")
|
||||||
actualOutcomes = append(actualOutcomes, &outcome{
|
actualOutcomes = append(actualOutcomes, &outcome{
|
||||||
target: cFields[0],
|
target: cFields[0],
|
||||||
privacyOrigin: cFields[3],
|
|
||||||
privacyCondition: cFields[1],
|
privacyCondition: cFields[1],
|
||||||
shareOrigin: cFields[9],
|
shareCondition: cFields[6],
|
||||||
shareCondition: cFields[8],
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(actualOutcomes) != len(tt.expectedOutcomes) {
|
if len(actualOutcomes) != len(tt.expectedOutcomes) {
|
||||||
|
@@ -327,13 +327,13 @@ func Test_plaintext(t *testing.T) {
|
|||||||
roots: []string{"highest.apex.meta_lic"},
|
roots: []string{"highest.apex.meta_lic"},
|
||||||
ctx: context{stripPrefix: "testdata/restricted/", labelConditions: true},
|
ctx: context{stripPrefix: "testdata/restricted/", labelConditions: true},
|
||||||
expectedOut: []string{
|
expectedOut: []string{
|
||||||
"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted static",
|
"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted_allows_dynamic_linking static",
|
||||||
"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static",
|
"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static",
|
||||||
"bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted dynamic",
|
"bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted dynamic",
|
||||||
"bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic",
|
"bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic",
|
||||||
"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static",
|
"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static",
|
||||||
"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static",
|
"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static",
|
||||||
"highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted static",
|
"highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted_allows_dynamic_linking static",
|
||||||
"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static",
|
"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -996,7 +996,7 @@ func Test_graphviz(t *testing.T) {
|
|||||||
matchTarget("bin/bin1.meta_lic", "notice"),
|
matchTarget("bin/bin1.meta_lic", "notice"),
|
||||||
matchTarget("bin/bin2.meta_lic", "notice"),
|
matchTarget("bin/bin2.meta_lic", "notice"),
|
||||||
matchTarget("highest.apex.meta_lic", "notice"),
|
matchTarget("highest.apex.meta_lic", "notice"),
|
||||||
matchTarget("lib/liba.so.meta_lic", "restricted"),
|
matchTarget("lib/liba.so.meta_lic", "restricted_allows_dynamic_linking"),
|
||||||
matchTarget("lib/libb.so.meta_lic", "restricted"),
|
matchTarget("lib/libb.so.meta_lic", "restricted"),
|
||||||
matchTarget("lib/libc.a.meta_lic", "reciprocal"),
|
matchTarget("lib/libc.a.meta_lic", "reciprocal"),
|
||||||
matchTarget("lib/libd.so.meta_lic", "notice"),
|
matchTarget("lib/libd.so.meta_lic", "notice"),
|
||||||
|
@@ -36,7 +36,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type context struct {
|
type context struct {
|
||||||
conditions []string
|
conditions []compliance.LicenseCondition
|
||||||
graphViz bool
|
graphViz bool
|
||||||
labelConditions bool
|
labelConditions bool
|
||||||
stripPrefix string
|
stripPrefix string
|
||||||
@@ -50,9 +50,9 @@ Outputs a space-separated Target ActsOn Origin Condition tuple for each
|
|||||||
resolution in the graph. When -dot flag given, outputs nodes and edges
|
resolution in the graph. When -dot flag given, outputs nodes and edges
|
||||||
in graphviz directed graph format.
|
in graphviz directed graph format.
|
||||||
|
|
||||||
If one or more '-c condition' conditions are given, outputs the joined
|
If one or more '-c condition' conditions are given, outputs the
|
||||||
set of resolutions for all of the conditions. Otherwise, outputs the
|
resolution for the union of the conditions. Otherwise, outputs the
|
||||||
result of the bottom-up and top-down resolve only.
|
resolution for all conditions.
|
||||||
|
|
||||||
In plain text mode, when '-label_conditions' is requested, the Target
|
In plain text mode, when '-label_conditions' is requested, the Target
|
||||||
and Origin have colon-separated license conditions appended:
|
and Origin have colon-separated license conditions appended:
|
||||||
@@ -86,13 +86,17 @@ func main() {
|
|||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lcs := make([]compliance.LicenseCondition, 0, len(*conditions))
|
||||||
|
for _, name := range *conditions {
|
||||||
|
lcs = append(lcs, compliance.RecognizedConditionNames[name])
|
||||||
|
}
|
||||||
ctx := &context{
|
ctx := &context{
|
||||||
conditions: append([]string{}, *conditions...),
|
conditions: lcs,
|
||||||
graphViz: *graphViz,
|
graphViz: *graphViz,
|
||||||
labelConditions: *labelConditions,
|
labelConditions: *labelConditions,
|
||||||
stripPrefix: *stripPrefix,
|
stripPrefix: *stripPrefix,
|
||||||
}
|
}
|
||||||
err := dumpResolutions(ctx, os.Stdout, os.Stderr, flag.Args()...)
|
_, err := dumpResolutions(ctx, os.Stdout, os.Stderr, flag.Args()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == failNoneRequested {
|
if err == failNoneRequested {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
@@ -104,36 +108,31 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dumpResolutions implements the dumpresolutions utility.
|
// dumpResolutions implements the dumpresolutions utility.
|
||||||
func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) error {
|
func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) (*compliance.LicenseGraph, error) {
|
||||||
if len(files) < 1 {
|
if len(files) < 1 {
|
||||||
return failNoneRequested
|
return nil, failNoneRequested
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the license graph from the license metadata files (*.meta_lic).
|
// Read the license graph from the license metadata files (*.meta_lic).
|
||||||
licenseGraph, err := compliance.ReadLicenseGraph(os.DirFS("."), stderr, files)
|
licenseGraph, err := compliance.ReadLicenseGraph(os.DirFS("."), stderr, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
|
return nil, fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
|
||||||
}
|
}
|
||||||
if licenseGraph == nil {
|
if licenseGraph == nil {
|
||||||
return failNoLicenses
|
return nil, failNoLicenses
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolutions will contain the requested set of resolutions.
|
compliance.ResolveTopDownConditions(licenseGraph)
|
||||||
var resolutions *compliance.ResolutionSet
|
cs := compliance.AllLicenseConditions
|
||||||
|
|
||||||
resolutions = compliance.ResolveTopDownConditions(licenseGraph)
|
|
||||||
if len(ctx.conditions) > 0 {
|
if len(ctx.conditions) > 0 {
|
||||||
rlist := make([]*compliance.ResolutionSet, 0, len(ctx.conditions))
|
cs = compliance.NewLicenseConditionSet()
|
||||||
for _, c := range ctx.conditions {
|
for _, c := range ctx.conditions {
|
||||||
rlist = append(rlist, compliance.WalkResolutionsForCondition(licenseGraph, resolutions, compliance.ConditionNames{c}))
|
cs = cs.Plus(c)
|
||||||
}
|
|
||||||
if len(rlist) == 1 {
|
|
||||||
resolutions = rlist[0]
|
|
||||||
} else {
|
|
||||||
resolutions = compliance.JoinResolutionSets(rlist...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolutions := compliance.WalkResolutionsForCondition(licenseGraph, cs)
|
||||||
|
|
||||||
// nodes maps license metadata file names to graphViz node names when graphViz requested.
|
// nodes maps license metadata file names to graphViz node names when graphViz requested.
|
||||||
nodes := make(map[string]string)
|
nodes := make(map[string]string)
|
||||||
n := 0
|
n := 0
|
||||||
@@ -142,11 +141,7 @@ func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) er
|
|||||||
targetOut := func(target *compliance.TargetNode, sep string) string {
|
targetOut := func(target *compliance.TargetNode, sep string) string {
|
||||||
tOut := strings.TrimPrefix(target.Name(), ctx.stripPrefix)
|
tOut := strings.TrimPrefix(target.Name(), ctx.stripPrefix)
|
||||||
if ctx.labelConditions {
|
if ctx.labelConditions {
|
||||||
conditions := make([]string, 0, target.LicenseConditions().Count())
|
conditions := target.LicenseConditions().Names()
|
||||||
for _, lc := range target.LicenseConditions().AsList() {
|
|
||||||
conditions = append(conditions, lc.Name())
|
|
||||||
}
|
|
||||||
sort.Strings(conditions)
|
|
||||||
if len(conditions) > 0 {
|
if len(conditions) > 0 {
|
||||||
tOut += sep + strings.Join(conditions, sep)
|
tOut += sep + strings.Join(conditions, sep)
|
||||||
}
|
}
|
||||||
@@ -168,26 +163,16 @@ func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) er
|
|||||||
// outputResolution prints a resolution in the requested format to `stdout`, where one can read
|
// outputResolution prints a resolution in the requested format to `stdout`, where one can read
|
||||||
// a resolution as `tname` resolves `oname`'s conditions named in `cnames`.
|
// a resolution as `tname` resolves `oname`'s conditions named in `cnames`.
|
||||||
// `tname` is the name of the target the resolution applies to.
|
// `tname` is the name of the target the resolution applies to.
|
||||||
// `oname` is the name of the target where the conditions originate.
|
|
||||||
// `cnames` is the list of conditions to resolve.
|
// `cnames` is the list of conditions to resolve.
|
||||||
outputResolution := func(tname, aname, oname string, cnames []string) {
|
outputResolution := func(tname, aname string, cnames []string) {
|
||||||
if ctx.graphViz {
|
if ctx.graphViz {
|
||||||
// ... one edge per line labelled with \\n-separated annotations.
|
// ... one edge per line labelled with \\n-separated annotations.
|
||||||
tNode := nodes[tname]
|
tNode := nodes[tname]
|
||||||
aNode := nodes[aname]
|
aNode := nodes[aname]
|
||||||
oNode := nodes[oname]
|
fmt.Fprintf(stdout, "\t%s -> %s [label=\"%s\"];\n", tNode, aNode, strings.Join(cnames, "\\n"))
|
||||||
fmt.Fprintf(stdout, "\t%s -> %s; %s -> %s [label=\"%s\"];\n", tNode, aNode, aNode, oNode, strings.Join(cnames, "\\n"))
|
|
||||||
} else {
|
} else {
|
||||||
// ... one edge per line with names in a colon-separated tuple.
|
// ... one edge per line with names in a colon-separated tuple.
|
||||||
fmt.Fprintf(stdout, "%s %s %s %s\n", tname, aname, oname, strings.Join(cnames, ":"))
|
fmt.Fprintf(stdout, "%s %s %s\n", tname, aname, strings.Join(cnames, ":"))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// outputSingleton prints `tname` to plain text in the unexpected event that `tname` is the name of
|
|
||||||
// a target in `resolutions.AppliesTo()` but has no conditions to resolve.
|
|
||||||
outputSingleton := func(tname, aname string) {
|
|
||||||
if !ctx.graphViz {
|
|
||||||
fmt.Fprintf(stdout, "%s %s\n", tname, aname)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,16 +185,11 @@ func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) er
|
|||||||
fmt.Fprintf(stdout, "strict digraph {\n\trankdir=LR;\n")
|
fmt.Fprintf(stdout, "strict digraph {\n\trankdir=LR;\n")
|
||||||
for _, target := range targets {
|
for _, target := range targets {
|
||||||
makeNode(target)
|
makeNode(target)
|
||||||
rl := compliance.ResolutionList(resolutions.Resolutions(target))
|
rl := resolutions.Resolutions(target)
|
||||||
sort.Sort(rl)
|
sort.Sort(rl)
|
||||||
for _, r := range rl {
|
for _, r := range rl {
|
||||||
makeNode(r.ActsOn())
|
makeNode(r.ActsOn())
|
||||||
}
|
}
|
||||||
conditions := rl.AllConditions().AsList()
|
|
||||||
sort.Sort(conditions)
|
|
||||||
for _, lc := range conditions {
|
|
||||||
makeNode(lc.Origin())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +202,7 @@ func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) er
|
|||||||
tname = targetOut(target, ":")
|
tname = targetOut(target, ":")
|
||||||
}
|
}
|
||||||
|
|
||||||
rl := compliance.ResolutionList(resolutions.Resolutions(target))
|
rl := resolutions.Resolutions(target)
|
||||||
sort.Sort(rl)
|
sort.Sort(rl)
|
||||||
for _, r := range rl {
|
for _, r := range rl {
|
||||||
var aname string
|
var aname string
|
||||||
@@ -232,38 +212,11 @@ func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) er
|
|||||||
aname = targetOut(r.ActsOn(), ":")
|
aname = targetOut(r.ActsOn(), ":")
|
||||||
}
|
}
|
||||||
|
|
||||||
conditions := r.Resolves().AsList()
|
|
||||||
sort.Sort(conditions)
|
|
||||||
|
|
||||||
// poname is the previous origin name or "" if no previous
|
|
||||||
poname := ""
|
|
||||||
|
|
||||||
// cnames accumulates the list of condition names originating at a single origin that apply to `target`.
|
// cnames accumulates the list of condition names originating at a single origin that apply to `target`.
|
||||||
cnames := make([]string, 0, len(conditions))
|
cnames := r.Resolves().Names()
|
||||||
|
|
||||||
// Output 1 line for each attachesTo+actsOn+origin combination.
|
// Output 1 line for each attachesTo+actsOn combination.
|
||||||
for _, condition := range conditions {
|
outputResolution(tname, aname, cnames)
|
||||||
var oname string
|
|
||||||
if ctx.graphViz {
|
|
||||||
oname = condition.Origin().Name()
|
|
||||||
} else {
|
|
||||||
oname = targetOut(condition.Origin(), ":")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect when origin changes and output prior origin's conditions.
|
|
||||||
if poname != oname && poname != "" {
|
|
||||||
outputResolution(tname, aname, poname, cnames)
|
|
||||||
cnames = cnames[:0]
|
|
||||||
}
|
|
||||||
poname = oname
|
|
||||||
cnames = append(cnames, condition.Name())
|
|
||||||
}
|
|
||||||
// Output last origin's conditions or a singleton if no origins.
|
|
||||||
if poname == "" {
|
|
||||||
outputSingleton(tname, aname)
|
|
||||||
} else {
|
|
||||||
outputResolution(tname, aname, poname, cnames)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If graphViz output, rank the root nodes together, and complete the directed graph.
|
// If graphViz output, rank the root nodes together, and complete the directed graph.
|
||||||
@@ -280,5 +233,5 @@ func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) er
|
|||||||
}
|
}
|
||||||
fmt.Fprintf(stdout, "}\n}\n")
|
fmt.Fprintf(stdout, "}\n}\n")
|
||||||
}
|
}
|
||||||
return nil
|
return licenseGraph, nil
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -83,17 +84,17 @@ func listShare(stdout, stderr io.Writer, files ...string) error {
|
|||||||
shareSource := compliance.ResolveSourceSharing(licenseGraph)
|
shareSource := compliance.ResolveSourceSharing(licenseGraph)
|
||||||
|
|
||||||
// Group the resolutions by project.
|
// Group the resolutions by project.
|
||||||
presolution := make(map[string]*compliance.LicenseConditionSet)
|
presolution := make(map[string]compliance.LicenseConditionSet)
|
||||||
for _, target := range shareSource.AttachesTo() {
|
for _, target := range shareSource.AttachesTo() {
|
||||||
rl := shareSource.Resolutions(target)
|
rl := shareSource.Resolutions(target)
|
||||||
sort.Sort(rl)
|
sort.Sort(rl)
|
||||||
for _, r := range rl {
|
for _, r := range rl {
|
||||||
for _, p := range r.ActsOn().Projects() {
|
for _, p := range r.ActsOn().Projects() {
|
||||||
if _, ok := presolution[p]; !ok {
|
if _, ok := presolution[p]; !ok {
|
||||||
presolution[p] = r.Resolves().Copy()
|
presolution[p] = r.Resolves()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
presolution[p].AddSet(r.Resolves())
|
presolution[p] = presolution[p].Union(r.Resolves())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,17 +108,11 @@ func listShare(stdout, stderr io.Writer, files ...string) error {
|
|||||||
|
|
||||||
// Output the sorted projects and the source-sharing license conditions that each project resolves.
|
// Output the sorted projects and the source-sharing license conditions that each project resolves.
|
||||||
for _, p := range projects {
|
for _, p := range projects {
|
||||||
fmt.Fprintf(stdout, "%s", p)
|
if presolution[p].IsEmpty() {
|
||||||
|
fmt.Fprintf(stdout, "%s\n", p)
|
||||||
// Sort the conditions for repeatability/stability.
|
} else {
|
||||||
conditions := presolution[p].AsList()
|
fmt.Fprintf(stdout, "%s,%s\n", p, strings.Join(presolution[p].Names(), ","))
|
||||||
sort.Sort(conditions)
|
|
||||||
|
|
||||||
// Output the sorted origin:condition pairs.
|
|
||||||
for _, lc := range conditions {
|
|
||||||
fmt.Fprintf(stdout, ",%s:%s", lc.Origin().Name(), lc.Name())
|
|
||||||
}
|
}
|
||||||
fmt.Fprintf(stdout, "\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@@ -98,12 +98,12 @@ func Test(t *testing.T) {
|
|||||||
expectedOut: []projectShare{
|
expectedOut: []projectShare{
|
||||||
{
|
{
|
||||||
project: "device/library",
|
project: "device/library",
|
||||||
conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
|
conditions: []string{"reciprocal"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "static/library",
|
project: "static/library",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/libc.a.meta_lic:reciprocal",
|
"reciprocal",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -115,12 +115,12 @@ func Test(t *testing.T) {
|
|||||||
expectedOut: []projectShare{
|
expectedOut: []projectShare{
|
||||||
{
|
{
|
||||||
project: "device/library",
|
project: "device/library",
|
||||||
conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
|
conditions: []string{"reciprocal"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "static/library",
|
project: "static/library",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/libc.a.meta_lic:reciprocal",
|
"reciprocal",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -132,7 +132,7 @@ func Test(t *testing.T) {
|
|||||||
expectedOut: []projectShare{
|
expectedOut: []projectShare{
|
||||||
{
|
{
|
||||||
project: "device/library",
|
project: "device/library",
|
||||||
conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
|
conditions: []string{"reciprocal"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -144,13 +144,13 @@ func Test(t *testing.T) {
|
|||||||
{
|
{
|
||||||
project: "device/library",
|
project: "device/library",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:reciprocal",
|
"reciprocal",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "static/library",
|
project: "static/library",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/libc.a.meta_lic:reciprocal",
|
"reciprocal",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -168,34 +168,34 @@ func Test(t *testing.T) {
|
|||||||
expectedOut: []projectShare{
|
expectedOut: []projectShare{
|
||||||
{
|
{
|
||||||
project: "base/library",
|
project: "base/library",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "device/library",
|
project: "device/library",
|
||||||
conditions: []string{"lib/liba.so.meta_lic:restricted"},
|
conditions: []string{"restricted_allows_dynamic_linking"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "dynamic/binary",
|
project: "dynamic/binary",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "highest/apex",
|
project: "highest/apex",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"restricted",
|
||||||
"lib/libb.so.meta_lic:restricted",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "static/binary",
|
project: "static/binary",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "static/library",
|
project: "static/library",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"reciprocal",
|
||||||
"lib/libc.a.meta_lic:reciprocal",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -207,34 +207,34 @@ func Test(t *testing.T) {
|
|||||||
expectedOut: []projectShare{
|
expectedOut: []projectShare{
|
||||||
{
|
{
|
||||||
project: "base/library",
|
project: "base/library",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "container/zip",
|
project: "container/zip",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"restricted",
|
||||||
"lib/libb.so.meta_lic:restricted",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "device/library",
|
project: "device/library",
|
||||||
conditions: []string{"lib/liba.so.meta_lic:restricted"},
|
conditions: []string{"restricted_allows_dynamic_linking"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "dynamic/binary",
|
project: "dynamic/binary",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "static/binary",
|
project: "static/binary",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "static/library",
|
project: "static/library",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"reciprocal",
|
||||||
"lib/libc.a.meta_lic:reciprocal",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -247,15 +247,15 @@ func Test(t *testing.T) {
|
|||||||
{
|
{
|
||||||
project: "device/library",
|
project: "device/library",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"restricted",
|
||||||
"lib/libb.so.meta_lic:restricted",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "distributable/application",
|
project: "distributable/application",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"restricted",
|
||||||
"lib/libb.so.meta_lic:restricted",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -268,20 +268,20 @@ func Test(t *testing.T) {
|
|||||||
{
|
{
|
||||||
project: "device/library",
|
project: "device/library",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "static/binary",
|
project: "static/binary",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "static/library",
|
project: "static/library",
|
||||||
conditions: []string{
|
conditions: []string{
|
||||||
"lib/liba.so.meta_lic:restricted",
|
"reciprocal",
|
||||||
"lib/libc.a.meta_lic:reciprocal",
|
"restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -299,15 +299,15 @@ func Test(t *testing.T) {
|
|||||||
expectedOut: []projectShare{
|
expectedOut: []projectShare{
|
||||||
{
|
{
|
||||||
project: "base/library",
|
project: "base/library",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "dynamic/binary",
|
project: "dynamic/binary",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "highest/apex",
|
project: "highest/apex",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -318,15 +318,15 @@ func Test(t *testing.T) {
|
|||||||
expectedOut: []projectShare{
|
expectedOut: []projectShare{
|
||||||
{
|
{
|
||||||
project: "base/library",
|
project: "base/library",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "container/zip",
|
project: "container/zip",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "dynamic/binary",
|
project: "dynamic/binary",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -337,11 +337,11 @@ func Test(t *testing.T) {
|
|||||||
expectedOut: []projectShare{
|
expectedOut: []projectShare{
|
||||||
{
|
{
|
||||||
project: "device/library",
|
project: "device/library",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
project: "distributable/application",
|
project: "distributable/application",
|
||||||
conditions: []string{"lib/libb.so.meta_lic:restricted"},
|
conditions: []string{"restricted"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -365,9 +365,6 @@ func Test(t *testing.T) {
|
|||||||
expectedOut.WriteString(p.project)
|
expectedOut.WriteString(p.project)
|
||||||
for _, lc := range p.conditions {
|
for _, lc := range p.conditions {
|
||||||
expectedOut.WriteString(",")
|
expectedOut.WriteString(",")
|
||||||
expectedOut.WriteString("testdata/")
|
|
||||||
expectedOut.WriteString(tt.condition)
|
|
||||||
expectedOut.WriteString("/")
|
|
||||||
expectedOut.WriteString(lc)
|
expectedOut.WriteString(lc)
|
||||||
}
|
}
|
||||||
expectedOut.WriteString("\n")
|
expectedOut.WriteString("\n")
|
||||||
|
@@ -16,150 +16,87 @@ package compliance
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// LicenseCondition describes an individual license condition or requirement
|
// LicenseCondition identifies a recognized license condition by setting the
|
||||||
// originating at a specific target node. (immutable)
|
// corresponding bit.
|
||||||
//
|
type LicenseCondition uint16
|
||||||
// e.g. A module licensed under GPL terms would originate a `restricted` condition.
|
|
||||||
type LicenseCondition struct {
|
|
||||||
name string
|
|
||||||
origin *TargetNode
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of the condition. e.g. "restricted" or "notice"
|
// LicenseConditionMask is a bitmask for the recognized license conditions.
|
||||||
|
const LicenseConditionMask = LicenseCondition(0x3ff)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UnencumberedCondition identifies public domain or public domain-
|
||||||
|
// like license that disclaims copyright.
|
||||||
|
UnencumberedCondition = LicenseCondition(0x0001)
|
||||||
|
// PermissiveCondition identifies a license without notice or other
|
||||||
|
// significant requirements.
|
||||||
|
PermissiveCondition = LicenseCondition(0x0002)
|
||||||
|
// NoticeCondition identifies a typical open-source license with only
|
||||||
|
// notice or attribution requirements.
|
||||||
|
NoticeCondition = LicenseCondition(0x0004)
|
||||||
|
// ReciprocalCondition identifies a license with requirement to share
|
||||||
|
// the module's source only.
|
||||||
|
ReciprocalCondition = LicenseCondition(0x0008)
|
||||||
|
// RestrictedCondition identifies a license with requirement to share
|
||||||
|
// all source code linked to the module's source.
|
||||||
|
RestrictedCondition = LicenseCondition(0x0010)
|
||||||
|
// RestrictedClasspathExceptionCondition identifies RestrictedCondition
|
||||||
|
// waived for dynamic linking from independent modules.
|
||||||
|
RestrictedClasspathExceptionCondition = LicenseCondition(0x0020)
|
||||||
|
// WeaklyRestrictedCondition identifies a RestrictedCondition waived
|
||||||
|
// for dynamic linking.
|
||||||
|
WeaklyRestrictedCondition = LicenseCondition(0x0040)
|
||||||
|
// ProprietaryCondition identifies a license with source privacy
|
||||||
|
// requirements.
|
||||||
|
ProprietaryCondition = LicenseCondition(0x0080)
|
||||||
|
// ByExceptionOnly identifies a license where policy requires product
|
||||||
|
// counsel review prior to use.
|
||||||
|
ByExceptionOnlyCondition = LicenseCondition(0x0100)
|
||||||
|
// NotAllowedCondition identifies a license with onerous conditions
|
||||||
|
// where policy prohibits use.
|
||||||
|
NotAllowedCondition = LicenseCondition(0x0200)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// RecognizedConditionNames maps condition strings to LicenseCondition.
|
||||||
|
RecognizedConditionNames = map[string]LicenseCondition{
|
||||||
|
"unencumbered": UnencumberedCondition,
|
||||||
|
"permissive": PermissiveCondition,
|
||||||
|
"notice": NoticeCondition,
|
||||||
|
"reciprocal": ReciprocalCondition,
|
||||||
|
"restricted": RestrictedCondition,
|
||||||
|
"restricted_with_classpath_exception": RestrictedClasspathExceptionCondition,
|
||||||
|
"restricted_allows_dynamic_linking": WeaklyRestrictedCondition,
|
||||||
|
"proprietary": ProprietaryCondition,
|
||||||
|
"by_exception_only": ByExceptionOnlyCondition,
|
||||||
|
"not_allowed": NotAllowedCondition,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Name returns the condition string corresponding to the LicenseCondition.
|
||||||
func (lc LicenseCondition) Name() string {
|
func (lc LicenseCondition) Name() string {
|
||||||
return lc.name
|
switch lc {
|
||||||
}
|
case UnencumberedCondition:
|
||||||
|
return "unencumbered"
|
||||||
// Origin identifies the TargetNode where the condition originates.
|
case PermissiveCondition:
|
||||||
func (lc LicenseCondition) Origin() *TargetNode {
|
return "permissive"
|
||||||
return lc.origin
|
case NoticeCondition:
|
||||||
}
|
return "notice"
|
||||||
|
case ReciprocalCondition:
|
||||||
// asString returns a string representation of a license condition:
|
return "reciprocal"
|
||||||
// origin+separator+condition.
|
case RestrictedCondition:
|
||||||
func (lc LicenseCondition) asString(separator string) string {
|
return "restricted"
|
||||||
return lc.origin.name + separator + lc.name
|
case RestrictedClasspathExceptionCondition:
|
||||||
}
|
return "restricted_with_classpath_exception"
|
||||||
|
case WeaklyRestrictedCondition:
|
||||||
// ConditionList implements introspection methods to arrays of LicenseCondition.
|
return "restricted_allows_dynamic_linking"
|
||||||
type ConditionList []LicenseCondition
|
case ProprietaryCondition:
|
||||||
|
return "proprietary"
|
||||||
|
case ByExceptionOnlyCondition:
|
||||||
// ConditionList orders arrays of LicenseCondition by Origin and Name.
|
return "by_exception_only"
|
||||||
|
case NotAllowedCondition:
|
||||||
// Len returns the length of the list.
|
return "not_allowed"
|
||||||
func (l ConditionList) Len() int { return len(l) }
|
|
||||||
|
|
||||||
// Swap rearranges 2 elements in the list so each occupies the other's former position.
|
|
||||||
func (l ConditionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
|
||||||
|
|
||||||
// Less returns true when the `i`th element is lexicographically less than tht `j`th element.
|
|
||||||
func (l ConditionList) Less(i, j int) bool {
|
|
||||||
if l[i].origin.name == l[j].origin.name {
|
|
||||||
return l[i].name < l[j].name
|
|
||||||
}
|
}
|
||||||
return l[i].origin.name < l[j].origin.name
|
panic(fmt.Errorf("unrecognized license condition: %04x", lc))
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of the set.
|
|
||||||
func (cl ConditionList) String() string {
|
|
||||||
var sb strings.Builder
|
|
||||||
fmt.Fprintf(&sb, "[")
|
|
||||||
sep := ""
|
|
||||||
for _, lc := range cl {
|
|
||||||
fmt.Fprintf(&sb, "%s%s:%s", sep, lc.origin.name, lc.name)
|
|
||||||
sep = ", "
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&sb, "]")
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Names returns the list of the conditions' names.
|
|
||||||
func (cl ConditionList) Names() []string {
|
|
||||||
result := make([]string, 0, len(cl))
|
|
||||||
for _, lc := range cl {
|
|
||||||
result = append(result, lc.name)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasByName returns true if the list contains any condition matching `name`.
|
|
||||||
func (cl ConditionList) HasByName(name ConditionNames) bool {
|
|
||||||
for _, lc := range cl {
|
|
||||||
if name.Contains(lc.name) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByName returns the sublist of conditions that match `name`.
|
|
||||||
func (cl ConditionList) ByName(name ConditionNames) ConditionList {
|
|
||||||
result := make(ConditionList, 0, cl.CountByName(name))
|
|
||||||
for _, lc := range cl {
|
|
||||||
if name.Contains(lc.name) {
|
|
||||||
result = append(result, lc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountByName returns the size of the sublist of conditions that match `name`.
|
|
||||||
func (cl ConditionList) CountByName(name ConditionNames) int {
|
|
||||||
size := 0
|
|
||||||
for _, lc := range cl {
|
|
||||||
if name.Contains(lc.name) {
|
|
||||||
size++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasByOrigin returns true if the list contains any condition originating at `origin`.
|
|
||||||
func (cl ConditionList) HasByOrigin(origin *TargetNode) bool {
|
|
||||||
for _, lc := range cl {
|
|
||||||
if lc.origin.name == origin.name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByOrigin returns the sublist of conditions that originate at `origin`.
|
|
||||||
func (cl ConditionList) ByOrigin(origin *TargetNode) ConditionList {
|
|
||||||
result := make(ConditionList, 0, cl.CountByOrigin(origin))
|
|
||||||
for _, lc := range cl {
|
|
||||||
if lc.origin.name == origin.name {
|
|
||||||
result = append(result, lc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountByOrigin returns the size of the sublist of conditions that originate at `origin`.
|
|
||||||
func (cl ConditionList) CountByOrigin(origin *TargetNode) int {
|
|
||||||
size := 0
|
|
||||||
for _, lc := range cl {
|
|
||||||
if lc.origin.name == origin.name {
|
|
||||||
size++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConditionNames implements the Contains predicate for slices of condition
|
|
||||||
// name strings.
|
|
||||||
type ConditionNames []string
|
|
||||||
|
|
||||||
// Contains returns true if the name matches one of the ConditionNames.
|
|
||||||
func (cn ConditionNames) Contains(name string) bool {
|
|
||||||
for _, cname := range cn {
|
|
||||||
if cname == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
@@ -15,204 +15,53 @@
|
|||||||
package compliance
|
package compliance
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConditionNames(t *testing.T) {
|
func TestConditionSetHas(t *testing.T) {
|
||||||
impliesShare := ConditionNames([]string{"restricted", "reciprocal"})
|
impliesShare := ImpliesShared
|
||||||
|
|
||||||
if impliesShare.Contains("notice") {
|
t.Logf("testing with imliesShare=%04x", impliesShare)
|
||||||
t.Errorf("impliesShare.Contains(\"notice\") got true, want false")
|
|
||||||
|
if impliesShare.HasAny(NoticeCondition) {
|
||||||
|
t.Errorf("impliesShare.HasAny(\"notice\"=%04x) got true, want false", NoticeCondition)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !impliesShare.Contains("restricted") {
|
if !impliesShare.HasAny(RestrictedCondition) {
|
||||||
t.Errorf("impliesShare.Contains(\"restricted\") got false, want true")
|
t.Errorf("impliesShare.HasAny(\"restricted\"=%04x) got false, want true", RestrictedCondition)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !impliesShare.Contains("reciprocal") {
|
if !impliesShare.HasAny(ReciprocalCondition) {
|
||||||
t.Errorf("impliesShare.Contains(\"reciprocal\") got false, want true")
|
t.Errorf("impliesShare.HasAny(\"reciprocal\"=%04x) got false, want true", ReciprocalCondition)
|
||||||
}
|
}
|
||||||
|
|
||||||
if impliesShare.Contains("") {
|
if impliesShare.HasAny(LicenseCondition(0x0000)) {
|
||||||
t.Errorf("impliesShare.Contains(\"\") got true, want false")
|
t.Errorf("impliesShare.HasAny(nil=%04x) got true, want false", LicenseCondition(0x0000))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConditionList(t *testing.T) {
|
func TestConditionName(t *testing.T) {
|
||||||
tests := []struct {
|
for expected, condition := range RecognizedConditionNames {
|
||||||
name string
|
actual := condition.Name()
|
||||||
conditions map[string][]string
|
if expected != actual {
|
||||||
byName map[string][]string
|
t.Errorf("unexpected name for condition %04x: got %s, want %s", condition, actual, expected)
|
||||||
byOrigin map[string][]string
|
}
|
||||||
}{
|
}
|
||||||
{
|
}
|
||||||
name: "noticeonly",
|
|
||||||
conditions: map[string][]string{
|
func TestConditionName_InvalidCondition(t *testing.T) {
|
||||||
"notice": []string{"bin1", "lib1"},
|
panicked := false
|
||||||
},
|
var lc LicenseCondition
|
||||||
byName: map[string][]string{
|
func() {
|
||||||
"notice": []string{"bin1", "lib1"},
|
defer func() {
|
||||||
"restricted": []string{},
|
if err := recover(); err != nil {
|
||||||
},
|
panicked = true
|
||||||
byOrigin: map[string][]string{
|
}
|
||||||
"bin1": []string{"notice"},
|
}()
|
||||||
"lib1": []string{"notice"},
|
name := lc.Name()
|
||||||
"bin2": []string{},
|
t.Errorf("invalid condition unexpected name: got %s, wanted panic", name)
|
||||||
"lib2": []string{},
|
}()
|
||||||
},
|
if !panicked {
|
||||||
},
|
t.Errorf("no expected panic for %04x.Name(): got no panic, wanted panic", lc)
|
||||||
{
|
|
||||||
name: "empty",
|
|
||||||
conditions: map[string][]string{},
|
|
||||||
byName: map[string][]string{
|
|
||||||
"notice": []string{},
|
|
||||||
"restricted": []string{},
|
|
||||||
},
|
|
||||||
byOrigin: map[string][]string{
|
|
||||||
"bin1": []string{},
|
|
||||||
"lib1": []string{},
|
|
||||||
"bin2": []string{},
|
|
||||||
"lib2": []string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "everything",
|
|
||||||
conditions: map[string][]string{
|
|
||||||
"notice": []string{"bin1", "bin2", "lib1", "lib2"},
|
|
||||||
"reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
|
|
||||||
"restricted": []string{"bin1", "bin2", "lib1", "lib2"},
|
|
||||||
"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
|
|
||||||
},
|
|
||||||
byName: map[string][]string{
|
|
||||||
"permissive": []string{},
|
|
||||||
"notice": []string{"bin1", "bin2", "lib1", "lib2"},
|
|
||||||
"reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
|
|
||||||
"restricted": []string{"bin1", "bin2", "lib1", "lib2"},
|
|
||||||
"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
|
|
||||||
},
|
|
||||||
byOrigin: map[string][]string{
|
|
||||||
"bin1": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
|
|
||||||
"bin2": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
|
|
||||||
"lib1": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
|
|
||||||
"lib2": []string{"notice", "reciprocal", "restricted", "by_exception_only"},
|
|
||||||
"other": []string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "allbutoneeach",
|
|
||||||
conditions: map[string][]string{
|
|
||||||
"notice": []string{"bin2", "lib1", "lib2"},
|
|
||||||
"reciprocal": []string{"bin1", "lib1", "lib2"},
|
|
||||||
"restricted": []string{"bin1", "bin2", "lib2"},
|
|
||||||
"by_exception_only": []string{"bin1", "bin2", "lib1"},
|
|
||||||
},
|
|
||||||
byName: map[string][]string{
|
|
||||||
"permissive": []string{},
|
|
||||||
"notice": []string{"bin2", "lib1", "lib2"},
|
|
||||||
"reciprocal": []string{"bin1", "lib1", "lib2"},
|
|
||||||
"restricted": []string{"bin1", "bin2", "lib2"},
|
|
||||||
"by_exception_only": []string{"bin1", "bin2", "lib1"},
|
|
||||||
},
|
|
||||||
byOrigin: map[string][]string{
|
|
||||||
"bin1": []string{"reciprocal", "restricted", "by_exception_only"},
|
|
||||||
"bin2": []string{"notice", "restricted", "by_exception_only"},
|
|
||||||
"lib1": []string{"notice", "reciprocal", "by_exception_only"},
|
|
||||||
"lib2": []string{"notice", "reciprocal", "restricted"},
|
|
||||||
"other": []string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "oneeach",
|
|
||||||
conditions: map[string][]string{
|
|
||||||
"notice": []string{"bin1"},
|
|
||||||
"reciprocal": []string{"bin2"},
|
|
||||||
"restricted": []string{"lib1"},
|
|
||||||
"by_exception_only": []string{"lib2"},
|
|
||||||
},
|
|
||||||
byName: map[string][]string{
|
|
||||||
"permissive": []string{},
|
|
||||||
"notice": []string{"bin1"},
|
|
||||||
"reciprocal": []string{"bin2"},
|
|
||||||
"restricted": []string{"lib1"},
|
|
||||||
"by_exception_only": []string{"lib2"},
|
|
||||||
},
|
|
||||||
byOrigin: map[string][]string{
|
|
||||||
"bin1": []string{"notice"},
|
|
||||||
"bin2": []string{"reciprocal"},
|
|
||||||
"lib1": []string{"restricted"},
|
|
||||||
"lib2": []string{"by_exception_only"},
|
|
||||||
"other": []string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
lg := newLicenseGraph()
|
|
||||||
cl := toConditionList(lg, tt.conditions)
|
|
||||||
for names, expected := range tt.byName {
|
|
||||||
name := ConditionNames(strings.Split(names, ":"))
|
|
||||||
if cl.HasByName(name) {
|
|
||||||
if len(expected) == 0 {
|
|
||||||
t.Errorf("unexpected ConditionList.HasByName(%q): got true, want false", name)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(expected) != 0 {
|
|
||||||
t.Errorf("unexpected ConditionList.HasByName(%q): got false, want true", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(expected) != cl.CountByName(name) {
|
|
||||||
t.Errorf("unexpected ConditionList.CountByName(%q): got %d, want %d", name, cl.CountByName(name), len(expected))
|
|
||||||
}
|
|
||||||
byName := cl.ByName(name)
|
|
||||||
if len(expected) != len(byName) {
|
|
||||||
t.Errorf("unexpected ConditionList.ByName(%q): got %v, want %v", name, byName, expected)
|
|
||||||
} else {
|
|
||||||
sort.Strings(expected)
|
|
||||||
actual := make([]string, 0, len(byName))
|
|
||||||
for _, lc := range byName {
|
|
||||||
actual = append(actual, lc.Origin().Name())
|
|
||||||
}
|
|
||||||
sort.Strings(actual)
|
|
||||||
for i := 0; i < len(expected); i++ {
|
|
||||||
if expected[i] != actual[i] {
|
|
||||||
t.Errorf("unexpected ConditionList.ByName(%q) index %d in %v: got %s, want %s", name, i, actual, actual[i], expected[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for origin, expected := range tt.byOrigin {
|
|
||||||
onode := newTestNode(lg, origin)
|
|
||||||
if cl.HasByOrigin(onode) {
|
|
||||||
if len(expected) == 0 {
|
|
||||||
t.Errorf("unexpected ConditionList.HasByOrigin(%q): got true, want false", origin)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(expected) != 0 {
|
|
||||||
t.Errorf("unexpected ConditionList.HasByOrigin(%q): got false, want true", origin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(expected) != cl.CountByOrigin(onode) {
|
|
||||||
t.Errorf("unexpected ConditionList.CountByOrigin(%q): got %d, want %d", origin, cl.CountByOrigin(onode), len(expected))
|
|
||||||
}
|
|
||||||
byOrigin := cl.ByOrigin(onode)
|
|
||||||
if len(expected) != len(byOrigin) {
|
|
||||||
t.Errorf("unexpected ConditionList.ByOrigin(%q): got %v, want %v", origin, byOrigin, expected)
|
|
||||||
} else {
|
|
||||||
sort.Strings(expected)
|
|
||||||
actual := make([]string, 0, len(byOrigin))
|
|
||||||
for _, lc := range byOrigin {
|
|
||||||
actual = append(actual, lc.Name())
|
|
||||||
}
|
|
||||||
sort.Strings(actual)
|
|
||||||
for i := 0; i < len(expected); i++ {
|
|
||||||
if expected[i] != actual[i] {
|
|
||||||
t.Errorf("unexpected ConditionList.ByOrigin(%q) index %d in %v: got %s, want %s", origin, i, actual, actual[i], expected[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,263 +16,174 @@ package compliance
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewLicenseConditionSet creates a new instance or variable of *LicenseConditionSet.
|
// LicenseConditionSet identifies sets of license conditions.
|
||||||
func NewLicenseConditionSet(conditions ...LicenseCondition) *LicenseConditionSet {
|
type LicenseConditionSet LicenseCondition
|
||||||
cs := newLicenseConditionSet()
|
|
||||||
cs.Add(conditions...)
|
// AllLicenseConditions is the set of all recognized license conditions.
|
||||||
|
const AllLicenseConditions = LicenseConditionSet(LicenseConditionMask)
|
||||||
|
|
||||||
|
// NewLicenseConditionSet returns a set containing exactly the elements of
|
||||||
|
// `conditions`.
|
||||||
|
func NewLicenseConditionSet(conditions ...LicenseCondition) LicenseConditionSet {
|
||||||
|
cs := LicenseConditionSet(0x00)
|
||||||
|
for _, lc := range conditions {
|
||||||
|
cs |= LicenseConditionSet(lc)
|
||||||
|
}
|
||||||
return cs
|
return cs
|
||||||
}
|
}
|
||||||
|
|
||||||
// LicenseConditionSet describes a mutable set of immutable license conditions.
|
// Plus returns a new set containing all of the elements of `cs` and all of the
|
||||||
type LicenseConditionSet struct {
|
// `conditions`.
|
||||||
// conditions describes the set of license conditions i.e. (condition name, origin target) pairs
|
func (cs LicenseConditionSet) Plus(conditions ...LicenseCondition) LicenseConditionSet {
|
||||||
// by mapping condition name -> origin target -> true.
|
result := cs
|
||||||
conditions map[string]map[*TargetNode]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add makes all `conditions` members of the set if they were not previously.
|
|
||||||
func (cs *LicenseConditionSet) Add(conditions ...LicenseCondition) {
|
|
||||||
if len(conditions) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, lc := range conditions {
|
for _, lc := range conditions {
|
||||||
if _, ok := cs.conditions[lc.name]; !ok {
|
result |= LicenseConditionSet(lc)
|
||||||
cs.conditions[lc.name] = make(map[*TargetNode]bool)
|
|
||||||
}
|
|
||||||
cs.conditions[lc.name][lc.origin] = true
|
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSet makes all elements of `conditions` members of the set if they were not previously.
|
// Union returns a new set containing all of the elements of `cs` and all of the
|
||||||
func (cs *LicenseConditionSet) AddSet(other *LicenseConditionSet) {
|
// elements of the `other` sets.
|
||||||
if len(other.conditions) == 0 {
|
func (cs LicenseConditionSet) Union(other ...LicenseConditionSet) LicenseConditionSet {
|
||||||
return
|
result := cs
|
||||||
}
|
for _, ls := range other {
|
||||||
for name, origins := range other.conditions {
|
result |= ls
|
||||||
if len(origins) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := cs.conditions[name]; !ok {
|
|
||||||
cs.conditions[name] = make(map[*TargetNode]bool)
|
|
||||||
}
|
|
||||||
for origin := range origins {
|
|
||||||
cs.conditions[name][origin] = other.conditions[name][origin]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByName returns a list of the conditions in the set matching `names`.
|
// MatchingAny returns the subset of `cs` equal to any of the `conditions`.
|
||||||
func (cs *LicenseConditionSet) ByName(names ...ConditionNames) *LicenseConditionSet {
|
func (cs LicenseConditionSet) MatchingAny(conditions ...LicenseCondition) LicenseConditionSet {
|
||||||
other := newLicenseConditionSet()
|
result := LicenseConditionSet(0x00)
|
||||||
for _, cn := range names {
|
for _, lc := range conditions {
|
||||||
for _, name := range cn {
|
result |= cs & LicenseConditionSet(lc)
|
||||||
if origins, ok := cs.conditions[name]; ok {
|
|
||||||
other.conditions[name] = make(map[*TargetNode]bool)
|
|
||||||
for origin := range origins {
|
|
||||||
other.conditions[name][origin] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return other
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAnyByName returns true if the set contains any conditions matching `names` originating at any target.
|
// MatchingAnySet returns the subset of `cs` that are members of any of the
|
||||||
func (cs *LicenseConditionSet) HasAnyByName(names ...ConditionNames) bool {
|
// `other` sets.
|
||||||
for _, cn := range names {
|
func (cs LicenseConditionSet) MatchingAnySet(other ...LicenseConditionSet) LicenseConditionSet {
|
||||||
for _, name := range cn {
|
result := LicenseConditionSet(0x00)
|
||||||
if origins, ok := cs.conditions[name]; ok {
|
for _, ls := range other {
|
||||||
if len(origins) > 0 {
|
result |= cs & ls
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountByName returns the number of conditions matching `names` originating at any target.
|
// HasAny returns true when `cs` contains at least one of the `conditions`.
|
||||||
func (cs *LicenseConditionSet) CountByName(names ...ConditionNames) int {
|
func (cs LicenseConditionSet) HasAny(conditions ...LicenseCondition) bool {
|
||||||
size := 0
|
for _, lc := range conditions {
|
||||||
for _, cn := range names {
|
if 0x0000 != (cs & LicenseConditionSet(lc)) {
|
||||||
for _, name := range cn {
|
|
||||||
if origins, ok := cs.conditions[name]; ok {
|
|
||||||
size += len(origins)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByOrigin returns all of the conditions that originate at `origin` regardless of name.
|
|
||||||
func (cs *LicenseConditionSet) ByOrigin(origin *TargetNode) *LicenseConditionSet {
|
|
||||||
other := newLicenseConditionSet()
|
|
||||||
for name, origins := range cs.conditions {
|
|
||||||
if _, ok := origins[origin]; ok {
|
|
||||||
other.conditions[name] = make(map[*TargetNode]bool)
|
|
||||||
other.conditions[name][origin] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return other
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasAnyByOrigin returns true if the set contains any conditions originating at `origin` regardless of condition name.
|
|
||||||
func (cs *LicenseConditionSet) HasAnyByOrigin(origin *TargetNode) bool {
|
|
||||||
for _, origins := range cs.conditions {
|
|
||||||
if _, ok := origins[origin]; ok {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountByOrigin returns the number of conditions originating at `origin` regardless of condition name.
|
// MatchesAnySet returns true when `cs` has a non-empty intersection with at
|
||||||
func (cs *LicenseConditionSet) CountByOrigin(origin *TargetNode) int {
|
// least one of the `other` condition sets.
|
||||||
size := 0
|
func (cs LicenseConditionSet) MatchesAnySet(other ...LicenseConditionSet) bool {
|
||||||
for _, origins := range cs.conditions {
|
for _, ls := range other {
|
||||||
if _, ok := origins[origin]; ok {
|
if 0x0000 != (cs & ls) {
|
||||||
size++
|
return true
|
||||||
}
|
|
||||||
}
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
// AsList returns a list of all the conditions in the set.
|
|
||||||
func (cs *LicenseConditionSet) AsList() ConditionList {
|
|
||||||
result := make(ConditionList, 0, cs.Count())
|
|
||||||
for name, origins := range cs.conditions {
|
|
||||||
for origin := range origins {
|
|
||||||
result = append(result, LicenseCondition{name, origin})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Names returns a list of the names of the conditions in the set.
|
|
||||||
func (cs *LicenseConditionSet) Names() []string {
|
|
||||||
result := make([]string, 0, len(cs.conditions))
|
|
||||||
for name := range cs.conditions {
|
|
||||||
result = append(result, name)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count returns the number of conditions in the set.
|
|
||||||
func (cs *LicenseConditionSet) Count() int {
|
|
||||||
size := 0
|
|
||||||
for _, origins := range cs.conditions {
|
|
||||||
size += len(origins)
|
|
||||||
}
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy creates a new LicenseCondition variable with the same value.
|
|
||||||
func (cs *LicenseConditionSet) Copy() *LicenseConditionSet {
|
|
||||||
other := newLicenseConditionSet()
|
|
||||||
for name := range cs.conditions {
|
|
||||||
other.conditions[name] = make(map[*TargetNode]bool)
|
|
||||||
for origin := range cs.conditions[name] {
|
|
||||||
other.conditions[name][origin] = cs.conditions[name][origin]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return other
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasCondition returns true if the set contains any condition matching both `names` and `origin`.
|
|
||||||
func (cs *LicenseConditionSet) HasCondition(names ConditionNames, origin *TargetNode) bool {
|
|
||||||
for _, name := range names {
|
|
||||||
if origins, ok := cs.conditions[name]; ok {
|
|
||||||
_, isPresent := origins[origin]
|
|
||||||
if isPresent {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEmpty returns true when the set of conditions contains zero elements.
|
// HasAll returns true when `cs` contains every one of the `conditions`.
|
||||||
func (cs *LicenseConditionSet) IsEmpty() bool {
|
func (cs LicenseConditionSet) HasAll(conditions ...LicenseCondition) bool {
|
||||||
for _, origins := range cs.conditions {
|
for _, lc := range conditions {
|
||||||
if 0 < len(origins) {
|
if 0x0000 == (cs & LicenseConditionSet(lc)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveAllByName changes the set to delete all conditions matching `names`.
|
// MatchesEverySet returns true when `cs` has a non-empty intersection with
|
||||||
func (cs *LicenseConditionSet) RemoveAllByName(names ...ConditionNames) {
|
// each of the `other` condition sets.
|
||||||
for _, cn := range names {
|
func (cs LicenseConditionSet) MatchesEverySet(other ...LicenseConditionSet) bool {
|
||||||
for _, name := range cn {
|
for _, ls := range other {
|
||||||
delete(cs.conditions, name)
|
if 0x0000 == (cs & ls) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove changes the set to delete `conditions`.
|
// Intersection returns the subset of `cs` that are members of every `other`
|
||||||
func (cs *LicenseConditionSet) Remove(conditions ...LicenseCondition) {
|
// set.
|
||||||
|
func (cs LicenseConditionSet) Intersection(other ...LicenseConditionSet) LicenseConditionSet {
|
||||||
|
result := cs
|
||||||
|
for _, ls := range other {
|
||||||
|
result &= ls
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minus returns the subset of `cs` that are not equaal to any `conditions`.
|
||||||
|
func (cs LicenseConditionSet) Minus(conditions ...LicenseCondition) LicenseConditionSet {
|
||||||
|
result := cs
|
||||||
for _, lc := range conditions {
|
for _, lc := range conditions {
|
||||||
if _, isPresent := cs.conditions[lc.name]; !isPresent {
|
result &^= LicenseConditionSet(lc)
|
||||||
panic(fmt.Errorf("attempt to remove non-existent condition: %q", lc.asString(":")))
|
|
||||||
}
|
|
||||||
if _, isPresent := cs.conditions[lc.name][lc.origin]; !isPresent {
|
|
||||||
panic(fmt.Errorf("attempt to remove non-existent origin: %q", lc.asString(":")))
|
|
||||||
}
|
|
||||||
delete(cs.conditions[lc.name], lc.origin)
|
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeSet changes the set to delete all conditions also present in `other`.
|
// Difference returns the subset of `cs` that are not members of any `other`
|
||||||
func (cs *LicenseConditionSet) RemoveSet(other *LicenseConditionSet) {
|
// set.
|
||||||
for name, origins := range other.conditions {
|
func (cs LicenseConditionSet) Difference(other ...LicenseConditionSet) LicenseConditionSet {
|
||||||
if _, isPresent := cs.conditions[name]; !isPresent {
|
result := cs
|
||||||
continue
|
for _, ls := range other {
|
||||||
}
|
result &^= ls
|
||||||
for origin := range origins {
|
}
|
||||||
delete(cs.conditions[name], origin)
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of license conditions in the set.
|
||||||
|
func (cs LicenseConditionSet) Len() int {
|
||||||
|
size := 0
|
||||||
|
for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
|
||||||
|
if 0x00 != (cs & lc) {
|
||||||
|
size++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
// compliance-only LicenseConditionSet methods
|
// AsList returns an array of the license conditions in the set.
|
||||||
|
func (cs LicenseConditionSet) AsList() []LicenseCondition {
|
||||||
// newLicenseConditionSet constructs a set of `conditions`.
|
result := make([]LicenseCondition, 0, cs.Len())
|
||||||
func newLicenseConditionSet() *LicenseConditionSet {
|
for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
|
||||||
return &LicenseConditionSet{make(map[string]map[*TargetNode]bool)}
|
if 0x00 != (cs & lc) {
|
||||||
}
|
result = append(result, LicenseCondition(lc))
|
||||||
|
|
||||||
// add changes the set to include each element of `conditions` originating at `origin`.
|
|
||||||
func (cs *LicenseConditionSet) add(origin *TargetNode, conditions ...string) {
|
|
||||||
for _, name := range conditions {
|
|
||||||
if _, ok := cs.conditions[name]; !ok {
|
|
||||||
cs.conditions[name] = make(map[*TargetNode]bool)
|
|
||||||
}
|
|
||||||
cs.conditions[name][origin] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// asStringList returns the conditions in the set as `separator`-separated (origin, condition-name) pair strings.
|
|
||||||
func (cs *LicenseConditionSet) asStringList(separator string) []string {
|
|
||||||
result := make([]string, 0, cs.Count())
|
|
||||||
for name, origins := range cs.conditions {
|
|
||||||
for origin := range origins {
|
|
||||||
result = append(result, origin.name+separator+name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// conditionNamesArray implements a `contains` predicate for arrays of ConditionNames
|
// Names returns an array of the names of the license conditions in the set.
|
||||||
type conditionNamesArray []ConditionNames
|
func (cs LicenseConditionSet) Names() []string {
|
||||||
|
result := make([]string, 0, cs.Len())
|
||||||
func (cn conditionNamesArray) contains(name string) bool {
|
for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
|
||||||
for _, names := range cn {
|
if 0x00 != (cs & lc) {
|
||||||
if names.Contains(name) {
|
result = append(result, LicenseCondition(lc).Name())
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true when the set contains no license conditions.
|
||||||
|
func (cs LicenseConditionSet) IsEmpty() bool {
|
||||||
|
return 0x00 == (cs & AllLicenseConditions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human-readable string representation of the set.
|
||||||
|
func (cs LicenseConditionSet) String() string {
|
||||||
|
return fmt.Sprintf("{%s}", strings.Join(cs.Names(), "|"))
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -52,30 +52,19 @@ type LicenseGraph struct {
|
|||||||
// edges lists the directed edges in the graph from target to dependency. (guarded by mu)
|
// edges lists the directed edges in the graph from target to dependency. (guarded by mu)
|
||||||
//
|
//
|
||||||
// Alternatively, the graph is the set of `edges`.
|
// Alternatively, the graph is the set of `edges`.
|
||||||
edges []*dependencyEdge
|
edges TargetEdgeList
|
||||||
|
|
||||||
// targets identifies, indexes by name, and describes the entire set of target node files.
|
// targets identifies, indexes, and describes the entire set of target node files.
|
||||||
/// (guarded by mu)
|
/// (guarded by mu)
|
||||||
targets map[string]*TargetNode
|
targets map[string]*TargetNode
|
||||||
|
|
||||||
// index facilitates looking up edges from targets. (creation guarded by my)
|
// wgBU becomes non-nil when the bottom-up resolve begins and reaches 0
|
||||||
//
|
// (i.e. Wait() proceeds) when the bottom-up resolve completes. (guarded by mu)
|
||||||
// This is a forward index from target to dependencies. i.e. "top-down"
|
wgBU *sync.WaitGroup
|
||||||
index map[string][]*dependencyEdge
|
|
||||||
|
|
||||||
// rsBU caches the results of a full bottom-up resolve. (creation guarded by mu)
|
// wgTD becomes non-nil when the top-down resolve begins and reaches 0 (i.e. Wait()
|
||||||
//
|
// proceeds) when the top-down resolve completes. (guarded by mu)
|
||||||
// A bottom-up resolve is a prerequisite for all of the top-down resolves so caching
|
wgTD *sync.WaitGroup
|
||||||
// the result is a performance win.
|
|
||||||
rsBU *ResolutionSet
|
|
||||||
|
|
||||||
// rsTD caches the results of a full top-down resolve. (creation guarded by mu)
|
|
||||||
//
|
|
||||||
// A top-down resolve is a prerequisite for final resolutions.
|
|
||||||
// e.g. a shipped node inheriting a `restricted` condition from a parent through a
|
|
||||||
// dynamic dependency implies a notice dependency on the parent; even though, the
|
|
||||||
// distribution does not happen as a result of the dynamic dependency itself.
|
|
||||||
rsTD *ResolutionSet
|
|
||||||
|
|
||||||
// shippedNodes caches the results of a full walk of nodes identifying targets
|
// shippedNodes caches the results of a full walk of nodes identifying targets
|
||||||
// distributed either directly or as derivative works. (creation guarded by mu)
|
// distributed either directly or as derivative works. (creation guarded by mu)
|
||||||
@@ -85,35 +74,18 @@ type LicenseGraph struct {
|
|||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// TargetNode returns the target node identified by `name`.
|
|
||||||
func (lg *LicenseGraph) TargetNode(name string) *TargetNode {
|
|
||||||
if _, ok := lg.targets[name]; !ok {
|
|
||||||
panic(fmt.Errorf("target node %q missing from graph", name))
|
|
||||||
}
|
|
||||||
return lg.targets[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasTargetNode returns true if a target node identified by `name` appears in
|
|
||||||
// the graph.
|
|
||||||
func (lg *LicenseGraph) HasTargetNode(name string) bool {
|
|
||||||
_, isPresent := lg.targets[name]
|
|
||||||
return isPresent
|
|
||||||
}
|
|
||||||
|
|
||||||
// Edges returns the list of edges in the graph. (unordered)
|
// Edges returns the list of edges in the graph. (unordered)
|
||||||
func (lg *LicenseGraph) Edges() TargetEdgeList {
|
func (lg *LicenseGraph) Edges() TargetEdgeList {
|
||||||
edges := make(TargetEdgeList, 0, len(lg.edges))
|
edges := make(TargetEdgeList, 0, len(lg.edges))
|
||||||
for _, e := range lg.edges {
|
edges = append(edges, lg.edges...)
|
||||||
edges = append(edges, TargetEdge{lg, e})
|
|
||||||
}
|
|
||||||
return edges
|
return edges
|
||||||
}
|
}
|
||||||
|
|
||||||
// Targets returns the list of target nodes in the graph. (unordered)
|
// Targets returns the list of target nodes in the graph. (unordered)
|
||||||
func (lg *LicenseGraph) Targets() TargetNodeList {
|
func (lg *LicenseGraph) Targets() TargetNodeList {
|
||||||
targets := make(TargetNodeList, 0, len(lg.targets))
|
targets := make(TargetNodeList, 0, len(lg.targets))
|
||||||
for target := range lg.targets {
|
for _, target := range lg.targets {
|
||||||
targets = append(targets, lg.targets[target])
|
targets = append(targets, target)
|
||||||
}
|
}
|
||||||
return targets
|
return targets
|
||||||
}
|
}
|
||||||
@@ -124,33 +96,10 @@ func (lg *LicenseGraph) Targets() TargetNodeList {
|
|||||||
func newLicenseGraph() *LicenseGraph {
|
func newLicenseGraph() *LicenseGraph {
|
||||||
return &LicenseGraph{
|
return &LicenseGraph{
|
||||||
rootFiles: []string{},
|
rootFiles: []string{},
|
||||||
edges: make([]*dependencyEdge, 0, 1000),
|
|
||||||
targets: make(map[string]*TargetNode),
|
targets: make(map[string]*TargetNode),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// indexForward guarantees the `index` map is populated to look up edges by
|
|
||||||
// `target`.
|
|
||||||
func (lg *LicenseGraph) indexForward() {
|
|
||||||
lg.mu.Lock()
|
|
||||||
defer func() {
|
|
||||||
lg.mu.Unlock()
|
|
||||||
}()
|
|
||||||
|
|
||||||
if lg.index != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
lg.index = make(map[string][]*dependencyEdge)
|
|
||||||
for _, e := range lg.edges {
|
|
||||||
if _, ok := lg.index[e.target]; ok {
|
|
||||||
lg.index[e.target] = append(lg.index[e.target], e)
|
|
||||||
} else {
|
|
||||||
lg.index[e.target] = []*dependencyEdge{e}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TargetEdge describes a directed, annotated edge from a target to a
|
// TargetEdge describes a directed, annotated edge from a target to a
|
||||||
// dependency. (immutable)
|
// dependency. (immutable)
|
||||||
//
|
//
|
||||||
@@ -159,25 +108,25 @@ func (lg *LicenseGraph) indexForward() {
|
|||||||
// i.e. `Target` depends on `Dependency` in the manner described by
|
// i.e. `Target` depends on `Dependency` in the manner described by
|
||||||
// `Annotations`.
|
// `Annotations`.
|
||||||
type TargetEdge struct {
|
type TargetEdge struct {
|
||||||
// lg identifies the scope, i.e. license graph, in which the edge appears.
|
// target and dependency identify the nodes connected by the edge.
|
||||||
lg *LicenseGraph
|
target, dependency *TargetNode
|
||||||
|
|
||||||
// e identifies describes the target, dependency, and annotations of the edge.
|
// annotations identifies the set of compliance-relevant annotations describing the edge.
|
||||||
e *dependencyEdge
|
annotations TargetEdgeAnnotations
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target identifies the target that depends on the dependency.
|
// Target identifies the target that depends on the dependency.
|
||||||
//
|
//
|
||||||
// Target needs Dependency to build.
|
// Target needs Dependency to build.
|
||||||
func (e TargetEdge) Target() *TargetNode {
|
func (e *TargetEdge) Target() *TargetNode {
|
||||||
return e.lg.targets[e.e.target]
|
return e.target
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dependency identifies the target depended on by the target.
|
// Dependency identifies the target depended on by the target.
|
||||||
//
|
//
|
||||||
// Dependency builds without Target, but Target needs Dependency to build.
|
// Dependency builds without Target, but Target needs Dependency to build.
|
||||||
func (e TargetEdge) Dependency() *TargetNode {
|
func (e *TargetEdge) Dependency() *TargetNode {
|
||||||
return e.lg.targets[e.e.dependency]
|
return e.dependency
|
||||||
}
|
}
|
||||||
|
|
||||||
// Annotations describes the type of edge by the set of annotations attached to
|
// Annotations describes the type of edge by the set of annotations attached to
|
||||||
@@ -186,12 +135,17 @@ func (e TargetEdge) Dependency() *TargetNode {
|
|||||||
// Only annotations prescribed by policy have any meaning for licensing, and
|
// Only annotations prescribed by policy have any meaning for licensing, and
|
||||||
// the meaning for licensing is likewise prescribed by policy. Other annotations
|
// the meaning for licensing is likewise prescribed by policy. Other annotations
|
||||||
// are preserved and ignored by policy.
|
// are preserved and ignored by policy.
|
||||||
func (e TargetEdge) Annotations() TargetEdgeAnnotations {
|
func (e *TargetEdge) Annotations() TargetEdgeAnnotations {
|
||||||
return e.e.annotations
|
return e.annotations
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human-readable string representation of the edge.
|
||||||
|
func (e *TargetEdge) String() string {
|
||||||
|
return fmt.Sprintf("%s -[%s]> %s", e.target.name, strings.Join(e.annotations.AsList(), ", "), e.dependency.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TargetEdgeList orders lists of edges by target then dependency then annotations.
|
// TargetEdgeList orders lists of edges by target then dependency then annotations.
|
||||||
type TargetEdgeList []TargetEdge
|
type TargetEdgeList []*TargetEdge
|
||||||
|
|
||||||
// Len returns the count of the elmements in the list.
|
// Len returns the count of the elmements in the list.
|
||||||
func (l TargetEdgeList) Len() int { return len(l) }
|
func (l TargetEdgeList) Len() int { return len(l) }
|
||||||
@@ -201,18 +155,63 @@ func (l TargetEdgeList) 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.
|
// Less returns true when the `i`th element is lexicographically less than the `j`th.
|
||||||
func (l TargetEdgeList) Less(i, j int) bool {
|
func (l TargetEdgeList) Less(i, j int) bool {
|
||||||
if l[i].e.target == l[j].e.target {
|
namei := l[i].target.name
|
||||||
if l[i].e.dependency == l[j].e.dependency {
|
namej := l[j].target.name
|
||||||
return l[i].e.annotations.Compare(l[j].e.annotations) < 0
|
if namei == namej {
|
||||||
}
|
namei = l[i].dependency.name
|
||||||
return l[i].e.dependency < l[j].e.dependency
|
namej = l[j].dependency.name
|
||||||
}
|
}
|
||||||
return l[i].e.target < l[j].e.target
|
if namei == namej {
|
||||||
|
return l[i].annotations.Compare(l[j].annotations) < 0
|
||||||
|
}
|
||||||
|
return namei < namej
|
||||||
|
}
|
||||||
|
|
||||||
|
// TargetEdgePathSegment describes a single arc in a TargetPath associating the
|
||||||
|
// edge with a context `ctx` defined by whatever process is creating the path.
|
||||||
|
type TargetEdgePathSegment struct {
|
||||||
|
edge *TargetEdge
|
||||||
|
ctx interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target identifies the target that depends on the dependency.
|
||||||
|
//
|
||||||
|
// Target needs Dependency to build.
|
||||||
|
func (s TargetEdgePathSegment) Target() *TargetNode {
|
||||||
|
return s.edge.target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dependency identifies the target depended on by the target.
|
||||||
|
//
|
||||||
|
// Dependency builds without Target, but Target needs Dependency to build.
|
||||||
|
func (s TargetEdgePathSegment) Dependency() *TargetNode {
|
||||||
|
return s.edge.dependency
|
||||||
|
}
|
||||||
|
|
||||||
|
// Annotations describes the type of edge by the set of annotations attached to
|
||||||
|
// it.
|
||||||
|
//
|
||||||
|
// Only annotations prescribed by policy have any meaning for licensing, and
|
||||||
|
// the meaning for licensing is likewise prescribed by policy. Other annotations
|
||||||
|
// are preserved and ignored by policy.
|
||||||
|
func (s TargetEdgePathSegment) Annotations() TargetEdgeAnnotations {
|
||||||
|
return s.edge.annotations
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context returns the context associated with the path segment. The type and
|
||||||
|
// value of the context defined by the process creating the path.
|
||||||
|
func (s TargetEdgePathSegment) Context() interface{} {
|
||||||
|
return s.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human-readable string representation of the edge.
|
||||||
|
func (s TargetEdgePathSegment) String() string {
|
||||||
|
return fmt.Sprintf("%s -[%s]> %s", s.edge.target.name, strings.Join(s.edge.annotations.AsList(), ", "), s.edge.dependency.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TargetEdgePath describes a sequence of edges starting at a root and ending
|
// TargetEdgePath describes a sequence of edges starting at a root and ending
|
||||||
// at some final dependency.
|
// at some final dependency.
|
||||||
type TargetEdgePath []TargetEdge
|
type TargetEdgePath []TargetEdgePathSegment
|
||||||
|
|
||||||
// NewTargetEdgePath creates a new, empty path with capacity `cap`.
|
// NewTargetEdgePath creates a new, empty path with capacity `cap`.
|
||||||
func NewTargetEdgePath(cap int) *TargetEdgePath {
|
func NewTargetEdgePath(cap int) *TargetEdgePath {
|
||||||
@@ -222,15 +221,15 @@ func NewTargetEdgePath(cap int) *TargetEdgePath {
|
|||||||
|
|
||||||
// Push appends a new edge to the list verifying that the target of the new
|
// Push appends a new edge to the list verifying that the target of the new
|
||||||
// edge is the dependency of the prior.
|
// edge is the dependency of the prior.
|
||||||
func (p *TargetEdgePath) Push(edge TargetEdge) {
|
func (p *TargetEdgePath) Push(edge *TargetEdge, ctx interface{}) {
|
||||||
if len(*p) == 0 {
|
if len(*p) == 0 {
|
||||||
*p = append(*p, edge)
|
*p = append(*p, TargetEdgePathSegment{edge, ctx})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (*p)[len(*p)-1].e.dependency != edge.e.target {
|
if (*p)[len(*p)-1].edge.dependency != edge.target {
|
||||||
panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.e.target))
|
panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.target.name))
|
||||||
}
|
}
|
||||||
*p = append(*p, edge)
|
*p = append(*p, TargetEdgePathSegment{edge, ctx})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop shortens the path by 1 edge.
|
// Pop shortens the path by 1 edge.
|
||||||
@@ -256,10 +255,11 @@ func (p *TargetEdgePath) String() string {
|
|||||||
}
|
}
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
fmt.Fprintf(&sb, "[")
|
fmt.Fprintf(&sb, "[")
|
||||||
for _, e := range *p {
|
for _, s := range *p {
|
||||||
fmt.Fprintf(&sb, "%s -> ", e.e.target)
|
fmt.Fprintf(&sb, "%s -> ", s.edge.target.name)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&sb, "%s]", (*p)[len(*p)-1].e.dependency)
|
lastSegment := (*p)[len(*p)-1]
|
||||||
|
fmt.Fprintf(&sb, "%s]", lastSegment.edge.dependency.name)
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +279,13 @@ func (tn *TargetNode) Name() string {
|
|||||||
return tn.name
|
return tn.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dependencies returns the list of edges to dependencies of `tn`.
|
||||||
|
func (tn *TargetNode) Dependencies() TargetEdgeList {
|
||||||
|
edges := make(TargetEdgeList, 0, len(tn.edges))
|
||||||
|
edges = append(edges, tn.edges...)
|
||||||
|
return edges
|
||||||
|
}
|
||||||
|
|
||||||
// PackageName returns the string that identifes the package for the target.
|
// PackageName returns the string that identifes the package for the target.
|
||||||
func (tn *TargetNode) PackageName() string {
|
func (tn *TargetNode) PackageName() string {
|
||||||
return tn.proto.GetPackageName()
|
return tn.proto.GetPackageName()
|
||||||
@@ -323,10 +330,8 @@ func (tn *TargetNode) LicenseKinds() []string {
|
|||||||
// is a matter of policy. (unordered)
|
// is a matter of policy. (unordered)
|
||||||
//
|
//
|
||||||
// e.g. notice or proprietary
|
// e.g. notice or proprietary
|
||||||
func (tn *TargetNode) LicenseConditions() *LicenseConditionSet {
|
func (tn *TargetNode) LicenseConditions() LicenseConditionSet {
|
||||||
result := newLicenseConditionSet()
|
return tn.licenseConditions
|
||||||
result.add(tn, tn.proto.LicenseConditions...)
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LicenseTexts returns the paths to the files containing the license texts for
|
// LicenseTexts returns the paths to the files containing the license texts for
|
||||||
@@ -387,12 +392,12 @@ type InstallMap struct {
|
|||||||
// Annotations typically distinguish between static linkage versus dynamic
|
// Annotations typically distinguish between static linkage versus dynamic
|
||||||
// versus tools that are used at build time but are not linked in any way.
|
// versus tools that are used at build time but are not linked in any way.
|
||||||
type TargetEdgeAnnotations struct {
|
type TargetEdgeAnnotations struct {
|
||||||
annotations map[string]bool
|
annotations map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.
|
// newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.
|
||||||
func newEdgeAnnotations() TargetEdgeAnnotations {
|
func newEdgeAnnotations() TargetEdgeAnnotations {
|
||||||
return TargetEdgeAnnotations{make(map[string]bool)}
|
return TargetEdgeAnnotations{make(map[string]struct{})}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAnnotation returns true if an annotation `ann` is in the set.
|
// HasAnnotation returns true if an annotation `ann` is in the set.
|
||||||
@@ -439,7 +444,7 @@ func (ea TargetEdgeAnnotations) AsList() []string {
|
|||||||
|
|
||||||
// TargetNodeSet describes a set of distinct nodes in a license graph.
|
// TargetNodeSet describes a set of distinct nodes in a license graph.
|
||||||
type TargetNodeSet struct {
|
type TargetNodeSet struct {
|
||||||
nodes map[*TargetNode]bool
|
nodes map[*TargetNode]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains returns true when `target` is an element of the set.
|
// Contains returns true when `target` is an element of the set.
|
||||||
@@ -466,6 +471,11 @@ func (ts *TargetNodeSet) Names() []string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns a human-readable string representation of the set.
|
||||||
|
func (ts *TargetNodeSet) String() string {
|
||||||
|
return fmt.Sprintf("{%s}", strings.Join(ts.Names(), ", "))
|
||||||
|
}
|
||||||
|
|
||||||
// TargetNodeList orders a list of targets by name.
|
// TargetNodeList orders a list of targets by name.
|
||||||
type TargetNodeList []*TargetNode
|
type TargetNodeList []*TargetNode
|
||||||
|
|
||||||
|
@@ -30,31 +30,33 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
|
// ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
|
||||||
ImpliesUnencumbered = ConditionNames{"unencumbered"}
|
ImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition)
|
||||||
|
|
||||||
// ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
|
// ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
|
||||||
ImpliesPermissive = ConditionNames{"permissive"}
|
ImpliesPermissive = LicenseConditionSet(PermissiveCondition)
|
||||||
|
|
||||||
// ImpliesNotice lists the condition names implying a notice or attribution policy.
|
// ImpliesNotice lists the condition names implying a notice or attribution policy.
|
||||||
ImpliesNotice = ConditionNames{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary", "by_exception_only"}
|
ImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition |
|
||||||
|
RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition |
|
||||||
|
ProprietaryCondition | ByExceptionOnlyCondition)
|
||||||
|
|
||||||
// ImpliesReciprocal lists the condition names implying a local source-sharing policy.
|
// ImpliesReciprocal lists the condition names implying a local source-sharing policy.
|
||||||
ImpliesReciprocal = ConditionNames{"reciprocal"}
|
ImpliesReciprocal = LicenseConditionSet(ReciprocalCondition)
|
||||||
|
|
||||||
// Restricted lists the condition names implying an infectious source-sharing policy.
|
// Restricted lists the condition names implying an infectious source-sharing policy.
|
||||||
ImpliesRestricted = ConditionNames{"restricted"}
|
ImpliesRestricted = LicenseConditionSet(RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition)
|
||||||
|
|
||||||
// ImpliesProprietary lists the condition names implying a confidentiality policy.
|
// ImpliesProprietary lists the condition names implying a confidentiality policy.
|
||||||
ImpliesProprietary = ConditionNames{"proprietary"}
|
ImpliesProprietary = LicenseConditionSet(ProprietaryCondition)
|
||||||
|
|
||||||
// ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
|
// ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
|
||||||
ImpliesByExceptionOnly = ConditionNames{"proprietary", "by_exception_only"}
|
ImpliesByExceptionOnly = LicenseConditionSet(ProprietaryCondition | ByExceptionOnlyCondition)
|
||||||
|
|
||||||
// ImpliesPrivate lists the condition names implying a source-code privacy policy.
|
// ImpliesPrivate lists the condition names implying a source-code privacy policy.
|
||||||
ImpliesPrivate = ConditionNames{"proprietary"}
|
ImpliesPrivate = LicenseConditionSet(ProprietaryCondition)
|
||||||
|
|
||||||
// ImpliesShared lists the condition names implying a source-code sharing policy.
|
// ImpliesShared lists the condition names implying a source-code sharing policy.
|
||||||
ImpliesShared = ConditionNames{"reciprocal", "restricted"}
|
ImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -64,100 +66,117 @@ var (
|
|||||||
ccBySa = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
|
ccBySa = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Resolution happens in two passes:
|
|
||||||
|
// LicenseConditionSetFromNames returns a set containing the recognized `names` and
|
||||||
|
// silently ignoring or discarding the unrecognized `names`.
|
||||||
|
func LicenseConditionSetFromNames(tn *TargetNode, names ...string) LicenseConditionSet {
|
||||||
|
cs := NewLicenseConditionSet()
|
||||||
|
for _, name := range names {
|
||||||
|
if name == "restricted" {
|
||||||
|
if 0 == len(tn.LicenseKinds()) {
|
||||||
|
cs = cs.Plus(RestrictedCondition)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hasLgpl := false
|
||||||
|
hasClasspath := false
|
||||||
|
hasGeneric := false
|
||||||
|
for _, kind := range tn.LicenseKinds() {
|
||||||
|
if strings.HasSuffix(kind, "-with-classpath-exception") {
|
||||||
|
cs = cs.Plus(RestrictedClasspathExceptionCondition)
|
||||||
|
hasClasspath = true
|
||||||
|
} else if anyLgpl.MatchString(kind) {
|
||||||
|
cs = cs.Plus(WeaklyRestrictedCondition)
|
||||||
|
hasLgpl = true
|
||||||
|
} else if versionedGpl.MatchString(kind) {
|
||||||
|
cs = cs.Plus(RestrictedCondition)
|
||||||
|
} else if genericGpl.MatchString(kind) {
|
||||||
|
hasGeneric = true
|
||||||
|
} else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
|
||||||
|
cs = cs.Plus(RestrictedCondition)
|
||||||
|
} else {
|
||||||
|
cs = cs.Plus(RestrictedCondition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasGeneric && !hasLgpl && !hasClasspath {
|
||||||
|
cs = cs.Plus(RestrictedCondition)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if lc, ok := RecognizedConditionNames[name]; ok {
|
||||||
|
cs |= LicenseConditionSet(lc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Resolution happens in three phases:
|
||||||
//
|
//
|
||||||
// 1. A bottom-up traversal propagates license conditions up to targets from
|
// 1. A bottom-up traversal propagates (restricted) license conditions up to
|
||||||
// dendencies as needed.
|
// targets from dendencies as needed.
|
||||||
//
|
//
|
||||||
// 2. For each condition of interest, a top-down traversal adjusts the attached
|
// 2. For each condition of interest, a top-down traversal propagates
|
||||||
// conditions pushing restricted down from targets into linked dependencies.
|
// (restricted) conditions down from targets into linked dependencies.
|
||||||
//
|
//
|
||||||
// The behavior of the 2 passes gets controlled by the 2 functions below.
|
// 3. Finally, a walk of the shipped target nodes attaches resolutions to the
|
||||||
|
// ancestor nodes from the root down to and including the first non-container.
|
||||||
//
|
//
|
||||||
// The first function controls what happens during the bottom-up traversal. In
|
// e.g. If a disk image contains a binary bin1 that links a library liba, the
|
||||||
// general conditions flow up through static links but not other dependencies;
|
// notice requirement for liba gets attached to the disk image and to bin1.
|
||||||
// except, restricted sometimes flows up through dynamic links.
|
// Because liba doesn't actually get shipped as a separate artifact, but only
|
||||||
|
// as bits in bin1, it has no actions 'attached' to it. The actions attached
|
||||||
|
// to the image and to bin1 'act on' liba by providing notice.
|
||||||
//
|
//
|
||||||
// In general, too, the originating target gets acted on to resolve the
|
// The behavior of the 3 phases gets controlled by the 3 functions below.
|
||||||
// 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
|
// The first function controls what happens during the bottom-up propagation.
|
||||||
// general, only restricted conditions flow down at all, and only through
|
// Restricted conditions propagate up all non-toolchain dependencies; except,
|
||||||
// static links.
|
// some do not propagate up dynamic links, which may depend on whether the
|
||||||
|
// modules are independent.
|
||||||
|
//
|
||||||
|
// The second function controls what happens during the top-down propagation.
|
||||||
|
// Restricted conditions propagate down as above with the added caveat that
|
||||||
|
// inherited restricted conditions do not propagate from pure aggregates to
|
||||||
|
// their dependencies.
|
||||||
|
//
|
||||||
|
// The final function controls which conditions apply/get attached to ancestors
|
||||||
|
// depending on the types of dependencies involved. All conditions apply across
|
||||||
|
// normal derivation dependencies. No conditions apply across toolchain
|
||||||
|
// dependencies. Some restricted conditions apply across dynamic link
|
||||||
|
// dependencies.
|
||||||
//
|
//
|
||||||
// Not all restricted licenses are create equal. Some have special rules or
|
// Not all restricted licenses are create equal. Some have special rules or
|
||||||
// exceptions. e.g. LGPL or "with classpath excption".
|
// exceptions. e.g. LGPL or "with classpath excption".
|
||||||
|
|
||||||
// depActionsApplicableToTarget returns the actions which propagate up an
|
|
||||||
|
// depConditionsPropagatingToTarget returns the conditions which propagate up an
|
||||||
// edge from dependency to target.
|
// edge from dependency to target.
|
||||||
//
|
//
|
||||||
// This function sets the policy for the bottom-up traversal and how conditions
|
// This function sets the policy for the bottom-up propagation and how conditions
|
||||||
// flow up the graph from dependencies to targets.
|
// flow up the graph from dependencies to targets.
|
||||||
//
|
//
|
||||||
// If a pure aggregation is built into a derivative work that is not a pure
|
// 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
|
// aggregation, per policy it ceases to be a pure aggregation in the context of
|
||||||
// that derivative work. The `treatAsAggregate` parameter will be false for
|
// that derivative work. The `treatAsAggregate` parameter will be false for
|
||||||
// non-aggregates and for aggregates in non-aggregate contexts.
|
// non-aggregates and for aggregates in non-aggregate contexts.
|
||||||
func depActionsApplicableToTarget(e TargetEdge, depActions actionSet, treatAsAggregate bool) actionSet {
|
func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
|
||||||
result := make(actionSet)
|
result := LicenseConditionSet(0x0000)
|
||||||
if edgeIsDerivation(e) {
|
if edgeIsDerivation(e) {
|
||||||
result.addSet(depActions)
|
result |= depConditions & ImpliesRestricted
|
||||||
for _, cs := range depActions.byName(ImpliesRestricted) {
|
|
||||||
result.add(e.Target(), cs)
|
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
if !edgeIsDynamicLink(e) {
|
if !edgeIsDynamicLink(e) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
restricted := depActions.byName(ImpliesRestricted)
|
result |= depConditions & LicenseConditionSet(RestrictedCondition)
|
||||||
for actsOn, cs := range restricted {
|
if 0 != (depConditions & LicenseConditionSet(RestrictedClasspathExceptionCondition)) && !edgeNodesAreIndependentModules(e) {
|
||||||
for _, lc := range cs.AsList() {
|
result |= LicenseConditionSet(RestrictedClasspathExceptionCondition)
|
||||||
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
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// targetConditionsApplicableToDep returns the conditions which propagate down
|
// targetConditionsPropagatingToDep returns the conditions which propagate down
|
||||||
// an edge from target to dependency.
|
// an edge from target to dependency.
|
||||||
//
|
//
|
||||||
// This function sets the policy for the top-down traversal and how conditions
|
// This function sets the policy for the top-down traversal and how conditions
|
||||||
@@ -167,81 +186,73 @@ func depActionsApplicableToTarget(e TargetEdge, depActions actionSet, treatAsAgg
|
|||||||
// aggregation, per policy it ceases to be a pure aggregation in the context of
|
// aggregation, per policy it ceases to be a pure aggregation in the context of
|
||||||
// that derivative work. The `treatAsAggregate` parameter will be false for
|
// that derivative work. The `treatAsAggregate` parameter will be false for
|
||||||
// non-aggregates and for aggregates in non-aggregate contexts.
|
// non-aggregates and for aggregates in non-aggregate contexts.
|
||||||
func targetConditionsApplicableToDep(e TargetEdge, targetConditions *LicenseConditionSet, treatAsAggregate bool) *LicenseConditionSet {
|
func targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
|
||||||
result := targetConditions.Copy()
|
result := targetConditions
|
||||||
|
|
||||||
// reverse direction -- none of these apply to things depended-on, only to targets depending-on.
|
// 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"})
|
result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition)
|
||||||
|
|
||||||
if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
|
if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
|
||||||
// target is not a derivative work of dependency and is not linked to dependency
|
// target is not a derivative work of dependency and is not linked to dependency
|
||||||
result.RemoveAllByName(ImpliesRestricted)
|
result = result.Difference(ImpliesRestricted)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
if treatAsAggregate {
|
if treatAsAggregate {
|
||||||
// If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
|
// If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
|
||||||
// Otherwise, restricted does not propagate back down to dependencies.
|
// Otherwise, restricted does not propagate back down to dependencies.
|
||||||
restricted := result.ByName(ImpliesRestricted).AsList()
|
if !LicenseConditionSetFromNames(e.target, e.target.proto.LicenseConditions...).MatchesAnySet(ImpliesRestricted) {
|
||||||
for _, lc := range restricted {
|
result = result.Difference(ImpliesRestricted)
|
||||||
if lc.origin.name != e.e.target {
|
|
||||||
result.Remove(lc)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
if edgeIsDerivation(e) {
|
if edgeIsDerivation(e) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
restricted := result.ByName(ImpliesRestricted).AsList()
|
result = result.Minus(WeaklyRestrictedCondition)
|
||||||
for _, lc := range restricted {
|
if edgeNodesAreIndependentModules(e) {
|
||||||
hasGpl := false
|
result = result.Minus(RestrictedClasspathExceptionCondition)
|
||||||
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
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// conditionsAttachingAcrossEdge returns the subset of conditions in `universe`
|
||||||
|
// that apply across edge `e`.
|
||||||
|
//
|
||||||
|
// This function sets the policy for attaching actions to ancestor nodes in the
|
||||||
|
// final resolution walk.
|
||||||
|
func conditionsAttachingAcrossEdge(lg *LicenseGraph, e *TargetEdge, universe LicenseConditionSet) LicenseConditionSet {
|
||||||
|
result := universe
|
||||||
|
if edgeIsDerivation(e) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if !edgeIsDynamicLink(e) {
|
||||||
|
return NewLicenseConditionSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
result &= LicenseConditionSet(RestrictedCondition | RestrictedClasspathExceptionCondition)
|
||||||
|
if 0 != (result & LicenseConditionSet(RestrictedClasspathExceptionCondition)) && edgeNodesAreIndependentModules(e) {
|
||||||
|
result &= LicenseConditionSet(RestrictedCondition)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// edgeIsDynamicLink returns true for edges representing shared libraries
|
// edgeIsDynamicLink returns true for edges representing shared libraries
|
||||||
// linked dynamically at runtime.
|
// linked dynamically at runtime.
|
||||||
func edgeIsDynamicLink(e TargetEdge) bool {
|
func edgeIsDynamicLink(e *TargetEdge) bool {
|
||||||
return e.e.annotations.HasAnnotation("dynamic")
|
return e.annotations.HasAnnotation("dynamic")
|
||||||
}
|
}
|
||||||
|
|
||||||
// edgeIsDerivation returns true for edges where the target is a derivative
|
// edgeIsDerivation returns true for edges where the target is a derivative
|
||||||
// work of dependency.
|
// work of dependency.
|
||||||
func edgeIsDerivation(e TargetEdge) bool {
|
func edgeIsDerivation(e *TargetEdge) bool {
|
||||||
isDynamic := e.e.annotations.HasAnnotation("dynamic")
|
isDynamic := e.annotations.HasAnnotation("dynamic")
|
||||||
isToolchain := e.e.annotations.HasAnnotation("toolchain")
|
isToolchain := e.annotations.HasAnnotation("toolchain")
|
||||||
return !isDynamic && !isToolchain
|
return !isDynamic && !isToolchain
|
||||||
}
|
}
|
||||||
|
|
||||||
// edgeNodesAreIndependentModules returns true for edges where the target and
|
// edgeNodesAreIndependentModules returns true for edges where the target and
|
||||||
// dependency are independent modules.
|
// dependency are independent modules.
|
||||||
func edgeNodesAreIndependentModules(e TargetEdge) bool {
|
func edgeNodesAreIndependentModules(e *TargetEdge) bool {
|
||||||
return e.Target().PackageName() != e.Dependency().PackageName()
|
return e.target.PackageName() != e.dependency.PackageName()
|
||||||
}
|
}
|
||||||
|
@@ -34,21 +34,21 @@ func TestPolicy_edgeConditions(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "firstparty",
|
name: "firstparty",
|
||||||
edge: annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
edge: annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{},
|
expectedTargetConditions: []string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "notice",
|
name: "notice",
|
||||||
edge: annotated{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
edge: annotated{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{},
|
expectedTargetConditions: []string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fponlgpl",
|
name: "fponlgpl",
|
||||||
edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{
|
expectedDepActions: []string{
|
||||||
"apacheBin.meta_lic:lgplLib.meta_lic:restricted",
|
"apacheBin.meta_lic:lgplLib.meta_lic:restricted_allows_dynamic_linking",
|
||||||
"lgplLib.meta_lic:lgplLib.meta_lic:restricted",
|
"lgplLib.meta_lic:lgplLib.meta_lic:restricted_allows_dynamic_linking",
|
||||||
},
|
},
|
||||||
expectedTargetConditions: []string{},
|
expectedTargetConditions: []string{},
|
||||||
},
|
},
|
||||||
@@ -86,8 +86,8 @@ func TestPolicy_edgeConditions(t *testing.T) {
|
|||||||
name: "independentmodulestatic",
|
name: "independentmodulestatic",
|
||||||
edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{
|
expectedDepActions: []string{
|
||||||
"apacheBin.meta_lic:gplWithClasspathException.meta_lic:restricted",
|
"apacheBin.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
|
||||||
"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted",
|
"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
|
||||||
},
|
},
|
||||||
expectedTargetConditions: []string{},
|
expectedTargetConditions: []string{},
|
||||||
},
|
},
|
||||||
@@ -95,8 +95,8 @@ func TestPolicy_edgeConditions(t *testing.T) {
|
|||||||
name: "dependentmodule",
|
name: "dependentmodule",
|
||||||
edge: annotated{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
|
edge: annotated{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
|
||||||
expectedDepActions: []string{
|
expectedDepActions: []string{
|
||||||
"dependentModule.meta_lic:gplWithClasspathException.meta_lic:restricted",
|
"dependentModule.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
|
||||||
"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted",
|
"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
|
||||||
},
|
},
|
||||||
expectedTargetConditions: []string{},
|
expectedTargetConditions: []string{},
|
||||||
},
|
},
|
||||||
@@ -104,8 +104,8 @@ func TestPolicy_edgeConditions(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "lgplonfp",
|
name: "lgplonfp",
|
||||||
edge: annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
edge: annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{"lgplBin.meta_lic:restricted"},
|
expectedTargetConditions: []string{"lgplBin.meta_lic:restricted_allows_dynamic_linking"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "lgplonfpdynamic",
|
name: "lgplonfpdynamic",
|
||||||
@@ -116,14 +116,14 @@ func TestPolicy_edgeConditions(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "gplonfp",
|
name: "gplonfp",
|
||||||
edge: annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
edge: annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
|
expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gplcontainer",
|
name: "gplcontainer",
|
||||||
edge: annotated{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
edge: annotated{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
||||||
treatAsAggregate: true,
|
treatAsAggregate: true,
|
||||||
expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{"gplContainer.meta_lic:restricted"},
|
expectedTargetConditions: []string{"gplContainer.meta_lic:restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -133,7 +133,6 @@ func TestPolicy_edgeConditions(t *testing.T) {
|
|||||||
otherCondition: "gplLib.meta_lic:restricted",
|
otherCondition: "gplLib.meta_lic:restricted",
|
||||||
expectedDepActions: []string{
|
expectedDepActions: []string{
|
||||||
"apacheContainer.meta_lic:gplLib.meta_lic:restricted",
|
"apacheContainer.meta_lic:gplLib.meta_lic:restricted",
|
||||||
"apacheLib.meta_lic:apacheLib.meta_lic:notice",
|
|
||||||
"apacheLib.meta_lic:gplLib.meta_lic:restricted",
|
"apacheLib.meta_lic:gplLib.meta_lic:restricted",
|
||||||
"gplLib.meta_lic:gplLib.meta_lic:restricted",
|
"gplLib.meta_lic:gplLib.meta_lic:restricted",
|
||||||
},
|
},
|
||||||
@@ -146,7 +145,6 @@ func TestPolicy_edgeConditions(t *testing.T) {
|
|||||||
otherCondition: "gplLib.meta_lic:restricted",
|
otherCondition: "gplLib.meta_lic:restricted",
|
||||||
expectedDepActions: []string{
|
expectedDepActions: []string{
|
||||||
"apacheBin.meta_lic:gplLib.meta_lic:restricted",
|
"apacheBin.meta_lic:gplLib.meta_lic:restricted",
|
||||||
"apacheLib.meta_lic:apacheLib.meta_lic:notice",
|
|
||||||
"apacheLib.meta_lic:gplLib.meta_lic:restricted",
|
"apacheLib.meta_lic:gplLib.meta_lic:restricted",
|
||||||
"gplLib.meta_lic:gplLib.meta_lic:restricted",
|
"gplLib.meta_lic:gplLib.meta_lic:restricted",
|
||||||
},
|
},
|
||||||
@@ -167,14 +165,14 @@ func TestPolicy_edgeConditions(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "independentmodulereversestatic",
|
name: "independentmodulereversestatic",
|
||||||
edge: annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
edge: annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{"apacheBin.meta_lic:apacheBin.meta_lic:notice"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted"},
|
expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted_with_classpath_exception"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "dependentmodulereverse",
|
name: "dependentmodulereverse",
|
||||||
edge: annotated{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
|
edge: annotated{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
|
||||||
expectedDepActions: []string{},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted"},
|
expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted_with_classpath_exception"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ponr",
|
name: "ponr",
|
||||||
@@ -188,31 +186,31 @@ func TestPolicy_edgeConditions(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "ronp",
|
name: "ronp",
|
||||||
edge: annotated{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
|
edge: annotated{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{"proprietary.meta_lic:proprietary.meta_lic:proprietary"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
|
expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "noticeonb_e_o",
|
name: "noticeonb_e_o",
|
||||||
edge: annotated{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
|
edge: annotated{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{"by_exception.meta_lic:by_exception.meta_lic:by_exception_only"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{},
|
expectedTargetConditions: []string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "b_e_oonnotice",
|
name: "b_e_oonnotice",
|
||||||
edge: annotated{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
edge: annotated{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{},
|
expectedTargetConditions: []string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "noticeonrecip",
|
name: "noticeonrecip",
|
||||||
edge: annotated{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
|
edge: annotated{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{"mplLib.meta_lic:mplLib.meta_lic:reciprocal"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{},
|
expectedTargetConditions: []string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "reciponnotice",
|
name: "reciponnotice",
|
||||||
edge: annotated{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
edge: annotated{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
|
expectedDepActions: []string{},
|
||||||
expectedTargetConditions: []string{},
|
expectedTargetConditions: []string{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -231,69 +229,80 @@ func TestPolicy_edgeConditions(t *testing.T) {
|
|||||||
t.Errorf("unexpected error reading graph: %w", err)
|
t.Errorf("unexpected error reading graph: %w", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
edge := lg.Edges()[0]
|
||||||
// simulate a condition inherited from another edge/dependency.
|
// simulate a condition inherited from another edge/dependency.
|
||||||
otherTarget := ""
|
otherTarget := ""
|
||||||
otherCondition := ""
|
otherCondition := ""
|
||||||
|
var otn *TargetNode
|
||||||
if len(tt.otherCondition) > 0 {
|
if len(tt.otherCondition) > 0 {
|
||||||
fields := strings.Split(tt.otherCondition, ":")
|
fields := strings.Split(tt.otherCondition, ":")
|
||||||
otherTarget = fields[0]
|
otherTarget = fields[0]
|
||||||
otherCondition = fields[1]
|
otherCondition = fields[1]
|
||||||
|
otn = &TargetNode{name: otherTarget}
|
||||||
// other target must exist in graph
|
// other target must exist in graph
|
||||||
lg.targets[otherTarget] = &TargetNode{name: otherTarget}
|
lg.targets[otherTarget] = otn
|
||||||
lg.targets[otherTarget].proto.LicenseConditions = append(lg.targets[otherTarget].proto.LicenseConditions, otherCondition)
|
otn.licenseConditions = LicenseConditionSet(RecognizedConditionNames[otherCondition])
|
||||||
|
}
|
||||||
|
targets := make(map[string]*TargetNode)
|
||||||
|
targets[edge.target.name] = edge.target
|
||||||
|
targets[edge.dependency.name] = edge.dependency
|
||||||
|
if otn != nil {
|
||||||
|
targets[otn.name] = otn
|
||||||
}
|
}
|
||||||
if tt.expectedDepActions != nil {
|
if tt.expectedDepActions != nil {
|
||||||
depActions := make(actionSet)
|
t.Run("depConditionsPropagatingToTarget", func(t *testing.T) {
|
||||||
depActions[lg.targets[tt.edge.dep]] = lg.targets[tt.edge.dep].LicenseConditions()
|
depConditions := edge.dependency.LicenseConditions()
|
||||||
if otherTarget != "" {
|
if otherTarget != "" {
|
||||||
// simulate a sub-dependency's condition having already propagated up to dep and about to go to target
|
// simulate a sub-dependency's condition having already propagated up to dep and about to go to target
|
||||||
otherCs := lg.targets[otherTarget].LicenseConditions()
|
otherCs := otn.LicenseConditions()
|
||||||
depActions[lg.targets[tt.edge.dep]].AddSet(otherCs)
|
depConditions |= 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
|
|
||||||
}
|
}
|
||||||
}
|
t.Logf("calculate target actions for edge=%s, dep conditions=%04x, treatAsAggregate=%v", edge.String(), depConditions, tt.treatAsAggregate)
|
||||||
|
csActual := depConditionsPropagatingToTarget(lg, edge, depConditions, tt.treatAsAggregate)
|
||||||
checkSameActions(lg, asActual, asExpected, t)
|
t.Logf("calculated target conditions as %04x{%s}", csActual, strings.Join(csActual.Names(), ", "))
|
||||||
|
csExpected := NewLicenseConditionSet()
|
||||||
|
for _, triple := range tt.expectedDepActions {
|
||||||
|
fields := strings.Split(triple, ":")
|
||||||
|
expectedConditions := NewLicenseConditionSet()
|
||||||
|
for _, cname := range fields[2:] {
|
||||||
|
expectedConditions = expectedConditions.Plus(RecognizedConditionNames[cname])
|
||||||
|
}
|
||||||
|
csExpected |= expectedConditions
|
||||||
|
}
|
||||||
|
t.Logf("expected target conditions as %04x{%s}", csExpected, strings.Join(csExpected.Names(), ", "))
|
||||||
|
if csActual != csExpected {
|
||||||
|
t.Errorf("unexpected license conditions: got %04x, want %04x", csActual, csExpected)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if tt.expectedTargetConditions != nil {
|
if tt.expectedTargetConditions != nil {
|
||||||
targetConditions := lg.TargetNode(tt.edge.target).LicenseConditions()
|
t.Run("targetConditionsPropagatingToDep", func(t *testing.T) {
|
||||||
if otherTarget != "" {
|
targetConditions := edge.target.LicenseConditions()
|
||||||
targetConditions.add(lg.targets[otherTarget], otherCondition)
|
if otherTarget != "" {
|
||||||
}
|
targetConditions = targetConditions.Union(otn.licenseConditions)
|
||||||
cs := targetConditionsApplicableToDep(
|
}
|
||||||
lg.Edges()[0],
|
t.Logf("calculate dep conditions for edge=%s, target conditions=%v, treatAsAggregate=%v", edge.String(), targetConditions.Names(), tt.treatAsAggregate)
|
||||||
targetConditions,
|
cs := targetConditionsPropagatingToDep(lg, edge, targetConditions, tt.treatAsAggregate)
|
||||||
tt.treatAsAggregate)
|
t.Logf("calculated dep conditions as %v", cs.Names())
|
||||||
actual := make([]string, 0, cs.Count())
|
actual := cs.Names()
|
||||||
for _, lc := range cs.AsList() {
|
sort.Strings(actual)
|
||||||
actual = append(actual, lc.asString(":"))
|
expected := make([]string, 0)
|
||||||
}
|
for _, expectedDepCondition := range tt.expectedTargetConditions {
|
||||||
sort.Strings(actual)
|
expected = append(expected, strings.Split(expectedDepCondition, ":")[1])
|
||||||
sort.Strings(tt.expectedTargetConditions)
|
}
|
||||||
if len(actual) != len(tt.expectedTargetConditions) {
|
sort.Strings(expected)
|
||||||
t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions",
|
if len(actual) != len(expected) {
|
||||||
actual, len(actual), tt.expectedTargetConditions, len(tt.expectedTargetConditions))
|
t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions",
|
||||||
} else {
|
actual, len(actual), expected, len(expected))
|
||||||
for i := 0; i < len(actual); i++ {
|
} else {
|
||||||
if actual[i] != tt.expectedTargetConditions[i] {
|
for i := 0; i < len(actual); i++ {
|
||||||
t.Errorf("unexpected target condition at element %d: got %q, want %q",
|
if actual[i] != expected[i] {
|
||||||
i, actual[i], tt.expectedTargetConditions[i])
|
t.Errorf("unexpected target condition at element %d: got %q, want %q",
|
||||||
|
i, actual[i], expected[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -14,228 +14,196 @@
|
|||||||
|
|
||||||
package compliance
|
package compliance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
|
// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
|
||||||
// propagating conditions up the graph as necessary according to the properties
|
// propagating conditions up the graph as necessary according to the properties
|
||||||
// of each edge and according to each license condition in question.
|
// 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
|
// 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
|
// of the statically-linked libraries and the transitive closure of their static
|
||||||
// dependencies; even if neither they nor the transitive closure of their
|
// dependencies; even if neither they nor the transitive closure of their
|
||||||
// dependencies originate any "restricted" conditions. The bottom-up walk will
|
// dependencies originate any "restricted" conditions. The bottom-up walk will
|
||||||
// not resolve the library and its transitive closure, but the later top-down
|
// not resolve the library and its transitive closure, but the later top-down
|
||||||
// walk will.
|
// walk will.
|
||||||
func ResolveBottomUpConditions(lg *LicenseGraph) *ResolutionSet {
|
func ResolveBottomUpConditions(lg *LicenseGraph) {
|
||||||
|
|
||||||
// short-cut if already walked and cached
|
// short-cut if already walked and cached
|
||||||
lg.mu.Lock()
|
lg.mu.Lock()
|
||||||
rs := lg.rsBU
|
wg := lg.wgBU
|
||||||
|
|
||||||
|
if wg != nil {
|
||||||
|
lg.mu.Unlock()
|
||||||
|
wg.Wait()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wg = &sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
lg.wgBU = wg
|
||||||
lg.mu.Unlock()
|
lg.mu.Unlock()
|
||||||
|
|
||||||
if rs != nil {
|
// amap identifes targets previously walked. (guarded by mu)
|
||||||
return rs
|
amap := make(map[*TargetNode]struct{})
|
||||||
|
|
||||||
|
// cmap identifies targets previously walked as pure aggregates. i.e. as containers
|
||||||
|
// (guarded by mu)
|
||||||
|
cmap := make(map[*TargetNode]struct{})
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
|
var walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet
|
||||||
|
|
||||||
|
walk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet {
|
||||||
|
priorWalkResults := func() (LicenseConditionSet, bool) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
if _, alreadyWalked := amap[target]; alreadyWalked {
|
||||||
|
if treatAsAggregate {
|
||||||
|
return target.resolution, true
|
||||||
|
}
|
||||||
|
if _, asAggregate := cmap[target]; !asAggregate {
|
||||||
|
return target.resolution, true
|
||||||
|
}
|
||||||
|
// previously walked in a pure aggregate context,
|
||||||
|
// needs to walk again in non-aggregate context
|
||||||
|
delete(cmap, target)
|
||||||
|
} else {
|
||||||
|
target.resolution |= target.licenseConditions
|
||||||
|
amap[target] = struct{}{}
|
||||||
|
}
|
||||||
|
if treatAsAggregate {
|
||||||
|
cmap[target] = struct{}{}
|
||||||
|
}
|
||||||
|
return target.resolution, false
|
||||||
|
}
|
||||||
|
cs, alreadyWalked := priorWalkResults()
|
||||||
|
if alreadyWalked {
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make(chan LicenseConditionSet, len(target.edges))
|
||||||
|
// add all the conditions from all the dependencies
|
||||||
|
for _, edge := range target.edges {
|
||||||
|
go func(edge *TargetEdge) {
|
||||||
|
// walk dependency to get its conditions
|
||||||
|
cs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer())
|
||||||
|
|
||||||
|
// turn those into the conditions that apply to the target
|
||||||
|
cs = depConditionsPropagatingToTarget(lg, edge, cs, treatAsAggregate)
|
||||||
|
|
||||||
|
c <- cs
|
||||||
|
}(edge)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(target.edges); i++ {
|
||||||
|
cs |= <-c
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
target.resolution |= cs
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
// return conditions up the tree
|
||||||
|
return cs
|
||||||
}
|
}
|
||||||
|
|
||||||
// must be indexed for fast lookup
|
// walk each of the roots
|
||||||
lg.indexForward()
|
for _, rname := range lg.rootFiles {
|
||||||
|
rnode := lg.targets[rname]
|
||||||
rs = resolveBottomUp(lg, make(map[*TargetNode]actionSet) /* empty map; no prior resolves */)
|
_ = walk(rnode, rnode.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
|
wg.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
|
// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
|
||||||
// resolving all reachable nodes for `condition`. Policy establishes the rules
|
// propagating conditions from target to dependency.
|
||||||
// for transforming and propagating resolutions down the graph.
|
|
||||||
//
|
//
|
||||||
// e.g. For current policy, none of the conditions propagate from target to
|
// e.g. For current policy, none of the conditions propagate from target to
|
||||||
// dependency except restricted. For restricted, the policy is to share the
|
// dependency except restricted. For restricted, the policy is to share the
|
||||||
// source of any libraries linked to restricted code and to provide notice.
|
// source of any libraries linked to restricted code and to provide notice.
|
||||||
func ResolveTopDownConditions(lg *LicenseGraph) *ResolutionSet {
|
func ResolveTopDownConditions(lg *LicenseGraph) {
|
||||||
|
|
||||||
// short-cut if already walked and cached
|
// short-cut if already walked and cached
|
||||||
lg.mu.Lock()
|
lg.mu.Lock()
|
||||||
rs := lg.rsTD
|
wg := lg.wgTD
|
||||||
lg.mu.Unlock()
|
|
||||||
|
|
||||||
if rs != nil {
|
if wg != nil {
|
||||||
return rs
|
lg.mu.Unlock()
|
||||||
|
wg.Wait()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
wg = &sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
lg.wgTD = wg
|
||||||
|
lg.mu.Unlock()
|
||||||
|
|
||||||
// start with the conditions propagated up the graph
|
// start with the conditions propagated up the graph
|
||||||
rs = ResolveBottomUpConditions(lg)
|
ResolveBottomUpConditions(lg)
|
||||||
|
|
||||||
// rmap maps 'appliesTo' targets to their applicable conditions
|
// amap contains the set of targets already walked. (guarded by mu)
|
||||||
//
|
amap := make(map[*TargetNode]struct{})
|
||||||
// rmap is the resulting ResolutionSet
|
|
||||||
rmap := make(map[*TargetNode]actionSet)
|
|
||||||
|
|
||||||
// cmap contains the set of targets walked as pure aggregates. i.e. containers
|
// cmap contains the set of targets walked as pure aggregates. i.e. containers
|
||||||
cmap := make(map[*TargetNode]bool)
|
// (guarded by mu)
|
||||||
|
cmap := make(map[*TargetNode]struct{})
|
||||||
|
|
||||||
var walk func(fnode *TargetNode, cs *LicenseConditionSet, treatAsAggregate bool)
|
// mu guards concurrent access to cmap
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
walk = func(fnode *TargetNode, cs *LicenseConditionSet, treatAsAggregate bool) {
|
var walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool)
|
||||||
if _, ok := rmap[fnode]; !ok {
|
|
||||||
rmap[fnode] = make(actionSet)
|
walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {
|
||||||
}
|
defer wg.Done()
|
||||||
rmap[fnode].add(fnode, cs)
|
mu.Lock()
|
||||||
|
fnode.resolution |= fnode.licenseConditions
|
||||||
|
fnode.resolution |= cs
|
||||||
|
amap[fnode] = struct{}{}
|
||||||
if treatAsAggregate {
|
if treatAsAggregate {
|
||||||
cmap[fnode] = true
|
cmap[fnode] = struct{}{}
|
||||||
}
|
|
||||||
// add conditions attached to `fnode`
|
|
||||||
cs = cs.Copy()
|
|
||||||
for _, fcs := range rs.resolutions[fnode] {
|
|
||||||
cs.AddSet(fcs)
|
|
||||||
}
|
}
|
||||||
|
cs = fnode.resolution
|
||||||
|
mu.Unlock()
|
||||||
// for each dependency
|
// for each dependency
|
||||||
for _, edge := range lg.index[fnode.name] {
|
for _, edge := range fnode.edges {
|
||||||
e := TargetEdge{lg, edge}
|
func(edge *TargetEdge) {
|
||||||
// dcs holds the dpendency conditions inherited from the target
|
// dcs holds the dpendency conditions inherited from the target
|
||||||
dcs := targetConditionsApplicableToDep(e, cs, treatAsAggregate)
|
dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate)
|
||||||
if dcs.IsEmpty() && !treatAsAggregate {
|
dnode := edge.dependency
|
||||||
continue
|
mu.Lock()
|
||||||
}
|
defer mu.Unlock()
|
||||||
dnode := lg.targets[edge.dependency]
|
depcs := dnode.resolution
|
||||||
if as, alreadyWalked := rmap[dnode]; alreadyWalked {
|
_, alreadyWalked := amap[dnode]
|
||||||
diff := dcs.Copy()
|
if !dcs.IsEmpty() && alreadyWalked {
|
||||||
diff.RemoveSet(as.conditions())
|
if dcs.Difference(depcs).IsEmpty() {
|
||||||
if diff.IsEmpty() {
|
// no new conditions
|
||||||
// no new conditions
|
|
||||||
|
|
||||||
// pure aggregates never need walking a 2nd time with same conditions
|
// pure aggregates never need walking a 2nd time with same conditions
|
||||||
if treatAsAggregate {
|
if treatAsAggregate {
|
||||||
continue
|
return
|
||||||
|
}
|
||||||
|
// non-aggregates don't need walking as non-aggregate a 2nd time
|
||||||
|
if _, asAggregate := cmap[dnode]; !asAggregate {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// previously walked as pure aggregate; need to re-walk as non-aggregate
|
||||||
|
delete(cmap, dnode)
|
||||||
}
|
}
|
||||||
// non-aggregates don't need walking as non-aggregate a 2nd time
|
|
||||||
if _, asAggregate := cmap[dnode]; !asAggregate {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// previously walked as pure aggregate; need to re-walk as non-aggregate
|
|
||||||
delete(cmap, dnode)
|
|
||||||
}
|
}
|
||||||
}
|
// add the conditions to the dependency
|
||||||
// add the conditions to the dependency
|
wg.Add(1)
|
||||||
walk(dnode, dcs, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
|
go walk(dnode, dcs, treatAsAggregate && dnode.IsContainer())
|
||||||
|
}(edge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// walk each of the roots
|
// walk each of the roots
|
||||||
for _, r := range lg.rootFiles {
|
for _, rname := range lg.rootFiles {
|
||||||
rnode := lg.targets[r]
|
rnode := lg.targets[rname]
|
||||||
as, ok := rs.resolutions[rnode]
|
wg.Add(1)
|
||||||
if !ok {
|
|
||||||
// no conditions in root or transitive closure of dependencies
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if as.isEmpty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the conditions to the root and its transitive closure
|
// add the conditions to the root and its transitive closure
|
||||||
walk(rnode, newLicenseConditionSet(), lg.targets[r].IsContainer())
|
go walk(rnode, NewLicenseConditionSet(), rnode.IsContainer())
|
||||||
}
|
}
|
||||||
|
wg.Done()
|
||||||
// back-fill any bottom-up conditions on targets missed by top-down walk
|
wg.Wait()
|
||||||
for attachesTo, as := range rs.resolutions {
|
|
||||||
if _, ok := rmap[attachesTo]; !ok {
|
|
||||||
rmap[attachesTo] = as.copy()
|
|
||||||
} else {
|
|
||||||
rmap[attachesTo].addSet(as)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// propagate any new conditions back up the graph
|
|
||||||
rs = resolveBottomUp(lg, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolveBottomUp implements a bottom-up resolve propagating conditions both
|
|
||||||
// from the graph, and from a `priors` map of resolutions.
|
|
||||||
func resolveBottomUp(lg *LicenseGraph, priors map[*TargetNode]actionSet) *ResolutionSet {
|
|
||||||
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 pas, ok := priors[target]; ok {
|
|
||||||
result.addSet(pas)
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
if len(priors) == 0 {
|
|
||||||
// on the first bottom-up resolve, parents have their own sharing and notice needs
|
|
||||||
// on the later resolve, if priors is empty, there will be nothing new to add
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
return rs
|
|
||||||
}
|
}
|
||||||
|
@@ -16,15 +16,16 @@ package compliance
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResolveBottomUpConditions(t *testing.T) {
|
func TestResolveBottomUpConditions(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
roots []string
|
roots []string
|
||||||
edges []annotated
|
edges []annotated
|
||||||
expectedResolutions []res
|
expectedActions []tcond
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "firstparty",
|
name: "firstparty",
|
||||||
@@ -32,10 +33,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
{"apacheLib.meta_lic", "notice"},
|
||||||
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -44,9 +44,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"toolchain"}},
|
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"toolchain"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
{"apacheLib.meta_lic", "notice"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -56,13 +56,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
{"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"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -72,12 +69,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
{"apacheLib.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
|
||||||
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -86,9 +81,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
{"apacheLib.meta_lic", "notice"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -98,11 +93,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheLib.meta_lic", "notice"},
|
||||||
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -112,11 +106,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
|
{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheLib.meta_lic", "notice"},
|
||||||
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -125,11 +118,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice|restricted"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
|
||||||
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -138,9 +129,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"toolchain"}},
|
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"toolchain"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -150,16 +141,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"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"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -169,13 +154,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"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"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -184,11 +166,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice|restricted"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
|
||||||
{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -198,16 +178,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"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"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -217,13 +191,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
|
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"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"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -232,11 +203,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
|
|
||||||
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -245,9 +214,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"toolchain"}},
|
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"toolchain"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -257,16 +226,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"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"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -276,13 +239,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"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"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -291,9 +251,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -303,11 +263,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -317,11 +276,10 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -330,11 +288,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice|restricted_with_classpath_exception"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
|
||||||
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
|
||||||
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -343,11 +299,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
|
{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
|
||||||
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
|
||||||
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
|
||||||
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -356,9 +310,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -367,11 +321,9 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
|
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
|
{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
|
||||||
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
|
||||||
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
|
||||||
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -383,19 +335,37 @@ func TestResolveBottomUpConditions(t *testing.T) {
|
|||||||
t.Errorf("unexpected test data error: got %w, want no error", err)
|
t.Errorf("unexpected test data error: got %w, want no error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
|
|
||||||
actualRs := ResolveBottomUpConditions(lg)
|
logGraph(lg, t)
|
||||||
checkSame(actualRs, expectedRs, t)
|
|
||||||
|
ResolveBottomUpConditions(lg)
|
||||||
|
actual := asActionList(lg)
|
||||||
|
sort.Sort(actual)
|
||||||
|
t.Logf("actual: %s", actual.String())
|
||||||
|
|
||||||
|
expected := toActionList(lg, tt.expectedActions)
|
||||||
|
sort.Sort(expected)
|
||||||
|
t.Logf("expected: %s", expected.String())
|
||||||
|
|
||||||
|
if len(actual) != len(expected) {
|
||||||
|
t.Errorf("unexpected number of actions: got %d, want %d", len(actual), len(expected))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < len(actual); i++ {
|
||||||
|
if actual[i] != expected[i] {
|
||||||
|
t.Errorf("unexpected action at index %d: got %s, want %s", i, actual[i].String(), expected[i].String())
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveTopDownConditions(t *testing.T) {
|
func TestResolveTopDownConditions(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
roots []string
|
roots []string
|
||||||
edges []annotated
|
edges []annotated
|
||||||
expectedResolutions []res
|
expectedActions []tcond
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "firstparty",
|
name: "firstparty",
|
||||||
@@ -403,10 +373,9 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
{"apacheLib.meta_lic", "notice"},
|
||||||
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -415,9 +384,9 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
edges: []annotated{
|
edges: []annotated{
|
||||||
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
{"apacheLib.meta_lic", "notice"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -427,15 +396,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice|restricted"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"mitLib.meta_lic", "notice|restricted"},
|
||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"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", "gplLib.meta_lic", "restricted"},
|
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -445,11 +409,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
|
{"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
{"mitLib.meta_lic", "notice"},
|
||||||
{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
|
{"gplBin.meta_lic", "restricted"},
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -462,27 +425,13 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
|
||||||
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"mitBin.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"mplLib.meta_lic", "reciprocal|restricted"},
|
||||||
{"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
|
{"mitLib.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", "gplLib.meta_lic", "restricted"},
|
|
||||||
{"mplLib.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -492,13 +441,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"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"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -508,14 +454,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice|restricted"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"mitLib.meta_lic", "notice|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", "gplLib.meta_lic", "restricted"},
|
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -528,23 +470,13 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
|
||||||
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
|
{"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"mitBin.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"mplLib.meta_lic", "reciprocal|restricted"},
|
||||||
{"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
|
{"mitLib.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", "gplLib.meta_lic", "restricted"},
|
|
||||||
{"mplLib.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -554,13 +486,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
|
{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"gplLib.meta_lic", "restricted"},
|
||||||
{"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"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -570,15 +499,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"mitLib.meta_lic", "notice|restricted_allows_dynamic_linking"},
|
||||||
{"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", "lgplLib.meta_lic", "restricted"},
|
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -588,11 +512,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
|
{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
{"lgplBin.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
|
{"mitLib.meta_lic", "notice"},
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -603,22 +526,11 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"mitLib.meta_lic", "notice|restricted_allows_dynamic_linking"},
|
||||||
{"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", "lgplLib.meta_lic", "restricted"},
|
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -628,13 +540,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"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"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -644,11 +553,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
|
{"mitLib.meta_lic", "notice"},
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -658,11 +566,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -672,11 +579,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
|
||||||
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
{"apacheContainer.meta_lic", "notice"},
|
||||||
{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
|
||||||
{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -686,15 +592,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice|restricted_with_classpath_exception"},
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
|
||||||
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"mitLib.meta_lic", "notice|restricted_with_classpath_exception"},
|
||||||
{"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", "gplWithClasspathException.meta_lic", "restricted"},
|
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -704,15 +605,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
||||||
{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
|
{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
|
||||||
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
|
||||||
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"mitLib.meta_lic", "notice|restricted_with_classpath_exception"},
|
||||||
{"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", "gplWithClasspathException.meta_lic", "restricted"},
|
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -722,11 +618,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
|
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
{"apacheBin.meta_lic", "notice"},
|
||||||
{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
|
||||||
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"mitLib.meta_lic", "notice"},
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -736,15 +631,10 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
|
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
|
||||||
{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
|
||||||
},
|
},
|
||||||
expectedResolutions: []res{
|
expectedActions: []tcond{
|
||||||
{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
|
{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
|
||||||
{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
|
||||||
{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
{"mitLib.meta_lic", "notice|restricted_with_classpath_exception"},
|
||||||
{"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", "gplWithClasspathException.meta_lic", "restricted"},
|
|
||||||
{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -756,9 +646,27 @@ func TestResolveTopDownConditions(t *testing.T) {
|
|||||||
t.Errorf("unexpected test data error: got %w, want no error", err)
|
t.Errorf("unexpected test data error: got %w, want no error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
|
|
||||||
actualRs := ResolveTopDownConditions(lg)
|
logGraph(lg, t)
|
||||||
checkSame(actualRs, expectedRs, t)
|
|
||||||
|
ResolveTopDownConditions(lg)
|
||||||
|
actual := asActionList(lg)
|
||||||
|
sort.Sort(actual)
|
||||||
|
t.Logf("actual: %s", actual.String())
|
||||||
|
|
||||||
|
expected := toActionList(lg, tt.expectedActions)
|
||||||
|
sort.Sort(expected)
|
||||||
|
t.Logf("expected: %s", expected.String())
|
||||||
|
|
||||||
|
if len(actual) != len(expected) {
|
||||||
|
t.Errorf("unexpected number of actions: got %d, want %d", len(actual), len(expected))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < len(actual); i++ {
|
||||||
|
if actual[i] != expected[i] {
|
||||||
|
t.Errorf("unexpected action at index %d: got %s, want %s", i, actual[i].String(), expected[i].String())
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
package compliance
|
package compliance
|
||||||
|
|
||||||
// ResolveNotices implements the policy for notices.
|
// ResolveNotices implements the policy for notices.
|
||||||
func ResolveNotices(lg *LicenseGraph) *ResolutionSet {
|
func ResolveNotices(lg *LicenseGraph) ResolutionSet {
|
||||||
rs := ResolveTopDownConditions(lg)
|
ResolveTopDownConditions(lg)
|
||||||
return WalkResolutionsForCondition(lg, rs, ImpliesNotice)
|
return WalkResolutionsForCondition(lg, ImpliesNotice)
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
package compliance
|
package compliance
|
||||||
|
|
||||||
// ResolveSourcePrivacy implements the policy for source privacy.
|
// ResolveSourcePrivacy implements the policy for source privacy.
|
||||||
func ResolveSourcePrivacy(lg *LicenseGraph) *ResolutionSet {
|
func ResolveSourcePrivacy(lg *LicenseGraph) ResolutionSet {
|
||||||
rs := ResolveTopDownConditions(lg)
|
ResolveTopDownConditions(lg)
|
||||||
return WalkResolutionsForCondition(lg, rs, ImpliesPrivate)
|
return WalkResolutionsForCondition(lg, ImpliesPrivate)
|
||||||
}
|
}
|
||||||
|
@@ -81,7 +81,7 @@ func TestResolveSourcePrivacy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
|
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
|
||||||
actualRs := ResolveSourcePrivacy(lg)
|
actualRs := ResolveSourcePrivacy(lg)
|
||||||
checkSame(actualRs, expectedRs, t)
|
checkResolves(actualRs, expectedRs, t)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
package compliance
|
package compliance
|
||||||
|
|
||||||
// ResolveSourceSharing implements the policy for source-sharing.
|
// ResolveSourceSharing implements the policy for source-sharing.
|
||||||
func ResolveSourceSharing(lg *LicenseGraph) *ResolutionSet {
|
func ResolveSourceSharing(lg *LicenseGraph) ResolutionSet {
|
||||||
rs := ResolveTopDownConditions(lg)
|
ResolveTopDownConditions(lg)
|
||||||
return WalkResolutionsForCondition(lg, rs, ImpliesShared)
|
return WalkResolutionsForCondition(lg, ImpliesShared)
|
||||||
}
|
}
|
||||||
|
@@ -291,7 +291,7 @@ func TestResolveSourceSharing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
|
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
|
||||||
actualRs := ResolveSourceSharing(lg)
|
actualRs := ResolveSourceSharing(lg)
|
||||||
checkSame(actualRs, expectedRs, t)
|
checkResolves(actualRs, expectedRs, t)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,57 +28,37 @@ type SourceSharePrivacyConflict struct {
|
|||||||
|
|
||||||
// Error returns a string describing the conflict.
|
// Error returns a string describing the conflict.
|
||||||
func (conflict SourceSharePrivacyConflict) Error() string {
|
func (conflict SourceSharePrivacyConflict) Error() string {
|
||||||
return fmt.Sprintf("%s %s from %s and must share from %s %s\n",
|
return fmt.Sprintf("%s %s and must share from %s condition\n", conflict.SourceNode.name,
|
||||||
conflict.SourceNode.name,
|
conflict.PrivacyCondition.Name(), conflict.ShareCondition.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.
|
// IsEqualTo returns true when `conflict` and `other` describe the same conflict.
|
||||||
func (conflict SourceSharePrivacyConflict) IsEqualTo(other SourceSharePrivacyConflict) bool {
|
func (conflict SourceSharePrivacyConflict) IsEqualTo(other SourceSharePrivacyConflict) bool {
|
||||||
return conflict.SourceNode.name == other.SourceNode.name &&
|
return conflict.SourceNode.name == other.SourceNode.name &&
|
||||||
conflict.ShareCondition.name == other.ShareCondition.name &&
|
conflict.ShareCondition == other.ShareCondition &&
|
||||||
conflict.ShareCondition.origin.name == other.ShareCondition.origin.name &&
|
conflict.PrivacyCondition == other.PrivacyCondition
|
||||||
conflict.PrivacyCondition.name == other.PrivacyCondition.name &&
|
|
||||||
conflict.PrivacyCondition.origin.name == other.PrivacyCondition.origin.name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConflictingSharedPrivateSource lists all of the targets where conflicting conditions to
|
// ConflictingSharedPrivateSource lists all of the targets where conflicting conditions to
|
||||||
// share the source and to keep the source private apply to the target.
|
// share the source and to keep the source private apply to the target.
|
||||||
func ConflictingSharedPrivateSource(lg *LicenseGraph) []SourceSharePrivacyConflict {
|
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{}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ResolveTopDownConditions(lg)
|
||||||
// combined is the combination of source-sharing and source privacy.
|
// combined is the combination of source-sharing and source privacy.
|
||||||
combined := JoinResolutionSets(shareSource, privateSource)
|
combined := WalkActionsForCondition(lg, ImpliesShared.Union(ImpliesPrivate))
|
||||||
|
|
||||||
// size is the size of the result
|
// size is the size of the result
|
||||||
size := 0
|
size := 0
|
||||||
for _, actsOn := range combined.ActsOn() {
|
for _, cs := range combined {
|
||||||
rl := combined.ResolutionsByActsOn(actsOn)
|
size += cs.Intersection(ImpliesShared).Len() * cs.Intersection(ImpliesPrivate).Len()
|
||||||
size += rl.CountConditionsByName(ImpliesShared) * rl.CountConditionsByName(ImpliesPrivate)
|
|
||||||
}
|
}
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return []SourceSharePrivacyConflict{}
|
return nil
|
||||||
}
|
}
|
||||||
result := make([]SourceSharePrivacyConflict, 0, size)
|
result := make([]SourceSharePrivacyConflict, 0, size)
|
||||||
for _, actsOn := range combined.ActsOn() {
|
for actsOn, cs := range combined {
|
||||||
rl := combined.ResolutionsByActsOn(actsOn)
|
pconditions := cs.Intersection(ImpliesPrivate).AsList()
|
||||||
if len(rl) == 0 {
|
ssconditions := cs.Intersection(ImpliesShared).AsList()
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pconditions := rl.ByName(ImpliesPrivate).AllConditions().AsList()
|
|
||||||
ssconditions := rl.ByName(ImpliesShared).AllConditions().AsList()
|
|
||||||
|
|
||||||
// report all conflicting condition combinations
|
// report all conflicting condition combinations
|
||||||
for _, p := range pconditions {
|
for _, p := range pconditions {
|
||||||
|
@@ -33,19 +33,13 @@ 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
|
// Less returns true when the `i`th element is lexicographically less than
|
||||||
// the `j`th element.
|
// the `j`th element.
|
||||||
func (l byConflict) Less(i, j int) bool {
|
func (l byConflict) Less(i, j int) bool {
|
||||||
if l[i].SourceNode.name == l[j].SourceNode.name {
|
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].ShareCondition.name == l[j].ShareCondition.name {
|
return l[i].PrivacyCondition.Name() < l[j].PrivacyCondition.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].ShareCondition.Name() < l[j].ShareCondition.Name()
|
||||||
}
|
}
|
||||||
return l[i].SourceNode.name < l[j].SourceNode.name
|
return l[i].SourceNode.Name() < l[j].SourceNode.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConflictingSharedPrivateSource(t *testing.T) {
|
func TestConflictingSharedPrivateSource(t *testing.T) {
|
||||||
|
@@ -24,18 +24,18 @@ func ShippedNodes(lg *LicenseGraph) *TargetNodeSet {
|
|||||||
return shipped
|
return shipped
|
||||||
}
|
}
|
||||||
|
|
||||||
tset := make(map[*TargetNode]bool)
|
tset := make(map[*TargetNode]struct{})
|
||||||
|
|
||||||
WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
|
WalkTopDown(NoEdgeContext{}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
|
||||||
if _, alreadyWalked := tset[tn]; alreadyWalked {
|
if _, alreadyWalked := tset[tn]; alreadyWalked {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(path) > 0 {
|
if len(path) > 0 {
|
||||||
if !edgeIsDerivation(path[len(path)-1]) {
|
if !edgeIsDerivation(path[len(path)-1].edge) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tset[tn] = true
|
tset[tn] = struct{}{}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@ package compliance
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -110,19 +111,31 @@ func TestShippedNodes(t *testing.T) {
|
|||||||
t.Errorf("unexpected test data error: got %w, want no error", err)
|
t.Errorf("unexpected test data error: got %w, want no error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
t.Logf("graph:")
|
||||||
|
for _, edge := range lg.Edges() {
|
||||||
|
t.Logf(" %s", edge.String())
|
||||||
|
}
|
||||||
expectedNodes := append([]string{}, tt.expectedNodes...)
|
expectedNodes := append([]string{}, tt.expectedNodes...)
|
||||||
actualNodes := ShippedNodes(lg).Names()
|
nodeset := ShippedNodes(lg)
|
||||||
|
t.Logf("shipped node set: %s", nodeset.String())
|
||||||
|
|
||||||
|
actualNodes := nodeset.Names()
|
||||||
|
t.Logf("shipped nodes: [%s]", strings.Join(actualNodes, ", "))
|
||||||
|
|
||||||
sort.Strings(expectedNodes)
|
sort.Strings(expectedNodes)
|
||||||
sort.Strings(actualNodes)
|
sort.Strings(actualNodes)
|
||||||
|
|
||||||
|
t.Logf("sorted nodes: [%s]", strings.Join(actualNodes, ", "))
|
||||||
|
t.Logf("expected nodes: [%s]", strings.Join(expectedNodes, ", "))
|
||||||
if len(expectedNodes) != len(actualNodes) {
|
if len(expectedNodes) != len(actualNodes) {
|
||||||
t.Errorf("unexpected number of shipped nodes: got %v with %d nodes, want %v with %d nodes",
|
t.Errorf("unexpected number of shipped nodes: %d nodes, want %d nodes",
|
||||||
actualNodes, len(actualNodes), expectedNodes, len(expectedNodes))
|
len(actualNodes), len(expectedNodes))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i := 0; i < len(actualNodes); i++ {
|
for i := 0; i < len(actualNodes); i++ {
|
||||||
if expectedNodes[i] != actualNodes[i] {
|
if expectedNodes[i] != actualNodes[i] {
|
||||||
t.Errorf("unexpected node at index %d: got %q in %v, want %q in %v",
|
t.Errorf("unexpected node at index %d: got %q, want %q",
|
||||||
i, actualNodes[i], actualNodes, expectedNodes[i], expectedNodes)
|
i, actualNodes[i], expectedNodes[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -14,27 +14,60 @@
|
|||||||
|
|
||||||
package compliance
|
package compliance
|
||||||
|
|
||||||
|
// EdgeContextProvider is an interface for injecting edge-specific context
|
||||||
|
// into walk paths.
|
||||||
|
type EdgeContextProvider interface {
|
||||||
|
// Context returns the context for `edge` when added to `path`.
|
||||||
|
Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoEdgeContext implements EdgeContextProvider for walks that use no context.
|
||||||
|
type NoEdgeContext struct{}
|
||||||
|
|
||||||
|
// Context returns nil.
|
||||||
|
func (ctx NoEdgeContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicableConditionsContext provides the subset of conditions in `universe`
|
||||||
|
// that apply to each edge in a path.
|
||||||
|
type ApplicableConditionsContext struct {
|
||||||
|
universe LicenseConditionSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context returns the LicenseConditionSet applicable to the edge.
|
||||||
|
func (ctx ApplicableConditionsContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} {
|
||||||
|
universe := ctx.universe
|
||||||
|
if len(path) > 0 {
|
||||||
|
universe = path[len(path)-1].ctx.(LicenseConditionSet)
|
||||||
|
}
|
||||||
|
return conditionsAttachingAcrossEdge(lg, edge, universe)
|
||||||
|
}
|
||||||
|
|
||||||
// VisitNode is called for each root and for each walked dependency node by
|
// VisitNode is called for each root and for each walked dependency node by
|
||||||
// WalkTopDown. When VisitNode returns true, WalkTopDown will proceed to walk
|
// WalkTopDown. When VisitNode returns true, WalkTopDown will proceed to walk
|
||||||
// down the dependences of the node
|
// down the dependences of the node
|
||||||
type VisitNode func(*LicenseGraph, *TargetNode, TargetEdgePath) bool
|
type VisitNode func(lg *LicenseGraph, target *TargetNode, path TargetEdgePath) bool
|
||||||
|
|
||||||
// WalkTopDown does a top-down walk of `lg` calling `visit` and descending
|
// WalkTopDown does a top-down walk of `lg` calling `visit` and descending
|
||||||
// into depenencies when `visit` returns true.
|
// into depenencies when `visit` returns true.
|
||||||
func WalkTopDown(lg *LicenseGraph, visit VisitNode) {
|
func WalkTopDown(ctx EdgeContextProvider, lg *LicenseGraph, visit VisitNode) {
|
||||||
path := NewTargetEdgePath(32)
|
path := NewTargetEdgePath(32)
|
||||||
|
|
||||||
// must be indexed for fast lookup
|
var walk func(fnode *TargetNode)
|
||||||
lg.indexForward()
|
walk = func(fnode *TargetNode) {
|
||||||
|
visitChildren := visit(lg, fnode, *path)
|
||||||
var walk func(f string)
|
|
||||||
walk = func(f string) {
|
|
||||||
visitChildren := visit(lg, lg.targets[f], *path)
|
|
||||||
if !visitChildren {
|
if !visitChildren {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, edge := range lg.index[f] {
|
for _, edge := range fnode.edges {
|
||||||
path.Push(TargetEdge{lg, edge})
|
var edgeContext interface{}
|
||||||
|
if ctx == nil {
|
||||||
|
edgeContext = nil
|
||||||
|
} else {
|
||||||
|
edgeContext = ctx.Context(lg, *path, edge)
|
||||||
|
}
|
||||||
|
path.Push(edge, edgeContext)
|
||||||
walk(edge.dependency)
|
walk(edge.dependency)
|
||||||
path.Pop()
|
path.Pop()
|
||||||
}
|
}
|
||||||
@@ -42,35 +75,164 @@ func WalkTopDown(lg *LicenseGraph, visit VisitNode) {
|
|||||||
|
|
||||||
for _, r := range lg.rootFiles {
|
for _, r := range lg.rootFiles {
|
||||||
path.Clear()
|
path.Clear()
|
||||||
walk(r)
|
walk(lg.targets[r])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolutionKey identifies results from walking a specific target for a
|
||||||
|
// specific set of conditions.
|
||||||
|
type resolutionKey struct {
|
||||||
|
target *TargetNode
|
||||||
|
cs LicenseConditionSet
|
||||||
|
}
|
||||||
|
|
||||||
// WalkResolutionsForCondition performs a top-down walk of the LicenseGraph
|
// WalkResolutionsForCondition performs a top-down walk of the LicenseGraph
|
||||||
// resolving all distributed works for condition `names`.
|
// resolving all distributed works for `conditions`.
|
||||||
func WalkResolutionsForCondition(lg *LicenseGraph, rs *ResolutionSet, names ConditionNames) *ResolutionSet {
|
func WalkResolutionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ResolutionSet {
|
||||||
shipped := ShippedNodes(lg)
|
shipped := ShippedNodes(lg)
|
||||||
|
|
||||||
// rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions
|
// rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions
|
||||||
//
|
rmap := make(map[resolutionKey]ActionSet)
|
||||||
// rmap is the resulting ResolutionSet
|
|
||||||
rmap := make(map[*TargetNode]actionSet)
|
|
||||||
|
|
||||||
WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, _ TargetEdgePath) bool {
|
// cmap identifies previously walked target/condition pairs.
|
||||||
if _, ok := rmap[tn]; ok {
|
cmap := make(map[resolutionKey]struct{})
|
||||||
|
|
||||||
|
// result accumulates the resolutions to return.
|
||||||
|
result := make(ResolutionSet)
|
||||||
|
WalkTopDown(ApplicableConditionsContext{conditions}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
|
||||||
|
universe := conditions
|
||||||
|
if len(path) > 0 {
|
||||||
|
universe = path[len(path)-1].ctx.(LicenseConditionSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if universe.IsEmpty() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
key := resolutionKey{tn, universe}
|
||||||
|
|
||||||
|
if _, alreadyWalked := cmap[key]; alreadyWalked {
|
||||||
|
pure := true
|
||||||
|
for _, p := range path {
|
||||||
|
target := p.Target()
|
||||||
|
tkey := resolutionKey{target, universe}
|
||||||
|
if _, ok := rmap[tkey]; !ok {
|
||||||
|
rmap[tkey] = make(ActionSet)
|
||||||
|
}
|
||||||
|
// attach prior walk outcome to ancestor
|
||||||
|
for actsOn, cs := range rmap[key] {
|
||||||
|
rmap[tkey][actsOn] = cs
|
||||||
|
}
|
||||||
|
// if prior walk produced results, copy results
|
||||||
|
// to ancestor.
|
||||||
|
if _, ok := result[tn]; ok && pure {
|
||||||
|
if _, ok := result[target]; !ok {
|
||||||
|
result[target] = make(ActionSet)
|
||||||
|
}
|
||||||
|
for actsOn, cs := range result[tn] {
|
||||||
|
result[target][actsOn] = cs
|
||||||
|
}
|
||||||
|
pure = target.IsContainer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if all ancestors are pure aggregates, attach
|
||||||
|
// matching prior walk conditions to self. Prior walk
|
||||||
|
// will not have done so if any ancestor was not an
|
||||||
|
// aggregate.
|
||||||
|
if pure {
|
||||||
|
match := rmap[key][tn].Intersection(universe)
|
||||||
|
if !match.IsEmpty() {
|
||||||
|
if _, ok := result[tn]; !ok {
|
||||||
|
result[tn] = make(ActionSet)
|
||||||
|
}
|
||||||
|
result[tn][tn] = match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// no need to walk node or dependencies if not shipped
|
||||||
|
if !shipped.Contains(tn) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, ok := rmap[key]; !ok {
|
||||||
|
rmap[key] = make(ActionSet)
|
||||||
|
}
|
||||||
|
// add self to walk outcome
|
||||||
|
rmap[key][tn] = tn.resolution
|
||||||
|
cmap[key] = struct{}{}
|
||||||
|
cs := tn.resolution
|
||||||
|
if !cs.IsEmpty() {
|
||||||
|
cs = cs.Intersection(universe)
|
||||||
|
pure := true
|
||||||
|
for _, p := range path {
|
||||||
|
target := p.Target()
|
||||||
|
tkey := resolutionKey{target, universe}
|
||||||
|
if _, ok := rmap[tkey]; !ok {
|
||||||
|
rmap[tkey] = make(ActionSet)
|
||||||
|
}
|
||||||
|
// copy current node's action into ancestor
|
||||||
|
rmap[tkey][tn] = tn.resolution
|
||||||
|
// conditionally put matching conditions into
|
||||||
|
// result
|
||||||
|
if pure && !cs.IsEmpty() {
|
||||||
|
if _, ok := result[target]; !ok {
|
||||||
|
result[target] = make(ActionSet)
|
||||||
|
}
|
||||||
|
result[target][tn] = cs
|
||||||
|
pure = target.IsContainer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if all ancestors are pure aggregates, attach
|
||||||
|
// matching conditions to self.
|
||||||
|
if pure && !cs.IsEmpty() {
|
||||||
|
if _, ok := result[tn]; !ok {
|
||||||
|
result[tn] = make(ActionSet)
|
||||||
|
}
|
||||||
|
result[tn][tn] = cs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalkActionsForCondition performs a top-down walk of the LicenseGraph
|
||||||
|
// resolving all distributed works for `conditions`.
|
||||||
|
func WalkActionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ActionSet {
|
||||||
|
shipped := ShippedNodes(lg)
|
||||||
|
|
||||||
|
// cmap identifies previously walked target/condition pairs.
|
||||||
|
cmap := make(map[resolutionKey]struct{})
|
||||||
|
|
||||||
|
// amap maps 'actsOn' targets to the applicable conditions
|
||||||
|
//
|
||||||
|
// amap is the resulting ActionSet
|
||||||
|
amap := make(ActionSet)
|
||||||
|
WalkTopDown(ApplicableConditionsContext{conditions}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
|
||||||
|
universe := conditions
|
||||||
|
if len(path) > 0 {
|
||||||
|
universe = path[len(path)-1].ctx.(LicenseConditionSet)
|
||||||
|
}
|
||||||
|
if universe.IsEmpty() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
key := resolutionKey{tn, universe}
|
||||||
|
if _, ok := cmap[key]; ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !shipped.Contains(tn) {
|
if !shipped.Contains(tn) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if as, ok := rs.resolutions[tn]; ok {
|
cs := universe.Intersection(tn.resolution)
|
||||||
fas := as.byActsOn(shipped).byName(names)
|
if !cs.IsEmpty() {
|
||||||
if !fas.isEmpty() {
|
if _, ok := amap[tn]; ok {
|
||||||
rmap[tn] = fas
|
amap[tn] = cs
|
||||||
|
} else {
|
||||||
|
amap[tn] = amap[tn].Union(cs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tn.IsContainer() // descend into containers
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
return &ResolutionSet{rmap}
|
return amap
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@ import (
|
|||||||
func TestWalkResolutionsForCondition(t *testing.T) {
|
func TestWalkResolutionsForCondition(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
condition ConditionNames
|
condition LicenseConditionSet
|
||||||
roots []string
|
roots []string
|
||||||
edges []annotated
|
edges []annotated
|
||||||
expectedResolutions []res
|
expectedResolutions []res
|
||||||
@@ -624,8 +624,617 @@ func TestWalkResolutionsForCondition(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
|
expectedRs := toResolutionSet(lg, tt.expectedResolutions)
|
||||||
actualRs := WalkResolutionsForCondition(lg, ResolveTopDownConditions(lg), tt.condition)
|
ResolveTopDownConditions(lg)
|
||||||
checkSame(actualRs, expectedRs, t)
|
actualRs := WalkResolutionsForCondition(lg, tt.condition)
|
||||||
|
checkResolves(actualRs, expectedRs, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWalkActionsForCondition(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
condition LicenseConditionSet
|
||||||
|
roots []string
|
||||||
|
edges []annotated
|
||||||
|
expectedActions []act
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "firstparty",
|
||||||
|
condition: ImpliesNotice,
|
||||||
|
roots: []string{"apacheBin.meta_lic"},
|
||||||
|
edges: []annotated{
|
||||||
|
{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
||||||
|
{"apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "independentmodulestaticnotice",
|
||||||
|
condition: ImpliesNotice,
|
||||||
|
roots: []string{"apacheBin.meta_lic"},
|
||||||
|
edges: []annotated{
|
||||||
|
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
||||||
|
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
|
||||||
|
{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
|
||||||
|
{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
|
||||||
|
{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
||||||
|
{"apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
|
||||||
|
{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
|
||||||
|
{"apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
|
||||||
|
{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
|
||||||
|
{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
||||||
|
{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
||||||
|
{"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"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
|
||||||
|
{"apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
||||||
|
{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
|
||||||
|
{"proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
|
||||||
|
{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "noticeonb_e_ob_e_o",
|
||||||
|
condition: ImpliesByExceptionOnly,
|
||||||
|
roots: []string{"mitBin.meta_lic"},
|
||||||
|
edges: []annotated{
|
||||||
|
{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
|
||||||
|
{"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"}},
|
||||||
|
},
|
||||||
|
expectedActions: []act{
|
||||||
|
{"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
|
||||||
|
}
|
||||||
|
expectedAs := toActionSet(lg, tt.expectedActions)
|
||||||
|
ResolveTopDownConditions(lg)
|
||||||
|
actualAs := WalkActionsForCondition(lg, tt.condition)
|
||||||
|
checkResolvesActions(lg, actualAs, expectedAs, t)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,16 +39,13 @@ type result struct {
|
|||||||
// target contains the parsed metadata or nil if an error
|
// target contains the parsed metadata or nil if an error
|
||||||
target *TargetNode
|
target *TargetNode
|
||||||
|
|
||||||
// edges contains the parsed dependencies
|
|
||||||
edges []*dependencyEdge
|
|
||||||
|
|
||||||
// err is nil unless an error occurs
|
// err is nil unless an error occurs
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// receiver coordinates the tasks for reading and parsing license metadata files.
|
// receiver coordinates the tasks for reading and parsing license metadata files.
|
||||||
type receiver struct {
|
type receiver struct {
|
||||||
// lg accumulates the read metadata and becomes the final resulting LicensGraph.
|
// lg accumulates the read metadata and becomes the final resulting LicenseGraph.
|
||||||
lg *LicenseGraph
|
lg *LicenseGraph
|
||||||
|
|
||||||
// rootFS locates the root of the file system from which to read the files.
|
// rootFS locates the root of the file system from which to read the files.
|
||||||
@@ -138,10 +135,7 @@ func ReadLicenseGraph(rootFS fs.FS, stderr io.Writer, files []string) (*LicenseG
|
|||||||
|
|
||||||
// record the parsed metadata (guarded by mutex)
|
// record the parsed metadata (guarded by mutex)
|
||||||
recv.lg.mu.Lock()
|
recv.lg.mu.Lock()
|
||||||
recv.lg.targets[r.file] = r.target
|
lg.targets[r.target.name] = r.target
|
||||||
if len(r.edges) > 0 {
|
|
||||||
recv.lg.edges = append(recv.lg.edges, r.edges...)
|
|
||||||
}
|
|
||||||
recv.lg.mu.Unlock()
|
recv.lg.mu.Unlock()
|
||||||
} else {
|
} else {
|
||||||
// finished -- nil the results channel
|
// finished -- nil the results channel
|
||||||
@@ -150,6 +144,21 @@ func ReadLicenseGraph(rootFS fs.FS, stderr io.Writer, files []string) (*LicenseG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if lg != nil {
|
||||||
|
esize := 0
|
||||||
|
for _, tn := range lg.targets {
|
||||||
|
esize += len(tn.proto.Deps)
|
||||||
|
}
|
||||||
|
lg.edges = make(TargetEdgeList, 0, esize)
|
||||||
|
for _, tn := range lg.targets {
|
||||||
|
tn.licenseConditions = LicenseConditionSetFromNames(tn, tn.proto.LicenseConditions...)
|
||||||
|
err = addDependencies(lg, tn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error indexing dependencies for %q: %w", tn.name, err)
|
||||||
|
}
|
||||||
|
tn.proto.Deps = []*license_metadata_proto.AnnotatedDependency{}
|
||||||
|
}
|
||||||
|
}
|
||||||
return lg, err
|
return lg, err
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -158,43 +167,48 @@ func ReadLicenseGraph(rootFS fs.FS, stderr io.Writer, files []string) (*LicenseG
|
|||||||
type targetNode struct {
|
type targetNode struct {
|
||||||
proto license_metadata_proto.LicenseMetadata
|
proto license_metadata_proto.LicenseMetadata
|
||||||
|
|
||||||
// name is the path to the metadata file
|
// name is the path to the metadata file.
|
||||||
name string
|
name string
|
||||||
}
|
|
||||||
|
|
||||||
// dependencyEdge describes a single edge in the license graph.
|
// lg is the license graph the node belongs to.
|
||||||
type dependencyEdge struct {
|
lg *LicenseGraph
|
||||||
// target identifies the target node being built and/or installed.
|
|
||||||
target string
|
|
||||||
|
|
||||||
// dependency identifies the target node being depended on.
|
// edges identifies the dependencies of the target.
|
||||||
//
|
edges TargetEdgeList
|
||||||
// i.e. `dependency` is necessary to build `target`.
|
|
||||||
dependency string
|
|
||||||
|
|
||||||
// annotations are a set of text attributes attached to the edge.
|
// licenseConditions identifies the set of license conditions originating at the target node.
|
||||||
//
|
licenseConditions LicenseConditionSet
|
||||||
// Policy prescribes meaning to a limited set of annotations; others
|
|
||||||
// are preserved and ignored.
|
// resolution identifies the set of conditions resolved by acting on the target node.
|
||||||
annotations TargetEdgeAnnotations
|
resolution LicenseConditionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// addDependencies converts the proto AnnotatedDependencies into `edges`
|
// addDependencies converts the proto AnnotatedDependencies into `edges`
|
||||||
func addDependencies(edges *[]*dependencyEdge, target string, dependencies []*license_metadata_proto.AnnotatedDependency) error {
|
func addDependencies(lg *LicenseGraph, tn *TargetNode) error {
|
||||||
for _, ad := range dependencies {
|
tn.edges = make(TargetEdgeList, 0,len(tn.proto.Deps))
|
||||||
|
for _, ad := range tn.proto.Deps {
|
||||||
dependency := ad.GetFile()
|
dependency := ad.GetFile()
|
||||||
if len(dependency) == 0 {
|
if len(dependency) == 0 {
|
||||||
return fmt.Errorf("missing dependency name")
|
return fmt.Errorf("missing dependency name")
|
||||||
}
|
}
|
||||||
|
dtn, ok := lg.targets[dependency]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unknown dependency name %q", dependency)
|
||||||
|
}
|
||||||
|
if dtn == nil {
|
||||||
|
return fmt.Errorf("nil dependency for name %q", dependency)
|
||||||
|
}
|
||||||
annotations := newEdgeAnnotations()
|
annotations := newEdgeAnnotations()
|
||||||
for _, a := range ad.Annotations {
|
for _, a := range ad.Annotations {
|
||||||
// look up a common constant annotation string from a small map
|
// look up a common constant annotation string from a small map
|
||||||
// instead of creating 1000's of copies of the same 3 strings.
|
// instead of creating 1000's of copies of the same 3 strings.
|
||||||
if ann, ok := RecognizedAnnotations[a]; ok {
|
if ann, ok := RecognizedAnnotations[a]; ok {
|
||||||
annotations.annotations[ann] = true
|
annotations.annotations[ann] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*edges = append(*edges, &dependencyEdge{target, dependency, annotations})
|
edge := &TargetEdge{tn, dtn, annotations}
|
||||||
|
lg.edges = append(lg.edges, edge)
|
||||||
|
tn.edges = append(tn.edges, edge)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -207,50 +221,44 @@ func readFile(recv *receiver, file string) {
|
|||||||
go func() {
|
go func() {
|
||||||
f, err := recv.rootFS.Open(file)
|
f, err := recv.rootFS.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recv.results <- &result{file, nil, nil, fmt.Errorf("error opening license metadata %q: %w", file, err)}
|
recv.results <- &result{file, nil, fmt.Errorf("error opening license metadata %q: %w", file, err)}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the file
|
// read the file
|
||||||
data, err := io.ReadAll(f)
|
data, err := io.ReadAll(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recv.results <- &result{file, nil, nil, fmt.Errorf("error reading license metadata %q: %w", file, err)}
|
recv.results <- &result{file, nil, fmt.Errorf("error reading license metadata %q: %w", file, err)}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
tn := &TargetNode{name: file}
|
tn := &TargetNode{lg: recv.lg, name: file}
|
||||||
|
|
||||||
err = prototext.Unmarshal(data, &tn.proto)
|
err = prototext.Unmarshal(data, &tn.proto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
recv.results <- &result{file, nil, nil, fmt.Errorf("error license metadata %q: %w", file, err)}
|
recv.results <- &result{file, nil, fmt.Errorf("error license metadata %q: %w", file, err)}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
edges := []*dependencyEdge{}
|
|
||||||
err = addDependencies(&edges, file, tn.proto.Deps)
|
|
||||||
if err != nil {
|
|
||||||
recv.results <- &result{file, nil, nil, fmt.Errorf("error license metadata dependency %q: %w", file, err)}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tn.proto.Deps = []*license_metadata_proto.AnnotatedDependency{}
|
|
||||||
|
|
||||||
// send result for this file and release task before scheduling dependencies,
|
// send result for this file and release task before scheduling dependencies,
|
||||||
// but do not signal done to WaitGroup until dependencies are scheduled.
|
// but do not signal done to WaitGroup until dependencies are scheduled.
|
||||||
recv.results <- &result{file, tn, edges, nil}
|
recv.results <- &result{file, tn, nil}
|
||||||
recv.task <- true
|
recv.task <- true
|
||||||
|
|
||||||
// schedule tasks as necessary to read dependencies
|
// schedule tasks as necessary to read dependencies
|
||||||
for _, e := range edges {
|
for _, ad := range tn.proto.Deps {
|
||||||
|
dependency := ad.GetFile()
|
||||||
// decide, signal and record whether to schedule task in critical section
|
// decide, signal and record whether to schedule task in critical section
|
||||||
recv.lg.mu.Lock()
|
recv.lg.mu.Lock()
|
||||||
_, alreadyScheduled := recv.lg.targets[e.dependency]
|
_, alreadyScheduled := recv.lg.targets[dependency]
|
||||||
if !alreadyScheduled {
|
if !alreadyScheduled {
|
||||||
recv.lg.targets[e.dependency] = nil
|
recv.lg.targets[dependency] = nil
|
||||||
}
|
}
|
||||||
recv.lg.mu.Unlock()
|
recv.lg.mu.Unlock()
|
||||||
// schedule task to read dependency file outside critical section
|
// schedule task to read dependency file outside critical section
|
||||||
if !alreadyScheduled {
|
if !alreadyScheduled {
|
||||||
readFile(recv, e.dependency)
|
readFile(recv, dependency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -108,29 +108,40 @@ func TestReadLicenseGraph(t *testing.T) {
|
|||||||
}
|
}
|
||||||
sort.Sort(byEdge(tt.expectedEdges))
|
sort.Sort(byEdge(tt.expectedEdges))
|
||||||
sort.Sort(byEdge(actualEdges))
|
sort.Sort(byEdge(actualEdges))
|
||||||
|
t.Logf("actualEdges:")
|
||||||
|
for _, edge := range actualEdges {
|
||||||
|
t.Logf(" %s", edge.String())
|
||||||
|
}
|
||||||
|
t.Logf("expectedEdges:")
|
||||||
|
for _, edge := range actualEdges {
|
||||||
|
t.Logf(" %s", edge.String())
|
||||||
|
}
|
||||||
if len(tt.expectedEdges) != len(actualEdges) {
|
if len(tt.expectedEdges) != len(actualEdges) {
|
||||||
t.Errorf("unexpected number of edges: got %v with %d elements, want %v with %d elements",
|
t.Errorf("len(actualEdges): got %d, want %d", len(actualEdges), len(tt.expectedEdges))
|
||||||
actualEdges, len(actualEdges), tt.expectedEdges, len(tt.expectedEdges))
|
|
||||||
} else {
|
} else {
|
||||||
for i := 0; i < len(actualEdges); i++ {
|
for i := 0; i < len(actualEdges); i++ {
|
||||||
if tt.expectedEdges[i] != actualEdges[i] {
|
if tt.expectedEdges[i] != actualEdges[i] {
|
||||||
t.Errorf("unexpected edge at element %d: got %s, want %s", i, actualEdges[i], tt.expectedEdges[i])
|
t.Errorf("actualEdges[%d]: got %s, want %s", i, actualEdges[i], tt.expectedEdges[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actualTargets := make([]string, 0)
|
actualTargets := make([]string, 0)
|
||||||
for _, t := range lg.Targets() {
|
for _, t := range lg.Targets() {
|
||||||
actualTargets = append(actualTargets, t.Name())
|
actualTargets = append(actualTargets, t.Name())
|
||||||
}
|
}
|
||||||
sort.Strings(tt.expectedTargets)
|
sort.Strings(tt.expectedTargets)
|
||||||
sort.Strings(actualTargets)
|
sort.Strings(actualTargets)
|
||||||
|
|
||||||
|
t.Logf("actualTargets: %v", actualTargets)
|
||||||
|
t.Logf("expectedTargets: %v", tt.expectedTargets)
|
||||||
|
|
||||||
if len(tt.expectedTargets) != len(actualTargets) {
|
if len(tt.expectedTargets) != len(actualTargets) {
|
||||||
t.Errorf("unexpected number of targets: got %v with %d elements, want %v with %d elements",
|
t.Errorf("len(actualTargets): got %d, want %d", len(actualTargets), len(tt.expectedTargets))
|
||||||
actualTargets, len(actualTargets), tt.expectedTargets, len(tt.expectedTargets))
|
|
||||||
} else {
|
} else {
|
||||||
for i := 0; i < len(actualTargets); i++ {
|
for i := 0; i < len(actualTargets); i++ {
|
||||||
if tt.expectedTargets[i] != actualTargets[i] {
|
if tt.expectedTargets[i] != actualTargets[i] {
|
||||||
t.Errorf("unexpected target at element %d: got %s, want %s", i, actualTargets[i], tt.expectedTargets[i])
|
t.Errorf("actualTargets[%d]: got %s, want %s", i, actualTargets[i], tt.expectedTargets[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,7 +32,7 @@ import (
|
|||||||
// resolve the restricted condition originating from the GPL code.
|
// resolve the restricted condition originating from the GPL code.
|
||||||
type Resolution struct {
|
type Resolution struct {
|
||||||
attachesTo, actsOn *TargetNode
|
attachesTo, actsOn *TargetNode
|
||||||
cs *LicenseConditionSet
|
cs LicenseConditionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// AttachesTo returns the target node the resolution attaches to.
|
// AttachesTo returns the target node the resolution attaches to.
|
||||||
@@ -48,16 +48,16 @@ func (r Resolution) ActsOn() *TargetNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resolves returns the set of license condition the resolution satisfies.
|
// Resolves returns the set of license condition the resolution satisfies.
|
||||||
func (r Resolution) Resolves() *LicenseConditionSet {
|
func (r Resolution) Resolves() LicenseConditionSet {
|
||||||
return r.cs.Copy()
|
return r.cs
|
||||||
}
|
}
|
||||||
|
|
||||||
// asString returns a string representation of the resolution.
|
// asString returns a string representation of the resolution.
|
||||||
func (r Resolution) asString() string {
|
func (r Resolution) asString() string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
cl := r.cs.AsList()
|
names := r.cs.Names()
|
||||||
sort.Sort(cl)
|
sort.Strings(names)
|
||||||
fmt.Fprintf(&sb, "%s -> %s -> %s", r.attachesTo.name, r.actsOn.name, cl.String())
|
fmt.Fprintf(&sb, "%s -> %s{%s}", r.attachesTo.name, r.actsOn.name, strings.Join(names, ", "))
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,67 +94,32 @@ func (rl ResolutionList) String() string {
|
|||||||
|
|
||||||
// AllConditions returns the union of all license conditions resolved by any
|
// AllConditions returns the union of all license conditions resolved by any
|
||||||
// element of the list.
|
// element of the list.
|
||||||
func (rl ResolutionList) AllConditions() *LicenseConditionSet {
|
func (rl ResolutionList) AllConditions() LicenseConditionSet {
|
||||||
result := newLicenseConditionSet()
|
result := NewLicenseConditionSet()
|
||||||
for _, r := range rl {
|
for _, r := range rl {
|
||||||
result.AddSet(r.cs)
|
result = result.Union(r.cs)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByName returns the sub-list of resolutions resolving conditions matching
|
// ByName returns the sub-list of resolutions resolving conditions matching
|
||||||
// `names`.
|
// `names`.
|
||||||
func (rl ResolutionList) ByName(names ConditionNames) ResolutionList {
|
func (rl ResolutionList) Matching(conditions LicenseConditionSet) ResolutionList {
|
||||||
result := make(ResolutionList, 0, rl.CountByName(names))
|
result := make(ResolutionList, 0, rl.CountMatching(conditions))
|
||||||
for _, r := range rl {
|
for _, r := range rl {
|
||||||
if r.Resolves().HasAnyByName(names) {
|
if r.Resolves().MatchesAnySet(conditions) {
|
||||||
result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.ByName(names)})
|
result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.MatchingAnySet(conditions)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountByName returns the number of resolutions resolving conditions matching
|
// CountMatching returns the number of resolutions resolving conditions matching
|
||||||
// `names`.
|
// `conditions`.
|
||||||
func (rl ResolutionList) CountByName(names ConditionNames) int {
|
func (rl ResolutionList) CountMatching(conditions LicenseConditionSet) int {
|
||||||
c := 0
|
c := 0
|
||||||
for _, r := range rl {
|
for _, r := range rl {
|
||||||
if r.Resolves().HasAnyByName(names) {
|
if r.Resolves().MatchesAnySet(conditions) {
|
||||||
c++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountConditionsByName returns a count of distinct resolution/conditions
|
|
||||||
// pairs matching `names`.
|
|
||||||
//
|
|
||||||
// A single resolution might resolve multiple conditions matching `names`.
|
|
||||||
func (rl ResolutionList) CountConditionsByName(names ConditionNames) int {
|
|
||||||
c := 0
|
|
||||||
for _, r := range rl {
|
|
||||||
c += r.Resolves().CountByName(names)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByAttachesTo returns the sub-list of resolutions attached to `attachesTo`.
|
|
||||||
func (rl ResolutionList) ByAttachesTo(attachesTo *TargetNode) ResolutionList {
|
|
||||||
result := make(ResolutionList, 0, rl.CountByActsOn(attachesTo))
|
|
||||||
for _, r := range rl {
|
|
||||||
if r.attachesTo == attachesTo {
|
|
||||||
result = append(result, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountByAttachesTo returns the number of resolutions attached to
|
|
||||||
// `attachesTo`.
|
|
||||||
func (rl ResolutionList) CountByAttachesTo(attachesTo *TargetNode) int {
|
|
||||||
c := 0
|
|
||||||
for _, r := range rl {
|
|
||||||
if r.attachesTo == attachesTo {
|
|
||||||
c++
|
c++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,27 +147,3 @@ func (rl ResolutionList) CountByActsOn(actsOn *TargetNode) int {
|
|||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByOrigin returns the sub-list of resolutions resolving license conditions
|
|
||||||
// originating at `origin`.
|
|
||||||
func (rl ResolutionList) ByOrigin(origin *TargetNode) ResolutionList {
|
|
||||||
result := make(ResolutionList, 0, rl.CountByOrigin(origin))
|
|
||||||
for _, r := range rl {
|
|
||||||
if r.Resolves().HasAnyByOrigin(origin) {
|
|
||||||
result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.ByOrigin(origin)})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountByOrigin returns the number of resolutions resolving license conditions
|
|
||||||
// originating at `origin`.
|
|
||||||
func (rl ResolutionList) CountByOrigin(origin *TargetNode) int {
|
|
||||||
c := 0
|
|
||||||
for _, r := range rl {
|
|
||||||
if r.Resolves().HasAnyByOrigin(origin) {
|
|
||||||
c++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
@@ -19,34 +19,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// JoinResolutionSets returns a new ResolutionSet combining the resolutions from
|
|
||||||
// multiple resolution sets. All sets must be derived from the same license
|
|
||||||
// graph.
|
|
||||||
//
|
|
||||||
// e.g. combine "restricted", "reciprocal", and "proprietary" resolutions.
|
|
||||||
func JoinResolutionSets(resolutions ...*ResolutionSet) *ResolutionSet {
|
|
||||||
if len(resolutions) < 1 {
|
|
||||||
panic(fmt.Errorf("attempt to join 0 resolution sets"))
|
|
||||||
}
|
|
||||||
rmap := make(map[*TargetNode]actionSet)
|
|
||||||
for _, r := range resolutions {
|
|
||||||
if len(r.resolutions) < 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for attachesTo, as := range r.resolutions {
|
|
||||||
if as.isEmpty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := rmap[attachesTo]; !ok {
|
|
||||||
rmap[attachesTo] = as.copy()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rmap[attachesTo].addSet(as)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &ResolutionSet{rmap}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolutionSet describes an immutable set of targets and the license
|
// ResolutionSet describes an immutable set of targets and the license
|
||||||
// conditions each target must satisfy or "resolve" in a specific context.
|
// conditions each target must satisfy or "resolve" in a specific context.
|
||||||
//
|
//
|
||||||
@@ -68,8 +40,8 @@ func JoinResolutionSets(resolutions ...*ResolutionSet) *ResolutionSet {
|
|||||||
//
|
//
|
||||||
// An "unencumbered" condition would originate from the binary, and a "notice"
|
// An "unencumbered" condition would originate from the binary, and a "notice"
|
||||||
// condition would originate from the .a library. A ResolutionSet for the
|
// condition would originate from the .a library. A ResolutionSet for the
|
||||||
// context of the Notice policy might apply both conditions to the binary while
|
// context of the Notice policy might attach both conditions to the binary to
|
||||||
// preserving the origin of each condition. By applying the notice condition to
|
// act on the origin of each condition. By attaching the notice condition to
|
||||||
// the binary, the ResolutionSet stipulates the policy that the release of the
|
// the binary, the ResolutionSet stipulates the policy that the release of the
|
||||||
// unencumbered binary must provide suitable notice for the .a library.
|
// unencumbered binary must provide suitable notice for the .a library.
|
||||||
//
|
//
|
||||||
@@ -77,228 +49,71 @@ func JoinResolutionSets(resolutions ...*ResolutionSet) *ResolutionSet {
|
|||||||
// validating that a suitable notice has been built into the distribution, or
|
// validating that a suitable notice has been built into the distribution, or
|
||||||
// for reporting what notices need to be given.
|
// for reporting what notices need to be given.
|
||||||
//
|
//
|
||||||
// Resolutions for different contexts may be combined in a new ResolutionSet
|
// The action is defined by the context. In the above example, the action is
|
||||||
// using JoinResolutions(...).
|
// providing notice for the module acted on. In another context, the action
|
||||||
//
|
// might be sharing the source-code or preserving the privacy of the module
|
||||||
// See: resolve.go for:
|
// acted on.
|
||||||
// * ResolveBottomUpConditions(...)
|
type ResolutionSet map[*TargetNode]ActionSet
|
||||||
// * ResolveTopDownForCondition(...)
|
|
||||||
// See also: policy.go for:
|
// AttachesTo identifies the list of targets triggering action to resolve
|
||||||
// * ResolveSourceSharing(...)
|
// conditions. (unordered)
|
||||||
// * ResolveSourcePrivacy(...)
|
func (rs ResolutionSet) AttachesTo() TargetNodeList {
|
||||||
type ResolutionSet struct {
|
result := make(TargetNodeList, 0, len(rs))
|
||||||
// resolutions maps names of target with applicable conditions to the set of conditions that apply.
|
for attachesTo := range rs {
|
||||||
resolutions map[*TargetNode]actionSet
|
result = append(result, attachesTo)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a string representation of the set.
|
|
||||||
func (rs *ResolutionSet) String() string {
|
// AttachesToTarget returns true if the set contains conditions that
|
||||||
|
// are `attachedTo`.
|
||||||
|
func (rs ResolutionSet) AttachesToTarget(target *TargetNode) bool {
|
||||||
|
_, isPresent := rs[target]
|
||||||
|
return isPresent
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Resolutions returns the list of resolutions that `attachedTo`
|
||||||
|
// target must resolve. Returns empty list if no conditions apply.
|
||||||
|
func (rs ResolutionSet) Resolutions(attachesTo *TargetNode) ResolutionList {
|
||||||
|
as, ok := rs[attachesTo]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
result := make(ResolutionList, 0, len(as))
|
||||||
|
for actsOn, cs := range as {
|
||||||
|
result = append(result, Resolution{attachesTo, actsOn, cs})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human-readable string representation of the set.
|
||||||
|
func (rs ResolutionSet) String() string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
fmt.Fprintf(&sb, "{")
|
fmt.Fprintf(&sb, "{")
|
||||||
sep := ""
|
sep := ""
|
||||||
for attachesTo, as := range rs.resolutions {
|
for attachesTo, as := range rs {
|
||||||
fmt.Fprintf(&sb, "%s%s -> %s", sep, attachesTo.name, as.String())
|
fmt.Fprintf(&sb, "%s%s -> %s", sep, attachesTo.Name(), as.String())
|
||||||
sep = ", "
|
sep = ", "
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&sb, "}")
|
fmt.Fprintf(&sb, "}")
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AttachesTo identifies the list of targets triggering action to resolve
|
// ActionSet identifies a set of targets to act on and the license conditions
|
||||||
// conditions. (unordered)
|
// the action will resolve.
|
||||||
func (rs *ResolutionSet) AttachesTo() TargetNodeList {
|
type ActionSet map[*TargetNode]LicenseConditionSet
|
||||||
targets := make(TargetNodeList, 0, len(rs.resolutions))
|
|
||||||
for attachesTo := range rs.resolutions {
|
|
||||||
targets = append(targets, attachesTo)
|
|
||||||
}
|
|
||||||
return targets
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActsOn identifies the list of targets to act on (share, give notice etc.)
|
// String returns a human-readable string representation of the set.
|
||||||
// to resolve conditions. (unordered)
|
func (as ActionSet) String() string {
|
||||||
func (rs *ResolutionSet) ActsOn() TargetNodeList {
|
var sb strings.Builder
|
||||||
tset := make(map[*TargetNode]bool)
|
fmt.Fprintf(&sb, "{")
|
||||||
for _, as := range rs.resolutions {
|
sep := ""
|
||||||
for actsOn := range as {
|
|
||||||
tset[actsOn] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
targets := make(TargetNodeList, 0, len(tset))
|
|
||||||
for target := range tset {
|
|
||||||
targets = append(targets, target)
|
|
||||||
}
|
|
||||||
return targets
|
|
||||||
}
|
|
||||||
|
|
||||||
// Origins identifies the list of targets originating conditions to resolve.
|
|
||||||
// (unordered)
|
|
||||||
func (rs *ResolutionSet) Origins() TargetNodeList {
|
|
||||||
tset := make(map[*TargetNode]bool)
|
|
||||||
for _, as := range rs.resolutions {
|
|
||||||
for _, cs := range as {
|
|
||||||
for _, origins := range cs.conditions {
|
|
||||||
for origin := range origins {
|
|
||||||
tset[origin] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
targets := make(TargetNodeList, 0, len(tset))
|
|
||||||
for target := range tset {
|
|
||||||
targets = append(targets, target)
|
|
||||||
}
|
|
||||||
return targets
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolutions returns the list of resolutions that `attachedTo`
|
|
||||||
// target must resolve. Returns empty list if no conditions apply.
|
|
||||||
//
|
|
||||||
// Panics if `attachedTo` does not appear in the set.
|
|
||||||
func (rs *ResolutionSet) Resolutions(attachedTo *TargetNode) ResolutionList {
|
|
||||||
as, ok := rs.resolutions[attachedTo]
|
|
||||||
if !ok {
|
|
||||||
return ResolutionList{}
|
|
||||||
}
|
|
||||||
result := make(ResolutionList, 0, len(as))
|
|
||||||
for actsOn, cs := range as {
|
for actsOn, cs := range as {
|
||||||
result = append(result, Resolution{attachedTo, actsOn, cs.Copy()})
|
fmt.Fprintf(&sb, "%s%s%s", sep, actsOn.Name(), cs.String())
|
||||||
}
|
sep = ", "
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolutionsByActsOn returns the list of resolutions that must `actOn` to
|
|
||||||
// resolvee. Returns empty list if no conditions apply.
|
|
||||||
//
|
|
||||||
// Panics if `actOn` does not appear in the set.
|
|
||||||
func (rs *ResolutionSet) ResolutionsByActsOn(actOn *TargetNode) ResolutionList {
|
|
||||||
c := 0
|
|
||||||
for _, as := range rs.resolutions {
|
|
||||||
if _, ok := as[actOn]; ok {
|
|
||||||
c++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result := make(ResolutionList, 0, c)
|
|
||||||
for attachedTo, as := range rs.resolutions {
|
|
||||||
if cs, ok := as[actOn]; ok {
|
|
||||||
result = append(result, Resolution{attachedTo, actOn, cs.Copy()})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttachesToByOrigin identifies the list of targets requiring action to
|
|
||||||
// resolve conditions originating at `origin`. (unordered)
|
|
||||||
func (rs *ResolutionSet) AttachesToByOrigin(origin *TargetNode) TargetNodeList {
|
|
||||||
tset := make(map[*TargetNode]bool)
|
|
||||||
for attachesTo, as := range rs.resolutions {
|
|
||||||
for _, cs := range as {
|
|
||||||
if cs.HasAnyByOrigin(origin) {
|
|
||||||
tset[attachesTo] = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
targets := make(TargetNodeList, 0, len(tset))
|
|
||||||
for target := range tset {
|
|
||||||
targets = append(targets, target)
|
|
||||||
}
|
|
||||||
return targets
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttachesToTarget returns true if the set contains conditions that
|
|
||||||
// are `attachedTo`.
|
|
||||||
func (rs *ResolutionSet) AttachesToTarget(attachedTo *TargetNode) bool {
|
|
||||||
_, isPresent := rs.resolutions[attachedTo]
|
|
||||||
return isPresent
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyByNameAttachToTarget returns true if the set contains conditions matching
|
|
||||||
// `names` that attach to `attachedTo`.
|
|
||||||
func (rs *ResolutionSet) AnyByNameAttachToTarget(attachedTo *TargetNode, names ...ConditionNames) bool {
|
|
||||||
as, isPresent := rs.resolutions[attachedTo]
|
|
||||||
if !isPresent {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, cs := range as {
|
|
||||||
for _, cn := range names {
|
|
||||||
for _, name := range cn {
|
|
||||||
_, isPresent = cs.conditions[name]
|
|
||||||
if isPresent {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllByNameAttachTo returns true if the set contains at least one condition
|
|
||||||
// matching each element of `names` for `attachedTo`.
|
|
||||||
func (rs *ResolutionSet) AllByNameAttachToTarget(attachedTo *TargetNode, names ...ConditionNames) bool {
|
|
||||||
as, isPresent := rs.resolutions[attachedTo]
|
|
||||||
if !isPresent {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, cn := range names {
|
|
||||||
found := false
|
|
||||||
asloop:
|
|
||||||
for _, cs := range as {
|
|
||||||
for _, name := range cn {
|
|
||||||
_, isPresent = cs.conditions[name]
|
|
||||||
if isPresent {
|
|
||||||
found = true
|
|
||||||
break asloop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns true if the set contains no conditions to resolve.
|
|
||||||
func (rs *ResolutionSet) IsEmpty() bool {
|
|
||||||
for _, as := range rs.resolutions {
|
|
||||||
if !as.isEmpty() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// compliance-only ResolutionSet methods
|
|
||||||
|
|
||||||
// newResolutionSet constructs a new, empty instance of resolutionSetImp for graph `lg`.
|
|
||||||
func newResolutionSet() *ResolutionSet {
|
|
||||||
return &ResolutionSet{make(map[*TargetNode]actionSet)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addConditions attaches all of the license conditions in `as` to `attachTo` to act on the originating node if not already applied.
|
|
||||||
func (rs *ResolutionSet) addConditions(attachTo *TargetNode, as actionSet) {
|
|
||||||
_, ok := rs.resolutions[attachTo]
|
|
||||||
if !ok {
|
|
||||||
rs.resolutions[attachTo] = as.copy()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rs.resolutions[attachTo].addSet(as)
|
|
||||||
}
|
|
||||||
|
|
||||||
// add attaches all of the license conditions in `as` to `attachTo` to act on `attachTo` if not already applied.
|
|
||||||
func (rs *ResolutionSet) addSelf(attachTo *TargetNode, as actionSet) {
|
|
||||||
for _, cs := range as {
|
|
||||||
if cs.IsEmpty() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, ok := rs.resolutions[attachTo]
|
|
||||||
if !ok {
|
|
||||||
rs.resolutions[attachTo] = make(actionSet)
|
|
||||||
}
|
|
||||||
_, ok = rs.resolutions[attachTo][attachTo]
|
|
||||||
if !ok {
|
|
||||||
rs.resolutions[attachTo][attachTo] = newLicenseConditionSet()
|
|
||||||
}
|
|
||||||
rs.resolutions[attachTo][attachTo].AddSet(cs)
|
|
||||||
}
|
}
|
||||||
|
fmt.Fprintf(&sb, "}")
|
||||||
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
@@ -77,113 +77,46 @@ var (
|
|||||||
proprietary = []res{}
|
proprietary = []res{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResolutionSet_JoinResolutionSets(t *testing.T) {
|
func TestResolutionSet_AttachesTo(t *testing.T) {
|
||||||
lg := newLicenseGraph()
|
|
||||||
|
|
||||||
rsNotice := toResolutionSet(lg, notice)
|
|
||||||
rsShare := toResolutionSet(lg, share)
|
|
||||||
rsExpected := toResolutionSet(lg, append(notice, share...))
|
|
||||||
|
|
||||||
rsActual := JoinResolutionSets(rsNotice, rsShare)
|
|
||||||
checkSame(rsActual, rsExpected, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResolutionSet_JoinResolutionsEmpty(t *testing.T) {
|
|
||||||
lg := newLicenseGraph()
|
|
||||||
|
|
||||||
rsShare := toResolutionSet(lg, share)
|
|
||||||
rsProprietary := toResolutionSet(lg, proprietary)
|
|
||||||
rsExpected := toResolutionSet(lg, append(share, proprietary...))
|
|
||||||
|
|
||||||
rsActual := JoinResolutionSets(rsShare, rsProprietary)
|
|
||||||
checkSame(rsActual, rsExpected, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResolutionSet_Origins(t *testing.T) {
|
|
||||||
lg := newLicenseGraph()
|
lg := newLicenseGraph()
|
||||||
|
|
||||||
rsShare := toResolutionSet(lg, share)
|
rsShare := toResolutionSet(lg, share)
|
||||||
|
|
||||||
origins := make([]string, 0)
|
t.Logf("checking resolution set %s", rsShare.String())
|
||||||
for _, target := range rsShare.Origins() {
|
|
||||||
origins = append(origins, target.Name())
|
|
||||||
}
|
|
||||||
sort.Strings(origins)
|
|
||||||
if len(origins) != 2 {
|
|
||||||
t.Errorf("unexpected number of origins: got %v with %d elements, want [\"bin1\", \"bin2\"] with 2 elements", origins, len(origins))
|
|
||||||
}
|
|
||||||
if origins[0] != "bin1" {
|
|
||||||
t.Errorf("unexpected origin at element 0: got %s, want \"bin1\"", origins[0])
|
|
||||||
}
|
|
||||||
if origins[1] != "bin2" {
|
|
||||||
t.Errorf("unexpected origin at element 0: got %s, want \"bin2\"", origins[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResolutionSet_AttachedToTarget(t *testing.T) {
|
actual := rsShare.AttachesTo().Names()
|
||||||
lg := newLicenseGraph()
|
sort.Strings(actual)
|
||||||
|
|
||||||
rsShare := toResolutionSet(lg, share)
|
expected := []string{"bin1", "bin2", "image"}
|
||||||
|
|
||||||
if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
|
t.Logf("actual rsShare: %v", actual)
|
||||||
t.Errorf("unexpected AttachedToTarget(\"binc\"): got true, want false")
|
t.Logf("expected rsShare: %v", expected)
|
||||||
}
|
|
||||||
if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
|
|
||||||
t.Errorf("unexpected AttachedToTarget(\"image\"): got false want true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResolutionSet_AnyByNameAttachToTarget(t *testing.T) {
|
if len(actual) != len(expected) {
|
||||||
lg := newLicenseGraph()
|
t.Errorf("rsShare: wrong number of targets: got %d, want %d", len(actual), len(expected))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < len(actual); i++ {
|
||||||
|
if actual[i] != expected[i] {
|
||||||
|
t.Errorf("rsShare: unexpected target at index %d: got %s, want %s", i, actual[i], expected[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rs := toResolutionSet(lg, bottomUp)
|
rsPrivate := toResolutionSet(lg, proprietary)
|
||||||
|
actual = rsPrivate.AttachesTo().Names()
|
||||||
|
expected = []string{}
|
||||||
|
|
||||||
pandp := ConditionNames{"permissive", "proprietary"}
|
t.Logf("actual rsPrivate: %v", actual)
|
||||||
pandn := ConditionNames{"permissive", "notice"}
|
t.Logf("expected rsPrivate: %v", expected)
|
||||||
p := ConditionNames{"proprietary"}
|
|
||||||
r := ConditionNames{"restricted"}
|
|
||||||
|
|
||||||
if rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), pandp, p) {
|
if len(actual) != len(expected) {
|
||||||
t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"proprietary\", \"permissive\"): want false, got true")
|
t.Errorf("rsPrivate: wrong number of targets: got %d, want %d", len(actual), len(expected))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if !rs.AnyByNameAttachToTarget(newTestNode(lg, "binc"), p) {
|
for i := 0; i < len(actual); i++ {
|
||||||
t.Errorf("unexpected AnyByNameAttachToTarget(\"binc\", \"proprietary\"): want true, got false")
|
if actual[i] != expected[i] {
|
||||||
}
|
t.Errorf("rsPrivate: unexpected target at index %d: got %s, want %s", i, actual[i], expected[i])
|
||||||
if !rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), pandn) {
|
}
|
||||||
t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"permissive\", \"notice\"): want true, got false")
|
|
||||||
}
|
|
||||||
if !rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), r, pandn) {
|
|
||||||
t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"restricted\", \"notice\"): want true, got false")
|
|
||||||
}
|
|
||||||
if !rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), r, p) {
|
|
||||||
t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"restricted\", \"proprietary\"): want true, got false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResolutionSet_AllByNameAttachToTarget(t *testing.T) {
|
|
||||||
lg := newLicenseGraph()
|
|
||||||
|
|
||||||
rs := toResolutionSet(lg, bottomUp)
|
|
||||||
|
|
||||||
pandp := ConditionNames{"permissive", "proprietary"}
|
|
||||||
pandn := ConditionNames{"permissive", "notice"}
|
|
||||||
p := ConditionNames{"proprietary"}
|
|
||||||
r := ConditionNames{"restricted"}
|
|
||||||
|
|
||||||
if rs.AllByNameAttachToTarget(newTestNode(lg, "image"), pandp, p) {
|
|
||||||
t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"proprietary\", \"permissive\"): want false, got true")
|
|
||||||
}
|
|
||||||
if !rs.AllByNameAttachToTarget(newTestNode(lg, "binc"), p) {
|
|
||||||
t.Errorf("unexpected AllByNameAttachToTarget(\"binc\", \"proprietary\"): want true, got false")
|
|
||||||
}
|
|
||||||
if !rs.AllByNameAttachToTarget(newTestNode(lg, "image"), pandn) {
|
|
||||||
t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"notice\"): want true, got false")
|
|
||||||
}
|
|
||||||
if !rs.AllByNameAttachToTarget(newTestNode(lg, "image"), r, pandn) {
|
|
||||||
t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"restricted\", \"notice\"): want true, got false")
|
|
||||||
}
|
|
||||||
if rs.AllByNameAttachToTarget(newTestNode(lg, "image"), r, p) {
|
|
||||||
t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"restricted\", \"proprietary\"): want false, got true")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,10 +125,12 @@ func TestResolutionSet_AttachesToTarget(t *testing.T) {
|
|||||||
|
|
||||||
rsShare := toResolutionSet(lg, share)
|
rsShare := toResolutionSet(lg, share)
|
||||||
|
|
||||||
|
t.Logf("checking resolution set %s", rsShare.String())
|
||||||
|
|
||||||
if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
|
if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
|
||||||
t.Errorf("unexpected hasTarget(\"binc\"): got true, want false")
|
t.Errorf("actual.AttachesToTarget(\"binc\"): got true, want false")
|
||||||
}
|
}
|
||||||
if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
|
if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
|
||||||
t.Errorf("unexpected AttachesToTarget(\"image\"): got false want true")
|
t.Errorf("actual.AttachesToTarget(\"image\"): got false want true")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -86,7 +86,6 @@ license_conditions: "proprietary"
|
|||||||
license_kinds: "legacy_by_exception_only"
|
license_kinds: "legacy_by_exception_only"
|
||||||
license_conditions: "by_exception_only"
|
license_conditions: "by_exception_only"
|
||||||
`
|
`
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -111,23 +110,39 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// toConditionList converts a test data map of condition name to origin names into a ConditionList.
|
|
||||||
func toConditionList(lg *LicenseGraph, conditions map[string][]string) ConditionList {
|
|
||||||
cl := make(ConditionList, 0)
|
|
||||||
for name, origins := range conditions {
|
|
||||||
for _, origin := range origins {
|
|
||||||
cl = append(cl, LicenseCondition{name, newTestNode(lg, origin)})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cl
|
|
||||||
}
|
|
||||||
|
|
||||||
// newTestNode constructs a test node in the license graph.
|
// newTestNode constructs a test node in the license graph.
|
||||||
func newTestNode(lg *LicenseGraph, targetName string) *TargetNode {
|
func newTestNode(lg *LicenseGraph, targetName string) *TargetNode {
|
||||||
if _, ok := lg.targets[targetName]; !ok {
|
if tn, alreadyExists := lg.targets[targetName]; alreadyExists {
|
||||||
lg.targets[targetName] = &TargetNode{name: targetName}
|
return tn
|
||||||
}
|
}
|
||||||
return lg.targets[targetName]
|
tn := &TargetNode{name: targetName}
|
||||||
|
lg.targets[targetName] = tn
|
||||||
|
return tn
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTestCondition constructs a test license condition in the license graph.
|
||||||
|
func newTestCondition(lg *LicenseGraph, targetName string, conditionName string) LicenseCondition {
|
||||||
|
tn := newTestNode(lg, targetName)
|
||||||
|
cl := LicenseConditionSetFromNames(tn, conditionName).AsList()
|
||||||
|
if len(cl) == 0 {
|
||||||
|
panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName))
|
||||||
|
} else if len(cl) != 1 {
|
||||||
|
panic(fmt.Errorf("unexpected multiple conditions from condition name: %q: got %d, want 1", conditionName, len(cl)))
|
||||||
|
}
|
||||||
|
lc := cl[0]
|
||||||
|
tn.licenseConditions = tn.licenseConditions.Plus(lc)
|
||||||
|
return lc
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTestConditionSet constructs a test license condition set in the license graph.
|
||||||
|
func newTestConditionSet(lg *LicenseGraph, targetName string, conditionName []string) LicenseConditionSet {
|
||||||
|
tn := newTestNode(lg, targetName)
|
||||||
|
cs := LicenseConditionSetFromNames(tn, conditionName...)
|
||||||
|
if cs.IsEmpty() {
|
||||||
|
panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName))
|
||||||
|
}
|
||||||
|
tn.licenseConditions = tn.licenseConditions.Union(cs)
|
||||||
|
return cs
|
||||||
}
|
}
|
||||||
|
|
||||||
// testFS implements a test file system (fs.FS) simulated by a map from filename to []byte content.
|
// testFS implements a test file system (fs.FS) simulated by a map from filename to []byte content.
|
||||||
@@ -270,6 +285,21 @@ func toGraph(stderr io.Writer, roots []string, edges []annotated) (*LicenseGraph
|
|||||||
return ReadLicenseGraph(&fs, stderr, roots)
|
return ReadLicenseGraph(&fs, stderr, roots)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// logGraph outputs a representation of the graph to a test log.
|
||||||
|
func logGraph(lg *LicenseGraph, t *testing.T) {
|
||||||
|
t.Logf("license graph:")
|
||||||
|
t.Logf(" targets:")
|
||||||
|
for _, target := range lg.Targets() {
|
||||||
|
t.Logf(" %s%s in package %q", target.Name(), target.LicenseConditions().String(), target.PackageName())
|
||||||
|
}
|
||||||
|
t.Logf(" /targets")
|
||||||
|
t.Logf(" edges:")
|
||||||
|
for _, edge := range lg.Edges() {
|
||||||
|
t.Logf(" %s", edge.String())
|
||||||
|
}
|
||||||
|
t.Logf(" /edges")
|
||||||
|
t.Logf("/license graph")
|
||||||
|
}
|
||||||
|
|
||||||
// byAnnotatedEdge orders edges by target then dep name then annotations.
|
// byAnnotatedEdge orders edges by target then dep name then annotations.
|
||||||
type byAnnotatedEdge []annotated
|
type byAnnotatedEdge []annotated
|
||||||
@@ -296,33 +326,137 @@ func (l byAnnotatedEdge) Less(i, j int) bool {
|
|||||||
return l[i].target < l[j].target
|
return l[i].target < l[j].target
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// act describes test data resolution actions to define test action sets.
|
||||||
|
type act struct {
|
||||||
|
actsOn, origin, condition string
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human-readable string representing the test action.
|
||||||
|
func (a act) String() string {
|
||||||
|
return fmt.Sprintf("%s{%s:%s}", a.actsOn, a.origin, a.condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toActionSet converts a list of act test data into a test action set.
|
||||||
|
func toActionSet(lg *LicenseGraph, data []act) ActionSet {
|
||||||
|
as := make(ActionSet)
|
||||||
|
for _, a := range data {
|
||||||
|
actsOn := newTestNode(lg, a.actsOn)
|
||||||
|
cs := newTestConditionSet(lg, a.origin, strings.Split(a.condition, "|"))
|
||||||
|
as[actsOn] = cs
|
||||||
|
}
|
||||||
|
return as
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// toResolutionSet converts a list of res test data into a test resolution set.
|
// toResolutionSet converts a list of res test data into a test resolution set.
|
||||||
func toResolutionSet(lg *LicenseGraph, data []res) *ResolutionSet {
|
func toResolutionSet(lg *LicenseGraph, data []res) ResolutionSet {
|
||||||
rmap := make(map[*TargetNode]actionSet)
|
rmap := make(ResolutionSet)
|
||||||
for _, r := range data {
|
for _, r := range data {
|
||||||
attachesTo := newTestNode(lg, r.attachesTo)
|
attachesTo := newTestNode(lg, r.attachesTo)
|
||||||
actsOn := newTestNode(lg, r.actsOn)
|
actsOn := newTestNode(lg, r.actsOn)
|
||||||
origin := newTestNode(lg, r.origin)
|
|
||||||
if _, ok := rmap[attachesTo]; !ok {
|
if _, ok := rmap[attachesTo]; !ok {
|
||||||
rmap[attachesTo] = make(actionSet)
|
rmap[attachesTo] = make(ActionSet)
|
||||||
}
|
}
|
||||||
if _, ok := rmap[attachesTo][actsOn]; !ok {
|
cs := newTestConditionSet(lg, r.origin, strings.Split(r.condition, ":"))
|
||||||
rmap[attachesTo][actsOn] = newLicenseConditionSet()
|
rmap[attachesTo][actsOn] |= cs
|
||||||
}
|
|
||||||
rmap[attachesTo][actsOn].add(origin, r.condition)
|
|
||||||
}
|
}
|
||||||
return &ResolutionSet{rmap}
|
return rmap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tcond associates a target name with '|' separated string conditions.
|
||||||
|
type tcond struct {
|
||||||
|
target, conditions string
|
||||||
|
}
|
||||||
|
|
||||||
|
// action represents a single element of an ActionSet for testing.
|
||||||
|
type action struct {
|
||||||
|
target *TargetNode
|
||||||
|
cs LicenseConditionSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human-readable string representation of the action.
|
||||||
|
func (a action) String() string {
|
||||||
|
return fmt.Sprintf("%s%s", a.target.Name(), a.cs.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// actionList represents an array of actions and a total order defined by
|
||||||
|
// target name followed by license condition set.
|
||||||
|
type actionList []action
|
||||||
|
|
||||||
|
// String returns a human-readable string representation of the list.
|
||||||
|
func (l actionList) String() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
fmt.Fprintf(&sb, "[")
|
||||||
|
sep := ""
|
||||||
|
for _, a := range l {
|
||||||
|
fmt.Fprintf(&sb, "%s%s", sep, a.String())
|
||||||
|
sep = ", "
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&sb, "]")
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the count of elements in the slice.
|
||||||
|
func (l actionList) Len() int { return len(l) }
|
||||||
|
|
||||||
|
// Swap rearranges 2 elements of the slice so that each occupies the other's
|
||||||
|
// former position.
|
||||||
|
func (l actionList) 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 actionList) Less(i, j int) bool {
|
||||||
|
if l[i].target == l[j].target {
|
||||||
|
return l[i].cs < l[j].cs
|
||||||
|
}
|
||||||
|
return l[i].target.Name() < l[j].target.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
// asActionList represents the resolved license conditions in a license graph
|
||||||
|
// as an actionList for comparison in a test.
|
||||||
|
func asActionList(lg *LicenseGraph) actionList {
|
||||||
|
result := make(actionList, 0, len(lg.targets))
|
||||||
|
for _, target := range lg.targets {
|
||||||
|
cs := target.resolution
|
||||||
|
if cs.IsEmpty() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, action{target, cs})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// toActionList converts an array of tcond into an actionList for comparison
|
||||||
|
// in a test.
|
||||||
|
func toActionList(lg *LicenseGraph, actions []tcond) actionList {
|
||||||
|
result := make(actionList, 0, len(actions))
|
||||||
|
for _, actn := range actions {
|
||||||
|
target := newTestNode(lg, actn.target)
|
||||||
|
cs := NewLicenseConditionSet()
|
||||||
|
for _, name := range strings.Split(actn.conditions, "|") {
|
||||||
|
lc, ok := RecognizedConditionNames[name]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("Unrecognized test condition name: %q", name))
|
||||||
|
}
|
||||||
|
cs = cs.Plus(lc)
|
||||||
|
}
|
||||||
|
result = append(result, action{target, cs})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// confl defines test data for a SourceSharePrivacyConflict as a target name,
|
||||||
|
// source condition name, privacy condition name triple.
|
||||||
type confl struct {
|
type confl struct {
|
||||||
sourceNode, share, privacy string
|
sourceNode, share, privacy string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// toConflictList converts confl test data into an array of
|
||||||
|
// SourceSharePrivacyConflict for comparison in a test.
|
||||||
func toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict {
|
func toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict {
|
||||||
result := make([]SourceSharePrivacyConflict, 0, len(data))
|
result := make([]SourceSharePrivacyConflict, 0, len(data))
|
||||||
for _, c := range data {
|
for _, c := range data {
|
||||||
@@ -334,30 +468,40 @@ func toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict
|
|||||||
cprivacy := fields[1]
|
cprivacy := fields[1]
|
||||||
result = append(result, SourceSharePrivacyConflict{
|
result = append(result, SourceSharePrivacyConflict{
|
||||||
newTestNode(lg, c.sourceNode),
|
newTestNode(lg, c.sourceNode),
|
||||||
LicenseCondition{cshare, newTestNode(lg, oshare)},
|
newTestCondition(lg, oshare, cshare),
|
||||||
LicenseCondition{cprivacy, newTestNode(lg, oprivacy)},
|
newTestCondition(lg, oprivacy, cprivacy),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result
|
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 := make(ResolutionSet)
|
||||||
rsExpected := ResolutionSet{make(map[*TargetNode]actionSet)}
|
rsExpected := make(ResolutionSet)
|
||||||
testNode := newTestNode(lg, "test")
|
testNode := newTestNode(lg, "test")
|
||||||
rsActual.resolutions[testNode] = asActual
|
rsActual[testNode] = asActual
|
||||||
rsExpected.resolutions[testNode] = asExpected
|
rsExpected[testNode] = asExpected
|
||||||
checkSame(&rsActual, &rsExpected, t)
|
checkSame(rsActual, rsExpected, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkSame compares an actual resolution set to an expected resolution set for a test.
|
// checkSame compares an actual resolution set to an expected resolution set for a test.
|
||||||
func checkSame(rsActual, rsExpected *ResolutionSet, t *testing.T) {
|
func checkSame(rsActual, rsExpected ResolutionSet, t *testing.T) {
|
||||||
|
t.Logf("actual resolution set: %s", rsActual.String())
|
||||||
|
t.Logf("expected resolution set: %s", rsExpected.String())
|
||||||
|
|
||||||
|
actualTargets := rsActual.AttachesTo()
|
||||||
|
sort.Sort(actualTargets)
|
||||||
|
|
||||||
expectedTargets := rsExpected.AttachesTo()
|
expectedTargets := rsExpected.AttachesTo()
|
||||||
sort.Sort(expectedTargets)
|
sort.Sort(expectedTargets)
|
||||||
|
|
||||||
|
t.Logf("actual targets: %s", actualTargets.String())
|
||||||
|
t.Logf("expected targets: %s", expectedTargets.String())
|
||||||
|
|
||||||
for _, target := range expectedTargets {
|
for _, target := range expectedTargets {
|
||||||
if !rsActual.AttachesToTarget(target) {
|
if !rsActual.AttachesToTarget(target) {
|
||||||
t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false in %s, want true in %s", target.name, rsActual, rsExpected)
|
t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false, want true", target.name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
expectedRl := rsExpected.Resolutions(target)
|
expectedRl := rsExpected.Resolutions(target)
|
||||||
@@ -365,8 +509,8 @@ func checkSame(rsActual, rsExpected *ResolutionSet, t *testing.T) {
|
|||||||
actualRl := rsActual.Resolutions(target)
|
actualRl := rsActual.Resolutions(target)
|
||||||
sort.Sort(actualRl)
|
sort.Sort(actualRl)
|
||||||
if len(expectedRl) != len(actualRl) {
|
if len(expectedRl) != len(actualRl) {
|
||||||
t.Errorf("unexpected number of resolutions attach to %q: got %s with %d elements, want %s with %d elements",
|
t.Errorf("unexpected number of resolutions attach to %q: %d elements, %d elements",
|
||||||
target.name, actualRl, len(actualRl), expectedRl, len(expectedRl))
|
target.name, len(actualRl), len(expectedRl))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for i := 0; i < len(expectedRl); i++ {
|
for i := 0; i < len(expectedRl); i++ {
|
||||||
@@ -375,34 +519,86 @@ func checkSame(rsActual, rsExpected *ResolutionSet, t *testing.T) {
|
|||||||
target.name, i, actualRl[i].asString(), expectedRl[i].asString())
|
target.name, i, actualRl[i].asString(), expectedRl[i].asString())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
expectedConditions := expectedRl[i].Resolves().AsList()
|
expectedConditions := expectedRl[i].Resolves()
|
||||||
actualConditions := actualRl[i].Resolves().AsList()
|
actualConditions := actualRl[i].Resolves()
|
||||||
sort.Sort(expectedConditions)
|
if expectedConditions != actualConditions {
|
||||||
sort.Sort(actualConditions)
|
t.Errorf("unexpected conditions apply to %q acting on %q: got %04x with names %s, want %04x with names %s",
|
||||||
if len(expectedConditions) != len(actualConditions) {
|
|
||||||
t.Errorf("unexpected number of conditions apply to %q acting on %q: got %s with %d elements, want %s with %d elements",
|
|
||||||
target.name, expectedRl[i].actsOn.name,
|
target.name, expectedRl[i].actsOn.name,
|
||||||
actualConditions, len(actualConditions),
|
actualConditions, actualConditions.Names(),
|
||||||
expectedConditions, len(expectedConditions))
|
expectedConditions, expectedConditions.Names())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for j := 0; j < len(expectedConditions); j++ {
|
|
||||||
if expectedConditions[j] != actualConditions[j] {
|
|
||||||
t.Errorf("unexpected condition attached to %q acting on %q at index %d: got %s at index %d in %s, want %s in %s",
|
|
||||||
target.name, expectedRl[i].actsOn.name, i,
|
|
||||||
actualConditions[j].asString(":"), j, actualConditions,
|
|
||||||
expectedConditions[j].asString(":"), expectedConditions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
actualTargets := rsActual.AttachesTo()
|
for _, target := range actualTargets {
|
||||||
sort.Sort(actualTargets)
|
|
||||||
for i, target := range actualTargets {
|
|
||||||
if !rsExpected.AttachesToTarget(target) {
|
if !rsExpected.AttachesToTarget(target) {
|
||||||
t.Errorf("unexpected target: got %q element %d in AttachesTo() %s with %d elements in %s, want %s with %d elements in %s",
|
t.Errorf("unexpected extra target: got expected.AttachesTo(%q) is false, want true", target.name)
|
||||||
target.name, i, actualTargets, len(actualTargets), rsActual, expectedTargets, len(expectedTargets), rsExpected)
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkResolvesActions compares an actual action set to an expected action set for a test verifying the actual set
|
||||||
|
// resolves all of the expected conditions.
|
||||||
|
func checkResolvesActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) {
|
||||||
|
rsActual := make(ResolutionSet)
|
||||||
|
rsExpected := make(ResolutionSet)
|
||||||
|
testNode := newTestNode(lg, "test")
|
||||||
|
rsActual[testNode] = asActual
|
||||||
|
rsExpected[testNode] = asExpected
|
||||||
|
checkResolves(rsActual, rsExpected, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkResolves compares an actual resolution set to an expected resolution set for a test verifying the actual set
|
||||||
|
// resolves all of the expected conditions.
|
||||||
|
func checkResolves(rsActual, rsExpected ResolutionSet, t *testing.T) {
|
||||||
|
t.Logf("actual resolution set: %s", rsActual.String())
|
||||||
|
t.Logf("expected resolution set: %s", rsExpected.String())
|
||||||
|
|
||||||
|
actualTargets := rsActual.AttachesTo()
|
||||||
|
sort.Sort(actualTargets)
|
||||||
|
|
||||||
|
expectedTargets := rsExpected.AttachesTo()
|
||||||
|
sort.Sort(expectedTargets)
|
||||||
|
|
||||||
|
t.Logf("actual targets: %s", actualTargets.String())
|
||||||
|
t.Logf("expected targets: %s", expectedTargets.String())
|
||||||
|
|
||||||
|
for _, target := range expectedTargets {
|
||||||
|
if !rsActual.AttachesToTarget(target) {
|
||||||
|
t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false, want true", target.name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
expectedRl := rsExpected.Resolutions(target)
|
||||||
|
sort.Sort(expectedRl)
|
||||||
|
actualRl := rsActual.Resolutions(target)
|
||||||
|
sort.Sort(actualRl)
|
||||||
|
if len(expectedRl) != len(actualRl) {
|
||||||
|
t.Errorf("unexpected number of resolutions attach to %q: %d elements, %d elements",
|
||||||
|
target.name, len(actualRl), len(expectedRl))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for i := 0; i < len(expectedRl); i++ {
|
||||||
|
if expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name {
|
||||||
|
t.Errorf("unexpected resolution attaches to %q at index %d: got %s, want %s",
|
||||||
|
target.name, i, actualRl[i].asString(), expectedRl[i].asString())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
expectedConditions := expectedRl[i].Resolves()
|
||||||
|
actualConditions := actualRl[i].Resolves()
|
||||||
|
if expectedConditions != (expectedConditions & actualConditions) {
|
||||||
|
t.Errorf("expected conditions missing from %q acting on %q: got %04x with names %s, want %04x with names %s",
|
||||||
|
target.name, expectedRl[i].actsOn.name,
|
||||||
|
actualConditions, actualConditions.Names(),
|
||||||
|
expectedConditions, expectedConditions.Names())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
for _, target := range actualTargets {
|
||||||
|
if !rsExpected.AttachesToTarget(target) {
|
||||||
|
t.Errorf("unexpected extra target: got expected.AttachesTo(%q) is false, want true", target.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user