or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

conditional-execution.mdcore-testing.mdextension-framework.mdindex.mdparallel-io.md

parallel-io.mddocs/

0

# Parallel Execution and I/O Support

1

2

Thread-safe test execution with resource management, temporary directory support, and execution control annotations. This module provides capabilities for running tests concurrently while ensuring proper resource isolation and file system operations.

3

4

## Capabilities

5

6

### Parallel Execution Control

7

8

Configure how tests execute in relation to each other and control thread usage.

9

10

```java { .api }

11

/**

12

* Configure parallel execution mode for test classes and methods

13

*/

14

@Execution(ExecutionMode value)

15

16

/**

17

* Execute test in isolation (forces sequential execution)

18

*/

19

@Isolated

20

21

/**

22

* Execution mode enumeration

23

*/

24

enum ExecutionMode {

25

/**

26

* Execute in same thread as parent

27

*/

28

SAME_THREAD,

29

30

/**

31

* Execute concurrently when parallel execution is enabled

32

*/

33

CONCURRENT

34

}

35

```

36

37

**Usage Examples:**

38

39

```java

40

import org.junit.jupiter.api.parallel.*;

41

import org.junit.jupiter.api.execution.ExecutionMode;

42

43

// Class-level parallel execution

44

@Execution(ExecutionMode.CONCURRENT)

45

class ParallelTest {

46

47

@Test

48

@Execution(ExecutionMode.CONCURRENT)

49

void concurrentTest1() {

50

// Runs concurrently with other concurrent tests

51

performIndependentCalculation();

52

}

53

54

@Test

55

@Execution(ExecutionMode.CONCURRENT)

56

void concurrentTest2() {

57

// Can run in parallel with concurrentTest1

58

performAnotherIndependentCalculation();

59

}

60

61

@Test

62

@Execution(ExecutionMode.SAME_THREAD)

63

void sequentialTest() {

64

// Always runs in same thread as parent

65

performThreadSensitiveOperation();

66

}

67

}

68

69

// Force sequential execution for entire class

70

@Execution(ExecutionMode.SAME_THREAD)

71

class SequentialTest {

72

73

@Test

74

void test1() {

75

// Always sequential due to class-level annotation

76

}

77

78

@Test

79

void test2() {

80

// Runs after test1 completes

81

}

82

}

83

84

// Isolation for tests that cannot run concurrently with anything

85

@Isolated

86

class IsolatedTest {

87

88

@Test

89

void isolatedTest() {

90

// No other tests run concurrently with this

91

modifyGlobalState();

92

}

93

}

94

```

95

96

### Resource Management

97

98

Declare dependencies on shared resources to prevent concurrent access conflicts.

99

100

```java { .api }

101

/**

102

* Declare a resource dependency with access mode

103

*/

104

@ResourceLock(String value, ResourceAccessMode mode = ResourceAccessMode.READ_WRITE)

105

106

/**

107

* Multiple resource locks

108

*/

109

@ResourceLocks({

110

@ResourceLock(value = "resource1", mode = ResourceAccessMode.READ),

111

@ResourceLock(value = "resource2", mode = ResourceAccessMode.READ_WRITE)

112

})

113

114

/**

115

* Resource access modes

116

*/

117

enum ResourceAccessMode {

118

/**

119

* Read-only access - multiple readers allowed

120

*/

121

READ,

122

123

/**

124

* Read-write access - exclusive access required

125

*/

126

READ_WRITE

127

}

128

129

/**

130

* Programmatic resource lock provider

131

*/

132

interface ResourceLocksProvider {

133

java.util.Set<Lock> provideForNestedClass(Class<?> testClass);

134

java.util.Set<Lock> provideForMethod(Class<?> testClass, java.lang.reflect.Method testMethod);

135

}

136

```

137

138

**Usage Examples:**

139

140

