Skip to content

Commit 2559a77

Browse files
authored
Rewrite the 'exported extension method' tests; add basic extension tests. (#3738)
I found this file and it was very hard for me to understand. It contained three tests for extensions being exported from private to public API. I've preserved the tests, but in an entirely new format. It is more standard, I think, and more readable. Since these were the only tests in a file called 'extension_methods_test', I decided that I should add a few basic extension tests (and move the file to extension_tests) to differentiate, a bit, what the file should contain.
1 parent b45174e commit 2559a77

File tree

4 files changed

+148
-172
lines changed

4 files changed

+148
-172
lines changed

test/dartdoc_test_base.dart

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ analyzer:
7979
packagePath, libraryName, Uri.file('$packagePath/'));
8080
}
8181

82-
Future<PackageGraph> _bootPackageFromFiles(Iterable<d.Descriptor> files,
82+
Future<PackageGraph> bootPackageFromFiles(Iterable<d.Descriptor> files,
8383
{List<String> additionalArguments = const []}) async {
8484
var packagePathBasename =
8585
resourceProvider.pathContext.basename(packagePath);
@@ -103,7 +103,7 @@ analyzer:
103103
{String libraryPreamble = '',
104104
Iterable<d.Descriptor> extraFiles = const [],
105105
List<String> additionalArguments = const []}) async {
106-
return (await _bootPackageFromFiles([
106+
return (await bootPackageFromFiles([
107107
d.dir('lib', [
108108
d.file('lib.dart', '''
109109
$libraryPreamble
@@ -118,54 +118,6 @@ $libraryContent
118118
.named(libraryName);
119119
}
120120

121-
/// Similar to [bootPackageWithLibrary], but allows for more complex
122-
/// cases to test the edges of canonicalization.
123-
///
124-
/// - Puts [reexportedContent] in a library named [libraryName]_src in
125-
/// `lib/src` (if [reexportPrivate] is true), or 'lib/subdir'.
126-
/// - Creates a reexporting library named [libraryName]_lib in `lib` that
127-
/// reexports [libraryName]_src.
128-
/// - Creates [libraryName] containing [libraryContent] that can optionally
129-
/// import 'lib.dart' to import the reexporting library.
130-
///
131-
/// Optionally, specify [show] or [hide] to change whether the reexport
132-
/// gives access to the full namespace.
133-
Future<Library> bootPackageWithReexportedLibrary(
134-
String reexportedContent, String libraryContent,
135-
{bool reexportPrivate = false,
136-
List<String> show = const [],
137-
List<String> hide = const []}) async {
138-
final subdir = reexportPrivate ? 'src' : 'subdir';
139-
if (show.isNotEmpty && hide.isNotEmpty) {
140-
throw DartdocTestBaseFailure('Can not specify show and hide');
141-
}
142-
final showHideString = '${show.isNotEmpty ? 'show ${show.join(', ')}' : ''}'
143-
'${hide.isNotEmpty ? 'hide ${hide.join(', ')}' : ''}';
144-
145-
return (await _bootPackageFromFiles([
146-
d.dir('lib', [
147-
d.dir(subdir, [
148-
d.file('lib.dart', '''
149-
library ${libraryName}_src;
150-
151-
$reexportedContent
152-
'''),
153-
]),
154-
d.file('lib.dart', '''
155-
library ${libraryName}_lib;
156-
157-
export '$subdir/lib.dart' $showHideString;
158-
'''),
159-
d.file('importing_lib.dart', '''
160-
library $libraryName;
161-
$libraryContent
162-
'''),
163-
])
164-
]))
165-
.libraries
166-
.named(libraryName);
167-
}
168-
169121
Future<Dartdoc> buildDartdoc({
170122
List<String> excludeLibraries = const [],
171123
List<String> additionalArguments = const [],

test/extension_methods_test.dart

Lines changed: 0 additions & 116 deletions
This file was deleted.

test/extensions_test.dart

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:dartdoc/src/markdown_processor.dart';
6+
import 'package:dartdoc/src/model/model.dart';
7+
import 'package:test/test.dart';
8+
import 'package:test_reflective_loader/test_reflective_loader.dart';
9+
10+
import 'dartdoc_test_base.dart';
11+
import 'src/test_descriptor_utils.dart' as d;
12+
import 'src/utils.dart';
13+
14+
void main() {
15+
defineReflectiveSuite(() {
16+
defineReflectiveTests(ExtensionMethodsTest);
17+
defineReflectiveTests(ExtensionMethodsExportTest);
18+
});
19+
}
20+
21+
@reflectiveTest
22+
class ExtensionMethodsTest extends DartdocTestBase {
23+
@override
24+
String get libraryName => 'extension_methods';
25+
26+
void test_referenceToExtension() async {
27+
var library = await bootPackageWithLibrary('''
28+
extension Ex on int {}
29+
30+
/// Text [Ex].
31+
var f() {}
32+
''');
33+
34+
expect(
35+
library.functions.named('f').documentationAsHtml,
36+
contains('<a href="$linkPrefix/Ex.html">Ex</a>'),
37+
);
38+
}
39+
40+
void test_referenceToExtensionMethod() async {
41+
var library = await bootPackageWithLibrary('''
42+
extension Ex on int {
43+
void m() {}
44+
}
45+
46+
/// Text [Ex.m].
47+
var f() {}
48+
''');
49+
50+
expect(
51+
library.functions.named('f').documentationAsHtml,
52+
contains('<a href="$linkPrefix/Ex/m.html">Ex.m</a>'),
53+
);
54+
}
55+
56+
// TODO(srawlins): Test everything else about extensions.
57+
}
58+
59+
@reflectiveTest
60+
class ExtensionMethodsExportTest extends DartdocTestBase {
61+
@override
62+
String get libraryName => 'extension_methods';
63+
64+
late Package package;
65+
66+
/// Verifies that comment reference text, [referenceText] attached to the
67+
/// function, `f`, is resolved to [expected], and links to [href].
68+
void expectReferenceValidFromF(
69+
String referenceText, ModelElement expected, String href) {
70+
var fFunction = package.functions.named('f');
71+
var reference = getMatchingLinkElement(referenceText, fFunction)
72+
.commentReferable as ModelElement;
73+
expect(identical(reference.canonicalModelElement, expected), isTrue);
74+
expect(expected.isCanonical, isTrue);
75+
expect(expected.href, endsWith(href));
76+
}
77+
78+
/// Sets up a package with three files:
79+
///
80+
/// * a private file containing a class, `C`, an extension on that class, `E`,
81+
/// `E`, and a method in that extension, `m`.
82+
/// * A public file that exports some members of the private file. The content
83+
/// of this file is specified with [exportingLibraryContent].
84+
/// * Another public file which is completely unrelated to the first two (no
85+
/// imports or exports linking them), which contains a function, `f`.
86+
Future<void> setupWith(String exportingLibraryContent) async {
87+
var packageGraph = await bootPackageFromFiles([
88+
d.dir('lib', [
89+
d.dir('src', [
90+
d.file('lib.dart', '''
91+
class C {}
92+
extension Ex on C {
93+
void m() {}
94+
}
95+
'''),
96+
]),
97+
d.file('one.dart', exportingLibraryContent),
98+
d.file('two.dart', '''
99+
/// Comment.
100+
var f() {}
101+
'''),
102+
])
103+
]);
104+
package = packageGraph.defaultPackage;
105+
}
106+
107+
void test_reexportWithShow() async {
108+
await setupWith("export 'src/lib.dart' show C, Ex;");
109+
110+
var ex = package.extensions.named('Ex');
111+
var m = ex.instanceMethods.named('m');
112+
expectReferenceValidFromF('Ex', ex, '%one/Ex.html');
113+
expectReferenceValidFromF('Ex.m', m, '%one/Ex/m.html');
114+
}
115+
116+
void test_reexportWithHide() async {
117+
await setupWith("export 'src/lib.dart' hide A;");
118+
119+
var ex = package.extensions.named('Ex');
120+
var m = ex.instanceMethods.named('m');
121+
expectReferenceValidFromF('Ex', ex, '%one/Ex.html');
122+
expectReferenceValidFromF('Ex.m', m, '%one/Ex/m.html');
123+
}
124+
125+
void test_reexportFull() async {
126+
await setupWith("export 'src/lib.dart';");
127+
128+
var ex = package.extensions.named('Ex');
129+
var m = ex.instanceMethods.named('m');
130+
expectReferenceValidFromF('Ex', ex, '%one/Ex.html');
131+
expectReferenceValidFromF('Ex.m', m, '%one/Ex/m.html');
132+
}
133+
}

test/src/utils.dart

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ import 'package:dartdoc/src/generator/resource_loader.dart';
1616
import 'package:dartdoc/src/logging.dart';
1717
import 'package:dartdoc/src/markdown_processor.dart';
1818
import 'package:dartdoc/src/matching_link_result.dart';
19-
import 'package:dartdoc/src/model/model_element.dart';
20-
import 'package:dartdoc/src/model/package_builder.dart';
21-
import 'package:dartdoc/src/model/package_graph.dart';
19+
import 'package:dartdoc/src/model/model.dart';
2220
import 'package:dartdoc/src/package_config_provider.dart';
2321
import 'package:dartdoc/src/package_meta.dart';
2422
import 'package:dartdoc/src/warnings.dart';
@@ -356,12 +354,11 @@ bool get classModifiersAllowed =>
356354
VersionRange(min: Version.parse('3.0.0-0.0-dev'), includeMin: true)
357355
.allows(platformVersion);
358356

359-
extension ModelElementIterableExtensions<T extends ModelElement>
360-
on Iterable<T> {
357+
extension ModelElementIterableExtension<T extends ModelElement> on Iterable<T> {
361358
T named(String name) => firstWhere((e) => e.name == name);
362359
}
363360

364-
extension IterableStringExtensions on Iterable<String> {
361+
extension IterableStringExtension on Iterable<String> {
365362
/// The main content line of `this`.
366363
Iterable<String> get mainContent =>
367364
skipWhile((line) => !line.contains('"dartdoc-main-content"'))
@@ -377,6 +374,16 @@ extension IterableStringExtensions on Iterable<String> {
377374
}
378375
}
379376

377+
extension PackageExtension on Package {
378+
/// Gathers all extensions found across a package.
379+
Iterable<Extension> get extensions =>
380+
libraries.expand((library) => library.extensions);
381+
382+
/// Gathers all functions found across a package.
383+
Iterable<ModelFunction> get functions =>
384+
libraries.expand((library) => library.functions);
385+
}
386+
380387
/// Extension methods just for tests.
381388
extension on ResourceProvider {
382389
Future<void> writeDartdocResource(String resourcePath, String content) async {

0 commit comments

Comments
 (0)