Commit 17057bb4 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

[devFS] Use URI to represent paths on device (#8446)

* [devFS] Use URI to represent paths on device

Previosuly, regular file paths in the format of the host platform were used to represent paths on device. That works when host and device share the same (POSIX) file path format. With a Windows host, this breaks. URIs are the solution as they are platform independent and the VM service on the device already interpreted the file paths as URIs anyways.

* review comments

* switch to file paths

* fix tests on Windows

* review comments
parent 5ce67b08
This diff is collapsed.
......@@ -434,17 +434,15 @@ class HotRunner extends ResidentRunner {
String reloadMessage;
try {
String entryPath = fs.path.relative(mainPath, from: projectRootPath);
String deviceEntryPath =
_devFS.baseUri.resolve(entryPath).toFilePath();
String devicePackagesPath =
_devFS.baseUri.resolve('.packages').toFilePath();
Uri deviceEntryUri = _devFS.baseUri.resolveUri(fs.path.toUri(entryPath));
Uri devicePackagesUri = _devFS.baseUri.resolve('.packages');
if (benchmarkMode)
vmReloadTimer.start();
Map<String, dynamic> reloadReport =
await currentView.uiIsolate.reloadSources(
pause: pause,
rootLibPath: deviceEntryPath,
packagesPath: devicePackagesPath);
rootLibUri: deviceEntryUri,
packagesUri: devicePackagesUri);
if (!validateReloadReport(reloadReport)) {
// Reload failed.
flutterUsage.sendEvent('hot', 'reload-reject');
......
......@@ -838,17 +838,19 @@ class Isolate extends ServiceObjectOwner {
Future<Map<String, dynamic>> reloadSources(
{ bool pause: false,
String rootLibPath,
String packagesPath}) async {
Uri rootLibUri,
Uri packagesUri}) async {
try {
Map<String, dynamic> arguments = <String, dynamic>{
'pause': pause
};
if (rootLibPath != null) {
arguments['rootLibUri'] = rootLibPath;
// TODO(goderbauer): Transfer Uri (instead of file path) when remote end supports it.
// Note: Despite the name, `rootLibUri` and `packagesUri` expect file paths.
if (rootLibUri != null) {
arguments['rootLibUri'] = rootLibUri.toFilePath(windows: false);
}
if (packagesPath != null) {
arguments['packagesUri'] = packagesPath;
if (packagesUri != null) {
arguments['packagesUri'] = packagesUri.toFilePath(windows: false);
}
Map<String, dynamic> response = await invokeRpcRaw('_reloadSources', params: arguments);
return response;
......
......@@ -4,7 +4,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
import 'package:flutter_tools/src/asset.dart';
import 'package:flutter_tools/src/base/io.dart';
......@@ -70,7 +69,7 @@ void main() {
File file = fs.file(fs.path.join(basePath, filePath));
await file.parent.create(recursive: true);
file.writeAsBytesSync(<int>[1, 2, 3]);
_packages['my_project'] = 'lib';
_packages['my_project'] = fs.path.toUri('lib');
// simulate package
await _createPackage('somepkg', 'somefile.txt');
......@@ -82,20 +81,20 @@ void main() {
int bytes = await devFS.update();
devFSOperations.expectMessages(<String>[
'writeFile test ${fs.path.join('lib', 'foo.txt')}',
'writeFile test ${fs.path.join('packages', 'somepkg', 'somefile.txt')}',
'writeFile test .packages',
'writeFile test lib/foo.txt',
'writeFile test packages/somepkg/somefile.txt',
]);
expect(devFS.assetPathsToEvict, isEmpty);
List<String> packageSpecOnDevice = LineSplitter.split(UTF8.decode(
await devFSOperations.devicePathToContent['.packages'].contentsAsBytes()
await devFSOperations.devicePathToContent[fs.path.toUri('.packages')].contentsAsBytes()
)).toList();
expect(packageSpecOnDevice,
unorderedEquals(<String>['my_project:lib', 'somepkg:packages/somepkg'])
unorderedEquals(<String>['my_project:lib/', 'somepkg:packages/somepkg/'])
);
expect(bytes, 46);
expect(bytes, 48);
});
testUsingContext('add new file to local file system', () async {
File file = fs.file(fs.path.join(basePath, filePath2));
......@@ -103,7 +102,7 @@ void main() {
file.writeAsBytesSync(<int>[1, 2, 3, 4, 5, 6, 7]);
int bytes = await devFS.update();
devFSOperations.expectMessages(<String>[
'writeFile test ${fs.path.join('foo', 'bar.txt')}',
'writeFile test foo/bar.txt',
]);
expect(devFS.assetPathsToEvict, isEmpty);
expect(bytes, 7);
......@@ -120,7 +119,7 @@ void main() {
await file.writeAsBytes(<int>[1, 2, 3, 4, 5, 6]);
bytes = await devFS.update();
devFSOperations.expectMessages(<String>[
'writeFile test ${fs.path.join('lib', 'foo.txt')}',
'writeFile test lib/foo.txt',
]);
expect(devFS.assetPathsToEvict, isEmpty);
expect(bytes, 6);
......@@ -130,7 +129,7 @@ void main() {
await file.delete();
int bytes = await devFS.update();
devFSOperations.expectMessages(<String>[
'deleteFile test ${fs.path.join('lib', 'foo.txt')}',
'deleteFile test lib/foo.txt',
]);
expect(devFS.assetPathsToEvict, isEmpty);
expect(bytes, 0);
......@@ -140,10 +139,10 @@ void main() {
int bytes = await devFS.update();
devFSOperations.expectMessages(<String>[
'writeFile test .packages',
'writeFile test ${fs.path.join('packages', 'newpkg', 'anotherfile.txt')}',
'writeFile test packages/newpkg/anotherfile.txt',
]);
expect(devFS.assetPathsToEvict, isEmpty);
expect(bytes, 66);
expect(bytes, 69);
});
testUsingContext('add an asset bundle', () async {
assetBundle.entries['a.txt'] = new DevFSStringContent('abc');
......@@ -207,7 +206,7 @@ void main() {
devFSOperations.expectMessages(<String>['destroy test']);
expect(devFS.assetPathsToEvict, isEmpty);
});
}, skip: io.Platform.isWindows);
});
group('devfs remote', () {
MockVMService vmService;
......@@ -240,11 +239,11 @@ void main() {
int bytes = await devFS.update();
vmService.expectMessages(<String>[
'writeFile test .packages',
'writeFile test ${fs.path.join('lib', 'foo.txt')}',
'writeFile test ${fs.path.join('packages', 'somepkg', 'somefile.txt')}',
'writeFile test lib/foo.txt',
'writeFile test packages/somepkg/somefile.txt',
]);
expect(devFS.assetPathsToEvict, isEmpty);
expect(bytes, 46);
expect(bytes, 48);
}, timeout: const Timeout(const Duration(seconds: 5)));
testUsingContext('delete dev file system', () async {
......@@ -252,7 +251,7 @@ void main() {
vmService.expectMessages(<String>['_deleteDevFS {fsName: test}']);
expect(devFS.assetPathsToEvict, isEmpty);
});
}, skip: io.Platform.isWindows);
});
}
class MockVMService extends BasicMock implements VMService {
......@@ -321,7 +320,7 @@ class MockVM implements VM {
final List<Directory> _tempDirs = <Directory>[];
final Map <String, String> _packages = <String, String>{};
final Map <String, Uri> _packages = <String, Uri>{};
Directory _newTempDir() {
Directory tempDir = fs.systemTempDirectory.createTempSync('devfs${_tempDirs.length}');
......@@ -340,12 +339,14 @@ Future<Null> _createPackage(String pkgName, String pkgFileName) async {
File pkgFile = fs.file(fs.path.join(pkgTempDir.path, pkgName, 'lib', pkgFileName));
await pkgFile.parent.create(recursive: true);
pkgFile.writeAsBytesSync(<int>[11, 12, 13]);
_packages[pkgName] = pkgFile.parent.path;
_packages[pkgName] = fs.path.toUri(pkgFile.parent.path);
StringBuffer sb = new StringBuffer();
_packages.forEach((String pkgName, String pkgPath) {
sb.writeln('$pkgName:$pkgPath');
_packages.forEach((String pkgName, Uri pkgUri) {
sb.writeln('$pkgName:$pkgUri');
});
fs.file(fs.path.join(_tempDirs[0].path, '.packages')).writeAsStringSync(sb.toString());
}
String _inAssetBuildDirectory(String filename) => fs.path.join(getAssetBuildDirectory(), filename);
String _inAssetBuildDirectory(String filename) {
return '${fs.path.toUri(getAssetBuildDirectory()).path}/$filename';
}
......@@ -95,7 +95,7 @@ class BasicMock {
}
class MockDevFSOperations extends BasicMock implements DevFSOperations {
Map<String, DevFSContent> devicePathToContent = <String, DevFSContent>{};
Map<Uri, DevFSContent> devicePathToContent = <Uri, DevFSContent>{};
@override
Future<Uri> create(String fsName) async {
......@@ -109,14 +109,14 @@ class MockDevFSOperations extends BasicMock implements DevFSOperations {
}
@override
Future<dynamic> writeFile(String fsName, String devicePath, DevFSContent content) async {
messages.add('writeFile $fsName $devicePath');
devicePathToContent[devicePath] = content;
Future<dynamic> writeFile(String fsName, Uri deviceUri, DevFSContent content) async {
messages.add('writeFile $fsName $deviceUri');
devicePathToContent[deviceUri] = content;
}
@override
Future<dynamic> deleteFile(String fsName, String devicePath) async {
messages.add('deleteFile $fsName $devicePath');
devicePathToContent.remove(devicePath);
Future<dynamic> deleteFile(String fsName, Uri deviceUri) async {
messages.add('deleteFile $fsName $deviceUri');
devicePathToContent.remove(deviceUri);
}
}
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