flutter/packages/flutter_tools/test/general.shard/isolated/native_assets_test.dart
Martin Kustermann abcba1fa7e
Reland "[flutter_tools] Cleanup of native asset related code (removes around 50% of the native asset related code) (#155430)" (#155745)
Changes to original CL: The code that issues an error on unsupported
operating system in the dry-run case was missing a case for iOS and
Android

Original CL description

  tl;dr Removes 50% (>1650 locs) of native asset related code in
  `packages/flutter_tools`

Before this PR the invocation of dart build/link/dry-run was implemented
  per OS. This lead to very large code duplication of almost identical,
  but slightly different code. It also led to similarly duplicated test
  code.

  Almost the entire dart build/link/dry-run implementation is identical
  across OSes. There's small variations:

- configuration of the build (e.g. android/macos/ios version, ios sdk,
...)
  - determining target locations & copying the final shared libraries

This PR unifies the implementation by reducing the code to basically two
  main functions:

    * `runFlutterSpecificDartBuild` which is responsible for
      - obtain flutter configuration
      - perform dart build (& link)
      - determine target location & install binaries

  * `runFlutterSpecificDartDryRunOnPlatforms` which is responsible for a
  similar (but not same):
      - obtain flutter configuration
      - perform dart dry run
      - determine target location

  these two functions will call out to helpers for the OS specific
  functionality:

* `_assetTargetLocationsForOS` for determining the location of the code
  assets

* `_copyNativeCodeAssetsForOS` for copying the code assets (and possibly
  overriting the install name, etc)

=> Since we get rid of the code duplication across OSes and have only a
  single code path for the build/link/dry-run, we can also remove the
  duplicated tests that were pretty much identical across OSes.

  We also harden the building code by adding asserts, e.g.

    * the dry fun functionality should never be used by `flutter test`

* the `build/native_assets/<os>/native_assets.yaml` should only be used
       by `flutter test` and the dry-run of `flutter run`

  => We change the tests to also comply with these invariants (so the
  tests are not testing things that cannot happen in reality)

  We also rename `{,Flutter}NativeAssetsBuildRunner` to disambiguate it
  from the `package:native_asset_builder`'s `NativeAssetsBuildRunner`.
2024-09-26 12:21:29 +02:00

309 lines
11 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.
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import 'package:package_config/package_config_types.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart';
import 'fake_native_assets_build_runner.dart';
void main() {
late FakeProcessManager processManager;
late Environment environment;
late Artifacts artifacts;
late FileSystem fileSystem;
late BufferLogger logger;
late Uri projectUri;
setUp(() {
processManager = FakeProcessManager.empty();
logger = BufferLogger.test();
artifacts = Artifacts.test();
fileSystem = MemoryFileSystem.test();
environment = Environment.test(
fileSystem.currentDirectory,
inputs: <String, String>{},
artifacts: artifacts,
processManager: processManager,
fileSystem: fileSystem,
logger: logger,
);
environment.buildDir.createSync(recursive: true);
projectUri = environment.projectDir.uri;
});
testUsingContext('dry run with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
expect(
await runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[TargetPlatform.windows_x64],
buildRunner: FakeFlutterNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
),
null,
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('build with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
},
targetPlatform: TargetPlatform.windows_x64,
projectUri: projectUri,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
buildRunner: FakeFlutterNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run for multiple OSes with no package config', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
await runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[
TargetPlatform.windows_x64,
TargetPlatform.darwin,
TargetPlatform.ios,
],
buildRunner: FakeFlutterNativeAssetsBuildRunner(
hasPackageConfigResult: false,
),
);
expect(
(globals.logger as BufferLogger).traceText,
contains('No package config found. Skipping native assets compilation.'),
);
});
testUsingContext('dry run with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[TargetPlatform.windows_x64],
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('dry run with assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
final FakeFlutterNativeAssetsBuildRunner buildRunner = FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: FakeFlutterNativeAssetsBuilderResult(
assets: <AssetImpl>[
NativeCodeAssetImpl(
id: 'package:bar/bar.dart',
linkMode: DynamicLoadingBundledImpl(),
os: OSImpl.windows,
architecture: ArchitectureImpl.x64,
file: Uri.file('bar.dll'),
),
],
),
);
final Uri? nativeAssetsYaml = await runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[TargetPlatform.windows_x64],
buildRunner: buildRunner,
);
expect(
(globals.logger as BufferLogger).traceText,
stringContainsInOrder(<String>[
'Dry running native assets for windows.',
'Dry running native assets for windows done.',
]),
);
expect(
nativeAssetsYaml,
projectUri.resolve('build/native_assets/windows/native_assets.yaml'),
);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
contains('package:bar/bar.dart'),
);
expect(buildRunner.buildDryRunInvocations, 1);
});
testUsingContext('build with assets but not enabled', overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
},
targetPlatform: TargetPlatform.windows_x64,
projectUri: projectUri,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
),
throwsToolExit(
message: 'Package(s) bar require the native assets feature to be enabled. '
'Enable using `flutter config --enable-native-assets`.',
),
);
});
testUsingContext('build no assets', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
final (_, Uri nativeAssetsYaml) = await runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
},
targetPlatform: TargetPlatform.windows_x64,
projectUri: projectUri,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
),
);
expect(nativeAssetsYaml, nonFlutterTesterAssetUri);
expect(
await fileSystem.file(nativeAssetsYaml).readAsString(),
isNot(contains('package:bar/bar.dart')),
);
expect(
environment.projectDir.childDirectory('build').childDirectory('native_assets').childDirectory('windows'),
exists,
);
});
testUsingContext('Native assets dry run error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => runFlutterSpecificDartDryRunOnPlatforms(
projectUri: projectUri,
fileSystem: fileSystem,
targetPlatforms: <TargetPlatform>[TargetPlatform.windows_x64],
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildDryRunResult: const FakeFlutterNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building (dry run) native assets failed. See the logs for more details.',
),
);
});
testUsingContext('Native assets build error', overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
ProcessManager: () => FakeProcessManager.empty(),
}, () async {
final File packageConfig =
environment.projectDir.childFile('.dart_tool/package_config.json');
final Uri nonFlutterTesterAssetUri = environment.buildDir.childFile('native_assets.yaml').uri;
await packageConfig.parent.create();
await packageConfig.create();
expect(
() => runFlutterSpecificDartBuild(
environmentDefines: <String, String>{
kBuildMode: BuildMode.debug.cliName,
},
targetPlatform: TargetPlatform.linux_x64,
projectUri: projectUri,
nativeAssetsYamlUri: nonFlutterTesterAssetUri,
fileSystem: fileSystem,
buildRunner: FakeFlutterNativeAssetsBuildRunner(
packagesWithNativeAssetsResult: <Package>[
Package('bar', projectUri),
],
buildResult: const FakeFlutterNativeAssetsBuilderResult(
success: false,
),
),
),
throwsToolExit(
message:
'Building native assets failed. See the logs for more details.',
),
);
});
}