or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

config-storage.mdengine-implementation.mdhierarchical-engine.mdindex.mdtest-descriptors.mdtest-discovery.mdtest-execution.md

config-storage.mddocs/

0

# Configuration and Storage

1

2

Configuration system and hierarchical storage for test engines to manage parameters and test-scoped data. This system provides engines with access to configuration parameters and hierarchical data storage with automatic lifecycle management.

3

4

## Capabilities

5

6

### ConfigurationParameters Interface

7

8

Interface for accessing configuration parameters provided to test engines during discovery and execution.

9

10

```java { .api }

11

/**

12

* Configuration parameters for test engines, providing type-safe parameter access.

13

*/

14

public interface ConfigurationParameters {

15

/**

16

* Get a configuration parameter value.

17

* @param key the parameter key

18

* @return optional parameter value

19

*/

20

Optional<String> get(String key);

21

22

/**

23

* Get a boolean configuration parameter.

24

* @param key the parameter key

25

* @return optional boolean value (true/false)

26

*/

27

Optional<Boolean> getBoolean(String key);

28

29

/**

30

* Get a configuration parameter with custom transformation.

31

* @param key the parameter key

32

* @param transformer function to transform string value to desired type

33

* @return optional transformed value

34

*/

35

<T> Optional<T> get(String key, Function<String, T> transformer);

36

37

/**

38

* Get all configuration parameter keys.

39

* @return unmodifiable set of parameter keys

40

*/

41

Set<String> keySet();

42

43

/**

44

* Default configuration file name.

45

*/

46

String CONFIG_FILE_NAME = "junit-platform.properties";

47

}

48

```

49

50

**Usage Example:**

51

52

```java

53

@Override

54

public void execute(ExecutionRequest request) {

55

ConfigurationParameters config = request.getConfigurationParameters();

56

57

// Get string parameters

58

String environment = config.get("test.environment").orElse("development");

59

Optional<String> dbUrl = config.get("database.url");

60

61

// Get boolean parameters

62

boolean parallelExecution = config.getBoolean("junit.jupiter.execution.parallel.enabled")

63

.orElse(false);

64

boolean verboseLogging = config.getBoolean("logging.verbose").orElse(false);

65

66

// Get numeric parameters with transformation

67

int timeout = config.get("test.timeout.seconds", Integer::parseInt).orElse(30);

68

double retryDelay = config.get("retry.delay", Double::parseDouble).orElse(1.0);

69

70

// Get enum parameters

71

LogLevel logLevel = config.get("log.level", LogLevel::valueOf)

72

.orElse(LogLevel.INFO);

73

74

// List all available configuration keys

75

Set<String> allKeys = config.keySet();

76

System.out.println("Available configuration keys: " + allKeys);

77

}

78

```

79

80

### PrefixedConfigurationParameters

81

82

Configuration parameters wrapper that automatically applies a prefix to parameter keys, useful for engine-specific configuration.

83

84

```java { .api }

85

/**

86

* Configuration parameters with automatic prefix handling for engine-specific configuration.

87

*/

88

public class PrefixedConfigurationParameters implements ConfigurationParameters {

89

/**

90

* Create prefixed configuration parameters.

91

* @param delegate the underlying configuration parameters

92

* @param prefix the prefix to apply to keys

93

*/

94

public PrefixedConfigurationParameters(ConfigurationParameters delegate, String prefix);

95

96

// All ConfigurationParameters methods automatically apply prefix

97

}

98

```

99

100

**Usage Example:**

101

102

```java

103

public class MyTestEngine implements TestEngine {

104

private static final String ENGINE_PREFIX = "myengine.";

105

106

@Override

107

public void execute(ExecutionRequest request) {

108

// Create engine-specific configuration

109

ConfigurationParameters engineConfig = new PrefixedConfigurationParameters(

110

request.getConfigurationParameters(), ENGINE_PREFIX

111

);

112

113

// Access engine-specific parameters without prefix

114

boolean enableFeature = engineConfig.getBoolean("feature.enabled").orElse(false);

115

// Actual key looked up: "myengine.feature.enabled"

116

117

int threadCount = engineConfig.get("thread.count", Integer::parseInt).orElse(1);

118

// Actual key looked up: "myengine.thread.count"

119

}

120

}

121

```

