or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

0

# Scalac Scoverage Runtime

1

2

Scalac Scoverage Runtime is the runtime support library for scoverage, a code coverage tool for Scala. This library provides the essential instrumentation infrastructure that collects coverage data during program execution when code has been instrumented by the scalac-scoverage-plugin compiler plugin. It supports both JVM and JavaScript platforms through Scala.js.

3

4

## Package Information

5

6

- **Package Name**: scalac-scoverage-runtime_2.11

7

- **Package Type**: Maven

8

- **Language**: Scala

9

- **Platforms**: JVM, JavaScript (Scala.js)

10

- **Installation**:

11

- SBT: `libraryDependencies += "org.scoverage" %% "scalac-scoverage-runtime" % "1.4.11"`

12

- Maven: `<artifactId>scalac-scoverage-runtime_2.11</artifactId>`

13

14

## Core Imports

15

16

```scala

17

import scoverage.Invoker

18

import scoverage.Platform._

19

```

20

21

For JavaScript-specific functionality:

22

23

```scala

24

import scalajssupport.{File, FileWriter, Source}

25

```

26

27

## Basic Usage

28

29

The runtime library is typically used automatically by instrumented code, but can also be used directly for coverage analysis:

30

31

```scala

32

import scoverage.Invoker

33

import scoverage.Platform._

34

35

// Record statement execution (called by instrumented code)

36

Invoker.invoked(statementId = 42, dataDir = "/path/to/coverage/data")

37

38

// Find measurement files for analysis

39

val dataDir = "/path/to/coverage/data"

40

val measurementFiles = Invoker.findMeasurementFiles(dataDir)

41

42

// Load invoked statement IDs from files

43

val invokedIds = Invoker.invoked(measurementFiles)

44

println(s"Covered statements: ${invokedIds.size}")

45

```

46

47

## Architecture

48

49

The runtime library uses a cross-platform architecture with several key components:

50

51

- **Platform Abstraction**: `scoverage.Platform` provides unified interfaces across JVM and JavaScript with compile-time type resolution

52

- **Coverage Collection**: `scoverage.Invoker` handles thread-safe recording of statement executions with per-thread measurement files

53

- **UUID-based File Naming**: Uses runtime UUID (`runtimeUUID`) combined with thread ID to create unique measurement files

54

- **File System Abstraction**: JavaScript support includes comprehensive file system abstractions that auto-detect and adapt to different JS runtimes (Node.js, Rhino, PhantomJS)

55

- **Thread Safety**: Uses ThreadLocal storage and concurrent TrieMap data structures on JVM, simplified HashMap collections on JavaScript

56

- **Deduplication**: In-memory tracking of recorded statement IDs prevents duplicate writes within the same JVM execution

57

58

## Capabilities

59

60

### Coverage Data Collection

61

62

Core instrumentation runtime that records which statements have been executed during program execution.

63

64

```scala { .api }

65

object Invoker {

66

/**

67

* Records that a statement has been executed

68

* @param id the ID of the statement that was invoked

69

* @param dataDir the directory where measurement data is stored

70

* @param reportTestName whether to include test name information

71

*/

72

def invoked(id: Int, dataDir: String, reportTestName: Boolean = false): Unit

73

74

/**

75

* Gets the measurement file path for a given data directory

76

* @param dataDir the data directory as File

77

* @return measurement file for current thread

78

*/

79

def measurementFile(dataDir: File): File

80

81

/**

82

* Gets the measurement file path for a given data directory

83

* @param dataDir the data directory path as String

84

* @return measurement file for current thread

85

*/

86

def measurementFile(dataDir: String): File

87

88

/**

89

* Finds all measurement files in the given directory

90

* @param dataDir the directory to search as String

91

* @return array of measurement files

92

*/

93

def findMeasurementFiles(dataDir: String): Array[File]

94

95

/**

96

* Finds all measurement files in the given directory

97

* @param dataDir the directory to search as File

98

* @return array of measurement files

99

*/

100

def findMeasurementFiles(dataDir: File): Array[File]

101

102

/**

103

* Loads all invoked statement IDs from measurement files

104

* @param files sequence of measurement files to read

105

* @return set of statement IDs that were executed

106

* @note Each line may contain just the ID, or ID followed by test name (space-separated)

107

*/

108

def invoked(files: Seq[File]): Set[Int]

109

110

/**

111

* Utility method to identify calling ScalaTest suite

112

* @return name of calling test suite or empty string

113

*/

114

def getCallingScalaTest: String

115

}

116

```

