0
# Cross-Platform Support
1
2
ZIO Test SBT provides cross-platform support for JVM, JavaScript, and Native environments with platform-specific optimizations and distributed testing capabilities.
3
4
## Platform Architecture
5
6
### JVM Platform
7
8
Simple single-process execution model:
9
10
```scala { .api }
11
class ZTestRunner(
12
args: Array[String],
13
remoteArgs: Array[String],
14
testClassLoader: ClassLoader
15
) extends Runner {
16
val summaries: AtomicReference[Vector[Summary]]
17
val sendSummary: SendSummary
18
def done(): String
19
def tasks(defs: Array[TaskDef]): Array[Task]
20
}
21
```
22
23
**Key Features:**
24
- Thread-safe summary collection using `AtomicReference`
25
- Direct task execution without continuation support
26
- Simple task policy system
27
28
### JavaScript/Native Platforms
29
30
Master-slave distributed execution model:
31
32
```scala { .api }
33
sealed abstract class ZTestRunner(
34
args: Array[String],
35
remoteArgs: Array[String],
36
testClassLoader: ClassLoader,
37
runnerType: String
38
) extends Runner {
39
val summaries: mutable.Buffer[Summary]
40
def sendSummary: SendSummary
41
def receiveMessage(summary: String): Option[String]
42
def serializeTask(task: Task, serializer: TaskDef => String): String
43
def deserializeTask(task: String, deserializer: String => TaskDef): Task
44
}
45
```
46
47
**Key Features:**
48
- Message-based communication between master and slave runners
49
- Task serialization/deserialization support
50
- Asynchronous task execution with continuations
51
52
## Runner Implementations
53
54
### ZMasterTestRunner (JS/Native)
55
56
Coordinates distributed test execution:
57
58
```scala { .api }
59
class ZMasterTestRunner(
60
args: Array[String],
61
remoteArgs: Array[String],
62
testClassLoader: ClassLoader
63
) extends ZTestRunner(args, remoteArgs, testClassLoader, "master") {
64
val sendSummary: SendSummary
65
}
66
```
67
68
**Responsibilities:**
69
- Collects summaries from slave runners
70
- Coordinates overall test execution
71
- Provides final test results
72
73
### ZSlaveTestRunner (JS/Native)
74
75
Executes tests and reports back to master:
76
77
```scala { .api }
78
class ZSlaveTestRunner(
79
args: Array[String],
80
remoteArgs: Array[String],
81
testClassLoader: ClassLoader,
82
sendSummary: SendSummary
83
) extends ZTestRunner(args, remoteArgs, testClassLoader, "slave")
84
```
85
86
**Responsibilities:**
87
- Executes individual test tasks
88
- Sends results to master via serialized summaries
89
- Handles task deserialization from master
90
91
## Framework Integration
92
93
### Platform-Specific Framework Creation
94
95
**JVM:**
96
```scala { .api }
97
class ZTestFramework extends Framework {
98
def runner(args: Array[String], remoteArgs: Array[String], testClassLoader: ClassLoader): ZTestRunner =
99
new ZTestRunner(args, remoteArgs, testClassLoader)
100
}
101
```
102
103
**JavaScript/Native:**
104
```scala { .api }
105
class ZTestFramework extends Framework {
106
def runner(args: Array[String], remoteArgs: Array[String], testClassLoader: ClassLoader): Runner =
107
new ZMasterTestRunner(args, remoteArgs, testClassLoader)
108
109
def slaveRunner(
110
args: Array[String],
111
remoteArgs: Array[String],
112
testClassLoader: ClassLoader,
113
send: String => Unit
114
): Runner =
115
new ZSlaveTestRunner(
116
args, remoteArgs, testClassLoader,
117
SendSummary.fromSend(summary => send(SummaryProtocol.serialize(summary)))
118
)
119
}
120
```
121
122
## Summary Serialization
123
124
### SummaryProtocol (JS/Native)
125
126
Handles serialization/deserialization of test summaries for cross-process communication:
127
128
```scala { .api }
129
object SummaryProtocol {
130
def serialize(summary: Summary): String
131
def deserialize(s: String): Option[Summary]
132
def escape(token: String): String
133
def unescape(token: String): String
134
}
135
```
136
137
#### serialize()
138
139
Converts a `Summary` to a tab-separated string.
140
141
**Parameters:**
142
- `summary: Summary` - Test summary to serialize
143
144
**Returns:** `String` - Tab-separated string containing success count, fail count, ignore count, and summary text
145
146
**Format:** `"<success>\t<fail>\t<ignore>\t<summary>"`
147
148
#### deserialize()
149
150
Parses a serialized string back to a `Summary`.
151
152
**Parameters:**
153
- `s: String` - Serialized summary string
154
155
**Returns:** `Option[Summary]` - Parsed summary or `None` if parsing fails
156
157
#### escape() / unescape()
158
159
Utility methods for escaping tab characters in summary text:
160
161
**Parameters:**
162
- `token: String` - String to escape/unescape
163
164
**Returns:** `String` - Processed string with tabs escaped as `\\t` or vice versa
165
166
## Task Execution Differences
167
168
### JVM Task Execution
169
170
Synchronous execution with direct result handling:
171
172
```scala
173
def execute(eventHandler: EventHandler, loggers: Array[Logger]): Array[Task] = {
174
Runtime((), specInstance.platform).unsafeRun {
175
run(eventHandler).provideLayer(sbtTestLayer(loggers))
176
}
177
Array()
178
}
179
```
180
181
### JavaScript/Native Task Execution
182
183
Asynchronous execution with continuation support:
184
185
```scala
186
def execute(
187
eventHandler: EventHandler,
188
loggers: Array[Logger],
189
continuation: Array[Task] => Unit
190
): Unit = {
191
Runtime((), specInstance.platform)
192
.unsafeRunAsync(
193
(sbtTestLayer(loggers).build >>> run(eventHandler).toManaged_).use_(ZIO.unit)
194
) { exit =>
195
exit match {
196
case Exit.Failure(cause) => Console.err.println(s"$runnerType failed: " + cause.prettyPrint)
197
case _ =>
198
}
199
continuation(Array())
200
}
201
}
202
```
203
204
## Platform Detection
205
206
The appropriate implementation is selected at compile time based on the target platform:
207
208
- **JVM**: Uses `artifacts/original-repo/test-sbt/jvm/src/main/scala/`
209
- **JavaScript**: Uses `artifacts/original-repo/test-sbt/js/src/main/scala/`
210
- **Native**: Uses `artifacts/original-repo/test-sbt/native/src/main/scala/`
211
- **Shared**: Common code in `artifacts/original-repo/test-sbt/shared/src/main/scala/`
212
213
## Usage Considerations
214
215
### JVM Platform
216
- Suitable for traditional server-side and desktop applications
217
- Full ZIO and Scala standard library support
218
- Thread-safe concurrent execution
219
220
### JavaScript Platform
221
- Runs in browser or Node.js environments
222
- Limited to JavaScript-compatible libraries
223
- Single-threaded execution model with async support
224
225
### Native Platform
226
- Compiles to native executables
227
- Fast startup and low memory usage
228
- Limited library ecosystem
229
- Explicit method overrides required for some interfaces
230
231
## Message Flow Example
232
233
```scala
234
// 1. Master creates slave runner with send function
235
val slaveRunner = framework.slaveRunner(args, remoteArgs, classLoader, send)
236
237
// 2. Slave executes tests and sends results
238
val summary = Summary(1, 0, 0, "Test passed")
239
send(SummaryProtocol.serialize(summary)) // Sends: "1\t0\t0\tTest passed"
240
241
// 3. Master receives and deserializes
242
def receiveMessage(msg: String): Option[String] = {
243
SummaryProtocol.deserialize(msg).foreach(summaries += _)
244
None
245
}
246
247
// 4. Master provides final results
248
def done(): String = {
249
// Aggregate all received summaries
250
summaries.map(_.summary).mkString("\n") + "Done"
251
}
252
```