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, + ); + } + }); + }); + } +}