```java

141

import org.junit.jupiter.api.parallel.*;

142

143

class ResourceLockTest {

144

145

@Test

146

@ResourceLock(value = "database", mode = ResourceAccessMode.READ_WRITE)

147

void exclusiveDatabaseTest() {

148

// Exclusive access to database resource

149

database.createTable("test_table");

150

database.insertData("test_data");

151

// No other database tests run concurrently

152

}

153

154

@Test

155

@ResourceLock(value = "database", mode = ResourceAccessMode.READ)

156

void readOnlyDatabaseTest1() {

157

// Can run concurrently with other READ mode tests

158

List<String> tables = database.listTables();

159

assertFalse(tables.isEmpty());

160

}

161

162

@Test

163

@ResourceLock(value = "database", mode = ResourceAccessMode.READ)

164

void readOnlyDatabaseTest2() {

165

// Can run concurrently with readOnlyDatabaseTest1

166

int count = database.getTableCount();

167

assertTrue(count >= 0);

168

}

169

170

@Test

171

@ResourceLocks({

172

@ResourceLock(value = "file.system", mode = ResourceAccessMode.READ_WRITE),

173

@ResourceLock(value = "network", mode = ResourceAccessMode.READ)

174

})

175

void multipleResourceTest() {

176

// Exclusive file system access, shared network access

177

File tempFile = new File("temp.txt");

178

tempFile.createNewFile();

179

180

NetworkService.readConfiguration(); // Read-only network access

181

}

182

}

183

184

// Built-in resource constants

185

class SystemResourceTest {

186

187

@Test

188

@ResourceLock(Resources.SYSTEM_PROPERTIES)

189

void systemPropertiesTest() {

190

// Exclusive access to system properties

191

String oldValue = System.getProperty("test.property");

192

System.setProperty("test.property", "test-value");

193

try {

194

// Test logic

195

} finally {

196

if (oldValue != null) {

197

System.setProperty("test.property", oldValue);

198

} else {

199

System.clearProperty("test.property");

200

}

201

}

202

}

203

204

@Test

205

@ResourceLock(Resources.SYSTEM_OUT)

206

void systemOutTest() {

207

// Exclusive access to System.out

208

PrintStream originalOut = System.out;

209

ByteArrayOutputStream capturedOutput = new ByteArrayOutputStream();

210

System.setOut(new PrintStream(capturedOutput));

211

212

try {

213

System.out.println("Test output");

214

assertEquals("Test output\n", capturedOutput.toString());

215

} finally {

216

System.setOut(originalOut);

217

}

218

}

219

}

220

```

221

222

### Built-in Resource Constants

223

224

Predefined resource identifiers for common shared resources.

225

226

```java { .api }

227

/**

228

* Predefined resource constants for common system resources

229

*/

230

class Resources {

231

/**

232

* System properties resource

233

*/

234

static final String SYSTEM_PROPERTIES = "java.lang.System.properties";

235

236

/**

237

* System.out resource

238

*/

239

static final String SYSTEM_OUT = "java.lang.System.out";

240

241

/**

242

* System.err resource

243

*/

244

static final String SYSTEM_ERR = "java.lang.System.err";

245

246

/**

247

* Default locale resource

248

*/

249

static final String LOCALE = "java.util.Locale.default";

250

}

251

```

252

253

### Temporary Directory Support

254

255

Automatic creation and cleanup of temporary directories for tests.

256

257

