diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index 17c4a218e..b91c2709d 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -11,12 +11,17 @@ - Migrate `package:dwds` to null safety. - Make `ChromeProxyService.getStack` wait for the debugger to perform initial resume operation. This avoids race conditions on isolate start. +- Make server paths match directory structure + - Allows correct relative source map paths resolution. + - Add `PackageUriMapper` class to allow mapping uris to server paths. - Update the min SDK constraint to 2.18.0. **Breaking changes** - Remove no longer used `ExpressionCompilerService.handler`. - Remove `assetHandler` parameter from `ExpressionCompilerService` constructor. +- Add `packageUriMapper` parameter to the constructor of + `FrontendServerRequireStrategyProvider`. ## 15.0.0 @@ -141,7 +146,7 @@ ## 11.5.1 -- Update SDK contraint to `>=2.15.0 <3.0.0`. +- Update SDK constraint to `>=2.15.0 <3.0.0`. ## 11.5.0 @@ -235,7 +240,7 @@ ## 11.1.2 - Return empty library from `ChromeProxyService.getObject` for libraries present - in medatata but not loaded at runtime. + in metadata but not loaded at runtime. - Log failures to load kernel during expression evaluation. - Show lowered final fields using their original dart names. - Limit simultaneous connections to asset server to prevent broken sockets. @@ -395,7 +400,7 @@ ## 7.0.2 -- Depend on the latest `pacakge:sse`. +- Depend on the latest `package:sse`. - Add more verbose logging around `hotRestart`, `fullReload` and entrypoint injection. @@ -432,7 +437,7 @@ - Change `ExpressionCompiler` to require a new `updateDependencies` method. - Update a number of `LoadStrategy` APIs to remove heuristics and rely on the `MetadataProvider`. -- No longer require a `LogWriter` and corresponding `verbose` arguement but +- No longer require a `LogWriter` and corresponding `verbose` argument but instead properly use `package:logger`. - `FrontendServerRequireStrategyProvider` now requires a `digestProvider`. @@ -503,7 +508,7 @@ - Change the returned errors for the unimplemented `getClassList` and `reloadSources` methods to -32601 ('method does not exist / is not available'). -- Do not include native JavaScipt objects on stack returned from the debugger. +- Do not include native JavaScript objects on stack returned from the debugger. ## 3.1.0 @@ -596,7 +601,7 @@ - Expose `middleware` and `handler`. **Breaking Change:** The `AssetHandler` will not automatically be added the DWDS -handler cascade. You must now also add the `middelware` to your server's +handler cascade. You must now also add the `middleware` to your server's pipeline. ## 0.8.5 diff --git a/dwds/lib/asset_reader.dart b/dwds/lib/asset_reader.dart index f7aeaf1c8..9066a04b1 100644 --- a/dwds/lib/asset_reader.dart +++ b/dwds/lib/asset_reader.dart @@ -2,4 +2,5 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -export 'src/readers/asset_reader.dart' show AssetReader, UrlEncoder; +export 'src/readers/asset_reader.dart' + show AssetReader, UrlEncoder, PackageUriMapper, stripLeadingSlashes; diff --git a/dwds/lib/dwds.dart b/dwds/lib/dwds.dart index 40f103467..84bf219ee 100644 --- a/dwds/lib/dwds.dart +++ b/dwds/lib/dwds.dart @@ -17,7 +17,8 @@ export 'src/loaders/frontend_server_require.dart' export 'src/loaders/legacy.dart' show LegacyStrategy; export 'src/loaders/require.dart' show RequireStrategy; export 'src/loaders/strategy.dart' show LoadStrategy, ReloadConfiguration; -export 'src/readers/asset_reader.dart' show AssetReader, UrlEncoder; +export 'src/readers/asset_reader.dart' + show AssetReader, UrlEncoder, PackageUriMapper; export 'src/readers/frontend_server_asset_reader.dart' show FrontendServerAssetReader; export 'src/readers/proxy_server_asset_reader.dart' show ProxyServerAssetReader; diff --git a/dwds/lib/src/debugging/debugger.dart b/dwds/lib/src/debugging/debugger.dart index 4de9d1568..8444ff7e2 100644 --- a/dwds/lib/src/debugging/debugger.dart +++ b/dwds/lib/src/debugging/debugger.dart @@ -546,7 +546,6 @@ class Debugger extends Domain { /// Handles pause events coming from the Chrome connection. Future _pauseHandler(DebuggerPausedEvent e) async { final isolate = inspector.isolate; - Event event; final timestamp = DateTime.now().millisecondsSinceEpoch; final jsBreakpointIds = e.hitBreakpoints ?? []; @@ -808,8 +807,9 @@ class _Breakpoints extends Domain { throw RPCError( 'addBreakpoint', 102, - 'The VM is unable to add a breakpoint ' - 'at the specified line or function'); + 'The VM is unable to add a breakpoint $id ' + 'at the specified line or function: ($scriptId:$line:$column): ' + ' cannot find Dart location.'); } try { @@ -822,8 +822,9 @@ class _Breakpoints extends Domain { throw RPCError( 'addBreakpoint', 102, - 'The VM is unable to add a breakpoint ' - 'at the specified line or function'); + 'The VM is unable to add a breakpoint $id ' + 'at the specified line or function: ($scriptId:$line:$column): ' + 'cannot set JS breakpoint at $location'); } _note(jsId: jsBreakpointId, bp: dartBreakpoint); return dartBreakpoint; diff --git a/dwds/lib/src/debugging/location.dart b/dwds/lib/src/debugging/location.dart index 5bf69980c..9b13a60aa 100644 --- a/dwds/lib/src/debugging/location.dart +++ b/dwds/lib/src/debugging/location.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'package:async/async.dart'; -import 'package:dwds/src/loaders/require.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as p; import 'package:source_maps/parser.dart'; @@ -162,8 +161,13 @@ class Locations { /// Returns all [Location] data for a provided JS server path. Future> locationsForUrl(String url) async { - final module = await globalLoadStrategy.moduleForServerPath( - _entrypoint, Uri.parse(url).path); + if (url.isEmpty) return {}; + + final dartUri = DartUri(url, _root); + final serverPath = dartUri.serverPath; + final module = + await globalLoadStrategy.moduleForServerPath(_entrypoint, serverPath); + final cache = _moduleToLocations[module]; if (cache != null) return cache; if (module != null) { @@ -286,7 +290,9 @@ class Locations { await globalLoadStrategy.sourceMapPathForModule(_entrypoint, module); final sourceMapContents = await _assetReader.sourceMapContents(sourceMapPath); - final scriptLocation = p.url.dirname('/${relativizePath(modulePath)}'); + final scriptLocation = + p.url.dirname('/${stripLeadingSlashes(modulePath)}'); + if (sourceMapContents == null) return result; // This happens to be a [SingleMapping] today in DDC. final mapping = parse(sourceMapContents); @@ -303,6 +309,7 @@ class Locations { final relativeSegments = p.split(mapping.urls[index]); final path = p.url.normalize( p.url.joinAll([scriptLocation, ...relativeSegments])); + final dartUri = DartUri(path, _root); result.add(Location.from( modulePath, diff --git a/dwds/lib/src/debugging/metadata/provider.dart b/dwds/lib/src/debugging/metadata/provider.dart index ae4696e5b..b045cbfb7 100644 --- a/dwds/lib/src/debugging/metadata/provider.dart +++ b/dwds/lib/src/debugging/metadata/provider.dart @@ -219,9 +219,12 @@ class MetadataProvider { } void _addMetadata(ModuleMetadata metadata) { - _moduleToSourceMap[metadata.name] = metadata.sourceMapUri; - _modulePathToModule[metadata.moduleUri] = metadata.name; - _moduleToModulePath[metadata.name] = metadata.moduleUri; + final modulePath = stripLeadingSlashes(metadata.moduleUri); + final sourceMapPath = stripLeadingSlashes(metadata.sourceMapUri); + + _moduleToSourceMap[metadata.name] = sourceMapPath; + _modulePathToModule[modulePath] = metadata.name; + _moduleToModulePath[metadata.name] = modulePath; for (var library in metadata.libraries.values) { if (library.importUri.startsWith('file:/')) { diff --git a/dwds/lib/src/debugging/modules.dart b/dwds/lib/src/debugging/modules.dart index 527a458c9..fc51c648c 100644 --- a/dwds/lib/src/debugging/modules.dart +++ b/dwds/lib/src/debugging/modules.dart @@ -78,6 +78,7 @@ class Modules { if (scriptToModule.containsKey(library)) { final module = scriptToModule[library]!; + _sourceToModule[libraryServerPath] = module; _sourceToLibrary[libraryServerPath] = Uri.parse(library); _libraryToModule[library] = module; diff --git a/dwds/lib/src/loaders/build_runner_require.dart b/dwds/lib/src/loaders/build_runner_require.dart index b679e21bf..6da17236e 100644 --- a/dwds/lib/src/loaders/build_runner_require.dart +++ b/dwds/lib/src/loaders/build_runner_require.dart @@ -75,7 +75,7 @@ class BuildRunnerRequireStrategyProvider { Future _moduleForServerPath( MetadataProvider metadataProvider, String serverPath) async { final modulePathToModule = await metadataProvider.modulePathToModule; - final relativePath = relativizePath(serverPath); + final relativePath = stripLeadingSlashes(serverPath); for (var e in modulePathToModule.entries) { if (stripTopLevelDirectory(e.key) == relativePath) { return e.value; @@ -97,10 +97,14 @@ class BuildRunnerRequireStrategyProvider { return stripTopLevelDirectory(path); } - String? _serverPathForAppUri(String appUri) { - if (appUri.startsWith('org-dartlang-app:')) { + String? _serverPathForAppUri(String appUrl) { + final appUri = Uri.parse(appUrl); + if (appUri.isScheme('org-dartlang-app')) { // We skip the root from which we are serving. - return Uri.parse(appUri).pathSegments.skip(1).join('/'); + return appUri.pathSegments.skip(1).join('/'); + } + if (appUri.isScheme('package')) { + return '/packages/${appUri.path}'; } return null; } diff --git a/dwds/lib/src/loaders/frontend_server_require.dart b/dwds/lib/src/loaders/frontend_server_require.dart index 4a63ea026..1b3dce2c8 100644 --- a/dwds/lib/src/loaders/frontend_server_require.dart +++ b/dwds/lib/src/loaders/frontend_server_require.dart @@ -14,6 +14,7 @@ import 'require.dart'; class FrontendServerRequireStrategyProvider { final ReloadConfiguration _configuration; final AssetReader _assetReader; + final PackageUriMapper _packageUriMapper; final Future> Function() _digestsProvider; final String _basePath; @@ -29,26 +30,31 @@ class FrontendServerRequireStrategyProvider { _assetReader, ); - FrontendServerRequireStrategyProvider(this._configuration, this._assetReader, - this._digestsProvider, this._basePath); + FrontendServerRequireStrategyProvider( + this._configuration, + this._assetReader, + this._packageUriMapper, + this._digestsProvider, + this._basePath, + ); RequireStrategy get strategy => _requireStrategy; String _removeBasePath(String path) { if (_basePath.isEmpty) return path; - // If path is a server path it might start with a '/'. - final base = path.startsWith('/') ? '/$_basePath' : _basePath; - return path.startsWith(base) ? path.substring(base.length) : path; + + final stripped = stripLeadingSlashes(path); + return stripLeadingSlashes(stripped.substring(_basePath.length)); } String _addBasePath(String serverPath) => _basePath.isEmpty - ? relativizePath(serverPath) - : '$_basePath/${relativizePath(serverPath)}'; + ? stripLeadingSlashes(serverPath) + : '$_basePath/${stripLeadingSlashes(serverPath)}'; Future> _moduleProvider( MetadataProvider metadataProvider) async => (await metadataProvider.moduleToModulePath).map((key, value) => - MapEntry(key, relativizePath(removeJsExtension(value)))); + MapEntry(key, stripLeadingSlashes(removeJsExtension(value)))); Future _moduleForServerPath( MetadataProvider metadataProvider, String serverPath) async { @@ -65,9 +71,16 @@ class FrontendServerRequireStrategyProvider { MetadataProvider metadataProvider, String module) async => _addBasePath((await metadataProvider.moduleToSourceMap)[module] ?? ''); - String? _serverPathForAppUri(String appUri) { - if (appUri.startsWith('org-dartlang-app:')) { - return _addBasePath(Uri.parse(appUri).path); + String? _serverPathForAppUri(String appUrl) { + final appUri = Uri.parse(appUrl); + if (appUri.isScheme('org-dartlang-app')) { + return _addBasePath(appUri.path); + } + if (appUri.isScheme('package')) { + final resolved = _packageUriMapper.packageUriToServerPath(appUri); + if (resolved != null) { + return resolved; + } } return null; } diff --git a/dwds/lib/src/loaders/require.dart b/dwds/lib/src/loaders/require.dart index b9ba9b6fd..fe9807386 100644 --- a/dwds/lib/src/loaders/require.dart +++ b/dwds/lib/src/loaders/require.dart @@ -23,9 +23,6 @@ String basePathForServerUri(String url) { return base = base.startsWith('/') ? base.substring(1) : base; } -String relativizePath(String path) => - path.startsWith('/') ? path.substring(1) : path; - String removeJsExtension(String path) => path.endsWith('.js') ? p.withoutExtension(path) : path; diff --git a/dwds/lib/src/readers/asset_reader.dart b/dwds/lib/src/readers/asset_reader.dart index 39ef444ed..435916db2 100644 --- a/dwds/lib/src/readers/asset_reader.dart +++ b/dwds/lib/src/readers/asset_reader.dart @@ -2,6 +2,11 @@ // for details. 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:collection/collection.dart'; +import 'package:file/file.dart'; +import 'package:logging/logging.dart'; +import 'package:package_config/package_config.dart'; + typedef UrlEncoder = Future Function(String url); /// A reader for Dart sources and related source maps. @@ -21,3 +26,84 @@ abstract class AssetReader { /// Closes connections Future close(); } + +class PackageUriMapper { + final _logger = Logger('PackageUriMapper'); + final PackageConfig packageConfig; + final bool useDebuggerModuleNames; + + static Future create( + FileSystem fileSystem, Uri packageConfigFile, + {bool useDebuggerModuleNames = false}) async { + final packageConfig = + await loadPackageConfig(fileSystem.file(packageConfigFile)); + return PackageUriMapper(packageConfig, + useDebuggerModuleNames: useDebuggerModuleNames); + } + + PackageUriMapper(this.packageConfig, {this.useDebuggerModuleNames = false}); + + /// Compute server path for package uri. + /// + /// Note: needs to match `urlForComponentUri` in javascript_bundle.dart + /// in SDK code. + String? packageUriToServerPath(Uri packageUri) { + final defaultServerPath = '/packages/${packageUri.path}'; + if (packageUri.isScheme('package')) { + if (!useDebuggerModuleNames) { + return defaultServerPath; + } + final Uri? resolvedUri = packageConfig.resolve(packageUri); + if (resolvedUri == null) { + _logger.severe('Cannot resolve package uri $packageUri'); + return defaultServerPath; + } + final Package? package = packageConfig.packageOf(resolvedUri); + if (package == null) { + _logger.severe('Cannot find package for package uri $packageUri'); + return defaultServerPath; + } + final Uri root = package.root; + final String relativeUrl = + resolvedUri.toString().replaceFirst('$root', ''); + final String? relativeRoot = _getRelativeRoot(root); + final String ret = relativeRoot == null + ? 'packages/$relativeUrl' + : 'packages/$relativeRoot/$relativeUrl'; + return ret; + } + _logger.severe('Expected package uri, but found $packageUri'); + return null; + } + + /// Compute resolved file uri for a server path. + Uri? serverPathToResolvedUri(String serverPath) { + serverPath = stripLeadingSlashes(serverPath); + final segments = serverPath.split('/'); + if (segments.first == 'packages') { + if (!useDebuggerModuleNames) { + return packageConfig + .resolve(Uri(scheme: 'package', pathSegments: segments.skip(1))); + } + final String relativeRoot = segments.skip(1).first; + final String relativeUrl = segments.skip(2).join('/'); + final Package package = packageConfig.packages + .firstWhere((Package p) => _getRelativeRoot(p.root) == relativeRoot); + final Uri resolvedUri = package.root.resolve(relativeUrl); + + return resolvedUri; + } + _logger.severe('Expected "packages/" path, but found $serverPath'); + return null; + } +} + +String stripLeadingSlashes(String path) { + while (path.startsWith('/')) { + path = path.substring(1); + } + return path; +} + +String? _getRelativeRoot(Uri root) => + root.pathSegments.lastWhereOrNull((segment) => segment.isNotEmpty); diff --git a/dwds/lib/src/utilities/dart_uri.dart b/dwds/lib/src/utilities/dart_uri.dart index 4b8b4af79..9c3e7d228 100644 --- a/dwds/lib/src/utilities/dart_uri.dart +++ b/dwds/lib/src/utilities/dart_uri.dart @@ -29,19 +29,18 @@ class DartUri { /// /// The optional [root] is the directory the app is served from. factory DartUri(String uri, [String? root]) { - final serverPath = globalLoadStrategy.serverPathForAppUri(uri); - if (serverPath != null) { - return DartUri._(serverPath); - } // TODO(annagrin): Support creating DartUris from `dart:` uris. // Issue: https://github.com/dart-lang/webdev/issues/1584 + if (uri.startsWith('org-dartlang-app:')) { + return DartUri._fromDartLangUri(uri); + } if (uri.startsWith('package:')) { return DartUri._fromPackageUri(uri, root: root); } if (uri.startsWith('file:')) { return DartUri._fromFileUri(uri, root: root); } - if (uri.startsWith('/packages/')) { + if (uri.startsWith('packages/') || uri.startsWith('/packages/')) { return DartUri._fromRelativePath(uri, root: root); } if (uri.startsWith('/')) { @@ -51,20 +50,30 @@ class DartUri { return DartUri(Uri.parse(uri).path); } - throw FormatException('Unsupported URI form', uri); + throw FormatException('Unsupported URI form: $uri'); } @override String toString() => 'DartUri: $serverPath'; + /// Construct from a package: URI + factory DartUri._fromDartLangUri(String uri) { + var serverPath = globalLoadStrategy.serverPathForAppUri(uri); + if (serverPath == null) { + _logger.severe('Cannot find server path for $uri'); + serverPath = uri; + } + return DartUri._(serverPath); + } + /// Construct from a package: URI factory DartUri._fromPackageUri(String uri, {String? root}) { - final packagePath = 'packages/${uri.substring("package:".length)}'; - if (root != null) { - final relativePath = p.url.join(root, packagePath); - return DartUri._fromRelativePath(relativePath); + var serverPath = globalLoadStrategy.serverPathForAppUri(uri); + if (serverPath == null) { + _logger.severe('Cannot find server path for $uri'); + serverPath = uri; } - return DartUri._(packagePath); + return DartUri._fromRelativePath(serverPath, root: root); } /// Construct from a file: URI diff --git a/dwds/test/build_daemon_circular_evaluate_test.dart b/dwds/test/build_daemon_circular_evaluate_test.dart new file mode 100644 index 000000000..6ff523a75 --- /dev/null +++ b/dwds/test/build_daemon_circular_evaluate_test.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +@TestOn('vm') + +import 'package:test/test.dart'; + +import 'fixtures/context.dart'; +import 'evaluate_circular_common.dart'; + +void main() async { + // Enable verbose logging for debugging. + final debug = false; + + for (var nullSafety in NullSafety.values) { + group('${nullSafety.name} null safety |', () { + testAll( + compilationMode: CompilationMode.buildDaemon, + nullSafety: nullSafety, + debug: debug, + ); + }); + } +} diff --git a/dwds/test/dart_uri_file_uri_test.dart b/dwds/test/dart_uri_file_uri_test.dart index 828f80475..80825dacc 100644 --- a/dwds/test/dart_uri_file_uri_test.dart +++ b/dwds/test/dart_uri_file_uri_test.dart @@ -12,48 +12,78 @@ import 'package:test/test.dart'; import 'fixtures/context.dart'; final context = TestContext( - directory: '../fixtures/_testPackage', + directory: p.join('..', 'fixtures', '_testPackage'), + entry: p.join('..', 'fixtures', '_testPackage', 'web', 'main.dart'), path: 'index.html', pathToServe: 'web'); -String get dwdsDir => Directory.current.absolute.path; +final dwdsDir = Directory.current.absolute.path; /// The directory for the general _test package. -String get testDir => p.join(p.dirname(dwdsDir), 'fixtures', '_test'); +final testDir = p.join(p.dirname(dwdsDir), 'fixtures', '_test'); /// The directory for the _testPackage package (contained within dwds), which /// imports _test. -String get testPackageDir => context.workingDirectory; +final testPackageDir = context.workingDirectory; // This tests converting file Uris into our internal paths. // // These tests are separated out because we need a running isolate in order to // look up packages. void main() { - setUpAll(() async { - await context.setUp(); - }); - - tearDownAll(() async { - await context.tearDown(); - }); - - test('file path to org-dartlang-app', () { - final webMain = Uri.file(p.join(testPackageDir, 'web', 'main.dart')); - final uri = DartUri('$webMain'); - expect(uri.serverPath, 'main.dart'); - }); - - test('file path to this package', () { - final testPackageLib = - Uri.file(p.join(testPackageDir, 'lib', 'test_library.dart')); - final uri = DartUri('$testPackageLib'); - expect(uri.serverPath, 'packages/_test_package/test_library.dart'); - }); - - test('file path to another package', () { - final testLib = Uri.file(p.join(testDir, 'lib', 'library.dart')); - final dartUri = DartUri('$testLib'); - expect(dartUri.serverPath, 'packages/_test/library.dart'); - }); + for (final compilationMode in CompilationMode.values) { + group('$compilationMode |', () { + for (final useDebuggerModuleNames in [false, true]) { + group('Debugger module names: $useDebuggerModuleNames |', () { + final appServerPath = + compilationMode == CompilationMode.frontendServer + ? 'web/main.dart' + : 'main.dart'; + + final serverPath = + compilationMode == CompilationMode.frontendServer && + useDebuggerModuleNames + ? 'packages/_testPackage/lib/test_library.dart' + : 'packages/_test_package/test_library.dart'; + + final anotherServerPath = + compilationMode == CompilationMode.frontendServer && + useDebuggerModuleNames + ? 'packages/_test/lib/library.dart' + : 'packages/_test/library.dart'; + + setUpAll(() async { + await context.setUp( + compilationMode: compilationMode, + useDebuggerModuleNames: useDebuggerModuleNames, + ); + }); + + tearDownAll(() async { + await context.tearDown(); + }); + + test('file path to org-dartlang-app', () { + final webMain = + Uri.file(p.join(testPackageDir, 'web', 'main.dart')); + final uri = DartUri('$webMain'); + expect(uri.serverPath, appServerPath); + }); + + test('file path to this package', () { + final testPackageLib = + Uri.file(p.join(testPackageDir, 'lib', 'test_library.dart')); + final uri = DartUri('$testPackageLib'); + expect(uri.serverPath, serverPath); + }); + + test('file path to another package', () { + final testLib = Uri.file(p.join(testDir, 'lib', 'library.dart')); + final dartUri = DartUri('$testLib'); + expect(dartUri.serverPath, anotherServerPath); + }); + }); + } + }); + } } diff --git a/dwds/test/dart_uri_test.dart b/dwds/test/dart_uri_test.dart index e44658206..ff271305c 100644 --- a/dwds/test/dart_uri_test.dart +++ b/dwds/test/dart_uri_test.dart @@ -16,8 +16,14 @@ import 'fixtures/fakes.dart'; class TestStrategy extends FakeStrategy { @override - String? serverPathForAppUri(String appUri) { - if (appUri.startsWith('org-dartlang-app:')) return 'foo'; + String? serverPathForAppUri(String appUrl) { + final appUri = Uri.parse(appUrl); + if (appUri.isScheme('org-dartlang-app')) { + return 'foo'; + } + if (appUri.isScheme('package')) { + return '/packages/${appUri.path}'; + } return null; } } diff --git a/dwds/test/evaluate_circular_common.dart b/dwds/test/evaluate_circular_common.dart new file mode 100644 index 000000000..0de304df4 --- /dev/null +++ b/dwds/test/evaluate_circular_common.dart @@ -0,0 +1,172 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +@TestOn('vm') +import 'dart:async'; + +import 'package:dwds/src/connections/debug_connection.dart'; +import 'package:dwds/src/services/chrome_proxy_service.dart'; +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; +import 'package:vm_service/vm_service.dart'; +import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; + +import 'fixtures/context.dart'; +import 'fixtures/logging.dart'; + +class TestSetup { + static TestContext createContext(String index, String packageRoot) => + TestContext( + directory: p.join('..', 'fixtures', packageRoot), + entry: p.join('..', 'fixtures', packageRoot, 'web', 'main.dart'), + path: index, + pathToServe: 'web'); + + static TestContext contextUnsound(String index) => + createContext(index, '_testCircular2'); + + static TestContext contextSound(String index) => + createContext(index, '_testCircular2Sound'); + + TestContext context; + + TestSetup.sound(IndexBaseMode baseMode) + : context = contextSound(_index(baseMode)); + + TestSetup.unsound(IndexBaseMode baseMode) + : context = contextUnsound(_index(baseMode)); + + factory TestSetup.create(NullSafety? nullSafety, IndexBaseMode baseMode) => + nullSafety == NullSafety.sound + ? TestSetup.sound(baseMode) + : TestSetup.unsound(baseMode); + + ChromeProxyService get service => + fetchChromeProxyService(context.debugConnection); + WipConnection get tabConnection => context.tabConnection; + + static String _index(IndexBaseMode baseMode) => + baseMode == IndexBaseMode.base ? 'base_index.html' : 'index.html'; +} + +void testAll({ + CompilationMode compilationMode = CompilationMode.buildDaemon, + IndexBaseMode indexBaseMode = IndexBaseMode.noBase, + NullSafety nullSafety = NullSafety.sound, + bool useDebuggerModuleNames = false, + bool debug = false, +}) { + if (compilationMode == CompilationMode.buildDaemon && + indexBaseMode == IndexBaseMode.base) { + throw StateError( + 'build daemon scenario does not support non-empty base in index file'); + } + final setup = TestSetup.create(nullSafety, indexBaseMode); + final context = setup.context; + + Future onBreakPoint(String isolate, ScriptRef script, + String breakPointId, Future Function() body) async { + Breakpoint? bp; + try { + final line = + await context.findBreakpointLine(breakPointId, isolate, script); + bp = await setup.service + .addBreakpointWithScriptUri(isolate, script.uri!, line); + await body(); + } finally { + // Remove breakpoint so it doesn't impact other tests or retries. + if (bp != null) { + await setup.service.removeBreakpoint(isolate, bp.id!); + } + } + } + + group('shared context with evaluation', () { + setUpAll(() async { + setCurrentLogWriter(debug: debug); + await context.setUp( + compilationMode: compilationMode, + nullSafety: nullSafety, + enableExpressionEvaluation: true, + useDebuggerModuleNames: useDebuggerModuleNames, + verboseCompiler: debug, + ); + }); + + tearDownAll(() async { + await context.tearDown(); + }); + + setUp(() => setCurrentLogWriter(debug: debug)); + + group('evaluateInFrame', () { + VM vm; + late Isolate isolate; + late String isolateId; + ScriptList scripts; + late ScriptRef test1LibraryScript; + late ScriptRef test2LibraryScript; + late Stream stream; + + setUp(() async { + setCurrentLogWriter(debug: debug); + vm = await setup.service.getVM(); + isolate = await setup.service.getIsolate(vm.isolates!.first.id!); + isolateId = isolate.id!; + scripts = await setup.service.getScripts(isolateId); + + await setup.service.streamListen('Debug'); + stream = setup.service.onEvent('Debug'); + + final soundNullSafety = nullSafety == NullSafety.sound; + final test1 = + soundNullSafety ? '_test_circular1_sound' : '_test_circular1'; + final test2 = + soundNullSafety ? '_test_circular2_sound' : '_test_circular2'; + + test1LibraryScript = scripts.scripts!.firstWhere( + (each) => each.uri!.contains('package:$test1/library1.dart')); + test2LibraryScript = scripts.scripts!.firstWhere( + (each) => each.uri!.contains('package:$test2/library2.dart')); + }); + + tearDown(() async { + await setup.service.resume(isolateId); + }); + + test('evaluate expression in _test_circular1/library', () async { + await onBreakPoint(isolateId, test1LibraryScript, 'Concatenate', + () async { + final event = await stream + .firstWhere((event) => event.kind == EventKind.kPauseBreakpoint); + + final result = await setup.service + .evaluateInFrame(isolateId, event.topFrame!.index!, 'a'); + + expect( + result, + isA().having( + (instance) => instance.valueAsString, 'valueAsString', 'a')); + }); + }); + + test('evaluate expression in _test_circular2/library', () async { + await onBreakPoint( + isolateId, test2LibraryScript, 'testCircularDependencies', + () async { + final event = await stream + .firstWhere((event) => event.kind == EventKind.kPauseBreakpoint); + + final result = await setup.service + .evaluateInFrame(isolateId, event.topFrame!.index!, 'true'); + + expect( + result, + isA().having((instance) => instance.valueAsString, + 'valueAsString', 'true')); + }); + }); + }); + }); +} diff --git a/dwds/test/evaluate_common.dart b/dwds/test/evaluate_common.dart index a1656623e..3ca60cc9c 100644 --- a/dwds/test/evaluate_common.dart +++ b/dwds/test/evaluate_common.dart @@ -16,17 +16,18 @@ import 'fixtures/context.dart'; import 'fixtures/logging.dart'; class TestSetup { - static TestContext contextUnsound(String index) => TestContext( - directory: p.join('..', 'fixtures', '_testPackage'), - entry: p.join('..', 'fixtures', '_testPackage', 'web', 'main.dart'), - path: index, - pathToServe: 'web'); - - static TestContext contextSound(String index) => TestContext( - directory: p.join('..', 'fixtures', '_testPackageSound'), - entry: p.join('..', 'fixtures', '_testPackageSound', 'web', 'main.dart'), - path: index, - pathToServe: 'web'); + static TestContext createContext(String index, String packageRoot) => + TestContext( + directory: p.join('..', 'fixtures', packageRoot), + entry: p.join('..', 'fixtures', packageRoot, 'web', 'main.dart'), + path: index, + pathToServe: 'web'); + + static TestContext contextUnsound(String index) => + createContext(index, '_testPackage'); + + static TestContext contextSound(String index) => + createContext(index, '_testPackageSound'); TestContext context; @@ -53,6 +54,7 @@ void testAll({ CompilationMode compilationMode = CompilationMode.buildDaemon, IndexBaseMode indexBaseMode = IndexBaseMode.noBase, NullSafety nullSafety = NullSafety.sound, + bool useDebuggerModuleNames = false, bool debug = false, }) { if (compilationMode == CompilationMode.buildDaemon && @@ -87,6 +89,7 @@ void testAll({ compilationMode: compilationMode, nullSafety: nullSafety, enableExpressionEvaluation: true, + useDebuggerModuleNames: useDebuggerModuleNames, verboseCompiler: debug, ); }); diff --git a/dwds/test/fixtures/context.dart b/dwds/test/fixtures/context.dart index 0e451f272..830c1658f 100644 --- a/dwds/test/fixtures/context.dart +++ b/dwds/test/fixtures/context.dart @@ -23,6 +23,7 @@ import 'package:dwds/src/services/expression_compiler_service.dart'; import 'package:dwds/src/utilities/dart_uri.dart'; import 'package:dwds/src/utilities/sdk_configuration.dart'; import 'package:dwds/src/utilities/shared.dart'; +import 'package:file/local.dart'; import 'package:frontend_server_common/src/resident_runner.dart'; import 'package:http/http.dart'; import 'package:http/io_client.dart'; @@ -159,10 +160,12 @@ class TestContext { bool enableExpressionEvaluation = false, bool verboseCompiler = false, SdkConfigurationProvider? sdkConfigurationProvider, + bool useDebuggerModuleNames = false, }) async { sdkConfigurationProvider ??= DefaultSdkConfigurationProvider(); try { + DartUri.currentDirectory = workingDirectory; configureLogWriter(); _client = IOClient(HttpClient() @@ -271,11 +274,19 @@ class TestContext { final entry = p.toUri(_entryFile.path .substring(_projectDirectory.toFilePath().length + 1)); + final fileSystem = LocalFileSystem(); + final packageUriMapper = await PackageUriMapper.create( + fileSystem, + _packageConfigFile, + useDebuggerModuleNames: useDebuggerModuleNames, + ); + _webRunner = ResidentWebRunner( entry, urlEncoder, _projectDirectory, _packageConfigFile, + packageUriMapper, [_projectDirectory], 'org-dartlang-app', outputDir.path, @@ -284,8 +295,8 @@ class TestContext { ); final assetServerPort = await findUnusedPort(); - await webRunner.run( - hostname, assetServerPort, p.join(pathToServe, path)); + await webRunner.run(fileSystem, hostname, assetServerPort, + p.join(pathToServe, path)); if (enableExpressionEvaluation) { expressionCompiler = webRunner.expressionCompiler; @@ -296,7 +307,11 @@ class TestContext { _assetHandler = webRunner.devFS.assetServer.handleRequest; requireStrategy = FrontendServerRequireStrategyProvider( - reloadConfiguration, assetReader, () async => {}, basePath) + reloadConfiguration, + assetReader, + packageUriMapper, + () async => {}, + basePath) .strategy; buildResults = const Stream.empty(); diff --git a/dwds/test/frontend_server_circular_evaluate_test.dart b/dwds/test/frontend_server_circular_evaluate_test.dart new file mode 100644 index 000000000..448f83280 --- /dev/null +++ b/dwds/test/frontend_server_circular_evaluate_test.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +@TestOn('vm') + +import 'dart:io'; + +import 'package:pub_semver/pub_semver.dart'; +import 'package:test/test.dart'; + +import 'fixtures/context.dart'; +import 'evaluate_circular_common.dart'; + +void main() async { + // Enable verbose logging for debugging. + final debug = false; + + // TODO(annagrin): Remove when 2.19.0-150.0.dev is in stable. + final debuggerModuleNamesSupported = + Version.parse(Platform.version.split(' ').first) >= + Version.parse('2.19.0-150.0.dev'); + + group('Context with circular dependencies |', () { + for (var nullSafety in NullSafety.values) { + group('${nullSafety.name} null safety |', () { + for (var indexBaseMode in IndexBaseMode.values) { + group( + 'with ${indexBaseMode.name} |', + () { + testAll( + compilationMode: CompilationMode.frontendServer, + indexBaseMode: indexBaseMode, + nullSafety: nullSafety, + useDebuggerModuleNames: true, + debug: debug, + ); + }, + skip: + // https://github.com/dart-lang/sdk/issues/49277 + indexBaseMode == IndexBaseMode.base && Platform.isWindows || + // https://github.com/dart-lang/webdev/issues/1591); + nullSafety == NullSafety.sound || + // Needs debugger module names change in the SDK to work. + !debuggerModuleNamesSupported, + ); + } + }); + } + }); +} diff --git a/dwds/test/frontend_server_evaluate_test.dart b/dwds/test/frontend_server_evaluate_test.dart index 770cd90fa..56441ddb9 100644 --- a/dwds/test/frontend_server_evaluate_test.dart +++ b/dwds/test/frontend_server_evaluate_test.dart @@ -6,6 +6,7 @@ import 'dart:io'; +import 'package:pub_semver/pub_semver.dart'; import 'package:test/test.dart'; import 'fixtures/context.dart'; @@ -15,27 +16,37 @@ void main() async { // Enable verbose logging for debugging. final debug = false; - for (var nullSafety in NullSafety.values) { - group('${nullSafety.name} null safety |', () { - for (var indexBaseMode in IndexBaseMode.values) { - group( - 'with ${indexBaseMode.name} |', - () { - testAll( - compilationMode: CompilationMode.frontendServer, - indexBaseMode: indexBaseMode, - nullSafety: nullSafety, - debug: debug, + // TODO(annagrin): Remove when 2.19.0-150.0.dev is in stable. + final debuggerModuleNamesSupported = + Version.parse(Platform.version.split(' ').first) >= + Version.parse('2.19.0-150.0.dev'); + + for (var useDebuggerModuleNames in [false, true]) { + group('Debugger module names: $useDebuggerModuleNames |', () { + for (var nullSafety in NullSafety.values) { + group('${nullSafety.name} null safety |', () { + for (var indexBaseMode in IndexBaseMode.values) { + group( + 'with ${indexBaseMode.name} |', + () { + testAll( + compilationMode: CompilationMode.frontendServer, + indexBaseMode: indexBaseMode, + nullSafety: nullSafety, + useDebuggerModuleNames: useDebuggerModuleNames, + debug: debug, + ); + }, + skip: + // https://github.com/dart-lang/sdk/issues/49277 + (indexBaseMode == IndexBaseMode.base && Platform.isWindows) || + // https://github.com/dart-lang/webdev/issues/1591 + (nullSafety == NullSafety.sound) || + // Needs debugger module names feature in SDK. + (useDebuggerModuleNames && !debuggerModuleNamesSupported), ); - }, - skip: (nullSafety == - NullSafety - .sound) // https://github.com/dart-lang/webdev/issues/1591 - || - (indexBaseMode == IndexBaseMode.base && - Platform - .isWindows), // https://github.com/dart-lang/sdk/issues/49277 - ); + } + }); } }); } diff --git a/dwds/test/location_test.dart b/dwds/test/location_test.dart index ab5d93bce..92acf636a 100644 --- a/dwds/test/location_test.dart +++ b/dwds/test/location_test.dart @@ -23,7 +23,7 @@ void main() { const lineLength = 150; globalLoadStrategy = MockLoadStrategy(); - final dartUri = DartUri('org-dartlang://web/main.dart'); + final dartUri = DartUri('org-dartlang-app://web/main.dart'); final assetReader = FakeAssetReader(sourceMap: sourceMapContents); final modules = MockModules(); @@ -175,7 +175,7 @@ Matcher _matchJsLocation(int line, int column) => isA() .having((l) => l.column, 'column', column); const _module = 'packages/module'; -const _serverPath = 'package/module.js'; +const _serverPath = 'web/main.dart'; const _sourceMapPath = 'packages/module.js.map'; class MockLoadStrategy extends FakeStrategy { diff --git a/dwds/test/package_uri_mapper_test.dart b/dwds/test/package_uri_mapper_test.dart new file mode 100644 index 000000000..e6e6d3e3e --- /dev/null +++ b/dwds/test/package_uri_mapper_test.dart @@ -0,0 +1,59 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +@TestOn('vm') + +import 'package:dwds/dwds.dart'; +import 'package:file/local.dart'; +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; + +void main() { + for (final useDebuggerModuleNames in [true, false]) { + group( + 'Package uri mapper with debugger module names: ' + ' $useDebuggerModuleNames |', () { + final fileSystem = LocalFileSystem(); + + final packageUri = + Uri(scheme: 'package', path: '_test_package/test_library.dart'); + + final serverPath = useDebuggerModuleNames + ? 'packages/_testPackage/lib/test_library.dart' + : '/packages/_test_package/test_library.dart'; + + final resolvedPath = + '/webdev/fixtures/_testPackage/lib/test_library.dart'; + + final packageConfigFile = Uri.file(p.normalize(p.absolute(p.join( + '..', + 'fixtures', + '_testPackage', + '.dart_tool', + 'package_config.json', + )))); + + late final PackageUriMapper packageUriMapper; + setUpAll(() async { + packageUriMapper = await PackageUriMapper.create( + fileSystem, + packageConfigFile, + useDebuggerModuleNames: useDebuggerModuleNames, + ); + }); + + test('Can convert package urls to server paths', () { + expect(packageUriMapper.packageUriToServerPath(packageUri), serverPath); + }); + + test('Can convert server paths to file paths', () { + expect( + packageUriMapper.serverPathToResolvedUri(serverPath), + isA() + .having((uri) => uri.scheme, 'scheme', 'file') + .having((uri) => uri.path, 'path', endsWith(resolvedPath))); + }); + }); + } +} diff --git a/fixtures/_test/lib/deferred_library.dart b/fixtures/_test/lib/deferred_library.dart index e550ee822..bd0b1f152 100644 --- a/fixtures/_test/lib/deferred_library.dart +++ b/fixtures/_test/lib/deferred_library.dart @@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +// @dart=2.9 + /// A library that we can import. library test_deferred_library; diff --git a/fixtures/_test/lib/library.dart b/fixtures/_test/lib/library.dart index c16783b04..5d00634a9 100644 --- a/fixtures/_test/lib/library.dart +++ b/fixtures/_test/lib/library.dart @@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +// @dart=2.9 + /// A library that we can import. library test_library; diff --git a/fixtures/_testCircular1/lib/library1.dart b/fixtures/_testCircular1/lib/library1.dart new file mode 100644 index 000000000..f9f2637c2 --- /dev/null +++ b/fixtures/_testCircular1/lib/library1.dart @@ -0,0 +1,14 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// @dart=2.9 + +/// A library that we can import. +library _test_circular1; + +import 'package:_test_circular2/library2.dart'; + +String concatenate(String a, String b) { + return '$a$b'; // Breakpoint: Concatenate +} diff --git a/fixtures/_testCircular1/pubspec.yaml b/fixtures/_testCircular1/pubspec.yaml new file mode 100644 index 000000000..69e4d87cc --- /dev/null +++ b/fixtures/_testCircular1/pubspec.yaml @@ -0,0 +1,18 @@ +name: _test_circular1 +version: 1.0.0 +description: >- + A fake package used for testing +publish_to: none + +environment: + sdk: ">=2.5.0 <3.0.0" + +dependencies: + intl: ^0.16.0 + path: ^1.6.1 + _test_circular2: + path: ../_testCircular2 + +dev_dependencies: + build_runner: ^2.0.0 + build_web_compilers: ^3.0.0 diff --git a/fixtures/_testCircular1Sound/lib/library1.dart b/fixtures/_testCircular1Sound/lib/library1.dart new file mode 100644 index 000000000..568f82257 --- /dev/null +++ b/fixtures/_testCircular1Sound/lib/library1.dart @@ -0,0 +1,12 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// A library that we can import. +library _test_circular1_sound; + +import 'package:_test_circular2_sound/library2.dart'; + +String concatenate(String a, String b) { + return '$a$b'; // Breakpoint: Concatenate +} diff --git a/fixtures/_testCircular1Sound/pubspec.yaml b/fixtures/_testCircular1Sound/pubspec.yaml new file mode 100644 index 000000000..d5a7a9886 --- /dev/null +++ b/fixtures/_testCircular1Sound/pubspec.yaml @@ -0,0 +1,18 @@ +name: _test_circular1_sound +version: 1.0.0 +description: >- + A fake package used for testing +publish_to: none + +environment: + sdk: ">=2.5.0 <3.0.0" + +dependencies: + intl: ^0.16.0 + path: ^1.6.1 + _test_circular2_sound: + path: ../_testCircular2Sound + +dev_dependencies: + build_runner: ^2.0.0 + build_web_compilers: ^3.0.0 diff --git a/fixtures/_testCircular2/lib/library2.dart b/fixtures/_testCircular2/lib/library2.dart new file mode 100644 index 000000000..4179dbbec --- /dev/null +++ b/fixtures/_testCircular2/lib/library2.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// @dart=2.9 + +import 'package:_test_circular1/library1.dart'; + +void testCircularDependencies() { + print(concatenate('a', 'b')); // Breakpoint: testCircularDependencies +} diff --git a/fixtures/_testCircular2/pubspec.yaml b/fixtures/_testCircular2/pubspec.yaml new file mode 100644 index 000000000..44a59a923 --- /dev/null +++ b/fixtures/_testCircular2/pubspec.yaml @@ -0,0 +1,19 @@ +name: _test_circular2 +version: 1.0.0 +description: >- + A fake package used for testing imports. Imports _test. + This code is for testing that debugger works with weak null safety so we + should not migrate it to null safety. Once Dart 3.0 is released, we should + delete this directory. +publish_to: none + +environment: + sdk: ">=2.10.0 <3.0.0" + +dependencies: + _test_circular1: + path: ../_testCircular1 + +dev_dependencies: + build_runner: ^2.0.0 + build_web_compilers: ^3.0.0 diff --git a/fixtures/_testCircular2/web/base_index.html b/fixtures/_testCircular2/web/base_index.html new file mode 100644 index 000000000..affdbcb5c --- /dev/null +++ b/fixtures/_testCircular2/web/base_index.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/fixtures/_testCircular2/web/index.html b/fixtures/_testCircular2/web/index.html new file mode 100644 index 000000000..d93440a94 --- /dev/null +++ b/fixtures/_testCircular2/web/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/fixtures/_testCircular2/web/main.dart b/fixtures/_testCircular2/web/main.dart new file mode 100644 index 000000000..cf8ac1181 --- /dev/null +++ b/fixtures/_testCircular2/web/main.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// @dart=2.9 + +import 'dart:async'; +import 'dart:core'; +import 'dart:html'; + +import 'package:_test_circular1/library1.dart'; +import 'package:_test_circular2/library2.dart'; + +void main() { + // For setting breakpoints. + Timer.periodic(const Duration(seconds: 1), (_) { + testCircularDependencies(); + }); + + document.body.appendText(concatenate('Program', ' is running!')); +} diff --git a/fixtures/_testCircular2Sound/lib/library2.dart b/fixtures/_testCircular2Sound/lib/library2.dart new file mode 100644 index 000000000..9a31ffb79 --- /dev/null +++ b/fixtures/_testCircular2Sound/lib/library2.dart @@ -0,0 +1,9 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. 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:_test_circular1_sound/library1.dart'; + +void testCircularDependencies() { + print(concatenate('a', 'b')); // Breakpoint: testCircularDependencies +} diff --git a/fixtures/_testCircular2Sound/pubspec.yaml b/fixtures/_testCircular2Sound/pubspec.yaml new file mode 100644 index 000000000..090859c6c --- /dev/null +++ b/fixtures/_testCircular2Sound/pubspec.yaml @@ -0,0 +1,19 @@ +name: _test_circular2_sound +version: 1.0.0 +description: >- + A fake package used for testing imports. Imports _test. + This code is for testing that debugger works with weak null safety so we + should not migrate it to null safety. Once Dart 3.0 is released, we should + delete this directory. +publish_to: none + +environment: + sdk: ">=2.10.0 <3.0.0" + +dependencies: + _test_circular1_sound: + path: ../_testCircular1Sound + +dev_dependencies: + build_runner: ^2.0.0 + build_web_compilers: ^3.0.0 diff --git a/fixtures/_testCircular2Sound/web/base_index.html b/fixtures/_testCircular2Sound/web/base_index.html new file mode 100644 index 000000000..affdbcb5c --- /dev/null +++ b/fixtures/_testCircular2Sound/web/base_index.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/fixtures/_testCircular2Sound/web/index.html b/fixtures/_testCircular2Sound/web/index.html new file mode 100644 index 000000000..d93440a94 --- /dev/null +++ b/fixtures/_testCircular2Sound/web/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/fixtures/_testCircular2Sound/web/main.dart b/fixtures/_testCircular2Sound/web/main.dart new file mode 100644 index 000000000..d9b634a07 --- /dev/null +++ b/fixtures/_testCircular2Sound/web/main.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:async'; +import 'dart:core'; +import 'dart:html'; + +import 'package:_test_circular1_sound/library1.dart'; +import 'package:_test_circular2_sound/library2.dart'; + +void main() { + // For setting breakpoints. + Timer.periodic(const Duration(seconds: 1), (_) { + testCircularDependencies(); + }); + + document.body.appendText(concatenate('Program', ' is running!')); +} diff --git a/fixtures/_testPackage/lib/src/test_part.dart b/fixtures/_testPackage/lib/src/test_part.dart index adef00b29..55e99f025 100644 --- a/fixtures/_testPackage/lib/src/test_part.dart +++ b/fixtures/_testPackage/lib/src/test_part.dart @@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +// @dart=2.9 + part of '../test_library.dart'; int testPartLibraryValue = 4; @@ -15,7 +17,7 @@ class TestLibraryPartClass { final int _field; TestLibraryPartClass(this.field, this._field) { - print('Contructor'); // Breakpoint: testLibraryPartClassConstructor + print('Constructor'); // Breakpoint: testLibraryPartClassConstructor } @override diff --git a/fixtures/_testPackage/lib/test_library.dart b/fixtures/_testPackage/lib/test_library.dart index c1004cb05..ee8f3866c 100644 --- a/fixtures/_testPackage/lib/test_library.dart +++ b/fixtures/_testPackage/lib/test_library.dart @@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +// @dart=2.9 + part 'src/test_part.dart'; int testLibraryValue = 3; @@ -14,7 +16,7 @@ class TestLibraryClass { final int field; final int _field; TestLibraryClass(this.field, this._field) { - print('Contructor'); // Breakpoint: testLibraryClassConstructor + print('Constructor'); // Breakpoint: testLibraryClassConstructor } @override diff --git a/fixtures/_testPackageSound/lib/src/test_part.dart b/fixtures/_testPackageSound/lib/src/test_part.dart index adef00b29..5478db276 100644 --- a/fixtures/_testPackageSound/lib/src/test_part.dart +++ b/fixtures/_testPackageSound/lib/src/test_part.dart @@ -15,7 +15,7 @@ class TestLibraryPartClass { final int _field; TestLibraryPartClass(this.field, this._field) { - print('Contructor'); // Breakpoint: testLibraryPartClassConstructor + print('Constructor'); // Breakpoint: testLibraryPartClassConstructor } @override diff --git a/fixtures/_testPackageSound/lib/test_library.dart b/fixtures/_testPackageSound/lib/test_library.dart index 087f25067..ac9eb90fa 100644 --- a/fixtures/_testPackageSound/lib/test_library.dart +++ b/fixtures/_testPackageSound/lib/test_library.dart @@ -14,7 +14,7 @@ class TestLibraryClass { final int field; final int _field; TestLibraryClass(this.field, this._field) { - print('Contructor'); // Breakpoint: testLibraryClassConstructor + print('Constructor'); // Breakpoint: testLibraryClassConstructor } @override diff --git a/frontend_server_common/lib/src/asset_server.dart b/frontend_server_common/lib/src/asset_server.dart index 7b6753572..6e56e9478 100644 --- a/frontend_server_common/lib/src/asset_server.dart +++ b/frontend_server_common/lib/src/asset_server.dart @@ -13,14 +13,12 @@ import 'package:dwds/asset_reader.dart'; import 'package:file/file.dart'; import 'package:logging/logging.dart'; import 'package:mime/mime.dart' as mime; -import 'package:package_config/package_config.dart'; // ignore: deprecated_member_use -import 'package:path/path.dart' as p; import 'package:shelf/shelf.dart' as shelf; import 'utilities.dart'; class TestAssetServer implements AssetReader { - final String basePath; + late final String basePath; final String index; final _logger = Logger('TestAssetServer'); @@ -34,16 +32,18 @@ class TestAssetServer implements AssetReader { final Map _sourceMaps = {}; final Map _metadata = {}; late String _mergedMetadata; - final PackageConfig _packageConfig; + final PackageUriMapper _packageUriMapper; final InternetAddress internetAddress; TestAssetServer( this.index, this._httpServer, - this._packageConfig, + this._packageUriMapper, this.internetAddress, this._fileSystem, - ) : basePath = _parseBasePathFromIndexHtml(index); + ) { + basePath = _parseBasePathFromIndexHtml(index); + } bool hasFile(String path) => _files.containsKey(path); Uint8List getFile(String path) => _files[path]!; @@ -64,17 +64,27 @@ class TestAssetServer implements AssetReader { String hostname, int port, UrlEncoder? urlTunneler, - PackageConfig packageConfig, + PackageUriMapper packageUriMapper, ) async { var address = (await InternetAddress.lookup(hostname)).first; var httpServer = await HttpServer.bind(address, port); - var server = - TestAssetServer(index, httpServer, packageConfig, address, fileSystem); + var server = TestAssetServer( + index, httpServer, packageUriMapper, address, fileSystem); return server; } // handle requests for JavaScript source, dart sources maps, or asset files. Future handleRequest(shelf.Request request) async { + if (request.method != 'GET') { + // Assets are served via GET only. + return shelf.Response.notFound(''); + } + + final requestPath = _stripBasePath(request.url.path, basePath); + if (requestPath == null) { + return shelf.Response.notFound(''); + } + var headers = {}; if (request.url.path.endsWith('index.html')) { @@ -88,33 +98,31 @@ class TestAssetServer implements AssetReader { return shelf.Response.notFound(''); } - // NOTE: shelf removes leading `/` for some reason. - var requestPath = request.url.path; - requestPath = requestPath.startsWith('/') ? requestPath : '/$requestPath'; - - if (!request.url.path.endsWith('/require.js') && - !request.url.path.endsWith('/dart_stack_trace_mapper.js')) { - requestPath = _stripBasePath(requestPath, basePath) ?? requestPath; - - requestPath = requestPath.startsWith('/') ? requestPath : '/$requestPath'; - - // If this is a JavaScript file, it must be in the in-memory cache. - // Attempt to look up the file by URI. - if (hasFile(requestPath)) { - final List bytes = getFile(requestPath); - headers[HttpHeaders.contentLengthHeader] = bytes.length.toString(); - headers[HttpHeaders.contentTypeHeader] = 'application/javascript'; - return shelf.Response.ok(bytes, headers: headers); - } - // If this is a sourcemap file, then it might be in the in-memory cache. - // Attempt to lookup the file by URI. - if (hasSourceMap(requestPath)) { - final List bytes = getSourceMap(requestPath); - headers[HttpHeaders.contentLengthHeader] = bytes.length.toString(); - headers[HttpHeaders.contentTypeHeader] = 'application/json'; - return shelf.Response.ok(bytes, headers: headers); - } + // If this is a JavaScript file, it must be in the in-memory cache. + // Attempt to look up the file by URI. + if (hasFile(requestPath)) { + final List bytes = getFile(requestPath); + headers[HttpHeaders.contentLengthHeader] = bytes.length.toString(); + headers[HttpHeaders.contentTypeHeader] = 'application/javascript'; + return shelf.Response.ok(bytes, headers: headers); + } + // If this is a sourcemap file, then it might be in the in-memory cache. + // Attempt to lookup the file by URI. + if (hasSourceMap(requestPath)) { + final List bytes = getSourceMap(requestPath); + headers[HttpHeaders.contentLengthHeader] = bytes.length.toString(); + headers[HttpHeaders.contentTypeHeader] = 'application/json'; + return shelf.Response.ok(bytes, headers: headers); } + // If this is a metadata file, then it might be in the in-memory cache. + // Attempt to lookup the file by URI. + if (hasMetadata(requestPath)) { + final List bytes = getMetadata(requestPath); + headers[HttpHeaders.contentLengthHeader] = bytes.length.toString(); + headers[HttpHeaders.contentTypeHeader] = 'application/json'; + return shelf.Response.ok(bytes, headers: headers); + } + var file = _resolveDartFile(requestPath); if (!file.existsSync()) { return shelf.Response.notFound(''); @@ -183,7 +191,10 @@ class TestAssetServer implements AssetReader { codeStart, codeEnd - codeStart, ); - _files[filePath] = byteView; + + final fileName = + filePath.startsWith('/') ? filePath.substring(1) : filePath; + _files[fileName] = byteView; var sourcemapStart = sourcemapOffsets[0]; var sourcemapEnd = sourcemapOffsets[1]; @@ -196,7 +207,7 @@ class TestAssetServer implements AssetReader { sourcemapStart, sourcemapEnd - sourcemapStart, ); - _sourceMaps['$filePath.map'] = sourcemapView; + _sourceMaps['$fileName.map'] = sourcemapView; var metadataStart = metadataOffsets[0]; var metadataEnd = metadataOffsets[1]; @@ -209,9 +220,9 @@ class TestAssetServer implements AssetReader { metadataStart, metadataEnd - metadataStart, ); - _metadata['$filePath.metadata'] = metadataView; + _metadata['$fileName.metadata'] = metadataView; - modules.add(filePath); + modules.add(fileName); } _mergedMetadata = _metadata.values @@ -226,25 +237,23 @@ class TestAssetServer implements AssetReader { // If this is a dart file, it must be on the local file system and is // likely coming from a source map request. The tool doesn't currently // consider the case of Dart files as assets. - var dartFile = + final dartFile = _fileSystem.file(_fileSystem.currentDirectory.uri.resolve(path)); if (dartFile.existsSync()) { return dartFile; } - var segments = p.split(path); - if (segments.first.isEmpty) { - segments.removeAt(0); - } + final segments = path.split('/'); // The file might have been a package file which is signaled by a // `/packages//` request. if (segments.first == 'packages') { - var packageFile = _fileSystem.file(_packageConfig - .resolve(Uri(scheme: 'package', pathSegments: segments.skip(1)))); + final resolved = _packageUriMapper.serverPathToResolvedUri(path); + final packageFile = _fileSystem.file(resolved); if (packageFile.existsSync()) { return packageFile; } + _logger.severe('Package file not found: $path ($packageFile)'); } // Otherwise it must be a Dart SDK source. @@ -271,9 +280,8 @@ class TestAssetServer implements AssetReader { Future sourceMapContents(String serverPath) async { final stripped = _stripBasePath(serverPath, basePath); if (stripped != null) { - var path = '/$stripped'; - if (hasSourceMap(path)) { - return utf8.decode(getSourceMap(path)); + if (hasSourceMap(stripped)) { + return utf8.decode(getSourceMap(stripped)); } } _logger.severe('Source map not found: $serverPath'); @@ -287,14 +295,36 @@ class TestAssetServer implements AssetReader { if (stripped.endsWith('.ddc_merged_metadata')) { return _mergedMetadata; } - var path = '/$stripped'; - if (hasMetadata(path)) { - return utf8.decode(getMetadata(path)); + if (hasMetadata(stripped)) { + return utf8.decode(getMetadata(stripped)); } } _logger.severe('Metadata not found: $serverPath'); return null; } + + String _parseBasePathFromIndexHtml(String index) { + final file = _fileSystem.file(index); + if (!file.existsSync()) { + throw StateError('Index file $index is not found'); + } + final contents = file.readAsStringSync(); + final matches = RegExp(r'').allMatches(contents); + if (matches.isEmpty) return ''; + return matches.first.group(1) ?? ''; + } + + String? _stripBasePath(String path, String basePath) { + path = stripLeadingSlashes(path); + if (path.startsWith(basePath)) { + path = path.substring(basePath.length); + } else { + // The given path isn't under base path, return null to indicate that. + _logger.severe('Path is not under $basePath: $path'); + return null; + } + return stripLeadingSlashes(path); + } } /// Given a data structure which is a Map of String to dynamic values, return @@ -303,32 +333,3 @@ Map _castStringKeyedMap(dynamic untyped) { var map = untyped as Map; return map.cast(); } - -String _stripLeadingSlashes(String path) { - while (path.startsWith('/')) { - path = path.substring(1); - } - return path; -} - -String? _stripBasePath(String path, String basePath) { - path = _stripLeadingSlashes(path); - if (path.startsWith(basePath)) { - path = path.substring(basePath.length); - } else { - // The given path isn't under base path, return null to indicate that. - return null; - } - return _stripLeadingSlashes(path); -} - -String _parseBasePathFromIndexHtml(String index) { - final file = fileSystem.file(index); - if (!file.existsSync()) { - throw StateError('Index file $index is not found'); - } - final contents = file.readAsStringSync(); - final matches = RegExp(r'').allMatches(contents); - if (matches.isEmpty) return ''; - return matches.first.group(1) ?? ''; -} diff --git a/frontend_server_common/lib/src/devfs.dart b/frontend_server_common/lib/src/devfs.dart index d11e1f970..25cd5c5ac 100644 --- a/frontend_server_common/lib/src/devfs.dart +++ b/frontend_server_common/lib/src/devfs.dart @@ -4,11 +4,8 @@ // Note: this is a copy from flutter tools, updated to work with dwds tests -import 'dart:io'; - import 'package:dwds/asset_reader.dart'; import 'package:file/file.dart'; -import 'package:package_config/package_config.dart'; import 'package:path/path.dart' as p; import 'asset_server.dart'; @@ -24,7 +21,7 @@ class WebDevFS { required this.hostname, required this.port, required this.projectDirectory, - required this.packageConfigFile, + required this.packageUriMapper, required this.index, required this.soundNullSafety, this.urlTunneler, @@ -35,23 +32,19 @@ class WebDevFS { final String hostname; final int port; final Uri projectDirectory; - final Uri packageConfigFile; + final PackageUriMapper packageUriMapper; final String index; final UrlEncoder? urlTunneler; final bool soundNullSafety; late final Directory _savedCurrentDirectory; - late final PackageConfig _packageConfig; Future create() async { _savedCurrentDirectory = fileSystem.currentDirectory; fileSystem.currentDirectory = projectDirectory.toFilePath(); - _packageConfig = await loadPackageConfigUri(packageConfigFile, - loader: (Uri uri) => fileSystem.file(uri).readAsBytes()); - assetServer = await TestAssetServer.start( - fileSystem, index, hostname, port, urlTunneler, _packageConfig); + fileSystem, index, hostname, port, urlTunneler, packageUriMapper); return Uri.parse('http://$hostname:$port'); } @@ -71,33 +64,39 @@ class WebDevFS { final entryPoint = mainUri.toString(); assetServer.writeFile( - '/main.dart.js', + entryPoint, fileSystem.file(mainPath).readAsStringSync()); + assetServer.writeFile('require.js', requireJS.readAsStringSync()); + assetServer.writeFile( + 'stack_trace_mapper.js', stackTraceMapper.readAsStringSync()); + assetServer.writeFile( + 'main.dart.js', generateBootstrapScript( - requireUrl: _filePathToUriFragment(requireJS.path), - mapperUrl: _filePathToUriFragment(stackTraceMapper.path), + requireUrl: 'require.js', + mapperUrl: 'stack_trace_mapper.js', entrypoint: entryPoint, ), ); assetServer.writeFile( - '/main_module.bootstrap.js', + 'main_module.bootstrap.js', generateMainModule( entrypoint: entryPoint, ), ); - assetServer.writeFile('/main_module.digests', '{}'); + assetServer.writeFile('main_module.digests', '{}'); + var sdk = soundNullSafety ? dartSdkSound : dartSdk; var sdkSourceMap = soundNullSafety ? dartSdkSourcemapSound : dartSdkSourcemap; - assetServer.writeFile('/dart_sdk.js', sdk.readAsStringSync()); - assetServer.writeFile('/dart_sdk.js.map', sdkSourceMap.readAsStringSync()); + assetServer.writeFile('dart_sdk.js', sdk.readAsStringSync()); + assetServer.writeFile('dart_sdk.js.map', sdkSourceMap.readAsStringSync()); generator.reset(); var compilerOutput = await generator.recompile( Uri.parse('org-dartlang-app:///$mainUri'), invalidatedFiles, outputPath: p.join(dillOutputPath, 'app.dill'), - packageConfig: _packageConfig, + packageConfig: packageUriMapper.packageConfig, ); if (compilerOutput == null || compilerOutput.errorCount > 0) { return UpdateFSReport(success: false); @@ -195,16 +194,3 @@ class UpdateFSReport { /// Only used for JavaScript compilation. List? invalidatedModules; } - -String _filePathToUriFragment(String path) { - if (Platform.isWindows) { - var startWithSlash = path.startsWith('/'); - var partial = - fileSystem.path.split(path).skip(startWithSlash ? 2 : 1).join('/'); - if (partial.startsWith('/')) { - return partial; - } - return '/$partial'; - } - return path; -} diff --git a/frontend_server_common/lib/src/frontend_server_client.dart b/frontend_server_common/lib/src/frontend_server_client.dart index 5fd9db117..321c6ac14 100644 --- a/frontend_server_common/lib/src/frontend_server_client.dart +++ b/frontend_server_common/lib/src/frontend_server_client.dart @@ -255,6 +255,7 @@ class ResidentCompiler { this.sdkRoot, { required this.projectDirectory, required this.packageConfigFile, + required this.useDebuggerModuleNames, required this.fileSystemRoots, required this.fileSystemScheme, required this.platformDill, @@ -264,6 +265,7 @@ class ResidentCompiler { final Uri projectDirectory; final Uri packageConfigFile; + final bool useDebuggerModuleNames; final List fileSystemRoots; final String fileSystemScheme; final String platformDill; @@ -382,6 +384,7 @@ class ResidentCompiler { '--platform', platformDill, ], + if (useDebuggerModuleNames) '--debugger-module-names', '--experimental-emit-debug-metadata', if (verbose) '--verbose' ]; @@ -611,8 +614,9 @@ class TestExpressionCompiler implements ExpressionCompiler { line, column, jsModules, jsFrameValues, moduleName, expression); if (compilerOutput != null) { - var content = utf8.decode( - fileSystem.file(compilerOutput.outputFilename).readAsBytesSync()); + var content = utf8.decode(localFileSystem + .file(compilerOutput.outputFilename) + .readAsBytesSync()); return ExpressionCompilationResult( content, compilerOutput.errorCount > 0); } diff --git a/frontend_server_common/lib/src/resident_runner.dart b/frontend_server_common/lib/src/resident_runner.dart index 4bbeab8e9..16c793b8a 100644 --- a/frontend_server_common/lib/src/resident_runner.dart +++ b/frontend_server_common/lib/src/resident_runner.dart @@ -3,12 +3,13 @@ // found in the LICENSE file. // Note: this is a copy from flutter tools, updated to work with dwds tests, -// and some functionality remioved (does not support hot reload yet) +// and some functionality removed (does not support hot reload yet) import 'dart:async'; import 'package:dwds/asset_reader.dart'; import 'package:dwds/expression_compiler.dart'; +import 'package:file/file.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as p; @@ -29,6 +30,7 @@ class ResidentWebRunner { this.urlTunneler, this.projectDirectory, this.packageConfigFile, + this.packageUriMapper, this.fileSystemRoots, this.fileSystemScheme, this.outputPath, @@ -38,6 +40,7 @@ class ResidentWebRunner { dartSdkPath, projectDirectory: projectDirectory, packageConfigFile: packageConfigFile, + useDebuggerModuleNames: packageUriMapper.useDebuggerModuleNames, platformDill: soundNullSafety ? '$platformDillSound' : '$platformDillUnsound', fileSystemRoots: fileSystemRoots, @@ -51,6 +54,7 @@ class ResidentWebRunner { final Uri mainUri; final Uri projectDirectory; final Uri packageConfigFile; + final PackageUriMapper packageUriMapper; final String outputPath; final List fileSystemRoots; final String fileSystemScheme; @@ -62,13 +66,14 @@ class ResidentWebRunner { late Uri uri; late Iterable modules; - Future run(String? hostname, int port, String index) async { + Future run( + FileSystem fileSystem, String? hostname, int port, String index) async { devFS = WebDevFS( fileSystem: fileSystem, hostname: hostname ?? 'localhost', port: port, projectDirectory: projectDirectory, - packageConfigFile: packageConfigFile, + packageUriMapper: packageUriMapper, index: index, urlTunneler: urlTunneler, soundNullSafety: soundNullSafety, diff --git a/frontend_server_common/lib/src/utilities.dart b/frontend_server_common/lib/src/utilities.dart index 859ab8ad8..1c68b52e6 100644 --- a/frontend_server_common/lib/src/utilities.dart +++ b/frontend_server_common/lib/src/utilities.dart @@ -19,4 +19,4 @@ final String _sdkDir = (() { final String dartSdkPath = _sdkDir; -const fs.FileSystem fileSystem = LocalFileSystem(); +const fs.FileSystem localFileSystem = LocalFileSystem(); diff --git a/webdev/lib/src/pubspec.dart b/webdev/lib/src/pubspec.dart index dc67a400e..a5be96f83 100644 --- a/webdev/lib/src/pubspec.dart +++ b/webdev/lib/src/pubspec.dart @@ -163,7 +163,7 @@ Future> _validateBuildDaemonVersion( } final buildRunnerConstraint = VersionConstraint.parse('>=1.6.2 <3.0.0'); -final buildWebCompilersContraint = VersionConstraint.parse('>=2.12.0 <4.0.0'); +final buildWebCompilersConstraint = VersionConstraint.parse('>=2.12.0 <4.0.0'); // Note the minimum versions should never be dev versions as users will not // get them by default. @@ -178,7 +178,7 @@ Future checkPubspecLock(PubspecLock pubspecLock, if (requireBuildWebCompilers) { issues.addAll(pubspecLock.checkPackage( - 'build_web_compilers', buildWebCompilersContraint)); + 'build_web_compilers', buildWebCompilersConstraint)); } if (buildRunnerIssues.isEmpty) { diff --git a/webdev/test/e2e_test.dart b/webdev/test/e2e_test.dart index e568895a5..29a4b6b1e 100644 --- a/webdev/test/e2e_test.dart +++ b/webdev/test/e2e_test.dart @@ -8,6 +8,7 @@ import 'dart:io'; import 'package:io/io.dart'; +import 'package:logging/logging.dart'; import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart' as semver; import 'package:test/test.dart'; @@ -15,6 +16,7 @@ import 'package:test_descriptor/test_descriptor.dart' as d; import 'package:test_process/test_process.dart'; import 'package:vm_service/vm_service.dart'; import 'package:vm_service/vm_service_io.dart'; +import 'package:webdev/src/logging.dart'; import 'package:webdev/src/pubspec.dart'; import 'package:webdev/src/serve/utils.dart'; import 'package:webdev/src/util.dart'; @@ -34,9 +36,13 @@ const _testItems = { }; void main() { + // Change to true for debugging. + final debug = false; + String exampleDirectory; String soundExampleDirectory; setUpAll(() async { + configureLogWriter(debug); exampleDirectory = p.absolute(p.join(p.current, '..', 'fixtures', '_webdevSmoke')); soundExampleDirectory = @@ -70,10 +76,10 @@ void main() { expect(smokeYaml['dev_dependencies']['build_runner'], equals(buildRunnerConstraint.toString())); expect(smokeYaml['dev_dependencies']['build_web_compilers'], - equals(buildWebCompilersContraint.toString())); + equals(buildWebCompilersConstraint.toString())); }); - test('build should fail if targetting an existing directory', () async { + test('build should fail if targeting an existing directory', () async { await d.file('simple thing', 'throw-away').create(); var args = ['build', '-o', 'web:${d.sandbox}']; @@ -277,10 +283,20 @@ void main() { }); group('should work with ', () { + setUp(() async { + configureLogWriter(debug); + }); + for (var soundNullSafety in [false, true]) { var nullSafetyOption = soundNullSafety ? 'sound' : 'unsound'; group('--null-safety=$nullSafetyOption', () { + setUp(() async { + configureLogWriter(debug); + }); group('and --enable-expression-evaluation:', () { + setUp(() async { + configureLogWriter(debug); + }); test('evaluateInFrame', () async { var openPort = await findUnusedPort(); // running daemon command that starts dwds without keyboard input @@ -295,6 +311,9 @@ void main() { soundNullSafety ? soundExampleDirectory : exampleDirectory); VmService vmService; + process.stdoutStream().listen(Logger.root.fine); + process.stderrStream().listen(Logger.root.warning); + try { // Wait for debug service Uri String wsUri;