Skip to content

Commit a6900d9

Browse files
authored
Update all (approximately) i/o operations to use ResourceProvider (#2315)
All i/o operations now pass through a ResourceProvider, an abstraction which allows disk-free testing. In this change, almost all ResourceProviders used are the PhysicalResourceProvider; no tests are converted from being filesystem-dependent to using MemoryResourceProvider. dartdoc uses the filesystem extensively, so this change is large. Often top-level or static elements become more complicated, depending on a ResourceProvider variable. Some extra dart:io functionality not found in ResourceProvider is added via an extension, ResourceProviderExtensions. Many classes have a new ResourceProvider resourceProvider field: DartdocFileWriter, SnapshotCache, DartToolDefinition, ToolConfiguration, DartdocOption, PackageMetaProvider, PackageMeta, ToolTempFileTracker, Each of SnapshotCache and ToolTempFileTracker has a static instance field which took no arguments; now that they need a ResourceProvider, it was unwieldy to pass a ResourceProvider to get the instance each time, so a new method, createInstance creates the instance.
1 parent 5f2e569 commit a6900d9

32 files changed

+1064
-673
lines changed

bin/dartdoc.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Future<void> main(List<String> arguments) async {
1717
// There was an error while parsing options.
1818
return;
1919
}
20-
final packageBuilder = PubPackageBuilder(config);
20+
final packageBuilder = PubPackageBuilder(config, pubPackageMetaProvider);
2121
final dartdoc = config.generateDocs
2222
? await Dartdoc.fromContext(config, packageBuilder)
2323
: await Dartdoc.withEmptyGenerator(config, packageBuilder);

lib/dartdoc.dart

Lines changed: 60 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ library dartdoc;
1010

1111
import 'dart:async';
1212
import 'dart:convert';
13-
import 'dart:io';
13+
import 'dart:io' show exitCode, stderr;
1414

15+
import 'package:analyzer/file_system/file_system.dart';
1516
import 'package:dartdoc/src/dartdoc_options.dart';
1617
import 'package:dartdoc/src/generator/empty_generator.dart';
1718
import 'package:dartdoc/src/generator/generator.dart';
@@ -41,17 +42,19 @@ const String dartdocVersion = packageVersion;
4142
/// Helper class that consolidates option contexts for instantiating generators.
4243
class DartdocGeneratorOptionContext extends DartdocOptionContext
4344
with GeneratorContext {
44-
DartdocGeneratorOptionContext(DartdocOptionSet optionSet, Directory dir)
45-
: super(optionSet, dir);
45+
DartdocGeneratorOptionContext(
46+
DartdocOptionSet optionSet, Folder dir, ResourceProvider resourceProvider)
47+
: super(optionSet, dir, resourceProvider);
4648
}
4749

4850
class DartdocFileWriter implements FileWriter {
4951
final String outputDir;
52+
ResourceProvider resourceProvider;
5053
final Map<String, Warnable> _fileElementMap = {};
5154
@override
5255
final Set<String> writtenFiles = {};
5356

54-
DartdocFileWriter(this.outputDir);
57+
DartdocFileWriter(this.outputDir, this.resourceProvider);
5558

5659
@override
5760
void write(String filePath, Object content,
@@ -73,10 +76,11 @@ class DartdocFileWriter implements FileWriter {
7376
}
7477
_fileElementMap[outFile] = element;
7578

76-
var file = File(path.join(outputDir, outFile));
79+
var file = resourceProvider
80+
.getFile(resourceProvider.pathContext.join(outputDir, outFile));
7781
var parent = file.parent;
78-
if (!parent.existsSync()) {
79-
parent.createSync(recursive: true);
82+
if (!parent.exists) {
83+
parent.create();
8084
}
8185

8286
if (content is String) {
@@ -100,14 +104,16 @@ class Dartdoc {
100104
final PackageBuilder packageBuilder;
101105
final DartdocOptionContext config;
102106
final Set<String> writtenFiles = {};
103-
Directory outputDir;
107+
Folder outputDir;
104108

105109
// Fires when the self checks make progress.
106110
final StreamController<String> _onCheckProgress =
107111
StreamController(sync: true);
108112

109113
Dartdoc._(this.config, this.generator, this.packageBuilder) {
110-
outputDir = Directory(config.output)..createSync(recursive: true);
114+
outputDir = config.resourceProvider
115+
.getFolder(config.resourceProvider.pathContext.absolute(config.output))
116+
..create();
111117
}
112118

113119
/// An asynchronous factory method that builds Dartdoc's file writers
@@ -182,9 +188,9 @@ class Dartdoc {
182188
final generator = this.generator;
183189
if (generator != null) {
184190
// Create the out directory.
185-
if (!outputDir.existsSync()) outputDir.createSync(recursive: true);
191+
if (!outputDir.exists) outputDir.create();
186192

187-
var writer = DartdocFileWriter(outputDir.path);
193+
var writer = DartdocFileWriter(outputDir.path, config.resourceProvider);
188194
await generator.generate(packageGraph, writer);
189195

190196
writtenFiles.addAll(writer.writtenFiles);
@@ -225,15 +231,16 @@ class Dartdoc {
225231
throw DartdocFailure(
226232
'dartdoc encountered $errorCount errors while processing.');
227233
}
228-
logInfo(
229-
'Success! Docs generated into ${dartdocResults.outDir.absolute.path}');
234+
var outDirPath = config.resourceProvider.pathContext
235+
.absolute(dartdocResults.outDir.path);
236+
logInfo('Success! Docs generated into $outDirPath');
230237
return dartdocResults;
231238
} finally {
232239
// Clear out any cached tool snapshots and temporary directories.
233240
// ignore: unawaited_futures
234-
SnapshotCache.instance.dispose();
241+
SnapshotCache.instance?.dispose();
235242
// ignore: unawaited_futures
236-
ToolTempFileTracker.instance.dispose();
243+
ToolTempFileTracker.instance?.dispose();
237244
}
238245
}
239246

@@ -289,35 +296,43 @@ class Dartdoc {
289296
var staticAssets = path.joinAll([normalOrigin, 'static-assets', '']);
290297
var indexJson = path.joinAll([normalOrigin, 'index.json']);
291298
var foundIndexJson = false;
292-
for (var f in Directory(normalOrigin).listSync(recursive: true)) {
293-
if (f is Directory) {
294-
continue;
295-
}
296-
var fullPath = path.normalize(f.path);
297-
if (fullPath.startsWith(staticAssets)) {
298-
continue;
299-
}
300-
if (fullPath == indexJson) {
301-
foundIndexJson = true;
302-
_onCheckProgress.add(fullPath);
303-
continue;
304-
}
305-
if (visited.contains(fullPath)) continue;
306-
var relativeFullPath = path.relative(fullPath, from: normalOrigin);
307-
if (!writtenFiles.contains(relativeFullPath)) {
308-
// This isn't a file we wrote (this time); don't claim we did.
309-
_warn(packageGraph, PackageWarning.unknownFile, fullPath, normalOrigin);
310-
} else {
311-
// Error messages are orphaned by design and do not appear in the search
312-
// index.
313-
if (<String>['__404error.html', 'categories.json'].contains(fullPath)) {
314-
_warn(packageGraph, PackageWarning.orphanedFile, fullPath,
315-
normalOrigin);
299+
300+
void checkDirectory(Folder dir) {
301+
for (var f in dir.getChildren()) {
302+
if (f is Folder) {
303+
checkDirectory(f);
304+
continue;
305+
}
306+
var fullPath = path.normalize(f.path);
307+
if (fullPath.startsWith(staticAssets)) {
308+
continue;
316309
}
310+
if (path.equals(fullPath, indexJson)) {
311+
foundIndexJson = true;
312+
_onCheckProgress.add(fullPath);
313+
continue;
314+
}
315+
if (visited.contains(fullPath)) continue;
316+
var relativeFullPath = path.relative(fullPath, from: normalOrigin);
317+
if (!writtenFiles.contains(relativeFullPath)) {
318+
// This isn't a file we wrote (this time); don't claim we did.
319+
_warn(
320+
packageGraph, PackageWarning.unknownFile, fullPath, normalOrigin);
321+
} else {
322+
// Error messages are orphaned by design and do not appear in the search
323+
// index.
324+
if (<String>['__404error.html', 'categories.json']
325+
.contains(fullPath)) {
326+
_warn(packageGraph, PackageWarning.orphanedFile, fullPath,
327+
normalOrigin);
328+
}
329+
}
330+
_onCheckProgress.add(fullPath);
317331
}
318-
_onCheckProgress.add(fullPath);
319332
}
320333

334+
checkDirectory(config.resourceProvider.getFolder(normalOrigin));
335+
321336
if (!foundIndexJson) {
322337
_warn(packageGraph, PackageWarning.brokenLink, indexJson, normalOrigin);
323338
_onCheckProgress.add(indexJson);
@@ -327,8 +342,8 @@ class Dartdoc {
327342
// This is extracted to save memory during the check; be careful not to hang
328343
// on to anything referencing the full file and doc tree.
329344
Tuple2<Iterable<String>, String> _getStringLinksAndHref(String fullPath) {
330-
var file = File('$fullPath');
331-
if (!file.existsSync()) {
345+
var file = config.resourceProvider.getFile('$fullPath');
346+
if (!file.exists) {
332347
return null;
333348
}
334349
// TODO(srawlins): It is possible that instantiating an HtmlParser using
@@ -353,8 +368,8 @@ class Dartdoc {
353368
PackageGraph packageGraph, String origin, Set<String> visited) {
354369
var fullPath = path.joinAll([origin, 'index.json']);
355370
var indexPath = path.joinAll([origin, 'index.html']);
356-
var file = File('$fullPath');
357-
if (!file.existsSync()) {
371+
var file = config.resourceProvider.getFile('$fullPath');
372+
if (!file.exists) {
358373
return null;
359374
}
360375
var decoder = JsonDecoder();
@@ -519,7 +534,7 @@ class DartdocFailure {
519534
class DartdocResults {
520535
final PackageMeta packageMeta;
521536
final PackageGraph packageGraph;
522-
final Directory outDir;
537+
final Folder outDir;
523538

524539
DartdocResults(this.packageMeta, this.packageGraph, this.outDir);
525540
}

lib/options.dart

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
11
import 'dart:async';
2-
import 'dart:io';
2+
import 'dart:io' show stderr, exitCode;
33

4+
import 'package:analyzer/file_system/file_system.dart';
45
import 'package:args/args.dart';
56
import 'package:dartdoc/dartdoc.dart';
67
import 'package:dartdoc/src/logging.dart';
78

89
class DartdocProgramOptionContext extends DartdocGeneratorOptionContext
910
with LoggingContext {
10-
DartdocProgramOptionContext(DartdocOptionSet optionSet, Directory dir)
11-
: super(optionSet, dir);
11+
DartdocProgramOptionContext(
12+
DartdocOptionSet optionSet, Folder dir, ResourceProvider resourceProvider)
13+
: super(optionSet, dir, resourceProvider);
1214

1315
bool get generateDocs => optionSet['generateDocs'].valueAt(context);
1416
bool get help => optionSet['help'].valueAt(context);
1517
bool get version => optionSet['version'].valueAt(context);
1618
}
1719

18-
Future<List<DartdocOption<bool>>> createDartdocProgramOptions() async {
20+
Future<List<DartdocOption<bool>>> createDartdocProgramOptions(
21+
PackageMetaProvider packageMetaProvider) async {
22+
var resourceProvider = packageMetaProvider.resourceProvider;
1923
return [
20-
DartdocOptionArgOnly<bool>('generateDocs', true,
24+
DartdocOptionArgOnly<bool>('generateDocs', true, resourceProvider,
2125
help:
22-
'Generate docs into the output directory (or only display warnings if false).',
26+
'Generate docs into the output directory (or only display warnings '
27+
'if false).',
2328
negatable: true),
24-
DartdocOptionArgOnly<bool>('help', false,
29+
DartdocOptionArgOnly<bool>('help', false, resourceProvider,
2530
abbr: 'h', help: 'Show command help.', negatable: false),
26-
DartdocOptionArgOnly<bool>('version', false,
31+
DartdocOptionArgOnly<bool>('version', false, resourceProvider,
2732
help: 'Display the version for $programName.', negatable: false),
2833
];
2934
}
@@ -33,13 +38,16 @@ Future<DartdocProgramOptionContext> parseOptions(
3338
List<String> arguments, {
3439
OptionGenerator additionalOptions,
3540
}) async {
36-
var optionSet = await DartdocOptionSet.fromOptionGenerators('dartdoc', [
37-
() => createDartdocOptions(packageMetaProvider),
38-
createDartdocProgramOptions,
39-
createLoggingOptions,
40-
createGeneratorOptions,
41-
if (additionalOptions != null) additionalOptions,
42-
]);
41+
var optionSet = await DartdocOptionSet.fromOptionGenerators(
42+
'dartdoc',
43+
[
44+
createDartdocOptions,
45+
createDartdocProgramOptions,
46+
createLoggingOptions,
47+
createGeneratorOptions,
48+
if (additionalOptions != null) additionalOptions,
49+
],
50+
packageMetaProvider);
4351

4452
try {
4553
optionSet.parseArguments(arguments);
@@ -51,20 +59,21 @@ Future<DartdocProgramOptionContext> parseOptions(
5159
exitCode = 64;
5260
return null;
5361
}
54-
if (optionSet['help'].valueAt(Directory.current)) {
62+
if (optionSet['help'].valueAtCurrent()) {
5563
_printHelp(optionSet.argParser);
5664
exitCode = 0;
5765
return null;
5866
}
59-
if (optionSet['version'].valueAt(Directory.current)) {
67+
if (optionSet['version'].valueAtCurrent()) {
6068
_printVersion(optionSet.argParser);
6169
exitCode = 0;
6270
return null;
6371
}
6472

6573
DartdocProgramOptionContext config;
6674
try {
67-
config = DartdocProgramOptionContext(optionSet, null);
75+
config = DartdocProgramOptionContext(
76+
optionSet, null, packageMetaProvider.resourceProvider);
6877
} on DartdocOptionError catch (e) {
6978
stderr.writeln(' fatal error: ${e.message}');
7079
stderr.writeln('');

0 commit comments

Comments
 (0)