From ffad86f71d7184795091f1574d1b073e6e60caed Mon Sep 17 00:00:00 2001 From: Reid Baker Date: Fri, 22 May 2026 01:09:51 -0400 Subject: [PATCH 1/3] Fix flaky Windows dart analyze; enforce dart_code_linter on test/ Drop the dart_code_linter analyzer `plugin` registration from analysis_options.yaml. When registered, `dart analyze` loaded the plugin in a separate isolate that re-emitted the `dart_code_linter:` rules as diagnostics over the plugin protocol for all non-excluded files. Delivery of those diagnostics to the CLI is asynchronous and racy, so on slower Windows CI runners they intermittently appeared and `--fatal-infos` turned them fatal -- a flaky, platform-specific failure (#144). `dart analyze` now enforces only core lints, deterministically across platforms. The dedicated `dart run dart_code_linter:metrics` step is the deterministic enforcement path for those rules; extend it to cover `test` as well as `lib` so the test suite gets real, cross-platform coverage instead of the accidental flaky version. Fix the test-file violations this surfaces: - avoid-late-keyword: replace `late Directory tempDir` fixtures with a shared createTempDir() helper that registers cleanup via addTearDown, removing duplicated tearDown blocks. - avoid-dynamic: `dynamic noSuchMethod` -> `Object?`. - avoid-redundant-async: drop the redundant async in a setUp. Exclude prefer-match-file-name for test/**: test files must be named `*_test.dart` and commonly hold several small fixture classes, so they cannot match the rule's "file name == first class name" convention. --- .../workflows/dart_skills_lint_workflow.yaml | 2 +- tool/dart_skills_lint/analysis_options.yaml | 20 ++++++++++++++++--- .../test/absolute_paths_test.dart | 11 +++------- .../test/cli_integration_test.dart | 11 +++------- .../test/config_file_test.dart | 13 +++++------- .../test/custom_rule_test.dart | 13 +++++------- .../test/directory_structure_test.dart | 15 ++++++-------- .../test/field_constraints_test.dart | 11 +++------- tool/dart_skills_lint/test/fixer_test.dart | 11 +++++----- .../test/metadata_validation_test.dart | 11 +++------- .../test/relative_path_flag_test.dart | 11 +++------- .../test/relative_paths_test.dart | 11 +++------- .../test/skills_ignores_storage_test.dart | 14 ++++++------- tool/dart_skills_lint/test/test_utils.dart | 14 +++++++++++++ 14 files changed, 77 insertions(+), 91 deletions(-) diff --git a/.github/workflows/dart_skills_lint_workflow.yaml b/.github/workflows/dart_skills_lint_workflow.yaml index fa9277c..7c322c9 100644 --- a/.github/workflows/dart_skills_lint_workflow.yaml +++ b/.github/workflows/dart_skills_lint_workflow.yaml @@ -38,7 +38,7 @@ jobs: - run: dart analyze --fatal-infos - name: Run cyclomatic complexity check - run: dart run dart_code_linter:metrics analyze lib + run: dart run dart_code_linter:metrics analyze lib test - run: dart test diff --git a/tool/dart_skills_lint/analysis_options.yaml b/tool/dart_skills_lint/analysis_options.yaml index 3b2de7f..dcc52e6 100644 --- a/tool/dart_skills_lint/analysis_options.yaml +++ b/tool/dart_skills_lint/analysis_options.yaml @@ -15,8 +15,17 @@ analyzer: strict-casts: true strict-inference: true strict-raw-types: true - plugins: - - dart_code_linter + # NOTE: dart_code_linter is intentionally NOT registered as an analyzer + # `plugin` here. When it was, `dart analyze` loaded the plugin in a separate + # isolate that re-emitted the `dart_code_linter:` rules (below) as diagnostics + # over the plugin protocol, covering all non-excluded files (including test/). + # Delivery of those diagnostics to the `dart analyze` CLI is asynchronous and + # racy, so on slower runners (Windows CI) they intermittently appeared, and + # `--fatal-infos` turned them fatal — a flaky, platform-specific failure + # (https://github.com/flutter/skills/issues/144). The dedicated + # `dart run dart_code_linter:metrics analyze lib test` CI step reads the + # `dart_code_linter:` config below and enforces those rules deterministically, + # so the plugin registration is unnecessary. errors: # allow deprecated members (we do this because otherwise we have to annotate # every member in every test, assert, etc, when we or the Dart SDK deprecates @@ -292,5 +301,10 @@ dart_code_linter: # Clean up - avoid-unused-parameters - prefer-moving-to-variable - - prefer-match-file-name + # Test files must be named `*_test.dart` for the test runner, and commonly + # hold several small fixture/mock classes, so they can never match this + # rule's "file name == first class name" convention. Enforce it on lib only. + - prefer-match-file-name: + exclude: + - test/** - always-remove-listener \ No newline at end of file diff --git a/tool/dart_skills_lint/test/absolute_paths_test.dart b/tool/dart_skills_lint/test/absolute_paths_test.dart index 46c3261..d8ce750 100644 --- a/tool/dart_skills_lint/test/absolute_paths_test.dart +++ b/tool/dart_skills_lint/test/absolute_paths_test.dart @@ -16,16 +16,11 @@ import 'test_utils.dart'; void main() { group('Absolute Paths Validation', () { - late Directory tempDir; + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = await Directory.systemTemp.createTemp('absolute_path_test.'); - }); - - tearDown(() async { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + tempDir = await createTempDir('absolute_path_test.'); }); test('flags absolute path starting with / as warning by default', () async { diff --git a/tool/dart_skills_lint/test/cli_integration_test.dart b/tool/dart_skills_lint/test/cli_integration_test.dart index 895cd28..5e51222 100644 --- a/tool/dart_skills_lint/test/cli_integration_test.dart +++ b/tool/dart_skills_lint/test/cli_integration_test.dart @@ -19,16 +19,11 @@ import 'test_utils.dart'; void main() { group('CLI Integration', () { - late Directory tempDir; + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = await Directory.systemTemp.createTemp('cli_test.'); - }); - - tearDown(() async { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + tempDir = await createTempDir('cli_test.'); }); test('de-duplicates baseline entries for multiple identical rule failures', () async { diff --git a/tool/dart_skills_lint/test/config_file_test.dart b/tool/dart_skills_lint/test/config_file_test.dart index dfd1d5c..8e34412 100644 --- a/tool/dart_skills_lint/test/config_file_test.dart +++ b/tool/dart_skills_lint/test/config_file_test.dart @@ -9,18 +9,15 @@ import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:test_process/test_process.dart'; +import 'test_utils.dart'; + void main() { group('Configuration File Integration', () { - late Directory tempDir; + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = await Directory.systemTemp.createTemp('config_test.'); - }); - - tearDown(() async { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + tempDir = await createTempDir('config_test.'); }); test('obeys disabled relative paths in config', () async { diff --git a/tool/dart_skills_lint/test/custom_rule_test.dart b/tool/dart_skills_lint/test/custom_rule_test.dart index 48aa0d6..c6b4822 100644 --- a/tool/dart_skills_lint/test/custom_rule_test.dart +++ b/tool/dart_skills_lint/test/custom_rule_test.dart @@ -4,6 +4,8 @@ import 'package:dart_skills_lint/dart_skills_lint.dart'; import 'package:logging/logging.dart'; import 'package:test/test.dart'; +import 'test_utils.dart'; + class CustomRule extends SkillRule { @override final String name = 'custom-rule'; @@ -50,16 +52,11 @@ class MismatchRule extends SkillRule { void main() { group('Custom Rules', () { - late Directory tempDir; + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = await Directory.systemTemp.createTemp('custom_rule_test.'); - }); - - tearDown(() async { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + tempDir = await createTempDir('custom_rule_test.'); }); test('Validator runs custom rule', () async { diff --git a/tool/dart_skills_lint/test/directory_structure_test.dart b/tool/dart_skills_lint/test/directory_structure_test.dart index 54a5faa..6aa80f6 100644 --- a/tool/dart_skills_lint/test/directory_structure_test.dart +++ b/tool/dart_skills_lint/test/directory_structure_test.dart @@ -10,6 +10,8 @@ import 'package:dart_skills_lint/src/validator.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; +import 'test_utils.dart'; + class MockInaccessibleFile implements File { MockInaccessibleFile(this._path); final String _path; @@ -26,7 +28,7 @@ class MockInaccessibleFile implements File { } @override - dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); + Object? noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } base class TestIOOverrides extends IOOverrides { @@ -44,16 +46,11 @@ base class TestIOOverrides extends IOOverrides { void main() { group('Directory Structure Validation', () { - late Directory tempDir; + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = await Directory.systemTemp.createTemp('skill_test.'); - }); - - tearDown(() async { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + tempDir = await createTempDir('skill_test.'); }); test('fails if directory does not exist', () async { diff --git a/tool/dart_skills_lint/test/field_constraints_test.dart b/tool/dart_skills_lint/test/field_constraints_test.dart index 31c37f8..1eb2cfc 100644 --- a/tool/dart_skills_lint/test/field_constraints_test.dart +++ b/tool/dart_skills_lint/test/field_constraints_test.dart @@ -16,16 +16,11 @@ import 'test_utils.dart'; void main() { group('Field Specific Constraints Validation', () { - late Directory tempDir; + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = await Directory.systemTemp.createTemp('fields_test.'); - }); - - tearDown(() async { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + tempDir = await createTempDir('fields_test.'); }); group('Skill Name', () { diff --git a/tool/dart_skills_lint/test/fixer_test.dart b/tool/dart_skills_lint/test/fixer_test.dart index 75e173f..549bd37 100644 --- a/tool/dart_skills_lint/test/fixer_test.dart +++ b/tool/dart_skills_lint/test/fixer_test.dart @@ -13,6 +13,8 @@ import 'package:dart_skills_lint/src/models/validation_error.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; +import 'test_utils.dart'; + class RuleA extends SkillRule implements FixableRule { @override String get name => 'rule-a'; @@ -90,14 +92,11 @@ class RuleThrows extends SkillRule implements FixableRule { void main() { group('Fixer Sequential Execution', () { - late Directory tempDir; + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = await Directory.systemTemp.createTemp('fixer_test.'); - }); - - tearDown(() async { - await tempDir.delete(recursive: true); + tempDir = await createTempDir('fixer_test.'); }); test('applies fixes in order', () async { diff --git a/tool/dart_skills_lint/test/metadata_validation_test.dart b/tool/dart_skills_lint/test/metadata_validation_test.dart index cd807a7..a3c0ed1 100644 --- a/tool/dart_skills_lint/test/metadata_validation_test.dart +++ b/tool/dart_skills_lint/test/metadata_validation_test.dart @@ -13,16 +13,11 @@ import 'test_utils.dart'; void main() { group('Metadata (YAML) Validation', () { - late Directory tempDir; + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = await Directory.systemTemp.createTemp('metadata_test.'); - }); - - tearDown(() async { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + tempDir = await createTempDir('metadata_test.'); }); test('fails if YAML metadata is invalid', () async { diff --git a/tool/dart_skills_lint/test/relative_path_flag_test.dart b/tool/dart_skills_lint/test/relative_path_flag_test.dart index b2a30dd..61d17d8 100644 --- a/tool/dart_skills_lint/test/relative_path_flag_test.dart +++ b/tool/dart_skills_lint/test/relative_path_flag_test.dart @@ -14,16 +14,11 @@ import 'test_utils.dart'; void main() { group('Relative Path Flag Validation', () { - late Directory tempDir; + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = await Directory.systemTemp.createTemp('relative_path_test.'); - }); - - tearDown(() async { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + tempDir = await createTempDir('relative_path_test.'); }); test('validates links when relativePathsSeverity = warning', () async { diff --git a/tool/dart_skills_lint/test/relative_paths_test.dart b/tool/dart_skills_lint/test/relative_paths_test.dart index 8b74b45..4cb2482 100644 --- a/tool/dart_skills_lint/test/relative_paths_test.dart +++ b/tool/dart_skills_lint/test/relative_paths_test.dart @@ -14,16 +14,11 @@ import 'test_utils.dart'; void main() { group('Relative Paths Validation', () { - late Directory tempDir; + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = await Directory.systemTemp.createTemp('paths_test.'); - }); - - tearDown(() async { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + tempDir = await createTempDir('paths_test.'); }); test('passes with valid relative file path (existing file)', () async { diff --git a/tool/dart_skills_lint/test/skills_ignores_storage_test.dart b/tool/dart_skills_lint/test/skills_ignores_storage_test.dart index 398f2b8..2b5ad77 100644 --- a/tool/dart_skills_lint/test/skills_ignores_storage_test.dart +++ b/tool/dart_skills_lint/test/skills_ignores_storage_test.dart @@ -8,17 +8,15 @@ import 'package:dart_skills_lint/src/models/skills_ignores.dart'; import 'package:dart_skills_lint/src/skills_ignores_storage.dart'; import 'package:test/test.dart'; +import 'test_utils.dart'; + void main() { - late Directory tempDir; - late SkillsIgnoresStorage storage; + final storage = SkillsIgnoresStorage(); + // Reassigned in setUp; the placeholder keeps the field non-`late`. + Directory tempDir = Directory.systemTemp; setUp(() async { - tempDir = Directory.systemTemp.createTempSync('storage_test.'); - storage = SkillsIgnoresStorage(); - }); - - tearDown(() async { - await tempDir.delete(recursive: true); + tempDir = await createTempDir('storage_test.'); }); group('SkillsIgnoresStorage.load Integration', () { diff --git a/tool/dart_skills_lint/test/test_utils.dart b/tool/dart_skills_lint/test/test_utils.dart index afa45fd..1f3d831 100644 --- a/tool/dart_skills_lint/test/test_utils.dart +++ b/tool/dart_skills_lint/test/test_utils.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:io'; import 'package:path/path.dart' as p; +import 'package:test/test.dart'; String buildFrontmatter({ String name = 'Skill-Name', @@ -22,6 +23,19 @@ String buildFrontmatter({ return sb.toString(); } +/// Creates a fresh temporary directory and registers automatic cleanup via +/// [addTearDown]. Assign the result to a (non-`late`) variable in `setUp`; the +/// directory is deleted after the test completes. +Future createTempDir(String prefix) async { + final Directory tempDir = await Directory.systemTemp.createTemp(prefix); + addTearDown(() async { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } + }); + return tempDir; +} + /// Creates a temporary directory for testing and automatically cleans it up. Future withTempDir(FutureOr Function(Directory tempDir) action) async { final Directory tempDir = await Directory.systemTemp.createTemp('api_test.'); From 003f11fb1f90587f5a46b92de719ec5e653e79f4 Mon Sep 17 00:00:00 2001 From: Reid Baker Date: Fri, 22 May 2026 01:15:23 -0400 Subject: [PATCH 2/3] Exclude avoid-late-keyword for tests instead of placeholder workaround Address review feedback: rather than initializing tempDir fixtures to a placeholder to satisfy avoid-late-keyword, exclude the rule for test/** (as already done for prefer-match-file-name). `late` for fields assigned in setUp is the idiomatic Dart test pattern. Reverts the createTempDir helper and placeholder initializations; fixtures are plain `late` again. The genuine, non-late fixes are kept: avoid-dynamic (Object? noSuchMethod) and avoid-redundant-async (drop async from a sync setUp). --- tool/dart_skills_lint/analysis_options.yaml | 6 +++++- .../test/absolute_paths_test.dart | 11 ++++++++--- .../test/cli_integration_test.dart | 11 ++++++++--- tool/dart_skills_lint/test/config_file_test.dart | 13 ++++++++----- tool/dart_skills_lint/test/custom_rule_test.dart | 13 ++++++++----- .../test/directory_structure_test.dart | 13 ++++++++----- .../test/field_constraints_test.dart | 11 ++++++++--- tool/dart_skills_lint/test/fixer_test.dart | 11 ++++++----- .../test/metadata_validation_test.dart | 11 ++++++++--- .../test/relative_path_flag_test.dart | 11 ++++++++--- .../test/relative_paths_test.dart | 11 ++++++++--- .../test/skills_ignores_storage_test.dart | 16 +++++++++------- tool/dart_skills_lint/test/test_utils.dart | 14 -------------- 13 files changed, 92 insertions(+), 60 deletions(-) diff --git a/tool/dart_skills_lint/analysis_options.yaml b/tool/dart_skills_lint/analysis_options.yaml index dcc52e6..195040e 100644 --- a/tool/dart_skills_lint/analysis_options.yaml +++ b/tool/dart_skills_lint/analysis_options.yaml @@ -295,7 +295,11 @@ dart_code_linter: - no-empty-block - avoid-redundant-async - avoid-passing-async-when-sync-expected - - avoid-late-keyword + # `late` for fields initialized in `setUp` is the idiomatic Dart test + # fixture pattern; enforce this rule on lib only. + - avoid-late-keyword: + exclude: + - test/** - prefer-named-record-fields # Clean up diff --git a/tool/dart_skills_lint/test/absolute_paths_test.dart b/tool/dart_skills_lint/test/absolute_paths_test.dart index d8ce750..46c3261 100644 --- a/tool/dart_skills_lint/test/absolute_paths_test.dart +++ b/tool/dart_skills_lint/test/absolute_paths_test.dart @@ -16,11 +16,16 @@ import 'test_utils.dart'; void main() { group('Absolute Paths Validation', () { - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; setUp(() async { - tempDir = await createTempDir('absolute_path_test.'); + tempDir = await Directory.systemTemp.createTemp('absolute_path_test.'); + }); + + tearDown(() async { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } }); test('flags absolute path starting with / as warning by default', () async { diff --git a/tool/dart_skills_lint/test/cli_integration_test.dart b/tool/dart_skills_lint/test/cli_integration_test.dart index 5e51222..895cd28 100644 --- a/tool/dart_skills_lint/test/cli_integration_test.dart +++ b/tool/dart_skills_lint/test/cli_integration_test.dart @@ -19,11 +19,16 @@ import 'test_utils.dart'; void main() { group('CLI Integration', () { - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; setUp(() async { - tempDir = await createTempDir('cli_test.'); + tempDir = await Directory.systemTemp.createTemp('cli_test.'); + }); + + tearDown(() async { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } }); test('de-duplicates baseline entries for multiple identical rule failures', () async { diff --git a/tool/dart_skills_lint/test/config_file_test.dart b/tool/dart_skills_lint/test/config_file_test.dart index 8e34412..dfd1d5c 100644 --- a/tool/dart_skills_lint/test/config_file_test.dart +++ b/tool/dart_skills_lint/test/config_file_test.dart @@ -9,15 +9,18 @@ import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:test_process/test_process.dart'; -import 'test_utils.dart'; - void main() { group('Configuration File Integration', () { - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; setUp(() async { - tempDir = await createTempDir('config_test.'); + tempDir = await Directory.systemTemp.createTemp('config_test.'); + }); + + tearDown(() async { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } }); test('obeys disabled relative paths in config', () async { diff --git a/tool/dart_skills_lint/test/custom_rule_test.dart b/tool/dart_skills_lint/test/custom_rule_test.dart index c6b4822..48aa0d6 100644 --- a/tool/dart_skills_lint/test/custom_rule_test.dart +++ b/tool/dart_skills_lint/test/custom_rule_test.dart @@ -4,8 +4,6 @@ import 'package:dart_skills_lint/dart_skills_lint.dart'; import 'package:logging/logging.dart'; import 'package:test/test.dart'; -import 'test_utils.dart'; - class CustomRule extends SkillRule { @override final String name = 'custom-rule'; @@ -52,11 +50,16 @@ class MismatchRule extends SkillRule { void main() { group('Custom Rules', () { - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; setUp(() async { - tempDir = await createTempDir('custom_rule_test.'); + tempDir = await Directory.systemTemp.createTemp('custom_rule_test.'); + }); + + tearDown(() async { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } }); test('Validator runs custom rule', () async { diff --git a/tool/dart_skills_lint/test/directory_structure_test.dart b/tool/dart_skills_lint/test/directory_structure_test.dart index 6aa80f6..8d501a1 100644 --- a/tool/dart_skills_lint/test/directory_structure_test.dart +++ b/tool/dart_skills_lint/test/directory_structure_test.dart @@ -10,8 +10,6 @@ import 'package:dart_skills_lint/src/validator.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; -import 'test_utils.dart'; - class MockInaccessibleFile implements File { MockInaccessibleFile(this._path); final String _path; @@ -46,11 +44,16 @@ base class TestIOOverrides extends IOOverrides { void main() { group('Directory Structure Validation', () { - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; setUp(() async { - tempDir = await createTempDir('skill_test.'); + tempDir = await Directory.systemTemp.createTemp('skill_test.'); + }); + + tearDown(() async { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } }); test('fails if directory does not exist', () async { diff --git a/tool/dart_skills_lint/test/field_constraints_test.dart b/tool/dart_skills_lint/test/field_constraints_test.dart index 1eb2cfc..31c37f8 100644 --- a/tool/dart_skills_lint/test/field_constraints_test.dart +++ b/tool/dart_skills_lint/test/field_constraints_test.dart @@ -16,11 +16,16 @@ import 'test_utils.dart'; void main() { group('Field Specific Constraints Validation', () { - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; setUp(() async { - tempDir = await createTempDir('fields_test.'); + tempDir = await Directory.systemTemp.createTemp('fields_test.'); + }); + + tearDown(() async { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } }); group('Skill Name', () { diff --git a/tool/dart_skills_lint/test/fixer_test.dart b/tool/dart_skills_lint/test/fixer_test.dart index 549bd37..75e173f 100644 --- a/tool/dart_skills_lint/test/fixer_test.dart +++ b/tool/dart_skills_lint/test/fixer_test.dart @@ -13,8 +13,6 @@ import 'package:dart_skills_lint/src/models/validation_error.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; -import 'test_utils.dart'; - class RuleA extends SkillRule implements FixableRule { @override String get name => 'rule-a'; @@ -92,11 +90,14 @@ class RuleThrows extends SkillRule implements FixableRule { void main() { group('Fixer Sequential Execution', () { - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; setUp(() async { - tempDir = await createTempDir('fixer_test.'); + tempDir = await Directory.systemTemp.createTemp('fixer_test.'); + }); + + tearDown(() async { + await tempDir.delete(recursive: true); }); test('applies fixes in order', () async { diff --git a/tool/dart_skills_lint/test/metadata_validation_test.dart b/tool/dart_skills_lint/test/metadata_validation_test.dart index a3c0ed1..cd807a7 100644 --- a/tool/dart_skills_lint/test/metadata_validation_test.dart +++ b/tool/dart_skills_lint/test/metadata_validation_test.dart @@ -13,11 +13,16 @@ import 'test_utils.dart'; void main() { group('Metadata (YAML) Validation', () { - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; setUp(() async { - tempDir = await createTempDir('metadata_test.'); + tempDir = await Directory.systemTemp.createTemp('metadata_test.'); + }); + + tearDown(() async { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } }); test('fails if YAML metadata is invalid', () async { diff --git a/tool/dart_skills_lint/test/relative_path_flag_test.dart b/tool/dart_skills_lint/test/relative_path_flag_test.dart index 61d17d8..b2a30dd 100644 --- a/tool/dart_skills_lint/test/relative_path_flag_test.dart +++ b/tool/dart_skills_lint/test/relative_path_flag_test.dart @@ -14,11 +14,16 @@ import 'test_utils.dart'; void main() { group('Relative Path Flag Validation', () { - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; setUp(() async { - tempDir = await createTempDir('relative_path_test.'); + tempDir = await Directory.systemTemp.createTemp('relative_path_test.'); + }); + + tearDown(() async { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } }); test('validates links when relativePathsSeverity = warning', () async { diff --git a/tool/dart_skills_lint/test/relative_paths_test.dart b/tool/dart_skills_lint/test/relative_paths_test.dart index 4cb2482..8b74b45 100644 --- a/tool/dart_skills_lint/test/relative_paths_test.dart +++ b/tool/dart_skills_lint/test/relative_paths_test.dart @@ -14,11 +14,16 @@ import 'test_utils.dart'; void main() { group('Relative Paths Validation', () { - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; setUp(() async { - tempDir = await createTempDir('paths_test.'); + tempDir = await Directory.systemTemp.createTemp('paths_test.'); + }); + + tearDown(() async { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } }); test('passes with valid relative file path (existing file)', () async { diff --git a/tool/dart_skills_lint/test/skills_ignores_storage_test.dart b/tool/dart_skills_lint/test/skills_ignores_storage_test.dart index 2b5ad77..99c10f3 100644 --- a/tool/dart_skills_lint/test/skills_ignores_storage_test.dart +++ b/tool/dart_skills_lint/test/skills_ignores_storage_test.dart @@ -8,15 +8,17 @@ import 'package:dart_skills_lint/src/models/skills_ignores.dart'; import 'package:dart_skills_lint/src/skills_ignores_storage.dart'; import 'package:test/test.dart'; -import 'test_utils.dart'; - void main() { - final storage = SkillsIgnoresStorage(); - // Reassigned in setUp; the placeholder keeps the field non-`late`. - Directory tempDir = Directory.systemTemp; + late Directory tempDir; + late SkillsIgnoresStorage storage; + + setUp(() { + tempDir = Directory.systemTemp.createTempSync('storage_test.'); + storage = SkillsIgnoresStorage(); + }); - setUp(() async { - tempDir = await createTempDir('storage_test.'); + tearDown(() async { + await tempDir.delete(recursive: true); }); group('SkillsIgnoresStorage.load Integration', () { diff --git a/tool/dart_skills_lint/test/test_utils.dart b/tool/dart_skills_lint/test/test_utils.dart index 1f3d831..afa45fd 100644 --- a/tool/dart_skills_lint/test/test_utils.dart +++ b/tool/dart_skills_lint/test/test_utils.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:io'; import 'package:path/path.dart' as p; -import 'package:test/test.dart'; String buildFrontmatter({ String name = 'Skill-Name', @@ -23,19 +22,6 @@ String buildFrontmatter({ return sb.toString(); } -/// Creates a fresh temporary directory and registers automatic cleanup via -/// [addTearDown]. Assign the result to a (non-`late`) variable in `setUp`; the -/// directory is deleted after the test completes. -Future createTempDir(String prefix) async { - final Directory tempDir = await Directory.systemTemp.createTemp(prefix); - addTearDown(() async { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } - }); - return tempDir; -} - /// Creates a temporary directory for testing and automatically cleans it up. Future withTempDir(FutureOr Function(Directory tempDir) action) async { final Directory tempDir = await Directory.systemTemp.createTemp('api_test.'); From 3efac9072f2757799ff7934337a36b469f40f4eb Mon Sep 17 00:00:00 2001 From: Reid Baker <1063596+reidbaker@users.noreply.github.com> Date: Fri, 22 May 2026 09:50:01 -0400 Subject: [PATCH 3/3] Apply suggestion from @reidbaker --- tool/dart_skills_lint/analysis_options.yaml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/tool/dart_skills_lint/analysis_options.yaml b/tool/dart_skills_lint/analysis_options.yaml index 195040e..7128ea6 100644 --- a/tool/dart_skills_lint/analysis_options.yaml +++ b/tool/dart_skills_lint/analysis_options.yaml @@ -15,17 +15,9 @@ analyzer: strict-casts: true strict-inference: true strict-raw-types: true - # NOTE: dart_code_linter is intentionally NOT registered as an analyzer - # `plugin` here. When it was, `dart analyze` loaded the plugin in a separate - # isolate that re-emitted the `dart_code_linter:` rules (below) as diagnostics - # over the plugin protocol, covering all non-excluded files (including test/). - # Delivery of those diagnostics to the `dart analyze` CLI is asynchronous and - # racy, so on slower runners (Windows CI) they intermittently appeared, and - # `--fatal-infos` turned them fatal — a flaky, platform-specific failure - # (https://github.com/flutter/skills/issues/144). The dedicated - # `dart run dart_code_linter:metrics analyze lib test` CI step reads the - # `dart_code_linter:` config below and enforces those rules deterministically, - # so the plugin registration is unnecessary. + # dart_code_linter is intentionally NOT registered as an analyzer + # `plugin` here. When it was, `dart analyze` had racy false positives. + # See https://github.com/flutter/skills/issues/144 errors: # allow deprecated members (we do this because otherwise we have to annotate # every member in every test, assert, etc, when we or the Dart SDK deprecates