load static method
Loads the manifest from root (default: current directory).
Implementation
static Manifest load(String root) {
final file = File('$root/$kManifestFileName');
if (!file.existsSync()) {
throw FileSystemException('no $kManifestFileName in $root — run '
'"dart run flutter_bench_contract:contract init" first');
}
final dynamic doc;
try {
doc = loadYaml(file.readAsStringSync());
} catch (e) {
throw FormatException('cannot parse $kManifestFileName: $e');
}
if (doc is! YamlMap) {
throw FormatException('$kManifestFileName: expected a YAML map');
}
String str(String key, {String fallback = ''}) =>
(doc[key] as String?) ?? fallback;
List<String> list(String key) =>
[for (final s in (doc[key] as List? ?? const [])) s as String];
// Multi-library shape: `libraries: {name: {driver, driverClass, ref?}}`.
// When present it fully replaces the single-library fields (which may be
// absent); the consumer runs each entry against the same scenario list.
final librariesDoc = doc['libraries'];
final libraries = <LibraryEntry>[];
if (librariesDoc != null) {
if (librariesDoc is! YamlMap) {
throw FormatException('$kManifestFileName: libraries must be a map '
'{name: {driver, driverClass, ref?}}');
}
for (final entry in librariesDoc.entries) {
final name = entry.key as String;
final spec = entry.value;
if (spec is! YamlMap || spec['driver'] is! String ||
spec['driverClass'] is! String) {
throw FormatException('$kManifestFileName: libraries.$name needs '
'driver + driverClass');
}
libraries.add(LibraryEntry(
name: name,
driver: spec['driver'] as String,
driverClass: spec['driverClass'] as String,
ref: spec['ref'] as String?,
));
}
}
// Consumer-owned scenarios: keys are short names, the metric id is
// `custom.<key>`; a key colliding with a contract scenario id is
// shadowing and rejected (a package scenario cannot be redefined).
final customScenarios = <CustomScenario>[];
final customDoc = doc['customScenarios'];
if (customDoc != null) {
if (customDoc is! YamlMap) {
throw FormatException('$kManifestFileName: customScenarios must be a '
'map {name: {target, ref?, runs?}}');
}
for (final entry in customDoc.entries) {
final name = entry.key as String;
if (name.isEmpty || name.startsWith('custom.')) {
throw FormatException('$kManifestFileName: customScenarios key '
'"$name" must be a short name — the id is custom.<name>');
}
if (kContractScenarioIds.contains(name)) {
throw FormatException('$kManifestFileName: customScenarios key '
'"$name" shadows the contract scenario — package scenarios '
'cannot be redefined');
}
final spec = entry.value;
if (spec is! YamlMap || spec['target'] is! String ||
(spec['target'] as String).isEmpty) {
throw FormatException('$kManifestFileName: customScenarios.$name '
'needs a non-empty target: (the test file path)');
}
final ref = spec['ref'] as String? ?? 'custom';
if (ref.isEmpty) {
throw FormatException('$kManifestFileName: customScenarios.$name '
'ref must not be empty');
}
final runs = spec['runs'] is int ? spec['runs'] as int : 1;
if (runs < 1) {
throw FormatException('$kManifestFileName: customScenarios.$name '
'runs must be >= 1');
}
customScenarios.add(CustomScenario(
id: 'custom.$name',
name: name,
target: spec['target'] as String,
ref: ref,
runs: runs,
));
}
}
// The contract `scenarios:` list holds only contract ids — a `custom.*`
// entry there is a shape error (custom scenarios live under
// `customScenarios:` and are not per-library).
final scenarios = list('scenarios');
for (final id in scenarios) {
if (id.startsWith('custom.')) {
throw FormatException('$kManifestFileName: scenario "$id" belongs '
'under customScenarios:, not scenarios:');
}
}
return Manifest(
library: str('library'),
driver: str('driver'),
driverClass: str('driverClass'),
contractDir: str('contractDir', fallback: 'bench/contract'),
scenarios: scenarios,
idleClasses: list('idleClasses'),
libraries: libraries,
customScenarios: customScenarios,
template: doc['template'] is int ? doc['template'] as int : kTemplateVersion,
size: SizeConfig.fromYaml(doc['size']),
card: _cardFromYaml(doc['card']),
readme: _readmeFromYaml(doc['readme']),
);
}