3 * Copyright (C) 2009-2023 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.server.telemetry;
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.sql.DatabaseMetaData;
26 import java.sql.SQLException;
27 import java.time.LocalDateTime;
28 import java.time.ZoneId;
29 import java.time.ZonedDateTime;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Optional;
34 import java.util.function.Consumer;
35 import java.util.function.Function;
36 import java.util.stream.Collectors;
37 import java.util.stream.IntStream;
38 import org.junit.Before;
39 import org.junit.Rule;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.sonar.api.config.Configuration;
43 import org.sonar.api.impl.utils.TestSystem2;
44 import org.sonar.core.platform.PlatformEditionProvider;
45 import org.sonar.core.platform.PluginInfo;
46 import org.sonar.core.platform.PluginRepository;
47 import org.sonar.db.DbSession;
48 import org.sonar.db.DbTester;
49 import org.sonar.db.alm.setting.AlmSettingDto;
50 import org.sonar.db.component.AnalysisPropertyDto;
51 import org.sonar.db.component.ComponentDto;
52 import org.sonar.db.component.SnapshotDto;
53 import org.sonar.db.metric.MetricDto;
54 import org.sonar.db.newcodeperiod.NewCodePeriodType;
55 import org.sonar.db.qualitygate.QualityGateDto;
56 import org.sonar.db.user.UserDbTester;
57 import org.sonar.db.user.UserDto;
58 import org.sonar.db.user.UserTelemetryDto;
59 import org.sonar.server.management.ManagedInstanceService;
60 import org.sonar.server.platform.DockerSupport;
61 import org.sonar.server.property.InternalProperties;
62 import org.sonar.server.property.MapInternalProperties;
63 import org.sonar.server.qualitygate.QualityGateCaycChecker;
64 import org.sonar.server.qualitygate.QualityGateFinder;
65 import org.sonar.server.telemetry.TelemetryData.Branch;
66 import org.sonar.server.telemetry.TelemetryData.NewCodeDefinition;
67 import org.sonar.server.telemetry.TelemetryData.ProjectStatistics;
68 import org.sonar.updatecenter.common.Version;
70 import static java.util.Arrays.asList;
71 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
72 import static org.assertj.core.api.Assertions.assertThat;
73 import static org.assertj.core.api.Assertions.entry;
74 import static org.assertj.core.groups.Tuple.tuple;
75 import static org.mockito.ArgumentMatchers.any;
76 import static org.mockito.Mockito.mock;
77 import static org.mockito.Mockito.spy;
78 import static org.mockito.Mockito.when;
79 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
80 import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
81 import static org.sonar.api.measures.CoreMetrics.COVERAGE_KEY;
82 import static org.sonar.api.measures.CoreMetrics.DEVELOPMENT_COST_KEY;
83 import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
84 import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
85 import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY;
86 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
87 import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
88 import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
89 import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDCI;
90 import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDSCM;
91 import static org.sonar.core.platform.EditionProvider.Edition.COMMUNITY;
92 import static org.sonar.core.platform.EditionProvider.Edition.DEVELOPER;
93 import static org.sonar.core.platform.EditionProvider.Edition.ENTERPRISE;
94 import static org.sonar.db.component.BranchType.BRANCH;
95 import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_CPP_KEY;
96 import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_C_KEY;
97 import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT;
99 @RunWith(DataProviderRunner.class)
100 public class TelemetryDataLoaderImplTest {
101 private final static Long NOW = 100_000_000L;
102 private final TestSystem2 system2 = new TestSystem2().setNow(NOW);
105 public DbTester db = DbTester.create(system2);
107 private final FakeServer server = new FakeServer();
108 private final PluginRepository pluginRepository = mock(PluginRepository.class);
109 private final Configuration configuration = mock(Configuration.class);
110 private final PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class);
111 private final DockerSupport dockerSupport = mock(DockerSupport.class);
112 private final QualityGateCaycChecker qualityGateCaycChecker = mock(QualityGateCaycChecker.class);
113 private final QualityGateFinder qualityGateFinder = new QualityGateFinder(db.getDbClient());
114 private final InternalProperties internalProperties = spy(new MapInternalProperties());
115 private final ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
116 private final CloudUsageDataProvider cloudUsageDataProvider = mock(CloudUsageDataProvider.class);
118 private final TelemetryDataLoader communityUnderTest = new TelemetryDataLoaderImpl(server, db.getDbClient(), pluginRepository, editionProvider,
119 internalProperties, configuration, dockerSupport, qualityGateCaycChecker, qualityGateFinder, managedInstanceService, cloudUsageDataProvider);
120 private final TelemetryDataLoader commercialUnderTest = new TelemetryDataLoaderImpl(server, db.getDbClient(), pluginRepository, editionProvider,
121 internalProperties, configuration, dockerSupport, qualityGateCaycChecker, qualityGateFinder, managedInstanceService, cloudUsageDataProvider);
123 private QualityGateDto builtInDefaultQualityGate;
124 private MetricDto bugsDto;
125 private MetricDto vulnerabilitiesDto;
126 private MetricDto securityHotspotsDto;
127 private MetricDto technicalDebtDto;
128 private MetricDto developmentCostDto;
131 public void setUpBuiltInQualityGate() {
132 String builtInQgName = "Sonar Way";
133 builtInDefaultQualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName(builtInQgName).setBuiltIn(true));
134 when(qualityGateCaycChecker.checkCaycCompliant(any(), any())).thenReturn(NON_COMPLIANT);
135 db.qualityGates().setDefaultQualityGate(builtInDefaultQualityGate);
137 bugsDto = db.measures().insertMetric(m -> m.setKey(BUGS_KEY));
138 vulnerabilitiesDto = db.measures().insertMetric(m -> m.setKey(VULNERABILITIES_KEY));
139 securityHotspotsDto = db.measures().insertMetric(m -> m.setKey(SECURITY_HOTSPOTS_KEY));
140 technicalDebtDto = db.measures().insertMetric(m -> m.setKey(TECHNICAL_DEBT_KEY));
141 developmentCostDto = db.measures().insertMetric(m -> m.setKey(DEVELOPMENT_COST_KEY));
145 public void send_telemetry_data() {
146 String serverId = "AU-TpxcB-iU5OvuD2FL7";
147 String version = "7.5.4";
148 Long analysisDate = 1L;
149 Long lastConnectionDate = 5L;
151 server.setId(serverId);
152 server.setVersion(version);
153 List<PluginInfo> plugins = asList(newPlugin("java", "4.12.0.11033"), newPlugin("scmgit", "1.2"), new PluginInfo("other"));
154 when(pluginRepository.getPluginInfos()).thenReturn(plugins);
155 when(editionProvider.get()).thenReturn(Optional.of(DEVELOPER));
157 List<UserDto> activeUsers = composeActiveUsers(3);
159 // update last connection
160 activeUsers.forEach(u -> db.users().updateLastConnectionDate(u, 5L));
162 UserDto inactiveUser = db.users().insertUser(u -> u.setActive(false).setExternalIdentityProvider("provider0"));
164 MetricDto lines = db.measures().insertMetric(m -> m.setKey(LINES_KEY));
165 MetricDto ncloc = db.measures().insertMetric(m -> m.setKey(NCLOC_KEY));
166 MetricDto coverage = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY));
167 MetricDto nclocDistrib = db.measures().insertMetric(m -> m.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY));
169 ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
170 db.measures().insertLiveMeasure(project1, lines, m -> m.setValue(110d));
171 db.measures().insertLiveMeasure(project1, ncloc, m -> m.setValue(110d));
172 db.measures().insertLiveMeasure(project1, coverage, m -> m.setValue(80d));
173 db.measures().insertLiveMeasure(project1, nclocDistrib, m -> m.setValue(null).setData("java=70;js=30;kotlin=10"));
174 db.measures().insertLiveMeasure(project1, bugsDto, m -> m.setValue(1d));
175 db.measures().insertLiveMeasure(project1, vulnerabilitiesDto, m -> m.setValue(1d).setData((String) null));
176 db.measures().insertLiveMeasure(project1, securityHotspotsDto, m -> m.setValue(1d).setData((String) null));
177 db.measures().insertLiveMeasure(project1, developmentCostDto, m -> m.setData("50").setValue(null));
178 db.measures().insertLiveMeasure(project1, technicalDebtDto, m -> m.setValue(5d).setData((String) null));
180 ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
181 db.measures().insertLiveMeasure(project2, lines, m -> m.setValue(200d));
182 db.measures().insertLiveMeasure(project2, ncloc, m -> m.setValue(200d));
183 db.measures().insertLiveMeasure(project2, coverage, m -> m.setValue(80d));
184 db.measures().insertLiveMeasure(project2, nclocDistrib, m -> m.setValue(null).setData("java=180;js=20"));
186 SnapshotDto project1Analysis = db.components().insertSnapshot(project1, t -> t.setLast(true).setBuildDate(analysisDate));
187 SnapshotDto project2Analysis = db.components().insertSnapshot(project2, t -> t.setLast(true).setBuildDate(analysisDate));
188 db.measures().insertMeasure(project1, project1Analysis, nclocDistrib, m -> m.setData("java=70;js=30;kotlin=10"));
189 db.measures().insertMeasure(project2, project2Analysis, nclocDistrib, m -> m.setData("java=180;js=20"));
191 insertAnalysisProperty(project1Analysis, "prop-uuid-1", SONAR_ANALYSIS_DETECTEDCI, "ci-1");
192 insertAnalysisProperty(project2Analysis, "prop-uuid-2", SONAR_ANALYSIS_DETECTEDCI, "ci-2");
193 insertAnalysisProperty(project1Analysis, "prop-uuid-3", SONAR_ANALYSIS_DETECTEDSCM, "scm-1");
194 insertAnalysisProperty(project2Analysis, "prop-uuid-4", SONAR_ANALYSIS_DETECTEDSCM, "scm-2");
197 db.almSettings().insertAzureAlmSetting();
198 db.almSettings().insertGitHubAlmSetting();
199 AlmSettingDto almSettingDto = db.almSettings().insertAzureAlmSetting(a -> a.setUrl("https://dev.azure.com"));
200 AlmSettingDto gitHubAlmSetting = db.almSettings().insertGitHubAlmSetting(a -> a.setUrl("https://api.github.com"));
201 db.almSettings().insertAzureProjectAlmSetting(almSettingDto, db.components().getProjectDtoByMainBranch(project1));
202 db.almSettings().insertGitlabProjectAlmSetting(gitHubAlmSetting, db.components().getProjectDtoByMainBranch(project2));
205 QualityGateDto qualityGate1 = db.qualityGates().insertQualityGate(qg -> qg.setName("QG1").setBuiltIn(true));
206 QualityGateDto qualityGate2 = db.qualityGates().insertQualityGate(qg -> qg.setName("QG2"));
208 // link one project to a non-default QG
209 db.qualityGates().associateProjectToQualityGate(db.components().getProjectDtoByMainBranch(project1), qualityGate1);
211 var branch1 = db.components().insertProjectBranch(project1, branchDto -> branchDto.setKey("reference"));
212 var branch2 = db.components().insertProjectBranch(project1, branchDto -> branchDto.setKey("custom"));
214 var ncd1 = db.newCodePeriods().insert(project1.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "30");
215 var ncd2 = db.newCodePeriods().insert(project1.uuid(), branch2.branchUuid(), NewCodePeriodType.REFERENCE_BRANCH, "reference");
217 var instanceNcdId = NewCodeDefinition.getInstanceDefault().hashCode();
218 var projectNcdId = new NewCodeDefinition(NewCodePeriodType.NUMBER_OF_DAYS.name(), "30", "project").hashCode();
219 var branchNcdId = new NewCodeDefinition(NewCodePeriodType.REFERENCE_BRANCH.name(), branch1.uuid(), "branch").hashCode();
221 TelemetryData data = communityUnderTest.load();
222 assertThat(data.getServerId()).isEqualTo(serverId);
223 assertThat(data.getVersion()).isEqualTo(version);
224 assertThat(data.getEdition()).contains(DEVELOPER);
225 assertThat(data.getDefaultQualityGate()).isEqualTo(builtInDefaultQualityGate.getUuid());
226 assertThat(data.getNcdId()).isEqualTo(NewCodeDefinition.getInstanceDefault().hashCode());
227 assertThat(data.getMessageSequenceNumber()).isOne();
228 assertDatabaseMetadata(data.getDatabase());
229 assertThat(data.getPlugins()).containsOnly(
230 entry("java", "4.12.0.11033"), entry("scmgit", "1.2"), entry("other", "undefined"));
231 assertThat(data.isInDocker()).isFalse();
233 assertThat(data.getUserTelemetries())
234 .extracting(UserTelemetryDto::getUuid, UserTelemetryDto::getLastConnectionDate, UserTelemetryDto::getLastSonarlintConnectionDate, UserTelemetryDto::isActive)
235 .containsExactlyInAnyOrder(
236 tuple(activeUsers.get(0).getUuid(), lastConnectionDate, activeUsers.get(0).getLastSonarlintConnectionDate(), true),
237 tuple(activeUsers.get(1).getUuid(), lastConnectionDate, activeUsers.get(1).getLastSonarlintConnectionDate(), true),
238 tuple(activeUsers.get(2).getUuid(), lastConnectionDate, activeUsers.get(2).getLastSonarlintConnectionDate(), true),
239 tuple(inactiveUser.getUuid(), null, inactiveUser.getLastSonarlintConnectionDate(), false));
240 assertThat(data.getProjects())
241 .extracting(TelemetryData.Project::projectUuid, TelemetryData.Project::language, TelemetryData.Project::loc, TelemetryData.Project::lastAnalysis)
242 .containsExactlyInAnyOrder(
243 tuple(project1.uuid(), "java", 70L, analysisDate),
244 tuple(project1.uuid(), "js", 30L, analysisDate),
245 tuple(project1.uuid(), "kotlin", 10L, analysisDate),
246 tuple(project2.uuid(), "java", 180L, analysisDate),
247 tuple(project2.uuid(), "js", 20L, analysisDate));
248 assertThat(data.getProjectStatistics())
249 .extracting(ProjectStatistics::getBranchCount, ProjectStatistics::getPullRequestCount, ProjectStatistics::getQualityGate,
250 ProjectStatistics::getScm, ProjectStatistics::getCi, ProjectStatistics::getDevopsPlatform,
251 ProjectStatistics::getBugs, ProjectStatistics::getVulnerabilities, ProjectStatistics::getSecurityHotspots,
252 ProjectStatistics::getDevelopmentCost, ProjectStatistics::getTechnicalDebt, ProjectStatistics::getNcdId)
253 .containsExactlyInAnyOrder(
254 tuple(3L, 0L, qualityGate1.getUuid(), "scm-1", "ci-1", "azure_devops_cloud", Optional.of(1L), Optional.of(1L), Optional.of(1L), Optional.of(50L), Optional.of(5L),
256 tuple(1L, 0L, builtInDefaultQualityGate.getUuid(), "scm-2", "ci-2", "github_cloud", Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(),
257 Optional.empty(), instanceNcdId));
259 assertThat(data.getBranches())
260 .extracting(Branch::branchUuid, Branch::ncdId)
261 .containsExactlyInAnyOrder(
262 tuple(branch1.uuid(), projectNcdId),
263 tuple(branch2.uuid(), branchNcdId),
264 tuple(project1.uuid(), projectNcdId),
265 tuple(project2.uuid(), instanceNcdId));
267 assertThat(data.getNewCodeDefinitions())
268 .extracting(NewCodeDefinition::scope, NewCodeDefinition::type, NewCodeDefinition::value)
269 .containsExactlyInAnyOrder(
270 tuple("instance", NewCodePeriodType.PREVIOUS_VERSION.name(), ""),
271 tuple("project", NewCodePeriodType.NUMBER_OF_DAYS.name(), "30"),
272 tuple("branch", NewCodePeriodType.REFERENCE_BRANCH.name(), branch1.uuid()));
274 assertThat(data.getQualityGates())
275 .extracting(TelemetryData.QualityGate::uuid, TelemetryData.QualityGate::caycStatus)
276 .containsExactlyInAnyOrder(
277 tuple(builtInDefaultQualityGate.getUuid(), "non-compliant"),
278 tuple(qualityGate1.getUuid(), "non-compliant"),
279 tuple(qualityGate2.getUuid(), "non-compliant")
284 public void send_branch_measures_data() {
285 Long analysisDate = ZonedDateTime.now(ZoneId.systemDefault()).toInstant().toEpochMilli();
287 MetricDto qg = db.measures().insertMetric(m -> m.setKey(ALERT_STATUS_KEY));
289 ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent();
291 ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
293 SnapshotDto project1Analysis1 = db.components().insertSnapshot(project1, t -> t.setLast(true).setBuildDate(analysisDate));
294 SnapshotDto project1Analysis2 = db.components().insertSnapshot(project1, t -> t.setLast(true).setBuildDate(analysisDate));
295 SnapshotDto project2Analysis = db.components().insertSnapshot(project2, t -> t.setLast(true).setBuildDate(analysisDate));
296 db.measures().insertMeasure(project1, project1Analysis1, qg, pm -> pm.setData("OK"));
297 db.measures().insertMeasure(project1, project1Analysis2, qg, pm -> pm.setData("ERROR"));
298 db.measures().insertMeasure(project2, project2Analysis, qg, pm -> pm.setData("ERROR"));
300 var branch1 = db.components().insertProjectBranch(project1, branchDto -> branchDto.setKey("reference"));
301 var branch2 = db.components().insertProjectBranch(project1, branchDto -> branchDto.setKey("custom"));
303 db.newCodePeriods().insert(project1.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "30");
304 db.newCodePeriods().insert(project1.uuid(), branch2.branchUuid(), NewCodePeriodType.REFERENCE_BRANCH, "reference");
306 var instanceNcdId = NewCodeDefinition.getInstanceDefault().hashCode();
307 var projectNcdId = new NewCodeDefinition(NewCodePeriodType.NUMBER_OF_DAYS.name(), "30", "project").hashCode();
308 var branchNcdId = new NewCodeDefinition(NewCodePeriodType.REFERENCE_BRANCH.name(), branch1.uuid(), "branch").hashCode();
310 TelemetryData data = communityUnderTest.load();
312 assertThat(data.getBranches())
313 .extracting(Branch::branchUuid, Branch::ncdId, Branch::greenQualityGateCount, Branch::analysisCount)
314 .containsExactlyInAnyOrder(
315 tuple(branch1.uuid(), projectNcdId, 0, 0),
316 tuple(branch2.uuid(), branchNcdId, 0, 0),
317 tuple(project1.uuid(), projectNcdId, 1, 2),
318 tuple(project2.uuid(), instanceNcdId, 0, 1));
322 private List<UserDto> composeActiveUsers(int count) {
323 UserDbTester userDbTester = db.users();
324 Function<Integer, Consumer<UserDto>> userConfigurator = index -> user -> user.setExternalIdentityProvider("provider" + index).setLastSonarlintConnectionDate(index * 2L);
327 .rangeClosed(1, count)
328 .mapToObj(userConfigurator::apply)
329 .map(userDbTester::insertUser)
330 .collect(Collectors.toList());
333 private void assertDatabaseMetadata(TelemetryData.Database database) {
334 try (DbSession dbSession = db.getDbClient().openSession(false)) {
335 DatabaseMetaData metadata = dbSession.getConnection().getMetaData();
336 assertThat(database.name()).isEqualTo("H2");
337 assertThat(database.version()).isEqualTo(metadata.getDatabaseProductVersion());
338 } catch (SQLException e) {
339 throw new RuntimeException(e);
344 public void take_largest_branch_snapshot_project_data() {
345 server.setId("AU-TpxcB-iU5OvuD2FL7").setVersion("7.5.4");
347 MetricDto lines = db.measures().insertMetric(m -> m.setKey(LINES_KEY));
348 MetricDto ncloc = db.measures().insertMetric(m -> m.setKey(NCLOC_KEY));
349 MetricDto coverage = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY));
350 MetricDto nclocDistrib = db.measures().insertMetric(m -> m.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY));
352 ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
353 db.measures().insertLiveMeasure(project, lines, m -> m.setValue(110d));
354 db.measures().insertLiveMeasure(project, ncloc, m -> m.setValue(110d));
355 db.measures().insertLiveMeasure(project, coverage, m -> m.setValue(80d));
356 db.measures().insertLiveMeasure(project, nclocDistrib, m -> m.setValue(null).setData("java=70;js=30;kotlin=10"));
358 ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
359 db.measures().insertLiveMeasure(branch, lines, m -> m.setValue(180d));
360 db.measures().insertLiveMeasure(branch, ncloc, m -> m.setValue(180d));
361 db.measures().insertLiveMeasure(branch, coverage, m -> m.setValue(80d));
362 db.measures().insertLiveMeasure(branch, nclocDistrib, m -> m.setValue(null).setData("java=100;js=50;kotlin=30"));
364 SnapshotDto project1Analysis = db.components().insertSnapshot(project, t -> t.setLast(true));
365 SnapshotDto project2Analysis = db.components().insertSnapshot(branch, t -> t.setLast(true));
366 db.measures().insertMeasure(project, project1Analysis, nclocDistrib, m -> m.setData("java=70;js=30;kotlin=10"));
367 db.measures().insertMeasure(branch, project2Analysis, nclocDistrib, m -> m.setData("java=100;js=50;kotlin=30"));
369 TelemetryData data = communityUnderTest.load();
371 assertThat(data.getProjects()).extracting(TelemetryData.Project::projectUuid, TelemetryData.Project::language, TelemetryData.Project::loc)
372 .containsExactlyInAnyOrder(
373 tuple(project.uuid(), "java", 100L),
374 tuple(project.uuid(), "js", 50L),
375 tuple(project.uuid(), "kotlin", 30L));
376 assertThat(data.getProjectStatistics())
377 .extracting(ProjectStatistics::getBranchCount, ProjectStatistics::getPullRequestCount,
378 ProjectStatistics::getScm, ProjectStatistics::getCi)
379 .containsExactlyInAnyOrder(
380 tuple(2L, 0L, "undetected", "undetected"));
384 public void test_ncd_on_community_edition() {
385 server.setId("AU-TpxcB-iU5OvuD2FL7").setVersion("7.5.4");
386 when(editionProvider.get()).thenReturn(Optional.of(COMMUNITY));
388 ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
390 ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
392 db.newCodePeriods().insert(project.uuid(), branch.branchUuid(), NewCodePeriodType.NUMBER_OF_DAYS, "30");
394 var projectNcdId = new NewCodeDefinition(NewCodePeriodType.NUMBER_OF_DAYS.name(), "30", "project").hashCode();
396 TelemetryData data = communityUnderTest.load();
398 assertThat(data.getProjectStatistics())
399 .extracting(ProjectStatistics::getBranchCount, ProjectStatistics::getNcdId)
400 .containsExactlyInAnyOrder(tuple(2L, projectNcdId));
402 assertThat(data.getBranches())
403 .extracting(Branch::branchUuid, Branch::ncdId)
404 .contains(tuple(branch.uuid(), projectNcdId));
408 public void data_contains_weekly_count_sonarlint_users() {
409 db.users().insertUser(c -> c.setLastSonarlintConnectionDate(NOW - 100_000L));
410 db.users().insertUser(c -> c.setLastSonarlintConnectionDate(NOW));
412 db.users().insertUser(c -> c.setLastSonarlintConnectionDate(NOW - 1_000_000_000L));
413 db.users().insertUser();
415 TelemetryData data = communityUnderTest.load();
416 assertThat(data.getUserTelemetries())
421 public void send_server_id_and_version() {
422 String id = randomAlphanumeric(40);
423 String version = randomAlphanumeric(10);
425 server.setVersion(version);
427 TelemetryData data = communityUnderTest.load();
428 assertThat(data.getServerId()).isEqualTo(id);
429 assertThat(data.getVersion()).isEqualTo(version);
431 data = commercialUnderTest.load();
432 assertThat(data.getServerId()).isEqualTo(id);
433 assertThat(data.getVersion()).isEqualTo(version);
437 public void send_server_installation_date_and_installation_version() {
438 String installationVersion = "7.9.BEST.LTS.EVER";
439 Long installationDate = 1546300800000L; // 2019/01/01
440 internalProperties.write(InternalProperties.INSTALLATION_DATE, String.valueOf(installationDate));
441 internalProperties.write(InternalProperties.INSTALLATION_VERSION, installationVersion);
443 TelemetryData data = communityUnderTest.load();
445 assertThat(data.getInstallationDate()).isEqualTo(installationDate);
446 assertThat(data.getInstallationVersion()).isEqualTo(installationVersion);
450 public void send_correct_sequence_number() {
451 internalProperties.write(TelemetryDaemon.I_PROP_MESSAGE_SEQUENCE, "10");
452 TelemetryData data = communityUnderTest.load();
453 assertThat(data.getMessageSequenceNumber()).isEqualTo(11L);
457 public void do_not_send_server_installation_details_if_missing_property() {
458 TelemetryData data = communityUnderTest.load();
459 assertThat(data.getInstallationDate()).isNull();
460 assertThat(data.getInstallationVersion()).isNull();
462 data = commercialUnderTest.load();
463 assertThat(data.getInstallationDate()).isNull();
464 assertThat(data.getInstallationVersion()).isNull();
468 public void send_unanalyzed_languages_flags_when_edition_is_community() {
469 when(editionProvider.get()).thenReturn(Optional.of(COMMUNITY));
470 MetricDto unanalyzedC = db.measures().insertMetric(m -> m.setKey(UNANALYZED_C_KEY));
471 MetricDto unanalyzedCpp = db.measures().insertMetric(m -> m.setKey(UNANALYZED_CPP_KEY));
472 ComponentDto project1 = db.components().insertPublicProject().getMainBranchComponent();
473 ComponentDto project2 = db.components().insertPublicProject().getMainBranchComponent();
474 db.measures().insertLiveMeasure(project1, unanalyzedC);
475 db.measures().insertLiveMeasure(project2, unanalyzedC);
476 db.measures().insertLiveMeasure(project2, unanalyzedCpp);
478 TelemetryData data = communityUnderTest.load();
480 assertThat(data.hasUnanalyzedC().get()).isTrue();
481 assertThat(data.hasUnanalyzedCpp().get()).isTrue();
485 public void do_not_send_unanalyzed_languages_flags_when_edition_is_not_community() {
486 when(editionProvider.get()).thenReturn(Optional.of(DEVELOPER));
487 MetricDto unanalyzedC = db.measures().insertMetric(m -> m.setKey(UNANALYZED_C_KEY));
488 MetricDto unanalyzedCpp = db.measures().insertMetric(m -> m.setKey(UNANALYZED_CPP_KEY));
489 ComponentDto project1 = db.components().insertPublicProject().getMainBranchComponent();
490 ComponentDto project2 = db.components().insertPublicProject().getMainBranchComponent();
491 db.measures().insertLiveMeasure(project1, unanalyzedC);
492 db.measures().insertLiveMeasure(project2, unanalyzedCpp);
494 TelemetryData data = communityUnderTest.load();
496 assertThat(data.hasUnanalyzedC()).isEmpty();
497 assertThat(data.hasUnanalyzedCpp()).isEmpty();
501 public void unanalyzed_languages_flags_are_set_to_false_when_no_unanalyzed_languages_and_edition_is_community() {
502 when(editionProvider.get()).thenReturn(Optional.of(COMMUNITY));
504 TelemetryData data = communityUnderTest.load();
506 assertThat(data.hasUnanalyzedC().get()).isFalse();
507 assertThat(data.hasUnanalyzedCpp().get()).isFalse();
511 public void populate_security_custom_config_for_languages_on_enterprise() {
512 when(editionProvider.get()).thenReturn(Optional.of(ENTERPRISE));
514 when(configuration.get("sonar.security.config.javasecurity")).thenReturn(Optional.of("{}"));
515 when(configuration.get("sonar.security.config.phpsecurity")).thenReturn(Optional.of("{}"));
516 when(configuration.get("sonar.security.config.pythonsecurity")).thenReturn(Optional.of("{}"));
517 when(configuration.get("sonar.security.config.roslyn.sonaranalyzer.security.cs")).thenReturn(Optional.of("{}"));
519 TelemetryData data = commercialUnderTest.load();
521 assertThat(data.getCustomSecurityConfigs())
522 .containsExactlyInAnyOrder("java", "php", "python", "csharp");
526 public void skip_security_custom_config_on_community() {
527 when(editionProvider.get()).thenReturn(Optional.of(COMMUNITY));
529 TelemetryData data = communityUnderTest.load();
531 assertThat(data.getCustomSecurityConfigs()).isEmpty();
535 public void undetected_alm_ci_slm_data() {
536 server.setId("AU-TpxcB-iU5OvuD2FL7").setVersion("7.5.4");
537 db.components().insertPublicProject().getMainBranchComponent();
538 TelemetryData data = communityUnderTest.load();
539 assertThat(data.getProjectStatistics())
540 .extracting(ProjectStatistics::getDevopsPlatform, ProjectStatistics::getScm, ProjectStatistics::getCi)
541 .containsExactlyInAnyOrder(tuple("undetected", "undetected", "undetected"));
545 @UseDataProvider("getManagedInstanceData")
546 public void managedInstanceData_containsCorrectInformation(boolean isManaged, String provider) {
547 when(managedInstanceService.isInstanceExternallyManaged()).thenReturn(isManaged);
548 when(managedInstanceService.getProviderName()).thenReturn(provider);
550 TelemetryData data = commercialUnderTest.load();
552 TelemetryData.ManagedInstanceInformation managedInstance = data.getManagedInstanceInformation();
553 assertThat(managedInstance.isManaged()).isEqualTo(isManaged);
554 assertThat(managedInstance.provider()).isEqualTo(provider);
558 public void default_quality_gate_sent_with_project() {
559 db.components().insertPublicProject().getMainBranchComponent();
560 QualityGateDto qualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName("anything").setBuiltIn(true));
561 db.qualityGates().setDefaultQualityGate(qualityGate);
562 TelemetryData data = communityUnderTest.load();
563 assertThat(data.getProjectStatistics())
564 .extracting(ProjectStatistics::getQualityGate)
565 .containsOnly(qualityGate.getUuid());
568 private PluginInfo newPlugin(String key, String version) {
569 return new PluginInfo(key)
570 .setVersion(Version.create(version));
573 private void insertAnalysisProperty(SnapshotDto snapshotDto, String uuid, String key, String value) {
574 db.getDbClient().analysisPropertiesDao().insert(db.getSession(), new AnalysisPropertyDto()
576 .setAnalysisUuid(snapshotDto.getUuid())
583 public static Set<String> getScimFeatureStatues() {
584 HashSet<String> result = new HashSet<>();
592 public static Object[][] getManagedInstanceData() {
593 return new Object[][] {