| // 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, |
| ); |
| } |
| }, skip: 'https://github.com/dart-lang/tools/issues/2134'); |
| } |