-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(nodesets): refactored and added tests (#140)
- Loading branch information
Showing
4 changed files
with
428 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package yamlbasics | ||
|
||
import ( | ||
"github.com/kong/go-apiops/logbasics" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
// | ||
// | ||
// NodeSet implementation, just a list of yaml nodes | ||
// | ||
// | ||
|
||
// represents a set of yaml nodes | ||
type NodeSet []*yaml.Node | ||
|
||
// Intersection returns the intersection of the two sets of nodes. | ||
// nil entries will be ignored. The result will a copy and have no duplicates. | ||
// The second return value is the remainder of set2 after the intersection was removed (also a copy). | ||
func (mainSet *NodeSet) Intersection(set2 NodeSet) (intersection NodeSet, remainder NodeSet) { | ||
if len(*mainSet) == 0 || len(set2) == 0 { | ||
intersection := make(NodeSet, 0) | ||
remainder := make(NodeSet, len(set2)) | ||
copy(remainder, set2) | ||
return intersection, remainder | ||
} | ||
|
||
// deduplicate | ||
seen1 := make(map[*yaml.Node]bool) | ||
for _, node := range *mainSet { | ||
if node != nil { | ||
seen1[node] = true | ||
} | ||
} | ||
|
||
intersection = make(NodeSet, 0) | ||
remainder = make(NodeSet, 0) | ||
seen2 := make(map[*yaml.Node]bool) | ||
for _, node := range set2 { | ||
if node != nil && !seen2[node] { | ||
seen2[node] = true | ||
if seen1[node] { | ||
intersection = append(intersection, node) | ||
} else { | ||
remainder = append(remainder, node) | ||
} | ||
} | ||
} | ||
logbasics.Debug("intersection", "#found", len(intersection), "#remainder", len(remainder)) | ||
return intersection, remainder | ||
} | ||
|
||
// IsIntersection returns true if all nodes in the subset also appear in the main set. | ||
// nil entries will be ignored. Returns true if subset is empty. | ||
func (mainSet *NodeSet) IsIntersection(subset NodeSet) bool { | ||
_, remainder := mainSet.Intersection(subset) | ||
return len(remainder) == 0 | ||
} | ||
|
||
// Subtract returns the set of nodes that are in mainSet but not in setToSubtract. | ||
// nil entries will be ignored. The result will have no duplicates. | ||
func (mainSet *NodeSet) Subtract(setToSubtract NodeSet) NodeSet { | ||
_, remainder := setToSubtract.Intersection(*mainSet) | ||
return remainder | ||
} | ||
|
||
// Union returns the union of the two (or more) sets of nodes. | ||
// nil entries will be ignored. The result will have no duplicates. | ||
func (mainSet *NodeSet) Union(sets ...NodeSet) NodeSet { | ||
union := make(NodeSet, 0) | ||
sets = append([]NodeSet{*mainSet}, sets...) | ||
|
||
seen := make(map[*yaml.Node]bool) | ||
for _, nodeset := range sets { | ||
for _, node := range nodeset { | ||
if node != nil && !seen[node] { | ||
seen[node] = true | ||
union = append(union, node) | ||
} | ||
} | ||
} | ||
|
||
return union | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
package yamlbasics_test | ||
|
||
import ( | ||
. "github.com/kong/go-apiops/yamlbasics" | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
var _ = Describe("NodeSet", func() { | ||
node1 := yaml.Node{} | ||
node2 := yaml.Node{} | ||
node3 := yaml.Node{} | ||
node4 := yaml.Node{} | ||
|
||
set1 := NodeSet{&node1, &node2} | ||
set2 := NodeSet{&node2, &node3} // overlaps with set1 and set3 | ||
set3 := NodeSet{&node3, &node4} | ||
set4 := NodeSet{&node4, &node4} // has duplicates | ||
setEmpty := NodeSet{} | ||
|
||
Describe("Intersection", func() { | ||
Context("when the mainset is empty", func() { | ||
It("should return an empty set", func() { | ||
intersection, remainder := setEmpty.Intersection(set1) | ||
// should be a copy | ||
Expect(remainder).ToNot(BeIdenticalTo(set1)) | ||
Expect(remainder).ToNot(BeIdenticalTo(setEmpty)) | ||
Expect(intersection).ToNot(BeIdenticalTo(set1)) | ||
Expect(intersection).ToNot(BeIdenticalTo(setEmpty)) | ||
// check results | ||
Expect(intersection).To(BeEmpty()) | ||
Expect(remainder).To(BeEquivalentTo(set1)) | ||
}) | ||
}) | ||
Context("when the subset is empty", func() { | ||
It("should return an empty set", func() { | ||
intersection, remainder := set1.Intersection(setEmpty) | ||
// should be a copy | ||
Expect(remainder).ToNot(BeIdenticalTo(set1)) | ||
Expect(remainder).ToNot(BeIdenticalTo(setEmpty)) | ||
Expect(intersection).ToNot(BeIdenticalTo(set1)) | ||
Expect(intersection).ToNot(BeIdenticalTo(setEmpty)) | ||
// check results | ||
Expect(intersection).To(BeEmpty()) | ||
Expect(remainder).To(BeEmpty()) | ||
}) | ||
}) | ||
Context("when the mainset and subset are not empty", func() { | ||
It("should return the intersection and the remainder", func() { | ||
intersection, remainder := set1.Intersection(set2) | ||
// should be a copy | ||
Expect(remainder).ToNot(BeIdenticalTo(set1)) | ||
Expect(remainder).ToNot(BeIdenticalTo(set2)) | ||
Expect(intersection).ToNot(BeIdenticalTo(set1)) | ||
Expect(intersection).ToNot(BeIdenticalTo(set2)) | ||
// check results | ||
Expect(intersection).To(BeEquivalentTo(NodeSet{&node2})) | ||
Expect(remainder).To(BeEquivalentTo(NodeSet{&node1})) | ||
}) | ||
}) | ||
Context("when the mainset and subset have no overlap", func() { | ||
It("should return an empty list and the remainder", func() { | ||
intersection, remainder := set1.Intersection(set3) | ||
// should be a copy | ||
Expect(remainder).ToNot(BeIdenticalTo(set1)) | ||
Expect(remainder).ToNot(BeIdenticalTo(set3)) | ||
Expect(intersection).ToNot(BeIdenticalTo(set1)) | ||
Expect(intersection).ToNot(BeIdenticalTo(set3)) | ||
// check results | ||
Expect(intersection).To(BeEquivalentTo(NodeSet{})) | ||
Expect(remainder).To(BeEquivalentTo(set3)) | ||
}) | ||
}) | ||
Context("when the mainset and subset are not empty and subset has duplicates", func() { | ||
It("should return the intersection and the remainder", func() { | ||
intersection, remainder := set3.Intersection(set4) | ||
// should be a copy | ||
Expect(remainder).ToNot(BeIdenticalTo(set3)) | ||
Expect(remainder).ToNot(BeIdenticalTo(set4)) | ||
Expect(intersection).ToNot(BeIdenticalTo(set3)) | ||
Expect(intersection).ToNot(BeIdenticalTo(set4)) | ||
// check results | ||
Expect(intersection).To(BeEquivalentTo(NodeSet{&node4})) | ||
Expect(remainder).To(BeEquivalentTo(NodeSet{})) | ||
}) | ||
}) | ||
Context("when the mainset and subset are not empty and mainset has duplicates", func() { | ||
It("should return the intersection and the remainder", func() { | ||
intersection, remainder := set4.Intersection(set3) | ||
// should be a copy | ||
Expect(remainder).ToNot(BeIdenticalTo(set3)) | ||
Expect(remainder).ToNot(BeIdenticalTo(set4)) | ||
Expect(intersection).ToNot(BeIdenticalTo(set3)) | ||
Expect(intersection).ToNot(BeIdenticalTo(set4)) | ||
// check results | ||
Expect(intersection).To(BeEquivalentTo(NodeSet{&node4})) | ||
Expect(remainder).To(BeEquivalentTo(NodeSet{&node3})) | ||
}) | ||
}) | ||
}) | ||
Describe("IsIntersection", func() { | ||
Context("when the mainset is empty", func() { | ||
It("should return false", func() { | ||
Expect(setEmpty.IsIntersection(set1)).To(BeFalse()) | ||
}) | ||
}) | ||
Context("when the subset is empty", func() { | ||
It("should return true", func() { | ||
Expect(set1.IsIntersection(setEmpty)).To(BeTrue()) | ||
}) | ||
}) | ||
Context("when the mainset and subset are empty", func() { | ||
It("should return true", func() { | ||
Expect(setEmpty.IsIntersection(setEmpty)).To(BeTrue()) | ||
}) | ||
}) | ||
Context("when the mainset and subset are not empty", func() { | ||
It("should return true", func() { | ||
Expect(set3.IsIntersection(set4)).To(BeTrue()) | ||
}) | ||
}) | ||
Context("when the mainset and subset have no overlap", func() { | ||
It("should return false", func() { | ||
Expect(set1.IsIntersection(set3)).To(BeFalse()) | ||
}) | ||
}) | ||
Context("when the mainset and subset are not empty and subset has duplicates", func() { | ||
It("should return true", func() { | ||
Expect(set3.IsIntersection(set4)).To(BeTrue()) | ||
}) | ||
}) | ||
Context("when the mainset and subset are not empty and mainset has duplicates", func() { | ||
It("should return true", func() { | ||
Expect(set4.IsIntersection(NodeSet{&node4})).To(BeTrue()) | ||
}) | ||
}) | ||
}) | ||
Describe("Union", func() { | ||
Context("when the mainset is empty", func() { | ||
It("should return a copy of the given set", func() { | ||
union := setEmpty.Union(set1) | ||
// should be a copy | ||
Expect(union).ToNot(BeIdenticalTo(set1)) | ||
Expect(union).ToNot(BeIdenticalTo(setEmpty)) | ||
// check results | ||
Expect(union).To(BeEquivalentTo(set1)) | ||
}) | ||
}) | ||
Context("when the given set is empty", func() { | ||
It("should return a copy of the mainset", func() { | ||
union := set1.Union(setEmpty) | ||
// should be a copy | ||
Expect(union).ToNot(BeIdenticalTo(set1)) | ||
Expect(union).ToNot(BeIdenticalTo(setEmpty)) | ||
// check results | ||
Expect(union).To(BeEquivalentTo(set1)) | ||
}) | ||
}) | ||
Context("when the mainset and given set are empty", func() { | ||
It("should return an empty list", func() { | ||
union := setEmpty.Union(setEmpty) | ||
// should be a copy | ||
Expect(union).ToNot(BeIdenticalTo(setEmpty)) | ||
// check results | ||
Expect(union).To(BeEquivalentTo(setEmpty)) | ||
}) | ||
}) | ||
Context("when the mainset and subset are not empty", func() { | ||
It("should return the union", func() { | ||
union := set1.Union(set2) | ||
// should be a copy | ||
Expect(union).ToNot(BeIdenticalTo(set1)) | ||
Expect(union).ToNot(BeIdenticalTo(set2)) | ||
// check results | ||
Expect(union).To(BeEquivalentTo(NodeSet{&node1, &node2, &node3})) | ||
}) | ||
}) | ||
Context("when the mainset and subset have no overlap", func() { | ||
It("should return the union", func() { | ||
union := set1.Union(set3) | ||
// should be a copy | ||
Expect(union).ToNot(BeIdenticalTo(set1)) | ||
Expect(union).ToNot(BeIdenticalTo(set3)) | ||
// check results | ||
Expect(union).To(BeEquivalentTo(NodeSet{&node1, &node2, &node3, &node4})) | ||
}) | ||
}) | ||
}) | ||
Describe("Subtract", func() { | ||
Context("when the mainset is empty", func() { | ||
It("should return an empty set", func() { | ||
subtract := setEmpty.Subtract(set1) | ||
// should be a copy | ||
Expect(subtract).ToNot(BeIdenticalTo(set1)) | ||
Expect(subtract).ToNot(BeIdenticalTo(setEmpty)) | ||
// check results | ||
Expect(subtract).To(BeEquivalentTo(setEmpty)) | ||
}) | ||
}) | ||
Context("when the given set is empty", func() { | ||
It("should return a copy of the mainset", func() { | ||
subtract := set1.Subtract(setEmpty) | ||
// should be a copy | ||
Expect(subtract).ToNot(BeIdenticalTo(set1)) | ||
Expect(subtract).ToNot(BeIdenticalTo(setEmpty)) | ||
// check results | ||
Expect(subtract).To(BeEquivalentTo(set1)) | ||
}) | ||
}) | ||
Context("when the mainset and given set are empty", func() { | ||
It("should return an empty list", func() { | ||
subtract := setEmpty.Subtract(setEmpty) | ||
// should be a copy | ||
Expect(subtract).ToNot(BeIdenticalTo(setEmpty)) | ||
// check results | ||
Expect(subtract).To(BeEquivalentTo(setEmpty)) | ||
}) | ||
}) | ||
Context("when the mainset and subset are not empty", func() { | ||
It("should return the difference", func() { | ||
subtract := set1.Subtract(set2) | ||
// should be a copy | ||
Expect(subtract).ToNot(BeIdenticalTo(set1)) | ||
Expect(subtract).ToNot(BeIdenticalTo(set2)) | ||
// check results | ||
Expect(subtract).To(BeEquivalentTo(NodeSet{&node1})) | ||
}) | ||
}) | ||
Context("when the mainset and subset have no overlap", func() { | ||
It("should return the difference", func() { | ||
subtract := set1.Subtract(set3) | ||
// should be a copy | ||
Expect(subtract).ToNot(BeIdenticalTo(set1)) | ||
Expect(subtract).ToNot(BeIdenticalTo(set3)) | ||
// check results | ||
Expect(subtract).To(BeEquivalentTo(set1)) | ||
}) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.