1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package io.earcam.instrumental.module.jpms.parser;
20
21 import static io.earcam.instrumental.module.jpms.RequireModifier.TRANSITIVE;
22 import static java.nio.charset.Charset.defaultCharset;
23 import static java.nio.charset.StandardCharsets.UTF_8;
24 import static org.hamcrest.MatcherAssert.assertThat;
25 import static org.hamcrest.Matchers.aMapWithSize;
26 import static org.hamcrest.Matchers.allOf;
27 import static org.hamcrest.Matchers.arrayContaining;
28 import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
29 import static org.hamcrest.Matchers.contains;
30 import static org.hamcrest.Matchers.containsInAnyOrder;
31 import static org.hamcrest.Matchers.equalTo;
32 import static org.hamcrest.Matchers.hasEntry;
33 import static org.hamcrest.Matchers.is;
34
35 import java.io.ByteArrayInputStream;
36 import java.io.IOException;
37 import java.nio.file.Files;
38 import java.nio.file.Paths;
39
40 import org.junit.jupiter.api.Nested;
41 import org.junit.jupiter.api.Test;
42
43 import io.earcam.instrumental.module.jpms.Export;
44 import io.earcam.instrumental.module.jpms.ModuleInfo;
45 import io.earcam.instrumental.module.jpms.Require;
46
47 @SuppressWarnings("squid:S2187")
48 public class AntlrParserTest {
49
50 @Nested
51 class ModuleDeclarationName {
52
53 @Test
54 void nameOnly()
55 {
56 String name = "com.acme.mod.a";
57
58 String source =
59 "module " + name + " { \n" +
60 "} \n";
61
62
63 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
64
65 assertThat(moduleInfo.name(), is(equalTo(name)));
66 }
67
68
69 @Test
70 void singleAnnotation()
71 {
72 String name = "com.acme.mod.b";
73
74 String source =
75 "@SuppressWarnings(\"some\") \n" +
76 "module " + name + " { \n" +
77 "} \n";
78
79
80 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
81
82 assertThat(moduleInfo.name(), is(equalTo(name)));
83 }
84
85
86 @Test
87 void multipleAnnotations()
88 {
89 String name = "com.acme.mod.c";
90
91 String source =
92 "@SuppressWarnings(\"some\") \n" +
93 "@Deprecated \n" +
94 "module " + name + " { \n" +
95 "} \n";
96
97
98 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
99
100 assertThat(moduleInfo.name(), is(equalTo(name)));
101 }
102
103
104 @Test
105 void open()
106 {
107 String name = "com.acme.mod.d";
108
109 String source =
110 "open module " + name + " { \n" +
111 "} \n";
112
113
114 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
115
116 assertThat(moduleInfo.name(), is(equalTo(name)));
117 }
118
119
120 @Test
121 void openWithMultipleAnnotations()
122 {
123 String name = "com.acme.mod.e";
124
125 String source =
126 "@SuppressWarnings(\"some\") \n" +
127 "@Deprecated \n" +
128 "open module " + name + " { \n" +
129 "} \n";
130
131
132 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
133
134 assertThat(moduleInfo.name(), is(equalTo(name)));
135 }
136 }
137
138 @Nested
139 class Required {
140
141 @Test
142 void singleRequire()
143 {
144
145 String source =
146 "module com.acme.mod { \n" +
147 " requires java.sql; \n" +
148 "} \n";
149
150
151 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
152
153 assertThat(moduleInfo.requires(), contains(new Require("java.sql", 0, null)));
154 }
155
156
157 @Test
158 void singleRequireWithTransitiveModifier()
159 {
160
161 String source =
162 "module com.acme.mod.yule { \n" +
163 " requires transitive java.xml; \n" +
164 "} \n";
165
166
167 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
168
169 assertThat(moduleInfo.requires(), contains(new Require("java.xml", TRANSITIVE.access(), null)));
170 }
171
172
173 @Test
174 void multipleRequires()
175 {
176
177 String source =
178 "module com.acme.mod.yule { \n" +
179 " requires java.sql; \n" +
180 " requires transitive java.xml; \n" +
181 "} \n";
182
183
184 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
185
186 assertThat(moduleInfo.requires(), containsInAnyOrder(
187 new Require("java.sql", 0, null),
188 new Require("java.xml", TRANSITIVE.access(), null)));
189 }
190 }
191
192 @Nested
193 class Exported {
194
195 @Test
196 void singleUnqalifiedExport()
197 {
198
199 String source =
200 "module com.acme.base { \n" +
201 " exports com.acme.api; \n" +
202 "} \n";
203
204
205 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
206
207 assertThat(moduleInfo.exports(), contains(new Export("com.acme.api")));
208 }
209
210
211 @Test
212 void singleQalifiedExport()
213 {
214
215 String source =
216 "module com.acme.base { \n" +
217 " exports com.acme.guts to com.acme.grunt; \n" +
218 "} \n";
219
220
221 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
222
223 assertThat(moduleInfo.exports(), contains(new Export("com.acme.guts", "com.acme.grunt")));
224 }
225
226
227 @Test
228 void multipleExports()
229 {
230
231 String source =
232 "module com.acme.base { \n" +
233 " exports com.acme.api; \n" +
234 " exports com.acme.guts to com.acme.grunt; \n" +
235 "} \n";
236
237
238 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
239
240 assertThat(moduleInfo.exports(), containsInAnyOrder(
241 new Export("com.acme.api"),
242 new Export("com.acme.guts", "com.acme.grunt")));
243 }
244
245
246 @Test
247 void multipleExportsViaInputStream()
248 {
249
250 String source =
251 "module com.acme.base { \n" +
252 " exports com.acme.api; \n" +
253 " exports com.acme.guts to com.acme.grunt; \n" +
254 "} \n";
255
256
257 ModuleInfo moduleInfo = ModuleInfoParser.parse(new ByteArrayInputStream(source.getBytes(UTF_8)), UTF_8);
258
259 assertThat(moduleInfo.exports(), containsInAnyOrder(
260 new Export("com.acme.api"),
261 new Export("com.acme.guts", "com.acme.grunt")));
262 }
263 }
264
265 @Nested
266 class Opened {
267
268 @Test
269 void singleUnqualifiedOpen()
270 {
271
272 String source =
273 "module com.acme.base { \n" +
274 " opens com.acme.every.thing; \n" +
275 "} \n";
276
277
278 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
279
280 assertThat(moduleInfo.opens(), contains(new Export("com.acme.every.thing")));
281 }
282
283
284 @Test
285 void singleQualifiedOpen()
286 {
287
288 String source =
289 "module com.acme.base { \n" +
290 " opens com.acme.up to com.acme.friend.sssh; \n" +
291 "} \n";
292
293
294 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
295
296 assertThat(moduleInfo.opens(), contains(new Export("com.acme.up", "com.acme.friend.sssh")));
297 }
298
299
300 @Test
301 void mutlipleOpens()
302 {
303
304 String source =
305 "module com.acme.base { \n" +
306 " opens com.acme.every.thing; \n" +
307 " opens com.acme.up to com.acme.friend.sssh; \n" +
308 "} \n";
309
310
311 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
312
313 assertThat(moduleInfo.opens(), contains(
314 new Export("com.acme.every.thing"),
315 new Export("com.acme.up", "com.acme.friend.sssh")));
316 }
317
318 }
319
320 @Nested
321 class Used {
322
323 @Test
324 void singleUse()
325 {
326
327 String source =
328 "module com.acme.app { \n" +
329 " uses com.acme.base.Boost; \n" +
330 "} \n";
331
332
333 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
334
335 assertThat(moduleInfo.uses(), contains("com.acme.base.Boost"));
336 }
337
338
339 @Test
340 void multipleUses()
341 {
342
343 String source =
344 "module com.acme.app { \n" +
345 " uses com.acme.base.Boost; \n" +
346 " uses com.acme.spi.Service; \n" +
347 " uses com.acme.spi.EventBus; \n" +
348 "} \n";
349
350
351 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
352
353 assertThat(moduleInfo.uses(), containsInAnyOrder(
354 "com.acme.base.Boost",
355 "com.acme.spi.Service",
356 "com.acme.spi.EventBus"));
357 }
358
359 }
360
361 @Nested
362 class Provided {
363
364 @Test
365 void singleProvisionOfSingleService()
366 {
367
368 String source =
369 "module com.acme.xyz.service { \n" +
370 " provides com.acme.spi.Service \n" +
371 " with com.acme.imp.DefaultService; \n" +
372 "} \n";
373
374
375 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
376
377 assertThat(moduleInfo.provides(), allOf(
378 is(aMapWithSize(1)),
379 hasEntry(equalTo("com.acme.spi.Service"), arrayContaining("com.acme.imp.DefaultService"))));
380 }
381
382
383 @Test
384 void multipleProvisionsOfSingleService()
385 {
386
387 String source =
388 "module com.acme.xyz.service { \n" +
389 " provides com.acme.spi.Greet with \n" +
390 " com.acme.imp.Hello, \n" +
391 " com.acme.imp.Hey, \n" +
392 " com.acme.imp.Howdy; \n" +
393 "} \n";
394
395
396 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
397
398 assertThat(moduleInfo.provides(), allOf(
399 is(aMapWithSize(1)),
400 hasEntry(equalTo("com.acme.spi.Greet"), arrayContainingInAnyOrder(
401 "com.acme.imp.Hello",
402 "com.acme.imp.Hey",
403 "com.acme.imp.Howdy"))));
404 }
405
406
407 @Test
408 void multipleProvisionsOfMultipleServices()
409 {
410
411 String source =
412 "module com.acme.xyz.service { \n" +
413 " \n" +
414 " provides com.acme.spi.Funk with \n" +
415 " com.acme.imp.Funky; \n" +
416 " \n" +
417 " provides com.acme.spi.Greet with \n" +
418 " com.acme.imp.Hello, \n" +
419 " com.acme.imp.Hey, \n" +
420 " com.acme.imp.Howdy; \n" +
421 "} \n";
422
423
424 ModuleInfo moduleInfo = ModuleInfoParser.parse(source);
425
426 assertThat(moduleInfo.provides(), allOf(
427 is(aMapWithSize(2)),
428 hasEntry(equalTo("com.acme.spi.Funk"), arrayContaining("com.acme.imp.Funky")),
429 hasEntry(equalTo("com.acme.spi.Greet"), arrayContainingInAnyOrder(
430 "com.acme.imp.Hello",
431 "com.acme.imp.Hey",
432 "com.acme.imp.Howdy"))));
433 }
434
435 }
436
437 @Nested
438 class LargeExamples {
439
440
441
442 @Test
443 void javaBase() throws IOException
444 {
445 String source = new String(Files.readAllBytes(Paths.get(".", "src", "test", "resources", "java.base_module-info.source")), defaultCharset());
446 ModuleInfoParser.parse(source);
447 }
448 }
449 }