Unverified Commit 15b9b58e authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Remove MockStdIn and MockStream (#78276)

parent 24258141
......@@ -9,50 +9,67 @@ import 'dart:async';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/async_guard.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.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';
import '../src/fake_process_manager.dart';
import '../src/fakes.dart';
void main() {
ProcessManager mockProcessManager;
ResidentCompiler generator;
ResidentCompiler generatorWithScheme;
MockProcess mockFrontendServer;
MockStdIn mockFrontendServerStdIn;
MockStream mockFrontendServerStdErr;
StreamController<String> stdErrStreamController;
MemoryIOSink frontendServerStdIn;
BufferLogger testLogger;
StdoutHandler generatorStdoutHandler;
StdoutHandler generatorWithSchemeStdoutHandler;
FakeProcessManager fakeProcessManager;
const List<String> frontendServerCommand = <String>[
'Artifact.engineDartBinary',
'--disable-dart-dev',
'Artifact.frontendServerSnapshotForEngineDartSdk',
'--sdk-root',
'sdkroot/',
'--incremental',
'--target=flutter',
'--debugger-module-names',
'--experimental-emit-debug-metadata',
'--output-dill',
'/build/',
'-Ddart.vm.profile=false',
'-Ddart.vm.product=false',
'--enable-asserts',
'--track-widget-creation',
];
setUp(() {
testLogger = BufferLogger.test();
mockProcessManager = MockProcessManager();
mockFrontendServer = MockProcess();
mockFrontendServerStdIn = MockStdIn();
mockFrontendServerStdErr = MockStream();
generator = ResidentCompiler(
frontendServerStdIn = MemoryIOSink();
fakeProcessManager = FakeProcessManager.list(<FakeCommand>[]);
generatorStdoutHandler = StdoutHandler(logger: testLogger, fileSystem: MemoryFileSystem.test());
generatorWithSchemeStdoutHandler = StdoutHandler(logger: testLogger, fileSystem: MemoryFileSystem.test());
generator = DefaultResidentCompiler(
'sdkroot',
buildMode: BuildMode.debug,
logger: testLogger,
processManager: mockProcessManager,
processManager: fakeProcessManager,
artifacts: Artifacts.test(),
platform: FakePlatform(operatingSystem: 'linux'),
fileSystem: MemoryFileSystem.test(),
stdoutHandler: generatorStdoutHandler,
);
generatorWithScheme = ResidentCompiler(
generatorWithScheme = DefaultResidentCompiler(
'sdkroot',
buildMode: BuildMode.debug,
logger: testLogger,
processManager: mockProcessManager,
processManager: fakeProcessManager,
artifacts: Artifacts.test(),
platform: FakePlatform(operatingSystem: 'linux'),
fileSystemRoots: <String>[
......@@ -60,31 +77,16 @@ void main() {
],
fileSystemScheme: 'scheme',
fileSystem: MemoryFileSystem.test(),
);
when(mockFrontendServer.stdin).thenReturn(mockFrontendServerStdIn);
when(mockFrontendServer.stderr)
.thenAnswer((Invocation invocation) => mockFrontendServerStdErr);
when(mockFrontendServer.exitCode).thenAnswer((Invocation invocation) {
return Completer<int>().future;
});
stdErrStreamController = StreamController<String>();
when(mockFrontendServerStdErr.transform<String>(any))
.thenAnswer((Invocation invocation) => stdErrStreamController.stream);
when(mockProcessManager.canRun(any)).thenReturn(true);
when(mockProcessManager.start(any)).thenAnswer(
(Invocation invocation) => Future<Process>.value(mockFrontendServer)
stdoutHandler: generatorWithSchemeStdoutHandler,
);
});
testWithoutContext('incremental compile single dart compile', () 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'
))
));
fakeProcessManager.addCommand(FakeCommand(
command: frontendServerCommand,
stdout: 'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0',
stdin: frontendServerStdIn,
));
final CompilerOutput output = await generator.recompile(
Uri.parse('/path/to/main.dart'),
......@@ -92,19 +94,24 @@ void main() {
outputPath: '/build/',
packageConfig: PackageConfig.empty,
);
expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(frontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
expect(testLogger.errorText, equals('line1\nline2\n'));
expect(output.outputFilename, equals('/path/to/main.dart.dill'));
expect(fakeProcessManager, hasNoRemainingExpectations);
});
testWithoutContext('incremental compile single dart compile with filesystem scheme', () 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'
))
));
fakeProcessManager.addCommand(FakeCommand(
command: const <String>[
...frontendServerCommand,
'--filesystem-root',
'/foo/bar/fizz',
'--filesystem-scheme',
'scheme',
],
stdout: 'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0',
stdin: frontendServerStdIn,
));
final CompilerOutput output = await generatorWithScheme.recompile(
Uri.parse('file:///foo/bar/fizz/main.dart'),
......@@ -112,16 +119,17 @@ void main() {
outputPath: '/build/',
packageConfig: PackageConfig.empty,
);
expect(mockFrontendServerStdIn.getAndClear(), 'compile scheme:///main.dart\n');
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(frontendServerStdIn.getAndClear(), 'compile scheme:///main.dart\n');
expect(testLogger.errorText, equals('line1\nline2\n'));
expect(output.outputFilename, equals('/path/to/main.dart.dill'));
expect(fakeProcessManager, hasNoRemainingExpectations);
});
testWithoutContext('incremental compile single dart compile abnormally terminates', () async {
when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => const Stream<List<int>>.empty()
);
fakeProcessManager.addCommand(FakeCommand(
command: frontendServerCommand,
stdin: frontendServerStdIn,
));
expect(asyncGuard(() => generator.recompile(
Uri.parse('/path/to/main.dart'),
......@@ -132,50 +140,54 @@ void main() {
});
testWithoutContext('incremental compile single dart compile abnormally terminates via exitCode', () async {
when(mockFrontendServer.exitCode)
.thenAnswer((Invocation invocation) async => 1);
when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => const Stream<List<int>>.empty()
);
fakeProcessManager.addCommand(FakeCommand(
command: frontendServerCommand,
stdin: frontendServerStdIn,
exitCode: 1,
));
expect(asyncGuard(() => generator.recompile(
Uri.parse('/path/to/main.dart'),
null, /* invalidatedFiles */
outputPath: '/build/',
packageConfig: PackageConfig.empty,
)), throwsToolExit());
)), throwsToolExit(message: 'the Dart compiler exited unexpectedly.'));
});
testWithoutContext('incremental compile and recompile', () async {
final StreamController<List<int>> streamController = StreamController<List<int>>();
when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => streamController.stream);
streamController.add(utf8.encode('result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0\n'));
final Completer<void> completer = Completer<void>();
fakeProcessManager.addCommand(FakeCommand(
command: frontendServerCommand,
stdout: 'result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0',
stdin: frontendServerStdIn,
completer: completer,
));
await generator.recompile(
Uri.parse('/path/to/main.dart'),
null, /* invalidatedFiles */
outputPath: '/build/',
packageConfig: PackageConfig.empty,
);
expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
expect(frontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
// No accept or reject commands should be issued until we
// send recompile request.
await _accept(streamController, generator, mockFrontendServerStdIn, '');
await _reject(streamController, generator, mockFrontendServerStdIn, '', '');
await _accept(generator, frontendServerStdIn, '');
await _reject(generatorStdoutHandler, generator, frontendServerStdIn, '', '');
await _recompile(streamController, generator, mockFrontendServerStdIn,
await _recompile(generatorStdoutHandler, generator, frontendServerStdIn,
'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n');
await _accept(streamController, generator, mockFrontendServerStdIn, r'^accept\n$');
await _accept(generator, frontendServerStdIn, r'^accept\n$');
await _recompile(streamController, generator, mockFrontendServerStdIn,
await _recompile(generatorStdoutHandler, generator, frontendServerStdIn,
'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n');
// No sources returned from reject command.
await _reject(streamController, generator, mockFrontendServerStdIn, 'result abc\nabc\n',
await _reject(generatorStdoutHandler, generator, frontendServerStdIn, 'result abc\nabc\n',
r'^reject\n$');
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
completer.complete();
expect(frontendServerStdIn.getAndClear(), isEmpty);
expect(testLogger.errorText, equals(
'line0\nline1\n'
'line1\nline2\n'
......@@ -184,39 +196,48 @@ void main() {
});
testWithoutContext('incremental compile and recompile with filesystem scheme', () async {
final StreamController<List<int>> streamController = StreamController<List<int>>();
when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => streamController.stream);
streamController.add(utf8.encode('result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0\n'));
final Completer<void> completer = Completer<void>();
fakeProcessManager.addCommand(FakeCommand(
command: const <String>[
...frontendServerCommand,
'--filesystem-root',
'/foo/bar/fizz',
'--filesystem-scheme',
'scheme',
],
stdout: 'result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0',
stdin: frontendServerStdIn,
completer: completer,
));
await generatorWithScheme.recompile(
Uri.parse('file:///foo/bar/fizz/main.dart'),
null, /* invalidatedFiles */
outputPath: '/build/',
packageConfig: PackageConfig.empty,
);
expect(mockFrontendServerStdIn.getAndClear(), 'compile scheme:///main.dart\n');
expect(frontendServerStdIn.getAndClear(), 'compile scheme:///main.dart\n');
// No accept or reject commands should be issued until we
// send recompile request.
await _accept(streamController, generatorWithScheme, mockFrontendServerStdIn, '');
await _reject(streamController, generatorWithScheme, mockFrontendServerStdIn, '', '');
await _accept(generatorWithScheme, frontendServerStdIn, '');
await _reject(generatorWithSchemeStdoutHandler, generatorWithScheme, frontendServerStdIn, '', '');
await _recompile(streamController, generatorWithScheme, mockFrontendServerStdIn,
await _recompile(generatorWithSchemeStdoutHandler, generatorWithScheme, frontendServerStdIn,
'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n',
mainUri: Uri.parse('file:///foo/bar/fizz/main.dart'),
expectedMainUri: 'scheme:///main.dart');
await _accept(streamController, generatorWithScheme, mockFrontendServerStdIn, r'^accept\n$');
await _accept(generatorWithScheme, frontendServerStdIn, r'^accept\n$');
await _recompile(streamController, generatorWithScheme, mockFrontendServerStdIn,
await _recompile(generatorWithSchemeStdoutHandler, generatorWithScheme, frontendServerStdIn,
'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n',
mainUri: Uri.parse('file:///foo/bar/fizz/main.dart'),
expectedMainUri: 'scheme:///main.dart');
// No sources returned from reject command.
await _reject(streamController, generatorWithScheme, mockFrontendServerStdIn, 'result abc\nabc\n',
await _reject(generatorWithSchemeStdoutHandler, generatorWithScheme, frontendServerStdIn, 'result abc\nabc\n',
r'^reject\n$');
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
completer.complete();
expect(frontendServerStdIn.getAndClear(), isEmpty);
expect(testLogger.errorText, equals(
'line0\nline1\n'
'line1\nline2\n'
......@@ -236,43 +257,52 @@ void main() {
'scheme:///other.dart',
];
final StreamController<List<int>> streamController = StreamController<List<int>>();
when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => streamController.stream);
streamController.add(utf8.encode('result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0\n'));
final Completer<void> completer = Completer<void>();
fakeProcessManager.addCommand(FakeCommand(
command: const <String>[
...frontendServerCommand,
'--filesystem-root',
'/foo/bar/fizz',
'--filesystem-scheme',
'scheme',
],
stdout: 'result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0',
stdin: frontendServerStdIn,
completer: completer,
));
await generatorWithScheme.recompile(
Uri.parse('file:///foo/bar/fizz/main.dart'),
null, /* invalidatedFiles */
outputPath: '/build/',
packageConfig: PackageConfig.empty,
);
expect(mockFrontendServerStdIn.getAndClear(), 'compile scheme:///main.dart\n');
expect(frontendServerStdIn.getAndClear(), 'compile scheme:///main.dart\n');
// No accept or reject commands should be issued until we
// send recompile request.
await _accept(streamController, generatorWithScheme, mockFrontendServerStdIn, '');
await _reject(streamController, generatorWithScheme, mockFrontendServerStdIn, '', '');
await _accept(generatorWithScheme, frontendServerStdIn, '');
await _reject(generatorWithSchemeStdoutHandler, generatorWithScheme, frontendServerStdIn, '', '');
await _recompile(streamController, generatorWithScheme, mockFrontendServerStdIn,
await _recompile(generatorWithSchemeStdoutHandler, generatorWithScheme, frontendServerStdIn,
'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n',
mainUri: mainUri,
expectedMainUri: expectedMainUri,
updatedUris: updatedUris,
expectedUpdatedUris: expectedUpdatedUris);
await _accept(streamController, generatorWithScheme, mockFrontendServerStdIn, r'^accept\n$');
await _accept(generatorWithScheme, frontendServerStdIn, r'^accept\n$');
await _recompile(streamController, generatorWithScheme, mockFrontendServerStdIn,
await _recompile(generatorWithSchemeStdoutHandler, generatorWithScheme, frontendServerStdIn,
'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n',
mainUri: mainUri,
expectedMainUri: expectedMainUri,
updatedUris: updatedUris,
expectedUpdatedUris: expectedUpdatedUris);
// No sources returned from reject command.
await _reject(streamController, generatorWithScheme, mockFrontendServerStdIn, 'result abc\nabc\n',
await _reject(generatorWithSchemeStdoutHandler, generatorWithScheme, frontendServerStdIn, 'result abc\nabc\n',
r'^reject\n$');
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
completer.complete();
expect(frontendServerStdIn.getAndClear(), isEmpty);
expect(testLogger.errorText, equals(
'line0\nline1\n'
'line1\nline2\n'
......@@ -281,11 +311,13 @@ void main() {
});
testWithoutContext('incremental compile can suppress errors', () async {
final StreamController<List<int>> stdoutController = StreamController<List<int>>();
when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => stdoutController.stream);
stdoutController.add(utf8.encode('result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0\n'));
final Completer<void> completer = Completer<void>();
fakeProcessManager.addCommand(FakeCommand(
command: frontendServerCommand,
stdout: 'result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0',
stdin: frontendServerStdIn,
completer: completer,
));
await generator.recompile(
Uri.parse('/path/to/main.dart'),
......@@ -293,18 +325,18 @@ void main() {
outputPath: '/build/',
packageConfig: PackageConfig.empty,
);
expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
expect(frontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
await _recompile(stdoutController, generator, mockFrontendServerStdIn,
await _recompile(generatorStdoutHandler, generator, frontendServerStdIn,
'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n');
await _accept(stdoutController, generator, mockFrontendServerStdIn, r'^accept\n$');
await _accept(generator, frontendServerStdIn, r'^accept\n$');
await _recompile(stdoutController, generator, mockFrontendServerStdIn,
await _recompile(generatorStdoutHandler, generator, frontendServerStdIn,
'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n', suppressErrors: true);
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
completer.complete();
expect(frontendServerStdIn.getAndClear(), isEmpty);
// Compiler message is not printed with suppressErrors: true above.
expect(testLogger.errorText, isNot(equals(
......@@ -316,27 +348,28 @@ void main() {
});
testWithoutContext('incremental compile and recompile twice', () async {
final StreamController<List<int>> streamController = StreamController<List<int>>();
when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => streamController.stream);
streamController.add(utf8.encode(
'result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0\n'
final Completer<void> completer = Completer<void>();
fakeProcessManager.addCommand(FakeCommand(
command: frontendServerCommand,
stdout: 'result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0',
stdin: frontendServerStdIn,
completer: completer,
));
await generator.recompile(
Uri.parse('/path/to/main.dart'),
null /* invalidatedFiles */,
outputPath: '/build/',
packageConfig: PackageConfig.empty,
packageConfig: PackageConfig.empty,
);
expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
expect(frontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
await _recompile(streamController, generator, mockFrontendServerStdIn,
await _recompile(generatorStdoutHandler, generator, frontendServerStdIn,
'result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n');
await _recompile(streamController, generator, mockFrontendServerStdIn,
await _recompile(generatorStdoutHandler, generator, frontendServerStdIn,
'result abc\nline2\nline3\nabc\nabc /path/to/main.dart.dill 0\n');
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
completer.complete();
expect(frontendServerStdIn.getAndClear(), isEmpty);
expect(testLogger.errorText, equals(
'line0\nline1\n'
'line1\nline2\n'
......@@ -346,9 +379,9 @@ void main() {
}
Future<void> _recompile(
StreamController<List<int>> streamController,
StdoutHandler stdoutHandler,
ResidentCompiler generator,
MockStdIn mockFrontendServerStdIn,
MemoryIOSink frontendServerStdIn,
String mockCompilerOutput, {
bool suppressErrors = false,
Uri mainUri,
......@@ -360,20 +393,22 @@ Future<void> _recompile(
updatedUris ??= <Uri>[mainUri];
expectedUpdatedUris ??= <String>[expectedMainUri];
// Put content into the output stream after generator.recompile gets
// going few lines below, resets completer.
scheduleMicrotask(() {
streamController.add(utf8.encode(mockCompilerOutput));
});
final CompilerOutput output = await generator.recompile(
final Future<CompilerOutput> recompileFuture = generator.recompile(
mainUri,
updatedUris,
outputPath: '/build/',
packageConfig: PackageConfig.empty,
suppressErrors: suppressErrors,
);
// Put content into the output stream after generator.recompile gets
// going few lines below, resets completer.
scheduleMicrotask(() {
LineSplitter.split(mockCompilerOutput).forEach(stdoutHandler.handler);
});
final CompilerOutput output = await recompileFuture;
expect(output.outputFilename, equals('/path/to/main.dart.dill'));
final String commands = mockFrontendServerStdIn.getAndClear();
final String commands = frontendServerStdIn.getAndClear();
final RegExp whitespace = RegExp(r'\s+');
final List<String> parts = commands.split(whitespace);
......@@ -383,43 +418,36 @@ Future<void> _recompile(
for (int i = 0; i < expectedUpdatedUris.length; i++) {
expect(parts[3 + i], equals(expectedUpdatedUris[i]));
}
mockFrontendServerStdIn.stdInWrites.clear();
}
Future<void> _accept(
StreamController<List<int>> streamController,
ResidentCompiler generator,
MockStdIn mockFrontendServerStdIn,
MemoryIOSink frontendServerStdIn,
String expected,
) async {
// Put content into the output stream after generator.recompile gets
// going few lines below, resets completer.
generator.accept();
final String commands = mockFrontendServerStdIn.getAndClear();
final String commands = frontendServerStdIn.getAndClear();
final RegExp re = RegExp(expected);
expect(commands, matches(re));
mockFrontendServerStdIn.stdInWrites.clear();
}
Future<void> _reject(
StreamController<List<int>> streamController,
StdoutHandler stdoutHandler,
ResidentCompiler generator,
MockStdIn mockFrontendServerStdIn,
MemoryIOSink frontendServerStdIn,
String mockCompilerOutput,
String expected,
) async {
// Put content into the output stream after generator.recompile gets
// going few lines below, resets completer.
final Future<CompilerOutput> rejectFuture = generator.reject();
scheduleMicrotask(() {
streamController.add(utf8.encode(mockCompilerOutput));
LineSplitter.split(mockCompilerOutput).forEach(stdoutHandler.handler);
});
final CompilerOutput output = await generator.reject();
final CompilerOutput output = await rejectFuture;
expect(output, isNull);
final String commands = mockFrontendServerStdIn.getAndClear();
final String commands = frontendServerStdIn.getAndClear();
final RegExp re = RegExp(expected);
expect(commands, matches(re));
mockFrontendServerStdIn.stdInWrites.clear();
}
class MockProcess extends Mock implements Process {}
class MockProcessManager extends Mock implements ProcessManager {}
......@@ -201,25 +201,3 @@ class MockIOSDevice extends Mock implements IOSDevice {
@override
bool isSupportedForProject(FlutterProject flutterProject) => true;
}
class MockStdIn extends Mock implements IOSink {
final StringBuffer stdInWrites = 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);
}
}
class MockStream extends Mock implements Stream<List<int>> {}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment