[flutter_tools] run web unit tests in sound null safety (#70799)
This commit is contained in:
parent
6d521e9aff
commit
a76289bb1b
@ -1126,13 +1126,13 @@ Future<void> _runFlutterWebTest(String workingDirectory, List<String> tests) asy
|
||||
'--concurrency=1', // do not parallelize on Cirrus, to reduce flakiness
|
||||
'-v',
|
||||
'--platform=chrome',
|
||||
'--sound-null-safety', // web tests do not autodetect yet.
|
||||
...?flutterTestArgs,
|
||||
...tests,
|
||||
],
|
||||
workingDirectory: workingDirectory,
|
||||
environment: <String, String>{
|
||||
'FLUTTER_WEB': 'true',
|
||||
'FLUTTER_LOW_RESOURCE_MODE': 'true',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
import 'dart:js' as js;
|
||||
|
||||
import 'package:flutter_driver/src/extension/_extension_web.dart';
|
||||
@ -10,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('test web_extension', () {
|
||||
Future<Map<String, dynamic>> Function(Map<String, String>) call;
|
||||
late Future<Map<String, dynamic>> Function(Map<String, String>) call;
|
||||
|
||||
setUp(() {
|
||||
call = (Map<String, String> args) async {
|
||||
|
@ -35,18 +35,9 @@ import '../globals.dart' as globals;
|
||||
import '../project.dart';
|
||||
import '../web/bootstrap.dart';
|
||||
import '../web/chrome.dart';
|
||||
import '../web/compile.dart';
|
||||
import '../web/memory_fs.dart';
|
||||
|
||||
/// Web rendering backend mode.
|
||||
enum WebRendererMode {
|
||||
/// Auto detects which rendering backend to use.
|
||||
autoDetect,
|
||||
/// Always uses canvaskit.
|
||||
canvaskit,
|
||||
/// Always uses html.
|
||||
html,
|
||||
}
|
||||
|
||||
typedef DwdsLauncher = Future<Dwds> Function(
|
||||
{@required AssetReader assetReader,
|
||||
@required Stream<BuildResult> buildResults,
|
||||
@ -549,46 +540,14 @@ class WebAssetServer implements AssetReader {
|
||||
return webSdkFile;
|
||||
}
|
||||
|
||||
static const Map<WebRendererMode, Map<NullSafetyMode, Artifact>> _dartSdkJsArtifactMap =
|
||||
<WebRendererMode, Map<NullSafetyMode, Artifact>> {
|
||||
WebRendererMode.autoDetect: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitAndHtmlSoundSdk,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitAndHtmlSdk,
|
||||
},
|
||||
WebRendererMode.canvaskit: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitSoundSdk,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitSdk,
|
||||
},
|
||||
WebRendererMode.html: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledSoundSdk,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledSdk,
|
||||
},
|
||||
};
|
||||
|
||||
static const Map<WebRendererMode, Map<NullSafetyMode, Artifact>> _dartSdkJsMapArtifactMap =
|
||||
<WebRendererMode, Map<NullSafetyMode, Artifact>> {
|
||||
WebRendererMode.autoDetect: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps,
|
||||
},
|
||||
WebRendererMode.canvaskit: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitSdkSourcemaps,
|
||||
},
|
||||
WebRendererMode.html: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledSoundSdkSourcemaps,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledSdkSourcemaps,
|
||||
},
|
||||
};
|
||||
|
||||
File get _resolveDartSdkJsFile =>
|
||||
globals.fs.file(globals.artifacts.getArtifactPath(
|
||||
_dartSdkJsArtifactMap[webRenderer][_nullSafetyMode]
|
||||
kDartSdkJsArtifactMap[webRenderer][_nullSafetyMode]
|
||||
));
|
||||
|
||||
File get _resolveDartSdkJsMapFile =>
|
||||
globals.fs.file(globals.artifacts.getArtifactPath(
|
||||
_dartSdkJsMapArtifactMap[webRenderer][_nullSafetyMode]
|
||||
kDartSdkJsMapArtifactMap[webRenderer][_nullSafetyMode]
|
||||
));
|
||||
|
||||
@override
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:package_config/package_config.dart';
|
||||
|
||||
import '../artifacts.dart';
|
||||
import '../base/common.dart';
|
||||
@ -10,6 +11,7 @@ import '../base/file_system.dart';
|
||||
import '../build_info.dart';
|
||||
import '../bundle.dart';
|
||||
import '../compile.dart';
|
||||
import '../dart/language_version.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../web/compile.dart';
|
||||
import '../web/memory_fs.dart';
|
||||
@ -27,13 +29,24 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
||||
@required List<String> testFiles,
|
||||
@required BuildInfo buildInfo,
|
||||
}) async {
|
||||
if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
|
||||
throwToolExit('flutter test --platform=chrome does not currently support sound mode');
|
||||
}
|
||||
LanguageVersion languageVersion = LanguageVersion(2, 8);
|
||||
Artifact platformDillArtifact;
|
||||
// TODO(jonahwilliams): to support autodetect this would need to partition the source code into a
|
||||
// a sound and unsound set and perform separate compilations.
|
||||
final List<String> extraFrontEndOptions = List<String>.of(buildInfo.extraFrontEndOptions ?? <String>[]);
|
||||
if (!extraFrontEndOptions.contains('--no-sound-null-safety')) {
|
||||
extraFrontEndOptions.add('--no-sound-null-safety');
|
||||
if (buildInfo.nullSafetyMode == NullSafetyMode.unsound || buildInfo.nullSafetyMode == NullSafetyMode.autodetect) {
|
||||
platformDillArtifact = Artifact.webPlatformKernelDill;
|
||||
if (!extraFrontEndOptions.contains('--no-sound-null-safety')) {
|
||||
extraFrontEndOptions.add('--no-sound-null-safety');
|
||||
}
|
||||
} else if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
|
||||
platformDillArtifact = Artifact.webPlatformSoundKernelDill;
|
||||
languageVersion = nullSafeVersion;
|
||||
if (!extraFrontEndOptions.contains('--sound-null-safety')) {
|
||||
extraFrontEndOptions.add('--sound-null-safety');
|
||||
}
|
||||
}
|
||||
|
||||
final Directory outputDirectory = globals.fs.directory(testOutputDir)
|
||||
..createSync(recursive: true);
|
||||
final List<File> generatedFiles = <File>[];
|
||||
@ -44,12 +57,12 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
||||
globals.fs.path.join(outputDirectory.path, '${relativeTestSegments.join('_')}.test.dart'));
|
||||
generatedFile
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(_generateEntrypoint(relativeTestSegments.join('/'), testFilePath));
|
||||
..writeAsStringSync(_generateEntrypoint(relativeTestSegments.join('/'), testFilePath, languageVersion));
|
||||
generatedFiles.add(generatedFile);
|
||||
}
|
||||
// Generate a fake main file that imports all tests to be executed. This will force
|
||||
// each of them to be compiled.
|
||||
final StringBuffer buffer = StringBuffer('// @dart=2.8\n');
|
||||
final StringBuffer buffer = StringBuffer('// @dart=${languageVersion.major}.${languageVersion.minor}\n');
|
||||
for (final File generatedFile in generatedFiles) {
|
||||
buffer.writeln('import "${globals.fs.path.basename(generatedFile.path)}";');
|
||||
}
|
||||
@ -77,7 +90,7 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
||||
targetModel: TargetModel.dartdevc,
|
||||
extraFrontEndOptions: extraFrontEndOptions,
|
||||
platformDill: globals.fs.file(globals.artifacts
|
||||
.getArtifactPath(Artifact.webPlatformKernelDill, mode: buildInfo.mode))
|
||||
.getArtifactPath(platformDillArtifact, mode: buildInfo.mode))
|
||||
.absolute.uri.toString(),
|
||||
dartDefines: buildInfo.dartDefines,
|
||||
librariesSpec: globals.fs.file(globals.artifacts
|
||||
@ -106,9 +119,9 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
||||
..write(codeFile, manifestFile, sourcemapFile, metadataFile);
|
||||
}
|
||||
|
||||
String _generateEntrypoint(String relativeTestPath, String absolutePath) {
|
||||
String _generateEntrypoint(String relativeTestPath, String absolutePath, LanguageVersion languageVersion) {
|
||||
return '''
|
||||
// @dart = 2.8
|
||||
// @dart = ${languageVersion.major}.${languageVersion.minor}
|
||||
import 'org-dartlang-app:///$relativeTestPath' as test;
|
||||
import 'dart:ui' as ui;
|
||||
import 'dart:html';
|
||||
@ -137,7 +150,7 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
||||
postMessageChannel().pipe(channel);
|
||||
}
|
||||
|
||||
StreamChannel serializeSuite(Function getMain(), {bool hidePrints = true, Future beforeLoad()}) => RemoteListener.start(getMain, hidePrints: hidePrints, beforeLoad: beforeLoad);
|
||||
StreamChannel serializeSuite(Function getMain(), {bool hidePrints = true}) => RemoteListener.start(getMain, hidePrints: hidePrints);
|
||||
|
||||
StreamChannel suiteChannel(String name) {
|
||||
var manager = SuiteChannelManager.current;
|
||||
|
@ -149,7 +149,6 @@ class TestGoldenComparatorProcess {
|
||||
final File testConfigFile = findTestConfigFile(globals.fs.file(testUri));
|
||||
// Generate comparator process for the file.
|
||||
return '''
|
||||
// @dart=2.9
|
||||
import 'dart:convert'; // ignore: dart_convert_import
|
||||
import 'dart:io'; // ignore: dart_io_import
|
||||
|
||||
@ -165,12 +164,12 @@ void main() async {
|
||||
final commands = stdin
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.map<Object>(jsonDecode);
|
||||
await for (final Object command in commands) {
|
||||
.map<dynamic>(jsonDecode);
|
||||
await for (final dynamic command in commands) {
|
||||
if (command is Map<String, dynamic>) {
|
||||
File imageFile = File(command['imageFile']);
|
||||
Uri goldenKey = Uri.parse(command['key']);
|
||||
bool update = command['update'];
|
||||
File imageFile = File(command['imageFile'] as String);
|
||||
Uri goldenKey = Uri.parse(command['key'] as String);
|
||||
bool update = command['update'] as bool;
|
||||
|
||||
final bytes = await File(imageFile.path).readAsBytes();
|
||||
if (update) {
|
||||
|
@ -32,6 +32,7 @@ import '../convert.dart';
|
||||
import '../dart/package_map.dart';
|
||||
import '../project.dart';
|
||||
import '../web/chrome.dart';
|
||||
import '../web/compile.dart';
|
||||
import '../web/memory_fs.dart';
|
||||
import 'flutter_web_goldens.dart';
|
||||
import 'test_compiler.dart';
|
||||
@ -158,13 +159,13 @@ class FlutterWebPlatform extends PlatformPlugin {
|
||||
'dart_stack_trace_mapper.js',
|
||||
));
|
||||
|
||||
/// The precompiled dart sdk.
|
||||
File get _dartSdk => _fileSystem.file(_fileSystem.path.join(
|
||||
_artifacts.getArtifactPath(Artifact.flutterWebSdk),
|
||||
'kernel',
|
||||
'amd',
|
||||
'dart_sdk.js',
|
||||
));
|
||||
File get _dartSdk => _fileSystem.file(_artifacts.getArtifactPath(kDartSdkJsArtifactMap[WebRendererMode.html][
|
||||
buildInfo.nullSafetyMode == NullSafetyMode.sound ? NullSafetyMode.sound : NullSafetyMode.unsound
|
||||
]));
|
||||
|
||||
File get _dartSdkSourcemaps => _fileSystem.file(_artifacts.getArtifactPath(kDartSdkJsMapArtifactMap[WebRendererMode.html][
|
||||
buildInfo.nullSafetyMode == NullSafetyMode.sound ? NullSafetyMode.sound : NullSafetyMode.unsound
|
||||
]));
|
||||
|
||||
/// The precompiled test javascript.
|
||||
File get _testDartJs => _fileSystem.file(_fileSystem.path.join(
|
||||
@ -223,6 +224,11 @@ class FlutterWebPlatform extends PlatformPlugin {
|
||||
_dartSdk.openRead(),
|
||||
headers: <String, String>{'Content-Type': 'text/javascript'},
|
||||
);
|
||||
} else if (request.requestedUri.path.contains('dart_sdk.js.map')) {
|
||||
return shelf.Response.ok(
|
||||
_dartSdkSourcemaps.openRead(),
|
||||
headers: <String, String>{'Content-Type': 'text/javascript'},
|
||||
);
|
||||
} else if (request.requestedUri.path
|
||||
.contains('dart_stack_trace_mapper.js')) {
|
||||
return shelf.Response.ok(
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../artifacts.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/context.dart';
|
||||
import '../base/file_system.dart';
|
||||
@ -106,3 +107,45 @@ class WebCompilationProxy {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
/// Web rendering backend mode.
|
||||
enum WebRendererMode {
|
||||
/// Auto detects which rendering backend to use.
|
||||
autoDetect,
|
||||
/// Always uses canvaskit.
|
||||
canvaskit,
|
||||
/// Always uses html.
|
||||
html,
|
||||
}
|
||||
|
||||
/// The correct precompiled artifact to use for each build and render mode.
|
||||
const Map<WebRendererMode, Map<NullSafetyMode, Artifact>> kDartSdkJsArtifactMap = <WebRendererMode, Map<NullSafetyMode, Artifact>>{
|
||||
WebRendererMode.autoDetect: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitAndHtmlSoundSdk,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitAndHtmlSdk,
|
||||
},
|
||||
WebRendererMode.canvaskit: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitSoundSdk,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitSdk,
|
||||
},
|
||||
WebRendererMode.html: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledSoundSdk,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledSdk,
|
||||
},
|
||||
};
|
||||
|
||||
/// The correct source map artifact to use for each build and render mode.
|
||||
const Map<WebRendererMode, Map<NullSafetyMode, Artifact>> kDartSdkJsMapArtifactMap = <WebRendererMode, Map<NullSafetyMode, Artifact>>{
|
||||
WebRendererMode.autoDetect: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps,
|
||||
},
|
||||
WebRendererMode.canvaskit: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledCanvaskitSdkSourcemaps,
|
||||
},
|
||||
WebRendererMode.html: <NullSafetyMode, Artifact> {
|
||||
NullSafetyMode.sound: Artifact.webPrecompiledSoundSdkSourcemaps,
|
||||
NullSafetyMode.unsound: Artifact.webPrecompiledSdkSourcemaps,
|
||||
},
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ import 'package:flutter_tools/src/isolated/devfs_web.dart';
|
||||
import 'package:flutter_tools/src/compile.dart';
|
||||
import 'package:flutter_tools/src/convert.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/web/compile.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:package_config/package_config.dart';
|
||||
import 'package:shelf/shelf.dart';
|
||||
|
@ -121,8 +121,8 @@ class _PlatformBinaryMessenger extends BinaryMessenger {
|
||||
|
||||
/// Sends a platform message from the platform side back to the framework.
|
||||
@override
|
||||
Future<ByteData> send(String channel, ByteData? message) {
|
||||
final Completer<ByteData> completer = Completer<ByteData>();
|
||||
Future<ByteData?> send(String channel, ByteData? message) {
|
||||
final Completer<ByteData?> completer = Completer<ByteData?>();
|
||||
ui.window.onPlatformMessage!(channel, message, (ByteData? reply) {
|
||||
try {
|
||||
completer.complete(reply);
|
||||
|
@ -57,7 +57,7 @@ void main() {
|
||||
ServicesBinding.instance!.defaultBinaryMessenger
|
||||
.setMessageHandler('test_send', (ByteData? data) {
|
||||
loggedMessages.add(codec.decodeMessage(data) as String);
|
||||
return null;
|
||||
return Future<ByteData?>.value(null);
|
||||
});
|
||||
|
||||
await pluginBinaryMessenger.send(
|
||||
|
Loading…
x
Reference in New Issue
Block a user