or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cross-platform-communication.mdevent-handling.mdframework-integration.mdindex.mdtest-execution.md

cross-platform-communication.mddocs/

0

# Cross-Platform Communication

1

2

The cross-platform communication system handles test result transmission across different platforms, with serialization support for JavaScript and Native platforms. It provides a unified interface for summary transmission while handling the platform-specific requirements for inter-process communication.

3

4

## Capabilities

5

6

### SendSummary Type Alias

7

8

Type alias that represents a ZIO effect for transmitting test summaries. It provides a consistent interface for summary transmission across all platforms.

9

10

```scala { .api }

11

/**

12

* ZIO effect for transmitting test summaries

13

* Provides consistent interface across all platforms

14

*/

15

type SendSummary = zio.URIO[zio.test.Summary, Unit]

16

```

17

18

This type alias encapsulates the pattern of taking a `Summary` and performing some transmission action (logging, sending to master process, etc.) without the possibility of failure.

19

20

### SendSummary Companion Object

21

22

Factory methods for creating `SendSummary` instances from different types of transmission functions.

23

24

```scala { .api }

25

object SendSummary {

26

/**

27

* Creates SendSummary from synchronous function

28

* @param send Function that processes summaries synchronously

29

* @return SendSummary effect that executes the function

30

*/

31

def fromSend(send: zio.test.Summary => Unit): SendSummary

32

33

/**

34

* Creates SendSummary from ZIO effect function

35

* @param send Function that returns a ZIO effect for processing summaries

36

* @return SendSummary effect that chains the provided effect

37

*/

38

def fromSendM(send: zio.test.Summary => zio.UIO[Unit]): SendSummary

39

40

/**

41

* No-operation SendSummary that discards summaries

42

* @return SendSummary that performs no action

43

*/

44

def noop: SendSummary

45

}

46

```

47

48

**Usage Examples:**

49

50

```scala

51

// Simple logging example

52

val loggingSummary = SendSummary.fromSend { summary =>

53

println(s"Test completed: ${summary.success} passed, ${summary.fail} failed")

54

}

55

56

// ZIO effect example

57

val asyncSummary = SendSummary.fromSendM { summary =>

58

ZIO.effect {

59

// Send to external system, write to database, etc.

60

externalReporter.report(summary)

61

}.catchAll(_ => ZIO.unit) // Never fail

62

}

63

64

// No-op for testing

65

val testSummary = SendSummary.noop

66

```

67

68

**Platform-Specific Implementations:**

69

70

**JVM Platform:**

71

```scala

72

// JVM runner collects summaries in AtomicReference

73

val sendSummary: SendSummary = SendSummary.fromSendM(summary =>

74

ZIO.effectTotal {

75

summaries.updateAndGet(_ :+ summary)

76

()

77

}

78

)

79

```

80

81

**JavaScript/Native Master:**

82

```scala

83

// Master runner collects summaries in mutable buffer

84

val sendSummary: SendSummary = SendSummary.fromSend { summary =>

85

summaries += summary

86

()

87

}

88

```

89

90

**JavaScript/Native Slave:**

91

```scala

92

// Slave runner serializes and transmits to master

93

val sendSummary: SendSummary = SendSummary.fromSend(summary =>

94

send(SummaryProtocol.serialize(summary))

95

)

96

```

97

98

### SummaryProtocol

99

100

Serialization protocol for transmitting test summaries across process boundaries on JavaScript and Native platforms. It handles the conversion between structured `Summary` objects and string representations.

101

102

```scala { .api }

103

/**

104

* Serialization protocol for test summaries

105

* Enables inter-process communication on JS/Native platforms

106

*/

107

object SummaryProtocol {

108

/**

109

* Serializes Summary to tab-delimited string

110

* @param summary Summary object to serialize

111

* @return Tab-delimited string representation

112

*/

113

def serialize(summary: zio.test.Summary): String

114

115

/**

116

* Deserializes string back to Summary object

117

* @param s Tab-delimited string from serialize()

118

* @return Some(Summary) if valid, None if malformed

119

*/

120

def deserialize(s: String): Option[zio.test.Summary]

121

122

/**

123

* Escapes tab characters in token strings

124

* @param token String token to escape

125

* @return String with tabs escaped as \\t

126

*/

127

def escape(token: String): String

128

129

/**

130

* Unescapes tab characters in token strings

131

* @param token String token to unescape

132

* @return String with \\t unescaped as tabs

133

*/

134

def unescape(token: String): String

135

}

136

```

137

138

**Serialization Format:**

139

140

The protocol uses tab-delimited format for simplicity and reliability:

141

142

```

143

success_count\tfail_count\tignore_count\tsummary_text

144

```

145

146

Where each field is escaped to handle tabs in the summary text.

147

148

**Usage Examples:**

149

150

```scala

151

import zio.test.Summary

152

import zio.test.sbt.SummaryProtocol

153

154

// Create a summary

155

val summary = Summary(

156

success = 10,

157

fail = 2,

158

ignore = 1,

159

summary = "Test run completed\nSome tests failed"

160

)

161

162

// Serialize for transmission

163

val serialized = SummaryProtocol.serialize(summary)

164

// Result: "10\t2\t1\tTest run completed\\nSome tests failed"

165

166

// Deserialize on receiving side

167

val received = SummaryProtocol.deserialize(serialized)

168

// Result: Some(Summary(10, 2, 1, "Test run completed\nSome tests failed"))

169

170

// Handle malformed data gracefully

171

val malformed = SummaryProtocol.deserialize("invalid")

172

// Result: None

173

```

174

175

**Error Handling:**

176

177

