feat: add directed graph to pkg (#2014)
Signed-off-by: Gaius <gaius.qi@gmail.com>
This commit is contained in:
parent
304448009f
commit
eb65cd76f6
|
|
@ -308,7 +308,7 @@ func (d *dag[T]) depthFirstSearch(fromVertexID, toVertexID string) bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// depthFirstSearch finds successors of vertex.
|
// search finds successors of vertex.
|
||||||
func (d *dag[T]) search(vertexID string, successors map[string]struct{}) {
|
func (d *dag[T]) search(vertexID string, successors map[string]struct{}) {
|
||||||
vertex, ok := d.vertices.Get(vertexID)
|
vertex, ok := d.vertices.Get(vertexID)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -7,7 +7,7 @@ package mocks
|
||||||
import (
|
import (
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
dag "d7y.io/dragonfly/v2/pkg/dag"
|
dag "d7y.io/dragonfly/v2/pkg/graph/dag"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,308 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 The Dragonfly Authors
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//go:generate mockgen -destination mocks/dg_mock.go -source dg.go -package mocks
|
||||||
|
|
||||||
|
package dg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
cmap "github.com/orcaman/concurrent-map/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrVertexNotFound represents vertex not found.
|
||||||
|
ErrVertexNotFound = errors.New("vertex not found")
|
||||||
|
|
||||||
|
// ErrVertexAlreadyExists represents vertex already exists.
|
||||||
|
ErrVertexAlreadyExists = errors.New("vertex already exists")
|
||||||
|
|
||||||
|
// ErrParnetAlreadyExists represents parent of vertex already exists.
|
||||||
|
ErrParnetAlreadyExists = errors.New("parent of vertex already exists")
|
||||||
|
|
||||||
|
// ErrChildAlreadyExists represents child of vertex already exists.
|
||||||
|
ErrChildAlreadyExists = errors.New("child of vertex already exists")
|
||||||
|
|
||||||
|
// ErrCycleBetweenVertices represents cycle between vertices.
|
||||||
|
ErrCycleBetweenVertices = errors.New("cycle between vertices")
|
||||||
|
)
|
||||||
|
|
||||||
|
// DG is the interface used for directed graph.
|
||||||
|
type DG[T comparable] interface {
|
||||||
|
// AddVertex adds vertex to graph.
|
||||||
|
AddVertex(id string, value T) error
|
||||||
|
|
||||||
|
// DeleteVertex deletes vertex graph.
|
||||||
|
DeleteVertex(id string)
|
||||||
|
|
||||||
|
// GetVertex gets vertex from graph.
|
||||||
|
GetVertex(id string) (*Vertex[T], error)
|
||||||
|
|
||||||
|
// GetVertices returns map of vertices.
|
||||||
|
GetVertices() map[string]*Vertex[T]
|
||||||
|
|
||||||
|
// GetRandomVertices returns random map of vertices.
|
||||||
|
GetRandomVertices(n uint) []*Vertex[T]
|
||||||
|
|
||||||
|
// GetVertexKeys returns keys of vertices.
|
||||||
|
GetVertexKeys() []string
|
||||||
|
|
||||||
|
// GetSourceVertices returns source vertices.
|
||||||
|
GetSourceVertices() []*Vertex[T]
|
||||||
|
|
||||||
|
// GetSinkVertices returns sink vertices.
|
||||||
|
GetSinkVertices() []*Vertex[T]
|
||||||
|
|
||||||
|
// VertexCount returns count of vertices.
|
||||||
|
VertexCount() int
|
||||||
|
|
||||||
|
// AddEdge adds edge between two vertices.
|
||||||
|
AddEdge(fromVertexID, toVertexID string) error
|
||||||
|
|
||||||
|
// DeleteEdge deletes edge between two vertices.
|
||||||
|
DeleteEdge(fromVertexID, toVertexID string) error
|
||||||
|
|
||||||
|
// CanAddEdge finds whether there are circles through depth-first search.
|
||||||
|
CanAddEdge(fromVertexID, toVertexID string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// dg provides directed graph function.
|
||||||
|
type dg[T comparable] struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
vertices cmap.ConcurrentMap[string, *Vertex[T]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new DG interface.
|
||||||
|
func NewDG[T comparable]() DG[T] {
|
||||||
|
return &dg[T]{
|
||||||
|
vertices: cmap.New[*Vertex[T]](),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddVertex adds vertex to graph.
|
||||||
|
func (d *dg[T]) AddVertex(id string, value T) error {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
if _, ok := d.vertices.Get(id); ok {
|
||||||
|
return ErrVertexAlreadyExists
|
||||||
|
}
|
||||||
|
|
||||||
|
d.vertices.Set(id, NewVertex(id, value))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVertex deletes vertex graph.
|
||||||
|
func (d *dg[T]) DeleteVertex(id string) {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
vertex, ok := d.vertices.Get(id)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, parent := range vertex.Parents.Values() {
|
||||||
|
parent.Children.Delete(vertex)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range vertex.Children.Values() {
|
||||||
|
child.Parents.Delete(vertex)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
d.vertices.Remove(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVertex gets vertex from graph.
|
||||||
|
func (d *dg[T]) GetVertex(id string) (*Vertex[T], error) {
|
||||||
|
vertex, ok := d.vertices.Get(id)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrVertexNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertex, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVertices returns map of vertices.
|
||||||
|
func (d *dg[T]) GetVertices() map[string]*Vertex[T] {
|
||||||
|
return d.vertices.Items()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRandomVertices returns random map of vertices.
|
||||||
|
func (d *dg[T]) GetRandomVertices(n uint) []*Vertex[T] {
|
||||||
|
d.mu.RLock()
|
||||||
|
defer d.mu.RUnlock()
|
||||||
|
|
||||||
|
keys := d.GetVertexKeys()
|
||||||
|
if int(n) >= len(keys) {
|
||||||
|
n = uint(len(keys))
|
||||||
|
}
|
||||||
|
|
||||||
|
rand.Seed(time.Now().Unix())
|
||||||
|
permutation := rand.Perm(len(keys))[:n]
|
||||||
|
randomVertices := make([]*Vertex[T], 0, n)
|
||||||
|
for _, v := range permutation {
|
||||||
|
key := keys[v]
|
||||||
|
if vertex, err := d.GetVertex(key); err == nil {
|
||||||
|
randomVertices = append(randomVertices, vertex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return randomVertices
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVertexKeys returns keys of vertices.
|
||||||
|
func (d *dg[T]) GetVertexKeys() []string {
|
||||||
|
return d.vertices.Keys()
|
||||||
|
}
|
||||||
|
|
||||||
|
// VertexCount returns count of vertices.
|
||||||
|
func (d *dg[T]) VertexCount() int {
|
||||||
|
return d.vertices.Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanAddEdge finds whether there are circles through depth-first search.
|
||||||
|
func (d *dg[T]) CanAddEdge(fromVertexID, toVertexID string) bool {
|
||||||
|
d.mu.RLock()
|
||||||
|
defer d.mu.RUnlock()
|
||||||
|
|
||||||
|
if fromVertexID == toVertexID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fromVertex, ok := d.vertices.Get(fromVertexID)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := d.vertices.Get(toVertexID); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range fromVertex.Children.Values() {
|
||||||
|
if child.ID == toVertexID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEdge adds edge between two vertices.
|
||||||
|
func (d *dg[T]) AddEdge(fromVertexID, toVertexID string) error {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
if fromVertexID == toVertexID {
|
||||||
|
return ErrCycleBetweenVertices
|
||||||
|
}
|
||||||
|
|
||||||
|
fromVertex, ok := d.vertices.Get(fromVertexID)
|
||||||
|
if !ok {
|
||||||
|
return ErrVertexNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
toVertex, ok := d.vertices.Get(toVertexID)
|
||||||
|
if !ok {
|
||||||
|
return ErrVertexNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range fromVertex.Children.Values() {
|
||||||
|
if child.ID == toVertexID {
|
||||||
|
return ErrCycleBetweenVertices
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := fromVertex.Children.Add(toVertex); !ok {
|
||||||
|
return ErrChildAlreadyExists
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := toVertex.Parents.Add(fromVertex); !ok {
|
||||||
|
return ErrParnetAlreadyExists
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteEdge deletes edge between two vertices.
|
||||||
|
func (d *dg[T]) DeleteEdge(fromVertexID, toVertexID string) error {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
fromVertex, ok := d.vertices.Get(fromVertexID)
|
||||||
|
if !ok {
|
||||||
|
return ErrVertexNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
toVertex, ok := d.vertices.Get(toVertexID)
|
||||||
|
if !ok {
|
||||||
|
return ErrVertexNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
fromVertex.Children.Delete(toVertex)
|
||||||
|
toVertex.Parents.Delete(fromVertex)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSourceVertices returns source vertices.
|
||||||
|
func (d *dg[T]) GetSourceVertices() []*Vertex[T] {
|
||||||
|
d.mu.RLock()
|
||||||
|
defer d.mu.RUnlock()
|
||||||
|
|
||||||
|
var sourceVertices []*Vertex[T]
|
||||||
|
for _, vertex := range d.vertices.Items() {
|
||||||
|
if vertex.InDegree() == 0 {
|
||||||
|
sourceVertices = append(sourceVertices, vertex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceVertices
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSinkVertices returns sink vertices.
|
||||||
|
func (d *dg[T]) GetSinkVertices() []*Vertex[T] {
|
||||||
|
d.mu.RLock()
|
||||||
|
defer d.mu.RUnlock()
|
||||||
|
|
||||||
|
var sinkVertices []*Vertex[T]
|
||||||
|
for _, vertex := range d.vertices.Items() {
|
||||||
|
if vertex.OutDegree() == 0 {
|
||||||
|
sinkVertices = append(sinkVertices, vertex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sinkVertices
|
||||||
|
}
|
||||||
|
|
||||||
|
// search finds successors of vertex.
|
||||||
|
func (d *dg[T]) search(vertexID string, successors map[string]struct{}) {
|
||||||
|
vertex, ok := d.vertices.Get(vertexID)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range vertex.Children.Values() {
|
||||||
|
if _, ok := successors[child.ID]; !ok {
|
||||||
|
successors[child.ID] = struct{}{}
|
||||||
|
d.search(child.ID, successors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,924 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 The Dragonfly Authors
|
||||||
|
*
|
||||||
|
* 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 dg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewDG(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.Equal(reflect.TypeOf(d).Elem().Name(), "dg[string]")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGAddVertex(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
id string
|
||||||
|
value any
|
||||||
|
expect func(t *testing.T, d DG[string], err error)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "add vertex",
|
||||||
|
id: mockVertexID,
|
||||||
|
value: mockVertexValue,
|
||||||
|
expect: func(t *testing.T, d DG[string], err error) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.NoError(err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vertex already exists",
|
||||||
|
id: mockVertexID,
|
||||||
|
value: mockVertexValue,
|
||||||
|
expect: func(t *testing.T, d DG[string], err error) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
assert.EqualError(d.AddVertex(mockVertexID, mockVertexValue), ErrVertexAlreadyExists.Error())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d, d.AddVertex(tc.id, tc.name))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGDeleteVertex(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "delete vertex",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
if err := d.AddVertex(mockVertexID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
d.DeleteVertex(mockVertexID)
|
||||||
|
|
||||||
|
_, err := d.GetVertex(mockVertexID)
|
||||||
|
assert.EqualError(err, ErrVertexNotFound.Error())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "delete vertex with edges",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mockToVertexID = "baz"
|
||||||
|
)
|
||||||
|
if err := d.AddVertex(mockToVertexID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexID, mockToVertexID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.DeleteVertex(mockVertexID)
|
||||||
|
|
||||||
|
_, err := d.GetVertex(mockVertexID)
|
||||||
|
assert.EqualError(err, ErrVertexNotFound.Error())
|
||||||
|
|
||||||
|
vertex, err := d.GetVertex(mockToVertexID)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(vertex.Parents.Len(), uint(0))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGGetVertex(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "get vertex",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
if err := d.AddVertex(mockVertexID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex, err := d.GetVertex(mockVertexID)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(vertex.ID, mockVertexID)
|
||||||
|
assert.Equal(vertex.Value, mockVertexValue)
|
||||||
|
assert.Equal(vertex.Parents.Len(), uint(0))
|
||||||
|
assert.Equal(vertex.Children.Len(), uint(0))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vertex not found",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
_, err := d.GetVertex(mockVertexID)
|
||||||
|
assert.EqualError(err, ErrVertexNotFound.Error())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGVertexVertexCount(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "get length of vertex",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
if err := d.AddVertex(mockVertexID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.VertexCount()
|
||||||
|
assert.Equal(d.VertexCount(), 1)
|
||||||
|
|
||||||
|
d.DeleteVertex(mockVertexID)
|
||||||
|
assert.Equal(d.VertexCount(), 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty dg",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.Equal(d.VertexCount(), 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGGetVertices(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "get vertices",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
if err := d.AddVertex(mockVertexID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices := d.GetVertices()
|
||||||
|
assert.Equal(len(vertices), 1)
|
||||||
|
assert.Equal(vertices[mockVertexID].ID, mockVertexID)
|
||||||
|
assert.Equal(vertices[mockVertexID].Value, mockVertexValue)
|
||||||
|
|
||||||
|
d.DeleteVertex(mockVertexID)
|
||||||
|
vertices = d.GetVertices()
|
||||||
|
assert.Equal(len(vertices), 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dg is empty",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
vertices := d.GetVertices()
|
||||||
|
assert.Equal(len(vertices), 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGGetRandomVertices(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "get random vertices",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
var (
|
||||||
|
mockVertexEID = "bae"
|
||||||
|
mockVertexFID = "baf"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexEID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexFID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices := d.GetRandomVertices(0)
|
||||||
|
assert.Equal(len(vertices), 0)
|
||||||
|
|
||||||
|
vertices = d.GetRandomVertices(1)
|
||||||
|
assert.Equal(len(vertices), 1)
|
||||||
|
|
||||||
|
vertices = d.GetRandomVertices(2)
|
||||||
|
assert.Equal(len(vertices), 2)
|
||||||
|
|
||||||
|
vertices = d.GetRandomVertices(3)
|
||||||
|
assert.Equal(len(vertices), 2)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dg is empty",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
vertices := d.GetRandomVertices(0)
|
||||||
|
assert.Equal(len(vertices), 0)
|
||||||
|
|
||||||
|
vertices = d.GetRandomVertices(1)
|
||||||
|
assert.Equal(len(vertices), 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGGetVertexKeys(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "get keys of vertices",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
if err := d.AddVertex(mockVertexID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := d.GetVertexKeys()
|
||||||
|
assert.Equal(len(keys), 1)
|
||||||
|
assert.Equal(keys[0], mockVertexID)
|
||||||
|
|
||||||
|
d.DeleteVertex(mockVertexID)
|
||||||
|
keys = d.GetVertexKeys()
|
||||||
|
assert.Equal(len(keys), 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGAddEdge(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "add edge",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
var (
|
||||||
|
mockVertexEID = "bae"
|
||||||
|
mockVertexFID = "baf"
|
||||||
|
mockVertexGID = "bag"
|
||||||
|
mockVertexHID = "bah"
|
||||||
|
mockVertexIID = "bai"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexEID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexFID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexGID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexHID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexIID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexEID, mockVertexFID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexFID, mockVertexGID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexFID, mockVertexHID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexGID, mockVertexIID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexIID, mockVertexHID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vertex not found",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
var (
|
||||||
|
mockVertexEID = "bae"
|
||||||
|
mockVertexFID = "baf"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexEID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexEID, mockVertexFID); err != nil {
|
||||||
|
assert.EqualError(err, ErrVertexNotFound.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexFID, mockVertexEID); err != nil {
|
||||||
|
assert.EqualError(err, ErrVertexNotFound.Error())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGCanAddEdge(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "can add edge",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
var (
|
||||||
|
mockVertexEID = "bae"
|
||||||
|
mockVertexFID = "baf"
|
||||||
|
mockVertexGID = "bag"
|
||||||
|
mockVertexHID = "bah"
|
||||||
|
mockVertexIID = "bai"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexEID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexFID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexGID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexHID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexIID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexEID, mockVertexFID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexFID, mockVertexGID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexFID, mockVertexHID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexGID, mockVertexIID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := d.CanAddEdge(mockVertexIID, mockVertexHID)
|
||||||
|
assert.True(ok)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cycle between vertices",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
var (
|
||||||
|
mockVertexEID = "bae"
|
||||||
|
mockVertexFID = "baf"
|
||||||
|
mockVertexGID = "bag"
|
||||||
|
mockVertexHID = "bah"
|
||||||
|
mockVertexIID = "bai"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexEID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexFID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexGID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexHID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexIID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexEID, mockVertexEID); err != nil {
|
||||||
|
assert.EqualError(err, ErrCycleBetweenVertices.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexEID, mockVertexFID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexFID, mockVertexEID); err != nil {
|
||||||
|
assert.EqualError(err, ErrCycleBetweenVertices.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexFID, mockVertexGID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexGID, mockVertexEID); err != nil {
|
||||||
|
assert.EqualError(err, ErrCycleBetweenVertices.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexGID, mockVertexHID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexHID, mockVertexIID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := d.CanAddEdge(mockVertexIID, mockVertexEID)
|
||||||
|
assert.True(ok)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vertex not found",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
var (
|
||||||
|
mockVertexEID = "bae"
|
||||||
|
mockVertexFID = "baf"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexEID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := d.CanAddEdge(mockVertexEID, mockVertexFID)
|
||||||
|
assert.False(ok)
|
||||||
|
ok = d.CanAddEdge(mockVertexFID, mockVertexEID)
|
||||||
|
assert.False(ok)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGDeleteEdge(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "delete edge",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
var (
|
||||||
|
mockVertexEID = "bae"
|
||||||
|
mockVertexFID = "baf"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexEID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexFID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexEID, mockVertexFID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.DeleteEdge(mockVertexFID, mockVertexEID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vf, err := d.GetVertex(mockVertexFID)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(vf.Parents.Len(), uint(1))
|
||||||
|
ve, err := d.GetVertex(mockVertexEID)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(ve.Children.Len(), uint(1))
|
||||||
|
|
||||||
|
if err := d.DeleteEdge(mockVertexEID, mockVertexFID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vf, err = d.GetVertex(mockVertexFID)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(vf.Parents.Len(), uint(0))
|
||||||
|
ve, err = d.GetVertex(mockVertexEID)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(ve.Children.Len(), uint(0))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vertex not found",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
var (
|
||||||
|
mockVertexEID = "bae"
|
||||||
|
mockVertexFID = "baf"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexEID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexEID, mockVertexFID); err != nil {
|
||||||
|
assert.EqualError(err, ErrVertexNotFound.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexFID, mockVertexEID); err != nil {
|
||||||
|
assert.EqualError(err, ErrVertexNotFound.Error())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGSourceVertices(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "get source vertices",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
var (
|
||||||
|
mockVertexEID = "bae"
|
||||||
|
mockVertexFID = "baf"
|
||||||
|
)
|
||||||
|
if err := d.AddVertex(mockVertexEID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexFID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexEID, mockVertexFID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceVertices := d.GetSourceVertices()
|
||||||
|
assert.Equal(len(sourceVertices), 1)
|
||||||
|
assert.Equal(sourceVertices[0].Value, mockVertexValue)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source vertices not found",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
sourceVertices := d.GetSourceVertices()
|
||||||
|
assert.Equal(len(sourceVertices), 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDGSinkVertices(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
expect func(t *testing.T, d DG[string])
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "get sink vertices",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
var (
|
||||||
|
mockVertexEID = "bae"
|
||||||
|
mockVertexFID = "baf"
|
||||||
|
)
|
||||||
|
if err := d.AddVertex(mockVertexEID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddVertex(mockVertexFID, mockVertexValue); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(mockVertexEID, mockVertexFID); err != nil {
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sinkVertices := d.GetSinkVertices()
|
||||||
|
assert.Equal(len(sinkVertices), 1)
|
||||||
|
assert.Equal(sinkVertices[0].Value, mockVertexValue)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sink vertices not found",
|
||||||
|
expect: func(t *testing.T, d DG[string]) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
sinkVertices := d.GetSinkVertices()
|
||||||
|
assert.Equal(len(sinkVertices), 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
tc.expect(t, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDGAddVertex(b *testing.B) {
|
||||||
|
var ids []string
|
||||||
|
d := NewDG[string]()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
ids = append(ids, fmt.Sprint(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for _, id := range ids {
|
||||||
|
if err := d.AddVertex(id, string(id)); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDGDeleteVertex(b *testing.B) {
|
||||||
|
var ids []string
|
||||||
|
d := NewDG[string]()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
id := fmt.Sprint(n)
|
||||||
|
if err := d.AddVertex(id, string(id)); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for _, id := range ids {
|
||||||
|
d.DeleteVertex(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDGGetRandomKeys(b *testing.B) {
|
||||||
|
d := NewDG[string]()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
id := fmt.Sprint(n)
|
||||||
|
if err := d.AddVertex(id, string(id)); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
vertices := d.GetRandomVertices(uint(n))
|
||||||
|
if len(vertices) != n {
|
||||||
|
b.Fatal(errors.New("get random vertices failed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDGDeleteVertexWithMultiEdges(b *testing.B) {
|
||||||
|
var ids []string
|
||||||
|
d := NewDG[string]()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
id := fmt.Sprint(n)
|
||||||
|
if err := d.AddVertex(id, string(id)); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeCount := 5
|
||||||
|
for index, id := range ids {
|
||||||
|
if index+edgeCount > len(ids)-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for n := 1; n < edgeCount; n++ {
|
||||||
|
if err := d.AddEdge(id, ids[index+n]); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for _, id := range ids {
|
||||||
|
d.DeleteVertex(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDGAddEdge(b *testing.B) {
|
||||||
|
var ids []string
|
||||||
|
d := NewDG[string]()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
id := fmt.Sprint(n)
|
||||||
|
if err := d.AddVertex(id, string(id)); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for index, id := range ids {
|
||||||
|
if index < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(id, ids[index-1]); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDGAddEdgeWithMultiEdges(b *testing.B) {
|
||||||
|
var ids []string
|
||||||
|
d := NewDG[string]()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
id := fmt.Sprint(n)
|
||||||
|
if err := d.AddVertex(id, string(id)); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeCount := 5
|
||||||
|
for index, id := range ids {
|
||||||
|
if index+edgeCount > len(ids)-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for n := 1; n < edgeCount; n++ {
|
||||||
|
if err := d.AddEdge(id, ids[index+n]); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for index, id := range ids {
|
||||||
|
if index+edgeCount+1 > len(ids)-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(id, ids[index+edgeCount+1]); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDGDeleteEdge(b *testing.B) {
|
||||||
|
var ids []string
|
||||||
|
d := NewDG[string]()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
id := fmt.Sprint(n)
|
||||||
|
if err := d.AddVertex(id, string(id)); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, id := range ids {
|
||||||
|
if index < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.AddEdge(id, ids[index-1]); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for index, id := range ids {
|
||||||
|
if index < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.DeleteEdge(id, ids[index-1]); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: dg.go
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
dg "d7y.io/dragonfly/v2/pkg/graph/dg"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockDG is a mock of DG interface.
|
||||||
|
type MockDG[T comparable] struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockDGMockRecorder[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockDGMockRecorder is the mock recorder for MockDG.
|
||||||
|
type MockDGMockRecorder[T comparable] struct {
|
||||||
|
mock *MockDG[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockDG creates a new mock instance.
|
||||||
|
func NewMockDG[T comparable](ctrl *gomock.Controller) *MockDG[T] {
|
||||||
|
mock := &MockDG[T]{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockDGMockRecorder[T]{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockDG[T]) EXPECT() *MockDGMockRecorder[T] {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEdge mocks base method.
|
||||||
|
func (m *MockDG[T]) AddEdge(fromVertexID, toVertexID string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "AddEdge", fromVertexID, toVertexID)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEdge indicates an expected call of AddEdge.
|
||||||
|
func (mr *MockDGMockRecorder[T]) AddEdge(fromVertexID, toVertexID interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddEdge", reflect.TypeOf((*MockDG[T])(nil).AddEdge), fromVertexID, toVertexID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddVertex mocks base method.
|
||||||
|
func (m *MockDG[T]) AddVertex(id string, value T) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "AddVertex", id, value)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddVertex indicates an expected call of AddVertex.
|
||||||
|
func (mr *MockDGMockRecorder[T]) AddVertex(id, value interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddVertex", reflect.TypeOf((*MockDG[T])(nil).AddVertex), id, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanAddEdge mocks base method.
|
||||||
|
func (m *MockDG[T]) CanAddEdge(fromVertexID, toVertexID string) bool {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CanAddEdge", fromVertexID, toVertexID)
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanAddEdge indicates an expected call of CanAddEdge.
|
||||||
|
func (mr *MockDGMockRecorder[T]) CanAddEdge(fromVertexID, toVertexID interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CanAddEdge", reflect.TypeOf((*MockDG[T])(nil).CanAddEdge), fromVertexID, toVertexID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteEdge mocks base method.
|
||||||
|
func (m *MockDG[T]) DeleteEdge(fromVertexID, toVertexID string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "DeleteEdge", fromVertexID, toVertexID)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteEdge indicates an expected call of DeleteEdge.
|
||||||
|
func (mr *MockDGMockRecorder[T]) DeleteEdge(fromVertexID, toVertexID interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteEdge", reflect.TypeOf((*MockDG[T])(nil).DeleteEdge), fromVertexID, toVertexID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVertex mocks base method.
|
||||||
|
func (m *MockDG[T]) DeleteVertex(id string) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "DeleteVertex", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVertex indicates an expected call of DeleteVertex.
|
||||||
|
func (mr *MockDGMockRecorder[T]) DeleteVertex(id interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVertex", reflect.TypeOf((*MockDG[T])(nil).DeleteVertex), id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRandomVertices mocks base method.
|
||||||
|
func (m *MockDG[T]) GetRandomVertices(n uint) []*dg.Vertex[T] {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetRandomVertices", n)
|
||||||
|
ret0, _ := ret[0].([]*dg.Vertex[T])
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRandomVertices indicates an expected call of GetRandomVertices.
|
||||||
|
func (mr *MockDGMockRecorder[T]) GetRandomVertices(n interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRandomVertices", reflect.TypeOf((*MockDG[T])(nil).GetRandomVertices), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSinkVertices mocks base method.
|
||||||
|
func (m *MockDG[T]) GetSinkVertices() []*dg.Vertex[T] {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetSinkVertices")
|
||||||
|
ret0, _ := ret[0].([]*dg.Vertex[T])
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSinkVertices indicates an expected call of GetSinkVertices.
|
||||||
|
func (mr *MockDGMockRecorder[T]) GetSinkVertices() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSinkVertices", reflect.TypeOf((*MockDG[T])(nil).GetSinkVertices))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSourceVertices mocks base method.
|
||||||
|
func (m *MockDG[T]) GetSourceVertices() []*dg.Vertex[T] {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetSourceVertices")
|
||||||
|
ret0, _ := ret[0].([]*dg.Vertex[T])
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSourceVertices indicates an expected call of GetSourceVertices.
|
||||||
|
func (mr *MockDGMockRecorder[T]) GetSourceVertices() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSourceVertices", reflect.TypeOf((*MockDG[T])(nil).GetSourceVertices))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVertex mocks base method.
|
||||||
|
func (m *MockDG[T]) GetVertex(id string) (*dg.Vertex[T], error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetVertex", id)
|
||||||
|
ret0, _ := ret[0].(*dg.Vertex[T])
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVertex indicates an expected call of GetVertex.
|
||||||
|
func (mr *MockDGMockRecorder[T]) GetVertex(id interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVertex", reflect.TypeOf((*MockDG[T])(nil).GetVertex), id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVertexKeys mocks base method.
|
||||||
|
func (m *MockDG[T]) GetVertexKeys() []string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetVertexKeys")
|
||||||
|
ret0, _ := ret[0].([]string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVertexKeys indicates an expected call of GetVertexKeys.
|
||||||
|
func (mr *MockDGMockRecorder[T]) GetVertexKeys() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVertexKeys", reflect.TypeOf((*MockDG[T])(nil).GetVertexKeys))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVertices mocks base method.
|
||||||
|
func (m *MockDG[T]) GetVertices() map[string]*dg.Vertex[T] {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetVertices")
|
||||||
|
ret0, _ := ret[0].(map[string]*dg.Vertex[T])
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVertices indicates an expected call of GetVertices.
|
||||||
|
func (mr *MockDGMockRecorder[T]) GetVertices() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVertices", reflect.TypeOf((*MockDG[T])(nil).GetVertices))
|
||||||
|
}
|
||||||
|
|
||||||
|
// VertexCount mocks base method.
|
||||||
|
func (m *MockDG[T]) VertexCount() int {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "VertexCount")
|
||||||
|
ret0, _ := ret[0].(int)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// VertexCount indicates an expected call of VertexCount.
|
||||||
|
func (mr *MockDGMockRecorder[T]) VertexCount() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VertexCount", reflect.TypeOf((*MockDG[T])(nil).VertexCount))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 The Dragonfly Authors
|
||||||
|
*
|
||||||
|
* 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 dg
|
||||||
|
|
||||||
|
import "d7y.io/dragonfly/v2/pkg/container/set"
|
||||||
|
|
||||||
|
// Vertex is a vertex of the directed graph.
|
||||||
|
type Vertex[T comparable] struct {
|
||||||
|
ID string
|
||||||
|
Value T
|
||||||
|
Parents set.SafeSet[*Vertex[T]]
|
||||||
|
Children set.SafeSet[*Vertex[T]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Vertex instance.
|
||||||
|
func NewVertex[T comparable](id string, value T) *Vertex[T] {
|
||||||
|
return &Vertex[T]{
|
||||||
|
ID: id,
|
||||||
|
Value: value,
|
||||||
|
Parents: set.NewSafeSet[*Vertex[T]](),
|
||||||
|
Children: set.NewSafeSet[*Vertex[T]](),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Degree returns the degree of vertex.
|
||||||
|
func (v *Vertex[T]) Degree() int {
|
||||||
|
return int(v.Parents.Len() + v.Children.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InDegree returns the indegree of vertex.
|
||||||
|
func (v *Vertex[T]) InDegree() int {
|
||||||
|
return int(v.Parents.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutDegree returns the outdegree of vertex.
|
||||||
|
func (v *Vertex[T]) OutDegree() int {
|
||||||
|
return int(v.Children.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteInEdges deletes inedges of vertex.
|
||||||
|
func (v *Vertex[T]) DeleteInEdges() {
|
||||||
|
for _, parent := range v.Parents.Values() {
|
||||||
|
parent.Children.Delete(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Parents = set.NewSafeSet[*Vertex[T]]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOutEdges deletes outedges of vertex.
|
||||||
|
func (v *Vertex[T]) DeleteOutEdges() {
|
||||||
|
for _, child := range v.Children.Values() {
|
||||||
|
child.Parents.Delete(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Children = set.NewSafeSet[*Vertex[T]]()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 The Dragonfly Authors
|
||||||
|
*
|
||||||
|
* 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 dg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mockVertexID = "foo"
|
||||||
|
mockVertexValue = "bar"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewVertex(t *testing.T) {
|
||||||
|
v := NewVertex(mockVertexID, mockVertexValue)
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.Equal(v.ID, mockVertexID)
|
||||||
|
assert.Equal(v.Value, mockVertexValue)
|
||||||
|
assert.Equal(v.Parents.Len(), uint(0))
|
||||||
|
assert.Equal(v.Children.Len(), uint(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVertexDegree(t *testing.T) {
|
||||||
|
v := NewVertex(mockVertexID, mockVertexValue)
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.Equal(v.ID, mockVertexID)
|
||||||
|
assert.Equal(v.Value, mockVertexValue)
|
||||||
|
assert.Equal(v.Degree(), 0)
|
||||||
|
|
||||||
|
v.Parents.Add(v)
|
||||||
|
assert.Equal(v.Degree(), 1)
|
||||||
|
|
||||||
|
v.Children.Add(v)
|
||||||
|
assert.Equal(v.Degree(), 2)
|
||||||
|
|
||||||
|
v.Parents.Delete(v)
|
||||||
|
assert.Equal(v.Degree(), 1)
|
||||||
|
|
||||||
|
v.Children.Delete(v)
|
||||||
|
assert.Equal(v.Degree(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVertexInDegree(t *testing.T) {
|
||||||
|
v := NewVertex(mockVertexID, mockVertexValue)
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.Equal(v.ID, mockVertexID)
|
||||||
|
assert.Equal(v.Value, mockVertexValue)
|
||||||
|
assert.Equal(v.InDegree(), 0)
|
||||||
|
|
||||||
|
v.Parents.Add(v)
|
||||||
|
assert.Equal(v.InDegree(), 1)
|
||||||
|
|
||||||
|
v.Children.Add(v)
|
||||||
|
assert.Equal(v.InDegree(), 1)
|
||||||
|
|
||||||
|
v.Parents.Delete(v)
|
||||||
|
assert.Equal(v.InDegree(), 0)
|
||||||
|
|
||||||
|
v.Children.Delete(v)
|
||||||
|
assert.Equal(v.InDegree(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVertexOutDegree(t *testing.T) {
|
||||||
|
v := NewVertex(mockVertexID, mockVertexValue)
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.Equal(v.ID, mockVertexID)
|
||||||
|
assert.Equal(v.Value, mockVertexValue)
|
||||||
|
assert.Equal(v.OutDegree(), 0)
|
||||||
|
|
||||||
|
v.Parents.Add(v)
|
||||||
|
assert.Equal(v.OutDegree(), 0)
|
||||||
|
|
||||||
|
v.Children.Add(v)
|
||||||
|
assert.Equal(v.OutDegree(), 1)
|
||||||
|
|
||||||
|
v.Parents.Delete(v)
|
||||||
|
assert.Equal(v.OutDegree(), 1)
|
||||||
|
|
||||||
|
v.Children.Delete(v)
|
||||||
|
assert.Equal(v.OutDegree(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVertexDeleteInEdges(t *testing.T) {
|
||||||
|
va := NewVertex(mockVertexID, mockVertexValue)
|
||||||
|
vb := NewVertex(mockVertexID, mockVertexValue)
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
va.Parents.Add(vb)
|
||||||
|
vb.Children.Add(va)
|
||||||
|
assert.Equal(va.Parents.Len(), uint(1))
|
||||||
|
assert.Equal(vb.Children.Len(), uint(1))
|
||||||
|
|
||||||
|
va.DeleteInEdges()
|
||||||
|
assert.Equal(va.Parents.Len(), uint(0))
|
||||||
|
assert.Equal(vb.Children.Len(), uint(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVertexDeleteOutEdges(t *testing.T) {
|
||||||
|
va := NewVertex(mockVertexID, mockVertexValue)
|
||||||
|
vb := NewVertex(mockVertexID, mockVertexValue)
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
va.Parents.Add(vb)
|
||||||
|
vb.Children.Add(va)
|
||||||
|
assert.Equal(va.Parents.Len(), uint(1))
|
||||||
|
assert.Equal(vb.Children.Len(), uint(1))
|
||||||
|
|
||||||
|
vb.DeleteOutEdges()
|
||||||
|
assert.Equal(va.Parents.Len(), uint(0))
|
||||||
|
assert.Equal(vb.Children.Len(), uint(0))
|
||||||
|
}
|
||||||
|
|
@ -31,7 +31,7 @@ import (
|
||||||
|
|
||||||
logger "d7y.io/dragonfly/v2/internal/dflog"
|
logger "d7y.io/dragonfly/v2/internal/dflog"
|
||||||
"d7y.io/dragonfly/v2/pkg/container/set"
|
"d7y.io/dragonfly/v2/pkg/container/set"
|
||||||
"d7y.io/dragonfly/v2/pkg/dag"
|
"d7y.io/dragonfly/v2/pkg/graph/dag"
|
||||||
"d7y.io/dragonfly/v2/pkg/types"
|
"d7y.io/dragonfly/v2/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue