Skip to content

Commit 4757248

Browse files
committed
+testkit,actorable #248 ActorableTestProbe
resolves #248
1 parent 32bf2d2 commit 4757248

21 files changed

+635
-115
lines changed

Docs/actorables.adoc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,13 @@ swift run GenActors
302302
The GenActors command by default inspects the `Sources` directory for files and detects top level types conforming to the
303303
api:Actorable[protocol] protocol. Optionally, you may pass folder paths or file names explicitly.
304304

305+
=== Actorables and Serialization
306+
307+
#TODO: This needs a remake of the serialization story as we want them to automatically "just work".#
308+
309+
Currently no special codegen is enabled for Actorables, so in order to send messages to other processes or nodes,
310+
you will have to register serializers same as in the behavior style, see: <<serialization>>.
311+
305312
=== Actorable.Message and Serialization
306313

307314
`GenActors` also generates an additional file suffixed `+GenCodable.swift` which contains `Codable` conformance for an actorable's `Message` type.
@@ -320,3 +327,34 @@ You still have to *register* the the type with the serialization infrastructure
320327

321328
TIP: We aim to simplify the serialization infrastructure to be able to infer more about what serializer to use for a given type,
322329
however the current approach remains valid (and most resilient in face of type refactorings).
330+
331+
=== ActorTestKit and ActorableTestProbe
332+
333+
An `ActorTestKit` is offered to simplify the testing of actors and asynchronous code.
334+
The most important part of the test kit is api:ActorTestProbe[class] (for `ActorRef` / `Behavior` style)
335+
and api:ActorableTestProbe[class] for actorables.
336+
337+
#TODO: Write full docs for ActorTestKit (and reconsider naming... w discussed but are unsure still ActorTestTools was an option)#
338+
339+
After creating an api:ActorTestKit[class] for an api:ActorSystem[class], one is able to `testKit.spawnActorableTestProbe` a probe for any given api:Actorable[protocol].
340+
The returned api:ActorableTestProbe[class] contains the "mocked" `.actor` reference as well as various `expectMessage`-style methods, which allow
341+
the test to block for a time, expecting a message to be send to the underlying test probe (configurable using the optional `within:` parameter).
342+
343+
For example, in order to mock out a reference to such an `GreetMe` actorable:
344+
345+
[source]
346+
----
347+
include::{dir_sact_doc_tests}/Actorable/ActorableActorTestKitDocExamples.swift[tag=actorable]
348+
----
349+
350+
You may spawn a probe for it, and expect that it receives a hello message when passed to another actor:
351+
352+
[source]
353+
----
354+
include::{dir_sact_doc_tests}/Actorable/ActorableActorTestKitDocExamples.swift[tag=full_testkit_example]
355+
----
356+
<1> Spawn a test probe, abiding to `GreetMe`'s message protocol
357+
<2> Spawn a real `GreetMeGreeter` actor, and tell it to greet the probe actor
358+
<3> Expect that the probe gets the expected message, and that it's contents are indeed correct
359+
360+
NOTE: To learn more about all the available expectations on test probes, refer to api:ActorableTestProbe[class] API documentation.

Samples/Sources/XPCActorServiceAPI/GenActors/GreetingsService+GenActor.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,6 @@
22
// === DO NOT EDIT: Generated by GenActors
33
// ==== ------------------------------------------------------------------ ====
44

5-
//===----------------------------------------------------------------------===//
6-
//
7-
// This source file is part of the Swift Distributed Actors open source project
8-
//
9-
// Copyright (c) 2019 Apple Inc. and the Swift Distributed Actors project authors
10-
// Licensed under Apache License v2.0
11-
//
12-
// See LICENSE.txt for license information
13-
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
14-
//
15-
// SPDX-License-Identifier: Apache-2.0
16-
//
17-
//===----------------------------------------------------------------------===//
185

196
// tag::xpc_example[]
207
import DistributedActors

Samples/Sources/XPCActorServiceAPI/GenActors/GreetingsService+GenCodable.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,6 @@
22
// === DO NOT EDIT: Generated by GenActors
33
// ==== ------------------------------------------------------------------ ====
44

5-
//===----------------------------------------------------------------------===//
6-
//
7-
// This source file is part of the Swift Distributed Actors open source project
8-
//
9-
// Copyright (c) 2019 Apple Inc. and the Swift Distributed Actors project authors
10-
// Licensed under Apache License v2.0
11-
//
12-
// See LICENSE.txt for license information
13-
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
14-
//
15-
// SPDX-License-Identifier: Apache-2.0
16-
//
17-
//===----------------------------------------------------------------------===//
185

