Unverified Commit 3e838da9 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] use flutter tool handler for dwds resources and precache tool...

[flutter_tools] use flutter tool handler for dwds resources and precache tool pub dependencies (#65814)

If the tool is downloaded from a precompiled snapshot, or if the backing source files in the pub cache are deleted, the dwds debugging functionality will break as the client.js file cannot be located. Instead use the PackageConfig to verify that package location, downloading if it is missing.

Override the dwds middleware to avoid Isolate.resolvePackageUri

Fixes #53644
Fixes #65475
parent b1d17c91
...@@ -7,7 +7,7 @@ import 'dart:typed_data'; ...@@ -7,7 +7,7 @@ import 'dart:typed_data';
import 'package:dwds/data/build_result.dart'; import 'package:dwds/data/build_result.dart';
import 'package:dwds/dwds.dart'; import 'package:dwds/dwds.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart' as logging;
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:mime/mime.dart' as mime; import 'package:mime/mime.dart' as mime;
import 'package:package_config/package_config.dart'; import 'package:package_config/package_config.dart';
...@@ -19,6 +19,7 @@ import '../asset.dart'; ...@@ -19,6 +19,7 @@ import '../asset.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../base/logger.dart';
import '../base/net.dart'; import '../base/net.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../base/utils.dart'; import '../base/utils.dart';
...@@ -45,7 +46,7 @@ typedef DwdsLauncher = Future<Dwds> Function({ ...@@ -45,7 +46,7 @@ typedef DwdsLauncher = Future<Dwds> Function({
bool useSseForDebugProxy, bool useSseForDebugProxy,
bool useSseForDebugBackend, bool useSseForDebugBackend,
bool serveDevTools, bool serveDevTools,
void Function(Level, String) logWriter, void Function(logging.Level, String) logWriter,
bool verbose, bool verbose,
UrlEncoder urlEncoder, UrlEncoder urlEncoder,
bool useFileProvider, bool useFileProvider,
...@@ -227,6 +228,21 @@ class WebAssetServer implements AssetReader { ...@@ -227,6 +228,21 @@ class WebAssetServer implements AssetReader {
} }
return null; return null;
} }
// Ensure dwds is present and provide middleware to avoid trying to
// load the through the isolate APIs.
final Directory directory = await _loadDwdsDirectory(globals.fs, globals.logger);
final shelf.Middleware middleware = (FutureOr<shelf.Response> Function(shelf.Request) innerHandler) {
return (shelf.Request request) async {
if (request.url.path.endsWith('dwds/src/injected/client.js')) {
final Uri uri = directory.uri.resolve('src/injected/client.js');
final String result = await globals.fs.file(uri.toFilePath()).readAsString();
return shelf.Response.ok(result, headers: <String, String>{
HttpHeaders.contentTypeHeader: 'application/javascript'
});
}
return innerHandler(request);
};
};
// In debug builds, spin up DWDS and the full asset server. // In debug builds, spin up DWDS and the full asset server.
final Dwds dwds = await dwdsLauncher( final Dwds dwds = await dwdsLauncher(
...@@ -243,7 +259,7 @@ class WebAssetServer implements AssetReader { ...@@ -243,7 +259,7 @@ class WebAssetServer implements AssetReader {
useSseForDebugProxy: useSseForDebugProxy, useSseForDebugProxy: useSseForDebugProxy,
useSseForDebugBackend: useSseForDebugBackend, useSseForDebugBackend: useSseForDebugBackend,
serveDevTools: false, serveDevTools: false,
logWriter: (Level logLevel, String message) => globals.printTrace(message), logWriter: (logging.Level logLevel, String message) => globals.printTrace(message),
loadStrategy: RequireStrategy( loadStrategy: RequireStrategy(
ReloadConfiguration.none, ReloadConfiguration.none,
'.lib.js', '.lib.js',
...@@ -258,6 +274,7 @@ class WebAssetServer implements AssetReader { ...@@ -258,6 +274,7 @@ class WebAssetServer implements AssetReader {
); );
shelf.Pipeline pipeline = const shelf.Pipeline(); shelf.Pipeline pipeline = const shelf.Pipeline();
if (enableDwds) { if (enableDwds) {
pipeline = pipeline.addMiddleware(middleware);
pipeline = pipeline.addMiddleware(dwds.middleware); pipeline = pipeline.addMiddleware(dwds.middleware);
} }
final shelf.Handler dwdsHandler = pipeline.addHandler(server.handleRequest); final shelf.Handler dwdsHandler = pipeline.addHandler(server.handleRequest);
...@@ -946,3 +963,14 @@ class ReleaseAssetServer { ...@@ -946,3 +963,14 @@ class ReleaseAssetServer {
return shelf.Response.notFound(''); return shelf.Response.notFound('');
} }
} }
Future<Directory> _loadDwdsDirectory(FileSystem fileSystem, Logger logger) async {
final String toolPackagePath = fileSystem.path.join(
Cache.flutterRoot, 'packages', 'flutter_tools');
final String packageFilePath = fileSystem.path.join(toolPackagePath, kPackagesFileName);
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
fileSystem.file(packageFilePath),
logger: logger,
);
return fileSystem.directory(packageConfig['dwds'].packageUriRoot);
}
...@@ -22,9 +22,7 @@ import '../base/terminal.dart'; ...@@ -22,9 +22,7 @@ import '../base/terminal.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../build_system/targets/web.dart'; import '../build_system/targets/web.dart';
import '../cache.dart';
import '../dart/language_version.dart'; import '../dart/language_version.dart';
import '../dart/pub.dart';
import '../devfs.dart'; import '../devfs.dart';
import '../device.dart'; import '../device.dart';
import '../features.dart'; import '../features.dart';
...@@ -444,15 +442,6 @@ class _ResidentWebRunner extends ResidentWebRunner { ...@@ -444,15 +442,6 @@ class _ResidentWebRunner extends ResidentWebRunner {
try { try {
return await asyncGuard(() async { return await asyncGuard(() async {
// Ensure dwds resources are cached. If the .packages file is missing then
// the client.js script cannot be located by the injected handler in dwds.
// This will result in a NoSuchMethodError thrown by injected_handler.darts
await pub.get(
context: PubContext.pubGet,
directory: globals.fs.path.join(Cache.flutterRoot, 'packages', 'flutter_tools'),
generateSyntheticPackage: false,
);
final ExpressionCompiler expressionCompiler = final ExpressionCompiler expressionCompiler =
debuggingOptions.webEnableExpressionEvaluation debuggingOptions.webEnableExpressionEvaluation
? WebExpressionCompiler(device.generator) ? WebExpressionCompiler(device.generator)
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'package:archive/archive.dart'; import 'package:archive/archive.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
import 'android/gradle_utils.dart'; import 'android/gradle_utils.dart';
import 'base/common.dart'; import 'base/common.dart';
...@@ -14,8 +15,11 @@ import 'base/net.dart'; ...@@ -14,8 +15,11 @@ import 'base/net.dart';
import 'base/os.dart' show OperatingSystemUtils; import 'base/os.dart' show OperatingSystemUtils;
import 'base/platform.dart'; import 'base/platform.dart';
import 'base/process.dart'; import 'base/process.dart';
import 'dart/package_map.dart';
import 'dart/pub.dart';
import 'features.dart'; import 'features.dart';
import 'globals.dart' as globals; import 'globals.dart' as globals;
import 'runner/flutter_command.dart';
/// A tag for a set of development artifacts that need to be cached. /// A tag for a set of development artifacts that need to be cached.
class DevelopmentArtifact { class DevelopmentArtifact {
...@@ -126,6 +130,14 @@ class Cache { ...@@ -126,6 +130,14 @@ class Cache {
_artifacts.add(IosUsbArtifacts(artifactName, this)); _artifacts.add(IosUsbArtifacts(artifactName, this));
} }
_artifacts.add(FontSubsetArtifacts(this)); _artifacts.add(FontSubsetArtifacts(this));
_artifacts.add(PubDependencies(
fileSystem: _fileSystem,
logger: _logger,
// flutter root and pub must be lazily initialized to avoid accessing
// before the version is determined.
flutterRoot: () => flutterRoot,
pub: () => pub,
));
} else { } else {
_artifacts.addAll(artifacts); _artifacts.addAll(artifacts);
} }
...@@ -432,7 +444,14 @@ class Cache { ...@@ -432,7 +444,14 @@ class Cache {
); );
} }
bool isUpToDate() => _artifacts.every((ArtifactSet artifact) => artifact.isUpToDate()); Future<bool> isUpToDate() async {
for (final ArtifactSet artifact in _artifacts) {
if (!await artifact.isUpToDate()) {
return false;
}
}
return true;
}
/// Update the cache to contain all `requiredArtifacts`. /// Update the cache to contain all `requiredArtifacts`.
Future<void> updateAll(Set<DevelopmentArtifact> requiredArtifacts) async { Future<void> updateAll(Set<DevelopmentArtifact> requiredArtifacts) async {
...@@ -444,7 +463,7 @@ class Cache { ...@@ -444,7 +463,7 @@ class Cache {
_logger.printTrace('Artifact $artifact is not required, skipping update.'); _logger.printTrace('Artifact $artifact is not required, skipping update.');
continue; continue;
} }
if (artifact.isUpToDate()) { if (await artifact.isUpToDate()) {
continue; continue;
} }
try { try {
...@@ -502,7 +521,7 @@ abstract class ArtifactSet { ...@@ -502,7 +521,7 @@ abstract class ArtifactSet {
final DevelopmentArtifact developmentArtifact; final DevelopmentArtifact developmentArtifact;
/// [true] if the artifact is up to date. /// [true] if the artifact is up to date.
bool isUpToDate(); Future<bool> isUpToDate();
/// The environment variables (if any) required to consume the artifacts. /// The environment variables (if any) required to consume the artifacts.
Map<String, String> get environment { Map<String, String> get environment {
...@@ -546,7 +565,7 @@ abstract class CachedArtifact extends ArtifactSet { ...@@ -546,7 +565,7 @@ abstract class CachedArtifact extends ArtifactSet {
} }
@override @override
bool isUpToDate() { Future<bool> isUpToDate() async {
if (!location.existsSync()) { if (!location.existsSync()) {
return false; return false;
} }
...@@ -592,6 +611,66 @@ abstract class CachedArtifact extends ArtifactSet { ...@@ -592,6 +611,66 @@ abstract class CachedArtifact extends ArtifactSet {
Uri _toStorageUri(String path) => Uri.parse('${cache.storageBaseUrl}/$path'); Uri _toStorageUri(String path) => Uri.parse('${cache.storageBaseUrl}/$path');
} }
/// Ensures that the source files for all of the dependencies for the
/// flutter_tool are present.
///
/// This does not handle cases wheere the source files are modified or the
/// directory contents are incomplete.
class PubDependencies extends ArtifactSet {
PubDependencies({
// Needs to be lazy to avoid reading from the cache before the root is initialized.
@required String Function() flutterRoot,
@required FileSystem fileSystem,
@required Logger logger,
@required Pub Function() pub,
}) : _logger = logger,
_fileSystem = fileSystem,
_flutterRoot = flutterRoot,
_pub = pub,
super(DevelopmentArtifact.universal);
final String Function() _flutterRoot;
final FileSystem _fileSystem;
final Logger _logger;
final Pub Function() _pub;
@override
Future<bool> isUpToDate() async {
final File toolPackageConfig = _fileSystem.file(
_fileSystem.path.join(_flutterRoot(), 'packages', 'flutter_tools', kPackagesFileName),
);
if (!toolPackageConfig.existsSync()) {
return false;
}
final PackageConfig packageConfig = await loadPackageConfigWithLogging(
toolPackageConfig,
logger: _logger,
throwOnError: false,
);
if (packageConfig == null || packageConfig == PackageConfig.empty ) {
return false;
}
for (final Package package in packageConfig.packages) {
if (!_fileSystem.directory(package.packageUriRoot).existsSync()) {
return false;
}
}
return true;
}
@override
String get name => 'pub_dependencies';
@override
Future<void> update(ArtifactUpdater artifactUpdater) async {
await _pub().get(
context: PubContext.pubGet,
directory: _fileSystem.path.join(_flutterRoot(), 'packages', 'flutter_tools'),
generateSyntheticPackage: false,
);
}
}
/// A cached artifact containing fonts used for Material Design. /// A cached artifact containing fonts used for Material Design.
class MaterialFonts extends CachedArtifact { class MaterialFonts extends CachedArtifact {
MaterialFonts(Cache cache) : super( MaterialFonts(Cache cache) : super(
...@@ -979,7 +1058,7 @@ class AndroidMavenArtifacts extends ArtifactSet { ...@@ -979,7 +1058,7 @@ class AndroidMavenArtifacts extends ArtifactSet {
} }
@override @override
bool isUpToDate() { Future<bool> isUpToDate() async {
// The dependencies are downloaded and cached by Gradle. // The dependencies are downloaded and cached by Gradle.
// The tool doesn't know if the dependencies are already cached at this point. // The tool doesn't know if the dependencies are already cached at this point.
// Therefore, call Gradle to figure this out. // Therefore, call Gradle to figure this out.
......
...@@ -156,7 +156,7 @@ class PrecacheCommand extends FlutterCommand { ...@@ -156,7 +156,7 @@ class PrecacheCommand extends FlutterCommand {
requiredArtifacts.add(artifact); requiredArtifacts.add(artifact);
} }
} }
if (!_cache.isUpToDate()) { if (!await _cache.isUpToDate()) {
await _cache.updateAll(requiredArtifacts); await _cache.updateAll(requiredArtifacts);
} else { } else {
_logger.printStatus('Already up-to-date.'); _logger.printStatus('Already up-to-date.');
......
...@@ -232,7 +232,8 @@ Future<T> runInContext<T>( ...@@ -232,7 +232,8 @@ Future<T> runInContext<T>(
botDetector: globals.botDetector, botDetector: globals.botDetector,
platform: globals.platform, platform: globals.platform,
usage: globals.flutterUsage, usage: globals.flutterUsage,
toolStampFile: globals.cache.getStampFileFor('flutter_tools'), // Avoid a circular dependency by making this access lazy.
toolStampFile: () => globals.cache.getStampFileFor('flutter_tools'),
), ),
ShutdownHooks: () => ShutdownHooks(logger: globals.logger), ShutdownHooks: () => ShutdownHooks(logger: globals.logger),
Stdio: () => Stdio(), Stdio: () => Stdio(),
......
...@@ -80,7 +80,7 @@ abstract class Pub { ...@@ -80,7 +80,7 @@ abstract class Pub {
@required Platform platform, @required Platform platform,
@required BotDetector botDetector, @required BotDetector botDetector,
@required Usage usage, @required Usage usage,
File toolStampFile, File Function() toolStampFile,
}) = _DefaultPub; }) = _DefaultPub;
/// Runs `pub get`. /// Runs `pub get`.
...@@ -141,7 +141,7 @@ class _DefaultPub implements Pub { ...@@ -141,7 +141,7 @@ class _DefaultPub implements Pub {
@required Platform platform, @required Platform platform,
@required BotDetector botDetector, @required BotDetector botDetector,
@required Usage usage, @required Usage usage,
File toolStampFile, File Function() toolStampFile,
}) : _toolStampFile = toolStampFile, }) : _toolStampFile = toolStampFile,
_fileSystem = fileSystem, _fileSystem = fileSystem,
_logger = logger, _logger = logger,
...@@ -159,7 +159,7 @@ class _DefaultPub implements Pub { ...@@ -159,7 +159,7 @@ class _DefaultPub implements Pub {
final Platform _platform; final Platform _platform;
final BotDetector _botDetector; final BotDetector _botDetector;
final Usage _usage; final Usage _usage;
final File _toolStampFile; final File Function() _toolStampFile;
@override @override
Future<void> get({ Future<void> get({
...@@ -399,10 +399,10 @@ class _DefaultPub implements Pub { ...@@ -399,10 +399,10 @@ class _DefaultPub implements Pub {
if (pubSpecYaml.lastModifiedSync().isAfter(dotPackagesLastModified)) { if (pubSpecYaml.lastModifiedSync().isAfter(dotPackagesLastModified)) {
return true; return true;
} }
final File toolStampFile = _toolStampFile != null ? _toolStampFile() : null;
if (_toolStampFile != null && if (toolStampFile != null &&
_toolStampFile.existsSync() && toolStampFile.existsSync() &&
_toolStampFile.lastModifiedSync().isAfter(dotPackagesLastModified)) { toolStampFile.lastModifiedSync().isAfter(dotPackagesLastModified)) {
return true; return true;
} }
return false; return false;
......
...@@ -291,36 +291,13 @@ Future<Directory> _templateImageDirectory(String name, FileSystem fileSystem, Lo ...@@ -291,36 +291,13 @@ Future<Directory> _templateImageDirectory(String name, FileSystem fileSystem, Lo
final String toolPackagePath = fileSystem.path.join( final String toolPackagePath = fileSystem.path.join(
Cache.flutterRoot, 'packages', 'flutter_tools'); Cache.flutterRoot, 'packages', 'flutter_tools');
final String packageFilePath = fileSystem.path.join(toolPackagePath, kPackagesFileName); final String packageFilePath = fileSystem.path.join(toolPackagePath, kPackagesFileName);
// Ensure that .packgaes is present. final PackageConfig packageConfig = await loadPackageConfigWithLogging(
if (!fileSystem.file(packageFilePath).existsSync()) {
await _ensurePackageDependencies(toolPackagePath, pub);
}
PackageConfig packageConfig = await loadPackageConfigWithLogging(
fileSystem.file(packageFilePath), fileSystem.file(packageFilePath),
logger: logger, logger: logger,
); );
Uri imagePackageLibDir = packageConfig['flutter_template_images']?.packageUriRoot; final Uri imagePackageLibDir = packageConfig['flutter_template_images']?.packageUriRoot;
// Ensure that the template image package is present.
if (imagePackageLibDir == null || !fileSystem.directory(imagePackageLibDir).existsSync()) {
await _ensurePackageDependencies(toolPackagePath, pub);
packageConfig = await loadPackageConfigWithLogging(
fileSystem.file(packageFilePath),
logger: logger,
);
imagePackageLibDir = packageConfig['flutter_template_images']?.packageUriRoot;
}
return fileSystem.directory(imagePackageLibDir) return fileSystem.directory(imagePackageLibDir)
.parent .parent
.childDirectory('templates') .childDirectory('templates')
.childDirectory(name); .childDirectory(name);
} }
// Runs 'pub get' for the given path to ensure that .packages is created and
// all dependencies are present.
Future<void> _ensurePackageDependencies(String packagePath, Pub pub) async {
await pub.get(
context: PubContext.pubGet,
directory: packagePath,
generateSyntheticPackage: false,
);
}
...@@ -117,7 +117,7 @@ void main() { ...@@ -117,7 +117,7 @@ void main() {
platform: const LocalPlatform(), platform: const LocalPlatform(),
usage: globals.flutterUsage, usage: globals.flutterUsage,
botDetector: globals.botDetector, botDetector: globals.botDetector,
toolStampFile: globals.fs.file('test'), toolStampFile: () => globals.fs.file('test'),
); );
await pub.get( await pub.get(
context: PubContext.flutterTests, context: PubContext.flutterTests,
......
...@@ -23,7 +23,7 @@ void main() { ...@@ -23,7 +23,7 @@ void main() {
// Release lock between test cases. // Release lock between test cases.
Cache.releaseLock(); Cache.releaseLock();
when(cache.isUpToDate()).thenReturn(false); when(cache.isUpToDate()).thenAnswer((Invocation _) => Future<bool>.value(false));
when(cache.updateAll(any)).thenAnswer((Invocation invocation) { when(cache.updateAll(any)).thenAnswer((Invocation invocation) {
artifacts = invocation.positionalArguments.first as Set<DevelopmentArtifact>; artifacts = invocation.positionalArguments.first as Set<DevelopmentArtifact>;
return Future<void>.value(null); return Future<void>.value(null);
...@@ -410,7 +410,7 @@ void main() { ...@@ -410,7 +410,7 @@ void main() {
}); });
testUsingContext('precache deletes artifact stampfiles when --force is provided', () async { testUsingContext('precache deletes artifact stampfiles when --force is provided', () async {
when(cache.isUpToDate()).thenReturn(true); when(cache.isUpToDate()).thenAnswer((Invocation _) => Future<bool>.value(true));
final PrecacheCommand command = PrecacheCommand( final PrecacheCommand command = PrecacheCommand(
cache: cache, cache: cache,
logger: BufferLogger.test(), logger: BufferLogger.test(),
......
...@@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/logger.dart'; ...@@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
...@@ -137,25 +138,25 @@ void main() { ...@@ -137,25 +138,25 @@ void main() {
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('should not be up to date, if some cached artifact is not', () { testUsingContext('should not be up to date, if some cached artifact is not', () async {
final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact1 = MockCachedArtifact();
final CachedArtifact artifact2 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact();
when(artifact1.isUpToDate()).thenReturn(true); when(artifact1.isUpToDate()).thenAnswer((Invocation _) => Future<bool>.value(true));
when(artifact2.isUpToDate()).thenReturn(false); when(artifact2.isUpToDate()).thenAnswer((Invocation _) => Future<bool>.value(false));
final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
expect(cache.isUpToDate(), isFalse); expect(await cache.isUpToDate(), isFalse);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
FileSystem: () => MemoryFileSystem.test(), FileSystem: () => MemoryFileSystem.test(),
}); });
testUsingContext('should be up to date, if all cached artifacts are', () { testUsingContext('should be up to date, if all cached artifacts are', () async {
final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact1 = MockCachedArtifact();
final CachedArtifact artifact2 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact();
when(artifact1.isUpToDate()).thenReturn(true); when(artifact1.isUpToDate()).thenAnswer((Invocation _) => Future<bool>.value(true));
when(artifact2.isUpToDate()).thenReturn(true); when(artifact2.isUpToDate()).thenAnswer((Invocation _) => Future<bool>.value(true));
final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
expect(cache.isUpToDate(), isTrue); expect(await cache.isUpToDate(), isTrue);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
FileSystem: () => MemoryFileSystem.test(), FileSystem: () => MemoryFileSystem.test(),
...@@ -164,8 +165,8 @@ void main() { ...@@ -164,8 +165,8 @@ void main() {
testUsingContext('should update cached artifacts which are not up to date', () async { testUsingContext('should update cached artifacts which are not up to date', () async {
final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact1 = MockCachedArtifact();
final CachedArtifact artifact2 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact();
when(artifact1.isUpToDate()).thenReturn(true); when(artifact1.isUpToDate()).thenAnswer((Invocation _) => Future<bool>.value(true));
when(artifact2.isUpToDate()).thenReturn(false); when(artifact2.isUpToDate()).thenAnswer((Invocation _) => Future<bool>.value(false));
final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
await cache.updateAll(<DevelopmentArtifact>{ await cache.updateAll(<DevelopmentArtifact>{
null, null,
...@@ -206,8 +207,8 @@ void main() { ...@@ -206,8 +207,8 @@ void main() {
testUsingContext('failed storage.googleapis.com download shows China warning', () async { testUsingContext('failed storage.googleapis.com download shows China warning', () async {
final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact1 = MockCachedArtifact();
final CachedArtifact artifact2 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact();
when(artifact1.isUpToDate()).thenReturn(false); when(artifact1.isUpToDate()).thenAnswer((Invocation _) => Future<bool>.value(false));
when(artifact2.isUpToDate()).thenReturn(false); when(artifact2.isUpToDate()).thenAnswer((Invocation _) => Future<bool>.value(false));
final MockInternetAddress address = MockInternetAddress(); final MockInternetAddress address = MockInternetAddress();
when(address.host).thenReturn('storage.googleapis.com'); when(address.host).thenReturn('storage.googleapis.com');
when(artifact1.update(any)).thenThrow(SocketException( when(artifact1.update(any)).thenThrow(SocketException(
...@@ -317,7 +318,7 @@ void main() { ...@@ -317,7 +318,7 @@ void main() {
..createSync(recursive: true); ..createSync(recursive: true);
when(mockCache.getRoot()).thenReturn(cacheRoot); when(mockCache.getRoot()).thenReturn(cacheRoot);
final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(mockCache); final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(mockCache);
expect(mavenArtifacts.isUpToDate(), isFalse); expect(await mavenArtifacts.isUpToDate(), isFalse);
final Directory gradleWrapperDir = globals.fs.systemTempDirectory.createTempSync('flutter_cache_test_gradle_wrapper.'); final Directory gradleWrapperDir = globals.fs.systemTempDirectory.createTempSync('flutter_cache_test_gradle_wrapper.');
when(mockCache.getArtifactDirectory('gradle_wrapper')).thenReturn(gradleWrapperDir); when(mockCache.getArtifactDirectory('gradle_wrapper')).thenReturn(gradleWrapperDir);
...@@ -340,7 +341,7 @@ void main() { ...@@ -340,7 +341,7 @@ void main() {
await mavenArtifacts.update(MockArtifactUpdater()); await mavenArtifacts.update(MockArtifactUpdater());
expect(mavenArtifacts.isUpToDate(), isFalse); expect(await mavenArtifacts.isUpToDate(), isFalse);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Cache: () => mockCache, Cache: () => mockCache,
FileSystem: () => memoryFileSystem, FileSystem: () => memoryFileSystem,
...@@ -660,6 +661,69 @@ void main() { ...@@ -660,6 +661,69 @@ void main() {
expect(cache.getStampFor('foo'), 'ABC'); expect(cache.getStampFor('foo'), 'ABC');
}); });
testWithoutContext('PubDependencies needs to be updated if the package config'
' file or the source directories are missing', () async {
final BufferLogger logger = BufferLogger.test();
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final PubDependencies pubDependencies = PubDependencies(
flutterRoot: () => '',
fileSystem: fileSystem,
logger: logger,
pub: () => MockPub(),
);
expect(await pubDependencies.isUpToDate(), false); // no package config
fileSystem.file('packages/flutter_tools/.packages')
..createSync(recursive: true)
..writeAsStringSync('\n');
fileSystem.file('packages/flutter_tools/.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('''
{
"configVersion": 2,
"packages": [
{
"name": "example",
"rootUri": "file:///.pub-cache/hosted/pub.dartlang.org/example-7.0.0",
"packageUri": "lib/",
"languageVersion": "2.7"
}
],
"generated": "2020-09-15T20:29:20.691147Z",
"generator": "pub",
"generatorVersion": "2.10.0-121.0.dev"
}
''');
expect(await pubDependencies.isUpToDate(), false); // dependencies are missing.
fileSystem.file('.pub-cache/hosted/pub.dartlang.org/example-7.0.0/lib/foo.dart')
.createSync(recursive: true);
expect(await pubDependencies.isUpToDate(), true);
});
testWithoutContext('PubDependencies updates via pub get', () async {
final BufferLogger logger = BufferLogger.test();
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final MockPub pub = MockPub();
final PubDependencies pubDependencies = PubDependencies(
flutterRoot: () => '',
fileSystem: fileSystem,
logger: logger,
pub: () => pub,
);
await pubDependencies.update(MockArtifactUpdater());
verify(pub.get(
context: PubContext.pubGet,
directory: 'packages/flutter_tools',
generateSyntheticPackage: false,
)).called(1);
});
} }
class FakeCachedArtifact extends EngineCachedArtifact { class FakeCachedArtifact extends EngineCachedArtifact {
...@@ -724,6 +788,7 @@ class MockInternetAddress extends Mock implements InternetAddress {} ...@@ -724,6 +788,7 @@ class MockInternetAddress extends Mock implements InternetAddress {}
class MockCache extends Mock implements Cache {} class MockCache extends Mock implements Cache {}
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {} class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
class MockVersionedPackageResolver extends Mock implements VersionedPackageResolver {} class MockVersionedPackageResolver extends Mock implements VersionedPackageResolver {}
class MockPub extends Mock implements Pub {}
class FakeCache extends Cache { class FakeCache extends Cache {
FakeCache({ FakeCache({
@required Logger logger, @required Logger logger,
......
...@@ -294,12 +294,6 @@ void main() { ...@@ -294,12 +294,6 @@ void main() {
verify(mockAppConnection.runMain()).called(1); verify(mockAppConnection.runMain()).called(1);
verify(status.stop()).called(1); verify(status.stop()).called(1);
verify(pub.get(
context: PubContext.pubGet,
directory: anyNamed('directory'),
generateSyntheticPackage: false,
)).called(1);
expect(bufferLogger.statusText, contains('Debug service listening on ws://127.0.0.1/abcd/')); expect(bufferLogger.statusText, contains('Debug service listening on ws://127.0.0.1/abcd/'));
expect(debugConnectionInfo.wsUri.toString(), 'ws://127.0.0.1/abcd/'); expect(debugConnectionInfo.wsUri.toString(), 'ws://127.0.0.1/abcd/');
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
......
...@@ -931,7 +931,7 @@ class FakeCache implements Cache { ...@@ -931,7 +931,7 @@ class FakeCache implements Cache {
} }
@override @override
bool isUpToDate() { Future<bool> isUpToDate() async {
return true; return true;
} }
......
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