added test case for new ASTUtil methods to iterate through type hierarchies

Signed-off-by: aboyko <alex.boyko@broadcom.com>
This commit is contained in:
Martin Lippert
2025-04-01 12:30:59 +02:00
committed by aboyko
parent d86e43f898
commit 5a0ac21324

View File

@@ -0,0 +1,286 @@
/*******************************************************************************
* Copyright (c) 2025 Broadcom, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FileASTRequestor;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
public class ASTUtilsTest {
private List<Path> createdFiles = new ArrayList<>();
private final String projectName = "test-spring-validations";
private MavenJavaProject project;
private Path mySimpleMain;
private Path myComponent;
@BeforeEach
void setup() throws Exception {
this.project = ProjectsHarness.INSTANCE.mavenProject(projectName);
createTestFiles();
}
@AfterEach
void tearDown() {
clearTestFiles();
}
@Test
void testTypeHierarchyIteratorSimpleClass() throws Exception {
runTestsAgainstTypeDeclaration(mySimpleMain, (type) -> {
Iterator<ITypeBinding> iter = ASTUtils.getSuperTypesIterator(type.resolveBinding());
assertNotNull(iter);
assertEquals("test.MySimpleMain", iter.next().getQualifiedName());
assertEquals("java.lang.Object", iter.next().getQualifiedName());
assertFalse(iter.hasNext());
});
}
@Test
void testSupertypesForSimpleClass() throws Exception {
runTestsAgainstTypeDeclaration(mySimpleMain, (type) -> {
Set<String> supertypes = new HashSet<>();
ASTUtils.findSupertypes(type.resolveBinding(), supertypes);
assertEquals(1, supertypes.size());
assertTrue(supertypes.contains("java.lang.Object"));
});
}
@Test
void testIsAnyTypeInHierarchyForSimpleClass() throws Exception {
runTestsAgainstTypeDeclaration(mySimpleMain, (type) -> {
assertTrue(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("java.lang.Object")));
assertTrue(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("java.lang.Object", "java.io.Serializable")));
assertFalse(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("java.io.Serializable")));
assertFalse(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of()));
assertTrue(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("test.MySimpleMain")));
});
}
@Test
void testAreAllTypesInHierarchyForSimpleClass() throws Exception {
runTestsAgainstTypeDeclaration(mySimpleMain, (type) -> {
assertTrue(ASTUtils.areAllTypesInHierarchy(type.resolveBinding(), List.of("java.lang.Object")));
assertFalse(ASTUtils.areAllTypesInHierarchy(type.resolveBinding(), List.of("java.lang.Object", "java.io.Serializable")));
assertTrue(ASTUtils.areAllTypesInHierarchy(type.resolveBinding(), List.of()));
assertTrue(ASTUtils.areAllTypesInHierarchy(type.resolveBinding(), List.of("test.MySimpleMain")));
});
}
@Test
void testTypeHierarchyIteratorWithSuperclassesAndInterfaces() throws Exception {
runTestsAgainstTypeDeclaration(myComponent, (type) -> {
Iterator<ITypeBinding> iter = ASTUtils.getSuperTypesIterator(type.resolveBinding());
assertNotNull(iter);
assertEquals("test.MyComponent", iter.next().getQualifiedName());
assertEquals("test.MyInterface", iter.next().getQualifiedName());
assertEquals("test.MySuperclass", iter.next().getQualifiedName());
assertEquals("test.MySuperInterface", iter.next().getQualifiedName());
assertEquals("test.MySuperclassInterface", iter.next().getQualifiedName());
assertEquals("java.lang.Object", iter.next().getQualifiedName());
assertFalse(iter.hasNext());
});
}
@Test
void testTypeHierarchyIteratorWithFullyQualifiedTypeNames() throws Exception {
runTestsAgainstTypeDeclaration(myComponent, (type) -> {
Iterator<String> iter = ASTUtils.getSuperTypesFqNamesIterator(type.resolveBinding());
assertNotNull(iter);
assertEquals("test.MyComponent", iter.next());
assertEquals("test.MyInterface", iter.next());
assertEquals("test.MySuperclass", iter.next());
assertEquals("test.MySuperInterface", iter.next());
assertEquals("test.MySuperclassInterface", iter.next());
assertEquals("java.lang.Object", iter.next());
assertFalse(iter.hasNext());
});
}
@Test
void testCircularTypeHierarchy() throws Exception {
createFile(projectName, "test", "Start.java", """
package test;
public class Start extends Third {
}
""");
createFile(projectName, "test", "Second.java", """
package test;
public class Second extends Start {
}
""");
Path third = createFile(projectName, "test", "Third.java", """
package test;
public class Third extends Second {
}
""");
runTestsAgainstTypeDeclaration(third, (type) -> {
assertFalse(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("java.io.Serializable")));
assertTrue(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("test.Start")));
assertTrue(ASTUtils.areAllTypesInHierarchy(type.resolveBinding(), List.of("test.Start", "test.Second", "test.Third")));
});
}
@Test
void testInterfaceAppearsMultipleTimesInHierarchy() throws Exception {
createFile(projectName, "test", "Start.java", """
package test;
public class Start implements TestInterface {
}
""");
Path second = createFile(projectName, "test", "Second.java", """
package test;
public class Second extends Start implements TestInterface {
}
""");
createFile(projectName, "test", "TestInterface.java", """
package test;
public interface TestInterface {
}
""");
runTestsAgainstTypeDeclaration(second, (type) -> {
Iterator<String> iter = ASTUtils.getSuperTypesFqNamesIterator(type.resolveBinding());
assertNotNull(iter);
assertEquals("test.Second", iter.next());
assertEquals("test.TestInterface", iter.next());
assertEquals("test.Start", iter.next());
// assertEquals("test.TestInterface", iter.next());
assertEquals("java.lang.Object", iter.next());
assertFalse(iter.hasNext());
});
}
private void runTestsAgainstTypeDeclaration(Path file, Consumer<TypeDeclaration> test) throws Exception {
SpringIndexerJava.createParser(this.project, new AnnotationHierarchies(), true).createASTs(new String[] { file.toFile().toString() }, null, new String[0], new FileASTRequestor() {
@Override
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
cu.accept(new ASTVisitor() {
@Override
public boolean visit(TypeDeclaration type) {
test.accept(type);
return super.visit(type);
}
});
}
}, null);
}
private void createTestFiles() throws Exception {
this.mySimpleMain = createFile(projectName, "test", "MySimpleMain.java", """
package test;
public class MySimpleMain {
}
""");
createFile(projectName, "test", "MySuperclass.java", """
package test;
public class MySuperclass implements MySuperclassInterface {
}
""");
createFile(projectName, "test", "MySuperclassInterface.java", """
package test;
public interface MySuperclassInterface {
}
""");
createFile(projectName, "test", "MyInterface.java", """
package test;
public interface MyInterface extends MySuperInterface {
}
""");
createFile(projectName, "test", "MySuperInterface.java", """
package test;
public interface MySuperInterface {
}
""");
this.myComponent = createFile(projectName, "test", "MyComponent.java", """
package test;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyComponent extends MySuperclass implements MyInterface {
}
""");
}
private Path createFile(String projectName, String packageName, String name, String content) throws Exception {
Path projectPath = Paths.get(getClass().getResource("/test-projects/" + projectName).toURI());
Path filePath = projectPath.resolve("src/main/java").resolve(packageName.replace('.', '/')).resolve(name);
Files.createDirectories(filePath.getParent());
createdFiles.add(Files.createFile(filePath));
Files.write(filePath, content.getBytes(StandardCharsets.UTF_8));
return filePath;
}
private void clearTestFiles() {
for (Iterator<Path> itr = createdFiles.iterator(); itr.hasNext();) {
Path path = itr.next();
try {
Files.delete(path);
itr.remove();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}