Migrate service tests to new analyzer

This commit is contained in:
Simon Binder 2022-11-13 17:41:09 +01:00
parent ce0dffb362
commit 873cc9a7a9
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
11 changed files with 156 additions and 521 deletions

View File

@ -41,7 +41,8 @@ class FileAnalyzer {
final availableElements = imported
.expand((reachable) {
final elementAnalysis = reachable.analysis.values;
return elementAnalysis.map((e) => e.result);
return elementAnalysis.map((e) => e.result).where(
(e) => e is DefinedSqlQuery || e is DriftSchemaElement);
})
.whereType<DriftElement>()
.followedBy(element.references)

View File

@ -1,87 +0,0 @@
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:logging/logging.dart';
/// A backend for the moor generator.
///
/// Currently, we only have a backend based on the build package, but we can
/// extend this to a backend for an analyzer plugin or a standalone tool.
abstract class Backend {
/// Resolves an [import] statement from the context of a [base] uri. This
/// should support both relative and `package:` imports.
///
/// Returns null if the url can't be resolved.
Uri? resolve(Uri base, String import);
}
/// Used to analyze a single file via ([entrypoint]). The other methods can be
/// used to read imports used by the other files.
abstract class BackendTask {
Uri get entrypoint;
Logger get log;
/// Resolve the Dart library at [uri].
///
/// If the file at [uri] isn't a library, for instance because it's a part
/// file, throws a [NotALibraryException].
Future<LibraryElement> resolveDart(Uri uri);
Future<String> readMoor(Uri uri);
/// Resolves a Dart expression from a string.
///
/// [context] is a file in which the expression should be resolved, which is
/// relevant for relevant imports. [imports] is a list of (relative) imports
/// which may be used to resolve the expression.
///
/// Throws a [CannotReadExpressionException] when the type could not be
/// resolved.
Future<Expression> resolveExpression(
Uri context, String dartExpression, Iterable<String> imports) {
throw CannotReadExpressionException(
'Resolving dart expressions not supported');
}
Future<AstNode?> loadElementDeclaration(Element element) async {
final library = element.library;
if (library == null) return null;
final info = await library.session.getResolvedLibraryByElement(library);
if (info is ResolvedLibraryResult) {
return info.getElementDeclaration(element)?.node;
} else {
return null;
}
}
/// Checks whether a file at [uri] exists.
Future<bool> exists(Uri uri);
/// Used from the higher-level api to notify the backend that a file would
/// have been read, but hasn't due to caching.
///
/// We use this so that the build package can generate the dependency graph
/// correctly.
Future<void> fakeRead(Uri uri) async {}
}
/// Thrown when attempting to read a Dart library from a file that's not a
/// library.
class NotALibraryException implements Exception {
/// The uri of the file that was attempted to read.
final Uri uri;
NotALibraryException(this.uri);
}
class CannotReadExpressionException implements Exception {
final String msg;
CannotReadExpressionException(this.msg);
@override
String toString() {
return 'Could not read expression: $msg';
}
}

View File

@ -42,13 +42,17 @@ class DriftBuildBackend extends DriftBackend {
final library = await _buildStep.resolver.findLibraryByName(name);
if (library == null) {
throw NonLibraryAssetException(AssetId('sdk', name));
throw NotALibraryException(uri);
} else {
return library;
}
}
return await _buildStep.resolver.libraryFor(AssetId.resolve(uri));
try {
return await _buildStep.resolver.libraryFor(AssetId.resolve(uri));
} on NonLibraryAssetException {
throw NotALibraryException(uri);
}
}
@override

View File

