Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions tree-sitter-rust/src/test/java/org/treesitter/TreeSitterRustTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,181 @@

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class TreeSitterRustTest {
@Test
void corpusTest() throws IOException {
CorpusTest.runAllTestsInDefaultFolder(new TreeSitterRust(), "rust");
}

@Test
void testIntegrationQuery() {
TSParser parser = new TSParser();
parser.setLanguage(new TreeSitterRust());
String source = "#[derive(Is)] \n" +
"pub enum Status {\n" +
" Running,\n" +
" Stopped,\n" +
" Initial,\n" +
"}";
TSTree tree = parser.parseString(null, source);
TSNode rootNode = tree.getRootNode();

String queryString = "(attribute_item\n" +
" (attribute\n" +
" (identifier) @_derive\n" +
" (#eq? @_derive \"derive\")\n" +
" (token_tree\n" +
" (identifier) @macro.derive.name\n" +
" )\n" +
" )\n" +
") @macro.derive";

TSQuery query = new TSQuery(parser.getLanguage(), queryString);
TSQueryCursor cursor = new TSQueryCursor();
cursor.exec(query, rootNode, source);

TSQueryMatch match = new TSQueryMatch();
assertTrue(cursor.nextMatch(match), "Query should have matched");

boolean foundMacroDerive = false;
boolean foundDeriveName = false;
for (TSQueryCapture capture : match.getCaptures()) {
String captureName = query.getCaptureNameForId(capture.getIndex());
TSNode node = capture.getNode();
String matchedText = source.substring(node.getStartByte(), node.getEndByte());

if ("macro.derive".equals(captureName)) {
foundMacroDerive = true;
assertTrue(matchedText.contains("#[derive(Is)]"),
"Matched text should contain the attribute. Found: " + matchedText);
} else if ("macro.derive.name".equals(captureName)) {
foundDeriveName = true;
assertEquals("Is", matchedText, "macro.derive.name should match 'Is'");
}
}
assertTrue(foundMacroDerive, "Should have found @macro.derive capture");
assertTrue(foundDeriveName, "Should have found @macro.derive.name capture");
}

@Test
void testIntegrationQueryNotEqShouldNotMatch() {
TSParser parser = new TSParser();
parser.setLanguage(new TreeSitterRust());

String source = "#[derive(Other)] \n" +
"pub enum Status {\n" +
" Running,\n" +
" Stopped,\n" +
" Initial,\n" +
"}";

TSTree tree = parser.parseString(null, source);
TSNode rootNode = tree.getRootNode();

String queryString = "(attribute_item\n" +
" (attribute\n" +
" (identifier) @_derive\n" +
" (#not-eq? @_derive \"derive\")\n" +
" (token_tree\n" +
" (identifier) @macro.derive.name\n" +
" )\n" +
" )\n" +
") @macro.derive";

TSQuery query = new TSQuery(parser.getLanguage(), queryString);
TSQueryCursor cursor = new TSQueryCursor();
cursor.exec(query, rootNode, source);

TSQueryMatch match = new TSQueryMatch();
assertFalse(cursor.nextMatch(match), "Query should not match because #not-eq? predicate fails");
}

@Test
void testIntegrationQueryEqShouldNotMatch() {
TSParser parser = new TSParser();
parser.setLanguage(new TreeSitterRust());

String source = "#[foo(Other)] \n" +
"pub enum Status {\n" +
" Running,\n" +
" Stopped,\n" +
" Initial,\n" +
"}";

TSTree tree = parser.parseString(null, source);
TSNode rootNode = tree.getRootNode();

String queryString = "(attribute_item\n" +
" (attribute\n" +
" (identifier) @_derive\n" +
" (#eq? @_derive \"derive\")\n" +
" (token_tree\n" +
" (identifier) @macro.derive.name\n" +
" )\n" +
" )\n" +
") @macro.derive";

TSQuery query = new TSQuery(parser.getLanguage(), queryString);
TSQueryCursor cursor = new TSQueryCursor();
cursor.exec(query, rootNode, source);

TSQueryMatch match = new TSQueryMatch();
assertFalse(cursor.nextMatch(match), "Query should not match because #eq? predicate fails");
}


@Test
void testIntegrationQueryNotEqShouldMatch() {
TSParser parser = new TSParser();
parser.setLanguage(new TreeSitterRust());

String source = "#[is_macro::Is]\n" +
"pub enum Status {\n" +
" Running,\n" +
" Stopped,\n" +
" Initial,\n" +
"}";

TSTree tree = parser.parseString(null, source);
TSNode rootNode = tree.getRootNode();

String queryString = "(attribute_item\n" +
" (attribute\n" +
" [\n" +
" ((identifier) @macro.attribute.name\n" +
" (#not-eq? @macro.attribute.name \"derive\"))\n" +
" (scoped_identifier) @macro.attribute.name\n" +
" ]\n" +
" )\n" +
") @macro.attribute";

TSQuery query = new TSQuery(parser.getLanguage(), queryString);
TSQueryCursor cursor = new TSQueryCursor();
cursor.exec(query, rootNode, source);

TSQueryMatch match = new TSQueryMatch();
assertTrue(cursor.nextMatch(match), "Query should have matched is_macro::Is");

boolean foundAttribute = false;
boolean foundName = false;
for (TSQueryCapture capture : match.getCaptures()) {
String captureName = query.getCaptureNameForId(capture.getIndex());
TSNode node = capture.getNode();
String matchedText = source.substring(node.getStartByte(), node.getEndByte());

if ("macro.attribute".equals(captureName)) {
foundAttribute = true;
assertTrue(matchedText.contains("#[is_macro::Is]"));
} else if ("macro.attribute.name".equals(captureName)) {
foundName = true;
assertEquals("is_macro::Is", matchedText);
}
}
assertTrue(foundAttribute, "Should have found @macro.attribute capture");
assertTrue(foundName, "Should have found @macro.attribute.name capture");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ public String getName() {
*/
public boolean test(TSQueryMatch match, byte[] sourceBytes) {
return test(match, n -> {
if (n == null || n.isNull() || sourceBytes == null) return "";
if (n == null || n.isNull()) return "";
if (sourceBytes == null || n.getStartByte() < 0 || n.getStartByte() > n.getEndByte() || n.getStartByte() >= sourceBytes.length) {
throw new IllegalStateException("Source bytes are required to evaluate text-based predicates");
}
int start = n.getStartByte();
int end = n.getEndByte();
if (start < 0 || start > end || start >= sourceBytes.length) {
return "";
}
int length = Math.min(end, sourceBytes.length) - start;
return new String(sourceBytes, start, length, java.nio.charset.StandardCharsets.UTF_8);
});
Expand Down
17 changes: 17 additions & 0 deletions tree-sitter/src/test/java/org/treesitter/TSQueryPredicateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,21 @@ void predicateWithMultiByteChars() {
assertTrue(cursor.nextMatch(match), "Should match '世界' using partial regex");
assertFalse(cursor.nextMatch(match));
}

@Test
void testMissingSourceBytesThrowsException() {
// [1, null]
// Use a simple query that matches something but doesn't have its own predicates
query = new TSQuery(json, "((number) @val)");
cursor.exec(query, rootNode, JSON_SRC);
TSQueryMatch match = new TSQueryMatch();
assertTrue(cursor.nextMatch(match));

// Manually create a predicate to test the exception throwing behavior.
// Capture index 0 is '@val' from the query above.
TSQueryPredicate predicate = new TSQueryPredicate.TSQueryPredicateEq("eq?", 0, "1", -1, false);

// Attempting to test predicates with null sourceBytes should throw IllegalStateException
assertThrows(IllegalStateException.class, () -> predicate.test(match, (byte[]) null));
}
}
Loading