Revert "[analyzer] Use "pub outdated" to get version numbers for pubspec completion"

This reverts commit 9cbc497d0a.

Reason for revert: Breaks the flutter build

Original change's description:
> [analyzer] Use "pub outdated" to get version numbers for pubspec completion
>
> Change-Id: Ic8ef9514946070d590fc4594db4d8474912b40ff
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/207760
> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>

TBR=brianwilkerson@google.com,danny@tuppeny.com

Change-Id: I78f8658385f3175d07bd453af0f110eff04741ae
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208344
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Brian Wilkerson 2021-07-28 18:33:25 +00:00 committed by commit-bot@chromium.org
parent 40a837d213
commit 4911f0968e
16 changed files with 50 additions and 818 deletions

View file

@ -55,7 +55,6 @@ import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
import 'package:analyzer_plugin/utilities/navigation/navigation_dart.dart';
import 'package:http/http.dart' as http;
import 'package:process/process.dart';
import 'package:telemetry/crash_reporting.dart';
import 'package:telemetry/telemetry.dart' as telemetry;
import 'package:watcher/watcher.dart';
@ -131,7 +130,6 @@ class AnalysisServer extends AbstractAnalysisServer {
CrashReportingAttachmentsBuilder crashReportingAttachmentsBuilder,
InstrumentationService instrumentationService, {
http.Client? httpClient,
ProcessManager? processManager,
RequestStatisticsHelper? requestStatistics,
DiagnosticServer? diagnosticServer,
this.detachableFileSystemManager,
@ -145,7 +143,6 @@ class AnalysisServer extends AbstractAnalysisServer {
baseResourceProvider,
instrumentationService,
httpClient,
processManager,
NotificationManager(channel, baseResourceProvider.pathContext),
requestStatistics: requestStatistics,
enableBazelWatcher: enableBazelWatcher,
@ -432,11 +429,9 @@ class AnalysisServer extends AbstractAnalysisServer {
bool isPubspec(String filePath) =>
file_paths.isPubspecYaml(resourceProvider.pathContext, filePath);
// When pubspecs are opened, trigger pre-loading of pub package names and
// versions.
final pubspecs = files.where(isPubspec).toList();
if (pubspecs.isNotEmpty) {
pubPackageService.beginCachePreloads(pubspecs);
// When a pubspec is opened, trigger package name caching for completion.
if (!pubPackageService.isRunning && files.any(isPubspec)) {
pubPackageService.beginPackageNamePreload();
}
priorityFiles.clear();
@ -692,18 +687,6 @@ class ServerContextManagerCallbacks extends ContextManagerCallbacks {
analysisDriver.priorityFiles = analysisServer.priorityFiles.toList();
}
@override
void pubspecChanged(String pubspecPath) {
analysisServer.pubPackageService.fetchPackageVersionsViaPubOutdated(
pubspecPath,
pubspecWasModified: true);
}
@override
void pubspecRemoved(String pubspecPath) {
analysisServer.pubPackageService.flushPackageCaches(pubspecPath);
}
@override
void recordAnalysisErrors(String path, List<AnalysisError> errors) {
filesToFlush.add(path);

View file

@ -19,7 +19,6 @@ import 'package:analysis_server/src/services/completion/dart/documentation_cache
import 'package:analysis_server/src/services/completion/dart/extension_cache.dart';
import 'package:analysis_server/src/services/correction/namespace.dart';
import 'package:analysis_server/src/services/pub/pub_api.dart';
import 'package:analysis_server/src/services/pub/pub_command.dart';
import 'package:analysis_server/src/services/pub/pub_package_service.dart';
import 'package:analysis_server/src/services/search/element_visitors.dart';
import 'package:analysis_server/src/services/search/search_engine.dart';
@ -53,7 +52,6 @@ import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:collection/collection.dart';
import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';
import 'package:process/process.dart';
/// Implementations of [AbstractAnalysisServer] implement a server that listens
/// on a [CommunicationChannel] for analysis messages and process them.
@ -154,26 +152,14 @@ abstract class AbstractAnalysisServer {
ResourceProvider baseResourceProvider,
this.instrumentationService,
http.Client? httpClient,
ProcessManager? processManager,
this.notificationManager, {
this.requestStatistics,
bool enableBazelWatcher = false,
}) : resourceProvider = OverlayResourceProvider(baseResourceProvider),
pubApi = PubApi(instrumentationService, httpClient,
Platform.environment['PUB_HOSTED_URL']) {
// We can only use a LocalProcessManager to run pub commands when backed by
// a real file system, otherwise we may try to run commands in folders that
// don't really exist. If processManager was supplied, it's likely a mock
// from a test in which case the pub command should still be created.
if (baseResourceProvider is PhysicalResourceProvider) {
processManager ??= LocalProcessManager();
}
final pubCommand = processManager != null
? PubCommand(instrumentationService, processManager)
: null;
pubPackageService = PubPackageService(
instrumentationService, baseResourceProvider, pubApi, pubCommand);
pubPackageService =
PubPackageService(instrumentationService, baseResourceProvider, pubApi);
performance = performanceDuringStartup;
pluginManager = PluginManager(

View file

@ -122,12 +122,6 @@ abstract class ContextManagerCallbacks {
/// TODO(scheglov) Just pass results in here?
void listenAnalysisDriver(AnalysisDriver driver);
/// The `pubspec.yaml` at [path] was added/modified.
void pubspecChanged(String path);
/// The `pubspec.yaml` at [path] was removed.
void pubspecRemoved(String path);
/// Record error information for the file with the given [path].
void recordAnalysisErrors(String path, List<protocol.AnalysisError> errors);
}
@ -572,21 +566,12 @@ class ContextManagerImpl implements ContextManager {
_instrumentationService.logWatchEvent('<unknown>', path, type.toString());
final isPubpsec = file_paths.isPubspecYaml(pathContext, path);
if (file_paths.isAnalysisOptionsYaml(pathContext, path) ||
file_paths.isDotPackages(pathContext, path) ||
file_paths.isPackageConfigJson(pathContext, path) ||
isPubpsec ||
file_paths.isPubspecYaml(pathContext, path) ||
false) {
_createAnalysisContexts();
if (isPubpsec) {
if (type == ChangeType.REMOVE) {
callbacks.pubspecRemoved(path);
} else {
callbacks.pubspecChanged(path);
}
}
return;
}
@ -741,12 +726,6 @@ class NoopContextManagerCallbacks implements ContextManagerCallbacks {
@override
void listenAnalysisDriver(AnalysisDriver driver) {}
@override
void pubspecChanged(String pubspecPath) {}
@override
void pubspecRemoved(String pubspecPath) {}
@override
void recordAnalysisErrors(String path, List<protocol.AnalysisError> errors) {}
}

View file

@ -52,7 +52,6 @@ import 'package:analyzer_plugin/src/protocol/protocol_internal.dart' as plugin;
import 'package:collection/collection.dart';
import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';
import 'package:process/process.dart';
import 'package:watcher/watcher.dart';
/// Instances of the class [LspAnalysisServer] implement an LSP-based server
@ -127,7 +126,6 @@ class LspAnalysisServer extends AbstractAnalysisServer {
CrashReportingAttachmentsBuilder crashReportingAttachmentsBuilder,
InstrumentationService instrumentationService, {
http.Client? httpClient,
ProcessManager? processManager,
DiagnosticServer? diagnosticServer,
// Disable to avoid using this in unit tests.
bool enableBazelWatcher = false,
@ -139,7 +137,6 @@ class LspAnalysisServer extends AbstractAnalysisServer {
baseResourceProvider,
instrumentationService,
httpClient,
processManager,
LspNotificationManager(channel, baseResourceProvider.pathContext),
enableBazelWatcher: enableBazelWatcher,
) {
@ -187,10 +184,10 @@ class LspAnalysisServer extends AbstractAnalysisServer {
RefactoringWorkspace(driverMap.values, searchEngine);
void addPriorityFile(String filePath) {
// When pubspecs are opened, trigger pre-loading of pub package names and
// versions.
if (file_paths.isPubspecYaml(resourceProvider.pathContext, filePath)) {
pubPackageService.beginCachePreloads([filePath]);
// When a pubspec is opened, trigger package name caching for completion.
if (!pubPackageService.isRunning &&
file_paths.isPubspecYaml(resourceProvider.pathContext, filePath)) {
pubPackageService.beginPackageNamePreload();
}
final didAdd = priorityFiles.add(filePath);
@ -854,18 +851,6 @@ class LspServerContextManagerCallbacks extends ContextManagerCallbacks {
analysisDriver.priorityFiles = analysisServer.priorityFiles.toList();
}
@override
void pubspecChanged(String pubspecPath) {
analysisServer.pubPackageService.fetchPackageVersionsViaPubOutdated(
pubspecPath,
pubspecWasModified: true);
}
@override
void pubspecRemoved(String pubspecPath) {
analysisServer.pubPackageService.flushPackageCaches(pubspecPath);
}
@override
void recordAnalysisErrors(String path, List<protocol.AnalysisError> errors) {
final errorsToSend = errors.where(_shouldSendError).toList();

View file

@ -49,15 +49,6 @@ final diagnosticTagsForErrorCode = <String, List<lsp.DiagnosticTag>>{
],
};
/// Pattern for docComplete text on completion items that can be upgraded to
/// the "detail" field so that it can be shown more prominently by clients.
///
/// This is typically used for labels like _latest compatible_ and _latest_ in
/// the pubspec version items. These go into docComplete so that they appear
/// reasonably for non-LSP clients where there is no equivalent of the detail
/// field.
final _upgradableDocCompletePattern = RegExp(r'^_([\w ]{0,20})_$');
lsp.Either2<String, lsp.MarkupContent> asStringOrMarkupContent(
Set<lsp.MarkupKind>? preferredFormats, String content) {
return preferredFormats == null
@ -965,21 +956,7 @@ lsp.CompletionItem toCompletionItem(
final insertText = insertTextInfo.first;
final insertTextFormat = insertTextInfo.last;
final isMultilineCompletion = insertText.contains('\n');
var cleanedDoc = cleanDartdoc(suggestion.docComplete);
var detail = getCompletionDetail(suggestion, completionKind,
supportsCompletionDeprecatedFlag || supportsDeprecatedTag);
// To improve the display of some items (like pubspec version numbers),
// short labels in the format `_foo_` in docComplete are "upgraded" to the
// detail field.
final labelMatch = cleanedDoc != null
? _upgradableDocCompletePattern.firstMatch(cleanedDoc)
: null;
if (labelMatch != null) {
cleanedDoc = null;
detail = labelMatch.group(1);
}
final cleanedDoc = cleanDartdoc(suggestion.docComplete);
// Because we potentially send thousands of these items, we should minimise
// the generated JSON as much as possible - for example using nulls in place
@ -994,7 +971,8 @@ lsp.CompletionItem toCompletionItem(
commitCharacters:
includeCommitCharacters ? dartCompletionCommitCharacters : null,
data: resolutionData,
detail: detail,
detail: getCompletionDetail(suggestion, completionKind,
supportsCompletionDeprecatedFlag || supportsDeprecatedTag),
documentation: cleanedDoc != null
? asStringOrMarkupContent(formats, cleanedDoc)
: null,

View file

@ -176,11 +176,9 @@ abstract class Producer {
const Producer();
/// A utility method used to create a suggestion for the [identifier].
CompletionSuggestion identifier(String identifier,
{int relevance = 1000, String? docComplete}) =>
CompletionSuggestion identifier(String identifier, {int relevance = 1000}) =>
CompletionSuggestion(CompletionSuggestionKind.IDENTIFIER, relevance,
identifier, identifier.length, 0, false, false,
docComplete: docComplete);
identifier, identifier.length, 0, false, false);
/// A utility method used to create a suggestion for the package [packageName].
CompletionSuggestion packageName(String packageName,

View file

@ -37,25 +37,18 @@ class PubPackageVersionProducer extends Producer {
@override
Iterable<CompletionSuggestion> suggestions(
YamlCompletionRequest request) sync* {
final versions = request.pubPackageService
?.cachedPubOutdatedVersions(request.filePath, package);
final resolvable = versions?.resolvableVersion;
var latest = versions?.latestVersion;
// If we didn't get a latest version from the "pub outdated" results, we can
// use the result from the Pub API if we've called it (this will usually
// only be the case for LSP where a resolve() call was sent).
// TOOD(dantup): Consider supporting async completion requests so this
// could call packageDetails() (with a short timeout, and pub retries
// disabled). A user that explicitly invokes completion in the location
// of a version may be prepared to wait a short period for a web request
// to get completion versions (this is also the only way for non-LSP
// clients to get them, since there are no resolve calls).
//
// This allows us (in some cases) to still show version numbers even if the
// package was newly added to pubspec and not saved, so not yet in the
// "pub outdated" results.
latest ??= request.pubPackageService?.cachedPubApiLatestVersion(package);
if (resolvable != null && resolvable != latest) {
yield identifier('^$resolvable', docComplete: '_latest compatible_');
}
if (latest != null) {
yield identifier('^$latest', docComplete: '_latest_');
// Supporting this will require making the completion async further up.
final details = request.pubPackageService?.cachedPackageDetails(package);
final version = details?.latestVersion;
if (version != null) {
yield identifier('^$version');
}
}
}

View file

@ -1,149 +0,0 @@
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:analyzer/instrumentation/service.dart';
import 'package:path/path.dart' as path;
import 'package:process/process.dart';
/// A class for interacting with the `pub` command.
///
/// `pub` commands will be queued and not run concurrently.
class PubCommand {
static const String _pubEnvironmentKey = 'PUB_ENVIRONMENT';
final InstrumentationService _instrumentationService;
late final ProcessManager _processManager;
late final String _pubPath;
late final String _pubEnvironmentValue;
/// Tracks the last queued command to avoid overlapping because pub does not
/// do its own locking when accessing the cache.
///
/// https://github.com/dart-lang/pub/issues/1178
///
/// This does not prevent running concurrently with commands spawned by other
/// tools (such as the IDE).
var _lastQueuedCommand = Future<void>.value();
PubCommand(this._instrumentationService, this._processManager) {
_pubPath = path.join(
path.dirname(Platform.resolvedExecutable),
Platform.isWindows ? 'pub.bat' : 'pub',
);
// When calling the `pub` command, we must add an identifier to the
// PUB_ENVIRONMENT environment variable (joined with colons).
const _pubEnvString = 'analysis_server.pub_api';
final existingPubEnv = Platform.environment[_pubEnvironmentKey];
_pubEnvironmentValue = [
if (existingPubEnv?.isNotEmpty ?? false) existingPubEnv,
_pubEnvString,
].join(':');
}
/// Runs `pub outdated --show-all` and returns the results.
///
/// If any error occurs executing the command, returns an empty list.
Future<List<PubOutdatedPackageDetails>> outdatedVersions(
String pubspecPath) async {
final packageDirectory = path.dirname(pubspecPath);
final result = await _runPubJsonCommand(
['outdated', '--show-all', '--json'],
workingDirectory: packageDirectory);
if (result == null) {
return [];
}
final packages =
(result['packages'] as List<dynamic>?)?.cast<Map<String, Object?>>();
if (packages == null) {
return [];
}
return packages
.map(
(json) => PubOutdatedPackageDetails(
json['package'] as String,
currentVersion: _version(json, 'current'),
latestVersion: _version(json, 'latest'),
resolvableVersion: _version(json, 'resolvable'),
upgradableVersion: _version(json, 'upgradable'),
),
)
.toList();
}
/// Runs a pub command and decodes JSON from `stdout`.
///
/// Returns null if:
/// - exit code is non-zero
/// - returned text cannot be decoded as JSON
Future<Map<String, Object?>?> _runPubJsonCommand(List<String> args,
{required String workingDirectory}) async {
// Atomically replace the lastQueuedCommand future with our own to ensure
// only one command waits on any previous commands future.
final completer = Completer<void>();
final lastCommand = _lastQueuedCommand;
_lastQueuedCommand = completer.future;
// And wait for that previous command to finish.
await lastCommand.catchError((_) {});
try {
final command = [_pubPath, ...args];
_instrumentationService.logInfo('Running pub command $command');
final result = await _processManager.run(command,
workingDirectory: workingDirectory,
environment: {_pubEnvironmentKey: _pubEnvironmentValue});
if (result.exitCode != 0) {
_instrumentationService.logError(
'pub command returned ${result.exitCode} exit code: ${result.stderr}.');
return null;
}
try {
final results = jsonDecode(result.stdout);
_instrumentationService.logInfo('pub command completed successfully');
return results;
} catch (e) {
_instrumentationService
.logError('pub command returned invalid JSON: $e.');
return null;
}
} catch (e) {
_instrumentationService.logError('pub command failed to run: $e.');
return null;
} finally {
completer.complete();
}
}
String? _version(Map<String, Object?> json, String type) {
final versionType = json[type] as Map<String, Object?>?;
final version =
versionType != null ? versionType['version'] as String? : null;
return version;
}
}
class PubOutdatedPackageDetails {
final String packageName;
final String? currentVersion;
final String? latestVersion;
final String? resolvableVersion;
final String? upgradableVersion;
PubOutdatedPackageDetails(
this.packageName, {
required this.currentVersion,
required this.latestVersion,
required this.resolvableVersion,
required this.upgradableVersion,
});
}

View file

@ -6,11 +6,10 @@ import 'dart:async';
import 'dart:convert';
import 'package:analysis_server/src/services/pub/pub_api.dart';
import 'package:analysis_server/src/services/pub/pub_command.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/instrumentation/service.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
/// Information about Pub packages that can be converted to/from JSON and
/// cached to disk.
@ -131,27 +130,16 @@ class PubPackage {
/// A service for providing Pub package information.
///
/// Uses a [PubApi] to communicate with the Pub API and a [PubCommand] to
/// interact with the local `pub` command.
///
/// Expensive results are cached to disk using [resourceProvider].
/// Uses a [PubApi] to communicate with Pub and caches to disk using [cacheResourceProvider].
class PubPackageService {
final InstrumentationService _instrumentationService;
final PubApi _api;
/// A wrapper over the "pub" command line too.
///
/// This can be null when not running on a real file system because it may
/// try to interact with folders that don't really exist.
final PubCommand? _command;
Timer? _nextPackageNameListRequestTimer;
Timer? _nextWriteDiskCacheTimer;
/// [ResourceProvider] used for accessing the disk for caches and checking
/// project types. This should generally be a [PhysicalResourceProvider]
/// outside of tests.
final ResourceProvider resourceProvider;
/// [ResourceProvider] used for caching. This should generally be a
/// [PhysicalResourceProvider] outside of tests.
final ResourceProvider cacheResourceProvider;
/// The current cache of package information. Initially `null`, but
/// overwritten after first read of cache from disk or fetch from the API.
@ -160,50 +148,25 @@ class PubPackageService {
int _packageDetailsRequestsInFlight = 0;
/// A cache of version numbers from running the "pub outdated" command used
/// for completion in pubspec.yaml.
final _pubspecPackageVersions =
<String, Map<String, PubOutdatedPackageDetails>>{};
PubPackageService(
this._instrumentationService, this.cacheResourceProvider, this._api);
PubPackageService(this._instrumentationService, this.resourceProvider,
this._api, this._command);
/// Gets the last set of package results from the Pub API or an empty List if
/// no results.
///
/// This data is used for completion of package names in pubspec.yaml
/// and for clients that support lazy resolution of completion items may also
/// include their descriptions and/or version numbers.
/// Gets the last set of package results or an empty List if no results.
List<PubPackage> get cachedPackages =>
packageCache?.packages.values.toList() ?? [];
@visibleForTesting
bool get isPackageNamesTimerRunning =>
_nextPackageNameListRequestTimer != null;
bool get isRunning => _nextPackageNameListRequestTimer != null;
@visibleForTesting
File get packageCacheFile {
final cacheFolder = resourceProvider
final cacheFolder = cacheResourceProvider
.getStateLocation('.pub-package-details-cache')!
..create();
return cacheFolder.getChildAssumingFile('packages.json');
}
/// Begins preloading caches for package names and pub versions.
void beginCachePreloads(List<String> pubspecs) {
beginPackageNamePreload();
for (final pubspec in pubspecs) {
fetchPackageVersionsViaPubOutdated(pubspec, pubspecWasModified: false);
}
}
/// Begin a timer to pre-load and update the package name list if one has not
/// already been started.
/// Begin a request to pre-load the package name list.
void beginPackageNamePreload() {
if (isPackageNamesTimerRunning) {
return;
}
// If first time, try to read from disk.
var cache = packageCache;
if (cache == null) {
@ -216,69 +179,11 @@ class PubPackageService {
Timer(cache.cacheTimeRemaining, _fetchFromServer);
}
/// Gets the latest cached package version fetched from the Pub API for the
/// package [packageName].
String? cachedPubApiLatestVersion(String packageName) =>
packageCache?.packages[packageName]?.latestVersion;
/// Gets the package versions cached using "pub outdated" for the package
/// [packageName] for the project using [pubspecPath].
/// Gets the cached package details for package [packageName].
///
/// Versions in here might only be available for packages that are in the
/// pubspec on disk. Newly-added packages in the overlay might not be
/// available.
PubOutdatedPackageDetails? cachedPubOutdatedVersions(
String pubspecPath, String packageName) {
final pubspecCache = _pubspecPackageVersions[pubspecPath];
return pubspecCache != null ? pubspecCache[packageName] : null;
}
/// Begin a request to pre-load package versions using the "pub outdated"
/// command.
///
/// If [pubspecWasModified] is true, the command will always be run. Otherwise it
/// will only be run if data is not already cached.
Future<void> fetchPackageVersionsViaPubOutdated(String pubspecPath,
{required bool pubspecWasModified}) async {
final pubCommand = _command;
if (pubCommand == null) {
return;
}
// If we already have a cache for the file and it was not modified (only
// opened) we do not need to re-run the command.
if (!pubspecWasModified &&
_pubspecPackageVersions.containsKey(pubspecPath)) {
return;
}
// Check if this pubspec is inside a DEPS-managed folder, and if so
// just cache an empty set of results since Pub is not managing
// dependencies.
if (_hasAncestorDEPSFile(pubspecPath)) {
_pubspecPackageVersions.putIfAbsent(pubspecPath, () => {});
return;
}
final results = await pubCommand.outdatedVersions(pubspecPath);
final cache = _pubspecPackageVersions.putIfAbsent(pubspecPath, () => {});
for (final package in results) {
// We use the versions from the "pub outdated" results but only cache them
// in-memory for this specific pubspec, as the resolved version may be
// restricted by constraints/dependencies in the pubspec. The "pub"
// command does caching of the JSON versions to make "pub outdated" fast.
cache[package.packageName] = package;
}
}
/// Clears package caches for [pubspecPath].
///
/// Does not remove other caches that are not pubspec-specific (for example
/// the latest version pulled directly from the Pub API independant of
/// pubspec).
Future<void> flushPackageCaches(String pubspecPath) async {
_pubspecPackageVersions.remove(pubspecPath);
}
/// Returns null if no package details are cached.
PubPackage? cachedPackageDetails(String packageName) =>
packageCache?.packages[packageName];
/// Gets package details for package [packageName].
///
@ -362,19 +267,6 @@ class PubPackageService {
}
}
/// Checks whether there is a DEPS file in any folder walking up from the
/// pubspec at [pubspecPath].
bool _hasAncestorDEPSFile(String pubspecPath) {
var folder = path.dirname(pubspecPath);
do {
if (resourceProvider.getFile(path.join(folder, 'DEPS')).exists) {
return true;
}
folder = path.dirname(folder);
} while (folder != path.dirname(folder));
return false;
}
/// Writes the package cache to disk after
/// [PackageDetailsCache._writeCacheDebounceDuration] has elapsed, restarting
/// the timer each time this method is called.

View file

@ -24,8 +24,6 @@ dependencies:
linter: any
meta:
path: ../meta
process:
path: ../../third_party/pkg/process
stream_channel: any
telemetry:
path: ../telemetry

View file

@ -8,9 +8,6 @@ import 'package:test/test.dart';
import 'server_abstract.dart';
mixin CompletionTestMixin on AbstractLspAnalysisServerTest {
/// The last set of completion results fetched.
List<CompletionItem> completionResults = [];
int sortTextSorter(CompletionItem item1, CompletionItem item2) =>
(item1.sortText ?? item1.label).compareTo(item2.sortText ?? item2.label);
@ -43,24 +40,23 @@ mixin CompletionTestMixin on AbstractLspAnalysisServerTest {
if (openCloseFile) {
await openFile(fileUri, withoutMarkers(content));
}
completionResults =
await getCompletion(fileUri, positionFromMarker(content));
final res = await getCompletion(fileUri, positionFromMarker(content));
if (openCloseFile) {
await closeFile(fileUri);
}
// Sort the completions by sortText and filter to those we expect, so the ordering
// can be compared.
final sortedResults = completionResults
final sortedResults = res
.where((r) => expectCompletions.contains(r.label))
.toList()
..sort(sortTextSorter);
..sort(sortTextSorter);
expect(sortedResults.map((item) => item.label), equals(expectCompletions));
// Check the edits apply correctly.
if (applyEditsFor != null) {
var item = completionResults.singleWhere((c) => c.label == applyEditsFor);
var item = res.singleWhere((c) => c.label == applyEditsFor);
final insertFormat = item.insertTextFormat;
if (resolve) {

View file

@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
import 'package:analysis_server/src/services/pub/pub_api.dart';
import 'package:http/http.dart';
import 'package:linter/src/rules.dart';
@ -369,7 +367,7 @@ dependencies:
);
}
Future<void> test_package_versions_fromApi() async {
Future<void> test_package_version() async {
httpClient.sendHandler = (BaseRequest request) async {
if (request.url.path.startsWith(PubApi.packageNameListPath)) {
return Response(samplePackageList, 200);
@ -420,163 +418,6 @@ dependencies:
);
}
Future<void> test_package_versions_fromPubOutdated() async {
final json = r'''
{
"packages": [
{
"package": "one",
"latest": { "version": "3.2.1" },
"resolvable": { "version": "1.2.4" }
}
]
}
''';
processManager.runHandler =
(command, {dir, env}) => ProcessResult(1, 0, json, '');
final content = '''
name: foo
version: 1.0.0
dependencies:
one: ^''';
final expected = '''
name: foo
version: 1.0.0
dependencies:
one: ^1.2.4''';
await initialize();
await openFile(pubspecFileUri, withoutMarkers(content));
await pumpEventQueue(times: 500);
await verifyCompletions(
pubspecFileUri,
content,
expectCompletions: ['^1.2.4', '^3.2.1'],
applyEditsFor: '^1.2.4',
expectedContent: expected,
openCloseFile: false,
);
}
Future<void> test_package_versions_fromPubOutdated_afterChange() async {
final initialJson = r'''
{
"packages": [
{
"package": "one",
"latest": { "version": "3.2.1" },
"resolvable": { "version": "1.2.3" }
}
]
}
''';
final updatedJson = r'''
{
"packages": [
{
"package": "one",
"latest": { "version": "2.1.0" },
"resolvable": { "version": "2.3.4" }
}
]
}
''';
processManager.runHandler =
(command, {dir, env}) => ProcessResult(1, 0, initialJson, '');
final content = '''
name: foo
version: 1.0.0
dependencies:
one: ^''';
final expected = '''
name: foo
version: 1.0.0
dependencies:
one: ^2.3.4''';
newFile(pubspecFilePath, content: content);
await initialize();
await openFile(pubspecFileUri, withoutMarkers(content));
await pumpEventQueue(times: 500);
// Modify the underlying file which should trigger an update of the
// cached data.
processManager.runHandler =
(command, {dir, env}) => ProcessResult(1, 0, updatedJson, '');
modifyFile(pubspecFilePath, '$content# trailing comment');
await pumpEventQueue(times: 500);
await verifyCompletions(
pubspecFileUri,
content,
expectCompletions: ['^2.3.4', '^2.1.0'],
applyEditsFor: '^2.3.4',
expectedContent: expected,
openCloseFile: false,
);
// Also veryify the detail fields were populated as expected.
expect(
completionResults.singleWhere((c) => c.label == '^2.3.4').detail,
equals('latest compatible'),
);
expect(
completionResults.singleWhere((c) => c.label == '^2.1.0').detail,
equals('latest'),
);
}
Future<void> test_package_versions_fromPubOutdated_afterDelete() async {
final initialJson = r'''
{
"packages": [
{
"package": "one",
"latest": { "version": "3.2.1" },
"resolvable": { "version": "1.2.3" }
}
]
}
''';
processManager.runHandler =
(command, {dir, env}) => ProcessResult(1, 0, initialJson, '');
final content = '''
name: foo
version: 1.0.0
dependencies:
one: ^''';
newFile(pubspecFilePath, content: content);
await initialize();
await openFile(pubspecFileUri, withoutMarkers(content));
await pumpEventQueue(times: 500);
// Delete the underlying file which should trigger eviction of the cache.
deleteFile(pubspecFilePath);
await pumpEventQueue(times: 500);
await verifyCompletions(
pubspecFileUri,
content,
expectCompletions: [],
openCloseFile: false,
);
// There should have been no version numbers.
expect(completionResults, isEmpty);
}
Future<void> test_topLevel() async {
final content = '''
version: 1.0.0

View file

@ -2,16 +2,10 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
import 'package:analysis_server/src/services/pub/pub_api.dart';
import 'package:analysis_server/src/services/pub/pub_command.dart';
import 'package:analysis_server/src/services/pub/pub_package_service.dart';
import 'package:analyzer/instrumentation/service.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:collection/collection.dart';
import 'package:http/http.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@ -21,7 +15,6 @@ import 'server_abstract.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(PubApiTest);
defineReflectiveTests(PubCommandTest);
defineReflectiveTests(PubPackageServiceTest);
});
}
@ -84,168 +77,6 @@ class PubApiTest {
}
}
@reflectiveTest
class PubCommandTest with ResourceProviderMixin {
late MockProcessManager processManager;
late PubCommand pubCommand;
late String pubspecPath, pubspec2Path;
void setUp() {
pubspecPath = convertPath('/home/project/pubspec.yaml');
pubspec2Path = convertPath('/home/project2/pubspec.yaml');
processManager = MockProcessManager();
pubCommand =
PubCommand(InstrumentationService.NULL_SERVICE, processManager);
}
Future<void> test_doesNotRunConcurrently() async {
var isRunning = false;
processManager.runHandler = (command, {dir, env}) async {
expect(isRunning, isFalse,
reason: 'pub commands should not run concurrently');
isRunning = true;
await pumpEventQueue(times: 500);
isRunning = false;
return ProcessResult(0, 0, '', '');
};
await Future.wait([
pubCommand.outdatedVersions(pubspecPath),
pubCommand.outdatedVersions(pubspecPath),
]);
}
Future<void> test_outdated_args() async {
processManager.runHandler = (command, {dir, env}) {
var expectedPubPath = path.join(
path.dirname(Platform.resolvedExecutable),
Platform.isWindows ? 'pub.bat' : 'pub',
);
expect(
command,
equals([
expectedPubPath,
'outdated',
'--show-all',
'--json',
]));
expect(dir, equals(convertPath('/home/project')));
expect(
env!['PUB_ENVIRONMENT'],
anyOf(equals('analysis_server.pub_api'),
endsWith(':analysis_server.pub_api')));
return ProcessResult(0, 0, '', '');
};
await pubCommand.outdatedVersions(pubspecPath);
}
Future<void> test_outdated_invalidJson() async {
processManager.runHandler = (List<String> command, {dir, env}) =>
ProcessResult(1, 0, 'NOT VALID JSON', '');
final result = await pubCommand.outdatedVersions(pubspecPath);
expect(result, isEmpty);
}
Future<void> test_outdated_missingFields() async {
final validJson = r'''
{
"packages": [
{
"package": "foo",
"current": { "version": "1.0.0" },
"upgradable": { "version": "2.0.0" },
"resolvable": { }
}
]
}
''';
processManager.runHandler =
(command, {dir, env}) => ProcessResult(1, 0, validJson, '');
final result = await pubCommand.outdatedVersions(pubspecPath);
expect(result, hasLength(1));
final package = result.first;
expect(package.packageName, equals('foo'));
expect(package.currentVersion, equals('1.0.0'));
expect(package.upgradableVersion, equals('2.0.0'));
expect(package.resolvableVersion, isNull);
expect(package.latestVersion, isNull);
}
Future<void> test_outdated_multiplePubspecs() async {
final pubspecJson1 = r'''
{
"packages": [
{
"package": "foo",
"resolvable": { "version": "1.1.1" }
}
]
}
''';
final pubspecJson2 = r'''
{
"packages": [
{
"package": "foo",
"resolvable": { "version": "2.2.2" }
}
]
}
''';
processManager.runHandler = (command, {dir, env}) {
// Return different json based on the directory we were invoked in.
final json =
dir == path.dirname(pubspecPath) ? pubspecJson1 : pubspecJson2;
return ProcessResult(1, 0, json, '');
};
final result1 = await pubCommand.outdatedVersions(pubspecPath);
final result2 = await pubCommand.outdatedVersions(pubspec2Path);
expect(result1.first.resolvableVersion, equals('1.1.1'));
expect(result2.first.resolvableVersion, equals('2.2.2'));
}
Future<void> test_outdated_nonZeroExitCode() async {
processManager.runHandler =
(command, {dir, env}) => ProcessResult(1, 123, '{}', '');
final result = await pubCommand.outdatedVersions(pubspecPath);
expect(result, isEmpty);
}
Future<void> test_validJson() async {
final validJson = r'''
{
"packages": [
{
"package": "foo",
"current": { "version": "1.0.0" },
"upgradable": { "version": "2.0.0" },
"resolvable": { "version": "3.0.0" },
"latest": { "version": "4.0.0" }
},
{
"package": "bar",
"current": { "version": "1.0.0" },
"upgradable": { "version": "2.0.0" },
"resolvable": { "version": "3.0.0" },
"latest": { "version": "4.0.0" }
}
]
}
''';
processManager.runHandler =
(command, {dir, env}) => ProcessResult(1, 0, validJson, '');
final result = await pubCommand.outdatedVersions(pubspecPath);
expect(result, hasLength(2));
result.forEachIndexed((index, package) {
expect(package.packageName, equals(index == 0 ? 'foo' : 'bar'));
expect(package.currentVersion, equals('1.0.0'));
expect(package.upgradableVersion, equals('2.0.0'));
expect(package.resolvableVersion, equals('3.0.0'));
expect(package.latestVersion, equals('4.0.0'));
});
}
}
@reflectiveTest
class PubPackageServiceTest extends AbstractLspAnalysisServerTest {
/// A sample API response for package names. This should match the JSON served
@ -363,13 +194,13 @@ class PubPackageServiceTest extends AbstractLspAnalysisServerTest {
Future<void> test_packageCache_initializesOnPubspecOpen() async {
await initialize();
expect(server.pubPackageService.isPackageNamesTimerRunning, isFalse);
expect(server.pubPackageService.isRunning, isFalse);
expect(server.pubPackageService.packageCache, isNull);
expectPackages([]);
await openFile(pubspecFileUri, '');
await pumpEventQueue();
expect(server.pubPackageService.isPackageNamesTimerRunning, isTrue);
expect(server.pubPackageService.isRunning, isTrue);
expect(server.pubPackageService.packageCache, isNotNull);
expectPackages([]);
}

View file

@ -48,7 +48,6 @@ abstract class AbstractLspAnalysisServerTest
late MockLspServerChannel channel;
late TestPluginManager pluginManager;
late LspAnalysisServer server;
late MockProcessManager processManager;
late MockHttpClient httpClient;
/// The number of context builds that had already occurred the last time
@ -152,7 +151,6 @@ abstract class AbstractLspAnalysisServerTest
void setUp() {
httpClient = MockHttpClient();
processManager = MockProcessManager();
channel = MockLspServerChannel(debugPrintCommunication);
// Create an SDK in the mock file system.
MockSdk(resourceProvider: resourceProvider);
@ -164,8 +162,7 @@ abstract class AbstractLspAnalysisServerTest
DartSdkManager(convertPath('/sdk')),
CrashReportingAttachmentsBuilder.empty,
InstrumentationService.NULL_SERVICE,
httpClient: httpClient,
processManager: processManager);
httpClient: httpClient);
server.pluginManager = pluginManager;
projectFolderPath = convertPath('/home/test');

View file

@ -3,14 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:http/http.dart' as http;
import 'package:process/process.dart';
import 'package:test/test.dart';
/// A [Matcher] that check that the given [Response] has an expected identifier
@ -50,31 +47,6 @@ class MockHttpClient extends http.BaseClient {
}
}
class MockProcessManager implements ProcessManager {
FutureOr<ProcessResult> Function(List<String> command,
{String? dir, Map<String, String>? env})? runHandler =
(command, {dir, env}) => throw UnimplementedError();
@override
dynamic noSuchMethod(Invocation invocation) {
return super.noSuchMethod(invocation);
}
@override
Future<ProcessResult> run(
List<dynamic> command, {
String? workingDirectory,
Map<String, String>? environment,
bool includeParentEnvironment = true,
bool runInShell = false,
Encoding stdoutEncoding = systemEncoding,
Encoding stderrEncoding = systemEncoding,
}) async {
return runHandler!(command.cast<String>(),
dir: workingDirectory, env: environment);
}
}
class MockSource implements Source {
@override
final String fullName;

View file

@ -2,11 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
import 'package:analysis_server/src/services/completion/yaml/pubspec_generator.dart';
import 'package:analysis_server/src/services/pub/pub_api.dart';
import 'package:analysis_server/src/services/pub/pub_command.dart';
import 'package:analysis_server/src/services/pub/pub_package_service.dart';
import 'package:analyzer/instrumentation/service.dart';
import 'package:http/http.dart';
@ -25,7 +22,6 @@ void main() {
@reflectiveTest
class PubspecGeneratorTest extends YamlGeneratorTest {
late MockHttpClient httpClient;
late MockProcessManager processManager;
late PubPackageService pubPackageService;
@ -38,12 +34,10 @@ class PubspecGeneratorTest extends YamlGeneratorTest {
void setUp() {
httpClient = MockHttpClient();
processManager = MockProcessManager();
pubPackageService = PubPackageService(
InstrumentationService.NULL_SERVICE,
resourceProvider,
PubApi(InstrumentationService.NULL_SERVICE, httpClient, null),
PubCommand(InstrumentationService.NULL_SERVICE, processManager));
PubApi(InstrumentationService.NULL_SERVICE, httpClient, null));
}
void tearDown() {
@ -345,46 +339,4 @@ dependencies:
''');
assertSuggestion('two: ');
}
void test_packageVersion() async {
final json = r'''
{
"packages": [
{
"package": "one",
"latest": { "version": "3.2.1" },
"resolvable": { "version": "1.2.4" }
}
]
}
''';
processManager.runHandler =
(command, {dir, env}) => ProcessResult(1, 0, json, '');
pubPackageService.beginCachePreloads([convertPath('/home/test/$fileName')]);
await pumpEventQueue(times: 500);
getCompletions('''
dependencies:
one: ^
''');
assertSuggestion('^1.2.4');
assertSuggestion('^3.2.1');
}
/// Ensure in a repo with a DEPS file like the SDK, we do not run pub
/// processes to cache the version numbers.
void test_packageVersion_withDEPSfile() async {
var didRun = false;
processManager.runHandler = (command, {dir, env}) {
didRun = true;
return ProcessResult(1, 0, '', '');
};
newFile('/home/DEPS');
pubPackageService.beginCachePreloads([convertPath('/home/test/$fileName')]);
await pumpEventQueue(times: 500);
expect(didRun, isFalse);
}
}