mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
WIP Commits separated as follows: - Update lints in analysis_options files - Run `dart fix --apply` - Clean up leftover analysis issues - Run `dart format .` in the right places. Local analysis and testing passes. Checking CI now. Part of https://github.com/flutter/flutter/issues/178827 - Adoption of flutter_lints in examples/api coming in a separate change (cc @loic-sharma) ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1105 lines
37 KiB
Dart
1105 lines
37 KiB
Dart
// 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 'dart:convert';
|
|
import 'dart:io' as io show Process, ProcessSignal;
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:args/args.dart';
|
|
import 'package:file/file.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:file_testing/file_testing.dart';
|
|
import 'package:flutter_tools/src/artifacts.dart';
|
|
import 'package:flutter_tools/src/asset.dart';
|
|
import 'package:flutter_tools/src/base/config.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/io.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/os.dart';
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
|
import 'package:flutter_tools/src/build_info.dart';
|
|
import 'package:flutter_tools/src/build_system/tools/shader_compiler.dart';
|
|
import 'package:flutter_tools/src/compile.dart';
|
|
import 'package:flutter_tools/src/devfs.dart';
|
|
import 'package:flutter_tools/src/flutter_manifest.dart';
|
|
import 'package:package_config/package_config.dart';
|
|
import 'package:test/fake.dart';
|
|
import 'package:vm_service/vm_service.dart' as vm_service;
|
|
|
|
import '../src/common.dart';
|
|
import '../src/context.dart';
|
|
import '../src/fake_http_client.dart';
|
|
import '../src/fake_process_manager.dart';
|
|
import '../src/fake_vm_services.dart';
|
|
import '../src/fakes.dart';
|
|
import '../src/logging_logger.dart';
|
|
|
|
final createDevFSRequest = FakeVmServiceRequest(
|
|
method: '_createDevFS',
|
|
args: <String, Object>{'fsName': 'test'},
|
|
jsonResponse: <String, Object>{'uri': Uri.parse('test').toString()},
|
|
);
|
|
|
|
FakeVmServiceRequest failingCreateDevFSRequest = FakeVmServiceRequest(
|
|
method: '_createDevFS',
|
|
args: <String, Object>{'fsName': 'test'},
|
|
error: FakeRPCError(code: vm_service.RPCErrorKind.kServiceDisappeared.code),
|
|
);
|
|
|
|
FakeVmServiceRequest failingDeleteDevFSRequest = FakeVmServiceRequest(
|
|
method: '_deleteDevFS',
|
|
args: <String, dynamic>{'fsName': 'test'},
|
|
error: FakeRPCError(code: vm_service.RPCErrorKind.kServiceDisappeared.code),
|
|
);
|
|
|
|
void main() {
|
|
testWithoutContext('DevFSByteContent', () {
|
|
final content = DevFSByteContent(<int>[4, 5, 6]);
|
|
|
|
expect(content.bytes, orderedEquals(<int>[4, 5, 6]));
|
|
expect(content.isModified, isTrue);
|
|
expect(content.isModified, isFalse);
|
|
});
|
|
|
|
testWithoutContext('DevFSStringContent', () {
|
|
final content = DevFSStringContent('some string');
|
|
|
|
expect(content.string, 'some string');
|
|
expect(content.bytes, orderedEquals(utf8.encode('some string')));
|
|
expect(content.isModified, isTrue);
|
|
expect(content.isModified, isFalse);
|
|
});
|
|
|
|
testWithoutContext('DevFSFileContent', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final File file = fileSystem.file('foo.txt');
|
|
final content = DevFSFileContent(file);
|
|
expect(content.isModified, isFalse);
|
|
expect(content.isModified, isFalse);
|
|
|
|
file.parent.createSync(recursive: true);
|
|
file.writeAsBytesSync(<int>[1, 2, 3], flush: true);
|
|
|
|
final DateTime fiveSecondsAgo = file.statSync().modified.subtract(const Duration(seconds: 5));
|
|
expect(content.isModifiedAfter(fiveSecondsAgo), isTrue);
|
|
expect(content.isModifiedAfter(fiveSecondsAgo), isTrue);
|
|
|
|
file.writeAsBytesSync(<int>[2, 3, 4], flush: true);
|
|
|
|
expect(content.isModified, isTrue);
|
|
expect(content.isModified, isFalse);
|
|
expect(await content.contentsAsBytes(), <int>[2, 3, 4]);
|
|
|
|
expect(content.isModified, isFalse);
|
|
expect(content.isModified, isFalse);
|
|
|
|
file.deleteSync();
|
|
expect(content.isModified, isTrue);
|
|
expect(content.isModified, isFalse);
|
|
expect(content.isModified, isFalse);
|
|
});
|
|
|
|
testWithoutContext('DevFSStringCompressingBytesContent', () {
|
|
final content = DevFSStringCompressingBytesContent('uncompressed string');
|
|
|
|
expect(content.equals('uncompressed string'), isTrue);
|
|
expect(content.bytes, isNotNull);
|
|
expect(content.isModified, isTrue);
|
|
expect(content.isModified, isFalse);
|
|
});
|
|
|
|
testWithoutContext(
|
|
'DevFS create throws a DevFSException when vmservice disconnects unexpectedly',
|
|
() async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final OperatingSystemUtils osUtils = FakeOperatingSystemUtils();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[failingCreateDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
osUtils: osUtils,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
httpClient: FakeHttpClient.any(),
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
expect(() async => devFS.create(), throwsA(isA<DevFSException>()));
|
|
},
|
|
);
|
|
|
|
testWithoutContext('DevFS destroy is resilient to vmservice disconnection', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final OperatingSystemUtils osUtils = FakeOperatingSystemUtils();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest, failingDeleteDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
osUtils: osUtils,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
httpClient: FakeHttpClient.any(),
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
expect(await devFS.create(), isNotNull);
|
|
await devFS.destroy(); // Testing that this does not throw.
|
|
});
|
|
|
|
testWithoutContext('DevFS retries uploads when connection reset by peer', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final osUtils = OperatingSystemUtils(
|
|
fileSystem: fileSystem,
|
|
platform: FakePlatform(),
|
|
logger: BufferLogger.test(),
|
|
processManager: FakeProcessManager.any(),
|
|
);
|
|
final residentCompiler = FakeResidentCompiler();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
residentCompiler.onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
|
fileSystem.file('lib/foo.dill')
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(<int>[1, 2, 3, 4, 5]);
|
|
return const CompilerOutput('lib/foo.dill', 0, <Uri>[]);
|
|
};
|
|
|
|
/// This output can change based on the host platform.
|
|
final List<List<int>> expectedEncoded = await osUtils
|
|
.gzipLevel1Stream(Stream<List<int>>.value(<int>[1, 2, 3, 4, 5]))
|
|
.toList();
|
|
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
osUtils: osUtils,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
httpClient: FakeHttpClient.list(<FakeRequest>[
|
|
FakeRequest(
|
|
Uri.parse('http://localhost'),
|
|
method: HttpMethod.put,
|
|
responseError: const OSError('Connection Reset by peer'),
|
|
),
|
|
FakeRequest(
|
|
Uri.parse('http://localhost'),
|
|
method: HttpMethod.put,
|
|
responseError: const OSError('Connection Reset by peer'),
|
|
),
|
|
FakeRequest(
|
|
Uri.parse('http://localhost'),
|
|
method: HttpMethod.put,
|
|
responseError: const OSError('Connection Reset by peer'),
|
|
),
|
|
FakeRequest(
|
|
Uri.parse('http://localhost'),
|
|
method: HttpMethod.put,
|
|
responseError: const OSError('Connection Reset by peer'),
|
|
),
|
|
FakeRequest(
|
|
Uri.parse('http://localhost'),
|
|
method: HttpMethod.put,
|
|
responseError: const OSError('Connection Reset by peer'),
|
|
),
|
|
// This is the value of `<int>[1, 2, 3, 4, 5]` run through `osUtils.gzipLevel1Stream`.
|
|
FakeRequest(
|
|
Uri.parse('http://localhost'),
|
|
method: HttpMethod.put,
|
|
body: <int>[for (final List<int> chunk in expectedEncoded) ...chunk],
|
|
),
|
|
]),
|
|
uploadRetryThrottle: Duration.zero,
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
await devFS.create();
|
|
|
|
final UpdateFSReport report = await devFS.update(
|
|
mainUri: Uri.parse('lib/foo.txt'),
|
|
dillOutputPath: 'lib/foo.dill',
|
|
generator: residentCompiler,
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
);
|
|
|
|
expect(report.syncedBytes, 5);
|
|
expect(report.success, isTrue);
|
|
});
|
|
|
|
testWithoutContext('DevFS reports unsuccessful compile when errors are returned', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
osUtils: FakeOperatingSystemUtils(),
|
|
httpClient: FakeHttpClient.any(),
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
await devFS.create();
|
|
final DateTime? previousCompile = devFS.lastCompiled;
|
|
|
|
final residentCompiler = FakeResidentCompiler();
|
|
residentCompiler.onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
|
return const CompilerOutput('lib/foo.dill', 2, <Uri>[]);
|
|
};
|
|
|
|
final UpdateFSReport report = await devFS.update(
|
|
mainUri: Uri.parse('lib/foo.txt'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
);
|
|
|
|
expect(report.success, false);
|
|
expect(devFS.lastCompiled, previousCompile);
|
|
});
|
|
|
|
testWithoutContext(
|
|
'DevFS correctly updates last compiled time when compilation does not fail',
|
|
() async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
osUtils: FakeOperatingSystemUtils(),
|
|
httpClient: FakeHttpClient.any(),
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
await devFS.create();
|
|
final DateTime? previousCompile = devFS.lastCompiled;
|
|
|
|
final residentCompiler = FakeResidentCompiler();
|
|
residentCompiler.onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
|
fileSystem.file('lib/foo.txt.dill').createSync(recursive: true);
|
|
return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]);
|
|
};
|
|
|
|
final UpdateFSReport report = await devFS.update(
|
|
mainUri: Uri.parse('lib/main.dart'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
);
|
|
|
|
expect(report.success, true);
|
|
expect(devFS.lastCompiled, isNot(previousCompile));
|
|
},
|
|
);
|
|
|
|
testWithoutContext('DevFS can reset compilation time', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
);
|
|
final localDevFSWriter = LocalDevFSWriter(fileSystem: fileSystem);
|
|
fileSystem.directory('test').createSync();
|
|
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
osUtils: FakeOperatingSystemUtils(),
|
|
httpClient: HttpClient(),
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
await devFS.create();
|
|
final DateTime? previousCompile = devFS.lastCompiled;
|
|
|
|
final residentCompiler = FakeResidentCompiler();
|
|
residentCompiler.onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
|
fileSystem.file('lib/foo.txt.dill').createSync(recursive: true);
|
|
return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]);
|
|
};
|
|
|
|
final UpdateFSReport report = await devFS.update(
|
|
mainUri: Uri.parse('lib/main.dart'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
devFSWriter: localDevFSWriter,
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
);
|
|
|
|
expect(report.success, true);
|
|
expect(devFS.lastCompiled, isNot(previousCompile));
|
|
|
|
devFS.resetLastCompiled();
|
|
expect(devFS.lastCompiled, previousCompile);
|
|
|
|
// Does not reset to report compile time.
|
|
devFS.resetLastCompiled();
|
|
expect(devFS.lastCompiled, previousCompile);
|
|
});
|
|
|
|
testWithoutContext('DevFS uses provided DevFSWriter instead of default HTTP writer', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final writer = FakeDevFSWriter();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
);
|
|
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
osUtils: FakeOperatingSystemUtils(),
|
|
httpClient: FakeHttpClient.any(),
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
await devFS.create();
|
|
|
|
final residentCompiler = FakeResidentCompiler();
|
|
residentCompiler.onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
|
fileSystem.file('example').createSync();
|
|
return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]);
|
|
};
|
|
|
|
expect(writer.written, false);
|
|
|
|
final UpdateFSReport report = await devFS.update(
|
|
mainUri: Uri.parse('lib/main.dart'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
devFSWriter: writer,
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
);
|
|
|
|
expect(report.success, true);
|
|
expect(writer.written, true);
|
|
});
|
|
|
|
testWithoutContext('Local DevFSWriter can copy and write files', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final File file = fileSystem.file('foo_bar')..writeAsStringSync('goodbye');
|
|
final writer = LocalDevFSWriter(fileSystem: fileSystem);
|
|
|
|
await writer.write(<Uri, DevFSContent>{
|
|
Uri.parse('hello'): DevFSStringContent('hello'),
|
|
Uri.parse('goodbye'): DevFSFileContent(file),
|
|
}, Uri.parse('/foo/bar/devfs/'));
|
|
|
|
expect(fileSystem.file('/foo/bar/devfs/hello'), exists);
|
|
expect(fileSystem.file('/foo/bar/devfs/hello').readAsStringSync(), 'hello');
|
|
expect(fileSystem.file('/foo/bar/devfs/goodbye'), exists);
|
|
expect(fileSystem.file('/foo/bar/devfs/goodbye').readAsStringSync(), 'goodbye');
|
|
});
|
|
|
|
testWithoutContext('Local DevFSWriter turns FileSystemException into DevFSException', () async {
|
|
final handler = FileExceptionHandler();
|
|
final FileSystem fileSystem = MemoryFileSystem.test(opHandle: handler.opHandle);
|
|
final writer = LocalDevFSWriter(fileSystem: fileSystem);
|
|
final File file = fileSystem.file('foo');
|
|
handler.addError(file, FileSystemOp.read, const FileSystemException('foo'));
|
|
|
|
await expectLater(
|
|
() async => writer.write(<Uri, DevFSContent>{
|
|
Uri.parse('goodbye'): DevFSFileContent(file),
|
|
}, Uri.parse('/foo/bar/devfs/')),
|
|
throwsA(isA<DevFSException>()),
|
|
);
|
|
});
|
|
|
|
testWithoutContext('DevFS correctly records the elapsed time', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
// final FakeDevFSWriter writer = FakeDevFSWriter();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
fileSystem: fileSystem,
|
|
logger: BufferLogger.test(),
|
|
osUtils: FakeOperatingSystemUtils(),
|
|
httpClient: FakeHttpClient.any(),
|
|
stopwatchFactory: FakeStopwatchFactory(
|
|
stopwatches: <String, Stopwatch>{
|
|
'compile': FakeStopwatch()..elapsed = const Duration(seconds: 3),
|
|
'transfer': FakeStopwatch()..elapsed = const Duration(seconds: 5),
|
|
},
|
|
),
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
await devFS.create();
|
|
|
|
final residentCompiler = FakeResidentCompiler();
|
|
residentCompiler.onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
|
fileSystem.file('lib/foo.txt.dill').createSync(recursive: true);
|
|
return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]);
|
|
};
|
|
|
|
final UpdateFSReport report = await devFS.update(
|
|
mainUri: Uri.parse('lib/main.dart'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
);
|
|
|
|
expect(report.success, true);
|
|
expect(report.compileDuration, const Duration(seconds: 3));
|
|
expect(report.transferDuration, const Duration(seconds: 5));
|
|
});
|
|
|
|
testUsingContext('DevFS actually starts compile before processing bundle', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
|
|
final logger = LoggingLogger();
|
|
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
fileSystem: fileSystem,
|
|
logger: logger,
|
|
osUtils: FakeOperatingSystemUtils(),
|
|
httpClient: FakeHttpClient.any(),
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
await devFS.create();
|
|
|
|
final frontendServerStdIn = MemoryIOSink();
|
|
Stream<List<int>> frontendServerStdOut() async* {
|
|
var processed = 0;
|
|
while (true) {
|
|
while (frontendServerStdIn.writes.length == processed) {
|
|
await Future<dynamic>.delayed(const Duration(milliseconds: 5));
|
|
}
|
|
|
|
String? boundaryKey;
|
|
while (processed < frontendServerStdIn.writes.length) {
|
|
final List<int> data = frontendServerStdIn.writes[processed];
|
|
final String stringData = utf8.decode(data);
|
|
if (stringData.startsWith('compile ')) {
|
|
yield utf8.encode('result abc1\nline1\nline2\nabc1\nabc1 lib/foo.txt.dill 0\n');
|
|
} else if (stringData.startsWith('recompile ')) {
|
|
final String line = stringData.split('\n').first;
|
|
final int spaceDelim = line.lastIndexOf(' ');
|
|
boundaryKey = line.substring(spaceDelim + 1);
|
|
} else if (boundaryKey != null && stringData.startsWith(boundaryKey)) {
|
|
yield utf8.encode('result abc2\nline1\nline2\nabc2\nabc2 lib/foo.txt.dill 0\n');
|
|
} else {
|
|
throw Exception('Saw $data ($stringData)');
|
|
}
|
|
processed++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Stream<List<int>> frontendServerStdErr() async* {
|
|
// Output nothing on stderr.
|
|
}
|
|
|
|
final fakeProcessManager = AnsweringFakeProcessManager(
|
|
frontendServerStdOut(),
|
|
frontendServerStdErr(),
|
|
frontendServerStdIn,
|
|
);
|
|
final generatorStdoutHandler = StdoutHandler(logger: testLogger, fileSystem: fileSystem);
|
|
|
|
final residentCompiler = DefaultResidentCompiler(
|
|
'sdkroot',
|
|
buildMode: BuildMode.debug,
|
|
logger: logger,
|
|
processManager: fakeProcessManager,
|
|
artifacts: Artifacts.test(),
|
|
platform: FakePlatform(),
|
|
fileSystem: fileSystem,
|
|
stdoutHandler: generatorStdoutHandler,
|
|
shutdownHooks: FakeShutdownHooks(),
|
|
);
|
|
|
|
fileSystem.file('lib/foo.txt.dill').createSync(recursive: true);
|
|
|
|
final UpdateFSReport report1 = await devFS.update(
|
|
mainUri: Uri.parse('lib/main.dart'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
bundle: FakeBundle(),
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
);
|
|
expect(report1.success, true);
|
|
logger.messages.clear();
|
|
|
|
final UpdateFSReport report2 = await devFS.update(
|
|
mainUri: Uri.parse('lib/main.dart'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
bundle: FakeBundle(),
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
);
|
|
expect(report2.success, true);
|
|
|
|
final int processingBundleIndex = logger.messages.indexOf('Processing bundle.');
|
|
final int bundleProcessingDoneIndex = logger.messages.indexOf('Bundle processing done.');
|
|
final int compileLibMainIndex = logger.messages.indexWhere(
|
|
(String element) => element.startsWith('<- recompile lib/main.dart '),
|
|
);
|
|
expect(processingBundleIndex, greaterThanOrEqualTo(0));
|
|
expect(bundleProcessingDoneIndex, greaterThanOrEqualTo(0));
|
|
expect(compileLibMainIndex, greaterThanOrEqualTo(0));
|
|
expect(bundleProcessingDoneIndex, greaterThan(compileLibMainIndex));
|
|
});
|
|
|
|
group('Shader compilation', () {
|
|
testWithoutContext('DevFS recompiles shaders', () async {
|
|
final fileSystem = MemoryFileSystem.test();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
final logger = BufferLogger.test();
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
fileSystem: fileSystem,
|
|
logger: logger,
|
|
osUtils: FakeOperatingSystemUtils(),
|
|
httpClient: FakeHttpClient.any(),
|
|
config: Config.test(),
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
await devFS.create();
|
|
|
|
final residentCompiler = FakeResidentCompiler()
|
|
..onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
|
fileSystem.file('lib/foo.dill')
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(<int>[1, 2, 3, 4, 5]);
|
|
return const CompilerOutput('lib/foo.dill', 0, <Uri>[]);
|
|
};
|
|
final bundle = FakeBundle()
|
|
..entries['foo.frag'] = AssetBundleEntry(
|
|
DevFSByteContent(<int>[1, 2, 3, 4]),
|
|
kind: AssetKind.shader,
|
|
transformers: const <AssetTransformerEntry>[],
|
|
)
|
|
..entries['not.frag'] = AssetBundleEntry(
|
|
DevFSByteContent(<int>[1, 2, 3, 4]),
|
|
kind: AssetKind.regular,
|
|
transformers: const <AssetTransformerEntry>[],
|
|
);
|
|
|
|
final UpdateFSReport report = await devFS.update(
|
|
mainUri: Uri.parse('lib/main.dart'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
bundle: bundle,
|
|
);
|
|
|
|
expect(report.success, true);
|
|
expect(devFS.shaderPathsToEvict, <String>{'foo.frag'});
|
|
expect(devFS.assetPathsToEvict, <String>{'not.frag'});
|
|
});
|
|
|
|
testWithoutContext('DevFS tracks when FontManifest is updated', () async {
|
|
final fileSystem = MemoryFileSystem.test();
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
final logger = BufferLogger.test();
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
fileSystem: fileSystem,
|
|
logger: logger,
|
|
osUtils: FakeOperatingSystemUtils(),
|
|
httpClient: FakeHttpClient.any(),
|
|
config: Config.test(),
|
|
processManager: FakeProcessManager.empty(),
|
|
artifacts: Artifacts.test(),
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
await devFS.create();
|
|
|
|
expect(devFS.didUpdateFontManifest, false);
|
|
|
|
final residentCompiler = FakeResidentCompiler()
|
|
..onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
|
fileSystem.file('lib/foo.dill')
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(<int>[1, 2, 3, 4, 5]);
|
|
return const CompilerOutput('lib/foo.dill', 0, <Uri>[]);
|
|
};
|
|
final bundle = FakeBundle()
|
|
..entries['FontManifest.json'] = AssetBundleEntry(
|
|
DevFSByteContent(<int>[1, 2, 3, 4]),
|
|
kind: AssetKind.regular,
|
|
transformers: const <AssetTransformerEntry>[],
|
|
);
|
|
|
|
final UpdateFSReport report = await devFS.update(
|
|
mainUri: Uri.parse('lib/main.dart'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
bundle: bundle,
|
|
);
|
|
|
|
expect(report.success, true);
|
|
expect(devFS.shaderPathsToEvict, <String>{});
|
|
expect(devFS.assetPathsToEvict, <String>{'FontManifest.json'});
|
|
expect(devFS.didUpdateFontManifest, true);
|
|
});
|
|
});
|
|
|
|
group('Asset transformation', () {
|
|
testWithoutContext('DevFS re-transforms assets with transformers during update', () async {
|
|
final fileSystem = MemoryFileSystem.test();
|
|
final artifacts = Artifacts.test();
|
|
final devFSWriter = FakeDevFSWriter();
|
|
final processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: <Pattern>[
|
|
artifacts.getArtifactPath(Artifact.engineDartBinary),
|
|
'run',
|
|
'increment',
|
|
'--input=/.tmp_rand0/rand0/retransformerInput-asset.txt-transformOutput0.txt',
|
|
'--output=/.tmp_rand0/rand0/retransformerInput-asset.txt-transformOutput1.txt',
|
|
],
|
|
onRun: (List<String> command) {
|
|
final ArgResults argParseResults =
|
|
(ArgParser()
|
|
..addOption('input', mandatory: true)
|
|
..addOption('output', mandatory: true))
|
|
.parse(command);
|
|
|
|
final File inputFile = fileSystem.file(argParseResults['input']);
|
|
final File outputFile = fileSystem.file(argParseResults['output']);
|
|
|
|
expect(inputFile, exists);
|
|
outputFile
|
|
..createSync()
|
|
..writeAsBytesSync(
|
|
Uint8List.fromList(inputFile.readAsBytesSync().map((int b) => b + 1).toList()),
|
|
);
|
|
},
|
|
),
|
|
]);
|
|
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
final logger = BufferLogger.test();
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
fileSystem: fileSystem,
|
|
logger: logger,
|
|
osUtils: FakeOperatingSystemUtils(),
|
|
httpClient: FakeHttpClient.any(),
|
|
config: Config.test(),
|
|
processManager: processManager,
|
|
artifacts: artifacts,
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
await devFS.create();
|
|
|
|
final residentCompiler = FakeResidentCompiler()
|
|
..onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
|
fileSystem.file('lib/foo.dill')
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(<int>[1, 2, 3, 4, 5]);
|
|
return const CompilerOutput('lib/foo.dill', 0, <Uri>[]);
|
|
};
|
|
|
|
final bundle = FakeBundle()
|
|
..entries['asset.txt'] = AssetBundleEntry(
|
|
DevFSByteContent(<int>[1, 2, 3, 4]),
|
|
kind: AssetKind.regular,
|
|
transformers: const <AssetTransformerEntry>[
|
|
AssetTransformerEntry(package: 'increment', args: <String>[]),
|
|
],
|
|
);
|
|
|
|
final UpdateFSReport report = await devFS.update(
|
|
mainUri: Uri.parse('lib/main.dart'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
devFSWriter: devFSWriter,
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
bundle: bundle,
|
|
);
|
|
|
|
expect(processManager, hasNoRemainingExpectations);
|
|
expect(report.success, true);
|
|
expect(devFSWriter.entries, isNotNull);
|
|
final assetUri = Uri(path: 'build/flutter_assets/asset.txt');
|
|
expect(devFSWriter.entries, contains(assetUri));
|
|
expect(
|
|
await devFSWriter.entries![assetUri]!.contentsAsBytes(),
|
|
containsAllInOrder(<int>[2, 3, 4, 5]),
|
|
);
|
|
});
|
|
|
|
testWithoutContext('DevFS reports failure when asset transformation fails', () async {
|
|
final fileSystem = MemoryFileSystem.test();
|
|
final artifacts = Artifacts.test();
|
|
final devFSWriter = FakeDevFSWriter();
|
|
final processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: <Pattern>[
|
|
artifacts.getArtifactPath(Artifact.engineDartBinary),
|
|
'run',
|
|
'increment',
|
|
'--input=/.tmp_rand0/rand0/retransformerInput-asset.txt-transformOutput0.txt',
|
|
'--output=/.tmp_rand0/rand0/retransformerInput-asset.txt-transformOutput1.txt',
|
|
],
|
|
exitCode: 1,
|
|
),
|
|
]);
|
|
|
|
final fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[createDevFSRequest],
|
|
httpAddress: Uri.parse('http://localhost'),
|
|
);
|
|
final logger = BufferLogger.test();
|
|
final devFS = DevFS(
|
|
fakeVmServiceHost.vmService,
|
|
'test',
|
|
fileSystem.currentDirectory,
|
|
fileSystem: fileSystem,
|
|
logger: logger,
|
|
osUtils: FakeOperatingSystemUtils(),
|
|
httpClient: FakeHttpClient.any(),
|
|
config: Config.test(),
|
|
processManager: processManager,
|
|
artifacts: artifacts,
|
|
buildMode: BuildMode.debug,
|
|
);
|
|
|
|
await devFS.create();
|
|
|
|
final residentCompiler = FakeResidentCompiler()
|
|
..onRecompile = (Uri mainUri, List<Uri>? invalidatedFiles) async {
|
|
fileSystem.file('lib/foo.dill')
|
|
..createSync(recursive: true)
|
|
..writeAsBytesSync(<int>[1, 2, 3, 4, 5]);
|
|
return const CompilerOutput('lib/foo.dill', 0, <Uri>[]);
|
|
};
|
|
|
|
final bundle = FakeBundle()
|
|
..entries['asset.txt'] = AssetBundleEntry(
|
|
DevFSByteContent(<int>[1, 2, 3, 4]),
|
|
kind: AssetKind.regular,
|
|
transformers: const <AssetTransformerEntry>[
|
|
AssetTransformerEntry(package: 'increment', args: <String>[]),
|
|
],
|
|
);
|
|
|
|
final UpdateFSReport report = await devFS.update(
|
|
mainUri: Uri.parse('lib/main.dart'),
|
|
generator: residentCompiler,
|
|
dillOutputPath: 'lib/foo.dill',
|
|
pathToReload: 'lib/foo.txt.dill',
|
|
trackWidgetCreation: false,
|
|
invalidatedFiles: <Uri>[],
|
|
packageConfig: PackageConfig.empty,
|
|
devFSWriter: devFSWriter,
|
|
shaderCompiler: const FakeShaderCompiler(),
|
|
bundle: bundle,
|
|
);
|
|
|
|
expect(processManager, hasNoRemainingExpectations);
|
|
expect(
|
|
report.success,
|
|
false,
|
|
reason: 'DevFS update should fail since asset transformation failed.',
|
|
);
|
|
expect(
|
|
devFSWriter.entries,
|
|
isNull,
|
|
reason: 'DevFS should not have written anything since the update failed.',
|
|
);
|
|
expect(
|
|
logger.errorText,
|
|
'Transformer process terminated with non-zero exit code: 1\n'
|
|
'Transformer package: increment\n'
|
|
'Full command: Artifact.engineDartBinary run increment --input=/.tmp_rand0/rand0/retransformerInput-asset.txt-transformOutput0.txt --output=/.tmp_rand0/rand0/retransformerInput-asset.txt-transformOutput1.txt\n'
|
|
'stdout:\n'
|
|
'\n'
|
|
'stderr:\n'
|
|
'\n',
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
class FakeResidentCompiler extends Fake implements ResidentCompiler {
|
|
Future<CompilerOutput> Function(Uri mainUri, List<Uri>? invalidatedFiles)? onRecompile;
|
|
|
|
@override
|
|
Future<CompilerOutput> recompile(
|
|
Uri mainUri,
|
|
List<Uri>? invalidatedFiles, {
|
|
String? outputPath,
|
|
PackageConfig? packageConfig,
|
|
String? projectRootPath,
|
|
FileSystem? fs,
|
|
bool suppressErrors = false,
|
|
bool checkDartPluginRegistry = false,
|
|
File? dartPluginRegistrant,
|
|
Uri? nativeAssetsYaml,
|
|
bool recompileRestart = false,
|
|
}) {
|
|
return onRecompile?.call(mainUri, invalidatedFiles) ??
|
|
Future<CompilerOutput>.value(const CompilerOutput('', 1, <Uri>[]));
|
|
}
|
|
}
|
|
|
|
class FakeDevFSWriter implements DevFSWriter {
|
|
bool written = false;
|
|
Map<Uri, DevFSContent>? entries;
|
|
|
|
@override
|
|
Future<void> write(Map<Uri, DevFSContent> entries, Uri baseUri, DevFSWriter parent) async {
|
|
written = true;
|
|
this.entries = entries;
|
|
}
|
|
}
|
|
|
|
class FakeBundle extends AssetBundle {
|
|
@override
|
|
List<File> get additionalDependencies => <File>[];
|
|
|
|
@override
|
|
Future<int> build({
|
|
FlutterHookResult? flutterHookResult,
|
|
String manifestPath = defaultManifestPath,
|
|
String? assetDirPath,
|
|
String? packageConfigPath,
|
|
bool deferredComponentsEnabled = false,
|
|
TargetPlatform? targetPlatform,
|
|
String? flavor,
|
|
bool includeAssetsFromDevDependencies = false,
|
|
}) async {
|
|
return 0;
|
|
}
|
|
|
|
@override
|
|
Map<String, Map<String, AssetBundleEntry>> get deferredComponentsEntries =>
|
|
<String, Map<String, AssetBundleEntry>>{};
|
|
|
|
@override
|
|
final entries = <String, AssetBundleEntry>{};
|
|
|
|
@override
|
|
List<File> get inputFiles => <File>[];
|
|
|
|
@override
|
|
bool needsBuild({String manifestPath = defaultManifestPath}) {
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
bool wasBuiltOnce() {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class AnsweringFakeProcessManager implements ProcessManager {
|
|
AnsweringFakeProcessManager(this.stdout, this.stderr, this.stdin);
|
|
|
|
final Stream<List<int>> stdout;
|
|
final Stream<List<int>> stderr;
|
|
final IOSink stdin;
|
|
|
|
@override
|
|
bool canRun(dynamic executable, {String? workingDirectory}) {
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
bool killPid(int pid, [io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
Future<ProcessResult> run(
|
|
List<Object> command, {
|
|
String? workingDirectory,
|
|
Map<String, String>? environment,
|
|
bool includeParentEnvironment = true,
|
|
bool runInShell = false,
|
|
Encoding? stdoutEncoding = systemEncoding,
|
|
Encoding? stderrEncoding = systemEncoding,
|
|
}) async {
|
|
throw UnimplementedError();
|
|
}
|
|
|
|
@override
|
|
ProcessResult runSync(
|
|
List<Object> command, {
|
|
String? workingDirectory,
|
|
Map<String, String>? environment,
|
|
bool includeParentEnvironment = true,
|
|
bool runInShell = false,
|
|
Encoding? stdoutEncoding = systemEncoding,
|
|
Encoding? stderrEncoding = systemEncoding,
|
|
}) {
|
|
throw UnimplementedError();
|
|
}
|
|
|
|
@override
|
|
Future<Process> start(
|
|
List<Object> command, {
|
|
String? workingDirectory,
|
|
Map<String, String>? environment,
|
|
bool includeParentEnvironment = true,
|
|
bool runInShell = false,
|
|
ProcessStartMode mode = ProcessStartMode.normal,
|
|
}) async {
|
|
return AnsweringFakeProcess(stdout, stderr, stdin);
|
|
}
|
|
}
|
|
|
|
class AnsweringFakeProcess implements io.Process {
|
|
AnsweringFakeProcess(this.stdout, this.stderr, this.stdin);
|
|
|
|
@override
|
|
final Stream<List<int>> stdout;
|
|
@override
|
|
final Stream<List<int>> stderr;
|
|
@override
|
|
final IOSink stdin;
|
|
|
|
@override
|
|
Future<int> get exitCode async => 0;
|
|
|
|
@override
|
|
bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
int get pid => 42;
|
|
}
|
|
|
|
class FakeShaderCompiler implements DevelopmentShaderCompiler {
|
|
const FakeShaderCompiler();
|
|
|
|
@override
|
|
void configureCompiler(TargetPlatform? platform) {}
|
|
|
|
@override
|
|
Future<DevFSContent> recompileShader(DevFSContent inputShader) async {
|
|
return DevFSByteContent(await inputShader.contentsAsBytes());
|
|
}
|
|
}
|