Files
dataStructures/tests/graphTests/weightedGraph_test.go
T
Acid 5fb97bc757
Go / build (push) Successful in 34s
new file: graphs/weightedGraph.go
2026-07-24 03:43:24 -04:00

253 lines
7.0 KiB
Go

package tests
import (
"testing"
"datastructures/graphs"
)
func TestNewWeightedGraphIsEmpty(t *testing.T) {
graph := graphs.NewWeightedGraph[int, int]()
if graph.Order() != 0 {
t.Errorf("new graph Order = %d, want 0", graph.Order())
}
if graph.Size() != 0 {
t.Errorf("new graph Size = %d, want 0", graph.Size())
}
}
func TestWeightedAddVertexCreatesBothAndConnects(t *testing.T) {
graph := graphs.NewWeightedGraph[string, int]()
if !graph.AddVertex("a", "b", 4) {
t.Error("AddVertex on a new edge = false, want true")
}
if graph.Order() != 2 {
t.Errorf("Order = %d, want 2", graph.Order())
}
if graph.Size() != 1 {
t.Errorf("Size = %d, want 1", graph.Size())
}
adjacency := graph.Display()
if adjacency["a"]["b"] != 4 {
t.Errorf("weight a->b = %d, want 4", adjacency["a"]["b"])
}
// undirected: the reverse direction carries the same weight
if adjacency["b"]["a"] != 4 {
t.Errorf("weight b->a = %d, want 4 (undirected)", adjacency["b"]["a"])
}
}
// Adding a second edge from the same vertex must not clobber the first.
func TestWeightedAddVertexKeepsPreviousEdges(t *testing.T) {
graph := graphs.NewWeightedGraph[string, int]()
graph.AddVertex("a", "b", 4)
graph.AddVertex("a", "c", 2)
graph.AddVertex("a", "d", 7)
adjacency := graph.Display()
if len(adjacency["a"]) != 3 {
t.Errorf("expected vertex a to have degree 3, got %v", adjacency["a"])
}
for neighbor, want := range map[string]int{"b": 4, "c": 2, "d": 7} {
if adjacency["a"][neighbor] != want {
t.Errorf("weight a->%s = %d, want %d", neighbor, adjacency["a"][neighbor], want)
}
}
if graph.Order() != 4 {
t.Errorf("Order = %d, want 4", graph.Order())
}
if graph.Size() != 3 {
t.Errorf("Size = %d, want 3", graph.Size())
}
}
func TestWeightedAddVertexRejectsDuplicateEdge(t *testing.T) {
graph := graphs.NewWeightedGraph[string, int]()
graph.AddVertex("a", "b", 4)
if graph.AddVertex("a", "b", 99) {
t.Error("AddVertex on an existing edge = true, want false")
}
// the original weight is kept, not overwritten
if weight := graph.Display()["a"]["b"]; weight != 4 {
t.Errorf("weight a->b = %d, want 4 (existing weight kept)", weight)
}
if graph.Size() != 1 {
t.Errorf("Size = %d, want 1", graph.Size())
}
}
func TestWeightedSelfLoopIsRejected(t *testing.T) {
graph := graphs.NewWeightedGraph[int, int]()
if graph.AddVertex(1, 1, 5) {
t.Error("AddVertex with a self-loop = true, want false")
}
if graph.Order() != 0 {
t.Errorf("self-loop should create no vertex, Order = %d", graph.Order())
}
if graph.Size() != 0 {
t.Errorf("self-loop should add no edge, Size = %d", graph.Size())
}
// a real edge on the same vertex must still work
graph.AddVertex(1, 2, 5)
if graph.Size() != 1 {
t.Errorf("Size = %d, want 1 after a valid edge", graph.Size())
}
}
func TestWeightedAddIsolatedVertex(t *testing.T) {
graph := graphs.NewWeightedGraph[string, int]()
if !graph.AddIsolatedVertex("solo") {
t.Error("AddIsolatedVertex on new vertex = false, want true")
}
if graph.AddIsolatedVertex("solo") {
t.Error("AddIsolatedVertex on existing vertex = true, want false")
}
if graph.Order() != 1 {
t.Errorf("Order = %d, want 1", graph.Order())
}
if graph.Size() != 0 {
t.Errorf("isolated vertex should add no edges, Size = %d", graph.Size())
}
if len(graph.Display()["solo"]) != 0 {
t.Errorf("isolated vertex should have empty adjacency, got %v", graph.Display()["solo"])
}
}
// An existing vertex must not be reset when a later edge is added to it.
func TestWeightedAddVertexPreservesExistingVertex(t *testing.T) {
graph := graphs.NewWeightedGraph[string, int]()
graph.AddVertex("a", "b", 4)
graph.AddVertex("c", "a", 2)
adjacency := graph.Display()
if len(adjacency["a"]) != 2 {
t.Errorf("expected vertex a to keep both edges, got %v", adjacency["a"])
}
if adjacency["a"]["b"] != 4 {
t.Errorf("weight a->b = %d, want 4", adjacency["a"]["b"])
}
if adjacency["a"]["c"] != 2 {
t.Errorf("weight a->c = %d, want 2", adjacency["a"]["c"])
}
}
func TestConnectEdgeAllExisting(t *testing.T) {
graph := graphs.NewWeightedGraph[int, int]()
graph.AddIsolatedVertex(1)
graph.AddIsolatedVertex(2)
if !graph.ConnectEdge(1, 2, 8) {
t.Error("ConnectEdge with existing vertices = false, want true")
}
if graph.Size() != 1 {
t.Errorf("Size = %d, want 1", graph.Size())
}
adjacency := graph.Display()
if adjacency[1][2] != 8 {
t.Errorf("weight 1->2 = %d, want 8", adjacency[1][2])
}
if adjacency[2][1] != 8 {
t.Errorf("weight 2->1 = %d, want 8 (undirected)", adjacency[2][1])
}
}
func TestConnectEdgeRejectsMissingVertex(t *testing.T) {
graph := graphs.NewWeightedGraph[int, int]()
graph.AddIsolatedVertex(1)
// 99 does not exist
if graph.ConnectEdge(1, 99, 3) {
t.Error("ConnectEdge with a missing target = true, want false")
}
if graph.ConnectEdge(99, 1, 3) {
t.Error("ConnectEdge with a missing source = true, want false")
}
if graph.Order() != 1 {
t.Errorf("ConnectEdge should not create vertices, Order = %d, want 1", graph.Order())
}
if graph.Size() != 0 {
t.Errorf("no edges should be added on failure, Size = %d", graph.Size())
}
}
func TestConnectEdgeRejectsDuplicate(t *testing.T) {
graph := graphs.NewWeightedGraph[int, int]()
graph.AddIsolatedVertex(1)
graph.AddIsolatedVertex(2)
graph.ConnectEdge(1, 2, 8)
if graph.ConnectEdge(1, 2, 99) {
t.Error("ConnectEdge on an existing edge = true, want false")
}
if weight := graph.Display()[1][2]; weight != 8 {
t.Errorf("weight 1->2 = %d, want 8 (existing weight kept)", weight)
}
if graph.Size() != 1 {
t.Errorf("Size = %d, want 1", graph.Size())
}
}
func TestWeightedOrderAndSize(t *testing.T) {
graph := graphs.NewWeightedGraph[string, int]()
graph.AddVertex("a", "b", 4)
graph.AddVertex("b", "c", 7)
graph.AddVertex("c", "a", 2)
if graph.Order() != 3 {
t.Errorf("Order = %d, want 3 (triangle)", graph.Order())
}
if graph.Size() != 3 {
t.Errorf("Size = %d, want 3 (triangle)", graph.Size())
}
}
// A missing edge reads as the zero weight, so existence needs the comma-ok form.
func TestWeightedMissingEdgeIsZero(t *testing.T) {
graph := graphs.NewWeightedGraph[string, int]()
graph.AddVertex("a", "b", 4)
weight, exists := graph.Display()["a"]["z"]
if exists {
t.Error("lookup of a missing edge reported exists = true, want false")
}
if weight != 0 {
t.Errorf("missing edge weight = %d, want the zero value 0", weight)
}
}
func TestWeightedFloatWeights(t *testing.T) {
graph := graphs.NewWeightedGraph[string, float64]()
graph.AddVertex("a", "b", 2.5)
if weight := graph.Display()["a"]["b"]; weight != 2.5 {
t.Errorf("weight a->b = %v, want 2.5", weight)
}
if weight := graph.Display()["b"]["a"]; weight != 2.5 {
t.Errorf("weight b->a = %v, want 2.5 (undirected)", weight)
}
}
// A defined type with a numeric underlying type satisfies the Number constraint.
type Distance float64
func TestWeightedNamedWeightType(t *testing.T) {
graph := graphs.NewWeightedGraph[string, Distance]()
graph.AddVertex("a", "b", Distance(1.5))
if weight := graph.Display()["a"]["b"]; weight != Distance(1.5) {
t.Errorf("weight a->b = %v, want 1.5", weight)
}
}