204 lines
4.9 KiB
Go
204 lines
4.9 KiB
Go
package tests
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
|
|
"datastructures/graphs"
|
|
)
|
|
|
|
func TestBFSUnknownStartReturnsNil(t *testing.T) {
|
|
graph := graphs.NewGraph[string]()
|
|
graph.AddVertex("a", "b")
|
|
|
|
if order := graph.BFS("zz"); order != nil {
|
|
t.Errorf("BFS from unknown vertex = %v, want nil", order)
|
|
}
|
|
}
|
|
|
|
func TestBFSEmptyGraphReturnsNil(t *testing.T) {
|
|
graph := graphs.NewGraph[int]()
|
|
|
|
if order := graph.BFS(1); order != nil {
|
|
t.Errorf("BFS on empty graph = %v, want nil", order)
|
|
}
|
|
}
|
|
|
|
func TestBFSIsolatedVertexVisitsOnlyItself(t *testing.T) {
|
|
graph := graphs.NewGraph[string]()
|
|
graph.AddIsolatedVertex("lonely")
|
|
|
|
order := graph.BFS("lonely")
|
|
if !slices.Equal(order, []string{"lonely"}) {
|
|
t.Errorf("BFS = %v, want [lonely]", order)
|
|
}
|
|
}
|
|
|
|
func TestBFSPathGraphVisitsInOrder(t *testing.T) {
|
|
graph := graphs.NewGraph[string]()
|
|
graph.AddVertex("a", "b")
|
|
graph.AddVertex("b", "c")
|
|
graph.AddVertex("c", "d")
|
|
|
|
order := graph.BFS("a")
|
|
if !slices.Equal(order, []string{"a", "b", "c", "d"}) {
|
|
t.Errorf("BFS = %v, want [a b c d]", order)
|
|
}
|
|
}
|
|
|
|
func TestBFSVisitsLevelByLevel(t *testing.T) {
|
|
// a
|
|
// / \
|
|
// b c
|
|
// | |
|
|
// d e
|
|
graph := graphs.NewGraph[string]()
|
|
graph.AddVertex("a", "b")
|
|
graph.AddVertex("a", "c")
|
|
graph.AddVertex("b", "d")
|
|
graph.AddVertex("c", "e")
|
|
|
|
order := graph.BFS("a")
|
|
if !slices.Equal(order, []string{"a", "b", "c", "d", "e"}) {
|
|
t.Errorf("BFS = %v, want [a b c d e]", order)
|
|
}
|
|
}
|
|
|
|
func TestBFSFromNonRootStart(t *testing.T) {
|
|
graph := graphs.NewGraph[string]()
|
|
graph.AddVertex("a", "b")
|
|
graph.AddVertex("a", "c")
|
|
graph.AddVertex("b", "d")
|
|
|
|
// b's adjacency is [a d], so a comes before d
|
|
order := graph.BFS("b")
|
|
if !slices.Equal(order, []string{"b", "a", "d", "c"}) {
|
|
t.Errorf("BFS from b = %v, want [b a d c]", order)
|
|
}
|
|
}
|
|
|
|
func TestBFSCycleTerminatesWithoutRepeats(t *testing.T) {
|
|
graph := graphs.NewGraph[string]()
|
|
graph.AddVertex("a", "b")
|
|
graph.AddVertex("b", "c")
|
|
graph.AddVertex("c", "a")
|
|
|
|
order := graph.BFS("a")
|
|
if len(order) != 3 {
|
|
t.Fatalf("BFS on triangle = %v, want 3 vertices", order)
|
|
}
|
|
assertNoDuplicates(t, order)
|
|
}
|
|
|
|
func TestBFSDenseGraphVisitsEachVertexOnce(t *testing.T) {
|
|
// K4: every vertex connected to every other
|
|
graph := graphs.NewGraph[int]()
|
|
for first := 1; first <= 4; first++ {
|
|
for second := first + 1; second <= 4; second++ {
|
|
graph.AddVertex(first, second)
|
|
}
|
|
}
|
|
|
|
order := graph.BFS(1)
|
|
if len(order) != graph.Order() {
|
|
t.Errorf("BFS visited %d vertices, want %d", len(order), graph.Order())
|
|
}
|
|
assertNoDuplicates(t, order)
|
|
}
|
|
|
|
func TestBFSSkipsDisconnectedComponent(t *testing.T) {
|
|
graph := graphs.NewGraph[string]()
|
|
graph.AddVertex("a", "b")
|
|
graph.AddVertex("x", "y")
|
|
|
|
order := graph.BFS("a")
|
|
if !slices.Equal(order, []string{"a", "b"}) {
|
|
t.Errorf("BFS = %v, want only the component of a: [a b]", order)
|
|
}
|
|
}
|
|
|
|
func TestBFSReachesEveryVertexInConnectedGraph(t *testing.T) {
|
|
graph := graphs.NewGraph[int]()
|
|
graph.AddVertex(1, 2)
|
|
graph.AddVertex(2, 3)
|
|
graph.AddVertex(3, 4)
|
|
graph.AddVertex(4, 1)
|
|
graph.AddVertex(2, 5)
|
|
|
|
order := graph.BFS(1)
|
|
if len(order) != graph.Order() {
|
|
t.Errorf("BFS visited %d vertices, want %d", len(order), graph.Order())
|
|
}
|
|
for vertex := range graph.Display() {
|
|
if !slices.Contains(order, vertex) {
|
|
t.Errorf("vertex %d was never visited, order = %v", vertex, order)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBFSDistancesAreNonDecreasing(t *testing.T) {
|
|
graph := graphs.NewGraph[string]()
|
|
graph.AddVertex("a", "b")
|
|
graph.AddVertex("a", "c")
|
|
graph.AddVertex("b", "d")
|
|
graph.AddVertex("c", "d")
|
|
graph.AddVertex("d", "e")
|
|
graph.AddVertex("e", "f")
|
|
|
|
order := graph.BFS("a")
|
|
|
|
// hop count from a, derived from the graph itself
|
|
distances := map[string]int{"a": 0, "b": 1, "c": 1, "d": 2, "e": 3, "f": 4}
|
|
previous := 0
|
|
for _, vertex := range order {
|
|
current, ok := distances[vertex]
|
|
if !ok {
|
|
t.Fatalf("BFS returned unexpected vertex %q", vertex)
|
|
}
|
|
if current < previous {
|
|
t.Errorf("BFS = %v: %q at distance %d visited after distance %d", order, vertex, current, previous)
|
|
}
|
|
previous = current
|
|
}
|
|
}
|
|
|
|
func TestBFSDoesNotMutateGraph(t *testing.T) {
|
|
graph := graphs.NewGraph[string]()
|
|
graph.AddVertex("a", "b")
|
|
graph.AddVertex("b", "c")
|
|
|
|
orderBefore, sizeBefore := graph.Order(), graph.Size()
|
|
graph.BFS("a")
|
|
|
|
if graph.Order() != orderBefore || graph.Size() != sizeBefore {
|
|
t.Errorf("after BFS Order/Size = %d/%d, want %d/%d",
|
|
graph.Order(), graph.Size(), orderBefore, sizeBefore)
|
|
}
|
|
}
|
|
|
|
func TestBFSIsRepeatable(t *testing.T) {
|
|
graph := graphs.NewGraph[string]()
|
|
graph.AddVertex("a", "b")
|
|
graph.AddVertex("a", "c")
|
|
graph.AddVertex("b", "d")
|
|
|
|
first := graph.BFS("a")
|
|
second := graph.BFS("a")
|
|
|
|
if !slices.Equal(first, second) {
|
|
t.Errorf("BFS not repeatable: %v then %v", first, second)
|
|
}
|
|
}
|
|
|
|
func assertNoDuplicates[T comparable](t *testing.T, order []T) {
|
|
t.Helper()
|
|
|
|
seen := make(map[T]bool, len(order))
|
|
for _, vertex := range order {
|
|
if seen[vertex] {
|
|
t.Errorf("vertex %v visited more than once in %v", vertex, order)
|
|
}
|
|
seen[vertex] = true
|
|
}
|
|
}
|