From dcdbcb71568d7a6917b3b3849569ce83d3c2e44f Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sun, 18 Sep 2022 21:52:36 +0200 Subject: [PATCH] Simple test for new query analyzer --- drift_dev/lib/src/analysis/driver/state.dart | 13 ++++ .../src/analysis/resolver/file_analysis.dart | 11 ++-- .../queries/queries_from_views_test.dart | 59 +++++++++++++++++++ drift_dev/test/analysis/test_utils.dart | 29 +++++++-- .../sql_queries/queries_from_views.dart | 2 +- 5 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 drift_dev/test/analysis/resolver/queries/queries_from_views_test.dart diff --git a/drift_dev/lib/src/analysis/driver/state.dart b/drift_dev/lib/src/analysis/driver/state.dart index c3c6be7e..25a7f36f 100644 --- a/drift_dev/lib/src/analysis/driver/state.dart +++ b/drift_dev/lib/src/analysis/driver/state.dart @@ -19,6 +19,19 @@ class FileState { String get extension => url.extension(ownUri.path); + Iterable get allErrors sync* { + yield* errorsDuringDiscovery; + + for (final entry in analysis.values) { + yield* entry.errorsDuringAnalysis; + } + + final fileResults = fileAnalysis; + if (fileResults != null) { + yield* fileResults.analysisErrors; + } + } + bool get isFullyAnalyzed { return discovery != null && discovery!.locallyDefinedElements diff --git a/drift_dev/lib/src/analysis/resolver/file_analysis.dart b/drift_dev/lib/src/analysis/resolver/file_analysis.dart index 71e60d7e..7a393671 100644 --- a/drift_dev/lib/src/analysis/resolver/file_analysis.dart +++ b/drift_dev/lib/src/analysis/resolver/file_analysis.dart @@ -44,7 +44,7 @@ class FileAnalyzer { final genericEngineForParsing = driver.newSqlEngine(); final source = await driver.backend.readAsString(state.ownUri); final parsedFile = - genericEngineForParsing.parse(source).rootNode as DriftFile; + genericEngineForParsing.parseDriftFile(source).rootNode as DriftFile; for (final elementAnalysis in state.analysis.values) { final element = elementAnalysis.result; @@ -52,12 +52,13 @@ class FileAnalyzer { final engine = driver.typeMapping.newEngineWithTables(element.references); final stmt = parsedFile.statements - .firstWhere((e) => e.firstPosition == element.sqlOffset) - as DeclaredStatement; + .whereType() + .firstWhere( + (e) => e.statement.firstPosition == element.sqlOffset); final options = _createOptionsAndVars(engine, stmt); - final analysisResult = - engine.analyzeNode(stmt, source, stmtOptions: options.options); + final analysisResult = engine.analyzeNode(stmt.statement, source, + stmtOptions: options.options); final analyzer = QueryAnalyzer(analysisResult, driver, references: element.references, diff --git a/drift_dev/test/analysis/resolver/queries/queries_from_views_test.dart b/drift_dev/test/analysis/resolver/queries/queries_from_views_test.dart new file mode 100644 index 00000000..cd03377e --- /dev/null +++ b/drift_dev/test/analysis/resolver/queries/queries_from_views_test.dart @@ -0,0 +1,59 @@ +import 'package:drift/drift.dart'; +import 'package:test/test.dart'; + +import '../../test_utils.dart'; + +void main() { + test('select from view', () async { + final backend = TestBackend.inTest({ + 'foo|lib/a.drift': ''' +CREATE TABLE artists ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name VARCHAR NOT NULL +); + +CREATE TABLE albums ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + artist INTEGER NOT NULL REFERENCES artists (id) +); + +CREATE TABLE tracks ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + album INTEGER NOT NULL REFERENCES albums (id), + duration_seconds INTEGER NOT NULL, + was_single BOOLEAN NOT NULL DEFAULT FALSE +); + +CREATE VIEW total_duration_by_artist_view AS + SELECT a.*, SUM(tracks.duration_seconds) AS duration + FROM artists a + INNER JOIN albums ON albums.artist = a.id + INNER JOIN tracks ON tracks.album = albums.id + GROUP BY a.id; + +totalDurationByArtist: +SELECT * FROM total_duration_by_artist_view; +''', + }); + + final file = + await backend.driver.fullyAnalyze(Uri.parse('package:foo/a.drift')); + + expect(file.allErrors, isEmpty); + + final results = file.fileAnalysis!; + final query = results.resolvedQueries.values + .singleWhere((q) => q.name == 'totalDurationByArtist'); + + expect( + query, + returnsColumns({ + 'id': DriftSqlType.int, + 'name': DriftSqlType.string, + 'duration': DriftSqlType.int, + }), + ); + }); +} diff --git a/drift_dev/test/analysis/test_utils.dart b/drift_dev/test/analysis/test_utils.dart index 1742085d..26761933 100644 --- a/drift_dev/test/analysis/test_utils.dart +++ b/drift_dev/test/analysis/test_utils.dart @@ -9,10 +9,12 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/file_system/overlay_file_system.dart'; import 'package:analyzer/file_system/physical_file_system.dart'; import 'package:build/build.dart'; +import 'package:drift/drift.dart'; import 'package:drift_dev/src/analysis/backend.dart'; import 'package:drift_dev/src/analysis/driver/driver.dart'; import 'package:drift_dev/src/analysis/driver/error.dart'; import 'package:drift_dev/src/analysis/driver/state.dart'; +import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/analyzer/options.dart'; import 'package:logging/logging.dart'; import 'package:package_config/package_config.dart'; @@ -133,10 +135,29 @@ class TestBackend extends DriftBackend { Future dispose() async {} } -Matcher get hasNoErrors => isA() - .having((e) => e.errorsDuringDiscovery, 'errorsDuringDiscovery', isEmpty) - .having((e) => e.analysis.values.expand((e) => e.errorsDuringAnalysis), - '(errors in analyzed elements)', isEmpty); +Matcher get hasNoErrors => + isA().having((e) => e.allErrors, 'allErrors', isEmpty); + +Matcher returnsColumns(Map 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.sqlType + }; + } +} TypeMatcher isDriftError(dynamic message) { return isA().having((e) => e.message, 'message', message); diff --git a/drift_dev/test/analyzer/sql_queries/queries_from_views.dart b/drift_dev/test/analyzer/sql_queries/queries_from_views.dart index f0bde193..cfea0afd 100644 --- a/drift_dev/test/analyzer/sql_queries/queries_from_views.dart +++ b/drift_dev/test/analyzer/sql_queries/queries_from_views.dart @@ -33,7 +33,7 @@ CREATE VIEW total_duration_by_artist_view AS FROM artists a INNER JOIN albums ON albums.artist = a.id INNER JOIN tracks ON tracks.album = albums.id - GROUP BY artists.id; + GROUP BY a.id; totalDurationByArtist: SELECT * FROM total_duration_by_artist_view;