117

118

### Platform Abstraction

119

120

Provides unified cross-platform interfaces for file operations and concurrent data structures.

121

122

```scala { .api }

123

object Platform {

124

// JVM Platform (when compiling to JVM bytecode)

125

type ThreadSafeMap[A, B] = scala.collection.concurrent.TrieMap[A, B]

126

lazy val ThreadSafeMap: TrieMap.type = TrieMap

127

type File = java.io.File

128

type FileWriter = java.io.FileWriter

129

type FileFilter = java.io.FileFilter

130

lazy val Source: scala.io.Source.type = scala.io.Source

131

132

// JavaScript Platform (when compiling to JavaScript via Scala.js)

133

type ThreadSafeMap[A, B] = scala.collection.mutable.HashMap[A, B]

134

lazy val ThreadSafeMap: HashMap.type = HashMap

135

type File = scalajssupport.File

136

type FileWriter = scalajssupport.FileWriter

137

type FileFilter = scalajssupport.FileFilter

138

lazy val Source: scalajssupport.Source.type = scalajssupport.Source

139

}

140

```

141

142

### JavaScript File System Support

143

144

JavaScript-compatible file system abstractions that emulate Java I/O APIs for Scala.js environments.

145

146

```scala { .api }

147

// JavaScript File abstraction

148

class File(path: String) {

149

/**

150

* Creates a File with parent path and child name

151

* @param path parent directory path

152

* @param child child file/directory name

153

*/

154

def this(path: String, child: String)

155

156

/** Deletes the file or directory */

157

def delete(): Unit

158

159

/** Returns the absolute path */

160

def getAbsolutePath(): String

161

162

/** Returns the file name */

163

def getName(): String

164

165

/** Returns the file path */

166

def getPath(): String

167

168

/** Checks if this is a directory */

169

def isDirectory(): Boolean

170

171

/** Creates directory structure */

172

def mkdirs(): Unit

173

174

/** Lists all files in directory */

175

def listFiles(): Array[File]

176

177

/** Lists files matching filter */

178

def listFiles(filter: FileFilter): Array[File]

179

180

/** Reads entire file content as string */

181

def readFile(): String

182

}

183

184

object File {

185

/** Joins path components */

186

def pathJoin(path: String, child: String): String

187

188

/** Writes data to file */

189

def write(path: String, data: String, mode: String = "a"): Unit

190

}

191

192

// JavaScript FileWriter abstraction

193

class FileWriter(file: File, append: Boolean) {

194

/** Create FileWriter for file without append mode */

195

def this(file: File)

196

/** Create FileWriter for file path without append mode */

197

def this(file: String)

198

/** Create FileWriter for file path with append mode */

199

def this(file: String, append: Boolean)

200

201

/** Appends character sequence, returns this for chaining */

202

def append(csq: CharSequence): FileWriter

203

204

/** Closes the writer */

205

def close(): Unit

206

207

/** Flushes the writer */

208

def flush(): Unit

209

}

210

211

// JavaScript Source abstraction

212

object Source {

213

/** Creates source reader from file */

214

def fromFile(file: File): scala.io.Source

215

}

216

217

// File filtering interface

218

trait FileFilter {

219

/** Returns true if file should be included */

220

def accept(file: File): Boolean

221

}

222

223

// JavaScript File trait hierarchy

224

trait JsFile {

225

/** Deletes the file or directory */

226

def delete(): Unit

227

/** Returns the absolute path */

228

def getAbsolutePath(): String

229

/** Returns the file name */

230

def getName(): String

231

/** Returns the file path */

232

def getPath(): String

233

/** Checks if this is a directory */

234

def isDirectory(): Boolean

235

/** Creates directory structure */

236

def mkdirs(): Unit

237

/** Lists all files in directory */

238

def listFiles(): Array[File]

239

/** Lists files matching filter */

240

def listFiles(filter: FileFilter): Array[File]

241

/** Reads entire file content as string */

242

def readFile(): String

243

}

244

245

trait JsFileObject {

246

/** Writes data to file with mode (default append) */

247

def write(path: String, data: String, mode: String = "a"): Unit

248

/** Joins path components */

249

def pathJoin(path: String, child: String): String

250

/** Creates JsFile instance for path */

251

def apply(path: String): JsFile

252

}

253

```

