tree_sitter test coverage + DI seam, ratchet floor to 95
Add `colorForRole` switch-arm tests (every role → token mapping plus the unknown-role fallback). Introduce a DI seam in `TreeSitterService` and `TreeSitterLib` so tests can substitute the FFI surface and asset loaders without dlopen'ing `libtree-sitter.so` — `TreeSitterLib.testing(...)` takes named per-function overrides with safe no-op defaults, and `TreeSitterLib.fromDynamicLibrary(...)` lets the smoke test load the vendored library explicitly. Production paths (`TreeSitterService.shared`, `TreeSitterLib.instance`) are unchanged. Fake-FFI tests walk every branch of `_init`, `_loadGrammar`, `highlight`, and `dispose`. The smoke test catches FFI-signature regressions the fakes can't, by exercising the real native library end-to-end on Linux. Together this takes `tree_sitter_service.dart` from 17% to 96% and crosses the global 95% target — closing out the D-66 line-coverage epic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,8 +6,11 @@
|
||||
library;
|
||||
|
||||
import 'package:clide/kernel/src/syntax/tree_sitter_service.dart';
|
||||
import 'package:clide/kernel/src/theme/tokens.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../helpers/kernel_fixture.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
@@ -63,4 +66,99 @@ void main() {
|
||||
expect(s.role, 'keyword');
|
||||
});
|
||||
});
|
||||
|
||||
group('TreeSitterService.colorForRole', () {
|
||||
late SurfaceTokens tokens;
|
||||
|
||||
setUpAll(() async {
|
||||
final f = await KernelFixture.create();
|
||||
tokens = f.services.theme.current.surface;
|
||||
await f.dispose();
|
||||
});
|
||||
|
||||
test('every keyword-flavoured role maps to syntaxKeyword', () {
|
||||
for (final role in ['keyword', 'repeat', 'conditional', 'include', 'exception', 'operator']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxKeyword, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('type-flavoured roles map to syntaxType', () {
|
||||
for (final role in ['type', 'type.builtin', 'constructor']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxType, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('string-flavoured roles map to syntaxString', () {
|
||||
for (final role in ['string', 'string.special']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxString, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('number-flavoured roles map to syntaxNumber', () {
|
||||
for (final role in ['number', 'float', 'boolean']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxNumber, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('comment maps to syntaxComment', () {
|
||||
expect(TreeSitterService.colorForRole('comment', tokens), tokens.syntaxComment);
|
||||
});
|
||||
|
||||
test('function-flavoured roles map to syntaxMethod', () {
|
||||
for (final role in ['function', 'function.builtin', 'function.method', 'method']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxMethod, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('punctuation roles map to syntaxPunct', () {
|
||||
for (final role in ['punctuation.bracket', 'punctuation.delimiter', 'punctuation.special']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxPunct, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('variable roles fall back to globalForeground', () {
|
||||
for (final role in ['variable', 'variable.builtin', 'variable.parameter']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.globalForeground, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('property / field share syntaxMethod with functions', () {
|
||||
for (final role in ['property', 'field']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxMethod, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('constant roles share syntaxNumber with numbers', () {
|
||||
for (final role in ['constant', 'constant.builtin']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxNumber, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('tag / attribute share syntaxKeyword', () {
|
||||
for (final role in ['tag', 'attribute']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxKeyword, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('namespace / module share syntaxType', () {
|
||||
for (final role in ['namespace', 'module']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxType, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('markdown text.* roles distribute across keyword / string / type tokens', () {
|
||||
expect(TreeSitterService.colorForRole('text.title', tokens), tokens.syntaxKeyword);
|
||||
for (final role in ['text.literal', 'text.reference', 'text.uri']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxString, reason: role);
|
||||
}
|
||||
for (final role in ['text.emphasis', 'text.strong']) {
|
||||
expect(TreeSitterService.colorForRole(role, tokens), tokens.syntaxType, reason: role);
|
||||
}
|
||||
});
|
||||
|
||||
test('unknown roles fall through to globalForeground', () {
|
||||
expect(TreeSitterService.colorForRole('definitely-not-a-real-role', tokens), tokens.globalForeground);
|
||||
expect(TreeSitterService.colorForRole('', tokens), tokens.globalForeground);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user