| // Copyright (c) 2020, 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)) |
| import 'dart:async'; |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:dwds/expression_compiler.dart'; |
| import 'package:dwds/sdk_configuration.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<String> get output => _output!; |
| late StreamController<String>? _output; |
| |
| void main() async { |
| group('expression compiler service with fake asset server', () { |
| final logger = Logger('ExpressionCompilerServiceTest'); |
| late Directory outputDir; |
| |
| Future<void> 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<String>.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(moduleFormat: 'amd', soundNullSafety: true); |
| |
| // 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<ExpressionCompilationResult>() |
| .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<ExpressionCompilationResult>() |
| .having((r) => r.result, 'result', contains('return true;')) |
| .having((r) => r.isError, 'isError', false), |
| ); |
| |
| expect( |
| compilationResult2, |
| isA<ExpressionCompilationResult>() |
| .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<ExpressionCompilationResult>() |
| .having((r) => r.result, 'result', contains('return true;')) |
| .having((r) => r.isError, 'isError', false), |
| ); |
| |
| expect( |
| results[1], |
| isA<ExpressionCompilationResult>() |
| .having((r) => r.result, 'result', contains('return false;')) |
| .having((r) => r.isError, 'isError', false), |
| ); |
| |
| await stop(); |
| }); |
| }); |
| } |
| |
| Stream<String> transformToLines(Stream<List<int>> byteStream) { |
| return byteStream |
| .transform<String>(utf8.decoder) |
| .transform<String>(const LineSplitter()); |
| } |