Skip to content

Commit

Permalink
chore(nodesets): refactored and added tests (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tieske committed Jan 19, 2024
1 parent ff8e2ca commit 2202b13
Show file tree
Hide file tree
Showing 4 changed files with 428 additions and 97 deletions.
84 changes: 84 additions & 0 deletions yamlbasics/nodeset.go
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
}
241 changes: 241 additions & 0 deletions yamlbasics/nodeset_test.go
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))
})
})
})
})
Loading

0 comments on commit 2202b13

Please sign in to comment.