Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
cd803ac7
Unverified
Commit
cd803ac7
authored
Mar 15, 2019
by
Jonah Williams
Committed by
GitHub
Mar 15, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve hot reload performance (#28152)
parent
bfac6048
Changes
50
Show whitespace changes
Inline
Side-by-side
Showing
50 changed files
with
434 additions
and
1311 deletions
+434
-1311
pubspec.yaml
dev/bots/pubspec.yaml
+1
-0
fuchsia_attach.dart
packages/flutter_tools/bin/fuchsia_attach.dart
+1
-3
attach.dart
packages/flutter_tools/lib/src/commands/attach.dart
+5
-0
daemon.dart
packages/flutter_tools/lib/src/commands/daemon.dart
+3
-0
run.dart
packages/flutter_tools/lib/src/commands/run.dart
+3
-0
update_packages.dart
packages/flutter_tools/lib/src/commands/update_packages.dart
+1
-1
dependencies.dart
packages/flutter_tools/lib/src/dart/dependencies.dart
+0
-150
dependency_checker.dart
packages/flutter_tools/lib/src/dependency_checker.dart
+0
-46
devfs.dart
packages/flutter_tools/lib/src/devfs.dart
+24
-297
resident_runner.dart
packages/flutter_tools/lib/src/resident_runner.dart
+12
-16
run_hot.dart
packages/flutter_tools/lib/src/run_hot.dart
+137
-96
pubspec.yaml
packages/flutter_tools/pubspec.yaml
+2
-2
attach_test.dart
packages/flutter_tools/test/commands/attach_test.dart
+4
-0
dart_dependencies_test.dart
packages/flutter_tools/test/dart_dependencies_test.dart
+0
-99
.dartignore
...lutter_tools/test/data/dart_dependencies_test/.dartignore
+0
-0
.packages
...ls/test/data/dart_dependencies_test/asci_casing/.packages
+0
-1
This_Import_Has_fuNNy_casING.dart
...encies_test/asci_casing/This_Import_Has_fuNNy_casING.dart
+0
-5
analysis_options.yaml
.../dart_dependencies_test/asci_casing/analysis_options.yaml
+0
-3
main.dart
...ls/test/data/dart_dependencies_test/asci_casing/main.dart
+0
-9
pubspec.yaml
...test/data/dart_dependencies_test/asci_casing/pubspec.yaml
+0
-1
analysis_options.yaml
...a/dart_dependencies_test/bad_import/analysis_options.yaml
+0
-3
main.dart
...ols/test/data/dart_dependencies_test/bad_import/main.dart
+0
-5
pubspec.yaml
.../test/data/dart_dependencies_test/bad_import/pubspec.yaml
+0
-1
.packages
...ls/test/data/dart_dependencies_test/bad_package/.packages
+0
-1
analysis_options.yaml
.../dart_dependencies_test/bad_package/analysis_options.yaml
+0
-3
main.dart
...ls/test/data/dart_dependencies_test/bad_package/main.dart
+0
-6
pubspec.yaml
...test/data/dart_dependencies_test/bad_package/pubspec.yaml
+0
-1
.packages
...tools/test/data/dart_dependencies_test/bad_path/.packages
+0
-1
analysis_options.yaml
...ata/dart_dependencies_test/bad_path/analysis_options.yaml
+0
-3
main.dart
...tools/test/data/dart_dependencies_test/bad_path/main.dart
+0
-5
pubspec.yaml
...ls/test/data/dart_dependencies_test/bad_path/pubspec.yaml
+0
-1
.packages
...ata/dart_dependencies_test/changed_sdk_location/.packages
+0
-2
main.dart
...dart_dependencies_test/changed_sdk_location/lib/main.dart
+0
-5
pubspec.yaml
.../dart_dependencies_test/changed_sdk_location/pubspec.yaml
+0
-9
.packages
...ter_tools/test/data/dart_dependencies_test/good/.packages
+0
-1
analysis_options.yaml
...st/data/dart_dependencies_test/good/analysis_options.yaml
+0
-3
foo.dart
...tter_tools/test/data/dart_dependencies_test/good/foo.dart
+0
-3
bar.dart
..._tools/test/data/dart_dependencies_test/good/lib/bar.dart
+0
-3
main.dart
...ter_tools/test/data/dart_dependencies_test/good/main.dart
+0
-6
pubspec.yaml
..._tools/test/data/dart_dependencies_test/good/pubspec.yaml
+0
-1
.packages
...s/test/data/dart_dependencies_test/syntax_error/.packages
+0
-1
analysis_options.yaml
...dart_dependencies_test/syntax_error/analysis_options.yaml
+0
-3
foo.dart
...ls/test/data/dart_dependencies_test/syntax_error/foo.dart
+0
-5
main.dart
...s/test/data/dart_dependencies_test/syntax_error/main.dart
+0
-5
pubspec.yaml
...est/data/dart_dependencies_test/syntax_error/pubspec.yaml
+0
-1
dependency_checker_test.dart
packages/flutter_tools/test/dependency_checker_test.dart
+0
-112
devfs_test.dart
packages/flutter_tools/test/devfs_test.dart
+1
-364
hot_test.dart
packages/flutter_tools/test/hot_test.dart
+6
-22
project_file_invalidator_test.dart
...ges/flutter_tools/test/project_file_invalidator_test.dart
+234
-0
mocks.dart
packages/flutter_tools/test/src/mocks.dart
+0
-6
No files found.
dev/bots/pubspec.yaml
View file @
cd803ac7
...
...
@@ -70,4 +70,5 @@ dev_dependencies:
mockito
:
4.0.0
test_api
:
0.2.2
# PUBSPEC CHECKSUM: 422e
packages/flutter_tools/bin/fuchsia_attach.dart
View file @
cd803ac7
...
...
@@ -15,7 +15,6 @@ import 'package:flutter_tools/src/cache.dart';
import
'package:flutter_tools/src/commands/attach.dart'
;
import
'package:flutter_tools/src/commands/doctor.dart'
;
import
'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart'
;
import
'package:flutter_tools/src/run_hot.dart'
;
import
'package:flutter_tools/src/runner/flutter_command.dart'
;
final
ArgParser
parser
=
ArgParser
()
...
...
@@ -100,8 +99,7 @@ Future<void> main(List<String> args) async {
platformKernelDill:
platformKernelDill
,
flutterPatchedSdk:
flutterPatchedSdk
,
),
HotRunnerConfig:
()
=>
HotRunnerConfig
()..
computeDartDependencies
=
false
,
},
}
);
}
...
...
packages/flutter_tools/lib/src/commands/attach.dart
View file @
cd803ac7
...
...
@@ -18,6 +18,7 @@ import '../fuchsia/fuchsia_device.dart';
import
'../globals.dart'
;
import
'../ios/devices.dart'
;
import
'../ios/simulators.dart'
;
import
'../project.dart'
;
import
'../protocol_discovery.dart'
;
import
'../resident_runner.dart'
;
import
'../run_cold.dart'
;
...
...
@@ -129,6 +130,7 @@ class AttachCommand extends FlutterCommand {
Future
<
FlutterCommandResult
>
runCommand
()
async
{
final
String
ipv4Loopback
=
InternetAddress
.
loopbackIPv4
.
address
;
final
String
ipv6Loopback
=
InternetAddress
.
loopbackIPv6
.
address
;
final
FlutterProject
flutterProject
=
await
FlutterProject
.
current
();
Cache
.
releaseLockEarly
();
...
...
@@ -228,6 +230,7 @@ class AttachCommand extends FlutterCommand {
projectRootPath:
argResults
[
'project-root'
],
dillOutputPath:
argResults
[
'output-dill'
],
ipv6:
usesIpv6
,
flutterProject:
flutterProject
,
)
:
ColdRunner
(
flutterDevices
,
...
...
@@ -289,6 +292,7 @@ class HotRunnerFactory {
String
dillOutputPath
,
bool
stayResident
=
true
,
bool
ipv6
=
false
,
FlutterProject
flutterProject
,
})
=>
HotRunner
(
devices
,
target:
target
,
...
...
@@ -302,6 +306,7 @@ class HotRunnerFactory {
dillOutputPath:
dillOutputPath
,
stayResident:
stayResident
,
ipv6:
ipv6
,
flutterProject:
flutterProject
,
);
}
...
...
packages/flutter_tools/lib/src/commands/daemon.dart
View file @
cd803ac7
...
...
@@ -19,6 +19,7 @@ import '../convert.dart';
import
'../device.dart'
;
import
'../emulator.dart'
;
import
'../globals.dart'
;
import
'../project.dart'
;
import
'../resident_runner.dart'
;
import
'../run_cold.dart'
;
import
'../run_hot.dart'
;
...
...
@@ -341,6 +342,7 @@ class AppDomain extends Domain {
if
(
await
device
.
isLocalEmulator
&&
!
options
.
buildInfo
.
supportsEmulator
)
{
throw
'
${toTitleCase(options.buildInfo.friendlyModeName)}
mode is not supported for emulators.'
;
}
final
FlutterProject
flutterProject
=
await
FlutterProject
.
current
();
// We change the current working directory for the duration of the `start` command.
final
Directory
cwd
=
fs
.
currentDirectory
;
...
...
@@ -368,6 +370,7 @@ class AppDomain extends Domain {
dillOutputPath:
dillOutputPath
,
ipv6:
ipv6
,
hostIsIde:
true
,
flutterProject:
flutterProject
,
);
}
else
{
runner
=
ColdRunner
(
...
...
packages/flutter_tools/lib/src/commands/run.dart
View file @
cd803ac7
...
...
@@ -13,6 +13,7 @@ import '../cache.dart';
import
'../device.dart'
;
import
'../globals.dart'
;
import
'../ios/mac.dart'
;
import
'../project.dart'
;
import
'../resident_runner.dart'
;
import
'../run_cold.dart'
;
import
'../run_hot.dart'
;
...
...
@@ -280,6 +281,7 @@ class RunCommand extends RunCommandBase {
// Enable hot mode by default if `--no-hot` was not passed and we are in
// debug mode.
final
bool
hotMode
=
shouldUseHotMode
();
final
FlutterProject
flutterProject
=
await
FlutterProject
.
current
();
writePidFile
(
argResults
[
'pid-file'
]);
...
...
@@ -389,6 +391,7 @@ class RunCommand extends RunCommandBase {
saveCompilationTrace:
argResults
[
'train'
],
stayResident:
stayResident
,
ipv6:
ipv6
,
flutterProject:
flutterProject
,
);
}
else
{
runner
=
ColdRunner
(
...
...
packages/flutter_tools/lib/src/commands/update_packages.dart
View file @
cd803ac7
...
...
@@ -1093,7 +1093,7 @@ class PubspecDependency extends PubspecLine {
/// Generates the File object for the pubspec.yaml file of a given Directory.
File
_pubspecFor
(
Directory
directory
)
{
return
fs
.
file
(
'
${directory.path}
/pubspec.yaml'
);
return
fs
.
file
(
fs
.
path
.
join
(
directory
.
path
,
'pubspec.yaml'
)
);
}
/// Generates the source of a fake pubspec.yaml file given a list of
...
...
packages/flutter_tools/lib/src/dart/dependencies.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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.
// TODO(dnfield): This will be removed when @jonahwilliams' work on build lands
// ignore: deprecated_member_use
import
'package:analyzer/analyzer.dart'
as
analyzer
;
import
'../base/file_system.dart'
;
import
'../dart/package_map.dart'
;
// List of flutter specific environment configurations.
// See https://github.com/munificent/dep-interface-libraries/blob/master/Proposal.md
// We will populate this list as required. Potentially, all of dart:* libraries
// supported by flutter would end up here.
final
List
<
String
>
_configurationConstants
=
<
String
>[
'dart.library.io'
];
String
_dottedNameToString
(
analyzer
.
DottedName
dottedName
)
{
String
result
=
''
;
for
(
analyzer
.
SimpleIdentifier
identifier
in
dottedName
.
components
)
{
if
(
result
.
isEmpty
)
{
result
+=
identifier
.
token
.
lexeme
;
}
else
{
result
+=
'.'
+
identifier
.
token
.
lexeme
;
}
}
return
result
;
}
class
DartDependencySetBuilder
{
DartDependencySetBuilder
(
String
mainScriptPath
,
String
packagesFilePath
)
:
_mainScriptPath
=
canonicalizePath
(
mainScriptPath
),
_mainScriptUri
=
fs
.
path
.
toUri
(
mainScriptPath
),
_packagesFilePath
=
canonicalizePath
(
packagesFilePath
);
final
String
_mainScriptPath
;
final
String
_packagesFilePath
;
final
Uri
_mainScriptUri
;
Set
<
String
>
build
()
{
final
List
<
String
>
dependencies
=
<
String
>[
_mainScriptPath
,
_packagesFilePath
];
final
List
<
Uri
>
toProcess
=
<
Uri
>[
_mainScriptUri
];
final
PackageMap
packageMap
=
PackageMap
(
_packagesFilePath
);
while
(
toProcess
.
isNotEmpty
)
{
final
Uri
currentUri
=
toProcess
.
removeLast
();
final
analyzer
.
CompilationUnit
unit
=
_parse
(
currentUri
.
toFilePath
());
for
(
analyzer
.
Directive
directive
in
unit
.
directives
)
{
if
(!(
directive
is
analyzer
.
UriBasedDirective
))
continue
;
String
uriAsString
;
if
(
directive
is
analyzer
.
NamespaceDirective
)
{
final
analyzer
.
NamespaceDirective
namespaceDirective
=
directive
;
// If the directive is a conditional import directive, we should
// select the imported uri based on the condition.
for
(
analyzer
.
Configuration
configuration
in
namespaceDirective
.
configurations
)
{
if
(
_configurationConstants
.
contains
(
_dottedNameToString
(
configuration
.
name
)))
{
uriAsString
=
configuration
.
uri
.
stringValue
;
break
;
}
}
}
if
(
uriAsString
==
null
)
{
final
analyzer
.
UriBasedDirective
uriBasedDirective
=
directive
;
uriAsString
=
uriBasedDirective
.
uri
.
stringValue
;
}
Uri
uri
;
try
{
uri
=
Uri
.
parse
(
uriAsString
);
}
on
FormatException
{
throw
DartDependencyException
(
'Unable to parse URI:
$uriAsString
'
);
}
Uri
resolvedUri
=
analyzer
.
resolveRelativeUri
(
currentUri
,
uri
);
if
(
resolvedUri
.
scheme
.
startsWith
(
'dart'
))
continue
;
if
(
resolvedUri
.
scheme
==
'package'
)
{
final
Uri
newResolvedUri
=
packageMap
.
uriForPackage
(
resolvedUri
);
if
(
newResolvedUri
==
null
)
{
throw
DartDependencyException
(
'The following Dart file:
\n
'
'
${currentUri.toFilePath()}
\n
'
'...refers, in an import, to the following library:
\n
'
'
$resolvedUri
\n
'
'That library is in a package that is not known. Maybe you forgot to '
'mention it in your pubspec.yaml file?'
);
}
resolvedUri
=
newResolvedUri
;
}
final
String
path
=
canonicalizePath
(
resolvedUri
.
toFilePath
());
if
(!
dependencies
.
contains
(
path
))
{
if
(!
fs
.
isFileSync
(
path
))
{
throw
DartDependencyException
(
'The following Dart file:
\n
'
'
${currentUri.toFilePath()}
\n
'
'...refers, in an import, to the following library:
\n
'
'
$path
\n
'
'Unfortunately, that library does not appear to exist on your file system.'
);
}
dependencies
.
add
(
path
);
toProcess
.
add
(
resolvedUri
);
}
}
}
return
dependencies
.
toSet
();
}
analyzer
.
CompilationUnit
_parse
(
String
path
)
{
String
body
;
try
{
body
=
fs
.
file
(
path
).
readAsStringSync
();
}
on
FileSystemException
catch
(
error
)
{
throw
DartDependencyException
(
'Could not read "
$path
" when determining Dart dependencies.'
,
error
,
);
}
try
{
return
analyzer
.
parseDirectives
(
body
,
name:
path
);
}
on
analyzer
.
AnalyzerError
catch
(
error
)
{
throw
DartDependencyException
(
'When trying to parse this Dart file to find its dependencies:
\n
'
'
$path
\n
'
'...the analyzer failed with the following error:
\n
'
'
${error.toString().trimRight()}
'
,
error
,
);
}
on
analyzer
.
AnalyzerErrorGroup
catch
(
error
)
{
throw
DartDependencyException
(
'When trying to parse this Dart file to find its dependencies:
\n
'
'
$path
\n
'
'...the analyzer failed with the following error:
\n
'
'
${error.toString().trimRight()}
'
,
error
,
);
}
}
}
class
DartDependencyException
implements
Exception
{
DartDependencyException
(
this
.
message
,
[
this
.
parent
]);
final
String
message
;
final
Exception
parent
;
@override
String
toString
()
=>
message
;
}
packages/flutter_tools/lib/src/dependency_checker.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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
'asset.dart'
;
import
'base/file_system.dart'
;
import
'dart/dependencies.dart'
;
import
'globals.dart'
;
class
DependencyChecker
{
DependencyChecker
(
this
.
builder
,
this
.
assets
);
final
DartDependencySetBuilder
builder
;
final
Set
<
String
>
_dependencies
=
<
String
>{};
final
AssetBundle
assets
;
/// Returns [true] if any components have been modified after [threshold] or
/// if it cannot be determined.
bool
check
(
DateTime
threshold
)
{
_dependencies
.
clear
();
// Build the set of Dart dependencies.
try
{
_dependencies
.
addAll
(
builder
.
build
());
}
catch
(
e
,
st
)
{
printTrace
(
'DependencyChecker: error determining .dart dependencies:
\n
$e
\n
$st
'
);
return
true
;
}
// TODO(johnmccutchan): Extract dependencies from the AssetBundle too.
// Check all dependency modification times.
for
(
String
path
in
_dependencies
)
{
final
File
file
=
fs
.
file
(
path
);
final
FileStat
stat
=
file
.
statSync
();
if
(
stat
.
type
==
FileSystemEntityType
.
notFound
)
{
printTrace
(
'DependencyChecker: Error stating
$path
.'
);
return
true
;
}
if
(
stat
.
modified
.
isAfter
(
threshold
))
{
printTrace
(
'DependencyChecker:
$path
is newer than
$threshold
'
);
return
true
;
}
}
printTrace
(
'DependencyChecker: nothing is modified after
$threshold
.'
);
return
false
;
}
}
packages/flutter_tools/lib/src/devfs.dart
View file @
cd803ac7
...
...
@@ -30,8 +30,6 @@ DevFSConfig get devFSConfig => context[DevFSConfig];
/// Common superclass for content copied to the device.
abstract
class
DevFSContent
{
bool
_exists
=
true
;
/// Return true if this is the first time this method is called
/// or if the entry has been modified since this method was last called.
bool
get
isModified
;
...
...
@@ -59,13 +57,6 @@ abstract class DevFSContent {
class
DevFSFileContent
extends
DevFSContent
{
DevFSFileContent
(
this
.
file
);
static
DevFSFileContent
clone
(
DevFSFileContent
fsFileContent
)
{
final
DevFSFileContent
newFsFileContent
=
DevFSFileContent
(
fsFileContent
.
file
);
newFsFileContent
.
_linkTarget
=
fsFileContent
.
_linkTarget
;
newFsFileContent
.
_fileStat
=
fsFileContent
.
_fileStat
;
return
newFsFileContent
;
}
final
FileSystemEntity
file
;
FileSystemEntity
_linkTarget
;
FileStat
_fileStat
;
...
...
@@ -218,7 +209,6 @@ abstract class DevFSOperations {
Future
<
Uri
>
create
(
String
fsName
);
Future
<
dynamic
>
destroy
(
String
fsName
);
Future
<
dynamic
>
writeFile
(
String
fsName
,
Uri
deviceUri
,
DevFSContent
content
);
Future
<
dynamic
>
deleteFile
(
String
fsName
,
Uri
deviceUri
);
}
/// An implementation of [DevFSOperations] that speaks to the
...
...
@@ -261,11 +251,6 @@ class ServiceProtocolDevFSOperations implements DevFSOperations {
printTrace
(
'DevFS: Failed to write
$deviceUri
:
$error
'
);
}
}
@override
Future
<
dynamic
>
deleteFile
(
String
fsName
,
Uri
deviceUri
)
async
{
// TODO(johnmccutchan): Add file deletion to the devFS protocol.
}
}
class
DevFSException
implements
Exception
{
...
...
@@ -288,16 +273,14 @@ class _DevFSHttpWriter {
int
_inFlight
=
0
;
Map
<
Uri
,
DevFSContent
>
_outstanding
;
Completer
<
void
>
_completer
;
HttpClient
_client
;
final
HttpClient
_client
=
HttpClient
()
;
Future
<
void
>
write
(
Map
<
Uri
,
DevFSContent
>
entries
)
async
{
_client
=
HttpClient
();
_client
.
maxConnectionsPerHost
=
kMaxInFlight
;
_completer
=
Completer
<
void
>();
_outstanding
=
Map
<
Uri
,
DevFSContent
>.
from
(
entries
);
_scheduleWrites
();
await
_completer
.
future
;
_client
.
close
();
}
void
_scheduleWrites
()
{
...
...
@@ -405,9 +388,6 @@ class DevFS {
final
Map
<
Uri
,
DevFSContent
>
_entries
=
<
Uri
,
DevFSContent
>{};
final
Set
<
String
>
assetPathsToEvict
=
<
String
>{};
final
List
<
Future
<
Map
<
String
,
dynamic
>>>
_pendingOperations
=
<
Future
<
Map
<
String
,
dynamic
>>>[];
Uri
_baseUri
;
Uri
get
baseUri
=>
_baseUri
;
...
...
@@ -453,66 +433,33 @@ class DevFS {
DateTime
firstBuildTime
,
bool
bundleFirstUpload
=
false
,
bool
bundleDirty
=
false
,
Set
<
String
>
fileFilter
,
@required
ResidentCompiler
generator
,
String
dillOutputPath
,
@required
bool
trackWidgetCreation
,
bool
fullRestart
=
false
,
String
projectRootPath
,
@required
String
pathToReload
,
@required
List
<
String
>
invalidatedFiles
,
})
async
{
assert
(
trackWidgetCreation
!=
null
);
assert
(
generator
!=
null
);
// Mark all entries as possibly deleted.
for
(
DevFSContent
content
in
_entries
.
values
)
{
content
.
_exists
=
false
;
}
// Scan workspace, packages, and assets
printTrace
(
'DevFS: Starting sync from
$rootDirectory
'
);
logger
.
printTrace
(
'Scanning project files'
);
await
_scanDirectory
(
rootDirectory
,
recursive:
true
,
fileFilter:
fileFilter
);
if
(
fs
.
isFileSync
(
_packagesFilePath
))
{
printTrace
(
'Scanning package files'
);
await
_scanPackages
(
fileFilter
);
}
if
(
bundle
!=
null
)
{
printTrace
(
'Scanning asset files'
);
// We write the assets into the AssetBundle working dir so that they
// are in the same location in DevFS and the iOS simulator.
final
String
assetDirectory
=
getAssetBuildDirectory
();
bundle
.
entries
.
forEach
((
String
archivePath
,
DevFSContent
content
)
{
_scanBundleEntry
(
archivePath
,
content
);
final
Uri
deviceUri
=
fs
.
path
.
toUri
(
fs
.
path
.
join
(
assetDirectory
,
archivePath
));
_entries
[
deviceUri
]
=
content
;
});
}
// Handle deletions.
printTrace
(
'Scanning for deleted files'
);
// Update modified files
final
String
assetBuildDirPrefix
=
_asUriPath
(
getAssetBuildDirectory
());
final
List
<
Uri
>
toRemove
=
<
Uri
>[];
_entries
.
forEach
((
Uri
deviceUri
,
DevFSContent
content
)
{
if
(!
content
.
_exists
)
{
final
Future
<
Map
<
String
,
dynamic
>>
operation
=
_operations
.
deleteFile
(
fsName
,
deviceUri
)
.
then
<
Map
<
String
,
dynamic
>>((
dynamic
v
)
=>
v
?.
cast
<
String
,
dynamic
>());
if
(
operation
!=
null
)
_pendingOperations
.
add
(
operation
);
toRemove
.
add
(
deviceUri
);
if
(
deviceUri
.
path
.
startsWith
(
assetBuildDirPrefix
))
{
final
String
archivePath
=
deviceUri
.
path
.
substring
(
assetBuildDirPrefix
.
length
);
assetPathsToEvict
.
add
(
archivePath
);
}
}
});
if
(
toRemove
.
isNotEmpty
)
{
printTrace
(
'Removing deleted files'
);
toRemove
.
forEach
(
_entries
.
remove
);
await
Future
.
wait
<
Map
<
String
,
dynamic
>>(
_pendingOperations
);
_pendingOperations
.
clear
();
}
final
Map
<
Uri
,
DevFSContent
>
dirtyEntries
=
<
Uri
,
DevFSContent
>{};
// Update modified files
int
syncedBytes
=
0
;
final
Map
<
Uri
,
DevFSContent
>
dirtyEntries
=
<
Uri
,
DevFSContent
>{};
_entries
.
forEach
((
Uri
deviceUri
,
DevFSContent
content
)
{
String
archivePath
;
if
(
deviceUri
.
path
.
startsWith
(
assetBuildDirPrefix
))
...
...
@@ -527,30 +474,10 @@ class DevFS {
assetPathsToEvict
.
add
(
archivePath
);
}
});
// We run generator even if [dirtyEntries] was empty because we want to
// keep logic of accepting/rejecting generator's output simple: we must
// accept/reject generator's output after every [update] call. Incremental
// run with no changes is supposed to be fast (considering that it is
// initiated by user key press).
final
List
<
String
>
invalidatedFiles
=
<
String
>[];
final
Set
<
Uri
>
filesUris
=
<
Uri
>{};
for
(
Uri
uri
in
dirtyEntries
.
keys
.
toList
())
{
if
(!
uri
.
path
.
startsWith
(
assetBuildDirPrefix
))
{
final
DevFSContent
content
=
dirtyEntries
[
uri
];
if
(
content
is
DevFSFileContent
)
{
filesUris
.
add
(
uri
);
invalidatedFiles
.
add
(
content
.
file
.
uri
.
toString
());
syncedBytes
-=
content
.
size
;
}
}
}
// No need to send source files because all compilation is done on the
// host and result of compilation is single kernel file.
filesUris
.
forEach
(
dirtyEntries
.
remove
);
printTrace
(
'Compiling dart to kernel with
${invalidatedFiles.length}
updated files'
);
if
(
fullRestart
)
{
generator
.
reset
();
}
printTrace
(
'Compiling dart to kernel with
${invalidatedFiles.length}
updated files'
);
final
CompilerOutput
compilerOutput
=
await
generator
.
recompile
(
mainPath
,
invalidatedFiles
,
...
...
@@ -566,16 +493,13 @@ class DevFS {
?
fs
.
path
.
relative
(
pathToReload
,
from:
projectRootPath
)
:
pathToReload
,
);
if
(!
dirtyEntries
.
containsKey
(
entryUri
))
{
final
DevFSFileContent
content
=
DevFSFileContent
(
fs
.
file
(
compiledBinary
));
dirtyEntries
[
entryUri
]
=
content
;
syncedBytes
+=
content
.
size
;
dirtyEntries
[
entryUri
]
=
content
;
}
}
}
if
(
dirtyEntries
.
isNotEmpty
)
{
printTrace
(
'Updating files'
);
if
(
_httpWriter
!=
null
)
{
if
(
dirtyEntries
.
isNotEmpty
)
{
try
{
await
_httpWriter
.
write
(
dirtyEntries
);
}
on
SocketException
catch
(
socketException
,
stackTrace
)
{
...
...
@@ -585,209 +509,12 @@ class DevFS {
printError
(
'Could not update files on device:
$exception
'
);
throw
DevFSException
(
'Sync failed'
,
exception
,
stackTrace
);
}
}
else
{
// Make service protocol requests for each.
dirtyEntries
.
forEach
((
Uri
deviceUri
,
DevFSContent
content
)
{
final
Future
<
Map
<
String
,
dynamic
>>
operation
=
_operations
.
writeFile
(
fsName
,
deviceUri
,
content
)
.
then
<
Map
<
String
,
dynamic
>>((
dynamic
v
)
=>
v
?.
cast
<
String
,
dynamic
>());
if
(
operation
!=
null
)
_pendingOperations
.
add
(
operation
);
});
await
Future
.
wait
<
Map
<
String
,
dynamic
>>(
_pendingOperations
,
eagerError:
true
);
_pendingOperations
.
clear
();
}
}
printTrace
(
'DevFS: Sync finished'
);
return
UpdateFSReport
(
success:
true
,
syncedBytes:
syncedBytes
,
invalidatedSourcesCount:
invalidatedFiles
.
length
);
}
void
_scanFile
(
Uri
deviceUri
,
FileSystemEntity
file
)
{
final
DevFSContent
content
=
_entries
.
putIfAbsent
(
deviceUri
,
()
=>
DevFSFileContent
(
file
));
content
.
_exists
=
true
;
}
void
_scanBundleEntry
(
String
archivePath
,
DevFSContent
content
)
{
// We write the assets into the AssetBundle working dir so that they
// are in the same location in DevFS and the iOS simulator.
final
Uri
deviceUri
=
fs
.
path
.
toUri
(
fs
.
path
.
join
(
getAssetBuildDirectory
(),
archivePath
));
_entries
[
deviceUri
]
=
content
;
content
.
_exists
=
true
;
}
bool
_shouldIgnore
(
Uri
deviceUri
)
{
final
List
<
String
>
ignoredUriPrefixes
=
<
String
>[
'android/'
,
_asUriPath
(
getBuildDirectory
()),
'ios/'
,
'.pub/'
];
for
(
String
ignoredUriPrefix
in
ignoredUriPrefixes
)
{
if
(
deviceUri
.
path
.
startsWith
(
ignoredUriPrefix
))
return
true
;
}
return
false
;
}
bool
_shouldSkip
(
FileSystemEntity
file
,
String
relativePath
,
Uri
directoryUriOnDevice
,
{
bool
ignoreDotFiles
=
true
,
})
{
if
(
file
is
Directory
)
{
// Skip non-files.
return
true
;
}
assert
((
file
is
Link
)
||
(
file
is
File
));
final
String
basename
=
fs
.
path
.
basename
(
file
.
path
);
if
(
ignoreDotFiles
&&
basename
.
startsWith
(
'.'
))
{
// Skip dot files, but not the '.packages' file (even though in dart1
// mode devfs['.packages'] will be overwritten with synthesized string content).
return
basename
!=
'.packages'
;
}
return
false
;
}
Uri
_directoryUriOnDevice
(
Uri
directoryUriOnDevice
,
Directory
directory
)
{
if
(
directoryUriOnDevice
==
null
)
{
final
String
relativeRootPath
=
fs
.
path
.
relative
(
directory
.
path
,
from:
rootDirectory
.
path
);
if
(
relativeRootPath
==
'.'
)
{
directoryUriOnDevice
=
Uri
();
}
else
{
directoryUriOnDevice
=
fs
.
path
.
toUri
(
relativeRootPath
);
}
}
return
directoryUriOnDevice
;
}
/// Scan all files from the [fileFilter] that are contained in [directory] and
/// pass various filters (e.g. ignoreDotFiles).
Future
<
bool
>
_scanFilteredDirectory
(
Set
<
String
>
fileFilter
,
Directory
directory
,
{
Uri
directoryUriOnDevice
,
bool
ignoreDotFiles
=
true
,
})
async
{
directoryUriOnDevice
=
_directoryUriOnDevice
(
directoryUriOnDevice
,
directory
);
try
{
final
String
absoluteDirectoryPath
=
canonicalizePath
(
directory
.
path
);
// For each file in the file filter.
for
(
String
filePath
in
fileFilter
)
{
if
(!
filePath
.
startsWith
(
absoluteDirectoryPath
))
{
// File is not in this directory. Skip.
continue
;
}
final
String
relativePath
=
fs
.
path
.
relative
(
filePath
,
from:
directory
.
path
);
final
FileSystemEntity
file
=
fs
.
file
(
filePath
);
if
(
_shouldSkip
(
file
,
relativePath
,
directoryUriOnDevice
,
ignoreDotFiles:
ignoreDotFiles
))
{
continue
;
}
final
Uri
deviceUri
=
directoryUriOnDevice
.
resolveUri
(
fs
.
path
.
toUri
(
relativePath
));
if
(!
_shouldIgnore
(
deviceUri
))
_scanFile
(
deviceUri
,
file
);
}
}
on
FileSystemException
catch
(
e
)
{
_printScanDirectoryError
(
directory
.
path
,
e
);
return
false
;
}
return
true
;
}
/// Scan all files in [directory] that pass various filters (e.g. ignoreDotFiles).
Future
<
bool
>
_scanDirectory
(
Directory
directory
,
{
Uri
directoryUriOnDevice
,
bool
recursive
=
false
,
bool
ignoreDotFiles
=
true
,
Set
<
String
>
fileFilter
,
})
async
{
directoryUriOnDevice
=
_directoryUriOnDevice
(
directoryUriOnDevice
,
directory
);
if
((
fileFilter
!=
null
)
&&
fileFilter
.
isNotEmpty
)
{
// When the fileFilter isn't empty, we can skip crawling the directory
// tree and instead use the fileFilter as the source of potential files.
return
_scanFilteredDirectory
(
fileFilter
,
directory
,
directoryUriOnDevice:
directoryUriOnDevice
,
ignoreDotFiles:
ignoreDotFiles
);
}
try
{
final
Stream
<
FileSystemEntity
>
files
=
directory
.
list
(
recursive:
recursive
,
followLinks:
false
);
await
for
(
FileSystemEntity
file
in
files
)
{
if
(!
devFSConfig
.
noDirectorySymlinks
&&
(
file
is
Link
))
{
// Check if this is a symlink to a directory and skip it.
try
{
final
FileSystemEntityType
linkType
=
fs
.
statSync
(
file
.
resolveSymbolicLinksSync
()).
type
;
if
(
linkType
==
FileSystemEntityType
.
directory
)
continue
;
}
on
FileSystemException
catch
(
e
)
{
_printScanDirectoryError
(
file
.
path
,
e
);
continue
;
}
}
final
String
relativePath
=
fs
.
path
.
relative
(
file
.
path
,
from:
directory
.
path
);
if
(
_shouldSkip
(
file
,
relativePath
,
directoryUriOnDevice
,
ignoreDotFiles:
ignoreDotFiles
))
{
continue
;
}
final
Uri
deviceUri
=
directoryUriOnDevice
.
resolveUri
(
fs
.
path
.
toUri
(
relativePath
));
if
(!
_shouldIgnore
(
deviceUri
))
_scanFile
(
deviceUri
,
file
);
}
}
on
FileSystemException
catch
(
e
)
{
_printScanDirectoryError
(
directory
.
path
,
e
);
return
false
;
}
return
true
;
}
void
_printScanDirectoryError
(
String
path
,
Exception
e
)
{
printError
(
'Error while scanning
$path
.
\n
'
'Hot Reload might not work until the following error is resolved:
\n
'
'
$e
\n
'
);
}
Future
<
void
>
_scanPackages
(
Set
<
String
>
fileFilter
)
async
{
StringBuffer
sb
;
final
PackageMap
packageMap
=
PackageMap
(
_packagesFilePath
);
for
(
String
packageName
in
packageMap
.
map
.
keys
)
{
final
Uri
packageUri
=
packageMap
.
map
[
packageName
];
final
String
packagePath
=
fs
.
path
.
fromUri
(
packageUri
);
final
Directory
packageDirectory
=
fs
.
directory
(
packageUri
);
Uri
directoryUriOnDevice
=
fs
.
path
.
toUri
(
fs
.
path
.
join
(
'packages'
,
packageName
)
+
fs
.
path
.
separator
);
bool
packageExists
=
packageDirectory
.
existsSync
();
if
(!
packageExists
)
{
// If the package directory doesn't exist at all, we ignore it.
continue
;
}
if
(
fs
.
path
.
isWithin
(
rootDirectory
.
path
,
packagePath
))
{
// We already scanned everything under the root directory.
directoryUriOnDevice
=
fs
.
path
.
toUri
(
fs
.
path
.
relative
(
packagePath
,
from:
rootDirectory
.
path
)
+
fs
.
path
.
separator
);
}
else
{
packageExists
=
await
_scanDirectory
(
packageDirectory
,
directoryUriOnDevice:
directoryUriOnDevice
,
recursive:
true
,
fileFilter:
fileFilter
);
}
if
(
packageExists
)
{
sb
??=
StringBuffer
();
sb
.
writeln
(
'
$packageName
:
$directoryUriOnDevice
'
);
}
}
}
}
/// Converts a platform-specific file path to a platform-independent Uri path.
String
_asUriPath
(
String
filePath
)
=>
fs
.
path
.
toUri
(
filePath
).
path
+
'/'
;
packages/flutter_tools/lib/src/resident_runner.dart
View file @
cd803ac7
...
...
@@ -18,9 +18,7 @@ import 'base/utils.dart';
import
'build_info.dart'
;
import
'codegen.dart'
;
import
'compile.dart'
;
import
'dart/dependencies.dart'
;
import
'dart/package_map.dart'
;
import
'dependency_checker.dart'
;
import
'devfs.dart'
;
import
'device.dart'
;
import
'globals.dart'
;
...
...
@@ -444,10 +442,10 @@ class FlutterDevice {
DateTime
firstBuildTime
,
bool
bundleFirstUpload
=
false
,
bool
bundleDirty
=
false
,
Set
<
String
>
fileFilter
,
bool
fullRestart
=
false
,
String
projectRootPath
,
String
pathToReload
,
@required
List
<
String
>
invalidatedFiles
,
})
async
{
final
Status
devFSStatus
=
logger
.
startProgress
(
'Syncing files to device
${device.name}
...'
,
...
...
@@ -462,13 +460,13 @@ class FlutterDevice {
firstBuildTime:
firstBuildTime
,
bundleFirstUpload:
bundleFirstUpload
,
bundleDirty:
bundleDirty
,
fileFilter:
fileFilter
,
generator:
generator
,
fullRestart:
fullRestart
,
dillOutputPath:
dillOutputPath
,
trackWidgetCreation:
trackWidgetCreation
,
projectRootPath:
projectRootPath
,
pathToReload:
pathToReload
,
invalidatedFiles:
invalidatedFiles
,
);
}
on
DevFSException
{
devFSStatus
.
cancel
();
...
...
@@ -945,21 +943,19 @@ abstract class ResidentRunner {
}
bool
hasDirtyDependencies
(
FlutterDevice
device
)
{
/// When using the build system, dependency analysis is handled by build
/// runner instead.
if
(
experimentalBuildEnabled
)
{
return
false
;
}
final
DartDependencySetBuilder
dartDependencySetBuilder
=
DartDependencySetBuilder
(
mainPath
,
packagesFilePath
);
final
DependencyChecker
dependencyChecker
=
DependencyChecker
(
dartDependencySetBuilder
,
assetBundle
);
if
(
device
.
package
.
packagesFile
==
null
||
!
device
.
package
.
packagesFile
.
existsSync
())
{
return
true
;
}
final
DateTime
lastBuildTime
=
device
.
package
.
packagesFile
.
statSync
().
modified
;
return
dependencyChecker
.
check
(
lastBuildTime
);
// Leave pubspec null to check all dependencies.
final
ProjectFileInvalidator
projectFileInvalidator
=
ProjectFileInvalidator
(
device
.
package
.
packagesFile
.
path
,
null
);
projectFileInvalidator
.
findInvalidated
();
final
int
lastBuildTime
=
device
.
package
.
packagesFile
.
statSync
().
modified
.
millisecondsSinceEpoch
;
for
(
int
updateTime
in
projectFileInvalidator
.
updateTime
.
values
)
{
if
(
updateTime
>
lastBuildTime
)
{
return
true
;
}
}
return
false
;
}
Future
<
void
>
preStop
()
async
{
}
...
...
packages/flutter_tools/lib/src/run_hot.dart
View file @
cd803ac7
...
...
@@ -7,29 +7,28 @@ import 'dart:async';
import
'package:json_rpc_2/error_code.dart'
as
rpc_error_code
;
import
'package:json_rpc_2/json_rpc_2.dart'
as
rpc
;
import
'package:meta/meta.dart'
;
import
'package:yaml/yaml.dart'
;
import
'base/common.dart'
;
import
'base/context.dart'
;
import
'base/file_system.dart'
;
import
'base/logger.dart'
;
import
'base/platform.dart'
;
import
'base/terminal.dart'
;
import
'base/utils.dart'
;
import
'build_info.dart'
;
import
'codegen.dart'
;
import
'compile.dart'
;
import
'convert.dart'
;
import
'dart/dependencies.dart'
;
import
'dart/pub.dart'
;
import
'dart/package_map.dart'
;
import
'devfs.dart'
;
import
'device.dart'
;
import
'globals.dart'
;
import
'project.dart'
;
import
'resident_runner.dart'
;
import
'usage.dart'
;
import
'vmservice.dart'
;
class
HotRunnerConfig
{
/// Should the hot runner compute the minimal Dart dependencies?
bool
computeDartDependencies
=
true
;
/// Should the hot runner assume that the minimal Dart dependencies do not change?
bool
stableDartDependencies
=
false
;
/// A hook for implementations to perform any necessary initialization prior
...
...
@@ -71,6 +70,7 @@ class HotRunner extends ResidentRunner {
bool
saveCompilationTrace
=
false
,
bool
stayResident
=
true
,
bool
ipv6
=
false
,
FlutterProject
flutterProject
,
})
:
super
(
devices
,
target:
target
,
debuggingOptions:
debuggingOptions
,
...
...
@@ -79,14 +79,19 @@ class HotRunner extends ResidentRunner {
packagesFilePath:
packagesFilePath
,
saveCompilationTrace:
saveCompilationTrace
,
stayResident:
stayResident
,
ipv6:
ipv6
);
ipv6:
ipv6
)
{
fileInvalidator
=
ProjectFileInvalidator
(
packagesFilePath
??
fs
.
path
.
absolute
(
PackageMap
.
globalPackagesPath
),
flutterProject
,
);
}
final
bool
benchmarkMode
;
final
File
applicationBinary
;
final
bool
hostIsIde
;
bool
_didAttach
=
false
;
Set
<
String
>
_dartDependencies
;
final
String
dillOutputPath
;
ProjectFileInvalidator
fileInvalidator
;
final
Map
<
String
,
List
<
int
>>
benchmarkData
=
<
String
,
List
<
int
>>{};
// The initial launch is from a snapshot.
...
...
@@ -98,54 +103,8 @@ class HotRunner extends ResidentRunner {
benchmarkData
[
name
].
add
(
value
);
}
Future
<
bool
>
_refreshDartDependencies
()
async
{
if
(!
hotRunnerConfig
.
computeDartDependencies
)
{
// Disabled.
return
true
;
}
if
(
_dartDependencies
!=
null
)
{
// Already computed.
return
true
;
}
try
{
// Will return immediately if pubspec.yaml is up-to-date.
await
pubGet
(
context:
PubContext
.
pubGet
,
directory:
projectRootPath
,
);
}
on
ToolExit
catch
(
error
)
{
printError
(
'Unable to reload your application because "flutter packages get" failed to update '
'package dependencies.
\n
'
'
$error
'
);
return
false
;
}
/// When using the build system, dependency analysis is handled by build
/// runner instead.
if
(
experimentalBuildEnabled
)
{
return
true
;
}
final
DartDependencySetBuilder
dartDependencySetBuilder
=
DartDependencySetBuilder
(
mainPath
,
packagesFilePath
);
try
{
_dartDependencies
=
Set
<
String
>.
from
(
dartDependencySetBuilder
.
build
());
}
on
DartDependencyException
catch
(
error
)
{
printError
(
'Your application could not be compiled, because its dependencies could not be established.
\n
'
'
$error
'
);
return
false
;
}
return
true
;
}
Future
<
void
>
_reloadSourcesService
(
String
isolateId
,
{
bool
force
=
false
,
bool
pause
=
false
,
})
async
{
Future
<
void
>
_reloadSourcesService
(
String
isolateId
,
{
bool
force
=
false
,
bool
pause
=
false
})
async
{
// TODO(cbernaschina): check that isolateId is the id of the UI isolate.
final
OperationResult
result
=
await
restart
(
pauseAfterRestart:
pause
);
if
(!
result
.
isOk
)
{
...
...
@@ -257,7 +216,6 @@ class HotRunner extends ResidentRunner {
// Measure time to perform a hot restart.
printStatus
(
'Benchmarking hot restart'
);
await
restart
(
fullRestart:
true
);
// TODO(johnmccutchan): Modify script entry point.
printStatus
(
'Benchmarking hot reload'
);
// Measure time to perform a hot reload.
await
restart
(
fullRestart:
false
);
...
...
@@ -296,12 +254,6 @@ class HotRunner extends ResidentRunner {
return
1
;
}
// Determine the Dart dependencies eagerly.
if
(!
await
_refreshDartDependencies
())
{
// Some kind of source level error or missing file in the Dart code.
return
1
;
}
firstBuildTime
=
DateTime
.
now
();
for
(
FlutterDevice
device
in
flutterDevices
)
{
...
...
@@ -336,8 +288,6 @@ class HotRunner extends ResidentRunner {
result
=
await
restart
(
fullRestart:
false
);
}
if
(!
result
.
isOk
)
{
// TODO(johnmccutchan): Attempt to determine the number of errors that
// occurred and tighten this message.
printStatus
(
'Try again after fixing the above error(s).'
,
emphasis:
true
);
}
}
else
if
(
lower
==
'l'
)
{
...
...
@@ -364,10 +314,6 @@ class HotRunner extends ResidentRunner {
}
Future
<
UpdateFSReport
>
_updateDevFS
({
bool
fullRestart
=
false
})
async
{
if
(!
await
_refreshDartDependencies
())
{
// Did not update DevFS because of a Dart source error.
return
UpdateFSReport
(
success:
false
);
}
final
bool
isFirstUpload
=
assetBundle
.
wasBuiltOnce
()
==
false
;
final
bool
rebuildBundle
=
assetBundle
.
needsBuild
();
if
(
rebuildBundle
)
{
...
...
@@ -376,7 +322,7 @@ class HotRunner extends ResidentRunner {
if
(
result
!=
0
)
return
UpdateFSReport
(
success:
false
);
}
final
List
<
String
>
invalidatedFiles
=
fileInvalidator
.
findInvalidated
();
final
UpdateFSReport
results
=
UpdateFSReport
(
success:
true
);
for
(
FlutterDevice
device
in
flutterDevices
)
{
results
.
incorporateResults
(
await
device
.
updateDevFS
(
...
...
@@ -386,39 +332,15 @@ class HotRunner extends ResidentRunner {
firstBuildTime:
firstBuildTime
,
bundleFirstUpload:
isFirstUpload
,
bundleDirty:
isFirstUpload
==
false
&&
rebuildBundle
,
fileFilter:
_dartDependencies
,
fullRestart:
fullRestart
,
projectRootPath:
projectRootPath
,
pathToReload:
getReloadPath
(
fullRestart:
fullRestart
),
invalidatedFiles:
invalidatedFiles
,
));
}
if
(!
results
.
success
)
{
return
results
;
}
if
(!
hotRunnerConfig
.
stableDartDependencies
)
{
// Clear the set after the sync so they are recomputed next time.
_dartDependencies
=
null
;
}
return
results
;
}
Future
<
void
>
_evictDirtyAssets
()
{
final
List
<
Future
<
Map
<
String
,
dynamic
>>>
futures
=
<
Future
<
Map
<
String
,
dynamic
>>>[];
for
(
FlutterDevice
device
in
flutterDevices
)
{
if
(
device
.
devFS
.
assetPathsToEvict
.
isEmpty
)
continue
;
if
(
device
.
views
.
first
.
uiIsolate
==
null
)
{
printError
(
'Application isolate not found for
$device
'
);
continue
;
}
for
(
String
assetPath
in
device
.
devFS
.
assetPathsToEvict
)
futures
.
add
(
device
.
views
.
first
.
uiIsolate
.
flutterEvictAsset
(
assetPath
));
device
.
devFS
.
assetPathsToEvict
.
clear
();
}
return
Future
.
wait
<
Map
<
String
,
dynamic
>>(
futures
);
}
void
_resetDirtyAssets
()
{
for
(
FlutterDevice
device
in
flutterDevices
)
device
.
devFS
.
assetPathsToEvict
.
clear
();
...
...
@@ -811,9 +733,9 @@ class HotRunner extends ResidentRunner {
return
OperationResult
(
OperationResult
.
ok
.
code
,
reloadMessage
);
}
}
assert
(
reassembleViews
.
isNotEmpty
);
printTrace
(
'Evicting dirty assets'
);
await
_evictDirtyAssets
();
assert
(
reassembleViews
.
isNotEmpty
);
printTrace
(
'Reassembling application'
);
bool
failedReassemble
=
false
;
final
List
<
Future
<
void
>>
futures
=
<
Future
<
void
>>[];
...
...
@@ -886,7 +808,6 @@ class HotRunner extends ResidentRunner {
// Only report timings if we reloaded a single view without any errors.
if
((
reassembleViews
.
length
==
1
)
&&
!
failedReassemble
&&
shouldReportReloadTime
)
flutterUsage
.
sendTiming
(
'hot'
,
'reload'
,
reloadDuration
);
return
OperationResult
(
failedReassemble
?
1
:
OperationResult
.
ok
.
code
,
reloadMessage
,
...
...
@@ -965,6 +886,23 @@ class HotRunner extends ResidentRunner {
}
}
Future
<
void
>
_evictDirtyAssets
()
{
final
List
<
Future
<
Map
<
String
,
dynamic
>>>
futures
=
<
Future
<
Map
<
String
,
dynamic
>>>[];
for
(
FlutterDevice
device
in
flutterDevices
)
{
if
(
device
.
devFS
.
assetPathsToEvict
.
isEmpty
)
continue
;
if
(
device
.
views
.
first
.
uiIsolate
==
null
)
{
printError
(
'Application isolate not found for
$device
'
);
continue
;
}
for
(
String
assetPath
in
device
.
devFS
.
assetPathsToEvict
)
{
futures
.
add
(
device
.
views
.
first
.
uiIsolate
.
flutterEvictAsset
(
assetPath
));
}
device
.
devFS
.
assetPathsToEvict
.
clear
();
}
return
Future
.
wait
<
Map
<
String
,
dynamic
>>(
futures
);
}
@override
Future
<
void
>
cleanupAfterSignal
()
async
{
await
stopEchoingDeviceLog
();
...
...
@@ -988,3 +926,106 @@ class HotRunner extends ResidentRunner {
await
stopEchoingDeviceLog
();
}
}
class
ProjectFileInvalidator
{
ProjectFileInvalidator
(
this
.
_packagesPath
,
this
.
_flutterProject
)
{
final
File
packagesFile
=
fs
.
file
(
_packagesPath
);
if
(
packagesFile
.
existsSync
())
{
_packagesUpdateTime
=
packagesFile
.
statSync
().
modified
.
millisecondsSinceEpoch
;
_packageMap
=
PackageMap
(
_packagesPath
).
map
;
}
else
{
_packagesUpdateTime
=
-
1
;
_packageMap
=
const
<
String
,
Uri
>{};
}
_computePackageMap
(
_packageMap
,
_flutterProject
);
}
// Used to avoid watching pubspec directories. This will not change even with pub upgrade,
// because that actually switches the directory and requires a corresponding
// update to .packages
static
const
String
_pubCachePathLinuxAndWindows
=
'.pub-cache'
;
static
const
String
_pubCachePathWindows
=
'Pub/Cache'
;
Map
<
String
,
Uri
>
_packageMap
;
final
String
_packagesPath
;
final
FlutterProject
_flutterProject
;
final
Map
<
String
,
int
>
_updateTime
=
<
String
,
int
>{};
int
_packagesUpdateTime
;
Map
<
String
,
int
>
get
updateTime
=>
_updateTime
;
@visibleForTesting
Map
<
String
,
Uri
>
get
packageMap
=>
_packageMap
;
static
void
_computePackageMap
(
Map
<
String
,
Uri
>
packageMap
,
FlutterProject
flutterProject
)
{
if
(
flutterProject
!=
null
&&
flutterProject
.
pubspecFile
.
existsSync
())
{
try
{
final
YamlMap
pubspec
=
loadYamlDocument
(
flutterProject
.
pubspecFile
.
readAsStringSync
()).
contents
;
final
YamlMap
dependencies
=
pubspec
[
'dependencies'
];
final
Set
<
String
>
relevantDependencies
=
Set
<
String
>.
from
(
dependencies
.
keys
);
// Remove any packages which were tagged as dev dependenices,
// But don't remove the app itself!
for
(
String
packageName
in
packageMap
.
keys
.
toList
())
{
if
(!
relevantDependencies
.
contains
(
packageName
)
&&
packageName
!=
flutterProject
.
manifest
.
appName
)
{
packageMap
.
remove
(
packageName
);
continue
;
}
}
}
catch
(
err
)
{
// If we detect a pubspec formatting problem, fallback to the packages file.
}
}
// Remove any packages which are derived from the pub cache.
for
(
String
packageName
in
packageMap
.
keys
.
toList
())
{
final
String
path
=
packageMap
[
packageName
].
path
;
if
((
platform
.
isWindows
&&
path
.
contains
(
_pubCachePathWindows
))
||
path
.
contains
(
_pubCachePathLinuxAndWindows
))
{
packageMap
.
remove
(
packageName
);
}
}
}
List
<
String
>
findInvalidated
()
{
final
File
packagesFile
=
fs
.
file
(
_packagesPath
);
if
(
packagesFile
.
existsSync
())
{
final
int
newPackagesUpdateTime
=
packagesFile
.
statSync
().
modified
.
millisecondsSinceEpoch
;
// Hot reloading with an updated package will often times kill a non-trivial
// appliction. This _might_ work, given certain application size and package
// constraints, so instead of exiting we print a warning so that the user has
// some hint on what went wrong.
if
(
newPackagesUpdateTime
>
_packagesUpdateTime
)
{
printError
(
'Warning: updated dependencies detected. The Flutter application will require a restart to safely use new packages.'
);
}
_packagesUpdateTime
=
newPackagesUpdateTime
;
}
final
List
<
String
>
invalidatedFiles
=
<
String
>[];
for
(
String
packageName
in
_packageMap
.
keys
)
{
final
Uri
packageUri
=
_packageMap
[
packageName
];
_scanDirectory
(
packageUri
,
invalidatedFiles
);
}
return
invalidatedFiles
;
}
void
_scanDirectory
(
Uri
path
,
List
<
String
>
invalidatedFiles
)
{
final
Directory
directory
=
fs
.
directory
(
path
);
if
(!
directory
.
existsSync
())
{
return
;
}
for
(
FileSystemEntity
entity
in
directory
.
listSync
(
recursive:
true
))
{
if
(
entity
.
path
.
endsWith
(
'.dart'
))
{
final
int
oldUpdatedAt
=
_updateTime
[
entity
.
path
];
final
int
updatedAt
=
fs
.
statSync
(
entity
.
path
).
modified
.
millisecondsSinceEpoch
;
if
(
oldUpdatedAt
==
null
||
updatedAt
>
oldUpdatedAt
)
{
// On windows convert to file uri in expected format.
if
(
platform
.
isWindows
)
{
final
Uri
uri
=
Uri
.
file
(
entity
.
path
,
windows:
platform
.
isWindows
);
invalidatedFiles
.
add
(
uri
.
toString
());
}
else
{
invalidatedFiles
.
add
(
entity
.
path
);
}
}
_updateTime
[
entity
.
path
]
=
updatedAt
;
}
}
}
}
packages/flutter_tools/pubspec.yaml
View file @
cd803ac7
...
...
@@ -9,7 +9,6 @@ environment:
dependencies
:
# To update these, use "flutter update-packages --force-upgrade".
analyzer
:
0.35.3
archive
:
2.0.8
args
:
1.5.1
bsdiff
:
0.1.0
...
...
@@ -53,6 +52,7 @@ dependencies:
build_modules
:
1.0.9
build_daemon
:
0.4.2
analyzer
:
0.35.3
# THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
async
:
2.0.8
# THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
bazel_worker
:
0.1.20
# THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
boolean_selector
:
1.0.4
# THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
...
...
@@ -100,8 +100,8 @@ dev_dependencies:
collection
:
1.14.11
mockito
:
4.0.0
file_testing
:
2.1.0
test
:
1.5.3
vm_service_lib
:
3.14.2
test
:
1.5.3
build_runner
:
1.2.8
build_vm_compilers
:
0.1.1+5
build_test
:
0.10.6
...
...
packages/flutter_tools/test/commands/attach_test.dart
View file @
cd803ac7
...
...
@@ -120,6 +120,7 @@ void main() {
debuggingOptions:
anyNamed
(
'debuggingOptions'
),
packagesFilePath:
anyNamed
(
'packagesFilePath'
),
usesTerminalUI:
anyNamed
(
'usesTerminalUI'
),
flutterProject:
anyNamed
(
'flutterProject'
),
ipv6:
false
,
),
).
thenReturn
(
mockHotRunner
);
...
...
@@ -151,6 +152,7 @@ void main() {
debuggingOptions:
anyNamed
(
'debuggingOptions'
),
packagesFilePath:
anyNamed
(
'packagesFilePath'
),
usesTerminalUI:
anyNamed
(
'usesTerminalUI'
),
flutterProject:
anyNamed
(
'flutterProject'
),
ipv6:
false
,
),
)..
called
(
1
);
...
...
@@ -225,6 +227,7 @@ void main() {
debuggingOptions:
anyNamed
(
'debuggingOptions'
),
packagesFilePath:
anyNamed
(
'packagesFilePath'
),
usesTerminalUI:
anyNamed
(
'usesTerminalUI'
),
flutterProject:
anyNamed
(
'flutterProject'
),
ipv6:
false
,
)).
thenReturn
(
mockHotRunner
);
...
...
@@ -254,6 +257,7 @@ void main() {
debuggingOptions:
anyNamed
(
'debuggingOptions'
),
packagesFilePath:
anyNamed
(
'packagesFilePath'
),
usesTerminalUI:
anyNamed
(
'usesTerminalUI'
),
flutterProject:
anyNamed
(
'flutterProject'
),
ipv6:
false
,
)).
called
(
1
);
},
overrides:
<
Type
,
Generator
>{
...
...
packages/flutter_tools/test/dart_dependencies_test.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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
'package:flutter_tools/src/dart/dependencies.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'src/common.dart'
;
import
'src/context.dart'
;
void
main
(
)
{
group
(
'DartDependencySetBuilder'
,
()
{
final
String
dataPath
=
fs
.
path
.
join
(
getFlutterRoot
(),
'packages'
,
'flutter_tools'
,
'test'
,
'data'
,
'dart_dependencies_test'
,
);
testUsingContext
(
'good'
,
()
{
final
String
testPath
=
fs
.
path
.
join
(
dataPath
,
'good'
);
final
String
mainPath
=
fs
.
path
.
join
(
testPath
,
'main.dart'
);
final
String
packagesPath
=
fs
.
path
.
join
(
testPath
,
'.packages'
);
final
DartDependencySetBuilder
builder
=
DartDependencySetBuilder
(
mainPath
,
packagesPath
);
final
Set
<
String
>
dependencies
=
builder
.
build
();
expect
(
dependencies
.
contains
(
canonicalizePath
(
mainPath
)),
isTrue
);
expect
(
dependencies
.
contains
(
canonicalizePath
(
fs
.
path
.
join
(
testPath
,
'foo.dart'
))),
isTrue
);
});
testUsingContext
(
'syntax_error'
,
()
{
final
String
testPath
=
fs
.
path
.
join
(
dataPath
,
'syntax_error'
);
final
String
mainPath
=
fs
.
path
.
join
(
testPath
,
'main.dart'
);
final
String
packagesPath
=
fs
.
path
.
join
(
testPath
,
'.packages'
);
final
DartDependencySetBuilder
builder
=
DartDependencySetBuilder
(
mainPath
,
packagesPath
);
try
{
builder
.
build
();
fail
(
'expect an exception to be thrown.'
);
}
on
DartDependencyException
catch
(
error
)
{
expect
(
error
.
toString
(),
contains
(
'foo.dart: Expected a string literal'
));
}
});
testUsingContext
(
'bad_path'
,
()
{
final
String
testPath
=
fs
.
path
.
join
(
dataPath
,
'bad_path'
);
final
String
mainPath
=
fs
.
path
.
join
(
testPath
,
'main.dart'
);
final
String
packagesPath
=
fs
.
path
.
join
(
testPath
,
'.packages'
);
final
DartDependencySetBuilder
builder
=
DartDependencySetBuilder
(
mainPath
,
packagesPath
);
try
{
builder
.
build
();
fail
(
'expect an exception to be thrown.'
);
}
on
DartDependencyException
catch
(
error
)
{
expect
(
error
.
toString
(),
contains
(
'amaze
${fs.path.separator}
and
${fs.path.separator}
astonish.dart'
));
}
});
testUsingContext
(
'bad_package'
,
()
{
final
String
testPath
=
fs
.
path
.
join
(
dataPath
,
'bad_package'
);
final
String
mainPath
=
fs
.
path
.
join
(
testPath
,
'main.dart'
);
final
String
packagesPath
=
fs
.
path
.
join
(
testPath
,
'.packages'
);
final
DartDependencySetBuilder
builder
=
DartDependencySetBuilder
(
mainPath
,
packagesPath
);
try
{
builder
.
build
();
fail
(
'expect an exception to be thrown.'
);
}
on
DartDependencyException
catch
(
error
)
{
expect
(
error
.
toString
(),
contains
(
'rochambeau'
));
expect
(
error
.
toString
(),
contains
(
'pubspec.yaml'
));
}
});
testUsingContext
(
'does not change ASCII casing of path'
,
()
{
final
String
testPath
=
fs
.
path
.
join
(
dataPath
,
'asci_casing'
);
final
String
mainPath
=
fs
.
path
.
join
(
testPath
,
'main.dart'
);
final
String
packagesPath
=
fs
.
path
.
join
(
testPath
,
'.packages'
);
final
DartDependencySetBuilder
builder
=
DartDependencySetBuilder
(
mainPath
,
packagesPath
);
final
Set
<
String
>
deps
=
builder
.
build
();
expect
(
deps
,
contains
(
endsWith
(
'This_Import_Has_fuNNy_casING.dart'
)));
});
testUsingContext
(
'bad_import'
,
()
{
final
String
testPath
=
fs
.
path
.
join
(
dataPath
,
'bad_import'
);
final
String
mainPath
=
fs
.
path
.
join
(
testPath
,
'main.dart'
);
final
String
packagesPath
=
fs
.
path
.
join
(
testPath
,
'.packages'
);
final
DartDependencySetBuilder
builder
=
DartDependencySetBuilder
(
mainPath
,
packagesPath
);
try
{
builder
.
build
();
fail
(
'expect an exception to be thrown.'
);
}
on
DartDependencyException
catch
(
error
)
{
expect
(
error
.
toString
(),
contains
(
'Unable to parse URI'
));
}
});
});
}
packages/flutter_tools/test/data/dart_dependencies_test/.dartignore
deleted
100644 → 0
View file @
bfac6048
packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/.packages
deleted
100644 → 0
View file @
bfac6048
self:lib/
packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/This_Import_Has_fuNNy_casING.dart
deleted
100644 → 0
View file @
bfac6048
// 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.
String
dummy
=
'Hello'
;
packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/analysis_options.yaml
deleted
100644 → 0
View file @
bfac6048
analyzer
:
exclude
:
-
'
**'
packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/main.dart
deleted
100644 → 0
View file @
bfac6048
// 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
'This_Import_Has_fuNNy_casING.dart'
;
void
main
(
)
{
print
(
dummy
);
}
packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/pubspec.yaml
deleted
100644 → 0
View file @
bfac6048
name
:
self
packages/flutter_tools/test/data/dart_dependencies_test/bad_import/analysis_options.yaml
deleted
100644 → 0
View file @
bfac6048
analyzer
:
exclude
:
-
'
**'
packages/flutter_tools/test/data/dart_dependencies_test/bad_import/main.dart
deleted
100644 → 0
View file @
bfac6048
// 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
'data://object.dart'
;
packages/flutter_tools/test/data/dart_dependencies_test/bad_import/pubspec.yaml
deleted
100644 → 0
View file @
bfac6048
name
:
self
packages/flutter_tools/test/data/dart_dependencies_test/bad_package/.packages
deleted
100644 → 0
View file @
bfac6048
self:lib/
packages/flutter_tools/test/data/dart_dependencies_test/bad_package/analysis_options.yaml
deleted
100644 → 0
View file @
bfac6048
analyzer
:
exclude
:
-
'
**'
packages/flutter_tools/test/data/dart_dependencies_test/bad_package/main.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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
'package:rochambeau/you_have_your_orders_now_go_man_go.dart'
;
packages/flutter_tools/test/data/dart_dependencies_test/bad_package/pubspec.yaml
deleted
100644 → 0
View file @
bfac6048
name
:
self
packages/flutter_tools/test/data/dart_dependencies_test/bad_path/.packages
deleted
100644 → 0
View file @
bfac6048
self:lib/
packages/flutter_tools/test/data/dart_dependencies_test/bad_path/analysis_options.yaml
deleted
100644 → 0
View file @
bfac6048
analyzer
:
exclude
:
-
'
**'
packages/flutter_tools/test/data/dart_dependencies_test/bad_path/main.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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
'amaze/and/astonish.dart'
;
packages/flutter_tools/test/data/dart_dependencies_test/bad_path/pubspec.yaml
deleted
100644 → 0
View file @
bfac6048
name
:
self
packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/.packages
deleted
100644 → 0
View file @
bfac6048
flutter:file:///a/wild/non-existent/directory/has/appeared
sdk-move-test:lib/
packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/lib/main.dart
deleted
100644 → 0
View file @
bfac6048
// 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.
// No content
packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/pubspec.yaml
deleted
100644 → 0
View file @
bfac6048
name
:
sdk-move-test
environment
:
# The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite.
sdk
:
"
>=2.0.0-dev.68.0
<3.0.0"
dependencies
:
flutter
:
sdk
:
flutter
packages/flutter_tools/test/data/dart_dependencies_test/good/.packages
deleted
100644 → 0
View file @
bfac6048
self:lib/
packages/flutter_tools/test/data/dart_dependencies_test/good/analysis_options.yaml
deleted
100644 → 0
View file @
bfac6048
analyzer
:
exclude
:
-
'
**'
packages/flutter_tools/test/data/dart_dependencies_test/good/foo.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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.
packages/flutter_tools/test/data/dart_dependencies_test/good/lib/bar.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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.
packages/flutter_tools/test/data/dart_dependencies_test/good/main.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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
'foo.dart'
;
import
'package:self/bar.dart'
;
packages/flutter_tools/test/data/dart_dependencies_test/good/pubspec.yaml
deleted
100644 → 0
View file @
bfac6048
name
:
self
packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/.packages
deleted
100644 → 0
View file @
bfac6048
self:lib/
packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/analysis_options.yaml
deleted
100644 → 0
View file @
bfac6048
analyzer
:
exclude
:
-
'
**'
packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/foo.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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
bad
programmer
!
packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/main.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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
'foo.dart'
;
packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/pubspec.yaml
deleted
100644 → 0
View file @
bfac6048
name
:
self
packages/flutter_tools/test/dependency_checker_test.dart
deleted
100644 → 0
View file @
bfac6048
// Copyright 2016 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
'package:file/memory.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/cache.dart'
;
import
'package:flutter_tools/src/commands/devices.dart'
;
import
'package:flutter_tools/src/dart/dependencies.dart'
;
import
'package:flutter_tools/src/dependency_checker.dart'
;
import
'src/common.dart'
;
import
'src/context.dart'
;
void
main
(
)
{
group
(
'DependencyChecker'
,
()
{
final
String
dataPath
=
fs
.
path
.
join
(
getFlutterRoot
(),
'packages'
,
'flutter_tools'
,
'test'
,
'data'
,
'dart_dependencies_test'
,
);
FileSystem
testFileSystem
;
setUpAll
(()
{
Cache
.
disableLocking
();
});
setUp
(()
{
testFileSystem
=
MemoryFileSystem
();
});
testUsingContext
(
'good'
,
()
{
final
String
testPath
=
fs
.
path
.
join
(
dataPath
,
'good'
);
final
String
mainPath
=
fs
.
path
.
join
(
testPath
,
'main.dart'
);
final
String
fooPath
=
fs
.
path
.
join
(
testPath
,
'foo.dart'
);
final
String
barPath
=
fs
.
path
.
join
(
testPath
,
'lib'
,
'bar.dart'
);
final
String
packagesPath
=
fs
.
path
.
join
(
testPath
,
'.packages'
);
final
DartDependencySetBuilder
builder
=
DartDependencySetBuilder
(
mainPath
,
packagesPath
);
final
DependencyChecker
dependencyChecker
=
DependencyChecker
(
builder
,
null
);
// Set file modification time on all dependencies to be in the past.
final
DateTime
baseTime
=
DateTime
.
now
();
updateFileModificationTime
(
packagesPath
,
baseTime
,
-
10
);
updateFileModificationTime
(
mainPath
,
baseTime
,
-
10
);
updateFileModificationTime
(
fooPath
,
baseTime
,
-
10
);
updateFileModificationTime
(
barPath
,
baseTime
,
-
10
);
expect
(
dependencyChecker
.
check
(
baseTime
),
isFalse
);
// Set .packages file modification time to be in the future.
updateFileModificationTime
(
packagesPath
,
baseTime
,
20
);
expect
(
dependencyChecker
.
check
(
baseTime
),
isTrue
);
// Reset .packages file modification time.
updateFileModificationTime
(
packagesPath
,
baseTime
,
0
);
expect
(
dependencyChecker
.
check
(
baseTime
),
isFalse
);
// Set 'package:self/bar.dart' file modification time to be in the future.
updateFileModificationTime
(
barPath
,
baseTime
,
10
);
expect
(
dependencyChecker
.
check
(
baseTime
),
isTrue
);
});
testUsingContext
(
'syntax error'
,
()
{
final
String
testPath
=
fs
.
path
.
join
(
dataPath
,
'syntax_error'
);
final
String
mainPath
=
fs
.
path
.
join
(
testPath
,
'main.dart'
);
final
String
fooPath
=
fs
.
path
.
join
(
testPath
,
'foo.dart'
);
final
String
packagesPath
=
fs
.
path
.
join
(
testPath
,
'.packages'
);
final
DartDependencySetBuilder
builder
=
DartDependencySetBuilder
(
mainPath
,
packagesPath
);
final
DependencyChecker
dependencyChecker
=
DependencyChecker
(
builder
,
null
);
final
DateTime
baseTime
=
DateTime
.
now
();
// Set file modification time on all dependencies to be in the past.
updateFileModificationTime
(
packagesPath
,
baseTime
,
-
10
);
updateFileModificationTime
(
mainPath
,
baseTime
,
-
10
);
updateFileModificationTime
(
fooPath
,
baseTime
,
-
10
);
// Dependencies are considered dirty because there is a syntax error in
// the .dart file.
expect
(
dependencyChecker
.
check
(
baseTime
),
isTrue
);
});
/// Test a flutter tool move.
///
/// Tests that the flutter tool doesn't crash and displays a warning when its own location
/// changed since it was last referenced to in a package's .packages file.
testUsingContext
(
'moved flutter sdk'
,
()
async
{
final
Directory
tempDir
=
fs
.
systemTempDirectory
.
createTempSync
(
'flutter_dependency_checker_test.'
);
// Copy the golden input and let the test run in an isolated temporary in-memory file system.
const
LocalFileSystem
localFileSystem
=
LocalFileSystem
();
final
Directory
sourcePath
=
localFileSystem
.
directory
(
localFileSystem
.
path
.
join
(
dataPath
,
'changed_sdk_location'
));
copyDirectorySync
(
sourcePath
,
tempDir
);
fs
.
currentDirectory
=
tempDir
;
// Doesn't matter what commands we run. Arbitrarily list devices here.
await
createTestCommandRunner
(
DevicesCommand
()).
run
(<
String
>[
'devices'
]);
expect
(
testLogger
.
errorText
,
contains
(
'.packages'
));
tryToDelete
(
tempDir
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
testFileSystem
,
});
});
}
packages/flutter_tools/test/devfs_test.dart
View file @
cd803ac7
...
...
@@ -7,10 +7,8 @@ import 'dart:convert';
import
'package:file/file.dart'
;
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/asset.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/build_info.dart'
;
import
'package:flutter_tools/src/devfs.dart'
;
import
'package:flutter_tools/src/vmservice.dart'
;
import
'package:json_rpc_2/json_rpc_2.dart'
as
rpc
;
...
...
@@ -22,16 +20,13 @@ import 'src/mocks.dart';
void
main
(
)
{
FileSystem
fs
;
String
filePath
;
String
filePath2
;
Directory
tempDir
;
String
basePath
;
DevFS
devFS
;
final
AssetBundle
assetBundle
=
AssetBundleFactory
.
defaultInstance
.
createBundle
();
setUpAll
(()
{
fs
=
MemoryFileSystem
();
filePath
=
fs
.
path
.
join
(
'lib'
,
'foo.txt'
);
filePath2
=
fs
.
path
.
join
(
'foo'
,
'bar.txt'
);
});
group
(
'DevFSContent'
,
()
{
...
...
@@ -94,362 +89,6 @@ void main() {
});
});
group
(
'devfs local'
,
()
{
final
MockDevFSOperations
devFSOperations
=
MockDevFSOperations
();
final
MockResidentCompiler
residentCompiler
=
MockResidentCompiler
();
setUpAll
(()
{
tempDir
=
_newTempDir
(
fs
);
basePath
=
tempDir
.
path
;
});
tearDownAll
(
_cleanupTempDirs
);
testUsingContext
(
'create dev file system'
,
()
async
{
// simulate workspace
final
File
file
=
fs
.
file
(
fs
.
path
.
join
(
basePath
,
filePath
));
await
file
.
parent
.
create
(
recursive:
true
);
file
.
writeAsBytesSync
(<
int
>[
1
,
2
,
3
]);
_packages
[
'my_project'
]
=
fs
.
path
.
toUri
(
'lib'
);
// simulate package
await
_createPackage
(
fs
,
'somepkg'
,
'somefile.txt'
);
devFS
=
DevFS
.
operations
(
devFSOperations
,
'test'
,
tempDir
);
await
devFS
.
create
();
devFSOperations
.
expectMessages
(<
String
>[
'create test'
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
true
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill.track.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'add new file to local file system'
,
()
async
{
final
File
file
=
fs
.
file
(
fs
.
path
.
join
(
basePath
,
filePath2
));
await
file
.
parent
.
create
(
recursive:
true
);
file
.
writeAsBytesSync
(<
int
>[
1
,
2
,
3
,
4
,
5
,
6
,
7
]);
final
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'modify existing file on local file system'
,
()
async
{
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
final
File
file
=
fs
.
file
(
fs
.
path
.
join
(
basePath
,
filePath
));
// Set the last modified time to 5 seconds in the past.
updateFileModificationTime
(
file
.
path
,
DateTime
.
now
(),
-
5
);
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
await
file
.
writeAsBytes
(<
int
>[
1
,
2
,
3
,
4
,
5
,
6
]);
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
// Set the last modified time to 5 seconds in the past.
updateFileModificationTime
(
file
.
path
,
DateTime
.
now
(),
-
5
);
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
true
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill.track.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
await
file
.
writeAsBytes
(<
int
>[
1
,
2
,
3
,
4
,
5
,
6
]);
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
true
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill.track.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'delete a file from the local file system'
,
()
async
{
final
File
file
=
fs
.
file
(
fs
.
path
.
join
(
basePath
,
filePath
));
await
file
.
delete
();
final
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'deleteFile test lib/foo.txt'
,
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'add new package'
,
()
async
{
await
_createPackage
(
fs
,
'newpkg'
,
'anotherfile.txt'
);
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
true
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill.track.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'add new package with double slashes in URI'
,
()
async
{
const
String
packageName
=
'doubleslashpkg'
;
await
_createPackage
(
fs
,
packageName
,
'somefile.txt'
,
doubleSlash:
true
);
final
Set
<
String
>
fileFilter
=
<
String
>{};
final
List
<
Uri
>
pkgUris
=
<
Uri
>[
fs
.
path
.
toUri
(
basePath
)]..
addAll
(
_packages
.
values
);
for
(
Uri
pkgUri
in
pkgUris
)
{
if
(!
pkgUri
.
isAbsolute
)
{
pkgUri
=
fs
.
path
.
toUri
(
fs
.
path
.
join
(
basePath
,
pkgUri
.
path
));
}
fileFilter
.
addAll
(
fs
.
directory
(
pkgUri
)
.
listSync
(
recursive:
true
)
.
whereType
<
File
>()
.
map
<
String
>((
File
file
)
=>
canonicalizePath
(
file
.
path
))
.
toList
());
}
final
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
fileFilter:
fileFilter
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'add an asset bundle'
,
()
async
{
assetBundle
.
entries
[
'a.txt'
]
=
DevFSStringContent
(
'abc'
);
final
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
bundle:
assetBundle
,
bundleDirty:
true
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test
${_inAssetBuildDirectory(fs, 'a.txt')}
'
,
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
unorderedMatches
(<
String
>[
'a.txt'
]));
devFS
.
assetPathsToEvict
.
clear
();
expect
(
report
.
syncedBytes
,
25
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'add a file to the asset bundle - bundleDirty'
,
()
async
{
assetBundle
.
entries
[
'b.txt'
]
=
DevFSStringContent
(
'abcd'
);
final
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
bundle:
assetBundle
,
bundleDirty:
true
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
// Expect entire asset bundle written because bundleDirty is true
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test
${_inAssetBuildDirectory(fs, 'a.txt')}
'
,
'writeFile test
${_inAssetBuildDirectory(fs, 'b.txt')}
'
,
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
unorderedMatches
(<
String
>[
'a.txt'
,
'b.txt'
]));
devFS
.
assetPathsToEvict
.
clear
();
expect
(
report
.
syncedBytes
,
29
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'add a file to the asset bundle'
,
()
async
{
assetBundle
.
entries
[
'c.txt'
]
=
DevFSStringContent
(
'12'
);
final
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
bundle:
assetBundle
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'writeFile test
${_inAssetBuildDirectory(fs, 'c.txt')}
'
,
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
unorderedMatches
(<
String
>[
'c.txt'
]));
devFS
.
assetPathsToEvict
.
clear
();
expect
(
report
.
syncedBytes
,
24
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'delete a file from the asset bundle'
,
()
async
{
assetBundle
.
entries
.
remove
(
'c.txt'
);
final
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
bundle:
assetBundle
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'deleteFile test
${_inAssetBuildDirectory(fs, 'c.txt')}
'
,
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
unorderedMatches
(<
String
>[
'c.txt'
]));
devFS
.
assetPathsToEvict
.
clear
();
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'delete all files from the asset bundle'
,
()
async
{
assetBundle
.
entries
.
clear
();
final
UpdateFSReport
report
=
await
devFS
.
update
(
mainPath:
'lib/foo.txt'
,
bundle:
assetBundle
,
bundleDirty:
true
,
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
);
devFSOperations
.
expectMessages
(<
String
>[
'deleteFile test
${_inAssetBuildDirectory(fs, 'a.txt')}
'
,
'deleteFile test
${_inAssetBuildDirectory(fs, 'b.txt')}
'
,
'writeFile test lib/foo.txt.dill build/app.dill'
,
]);
expect
(
devFS
.
assetPathsToEvict
,
unorderedMatches
(<
String
>[
'a.txt'
,
'b.txt'
,
]));
devFS
.
assetPathsToEvict
.
clear
();
expect
(
report
.
syncedBytes
,
22
);
expect
(
report
.
success
,
true
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'delete dev file system'
,
()
async
{
await
devFS
.
destroy
();
devFSOperations
.
expectMessages
(<
String
>[
'destroy test'
]);
expect
(
devFS
.
assetPathsToEvict
,
isEmpty
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
});
group
(
'devfs remote'
,
()
{
MockVMService
vmService
;
final
MockResidentCompiler
residentCompiler
=
MockResidentCompiler
();
...
...
@@ -484,6 +123,7 @@ void main() {
generator:
residentCompiler
,
pathToReload:
'lib/foo.txt.dill'
,
trackWidgetCreation:
false
,
invalidatedFiles:
<
String
>[],
);
vmService
.
expectMessages
(<
String
>[
'writeFile test lib/foo.txt.dill'
,
...
...
@@ -652,6 +292,3 @@ Future<void> _createPackage(FileSystem fs, String pkgName, String pkgFileName, {
fs
.
file
(
fs
.
path
.
join
(
_tempDirs
[
0
].
path
,
'.packages'
)).
writeAsStringSync
(
sb
.
toString
());
}
String
_inAssetBuildDirectory
(
FileSystem
fs
,
String
filename
)
{
return
'
${fs.path.toUri(getAssetBuildDirectory()).path}
/
$filename
'
;
}
packages/flutter_tools/test/hot_test.dart
View file @
cd803ac7
...
...
@@ -105,13 +105,13 @@ void main() {
firstBuildTime:
anyNamed
(
'firstBuildTime'
),
bundleFirstUpload:
anyNamed
(
'bundleFirstUpload'
),
bundleDirty:
anyNamed
(
'bundleDirty'
),
fileFilter:
anyNamed
(
'fileFilter'
),
generator:
anyNamed
(
'generator'
),
fullRestart:
anyNamed
(
'fullRestart'
),
dillOutputPath:
anyNamed
(
'dillOutputPath'
),
trackWidgetCreation:
anyNamed
(
'trackWidgetCreation'
),
projectRootPath:
anyNamed
(
'projectRootPath'
),
pathToReload:
anyNamed
(
'pathToReload'
),
invalidatedFiles:
anyNamed
(
'invalidatedFiles'
),
)).
thenAnswer
((
Invocation
_
)
=>
Future
<
UpdateFSReport
>.
value
(
UpdateFSReport
(
success:
true
,
syncedBytes:
1000
,
invalidatedSourcesCount:
1
)));
when
(
mockDevFs
.
assetPathsToEvict
).
thenReturn
(<
String
>{});
...
...
@@ -122,18 +122,6 @@ void main() {
when
(
mockArtifacts
.
getArtifactPath
(
Artifact
.
flutterPatchedSdkPath
)).
thenReturn
(
'some/path'
);
});
testUsingContext
(
'no setup'
,
()
async
{
final
MockDevice
mockDevice
=
MockDevice
();
when
(
mockDevice
.
supportsHotReload
).
thenReturn
(
true
);
when
(
mockDevice
.
supportsHotRestart
).
thenReturn
(
true
);
final
List
<
FlutterDevice
>
devices
=
<
FlutterDevice
>[
FlutterDevice
(
mockDevice
,
generator:
residentCompiler
,
trackWidgetCreation:
false
),
];
expect
((
await
HotRunner
(
devices
).
restart
(
fullRestart:
true
)).
isOk
,
false
);
},
overrides:
<
Type
,
Generator
>{
Artifacts:
()
=>
mockArtifacts
,
});
testUsingContext
(
'Does not hot restart when device does not support it'
,
()
async
{
// Setup mocks
final
MockDevice
mockDevice
=
MockDevice
();
...
...
@@ -149,7 +137,7 @@ void main() {
expect
(
result
.
message
,
'hotRestart not supported'
);
},
overrides:
<
Type
,
Generator
>{
Artifacts:
()
=>
mockArtifacts
,
HotRunnerConfig:
()
=>
TestHotRunnerConfig
(
successfulSetup:
true
,
computeDartDependencies:
false
),
HotRunnerConfig:
()
=>
TestHotRunnerConfig
(
successfulSetup:
true
),
});
testUsingContext
(
'Does not hot restart when one of many devices does not support it'
,
()
async
{
...
...
@@ -171,7 +159,7 @@ void main() {
expect
(
result
.
message
,
'hotRestart not supported'
);
},
overrides:
<
Type
,
Generator
>{
Artifacts:
()
=>
mockArtifacts
,
HotRunnerConfig:
()
=>
TestHotRunnerConfig
(
successfulSetup:
true
,
computeDartDependencies:
false
),
HotRunnerConfig:
()
=>
TestHotRunnerConfig
(
successfulSetup:
true
),
});
testUsingContext
(
'Does hot restarts when all devices support it'
,
()
async
{
...
...
@@ -193,7 +181,7 @@ void main() {
expect
(
result
.
message
,
isNot
(
'hotRestart not supported'
));
},
overrides:
<
Type
,
Generator
>{
Artifacts:
()
=>
mockArtifacts
,
HotRunnerConfig:
()
=>
TestHotRunnerConfig
(
successfulSetup:
true
,
computeDartDependencies:
false
),
HotRunnerConfig:
()
=>
TestHotRunnerConfig
(
successfulSetup:
true
),
});
testUsingContext
(
'setup function fails'
,
()
async
{
...
...
@@ -226,7 +214,7 @@ void main() {
expect
(
result
.
message
,
isNot
(
'setupHotRestart failed'
));
},
overrides:
<
Type
,
Generator
>{
Artifacts:
()
=>
mockArtifacts
,
HotRunnerConfig:
()
=>
TestHotRunnerConfig
(
successfulSetup:
true
,
computeDartDependencies:
false
),
HotRunnerConfig:
()
=>
TestHotRunnerConfig
(
successfulSetup:
true
),
});
group
(
'shutdown hook tests'
,
()
{
...
...
@@ -235,7 +223,6 @@ void main() {
setUp
(()
{
shutdownTestingConfig
=
TestHotRunnerConfig
(
successfulSetup:
true
,
computeDartDependencies:
false
,
);
});
...
...
@@ -283,10 +270,7 @@ class MockDevice extends Mock implements Device {
}
class
TestHotRunnerConfig
extends
HotRunnerConfig
{
TestHotRunnerConfig
({
@required
this
.
successfulSetup
,
bool
computeDartDependencies
=
true
})
{
this
.
computeDartDependencies
=
computeDartDependencies
;
}
TestHotRunnerConfig
({
@required
this
.
successfulSetup
});
bool
successfulSetup
;
bool
shutdownHookCalled
=
false
;
...
...
packages/flutter_tools/test/project_file_invalidator_test.dart
0 → 100644
View file @
cd803ac7
// Copyright 2019 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
'package:file/memory.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/project.dart'
;
import
'package:flutter_tools/src/run_hot.dart'
;
import
'package:mockito/mockito.dart'
;
import
'src/common.dart'
;
import
'src/context.dart'
;
void
main
(
)
{
final
Platform
windowsPlatform
=
MockPlatform
();
final
Platform
notWindowsPlatform
=
MockPlatform
();
final
BufferLogger
bufferLogger
=
BufferLogger
();
when
(
windowsPlatform
.
isWindows
).
thenReturn
(
true
);
when
(
notWindowsPlatform
.
isWindows
).
thenReturn
(
false
);
group
(
'ProjectFileInvalidator linux/mac'
,
()
{
final
MemoryFileSystem
memoryFileSystem
=
MemoryFileSystem
();
final
File
packagesFile
=
memoryFileSystem
.
file
(
'.packages'
)
..
createSync
()
..
writeAsStringSync
(
r''
'
foo:file:///foo/lib/
bar:file:///.pub-cache/bar/lib/
baz:file:///baz/lib/
test_package:file:///lib/
'''
);
final
File
pubspecFile
=
memoryFileSystem
.
file
(
'pubspec.yaml'
)
..
createSync
()
..
writeAsStringSync
(
r''
'
name: test_package
dependencies:
foo: any
bar:
dev_dependencies:
baz: any
'''
);
final
File
mainFile
=
memoryFileSystem
.
file
(
'lib/main.dart'
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
r''
'
void main() {}
'''
);
final
File
fooFile
=
memoryFileSystem
.
file
(
'foo/lib/foo.dart'
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
''
);
memoryFileSystem
.
file
(
'bar/lib/bar.dart'
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
''
);
final
File
bazFile
=
memoryFileSystem
.
file
(
'baz/lib/baz.dart'
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
''
);
testUsingContext
(
'No .packages, no pubspec'
,
()
async
{
// Instead of setting up multiple filesystems, passing a .packages file which does not exist.
final
ProjectFileInvalidator
invalidator
=
ProjectFileInvalidator
(
'.packages-wrong'
,
null
);
invalidator
.
findInvalidated
();
expect
(
invalidator
.
packageMap
,
isEmpty
);
expect
(
invalidator
.
updateTime
,
isEmpty
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
Platform:
()
=>
notWindowsPlatform
,
});
testUsingContext
(
'.packages only'
,
()
async
{
final
ProjectFileInvalidator
invalidator
=
ProjectFileInvalidator
(
packagesFile
.
path
,
null
);
invalidator
.
findInvalidated
();
expect
(
invalidator
.
packageMap
,
<
String
,
Uri
>{
'foo'
:
Uri
.
parse
(
'file:///foo/lib/'
),
// Excluded because it is in pub cache.
// 'bar': Uri.parse('file:///.pub-cache/bar/lib/'),
'baz'
:
Uri
.
parse
(
'file:///baz/lib/'
),
'test_package'
:
Uri
.
parse
(
'file:///lib/'
),
});
expect
(
invalidator
.
updateTime
,
<
String
,
int
>{
'/baz/lib/baz.dart'
:
bazFile
.
statSync
().
modified
.
millisecondsSinceEpoch
,
'/lib/main.dart'
:
mainFile
.
statSync
().
modified
.
millisecondsSinceEpoch
,
'/foo/lib/foo.dart'
:
fooFile
.
statSync
().
modified
.
millisecondsSinceEpoch
,
});
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
Platform:
()
=>
notWindowsPlatform
,
});
testUsingContext
(
'.packages and pubspec'
,
()
async
{
final
FlutterProject
flutterProject
=
await
FlutterProject
.
fromDirectory
(
pubspecFile
.
parent
);
final
ProjectFileInvalidator
invalidator
=
ProjectFileInvalidator
(
packagesFile
.
path
,
flutterProject
);
invalidator
.
findInvalidated
();
expect
(
invalidator
.
packageMap
,
<
String
,
Uri
>{
'foo'
:
Uri
.
parse
(
'file:///foo/lib/'
),
// Excluded because it is in pub cache.
// 'bar': Uri.parse('file:///.pub-cache/bar/lib/'),
// Excluded because it is a dev dependency/
// 'baz': Uri.parse('file:///baz/lib/'),
'test_package'
:
Uri
.
parse
(
'file:///lib/'
),
});
expect
(
invalidator
.
updateTime
,
<
String
,
int
>{
'/foo/lib/foo.dart'
:
fooFile
.
statSync
().
modified
.
millisecondsSinceEpoch
,
'/lib/main.dart'
:
mainFile
.
statSync
().
modified
.
millisecondsSinceEpoch
,
});
expect
(
invalidator
.
findInvalidated
(),
isEmpty
);
// Invalidate main.dart.
mainFile
.
writeAsStringSync
(
'void main() { }'
);
expect
(
invalidator
.
findInvalidated
(),
<
String
>[
'/lib/main.dart'
]);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
Platform:
()
=>
notWindowsPlatform
,
});
testUsingContext
(
'update to .packages triggers warning'
,
()
async
{
final
FlutterProject
flutterProject
=
await
FlutterProject
.
fromDirectory
(
pubspecFile
.
parent
);
final
ProjectFileInvalidator
invalidator
=
ProjectFileInvalidator
(
packagesFile
.
path
,
flutterProject
);
invalidator
.
findInvalidated
();
packagesFile
.
writeAsStringSync
(
r''
'
foo:file:///foo/lib/
bar:file:///.pub-cache/bar/lib/
baz:file:///baz/lib/
new_dep:file:///new_dep/lib/
test_package:file:///lib/
'''
);
invalidator
.
findInvalidated
();
expect
(
bufferLogger
.
errorText
,
'Warning: updated dependencies detected. The Flutter application will require a restart to safely use new packages.
\n
'
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
Platform:
()
=>
notWindowsPlatform
,
Logger:
()
=>
bufferLogger
,
});
});
group
(
'ProjectFileInvalidator windows'
,
()
{
final
MemoryFileSystem
memoryFileSystem
=
MemoryFileSystem
(
style:
FileSystemStyle
.
windows
);
// On windows .packages still contains file Uris, albeit ones with the Drive prefix.
final
File
packagesFile
=
memoryFileSystem
.
file
(
r'C:\.packages'
)
..
createSync
()
..
writeAsStringSync
(
r''
'
foo:file:///C:/foo/lib/
bar:file:///C:/Pub/Cache/bar/lib/
baz:file:///C:/baz/lib/
test_package:file:///C:/lib/
'''
);
memoryFileSystem
.
file
(
r'C:\pubspec.yaml'
)
..
createSync
()
..
writeAsStringSync
(
r''
'
name: test_package
dependencies:
foo: any
bar:
dev_dependencies:
baz: any
'''
);
final
File
mainFile
=
memoryFileSystem
.
file
(
r'C:\lib\main.dart'
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
r''
'
void main() {}
'''
);
final
File
fooFile
=
memoryFileSystem
.
file
(
r'C:\foo\lib\foo.dart'
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
''
);
memoryFileSystem
.
file
(
r'C:\bar\lib\bar.dart'
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
''
);
final
File
bazFile
=
memoryFileSystem
.
file
(
r'C:\baz\lib\baz.dart'
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
''
);
testUsingContext
(
'No .packages, no pubspec'
,
()
async
{
// Instead of setting up multiple filesystems, passing a .packages file which does not exist.
final
ProjectFileInvalidator
invalidator
=
ProjectFileInvalidator
(
'.packages-wrong'
,
null
);
invalidator
.
findInvalidated
();
expect
(
invalidator
.
packageMap
,
isEmpty
);
expect
(
invalidator
.
updateTime
,
isEmpty
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
Platform:
()
=>
windowsPlatform
,
});
testUsingContext
(
'.packages only'
,
()
async
{
final
ProjectFileInvalidator
invalidator
=
ProjectFileInvalidator
(
packagesFile
.
path
,
null
);
invalidator
.
findInvalidated
();
expect
(
invalidator
.
packageMap
,
<
String
,
Uri
>{
'foo'
:
Uri
.
file
(
r'C:\foo\lib\'
,
windows:
true
),
// Excluded because it is in pub cache.
// 'bar': Uri.parse('file:///Pub/Cache/bar/lib/'),
'baz'
:
Uri
.
file
(
r'C:\baz\lib\'
,
windows:
true
),
'test_package'
:
Uri
.
file
(
r'C:\lib\'
,
windows:
true
),
});
expect
(
invalidator
.
updateTime
,
<
String
,
int
>{
r'C:\baz\lib\baz.dart'
:
bazFile
.
statSync
().
modified
.
millisecondsSinceEpoch
,
r'C:\lib\main.dart'
:
mainFile
.
statSync
().
modified
.
millisecondsSinceEpoch
,
r'C:\foo\lib\foo.dart'
:
fooFile
.
statSync
().
modified
.
millisecondsSinceEpoch
,
});
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
Platform:
()
=>
windowsPlatform
,
});
testUsingContext
(
'.packages and pubspec'
,
()
async
{
final
FlutterProject
flutterProject
=
await
FlutterProject
.
fromDirectory
(
fs
.
directory
(
r'C:\'
));
final
ProjectFileInvalidator
invalidator
=
ProjectFileInvalidator
(
packagesFile
.
path
,
flutterProject
);
invalidator
.
findInvalidated
();
expect
(
invalidator
.
packageMap
,
<
String
,
Uri
>{
'foo'
:
Uri
.
file
(
r'C:\foo\lib\'
,
windows:
true
),
// Excluded because it is in pub cache.
// 'bar': Uri.parse('file:///C:/Pub/Cache/bar/lib/'),
// Excluded because it is a dev dependency/
// 'baz': Uri.parse('file:///baz/lib/'),
'test_package'
:
Uri
.
file
(
r'C:\lib\'
,
windows:
true
),
});
expect
(
invalidator
.
updateTime
,
<
String
,
int
>{
r'C:\lib\main.dart'
:
mainFile
.
statSync
().
modified
.
millisecondsSinceEpoch
,
r'C:\foo\lib\foo.dart'
:
fooFile
.
statSync
().
modified
.
millisecondsSinceEpoch
,
});
expect
(
invalidator
.
findInvalidated
(),
isEmpty
);
// Invalidate main.dart.
mainFile
.
writeAsStringSync
(
'void main() { }'
);
expect
(
invalidator
.
findInvalidated
(),
<
String
>[
'file:///C:/lib/main.dart'
]);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
memoryFileSystem
,
Platform:
()
=>
windowsPlatform
,
});
});
}
class
MockPlatform
extends
Mock
implements
Platform
{}
packages/flutter_tools/test/src/mocks.dart
View file @
cd803ac7
...
...
@@ -460,12 +460,6 @@ class MockDevFSOperations extends BasicMock implements DevFSOperations {
messages
.
add
(
message
);
devicePathToContent
[
deviceUri
]
=
content
;
}
@override
Future
<
dynamic
>
deleteFile
(
String
fsName
,
Uri
deviceUri
)
async
{
messages
.
add
(
'deleteFile
$fsName
$deviceUri
'
);
devicePathToContent
.
remove
(
deviceUri
);
}
}
class
MockResidentCompiler
extends
BasicMock
implements
ResidentCompiler
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment