blob: b830640caf280747f59b71017548e790b43d76fe [file] [log] [blame]
// 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:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.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_ios_framework.dart';
import 'package:flutter_tools/src/commands/build_macos_framework.dart';
import 'package:flutter_tools/src/version.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fake_process_manager.dart';
import '../../src/fakes.dart';
import '../../src/test_build_system.dart';
void main() {
late MemoryFileSystem memoryFileSystem;
late Directory outputDirectory;
late FakePlatform fakePlatform;
setUpAll(() {
Cache.disableLocking();
});
const String storageBaseUrl = 'https://fake.googleapis.com';
setUp(() {
memoryFileSystem = MemoryFileSystem.test();
fakePlatform = FakePlatform(
operatingSystem: 'macos',
environment: <String, String>{
'FLUTTER_STORAGE_BASE_URL': storageBaseUrl,
},
);
outputDirectory = memoryFileSystem.systemTempDirectory
.createTempSync('flutter_build_framework_test_output.')
.childDirectory('Debug')
..createSync();
});
group('build ios-framework', () {
group('podspec', () {
const String engineRevision = '0123456789abcdef';
late Cache cache;
setUp(() {
final Directory rootOverride = memoryFileSystem.directory('cache');
cache = Cache.test(
rootOverride: rootOverride,
platform: fakePlatform,
fileSystem: memoryFileSystem,
processManager: FakeProcessManager.any(),
);
rootOverride.childDirectory('bin').childDirectory('internal').childFile('engine.version')
..createSync(recursive: true)
..writeAsStringSync(engineRevision);
});
testUsingContext('version unknown', () async {
const String frameworkVersion = '0.0.0-unknown';
final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(frameworkVersion: frameworkVersion);
final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
expect(() => command.produceFlutterPodspec(BuildMode.debug, outputDirectory),
throwsToolExit(message: 'Detected version is $frameworkVersion'));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('throws when not on a released version', () async {
const String frameworkVersion = 'v1.13.10+hotfix-pre.2';
const GitTagVersion gitTagVersion = GitTagVersion(
x: 1,
y: 13,
z: 10,
hotfix: 13,
commits: 2,
);
final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(
gitTagVersion: gitTagVersion,
frameworkVersion: frameworkVersion,
);
final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
expect(() => command.produceFlutterPodspec(BuildMode.debug, outputDirectory),
throwsToolExit(message: 'Detected version is $frameworkVersion'));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('throws when license not found', () async {
final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(
gitTagVersion: const GitTagVersion(
x: 1,
y: 13,
z: 10,
hotfix: 13,
commits: 0,
),
);
final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
expect(() => command.produceFlutterPodspec(BuildMode.debug, outputDirectory),
throwsToolExit(message: 'Could not find license'));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
group('is created', () {
const String frameworkVersion = 'v1.13.11+hotfix.13';
const String licenseText = 'This is the license!';
setUp(() {
// cache.getLicenseFile() relies on the flutter root being set.
Cache.flutterRoot ??= getFlutterRoot();
cache.getLicenseFile()
..createSync(recursive: true)
..writeAsStringSync(licenseText);
});
group('on master channel', () {
testUsingContext('created when forced', () async {
const GitTagVersion gitTagVersion = GitTagVersion(
x: 1,
y: 13,
z: 11,
hotfix: 13,
commits: 100,
);
final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(
gitTagVersion: gitTagVersion,
frameworkVersion: frameworkVersion,
);
final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
command.produceFlutterPodspec(BuildMode.debug, outputDirectory, force: true);
final File expectedPodspec = outputDirectory.childFile('Flutter.podspec');
expect(expectedPodspec.existsSync(), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
});
group('not on master channel', () {
late FakeFlutterVersion fakeFlutterVersion;
setUp(() {
const GitTagVersion gitTagVersion = GitTagVersion(
x: 1,
y: 13,
z: 11,
hotfix: 13,
commits: 0,
);
fakeFlutterVersion = FakeFlutterVersion(
gitTagVersion: gitTagVersion,
frameworkVersion: frameworkVersion,
);
});
testUsingContext('contains license and version', () async {
final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
command.produceFlutterPodspec(BuildMode.debug, outputDirectory);
final File expectedPodspec = outputDirectory.childFile('Flutter.podspec');
final String podspecContents = expectedPodspec.readAsStringSync();
expect(podspecContents, contains("'1.13.1113'"));
expect(podspecContents, contains('# $frameworkVersion'));
expect(podspecContents, contains(licenseText));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('debug URL', () async {
final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
command.produceFlutterPodspec(BuildMode.debug, outputDirectory);
final File expectedPodspec = outputDirectory.childFile('Flutter.podspec');
final String podspecContents = expectedPodspec.readAsStringSync();
expect(podspecContents, contains("'$storageBaseUrl/flutter_infra_release/flutter/$engineRevision/ios/artifacts.zip'"));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('profile URL', () async {
final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
command.produceFlutterPodspec(BuildMode.profile, outputDirectory);
final File expectedPodspec = outputDirectory.childFile('Flutter.podspec');
final String podspecContents = expectedPodspec.readAsStringSync();
expect(podspecContents, contains("'$storageBaseUrl/flutter_infra_release/flutter/$engineRevision/ios-profile/artifacts.zip'"));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('release URL', () async {
final BuildIOSFrameworkCommand command = BuildIOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
command.produceFlutterPodspec(BuildMode.release, outputDirectory);
final File expectedPodspec = outputDirectory.childFile('Flutter.podspec');
final String podspecContents = expectedPodspec.readAsStringSync();
expect(podspecContents, contains("'$storageBaseUrl/flutter_infra_release/flutter/$engineRevision/ios-release/artifacts.zip'"));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
});
});
});
});
group('build macos-framework', () {
group('podspec', () {
const String engineRevision = '0123456789abcdef';
late Cache cache;
setUp(() {
final Directory rootOverride = memoryFileSystem.directory('cache');
cache = Cache.test(
rootOverride: rootOverride,
platform: fakePlatform,
fileSystem: memoryFileSystem,
processManager: FakeProcessManager.any(),
);
rootOverride.childDirectory('bin').childDirectory('internal').childFile('engine.version')
..createSync(recursive: true)
..writeAsStringSync(engineRevision);
});
testUsingContext('version unknown', () async {
const String frameworkVersion = '0.0.0-unknown';
final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(frameworkVersion: frameworkVersion);
final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
expect(() => command.produceFlutterPodspec(BuildMode.debug, outputDirectory),
throwsToolExit(message: 'Detected version is $frameworkVersion'));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('throws when not on a released version', () async {
const String frameworkVersion = 'v1.13.10+hotfix-pre.2';
const GitTagVersion gitTagVersion = GitTagVersion(
x: 1,
y: 13,
z: 10,
hotfix: 13,
commits: 2,
);
final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(
gitTagVersion: gitTagVersion,
frameworkVersion: frameworkVersion,
);
final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
expect(() => command.produceFlutterPodspec(BuildMode.debug, outputDirectory),
throwsToolExit(message: 'Detected version is $frameworkVersion'));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('throws when license not found', () async {
final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(
gitTagVersion: const GitTagVersion(
x: 1,
y: 13,
z: 10,
hotfix: 13,
commits: 0,
),
);
final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
expect(() => command.produceFlutterPodspec(BuildMode.debug, outputDirectory),
throwsToolExit(message: 'Could not find license'));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
group('is created', () {
const String frameworkVersion = 'v1.13.11+hotfix.13';
const String licenseText = 'This is the license!';
setUp(() {
// cache.getLicenseFile() relies on the flutter root being set.
Cache.flutterRoot ??= getFlutterRoot();
cache.getLicenseFile()
..createSync(recursive: true)
..writeAsStringSync(licenseText);
});
group('on master channel', () {
testUsingContext('created when forced', () async {
const GitTagVersion gitTagVersion = GitTagVersion(
x: 1,
y: 13,
z: 11,
hotfix: 13,
commits: 100,
);
final FakeFlutterVersion fakeFlutterVersion = FakeFlutterVersion(
gitTagVersion: gitTagVersion,
frameworkVersion: frameworkVersion,
);
final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
command.produceFlutterPodspec(BuildMode.debug, outputDirectory, force: true);
final File expectedPodspec = outputDirectory.childFile('FlutterMacOS.podspec');
expect(expectedPodspec.existsSync(), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
});
group('not on master channel', () {
late FakeFlutterVersion fakeFlutterVersion;
setUp(() {
const GitTagVersion gitTagVersion = GitTagVersion(
x: 1,
y: 13,
z: 11,
hotfix: 13,
commits: 0,
);
fakeFlutterVersion = FakeFlutterVersion(
gitTagVersion: gitTagVersion,
frameworkVersion: frameworkVersion,
);
});
testUsingContext('contains license and version', () async {
final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
command.produceFlutterPodspec(BuildMode.debug, outputDirectory);
final File expectedPodspec = outputDirectory.childFile('FlutterMacOS.podspec');
final String podspecContents = expectedPodspec.readAsStringSync();
expect(podspecContents, contains("'1.13.1113'"));
expect(podspecContents, contains('# $frameworkVersion'));
expect(podspecContents, contains(licenseText));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('debug URL', () async {
final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
command.produceFlutterPodspec(BuildMode.debug, outputDirectory);
final File expectedPodspec = outputDirectory.childFile('FlutterMacOS.podspec');
final String podspecContents = expectedPodspec.readAsStringSync();
expect(podspecContents, contains("'$storageBaseUrl/flutter_infra_release/flutter/$engineRevision/darwin-x64/artifacts.zip'"));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('profile URL', () async {
final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
command.produceFlutterPodspec(BuildMode.profile, outputDirectory);
final File expectedPodspec = outputDirectory.childFile('FlutterMacOS.podspec');
final String podspecContents = expectedPodspec.readAsStringSync();
expect(podspecContents, contains("'$storageBaseUrl/flutter_infra_release/flutter/$engineRevision/darwin-x64-profile/artifacts.zip'"));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('release URL', () async {
final BuildMacOSFrameworkCommand command = BuildMacOSFrameworkCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
platform: fakePlatform,
flutterVersion: fakeFlutterVersion,
cache: cache,
verboseHelp: false,
);
command.produceFlutterPodspec(BuildMode.release, outputDirectory);
final File expectedPodspec = outputDirectory.childFile('FlutterMacOS.podspec');
final String podspecContents = expectedPodspec.readAsStringSync();
expect(podspecContents, contains("'$storageBaseUrl/flutter_infra_release/flutter/$engineRevision/darwin-x64-release/artifacts.zip'"));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
});
});
});
});
group('XCFrameworks', () {
late MemoryFileSystem fileSystem;
late FakeProcessManager fakeProcessManager;
setUp(() {
fileSystem = MemoryFileSystem.test();
fakeProcessManager = FakeProcessManager.empty();
});
testWithoutContext('created', () async {
final Directory frameworkA = fileSystem.directory('FrameworkA.framework')..createSync();
final Directory frameworkB = fileSystem.directory('FrameworkB.framework')..createSync();
final Directory output = fileSystem.directory('output');
fakeProcessManager.addCommand(FakeCommand(
command: <String>[
'xcrun',
'xcodebuild',
'-create-xcframework',
'-framework',
frameworkA.path,
'-framework',
frameworkB.path,
'-output',
output.childDirectory('Combine.xcframework').path,
],
));
await BuildFrameworkCommand.produceXCFramework(
<Directory>[frameworkA, frameworkB],
'Combine',
output,
fakeProcessManager,
);
expect(fakeProcessManager, hasNoRemainingExpectations);
});
testWithoutContext('created with symbols', () async {
final Directory parentA = fileSystem.directory('FrameworkA')..createSync();
final File bcsymbolmapA = parentA.childFile('ABC123.bcsymbolmap')..createSync();
final File dSYMA = parentA.childFile('FrameworkA.framework.dSYM')..createSync();
final Directory frameworkA = parentA.childDirectory('FrameworkA.framework')..createSync();
final Directory parentB = fileSystem.directory('FrameworkB')..createSync();
final File bcsymbolmapB = parentB.childFile('ZYX987.bcsymbolmap')..createSync();
final File dSYMB = parentB.childFile('FrameworkB.framework.dSYM')..createSync();
final Directory frameworkB = parentB.childDirectory('FrameworkB.framework')..createSync();
final Directory output = fileSystem.directory('output');
fakeProcessManager.addCommand(FakeCommand(
command: <String>[
'xcrun',
'xcodebuild',
'-create-xcframework',
'-framework',
frameworkA.path,
'-debug-symbols',
bcsymbolmapA.path,
'-debug-symbols',
dSYMA.path,
'-framework',
frameworkB.path,
'-debug-symbols',
bcsymbolmapB.path,
'-debug-symbols',
dSYMB.path,
'-output',
output.childDirectory('Combine.xcframework').path,
],
));
await BuildFrameworkCommand.produceXCFramework(
<Directory>[frameworkA, frameworkB],
'Combine',
output,
fakeProcessManager,
);
expect(fakeProcessManager, hasNoRemainingExpectations);
});
});
}