// Copyright 2014 The Flutter Authors. 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 'package:file/memory.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/convert.dart'; import 'package:mockito/mockito.dart'; import 'package:package_config/package_config.dart'; import 'package:process/process.dart'; import '../src/common.dart'; import '../src/context.dart'; import '../src/mocks.dart'; void main() { ProcessManager mockProcessManager; MockProcess mockFrontendServer; MockStdIn mockFrontendServerStdIn; MockStream mockFrontendServerStdErr; List<String> latestCommand; setUp(() { mockProcessManager = MockProcessManager(); mockFrontendServer = MockProcess(); mockFrontendServerStdIn = MockStdIn(); mockFrontendServerStdErr = MockStream(); when(mockFrontendServer.stderr) .thenAnswer((Invocation invocation) => mockFrontendServerStdErr); final StreamController<String> stdErrStreamController = StreamController<String>(); when(mockFrontendServerStdErr.transform<String>(any)).thenAnswer((_) => stdErrStreamController.stream); when(mockFrontendServer.stdin).thenReturn(mockFrontendServerStdIn); when(mockProcessManager.canRun(any)).thenReturn(true); when(mockProcessManager.start(any)).thenAnswer( (Invocation invocation) { latestCommand = invocation.positionalArguments.first as List<String>; return Future<Process>.value(mockFrontendServer); }); when(mockFrontendServer.exitCode).thenAnswer((_) async => 0); }); testWithoutContext('batch compile single dart successful compilation', () async { when(mockFrontendServer.stdout) .thenAnswer((Invocation invocation) => Stream<List<int>>.fromFuture( Future<List<int>>.value(utf8.encode( 'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0' )) )); final BufferLogger logger = BufferLogger.test(); final KernelCompiler kernelCompiler = KernelCompiler( artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), fileSystemRoots: <String>[], fileSystemScheme: '', logger: logger, processManager: mockProcessManager ); final CompilerOutput output = await kernelCompiler.compile(sdkRoot: '/path/to/sdkroot', mainPath: '/path/to/main.dart', buildMode: BuildMode.debug, trackWidgetCreation: false, dartDefines: const <String>[], packageConfig: PackageConfig.empty, packagesPath: '.packages', ); expect(mockFrontendServerStdIn.getAndClear(), isEmpty); expect(logger.errorText, equals('line1\nline2\n')); expect(output.outputFilename, equals('/path/to/main.dart.dill')); final VerificationResult argVerification = verify(mockProcessManager.start(captureAny)); expect(argVerification.captured.single, containsAll(<String>[ '-Ddart.developer.causal_async_stacks=true', ])); }); testWithoutContext('passes correct AOT config to kernel compiler in aot/profile mode', () async { when(mockFrontendServer.stdout) .thenAnswer((Invocation invocation) => Stream<List<int>>.fromFuture( Future<List<int>>.value(utf8.encode( 'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0' )) )); final KernelCompiler kernelCompiler = KernelCompiler( artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), fileSystemRoots: <String>[], fileSystemScheme: '', logger: BufferLogger.test(), processManager: mockProcessManager ); await kernelCompiler.compile(sdkRoot: '/path/to/sdkroot', mainPath: '/path/to/main.dart', buildMode: BuildMode.profile, trackWidgetCreation: false, aot: true, dartDefines: const <String>[], packageConfig: PackageConfig.empty, packagesPath: '.packages', ); expect(mockFrontendServerStdIn.getAndClear(), isEmpty); final VerificationResult argVerification = verify(mockProcessManager.start(captureAny)); expect(argVerification.captured.single, containsAll(<String>[ '--aot', '--tfa', '-Ddart.vm.profile=true', '-Ddart.vm.product=false', '--bytecode-options=source-positions', '-Ddart.developer.causal_async_stacks=false', ])); }); testWithoutContext('passes correct AOT config to kernel compiler in aot/release mode', () async { when(mockFrontendServer.stdout) .thenAnswer((Invocation invocation) => Stream<List<int>>.fromFuture( Future<List<int>>.value(utf8.encode( 'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0' )) )); final KernelCompiler kernelCompiler = KernelCompiler( artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), fileSystemRoots: <String>[], fileSystemScheme: '', logger: BufferLogger.test(), processManager: mockProcessManager ); await kernelCompiler.compile(sdkRoot: '/path/to/sdkroot', mainPath: '/path/to/main.dart', buildMode: BuildMode.release, trackWidgetCreation: false, aot: true, dartDefines: const <String>[], packageConfig: PackageConfig.empty, packagesPath: '.packages', ); expect(mockFrontendServerStdIn.getAndClear(), isEmpty); final VerificationResult argVerification = verify(mockProcessManager.start(captureAny)); expect(argVerification.captured.single, containsAll(<String>[ '--aot', '--tfa', '-Ddart.vm.profile=false', '-Ddart.vm.product=true', '--bytecode-options=source-positions', '-Ddart.developer.causal_async_stacks=false', ])); }); testWithoutContext('batch compile single dart failed compilation', () async { when(mockFrontendServer.stdout) .thenAnswer((Invocation invocation) => Stream<List<int>>.fromFuture( Future<List<int>>.value(utf8.encode( 'result abc\nline1\nline2\nabc\nabc' )) )); final BufferLogger logger = BufferLogger.test(); final KernelCompiler kernelCompiler = KernelCompiler( artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), fileSystemRoots: <String>[], fileSystemScheme: '', logger: logger, processManager: mockProcessManager ); final CompilerOutput output = await kernelCompiler.compile(sdkRoot: '/path/to/sdkroot', mainPath: '/path/to/main.dart', buildMode: BuildMode.debug, trackWidgetCreation: false, dartDefines: const <String>[], packageConfig: PackageConfig.empty, packagesPath: '.packages', ); expect(mockFrontendServerStdIn.getAndClear(), isEmpty); expect(logger.errorText, equals('line1\nline2\n')); expect(output, equals(null)); }); testWithoutContext('batch compile single dart abnormal compiler termination', () async { when(mockFrontendServer.exitCode).thenAnswer((_) async => 255); when(mockFrontendServer.stdout) .thenAnswer((Invocation invocation) => Stream<List<int>>.fromFuture( Future<List<int>>.value(utf8.encode( 'result abc\nline1\nline2\nabc\nabc' )) )); final BufferLogger logger = BufferLogger.test(); final KernelCompiler kernelCompiler = KernelCompiler( artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), fileSystemRoots: <String>[], fileSystemScheme: '', logger: logger, processManager: mockProcessManager ); final CompilerOutput output = await kernelCompiler.compile( sdkRoot: '/path/to/sdkroot', mainPath: '/path/to/main.dart', buildMode: BuildMode.debug, trackWidgetCreation: false, dartDefines: const <String>[], packageConfig: PackageConfig.empty, packagesPath: '.packages', ); expect(mockFrontendServerStdIn.getAndClear(), isEmpty); expect(logger.errorText, equals('line1\nline2\n')); expect(output, equals(null)); }); testWithoutContext('passes dartDefines to the kernel compiler', () async { // Use unsuccessful result because it's easier to setup in test. We only care about arguments passed to the compiler. when(mockFrontendServer.exitCode).thenAnswer((_) async => 255); when(mockFrontendServer.stdout).thenAnswer((Invocation invocation) => Stream<List<int>>.fromFuture( Future<List<int>>.value(<int>[]) )); final KernelCompiler kernelCompiler = KernelCompiler( artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), fileSystemRoots: <String>[], fileSystemScheme: '', logger: BufferLogger.test(), processManager: mockProcessManager ); await kernelCompiler.compile(sdkRoot: '/path/to/sdkroot', mainPath: '/path/to/main.dart', buildMode: BuildMode.debug, trackWidgetCreation: false, dartDefines: const <String>['FOO=bar', 'BAZ=qux'], packageConfig: PackageConfig.empty, packagesPath: '.packages', ); expect(latestCommand, containsAllInOrder(<String>['-DFOO=bar', '-DBAZ=qux'])); }); testWithoutContext('maps a file to a multiroot scheme if providfed', () async { // Use unsuccessful result because it's easier to setup in test. We only care about arguments passed to the compiler. when(mockFrontendServer.exitCode).thenAnswer((_) async => 255); when(mockFrontendServer.stdout).thenAnswer((Invocation invocation) => Stream<List<int>>.fromFuture( Future<List<int>>.value(<int>[]) )); final KernelCompiler kernelCompiler = KernelCompiler( artifacts: Artifacts.test(), fileSystem: MemoryFileSystem.test(), fileSystemRoots: <String>[ '/foo/bar/fizz', ], fileSystemScheme: 'scheme', logger: BufferLogger.test(), processManager: mockProcessManager ); await kernelCompiler.compile(sdkRoot: '/path/to/sdkroot', mainPath: '/foo/bar/fizz/main.dart', buildMode: BuildMode.debug, trackWidgetCreation: false, dartDefines: const <String>[], packageConfig: PackageConfig.empty, packagesPath: '.packages', ); expect(latestCommand, containsAll(<String>['scheme:///main.dart'])); }); } class MockProcess extends Mock implements Process {} class MockProcessManager extends Mock implements ProcessManager {}