Skip to content

Commit 7e8a907

Browse files
committed
added test case for new ASTUtil methods to iterate through type hierarchies
1 parent 215b255 commit 7e8a907

File tree

1 file changed

+286
-0
lines changed
  • headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test

1 file changed

+286
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Broadcom, Inc.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java.utils.test;
12+
13+
import static org.junit.Assert.assertEquals;
14+
import static org.junit.Assert.assertFalse;
15+
import static org.junit.Assert.assertNotNull;
16+
import static org.junit.Assert.assertTrue;
17+
18+
import java.io.IOException;
19+
import java.nio.charset.StandardCharsets;
20+
import java.nio.file.Files;
21+
import java.nio.file.Path;
22+
import java.nio.file.Paths;
23+
import java.util.ArrayList;
24+
import java.util.HashSet;
25+
import java.util.Iterator;
26+
import java.util.List;
27+
import java.util.Set;
28+
import java.util.function.Consumer;
29+
30+
import org.eclipse.jdt.core.dom.ASTVisitor;
31+
import org.eclipse.jdt.core.dom.CompilationUnit;
32+
import org.eclipse.jdt.core.dom.FileASTRequestor;
33+
import org.eclipse.jdt.core.dom.ITypeBinding;
34+
import org.eclipse.jdt.core.dom.TypeDeclaration;
35+
import org.junit.jupiter.api.AfterEach;
36+
import org.junit.jupiter.api.BeforeEach;
37+
import org.junit.jupiter.api.Test;
38+
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
39+
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
40+
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJava;
41+
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
42+
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
43+
44+
public class ASTUtilsTest {
45+
46+
private List<Path> createdFiles = new ArrayList<>();
47+
48+
private final String projectName = "test-spring-validations";
49+
50+
private MavenJavaProject project;
51+
52+
private Path mySimpleMain;
53+
private Path myComponent;
54+
55+
56+
@BeforeEach
57+
void setup() throws Exception {
58+
this.project = ProjectsHarness.INSTANCE.mavenProject(projectName);
59+
createTestFiles();
60+
}
61+
62+
@AfterEach
63+
void tearDown() {
64+
clearTestFiles();
65+
}
66+
67+
@Test
68+
void testTypeHierarchyIteratorSimpleClass() throws Exception {
69+
runTestsAgainstTypeDeclaration(mySimpleMain, (type) -> {
70+
Iterator<ITypeBinding> iter = ASTUtils.getSuperTypesIterator(type.resolveBinding());
71+
assertNotNull(iter);
72+
73+
assertEquals("test.MySimpleMain", iter.next().getQualifiedName());
74+
assertEquals("java.lang.Object", iter.next().getQualifiedName());
75+
assertFalse(iter.hasNext());
76+
});
77+
}
78+
79+
@Test
80+
void testSupertypesForSimpleClass() throws Exception {
81+
runTestsAgainstTypeDeclaration(mySimpleMain, (type) -> {
82+
Set<String> supertypes = new HashSet<>();
83+
ASTUtils.findSupertypes(type.resolveBinding(), supertypes);
84+
85+
assertEquals(1, supertypes.size());
86+
assertTrue(supertypes.contains("java.lang.Object"));
87+
});
88+
}
89+
90+
@Test
91+
void testIsAnyTypeInHierarchyForSimpleClass() throws Exception {
92+
runTestsAgainstTypeDeclaration(mySimpleMain, (type) -> {
93+
assertTrue(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("java.lang.Object")));
94+
assertTrue(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("java.lang.Object", "java.io.Serializable")));
95+
assertFalse(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("java.io.Serializable")));
96+
assertFalse(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of()));
97+
98+
assertTrue(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("test.MySimpleMain")));
99+
});
100+
}
101+
102+
@Test
103+
void testAreAllTypesInHierarchyForSimpleClass() throws Exception {
104+
runTestsAgainstTypeDeclaration(mySimpleMain, (type) -> {
105+
assertTrue(ASTUtils.areAllTypesInHierarchy(type.resolveBinding(), List.of("java.lang.Object")));
106+
assertFalse(ASTUtils.areAllTypesInHierarchy(type.resolveBinding(), List.of("java.lang.Object", "java.io.Serializable")));
107+
assertTrue(ASTUtils.areAllTypesInHierarchy(type.resolveBinding(), List.of()));
108+
109+
assertTrue(ASTUtils.areAllTypesInHierarchy(type.resolveBinding(), List.of("test.MySimpleMain")));
110+
});
111+
}
112+
113+
@Test
114+
void testTypeHierarchyIteratorWithSuperclassesAndInterfaces() throws Exception {
115+
runTestsAgainstTypeDeclaration(myComponent, (type) -> {
116+
Iterator<ITypeBinding> iter = ASTUtils.getSuperTypesIterator(type.resolveBinding());
117+
assertNotNull(iter);
118+
119+
assertEquals("test.MyComponent", iter.next().getQualifiedName());
120+
assertEquals("test.MyInterface", iter.next().getQualifiedName());
121+
assertEquals("test.MySuperclass", iter.next().getQualifiedName());
122+
assertEquals("test.MySuperInterface", iter.next().getQualifiedName());
123+
assertEquals("test.MySuperclassInterface", iter.next().getQualifiedName());
124+
assertEquals("java.lang.Object", iter.next().getQualifiedName());
125+
assertFalse(iter.hasNext());
126+
});
127+
}
128+
129+
@Test
130+
void testTypeHierarchyIteratorWithFullyQualifiedTypeNames() throws Exception {
131+
runTestsAgainstTypeDeclaration(myComponent, (type) -> {
132+
Iterator<String> iter = ASTUtils.getSuperTypesFqNamesIterator(type.resolveBinding());
133+
assertNotNull(iter);
134+
135+
assertEquals("test.MyComponent", iter.next());
136+
assertEquals("test.MyInterface", iter.next());
137+
assertEquals("test.MySuperclass", iter.next());
138+
assertEquals("test.MySuperInterface", iter.next());
139+
assertEquals("test.MySuperclassInterface", iter.next());
140+
assertEquals("java.lang.Object", iter.next());
141+
assertFalse(iter.hasNext());
142+
});
143+
}
144+
145+
@Test
146+
void testCircularTypeHierarchy() throws Exception {
147+
createFile(projectName, "test", "Start.java", """
148+
package test;
149+
public class Start extends Third {
150+
}
151+
""");
152+
153+
createFile(projectName, "test", "Second.java", """
154+
package test;
155+
public class Second extends Start {
156+
}
157+
""");
158+
159+
Path third = createFile(projectName, "test", "Third.java", """
160+
package test;
161+
public class Third extends Second {
162+
}
163+
""");
164+
165+
runTestsAgainstTypeDeclaration(third, (type) -> {
166+
assertFalse(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("java.io.Serializable")));
167+
assertTrue(ASTUtils.isAnyTypeInHierarchy(type.resolveBinding(), List.of("test.Start")));
168+
assertTrue(ASTUtils.areAllTypesInHierarchy(type.resolveBinding(), List.of("test.Start", "test.Second", "test.Third")));
169+
});
170+
171+
}
172+
173+
@Test
174+
void testInterfaceAppearsMultipleTimesInHierarchy() throws Exception {
175+
createFile(projectName, "test", "Start.java", """
176+
package test;
177+
public class Start implements TestInterface {
178+
}
179+
""");
180+
181+
Path second = createFile(projectName, "test", "Second.java", """
182+
package test;
183+
public class Second extends Start implements TestInterface {
184+
}
185+
""");
186+
187+
createFile(projectName, "test", "TestInterface.java", """
188+
package test;
189+
public interface TestInterface {
190+
}
191+
""");
192+
193+
runTestsAgainstTypeDeclaration(second, (type) -> {
194+
Iterator<String> iter = ASTUtils.getSuperTypesFqNamesIterator(type.resolveBinding());
195+
assertNotNull(iter);
196+
197+
assertEquals("test.Second", iter.next());
198+
assertEquals("test.TestInterface", iter.next());
199+
assertEquals("test.Start", iter.next());
200+
// assertEquals("test.TestInterface", iter.next());
201+
assertEquals("java.lang.Object", iter.next());
202+
assertFalse(iter.hasNext());
203+
});
204+
205+
}
206+
207+
private void runTestsAgainstTypeDeclaration(Path file, Consumer<TypeDeclaration> test) throws Exception {
208+
SpringIndexerJava.createParser(this.project, new AnnotationHierarchies(), true).createASTs(new String[] { file.toFile().toString() }, null, new String[0], new FileASTRequestor() {
209+
@Override
210+
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
211+
cu.accept(new ASTVisitor() {
212+
213+
@Override
214+
public boolean visit(TypeDeclaration type) {
215+
test.accept(type);
216+
return super.visit(type);
217+
}
218+
219+
});
220+
}
221+
}, null);
222+
}
223+
224+
private void createTestFiles() throws Exception {
225+
this.mySimpleMain = createFile(projectName, "test", "MySimpleMain.java", """
226+
package test;
227+
public class MySimpleMain {
228+
}
229+
""");
230+
231+
createFile(projectName, "test", "MySuperclass.java", """
232+
package test;
233+
public class MySuperclass implements MySuperclassInterface {
234+
}
235+
""");
236+
237+
createFile(projectName, "test", "MySuperclassInterface.java", """
238+
package test;
239+
public interface MySuperclassInterface {
240+
}
241+
""");
242+
243+
createFile(projectName, "test", "MyInterface.java", """
244+
package test;
245+
public interface MyInterface extends MySuperInterface {
246+
}
247+
""");
248+
249+
createFile(projectName, "test", "MySuperInterface.java", """
250+
package test;
251+
public interface MySuperInterface {
252+
}
253+
""");
254+
255+
this.myComponent = createFile(projectName, "test", "MyComponent.java", """
256+
package test;
257+
import org.springframework.boot.autoconfigure.SpringBootApplication;
258+
259+
@SpringBootApplication
260+
public class MyComponent extends MySuperclass implements MyInterface {
261+
}
262+
""");
263+
}
264+
265+
private Path createFile(String projectName, String packageName, String name, String content) throws Exception {
266+
Path projectPath = Paths.get(getClass().getResource("/test-projects/" + projectName).toURI());
267+
Path filePath = projectPath.resolve("src/main/java").resolve(packageName.replace('.', '/')).resolve(name);
268+
Files.createDirectories(filePath.getParent());
269+
createdFiles.add(Files.createFile(filePath));
270+
Files.write(filePath, content.getBytes(StandardCharsets.UTF_8));
271+
return filePath;
272+
}
273+
274+
private void clearTestFiles() {
275+
for (Iterator<Path> itr = createdFiles.iterator(); itr.hasNext();) {
276+
Path path = itr.next();
277+
try {
278+
Files.delete(path);
279+
itr.remove();
280+
} catch (IOException e) {
281+
e.printStackTrace();
282+
}
283+
}
284+
}
285+
286+
}

0 commit comments

Comments
 (0)