This is still a dirty prototype, so please excuse the mess. Please excuse the fact that this is a mega patch. Once things settle down this won't happen any more. Some of the changes squashed into here include: * Merge vertex loop with type loop (The file watcher seems to cache events anyways) * Improve pgraph library * Add indegree, outdegree, and topological sort with tests * Add reverse function for vertex list * Tons of additional cleanup! Amazingly, on my first successful compile, this seemed to run! A special thanks to Ira Cooper who helped me talk through some of the algorithmic decisions and for his help in finding better ones!
409 lines
10 KiB
Go
409 lines
10 KiB
Go
// Mgmt
|
|
// Copyright (C) 2013-2015+ James Shubin and the project contributors
|
|
// Written by James Shubin <james@shubin.ca> and the project contributors
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
// NOTE: this is pgraph, a pointer graph
|
|
|
|
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestPgraphT1(t *testing.T) {
|
|
|
|
G := NewGraph("g1")
|
|
|
|
if i := G.NumVertices(); i != 0 {
|
|
t.Errorf("Should have 0 vertices instead of: %d.", i)
|
|
}
|
|
|
|
if i := G.NumEdges(); i != 0 {
|
|
t.Errorf("Should have 0 edges instead of: %d.", i)
|
|
}
|
|
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
e1 := NewEdge("e1")
|
|
G.AddEdge(v1, v2, e1)
|
|
|
|
if i := G.NumVertices(); i != 2 {
|
|
t.Errorf("Should have 2 vertices instead of: %d.", i)
|
|
}
|
|
|
|
if i := G.NumEdges(); i != 1 {
|
|
t.Errorf("Should have 1 edges instead of: %d.", i)
|
|
}
|
|
}
|
|
|
|
func TestPgraphT2(t *testing.T) {
|
|
|
|
G := NewGraph("g2")
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
v3 := NewVertex(NewNoopType("v3"))
|
|
v4 := NewVertex(NewNoopType("v4"))
|
|
v5 := NewVertex(NewNoopType("v5"))
|
|
v6 := NewVertex(NewNoopType("v6"))
|
|
e1 := NewEdge("e1")
|
|
e2 := NewEdge("e2")
|
|
e3 := NewEdge("e3")
|
|
e4 := NewEdge("e4")
|
|
e5 := NewEdge("e5")
|
|
//e6 := NewEdge("e6")
|
|
G.AddEdge(v1, v2, e1)
|
|
G.AddEdge(v2, v3, e2)
|
|
G.AddEdge(v3, v1, e3)
|
|
|
|
G.AddEdge(v4, v5, e4)
|
|
G.AddEdge(v5, v6, e5)
|
|
|
|
if i := G.NumVertices(); i != 6 {
|
|
t.Errorf("Should have 6 vertices instead of: %d.", i)
|
|
}
|
|
}
|
|
|
|
func TestPgraphT3(t *testing.T) {
|
|
|
|
G := NewGraph("g3")
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
v3 := NewVertex(NewNoopType("v3"))
|
|
v4 := NewVertex(NewNoopType("v4"))
|
|
v5 := NewVertex(NewNoopType("v5"))
|
|
v6 := NewVertex(NewNoopType("v6"))
|
|
e1 := NewEdge("e1")
|
|
e2 := NewEdge("e2")
|
|
e3 := NewEdge("e3")
|
|
e4 := NewEdge("e4")
|
|
e5 := NewEdge("e5")
|
|
//e6 := NewEdge("e6")
|
|
G.AddEdge(v1, v2, e1)
|
|
G.AddEdge(v2, v3, e2)
|
|
G.AddEdge(v3, v1, e3)
|
|
|
|
G.AddEdge(v4, v5, e4)
|
|
G.AddEdge(v5, v6, e5)
|
|
//G.AddEdge(v6, v4, e6)
|
|
out1 := G.DFS(v1)
|
|
if i := len(out1); i != 3 {
|
|
t.Errorf("Should have 3 vertices instead of: %d.", i)
|
|
t.Errorf("Found: %v", out1)
|
|
for _, v := range out1 {
|
|
t.Errorf("Value: %v", v.GetName())
|
|
}
|
|
}
|
|
|
|
out2 := G.DFS(v4)
|
|
if i := len(out2); i != 3 {
|
|
t.Errorf("Should have 3 vertices instead of: %d.", i)
|
|
t.Errorf("Found: %v", out1)
|
|
for _, v := range out1 {
|
|
t.Errorf("Value: %v", v.GetName())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPgraphT4(t *testing.T) {
|
|
|
|
G := NewGraph("g4")
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
v3 := NewVertex(NewNoopType("v3"))
|
|
e1 := NewEdge("e1")
|
|
e2 := NewEdge("e2")
|
|
e3 := NewEdge("e3")
|
|
G.AddEdge(v1, v2, e1)
|
|
G.AddEdge(v2, v3, e2)
|
|
G.AddEdge(v3, v1, e3)
|
|
|
|
out := G.DFS(v1)
|
|
if i := len(out); i != 3 {
|
|
t.Errorf("Should have 3 vertices instead of: %d.", i)
|
|
t.Errorf("Found: %v", out)
|
|
for _, v := range out {
|
|
t.Errorf("Value: %v", v.GetName())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPgraphT5(t *testing.T) {
|
|
G := NewGraph("g5")
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
v3 := NewVertex(NewNoopType("v3"))
|
|
v4 := NewVertex(NewNoopType("v4"))
|
|
v5 := NewVertex(NewNoopType("v5"))
|
|
v6 := NewVertex(NewNoopType("v6"))
|
|
e1 := NewEdge("e1")
|
|
e2 := NewEdge("e2")
|
|
e3 := NewEdge("e3")
|
|
e4 := NewEdge("e4")
|
|
e5 := NewEdge("e5")
|
|
//e6 := NewEdge("e6")
|
|
G.AddEdge(v1, v2, e1)
|
|
G.AddEdge(v2, v3, e2)
|
|
G.AddEdge(v3, v1, e3)
|
|
|
|
G.AddEdge(v4, v5, e4)
|
|
G.AddEdge(v5, v6, e5)
|
|
//G.AddEdge(v6, v4, e6)
|
|
|
|
save := []*Vertex{v1, v2, v3}
|
|
out := G.FilterGraph("new g5", save)
|
|
if i := out.NumVertices(); i != 3 {
|
|
t.Errorf("Should have 3 vertices instead of: %d.", i)
|
|
}
|
|
}
|
|
|
|
func TestPgraphT6(t *testing.T) {
|
|
G := NewGraph("g6")
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
v3 := NewVertex(NewNoopType("v3"))
|
|
v4 := NewVertex(NewNoopType("v4"))
|
|
v5 := NewVertex(NewNoopType("v5"))
|
|
v6 := NewVertex(NewNoopType("v6"))
|
|
e1 := NewEdge("e1")
|
|
e2 := NewEdge("e2")
|
|
e3 := NewEdge("e3")
|
|
e4 := NewEdge("e4")
|
|
e5 := NewEdge("e5")
|
|
//e6 := NewEdge("e6")
|
|
G.AddEdge(v1, v2, e1)
|
|
G.AddEdge(v2, v3, e2)
|
|
G.AddEdge(v3, v1, e3)
|
|
|
|
G.AddEdge(v4, v5, e4)
|
|
G.AddEdge(v5, v6, e5)
|
|
//G.AddEdge(v6, v4, e6)
|
|
|
|
graphs := G.GetDisconnectedGraphs()
|
|
HeisenbergGraphCount := func(ch chan *Graph) int {
|
|
c := 0
|
|
for x := range ch {
|
|
_ = x
|
|
c++
|
|
}
|
|
return c
|
|
}
|
|
|
|
if i := HeisenbergGraphCount(graphs); i != 2 {
|
|
t.Errorf("Should have 2 graphs instead of: %d.", i)
|
|
}
|
|
}
|
|
|
|
func TestPgraphT7(t *testing.T) {
|
|
|
|
G := NewGraph("g7")
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
v3 := NewVertex(NewNoopType("v3"))
|
|
e1 := NewEdge("e1")
|
|
e2 := NewEdge("e2")
|
|
e3 := NewEdge("e3")
|
|
G.AddEdge(v1, v2, e1)
|
|
G.AddEdge(v2, v3, e2)
|
|
G.AddEdge(v3, v1, e3)
|
|
|
|
if i := G.NumVertices(); i != 3 {
|
|
t.Errorf("Should have 3 vertices instead of: %d.", i)
|
|
}
|
|
|
|
G.DeleteVertex(v2)
|
|
|
|
if i := G.NumVertices(); i != 2 {
|
|
t.Errorf("Should have 2 vertices instead of: %d.", i)
|
|
}
|
|
|
|
G.DeleteVertex(v1)
|
|
|
|
if i := G.NumVertices(); i != 1 {
|
|
t.Errorf("Should have 1 vertices instead of: %d.", i)
|
|
}
|
|
|
|
G.DeleteVertex(v3)
|
|
|
|
if i := G.NumVertices(); i != 0 {
|
|
t.Errorf("Should have 0 vertices instead of: %d.", i)
|
|
}
|
|
|
|
G.DeleteVertex(v2) // duplicate deletes don't error...
|
|
|
|
if i := G.NumVertices(); i != 0 {
|
|
t.Errorf("Should have 0 vertices instead of: %d.", i)
|
|
}
|
|
}
|
|
|
|
func TestPgraphT8(t *testing.T) {
|
|
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
v3 := NewVertex(NewNoopType("v3"))
|
|
if HasVertex(v1, []*Vertex{v1, v2, v3}) != true {
|
|
t.Errorf("Should be true instead of false.")
|
|
}
|
|
|
|
v4 := NewVertex(NewNoopType("v4"))
|
|
v5 := NewVertex(NewNoopType("v5"))
|
|
v6 := NewVertex(NewNoopType("v6"))
|
|
if HasVertex(v4, []*Vertex{v5, v6}) != false {
|
|
t.Errorf("Should be false instead of true.")
|
|
}
|
|
|
|
v7 := NewVertex(NewNoopType("v7"))
|
|
v8 := NewVertex(NewNoopType("v8"))
|
|
v9 := NewVertex(NewNoopType("v9"))
|
|
if HasVertex(v8, []*Vertex{v7, v8, v9}) != true {
|
|
t.Errorf("Should be true instead of false.")
|
|
}
|
|
|
|
v_1 := NewVertex(NewNoopType("v1")) // same value, different objects
|
|
if HasVertex(v_1, []*Vertex{v1, v2, v3}) != false {
|
|
t.Errorf("Should be false instead of true.")
|
|
}
|
|
}
|
|
|
|
func TestPgraphT9(t *testing.T) {
|
|
|
|
G := NewGraph("g9")
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
v3 := NewVertex(NewNoopType("v3"))
|
|
v4 := NewVertex(NewNoopType("v4"))
|
|
v5 := NewVertex(NewNoopType("v5"))
|
|
v6 := NewVertex(NewNoopType("v6"))
|
|
e1 := NewEdge("e1")
|
|
e2 := NewEdge("e2")
|
|
e3 := NewEdge("e3")
|
|
e4 := NewEdge("e4")
|
|
e5 := NewEdge("e5")
|
|
e6 := NewEdge("e6")
|
|
G.AddEdge(v1, v2, e1)
|
|
G.AddEdge(v1, v3, e2)
|
|
G.AddEdge(v2, v4, e3)
|
|
G.AddEdge(v3, v4, e4)
|
|
|
|
G.AddEdge(v4, v5, e5)
|
|
G.AddEdge(v5, v6, e6)
|
|
|
|
indegree := G.InDegree() // map[*Vertex]int
|
|
if i := indegree[v1]; i != 0 {
|
|
t.Errorf("Indegree of v1 should be 0 instead of: %d.", i)
|
|
}
|
|
if i := indegree[v2]; i != 1 {
|
|
t.Errorf("Indegree of v2 should be 1 instead of: %d.", i)
|
|
}
|
|
if i := indegree[v3]; i != 1 {
|
|
t.Errorf("Indegree of v3 should be 1 instead of: %d.", i)
|
|
}
|
|
if i := indegree[v4]; i != 2 {
|
|
t.Errorf("Indegree of v4 should be 2 instead of: %d.", i)
|
|
}
|
|
if i := indegree[v5]; i != 1 {
|
|
t.Errorf("Indegree of v5 should be 1 instead of: %d.", i)
|
|
}
|
|
if i := indegree[v6]; i != 1 {
|
|
t.Errorf("Indegree of v6 should be 1 instead of: %d.", i)
|
|
}
|
|
|
|
outdegree := G.OutDegree() // map[*Vertex]int
|
|
if i := outdegree[v1]; i != 2 {
|
|
t.Errorf("Outdegree of v1 should be 2 instead of: %d.", i)
|
|
}
|
|
if i := outdegree[v2]; i != 1 {
|
|
t.Errorf("Outdegree of v2 should be 1 instead of: %d.", i)
|
|
}
|
|
if i := outdegree[v3]; i != 1 {
|
|
t.Errorf("Outdegree of v3 should be 1 instead of: %d.", i)
|
|
}
|
|
if i := outdegree[v4]; i != 1 {
|
|
t.Errorf("Outdegree of v4 should be 1 instead of: %d.", i)
|
|
}
|
|
if i := outdegree[v5]; i != 1 {
|
|
t.Errorf("Outdegree of v5 should be 1 instead of: %d.", i)
|
|
}
|
|
if i := outdegree[v6]; i != 0 {
|
|
t.Errorf("Outdegree of v6 should be 0 instead of: %d.", i)
|
|
}
|
|
|
|
s, ok := G.TopologicalSort()
|
|
// either possibility is a valid toposort
|
|
match := reflect.DeepEqual(s, []*Vertex{v1, v2, v3, v4, v5, v6}) || reflect.DeepEqual(s, []*Vertex{v1, v3, v2, v4, v5, v6})
|
|
if !ok || !match {
|
|
t.Errorf("Topological sort failed, status: %v.", ok)
|
|
str := "Found:"
|
|
for _, v := range s {
|
|
str += " " + v.Type.GetName()
|
|
}
|
|
t.Errorf(str)
|
|
}
|
|
}
|
|
|
|
func TestPgraphT10(t *testing.T) {
|
|
|
|
G := NewGraph("g10")
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
v3 := NewVertex(NewNoopType("v3"))
|
|
v4 := NewVertex(NewNoopType("v4"))
|
|
v5 := NewVertex(NewNoopType("v5"))
|
|
v6 := NewVertex(NewNoopType("v6"))
|
|
e1 := NewEdge("e1")
|
|
e2 := NewEdge("e2")
|
|
e3 := NewEdge("e3")
|
|
e4 := NewEdge("e4")
|
|
e5 := NewEdge("e5")
|
|
e6 := NewEdge("e6")
|
|
G.AddEdge(v1, v2, e1)
|
|
G.AddEdge(v2, v3, e2)
|
|
G.AddEdge(v3, v4, e3)
|
|
G.AddEdge(v4, v5, e4)
|
|
G.AddEdge(v5, v6, e5)
|
|
G.AddEdge(v4, v2, e6) // cycle
|
|
|
|
if _, ok := G.TopologicalSort(); ok {
|
|
t.Errorf("Topological sort passed, but graph is cyclic.")
|
|
}
|
|
}
|
|
|
|
func TestPgraphT11(t *testing.T) {
|
|
v1 := NewVertex(NewNoopType("v1"))
|
|
v2 := NewVertex(NewNoopType("v2"))
|
|
v3 := NewVertex(NewNoopType("v3"))
|
|
v4 := NewVertex(NewNoopType("v4"))
|
|
v5 := NewVertex(NewNoopType("v5"))
|
|
v6 := NewVertex(NewNoopType("v6"))
|
|
|
|
if rev := Reverse([]*Vertex{}); !reflect.DeepEqual(rev, []*Vertex{}) {
|
|
t.Errorf("Reverse of vertex slice failed.")
|
|
}
|
|
|
|
if rev := Reverse([]*Vertex{v1}); !reflect.DeepEqual(rev, []*Vertex{v1}) {
|
|
t.Errorf("Reverse of vertex slice failed.")
|
|
}
|
|
|
|
if rev := Reverse([]*Vertex{v1, v2, v3, v4, v5, v6}); !reflect.DeepEqual(rev, []*Vertex{v6, v5, v4, v3, v2, v1}) {
|
|
t.Errorf("Reverse of vertex slice failed.")
|
|
}
|
|
|
|
if rev := Reverse([]*Vertex{v6, v5, v4, v3, v2, v1}); !reflect.DeepEqual(rev, []*Vertex{v1, v2, v3, v4, v5, v6}) {
|
|
t.Errorf("Reverse of vertex slice failed.")
|
|
}
|
|
|
|
}
|