From 3354ea51eaa937940f08460b4168fc02c64b2f98 Mon Sep 17 00:00:00 2001 From: Srujan Gaddam <58529443+srujzs@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:35:47 -0700 Subject: [PATCH 1/2] Expand expression evaluation tests to also test DDC module format + canary (#2465) DDC hot reload requires a new module format. This is represented by the intersection of the DDC module format and canary. In order to make sure the new format is tested, this changes replicates any existing expression evaluation tests to use the DDC module format and canary. --- ...d_daemon_ddc_and_canary_evaluate_test.dart | 33 ++ .../expression_compiler_service_common.dart | 353 +++++++++++++++++ ..._compiler_service_ddc_and_canary_test.dart | 24 ++ .../expression_compiler_service_test.dart | 357 +----------------- ...d_server_ddc_and_canary_evaluate_test.dart | 53 +++ 5 files changed, 473 insertions(+), 347 deletions(-) create mode 100644 dwds/test/build_daemon_ddc_and_canary_evaluate_test.dart create mode 100644 dwds/test/expression_compiler_service_common.dart create mode 100644 dwds/test/expression_compiler_service_ddc_and_canary_test.dart create mode 100644 dwds/test/frontend_server_ddc_and_canary_evaluate_test.dart diff --git a/dwds/test/build_daemon_ddc_and_canary_evaluate_test.dart b/dwds/test/build_daemon_ddc_and_canary_evaluate_test.dart new file mode 100644 index 000000000..e9facab56 --- /dev/null +++ b/dwds/test/build_daemon_ddc_and_canary_evaluate_test.dart @@ -0,0 +1,33 @@ +// Copyright (c) 2024, 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. + +@Tags(['daily']) +@TestOn('vm') +@Timeout(Duration(minutes: 2)) +library; + +import 'package:dwds/expression_compiler.dart'; +import 'package:test/test.dart'; +import 'package:test_common/test_sdk_configuration.dart'; + +import 'evaluate_common.dart'; +import 'fixtures/context.dart'; + +void main() async { + // Enable verbose logging for debugging. + final debug = false; + + final provider = TestSdkConfigurationProvider( + verbose: debug, + ddcModuleFormat: ModuleFormat.ddc, + canaryFeatures: true, + ); + tearDownAll(provider.dispose); + + testAll( + provider: provider, + compilationMode: CompilationMode.buildDaemon, + debug: debug, + ); +} diff --git a/dwds/test/expression_compiler_service_common.dart b/dwds/test/expression_compiler_service_common.dart new file mode 100644 index 000000000..9320e6226 --- /dev/null +++ b/dwds/test/expression_compiler_service_common.dart @@ -0,0 +1,353 @@ +// Copyright (c) 2024, 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') +@Timeout(Duration(minutes: 2)) +library; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:dwds/sdk_configuration.dart'; +import 'package:dwds/src/services/expression_compiler.dart'; +import 'package:dwds/src/services/expression_compiler_service.dart'; +import 'package:dwds/src/utilities/server.dart'; +import 'package:logging/logging.dart'; +import 'package:shelf/shelf.dart'; +import 'package:test/test.dart'; +import 'package:test_common/logging.dart'; + +ExpressionCompilerService get service => _service!; +late ExpressionCompilerService? _service; + +HttpServer get server => _server!; +late HttpServer? _server; + +StreamController get output => _output!; +late StreamController? _output; + +void testAll({required CompilerOptions compilerOptions}) { + group('expression compiler service with fake asset server', () { + final logger = Logger('ExpressionCompilerServiceTest'); + late Directory outputDir; + + Future stop() async { + await _service?.stop(); + await _server?.close(); + await _output?.close(); + _service = null; + _server = null; + _output = null; + } + + setUp(() async { + final systemTempDir = Directory.systemTemp; + outputDir = systemTempDir.createTempSync('foo bar'); + final source = outputDir.uri.resolve('try.dart'); + final packages = outputDir.uri.resolve('package_config.json'); + final kernel = outputDir.uri.resolve('try.full.dill'); + // Expression compiler service does not need any extra assets + // generated in the SDK, so we use the current SDK layout and + // configuration. + final executable = Platform.resolvedExecutable; + final dartdevc = + SdkConfiguration.defaultConfiguration.compilerWorkerPath!; + // redirect logs for testing + _output = StreamController.broadcast(); + output.stream.listen(printOnFailure); + + configureLogWriter( + customLogWriter: (level, message, {error, loggerName, stackTrace}) { + final e = error == null ? '' : ': $error'; + final s = stackTrace == null ? '' : ':\n$stackTrace'; + output.add('[$level] $loggerName: $message$e$s'); + }, + ); + + // start asset server + _server = await startHttpServer('localhost'); + final port = server.port; + + // start expression compilation service + Response assetHandler(request) => + Response(200, body: File.fromUri(kernel).readAsBytesSync()); + _service = ExpressionCompilerService( + 'localhost', + port, + verbose: false, + sdkConfigurationProvider: DefaultSdkConfigurationProvider(), + ); + + await service.initialize(compilerOptions); + + // setup asset server + serveHttpRequests(server, assetHandler, (e, s) { + logger.warning('Error serving requests', e, s); + }); + + // generate full dill + File.fromUri(source) + ..createSync() + ..writeAsStringSync('''void main() { + // breakpoint line + }'''); + + File.fromUri(packages) + ..createSync() + ..writeAsStringSync(''' + { + "configVersion": 2, + "packages": [ + { + "name": "try", + "rootUri": "./", + "packageUri": "./" + } + ] + } + '''); + + final args = [ + dartdevc, + 'try.dart', + '-o', + 'try.js', + '--experimental-output-compiled-kernel', + '--multi-root', + '${outputDir.uri}', + '--multi-root-scheme', + 'org-dartlang-app', + '--packages', + packages.path, + ]; + final process = await Process.start( + executable, + args, + workingDirectory: outputDir.path, + ).then((p) { + transformToLines(p.stdout).listen(output.add); + transformToLines(p.stderr).listen(output.add); + return p; + }); + expect( + await process.exitCode, + 0, + reason: 'failed running $executable with args $args', + ); + expect( + File.fromUri(kernel).existsSync(), + true, + reason: 'failed to create full dill', + ); + }); + + tearDown(() async { + await stop(); + outputDir.deleteSync(recursive: true); + }); + + test('works with no errors', () async { + expect(output.stream, neverEmits(contains('[SEVERE]'))); + expect( + output.stream, + emitsThrough( + contains( + '[INFO] ExpressionCompilerService: Updating dependencies...', + ), + ), + ); + expect( + output.stream, + emitsThrough( + contains( + '[INFO] ExpressionCompilerService: Updated dependencies.', + ), + ), + ); + expect( + output.stream, + emitsThrough( + contains( + '[FINEST] ExpressionCompilerService: Compiling "true" at', + ), + ), + ); + expect( + output.stream, + emitsThrough( + contains( + '[FINEST] ExpressionCompilerService: Compiled "true" to:', + ), + ), + ); + expect( + output.stream, + emitsThrough(contains('[INFO] ExpressionCompilerService: Stopped.')), + ); + final result = await service + .updateDependencies({'try': ModuleInfo('try.full.dill', 'try.dill')}); + expect(result, true, reason: 'failed to update dependencies'); + + final compilationResult = await service.compileExpressionToJs( + '0', + 'org-dartlang-app:/try.dart', + 2, + 1, + {}, + {}, + 'try', + 'true', + ); + + expect( + compilationResult, + isA() + .having((r) => r.result, 'result', contains('return true;')) + .having((r) => r.isError, 'isError', false), + ); + + await stop(); + }); + + test('can evaluate multiple expressions', () async { + expect(output.stream, neverEmits(contains('[SEVERE]'))); + expect( + output.stream, + emitsThrough( + contains( + '[INFO] ExpressionCompilerService: Updating dependencies...', + ), + ), + ); + expect( + output.stream, + emitsThrough( + contains( + '[INFO] ExpressionCompilerService: Updated dependencies.', + ), + ), + ); + + expect( + output.stream, + emitsThrough(contains('[INFO] ExpressionCompilerService: Stopped.')), + ); + final result = await service + .updateDependencies({'try': ModuleInfo('try.full.dill', 'try.dill')}); + expect(result, true, reason: 'failed to update dependencies'); + + final compilationResult1 = await service.compileExpressionToJs( + '0', + 'org-dartlang-app:/try.dart', + 2, + 1, + {}, + {}, + 'try', + 'true', + ); + final compilationResult2 = await service.compileExpressionToJs( + '0', + 'org-dartlang-app:/try.dart', + 2, + 1, + {}, + {}, + 'try', + 'false', + ); + + expect( + compilationResult1, + isA() + .having((r) => r.result, 'result', contains('return true;')) + .having((r) => r.isError, 'isError', false), + ); + + expect( + compilationResult2, + isA() + .having((r) => r.result, 'result', contains('return false;')) + .having((r) => r.isError, 'isError', false), + ); + + await stop(); + }); + + test('can compile multiple expressions in parallel', () async { + expect(output.stream, neverEmits(contains('[SEVERE]'))); + expect( + output.stream, + emitsThrough( + contains( + '[INFO] ExpressionCompilerService: Updating dependencies...', + ), + ), + ); + expect( + output.stream, + emitsThrough( + contains( + '[INFO] ExpressionCompilerService: Updated dependencies.', + ), + ), + ); + + expect( + output.stream, + emitsThrough(contains('[INFO] ExpressionCompilerService: Stopped.')), + ); + final result = await service + .updateDependencies({'try': ModuleInfo('try.full.dill', 'try.dill')}); + expect(result, true, reason: 'failed to update dependencies'); + + final compilationResult1 = service.compileExpressionToJs( + '0', + 'org-dartlang-app:/try.dart', + 2, + 1, + {}, + {}, + 'try', + 'true', + ); + final compilationResult2 = service.compileExpressionToJs( + '0', + 'org-dartlang-app:/try.dart', + 2, + 1, + {}, + {}, + 'try', + 'false', + ); + + final results = + await Future.wait([compilationResult1, compilationResult2]); + + expect( + results[0], + isA() + .having((r) => r.result, 'result', contains('return true;')) + .having((r) => r.isError, 'isError', false), + ); + + expect( + results[1], + isA() + .having((r) => r.result, 'result', contains('return false;')) + .having((r) => r.isError, 'isError', false), + ); + + await stop(); + }); + }); +} + +Stream transformToLines(Stream> byteStream) { + return byteStream + .transform(utf8.decoder) + .transform(const LineSplitter()); +} diff --git a/dwds/test/expression_compiler_service_ddc_and_canary_test.dart b/dwds/test/expression_compiler_service_ddc_and_canary_test.dart new file mode 100644 index 000000000..658d8aa67 --- /dev/null +++ b/dwds/test/expression_compiler_service_ddc_and_canary_test.dart @@ -0,0 +1,24 @@ +// Copyright (c) 2024, 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. + +@Tags(['daily']) +@TestOn('vm') +@Timeout(Duration(minutes: 2)) +library; + +import 'package:dwds/expression_compiler.dart'; +import 'package:test/test.dart'; + +import 'expression_compiler_service_common.dart'; + +void main() async { + testAll( + compilerOptions: CompilerOptions( + moduleFormat: ModuleFormat.ddc, + soundNullSafety: true, + canaryFeatures: true, + experiments: const [], + ), + ); +} diff --git a/dwds/test/expression_compiler_service_test.dart b/dwds/test/expression_compiler_service_test.dart index 183813a9f..5f59de857 100644 --- a/dwds/test/expression_compiler_service_test.dart +++ b/dwds/test/expression_compiler_service_test.dart @@ -7,355 +7,18 @@ @Timeout(Duration(minutes: 2)) library; -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:dwds/sdk_configuration.dart'; -import 'package:dwds/src/services/expression_compiler.dart'; -import 'package:dwds/src/services/expression_compiler_service.dart'; -import 'package:dwds/src/utilities/server.dart'; -import 'package:logging/logging.dart'; -import 'package:shelf/shelf.dart'; +import 'package:dwds/expression_compiler.dart'; import 'package:test/test.dart'; -import 'package:test_common/logging.dart'; - -ExpressionCompilerService get service => _service!; -late ExpressionCompilerService? _service; - -HttpServer get server => _server!; -late HttpServer? _server; -StreamController get output => _output!; -late StreamController? _output; +import 'expression_compiler_service_common.dart'; void main() async { - group('expression compiler service with fake asset server', () { - final logger = Logger('ExpressionCompilerServiceTest'); - late Directory outputDir; - - Future stop() async { - await _service?.stop(); - await _server?.close(); - await _output?.close(); - _service = null; - _server = null; - _output = null; - } - - setUp(() async { - final systemTempDir = Directory.systemTemp; - outputDir = systemTempDir.createTempSync('foo bar'); - final source = outputDir.uri.resolve('try.dart'); - final packages = outputDir.uri.resolve('package_config.json'); - final kernel = outputDir.uri.resolve('try.full.dill'); - // Expression compiler service does not need any extra assets - // generated in the SDK, so we use the current SDK layout and - // configuration. - final executable = Platform.resolvedExecutable; - final dartdevc = - SdkConfiguration.defaultConfiguration.compilerWorkerPath!; - // redirect logs for testing - _output = StreamController.broadcast(); - output.stream.listen(printOnFailure); - - configureLogWriter( - customLogWriter: (level, message, {error, loggerName, stackTrace}) { - final e = error == null ? '' : ': $error'; - final s = stackTrace == null ? '' : ':\n$stackTrace'; - output.add('[$level] $loggerName: $message$e$s'); - }, - ); - - // start asset server - _server = await startHttpServer('localhost'); - final port = server.port; - - // start expression compilation service - Response assetHandler(request) => - Response(200, body: File.fromUri(kernel).readAsBytesSync()); - _service = ExpressionCompilerService( - 'localhost', - port, - verbose: false, - sdkConfigurationProvider: DefaultSdkConfigurationProvider(), - ); - - final compilerOptions = CompilerOptions( - moduleFormat: ModuleFormat.amd, - soundNullSafety: true, - canaryFeatures: false, - experiments: const [], - ); - - await service.initialize(compilerOptions); - - // setup asset server - serveHttpRequests(server, assetHandler, (e, s) { - logger.warning('Error serving requests', e, s); - }); - - // generate full dill - File.fromUri(source) - ..createSync() - ..writeAsStringSync('''void main() { - // breakpoint line - }'''); - - File.fromUri(packages) - ..createSync() - ..writeAsStringSync(''' - { - "configVersion": 2, - "packages": [ - { - "name": "try", - "rootUri": "./", - "packageUri": "./" - } - ] - } - '''); - - final args = [ - dartdevc, - 'try.dart', - '-o', - 'try.js', - '--experimental-output-compiled-kernel', - '--multi-root', - '${outputDir.uri}', - '--multi-root-scheme', - 'org-dartlang-app', - '--packages', - packages.path, - ]; - final process = await Process.start( - executable, - args, - workingDirectory: outputDir.path, - ).then((p) { - transformToLines(p.stdout).listen(output.add); - transformToLines(p.stderr).listen(output.add); - return p; - }); - expect( - await process.exitCode, - 0, - reason: 'failed running $executable with args $args', - ); - expect( - File.fromUri(kernel).existsSync(), - true, - reason: 'failed to create full dill', - ); - }); - - tearDown(() async { - await stop(); - outputDir.deleteSync(recursive: true); - }); - - test('works with no errors', () async { - expect(output.stream, neverEmits(contains('[SEVERE]'))); - expect( - output.stream, - emitsThrough( - contains( - '[INFO] ExpressionCompilerService: Updating dependencies...', - ), - ), - ); - expect( - output.stream, - emitsThrough( - contains( - '[INFO] ExpressionCompilerService: Updated dependencies.', - ), - ), - ); - expect( - output.stream, - emitsThrough( - contains( - '[FINEST] ExpressionCompilerService: Compiling "true" at', - ), - ), - ); - expect( - output.stream, - emitsThrough( - contains( - '[FINEST] ExpressionCompilerService: Compiled "true" to:', - ), - ), - ); - expect( - output.stream, - emitsThrough(contains('[INFO] ExpressionCompilerService: Stopped.')), - ); - final result = await service - .updateDependencies({'try': ModuleInfo('try.full.dill', 'try.dill')}); - expect(result, true, reason: 'failed to update dependencies'); - - final compilationResult = await service.compileExpressionToJs( - '0', - 'org-dartlang-app:/try.dart', - 2, - 1, - {}, - {}, - 'try', - 'true', - ); - - expect( - compilationResult, - isA() - .having((r) => r.result, 'result', contains('return true;')) - .having((r) => r.isError, 'isError', false), - ); - - await stop(); - }); - - test('can evaluate multiple expressions', () async { - expect(output.stream, neverEmits(contains('[SEVERE]'))); - expect( - output.stream, - emitsThrough( - contains( - '[INFO] ExpressionCompilerService: Updating dependencies...', - ), - ), - ); - expect( - output.stream, - emitsThrough( - contains( - '[INFO] ExpressionCompilerService: Updated dependencies.', - ), - ), - ); - - expect( - output.stream, - emitsThrough(contains('[INFO] ExpressionCompilerService: Stopped.')), - ); - final result = await service - .updateDependencies({'try': ModuleInfo('try.full.dill', 'try.dill')}); - expect(result, true, reason: 'failed to update dependencies'); - - final compilationResult1 = await service.compileExpressionToJs( - '0', - 'org-dartlang-app:/try.dart', - 2, - 1, - {}, - {}, - 'try', - 'true', - ); - final compilationResult2 = await service.compileExpressionToJs( - '0', - 'org-dartlang-app:/try.dart', - 2, - 1, - {}, - {}, - 'try', - 'false', - ); - - expect( - compilationResult1, - isA() - .having((r) => r.result, 'result', contains('return true;')) - .having((r) => r.isError, 'isError', false), - ); - - expect( - compilationResult2, - isA() - .having((r) => r.result, 'result', contains('return false;')) - .having((r) => r.isError, 'isError', false), - ); - - await stop(); - }); - - test('can compile multiple expressions in parallel', () async { - expect(output.stream, neverEmits(contains('[SEVERE]'))); - expect( - output.stream, - emitsThrough( - contains( - '[INFO] ExpressionCompilerService: Updating dependencies...', - ), - ), - ); - expect( - output.stream, - emitsThrough( - contains( - '[INFO] ExpressionCompilerService: Updated dependencies.', - ), - ), - ); - - expect( - output.stream, - emitsThrough(contains('[INFO] ExpressionCompilerService: Stopped.')), - ); - final result = await service - .updateDependencies({'try': ModuleInfo('try.full.dill', 'try.dill')}); - expect(result, true, reason: 'failed to update dependencies'); - - final compilationResult1 = service.compileExpressionToJs( - '0', - 'org-dartlang-app:/try.dart', - 2, - 1, - {}, - {}, - 'try', - 'true', - ); - final compilationResult2 = service.compileExpressionToJs( - '0', - 'org-dartlang-app:/try.dart', - 2, - 1, - {}, - {}, - 'try', - 'false', - ); - - final results = - await Future.wait([compilationResult1, compilationResult2]); - - expect( - results[0], - isA() - .having((r) => r.result, 'result', contains('return true;')) - .having((r) => r.isError, 'isError', false), - ); - - expect( - results[1], - isA() - .having((r) => r.result, 'result', contains('return false;')) - .having((r) => r.isError, 'isError', false), - ); - - await stop(); - }); - }); -} - -Stream transformToLines(Stream> byteStream) { - return byteStream - .transform(utf8.decoder) - .transform(const LineSplitter()); + testAll( + compilerOptions: CompilerOptions( + moduleFormat: ModuleFormat.amd, + soundNullSafety: true, + canaryFeatures: false, + experiments: const [], + ), + ); } diff --git a/dwds/test/frontend_server_ddc_and_canary_evaluate_test.dart b/dwds/test/frontend_server_ddc_and_canary_evaluate_test.dart new file mode 100644 index 000000000..e865e8248 --- /dev/null +++ b/dwds/test/frontend_server_ddc_and_canary_evaluate_test.dart @@ -0,0 +1,53 @@ +// Copyright (c) 2024, 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. + +@Tags(['daily']) +@TestOn('vm') +@Timeout(Duration(minutes: 5)) +library; + +import 'dart:io'; + +import 'package:dwds/expression_compiler.dart'; +import 'package:test/test.dart'; +import 'package:test_common/test_sdk_configuration.dart'; + +import 'evaluate_common.dart'; +import 'fixtures/context.dart'; +import 'fixtures/project.dart'; + +void main() async { + // Enable verbose logging for debugging. + final debug = false; + + final provider = TestSdkConfigurationProvider( + verbose: debug, + ddcModuleFormat: ModuleFormat.ddc, + canaryFeatures: true, + ); + tearDownAll(provider.dispose); + + for (var useDebuggerModuleNames in [false, true]) { + group('Debugger module names: $useDebuggerModuleNames |', () { + group('DDC module system and canary |', () { + for (var indexBaseMode in IndexBaseMode.values) { + group( + 'with ${indexBaseMode.name} |', + () { + testAll( + provider: provider, + compilationMode: CompilationMode.frontendServer, + indexBaseMode: indexBaseMode, + useDebuggerModuleNames: useDebuggerModuleNames, + debug: debug, + ); + }, + // https://github.com/dart-lang/sdk/issues/49277 + skip: indexBaseMode == IndexBaseMode.base && Platform.isWindows, + ); + } + }); + }); + } +} From f0ae4639fa51b7908b86f06381db5e827be0b6bf Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 1 Aug 2024 16:07:24 -0400 Subject: [PATCH 2/2] Disable `variable scope variables in function` in `variable_scope_test.dart` (#2470) See https://github.com/dart-lang/webdev/issues/2469 --- dwds/test/variable_scope_test.dart | 44 ++++++++++++++++-------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/dwds/test/variable_scope_test.dart b/dwds/test/variable_scope_test.dart index 1567f7188..bb0f18763 100644 --- a/dwds/test/variable_scope_test.dart +++ b/dwds/test/variable_scope_test.dart @@ -187,26 +187,30 @@ void main() { expect(variableNames, containsAll(['formal'])); }); - test('variables in function', () async { - stack = await breakAt('nestedFunction', mainScript); - final variables = getFrameVariables(stack.frames!.first); - await expectDartVariables(variables); - - final variableNames = variables.keys.toList()..sort(); - expect( - variableNames, - containsAll([ - 'aClass', - 'another', - 'intLocalInMain', - 'local', - 'localThatsNull', - 'nestedFunction', - 'parameter', - 'testClass', - ]), - ); - }); + test( + 'variables in function', + () async { + stack = await breakAt('nestedFunction', mainScript); + final variables = getFrameVariables(stack.frames!.first); + await expectDartVariables(variables); + + final variableNames = variables.keys.toList()..sort(); + expect( + variableNames, + containsAll([ + 'aClass', + 'another', + 'intLocalInMain', + 'local', + 'localThatsNull', + 'nestedFunction', + 'parameter', + 'testClass', + ]), + ); + }, + skip: 'See https://github.com/dart-lang/webdev/issues/2469', + ); test('variables in closure nested in method', () async { stack = await breakAt('nestedClosure', mainScript);