mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

Towards https://github.com/flutter/flutter/issues/160915 (future cleanup: remove the validation from `flutter/cocoon`). This PR effectively duplicates the logic in https://github.com/flutter/cocoon/pull/4137, in `flutter/flutter`. /cc @jtmcdole
162 lines
4.9 KiB
Dart
162 lines
4.9 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
@TestOn('vm')
|
|
library;
|
|
|
|
import 'dart:io' as io;
|
|
|
|
import 'package:path/path.dart' as p;
|
|
import 'package:source_span/source_span.dart';
|
|
import 'package:yaml/yaml.dart';
|
|
|
|
import './common.dart';
|
|
|
|
void main() {
|
|
final String flutterRoot = () {
|
|
io.Directory current = io.Directory.current;
|
|
while (!io.File(p.join(current.path, 'DEPS')).existsSync()) {
|
|
if (current.path == current.parent.path) {
|
|
fail(
|
|
'Could not find flutter repository root (${io.Directory.current.path} -> ${current.path})',
|
|
);
|
|
}
|
|
current = current.parent;
|
|
}
|
|
return current.path;
|
|
}();
|
|
|
|
group('framework', () {
|
|
final List<_CiYamlTarget> targets = _CiYamlTarget.parseAll(p.join(flutterRoot, '.ci.yaml'));
|
|
|
|
for (final _CiYamlTarget target in targets) {
|
|
if (target.runIf == null || target.runIf!.isEmpty) {
|
|
continue;
|
|
}
|
|
|
|
setUp(() {
|
|
printOnFailure(target.span.message('One or more errors occurred validating'));
|
|
});
|
|
|
|
group(target.name, () {
|
|
test('must include .ci.yaml', () {
|
|
expect(
|
|
target.runIf,
|
|
contains('.ci.yaml'),
|
|
reason: '.ci.yaml inclusion means changes to the runIfs will trigger presubmit tests.',
|
|
);
|
|
});
|
|
|
|
test('must include DEPS', () {
|
|
expect(
|
|
target.runIf,
|
|
contains('DEPS'),
|
|
reason: 'DEPS updates (including the Dart SDK) mean presubmit tests must be run.',
|
|
);
|
|
});
|
|
|
|
test('must include the engine sources', () {
|
|
expect(
|
|
target.runIf,
|
|
contains('engine/**'),
|
|
reason: 'Engine updates means framework presubmit tests must be run.',
|
|
);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
group('engine', () {
|
|
final List<_CiYamlTarget> targets = _CiYamlTarget.parseAll(
|
|
p.join(flutterRoot, 'engine', 'src', 'flutter', '.ci.yaml'),
|
|
);
|
|
|
|
for (final _CiYamlTarget target in targets) {
|
|
if (target.runIf == null || target.runIf!.isEmpty) {
|
|
continue;
|
|
}
|
|
|
|
setUp(() {
|
|
printOnFailure(target.span.message('One or more errors occurred validating'));
|
|
});
|
|
|
|
group(target.name, () {
|
|
test('must include .ci.yaml', () {
|
|
expect(
|
|
target.runIf,
|
|
contains('engine/src/flutter/.ci.yaml'),
|
|
reason: '.ci.yaml inclusion means changes to the runIfs will trigger presubmit tests.',
|
|
);
|
|
});
|
|
|
|
test('must include DEPS', () {
|
|
expect(
|
|
target.runIf,
|
|
contains('DEPS'),
|
|
reason: 'DEPS updates (including the Dart SDK) mean presubmit tests must be run.',
|
|
);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/// A minimal representation of an ostensibly well-formatted `.ci.yaml` file.
|
|
///
|
|
/// Due to the repository setup, it's not possible to reuse the existing
|
|
/// specifications of this file, and since the test case is only testing a
|
|
/// subset of the encoding, this class exposes only that subset.
|
|
///
|
|
/// For a discussion leading to this design decision, see
|
|
/// <https://github.com/flutter/flutter/issues/160915>.
|
|
///
|
|
/// See also:
|
|
/// - [`scheduler.proto`][1], the schema definition of the file format.
|
|
/// - [`CI_YAML.md`][2], a human-authored description of the file format.
|
|
/// - [`ci_yaml.dart`][3], where validation is performed (in `flutter/cocoon`).
|
|
///
|
|
/// [1]: https://github.com/flutter/cocoon/blob/main/app_dart/lib/src/model/proto/internal/scheduler.proto
|
|
/// [2]: https://github.com/flutter/cocoon/blob/main/CI_YAML.md
|
|
/// [3]: https://github.com/flutter/cocoon/blob/main/app_dart/lib/src/model/ci_yaml/ci_yaml.dart
|
|
final class _CiYamlTarget {
|
|
_CiYamlTarget({required this.name, required this.span, required this.runIf});
|
|
|
|
factory _CiYamlTarget.fromYamlMap(YamlMap map) {
|
|
return _CiYamlTarget(
|
|
name: map['name'] as String,
|
|
span: map.span,
|
|
runIf: () {
|
|
final YamlList? runIf = map['runIf'] as YamlList?;
|
|
if (runIf == null) {
|
|
return null;
|
|
}
|
|
return runIf.cast<String>().toList();
|
|
}(),
|
|
);
|
|
}
|
|
|
|
/// Parses a list of targets from the provided `.ci.yaml` file [path].
|
|
static List<_CiYamlTarget> parseAll(String path) {
|
|
final YamlDocument yamlDoc = loadYamlDocument(
|
|
io.File(path).readAsStringSync(),
|
|
sourceUrl: Uri.parse(path),
|
|
);
|
|
|
|
final YamlMap root = yamlDoc.contents as YamlMap;
|
|
final YamlList targets = root['targets'] as YamlList;
|
|
return targets.nodes.map((YamlNode node) {
|
|
return _CiYamlTarget.fromYamlMap(node as YamlMap);
|
|
}).toList();
|
|
}
|
|
|
|
/// Name of the target.
|
|
final String name;
|
|
|
|
/// Where the target was parsed at in the `.ci.yaml` file.
|
|
final SourceSpan span;
|
|
|
|
/// Which lines were present in a `runIf` block, if any.
|
|
final List<String>? runIf;
|
|
}
|