Files
FlClash/test/lint/ui_layer_singleton_test.dart
T
chen08209 96fcb9ab5e chore(agents,tool): rebuild agent config, lint gates, CI, and release tooling
Rework .agents/, Claude/Codex config, CI workflows, lint gates and
lint-rule tests, and the release/changelog tooling. The 5% comment
density gate stays diff-scoped inside git hooks, where GIT_DIR outranks
the -C the gate uses to locate the repository. The tray icon generator
writes multi-size ICO files from the SVG sources into the per-platform
tray asset directories. The custom code-review subagents are gone: the
built-in /code-review never dispatched them, and the subagent model now
lives in a user-level setting outside the repository.

The Makefile, build_config.yaml and CI hand the Core and Helper builds to
the setup build hook, so the buildkit launchers go, and setup.dart
packages AppImage and rpm on arm64 with flutter_distributor pinned to the
fork tag that normalizes packaging permissions.

Changelog: skip
2026-09-10 12:42:52 +08:00

56 lines
1.6 KiB
Dart

import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
const _uiRoots = ['lib/views', 'lib/widgets', 'lib/pages', 'lib/features'];
const _forbidden = <String, String>{
r'globalState\.container':
'read providers through a WidgetRef, not the global container',
r'(?<![\w.])coreController\b':
'reach Core through coreHandlerProvider, not the global singleton',
};
bool _isGenerated(String path) {
return path.contains('/generated/') ||
path.endsWith('.g.dart') ||
path.endsWith('.freezed.dart');
}
void main() {
test('the UI layer never reaches a process-wide singleton directly', () {
final offenders = <String>[];
for (final root in _uiRoots) {
final directory = Directory(root);
if (!directory.existsSync()) {
fail('$root no longer exists; update _uiRoots.');
}
for (final entity in directory.listSync(recursive: true)) {
if (entity is! File || !entity.path.endsWith('.dart')) {
continue;
}
final relative = p.relative(entity.path);
if (_isGenerated(relative)) {
continue;
}
final lines = entity.readAsLinesSync();
for (var i = 0; i < lines.length; i++) {
final line = lines[i];
if (line.trimLeft().startsWith('//')) {
continue;
}
for (final entry in _forbidden.entries) {
if (RegExp(entry.key).hasMatch(line)) {
offenders.add('$relative:${i + 1} — ${entry.value}');
}
}
}
}
}
expect(offenders, isEmpty, reason: offenders.join('\n'));
});
}