@ -1,127 +0,0 @@
import 'package:analyzer/dart/analysis/analysis_context.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/overlay_file_system.dart';
import 'package:logging/logging.dart';
import 'backend.dart';
class StandaloneBackend extends Backend {
final AnalysisContext context;
/// An overlay resource provider, which must also be used by the [context].
///
/// This will be used to create artificial files used to resolve the type of
/// Dart expressions.
final OverlayResourceProvider provider;
StandaloneBackend(this.context, this.provider);
String? pathOfUri(Uri uri) {
final currentSession = context.currentSession;
final path = currentSession.uriConverter.uriToPath(uri);
return path;
}
@override
Uri resolve(Uri base, String import) {
final resolved = base.resolve(import);
final uriConverter = context.currentSession.uriConverter;
// Try to make uris consistent by going to path and back
final path = uriConverter.uriToPath(resolved);
if (path == null) return resolved;
return uriConverter.pathToUri(path) ?? resolved;
}
BackendTask newTask(Uri entrypoint) =>
_StandaloneBackendTask(this, entrypoint);
}
class _StandaloneBackendTask extends BackendTask {
final StandaloneBackend backend;
@override
final Uri entrypoint;
_StandaloneBackendTask(this.backend, this.entrypoint);
@override
Future<bool> exists(Uri uri) {
final path = backend.pathOfUri(uri);
return Future.value(path != null &&
backend.context.currentSession.resourceProvider.getFile(path).exists);
}
@override
Logger get log => Logger.root;
@override
Future<String> readMoor(Uri uri) {
final path = backend.pathOfUri(uri);
if (path == null) return Future.error('Uri $uri could not be resolved');
final resourceProvider = backend.context.currentSession.resourceProvider;
return Future.value(resourceProvider.getFile(path).readAsStringSync());
}
@override
Future<LibraryElement> resolveDart(Uri uri) async {
final result =
await backend.context.currentSession.getLibraryByUri(uri.toString());
if (result is LibraryElementResult) {
return result.element;
}
throw NotALibraryException(uri);
}
@override
Future<Expression> resolveExpression(
Uri context, String dartExpression, Iterable<String> imports) async {
// Create a fake file next to the content
final provider = backend.provider;
final path = backend.pathOfUri(context)!;
final pathContext = provider.pathContext;
final pathForTemp = pathContext.join(
pathContext.dirname(path), 'moor_temp_${dartExpression.hashCode}.dart');
final content = StringBuffer();
for (final import in imports) {
content.writeln('import "$import";');
}
content.writeln('var e = $dartExpression;');
provider.setOverlay(
pathForTemp,
content: content.toString(),
modificationStamp: DateTime.now().millisecondsSinceEpoch,
);
try {
final result =
await backend.context.currentSession.getResolvedLibrary(pathForTemp);
if (result is! ResolvedLibraryResult) {
throw CannotReadExpressionException(
'Could not resolve temporary helper file');
}
final compilationUnit = result.units.first.unit;
for (final member in compilationUnit.declarations) {
if (member is TopLevelVariableDeclaration) {
return member.variables.variables.first.initializer!;
}
}
throw CannotReadExpressionException(
'Temporary helper file contains no field.');
} finally {
provider.removeOverlay(pathForTemp);
}
}
}

View File