The protocol provides robust error handling for malformed data:

178

179

```scala

180

// Valid format returns Some(Summary)

181

SummaryProtocol.deserialize("5\t1\t0\tAll tests passed")

182

// = Some(Summary(5, 1, 0, "All tests passed"))

183

184

// Invalid formats return None

185

SummaryProtocol.deserialize("not-enough-fields") // = None

186

SummaryProtocol.deserialize("5\tnot-a-number\t0\ttest") // = None (NumberFormatException)

187

SummaryProtocol.deserialize("") // = None

188

```

189

190

## Platform Integration Patterns

191

192

### JVM Platform Communication

193

194

JVM platform uses direct in-memory communication since everything runs in the same process:

195

196

```scala

197

class ZTestRunner(/* ... */) extends sbt.testing.Runner {

198

val summaries: AtomicReference[Vector[Summary]] = new AtomicReference(Vector.empty)

199

200

val sendSummary: SendSummary = SendSummary.fromSendM(summary =>

201

ZIO.effectTotal {

202

summaries.updateAndGet(_ :+ summary) // Thread-safe collection

203

()

204

}

205

)

206

207

def done(): String = {

208

val allSummaries = summaries.get

209

// Process summaries for final report

210

formatSummaries(allSummaries)

211

}

212

}

213

```

214

215

### JavaScript/Native Platform Communication

216

217

JavaScript and Native platforms support distributed execution with serialization:

218

219

```scala

220

class ZSlaveTestRunner(/* ... */, send: String => Unit) extends ZTestRunner {

221

// Serializes summaries and sends to master process

222

val sendSummary: SendSummary = SendSummary.fromSend(summary =>

223

send(SummaryProtocol.serialize(summary))

224

)

225

}

226

227

class ZMasterTestRunner(/* ... */) extends ZTestRunner {

228

val summaries: mutable.Buffer[Summary] = mutable.Buffer.empty

229

230

// Receives serialized summaries from slave processes

231

override def receiveMessage(summary: String): Option[String] = {

232

SummaryProtocol.deserialize(summary).foreach(s => summaries += s)

233

None // No response needed

234

}

235

236

// Also handles local execution

237

val sendSummary: SendSummary = SendSummary.fromSend { summary =>

238

summaries += summary

239

()

240

}

241

}

242

```

243

244

### Task Serialization Support

245

246

The runners also support task serialization for distributed execution:

247

248

```scala

249

// Serialize task for sending to slave process

250

def serializeTask(task: Task, serializer: TaskDef => String): String = {

251

serializer(task.taskDef())

252

}

253

254

// Deserialize task received from master process

255

def deserializeTask(task: String, deserializer: String => TaskDef): Task = {

256

new ZTestTask(deserializer(task), testClassLoader, runnerType, sendSummary, TestArgs.parse(args))

257

}

258

```

259

260

## Advanced Communication Patterns

261

262

### Custom Summary Processing

263

264

You can implement custom summary processing by creating custom `SendSummary` instances:

265

266

```scala

267

// Custom summary processor with external reporting

268

val customSendSummary = SendSummary.fromSendM { summary =>

269

for {

270

_ <- ZIO.effect(logToFile(summary)) // Log to file

271

_ <- ZIO.effect(sendToMetrics(summary)) // Send to metrics system

272

_ <- ZIO.effect(updateDatabase(summary)) // Update test database

273

} yield ()

274

}

275

276

// Error-resilient version

277

val resilientSendSummary = SendSummary.fromSendM { summary =>

278

ZIO.effect(processExternally(summary))

279

.catchAll(error => ZIO.effect(println(s"Summary processing failed: $error")))

280

.as(()) // Always succeeds

281

}

282

```

283

284

### Multi-Platform Summary Collection

285

286

For applications that need to collect summaries across multiple platforms:

287

288

```scala

289

// Centralized summary collector

290

class SummaryCollector {

291

private val summaries = new ConcurrentLinkedQueue[Summary]()

292

293

def createSendSummary(): SendSummary = SendSummary.fromSend { summary =>

294

summaries.offer(summary)

295

()

296

}

297

298

def getAllSummaries(): List[Summary] = summaries.asScala.toList

299

300

def getAggregatedSummary(): Summary = {

301

val all = getAllSummaries()

302

Summary(

303

success = all.map(_.success).sum,

304

fail = all.map(_.fail).sum,

305

ignore = all.map(_.ignore).sum,

306

summary = all.map(_.summary).filter(_.nonEmpty).mkString("\n")

307

)

308

}

309

}

310

311

// Usage across platforms

312

val collector = new SummaryCollector()

313

val sendSummary = collector.createSendSummary()

314

// Use sendSummary in test runners across all platforms

315

```

316

317

## Utility Functions

318

319

### Colored Output

320

321

Utility function for handling ANSI color codes in test output across different platforms.

322

323

```scala { .api }

324

/**

325

* Inserts ANSI escape codes at the beginning of each line

326

* Ensures colored output displays correctly in SBT test loggers

327

* @param s String potentially containing ANSI color codes

328

* @return String with color codes properly positioned for multi-line output

329

*/

330

private[sbt] def colored(s: String): String

331

```

332

333

**Usage:**

334

335

```scala

336

// Used internally by test runners for formatted output

337

val coloredSummary = colored(summary.summary)

338

println(coloredSummary) // Maintains colors across multiple lines

339

```

340

341

The `colored` function ensures that ANSI color codes are properly maintained when displaying multi-line test output, which is essential for readable test reports with syntax highlighting and status indicators.

342

343

The communication system ensures reliable test result transmission while maintaining platform abstraction and providing flexibility for custom integration patterns.