```java { .api }

258

/**

259

* Inject temporary directory into test parameter or field

260

*/

261

@TempDir(

262

CleanupMode cleanup = CleanupMode.DEFAULT,

263

TempDirFactory factory = TempDirFactory.Standard.class

264

)

265

266

/**

267

* Cleanup modes for temporary directories

268

*/

269

enum CleanupMode {

270

/**

271

* Use default cleanup behavior (clean up after test completion)

272

*/

273

DEFAULT,

274

275

/**

276

* Always clean up temporary directory

277

*/

278

ALWAYS,

279

280

/**

281

* Clean up only if test succeeds

282

*/

283

ON_SUCCESS,

284

285

/**

286

* Never clean up temporary directory

287

*/

288

NEVER

289

}

290

291

/**

292

* Factory for creating temporary directories

293

*/

294

interface TempDirFactory extends java.io.Closeable {

295

/**

296

* Create a new temporary directory

297

* @param elementContext the context of the field or parameter where @TempDir is declared

298

* @param extensionContext the current extension context

299

* @return the path to the newly created temporary directory

300

* @throws Exception in case of failures

301

*/

302

java.nio.file.Path createTempDirectory(java.lang.reflect.AnnotatedElement elementContext, ExtensionContext extensionContext) throws Exception;

303

304

/**

305

* Close resources - default implementation does nothing

306

*/

307

@Override

308

default void close() throws java.io.IOException;

309

310

/**

311

* Standard implementation using Files.createTempDirectory() with "junit-" prefix

312

*/

313

class Standard implements TempDirFactory {

314

public static final TempDirFactory INSTANCE = new Standard();

315

316

@Override

317

public java.nio.file.Path createTempDirectory(java.lang.reflect.AnnotatedElement elementContext, ExtensionContext extensionContext) throws java.io.IOException;

318

}

319

}

320

```

321

322

**Usage Examples:**

323

324

```java

325

import org.junit.jupiter.api.io.*;

326

import java.nio.file.*;

327

328

class TempDirectoryTest {

329

330

// Field injection

331

@TempDir

332

Path tempDirectory;

333

334

// Alternative field injection with File type

335

@TempDir

336

File tempDir;

337

338

@Test

339

void testWithFieldInjection() throws Exception {

340

// Use tempDirectory field

341

Path testFile = tempDirectory.resolve("test.txt");

342

Files.write(testFile, "Hello World".getBytes());

343

344

assertTrue(Files.exists(testFile));

345

assertEquals("Hello World", Files.readString(testFile));

346

347

// Also works with File type

348

File anotherFile = new File(tempDir, "another.txt");

349

assertTrue(anotherFile.createNewFile());

350

}

351

352

@Test

353

void testWithParameterInjection(@TempDir Path tempDir) throws Exception {

354

// Parameter injection

355

Path configFile = tempDir.resolve("config.properties");

356

Properties props = new Properties();

357

props.setProperty("key", "value");

358

359

try (OutputStream out = Files.newOutputStream(configFile)) {

360

props.store(out, "Test configuration");

361

}

362

363

assertTrue(Files.exists(configFile));

364

}

365

366

@Test

367

void testWithCustomCleanup(@TempDir(cleanup = CleanupMode.NEVER) Path persistentDir) throws Exception {

368

// Directory won't be cleaned up after test

369

Path importantFile = persistentDir.resolve("important.data");

370

Files.write(importantFile, "Important data that should persist".getBytes());

371

372

System.out.println("Data saved to: " + importantFile.toAbsolutePath());

373

}

374

375

@Test

376

void testCleanupOnSuccess(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path conditionalDir) throws Exception {

377

// Directory cleaned up only if test passes

378

Path debugFile = conditionalDir.resolve("debug.log");

379

Files.write(debugFile, "Debug information".getBytes());

380

381

// If test fails, debug.log will remain for investigation

382

assertTrue(Files.exists(debugFile));

383

}

384

385

// Multiple temp directories

386

@Test

387

void testMultipleTempDirs(@TempDir Path inputDir, @TempDir Path outputDir) throws Exception {

388

// Create input file

389

Path input = inputDir.resolve("input.txt");

390

Files.write(input, "Input data".getBytes());

391

392

// Process and create output file

393

Path output = outputDir.resolve("output.txt");

394

String processed = Files.readString(input).toUpperCase();

395

Files.write(output, processed.getBytes());

396

397

assertEquals("INPUT DATA", Files.readString(output));

398

}

399

}

400

401

// Custom temp directory factory

402

class CustomTempDirectoryTest {

403

404

public static class CustomTempDirFactory implements TempDirFactory {

405

@Override

406

public java.nio.file.Path createTempDirectory(java.lang.reflect.AnnotatedElement annotatedElement, ExtensionContext extensionContext) throws Exception {

407

// Create temp directory in specific location with custom naming

408

java.nio.file.Path baseDir = java.nio.file.Paths.get(System.getProperty("user.home"), "test-temp");

409

java.nio.file.Files.createDirectories(baseDir);

410

411

String dirName = "test-" + extensionContext.getDisplayName().replaceAll("[^a-zA-Z0-9]", "-") + "-" + System.currentTimeMillis();

412

return java.nio.file.Files.createDirectory(baseDir.resolve(dirName));

413

}

414

}

415

416

@Test

417

void testWithCustomFactory(@TempDir(factory = CustomTempDirFactory.class) Path customTempDir) throws Exception {

418

// Uses custom factory for directory creation

419

assertTrue(customTempDir.toAbsolutePath().toString().contains("test-temp"));

420

421

Path testFile = customTempDir.resolve("custom-test.txt");

422

Files.write(testFile, "Custom temp directory test".getBytes());

423

assertTrue(Files.exists(testFile));

424

}

425

}

426

```

