@@ -145,6 +145,7 @@ targets:
145145 // Write the generated test
146146 await writer.writeTest ();
147147 await writer.flush ();
148+ writer.suggestDataMigrationTest ();
148149 }
149150 }
150151}
@@ -200,11 +201,10 @@ class _MigrationTestEmitter {
200201 }
201202
202203 /// All the schema files for this database
203- Map <int , ExportedSchema > schemas;
204+ Map <int , ExportedSchema > schemas = const {} ;
204205
205206 /// Migration writer for each migration
206- List <_MigrationTestWriter > get migrations =>
207- _MigrationTestWriter .fromSchema (schemas);
207+ List <_MigrationTestWriter > migrations = const [];
208208
209209 _MigrationTestEmitter (
210210 {required this .cli,
@@ -219,7 +219,6 @@ class _MigrationTestEmitter {
219219 required this .dbClassName,
220220 required this .db,
221221 required this .driftElements,
222- required this .schemas,
223222 required this .dumpGeneratedSchemaCode,
224223 required this .needsToUseFlutterTest});
225224
@@ -260,7 +259,6 @@ class _MigrationTestEmitter {
260259 if (db == null ) {
261260 cli.exit ('Could not read database class from the "$dbName " database.' );
262261 }
263- final schemas = await parseSchema (schemaDir);
264262
265263 final config = await cli.project.packageConfig;
266264 final hasTest = config? .packages.any ((e) => e.name == 'test' ) == true ;
@@ -271,7 +269,7 @@ class _MigrationTestEmitter {
271269 'dependency on flutter_test or test.' );
272270 }
273271
274- return _MigrationTestEmitter (
272+ final emitter = _MigrationTestEmitter (
275273 cli: cli,
276274 rootSchemaDir: rootSchemaDir,
277275 rootTestDir: rootTestDir,
@@ -280,14 +278,21 @@ class _MigrationTestEmitter {
280278 schemaDir: schemaDir,
281279 testDir: testDir,
282280 db: db,
283- schemas: schemas,
284281 driftElements: elements,
285282 dbClassName: db.definingDartClass.toString (),
286283 testDatabasesDir: testDatabasesDir,
287284 schemaVersion: schemaVersion,
288285 dumpGeneratedSchemaCode: dumpGeneratedSchemaCode,
289286 needsToUseFlutterTest: ! hasTest,
290287 );
288+ await emitter._readSchemas ();
289+ return emitter;
290+ }
291+
292+ Future <void > _readSchemas () async {
293+ schemas = await parseSchema (schemaDir);
294+ migrations = _MigrationTestWriter .fromSchema (schemas)
295+ .sorted ((a, b) => a.from.compareTo (b.from));
291296 }
292297
293298 /// Create a .json dump of the current schema
@@ -310,7 +315,7 @@ class _MigrationTestEmitter {
310315 .info ('$dbName : Creating schema file for version $schemaVersion ' );
311316 schemaFile.writeAsStringSync (content);
312317 // Re-parse the schema to include the newly created schema file
313- schemas = await parseSchema (schemaDir );
318+ await _readSchemas ( );
314319 } else if (schemaFile.readAsStringSync () != content) {
315320 cli.exit (
316321 "A schema for version $schemaVersion of the $dbName database already exists and differs from the current schema."
@@ -372,8 +377,7 @@ ${blue.wrap("class")} ${green.wrap(dbClassName)} ${blue.wrap("extends")} ${green
372377 return ;
373378 }
374379
375- final firstMigration =
376- migrations.sorted ((a, b) => a.from.compareTo (b.from)).first;
380+ final firstMigration = migrations.first;
377381
378382 final packageName = cli.project.buildConfig.packageName;
379383 final relativeDbPath = p.posix.relative (dbClassFile.path,
@@ -464,6 +468,52 @@ void main() {
464468 return File (dbClassFile.absolute.path
465469 .replaceFirst (RegExp (r'\.dart$' ), '.steps.dart' ));
466470 }
471+
472+ void suggestDataMigrationTest () {
473+ final lastMigration = migrations.last;
474+ final schemaBefore = schemas[lastMigration.from]! ;
475+ final schemaAfter = schemas[lastMigration.to]! ;
476+
477+ // Find changes that suggest a test with data (instead of the default tests
478+ // only using empty tables) might be useful.
479+ final reasons = < String > [];
480+
481+ for (final elementAfter in schemaAfter.schema) {
482+ final elementBefore = schemaBefore.schema
483+ .firstWhereOrNull ((e) => e.id.name == elementAfter.id.name);
484+
485+ if (elementBefore == null ) {
486+ continue ;
487+ }
488+
489+ if (elementAfter is DriftTable && elementBefore is DriftTable ) {
490+ for (final columnAfter in elementAfter.columns) {
491+ final columnBefore =
492+ elementBefore.columnBySqlName[columnAfter.nameInSql];
493+
494+ if (columnBefore == null &&
495+ elementAfter.isColumnRequiredForInsert (columnAfter)) {
496+ reasons.add (
497+ 'Added column "${columnAfter .nameInSql }" in "${elementAfter .schemaName }" without a default.' );
498+ }
499+ }
500+ }
501+ }
502+
503+ if (reasons.isNotEmpty) {
504+ cli.logger.info (
505+ '${cyan .wrap ('Hint' )}: Your latest migration might benefit from a test '
506+ 'with data, as it might need special setup to transform existing data. '
507+ 'Drit has detected the following reasons for that: ' ,
508+ );
509+ for (final reason in reasons) {
510+ cli.logger.info (' - $reason ' );
511+ }
512+
513+ cli.logger.info (
514+ 'For more information on writing these tests, see ${yellow .wrap ('https://drift.simonbinder.eu/migrations/tests/#verifying-data-integrity' )}' );
515+ }
516+ }
467517}
468518
469519/// A writer that generates example code for a migration test with data
0 commit comments