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.

HighlightingLineReaderTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 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.ce.task.projectanalysis.source.linereader;
  21. import java.util.ArrayList;
  22. import java.util.Collections;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Random;
  26. import org.junit.Rule;
  27. import org.junit.Test;
  28. import org.sonar.api.utils.log.LogTester;
  29. import org.sonar.ce.task.projectanalysis.component.Component;
  30. import org.sonar.ce.task.projectanalysis.source.linereader.RangeOffsetConverter.RangeOffsetConverterException;
  31. import org.sonar.db.protobuf.DbFileSources;
  32. import org.sonar.scanner.protocol.output.ScannerReport;
  33. import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType;
  34. import org.sonar.scanner.protocol.output.ScannerReport.TextRange;
  35. import static com.google.common.collect.ImmutableMap.of;
  36. import static org.assertj.core.api.Assertions.assertThat;
  37. import static org.mockito.Mockito.doThrow;
  38. import static org.mockito.Mockito.mock;
  39. import static org.mockito.Mockito.when;
  40. import static org.sonar.api.utils.log.LoggerLevel.DEBUG;
  41. import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
  42. import static org.sonar.ce.task.projectanalysis.source.linereader.LineReader.Data.HIGHLIGHTING;
  43. import static org.sonar.db.protobuf.DbFileSources.Data.newBuilder;
  44. import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.ANNOTATION;
  45. import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.COMMENT;
  46. import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.CONSTANT;
  47. import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.HIGHLIGHTING_STRING;
  48. import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.KEYWORD;
  49. public class HighlightingLineReaderTest {
  50. @Rule
  51. public LogTester logTester = new LogTester();
  52. private static final Component FILE = builder(Component.Type.FILE, 1).setUuid("FILE_UUID").setKey("FILE_KEY").build();
  53. private static final int DEFAULT_LINE_LENGTH = 5;
  54. private static final int LINE_1 = 1;
  55. private static final int LINE_2 = 2;
  56. private static final int LINE_3 = 3;
  57. private static final int LINE_4 = 4;
  58. private static final String RANGE_LABEL_1 = "1,2";
  59. private static final String RANGE_LABEL_2 = "2,3";
  60. private static final String RANGE_LABEL_3 = "3,4";
  61. private static final String RANGE_LABEL_4 = "0,2";
  62. private static final String RANGE_LABEL_5 = "0,3";
  63. private RangeOffsetConverter rangeOffsetConverter = mock(RangeOffsetConverter.class);
  64. private DbFileSources.Data.Builder sourceData = newBuilder();
  65. private DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1);
  66. private DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2);
  67. private DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3);
  68. private DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4);
  69. @Test
  70. public void nothing_to_read() {
  71. HighlightingLineReader highlightingLineReader = newReader(Collections.emptyMap());
  72. DbFileSources.Line.Builder lineBuilder = newBuilder().addLinesBuilder().setLine(1);
  73. assertThat(highlightingLineReader.read(lineBuilder)).isEmpty();
  74. assertThat(lineBuilder.hasHighlighting()).isFalse();
  75. }
  76. @Test
  77. public void read_one_line() {
  78. HighlightingLineReader highlightingLineReader = newReader(of(
  79. newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION));
  80. assertThat(highlightingLineReader.read(line1)).isEmpty();
  81. assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
  82. }
  83. @Test
  84. public void read_many_lines() {
  85. HighlightingLineReader highlightingLineReader = newReader(of(
  86. newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
  87. newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_2), COMMENT,
  88. newSingleLineTextRangeWithExpectingLabel(LINE_4, RANGE_LABEL_3), CONSTANT));
  89. assertThat(highlightingLineReader.read(line1)).isEmpty();
  90. assertThat(highlightingLineReader.read(line2)).isEmpty();
  91. assertThat(highlightingLineReader.read(line3)).isEmpty();
  92. assertThat(highlightingLineReader.read(line4)).isEmpty();
  93. assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
  94. assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",cd");
  95. assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",c");
  96. }
  97. @Test
  98. public void supports_highlighting_over_multiple_lines_including_an_empty_one() {
  99. List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList = new ArrayList<>();
  100. addHighlighting(syntaxHighlightingList, 1, 0, 1, 7, KEYWORD); // package
  101. addHighlighting(syntaxHighlightingList, 2, 0, 4, 6, COMMENT); // comment over 3 lines
  102. addHighlighting(syntaxHighlightingList, 5, 0, 5, 6, KEYWORD); // public
  103. addHighlighting(syntaxHighlightingList, 5, 7, 5, 12, KEYWORD); // class
  104. HighlightingLineReader highlightingLineReader = new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), new RangeOffsetConverter());
  105. DbFileSources.Line.Builder[] builders = new DbFileSources.Line.Builder[] {
  106. addSourceLine(highlightingLineReader, 1, "package example;"),
  107. addSourceLine(highlightingLineReader, 2, "/*"),
  108. addSourceLine(highlightingLineReader, 3, ""),
  109. addSourceLine(highlightingLineReader, 4, " foo*/"),
  110. addSourceLine(highlightingLineReader, 5, "public class One {"),
  111. addSourceLine(highlightingLineReader, 6, "}")
  112. };
  113. assertThat(builders)
  114. .extracting("highlighting")
  115. .containsExactly(
  116. "0,7,k",
  117. "0,2,cd",
  118. "",
  119. "0,6,cd",
  120. "0,6,k;7,12,k",
  121. "");
  122. }
  123. private DbFileSources.Line.Builder addSourceLine(HighlightingLineReader highlightingLineReader, int line, String source) {
  124. DbFileSources.Line.Builder lineBuilder = sourceData.addLinesBuilder().setSource(source).setLine(line);
  125. assertThat(highlightingLineReader.read(lineBuilder)).isEmpty();
  126. return lineBuilder;
  127. }
  128. private void addHighlighting(List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList,
  129. int startLine, int startOffset,
  130. int endLine, int endOffset,
  131. HighlightingType type) {
  132. TextRange.Builder textRangeBuilder = TextRange.newBuilder();
  133. ScannerReport.SyntaxHighlightingRule.Builder ruleBuilder = ScannerReport.SyntaxHighlightingRule.newBuilder();
  134. syntaxHighlightingList.add(ruleBuilder
  135. .setRange(textRangeBuilder
  136. .setStartLine(startLine).setEndLine(endLine)
  137. .setStartOffset(startOffset).setEndOffset(endOffset)
  138. .build())
  139. .setType(type)
  140. .build());
  141. }
  142. @Test
  143. public void read_many_syntax_highlighting_on_same_line() {
  144. HighlightingLineReader highlightingLineReader = newReader(of(
  145. newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
  146. newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_2), COMMENT));
  147. assertThat(highlightingLineReader.read(line1)).isEmpty();
  148. assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a;" + RANGE_LABEL_2 + ",cd");
  149. }
  150. @Test
  151. public void read_one_syntax_highlighting_on_many_lines() {
  152. // This highlighting begin on line 1 and finish on line 3
  153. TextRange textRange = newTextRange(LINE_1, LINE_3);
  154. when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
  155. when(rangeOffsetConverter.offsetToString(textRange, LINE_2, 6)).thenReturn(RANGE_LABEL_2);
  156. when(rangeOffsetConverter.offsetToString(textRange, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3);
  157. HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION));
  158. assertThat(highlightingLineReader.read(line1)).isEmpty();
  159. DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line 2").setLine(2);
  160. assertThat(highlightingLineReader.read(line2)).isEmpty();
  161. assertThat(highlightingLineReader.read(line3)).isEmpty();
  162. assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
  163. assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a");
  164. assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a");
  165. }
  166. @Test
  167. public void read_many_syntax_highlighting_on_many_lines() {
  168. TextRange textRange1 = newTextRange(LINE_1, LINE_3);
  169. when(rangeOffsetConverter.offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
  170. when(rangeOffsetConverter.offsetToString(textRange1, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
  171. when(rangeOffsetConverter.offsetToString(textRange1, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3);
  172. TextRange textRange2 = newTextRange(LINE_2, LINE_4);
  173. when(rangeOffsetConverter.offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
  174. when(rangeOffsetConverter.offsetToString(textRange2, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
  175. when(rangeOffsetConverter.offsetToString(textRange2, LINE_4, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_4);
  176. TextRange textRange3 = newTextRange(LINE_2, LINE_2);
  177. when(rangeOffsetConverter.offsetToString(textRange3, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_5);
  178. HighlightingLineReader highlightingLineReader = newReader(of(
  179. textRange1, ANNOTATION,
  180. textRange2, HIGHLIGHTING_STRING,
  181. textRange3, COMMENT));
  182. assertThat(highlightingLineReader.read(line1)).isEmpty();
  183. assertThat(highlightingLineReader.read(line2)).isEmpty();
  184. assertThat(highlightingLineReader.read(line3)).isEmpty();
  185. assertThat(highlightingLineReader.read(line4)).isEmpty();
  186. assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
  187. assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a;" + RANGE_LABEL_2 + ",s;" + RANGE_LABEL_5 + ",cd");
  188. assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a;" + RANGE_LABEL_2 + ",s");
  189. assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_4 + ",s");
  190. }
  191. @Test
  192. public void read_highlighting_declared_on_a_whole_line() {
  193. TextRange textRange = newTextRange(LINE_1, LINE_2);
  194. when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
  195. when(rangeOffsetConverter.offsetToString(textRange, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn("");
  196. HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION));
  197. assertThat(highlightingLineReader.read(line1)).isEmpty();
  198. assertThat(highlightingLineReader.read(line2)).isEmpty();
  199. assertThat(highlightingLineReader.read(line3)).isEmpty();
  200. assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
  201. // Nothing should be set on line 2
  202. assertThat(line2.getHighlighting()).isEmpty();
  203. assertThat(line3.getHighlighting()).isEmpty();
  204. }
  205. @Test
  206. public void not_fail_and_stop_processing_when_range_offset_converter_throw_RangeOffsetConverterException() {
  207. TextRange textRange1 = newTextRange(LINE_1, LINE_1);
  208. doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH);
  209. HighlightingLineReader highlightingLineReader = newReader(of(
  210. textRange1, HighlightingType.ANNOTATION,
  211. newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_1), HIGHLIGHTING_STRING));
  212. LineReader.ReadError readErrorLine1 = new LineReader.ReadError(HIGHLIGHTING, LINE_1);
  213. assertThat(highlightingLineReader.read(line1)).contains(readErrorLine1);
  214. assertThat(highlightingLineReader.read(line2)).contains(readErrorLine1);
  215. assertNoHighlighting();
  216. assertThat(logTester.logs(DEBUG)).isNotEmpty();
  217. }
  218. @Test
  219. public void keep_existing_processed_highlighting_when_range_offset_converter_throw_RangeOffsetConverterException() {
  220. TextRange textRange2 = newTextRange(LINE_2, LINE_2);
  221. doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH);
  222. TextRange textRange3 = newTextRange(LINE_3, LINE_3);
  223. HighlightingLineReader highlightingLineReader = newReader(of(
  224. newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
  225. textRange2, HIGHLIGHTING_STRING,
  226. textRange3, COMMENT));
  227. assertThat(highlightingLineReader.read(line1)).isEmpty();
  228. LineReader.ReadError readErrorLine2 = new LineReader.ReadError(HIGHLIGHTING, LINE_2);
  229. assertThat(highlightingLineReader.read(line2)).contains(readErrorLine2);
  230. assertThat(highlightingLineReader.read(line3)).contains(readErrorLine2);
  231. assertThat(line1.hasHighlighting()).isTrue();
  232. assertThat(line2.hasHighlighting()).isFalse();
  233. assertThat(line3.hasHighlighting()).isFalse();
  234. assertThat(logTester.logs(DEBUG)).isNotEmpty();
  235. }
  236. @Test
  237. public void display_file_key_in_debug_when_range_offset_converter_throw_RangeOffsetConverterException() {
  238. TextRange textRange1 = newTextRange(LINE_1, LINE_1);
  239. doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH);
  240. HighlightingLineReader highlightingLineReader = newReader(of(textRange1, ANNOTATION));
  241. assertThat(highlightingLineReader.read(line1))
  242. .contains(new LineReader.ReadError(HIGHLIGHTING, 1));
  243. assertThat(logTester.logs(DEBUG)).containsOnly("Inconsistency detected in Highlighting data. Highlighting will be ignored for file 'FILE_KEY'");
  244. }
  245. private HighlightingLineReader newReader(Map<TextRange, HighlightingType> textRangeByType) {
  246. List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList = new ArrayList<>();
  247. for (Map.Entry<TextRange, HighlightingType> entry : textRangeByType.entrySet()) {
  248. syntaxHighlightingList.add(ScannerReport.SyntaxHighlightingRule.newBuilder()
  249. .setRange(entry.getKey())
  250. .setType(entry.getValue())
  251. .build());
  252. }
  253. return new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), rangeOffsetConverter);
  254. }
  255. private static TextRange newTextRange(int startLine, int enLine) {
  256. Random random = new Random();
  257. return TextRange.newBuilder()
  258. .setStartLine(startLine).setEndLine(enLine)
  259. // Offsets are not used by the reader
  260. .setStartOffset(random.nextInt()).setEndOffset(random.nextInt())
  261. .build();
  262. }
  263. private TextRange newSingleLineTextRangeWithExpectingLabel(int line, String rangeLabel) {
  264. TextRange textRange = newTextRange(line, line);
  265. when(rangeOffsetConverter.offsetToString(textRange, line, DEFAULT_LINE_LENGTH)).thenReturn(rangeLabel);
  266. return textRange;
  267. }
  268. private void assertNoHighlighting() {
  269. assertThat(line1.hasHighlighting()).isFalse();
  270. assertThat(line2.hasHighlighting()).isFalse();
  271. assertThat(line3.hasHighlighting()).isFalse();
  272. assertThat(line4.hasHighlighting()).isFalse();
  273. }
  274. }