Skip to content

Commit 81c809e

Browse files
author
Jegors Cemisovs
committed
Add tests for subproject
1 parent 74afa68 commit 81c809e

File tree

6 files changed

+417
-1
lines changed

6 files changed

+417
-1
lines changed

algorithm/build.gradle

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
plugins {
2+
id 'groovy'
23
id 'java-library'
34
id 'maven-publish'
45
}
@@ -41,4 +42,22 @@ publishing {
4142
}
4243
}
4344
}
44-
}
45+
}
46+
47+
dependencies {
48+
// Spock Framework
49+
testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0'
50+
testImplementation 'org.codehaus.groovy:groovy-all:3.0.9'
51+
52+
// Spock Reports
53+
testRuntimeClasspath( "com.athaydes:spock-reports:2.1.1-groovy-3.0" ) {
54+
transitive = false // this avoids affecting your version of Groovy/Spock
55+
}
56+
// Required for spock-reports
57+
testImplementation 'org.slf4j:slf4j-api:1.7.32'
58+
testRuntimeClasspath 'org.slf4j:slf4j-simple:1.7.32'
59+
}
60+
61+
test {
62+
useJUnitPlatform()
63+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package lv.id.jc.graph
2+
3+
import lv.id.jc.algorithm.graph.BreadthFirstSearch
4+
import lv.id.jc.algorithm.graph.Graph
5+
import spock.lang.*
6+
7+
@Title("Breadth First Search Algorithm")
8+
@See("https://en.wikipedia.org/wiki/Breadth-first_search")
9+
@Narrative("""
10+
Breadth First Search algorithm for finding the shortest paths between nodes in a graph
11+
""")
12+
class BreadthFirstSearchSpec extends Specification {
13+
@Subject
14+
def algorithm = new BreadthFirstSearch()
15+
16+
def 'should find a route for simple graph'() {
17+
given: 'A simple graph'
18+
def graph = Graph.of([
19+
A: [B: 7, C: 2],
20+
B: [A: 3, C: 5],
21+
C: [A: 1, B: 3]
22+
])
23+
24+
when: 'we use Breadth First Search algorithm to find a path'
25+
def path = algorithm.findPath(graph, source, target)
26+
27+
then: 'we get the shortest path'
28+
path == shortest
29+
30+
and: 'the distance calculated correctly'
31+
graph.getDistance(path) == time as double
32+
33+
where:
34+
source | target || time | shortest
35+
'A' | 'A' || 0 | ['A']
36+
'A' | 'B' || 7 | ['A', 'B']
37+
'B' | 'C' || 5 | ['B', 'C']
38+
'C' | 'B' || 3 | ['C', 'B']
39+
}
40+
41+
def 'should find a route for complex graph'() {
42+
given: 'A complex graph'
43+
def graph = Graph.of([
44+
A: [B: 1],
45+
B: [A: 1, D: 1],
46+
C: [A: 1],
47+
D: [C: 1, E: 1],
48+
E: [F: 1],
49+
F: [D: 1, E: 1]])
50+
51+
when: 'we use Breadth First Search algorithm to find a path'
52+
def path = algorithm.findPath(graph, source, target)
53+
54+
then: 'we get the shortest path'
55+
path == shortest
56+
57+
and: 'the distance calculated correctly'
58+
graph.getDistance(path) == time as double
59+
60+
where:
61+
source | target || shortest
62+
'A' | 'A' || ['A']
63+
'B' | 'B' || ['B']
64+
'A' | 'B' || ['A', 'B']
65+
'B' | 'A' || ['B', 'A']
66+
'A' | 'C' || ['A', 'B', 'D', 'C']
67+
'C' | 'A' || ['C', 'A']
68+
'E' | 'B' || ['E', 'F', 'D', 'C', 'A', 'B']
69+
70+
and:
71+
time = shortest.size() - 1
72+
}
73+
74+
def 'should thrown NPE path for an empty graph'() {
75+
given: 'an empty graph'
76+
def graph = Graph.of([:])
77+
78+
when: "we use Dijkstra's algorithm to find a path"
79+
algorithm.findPath(graph, 'A', 'B')
80+
81+
then: 'the exception was thrown'
82+
thrown NullPointerException
83+
}
84+
85+
def "should return an empty path if can't find a route"() {
86+
given: 'a simple graph with no edge between nodes'
87+
def graph = Graph.of([A: [:], B: [:]])
88+
89+
when: 'we use Breadth First Search algorithm to find a path'
90+
def path = algorithm.findPath(graph, 'A', 'B')
91+
92+
then: 'we get an empty path'
93+
path == []
94+
}
95+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package lv.id.jc.graph
2+
3+
import lv.id.jc.algorithm.graph.DijkstrasAlgorithm
4+
import lv.id.jc.algorithm.graph.Graph
5+
import spock.lang.*
6+
7+
@Title("Dijkstra's Algorithm")
8+
@See("https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm")
9+
@Narrative("Dijkstra's algorithm is an algorithm for finding the fastest paths between nodes in a graph")
10+
class DijkstrasAlgorithmSpec extends Specification {
11+
@Subject
12+
def algorithm = new DijkstrasAlgorithm()
13+
14+
def 'should find a route for a simple graph'() {
15+
given: 'A simple graph'
16+
def graph = Graph.of([
17+
A: [B: 7, C: 2],
18+
B: [A: 3, C: 5],
19+
C: [A: 1, B: 3]
20+
])
21+
22+
when: "we use Dijkstra's algorithm to find a path"
23+
def path = algorithm.findPath(graph, source, target)
24+
25+
then: 'we get the fastest way'
26+
path == fastest
27+
28+
and: 'the distance calculated correctly'
29+
graph.getDistance(path) == time as double
30+
31+
where:
32+
source | target || time | fastest
33+
'A' | 'A' || 0 | ['A']
34+
'B' | 'B' || 0 | ['B']
35+
'C' | 'C' || 0 | ['C']
36+
'A' | 'B' || 5 | ['A', 'C', 'B']
37+
}
38+
39+
def 'should find a route for a medium graph'() {
40+
given: 'A medium graph'
41+
def graph = Graph.of([
42+
A: [B: 5],
43+
B: [A: 5, C: 10],
44+
C: [B: 20, D: 5],
45+
D: [E: 5],
46+
E: [B: 5]
47+
])
48+
49+
when: "we use Dijkstra's algorithm to find a path"
50+
def path = algorithm.findPath(graph, source, target)
51+
52+
then: 'we get the fastest way'
53+
path == fastest
54+
55+
and: 'the distance calculated correctly'
56+
graph.getDistance(path) == time as double
57+
58+
where:
59+
source | target || time | fastest
60+
'A' | 'A' || 0 | ['A']
61+
'B' | 'B' || 0 | ['B']
62+
'A' | 'B' || 5 | ['A', 'B']
63+
'B' | 'A' || 5 | ['B', 'A']
64+
'A' | 'C' || 15 | ['A', 'B', 'C']
65+
'C' | 'A' || 20 | ['C', 'D', 'E', 'B', 'A']
66+
}
67+
68+
def 'should find a route for a complex graph'() {
69+
given: 'A complex graph'
70+
def graph = Graph.of([
71+
A: [B: 5, H: 2],
72+
B: [A: 5, C: 7],
73+
C: [B: 7, D: 3, G: 4],
74+
D: [C: 20, E: 4],
75+
E: [F: 5],
76+
F: [G: 6],
77+
G: [C: 4],
78+
H: [G: 3]
79+
])
80+
81+
when: "we use Dijkstra's algorithm to find a path"
82+
def path = algorithm.findPath(graph, source, target)
83+
84+
then: 'we get the fastest way'
85+
path == fastest
86+
87+
and: 'the distance calculated correctly'
88+
graph.getDistance(path) == time as double
89+
90+
where:
91+
source | target || time | fastest
92+
'A' | 'A' || 0 | ['A']
93+
'B' | 'B' || 0 | ['B']
94+
'A' | 'B' || 5 | ['A', 'B']
95+
'B' | 'A' || 5 | ['B', 'A']
96+
'A' | 'C' || 9 | ['A', 'H', 'G', 'C']
97+
'C' | 'A' || 12 | ['C', 'B', 'A']
98+
'A' | 'G' || 5 | ['A', 'H', 'G']
99+
'C' | 'D' || 3 | ['C', 'D']
100+
'D' | 'C' || 19 | ['D', 'E', 'F', 'G', 'C']
101+
'B' | 'D' || 10 | ['B', 'C', 'D']
102+
'D' | 'B' || 26 | ['D', 'E', 'F', 'G', 'C', 'B']
103+
'D' | 'H' || 33 | ['D', 'E', 'F', 'G', 'C', 'B', 'A', 'H']
104+
}
105+
106+
def 'should thrown NPE for an empty graph'() {
107+
given: 'an empty graph'
108+
def graph = Graph.of([:])
109+
110+
when: "we use Dijkstra's algorithm to find a path"
111+
algorithm.findPath(graph, 'A', 'B')
112+
113+
then: 'the exception was thrown'
114+
thrown NullPointerException
115+
}
116+
117+
def "should return an empty path if can't find a route"() {
118+
given: 'a simple graph with no edge between nodes'
119+
def graph = Graph.of([A: [:], B: [:]])
120+
121+
when: "we use Dijkstra's algorithm to find a path"
122+
def path = algorithm.findPath(graph, 'A', 'B')
123+
124+
then: 'we get an empty path'
125+
path == []
126+
}
127+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package lv.id.jc.graph
2+
3+
import lv.id.jc.algorithm.graph.Graph
4+
import spock.lang.Narrative
5+
import spock.lang.Specification
6+
import spock.lang.Title
7+
8+
@Title("Generic Graph")
9+
@Narrative("A generic implementation of Graph structure")
10+
class GraphSpec extends Specification {
11+
12+
def "should return edges for a given node"() {
13+
given: 'a simple graph with three nodes'
14+
def graph = Graph.of([
15+
A: [B: 7, C: 2],
16+
B: [A: 3, C: 5],
17+
C: [A: 1, B: 3]
18+
])
19+
20+
expect: 'The method returns expected edges for given node'
21+
graph.edges(node) == expected
22+
23+
where:
24+
node | expected
25+
'A' | [B: 7, C: 2]
26+
'B' | [A: 3, C: 5]
27+
'C' | [A: 1, B: 3]
28+
}
29+
30+
def 'should calculate distance for a path'() {
31+
given: "a complex graph with eight nodes"
32+
def graph = Graph.of([
33+
A: [B: 5, H: 2],
34+
B: [A: 5, C: 7],
35+
C: [B: 7, D: 3, G: 4],
36+
D: [C: 20, E: 4],
37+
E: [F: 5],
38+
F: [G: 6],
39+
G: [C: 4],
40+
H: [G: 3]
41+
])
42+
43+
expect: 'the distance for a path correctly calculated'
44+
graph.getDistance(path) == distance as double
45+
46+
where: 'path and expected distance'
47+
path | distance
48+
['A'] | 0
49+
['A', 'B'] | 5
50+
['B', 'A'] | 5
51+
['A', 'B', 'A'] | 10
52+
['A', 'B', 'A', 'B'] | 15
53+
['C', 'D'] | 3
54+
['D', 'C'] | 20
55+
['D', 'E', 'F', 'G', 'C'] | 19
56+
}
57+
58+
def 'should be zero distance for an empty path'() {
59+
given: 'any graph'
60+
def graph = Graph.of(_ as Map)
61+
62+
expect: 'the distance is zero for an empty path'
63+
graph.getDistance([]) == 0
64+
}
65+
66+
def 'should be zero distance for any one node path'() {
67+
given: 'any graph'
68+
def graph = Graph.of(_ as Map)
69+
70+
expect: 'the zero distance for any one-node path'
71+
graph.getDistance(oneNodePath) == 0
72+
73+
where: 'the node may be of any type and even non-existent'
74+
oneNodePath << [
75+
['A'], ['B'], [2], ['X' as char], [12.56]
76+
]
77+
}
78+
79+
def 'should throw NPE for incorrect path'() {
80+
given: "a medium graph with five nodes"
81+
def graph = Graph.of([
82+
A: [B: 5],
83+
B: [A: 5, C: 10],
84+
C: [B: 20, D: 5],
85+
D: [E: 5],
86+
E: [B: 5]
87+
])
88+
89+
when: 'we call the method with incorrect path'
90+
graph.getDistance(incorrectPath)
91+
92+
then: 'the NPE thrown'
93+
thrown NullPointerException
94+
95+
where: 'path is more then one node'
96+
incorrectPath << [
97+
['E', 'D'], ['A', 'C'], ['A', 'B', 'D']
98+
]
99+
}
100+
}

0 commit comments

Comments
 (0)