Skip to content

Commit 4db9844

Browse files
committed
Add unit tests for TypeInferrer
These new tests offer much more comprehensive and granular coverage than the existing type inference tests in `DelphiSymbolTableExecutor`.
1 parent a885d0d commit 4db9844

File tree

1 file changed

+334
-0
lines changed

1 file changed

+334
-0
lines changed
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
/*
2+
* Sonar Delphi Plugin
3+
* Copyright (C) 2024 Integrated Application Development
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
18+
*/
19+
package au.com.integradev.delphi.symbol.resolve;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
import static org.mockito.Mockito.doAnswer;
23+
import static org.mockito.Mockito.doReturn;
24+
import static org.mockito.Mockito.spy;
25+
26+
import au.com.integradev.delphi.antlr.DelphiLexer;
27+
import au.com.integradev.delphi.antlr.ast.node.ArrayConstructorNodeImpl;
28+
import au.com.integradev.delphi.antlr.ast.node.CommonDelphiNodeImpl;
29+
import au.com.integradev.delphi.antlr.ast.node.DelphiNodeImpl;
30+
import au.com.integradev.delphi.antlr.ast.node.IntegerLiteralNodeImpl;
31+
import au.com.integradev.delphi.antlr.ast.node.ParenthesizedExpressionNodeImpl;
32+
import au.com.integradev.delphi.antlr.ast.node.PrimaryExpressionNodeImpl;
33+
import au.com.integradev.delphi.type.factory.ArrayOption;
34+
import au.com.integradev.delphi.type.factory.TypeFactoryImpl;
35+
import au.com.integradev.delphi.utils.types.TypeFactoryUtils;
36+
import java.util.Collections;
37+
import java.util.Set;
38+
import java.util.stream.Stream;
39+
import org.antlr.runtime.CommonToken;
40+
import org.junit.jupiter.api.Test;
41+
import org.junit.jupiter.api.extension.ExtensionContext;
42+
import org.junit.jupiter.params.ParameterizedTest;
43+
import org.junit.jupiter.params.provider.Arguments;
44+
import org.junit.jupiter.params.provider.ArgumentsProvider;
45+
import org.junit.jupiter.params.provider.ArgumentsSource;
46+
import org.sonar.plugins.communitydelphi.api.ast.CommonDelphiNode;
47+
import org.sonar.plugins.communitydelphi.api.ast.ExpressionNode;
48+
import org.sonar.plugins.communitydelphi.api.type.IntrinsicType;
49+
import org.sonar.plugins.communitydelphi.api.type.Type;
50+
import org.sonar.plugins.communitydelphi.api.type.TypeFactory;
51+
52+
class TypeInferrerTest {
53+
private static final TypeFactoryImpl TYPE_FACTORY =
54+
(TypeFactoryImpl) TypeFactoryUtils.defaultFactory();
55+
56+
static class ArrayConstructorArgumentsProvider implements ArgumentsProvider {
57+
@Override
58+
public Stream<Arguments> provideArguments(ExtensionContext context) {
59+
return Stream.of(
60+
Arguments.of(arrayConstructor(), dynamicArrayType(TypeFactory.voidType())),
61+
Arguments.of(
62+
arrayConstructor(integerLiteral("127")), dynamicArrayType(IntrinsicType.INTEGER)),
63+
Arguments.of(
64+
arrayConstructor(integerLiteral("127"), integerLiteral("128")),
65+
dynamicArrayType(IntrinsicType.INTEGER)),
66+
Arguments.of(
67+
arrayConstructor(integerLiteral("127"), integerLiteral("128"), integerLiteral("255")),
68+
dynamicArrayType(IntrinsicType.INTEGER)),
69+
Arguments.of(
70+
arrayConstructor(
71+
integerLiteral("127"),
72+
integerLiteral("128"),
73+
integerLiteral("255"),
74+
integerLiteral("256")),
75+
dynamicArrayType(IntrinsicType.INTEGER)),
76+
Arguments.of(
77+
arrayConstructor(
78+
integerLiteral("127"),
79+
integerLiteral("128"),
80+
integerLiteral("255"),
81+
integerLiteral("256"),
82+
integerLiteral("32767")),
83+
dynamicArrayType(IntrinsicType.INTEGER)),
84+
Arguments.of(
85+
arrayConstructor(
86+
integerLiteral("127"),
87+
integerLiteral("128"),
88+
integerLiteral("255"),
89+
integerLiteral("256"),
90+
integerLiteral("32767"),
91+
integerLiteral("32768")),
92+
dynamicArrayType(IntrinsicType.INTEGER)),
93+
Arguments.of(
94+
arrayConstructor(
95+
integerLiteral("127"),
96+
integerLiteral("128"),
97+
integerLiteral("255"),
98+
integerLiteral("256"),
99+
integerLiteral("32767"),
100+
integerLiteral("32768"),
101+
integerLiteral("2147483647")),
102+
dynamicArrayType(IntrinsicType.INTEGER)),
103+
Arguments.of(
104+
arrayConstructor(
105+
integerLiteral("127"),
106+
integerLiteral("128"),
107+
integerLiteral("255"),
108+
integerLiteral("256"),
109+
integerLiteral("32767"),
110+
integerLiteral("32768"),
111+
integerLiteral("2147483647"),
112+
integerLiteral("2147483648")),
113+
dynamicArrayType(IntrinsicType.CARDINAL)),
114+
Arguments.of(
115+
arrayConstructor(
116+
integerLiteral("127"),
117+
integerLiteral("128"),
118+
integerLiteral("255"),
119+
integerLiteral("256"),
120+
integerLiteral("32767"),
121+
integerLiteral("32768"),
122+
integerLiteral("2147483647"),
123+
integerLiteral("2147483648"),
124+
integerLiteral("4294967295")),
125+
dynamicArrayType(IntrinsicType.CARDINAL)),
126+
Arguments.of(
127+
arrayConstructor(
128+
integerLiteral("127"),
129+
integerLiteral("128"),
130+
integerLiteral("255"),
131+
integerLiteral("256"),
132+
integerLiteral("32767"),
133+
integerLiteral("32768"),
134+
integerLiteral("2147483647"),
135+
integerLiteral("2147483648"),
136+
integerLiteral("4294967295"),
137+
integerLiteral("4294967296")),
138+
dynamicArrayType(IntrinsicType.INT64)),
139+
Arguments.of(
140+
arrayConstructor(
141+
integerLiteral("127"),
142+
integerLiteral("128"),
143+
integerLiteral("255"),
144+
integerLiteral("256"),
145+
integerLiteral("32767"),
146+
integerLiteral("32768"),
147+
integerLiteral("2147483647"),
148+
integerLiteral("2147483648"),
149+
integerLiteral("4294967295"),
150+
integerLiteral("4294967296"),
151+
integerLiteral("9223372036854775807")),
152+
dynamicArrayType(IntrinsicType.INT64)),
153+
Arguments.of(
154+
arrayConstructor(
155+
integerLiteral("127"),
156+
integerLiteral("128"),
157+
integerLiteral("255"),
158+
integerLiteral("256"),
159+
integerLiteral("32767"),
160+
integerLiteral("32768"),
161+
integerLiteral("2147483647"),
162+
integerLiteral("2147483648"),
163+
integerLiteral("4294967295"),
164+
integerLiteral("4294967296"),
165+
integerLiteral("9223372036854775807"),
166+
integerLiteral("9223372036854775808")),
167+
dynamicArrayType(IntrinsicType.UINT64)));
168+
}
169+
}
170+
171+
static class IntegerArgumentsProvider implements ArgumentsProvider {
172+
@Override
173+
public Stream<Arguments> provideArguments(ExtensionContext context) {
174+
return Stream.of(
175+
Arguments.of(integerLiteral("-2147483649"), intrinsicType(IntrinsicType.INT64)),
176+
Arguments.of(integerLiteral("-2147483648"), intrinsicType(IntrinsicType.INTEGER)),
177+
Arguments.of(integerLiteral("-32769"), intrinsicType(IntrinsicType.INTEGER)),
178+
Arguments.of(integerLiteral("-32768"), intrinsicType(IntrinsicType.INTEGER)),
179+
Arguments.of(integerLiteral("-129"), intrinsicType(IntrinsicType.INTEGER)),
180+
Arguments.of(integerLiteral("-128"), intrinsicType(IntrinsicType.INTEGER)),
181+
Arguments.of(integerLiteral("-127"), intrinsicType(IntrinsicType.INTEGER)),
182+
Arguments.of(integerLiteral("128"), intrinsicType(IntrinsicType.INTEGER)),
183+
Arguments.of(integerLiteral("255"), intrinsicType(IntrinsicType.INTEGER)),
184+
Arguments.of(integerLiteral("256"), intrinsicType(IntrinsicType.INTEGER)),
185+
Arguments.of(integerLiteral("32767"), intrinsicType(IntrinsicType.INTEGER)),
186+
Arguments.of(integerLiteral("32768"), intrinsicType(IntrinsicType.INTEGER)),
187+
Arguments.of(integerLiteral("65535"), intrinsicType(IntrinsicType.INTEGER)),
188+
Arguments.of(integerLiteral("65536"), intrinsicType(IntrinsicType.INTEGER)),
189+
Arguments.of(integerLiteral("2147483647"), intrinsicType(IntrinsicType.INTEGER)),
190+
Arguments.of(integerLiteral("2147483648"), intrinsicType(IntrinsicType.CARDINAL)),
191+
Arguments.of(integerLiteral("4294967295"), intrinsicType(IntrinsicType.CARDINAL)),
192+
Arguments.of(integerLiteral("4294967296"), intrinsicType(IntrinsicType.INT64)),
193+
Arguments.of(integerLiteral("9223372036854775807"), intrinsicType(IntrinsicType.INT64)),
194+
Arguments.of(integerLiteral("9223372036854775808"), intrinsicType(IntrinsicType.UINT64)));
195+
}
196+
}
197+
198+
static class ProceduralArgumentsProvider implements ArgumentsProvider {
199+
@Override
200+
public Stream<Arguments> provideArguments(ExtensionContext context) {
201+
Type integer = intrinsicType(IntrinsicType.INTEGER);
202+
ExpressionNode voidProcedural = procedural(TypeFactory.voidType());
203+
204+
return Stream.of(
205+
Arguments.of(procedural(integer), integer),
206+
Arguments.of(voidProcedural, voidProcedural.getType()));
207+
}
208+
}
209+
210+
static class RealArgumentsProvider implements ArgumentsProvider {
211+
@Override
212+
public Stream<Arguments> provideArguments(ExtensionContext context) {
213+
Type extended = intrinsicType(IntrinsicType.EXTENDED);
214+
Type comp = intrinsicType(IntrinsicType.COMP);
215+
Type currency = intrinsicType(IntrinsicType.CURRENCY);
216+
217+
return Stream.of(
218+
Arguments.of(genericExpression(IntrinsicType.DOUBLE), extended),
219+
Arguments.of(genericExpression(IntrinsicType.REAL48), extended),
220+
Arguments.of(genericExpression(extended), extended),
221+
Arguments.of(genericExpression(comp), comp),
222+
Arguments.of(genericExpression(currency), currency));
223+
}
224+
}
225+
226+
@Test
227+
void testInferNull() {
228+
assertInferred(null, TypeFactory.unknownType());
229+
}
230+
231+
@ParameterizedTest
232+
@ArgumentsSource(ArrayConstructorArgumentsProvider.class)
233+
@ArgumentsSource(IntegerArgumentsProvider.class)
234+
@ArgumentsSource(ProceduralArgumentsProvider.class)
235+
@ArgumentsSource(RealArgumentsProvider.class)
236+
void testInfer(ExpressionNode expression, Type elementType) {
237+
assertInferred(expression, elementType);
238+
}
239+
240+
private static Type dynamicArrayType(IntrinsicType elementType) {
241+
return dynamicArrayType(intrinsicType(elementType));
242+
}
243+
244+
private static Type dynamicArrayType(Type elementType) {
245+
return TYPE_FACTORY.array(null, elementType, Set.of(ArrayOption.DYNAMIC));
246+
}
247+
248+
private static Type intrinsicType(IntrinsicType intrinsic) {
249+
return TYPE_FACTORY.getIntrinsic(intrinsic);
250+
}
251+
252+
private static ExpressionNode arrayConstructor(ExpressionNode... elements) {
253+
var arrayConstructor = makeSpy(new ArrayConstructorNodeImpl(DelphiLexer.TkArrayConstructor));
254+
arrayConstructor.addChild(commonDelphiNode(DelphiLexer.SQUARE_BRACKET_LEFT, "["));
255+
for (ExpressionNode element : elements) {
256+
arrayConstructor.addChild(element);
257+
}
258+
arrayConstructor.addChild(commonDelphiNode(DelphiLexer.SQUARE_BRACKET_RIGHT, "]"));
259+
260+
var primaryExpression = makeSpy(new PrimaryExpressionNodeImpl(DelphiLexer.TkPrimaryExpression));
261+
primaryExpression.addChild(arrayConstructor);
262+
263+
return primaryExpression;
264+
}
265+
266+
private static ExpressionNode integerLiteral(String image) {
267+
var integerLiteralToken = new CommonToken(DelphiLexer.TkIntNumber, image);
268+
var integerLiteral = makeSpy(new IntegerLiteralNodeImpl(integerLiteralToken));
269+
270+
var expression = makeSpy(new PrimaryExpressionNodeImpl(DelphiLexer.TkPrimaryExpression));
271+
expression.addChild(integerLiteral);
272+
273+
return expression;
274+
}
275+
276+
private static ExpressionNode procedural(Type returnType) {
277+
return genericExpression(TYPE_FACTORY.routine(Collections.emptyList(), returnType));
278+
}
279+
280+
private static ExpressionNode genericExpression(IntrinsicType type) {
281+
return genericExpression(intrinsicType(type));
282+
}
283+
284+
private static ExpressionNode genericExpression(Type type) {
285+
var primaryExpression = makeSpy(new PrimaryExpressionNodeImpl(DelphiLexer.TkPrimaryExpression));
286+
doReturn(type).when(primaryExpression).getType();
287+
doReturn("<expression of type " + type.getImage() + ">").when(primaryExpression).getImage();
288+
return primaryExpression;
289+
}
290+
291+
private static ExpressionNode parenthesize(ExpressionNode expression) {
292+
var parenthesized = new ParenthesizedExpressionNodeImpl(DelphiLexer.TkNestedExpression);
293+
parenthesized.addChild(commonDelphiNode(DelphiLexer.PAREN_BRACKET_LEFT, "("));
294+
parenthesized.addChild(expression);
295+
parenthesized.addChild(commonDelphiNode(DelphiLexer.PAREN_BRACKET_RIGHT, ")"));
296+
return parenthesized;
297+
}
298+
299+
@SuppressWarnings("ObjectToString")
300+
private static <T extends DelphiNodeImpl> T makeSpy(T spied) {
301+
final T result = spy(spied);
302+
303+
doReturn(TYPE_FACTORY).when(result).getTypeFactory();
304+
// This is just to improve the display string in parameterized tests
305+
doAnswer(invocation -> result.getImage()).when(result).toString();
306+
307+
return result;
308+
}
309+
310+
private static CommonDelphiNode commonDelphiNode(int type, String text) {
311+
return new CommonDelphiNodeImpl(new CommonToken(type, text));
312+
}
313+
314+
private static void assertInferred(ExpressionNode expression, Type type) {
315+
doAssertInferred(expression, type);
316+
if (expression != null) {
317+
doAssertInferred(parenthesize(expression), type);
318+
}
319+
}
320+
321+
private static void doAssertInferred(ExpressionNode expression, Type type) {
322+
TypeInferrer inferrer = new TypeInferrer(TYPE_FACTORY);
323+
Type inferred = inferrer.infer(expression);
324+
325+
assertThat(inferred.is(type))
326+
.withFailMessage(
327+
String.format(
328+
"Expected %s to be inferred to %s, but was %s",
329+
expression == null ? "null" : expression.getImage(),
330+
type.getImage(),
331+
inferred.getImage()))
332+
.isTrue();
333+
}
334+
}

0 commit comments

Comments
 (0)