mirror of https://github.com/AMT-Cheif/drift.git
Migrate service tests to new analyzer
This commit is contained in:
parent
ce0dffb362
commit
873cc9a7a9
|
@ -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)
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
|
@ -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)),
|
||||
);
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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);"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue