compile_test.dart 8.95 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// Copyright 2017 The Chromium 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 'dart:convert';

import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/compile.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import 'package:test/test.dart';

import 'src/context.dart';

void main() {
  group('batch compile', () {
    ProcessManager mockProcessManager;
    MockProcess mockFrontendServer;
    MockStdIn mockFrontendServerStdIn;
    MockStream mockFrontendServerStdErr;
    setUp(() {
      mockProcessManager = new MockProcessManager();
      mockFrontendServer = new MockProcess();
      mockFrontendServerStdIn = new MockStdIn();
      mockFrontendServerStdErr = new MockStream();

30 31
      when(mockFrontendServer.stderr)
          .thenAnswer((Invocation invocation) => mockFrontendServerStdErr);
32 33 34
      final StreamController<String> stdErrStreamController = new StreamController<String>();
      when(mockFrontendServerStdErr.transform<String>(any)).thenReturn(stdErrStreamController.stream);
      when(mockFrontendServer.stdin).thenReturn(mockFrontendServerStdIn);
35 36
      when(mockProcessManager.start(any)).thenAnswer(
          (Invocation invocation) => new Future<Process>.value(mockFrontendServer));
37 38 39 40 41
      when(mockFrontendServer.exitCode).thenReturn(0);
    });

    testUsingContext('single dart successful compilation', () async {
      final BufferLogger logger = context[Logger];
42 43 44 45 46 47
      when(mockFrontendServer.stdout)
          .thenAnswer((Invocation invocation) => new Stream<List<int>>.fromFuture(
            new Future<List<int>>.value(UTF8.encode(
              'result abc\nline1\nline2\nabc /path/to/main.dart.dill'
            ))
          ));
48 49 50
      final String output = await compile(sdkRoot: '/path/to/sdkroot',
        mainPath: '/path/to/main.dart'
      );
51
      expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
52 53 54 55 56 57 58 59 60
      expect(logger.traceText, equals('compile debug message: line1\ncompile debug message: line2\n'));
      expect(output, equals('/path/to/main.dart.dill'));
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
    });

    testUsingContext('single dart failed compilation', () async {
      final BufferLogger logger = context[Logger];

61 62 63 64 65 66
      when(mockFrontendServer.stdout)
          .thenAnswer((Invocation invocation) => new Stream<List<int>>.fromFuture(
            new Future<List<int>>.value(UTF8.encode(
              'result abc\nline1\nline2\nabc'
            ))
          ));
67 68 69 70

      final String output = await compile(sdkRoot: '/path/to/sdkroot',
        mainPath: '/path/to/main.dart'
      );
71
      expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
      expect(logger.traceText, equals('compile debug message: line1\ncompile debug message: line2\n'));
      expect(output, equals(null));
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
    });
  });

  group('incremental compile', () {
    ProcessManager mockProcessManager;
    ResidentCompiler generator;
    MockProcess mockFrontendServer;
    MockStdIn mockFrontendServerStdIn;
    MockStream mockFrontendServerStdErr;
    StreamController<String> stdErrStreamController;

    setUp(() {
      generator = new ResidentCompiler('sdkroot');
      mockProcessManager = new MockProcessManager();
      mockFrontendServer = new MockProcess();
      mockFrontendServerStdIn = new MockStdIn();
      mockFrontendServerStdErr = new MockStream();

      when(mockFrontendServer.stdin).thenReturn(mockFrontendServerStdIn);
95 96
      when(mockFrontendServer.stderr)
          .thenAnswer((Invocation invocation) => mockFrontendServerStdErr);
97
      stdErrStreamController = new StreamController<String>();
98 99
      when(mockFrontendServerStdErr.transform<String>(any))
          .thenAnswer((Invocation invocation) => stdErrStreamController.stream);
100

101 102
      when(mockProcessManager.start(any)).thenAnswer(
          (Invocation invocation) => new Future<Process>.value(mockFrontendServer)
103 104 105 106 107 108 109
      );
      when(mockFrontendServer.exitCode).thenReturn(0);
    });

    testUsingContext('single dart compile', () async {
      final BufferLogger logger = context[Logger];

110 111 112 113 114 115
      when(mockFrontendServer.stdout)
          .thenAnswer((Invocation invocation) => new Stream<List<int>>.fromFuture(
            new Future<List<int>>.value(UTF8.encode(
              'result abc\nline1\nline2\nabc /path/to/main.dart.dill'
            ))
          ));
116 117 118 119

      final String output = await generator.recompile(
        '/path/to/main.dart', null /* invalidatedFiles */
      );
120
      expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
121 122 123 124 125 126 127 128 129 130
      verifyNoMoreInteractions(mockFrontendServerStdIn);
      expect(logger.traceText, equals('compile debug message: line1\ncompile debug message: line2\n'));
      expect(output, equals('/path/to/main.dart.dill'));
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
    });

    testUsingContext('compile and recompile', () async {
      final BufferLogger logger = context[Logger];

131
      final StreamController<List<int>> streamController = new StreamController<List<int>>();
132 133
      when(mockFrontendServer.stdout)
          .thenAnswer((Invocation invocation) => streamController.stream);
134 135
      streamController.add(UTF8.encode('result abc\nline0\nline1\nabc /path/to/main.dart.dill\n'));
      await generator.recompile('/path/to/main.dart', null /* invalidatedFiles */);
136
      expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
137 138 139 140 141

      await _recompile(streamController, generator, mockFrontendServerStdIn,
        'result abc\nline1\nline2\nabc /path/to/main.dart.dill\n');

      verifyNoMoreInteractions(mockFrontendServerStdIn);
142
      expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
143 144 145
      expect(logger.traceText, equals(
        'compile debug message: line0\ncompile debug message: line1\n'
        'compile debug message: line1\ncompile debug message: line2\n'
146
      ));
147 148 149
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
    });
150

151 152 153 154
    testUsingContext('compile and recompile twice', () async {
      final BufferLogger logger = context[Logger];

      final StreamController<List<int>> streamController = new StreamController<List<int>>();
155 156
      when(mockFrontendServer.stdout)
          .thenAnswer((Invocation invocation) => streamController.stream);
157 158 159
      streamController.add(UTF8.encode(
        'result abc\nline0\nline1\nabc /path/to/main.dart.dill\n'
      ));
160
      await generator.recompile('/path/to/main.dart', null /* invalidatedFiles */);
161
      expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
162

163 164 165 166 167
      await _recompile(streamController, generator, mockFrontendServerStdIn,
        'result abc\nline1\nline2\nabc /path/to/main.dart.dill\n');
      await _recompile(streamController, generator, mockFrontendServerStdIn,
        'result abc\nline2\nline3\nabc /path/to/main.dart.dill\n');

168
      verifyNoMoreInteractions(mockFrontendServerStdIn);
169
      expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
170 171 172 173 174
      expect(logger.traceText, equals(
        'compile debug message: line0\ncompile debug message: line1\n'
        'compile debug message: line1\ncompile debug message: line2\n'
        'compile debug message: line2\ncompile debug message: line3\n'
      ));
175 176 177 178 179 180
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
    });
  });
}

181 182 183 184 185 186 187 188 189 190
Future<Null> _recompile(StreamController<List<int>> streamController,
  ResidentCompiler generator, MockStdIn mockFrontendServerStdIn,
  String mockCompilerOutput) async {
  // Put content into the output stream after generator.recompile gets
  // going few lines below, resets completer.
  new Future<List<int>>(() {
    streamController.add(UTF8.encode(mockCompilerOutput));
  });
  final String output = await generator.recompile(null /* mainPath */, <String>['/path/to/main.dart']);
  expect(output, equals('/path/to/main.dart.dill'));
191 192 193 194 195 196
  final String commands = mockFrontendServerStdIn.getAndClear();
  final RegExp re = new RegExp(r'^recompile (.*)\n/path/to/main.dart\n(.*)\n$');
  expect(commands, matches(re));
  final Match match = re.firstMatch(commands);
  expect(match[1] == match[2], isTrue);
  mockFrontendServerStdIn._stdInWrites.clear();
197 198
}

199 200 201
class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {}
class MockStream extends Mock implements Stream<List<int>> {}
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
class MockStdIn extends Mock implements IOSink {
  final StringBuffer _stdInWrites = new StringBuffer();

  String getAndClear() {
    final String result = _stdInWrites.toString();
    _stdInWrites.clear();
    return result;
  }

  @override
  void write([Object o = '']) {
    _stdInWrites.write(o);
  }

  @override
  void writeln([Object o = '']) {
    _stdInWrites.writeln(o);
  }
}