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

[flutter_tools] use frontend_server for web test compilation (#70714)

parent 94244747
......@@ -88,7 +88,10 @@ const List<String> kWebTestFileKnownFailures = <String>[
'test/examples/sector_layout_test.dart',
// This test relies on widget tracking capability in the VM.
'test/widgets/widget_inspector_test.dart',
'test/painting/decoration_test.dart',
'test/material/time_picker_test.dart',
'test/material/text_field_test.dart',
'test/material/floating_action_button_test.dart',
'test/widgets/selectable_text_test.dart',
'test/widgets/color_filter_test.dart',
'test/widgets/editable_text_cursor_test.dart',
......@@ -97,6 +100,16 @@ const List<String> kWebTestFileKnownFailures = <String>[
'test/cupertino/refresh_test.dart',
'test/cupertino/text_field_test.dart',
'test/cupertino/route_test.dart',
'test/foundation/error_reporting_test.dart',
'test/foundation/consolidate_response_test.dart',
'test/foundation/stack_trace_test.dart',
'test/services/message_codecs_vm_test.dart',
'test/services/platform_messages_test.dart',
'test/widgets/image_resolution_test.dart ',
'test/widgets/platform_view_test.dart',
'test/widgets/route_notification_messages_test.dart',
'test/widgets/semantics_tester_generateTestSemanticsExpressionForCurrentSemanticsTree_test.dart',
'test/widgets/text_golden_test.dart',
];
/// When you call this, you can pass additional arguments to pass custom
......@@ -782,7 +795,7 @@ Future<void> _runWebUnitTests() async {
)
.whereType<File>()
.map<String>((File file) => path.relative(file.path, from: flutterPackageDirectory.path))
.where((String filePath) => !kWebTestFileKnownFailures.contains(filePath))
.where((String filePath) => !kWebTestFileKnownFailures.contains(path.split(filePath).join('/')))
.toList()
// Finally we shuffle the list because we want the average cost per file to be uniformly
// distributed. If the list is not sorted then different shards and batches may have
......
This package contains the custom build script used to compile Flutter for Web applications before support for JavaScript was added into the frontend_server. It is still used for test compilation today and is hosted here to reduce the number of dependencies that the flutter_tool has. For more information on the plans for flutter test --platform=chrome, see https://github.com/flutter/flutter/issues/50594
# Use the analysis options settings from the top level of the repo (not
# the ones from above, which include the `public_member_api_docs` rule).
include: ../../analysis_options.yaml
linter:
rules:
unawaited_futures: true
curly_braces_in_flow_control_structures: true
// 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.
// ignore_for_file: implementation_imports
import 'dart:isolate';
import 'package:build/build.dart';
import 'package:build_config/build_config.dart';
import 'package:build_modules/build_modules.dart';
import 'package:build_modules/builders.dart';
import 'package:build_modules/src/module_builder.dart';
import 'package:build_modules/src/platform.dart';
import 'package:build_runner/build_runner.dart' as build_runner;
import 'package:build_runner_core/build_runner_core.dart' as core;
import 'package:build_test/builder.dart';
import 'package:build_test/src/debug_test_builder.dart';
import 'package:build_web_compilers/build_web_compilers.dart';
import 'package:build_web_compilers/builders.dart';
import 'package:build_web_compilers/src/dev_compiler_bootstrap.dart';
import 'package:path/path.dart' as path; // ignore: package_path_import
import 'package:test_core/backend.dart'; // ignore: deprecated_member_use
import 'package:build_runner_core/src/util/constants.dart' as core;
const String ddcBootstrapExtension = '.dart.bootstrap.js';
const String jsEntrypointExtension = '.dart.js';
const String jsEntrypointSourceMapExtension = '.dart.js.map';
const String jsEntrypointArchiveExtension = '.dart.js.tar.gz';
const String digestsEntrypointExtension = '.digests';
const String jsModuleErrorsExtension = '.ddc.js.errors';
const String jsModuleExtension = '.ddc.js';
const String jsSourceMapExtension = '.ddc.js.map';
const String kReleaseFlag = 'release';
const String kProfileFlag = 'profile';
final DartPlatform flutterWebPlatform = DartPlatform.register('flutter_web', <String>[
'async',
'collection',
'convert',
'core',
'developer',
'html',
'html_common',
'indexed_db',
'js',
'js_util',
'math',
'svg',
'typed_data',
'web_audio',
'web_gl',
'web_sql',
'_internal',
// Flutter web specific libraries.
'ui',
'_engine',
]);
/// The builders required to compile a Flutter application to the web.
final List<core.BuilderApplication> builders = <core.BuilderApplication>[
core.apply(
'flutter_tools:test_bootstrap',
<BuilderFactory>[
(BuilderOptions options) => const DebugTestBuilder(),
(BuilderOptions options) => const FlutterWebTestBootstrapBuilder(),
],
core.toRoot(),
hideOutput: true,
defaultGenerateFor: const InputSet(
include: <String>[
'test/**',
],
),
),
core.apply(
'flutter_tools:module_library',
<Builder Function(BuilderOptions)>[moduleLibraryBuilder],
core.toAllPackages(),
isOptional: true,
hideOutput: true,
appliesBuilders: <String>['flutter_tools:module_cleanup']),
core.apply(
'flutter_tools:ddc_modules',
<Builder Function(BuilderOptions)>[
(BuilderOptions options) => MetaModuleBuilder(flutterWebPlatform),
(BuilderOptions options) => MetaModuleCleanBuilder(flutterWebPlatform),
(BuilderOptions options) => ModuleBuilder(flutterWebPlatform),
],
core.toNoneByDefault(),
isOptional: true,
hideOutput: true,
appliesBuilders: <String>['flutter_tools:module_cleanup']),
core.apply(
'flutter_tools:ddc',
<Builder Function(BuilderOptions)>[
(BuilderOptions builderOptions) => KernelBuilder(
platformSdk: builderOptions.config['flutterWebSdk'] as String,
summaryOnly: true,
sdkKernelPath: path.join('kernel', 'flutter_ddc_sdk.dill'),
outputExtension: ddcKernelExtension,
platform: flutterWebPlatform,
librariesPath: path.absolute(path.join(builderOptions.config['flutterWebSdk'] as String, 'libraries.json')),
kernelTargetName: 'ddc',
useIncrementalCompiler: true,
trackUnusedInputs: true,
experiments: <String>['non-nullable'], // ignore: deprecated_member_use
),
(BuilderOptions builderOptions) => DevCompilerBuilder(
useIncrementalCompiler: true,
trackUnusedInputs: true,
platform: flutterWebPlatform,
platformSdk: builderOptions.config['flutterWebSdk'] as String,
sdkKernelPath: path.url.join('kernel', 'flutter_ddc_sdk.dill'),
experiments: <String>['non-nullable'],
librariesPath: path.absolute(path.join(builderOptions.config['flutterWebSdk'] as String, 'libraries.json')),
),
],
core.toAllPackages(),
isOptional: true,
hideOutput: true,
appliesBuilders: <String>['flutter_tools:ddc_modules']),
core.apply(
'flutter_tools:test_entrypoint',
<BuilderFactory>[
(BuilderOptions options) => const FlutterWebTestEntrypointBuilder(),
],
core.toRoot(),
hideOutput: true,
defaultGenerateFor: const InputSet(
include: <String>[
'test/**_test.dart.browser_test.dart',
],
),
),
core.applyPostProcess('flutter_tools:module_cleanup', moduleCleanup,
defaultGenerateFor: const InputSet()),
];
/// The entry point to this build script.
Future<void> main(List<String> args, [SendPort sendPort]) async {
core.overrideGeneratedOutputDirectory('flutter_web');
final int result = await build_runner.run(args, builders);
sendPort?.send(result);
}
/// A ddc-only entry point builder that respects the Flutter target flag.
class FlutterWebTestEntrypointBuilder implements Builder {
const FlutterWebTestEntrypointBuilder();
@override
Map<String, List<String>> get buildExtensions => const <String, List<String>>{
'.dart': <String>[
ddcBootstrapExtension,
jsEntrypointExtension,
jsEntrypointSourceMapExtension,
jsEntrypointArchiveExtension,
digestsEntrypointExtension,
],
};
@override
Future<void> build(BuildStep buildStep) async {
log.info('building for target ${buildStep.inputId.path}');
await bootstrapDdc(
buildStep,
platform: flutterWebPlatform,
skipPlatformCheck: true,
);
}
}
/// Bootstraps the test entry point.
class FlutterWebTestBootstrapBuilder implements Builder {
const FlutterWebTestBootstrapBuilder();
@override
Map<String, List<String>> get buildExtensions => const <String, List<String>>{
'_test.dart': <String>[
'_test.dart.browser_test.dart',
],
};
@override
Future<void> build(BuildStep buildStep) async {
final AssetId id = buildStep.inputId;
final String contents = await buildStep.readAsString(id);
final String assetPath = id.pathSegments.first == 'lib'
? path.url.join('packages', id.package, id.path)
: id.path;
final Uri testUrl = path.toUri(path.absolute(assetPath));
final Metadata metadata = parseMetadata(
assetPath, contents, Runtime.builtIn.map((Runtime runtime) => runtime.name).toSet());
if (metadata.testOn.evaluate(SuitePlatform(Runtime.chrome))) {
await buildStep.writeAsString(id.addExtension('.browser_test.dart'), '''
// @dart = 2.8
import 'dart:ui' as ui;
import 'dart:html';
import 'dart:js';
import 'package:stream_channel/stream_channel.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:test_api/src/backend/stack_trace_formatter.dart'; // ignore: implementation_imports
import 'package:test_api/src/remote_listener.dart'; // ignore: implementation_imports
import 'package:test_api/src/suite_channel_manager.dart'; // ignore: implementation_imports
import "${path.url.basename(id.path)}" as test;
Future<void> main() async {
// Extra initialization for flutter_web.
// The following parameters are hard-coded in Flutter's test embedder. Since
// we don't have an embedder yet this is the lowest-most layer we can put
// this stuff in.
ui.debugEmulateFlutterTesterEnvironment = true;
await ui.webOnlyInitializePlatform();
webGoldenComparator = DefaultWebGoldenComparator(Uri.parse('$testUrl'));
// TODO(flutterweb): remove need for dynamic cast.
(ui.window as dynamic).debugOverrideDevicePixelRatio(3.0);
(ui.window as dynamic).webOnlyDebugPhysicalSizeOverride = const ui.Size(2400, 1800);
internalBootstrapBrowserTest(() => test.main);
}
void internalBootstrapBrowserTest(Function getMain()) {
var channel = serializeSuite(getMain, hidePrints: false);
postMessageChannel().pipe(channel);
}
StreamChannel serializeSuite(Function getMain(),
{bool hidePrints = true, Future beforeLoad()}) =>
RemoteListener.start(getMain,
hidePrints: hidePrints, beforeLoad: beforeLoad);
StreamChannel suiteChannel(String name) {
var manager = SuiteChannelManager.current;
if (manager == null) {
throw StateError('suiteChannel() may only be called within a test worker.');
}
return manager.connectOut(name);
}
StreamChannel postMessageChannel() {
var controller = StreamChannelController(sync: true);
window.onMessage.firstWhere((message) {
return message.origin == window.location.origin && message.data == "port";
}).then((message) {
var port = message.ports.first;
var portSubscription = port.onMessage.listen((message) {
controller.local.sink.add(message.data);
});
controller.local.stream.listen((data) {
port.postMessage({"data": data});
}, onDone: () {
port.postMessage({"event": "done"});
portSubscription.cancel();
});
});
context['parent'].callMethod('postMessage', [
JsObject.jsify({"href": window.location.href, "ready": true}),
window.location.origin,
]);
return controller.foreign;
}
''');
}
}
}
name: _flutter_web_build_script
description: A build script for flutter test --platform=chrome
homepage: https://flutter.dev
author: Flutter Authors <flutter-dev@googlegroups.com>
environment:
# The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite.
sdk: ">=2.7.0 <3.0.0"
dependencies:
# We depend on very specific internal implementation details of the
# 'test' package, which change between versions, so when upgrading
# this, make sure the tests are still running correctly.
test_api: 0.2.19-nullsafety.6
test_core: 0.3.12-nullsafety.9
build_runner: 1.10.2
build_test: 1.3.0
build_runner_core: 5.2.0
dart_style: 1.3.6
code_builder: 3.5.0
build: 1.3.0
build_modules: 2.10.1
build_daemon: 2.1.4
build_web_compilers: 2.11.0
_fe_analyzer_shared: 7.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
analyzer: 0.39.17 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
archive: 2.0.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
args: 1.6.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
async: 2.5.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
bazel_worker: 0.1.25 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
boolean_selector: 2.1.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
build_config: 0.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
build_resolvers: 1.3.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
built_collection: 4.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
built_value: 7.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
charcode: 1.2.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
checked_yaml: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
cli_util: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
collection: 1.15.0-nullsafety.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
coverage: 0.14.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
crypto: 2.1.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
csslib: 0.16.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
fixnum: 0.10.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
graphs: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
html: 0.14.0+4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
http_multi_server: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
http_parser: 3.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
io: 0.3.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
js: 0.6.3-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
json_annotation: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
logging: 0.11.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
matcher: 0.12.10-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
meta: 1.3.0-nullsafety.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
mime: 0.9.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
node_interop: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
node_io: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
node_preamble: 1.4.12 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
package_config: 1.9.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
path: 1.8.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
pedantic: 1.10.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
pool: 1.5.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
protobuf: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
pub_semver: 1.4.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
pubspec_parse: 0.1.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
quiver: 2.1.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
scratch_space: 0.0.4+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf: 0.7.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf_packages_handler: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf_static: 0.2.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf_web_socket: 0.2.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
source_map_stack_trace: 2.1.0-nullsafety.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
source_maps: 0.10.10-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
source_span: 1.8.0-nullsafety.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
stack_trace: 1.10.0-nullsafety.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
stream_channel: 2.1.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
stream_transform: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
string_scanner: 1.1.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
term_glyph: 1.2.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
test: 1.16.0-nullsafety.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
timing: 0.1.1+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
typed_data: 1.3.0-nullsafety.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
vm_service: 5.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
watcher: 0.9.7+15 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
web_socket_channel: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
webkit_inspection_protocol: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
yaml: 2.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
dartdoc:
# Exclude this package from the hosted API docs.
nodoc: true
# PUBSPEC CHECKSUM: d4a7
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome')
import 'package:flutter/foundation.dart';
import '../flutter_test_alternative.dart';
......@@ -40,7 +38,7 @@ void main() {
expect(field[_TestEnum.c], isTrue);
expect(field[_TestEnum.d], isTrue);
expect(field[_TestEnum.e], isTrue);
});
}, skip: kIsWeb);
test('BitField.filed control test', () {
final BitField<_TestEnum> field1 = BitField<_TestEnum>.filled(8, true);
......@@ -50,5 +48,5 @@ void main() {
final BitField<_TestEnum> field2 = BitField<_TestEnum>.filled(8, false);
expect(field2[_TestEnum.d], isFalse);
});
}, skip: kIsWeb);
}
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome')
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
......@@ -147,7 +145,7 @@ void main() {
]);
});
});
});
}, skip: kIsWeb);
}
class MockHttpClientResponse extends Fake implements HttpClientResponse {
......
......@@ -2,12 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // This test is not intended to run on the web.
import 'package:flutter/foundation.dart';
import '../flutter_test_alternative.dart';
void main() {
test('isWeb is false for flutter tester', () {
expect(kIsWeb, false);
});
}, skip: kIsWeb);
}
// 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:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('defines match expectations per platform', () {
expect(kIsWeb, !const bool.fromEnvironment('dart.library.io'));
expect(kIsWeb, !const bool.fromEnvironment('dart.library.isolate'));
expect(kIsWeb, const bool.fromEnvironment('dart.library.html'));
});
}
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // web has different stack traces
import 'package:flutter/foundation.dart';
import '../flutter_test_alternative.dart';
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // isolates not supported on the web.
import 'package:flutter/foundation.dart';
import '../flutter_test_alternative.dart';
......@@ -31,5 +29,5 @@ void main() {
expect(await compute(test1Async, 0), 1);
expect(compute(test2Async, 0), throwsException);
});
}, skip: kIsWeb);
}
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome')
import 'package:flutter/foundation.dart';
import '../flutter_test_alternative.dart';
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // web does not support certain 64bit behavior
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
......@@ -42,7 +40,7 @@ void main() {
expect(written.lengthInBytes, equals(8));
final ReadBuffer read = ReadBuffer(written);
expect(read.getInt64(), equals(-9000000000000));
});
}, skip: kIsWeb);
test('of 64-bit integer in big endian', () {
final WriteBuffer write = WriteBuffer();
write.putInt64(-9000000000000, endian: Endian.big);
......@@ -50,7 +48,7 @@ void main() {
expect(written.lengthInBytes, equals(8));
final ReadBuffer read = ReadBuffer(written);
expect(read.getInt64(endian: Endian.big), equals(-9000000000000));
});
}, skip: kIsWeb);
test('of double', () {
final WriteBuffer write = WriteBuffer();
write.putFloat64(3.14);
......@@ -88,7 +86,7 @@ void main() {
final ReadBuffer read = ReadBuffer(written);
read.getUint8();
expect(read.getInt64List(3), equals(integers));
});
}, skip: kIsWeb);
test('of double list when unaligned', () {
final Float64List doubles = Float64List.fromList(<double>[3.14, double.nan]);
final WriteBuffer write = WriteBuffer();
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // web has different stack traces
import 'package:flutter/foundation.dart';
import '../flutter_test_alternative.dart';
......@@ -16,7 +14,7 @@ void main() {
expect(filtered[0], matches(r'^#0 +main\.<anonymous closure> \(.*stack_trace_test\.dart:[0-9]+:[0-9]+\)$'));
expect(filtered[1], matches(r'^#1 +Declarer\.test\.<anonymous closure>.<anonymous closure> \(package:test_api/.+:[0-9]+:[0-9]+\)$'));
expect(filtered[2], equals('<asynchronous suspension>'));
});
}, skip: kIsWeb);
test('FlutterError.defaultStackFilter (async test body)', () async {
final List<String> filtered = FlutterError.defaultStackFilter(StackTrace.current.toString().trimRight().split('\n')).toList();
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // whole file needs triage.
import 'dart:ui';
import 'package:flutter/material.dart';
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // This whole test suite needs triage.
import 'dart:math' as math;
import 'dart:ui' as ui show window, BoxHeightStyle, BoxWidthStyle;
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // entire file needs triage.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome')
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui show Image, ColorFilter;
......@@ -357,7 +356,7 @@ void main() {
' The ImageConfiguration was:\n'
' ImageConfiguration(size: Size(100.0, 100.0))\n'
);
});
}, skip: kIsWeb);
test('DecorationImage - error listener', () async {
late String exception;
......@@ -702,5 +701,5 @@ void main() {
expect(info.image.debugGetOpenHandleStackTraces()!.length, baselineRefCount);
info.dispose();
});
}, skip: kIsWeb);
}
......@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(yjbanov): enable Web when https://github.com/flutter/engine/pull/12747 rolls into the framework.
@TestOn('!chrome')
import 'dart:convert';
import 'dart:typed_data';
import 'dart:ui' as ui;
......
......@@ -2,13 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contains tests that are only supported by the Dart VM. For
// example, on the Web there's no way to express large integers.
@TestOn('!chrome')
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import '../flutter_test_alternative.dart';
import 'message_codecs_testing.dart';
......@@ -81,5 +77,5 @@ void main() {
];
checkEncodeDecode<dynamic>(standard, message);
});
});
}, skip: kIsWeb);
}
......@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // missing web infrastructure for plugins.
import 'dart:typed_data';
import 'package:flutter/services.dart';
......
......@@ -248,7 +248,7 @@ void main() {
expect(errorDetails.exception, isAssertionError);
const String toMatch = '... Normal element mounting (';
expect(toMatch.allMatches(errorDetails.toString()).length, 1);
});
}, skip: kIsWeb);
}
class TestStatefulWidget extends StatefulWidget {
......
......@@ -3,7 +3,6 @@
// found in the LICENSE file.
@TestOn('chrome')
import 'dart:async';
import 'package:flutter/rendering.dart';
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // asset bundle behaves differently.
import 'dart:typed_data';
import 'dart:ui' as ui show Image;
......
......@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // diagnostics use Platform.operatingSystem.
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
......@@ -530,6 +530,6 @@ void main() {
' constraints: BoxConstraints(w=800.0, h=400.0)\n'
' size: Size(800.0, 400.0)\n'
));
});
}, skip: kIsWeb);
}
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome')
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('chrome')
import 'dart:ui';
import 'package:flutter/foundation.dart';
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome')
import 'dart:io';
import 'package:flutter/material.dart';
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@TestOn('!chrome') // Flaky on web
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
......
......@@ -950,10 +950,10 @@ class WebDevFS implements DevFS {
parentDirectory.childFile('${compilerOutput.outputFilename}.json');
sourcemapFile =
parentDirectory.childFile('${compilerOutput.outputFilename}.map');
metadataFile = parentDirectory
.childFile('${compilerOutput.outputFilename}.metadata');
modules = webAssetServer.write(
codeFile, manifestFile, sourcemapFile, metadataFile);
metadataFile =
parentDirectory.childFile('${compilerOutput.outputFilename}.metadata');
modules =
webAssetServer.write(codeFile, manifestFile, sourcemapFile, metadataFile);
} on FileSystemException catch (err) {
throwToolExit('Failed to load recompiled sources:\n$err');
}
......
......@@ -2,266 +2,261 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:build_daemon/client.dart';
import 'package:build_daemon/constants.dart' as daemon;
import 'package:build_daemon/data/build_status.dart';
import 'package:build_daemon/data/build_target.dart';
import 'package:build_daemon/data/server_log.dart';
import 'package:path/path.dart' as path; // ignore: package_path_import
import 'dart:typed_data';
import 'package:meta/meta.dart';
import '../artifacts.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../cache.dart';
import '../dart/pub.dart';
import '../bundle.dart';
import '../compile.dart';
import '../convert.dart';
import '../globals.dart' as globals;
import '../platform_plugins.dart';
import '../plugins.dart';
import '../project.dart';
import '../web/compile.dart';
/// A build_runner specific implementation of the [WebCompilationProxy].
// TODO(jonahwilliams): this class was kept around to reduce the diff in the migration
// from build_runner to the frontend_server, but should be removed/refactored to be
// similar to the test_compiler pattern used for regular flutter tests
class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
BuildRunnerWebCompilationProxy();
@override
Future<bool> initialize({
Directory projectDirectory,
String testOutputDir,
List<String> testFiles,
BuildMode mode,
String projectName,
bool initializePlatform,
Future<WebVirtualFS> initialize({
@required Directory projectDirectory,
@required String testOutputDir,
@required List<String> testFiles,
@required BuildInfo buildInfo,
}) async {
// Create the .dart_tool directory if it doesn't exist.
projectDirectory
.childDirectory('.dart_tool')
.createSync();
final FlutterProject flutterProject = FlutterProject.fromDirectory(projectDirectory);
final bool hasWebPlugins = (await findPlugins(flutterProject))
.any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
final BuildDaemonClient client = await const BuildDaemonCreator().startBuildDaemon(
projectDirectory.path,
release: mode == BuildMode.release,
profile: mode == BuildMode.profile,
hasPlugins: hasWebPlugins,
initializePlatform: initializePlatform,
testTargets: WebTestTargetManifest(
testFiles
.map<String>((String absolutePath) {
final String relativePath = path.relative(absolutePath, from: projectDirectory.path);
return '${path.withoutExtension(relativePath)}.*';
})
.toList(),
),
);
client.startBuild();
bool success = true;
await for (final BuildResults results in client.buildResults) {
final BuildResult result = results.results.firstWhere((BuildResult result) {
return result.target == 'web';
}, orElse: () {
// Assume build failed if we lack any results.
return DefaultBuildResult((DefaultBuildResultBuilder b) => b.status == BuildStatus.failed);
});
if (result.status == BuildStatus.failed) {
success = false;
break;
}
if (result.status == BuildStatus.succeeded) {
break;
}
if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
throwToolExit('flutter test --platform=chrome does not currently support sound mode');
}
if (!success || testOutputDir == null) {
return success;
final List<String> extraFrontEndOptions = List<String>.of(buildInfo.extraFrontEndOptions ?? <String>[]);
if (!extraFrontEndOptions.contains('--no-sound-null-safety')) {
extraFrontEndOptions.add('--no-sound-null-safety');
}
final Directory rootDirectory = projectDirectory
.childDirectory('.dart_tool')
.childDirectory('build')
.childDirectory('flutter_web');
final Directory outputDirectory = globals.fs.directory(testOutputDir)
..createSync(recursive: true);
final List<File> generatedFiles = <File>[];
for (final String testFilePath in testFiles) {
final List<String> relativeTestSegments = globals.fs.path.split(
globals.fs.path.relative(testFilePath, from: projectDirectory.childDirectory('test').path));
final File generatedFile = globals.fs.file(
globals.fs.path.join(outputDirectory.path, '${relativeTestSegments.join('_')}.test.dart'));
generatedFile
..createSync(recursive: true)
..writeAsStringSync(_generateEntrypoint(relativeTestSegments.join('/'), testFilePath));
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');
for (final File generatedFile in generatedFiles) {
buffer.writeln('import "${globals.fs.path.basename(generatedFile.path)}";');
}
buffer.writeln('void main() {}');
globals.fs.file(globals.fs.path.join(outputDirectory.path, 'main.dart'))
..createSync()
..writeAsStringSync(buffer.toString());
final Iterable<Directory> childDirectories = rootDirectory
.listSync()
.whereType<Directory>();
for (final Directory childDirectory in childDirectories) {
final String path = globals.fs.path.join(
final ResidentCompiler residentCompiler = ResidentCompiler(
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: buildInfo.mode),
buildMode: buildInfo.mode,
trackWidgetCreation: buildInfo.trackWidgetCreation,
fileSystemRoots: <String>[
projectDirectory.childDirectory('test').path,
testOutputDir,
'packages',
globals.fs.path.basename(childDirectory.path),
);
globals.fsUtils.copyDirectorySync(
childDirectory.childDirectory('lib'),
globals.fs.directory(path),
);
],
// Override the filesystem scheme so that the frontend_server can find
// the generated entrypoint code.
fileSystemScheme: 'org-dartlang-app',
initializeFromDill: getDefaultCachedKernelPath(
trackWidgetCreation: buildInfo.trackWidgetCreation,
dartDefines: buildInfo.dartDefines,
extraFrontEndOptions: extraFrontEndOptions,
),
targetModel: TargetModel.dartdevc,
extraFrontEndOptions: extraFrontEndOptions,
platformDill: globals.fs.file(globals.artifacts
.getArtifactPath(Artifact.webPlatformKernelDill, mode: buildInfo.mode))
.absolute.uri.toString(),
dartDefines: buildInfo.dartDefines,
librariesSpec: globals.fs.file(globals.artifacts
.getArtifactPath(Artifact.flutterWebLibrariesJson)).uri.toString(),
packagesPath: buildInfo.packagesPath,
artifacts: globals.artifacts,
processManager: globals.processManager,
logger: globals.logger,
platform: globals.platform,
);
final CompilerOutput output = await residentCompiler.recompile(
Uri.parse('org-dartlang-app:///main.dart'),
<Uri>[],
outputPath: outputDirectory.childFile('out').path,
packageConfig: buildInfo.packageConfig,
);
if (output.errorCount > 0) {
throwToolExit('Failed to compile');
}
final Directory outputDirectory = rootDirectory
.childDirectory(projectName)
.childDirectory('test');
globals.fsUtils.copyDirectorySync(
outputDirectory,
globals.fs.directory(globals.fs.path.join(testOutputDir)),
final File codeFile = outputDirectory.childFile('${output.outputFilename}.sources');
final File manifestFile = outputDirectory.childFile('${output.outputFilename}.json');
final File sourcemapFile = outputDirectory.childFile('${output.outputFilename}.map');
final File metadataFile = outputDirectory.childFile('${output.outputFilename}.metadata');
final WebVirtualFS webVirtualFS = WebVirtualFS();
_write(
codeFile,
manifestFile,
sourcemapFile,
metadataFile,
webVirtualFS,
);
return success;
return webVirtualFS;
}
}
class WebTestTargetManifest {
WebTestTargetManifest(this.buildFilters);
WebTestTargetManifest.all() : buildFilters = null;
final List<String> buildFilters;
bool get hasBuildFilters => buildFilters != null && buildFilters.isNotEmpty;
}
/// A testable interface for starting a build daemon.
class BuildDaemonCreator {
const BuildDaemonCreator();
void _write(
File codeFile,
File manifestFile,
File sourcemapFile,
File metadataFile,
WebVirtualFS webVirtualFS,
) {
final Uint8List codeBytes = codeFile.readAsBytesSync();
final Uint8List sourcemapBytes = sourcemapFile.readAsBytesSync();
final Uint8List metadataBytes = metadataFile.readAsBytesSync();
final Map<String, dynamic> manifest =
castStringKeyedMap(json.decode(manifestFile.readAsStringSync()));
for (final String filePath in manifest.keys) {
if (filePath == null) {
globals.printTrace('Invalid manifest file: $filePath');
continue;
}
final Map<String, dynamic> offsets =
castStringKeyedMap(manifest[filePath]);
final List<int> codeOffsets =
(offsets['code'] as List<dynamic>).cast<int>();
final List<int> sourcemapOffsets =
(offsets['sourcemap'] as List<dynamic>).cast<int>();
final List<int> metadataOffsets =
(offsets['metadata'] as List<dynamic>).cast<int>();
if (codeOffsets.length != 2 ||
sourcemapOffsets.length != 2 ||
metadataOffsets.length != 2) {
globals.printTrace('Invalid manifest byte offsets: $offsets');
continue;
}
// TODO(jonahwilliams): find a way to get build checks working for flutter for web.
static const String _ignoredLine1 = 'Warning: Interpreting this as package URI';
static const String _ignoredLine2 = 'build_script.dart was not found in the asset graph, incremental builds will not work';
static const String _ignoredLine3 = 'have your dependencies specified fully in your pubspec.yaml';
final int codeStart = codeOffsets[0];
final int codeEnd = codeOffsets[1];
if (codeStart < 0 || codeEnd > codeBytes.lengthInBytes) {
globals.printTrace('Invalid byte index: [$codeStart, $codeEnd]');
continue;
}
final Uint8List byteView = Uint8List.view(
codeBytes.buffer,
codeStart,
codeEnd - codeStart,
);
final String fileName =
filePath.startsWith('/') ? filePath.substring(1) : filePath;
webVirtualFS.files[fileName] = byteView;
/// Start a build daemon and register the web targets.
///
/// [initializePlatform] controls whether we should invoke [webOnlyInitializePlatform].
Future<BuildDaemonClient> startBuildDaemon(String workingDirectory, {
bool release = false,
bool profile = false,
bool hasPlugins = false,
bool initializePlatform = true,
WebTestTargetManifest testTargets,
}) async {
try {
final BuildDaemonClient client = await _connectClient(
workingDirectory,
release: release,
profile: profile,
hasPlugins: hasPlugins,
initializePlatform: initializePlatform,
testTargets: testTargets,
final int sourcemapStart = sourcemapOffsets[0];
final int sourcemapEnd = sourcemapOffsets[1];
if (sourcemapStart < 0 || sourcemapEnd > sourcemapBytes.lengthInBytes) {
globals.printTrace('Invalid byte index: [$sourcemapStart, $sourcemapEnd]');
continue;
}
final Uint8List sourcemapView = Uint8List.view(
sourcemapBytes.buffer,
sourcemapStart,
sourcemapEnd - sourcemapStart,
);
_registerBuildTargets(client, testTargets);
return client;
} on OptionsSkew {
throwToolExit(
'Incompatible options with current running build daemon.\n\n'
'Please stop other flutter_tool instances running in this directory '
'before starting a new instance with these options.'
final String sourcemapName = '$fileName.map';
webVirtualFS.sourcemaps[sourcemapName] = sourcemapView;
final int metadataStart = metadataOffsets[0];
final int metadataEnd = metadataOffsets[1];
if (metadataStart < 0 || metadataEnd > metadataBytes.lengthInBytes) {
globals
.printTrace('Invalid byte index: [$metadataStart, $metadataEnd]');
continue;
}
final Uint8List metadataView = Uint8List.view(
metadataBytes.buffer,
metadataStart,
metadataEnd - metadataStart,
);
final String metadataName = '$fileName.metadata';
webVirtualFS.metadataFiles[metadataName] = metadataView;
}
return null;
}
void _registerBuildTargets(
BuildDaemonClient client,
WebTestTargetManifest testTargets,
) {
final OutputLocation outputLocation = OutputLocation((OutputLocationBuilder b) => b
..output = ''
..useSymlinks = true
..hoist = false);
client.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder b) => b
..target = 'web'
..outputLocation = outputLocation?.toBuilder()));
if (testTargets != null) {
client.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder b) {
b.target = 'test';
b.outputLocation = outputLocation?.toBuilder();
if (testTargets.hasBuildFilters) {
b.buildFilters.addAll(testTargets.buildFilters);
}
}));
}
String _generateEntrypoint(String relativeTestPath, String absolutePath) {
return '''
// @dart = 2.8
import 'org-dartlang-app:///$relativeTestPath' as test;
import 'dart:ui' as ui;
import 'dart:html';
import 'dart:js';
import 'package:stream_channel/stream_channel.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:test_api/src/backend/stack_trace_formatter.dart'; // ignore: implementation_imports
import 'package:test_api/src/remote_listener.dart'; // ignore: implementation_imports
import 'package:test_api/src/suite_channel_manager.dart'; // ignore: implementation_imports
// Extra initialization for flutter_web.
// The following parameters are hard-coded in Flutter's test embedder. Since
// we don't have an embedder yet this is the lowest-most layer we can put
// this stuff in.
Future<void> main() async {
ui.debugEmulateFlutterTesterEnvironment = true;
await ui.webOnlyInitializePlatform();
webGoldenComparator = DefaultWebGoldenComparator(Uri.parse('$absolutePath'));
(ui.window as dynamic).debugOverrideDevicePixelRatio(3.0);
(ui.window as dynamic).webOnlyDebugPhysicalSizeOverride = const ui.Size(2400, 1800);
internalBootstrapBrowserTest(() => test.main);
}
Future<BuildDaemonClient> _connectClient(
String workingDirectory, {
bool release,
bool profile,
bool hasPlugins,
bool initializePlatform,
WebTestTargetManifest testTargets,
}) async {
// The build script is stored in an auxiliary package to reduce
// dependencies of the main tool.
final String buildScriptPackages = globals.fs.path.join(
Cache.flutterRoot,
'packages',
'_flutter_web_build_script',
'.packages',
);
final String buildScript = globals.fs.path.join(
Cache.flutterRoot,
'packages',
'_flutter_web_build_script',
'lib',
'build_script.dart',
);
if (!globals.fs.isFileSync(buildScript)) {
throwToolExit('Expected a file $buildScript to exist in the Flutter SDK.');
}
// If we're missing the .packages file, perform a pub get.
if (!globals.fs.isFileSync(buildScriptPackages)) {
await pub.get(
context: PubContext.pubGet,
directory: globals.fs.file(buildScriptPackages).parent.path,
generateSyntheticPackage: false,
);
}
final String flutterWebSdk = globals.artifacts.getArtifactPath(Artifact.flutterWebSdk);
void internalBootstrapBrowserTest(Function getMain()) {
var channel = serializeSuite(getMain, hidePrints: false);
postMessageChannel().pipe(channel);
}
// On Windows we need to call the snapshot directly otherwise
// the process will start in a disjoint cmd without access to
// STDIO.
final List<String> args = <String>[
globals.artifacts.getArtifactPath(Artifact.engineDartBinary),
'--disable-dart-dev',
'--packages=$buildScriptPackages',
buildScript,
'daemon',
'--enable-experiment=non-nullable',
'--skip-build-script-check',
'--define', 'flutter_tools:ddc=flutterWebSdk=$flutterWebSdk',
// The following will cause build runner to only build tests that were requested.
if (testTargets != null && testTargets.hasBuildFilters)
for (final String buildFilter in testTargets.buildFilters)
'--build-filter=$buildFilter',
];
StreamChannel serializeSuite(Function getMain(), {bool hidePrints = true, Future beforeLoad()}) => RemoteListener.start(getMain, hidePrints: hidePrints, beforeLoad: beforeLoad);
return BuildDaemonClient.connect(
workingDirectory,
args,
logHandler: (ServerLog serverLog) {
switch (serverLog.level) {
case Level.SEVERE:
case Level.SHOUT:
// Ignore certain non-actionable messages on startup.
if (serverLog.message.contains(_ignoredLine1) ||
serverLog.message.contains(_ignoredLine2) ||
serverLog.message.contains(_ignoredLine3)) {
return;
}
globals.printError(serverLog.message);
if (serverLog.error != null) {
globals.printError(serverLog.error);
}
if (serverLog.stackTrace != null) {
globals.printTrace(serverLog.stackTrace);
}
break;
default:
if (serverLog.message.contains('Skipping compiling')) {
globals.printError(serverLog.message);
} else {
globals.printTrace(serverLog.message);
}
}
},
buildMode: daemon.BuildMode.Manual,
);
StreamChannel suiteChannel(String name) {
var manager = SuiteChannelManager.current;
if (manager == null) {
throw StateError('suiteChannel() may only be called within a test worker.');
}
return manager.connectOut(name);
}
StreamChannel postMessageChannel() {
var controller = StreamChannelController(sync: true);
window.onMessage.firstWhere((message) {
return message.origin == window.location.origin && message.data == "port";
}).then((message) {
var port = message.ports.first;
var portSubscription = port.onMessage.listen((message) {
controller.local.sink.add(message.data);
});
controller.local.stream.listen((data) {
port.postMessage({"data": data});
}, onDone: () {
port.postMessage({"event": "done"});
portSubscription.cancel();
});
});
context['parent'].callMethod('postMessage', [
JsObject.jsify({"href": window.location.href, "ready": true}),
window.location.origin,
]);
return controller.foreign;
}
''';
}
}
......@@ -15,7 +15,6 @@ import 'package:path/path.dart' as p; // ignore: package_path_import
import 'package:pool/pool.dart';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_packages_handler/shelf_packages_handler.dart';
import 'package:shelf_static/shelf_static.dart';
import 'package:shelf_web_socket/shelf_web_socket.dart';
import 'package:stream_channel/stream_channel.dart';
......@@ -34,6 +33,7 @@ import '../dart/package_map.dart';
import '../globals.dart' as globals;
import '../project.dart';
import '../web/chrome.dart';
import '../web/compile.dart';
import 'test_compiler.dart';
import 'test_config.dart';
......@@ -42,23 +42,19 @@ class FlutterWebPlatform extends PlatformPlugin {
FlutterProject flutterProject,
String shellPath,
this.updateGoldens,
@required BuildInfo buildInfo,
@required this.buildInfo,
@required this.webVirtualFS,
}) {
final shelf.Cascade cascade = shelf.Cascade()
.add(_webSocketHandler.handler)
.add(packagesDirHandler())
.add(_jsHandler.handler)
.add(createStaticHandler(
globals.fs.path.join(Cache.flutterRoot, 'packages', 'flutter_tools'),
serveFilesOutsidePath: true,
))
.add(createStaticHandler(
_config.suiteDefaults.precompiledPath,
serveFilesOutsidePath: true,
))
.add(_handleStaticArtifact)
.add(_goldenFileHandler)
.add(_wrapperHandler)
.add(_handleTestRequest)
.add(createStaticHandler(
p.join(p.current, 'test'),
serveFilesOutsidePath: true,
......@@ -72,15 +68,18 @@ class FlutterWebPlatform extends PlatformPlugin {
);
}
final WebVirtualFS webVirtualFS;
final BuildInfo buildInfo;
static Future<FlutterWebPlatform> start(String root, {
FlutterProject flutterProject,
String shellPath,
bool updateGoldens = false,
bool pauseAfterLoad = false,
@required BuildInfo buildInfo,
@required WebVirtualFS webVirtualFS,
}) async {
final shelf_io.IOServer server =
shelf_io.IOServer(await HttpMultiServer.loopback(0));
final shelf_io.IOServer server = shelf_io.IOServer(await HttpMultiServer.loopback(0));
return FlutterWebPlatform._(
server,
Configuration.current.change(pauseAfterLoad: pauseAfterLoad),
......@@ -89,14 +88,10 @@ class FlutterWebPlatform extends PlatformPlugin {
shellPath: shellPath,
updateGoldens: updateGoldens,
buildInfo: buildInfo,
webVirtualFS: webVirtualFS,
);
}
final Future<PackageConfig> _packagesFuture = loadPackageConfigWithLogging(
globals.fs.file(globals.fs.path.join('.dart_tool', 'package_config.json')),
logger: globals.logger,
);
final Future<PackageConfig> _flutterToolsPackageMap = loadPackageConfigWithLogging(
globals.fs.file(globals.fs.path.join(
Cache.flutterRoot,
......@@ -176,6 +171,35 @@ class FlutterWebPlatform extends PlatformPlugin {
'host.dart.js',
));
Future<shelf.Response> _handleTestRequest(shelf.Request request) async {
if (request.url.path.endsWith('.dart.browser_test.dart.js')) {
final String leadingPath = request.url.path.split('.browser_test.dart.js')[0];
final String generatedFile = globals.fs.path.split(leadingPath).join('_') + '.bootstrap.js';
return shelf.Response.ok(bootstrapFileContents('/' + generatedFile, 'require.js', 'dart_stack_trace_mapper.js'), headers: <String, String>{
HttpHeaders.contentTypeHeader: 'text/javascript',
});
}
if (request.url.path.endsWith('.dart.bootstrap.js')) {
final String leadingPath = request.url.path.split('.dart.bootstrap.js')[0];
final String generatedFile = globals.fs.path.split(leadingPath).join('_') + '.dart.test.dart.js';
return shelf.Response.ok(generatedActualMain(globals.fs.path.basename(leadingPath) + '.dart.bootstrap', '/' + generatedFile), headers: <String, String>{
HttpHeaders.contentTypeHeader: 'text/javascript',
});
}
if (request.url.path.endsWith('.dart.js')) {
final String path = request.url.path.split('.dart.js')[0];
return shelf.Response.ok(webVirtualFS.files[path + '.dart.lib.js'], headers: <String, String>{
HttpHeaders.contentTypeHeader: 'text/javascript',
});
}
if (request.url.path.endsWith('.lib.js.map')) {
return shelf.Response.ok(webVirtualFS.sourcemaps[request.url.path], headers: <String, String>{
HttpHeaders.contentTypeHeader: 'text/plain',
});
}
return shelf.Response.notFound('');
}
Future<shelf.Response> _handleStaticArtifact(shelf.Request request) async {
if (request.requestedUri.path.contains('require.js')) {
return shelf.Response.ok(
......@@ -190,7 +214,7 @@ class FlutterWebPlatform extends PlatformPlugin {
headers: <String, String>{'Content-Type': 'text/javascript'},
);
} else if (request.requestedUri.path
.contains('stack_trace_mapper.dart.js')) {
.contains('dart_stack_trace_mapper.js')) {
return shelf.Response.ok(
stackTraceMapper.openRead(),
headers: <String, String>{'Content-Type': 'text/javascript'},
......@@ -212,8 +236,7 @@ class FlutterWebPlatform extends PlatformPlugin {
FutureOr<shelf.Response> _packageFilesHandler(shelf.Request request) async {
if (request.requestedUri.pathSegments.first == 'packages') {
final PackageConfig packageConfig = await _packagesFuture;
final Uri fileUri = packageConfig.resolve(Uri(
final Uri fileUri = buildInfo.packageConfig.resolve(Uri(
scheme: 'package',
pathSegments: request.requestedUri.pathSegments.skip(1),
));
......@@ -289,7 +312,6 @@ class FlutterWebPlatform extends PlatformPlugin {
}
final OneOffHandler _webSocketHandler = OneOffHandler();
final PathHandler _jsHandler = PathHandler();
final AsyncMemoizer<void> _closeMemo = AsyncMemoizer<void>();
final String _root;
......@@ -315,7 +337,6 @@ class FlutterWebPlatform extends PlatformPlugin {
</html>
''', headers: <String, String>{'Content-Type': 'text/html'});
}
globals.printTrace('Did not find anything for request: ${request.url}');
return shelf.Response.notFound('Not found.');
}
......@@ -351,7 +372,8 @@ class FlutterWebPlatform extends PlatformPlugin {
final Uri suiteUrl = url.resolveUri(globals.fs.path.toUri(globals.fs.path.withoutExtension(
globals.fs.path.relative(path, from: globals.fs.path.join(_root, 'test'))) +
'.html'));
final RunnerSuite suite = await _browserManager.load(path, suiteUrl, suiteConfig, message, onDone: () async {
final String relativePath = globals.fs.path.relative(globals.fs.path.normalize(path), from: globals.fs.currentDirectory.path);
final RunnerSuite suite = await _browserManager.load(relativePath, suiteUrl, suiteConfig, message, onDone: () async {
await _browserManager.close();
_browserManager = null;
lockResource.release();
......@@ -374,10 +396,8 @@ class FlutterWebPlatform extends PlatformPlugin {
throw StateError('Another browser is currently running.');
}
final Completer<WebSocketChannel> completer =
Completer<WebSocketChannel>.sync();
final String path =
_webSocketHandler.create(webSocketHandler(completer.complete));
final Completer<WebSocketChannel> completer = Completer<WebSocketChannel>.sync();
final String path = _webSocketHandler.create(webSocketHandler(completer.complete));
final Uri webSocketUrl = url.replace(scheme: 'ws').resolve(path);
final Uri hostUrl = url
.resolve('static/index.html')
......@@ -453,66 +473,6 @@ class OneOffHandler {
}
}
class PathHandler {
/// A trie of path components to handlers.
final _Node _paths = _Node();
/// The shelf handler.
shelf.Handler get handler => _onRequest;
/// Returns middleware that nests all requests beneath the URL prefix
/// [beneath].
static shelf.Middleware nestedIn(String beneath) {
return (FutureOr<shelf.Response> Function(shelf.Request) handler) {
final PathHandler pathHandler = PathHandler()..add(beneath, handler);
return pathHandler.handler;
};
}
/// Routes requests at or under [path] to [handler].
///
/// If [path] is a parent or child directory of another path in this handler,
/// the longest matching prefix wins.
void add(String path, shelf.Handler handler) {
_Node node = _paths;
for (final String component in p.url.split(path)) {
node = node.children.putIfAbsent(component, () => _Node());
}
node.handler = handler;
}
FutureOr<shelf.Response> _onRequest(shelf.Request request) {
shelf.Handler handler;
int handlerIndex;
_Node node = _paths;
final List<String> components = p.url.split(request.url.path);
for (int i = 0; i < components.length; i++) {
node = node.children[components[i]];
if (node == null) {
break;
}
if (node.handler == null) {
continue;
}
handler = node.handler;
handlerIndex = i;
}
if (handler == null) {
return shelf.Response.notFound('Not found.');
}
return handler(
request.change(path: p.url.joinAll(components.take(handlerIndex + 1))));
}
}
/// A trie node.
class _Node {
shelf.Handler handler;
final Map<String, _Node> children = <String, _Node>{};
}
class BrowserManager {
/// Creates a new BrowserManager that communicates with [browser] over
/// [webSocket].
......@@ -992,3 +952,68 @@ void main() async {
''';
}
}
String bootstrapFileContents(String mainUri, String requireUrl, String mapperUrl) {
return '''
(function() {
if (typeof document != 'undefined') {
var el = document.createElement("script");
el.defer = true;
el.async = false;
el.src = '$mapperUrl';
document.head.appendChild(el);
el = document.createElement("script");
el.defer = true;
el.async = false;
el.src = '$requireUrl';
el.setAttribute("data-main", '$mainUri');
document.head.appendChild(el);
} else {
importScripts('$mapperUrl', '$requireUrl');
require.config({
baseUrl: baseUrl,
});
window = self;
require(['$mainUri']);
}
})();
''';
}
String generatedActualMain(String bootstrapUrl, String mainUri) {
return '''
/* ENTRYPOINT_EXTENTION_MARKER */
// Create the main module loaded below.
define("$bootstrapUrl", ["$mainUri", "dart_sdk"], function(app, dart_sdk) {
dart_sdk.dart.setStartAsyncSynchronously(true);
dart_sdk._debugger.registerDevtoolsFormatter();
if (false) {
dart_sdk.dart.nonNullAsserts(true);
}
// See the generateMainModule doc comment.
var child = {};
child.main = app[Object.keys(app)[0]].main;
/* MAIN_EXTENSION_MARKER */
child.main();
window.\$dartLoader = {};
window.\$dartLoader.rootDirectories = [];
if (window.\$requireLoader) {
window.\$requireLoader.getModuleLibraries = dart_sdk.dart.getModuleLibraries;
}
if (window.\$dartStackTraceUtility && !window.\$dartStackTraceUtility.ready) {
window.\$dartStackTraceUtility.ready = true;
let dart = dart_sdk.dart;
window.\$dartStackTraceUtility.setSourceMapProvider(function(url) {
var baseUrl = window.location.protocol + '//' + window.location.host;
url = url.replace(baseUrl + '/', '');
if (url == 'dart_sdk.js') {
return dart.getSourceMap('dart_sdk');
}
url = url.replace(".lib.js", "");
return dart.getSourceMap(url);
});
}
});
''';
}
......@@ -121,14 +121,13 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner {
.absolute
.uri
.toFilePath();
final bool result = await webCompilationProxy.initialize(
final WebVirtualFS result = await webCompilationProxy.initialize(
projectDirectory: flutterProject.directory,
testOutputDir: tempBuildDir,
testFiles: testFiles,
projectName: flutterProject.manifest.appName,
initializePlatform: true,
buildInfo: buildInfo,
);
if (!result) {
if (result == null) {
throwToolExit('Failed to compile tests');
}
testArgs
......@@ -146,6 +145,7 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner {
flutterProject: flutterProject,
pauseAfterLoad: startPaused,
buildInfo: buildInfo,
webVirtualFS: result,
);
},
);
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:typed_data';
import 'package:meta/meta.dart';
import '../base/common.dart';
......@@ -96,19 +98,19 @@ class WebCompilationProxy {
const WebCompilationProxy();
/// Initialize the web compiler from the `projectDirectory`.
///
/// Returns whether or not the build was successful.
///
/// `release` controls whether we build the bundle for dartdevc or only
/// the entry points for dart2js to later take over.
Future<bool> initialize({
Future<WebVirtualFS> initialize({
@required Directory projectDirectory,
@required String projectName,
String testOutputDir,
List<String> testFiles,
BuildMode mode,
bool initializePlatform,
@required String testOutputDir,
@required List<String> testFiles,
@required BuildInfo buildInfo,
}) async {
throw UnimplementedError();
}
}
class WebVirtualFS {
final Map<String, Uint8List> metadataFiles = <String, Uint8List>{};
final Map<String, Uint8List> files = <String, Uint8List>{};
final Map<String, Uint8List> sourcemaps = <String, Uint8List>{};
}
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