runMatrix function
- List<
SmfModule> modules, { - required String directory,
- Map<
String, String?> roleOptions = const {}, - MatrixAppTests appTests = const MatrixAppTests([]),
- MatrixSelection selection = const MatrixSelection(),
- MatrixCommands commands = const MatrixCommands(),
Generates every app of the matrixOf of modules with roleOptions in
directory, with the options of CI, analyzes each with
flutter analyze, and, in an app that some of the tests of appTests
apply to, adds them and runs every test of the app with flutter test.
Returns the exit code: 0 if every app was generated with every module
and every step that the options of CI do not leave for later, has no
issue and passes its tests, each of the tests applies to some app, they
check the contract of their roles with every provider (see
MatrixAppTests.roleProblems), and none of them reads the name or the
options of an app (see MatrixAppTests.modeProblems); 1 otherwise.
It checks the apps of selection, every app of the matrix by default,
such as those with some names, only the apps with every module, or a
share of them (see MatrixSelection), and runs the appTests that
apply to them. A problem of the selection, such as a name that no app of
the matrix has, is a problem of the run too. The apps keep their numbers
in the matrix when only some are checked.
A run for one app with every module by its name
(NamedEveryModuleApp), as a job of CI checks its app of the plan, has
a matrix with that app alone among the apps with every module. The
other jobs check the others, so such a run holds the appTests against
the matrix with all of them: a test that applies only to an app with
every module that another job checks, such as one for another value of
a mode option, is no problem there.
The apps stay in directory, with the tests. commands run for each
app, and their log gets what happens.
Implementation
Future<int> runMatrix(
List<SmfModule> modules, {
required String directory,
Map<String, String?> roleOptions = const {},
MatrixAppTests appTests = const MatrixAppTests([]),
MatrixSelection selection = const MatrixSelection(),
MatrixCommands commands = const MatrixCommands(),
}) async {
final run = _MatrixRun(
directory: directory,
appTests: appTests.tests,
// coverage:ignore-start
// The defaults print to the terminal, create the apps with smf and
// analyze them with Flutter, as the runs of the matrix in CI do; the
// tests give their own.
say: commands.log ?? _print,
create: commands.create ?? _smfCreate(modules),
analyze:
commands.analyze ?? (directory) => _flutter(['analyze'], directory),
// coverage:ignore-end
test: commands.test ?? runAppTests,
);
final (:apps, :failed) = await matrixOf(
modules,
roleOptions: roleOptions,
everyModuleApps: selection.everyModuleApps,
);
final problems = [
for (final result in failed)
'${result.contractCase}: ${result.errors.join('; ')}',
...selection.problemsOf(apps),
];
final checked = <MatrixApp>[];
for (final (index, app) in selection.of(apps)) {
checked.add(app);
// An app keeps its number in the matrix when only some are checked.
problems.addAll(await run.check(app, 'app_${index + 1}'));
}
// Tests that apply to no app would leave CI without saying so. Those of
// the apps that are not checked run where the whole matrix is, and the
// matrix of a run for one app with every module lacks the others.
final all = switch (selection.everyModuleApps) {
NamedEveryModuleApp() =>
(await matrixOf(modules, roleOptions: roleOptions)).apps,
_ => apps,
};
for (final test in appTests.tests) {
if (!all.any(test.appliesTo)) {
problems.add('The tests of ${test.directory} apply to no app.');
}
}
problems
..addAll(appTests.roleProblems(modules, all))
..addAll(appTests.modeProblems(all));
run.say('\n${checked.length} apps generated in $directory.');
return _exitCode(problems, run.say);
}