3 * Copyright (C) 2009-2016 SonarSource SA
4 * mailto:contact AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.computation.source;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
26 import java.util.Random;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.sonar.api.utils.log.LogTester;
30 import org.sonar.db.protobuf.DbFileSources;
31 import org.sonar.scanner.protocol.output.ScannerReport;
32 import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType;
33 import org.sonar.scanner.protocol.output.ScannerReport.TextRange;
34 import org.sonar.server.computation.component.Component;
35 import org.sonar.server.computation.source.RangeOffsetConverter.RangeOffsetConverterException;
37 import static com.google.common.collect.ImmutableMap.of;
38 import static org.assertj.core.api.Assertions.assertThat;
39 import static org.mockito.Mockito.doThrow;
40 import static org.mockito.Mockito.mock;
41 import static org.mockito.Mockito.when;
42 import static org.sonar.api.utils.log.LoggerLevel.WARN;
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.server.computation.component.ReportComponent.builder;
50 public class HighlightingLineReaderTest {
53 public LogTester logTester = new LogTester();
55 static final Component FILE = builder(Component.Type.FILE, 1).setUuid("FILE_UUID").setKey("FILE_KEY").build();
57 static final int DEFAULT_LINE_LENGTH = 5;
59 static final int LINE_1 = 1;
60 static final int LINE_2 = 2;
61 static final int LINE_3 = 3;
62 static final int LINE_4 = 4;
64 static final String RANGE_LABEL_1 = "1,2";
65 static final String RANGE_LABEL_2 = "2,3";
66 static final String RANGE_LABEL_3 = "3,4";
67 static final String RANGE_LABEL_4 = "0,2";
68 static final String RANGE_LABEL_5 = "0,3";
70 RangeOffsetConverter rangeOffsetConverter = mock(RangeOffsetConverter.class);
72 DbFileSources.Data.Builder sourceData = newBuilder();
73 DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1);
74 DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2);
75 DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3);
76 DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4);
79 public void nothing_to_read() {
80 HighlightingLineReader highlightingLineReader = newReader(Collections.<TextRange, HighlightingType>emptyMap());
82 DbFileSources.Line.Builder lineBuilder = newBuilder().addLinesBuilder().setLine(1);
83 highlightingLineReader.read(lineBuilder);
85 assertThat(lineBuilder.hasHighlighting()).isFalse();
89 public void read_one_line() {
90 HighlightingLineReader highlightingLineReader = newReader(of(
91 newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION));
93 highlightingLineReader.read(line1);
95 assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
99 public void read_many_lines() {
100 HighlightingLineReader highlightingLineReader = newReader(of(
101 newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
102 newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_2), COMMENT,
103 newSingleLineTextRangeWithExpectingLabel(LINE_4, RANGE_LABEL_3), CONSTANT));
105 highlightingLineReader.read(line1);
106 highlightingLineReader.read(line2);
107 highlightingLineReader.read(line3);
108 highlightingLineReader.read(line4);
110 assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
111 assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",cd");
112 assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",c");
116 public void read_many_syntax_highlighting_on_same_line() {
117 HighlightingLineReader highlightingLineReader = newReader(of(
118 newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
119 newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_2), COMMENT));
121 highlightingLineReader.read(line1);
123 assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a;" + RANGE_LABEL_2 + ",cd");
127 public void read_one_syntax_highlighting_on_many_lines() {
128 // This highlighting begin on line 1 and finish on line 3
129 TextRange textRange = newTextRange(LINE_1, LINE_3);
130 when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
131 when(rangeOffsetConverter.offsetToString(textRange, LINE_2, 6)).thenReturn(RANGE_LABEL_2);
132 when(rangeOffsetConverter.offsetToString(textRange, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3);
134 HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION));
136 highlightingLineReader.read(line1);
137 DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line 2").setLine(2);
138 highlightingLineReader.read(line2);
139 highlightingLineReader.read(line3);
141 assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
142 assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a");
143 assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a");
147 public void read_many_syntax_highlighting_on_many_lines() {
148 TextRange textRange1 = newTextRange(LINE_1, LINE_3);
149 when(rangeOffsetConverter.offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
150 when(rangeOffsetConverter.offsetToString(textRange1, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
151 when(rangeOffsetConverter.offsetToString(textRange1, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3);
153 TextRange textRange2 = newTextRange(LINE_2, LINE_4);
154 when(rangeOffsetConverter.offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
155 when(rangeOffsetConverter.offsetToString(textRange2, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2);
156 when(rangeOffsetConverter.offsetToString(textRange2, LINE_4, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_4);
158 TextRange textRange3 = newTextRange(LINE_2, LINE_2);
159 when(rangeOffsetConverter.offsetToString(textRange3, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_5);
161 HighlightingLineReader highlightingLineReader = newReader(of(
162 textRange1, ANNOTATION,
163 textRange2, HIGHLIGHTING_STRING,
164 textRange3, COMMENT));
166 highlightingLineReader.read(line1);
167 highlightingLineReader.read(line2);
168 highlightingLineReader.read(line3);
169 highlightingLineReader.read(line4);
171 assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
172 assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a;" + RANGE_LABEL_2 + ",s;" + RANGE_LABEL_5 + ",cd");
173 assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a;" + RANGE_LABEL_2 + ",s");
174 assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_4 + ",s");
178 public void read_highlighting_declared_on_a_whole_line() {
179 TextRange textRange = newTextRange(LINE_1, LINE_2);
180 when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1);
181 when(rangeOffsetConverter.offsetToString(textRange, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn("");
183 HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION));
185 highlightingLineReader.read(line1);
186 highlightingLineReader.read(line2);
187 highlightingLineReader.read(line3);
189 assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
190 // Nothing should be set on line 2
191 assertThat(line2.getHighlighting()).isEmpty();
192 assertThat(line3.getHighlighting()).isEmpty();
196 public void not_fail_and_stop_processing_when_range_offset_converter_throw_RangeOffsetConverterException() {
197 TextRange textRange1 = newTextRange(LINE_1, LINE_1);
198 doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH);
200 HighlightingLineReader highlightingLineReader = newReader(of(
201 textRange1, HighlightingType.ANNOTATION,
202 newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_1), HIGHLIGHTING_STRING));
204 highlightingLineReader.read(line1);
205 highlightingLineReader.read(line2);
207 assertNoHighlighting();
208 assertThat(logTester.logs(WARN)).isNotEmpty();
212 public void keep_existing_processed_highlighting_when_range_offset_converter_throw_RangeOffsetConverterException() {
213 TextRange textRange2 = newTextRange(LINE_2, LINE_2);
214 doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH);
216 HighlightingLineReader highlightingLineReader = newReader(of(
217 newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
218 textRange2, HIGHLIGHTING_STRING));
220 highlightingLineReader.read(line1);
221 highlightingLineReader.read(line2);
223 assertThat(line1.hasHighlighting()).isTrue();
224 assertThat(line2.hasHighlighting()).isFalse();
225 assertThat(logTester.logs(WARN)).isNotEmpty();
229 public void display_file_key_in_warning_when_range_offset_converter_throw_RangeOffsetConverterException() {
230 TextRange textRange1 = newTextRange(LINE_1, LINE_1);
231 doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH);
232 HighlightingLineReader highlightingLineReader = newReader(of(textRange1, ANNOTATION));
234 highlightingLineReader.read(line1);
236 assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Highlighting data. Highlighting will be ignored for file 'FILE_KEY'");
239 private HighlightingLineReader newReader(Map<TextRange, HighlightingType> textRangeByType) {
240 List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList = new ArrayList<>();
241 for (Map.Entry<TextRange, HighlightingType> entry : textRangeByType.entrySet()) {
242 syntaxHighlightingList.add(ScannerReport.SyntaxHighlightingRule.newBuilder()
243 .setRange(entry.getKey())
244 .setType(entry.getValue())
247 return new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), rangeOffsetConverter);
250 private static TextRange newTextRange(int startLine, int enLine) {
251 Random random = new Random();
252 return TextRange.newBuilder()
253 .setStartLine(startLine).setEndLine(enLine)
254 // Offsets are not used by the reader
255 .setStartOffset(random.nextInt()).setEndOffset(random.nextInt())
259 private TextRange newSingleLineTextRangeWithExpectingLabel(int line, String rangeLabel) {
260 TextRange textRange = newTextRange(line, line);
261 when(rangeOffsetConverter.offsetToString(textRange, line, DEFAULT_LINE_LENGTH)).thenReturn(rangeLabel);
265 private void assertNoHighlighting() {
266 assertThat(line1.hasHighlighting()).isFalse();
267 assertThat(line2.hasHighlighting()).isFalse();
268 assertThat(line3.hasHighlighting()).isFalse();
269 assertThat(line4.hasHighlighting()).isFalse();