254

255

## Types

256

257

```scala { .api }

258

// Core platform types (resolved at compile time based on target platform)

259

type ThreadSafeMap[A, B] // TrieMap on JVM, HashMap on JS

260

type File // java.io.File on JVM, scalajssupport.File on JS

261

type FileWriter // java.io.FileWriter on JVM, scalajssupport.FileWriter on JS

262

type FileFilter // java.io.FileFilter on JVM, scalajssupport.FileFilter on JS

263

```

264

265

## Error Handling

266

267

The library handles errors gracefully across both platforms:

268

269

- **File operations**: JavaScript implementations catch exceptions and return appropriate defaults (e.g., `false` for `isDirectory()` if file doesn't exist)

270

- **Thread safety**:

271

- JVM: Synchronized blocks prevent race conditions in concurrent ID tracking using `dataDirToIds.synchronized` to guard against SI-7943

272

- JavaScript: Single-threaded environment eliminates race conditions

273

- **Measurement files**: Each thread writes to separate files (using thread ID in filename) to avoid write conflicts, especially important on Windows

274

- **Empty lines**: The `invoked(files)` method skips empty lines in measurement files using `if (!line.isEmpty)`

275

- **ThreadLocal cleanup**: ThreadLocal storage for FileWriter instances is properly managed per thread

276

- **File parsing**: Measurement file lines are parsed as integers with proper error handling for malformed data

277

278

## Platform-Specific Behavior

279

280

### JVM Platform

281

- Uses `java.util.concurrent.TrieMap` for thread-safe collections

282

- Leverages standard Java I/O classes

283

- Supports true concurrent access across multiple threads

284

- Uses `ThreadLocal` storage for file writers

285

286

### JavaScript Platform

287

- Uses `scala.collection.mutable.HashMap` (JavaScript is single-threaded)

288

- Provides file system abstractions for different JS environments

289

- Automatically detects and adapts to Node.js, Rhino, or PhantomJS environments using global object detection

290

- File operations delegate to environment-specific implementations (`NodeFile`, `RhinoFile`, `PhantomFile`)

291

- Environment detection: checks for `Packages` (Rhino), `callPhantom` (PhantomJS), or defaults to Node.js

292

293

### Measurement File Format

294

- **File naming**: `scoverage.measurements.<UUID>.<threadId>`

295

- **Content format**: Each line contains either:

296

- Just the statement ID: `"123"`

297

- ID with test name: `"123 MySuite"` (when `reportTestName = true`)

298

- **Multiple files**: One file per thread to avoid write conflicts

299

- **Parsing**: Only the first part (before space) is used as the statement ID

300

301

## Usage Examples

302

303

**Recording coverage data (typically done by instrumented code):**

304

305

```scala

306

import scoverage.Invoker

307

308

// Record that statement 123 was executed

309

Invoker.invoked(123, "/tmp/scoverage-data")

310

311

// Record with test name information

312

Invoker.invoked(456, "/tmp/scoverage-data", reportTestName = true)

313

```

314

315

**Analyzing coverage data:**

316

317

```scala

318

import scoverage.Invoker

319

import scoverage.Platform._

320

321

val dataDir = "/tmp/scoverage-data"

322

323

// Find all measurement files

324

val files = Invoker.findMeasurementFiles(dataDir)

325

println(s"Found ${files.length} measurement files")

326

327

// Load executed statement IDs

328

val executedIds = Invoker.invoked(files.toSeq)

329

println(s"Total statements executed: ${executedIds.size}")

330

331

// Check if specific statement was executed

332

val statementId = 42

333

if (executedIds.contains(statementId)) {

334

println(s"Statement $statementId was executed")

335

}

336

```

337

338

**Cross-platform file operations:**

339

340

```scala

341

import scoverage.Platform._

342

343

// This works on both JVM and JavaScript

344

val file = new File("/path/to/data", "measurements.txt")

345

val writer = new FileWriter(file, append = true)

346

writer.append("42\n")

347

writer.close()

348

349

// Read measurement data

350

val source = Source.fromFile(file)

351

val lines = source.getLines().toList

352

source.close()

353

```