122

123

### NamespacedHierarchicalStore

124

125

Hierarchical, namespaced key-value store with automatic resource management and parent-child relationships for test-scoped data storage.

126

127

```java { .api }

128

/**

129

* Hierarchical, namespaced key-value store with automatic resource management.

130

* @param N the namespace type

131

*/

132

public final class NamespacedHierarchicalStore<N> implements AutoCloseable {

133

134

/**

135

* Get a value from the store.

136

* @param namespace the namespace

137

* @param key the key

138

* @return the stored value, or null if not found

139

*/

140

public Object get(N namespace, Object key);

141

142

/**

143

* Get a typed value from the store.

144

* @param namespace the namespace

145

* @param key the key

146

* @param requiredType the required value type

147

* @return the stored value cast to the required type

148

* @throws ClassCastException if value cannot be cast to required type

149

*/

150

public <T> T get(N namespace, Object key, Class<T> requiredType);

151

152

/**

153

* Store a value in the store.

154

* @param namespace the namespace

155

* @param key the key

156

* @param value the value to store

157

* @return the previously stored value, or null if none

158

*/

159

public Object put(N namespace, Object key, Object value);

160

161

/**

162

* Remove a value from the store.

163

* @param namespace the namespace

164

* @param key the key

165

* @return the removed value, or null if not found

166

*/

167

public Object remove(N namespace, Object key);

168

169

/**

170

* Get a value or compute it if absent.

171

* @param namespace the namespace

172

* @param key the key

173

* @param defaultCreator function to create default value

174

* @return existing or newly computed value

175

*/

176

public <K, V> Object getOrComputeIfAbsent(N namespace, K key, Function<K, V> defaultCreator);

177

178

/**

179

* Create a child store with this store as parent.

180

* @return new child store

181

*/

182

public NamespacedHierarchicalStore<N> newChild();

183

184

/**

185

* Get the parent store.

186

* @return optional parent store

187

*/

188

public Optional<NamespacedHierarchicalStore<N>> getParent();

189

190

/**

191

* Check if this store has been closed.

192

* @return true if closed

193

*/

194

public boolean isClosed();

195

196

/**

197

* Close this store and execute all close actions.

198

* Automatically called when execution context ends.

199

*/

200

@Override

201

public void close() throws Exception;

202

203

/**

204

* Action to execute when store is closed.

205

* @param N the namespace type

206

*/

207

@FunctionalInterface

208

public interface CloseAction<N> {

209

void close(N namespace, Object key, Object value) throws Exception;

210

}

211

}

212

```

213

214

**Usage Example:**

215

216

```java

217

public class MyExecutionContext implements EngineExecutionContext {

218

private final NamespacedHierarchicalStore<Namespace> store;

219

220

public void setupTestInstance(TestDescriptor testDescriptor) {

221

// Store test instance in test-specific namespace

222

Namespace testNamespace = Namespace.create("test", testDescriptor.getUniqueId());

223

Object testInstance = createTestInstance();

224

store.put(testNamespace, "instance", testInstance);

225

226

// Store database connection with cleanup

227

DataSource dataSource = store.getOrComputeIfAbsent(

228

Namespace.create("database"),

229

"connection",

230

key -> createDatabaseConnection()

231

);

232

233

// Store temporary files with automatic cleanup

234

Path tempDir = store.getOrComputeIfAbsent(

235

testNamespace,

236

"temp.dir",

237

key -> {

238

try {

239

Path dir = Files.createTempDirectory("test-");

240

// Register cleanup action

241

store.put(testNamespace, "temp.dir.cleanup",

242

(CloseAction<Namespace>) (ns, k, v) -> deleteDirectory((Path) v));

243

return dir;

244

} catch (IOException e) {

245

throw new RuntimeException(e);

246

}

247

}

248

);

249

}

250

251

public <T> T getTestScopedValue(TestDescriptor testDescriptor, String key, Class<T> type) {

252

Namespace testNamespace = Namespace.create("test", testDescriptor.getUniqueId());

253

return store.get(testNamespace, key, type);

254

}

255

256

public void cleanupTest(TestDescriptor testDescriptor) {

257

// Create child store for this test - will be automatically closed

258

try (NamespacedHierarchicalStore<Namespace> testStore = store.newChild()) {

259

Namespace testNamespace = Namespace.create("test", testDescriptor.getUniqueId());

260

261

// Test-specific operations...

262

// Store will be automatically closed, executing cleanup actions

263

}

264

}

265

}

266

```