196
// tag::xpc_example[]
207
import DistributedActors

Samples/Sources/XPCActorServiceAPI/GenActors/GreetingsService+XPCProtocolStub.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,6 @@
22
// === DO NOT EDIT: Generated by GenActors
33
// ==== ------------------------------------------------------------------ ====
44

5-
//===----------------------------------------------------------------------===//
6-
//
7-
// This source file is part of the Swift Distributed Actors open source project
8-
//
9-
// Copyright (c) 2019 Apple Inc. and the Swift Distributed Actors project authors
10-
// Licensed under Apache License v2.0
11-
//
12-
// See LICENSE.txt for license information
13-
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
14-
//
15-
// SPDX-License-Identifier: Apache-2.0
16-
//
17-
//===----------------------------------------------------------------------===//
185

196
// tag::xpc_example[]
207
import DistributedActors

Samples/Sources/XPCActorServiceAPI/GenActors/GreetingsServiceStub+GenActor.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,6 @@
66
// === DO NOT EDIT: Generated by GenActors
77
// ==== ------------------------------------------------------------------ ====
88

9-
//===----------------------------------------------------------------------===//
10-
//
11-
// This source file is part of the Swift Distributed Actors open source project
12-
//
13-
// Copyright (c) 2019 Apple Inc. and the Swift Distributed Actors project authors
14-
// Licensed under Apache License v2.0
15-
//
16-
// See LICENSE.txt for license information
17-
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
18-
//
19-
// SPDX-License-Identifier: Apache-2.0
20-
//
21-
//===----------------------------------------------------------------------===//
229

2310
// tag::xpc_example[]
2411
import DistributedActors

Samples/Sources/XPCActorServiceAPI/GenActors/GreetingsServiceStub+GenCodable.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,6 @@
66
// === DO NOT EDIT: Generated by GenActors
77
// ==== ------------------------------------------------------------------ ====
88

9-
//===----------------------------------------------------------------------===//
10-
//
11-
// This source file is part of the Swift Distributed Actors open source project
12-
//
13-
// Copyright (c) 2019 Apple Inc. and the Swift Distributed Actors project authors
14-
// Licensed under Apache License v2.0
15-
//
16-
// See LICENSE.txt for license information
17-
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
18-
//
19-
// SPDX-License-Identifier: Apache-2.0
20-
//
21-
//===----------------------------------------------------------------------===//
229

2310
// tag::xpc_example[]
2411
import DistributedActors

Samples/Sources/XPCActorServiceProvider/GenActors/GreetingsServiceImpl+GenActor.swift

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,7 @@
22
// === DO NOT EDIT: Generated by GenActors
33
// ==== ------------------------------------------------------------------ ====
44

5-
//===----------------------------------------------------------------------===//
6-
//
7-
// This source file is part of the Swift Distributed Actors open source project
8-
//
9-
// Copyright (c) 2019 Apple Inc. and the Swift Distributed Actors project authors
10-
// Licensed under Apache License v2.0
11-
//
12-
// See LICENSE.txt for license information
13-
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
14-
//
15-
// SPDX-License-Identifier: Apache-2.0
16-
//
17-
//===----------------------------------------------------------------------===//
5+
186

197
import DistributedActors
208
import XPCActorServiceAPI

Samples/Sources/XPCActorServiceProvider/GenActors/GreetingsServiceImpl+GenCodable.swift

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,7 @@
22
// === DO NOT EDIT: Generated by GenActors
33
// ==== ------------------------------------------------------------------ ====
44

5-
//===----------------------------------------------------------------------===//
6-
//
7-
// This source file is part of the Swift Distributed Actors open source project
8-
//
9-
// Copyright (c) 2019 Apple Inc. and the Swift Distributed Actors project authors
10-
// Licensed under Apache License v2.0
11-
//
12-
// See LICENSE.txt for license information
13-
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
14-
//
15-
// SPDX-License-Identifier: Apache-2.0
16-
//
17-
//===----------------------------------------------------------------------===//
5+
186

197
import DistributedActors
208
import XPCActorServiceAPI