@ -56,7 +56,7 @@ class SchemaWriter {
type = 'trigger';
data = {
'on': _idOf(entity.on!),
'refences_in_body': [
'references_in_body': [
for (final ref in entity.references.whereType<DriftSchemaElement>())
_idOf(ref),
],
@ -152,7 +152,7 @@ class SchemaWriter {
'dsl_features': [...column.constraints.map(_dslFeatureData)],
if (column.typeConverter != null)
'type_converter': {
'dart_expr': column.typeConverter!.expression,
'dart_expr': column.typeConverter!.expression.toString(),
'dart_type_name': column.typeConverter!.dartType
.getDisplayString(withNullability: false),
}
@ -271,7 +271,8 @@ class SchemaReader {
final name = content['name'] as String;
final sql = content['sql'] as String;
return DriftIndex(_id(name), _declaration, table: on, createStmt: sql);
return DriftIndex(_id(name), _declaration, table: on, createStmt: sql)
..parsedStatement = _engine.parse(sql).rootNode as CreateIndexStatement;
}
DriftTrigger _readTrigger(Map<String, dynamic> content) {
@ -290,7 +291,7 @@ class SchemaReader {
],
createStmt: sql,
writes: const [],
);
)..parsedStatement = _engine.parse(sql).rootNode as CreateTriggerStatement;
}
DriftTable _readTable(Map<String, dynamic> content) {

View File

@ -1,82 +0,0 @@
import 'package:build/build.dart';
import 'package:drift_dev/moor_generator.dart';
import 'package:drift_dev/src/analyzer/errors.dart';
import 'package:drift_dev/src/analysis/options.dart';
import 'package:drift_dev/src/analyzer/runner/file_graph.dart';
import 'package:drift_dev/src/analyzer/runner/task.dart';
import 'package:drift_dev/src/analyzer/session.dart';
import 'package:test/test.dart';
import '../utils/test_backend.dart';
class TestState {
TestBackend backend;
MoorSession session;
TestState(this.backend, this.session);
factory TestState.withContent(Map<String, String> content,
{DriftOptions? options}) {
final backend = TestBackend(
{
for (final entry in content.entries)
AssetId.parse(entry.key): entry.value,
},
);
final session = MoorSession(backend);
if (options != null) {
session.options = options;
}
return TestState(backend, session);
}
Future<Task> runTask(String entrypointUri) async {
final backendTask = backend.startTask(Uri.parse(entrypointUri));
final task = session.startTask(backendTask);
await task.runTask();
return task;
}
FoundFile file(String uri) {
return session.registerFile(Uri.parse(uri));
}
Future<FoundFile> analyze(String uri) async {
await runTask(uri);
return file(uri);
}
void close() {
backend.finish();
}
}
// Matchers
Matcher returnsColumns(Map<String, DriftSqlType> columns) {
return _HasInferredColumnTypes(columns);
}
class _HasInferredColumnTypes extends CustomMatcher {
_HasInferredColumnTypes(dynamic expected)
: super('Select query with inferred columns', 'columns', expected);
@override
Object? featureValueOf(dynamic actual) {
if (actual is! SqlSelectQuery) {
return actual;
}
final resultSet = actual.resultSet;
return {for (final column in resultSet.columns) column.name: column.type};
}
}
extension ExpectErrors on FoundFile {
void expectDartError(dynamic matcher, String lexeme) {
expect(
errors.errors,
contains(isA<ErrorInDartCode>()
.having((e) => e.message, 'message', matcher)
.having((e) => e.span?.text, 'span.text', lexeme)));
}
}

View File

@ -1,21 +1,19 @@
@Tags(['analyzer'])
import 'package:build_test/build_test.dart';
import 'package:drift_dev/src/backends/backend.dart';
import 'package:drift_dev/src/backends/build/build_backend.dart';
import 'package:drift_dev/src/analysis/backend.dart';
import 'package:drift_dev/src/backends/build/backend.dart';
import 'package:test/test.dart';
void main() {
final backend = BuildBackend();
test('throws NotALibraryException when resolving a part file', () {
testBuilder(
TestBuilder(
build: (step, _) async {
final task = backend.createTask(step);
final backend = DriftBuildBackend(step);
final partOfUri = Uri.parse('package:foo/helper.dart');
await expectLater(
() => task.resolveDart(partOfUri),
() => backend.readDart(partOfUri),
throwsA(const TypeMatcher<NotALibraryException>()
.having((e) => e.uri, 'uri', partOfUri)),
);

View File

@ -1,15 +1,14 @@
@Tags(['analyzer'])
import 'package:drift/drift.dart';
import 'package:drift_dev/src/analyzer/runner/results.dart';
import 'package:drift_dev/src/services/find_stream_update_rules.dart';
import 'package:test/test.dart';
import '../analyzer/utils.dart';
import '../analysis/test_utils.dart';
void main() {
test('finds update rules for triggers', () async {
final state = TestState.withContent({
'foo|lib/a.moor': '''
final state = TestBackend.inTest({
'a|lib/a.drift': '''
CREATE TABLE users (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR NOT NULL
@ -22,17 +21,18 @@ BEGIN
INSERT INTO users (name) VALUES (UPPER(new.name));
END;
''',
'foo|lib/main.dart': '''
'a|lib/main.dart': '''
import 'package:drift/drift.dart';
@DriftDatabase(include: {'a.moor'})
@DriftDatabase(include: {'a.drift'})
class MyDatabase {}
'''
});
final file = await state.analyze('package:foo/main.dart');
state.close();
final db = (file.currentResult as ParsedDartFile).declaredDatabases.single;
final file = await state.analyze('package:a/main.dart');
state.expectNoErrors();
final db = file.fileAnalysis!.resolvedDatabases.values.single;
final rules = FindStreamUpdateRules(db).identifyRules();
@ -51,8 +51,8 @@ class MyDatabase {}
});
test('finds update rules for foreign key constraint', () async {
final state = TestState.withContent({
'foo|lib/a.moor': '''
final state = TestBackend.inTest({
'a|lib/a.drift': '''
CREATE TABLE a (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
bar TEXT
@ -78,21 +78,18 @@ CREATE TABLE unaffected_on_update (
col INTEGER NOT NULL REFERENCES a(id) ON UPDATE NO ACTION
);
''',
'foo|lib/main.dart': '''
'a|lib/main.dart': '''
import 'package:drift/drift.dart';
@DriftDatabase(include: {'a.moor'})
@DriftDatabase(include: {'a.drift'})
class MyDatabase {}
'''
});
final file = await state.analyze('package:foo/main.dart');
state.close();
final db = (file.currentResult as ParsedDartFile).declaredDatabases.single;
expect(state.file('package:foo/a.moor').errors.errors, isEmpty);
final file = await state.analyze('package:a/main.dart');
state.expectNoErrors();
final db = file.fileAnalysis!.resolvedDatabases.values.single;
final rules = FindStreamUpdateRules(db).identifyRules();
const updateA =

View File

@ -1,20 +1,23 @@
@Tags(['analyzer'])
import 'dart:convert';
import 'dart:io';
import 'package:drift_dev/moor_generator.dart';
import 'package:drift_dev/src/analysis/options.dart';
import 'package:drift_dev/src/analyzer/runner/results.dart';
import 'package:drift_dev/src/analysis/results/database.dart';
import 'package:drift_dev/src/analysis/results/file_results.dart';
import 'package:drift_dev/src/analysis/results/results.dart';
import 'package:drift_dev/src/services/schema/schema_files.dart';
import 'package:drift_dev/src/writer/database_writer.dart';
import 'package:drift_dev/src/writer/import_manager.dart';
import 'package:drift_dev/src/writer/writer.dart';
import 'package:test/test.dart';
import '../../analyzer/utils.dart';
import '../../analysis/test_utils.dart';
void main() {
test('writer integration test', () async {
final state = TestState.withContent({
'foo|lib/a.moor': '''
final state = TestBackend.inTest({
'a|lib/a.drift': '''
import 'main.dart';
CREATE TABLE "groups" (
@ -43,7 +46,7 @@ CREATE INDEX groups_name ON "groups"(name);
CREATE VIEW my_view AS SELECT id FROM "groups";
''',
'foo|lib/main.dart': '''
'a|lib/main.dart': '''
import 'package:drift/drift.dart';
class Users extends Table {
@ -64,22 +67,22 @@ class SettingsConverter extends TypeConverter<Settings, String> {
Settings fromSql(String db) => Settings();
}
@DriftDatabase(include: {'a.moor'}, tables: [Users])
@DriftDatabase(include: {'a.drift'}, tables: [Users])
class Database {}
''',
}, options: const DriftOptions.defaults(modules: [SqlModule.fts5]));
final file = await state.analyze('package:foo/main.dart');
expect(state.session.errorsInFileAndImports(file), isEmpty);
final file = await state.analyze('package:a/main.dart');
state.expectNoErrors();
final result = file.currentResult as ParsedDartFile;
final db = result.declaredDatabases.single;
final db = file.fileAnalysis!.resolvedDatabases.values.single;
final schemaJson = SchemaWriter(db.availableElements).createSchemaJson();
final schemaJson = SchemaWriter(db).createSchemaJson();
expect(schemaJson, json.decode(expected));
final schemaWithOptions = SchemaWriter(
db,
db.availableElements,
options: const DriftOptions.defaults(storeDateTimeValuesAsText: true),
).createSchemaJson();
expect(
@ -88,15 +91,33 @@ class Database {}
test('can generate code from schema json', () {
final serializedSchema = json.decode(expected) as Map<String, dynamic>;
final reader = SchemaReader.readJson(serializedSchema);
final fakeDb = Database()..entities = [...reader.entities];
final writer = Writer(
const DriftOptions.defaults(),
generationOptions: GenerationOptions(
forSchema: 1,
writeCompanions: true,
writeDataClasses: true,
imports: ImportManagerForPartFiles(),
),
);
final database = DriftDatabase(
id: DriftElementId(SchemaReader.elementUri, 'database'),
declaration: DriftDeclaration(SchemaReader.elementUri, 0, 'database'),
declaredIncludes: const [],
declaredQueries: const [],
declaredTables: const [],
declaredViews: const [],
);
final resolved =
ResolvedDatabaseAccessor(const {}, const [], reader.entities.toList());
final input = DatabaseGenerationInput(database, resolved, const {});
// Write the database. Not crashing is good enough for us here, we have
// separate tests for verification
final writer = Writer(const DriftOptions.defaults(),
generationOptions: const GenerationOptions(forSchema: 1));
DatabaseWriter(fakeDb, writer.child()).write();
DatabaseWriter(input, writer.child()).write();
});
}
@ -107,14 +128,12 @@ const expected = r'''
"version": "1.0.0"
},
"options": {
"store_date_time_values_as_text": false
"store_date_time_values_as_text": false
},
"entities": [
{
"id": 0,
"references": [
],
"references": [],
"type": "table",
"data": {
"name": "groups",
@ -129,7 +148,6 @@ const expected = r'''
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
"primary-key",
"auto-increment"
]
},
@ -141,22 +159,69 @@ const expected = r'''
"customConstraints": "NOT NULL",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
]
"dsl_features": []
}
],
"is_virtual": false,
"without_rowid": false,
"constraints": [
"UNIQUE(name)"
],
"unique_keys": [
[
"name"
]
]
}
},
{
"id": 1,
"references": [
],
"references": [],
"type": "table",
"data": {
"name": "email",
"was_declared_in_moor": true,
"columns": [
{
"name": "sender",
"getter_name": "sender",
"moor_type": "ColumnType.text",
"nullable": false,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "title",
"getter_name": "title",
"moor_type": "ColumnType.text",
"nullable": false,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "body",
"getter_name": "body",
"moor_type": "ColumnType.text",
"nullable": false,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
}
],
"is_virtual": true,
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"email\" USING fts5(sender, title, body)",
"without_rowid": false,
"constraints": []
}
},
{
"id": 2,
"references": [],
"type": "table",
"data": {
"name": "users",
@ -172,8 +237,7 @@ const expected = r'''
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
"auto-increment",
"primary-key"
"auto-increment"
]
},
{
@ -184,9 +248,7 @@ const expected = r'''
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
]
"dsl_features": []
},
{
"name": "setting",
@ -196,9 +258,7 @@ const expected = r'''
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
],
"dsl_features": [],
"type_converter": {
"dart_expr": "const SettingsConverter()",
"dart_type_name": "Settings"
@ -206,14 +266,20 @@ const expected = r'''
}
],
"is_virtual": false,
"unique_keys": [["name", "setting"]]
"without_rowid": false,
"unique_keys": [
[
"name",
"setting"
]
]
}
},
{
"id": 2,
"id": 3,
"references": [
0,
1
2
],
"type": "table",
"data": {
@ -229,7 +295,7 @@ const expected = r'''
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
"unknown"
]
},
{
@ -241,7 +307,7 @@ const expected = r'''
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
"unknown"
]
},
{
@ -250,16 +316,15 @@ const expected = r'''
"moor_type": "ColumnType.boolean",
"nullable": false,
"customConstraints": "NOT NULL DEFAULT FALSE",
"default_dart": "const CustomExpression<bool>('FALSE')",
"default_dart": "const CustomExpression('FALSE')",
"default_client_dart": null,
"dsl_features": [
]
"dsl_features": []
}
],
"is_virtual": false,
"without_rowid": false,
"constraints": [
"PRIMARY KEY (\"group\", user) ON CONFLICT REPLACE"
"PRIMARY KEY(\"group\", user)ON CONFLICT REPLACE"
],
"explicit_pk": [
"group",
@ -268,24 +333,24 @@ const expected = r'''
}
},
{
"id": 3,
"id": 4,
"references": [
2,
3,
0
],
"type": "trigger",
"data": {
"on": 2,
"refences_in_body": [
0,
2
"on": 3,
"references_in_body": [
3,
0
],
"name": "delete_empty_groups",
"sql": "CREATE TRIGGER delete_empty_groups AFTER DELETE ON group_members BEGIN DELETE FROM \"groups\" WHERE NOT EXISTS (SELECT * FROM group_members WHERE \"group\" = \"groups\".id);END"
"sql": "CREATE TRIGGER delete_empty_groups AFTER DELETE ON group_members BEGIN\n DELETE FROM \"groups\"\n WHERE NOT EXISTS (SELECT * FROM group_members WHERE \"group\" = \"groups\".id);\nEND;"
}
},
{
"id": 4,
"id": 5,
"references": [
0
],
@ -297,14 +362,14 @@ const expected = r'''
}
},
{
"id": 5,
"id": 6,
"references": [
0
],
"type": "view",
"data": {
"name": "my_view",
"sql": "CREATE VIEW my_view AS SELECT id FROM \"groups\"",
"sql": "CREATE VIEW my_view AS SELECT id FROM \"groups\";",
"dart_data_name": "MyViewData",
"dart_info_name": "MyView",
"columns": [
@ -316,63 +381,10 @@ const expected = r'''
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
]
"dsl_features": []
}
]
}
},
{
"id": 6,
"references": [
],
"type": "table",
"data": {
"name": "email",
"was_declared_in_moor": true,
"columns": [
{
"name": "sender",
"getter_name": "sender",
"moor_type": "ColumnType.text",
"nullable": false,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
]
},
{
"name": "title",
"getter_name": "title",
"moor_type": "ColumnType.text",
"nullable": false,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
]
},
{
"name": "body",
"getter_name": "body",
"moor_type": "ColumnType.text",
"nullable": false,
"customConstraints": "",
"default_dart": null,
"default_client_dart": null,
"dsl_features": [
]
}
],
"is_virtual": true,
"create_virtual_stmt": "CREATE VIRTUAL TABLE email USING fts5(sender, title, body);"
}
}
]
}

View File

@ -1,82 +0,0 @@
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:build_test/build_test.dart';
import 'package:drift_dev/src/backends/backend.dart';
import 'package:logging/logging.dart';
class TestBackend extends Backend {
final Map<AssetId, String> fakeContent;
late Resolver _resolver;
final Completer _initCompleter = Completer();
final Completer _finish = Completer();
/// Future that completes when this backend is ready, which happens when all
/// input files have been parsed and analyzed by the Dart analyzer.
Future get _ready => _initCompleter.future;
TestBackend(this.fakeContent, {bool enableDartAnalyzer = true}) {
if (enableDartAnalyzer) {
_init();
} else {
_initCompleter.complete();
}
}
void _init() {
resolveSources(fakeContent.map((k, v) => MapEntry(k.toString(), v)), (r) {
_resolver = r;
_initCompleter.complete();
return _finish.future;
});
}
BackendTask startTask(Uri uri) {
return _TestBackendTask(this, uri);
}
void finish() {
_finish.complete();
}
@override
Uri resolve(Uri base, String import) {
final from = AssetId.resolve(base);
return AssetId.resolve(Uri.parse(import), from: from).uri;
}
}
class _TestBackendTask extends BackendTask {
final TestBackend backend;
@override
final Uri entrypoint;
@override
Logger get log => Logger.root;
_TestBackendTask(this.backend, this.entrypoint);
@override
Future<String> readMoor(Uri path) async {
await backend._ready;
return backend.fakeContent[AssetId.resolve(path)]!;
}
@override
Future<LibraryElement> resolveDart(Uri path) async {
await backend._ready;
try {
return await backend._resolver.libraryFor(AssetId.resolve(path));
} on NonLibraryAssetException catch (_) {
throw NotALibraryException(path);
}
}
@override
Future<bool> exists(Uri uri) async {
return backend.fakeContent.containsKey(AssetId.resolve(uri));
}
}