3 * Copyright (C) 2009-2024 SonarSource SA
4 * mailto:info 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.ce.task.projectanalysis.source;
22 import com.tngtech.java.junit.dataprovider.DataProvider;
23 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
24 import com.tngtech.java.junit.dataprovider.UseDataProvider;
25 import java.util.Arrays;
26 import java.util.Random;
27 import java.util.stream.Collectors;
28 import java.util.stream.IntStream;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.sonar.api.utils.System2;
32 import org.sonar.ce.task.log.CeTaskMessages;
33 import org.sonar.ce.task.projectanalysis.component.Component;
34 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
35 import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
37 import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
38 import static org.assertj.core.api.Assertions.assertThatThrownBy;
39 import static org.mockito.Mockito.mock;
40 import static org.mockito.Mockito.times;
41 import static org.mockito.Mockito.verify;
42 import static org.mockito.Mockito.verifyNoInteractions;
43 import static org.mockito.Mockito.when;
44 import static org.sonar.ce.task.projectanalysis.source.linereader.LineReader.Data.HIGHLIGHTING;
45 import static org.sonar.ce.task.projectanalysis.source.linereader.LineReader.Data.SYMBOLS;
47 @RunWith(DataProviderRunner.class)
48 public class FileSourceDataWarningsTest {
50 private CeTaskMessages taskMessages = mock(CeTaskMessages.class);
51 private System2 system2 = mock(System2.class);
52 private Random random = new Random();
53 private int line = 1 + new Random().nextInt(200);
54 private long timeStamp = 9_887L + new Random().nextInt(300);
55 private String path = randomAlphabetic(50);
57 private FileSourceDataWarnings underTest = new FileSourceDataWarnings(taskMessages, system2);
60 public void addWarning_fails_with_NPE_if_file_is_null() {
61 LineReader.ReadError readError = new LineReader.ReadError(HIGHLIGHTING, 2);
63 assertThatThrownBy(() -> underTest.addWarning(null, readError))
64 .isInstanceOf(NullPointerException.class)
65 .hasMessage("file can't be null");
69 public void addWarning_fails_with_NPE_if_readError_is_null() {
70 Component component = mock(Component.class);
72 assertThatThrownBy(() -> underTest.addWarning(component, null))
73 .isInstanceOf(NullPointerException.class)
74 .hasMessage("readError can't be null");
78 public void addWarnings_fails_with_ISE_if_called_after_commitWarnings() {
79 underTest.commitWarnings();
81 assertThatThrownBy(() -> underTest.addWarning(null /*doesn't matter*/, null /*doesn't matter*/))
82 .isInstanceOf(IllegalStateException.class)
83 .hasMessage("warnings already commit");
87 public void commitWarnings_fails_with_ISE_if_called_after_commitWarnings() {
88 underTest.commitWarnings();
90 assertThatThrownBy(() -> underTest.commitWarnings())
91 .isInstanceOf(IllegalStateException.class)
92 .hasMessage("warnings already commit");
96 public void create_highlighting_warning_when_one_file_HIGHLIGHT_read_error() {
97 ReportComponent file = ReportComponent.builder(Component.Type.FILE, 1)
101 LineReader.ReadError readError = new LineReader.ReadError(HIGHLIGHTING, line);
102 when(system2.now()).thenReturn(timeStamp);
104 underTest.addWarning(file, readError);
106 verifyNoInteractions(taskMessages);
108 underTest.commitWarnings();
110 verify(taskMessages, times(1))
111 .add(new CeTaskMessages.Message("Inconsistent highlighting data detected on file '" + path + "'. " +
112 "File source may have been modified while analysis was running.", timeStamp));
116 public void create_highlighting_warning_when_any_number_of_read_error_for_one_file() {
117 ReportComponent file = ReportComponent.builder(Component.Type.FILE, 1)
121 LineReader.ReadError[] readErrors = IntStream.range(0, 1 + random.nextInt(10))
122 .mapToObj(i -> new LineReader.ReadError(HIGHLIGHTING, line + i))
123 .toArray(LineReader.ReadError[]::new);
124 when(system2.now()).thenReturn(timeStamp);
126 Arrays.stream(readErrors).forEach(readError -> underTest.addWarning(file, readError));
128 verifyNoInteractions(taskMessages);
130 underTest.commitWarnings();
132 verify(taskMessages, times(1))
133 .add(new CeTaskMessages.Message("Inconsistent highlighting data detected on file '" + path + "'. " +
134 "File source may have been modified while analysis was running.", timeStamp));
138 public void create_highlighting_warning_when_any_number_of_read_error_for_less_than_5_files() {
139 int fileCount = 2 + random.nextInt(3);
140 Component[] files = IntStream.range(0, fileCount)
141 .mapToObj(i -> ReportComponent.builder(Component.Type.FILE, i)
142 .setUuid("uuid_" + i)
143 .setName(path + "_" + i)
145 .toArray(Component[]::new);
146 when(system2.now()).thenReturn(timeStamp);
148 Arrays.stream(files).forEach(file -> IntStream.range(0, 1 + random.nextInt(10))
149 .forEach(i -> underTest.addWarning(file, new LineReader.ReadError(HIGHLIGHTING, line + i))));
151 verifyNoInteractions(taskMessages);
153 underTest.commitWarnings();
155 String expectedMessage = "Inconsistent highlighting data detected on some files (" + fileCount + " in total). " +
156 "File source may have been modified while analysis was running." +
157 Arrays.stream(files).map(Component::getName).collect(Collectors.joining("\n ° ", "\n ° ", ""));
158 verify(taskMessages, times(1))
159 .add(new CeTaskMessages.Message(expectedMessage, timeStamp));
163 public void create_highlighting_warning_when_any_number_of_read_error_for_more_than_5_files_only_the_5_first_by_ref() {
164 int fileCount = 6 + random.nextInt(4);
165 Component[] files = IntStream.range(0, fileCount)
166 .mapToObj(i -> ReportComponent.builder(Component.Type.FILE, i)
167 .setUuid("uuid_" + i)
168 .setName(path + "_" + i)
170 .toArray(Component[]::new);
171 when(system2.now()).thenReturn(timeStamp);
173 Arrays.stream(files).forEach(file -> IntStream.range(0, 1 + random.nextInt(10))
174 .forEach(i -> underTest.addWarning(file, new LineReader.ReadError(HIGHLIGHTING, line + i))));
176 verifyNoInteractions(taskMessages);
178 underTest.commitWarnings();
180 String expectedMessage = "Inconsistent highlighting data detected on some files (" + fileCount + " in total). " +
181 "File source may have been modified while analysis was running." +
182 Arrays.stream(files).limit(5).map(Component::getName).collect(Collectors.joining("\n ° ", "\n ° ", ""));
183 verify(taskMessages, times(1))
184 .add(new CeTaskMessages.Message(expectedMessage, timeStamp));
188 public void create_symbol_warning_when_one_file_HIGHLIGHT_read_error() {
189 ReportComponent file = ReportComponent.builder(Component.Type.FILE, 1)
193 LineReader.ReadError readError = new LineReader.ReadError(SYMBOLS, line);
194 when(system2.now()).thenReturn(timeStamp);
196 underTest.addWarning(file, readError);
198 verifyNoInteractions(taskMessages);
200 underTest.commitWarnings();
202 verify(taskMessages, times(1))
203 .add(new CeTaskMessages.Message("Inconsistent symbol data detected on file '" + path + "'. " +
204 "File source may have been modified while analysis was running.", timeStamp));
208 public void create_symbol_warning_when_any_number_of_read_error_for_one_file() {
209 ReportComponent file = ReportComponent.builder(Component.Type.FILE, 1)
213 LineReader.ReadError[] readErrors = IntStream.range(0, 1 + random.nextInt(10))
214 .mapToObj(i -> new LineReader.ReadError(SYMBOLS, line + i))
215 .toArray(LineReader.ReadError[]::new);
216 when(system2.now()).thenReturn(timeStamp);
218 Arrays.stream(readErrors).forEach(readError -> underTest.addWarning(file, readError));
220 verifyNoInteractions(taskMessages);
222 underTest.commitWarnings();
224 verify(taskMessages, times(1))
225 .add(new CeTaskMessages.Message("Inconsistent symbol data detected on file '" + path + "'. " +
226 "File source may have been modified while analysis was running.", timeStamp));
230 public void create_symbol_warning_when_any_number_of_read_error_for_less_than_5_files() {
231 int fileCount = 2 + random.nextInt(3);
232 Component[] files = IntStream.range(0, fileCount)
233 .mapToObj(i -> ReportComponent.builder(Component.Type.FILE, i)
234 .setUuid("uuid_" + i)
235 .setName(path + "_" + i)
237 .toArray(Component[]::new);
238 when(system2.now()).thenReturn(timeStamp);
240 Arrays.stream(files).forEach(file -> IntStream.range(0, 1 + random.nextInt(10))
241 .forEach(i -> underTest.addWarning(file, new LineReader.ReadError(SYMBOLS, line + i))));
243 verifyNoInteractions(taskMessages);
245 underTest.commitWarnings();
247 String expectedMessage = "Inconsistent symbol data detected on some files (" + fileCount + " in total). " +
248 "File source may have been modified while analysis was running." +
249 Arrays.stream(files).map(Component::getName).collect(Collectors.joining("\n ° ", "\n ° ", ""));
250 verify(taskMessages, times(1))
251 .add(new CeTaskMessages.Message(expectedMessage, timeStamp));
255 public void create_symbol_warning_when_any_number_of_read_error_for_more_than_5_files_only_the_5_first_by_ref() {
256 int fileCount = 6 + random.nextInt(4);
257 Component[] files = IntStream.range(0, fileCount)
258 .mapToObj(i -> ReportComponent.builder(Component.Type.FILE, i)
259 .setUuid("uuid_" + i)
260 .setName(path + "_" + i)
262 .toArray(Component[]::new);
263 when(system2.now()).thenReturn(timeStamp);
265 Arrays.stream(files).forEach(file -> IntStream.range(0, 1 + random.nextInt(10))
266 .forEach(i -> underTest.addWarning(file, new LineReader.ReadError(SYMBOLS, line + i))));
268 verifyNoInteractions(taskMessages);
270 underTest.commitWarnings();
272 String expectedMessage = "Inconsistent symbol data detected on some files (" + fileCount + " in total). " +
273 "File source may have been modified while analysis was running." +
274 Arrays.stream(files).limit(5).map(Component::getName).collect(Collectors.joining("\n ° ", "\n ° ", ""));
275 verify(taskMessages, times(1))
276 .add(new CeTaskMessages.Message(expectedMessage, timeStamp));
280 @UseDataProvider("anyDataButHighlightAndSymbols")
281 public void creates_no_warning_when_read_error_for_anything_but_highlighting_and_symbols(LineReader.Data data) {
282 ReportComponent file = ReportComponent.builder(Component.Type.FILE, 1)
286 LineReader.ReadError readError = new LineReader.ReadError(data, line);
287 when(system2.now()).thenReturn(timeStamp);
289 underTest.addWarning(file, readError);
291 verifyNoInteractions(taskMessages);
293 underTest.commitWarnings();
295 verifyNoInteractions(taskMessages);
299 public static Object[][] anyDataButHighlightAndSymbols() {
300 return Arrays.stream(LineReader.Data.values())
301 .filter(t -> t != HIGHLIGHTING && t != SYMBOLS)
302 .map(t -> new Object[] {t})
303 .toArray(Object[][]::new);