You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JavaStatementBuilderTest.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.duplications.java;
  21. import java.io.File;
  22. import java.io.FileInputStream;
  23. import java.io.FileNotFoundException;
  24. import java.io.InputStreamReader;
  25. import java.io.Reader;
  26. import java.nio.charset.StandardCharsets;
  27. import java.util.List;
  28. import org.apache.commons.io.IOUtils;
  29. import org.junit.Test;
  30. import org.sonar.duplications.DuplicationsTestUtil;
  31. import org.sonar.duplications.statement.Statement;
  32. import org.sonar.duplications.statement.StatementChunker;
  33. import org.sonar.duplications.token.TokenChunker;
  34. import static org.assertj.core.api.Assertions.assertThat;
  35. public class JavaStatementBuilderTest {
  36. private final TokenChunker tokenChunker = JavaTokenProducer.build();
  37. private final StatementChunker statementChunker = JavaStatementBuilder.build();
  38. private List<Statement> chunk(String sourceCode) {
  39. return statementChunker.chunk(tokenChunker.chunk(sourceCode));
  40. }
  41. @Test
  42. public void shouldIgnoreImportStatement() {
  43. assertThat(chunk("import org.sonar.duplications.java;")).isEmpty();
  44. }
  45. @Test
  46. public void shouldIgnorePackageStatement() {
  47. assertThat(chunk("package org.sonar.duplications.java;")).isEmpty();
  48. }
  49. @Test
  50. public void shouldHandleAnnotation() {
  51. List<Statement> statements = chunk("" +
  52. "@Entity" +
  53. "@Table(name = \"properties\")" +
  54. "@Column(updatable = true, nullable = true)");
  55. assertThat(statements).hasSize(3);
  56. assertThat(statements.get(0).getValue()).isEqualTo("@Entity");
  57. assertThat(statements.get(1).getValue()).isEqualTo("@Table(name=$CHARS)");
  58. assertThat(statements.get(2).getValue()).isEqualTo("@Column(updatable=true,nullable=true)");
  59. }
  60. @Test
  61. public void shouldHandleIf() {
  62. List<Statement> statements = chunk("if (a > b) { something(); }");
  63. assertThat(statements.size()).isEqualTo(2);
  64. assertThat(statements.get(0).getValue()).isEqualTo("if(a>b)");
  65. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  66. statements = chunk("if (a > b) { something(); } else { somethingOther(); }");
  67. assertThat(statements.size()).isEqualTo(4);
  68. assertThat(statements.get(0).getValue()).isEqualTo("if(a>b)");
  69. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  70. assertThat(statements.get(2).getValue()).isEqualTo("else");
  71. assertThat(statements.get(3).getValue()).isEqualTo("somethingOther()");
  72. statements = chunk("if (a > 0) { something(); } else if (a == 0) { somethingOther(); }");
  73. assertThat(statements.size()).isEqualTo(4);
  74. assertThat(statements.get(0).getValue()).isEqualTo("if(a>$NUMBER)");
  75. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  76. assertThat(statements.get(2).getValue()).isEqualTo("elseif(a==$NUMBER)");
  77. assertThat(statements.get(3).getValue()).isEqualTo("somethingOther()");
  78. }
  79. @Test
  80. public void shouldHandleFor() {
  81. List<Statement> statements = chunk("for (int i = 0; i < 10; i++) { something(); }");
  82. assertThat(statements.size()).isEqualTo(2);
  83. assertThat(statements.get(0).getValue()).isEqualTo("for(inti=$NUMBER;i<$NUMBER;i++)");
  84. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  85. statements = chunk("for (Item item : items) { something(); }");
  86. assertThat(statements.size()).isEqualTo(2);
  87. assertThat(statements.get(0).getValue()).isEqualTo("for(Itemitem:items)");
  88. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  89. }
  90. @Test
  91. public void shouldHandleWhile() {
  92. List<Statement> statements = chunk("while (i < args.length) { something(); }");
  93. assertThat(statements.size()).isEqualTo(2);
  94. assertThat(statements.get(0).getValue()).isEqualTo("while(i<args.length)");
  95. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  96. statements = chunk("while (true);");
  97. assertThat(statements.size()).isEqualTo(1);
  98. assertThat(statements.get(0).getValue()).isEqualTo("while(true)");
  99. }
  100. @Test
  101. public void shouldHandleDoWhile() {
  102. List<Statement> statements = chunk("do { something(); } while (true);");
  103. assertThat(statements.size()).isEqualTo(3);
  104. assertThat(statements.get(0).getValue()).isEqualTo("do");
  105. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  106. assertThat(statements.get(2).getValue()).isEqualTo("while(true)");
  107. }
  108. @Test
  109. public void shouldHandleSwitch() {
  110. List<Statement> statements = chunk("" +
  111. "switch (month) {" +
  112. " case 1 : monthString=\"January\"; break;" +
  113. " case 2 : monthString=\"February\"; break;" +
  114. " default: monthString=\"Invalid\";" +
  115. "}");
  116. assertThat(statements.size()).isEqualTo(6);
  117. assertThat(statements.get(0).getValue()).isEqualTo("switch(month)");
  118. assertThat(statements.get(1).getValue()).isEqualTo("case$NUMBER:monthString=$CHARS");
  119. assertThat(statements.get(2).getValue()).isEqualTo("break");
  120. assertThat(statements.get(3).getValue()).isEqualTo("case$NUMBER:monthString=$CHARS");
  121. assertThat(statements.get(4).getValue()).isEqualTo("break");
  122. assertThat(statements.get(5).getValue()).isEqualTo("default:monthString=$CHARS");
  123. }
  124. /**
  125. * See SONAR-2782
  126. */
  127. @Test
  128. public void shouldHandleNestedSwitch() {
  129. List<Statement> statements = chunk("" +
  130. "switch (a) {" +
  131. " case 'a': case 'b': case 'c': something(); break;" +
  132. " case 'd': case 'e': case 'f': somethingOther(); break;" +
  133. "}");
  134. assertThat(statements.size()).isEqualTo(5);
  135. assertThat(statements.get(0).getValue()).isEqualTo("switch(a)");
  136. assertThat(statements.get(1).getValue()).isEqualTo("case$CHARS:case$CHARS:case$CHARS:something()");
  137. assertThat(statements.get(2).getValue()).isEqualTo("break");
  138. assertThat(statements.get(3).getValue()).isEqualTo("case$CHARS:case$CHARS:case$CHARS:somethingOther()");
  139. assertThat(statements.get(4).getValue()).isEqualTo("break");
  140. }
  141. @Test
  142. public void shouldHandleArray() {
  143. List<Statement> statements = chunk("new Integer[] { 1, 2, 3, 4 };");
  144. assertThat(statements.size()).isEqualTo(2);
  145. assertThat(statements.get(0).getValue()).isEqualTo("newInteger[]");
  146. assertThat(statements.get(1).getValue()).isEqualTo("{$NUMBER,$NUMBER,$NUMBER,$NUMBER}");
  147. }
  148. /**
  149. * See SONAR-2837
  150. */
  151. @Test
  152. public void shouldHandleMultidimensionalArray() {
  153. List<Statement> statements = chunk("new Integer[][] { { 1, 2 }, {3, 4} };");
  154. assertThat(statements.size()).isEqualTo(2);
  155. assertThat(statements.get(0).getValue()).isEqualTo("newInteger[][]");
  156. assertThat(statements.get(1).getValue()).isEqualTo("{{$NUMBER,$NUMBER},{$NUMBER,$NUMBER}}");
  157. statements = chunk("new Integer[][] { null, {3, 4} };");
  158. assertThat(statements.size()).isEqualTo(2);
  159. assertThat(statements.get(0).getValue()).isEqualTo("newInteger[][]");
  160. assertThat(statements.get(1).getValue()).isEqualTo("{null,{$NUMBER,$NUMBER}}");
  161. }
  162. @Test
  163. public void shouldHandleTryCatch() {
  164. List<Statement> statements;
  165. statements = chunk("try { } catch (Exception e) { }");
  166. assertThat(statements.size()).isEqualTo(4);
  167. assertThat(statements.get(0).getValue()).isEqualTo("try");
  168. assertThat(statements.get(1).getValue()).isEqualTo("{}");
  169. assertThat(statements.get(2).getValue()).isEqualTo("catch(Exceptione)");
  170. assertThat(statements.get(3).getValue()).isEqualTo("{}");
  171. statements = chunk("try { something(); } catch (Exception e) { }");
  172. assertThat(statements.size()).isEqualTo(4);
  173. assertThat(statements.get(0).getValue()).isEqualTo("try");
  174. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  175. assertThat(statements.get(2).getValue()).isEqualTo("catch(Exceptione)");
  176. assertThat(statements.get(3).getValue()).isEqualTo("{}");
  177. statements = chunk("try { something(); } catch (Exception e) { onException(); }");
  178. assertThat(statements.size()).isEqualTo(4);
  179. assertThat(statements.get(0).getValue()).isEqualTo("try");
  180. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  181. assertThat(statements.get(2).getValue()).isEqualTo("catch(Exceptione)");
  182. assertThat(statements.get(3).getValue()).isEqualTo("onException()");
  183. statements = chunk("try { something(); } catch (Exception1 e) { onException1(); } catch (Exception2 e) { onException2(); }");
  184. assertThat(statements.size()).isEqualTo(6);
  185. assertThat(statements.get(0).getValue()).isEqualTo("try");
  186. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  187. assertThat(statements.get(2).getValue()).isEqualTo("catch(Exception1e)");
  188. assertThat(statements.get(3).getValue()).isEqualTo("onException1()");
  189. assertThat(statements.get(4).getValue()).isEqualTo("catch(Exception2e)");
  190. assertThat(statements.get(5).getValue()).isEqualTo("onException2()");
  191. }
  192. @Test
  193. public void shouldHandleTryFinnaly() {
  194. List<Statement> statements;
  195. statements = chunk("try { } finally { }");
  196. assertThat(statements.size()).isEqualTo(4);
  197. assertThat(statements.get(0).getValue()).isEqualTo("try");
  198. assertThat(statements.get(1).getValue()).isEqualTo("{}");
  199. assertThat(statements.get(2).getValue()).isEqualTo("finally");
  200. assertThat(statements.get(3).getValue()).isEqualTo("{}");
  201. statements = chunk("try { something(); } finally { }");
  202. assertThat(statements.size()).isEqualTo(4);
  203. assertThat(statements.get(0).getValue()).isEqualTo("try");
  204. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  205. assertThat(statements.get(2).getValue()).isEqualTo("finally");
  206. assertThat(statements.get(3).getValue()).isEqualTo("{}");
  207. statements = chunk("try { something(); } finally { somethingOther(); }");
  208. assertThat(statements.size()).isEqualTo(4);
  209. assertThat(statements.get(0).getValue()).isEqualTo("try");
  210. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  211. assertThat(statements.get(2).getValue()).isEqualTo("finally");
  212. assertThat(statements.get(3).getValue()).isEqualTo("somethingOther()");
  213. }
  214. @Test
  215. public void shouldHandleTryCatchFinally() {
  216. List<Statement> statements;
  217. statements = chunk("try { } catch (Exception e) {} finally { }");
  218. assertThat(statements.size()).isEqualTo(6);
  219. assertThat(statements.get(0).getValue()).isEqualTo("try");
  220. assertThat(statements.get(1).getValue()).isEqualTo("{}");
  221. assertThat(statements.get(2).getValue()).isEqualTo("catch(Exceptione)");
  222. assertThat(statements.get(3).getValue()).isEqualTo("{}");
  223. assertThat(statements.get(4).getValue()).isEqualTo("finally");
  224. assertThat(statements.get(5).getValue()).isEqualTo("{}");
  225. statements = chunk("try { something(); } catch (Exception e) { onException(); } finally { somethingOther(); }");
  226. assertThat(statements.size()).isEqualTo(6);
  227. assertThat(statements.get(0).getValue()).isEqualTo("try");
  228. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  229. assertThat(statements.get(2).getValue()).isEqualTo("catch(Exceptione)");
  230. assertThat(statements.get(3).getValue()).isEqualTo("onException()");
  231. assertThat(statements.get(4).getValue()).isEqualTo("finally");
  232. assertThat(statements.get(5).getValue()).isEqualTo("somethingOther()");
  233. }
  234. /**
  235. * Java 7.
  236. */
  237. @Test
  238. public void shouldHandleMultiCatch() {
  239. List<Statement> statements;
  240. statements = chunk("try { } catch (Exception1 | Exception2 e) { }");
  241. assertThat(statements.size()).isEqualTo(4);
  242. assertThat(statements.get(0).getValue()).isEqualTo("try");
  243. assertThat(statements.get(1).getValue()).isEqualTo("{}");
  244. assertThat(statements.get(2).getValue()).isEqualTo("catch(Exception1|Exception2e)");
  245. assertThat(statements.get(3).getValue()).isEqualTo("{}");
  246. statements = chunk("try { something(); } catch (Exception1 | Exception2 e) { }");
  247. assertThat(statements.size()).isEqualTo(4);
  248. assertThat(statements.get(0).getValue()).isEqualTo("try");
  249. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  250. assertThat(statements.get(2).getValue()).isEqualTo("catch(Exception1|Exception2e)");
  251. assertThat(statements.get(3).getValue()).isEqualTo("{}");
  252. statements = chunk("try { something(); } catch (Exception1 | Exception2 e) { onException(); }");
  253. assertThat(statements.size()).isEqualTo(4);
  254. assertThat(statements.get(0).getValue()).isEqualTo("try");
  255. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  256. assertThat(statements.get(2).getValue()).isEqualTo("catch(Exception1|Exception2e)");
  257. assertThat(statements.get(3).getValue()).isEqualTo("onException()");
  258. }
  259. /**
  260. * Java 7.
  261. */
  262. @Test
  263. public void shouldHandleTryWithResource() {
  264. List<Statement> statements;
  265. statements = chunk("try (FileInputStream in = new FileInputStream()) {}");
  266. assertThat(statements.size()).isEqualTo(2);
  267. assertThat(statements.get(0).getValue()).isEqualTo("try(FileInputStreamin=newFileInputStream())");
  268. assertThat(statements.get(1).getValue()).isEqualTo("{}");
  269. statements = chunk("try (FileInputStream in = new FileInputStream(); FileOutputStream out = new FileOutputStream()) {}");
  270. assertThat(statements.size()).isEqualTo(2);
  271. assertThat(statements.get(0).getValue()).isEqualTo("try(FileInputStreamin=newFileInputStream();FileOutputStreamout=newFileOutputStream())");
  272. assertThat(statements.get(1).getValue()).isEqualTo("{}");
  273. statements = chunk("try (FileInputStream in = new FileInputStream(); FileOutputStream out = new FileOutputStream();) {}");
  274. assertThat(statements.size()).isEqualTo(2);
  275. assertThat(statements.get(0).getValue()).isEqualTo("try(FileInputStreamin=newFileInputStream();FileOutputStreamout=newFileOutputStream();)");
  276. assertThat(statements.get(1).getValue()).isEqualTo("{}");
  277. statements = chunk("try (FileInputStream in = new FileInputStream()) { something(); }");
  278. assertThat(statements.size()).isEqualTo(2);
  279. assertThat(statements.get(0).getValue()).isEqualTo("try(FileInputStreamin=newFileInputStream())");
  280. assertThat(statements.get(1).getValue()).isEqualTo("something()");
  281. }
  282. /**
  283. * Java 8.
  284. */
  285. @Test
  286. public void shouldHandleLambda() {
  287. List<Statement> statements;
  288. statements = chunk("List<String> result = lines.stream().filter(line -> !\"mkyong\".equals(line)).collect(Collectors.toList());");
  289. assertThat(statements.size()).isEqualTo(1);
  290. assertThat(statements).extracting(Statement::getValue).containsExactly("List<String>result=lines.stream().filter(line->!$CHARS.equals(line)).collect(Collectors.toList())");
  291. statements = chunk("items.forEach((k,v)->{System.out.println(\"Item : \" + k + \" Count : \" + v); if(\"E\".equals(k)) { System.out.println(\"Hello E\");}});");
  292. assertThat(statements.size()).isEqualTo(5);
  293. assertThat(statements).extracting(Statement::getValue)
  294. .containsExactly("items.forEach((k,v)->",
  295. "System.out.println($CHARS+k+$CHARS+v)",
  296. "if($CHARS.equals(k))",
  297. "System.out.println($CHARS)",
  298. ")");
  299. }
  300. /**
  301. * Java 9.
  302. */
  303. @Test
  304. public void shouldHandleModuleInfo() {
  305. List<Statement> statements;
  306. statements = chunk("module com.application.infra { requires com.application.domain; exports com.application.infra.api; }");
  307. assertThat(statements.size()).isEqualTo(3);
  308. assertThat(statements).extracting(Statement::getValue)
  309. .containsExactly("modulecom.application.infra",
  310. "requirescom.application.domain",
  311. "exportscom.application.infra.api");
  312. }
  313. /**
  314. * Java 11.
  315. */
  316. @Test
  317. public void shouldHandleVar() {
  318. List<Statement> statements;
  319. statements = chunk("IFunc f = (@NonNull var x, final var y) -> Foo.foo(x, y);");
  320. assertThat(statements.size()).isEqualTo(1);
  321. assertThat(statements).extracting(Statement::getValue).containsExactly("IFuncf=(@NonNullvarx,finalvary)->Foo.foo(x,y)");
  322. }
  323. @Test
  324. public void realExamples() {
  325. assertThat(chunk(DuplicationsTestUtil.findFile("/java/MessageResources.java")).size()).isGreaterThan(0);
  326. assertThat(chunk(DuplicationsTestUtil.findFile("/java/RequestUtils.java")).size()).isGreaterThan(0);
  327. }
  328. private List<Statement> chunk(File file) {
  329. Reader reader = null;
  330. try {
  331. reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
  332. return statementChunker.chunk(tokenChunker.chunk(reader));
  333. } catch (FileNotFoundException e) {
  334. throw new RuntimeException(e);
  335. } finally {
  336. IOUtils.closeQuietly(reader);
  337. }
  338. }
  339. }