@@ -2,6 +2,9 @@ package com.dimafeng.testcontainers
2
2
3
3
import java .util .function .Consumer
4
4
5
+ import com .dimafeng .testcontainers .TestContainers .TestContainersSuite
6
+ import com .dimafeng .testcontainers .lifecycle .TestLifecycleAware
7
+ import org .junit .runner .{Description => JunitDescription }
5
8
import com .github .dockerjava .api .DockerClient
6
9
import com .github .dockerjava .api .command .{CreateContainerCmd , InspectContainerResponse }
7
10
import com .github .dockerjava .api .model .{Bind , Info , VolumesFrom }
@@ -11,60 +14,113 @@ import org.testcontainers.containers.output.OutputFrame
11
14
import org .testcontainers .containers .startupcheck .StartupCheckStrategy
12
15
import org .testcontainers .containers .traits .LinkableContainer
13
16
import org .testcontainers .containers .{FailureDetectingExternalResource , Network , TestContainerAccessor , GenericContainer => OTCGenericContainer }
17
+ import org .testcontainers .lifecycle .{Startable , TestDescription }
14
18
15
19
import scala .collection .JavaConverters ._
16
20
import scala .concurrent .{Future , blocking }
17
21
18
- trait ForEachTestContainer extends SuiteMixin {
19
- self : Suite =>
22
+ private [testcontainers] object TestContainers {
23
+
24
+ implicit def junit2testContainersDescription (junit : JunitDescription ): TestDescription = {
25
+ new TestDescription {
26
+ override def getTestId : String = junit.getDisplayName
27
+ override def getFilesystemFriendlyName : String = s " ${junit.getClassName}- ${junit.getMethodName}"
28
+ }
29
+ }
30
+
31
+ // Copy-pasted from `org.scalatest.junit.JUnitRunner.createDescription`
32
+ def createDescription (suite : Suite ): JunitDescription = {
33
+ val description = JunitDescription .createSuiteDescription(suite.getClass)
34
+ // If we don't add the testNames and nested suites in, we get
35
+ // Unrooted Tests show up in Eclipse
36
+ for (name <- suite.testNames) {
37
+ description.addChild(JunitDescription .createTestDescription(suite.getClass, name))
38
+ }
39
+ for (nestedSuite <- suite.nestedSuites) {
40
+ description.addChild(createDescription(nestedSuite))
41
+ }
42
+ description
43
+ }
44
+
45
+ trait TestContainersSuite extends SuiteMixin { self : Suite =>
46
+
47
+ val container : Container
48
+
49
+ def afterStart (): Unit = {}
50
+
51
+ def beforeStop (): Unit = {}
52
+
53
+ private val suiteDescription = createDescription(self)
20
54
21
- val container : Container
55
+ private [testcontainers] def beforeTest (): Unit = {
56
+ container match {
57
+ case container : TestLifecycleAware => container.beforeTest(suiteDescription)
58
+ case _ => // do nothing
59
+ }
60
+ }
22
61
23
- implicit private val suiteDescription = Description .createSuiteDescription(self.getClass)
62
+ private [testcontainers] def afterTest (throwable : Option [Throwable ]): Unit = {
63
+ container match {
64
+ case container : TestLifecycleAware => container.afterTest(suiteDescription, throwable)
65
+ case _ => // do nothing
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ trait ForEachTestContainer extends TestContainersSuite {
72
+ self : Suite =>
24
73
25
74
abstract protected override def runTest (testName : String , args : Args ): Status = {
26
- container.starting()
75
+ container.start()
76
+
77
+ @ volatile var testCalled = false
78
+ @ volatile var afterTestCalled = false
79
+
27
80
try {
28
81
afterStart()
82
+ beforeTest()
83
+
84
+ testCalled = true
29
85
val status = super .runTest(testName, args)
30
- status match {
31
- case FailedStatus => container.failed(new RuntimeException (status.toString))
32
- case _ => container.succeeded()
86
+
87
+ afterTestCalled = true
88
+ if (! status.succeeds()) {
89
+ afterTest(Some (new RuntimeException (" Test failed" )))
90
+ } else {
91
+ afterTest(None )
33
92
}
93
+
34
94
status
35
95
}
36
96
catch {
37
97
case e : Throwable =>
38
- container.failed(e)
98
+ if (testCalled && ! afterTestCalled) {
99
+ afterTestCalled = true
100
+ afterTest(Some (e))
101
+ }
102
+
39
103
throw e
40
104
}
41
105
finally {
42
106
try {
43
107
beforeStop()
44
108
}
45
109
finally {
46
- container.finished ()
110
+ container.stop ()
47
111
}
48
112
}
49
113
}
50
-
51
- def afterStart (): Unit = {}
52
-
53
- def beforeStop (): Unit = {}
54
114
}
55
115
56
- trait ForAllTestContainer extends SuiteMixin {
116
+ trait ForAllTestContainer extends TestContainersSuite {
57
117
self : Suite =>
58
118
59
- val container : Container
60
-
61
- implicit private val suiteDescription = Description .createSuiteDescription(self.getClass)
62
-
63
119
abstract override def run (testName : Option [String ], args : Args ): Status = {
64
120
if (expectedTestCount(args.filter) == 0 ) {
65
121
new CompositeStatus (Set .empty)
66
122
} else {
67
- container.starting ()
123
+ container.start ()
68
124
try {
69
125
afterStart()
70
126
super .run(testName, args)
@@ -73,43 +129,82 @@ trait ForAllTestContainer extends SuiteMixin {
73
129
beforeStop()
74
130
}
75
131
finally {
76
- container.finished ()
132
+ container.stop ()
77
133
}
78
134
}
79
135
}
80
136
}
81
137
82
- def afterStart (): Unit = {}
138
+ abstract protected override def runTest (testName : String , args : Args ): Status = {
139
+ @ volatile var testCalled = false
140
+ @ volatile var afterTestCalled = false
141
+
142
+ try {
143
+ beforeTest()
144
+
145
+ testCalled = true
146
+ val status = super .runTest(testName, args)
147
+
148
+ afterTestCalled = true
149
+ if (! status.succeeds()) {
150
+ afterTest(Some (new RuntimeException (" Test failed" )))
151
+ } else {
152
+ afterTest(None )
153
+ }
83
154
84
- def beforeStop (): Unit = {}
155
+ status
156
+ }
157
+ catch {
158
+ case e : Throwable =>
159
+ if (testCalled && ! afterTestCalled) {
160
+ afterTestCalled = true
161
+ afterTest(Some (e))
162
+ }
163
+
164
+ throw e
165
+ }
166
+ }
85
167
}
86
168
87
- trait Container {
88
- def finished ()(implicit description : Description ): Unit
169
+ trait Container extends Startable {
89
170
90
- def failed (e : Throwable )(implicit description : Description ): Unit
171
+ @ deprecated(" Use `stop` instead" )
172
+ def finished ()(implicit description : Description ): Unit = stop()
91
173
92
- def starting ()(implicit description : Description ): Unit
174
+ @ deprecated(" Use `stop` and/or `TestLifecycleAware.afterTest` instead" )
175
+ def failed (e : Throwable )(implicit description : Description ): Unit = {}
93
176
94
- def succeeded ()(implicit description : Description ): Unit
177
+ @ deprecated(" Use `start` instead" )
178
+ def starting ()(implicit description : Description ): Unit = start()
179
+
180
+ @ deprecated(" Use `stop` and/or `TestLifecycleAware.afterTest` instead" )
181
+ def succeeded ()(implicit description : Description ): Unit = {}
95
182
}
96
183
97
184
trait TestContainerProxy [T <: FailureDetectingExternalResource ] extends Container {
98
185
99
186
@ deprecated(" Please use reflective methods from the wrapper and `configure` method for creation" )
100
- implicit val container : T
187
+ implicit def container : T
101
188
189
+ @ deprecated(" Use `stop` instead" )
102
190
override def finished ()(implicit description : Description ): Unit = TestContainerAccessor .finished(description)
103
191
192
+ @ deprecated(" Use `stop` and/or `TestLifecycleAware.afterTest` instead" )
104
193
override def succeeded ()(implicit description : Description ): Unit = TestContainerAccessor .succeeded(description)
105
194
195
+ @ deprecated(" Use `start` instead" )
106
196
override def starting ()(implicit description : Description ): Unit = TestContainerAccessor .starting(description)
107
197
198
+ @ deprecated(" Use `stop` and/or `TestLifecycleAware.afterTest` instead" )
108
199
override def failed (e : Throwable )(implicit description : Description ): Unit = TestContainerAccessor .failed(e, description)
109
200
}
110
201
111
202
abstract class SingleContainer [T <: OTCGenericContainer [_]] extends TestContainerProxy [T ] {
112
203
204
+ override def start (): Unit = container.start()
205
+
206
+ override def stop (): Unit = container.stop()
207
+
113
208
def binds : Seq [Bind ] = container.getBinds.asScala.toSeq
114
209
115
210
def command : Seq [String ] = container.getCommandParts
0 commit comments