427

428

### Timeout Support

429

430

Control test execution timeouts at method and class levels.

431

432

```java { .api }

433

import java.util.concurrent.TimeUnit;

434

435

/**

436

* Set timeout for test execution

437

*/

438

@Timeout(

439

long value,

440

TimeUnit unit = TimeUnit.SECONDS,

441

ThreadMode threadMode = ThreadMode.INFERRED

442

)

443

444

/**

445

* Thread mode for timeout handling

446

*/

447

enum ThreadMode {

448

/**

449

* Infer thread mode based on execution mode

450

*/

451

INFERRED,

452

453

/**

454

* Interrupt test execution in same thread

455

*/

456

SAME_THREAD,

457

458

/**

459

* Execute test in separate thread for timeout

460

*/

461

SEPARATE_THREAD

462

}

463

```

464

465

**Usage Examples:**

466

467

```java

468

import org.junit.jupiter.api.Timeout;

469

import java.util.concurrent.TimeUnit;

470

471

class TimeoutTest {

472

473

@Test

474

@Timeout(5) // 5 seconds default

475

void fastTest() {

476

// Must complete within 5 seconds

477

performQuickOperation();

478

}

479

480

@Test

481

@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)

482

void veryFastTest() {

483

// Must complete within 500 milliseconds

484

int result = 2 + 2;

485

assertEquals(4, result);

486

}

487

488

@Test

489

@Timeout(value = 30, unit = TimeUnit.SECONDS, threadMode = ThreadMode.SEPARATE_THREAD)

490

void longRunningTest() {

491

// Runs in separate thread, interrupted after 30 seconds

492

performLongRunningOperation();

493

}

494

495

@Test

496

@Timeout(value = 1, unit = TimeUnit.MINUTES)

497

void integrationTest() {

498

// Integration test with 1 minute timeout

499

performIntegrationTest();

500

}

501

}

502

503

// Class-level timeout applies to all test methods

504

@Timeout(10)

505

class TimedTestClass {

506

507

@Test

508

void test1() {

509

// Inherits 10 second timeout from class

510

}

511

512

@Test

513

@Timeout(30) // Overrides class-level timeout

514

void slowTest() {

515

// Gets 30 second timeout instead of class default

516

}

517

}

518

```

519

520

### Thread Interruption Handling

521

522

Extensions can register callbacks for thread interruption events.

523

524

```java { .api }

525

/**

526

* Called before a thread is interrupted due to timeout

527

*/

528

interface PreInterruptCallback extends Extension {

529

void preInterrupt(ExtensionContext context, Thread thread) throws Exception;

530

}

531

```

532

533

**Usage Examples:**

534

535

