blob: 238571e89357e93d7294a317e6d99c108b06c805 [file] [log] [blame]
// Copyright (c) 2022, 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:analyzer/file_system/memory_file_system.dart';
import 'package:dartdoc/src/dartdoc.dart';
import 'package:dartdoc/src/logging.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:meta/meta.dart';
import 'package:path/path.dart' as path;
import 'src/test_descriptor_utils.dart' as d;
import 'src/utils.dart';
/// Exception thrown for invalid use of [DartdocTestBase]'s api.
class DartdocTestBaseFailure implements Exception {
final String message;
DartdocTestBaseFailure(this.message);
@override
String toString() => message;
}
abstract class DartdocTestBase {
late final PackageMetaProvider packageMetaProvider;
late final MemoryResourceProvider resourceProvider;
late final FakePackageConfigProvider packageConfigProvider;
late String packagePath;
String get libraryName;
String get placeholder => '%%__HTMLBASE_dartdoc_internal__%%';
String get linkPrefix => '$placeholder$libraryName';
String get dartAsyncUrlPrefix => '$dartSdkUrlPrefix/dart-async';
String get dartCoreUrlPrefix => '$dartSdkUrlPrefix/dart-core';
String get dartSdkUrlPrefix => 'https://api.dart.dev/stable/3.2.0';
String get sdkConstraint => '>=3.7.0 <4.0.0';
/// A mapping of pub dependency names to the paths.
///
/// These are written out to a pubspec file using `path` keys.
Map<String, String> get pubDependencyPaths => const {};
List<String> get experiments => ['enhanced-parts', 'wildcard-variables'];
bool get skipUnreachableSdkLibraries => true;
late StringBuffer outBuffer;
late StringBuffer errBuffer;
@mustCallSuper
Future<void> setUp() async {
outBuffer = StringBuffer();
errBuffer = StringBuffer();
packageMetaProvider = testPackageMetaProvider;
resourceProvider =
packageMetaProvider.resourceProvider as MemoryResourceProvider;
await _setUpPackage();
}
Future<void> _setUpPackage() async {
var pubspec = d.buildPubspecText(
sdkConstraint: sdkConstraint,
dependencies: pubDependencyPaths,
);
String? analysisOptions;
if (experiments.isNotEmpty) {
analysisOptions = '''
analyzer:
enable-experiment:${experiments.map((experiment) => '\n - $experiment').join('')}
''';
}
packagePath = await d.createPackage(
libraryName,
pubspec: pubspec,
analysisOptions: analysisOptions,
resourceProvider: resourceProvider,
);
packageConfigProvider =
getTestPackageConfigProvider(packageMetaProvider.defaultSdkDir.path);
packageConfigProvider.addPackageToConfigFor(
packagePath, libraryName, Uri.file('$packagePath/'));
}
Future<PackageGraph> bootPackageFromFiles(Iterable<d.Descriptor> files,
{List<String> additionalArguments = const []}) async {
var packagePathBasename =
resourceProvider.pathContext.basename(packagePath);
var packagePathDirname = resourceProvider.pathContext.dirname(packagePath);
await d
.dir(packagePathBasename, files)
.createInMemory(resourceProvider, packagePathDirname);
return await bootBasicPackage(
packagePath,
packageMetaProvider,
packageConfigProvider,
additionalArguments: additionalArguments,
skipUnreachableSdkLibraries: skipUnreachableSdkLibraries,
);
}
/// Creates a single library named [libraryName], with optional preamble
/// [libraryPreamble]. Optionally, pass [extraFiles] such as
/// `dartdoc_options.yaml`.
Future<Library> bootPackageWithLibrary(
String libraryContent, {
String libraryFilePath = 'lib/library.dart',
String libraryPreamble = '',
Iterable<d.Descriptor> extraFiles = const [],
List<String> additionalArguments = const [],
}) async {
return (await bootPackageFromFiles([
d.file(libraryFilePath, '''
$libraryPreamble
library $libraryName;
$libraryContent
'''),
...extraFiles,
], additionalArguments: additionalArguments))
.libraries
.named(libraryName);
}
Future<Dartdoc> buildDartdoc({
List<String> excludeLibraries = const [],
List<String> additionalArguments = const [],
bool skipUnreachableSdkLibraries = true,
bool useJson = false,
}) async {
final dir = resourceProvider.getFolder(resourceProvider.pathContext
.absolute(resourceProvider.pathContext.normalize(packagePath)));
final context = await generatorContextFromArgv([
'--input',
dir.path,
'--output',
path.join(packagePath, 'doc'),
'--sdk-dir',
packageMetaProvider.defaultSdkDir.path,
'--exclude',
excludeLibraries.join(','),
'--allow-tools',
'--no-link-to-remote',
...additionalArguments,
], packageMetaProvider);
final packageBuilder = PubPackageBuilder(
context,
packageMetaProvider,
packageConfigProvider,
skipUnreachableSdkLibraries: skipUnreachableSdkLibraries,
);
startLogging(
isJson: useJson,
isQuiet: true,
showProgress: true,
outSink: outBuffer,
errSink: errBuffer,
);
return await Dartdoc.fromContext(context, packageBuilder);
}
/// The real offset in a library generated with [bootPackageWithLibrary].
///
/// When a library is written via [bootPackageWithLibrary], the test author
/// provides `libraryContent`, which is a snippet of Dart library text.
int realOffsetFor(int offsetInContent) =>
'\n\nlibrary $libraryName\n\n'.length + offsetInContent;
}