267

268

### Namespace

269

270

Represents namespaces for organizing data within the hierarchical store.

271

272

```java { .api }

273

/**

274

* Represents a namespace for organizing data in hierarchical stores.

275

*/

276

public final class Namespace {

277

/**

278

* Create a namespace from parts.

279

* @param parts the namespace parts

280

* @return namespace instance

281

*/

282

public static Namespace create(Object... parts);

283

284

/**

285

* Get namespace parts.

286

* @return list of namespace parts

287

*/

288

public List<Object> getParts();

289

290

// Common namespaces

291

public static final Namespace GLOBAL = create("global");

292

}

293

```

294

295

### Store Exception Handling

296

297

Exception handling for hierarchical store operations.

298

299

```java { .api }

300

/**

301

* Exception thrown by hierarchical store operations.

302

*/

303

public class NamespacedHierarchicalStoreException extends RuntimeException {

304

/**

305

* Create store exception with message.

306

* @param message the exception message

307

*/

308

public NamespacedHierarchicalStoreException(String message);

309

310

/**

311

* Create store exception with message and cause.

312

* @param message the exception message

313

* @param cause the underlying cause

314

*/

315

public NamespacedHierarchicalStoreException(String message, Throwable cause);

316

}

317

```

318

319

### Advanced Store Usage Patterns

320

321

**Resource Management with Cleanup:**

322

323

```java

324

public class DatabaseTestSupport {

325

private final NamespacedHierarchicalStore<Namespace> store;

326

327

public DataSource getDatabaseConnection(TestDescriptor testDescriptor) {

328

Namespace dbNamespace = Namespace.create("database", testDescriptor.getUniqueId());

329

330

return store.getOrComputeIfAbsent(dbNamespace, "connection", key -> {

331

try {

332

DataSource dataSource = createDataSource();

333

334

// Register cleanup action

335

store.put(dbNamespace, "connection.cleanup",

336

(NamespacedHierarchicalStore.CloseAction<Namespace>)

337

(namespace, k, value) -> {

338

if (value instanceof DataSource) {

339

closeDataSource((DataSource) value);

340

}

341

});

342

343

return dataSource;

344

} catch (SQLException e) {

345

throw new RuntimeException("Failed to create database connection", e);

346

}

347

});

348

}

349

}

350

```

351

352

**Hierarchical Configuration:**

353

354

```java

355

public class ConfigurableTestEngine implements TestEngine {

356

357

@Override

358

public void execute(ExecutionRequest request) {

359

ConfigurationParameters config = request.getConfigurationParameters();

360

NamespacedHierarchicalStore<Namespace> store = request.getStore();

361

362

// Store global configuration

363

Namespace globalConfig = Namespace.create("config", "global");

364

store.put(globalConfig, "parallel.enabled",

365

config.getBoolean("parallel.enabled").orElse(false));

366

store.put(globalConfig, "timeout.seconds",

367

config.get("timeout.seconds", Integer::parseInt).orElse(30));

368

369

// Execute test hierarchy with inherited configuration

370

executeTestHierarchy(request.getRootTestDescriptor(), store);

371

}

372

373

private void executeTestHierarchy(TestDescriptor descriptor,

374

NamespacedHierarchicalStore<Namespace> store) {

375

// Create child store for this test level

376

try (NamespacedHierarchicalStore<Namespace> childStore = store.newChild()) {

377

Namespace testConfig = Namespace.create("config", descriptor.getUniqueId());

378

379

// Test-specific configuration inherits from parent through hierarchy

380

Boolean parallelEnabled = childStore.get(

381

Namespace.create("config", "global"),

382

"parallel.enabled",

383

Boolean.class

384

);

385

386

// Override for specific tests if needed

387

if (hasCustomParallelSetting(descriptor)) {

388

childStore.put(testConfig, "parallel.enabled", false);

389

}

390

391

// Execute with child store context

392

executeWithStore(descriptor, childStore);

393

}

394

}

395

}

396

```