Skip to content

Commit 2bef0f2

Browse files
authored
PackageConfigProvider, MockSdk, etc for improved unit testing (#2332)
* The new PackageConfigProvider class abstracts over PackageConfig from package_config. PhysicalPackageConfigProvider uses the real one; MemoryPackageConfigProvider is used in tests. * The new `isSdkLibraryDocumented` abstracts over SdkLibrary's `isDocumented`, because it throws unimplemented for `MockSdkLibrary`. * Remove `ResourceProviderExtensions.defaultSdkDir`. Now this is a property of PackageMetaProvider. * **Breaking change**: Move `io_utils` `listDir` to be a private method in PackageBuilder. * **Breaking change**: Add parameter to PubPackageBuilder constructor for a PackageConfigProvider. * **Breaking change**: Add two parameters to the PackageMetaProvider constructor, one for the default SDK directory, and one for the DartSdk. * Deprecate package.dart's `substituteNameVersion`. * Shorten doc comments here and there to 80 columns. * Move any tests which use `testing/test_package_small` to be unit tests; delete the package in `testing/`. * Move some tests for properties of package which use the ginormous testing package to unit tests
1 parent 54cf40d commit 2bef0f2

19 files changed

+447
-250
lines changed

bin/dartdoc.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ Future<void> main(List<String> arguments) async {
1717
// There was an error while parsing options.
1818
return;
1919
}
20-
final packageBuilder = PubPackageBuilder(config, pubPackageMetaProvider);
20+
final packageConfigProvider = PhysicalPackageConfigProvider();
21+
final packageBuilder =
22+
PubPackageBuilder(config, pubPackageMetaProvider, packageConfigProvider);
2123
final dartdoc = config.generateDocs
2224
? await Dartdoc.fromContext(config, packageBuilder)
2325
: await Dartdoc.withEmptyGenerator(config, packageBuilder);

lib/dartdoc.dart

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export 'package:dartdoc/src/dartdoc_options.dart';
3333
export 'package:dartdoc/src/element_type.dart';
3434
export 'package:dartdoc/src/generator/generator.dart';
3535
export 'package:dartdoc/src/model/model.dart';
36+
export 'package:dartdoc/src/package_config_provider.dart';
3637
export 'package:dartdoc/src/package_meta.dart';
3738

3839
const String programName = 'dartdoc';

lib/src/dartdoc_options.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -671,8 +671,8 @@ abstract class DartdocOption<T> {
671671
String get _directoryCurrentPath => resourceProvider.pathContext.current;
672672

673673
/// Calls [valueAt] on the directory this element is defined in.
674-
T valueAtElement(Element element) => valueAt(resourceProvider.getFolder(
675-
resourceProvider.pathContext.canonicalize(
674+
T valueAtElement(Element element) =>
675+
valueAt(resourceProvider.getFolder(resourceProvider.pathContext.normalize(
676676
resourceProvider.pathContext.basename(element.source.fullName))));
677677

678678
/// Adds a DartdocOption to the children of this DartdocOption.
@@ -1685,7 +1685,7 @@ Future<List<DartdocOption<Object>>> createDartdocOptions(
16851685
return resourceProvider.pathContext
16861686
.join(flutterRoot, 'bin', 'cache', 'dart-sdk');
16871687
}
1688-
return resourceProvider.defaultSdkDir.path;
1688+
return packageMetaProvider.defaultSdkDir.path;
16891689
}, packageMetaProvider.resourceProvider,
16901690
help: 'Path to the SDK directory.', isDir: true, mustExist: true),
16911691
DartdocOptionArgFile<bool>(

lib/src/io_utils.dart

+10-57
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import 'dart:io' as io;
1111

1212
import 'package:analyzer/file_system/file_system.dart';
1313
import 'package:analyzer/file_system/physical_file_system.dart';
14-
import 'package:dartdoc/src/package_meta.dart';
14+
import 'package:analyzer/src/generated/sdk.dart';
15+
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
1516
import 'package:path/path.dart' as path;
1617

1718
Encoding utf8AllowMalformed = Utf8Codec(allowMalformed: true);
@@ -34,6 +35,14 @@ String resolveTildePath(String originalPath) {
3435
return path.join(homeDir, originalPath.substring(2));
3536
}
3637

38+
bool isSdkLibraryDocumented(SdkLibrary library) {
39+
if (library is MockSdkLibrary) {
40+
// Not implemented in [MockSdkLibrary].
41+
return true;
42+
}
43+
return library.isDocumented;
44+
}
45+
3746
extension ResourceProviderExtensions on ResourceProvider {
3847
Folder createSystemTemp(String prefix) {
3948
if (this is PhysicalResourceProvider) {
@@ -43,20 +52,6 @@ extension ResourceProviderExtensions on ResourceProvider {
4352
}
4453
}
4554

46-
Folder get defaultSdkDir {
47-
if (this is PhysicalResourceProvider) {
48-
var sdkDir = getFile(pathContext.absolute(io.Platform.resolvedExecutable))
49-
.parent
50-
.parent;
51-
assert(pathContext.equals(
52-
sdkDir.path, PubPackageMeta.sdkDirParent(sdkDir, this).path));
53-
return sdkDir;
54-
} else {
55-
// TODO(srawlins): Return what is needed for tests.
56-
return null;
57-
}
58-
}
59-
6055
String get resolvedExecutable {
6156
if (this is PhysicalResourceProvider) {
6257
return io.Platform.resolvedExecutable;
@@ -94,48 +89,6 @@ extension ResourceProviderExtensions on ResourceProvider {
9489
}
9590
}
9691

97-
/// Lists the contents of [dir].
98-
///
99-
/// If [recursive] is `true`, lists subdirectory contents (defaults to `false`).
100-
///
101-
/// Excludes files and directories beginning with `.`
102-
///
103-
/// The returned paths are guaranteed to begin with [dir].
104-
Iterable<String> listDir(String dir,
105-
{bool recursive = false,
106-
Iterable<io.FileSystemEntity> Function(io.Directory dir) listDir}) {
107-
listDir ??= (io.Directory dir) => dir.listSync();
108-
109-
return _doList(dir, <String>{}, recursive, listDir);
110-
}
111-
112-
Iterable<String> _doList(
113-
String dir,
114-
Set<String> listedDirectories,
115-
bool recurse,
116-
Iterable<io.FileSystemEntity> Function(io.Directory dir) listDir) sync* {
117-
// Avoid recursive symlinks.
118-
var resolvedPath = io.Directory(dir).resolveSymbolicLinksSync();
119-
if (!listedDirectories.contains(resolvedPath)) {
120-
listedDirectories = Set<String>.from(listedDirectories);
121-
listedDirectories.add(resolvedPath);
122-
123-
for (var entity in listDir(io.Directory(dir))) {
124-
// Skip hidden files and directories
125-
if (path.basename(entity.path).startsWith('.')) {
126-
continue;
127-
}
128-
129-
yield entity.path;
130-
if (entity is io.Directory) {
131-
if (recurse) {
132-
yield* _doList(entity.path, listedDirectories, recurse, listDir);
133-
}
134-
}
135-
}
136-
}
137-
}
138-
13992
/// Converts `.` and `:` into `-`, adding a ".html" extension.
14093
///
14194
/// For example:

lib/src/model/library.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:analyzer/dart/element/visitor.dart';
1010
import 'package:analyzer/source/line_info.dart';
1111
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
1212
import 'package:analyzer/src/generated/sdk.dart';
13+
import 'package:dartdoc/src/io_utils.dart';
1314
import 'package:dartdoc/src/model/model.dart';
1415
import 'package:dartdoc/src/package_meta.dart' show PackageMeta;
1516
import 'package:dartdoc/src/quiver.dart' as quiver;
@@ -212,7 +213,8 @@ class Library extends ModelElement with Categorization, TopLevelContainer {
212213
@override
213214
bool get isPublic {
214215
if (!super.isPublic) return false;
215-
if (sdkLib != null && (sdkLib.isInternal || !sdkLib.isDocumented)) {
216+
if (sdkLib != null &&
217+
(sdkLib.isInternal || !isSdkLibraryDocumented(sdkLib))) {
216218
return false;
217219
}
218220
if (config.isLibraryExcluded(name) ||

lib/src/model/package.dart

+49-42
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import 'package:dartdoc/src/warnings.dart';
1212
import 'package:path/path.dart' as path;
1313
import 'package:pub_semver/pub_semver.dart';
1414

15-
final RegExp substituteNameVersion = RegExp(r'%([bnv])%');
15+
@Deprecated('Public variable intended to be private; will be removed as early '
16+
'as Dartdoc 1.0.0')
17+
RegExp get substituteNameVersion => Package._substituteNameVersion;
1618

1719
// All hrefs are emitted as relative paths from the output root. We are unable
1820
// to compute them from the page we are generating, and many properties computed
@@ -224,51 +226,56 @@ class Package extends LibraryContainer
224226
String _baseHref;
225227

226228
String get baseHref {
227-
if (_baseHref == null) {
228-
if (documentedWhere == DocumentLocation.remote) {
229-
_baseHref =
230-
config.linkToUrl.replaceAllMapped(substituteNameVersion, (m) {
231-
switch (m.group(1)) {
232-
// Return the prerelease tag of the release if a prerelease,
233-
// or 'stable' otherwise. Mostly coded around
234-
// the Dart SDK's use of dev/stable, but theoretically applicable
235-
// elsewhere.
236-
case 'b':
237-
{
238-
var version = Version.parse(packageMeta.version);
239-
var tag = 'stable';
240-
if (version.isPreRelease) {
241-
// version.preRelease is a List<dynamic> with a mix of
242-
// integers and strings. Given this, handle
243-
// 2.8.0-dev.1.0, 2.9.0-1.0.dev, and similar
244-
// variations.
245-
tag = version.preRelease.whereType<String>().first;
246-
// Who knows about non-SDK packages, but assert that SDKs
247-
// must conform to the known format.
248-
assert(
249-
packageMeta.isSdk == false || int.tryParse(tag) == null,
250-
'Got an integer as string instead of the expected "dev" tag');
251-
}
252-
return tag;
253-
}
254-
case 'n':
255-
return name;
256-
// The full version string of the package.
257-
case 'v':
258-
return packageMeta.version;
259-
default:
260-
assert(false, 'Unsupported case: ${m.group(1)}');
261-
return null;
262-
}
263-
});
264-
if (!_baseHref.endsWith('/')) _baseHref = '${_baseHref}/';
265-
} else {
266-
_baseHref = config.useBaseHref ? '' : HTMLBASE_PLACEHOLDER;
267-
}
229+
if (_baseHref != null) {
230+
return _baseHref;
268231
}
232+
233+
if (documentedWhere == DocumentLocation.remote) {
234+
_baseHref = _remoteBaseHref;
235+
if (!_baseHref.endsWith('/')) _baseHref = '${_baseHref}/';
236+
} else {
237+
_baseHref = config.useBaseHref ? '' : HTMLBASE_PLACEHOLDER;
238+
}
239+
269240
return _baseHref;
270241
}
271242

243+
String get _remoteBaseHref {
244+
return config.linkToUrl.replaceAllMapped(_substituteNameVersion, (m) {
245+
switch (m.group(1)) {
246+
// Return the prerelease tag of the release if a prerelease, or 'stable'
247+
// otherwise. Mostly coded around the Dart SDK's use of dev/stable, but
248+
// theoretically applicable elsewhere.
249+
case 'b':
250+
{
251+
var version = Version.parse(packageMeta.version);
252+
var tag = 'stable';
253+
if (version.isPreRelease) {
254+
// `version.preRelease` is a `List<dynamic>` with a mix of
255+
// integers and strings. Given this, handle
256+
// "2.8.0-dev.1.0, 2.9.0-1.0.dev", and similar variations.
257+
tag = version.preRelease.whereType<String>().first;
258+
// Who knows about non-SDK packages, but SDKs must conform to the
259+
// known format.
260+
assert(packageMeta.isSdk == false || int.tryParse(tag) == null,
261+
'Got an integer as string instead of the expected "dev" tag');
262+
}
263+
return tag;
264+
}
265+
case 'n':
266+
return name;
267+
// The full version string of the package.
268+
case 'v':
269+
return packageMeta.version;
270+
default:
271+
assert(false, 'Unsupported case: ${m.group(1)}');
272+
return null;
273+
}
274+
});
275+
}
276+
277+
static final _substituteNameVersion = RegExp(r'%([bnv])%');
278+
272279
@override
273280
String get href => '$baseHref$filePath';
274281

0 commit comments

Comments
 (0)