Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite the 'exported extension method' tests; add basic extension tests #3738

Merged
merged 1 commit into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 2 additions & 50 deletions test/dartdoc_test_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ analyzer:
packagePath, libraryName, Uri.file('$packagePath/'));
}

Future<PackageGraph> _bootPackageFromFiles(Iterable<d.Descriptor> files,
Future<PackageGraph> bootPackageFromFiles(Iterable<d.Descriptor> files,
{List<String> additionalArguments = const []}) async {
var packagePathBasename =
resourceProvider.pathContext.basename(packagePath);
Expand All @@ -103,7 +103,7 @@ analyzer:
{String libraryPreamble = '',
Iterable<d.Descriptor> extraFiles = const [],
List<String> additionalArguments = const []}) async {
return (await _bootPackageFromFiles([
return (await bootPackageFromFiles([
d.dir('lib', [
d.file('lib.dart', '''
$libraryPreamble
Expand All @@ -118,54 +118,6 @@ $libraryContent
.named(libraryName);
}

/// Similar to [bootPackageWithLibrary], but allows for more complex
/// cases to test the edges of canonicalization.
///
/// - Puts [reexportedContent] in a library named [libraryName]_src in
/// `lib/src` (if [reexportPrivate] is true), or 'lib/subdir'.
/// - Creates a reexporting library named [libraryName]_lib in `lib` that
/// reexports [libraryName]_src.
/// - Creates [libraryName] containing [libraryContent] that can optionally
/// import 'lib.dart' to import the reexporting library.
///
/// Optionally, specify [show] or [hide] to change whether the reexport
/// gives access to the full namespace.
Future<Library> bootPackageWithReexportedLibrary(
String reexportedContent, String libraryContent,
{bool reexportPrivate = false,
List<String> show = const [],
List<String> hide = const []}) async {
final subdir = reexportPrivate ? 'src' : 'subdir';
if (show.isNotEmpty && hide.isNotEmpty) {
throw DartdocTestBaseFailure('Can not specify show and hide');
}
final showHideString = '${show.isNotEmpty ? 'show ${show.join(', ')}' : ''}'
'${hide.isNotEmpty ? 'hide ${hide.join(', ')}' : ''}';

return (await _bootPackageFromFiles([
d.dir('lib', [
d.dir(subdir, [
d.file('lib.dart', '''
library ${libraryName}_src;

$reexportedContent
'''),
]),
d.file('lib.dart', '''
library ${libraryName}_lib;

export '$subdir/lib.dart' $showHideString;
'''),
d.file('importing_lib.dart', '''
library $libraryName;
$libraryContent
'''),
])
]))
.libraries
.named(libraryName);
}

Future<Dartdoc> buildDartdoc({
List<String> excludeLibraries = const [],
List<String> additionalArguments = const [],
Expand Down
116 changes: 0 additions & 116 deletions test/extension_methods_test.dart

This file was deleted.

133 changes: 133 additions & 0 deletions test/extensions_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (c) 2024, 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 'package:dartdoc/src/markdown_processor.dart';
import 'package:dartdoc/src/model/model.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import 'dartdoc_test_base.dart';
import 'src/test_descriptor_utils.dart' as d;
import 'src/utils.dart';

void main() {
defineReflectiveSuite(() {
defineReflectiveTests(ExtensionMethodsTest);
defineReflectiveTests(ExtensionMethodsExportTest);
});
}

@reflectiveTest
class ExtensionMethodsTest extends DartdocTestBase {
@override
String get libraryName => 'extension_methods';

void test_referenceToExtension() async {
var library = await bootPackageWithLibrary('''
extension Ex on int {}

/// Text [Ex].
var f() {}
''');

expect(
library.functions.named('f').documentationAsHtml,
contains('<a href="$linkPrefix/Ex.html">Ex</a>'),
);
}

void test_referenceToExtensionMethod() async {
var library = await bootPackageWithLibrary('''
extension Ex on int {
void m() {}
}

/// Text [Ex.m].
var f() {}
''');

expect(
library.functions.named('f').documentationAsHtml,
contains('<a href="$linkPrefix/Ex/m.html">Ex.m</a>'),
);
}

// TODO(srawlins): Test everything else about extensions.
}

@reflectiveTest
class ExtensionMethodsExportTest extends DartdocTestBase {
@override
String get libraryName => 'extension_methods';

late Package package;

/// Verifies that comment reference text, [referenceText] attached to the
/// function, `f`, is resolved to [expected], and links to [href].
void expectReferenceValidFromF(
String referenceText, ModelElement expected, String href) {
var fFunction = package.functions.named('f');
var reference = getMatchingLinkElement(referenceText, fFunction)
.commentReferable as ModelElement;
expect(identical(reference.canonicalModelElement, expected), isTrue);
expect(expected.isCanonical, isTrue);
expect(expected.href, endsWith(href));
}

/// Sets up a package with three files:
///
/// * a private file containing a class, `C`, an extension on that class, `E`,
/// `E`, and a method in that extension, `m`.
/// * A public file that exports some members of the private file. The content
/// of this file is specified with [exportingLibraryContent].
/// * Another public file which is completely unrelated to the first two (no
/// imports or exports linking them), which contains a function, `f`.
Future<void> setupWith(String exportingLibraryContent) async {
var packageGraph = await bootPackageFromFiles([
d.dir('lib', [
d.dir('src', [
d.file('lib.dart', '''
class C {}
extension Ex on C {
void m() {}
}
'''),
]),
d.file('one.dart', exportingLibraryContent),
d.file('two.dart', '''
/// Comment.
var f() {}
'''),
])
]);
package = packageGraph.defaultPackage;
}

void test_reexportWithShow() async {
await setupWith("export 'src/lib.dart' show C, Ex;");

var ex = package.extensions.named('Ex');
var m = ex.instanceMethods.named('m');
expectReferenceValidFromF('Ex', ex, '%one/Ex.html');
expectReferenceValidFromF('Ex.m', m, '%one/Ex/m.html');
}

void test_reexportWithHide() async {
await setupWith("export 'src/lib.dart' hide A;");

var ex = package.extensions.named('Ex');
var m = ex.instanceMethods.named('m');
expectReferenceValidFromF('Ex', ex, '%one/Ex.html');
expectReferenceValidFromF('Ex.m', m, '%one/Ex/m.html');
}

void test_reexportFull() async {
await setupWith("export 'src/lib.dart';");

var ex = package.extensions.named('Ex');
var m = ex.instanceMethods.named('m');
expectReferenceValidFromF('Ex', ex, '%one/Ex.html');
expectReferenceValidFromF('Ex.m', m, '%one/Ex/m.html');
}
}
19 changes: 13 additions & 6 deletions test/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ import 'package:dartdoc/src/generator/resource_loader.dart';
import 'package:dartdoc/src/logging.dart';
import 'package:dartdoc/src/markdown_processor.dart';
import 'package:dartdoc/src/matching_link_result.dart';
import 'package:dartdoc/src/model/model_element.dart';
import 'package:dartdoc/src/model/package_builder.dart';
import 'package:dartdoc/src/model/package_graph.dart';
import 'package:dartdoc/src/model/model.dart';
import 'package:dartdoc/src/package_config_provider.dart';
import 'package:dartdoc/src/package_meta.dart';
import 'package:dartdoc/src/warnings.dart';
Expand Down Expand Up @@ -356,12 +354,11 @@ bool get classModifiersAllowed =>
VersionRange(min: Version.parse('3.0.0-0.0-dev'), includeMin: true)
.allows(platformVersion);

extension ModelElementIterableExtensions<T extends ModelElement>
on Iterable<T> {
extension ModelElementIterableExtension<T extends ModelElement> on Iterable<T> {
T named(String name) => firstWhere((e) => e.name == name);
}

extension IterableStringExtensions on Iterable<String> {
extension IterableStringExtension on Iterable<String> {
/// The main content line of `this`.
Iterable<String> get mainContent =>
skipWhile((line) => !line.contains('"dartdoc-main-content"'))
Expand All @@ -377,6 +374,16 @@ extension IterableStringExtensions on Iterable<String> {
}
}

extension PackageExtension on Package {
/// Gathers all extensions found across a package.
Iterable<Extension> get extensions =>
libraries.expand((library) => library.extensions);

/// Gathers all functions found across a package.
Iterable<ModelFunction> get functions =>
libraries.expand((library) => library.functions);
}

/// Extension methods just for tests.
extension on ResourceProvider {
Future<void> writeDartdocResource(String resourcePath, String content) async {
Expand Down