blob: 33e8bf7d674963d5397d4f5e2789189db602e987 [file]
// Copyright (c) 2023, 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:isolate' show Isolate;
import 'package:extension_discovery/extension_discovery.dart';
import 'package:extension_discovery/src/io.dart';
import 'package:test/test.dart';
import 'test_descriptor.dart' as d;
void main() {
test('findExtensions', () async {
// Override the maturity delay for reliable testing of caching logic.
modificationMaturityDelay = const Duration(milliseconds: 500);
final pkgLibDir = await Isolate.resolvePackageUri(
Uri.parse('package:extension_discovery/'),
);
final pkgDir = pkgLibDir!.resolve('..');
await d.dir('myapp', [
d.pubspec({
'name': 'myapp',
'dependencies': {
'extension_discovery': {'path': pkgDir.toFilePath()},
},
'environment': {'sdk': '^3.0.0'},
}),
]).create();
// Get dependencies
await d.dartPubGet(d.path('myapp'));
// Check that we don't find any extensions
expect(
await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
),
isEmpty);
// Check that there is a myapp.json cache file
await d
.file(
'myapp/.dart_tool/extension_discovery/myapp.json',
isNotEmpty,
)
.validate();
// ################################## Create foo that provides extension
await d.dir('foo', [
d.pubspec({
'name': 'foo',
'environment': {'sdk': '^3.0.0'},
}),
// It has a config.yaml for myapp
d.dir('extension/myapp', [
d.json('config.yaml', {'fromFoo': true}),
]),
]).create();
// Update the pubspec.yaml with a dependency on foo
await d.dir('myapp', [
d.pubspec({
'name': 'myapp',
'dependencies': {
'extension_discovery': {'path': pkgDir.toFilePath()},
'foo': {'path': '../foo'},
},
'environment': {'sdk': '^3.0.0'},
}),
]).create();
// Get dependencies
await d.dartPubGet(d.path('myapp'));
// Check that we do find an extension
{
final ext = await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext, isNotEmpty);
expect(
ext.any(
(e) =>
e.package == 'foo' &&
e.rootUri == d.directoryUri('foo/') &&
e.packageUri == Uri.parse('lib/') &&
e.config['fromFoo'] == true,
),
isTrue,
);
}
// ################################## Modify the config
await d.json('foo/extension/myapp/config.yaml', {'fromFoo': 42}).create();
// Check that we do find an extension
{
final ext = await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext, isNotEmpty);
expect(
ext.any(
(e) =>
e.package == 'foo' &&
e.rootUri == d.directoryUri('foo/') &&
e.packageUri == Uri.parse('lib/') &&
e.config['fromFoo'] == 42,
),
isTrue,
);
}
// ################################## Make config invalid
await d.file('foo/extension/myapp/config.yaml', 'invalid YAML').create();
// Check that we do not find extensions
{
final ext = await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext, isEmpty);
}
// ################################## Make config valid again
await d.json('foo/extension/myapp/config.yaml', {'fromFoo': true}).create();
// Check that we do find extensions
{
final ext = await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext, isNotEmpty);
}
// ################################## Remove config file
await d.file('foo/extension/myapp/config.yaml').io.delete();
// Check that we do not find extensions
{
final ext = await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext, isEmpty);
}
// ################################## Make config valid again
await d.json('foo/extension/myapp/config.yaml', {'fromFoo': true}).create();
// Check that we do find extensions
{
final ext = await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext, isNotEmpty);
}
// ################################## Create bar as absolute path-dep
await d.dir('bar', [
d.pubspec({
'name': 'bar',
'environment': {'sdk': '^3.0.0'},
}),
// It has a config.yaml for myapp
d.dir('extension/myapp', [
d.json('config.yaml', {'fromFoo': false}),
]),
]).create();
// Update the pubspec.yaml with a dependency on foo
await d.dir('myapp', [
d.pubspec({
'name': 'myapp',
'dependencies': {
'extension_discovery': {'path': pkgDir.toFilePath()},
'foo': {'path': '../foo'},
'bar': {'path': d.path('bar')},
},
'environment': {'sdk': '^3.0.0'},
}),
]).create();
// We won't find bar without running `dart pub get`
{
final ext = await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext.any((e) => e.package == 'bar'), isFalse);
}
// Get dependencies
await d.dartPubGet(d.path('myapp'));
// Check that we do find both extensions
{
final ext = await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext, isNotEmpty);
expect(
ext.any(
(e) =>
e.package == 'foo' &&
e.rootUri == d.directoryUri('foo/') &&
e.packageUri == Uri.parse('lib/') &&
e.config['fromFoo'] == true,
),
isTrue,
);
expect(
ext.any(
(e) =>
e.package == 'bar' &&
e.rootUri == d.directoryUri('bar/') &&
e.packageUri == Uri.parse('lib/') &&
e.config['fromFoo'] == false,
),
isTrue,
);
}
// ################################## Changes to bar are not detected
// Ensure that we wait long enough for the cache to be trusted,
// this delay is to make sure that the logic we have handling races isn't
// triggered. Think of it as waiting...
// If the modification time of package_config.json and the cache is no more
// than [modificationMaturityDelay] time apart, we'll ignore the cache.
// Under the assumption that there was a tiny risk of a race condition.
await Future<void>.delayed(modificationMaturityDelay * 2);
// Ensure that we have a cache value!
await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
// Delete config from package that has an absolute path, hence, a package
// we assume to be immutable.
await d.file('bar/extension/myapp/config.yaml').io.delete();
// Check that we do find both extensions
{
final ext = await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext.any((e) => e.package == 'foo'), isTrue);
// This is the wrong result, but it is expected. Essentially, if we expect
// that if you have packages in package_config.json using an absolute path
// then these won't be modified. If you do modify the
// extension/<targetPackage>/config.yaml
// file, then the cache will give the wrong result. The workaround is to
// touch `package_config.json`, or run `dart pub get` (essentially).
expect(ext.any((e) => e.package == 'bar'), isTrue);
}
// Get dependencies
await d.dartPubGet(d.path('myapp'));
// We won't find bar after `dart pub get`, because we removed the config
{
final ext = await findExtensions(
'myapp',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext.any((e) => e.package == 'foo'), isTrue);
expect(ext.any((e) => e.package == 'bar'), isFalse);
}
// ################################## Sanity check bar and foo
expect(
await findExtensions(
'foo',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
),
isEmpty,
);
expect(
await findExtensions(
'bar',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
),
isEmpty,
);
// ################################## What if foo has extensions for bar
await d.dir('foo/extension/bar', [
d.json('config.yaml', {'fromFoo': true}),
]).create();
{
final ext = await findExtensions(
'bar',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext.any((e) => e.package == 'foo'), isTrue);
expect(ext.any((e) => e.package == 'bar'), isFalse);
expect(ext.any((e) => e.package == 'myapp'), isFalse);
}
// ################################## myapp could also extend bar
await d.dir('myapp/extension/bar', [
d.json('config.yaml', {'fromFoo': false}),
]).create();
{
final ext = await findExtensions(
'bar',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext.any((e) => e.package == 'foo'), isTrue);
expect(ext.any((e) => e.package == 'bar'), isFalse);
expect(ext.any((e) => e.package == 'myapp'), isTrue);
}
// ################################## config could also be written in YAML
await d.dir('myapp/extension/bar', [
d.file('config.yaml', '''
writtenAsYaml: true
'''),
]).create();
{
final ext = await findExtensions(
'bar',
packageConfig: d.fileUri('myapp/.dart_tool/package_config.json'),
);
expect(ext.any((e) => e.package == 'foo'), isTrue);
expect(ext.any((e) => e.package == 'bar'), isFalse);
expect(ext.any((e) => e.package == 'myapp'), isTrue);
expect(
ext.any(
(e) => e.package == 'myapp' && e.config['writtenAsYaml'] == true,
),
isTrue,
);
}
});
}