test sweep: cover kernel/src/syntax (T-91)
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s

Two test files covering the pure-Dart and fallback paths of the
tree-sitter integration. The native-FFI parsing depth stays
untested — DynamicLibrary.open('libtree-sitter.so') doesn't resolve
under the flutter test runner because the bundled lib sits at
native/linux-x64/ rather than on the linker search path. Real
parsing coverage would need an integration-test harness that copies
or symlinks the lib into the runner's working directory.

- test/kernel/src/syntax/language_map_test.dart (6 tests): every
  branch of grammarForPath — common extensions, case-insensitive
  matching, special filenames (Makefile / Dockerfile / .gitignore /
  justfile), no-dot/no-special-match fallthrough, unknown extension.
- test/kernel/src/syntax/tree_sitter_service_test.dart (9 tests):
  graceful-fallback paths of TreeSitterService when the library
  can't load — hasGrammar / languageFor / highlight all short-
  circuit cleanly, plus SyntaxResult.empty and SyntaxSpan field
  storage.

Coverage: language_map.dart 0/7 -> 7/7; tree_sitter_service.dart
1/131 -> 22/131 (the rest is deep FFI work); tree_sitter_ffi.dart
0/15 -> 1/15 (init entry only).

Total coverage 75.11% -> 75.42%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-13 06:41:14 +02:00
co-authored by Claude Opus 4.7
parent 331201e287
commit 3175b7a633
2 changed files with 114 additions and 0 deletions
@@ -0,0 +1,48 @@
/// Pure-Dart tests for `grammarForPath` in
/// `lib/kernel/src/syntax/language_map.dart`.
library;
import 'package:clide/kernel/src/syntax/language_map.dart';
import 'package:test/test.dart';
void main() {
group('grammarForPath', () {
test('maps common file extensions to grammar names', () {
expect(grammarForPath('foo.dart'), 'dart');
expect(grammarForPath('a/b/c.go'), 'go');
expect(grammarForPath('script.py'), 'python');
expect(grammarForPath('view.tsx'), 'typescript');
expect(grammarForPath('config.yaml'), 'yaml');
expect(grammarForPath('config.yml'), 'yaml');
expect(grammarForPath('readme.md'), 'markdown');
});
test('extension match is case-insensitive', () {
expect(grammarForPath('a.R'), 'r');
expect(grammarForPath('Q.PY'), 'python');
});
test('special filenames bypass the extension lookup', () {
expect(grammarForPath('Makefile'), 'make');
expect(grammarForPath('a/b/Dockerfile'), 'dockerfile');
expect(grammarForPath('.gitignore'), 'gitignore');
expect(grammarForPath('justfile'), 'just');
});
test('special filename + recognised extension still picks the special name', () {
// Makefile has no '.' so the extension branch wouldn't fire anyway —
// this just locks in that the filename map runs first.
expect(grammarForPath('Makefile'), 'make');
});
test('returns null when there is no dot and no special filename match', () {
expect(grammarForPath('README'), isNull);
expect(grammarForPath('a/b/UNKNOWNFILE'), isNull);
});
test('returns null for an unknown extension', () {
expect(grammarForPath('foo.unknownextension'), isNull);
expect(grammarForPath('a/b/c.xyz'), isNull);
});
});
}
@@ -0,0 +1,66 @@
/// Tests the graceful-fallback paths of `TreeSitterService` when the
/// native `libtree-sitter` library isn't dlopen-resolvable. In that
/// environment the service short-circuits every public method without
/// throwing — which is also how the running app behaves on platforms
/// where the library hasn't been bundled.
library;
import 'package:clide/kernel/src/syntax/tree_sitter_service.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('TreeSitterService — fallback paths', () {
final svc = TreeSitterService.shared;
test('hasGrammar(path) returns false for an unknown extension', () async {
expect(await svc.hasGrammar('foo.unknownextension'), isFalse);
});
test('hasGrammar(path) returns false when the library cannot load', () async {
// 'foo.dart' resolves to grammar "dart", which still requires the
// native library + asset to actually load. In the test runner the
// native lib isn't on the dlopen search path, so _init() fails and
// hasGrammar reports false.
expect(await svc.hasGrammar('foo.dart'), isFalse);
});
test('languageFor(path) returns null when the language is unknown', () async {
expect(await svc.languageFor('foo.unknownextension'), isNull);
});
test('languageFor(path) returns null when the library cannot load', () async {
expect(await svc.languageFor('foo.dart'), isNull);
});
test('highlight returns an empty result for unknown extensions', () async {
final r = await svc.highlight('foo.unknownextension', 'whatever');
expect(r.spans, isEmpty);
});
test('highlight returns an empty result when the library cannot load', () async {
final r = await svc.highlight('foo.dart', 'void main() {}');
expect(r.spans, isEmpty);
});
test('loadedLanguages getter returns a list (may be empty)', () {
// Just exercise the getter — the actual contents depend on the
// environment.
expect(svc.loadedLanguages, isA<List<String>>());
});
});
group('SyntaxResult / SyntaxSpan', () {
test('SyntaxResult.empty has no spans', () {
expect(SyntaxResult.empty.spans, isEmpty);
});
test('SyntaxSpan stores start / end / role', () {
const s = SyntaxSpan(start: 3, end: 7, role: 'keyword');
expect(s.start, 3);
expect(s.end, 7);
expect(s.role, 'keyword');
});
});
}