blob: c650a3f07f2dfa3fe96feb789d53cb47d9b7f17a [file]
// 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.
import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_builder.dart';
import 'package:flutter_tools/src/base/exit.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import '../../src/android_common.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fake_build_command.dart';
import '../../src/fakes.dart';
import '../../src/test_build_system.dart';
import '../../src/test_flutter_command_runner.dart';
void main() {
setUp(() {
Cache.disableLocking();
});
tearDown(() {
restoreExitFunction();
});
testUsingContext('flutter build without arguments prints help and exits with code 1', () async {
int? exitCode;
setExitFunctionForTests((int code) {
exitCode = code;
});
final BuildCommand command = createFakeBuildCommand(
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: testLogger,
osUtils: FakeOperatingSystemUtils(),
config: FakeConfig(),
platform: FakePlatform(),
fileSystemUtils: FakeFileSystemUtils(),
terminal: FakeTerminal(),
plistParser: FakePlistParser(),
processUtils: FakeProcessUtils(),
processManager: FakeProcessManager.any(),
templateRenderer: FakeTemplateRenderer(),
xcode: FakeXcode(),
artifacts: FakeArtifacts(),
cache: FakeCache(),
flutterVersion: FakeFlutterVersion(),
featureFlags: TestFeatureFlags(),
);
final CommandRunner<void> commandRunner = createTestCommandRunner(command);
await commandRunner.run(<String>['build']);
expect(exitCode, 1);
expect(testLogger.statusText, contains(command.description));
});
testUsingContext('obfuscate requires split-debug-info', () {
final command = FakeBuildInfoCommand();
final CommandRunner<void> commandRunner = createTestCommandRunner(command);
expect(
() => commandRunner.run(<String>['fake', '--obfuscate']),
throwsToolExit(
message:
'"--${FlutterOptions.kDartObfuscationOption}" can only be used in '
'combination with "--${FlutterOptions.kSplitDebugInfoOption}"',
),
);
});
group('Fatal Logs', () {
late FakeBuildCommand command;
late MemoryFileSystem fs;
late ProcessManager processManager;
setUp(() {
fs = MemoryFileSystem.test();
fs.file('/package/pubspec.yaml').createSync(recursive: true);
fs.currentDirectory = '/package';
Cache.disableLocking();
processManager = FakeProcessManager.empty();
});
testUsingContext("doesn't fail if --fatal-warnings specified and no warnings occur", () async {
command = FakeBuildCommand(
androidContext: FakeAndroidContext(),
appleContext: FakeAppleContext(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
templateRenderer: FakeTemplateRenderer(),
toolContext: FakeToolContext(fs: fs, logger: testLogger),
);
try {
await createTestCommandRunner(command)
.run(<String>['build', 'test', '--${FlutterOptions.kFatalWarnings}']);
} on Exception {
fail('Unexpected exception thrown');
}
}, overrides: <Type, Generator>{FileSystem: () => fs, ProcessManager: () => processManager});
testUsingContext("doesn't fail if --fatal-warnings not specified", () async {
command = FakeBuildCommand(
androidContext: FakeAndroidContext(),
appleContext: FakeAppleContext(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
templateRenderer: FakeTemplateRenderer(),
toolContext: FakeToolContext(fs: fs, logger: testLogger),
);
testLogger.printWarning('Warning: Mild annoyance Will Robinson!');
try {
await createTestCommandRunner(command).run(<String>['build', 'test']);
} on Exception {
fail('Unexpected exception thrown');
}
}, overrides: <Type, Generator>{FileSystem: () => fs, ProcessManager: () => processManager});
testUsingContext('fails if --fatal-warnings specified and warnings emitted', () async {
command = FakeBuildCommand(
androidContext: FakeAndroidContext(),
appleContext: FakeAppleContext(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
templateRenderer: FakeTemplateRenderer(),
toolContext: FakeToolContext(fs: fs, logger: testLogger),
);
testLogger.printWarning('Warning: Mild annoyance Will Robinson!');
await expectLater(
createTestCommandRunner(command)
.run(<String>['build', 'test', '--${FlutterOptions.kFatalWarnings}']),
throwsToolExit(
message:
'Logger received warning output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.',
),
);
}, overrides: <Type, Generator>{FileSystem: () => fs, ProcessManager: () => processManager});
testUsingContext('fails if --fatal-warnings specified and errors emitted', () async {
command = FakeBuildCommand(
androidContext: FakeAndroidContext(),
appleContext: FakeAppleContext(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
templateRenderer: FakeTemplateRenderer(),
toolContext: FakeToolContext(fs: fs, logger: testLogger),
);
testLogger.printError('Error: Danger Will Robinson!');
await expectLater(
createTestCommandRunner(command)
.run(<String>['build', 'test', '--${FlutterOptions.kFatalWarnings}']),
throwsToolExit(
message:
'Logger received error output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.',
),
);
}, overrides: <Type, Generator>{FileSystem: () => fs, ProcessManager: () => processManager});
});
}
class FakeBuildInfoCommand extends FlutterCommand {
FakeBuildInfoCommand() : super() {
addSplitDebugInfoOption();
addDartObfuscationOption();
}
@override
String get description => '';
@override
String get name => 'fake';
@override
Future<FlutterCommandResult> runCommand() async {
await getBuildInfo();
return FlutterCommandResult.success();
}
}
class FakeBuildCommand extends BuildCommand {
FakeBuildCommand({
required super.androidContext,
required super.appleContext,
required super.buildSystem,
required super.templateRenderer,
required super.toolContext,
AndroidBuilder? androidBuilder,
FeatureFlags? featureFlags,
bool verboseHelp = false,
}) : super(
androidBuilder: androidBuilder ?? FakeAndroidBuilder(),
featureFlags: featureFlags ?? TestFeatureFlags(),
verboseHelp: verboseHelp,
) {
addSubcommand(FakeBuildSubcommand(logger: toolContext.logger, verboseHelp: verboseHelp));
}
@override
String get description => '';
@override
String get name => 'build';
@override
Future<FlutterCommandResult> runCommand() async {
return FlutterCommandResult.success();
}
}
class FakeBuildSubcommand extends BuildSubCommand {
FakeBuildSubcommand({required super.logger, required super.verboseHelp});
@override
String get description => '';
@override
String get name => 'test';
@override
Future<FlutterCommandResult> runCommand() async {
return FlutterCommandResult.success();
}
}