Files
langkit/testsuite/tests/java_api/general/common/BindingsTests.java
Hugo Guerrier 7809c5bfd0 Java API: Ensure empty structs 'dummy' field is correctly initialized when unwrapping
Also ensure empty struct types have a valid 'wrap' method.
2025-09-01 11:51:18 +02:00

877 lines
29 KiB
Java

import java.io.File;
import java.util.Arrays;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.math.BigInteger;
import com.adacore.libfoolang.Libfoolang.*;
public final class BindingsTests {
// ----- Util methods -----
/**
* Display the section header with the given name
*
* @param name The name of the section
*/
private static void header(String name) {
System.out.println("--- " + name + " ---");
}
/**
* Display the footer of a section
*
* @param name The name of the section
*/
private static void footer(String name) {
System.out.println("----" + "-".repeat(name.length()) + "----\n");
}
/**
* Display the information about the tokens in the file
*
* @param file The file to get tokens from
* @param withTrivia If the info should include trivia
*/
private static void tokensInfo(String file, boolean withTrivia) {
try(
AnalysisContext context = AnalysisContext.create(
null,
null,
null,
null,
withTrivia,
8
)
) {
AnalysisUnit unit = context.getUnitFromFile(file);
Token first = unit.getFirstToken();
Token last = unit.getLastToken();
FooNode root = unit.getRoot();
Token rootStart = root.tokenStart();
Token rootEnd = root.tokenEnd();
System.out.println(
" unit_text =\n " + unit.getText() +
" first_token = " + first.toString() +
"\n first_text = " + first.getText() +
"\n last_token = " + last.toString() +
"\n last_text = " + last.getText() +
"\n root_node = " + root.toString() +
"\n root_start = " + rootStart.toString() +
"\n root_end = " + rootEnd.toString() +
"\n token_count = " + unit.getTokenCount() +
"\n trivia_count = " + unit.getTriviaCount()
);
}
}
// ----- Test methods -----
/**
* Test the diagnostic manipulation
*/
private static void testDiagnostics() {
// Display header
header("Diagnostics");
// Prepare the working variables
AnalysisUnit unit;
Diagnostic[] diags;
try(
AnalysisContext context = AnalysisContext.create()
) {
// Create an unit from an unknown file
unit = context.getUnitFromFile("unknown");
diags = unit.getDiagnostics();
for(Diagnostic diag : diags) {
System.out.println(diag.toString());
}
// Create a unit with a synthax error
unit = context.getUnitFromBuffer("var identifier", "foo.txt");
diags = unit.getDiagnostics();
for(Diagnostic diag : diags) {
System.out.println(diag.toString());
}
}
// Display footer
footer("Diagnostics");
}
/**
* Test the unit file name getting
*/
private static void testUnitFileName() {
// Display the header
header("Unit Filename");
try(
AnalysisContext context = AnalysisContext.create()
) {
// Get the unit for the foo.txt file
AnalysisUnit unit = context.getUnitFromFile("foo.txt");
AnalysisUnit other = context.getUnitFromFile("foo.txt");
AnalysisUnit notSame =
context.getUnitFromBuffer("var identifier", "bar.txt");
System.out.println(
unit.toString() + " != " + other + " = " + (unit != other)
);
System.out.println(
unit.toString() + ".equals(" + other + ") = " +
unit.equals(other)
);
System.out.println(
unit.toString() + ".equals(" + notSame +
") = " + unit.equals(notSame)
);
System.out.println(
"Unit for the file " +
new File(unit.getFileName()).getName()
);
}
// Display the footer
footer("Unit Filename");
}
/**
* Test getting a unit from the unit provider.
*/
private static void testUnitProvider() {
header("Unit provider");
AnalysisContext ctx = AnalysisContext.create();
AnalysisUnit unit = ctx.getUnitFromProvider(
Text.create("pkg"),
AnalysisUnitKind.UNIT_BODY
);
System.out.println(
"pkg/body resolved to: "
+ new File(unit.getFileName()).getName()
);
footer("Unit provider");
}
/**
* Test the token manipulation
*/
private static void testTokens() {
// Display the header
header("Tokens");
try(
AnalysisContext context = AnalysisContext.create()
) {
// Test the trivia filtering
System.out.println("--- Token info without trivia");
tokensInfo("foo.txt", false);
System.out.println("--- Token info with trivia");
tokensInfo("foo.txt", true);
System.out.println("");
// Parse the foo.txt file to inspect its tokens
AnalysisUnit unit = context.getUnitFromFile("foo.txt");
Token first = unit.getFirstToken();
Token none = first.previous();
System.out.println("The NO_TOKEN = " + none.toString());
System.out.println("Text range with a NO_TOKEN = " +
Token.textRange(first, none));
// Test the token equivalence and equality
AnalysisUnit eqUnit = context.getUnitFromBuffer(
"null identifier example identifier example",
"foo.txt"
);
Token current = eqUnit.getFirstToken();
while(!(current.isNone())) {
Token other = current.next();
if(!current.isTrivia()) {
while(!(other.isNone())) {
if(current.isEquivalent(other))
System.out.println(
"Equivalent tokens : " +
current.toString() + " and " +
other.toString()
);
other = other.next();
}
}
current = current.next();
}
Token left = eqUnit.getFirstToken();
Token right = eqUnit.getFirstToken();
while(!(left.isNone() || right.isNone())) {
System.out.println(
left.toString() + " != " + right + " = " + (left != right)
);
System.out.println(
left.toString() + ".equals(" + right + ") = " +
left.equals(right)
);
left = left.next();
right = right.next();
}
}
// Display the footer
footer("Tokens");
}
/**
* Test the node manipulation
*/
private static void testNodes() {
// Display the header
header("Nodes");
try(
AnalysisContext context = AnalysisContext.create()
) {
AnalysisUnit unit = context.getUnitFromFile("foo.txt");
FooNode root = unit.getRoot();
System.out.println("Unit = " + unit.toString());
System.out.println("Unit root = " + root.toString());
System.out.println("Unit root kind = " + root.getClassName());
System.out.println(
"Unit root children count = " + root.getChildrenCount()
);
System.out.println(
"Unit root children = " + Arrays.toString(root.children())
);
System.out.println(
"Unit root tree dump = " + root.dumpTree()
);
System.out.println(
"Unit root is a list node = " + root.isListNode()
);
System.out.println(
"Unit root is a token node = " + root.isTokenNode()
);
System.out.println("Unit root image = " + root.getImage());
System.out.println("Unit root text = " + root.getText());
Sequence sequence = (Sequence) root;
System.out.println("Iterating on root sequence:");
for (FooNode node : sequence) {
System.out.println(" - " + node.toString());
}
FooNode[] items = sequence.pAllItems();
System.out.println(
"Root \"p_all_items\" = " + Arrays.toString(items)
);
Example[] examples = sequence.pExampleItems();
System.out.println(
"Root \"p_example_items\" = " + Arrays.toString(examples)
);
Var var = (Var) root.getChild(2);
System.out.println("Var (3rd child) = " + var.toString());
System.out.println("Var image = " + var.getImage());
System.out.println(
"Var fields = " + Arrays.toString(var.getFieldNames())
);
Sequence arg = var.fArg();
FooNode[] argItems = arg.pAllItems();
System.out.println("Var arg = " + arg.toString());
System.out.println("Var content = " + Arrays.toString(argItems));
Var var2 = (Var) root.getChild(2);
System.out.println("Node equality = " + var.equals(var2));
System.out.println(
"Node hash equality = " + (var.hashCode() == var2.hashCode())
);
System.out.println(
"Node 'p_all_items' member reference equality = " +
(root.getFieldDescription("p_all_items").memberRef ==
MemberReference.FOO_SEQUENCE_P_ALL_ITEMS)
);
// Test node equality and hash codes
FooNode e1 = root.pWithMd(true, false);
FooNode e2 = root.pWithMd(true, true);
FooNode e3 = root.pWithMd(false, true);
System.out.println("e1.equals(e2) = " + e1.equals(e2));
System.out.println("e1.equals(e3) = " + e1.equals(e3));
System.out.println(
"hash(e1) == hash(e2) = " + (e1.hashCode() == e2.hashCode())
);
System.out.println(
"hash(e1) == hash(e3) = " + (e1.hashCode() == e3.hashCode())
);
// Test empty struct nullity
EmptyStruct emptyStruct = root.pNewEmptyStruct();
System.out.println(
"empty struct is_struct_null() = " +
root.pIsStructNull(emptyStruct)
);
System.out.println(
"null empty struct is_struct_null() = " +
root.pIsStructNull(EmptyStruct.NONE)
);
}
// Display the footer
footer("Nodes");
}
/**
* Test the parent navigation
*/
private static void testParent() {
// Display the header
header("Parent(s)");
try(
AnalysisContext context = AnalysisContext.create()
) {
AnalysisUnit unit = context.getUnitFromFile("foo.txt");
FooNode root = unit.getRoot();
// Visit all the nodes and display their parents
List<FooNode> visitList = new LinkedList<>();
visitList.add(root);
while(!visitList.isEmpty()) {
FooNode current = visitList.remove(0);
FooNode parent = current.parent();
System.out.println(
"Node " + current.toString() + " | Parents :"
);
while(!parent.isNone()) {
System.out.println(" " + parent.toString());
parent = parent.parent();
}
for(FooNode child : current.children()) {
if(!child.isNone()) visitList.add(child);
}
System.out.println("");
}
System.out.println(
"proposition (for all node n. " +
"for fields fields f of n. parent f = n) is true"
);
}
// Display the footer
footer("Parent(s)");
}
/**
* Test the sibling navigation
*/
private static void testSiblings() {
// Display the header
header("Siblings");
try(
AnalysisContext context = AnalysisContext.create()
) {
AnalysisUnit unit = context.getUnitFromFile("foo.txt");
Sequence root = (Sequence) unit.getRoot();
FooNode[] items = root.pAllItems();
// Visit all the items and display their siblings
for(FooNode item : items) {
System.out.println(
item.toString() +
" | previous = " +
item.previousSibling() +
" | next = " +
item.nextSibling()
);
}
}
// Display the footer
footer("Siblings");
}
private static void testArrays() {
// Display the header
header("Arrays");
try(
AnalysisContext context = AnalysisContext.create()
) {
AnalysisUnit unit = context.getUnitFromBuffer(
"example null null example var (example null example)",
"foo.txt"
);
Sequence root = (Sequence) unit.getRoot();
// Visit the sequence
FooNode[] items = root.pAllItems();
System.out.println("Sequence size = " + items.length);
System.out.println("Sequence content :");
for(int i = 0 ; i < items.length ; i++) {
FooNode item = items[i];
System.out.println(" " + item.toString());
}
FooNode[] eqItems = root.pAllItems();
System.out.println("Array equality = " + items.equals(eqItems));
// Test an array of primitive type (integer)
int[] intArray = root.pGetIntArray();
System.out.println("Integer array size = " + intArray.length);
System.out.println(
"Integer array size (from property) = " +
root.pArrayLen(intArray)
);
System.out.println("Integer array content :");
for (int i = 0; i < intArray.length; i++) {
System.out.println(" " + intArray[i]);
}
// Test a property error raised by an array typed property
try {
FooNode[] invalid = root.pArrayPropError();
System.out.println("No exception raised by 'array_prop_error'");
} catch (LangkitException exc) {
System.out.println(
"Expected exception: " + exc.kind + " - " +
exc.getMessage() + (
exc.nativeStackTrace() != "" ?
" (Stack trace available)" :
" (NO STACK TRACE)"
)
);
}
}
// Display the footer
footer("Arrays");
}
private static void testIterators() {
header("Iterators");
try (
AnalysisContext context = AnalysisContext.create();
) {
AnalysisUnit unit = context.getUnitFromBuffer(
"my_ident",
"foo.txt"
);
Sequence root = (Sequence) unit.getRoot();
// Get and display an integer iterator
System.out.println("--- Integer iterator");
try (
IntegerIterator intIterator = root.pIterInt()
) {
while (intIterator.hasNext()) {
int i = intIterator.next();
System.out.println(" " + i);
}
}
// Get and display an entity iterator
System.out.println("--- Entity iterator");
try (
FooNodeIterator entityIterator = root.pIterEntity()
) {
while (entityIterator.hasNext()) {
FooNode i = entityIterator.next();
System.out.println(" " + i);
}
}
// Get and display a struct iterator (ensure handing of null values)
System.out.println("--- Struct iterator");
try (
SomeStructIterator structIterator = root.pIterStruct()
) {
while (structIterator.hasNext()) {
SomeStruct i = structIterator.next();
System.out.println(
" SomeStruct(examples=" +
Arrays.toString(i.examples) +
")"
);
}
}
// Check passing an iterator as property parameter
System.out.println("--- Iterator as property parameter");
try (
IntegerIterator iterator = root.pIterInt();
IntegerIterator same = root.pIterIntId(iterator)
) {
System.out.println(
"Identity iterator is the same = " + iterator.equals(same)
);
}
}
footer("Iterators");
}
private static void testSymbols() {
// Display the header
header("Symbols");
try(
AnalysisContext context = AnalysisContext.create()
) {
AnalysisUnit unit = context.getUnitFromBuffer(
"my_ident",
"foo.txt"
);
Sequence root = (Sequence) unit.getRoot();
Ident ident = (Ident) root.getChild(0);
String[] sources = {
"my_ident",
"MY_IDENT",
"no_such_symbol",
"invalid_symbol0"
};
for(String source : sources) {
try {
Symbol symbol = ident.pSym(Symbol.create(source));
System.out.println(
"source = " +
source +
" | symbol = " +
symbol.text
);
} catch(SymbolException e) {
System.out.println(
"source = " +
source +
" | exception = " +
e.getMessage()
);
}
}
}
// Display the footer
footer("Symbols");
}
/**
* Test the langkit characters
*/
private static void testCharacter() {
// Display the header
header("Character");
try(
AnalysisContext context = AnalysisContext.create()
) {
AnalysisUnit unit = context.getUnitFromBuffer(
"my_ident",
"foo.txt"
);
FooNode root = unit.getRoot();
// Test the character manipulation
Reflection.Field getAField = root.getFieldDescription("p_get_a");
Reflection.Field getEacuteField =
root.getFieldDescription("p_get_eacute");
Char c1 = root.pGetA(
(Char) (
(Reflection.Param) getAField.params.get(0)
).defaultValue.get()
);
Char c2 = root.pGetEacute(
(Char) (
(Reflection.Param) getEacuteField.params.get(0)
).defaultValue.get()
);
Char cIdent = root.pIdentity(Char.create('\u00e9'));
System.out.println("The 'a' char = " + c1.toString());
System.out.println("The eacute char = " + String.valueOf(c2.value));
System.out.println(
"Identity of eacute = " + String.valueOf(cIdent.value)
);
}
// Display the footer
footer("Character");
}
/**
* Test the langkit strings
*/
private static void testString() {
// Display the header
header("String");
try(
AnalysisContext context = AnalysisContext.create()
) {
AnalysisUnit unit = context.getUnitFromBuffer(
"my_ident",
"foo.txt"
);
FooNode root = unit.getRoot();
// Try creating and passing strings
String source = "Hello!";
String emptySource = "";
String identity = root.pGetStr(source);
String emptyIdentity = root.pGetStr(emptySource);
System.out.println(
"Source = \"" +
source +
"\" | Identity = \"" +
identity +
"\""
);
System.out.println(
"Empty source = \"" +
emptySource +
"\" | Empty identity = \"" +
emptyIdentity +
"\""
);
}
// Display the footer
footer("String");
}
/**
* Test the langkit big int
*/
private static void testBigInt() {
// Display the header
header("Big Integer");
try(
AnalysisContext context = AnalysisContext.create()
) {
AnalysisUnit unit = context.getUnitFromFile("foo.txt");
FooNode root = unit.getRoot();
// Test the big integer manipulation
BigInteger ft = new BigInteger("42");
BigInteger big = new BigInteger(
"100000000000000000000000000000000000000000000000" +
"00000000000000000000000000000000000000000000000000000"
);
BigInteger ftDouble = root.pIntDouble(ft);
BigInteger bigDouble = root.pIntDouble(big);
System.out.println(
"Double of " +
ft.toString() +
" = " +
ftDouble.toString()
);
System.out.println(
"Double of 10**100 = " +
bigDouble.toString()
);
}
// Display the footer
footer("Big Integer");
}
private static void testStruct() {
// Display the header
header("Struct");
try(
AnalysisContext context = AnalysisContext.create()
) {
AnalysisUnit unit = context.getUnitFromFile("foo.txt");
FooNode root = unit.getRoot();
// Test a struct with an entity
FooNodeStruct falseNodeStruct = root.pMe(false);
FooNodeStruct trueNodeStruct = root.pMe(true);
FooNode falseNode = root.pGetNode(falseNodeStruct);
FooNode trueNode = root.pGetNode(trueNodeStruct);
System.out.println("me false = " + falseNode);
System.out.println("me true = " + trueNode);
System.out.println(
"falseNode.equals(falseNodeStruct.node) = " +
falseNode.equals(falseNodeStruct.node)
);
// Test a struct with a bare node and verify that it has been
// wrapped.
BareNodeStruct bareNodeStruct = root.pMyNode();
FooNode bareNode = bareNodeStruct.bareNode;
System.out.println("root.pMyNode().bareNode = " + bareNode);
// Test passing this structure as a property parameter
Char aChar = bareNodeStruct.aChar;
Char otherChar = root.pGetChar(bareNodeStruct);
System.out.println(
"aChar.equals(otherChar) = " + aChar.equals(otherChar)
);
}
// Display the footer
footer("Struct");
}
private static void testEventHandlers() {
header("Event handlers");
// Local function to test event handlers
BiConsumer<
EventHandler.UnitRequestedCallback,
EventHandler.UnitParsedCallback
> testHandler = (unitRequestedCallback, unitParsedCallback) -> {
// Create the context with the event handler and create units
try(
EventHandler eventHandler = EventHandler.create(
unitRequestedCallback,
unitParsedCallback
);
AnalysisContext context = AnalysisContext.create(
null,
null,
null,
eventHandler,
true,
8
);
) {
// Parse the unit twice to test the "reparsed" callback param
AnalysisUnit unit = context.getUnitFromBuffer(
"example",
"example"
);
unit = context.getUnitFromBuffer("example example", "example");
// Call the property to trigger "unit requested" callback
FooNode root = unit.getRoot();
root.pTriggerUnitRequested(
Symbol.create("foo_a"),
false,
false
);
root.pTriggerUnitRequested(
Symbol.create("foo_b"),
true,
false
);
root.pTriggerUnitRequested(
Symbol.create("foo_c"),
true,
true
);
root.pTriggerUnitRequested(
Symbol.create("foo_d"),
false,
true
);
}
};
// Create the callback functions
EventHandler.UnitRequestedCallback unitRequestedCallback = (
AnalysisContext context,
String name,
AnalysisUnit from,
boolean found,
boolean isNotFoundError
) -> {
System.out.println("--- Unit requested callback");
System.out.println("name: " + name);
System.out.println("from: " + from);
System.out.println("found: " + found);
System.out.println("is_not_found_error: " + isNotFoundError);
System.out.println();
};
EventHandler.UnitParsedCallback unitParsedCallback = (
AnalysisContext context,
AnalysisUnit unit,
boolean reparsed
) -> {
System.out.println("--- Unit parsed callback");
System.out.println("unit: " + unit);
System.out.println("reparsed: " + reparsed);
System.out.println();
};
System.out.println("=== Non null callbacks ===");
testHandler.accept(
unitRequestedCallback,
unitParsedCallback
);
System.out.println("=== Null callbacks ===");
testHandler.accept(null, null);
System.out.println("=== Unclosed event handler ===");
EventHandler dontClose = EventHandler.create(
unitRequestedCallback,
unitParsedCallback
);
footer("Event handlers");
}
private static class FooVisitor extends DefaultVisitor<Integer> {
protected Function<FooNode, Integer> createDefaultBehavior() {
return (node) -> {
System.out.println("Visiting " + node.getImage());
Integer count = 1;
for (var c : node.children()) {
if (!c.isNone()) count += c.accept(this);
}
return count;
};
}
}
private static void testDefaultVisitor() {
header("Default visitor");
FooVisitor visitor = new FooVisitor();
try(
AnalysisContext context = AnalysisContext.create()
) {
AnalysisUnit unit = context.getUnitFromFile("foo.txt");
Integer count = unit.getRoot().accept(visitor);
System.out.println("Visited " + String.valueOf(count) + " nodes");
}
footer("Default visitor");
}
/**
* Run the Java tests one by one
*
* @param args The arguments for the tests running
*/
public static void main(String[] args) {
// Run the tests
System.out.println("===== Start the Java tests =====\n");
testDiagnostics();
testUnitFileName();
testUnitProvider();
testTokens();
testNodes();
testParent();
testSiblings();
testArrays();
testIterators();
testSymbols();
testCharacter();
testString();
testBigInt();
testStruct();
testEventHandlers();
testDefaultVisitor();
System.out.println("===== End of the Java tests =====");
}
}