| // Copyright 2020 The Dart Authors. 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:convert'; | 
 | import 'dart:io'; | 
 |  | 
 | import 'package:async/async.dart'; | 
 | import 'package:frontend_server_client/frontend_server_client.dart'; | 
 | import 'package:package_config/package_config.dart'; | 
 | import 'package:path/path.dart' as p; | 
 | import 'package:test/test.dart'; | 
 | import 'package:test_descriptor/test_descriptor.dart' as d; | 
 | import 'package:vm_service/vm_service.dart'; | 
 | import 'package:vm_service/vm_service_io.dart'; | 
 |  | 
 | void main() async { | 
 |   late FrontendServerClient client; | 
 |   late PackageConfig packageConfig; | 
 |   late String packageRoot; | 
 |  | 
 |   setUp(() async { | 
 |     await d.dir('a', [ | 
 |       d.file('pubspec.yaml', ''' | 
 | name: a | 
 | dependencies: | 
 |   path: ^1.0.0 | 
 |  | 
 | environment: | 
 |   sdk: '>=2.12.0 <3.0.0' | 
 |       '''), | 
 |       d.dir('bin', [ | 
 |         d.file('main.dart', ''' | 
 | import 'package:path/path.dart' as p; | 
 |  | 
 | void main() async { | 
 |   print(message); | 
 |   /// Runs in a loop until it is hot reloaded with a new message. | 
 |   while (!message.contains('goodbye')) { | 
 |     await Future.delayed(const Duration(seconds: 1)); | 
 |   } | 
 |   print(message); | 
 | } | 
 |  | 
 | String get message => p.join('hello', 'world'); | 
 |  | 
 | ''') | 
 |       ]), | 
 |     ]).create(); | 
 |     packageRoot = p.join(d.sandbox, 'a'); | 
 |     await Process.run(Platform.resolvedExecutable, ['pub', 'get'], | 
 |         workingDirectory: packageRoot); | 
 |     packageConfig = (await findPackageConfig(Directory(packageRoot)))!; | 
 |   }); | 
 |  | 
 |   tearDown(() async { | 
 |     await client.shutdown(); | 
 |   }); | 
 |  | 
 |   test('can compile, recompile, and hot reload a vm app', () async { | 
 |     var entrypoint = p.join(packageRoot, 'bin', 'main.dart'); | 
 |     client = await FrontendServerClient.start( | 
 |         entrypoint, p.join(packageRoot, 'out.dill'), vmPlatformDill); | 
 |     var result = await client.compile(); | 
 |     client.accept(); | 
 |     expect(result.compilerOutputLines, isEmpty); | 
 |     expect(result.errorCount, 0); | 
 |     expect( | 
 |         result.newSources, | 
 |         containsAll([ | 
 |           File(entrypoint).uri, | 
 |           packageConfig.resolve(Uri.parse('package:path/path.dart')), | 
 |         ])); | 
 |     expect(result.removedSources, isEmpty); | 
 |     expect(result.dillOutput, isNotNull); | 
 |     expect(File(result.dillOutput!).existsSync(), true); | 
 |     var process = await Process.start(Platform.resolvedExecutable, [ | 
 |       '--observe', | 
 |       '--no-pause-isolates-on-exit', | 
 |       '--pause-isolates-on-start', | 
 |       result.dillOutput! | 
 |     ]); | 
 |     addTearDown(process.kill); | 
 |     var stdoutLines = StreamQueue( | 
 |         process.stdout.transform(utf8.decoder).transform(const LineSplitter())); | 
 |  | 
 |     var observatoryLine = await stdoutLines.next; | 
 |     var observatoryUri = | 
 |         '${observatoryLine.split(' ').last.replaceFirst('http', 'ws')}ws'; | 
 |     var vmService = await vmServiceConnectUri(observatoryUri); | 
 |     var isolate = await waitForIsolatesAndResume(vmService); | 
 |  | 
 |     await expectLater(stdoutLines, emitsThrough(p.join('hello', 'world'))); | 
 |  | 
 |     var appFile = File(entrypoint); | 
 |     var originalContent = await appFile.readAsString(); | 
 |     var newContent = originalContent.replaceFirst('hello', 'goodbye'); | 
 |     await appFile.writeAsString(newContent); | 
 |  | 
 |     result = await client.compile([File(entrypoint).uri]); | 
 |  | 
 |     client.accept(); | 
 |     expect(result.newSources, isEmpty); | 
 |     expect(result.removedSources, isEmpty); | 
 |     expect(result.compilerOutputLines, isEmpty); | 
 |     expect(result.errorCount, 0); | 
 |     expect(result.dillOutput, endsWith('.incremental.dill')); | 
 |  | 
 |     await vmService.reloadSources(isolate.id!, rootLibUri: result.dillOutput); | 
 |  | 
 |     expect(await stdoutLines.next, p.join('goodbye', 'world')); | 
 |     expect(await process.exitCode, 0); | 
 |   }); | 
 |  | 
 |   test('can handle compile errors and reload fixes', () async { | 
 |     var entrypoint = p.join(packageRoot, 'bin', 'main.dart'); | 
 |     var entrypointFile = File(entrypoint); | 
 |     var originalContent = await entrypointFile.readAsString(); | 
 |     // append two compile errors to the bottom | 
 |     await entrypointFile | 
 |         .writeAsString('$originalContent\nint foo = 1.0;\nString bar = 4;'); | 
 |  | 
 |     client = await FrontendServerClient.start( | 
 |         entrypoint, p.join(packageRoot, 'out.dill'), vmPlatformDill); | 
 |     var result = await client.compile(); | 
 |  | 
 |     client.accept(); | 
 |     expect(result.errorCount, 2); | 
 |     expect(result.compilerOutputLines, | 
 |         allOf(contains('int foo = 1.0;'), contains('String bar = 4;'))); | 
 |     expect( | 
 |         result.newSources, | 
 |         containsAll([ | 
 |           File(entrypoint).uri, | 
 |           packageConfig.resolve(Uri.parse('package:path/path.dart')), | 
 |         ])); | 
 |     expect(result.removedSources, isEmpty); | 
 |     expect(result.dillOutput, isNotNull); | 
 |     expect(File(result.dillOutput!).existsSync(), true); | 
 |  | 
 |     var process = await Process.start(Platform.resolvedExecutable, [ | 
 |       '--observe', | 
 |       '--no-pause-isolates-on-exit', | 
 |       '--pause-isolates-on-start', | 
 |       result.dillOutput! | 
 |     ]); | 
 |     addTearDown(process.kill); | 
 |     var stdoutLines = StreamQueue( | 
 |         process.stdout.transform(utf8.decoder).transform(const LineSplitter())); | 
 |  | 
 |     var observatoryLine = await stdoutLines.next; | 
 |     var observatoryUri = | 
 |         '${observatoryLine.split(' ').last.replaceFirst('http', 'ws')}ws'; | 
 |     var vmService = await vmServiceConnectUri(observatoryUri); | 
 |     var isolate = await waitForIsolatesAndResume(vmService); | 
 |  | 
 |     // The program actually runs regardless of the errors, as they don't affect | 
 |     // the runtime behavior. | 
 |     await expectLater(stdoutLines, emitsThrough(p.join('hello', 'world'))); | 
 |  | 
 |     await entrypointFile | 
 |         .writeAsString(originalContent.replaceFirst('hello', 'goodbye')); | 
 |     result = await client.compile([entrypointFile.uri]); | 
 |     client.accept(); | 
 |     expect(result.errorCount, 0); | 
 |     expect(result.compilerOutputLines, isEmpty); | 
 |     expect(result.newSources, isEmpty); | 
 |     expect(result.removedSources, isEmpty); | 
 |     expect(result.dillOutput, isNotNull); | 
 |     expect(File(result.dillOutput!).existsSync(), true); | 
 |  | 
 |     await vmService.reloadSources(isolate.id!, rootLibUri: result.dillOutput); | 
 |  | 
 |     expect(await stdoutLines.next, p.join('goodbye', 'world')); | 
 |     expect(await process.exitCode, 0); | 
 |   }); | 
 |  | 
 |   test('can compile and recompile a dartdevc app', () async { | 
 |     var entrypoint = | 
 |         p.toUri(p.join(packageRoot, 'bin', 'main.dart')).toString(); | 
 |     var dartDevcClient = client = await DartDevcFrontendServerClient.start( | 
 |         entrypoint, p.join(packageRoot, 'out.dill'), | 
 |         platformKernel: p | 
 |             .toUri( | 
 |                 p.join(sdkDir, 'lib', '_internal', 'ddc_platform_sound.dill')) | 
 |             .toString()); | 
 |     var result = await client.compile(); | 
 |     client.accept(); | 
 |  | 
 |     expect(result.compilerOutputLines, isEmpty); | 
 |     expect(result.errorCount, 0); | 
 |     expect( | 
 |         result.newSources, | 
 |         containsAll([ | 
 |           Uri.parse(entrypoint), | 
 |           packageConfig.resolve(Uri.parse('package:path/path.dart')), | 
 |         ])); | 
 |     expect(result.removedSources, isEmpty); | 
 |  | 
 |     expect(result.dillOutput, isNotNull); | 
 |     expect(File(result.jsManifestOutput!).existsSync(), true); | 
 |     expect(File(result.jsSourcesOutput!).existsSync(), true); | 
 |     expect(File(result.jsSourceMapsOutput!).existsSync(), true); | 
 |  | 
 |     var entrypointUri = Uri.parse(entrypoint); | 
 |     expect( | 
 |         utf8.decode(dartDevcClient.assetBytes('${entrypointUri.path}.lib.js')!), | 
 |         contains('hello')); | 
 |  | 
 |     var appFile = File(entrypointUri.toFilePath()); | 
 |     var originalContent = await appFile.readAsString(); | 
 |     var newContent = originalContent.replaceFirst('hello', 'goodbye'); | 
 |     await appFile.writeAsString(newContent); | 
 |  | 
 |     result = await client.compile([entrypointUri]); | 
 |     client.accept(); | 
 |     expect(result.newSources, isEmpty); | 
 |     expect(result.removedSources, isEmpty); | 
 |     expect(result.compilerOutputLines, isEmpty); | 
 |     expect(result.errorCount, 0); | 
 |     expect(result.jsManifestOutput, endsWith('.incremental.dill.json')); | 
 |  | 
 |     expect( | 
 |         utf8.decode(dartDevcClient.assetBytes('${entrypointUri.path}.lib.js')!), | 
 |         contains('goodbye')); | 
 |   }); | 
 |  | 
 |   test('can enable experiments', () async { | 
 |     await d.dir('a', [ | 
 |       d.dir('bin', [ | 
 |         d.file('nnbd.dart', ''' | 
 | // @dart=2.10 | 
 |  | 
 | // Compile time error if nnbd is enabled | 
 | int x; | 
 |  | 
 | void main() { | 
 |   print(x); | 
 | } | 
 | ''') | 
 |       ]) | 
 |     ]).create(); | 
 |     var entrypoint = p.join(packageRoot, 'bin', 'nnbd.dart'); | 
 |     client = await FrontendServerClient.start( | 
 |         entrypoint, p.join(packageRoot, 'out.dill'), vmPlatformDill, | 
 |         enabledExperiments: ['non-nullable']); | 
 |     var result = await client.compile(); | 
 |     client.accept(); | 
 |     expect(result.errorCount, 1); | 
 |     expect(result.compilerOutputLines, contains(contains('int x;'))); | 
 |   }); | 
 |  | 
 |   test('can compile and recompile filenames with spaces', () async { | 
 |     await d.dir('a', [ | 
 |       d.dir('bin', [ | 
 |         d.file('main with spaces.dart', ''' | 
 | void main() { | 
 |   print('hello world'); | 
 | } | 
 | ''') | 
 |       ]), | 
 |     ]).create(); | 
 |  | 
 |     var entrypoint = p.join(packageRoot, 'bin', 'main with spaces.dart'); | 
 |     client = await FrontendServerClient.start(entrypoint, | 
 |         p.join(packageRoot, 'out with spaces.dill'), vmPlatformDill); | 
 |     var result = await client.compile(); | 
 |     client.accept(); | 
 |     expect(result.compilerOutputLines, isEmpty); | 
 |     expect(result.errorCount, 0); | 
 |     expect( | 
 |         result.newSources, | 
 |         containsAll([ | 
 |           File(entrypoint).uri, | 
 |         ])); | 
 |     expect(result.removedSources, isEmpty); | 
 |     expect(result.dillOutput, isNotNull); | 
 |     expect(File(result.dillOutput!).existsSync(), true); | 
 |     var processResult = | 
 |         await Process.run(Platform.resolvedExecutable, [result.dillOutput!]); | 
 |  | 
 |     expect(processResult.stdout, startsWith('hello world')); | 
 |     expect(processResult.exitCode, 0); | 
 |  | 
 |     var appFile = File(entrypoint); | 
 |     var originalContent = await appFile.readAsString(); | 
 |     var newContent = originalContent.replaceFirst('hello', 'goodbye'); | 
 |     await appFile.writeAsString(newContent); | 
 |     result = await client.compile([appFile.uri]); | 
 |     expect(result.compilerOutputLines, isEmpty); | 
 |     expect(result.errorCount, 0); | 
 |     expect(result.newSources, isEmpty); | 
 |     expect(result.removedSources, isEmpty); | 
 |  | 
 |     processResult = | 
 |         await Process.run(Platform.resolvedExecutable, [result.dillOutput!]); | 
 |     expect(processResult.stdout, startsWith('goodbye world')); | 
 |     expect(processResult.exitCode, 0); | 
 |   }); | 
 | } | 
 |  | 
 | Future<Isolate> waitForIsolatesAndResume(VmService vmService) async { | 
 |   var vm = await vmService.getVM(); | 
 |   var isolates = vm.isolates; | 
 |   while (isolates == null || isolates.isEmpty) { | 
 |     await Future.delayed(const Duration(milliseconds: 100)); | 
 |     vm = await vmService.getVM(); | 
 |     isolates = vm.isolates; | 
 |   } | 
 |   var isolateRef = isolates.first; | 
 |   var isolate = await vmService.getIsolate(isolateRef.id!); | 
 |   while (isolate.pauseEvent?.kind != EventKind.kPauseStart) { | 
 |     await Future.delayed(const Duration(milliseconds: 100)); | 
 |     isolate = await vmService.getIsolate(isolateRef.id!); | 
 |   } | 
 |   await vmService.resume(isolate.id!); | 
 |   return isolate; | 
 | } | 
 |  | 
 | final vmPlatformDill = p | 
 |     .toUri(p.join(sdkDir, 'lib', '_internal', 'vm_platform_strong.dill')) | 
 |     .toString(); | 
 | final sdkDir = p.dirname(p.dirname(Platform.resolvedExecutable)); |