Sources/DistributedActorsTestKit/ActorTestKit.swift

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ public struct ActorTestKitSettings {
6363
// ==== ----------------------------------------------------------------------------------------------------------------
6464
// MARK: Test Probes
6565

66-
public extension ActorTestKit {
66+
extension ActorTestKit {
6767
/// Spawn an `ActorTestProbe` which offers various assertion methods for actor messaging interactions.
68-
func spawnTestProbe<M>(_ naming: ActorNaming? = nil, expecting type: M.Type = M.self, file: StaticString = #file, line: UInt = #line) -> ActorTestProbe<M> {
68+
public func spawnTestProbe<M>(_ naming: ActorNaming? = nil, expecting type: M.Type = M.self, file: StaticString = #file, line: UInt = #line) -> ActorTestProbe<M> {
6969
self.spawnProbesLock.lock()
7070
defer { self.spawnProbesLock.unlock() }
7171
// we want to use our own sequence number for the naming here, so we make it here rather than let the
@@ -88,6 +88,43 @@ public extension ActorTestKit {
8888
return try system.spawn(.init(unchecked: .unique(name)), props: testProbeProps, probeBehavior)
8989
}, settings: self.settings)
9090
}
91+
92+
/// Spawn `ActorableTestProbe` which offers various assertions for actor messaging interactions.
93+
public func spawnActorableTestProbe<A: Actorable>(_ naming: ActorNaming? = nil, of actorable: A.Type = A.self, file: StaticString = #file, line: UInt = #line)
94+
-> ActorableTestProbe<A> {
95+
self.spawnProbesLock.lock()
96+
defer { self.spawnProbesLock.unlock() }
97+
// we want to use our own sequence number for the naming here, so we make it here rather than let the
98+
// system use its own sequence number -- which should only be in use for the user actors.
99+
let name: String
100+
if let naming = naming {
101+
name = naming.makeName(&self._namingContext)
102+
} else {
103+
name = ActorTestProbe<A.Message>.naming.makeName(&self._namingContext)
104+
}
105+
106+
return ActorableTestProbe(spawn: { probeBehavior in
107+
108+
// TODO: allow configuring dispatcher for the probe or always use the calling thread one
109+
var testProbeProps = Props()
110+
#if SACT_PROBE_CALLING_THREAD
111+
testProbeProps.dispatcher = .callingThread
112+
#endif
113+
114+
return try system.spawn(.init(unchecked: .unique(name)), props: testProbeProps, probeBehavior)
115+
}, settings: self.settings)
116+
}
117+
}
118+
119+
/// A test probe pretends to be the `Actorable` and allows expecting messages be sent to it.
120+
///
121+
/// - SeeAlso: `ActorTestProbe` which is the equivalent API for `ActorRef`s.
122+
public final class ActorableTestProbe<A: Actorable>: ActorTestProbe<A.Message> {
123+
/// `Actor` reference to the underlying test probe actor.
124+
/// All message sends invoked on this Actor will result in messages in the probe available to be `expect`-ed.
125+
public var actor: Actor<A> {
126+
return Actor(ref: self.ref)
127+
}
91128
}
92129

93130
// ==== ----------------------------------------------------------------------------------------------------------------

Sources/DistributedActorsTestKit/TestProbes.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,32 @@ extension ActorTestProbeCommand: NoSerializationVerification {}
2929

3030
/// A special actor that can be used in place of real actors, yet in addition exposes useful assertion methods
3131
/// which make testing asynchronous actor interactions simpler.
32-
public final class ActorTestProbe<Message> {
32+
///
33+
/// - SeeAlso: `ActorableTestProbe` which is the equivalent API for `Actorable`s.
34+
public class ActorTestProbe<Message> {
35+
/// Name of the test probe (and underlying actor).
3336
public let name: String
37+
38+
/// Naming strategy for anonymous test probes.
39+
/// By default test probes are named as `$testProbe-###`.
3440
public static var naming: ActorNaming {
3541
// has to be computed property since: static stored properties are not supported in generic types
36-
return ActorNaming(unchecked: .prefixed(prefix: "$testProbe", suffixScheme: .sequentialNumeric))
42+
ActorNaming(unchecked: .prefixed(prefix: "$testProbe", suffixScheme: .sequentialNumeric))
3743
}
3844

3945
typealias ProbeCommands = ActorTestProbeCommand<Message>
4046
internal let internalRef: ActorRef<ProbeCommands>
4147
internal let exposedRef: ActorRef<Message>
4248

49+
/// The reference to the underlying "mock" actor.
50+
/// Sending messages to this reference allows the probe to inspect them using the `expect...` family of functions.
4351
public var ref: ActorRef<Message> {
44-
return self.exposedRef
52+
self.exposedRef
4553
}
4654

4755
private let settings: ActorTestKitSettings
4856
private var expectationTimeout: TimeAmount {
49-
return self.settings.expectationTimeout
57+
self.settings.expectationTimeout
5058
}
5159

5260
/// Blocking linked queue, available to run assertions on

0 commit comments

Comments
 (0)