```java

536

// Extension to handle cleanup before thread interruption

537

public class CleanupBeforeInterruptExtension implements PreInterruptCallback {

538

539

@Override

540

public void preInterrupt(ExtensionContext context, Thread thread) throws Exception {

541

System.out.println("Test about to be interrupted: " + context.getDisplayName());

542

543

// Perform cleanup before interruption

544

cleanup();

545

546

// Set flag to allow graceful shutdown

547

TestState.setInterrupting(true);

548

}

549

550

private void cleanup() {

551

// Close resources, save state, etc.

552

ResourceManager.closeAll();

553

}

554

}

555

556

@ExtendWith(CleanupBeforeInterruptExtension.class)

557

class InterruptibleTest {

558

559

@Test

560

@Timeout(5)

561

void longRunningTest() {

562

while (!TestState.isInterrupting()) {

563

// Check interruption flag periodically

564

performWork();

565

566

if (Thread.currentThread().isInterrupted()) {

567

break;

568

}

569

}

570

}

571

}

572

```

573

574

## Configuration

575

576

Parallel execution is configured through JUnit Platform configuration properties.

577

578

**Usage Examples:**

579

580

```properties

581

# Enable parallel execution

582

junit.jupiter.execution.parallel.enabled=true

583

584

# Execution mode strategy

585

junit.jupiter.execution.parallel.mode.default=concurrent

586

junit.jupiter.execution.parallel.mode.classes.default=concurrent

587

588

# Thread pool configuration

589

junit.jupiter.execution.parallel.config.strategy=dynamic

590

junit.jupiter.execution.parallel.config.dynamic.factor=1.0

591

592

# Custom thread pool

593

junit.jupiter.execution.parallel.config.strategy=custom

594

junit.jupiter.execution.parallel.config.custom.class=com.example.CustomParallelExecutionConfigurationStrategy

595

```

596

597

```java

598

// Programmatic configuration

599

@ExtendWith(ParallelConfigExtension.class)

600

class ConfiguredParallelTest {

601

// Tests with custom parallel configuration

602

}

603

604

public class ParallelConfigExtension implements BeforeAllCallback {

605

@Override

606

public void beforeAll(ExtensionContext context) throws Exception {

607

// Configure parallel execution programmatically

608

System.setProperty("junit.jupiter.execution.parallel.enabled", "true");

609

System.setProperty("junit.jupiter.execution.parallel.mode.default", "concurrent");

610

}

611

}

612

```

613

614

## Types

615

616

### Resource Lock

617

618

```java { .api }

619

/**

620

* Represents a resource lock for synchronization

621

*/

622

class Lock {

623

Lock(String key, ResourceAccessMode mode);

624

625

String getKey();

626

ResourceAccessMode getMode();

627

}

628

```

629

630

### Execution Configuration

631

632

```java { .api }

633

/**

634

* Strategy interface for parallel execution configuration

635

*/

636

interface ParallelExecutionConfigurationStrategy {

637

ParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters);

638

}

639

640

/**

641

* Configuration for parallel execution

642

*/

643

interface ParallelExecutionConfiguration {

644

int getParallelism();

645

int getMinimumRunnable();

646

int getMaxPoolSize();

647

int getCorePoolSize();

648

long getKeepAliveTime();

649

java.util.concurrent.TimeUnit getKeepAliveTimeUnit();

650

}

651

```

652

653

### Test Resource Management

654

655

```java { .api }

656

/**

657

* Interface for closeable test resources that are automatically cleaned up

658

*/

659

@FunctionalInterface

660

interface CloseableResource {

661

void close() throws Exception;

662

}

663

664

/**

665

* Resource management through extension context

666

*/

667

interface Store {

668

/**

669

* Store a closeable resource that will be automatically closed

670

*/

671

void put(Object key, CloseableResource resource);

672

}

673

```

674

675

**Usage Examples:**

676

677

```java

678

public class DatabaseTestExtension implements BeforeEachCallback, AfterEachCallback {

679

680

private static final Namespace NAMESPACE = Namespace.create("database");

681

682

@Override

683

public void beforeEach(ExtensionContext context) throws Exception {

684

// Store closeable resource

685

DatabaseConnection connection = new DatabaseConnection();

686

context.getStore(NAMESPACE).put("connection", connection::close);

687

context.getStore(NAMESPACE).put("connectionObject", connection);

688

}

689

690

// AfterEachCallback not needed - resources auto-closed by JUnit

691

}

692

```