0
# Runtime Support
1
2
The runtime module provides the infrastructure for collecting coverage measurements during test execution. When the compiler plugin instruments code, it injects calls to the runtime system that record which statements were executed. This module supports JVM, Scala.js, and Scala Native platforms.
3
4
## Capabilities
5
6
### Coverage Data Collection
7
8
The main interface for recording coverage measurements during runtime.
9
10
```scala { .api }
11
/**
12
* Core runtime object for collecting coverage measurements
13
* This object is called by instrumented code to record statement execution
14
*/
15
object Invoker {
16
/**
17
* Record that a statement was invoked during execution
18
* @param id Unique identifier for the statement
19
* @param dataDir Directory where measurement data should be written
20
* @param reportTestName Whether to include test name information
21
*/
22
def invoked(id: Int, dataDir: String, reportTestName: Boolean = false): Unit
23
24
/**
25
* Get the name of the currently executing ScalaTest test
26
* @returns Test name if running under ScalaTest, empty string otherwise
27
*/
28
def getCallingScalaTest: String
29
30
/**
31
* Get the measurement file for a data directory
32
* @param dataDir Coverage data directory as File
33
* @returns File where measurements are written
34
*/
35
def measurementFile(dataDir: File): File
36
37
/**
38
* Get the measurement file for a data directory
39
* @param dataDir Coverage data directory as String path
40
* @returns File where measurements are written
41
*/
42
def measurementFile(dataDir: String): File
43
44
/**
45
* Find all measurement files in a data directory
46
* @param dataDir Coverage data directory as String path
47
* @returns Array of measurement files found
48
*/
49
def findMeasurementFiles(dataDir: String): Array[File]
50
51
/**
52
* Find all measurement files in a data directory
53
* @param dataDir Coverage data directory as File
54
* @returns Array of measurement files found
55
*/
56
def findMeasurementFiles(dataDir: File): Array[File]
57
58
/**
59
* Load statement IDs that were invoked from measurement files
60
* @param files Sequence of measurement files to read
61
* @returns Set of statement IDs that were executed
62
*/
63
def invoked(files: Seq[File]): Set[Int]
64
}
65
```
66
67
**Usage Examples:**
68
69
```scala
70
import scoverage.Invoker
71
import java.io.File
72
73
// This is typically called by instrumented code, not manually
74
Invoker.invoked(12345, "target/scoverage-data", reportTestName = true)
75
76
// Find measurement files
77
val dataDir = new File("target/scoverage-data")
78
val measurementFiles = Invoker.findMeasurementFiles(dataDir)
79
80
// Load executed statement IDs
81
val executedStatements = Invoker.invoked(measurementFiles.toSeq)
82
println(s"Executed ${executedStatements.size} statements")
83
84
// Get measurement file location
85
val measurementFile = Invoker.measurementFile(dataDir)
86
println(s"Measurements written to: ${measurementFile.getAbsolutePath}")
87
```
88
89
### Platform Abstractions
90
91
The runtime provides platform-specific abstractions for different Scala targets.
92
93
#### JVM Platform
94
95
```scala { .api }
96
/**
97
* JVM-specific platform abstractions
98
*/
99
object Platform {
100
/** Thread-safe map implementation for JVM */
101
type ThreadSafeMap[A, B] = scala.collection.concurrent.TrieMap[A, B]
102
lazy val ThreadSafeMap = scala.collection.concurrent.TrieMap
103
104
/** File type for JVM platform */
105
type File = java.io.File
106
107
/** FileWriter type for JVM platform */
108
type FileWriter = java.io.FileWriter
109
110
/** FileFilter type for JVM platform */
111
type FileFilter = java.io.FileFilter
112
113
/** Source companion object for reading files */
114
lazy val Source = scala.io.Source
115
116
/** Generate random UUID (not cryptographically secure) */
117
def insecureRandomUUID(): java.util.UUID
118
}
119
```
120
121
#### Scala.js Platform
122
123
```scala { .api }
124
/**
125
* Scala.js-specific platform abstractions
126
*/
127
object Platform {
128
/** Thread-safe map implementation for Scala.js */
129
type ThreadSafeMap[A, B] = scala.collection.mutable.HashMap[A, B]
130
lazy val ThreadSafeMap = scala.collection.mutable.HashMap
131
132
/** File type for Scala.js platform */
133
type File = scalajssupport.File
134
135
/** FileWriter type for Scala.js platform */
136
type FileWriter = scalajssupport.FileWriter
137
138
/** FileFilter type for Scala.js platform */
139
type FileFilter = scalajssupport.FileFilter
140
141
/** Source companion object for reading files */
142
lazy val Source = scalajssupport.Source
143
144
/** Generate random UUID (not cryptographically secure) */
145
def insecureRandomUUID(): java.util.UUID
146
}
147
```
148
149
#### Scala Native Platform
150
151
```scala { .api }
152
/**
153
* Scala Native-specific platform abstractions
154
*/
155
object Platform {
156
/** Thread-safe map implementation for Scala Native */
157
type ThreadSafeMap[A, B] = scala.collection.mutable.HashMap[A, B]
158
lazy val ThreadSafeMap = scala.collection.mutable.HashMap
159
160
/** File type for Scala Native platform */
161
type File = scalanativesupport.File
162
163
/** FileWriter type for Scala Native platform */
164
type FileWriter = scalanativesupport.FileWriter
165
166
/** FileFilter type for Scala Native platform */
167
type FileFilter = scalanativesupport.FileFilter
168
169
/** Source companion object for reading files */
170
lazy val Source = scalanativesupport.Source
171
172
/** Generate random UUID (not cryptographically secure) */
173
def insecureRandomUUID(): java.util.UUID
174
}
175
```
176
177
### Scala.js File Support
178
179
File system abstractions for Scala.js environments.
180
181
```scala { .api }
182
/**
183
* File abstraction for Scala.js environments
184
* @param path File path
185
*/
186
class File(path: String) {
187
/**
188
* Create file with parent path and child name
189
* @param path Parent directory path
190
* @param child Child file/directory name
191
*/
192
def this(path: String, child: String)
193
194
/** Delete this file or directory */
195
def delete(): Unit
196
197
/** Get absolute path of this file */
198
def getAbsolutePath(): String
199
200
/** Get name of this file (last component of path) */
201
def getName(): String
202
203
/** Get path of this file */
204
def getPath(): String
205
206
/** Check if this file represents a directory */
207
def isDirectory(): Boolean
208
209
/** Create directory and any necessary parent directories */
210
def mkdirs(): Unit
211
212
/** List all files in this directory */
213
def listFiles(): Array[File]
214
215
/**
216
* List files in this directory matching the filter
217
* @param filter FileFilter to apply
218
* @returns Array of matching files
219
*/
220
def listFiles(filter: FileFilter): Array[File]
221
222
/** Read entire contents of this file as string */
223
def readFile(): String
224
225
override def toString: String
226
}
227
228
object File {
229
/** Global JavaScript object reference */
230
val globalObject: js.Dynamic
231
232
/** JavaScript file operations object */
233
val jsFile: JsFileObject
234
235
/**
236
* Join path components
237
* @param path Base path
238
* @param child Child component to append
239
* @returns Joined path
240
*/
241
def pathJoin(path: String, child: String): String
242
243
/**
244
* Write data to file
245
* @param path File path to write to
246
* @param data String data to write
247
* @param mode Write mode ("a" for append, "w" for overwrite)
248
*/
249
def write(path: String, data: String, mode: String = "a"): Unit
250
}
251
```
252
253
### File I/O Support
254
255
Additional file system support classes for different JavaScript environments.
256
257
```scala { .api }
258
/**
259
* File writer abstraction for Scala.js
260
*/
261
class FileWriter(file: File) {
262
def write(data: String): Unit
263
def close(): Unit
264
}
265
266
/**
267
* File filter for selecting files
268
*/
269
trait FileFilter {
270
def accept(file: File): Boolean
271
}
272
273
/**
274
* Source file reading support
275
*/
276
object Source {
277
/**
278
* Create source from file
279
* @param file File to read from
280
* @returns Source for reading file contents
281
*/
282
def fromFile(file: File): Source
283
284
/**
285
* Create source from file path
286
* @param path Path to file
287
* @returns Source for reading file contents
288
*/
289
def fromFile(path: String): Source
290
}
291
292
class Source {
293
/** Get all lines from source as iterator */
294
def getLines(): Iterator[String]
295
296
/** Close the source */
297
def close(): Unit
298
}
299
```
300
301
**Usage Examples:**
302
303
```scala
304
import scoverage.Platform
305
import Platform.{File, FileWriter, Source}
306
307
// Create and write to file (works across all platforms)
308
val file = new File("coverage-data", "measurements.txt")
309
file.mkdirs() // Ensure parent directory exists
310
311
val writer = new FileWriter(file)
312
writer.write("statement_id:12345\n")
313
writer.close()
314
315
// Read file contents
316
val source = Source.fromFile(file)
317
val lines = source.getLines().toList
318
source.close()
319
320
// Use platform-specific thread-safe map
321
val measurements = Platform.ThreadSafeMap[Int, String]()
322
measurements.put(12345, "com.example.Service.method")
323
```
324
325
## Runtime Integration
326
327
### Instrumented Code Example
328
329
When the compiler plugin instruments code, it injects calls like:
330
331
```scala
332
// Original code:
333
def processUser(user: User): String = {
334
val name = user.name
335
if (name.nonEmpty) {
336
s"Hello, $name"
337
} else {
338
"Hello, Anonymous"
339
}
340
}
341
342
// Instrumented code (simplified):
343
def processUser(user: User): String = {
344
scoverage.Invoker.invoked(1001, "target/scoverage-data")
345
val name = {
346
scoverage.Invoker.invoked(1002, "target/scoverage-data")
347
user.name
348
}
349
if ({
350
scoverage.Invoker.invoked(1003, "target/scoverage-data")
351
name.nonEmpty
352
}) {
353
scoverage.Invoker.invoked(1004, "target/scoverage-data")
354
s"Hello, $name"
355
} else {
356
scoverage.Invoker.invoked(1005, "target/scoverage-data")
357
"Hello, Anonymous"
358
}
359
}
360
```
361
362
### Test Integration
363
364
The runtime automatically detects ScalaTest execution:
365
366
```scala
367
import org.scalatest.flatspec.AnyFlatSpec
368
369
class UserServiceSpec extends AnyFlatSpec {
370
"UserService" should "process valid users" in {
371
// When this test runs, Invoker.getCallingScalaTest returns:
372
// "UserServiceSpec.UserService should process valid users"
373
val result = userService.processUser(validUser)
374
assert(result.startsWith("Hello,"))
375
}
376
}
377
```
378
379
### Multi-Module Support
380
381
For multi-module projects, each module writes to its own measurement files:
382
383
```scala
384
// Module A measurements: target/scoverage-data/scoverage.measurements.module-a.12345
385
// Module B measurements: target/scoverage-data/scoverage.measurements.module-b.67890
386
387
val allFiles = Invoker.findMeasurementFiles("target/scoverage-data")
388
val allInvoked = Invoker.invoked(allFiles.toSeq)
389
// Contains statement IDs from all modules
390
```
391
392
## Error Handling
393
394
- **IOException**: File I/O operations may fail if coverage data directory is not writable
395
- **SecurityException**: May be thrown if running in restricted security environment
396
- **OutOfMemoryError**: Can occur with very large codebases generating excessive measurement data
397
- **ConcurrentModificationException**: Rare threading issues when multiple test processes write simultaneously