Unverified Commit a76289bb authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] run web unit tests in sound null safety (#70799)

parent 6d521e9a
......@@ -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(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment