diff --git a/tools/engine_tool/test/build_command_test.dart b/tools/engine_tool/test/build_command_test.dart index 89844cef3048e..acf8ea70b5684 100644 --- a/tools/engine_tool/test/build_command_test.dart +++ b/tools/engine_tool/test/build_command_test.dart @@ -3,43 +3,20 @@ // found in the LICENSE file. import 'dart:convert' as convert; +import 'dart:ffi'; import 'package:engine_build_configs/engine_build_configs.dart'; import 'package:engine_tool/src/build_utils.dart'; import 'package:engine_tool/src/commands/command_runner.dart'; import 'package:engine_tool/src/logger.dart'; import 'package:path/path.dart' as path; -import 'package:platform/platform.dart'; import 'package:test/test.dart'; import 'fixtures.dart' as fixtures; +import 'src/test_build_configs.dart'; import 'utils.dart'; void main() { - final linuxTestConfig = BuilderConfig.fromJson( - path: 'ci/builders/linux_test_config.json', - map: convert.jsonDecode(fixtures.testConfig('Linux', Platform.linux)) - as Map, - ); - - final macTestConfig = BuilderConfig.fromJson( - path: 'ci/builders/mac_test_config.json', - map: convert.jsonDecode(fixtures.testConfig('Mac-12', Platform.macOS)) - as Map, - ); - - final winTestConfig = BuilderConfig.fromJson( - path: 'ci/builders/win_test_config.json', - map: convert.jsonDecode(fixtures.testConfig('Windows-11', Platform.windows)) - as Map, - ); - - final configs = { - 'linux_test_config': linuxTestConfig, - 'mac_test_config': macTestConfig, - 'win_test_config': winTestConfig, - }; - final cannedProcesses = [ CannedProcess( (command) => command.contains('desc'), @@ -49,21 +26,57 @@ void main() { test('can find host runnable build', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'macos/host_debug', + dimension: TestDroneDimension.mac, + ); + builder.addBuild( + name: 'mac/host_profile', + dimension: TestDroneDimension.mac, + ); + builder.addBuild( + name: 'linux/host_debug', + dimension: TestDroneDimension.linux, + ); + + final configs = { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }; + final result = runnableBuilds(testEnv.environment, configs, true); - expect(result.length, equals(4)); - expect(result[0].name, equals('ci/build_name')); + expect( + result.map((r) => r.name), + unorderedEquals(['macos/host_debug', 'mac/host_profile']), + ); }); test('build command invokes gn', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'macos/host_debug', + dimension: TestDroneDimension.mac, + ); + + final configs = { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }; + final runner = ToolCommandRunner( environment: testEnv.environment, configs: configs, @@ -71,8 +84,10 @@ void main() { final result = await runner.run([ 'build', '--config', - 'ci/build_name', + 'host_debug', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); expect(testEnv.processHistory.length, greaterThanOrEqualTo(1)); expect(testEnv.processHistory[0].command[0], contains('gn')); @@ -80,10 +95,22 @@ void main() { test('build command invokes ninja', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'macos/host_debug', + dimension: TestDroneDimension.mac, + ); + + final configs = { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }; final runner = ToolCommandRunner( environment: testEnv.environment, configs: configs, @@ -91,8 +118,10 @@ void main() { final result = await runner.run([ 'build', '--config', - 'ci/build_name', + 'host_debug', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); expect(testEnv.processHistory.length, greaterThanOrEqualTo(2)); expect(testEnv.processHistory[1].command[0], contains('ninja')); @@ -100,10 +129,23 @@ void main() { test('build command invokes generator', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'macos/host_debug', + dimension: TestDroneDimension.mac, + generatorTask: ('gen/script.py', ['--test-param']), + ); + + final configs = { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }; final runner = ToolCommandRunner( environment: testEnv.environment, configs: configs, @@ -111,22 +153,36 @@ void main() { final result = await runner.run([ 'build', '--config', - 'ci/build_name', + 'host_debug', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); - expect(testEnv.processHistory.length, greaterThanOrEqualTo(3)); expect( - testEnv.processHistory[2].command, - containsAllInOrder(['python3', 'gen/script.py']), + testEnv.processHistory.map((p) => p.command), + containsOnce(containsAllInOrder(['python3', 'gen/script.py'])), ); }); test('build command does not invoke tests', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'macos/host_debug', + dimension: TestDroneDimension.mac, + testTask: ('test/script.py', ['--test-param']), + ); + + final configs = { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }; final runner = ToolCommandRunner( environment: testEnv.environment, configs: configs, @@ -134,19 +190,37 @@ void main() { final result = await runner.run([ 'build', '--config', - 'ci/build_name', + 'host_debug', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); - expect(testEnv.processHistory.length, lessThanOrEqualTo(4)); + expect( + testEnv.processHistory.map((p) => p.command), + isNot(contains(containsAllInOrder(['python3', 'gen/script.py']))), + ); }); test('build command runs rbe on an rbe build', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, withRbe: true, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'ci/android_debug_rbe_arm64', + dimension: TestDroneDimension.mac, + enableRbe: true, + ); + final configs = { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }; + final runner = ToolCommandRunner( environment: testEnv.environment, configs: configs, @@ -156,50 +230,93 @@ void main() { '--config', 'ci/android_debug_rbe_arm64', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); - expect(testEnv.processHistory[0].command[0], - contains(path.join('tools', 'gn'))); - expect(testEnv.processHistory[0].command[2], equals('--rbe')); - expect(testEnv.processHistory[1].command[0], - contains(path.join('reclient', 'bootstrap'))); + + final [gnCall, reclientCall, ..._] = testEnv.processHistory; + expect( + gnCall.command, + containsAllInOrder([ + endsWith('tools/gn'), + contains('--rbe'), + ]), + ); + expect( + reclientCall.command, + containsAllInOrder([ + endsWith('reclient/bootstrap'), + ]), + ); }); test('build command plumbs -j to ninja', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, withRbe: true, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'ci/android_debug_arm64', + dimension: TestDroneDimension.mac, + ); + final runner = ToolCommandRunner( environment: testEnv.environment, - configs: configs, + configs: { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }, ); final result = await runner.run([ 'build', '--config', - 'ci/android_debug_rbe_arm64', + 'ci/android_debug_arm64', '-j', '500', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); - expect(testEnv.processHistory[0].command[0], - contains(path.join('tools', 'gn'))); - expect(testEnv.processHistory[0].command[2], equals('--rbe')); - expect(testEnv.processHistory[2].command.contains('500'), isTrue); + + print(testEnv.processHistory); + final [_, ninja, ..._] = testEnv.processHistory; + expect( + ninja.command, + containsAllInOrder([ + endsWith('ninja/ninja'), + '-j', + '500', + ]), + ); }); test('build command fails when rbe is enabled but not supported', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, // Intentionally omit withRbe: true. // That means the //flutter/build/rbe directory will not be created. ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'ci/android_debug_rbe_arm64', + dimension: TestDroneDimension.mac, + enableRbe: true, + ); final runner = ToolCommandRunner( environment: testEnv.environment, - configs: configs, + configs: { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }, ); final result = await runner.run([ 'build', @@ -216,14 +333,28 @@ void main() { test('build command does not run rbe when disabled', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, withRbe: true, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'ci/android_debug_rbe_arm64', + dimension: TestDroneDimension.mac, + + // Intentionally show that RBE is disabled. + // ignore: avoid_redundant_argument_values + enableRbe: false, + ); final runner = ToolCommandRunner( environment: testEnv.environment, - configs: configs, + configs: { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }, ); final result = await runner.run([ 'build', @@ -231,36 +362,64 @@ void main() { 'ci/android_debug_rbe_arm64', '--no-rbe', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); - expect(testEnv.processHistory[0].command[0], - contains(path.join('tools', 'gn'))); - expect(testEnv.processHistory[0].command, isNot(contains(['--rbe']))); - expect(testEnv.processHistory[1].command[0], - contains(path.join('ninja', 'ninja'))); + + final [gn, ninja, ..._] = testEnv.processHistory; + expect(gn.command, isNot(contains('--rbe'))); + + expect( + ninja.command, + containsAllInOrder( + [ + endsWith('ninja/ninja'), + ], + ), + ); }); test('build command does not run rbe when rbe configs do not exist', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'ci/android_debug_rbe_arm64', + dimension: TestDroneDimension.mac, + enableRbe: true, + ); final runner = ToolCommandRunner( environment: testEnv.environment, - configs: configs, + configs: { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }, ); final result = await runner.run([ 'build', '--config', 'ci/android_debug_rbe_arm64', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); - expect(testEnv.processHistory[0].command[0], - contains(path.join('tools', 'gn'))); - expect(testEnv.processHistory[0].command, isNot(contains(['--rbe']))); - expect(testEnv.processHistory[1].command[0], - contains(path.join('ninja', 'ninja'))); + + final [gn, ninja, ..._] = testEnv.processHistory; + expect(gn.command, isNot(contains('--rbe'))); + expect( + ninja.command, + containsAllInOrder( + [ + endsWith('ninja/ninja'), + ], + ), + ); }); test('mangleConfigName removes the OS and adds ci/ as needed', () { @@ -359,60 +518,108 @@ void main() { test('build command invokes ninja with the specified target', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'ci/host_debug', + targetDir: 'host_debug', + dimension: TestDroneDimension.mac, + ); final runner = ToolCommandRunner( environment: testEnv.environment, - configs: configs, + configs: { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }, ); final result = await runner.run([ 'build', '--config', - 'host_debug', + 'ci/host_debug', '//flutter/fml:fml_arc_unittests', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); - expect(testEnv.processHistory, containsCommand((command) { - return command.length > 3 && - command[0].contains('ninja') && - command[1].contains('-C') && - command[2].endsWith('/host_debug') && - // TODO(matanlurey): Tighten this up to be more specific. - // The reason we need a broad check is because the test fixture - // always returns multiple targets for gn desc, even though that is - // not the actual behavior. - command.sublist(3).contains('flutter/fml:fml_arc_unittests'); - })); + + final ninjaCmd = testEnv.processHistory.firstWhere( + (p) => p.command.first.endsWith('ninja'), + ); + expect( + ninjaCmd.command, + containsAllInOrder( + [ + endsWith('ninja'), + '-C', + endsWith('host_debug'), + ], + ), + ); + expect( + ninjaCmd.command, + contains( + contains('flutter/fml:fml_arc_unittests'), + ), + ); }); test('build command invokes ninja with all matched targets', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'ci/host_debug', + targetDir: 'host_debug', + dimension: TestDroneDimension.mac, + ); final runner = ToolCommandRunner( environment: testEnv.environment, - configs: configs, + configs: { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }, ); final result = await runner.run([ 'build', '--config', - 'host_debug', + 'ci/host_debug', '//flutter/...', ]); + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); - expect(testEnv.processHistory, containsCommand((command) { - return command.length > 5 && - command[0].contains('ninja') && - command[1].contains('-C') && - command[2].endsWith('/host_debug') && - command[3] == 'flutter/display_list:display_list_unittests' && - command[4] == 'flutter/flow:flow_unittests' && - command[5] == 'flutter/fml:fml_arc_unittests'; - })); + + final ninjaCmd = testEnv.processHistory.firstWhere( + (p) => p.command.first.endsWith('ninja'), + ); + expect( + ninjaCmd.command, + containsAllInOrder( + [ + endsWith('ninja'), + '-C', + endsWith('host_debug'), + ], + ), + ); + + expect( + ninjaCmd.command, + containsAll([ + 'flutter/display_list:display_list_unittests', + 'flutter/flow:flow_unittests', + 'flutter/fml:fml_arc_unittests', + ]), + ); }); test('build command gracefully handles no matched targets', () async { @@ -420,27 +627,42 @@ void main() { CannedProcess( (command) => command.contains('desc'), stdout: fixtures.gnDescOutputEmpty( - gnPattern: 'testing/scenario_app:sceario_app'), + gnPattern: 'testing/scenario_app:sceario_app', + ), exitCode: 1, ), ]; final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'ci/host_debug', + targetDir: 'host_debug', + dimension: TestDroneDimension.mac, + ); final runner = ToolCommandRunner( environment: testEnv.environment, - configs: configs, + configs: { + 'mac_test_config': builder.buildConfig( + path: 'ci/builders/mac_test_config.json', + ), + }, ); final result = await runner.run([ 'build', '--config', - 'host_debug', + 'ci/host_debug', // Intentionally omit the prefix '//flutter/' to trigger the warning. '//testing/scenario_app', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); + expect( testEnv.testLogs.map((LogRecord r) => r.message).join(), contains('No targets matched the pattern `testing/scenario_app'), @@ -456,7 +678,7 @@ void main() { final runner = ToolCommandRunner( environment: testEnv.environment, - configs: configs, + configs: {}, help: true, ); final result = await runner.run([ @@ -474,21 +696,74 @@ void main() { ); }); + test('verbose "et help build" contains CI builds', () async { + final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, + cannedProcesses: cannedProcesses, + verbose: true, + ); + addTearDown(testEnv.cleanup); + + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'ci/linux_android_debug', + dimension: TestDroneDimension.mac, + ); + final runner = ToolCommandRunner( + environment: testEnv.environment, + configs: { + 'linux_test_config': builder.buildConfig( + path: 'ci/builders/linux_test_config.json', + ) + }, + help: true, + ); + final result = await runner.run([ + '--verbose', + 'help', + 'build', + ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); + expect(result, equals(0)); + + // Avoid a degenerate case where nothing is logged. + expect(testEnv.testLogs, isNotEmpty, reason: 'No logs were emitted'); + print(testEnv.testLogs); + + expect( + testEnv.testLogs.map((LogRecord r) => r.message), + contains(contains('[ci/')), + ); + }); + test('non-verbose "et help build" does not contain ci builds', () async { final testEnv = TestEnvironment.withTestEngine( + abi: Abi.macosArm64, cannedProcesses: cannedProcesses, ); addTearDown(testEnv.cleanup); + final builder = TestBuilderConfig(); + builder.addBuild( + name: 'ci/linux_android_debug', + dimension: TestDroneDimension.mac, + ); final runner = ToolCommandRunner( environment: testEnv.environment, - configs: configs, + configs: { + 'linux_test_config': builder.buildConfig( + path: 'ci/builders/linux_test_config.json', + ) + }, help: true, ); final result = await runner.run([ 'help', 'build', ]); + + printOnFailure(testEnv.testLogs.map((r) => r.message).join('\n')); expect(result, equals(0)); // Avoid a degenerate case where nothing is logged. @@ -496,7 +771,8 @@ void main() { expect( testEnv.testLogs.map((LogRecord r) => r.message), - isNot(contains('[ci/')), + isNot(contains(contains('[ci/'))), + reason: 'The log should not contain CI-prefixed builds', ); }); } diff --git a/tools/engine_tool/test/lint_command_test.dart b/tools/engine_tool/test/lint_command_test.dart index 2d6eb697b73c8..bae295b499576 100644 --- a/tools/engine_tool/test/lint_command_test.dart +++ b/tools/engine_tool/test/lint_command_test.dart @@ -2,11 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:convert' as convert; import 'dart:ffi' as ffi show Abi; import 'dart:io' as io; -import 'package:engine_build_configs/engine_build_configs.dart'; import 'package:engine_repo_tools/engine_repo_tools.dart'; import 'package:engine_tool/src/commands/command_runner.dart'; import 'package:engine_tool/src/environment.dart'; @@ -16,56 +14,25 @@ import 'package:process_fakes/process_fakes.dart'; import 'package:process_runner/process_runner.dart'; import 'package:test/test.dart'; -import 'fixtures.dart' as fixtures; - void main() { - final Engine engine; - try { - engine = Engine.findWithin(); - } catch (e) { - io.stderr.writeln(e); - io.exitCode = 1; - return; - } - final BuilderConfig linuxTestConfig = BuilderConfig.fromJson( - path: 'ci/builders/linux_test_config.json', - map: convert.jsonDecode(fixtures.testConfig('Linux', Platform.linux)) - as Map, - ); - - final BuilderConfig macTestConfig = BuilderConfig.fromJson( - path: 'ci/builders/mac_test_config.json', - map: convert.jsonDecode(fixtures.testConfig('Mac-12', Platform.macOS)) - as Map, - ); - - final BuilderConfig winTestConfig = BuilderConfig.fromJson( - path: 'ci/builders/win_test_config.json', - map: convert.jsonDecode(fixtures.testConfig('Windows-11', Platform.windows)) - as Map, - ); - - final Map configs = { - 'linux_test_config': linuxTestConfig, - 'mac_test_config': macTestConfig, - 'win_test_config': winTestConfig, - }; + final engine = Engine.findWithin(); (Environment, List>) macEnv(Logger logger) { - final List> runHistory = >[]; + final runHistory = >[]; return ( Environment( abi: ffi.Abi.macosArm64, engine: engine, platform: FakePlatform( - operatingSystem: Platform.macOS, - resolvedExecutable: io.Platform.resolvedExecutable, - pathSeparator: '/'), + operatingSystem: Platform.macOS, + resolvedExecutable: io.Platform.resolvedExecutable, + pathSeparator: '/', + ), processRunner: ProcessRunner( - processManager: FakeProcessManager(onStart: (List command) { + processManager: FakeProcessManager(onStart: (command) { runHistory.add(command); return FakeProcess(); - }, onRun: (List command) { + }, onRun: (command) { // Should not be executed. assert(false); return io.ProcessResult(81, 1, '', ''); @@ -81,7 +48,7 @@ void main() { final (Environment env, List> runHistory) = macEnv(logger); final ToolCommandRunner runner = ToolCommandRunner( environment: env, - configs: configs, + configs: {}, ); final int result = await runner.run(['lint']); expect(result, equals(0)); diff --git a/tools/engine_tool/test/src/test_build_configs.dart b/tools/engine_tool/test/src/test_build_configs.dart new file mode 100644 index 0000000000000..5cc50b467013d --- /dev/null +++ b/tools/engine_tool/test/src/test_build_configs.dart @@ -0,0 +1,138 @@ +// Copyright 2013 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. + +import 'package:engine_build_configs/engine_build_configs.dart'; + +/// Builds a test [BuilderConfig]. +/// +/// Many tests will involve exactly one build configuration, or a small number +/// of build configurations. Instead of constructing these configurations ahead +/// of time, this builder is used to create them on-the-fly, with convenient +/// methods for setting up and cloning configurations. +/// +/// This builder exists in order to avoid global fixtures in tests that do not +/// isolate elements of the test environment relevant to their test. Prior to +/// the builder, 100s of lines of static configuration were used to setup and +/// instrument tests across multiple files; instead, this builder is used to +/// precisely configure the test environment for each test. +/// +/// See for more information. +final class TestBuilderConfig { + final _builds = >[]; + + /// Appends a build to the configuration. + void addBuild({ + required String name, + required TestDroneDimension dimension, + bool enableRbe = false, + String description = 'A default description.', + String? targetDir, + (String, List)? generatorTask, + (String, List)? testTask, + }) { + _builds.add({ + 'archives': [], + 'drone_dimensions': [ + dimension._dimension, + ], + 'gclient_variables': {}, + 'gn': [ + if (enableRbe) '--rbe', + ], + 'name': name, + 'description': description, + 'ninja': { + if (targetDir case final targetDir?) ...{ + 'config': targetDir, + 'targets': ['ninja_target'], + } + }, + 'tests': _testTask(testTask), + 'generators': _generatorTask(generatorTask), + }); + } + + static List _testTask((String, List)? task) { + if (task == null) { + return []; + } + final (script, args) = task; + return [ + { + 'name': 'test_task', + 'language': 'python', + 'scripts': [script], + 'parameters': args, + 'contexts': ['context'], + }, + ]; + } + + static Map _generatorTask((String, List)? task) { + if (task == null) { + return {}; + } + final (script, args) = task; + return { + 'tasks': [ + { + 'name': 'generator_task', + 'language': 'python', + 'scripts': [script], + 'parameters': args, + }, + ], + }; + } + + /// Copies the state of `this` as a new [TestBuilderConfig]. + TestBuilderConfig clone() { + final clone = TestBuilderConfig(); + clone._builds.addAll(_builds); + return clone; + } + + /// Creates and returns a [BuilderConfig] capturing the current builder state. + /// + /// [path] is the path to the configuration file that would be read from disk. + /// + /// After creation, the builder state remains, and changes can be made to the + /// builder to create a new configuration without affecting the previous one + /// created. + BuilderConfig buildConfig({ + required String path, + }) { + final config = BuilderConfig.fromJson(map: buildJson(), path: path); + if (config.check(path) case final errors when errors.isNotEmpty) { + throw StateError('Invalid configuration:\n${errors.join('\n')}'); + } + return config; + } + + /// Creates and returns the JSON serialized format of a [BuilderConfig]. + /// + /// Most of the time, use [build] instead of this method. + /// + /// It is undefined behavior to mutate the returned map. + Map buildJson() { + return { + 'builds': _builds, + }; + } +} + +/// Fixed set of dimensions for [TestBuilderConfig.addBuild]. +enum TestDroneDimension { + /// Runs on Linux. + linux('os=Linux'), + + /// Runs on macOS. + mac('os=Mac-12'), + + /// Runs on Windows. + win('os=Windows-11'); + + const TestDroneDimension(this._dimension); + final String _dimension; +} diff --git a/tools/engine_tool/test/utils.dart b/tools/engine_tool/test/utils.dart index 6cc07e320bd13..4ab0c4dd4e44b 100644 --- a/tools/engine_tool/test/utils.dart +++ b/tools/engine_tool/test/utils.dart @@ -69,10 +69,10 @@ class TestEnvironment { abi: abi, engine: engine, platform: FakePlatform( - operatingSystem: _operatingSystemForAbi(abi), - resolvedExecutable: io.Platform.resolvedExecutable, - pathSeparator: _pathSeparatorForAbi(abi), - numberOfProcessors: 32, + operatingSystem: _operatingSystemForAbi(abi), + resolvedExecutable: io.Platform.resolvedExecutable, + pathSeparator: _pathSeparatorForAbi(abi), + numberOfProcessors: 32, ), processRunner: ProcessRunner( processManager: FakeProcessManager(onStart: (List command) { @@ -84,7 +84,8 @@ class TestEnvironment { return processResult; }, onRun: (List command) { final io.ProcessResult result = _getCannedProcessResult( - command, cannedProcesses, + command, + cannedProcesses, ); processHistory.add(ExecutedProcess( command,