]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22649 Add db populator logic.
authorSteve Marion <steve.marion@sonarsource.com>
Wed, 31 Jul 2024 13:19:17 +0000 (15:19 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 12 Aug 2024 20:02:46 +0000 (20:02 +0000)
server/sonar-db-dao/src/test/java/org/sonar/db/createdb/PopulateDb.java [new file with mode: 0644]
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/measure/MeasureDbTester.java
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/measure/MeasureTesting.java

diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/createdb/PopulateDb.java b/server/sonar-db-dao/src/test/java/org/sonar/db/createdb/PopulateDb.java
new file mode 100644 (file)
index 0000000..884bdfc
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.db.createdb;
+
+import com.google.common.collect.Streams;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.jetbrains.annotations.NotNull;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.component.SnapshotTesting;
+import org.sonar.db.issue.IssueDto;
+import org.sonar.db.metric.MetricDto;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.rule.RuleDto;
+
+import static org.sonar.db.component.BranchType.BRANCH;
+
+public class PopulateDb {
+  public static void main(String[] args) {
+    final DbTester dbTester = createDbTester();
+
+    // read base data
+    final Map<String, MetricDto> metricDtosByKey;
+    final List<ProjectDto> allProjects;
+    final Set<RuleDto> enabledRules;
+
+    DbSession initSession = dbTester.getSession();
+    metricDtosByKey = dbTester.getDbClient().metricDao().selectAll(initSession).stream().collect(
+      Collectors.toMap(MetricDto::getKey, Function.identity())
+    );
+    allProjects = dbTester.getDbClient().projectDao().selectProjects(initSession);
+    enabledRules = new HashSet<>(dbTester.getDbClient().ruleDao().selectEnabled(dbTester.getSession()));
+
+    generateProject(new SqContext(enabledRules, metricDtosByKey, dbTester),
+      new ProjectStructure("project " + (allProjects.size() + 1), 10, 10, 10, 2, 5));
+
+    // close database connexion
+    dbTester.getDbClient().getDatabase().stop();
+  }
+
+  private record SqContext(Set<RuleDto> rules, Map<String, MetricDto> metricDtosByKey, DbTester dbTester) {
+    public RuleDto findNotSecurityHotspotRule() {
+      return rules.stream().filter(r -> r.getType() != RuleType.SECURITY_HOTSPOT.getDbConstant()).findAny().orElseThrow();
+    }
+  }
+
+  private static @NotNull DbTester createDbTester() {
+    System.setProperty("sonar.jdbc.url", "jdbc:postgresql://localhost:5432/sonarqube");
+    System.setProperty("sonar.jdbc.username", "sonarqube");
+    System.setProperty("sonar.jdbc.password", "sonarqube");
+    System.setProperty("sonar.jdbc.dialect", "postgresql");
+
+    return DbTester.create(System2.INSTANCE);
+  }
+
+  private record ProjectStructure(String projectName, int branchPerProject, int filePerBranch, int issuePerFile, int issueChangePerIssue,
+                                  int snapshotPerBranch) {
+  }
+
+  private record BranchAndComponentDto(BranchDto branch, ComponentDto compo) {
+  }
+
+  private static ProjectDto generateProject(SqContext sqContext, ProjectStructure pj) {
+    ComponentDto projectCompoDto = sqContext.dbTester.components().insertPublicProject(p -> p.setName(pj.projectName));
+    ProjectDto projectDto = sqContext.dbTester.components().getProjectDto(projectCompoDto);
+
+
+    Streams.concat(
+        // main branch
+        Stream.of(new BranchAndComponentDto(
+          sqContext.dbTester.getDbClient().branchDao().selectByBranchKey(sqContext.dbTester.getSession(), projectDto.getUuid(), "main").orElseThrow(),
+          projectCompoDto)),
+        // other branches
+        Stream.generate(() -> {
+          var branchDto = ComponentTesting.newBranchDto(projectDto.getUuid(), BRANCH);
+          return new BranchAndComponentDto(
+            branchDto,
+            sqContext.dbTester.components().insertProjectBranch(projectDto, branchDto)
+          );
+        }))
+      // until there are enough branches generated
+      .limit(pj.branchPerProject)
+      // for every branche (main included)
+      .forEach(branchAndComponentDto -> {
+        // for every file in branch
+        for (int file = 0; file < pj.filePerBranch; file++) {
+          ComponentDto fileComponentDto = sqContext.dbTester.components().insertFile(branchAndComponentDto.compo);
+          // for every issue in file
+          for (int issue = 0; issue < pj.issuePerFile; issue++) {
+            IssueDto issueDto = sqContext.dbTester.issues().insertIssue(sqContext.findNotSecurityHotspotRule(), branchAndComponentDto.compo, fileComponentDto);
+            // for every issue change in issue
+            for (int issueChange = 0; issueChange < pj.issueChangePerIssue; issueChange++) {
+              sqContext.dbTester.issues().insertChange(issueDto);
+            }
+          }
+          // create live measure for this file
+          fileLiveMeasureMetrics.stream()
+            .map(sqContext.metricDtosByKey::get)
+            .forEach(metricDto -> sqContext.dbTester().measures().insertLiveMeasureWithSensibleValues(fileComponentDto, metricDto));
+        }
+
+        // create live measure for the branch
+        projectLiveMeasureMetrics.stream()
+          .map(sqContext.metricDtosByKey::get)
+          .forEach(metricDto -> sqContext.dbTester().measures().insertLiveMeasureWithSensibleValues(branchAndComponentDto.compo, metricDto));
+
+        long time = System2.INSTANCE.now();
+        List<SnapshotDto> snapshots = new ArrayList<>();
+        // for every snapshot on the current branch
+        for (int snapshot = 0; snapshot < pj.snapshotPerBranch; snapshot++) {
+          SnapshotDto snapshotDto = SnapshotTesting.newAnalysis(branchAndComponentDto.branch);
+          snapshotDto.setLast(false);
+          snapshotDto.setCreatedAt(time);
+          time -= 10_000_000;
+          snapshots.add(snapshotDto);
+          // insert project measure for the snapshot
+          projectProjectMeasureMetrics.stream()
+            .map(sqContext.metricDtosByKey::get)
+            .forEach(metricDto -> sqContext.dbTester().measures().insertMeasureWithSensibleValues(branchAndComponentDto.compo, snapshotDto, metricDto));
+        }
+        SnapshotDto lastSnapshotDto = snapshots.get(0);
+        lastSnapshotDto.setLast(true);
+        sqContext.dbTester.components().insertSnapshots(snapshots.toArray(new SnapshotDto[0]));
+      });
+
+    return projectDto;
+  }
+
+  private static final List<String> projectLiveMeasureMetrics = List.of(
+    CoreMetrics.LINES_KEY,
+    CoreMetrics.NCLOC_KEY,
+    CoreMetrics.NEW_LINES_KEY,
+    CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY,
+    CoreMetrics.CLASSES_KEY,
+    CoreMetrics.FILES_KEY,
+    CoreMetrics.FUNCTIONS_KEY,
+    CoreMetrics.STATEMENTS_KEY,
+    CoreMetrics.COMMENT_LINES_KEY,
+    CoreMetrics.COMMENT_LINES_DENSITY_KEY,
+    CoreMetrics.COMPLEXITY_KEY,
+    CoreMetrics.COGNITIVE_COMPLEXITY_KEY,
+    CoreMetrics.COVERAGE_KEY,
+    CoreMetrics.LINES_TO_COVER_KEY,
+    CoreMetrics.NEW_LINES_TO_COVER_KEY,
+    CoreMetrics.NEW_UNCOVERED_LINES_KEY,
+    CoreMetrics.LINE_COVERAGE_KEY,
+    CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY,
+    CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY,
+    CoreMetrics.DUPLICATED_LINES_KEY,
+    CoreMetrics.NEW_DUPLICATED_LINES_KEY,
+    CoreMetrics.UNCOVERED_LINES_KEY,
+    CoreMetrics.DUPLICATED_BLOCKS_KEY,
+    CoreMetrics.NEW_BLOCKS_DUPLICATED_KEY,
+    CoreMetrics.DUPLICATED_FILES_KEY,
+    CoreMetrics.DUPLICATED_LINES_DENSITY_KEY,
+    CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY,
+    CoreMetrics.VIOLATIONS_KEY,
+    CoreMetrics.BLOCKER_VIOLATIONS_KEY,
+    CoreMetrics.CRITICAL_VIOLATIONS_KEY,
+    CoreMetrics.MAJOR_VIOLATIONS_KEY,
+    CoreMetrics.MINOR_VIOLATIONS_KEY,
+    CoreMetrics.INFO_VIOLATIONS_KEY,
+    CoreMetrics.NEW_VIOLATIONS_KEY,
+    CoreMetrics.NEW_BLOCKER_VIOLATIONS_KEY,
+    CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY,
+    CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY,
+    CoreMetrics.NEW_MINOR_VIOLATIONS_KEY,
+    CoreMetrics.NEW_INFO_VIOLATIONS_KEY,
+    CoreMetrics.FALSE_POSITIVE_ISSUES_KEY,
+    CoreMetrics.WONT_FIX_ISSUES_KEY,
+    CoreMetrics.OPEN_ISSUES_KEY,
+    CoreMetrics.REOPENED_ISSUES_KEY,
+    CoreMetrics.CONFIRMED_ISSUES_KEY,
+    CoreMetrics.CODE_SMELLS_KEY,
+    CoreMetrics.NEW_CODE_SMELLS_KEY,
+    CoreMetrics.BUGS_KEY,
+    CoreMetrics.NEW_BUGS_KEY,
+    CoreMetrics.VULNERABILITIES_KEY,
+    CoreMetrics.NEW_VULNERABILITIES_KEY,
+    CoreMetrics.SECURITY_HOTSPOTS_KEY,
+    CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY,
+    CoreMetrics.TECHNICAL_DEBT_KEY,
+    CoreMetrics.NEW_TECHNICAL_DEBT_KEY,
+    CoreMetrics.SQALE_RATING_KEY,
+    CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY,
+    CoreMetrics.DEVELOPMENT_COST_KEY,
+    CoreMetrics.NEW_DEVELOPMENT_COST_KEY,
+    CoreMetrics.SQALE_DEBT_RATIO_KEY,
+    CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY,
+    CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY,
+    CoreMetrics.RELIABILITY_REMEDIATION_EFFORT_KEY,
+    CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT_KEY,
+    CoreMetrics.RELIABILITY_RATING_KEY,
+    CoreMetrics.NEW_RELIABILITY_RATING_KEY,
+    CoreMetrics.SECURITY_REMEDIATION_EFFORT_KEY,
+    CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT_KEY,
+    CoreMetrics.SECURITY_RATING_KEY,
+    CoreMetrics.NEW_SECURITY_RATING_KEY,
+    CoreMetrics.SECURITY_REVIEW_RATING_KEY,
+    CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY,
+    CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY,
+    CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY,
+    CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY,
+    CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY,
+    CoreMetrics.ALERT_STATUS_KEY,
+    CoreMetrics.QUALITY_GATE_DETAILS_KEY,
+    CoreMetrics.LAST_COMMIT_DATE_KEY,
+    CoreMetrics.ANALYSIS_FROM_SONARQUBE_9_4_KEY
+  );
+
+  private static final List<String> fileLiveMeasureMetrics = List.of(
+    CoreMetrics.LINE_COVERAGE_KEY,
+    CoreMetrics.COMMENT_LINES_KEY,
+    CoreMetrics.VIOLATIONS_KEY,
+    CoreMetrics.LAST_COMMIT_DATE_KEY,
+    CoreMetrics.MAJOR_VIOLATIONS_KEY,
+    CoreMetrics.DEVELOPMENT_COST_KEY,
+    CoreMetrics.FUNCTIONS_KEY,
+    CoreMetrics.VULNERABILITIES_KEY,
+    CoreMetrics.EXECUTABLE_LINES_DATA_KEY,
+    CoreMetrics.TECHNICAL_DEBT_KEY,
+    CoreMetrics.LINES_KEY,
+    CoreMetrics.CODE_SMELLS_KEY,
+    CoreMetrics.UNCOVERED_LINES_KEY,
+    CoreMetrics.COVERAGE_KEY,
+    CoreMetrics.SQALE_RATING_KEY,
+    CoreMetrics.COGNITIVE_COMPLEXITY_KEY,
+    CoreMetrics.NCLOC_KEY,
+    CoreMetrics.STATEMENTS_KEY,
+    CoreMetrics.COMMENT_LINES_DENSITY_KEY,
+    CoreMetrics.LINES_TO_COVER_KEY,
+    CoreMetrics.CLASSES_KEY,
+    CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY,
+    CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY,
+    CoreMetrics.NCLOC_DATA_KEY,
+    CoreMetrics.RELIABILITY_RATING_KEY,
+    CoreMetrics.MINOR_VIOLATIONS_KEY,
+    CoreMetrics.SECURITY_REVIEW_RATING_KEY,
+    CoreMetrics.BLOCKER_VIOLATIONS_KEY,
+    CoreMetrics.SQALE_DEBT_RATIO_KEY,
+    CoreMetrics.COMPLEXITY_KEY,
+    CoreMetrics.SECURITY_RATING_KEY,
+    CoreMetrics.OPEN_ISSUES_KEY,
+    CoreMetrics.SECURITY_REMEDIATION_EFFORT_KEY,
+    CoreMetrics.FILES_KEY,
+    CoreMetrics.NEW_DUPLICATED_LINES_KEY,
+    CoreMetrics.NEW_UNCOVERED_CONDITIONS_KEY,
+    CoreMetrics.NEW_BLOCKS_DUPLICATED_KEY,
+    CoreMetrics.NEW_CONDITIONS_TO_COVER_KEY,
+    CoreMetrics.NEW_LINES_TO_COVER_KEY,
+    CoreMetrics.NEW_UNCOVERED_LINES_KEY,
+    CoreMetrics.NEW_LINES_KEY
+  );
+
+  private static final List<String> projectProjectMeasureMetrics = List.of(
+    CoreMetrics.LINE_COVERAGE_KEY,
+    CoreMetrics.COMMENT_LINES_KEY,
+    CoreMetrics.VIOLATIONS_KEY,
+    CoreMetrics.LAST_COMMIT_DATE_KEY,
+    CoreMetrics.MAJOR_VIOLATIONS_KEY,
+    CoreMetrics.DUPLICATED_FILES_KEY,
+    CoreMetrics.DEVELOPMENT_COST_KEY,
+    CoreMetrics.INFO_VIOLATIONS_KEY,
+    CoreMetrics.FUNCTIONS_KEY,
+    CoreMetrics.VULNERABILITIES_KEY,
+    CoreMetrics.ANALYSIS_FROM_SONARQUBE_9_4_KEY,
+    CoreMetrics.FALSE_POSITIVE_ISSUES_KEY,
+    CoreMetrics.CRITICAL_VIOLATIONS_KEY,
+    CoreMetrics.TECHNICAL_DEBT_KEY,
+    CoreMetrics.LINES_KEY,
+    CoreMetrics.RELIABILITY_REMEDIATION_EFFORT_KEY,
+    CoreMetrics.CODE_SMELLS_KEY,
+    CoreMetrics.UNCOVERED_LINES_KEY,
+    CoreMetrics.COVERAGE_KEY,
+    CoreMetrics.SQALE_RATING_KEY,
+    CoreMetrics.COGNITIVE_COMPLEXITY_KEY,
+    CoreMetrics.DUPLICATED_BLOCKS_KEY,
+    CoreMetrics.NCLOC_KEY,
+    CoreMetrics.STATEMENTS_KEY,
+    CoreMetrics.SECURITY_HOTSPOTS_KEY,
+    CoreMetrics.COMMENT_LINES_DENSITY_KEY,
+    CoreMetrics.BUGS_KEY,
+    CoreMetrics.LINES_TO_COVER_KEY,
+    CoreMetrics.CLASSES_KEY,
+    CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY,
+    CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY,
+    CoreMetrics.RELIABILITY_RATING_KEY,
+    CoreMetrics.MINOR_VIOLATIONS_KEY,
+    CoreMetrics.DUPLICATED_LINES_DENSITY_KEY,
+    CoreMetrics.WONT_FIX_ISSUES_KEY,
+    CoreMetrics.SECURITY_REVIEW_RATING_KEY,
+    CoreMetrics.BLOCKER_VIOLATIONS_KEY,
+    CoreMetrics.CONFIRMED_ISSUES_KEY,
+    CoreMetrics.DUPLICATED_LINES_KEY,
+    CoreMetrics.ALERT_STATUS_KEY,
+    CoreMetrics.SQALE_DEBT_RATIO_KEY,
+    CoreMetrics.COMPLEXITY_KEY,
+    CoreMetrics.SECURITY_RATING_KEY,
+    CoreMetrics.OPEN_ISSUES_KEY,
+    CoreMetrics.SECURITY_REMEDIATION_EFFORT_KEY,
+    CoreMetrics.QUALITY_GATE_DETAILS_KEY,
+    CoreMetrics.REOPENED_ISSUES_KEY,
+    CoreMetrics.FILES_KEY
+  );
+}
index 1580ce3e7e2de9b6de218b1ebd49aa541b4b31a4..a036b7a104dca030447783fb2f345ef9a559ac8f 100644 (file)
@@ -27,6 +27,8 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.metric.MetricDto;
 
+import static org.sonar.db.measure.MeasureTesting.createLiveMeasure;
+import static org.sonar.db.measure.MeasureTesting.createProjectMeasure;
 import static org.sonar.db.measure.MeasureTesting.newLiveMeasure;
 import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
 import static org.sonar.db.metric.MetricTesting.newMetricDto;
@@ -40,6 +42,24 @@ public class MeasureDbTester {
     this.db = db;
   }
 
+  @SafeVarargs
+  public final MeasureDto insertMeasureWithSensibleValues(ComponentDto component, SnapshotDto analysis, MetricDto metricDto, Consumer<MeasureDto>... consumers) {
+    MeasureDto measureDto = createProjectMeasure(metricDto, analysis, component);
+    Arrays.stream(consumers).forEach(c -> c.accept(measureDto));
+    dbClient.measureDao().insert(db.getSession(), measureDto);
+    db.commit();
+    return measureDto;
+  }
+
+  @SafeVarargs
+  public final LiveMeasureDto insertLiveMeasureWithSensibleValues(ComponentDto component, MetricDto metric, Consumer<LiveMeasureDto>... consumers) {
+    LiveMeasureDto dto = createLiveMeasure(metric, component);
+    Arrays.stream(consumers).forEach(c -> c.accept(dto));
+    dbClient.liveMeasureDao().insert(db.getSession(), dto);
+    db.commit();
+    return dto;
+  }
+
   @SafeVarargs
   public final MeasureDto insertMeasure(ComponentDto component, SnapshotDto analysis, MetricDto metricDto, Consumer<MeasureDto>... consumers) {
     MeasureDto measureDto = newMeasureDto(metricDto, component, analysis);
index a25ced23b77304d7b0c03580efdb909cad59b2cb..675aa941777db13ac5523ab0d3ac6043f66ad767 100644 (file)
  */
 package org.sonar.db.measure;
 
+import java.time.Instant;
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 import org.apache.commons.lang.math.RandomUtils;
+import org.sonar.api.measures.CoreMetrics;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.metric.MetricDto;
@@ -73,4 +79,109 @@ public class MeasureTesting {
       .setData(String.valueOf(cursor++))
       .setValue((double) cursor++);
   }
+
+  public static LiveMeasureDto createLiveMeasure(MetricDto metricDto, ComponentDto componentDto) {
+    BiConsumer<MetricDto, MeasureAdapter> populator = specificLiveMeasurePopulator.getOrDefault(metricDto.getKey(), defaultLiveMeasurePopulator);
+    LiveMeasureDto liveMeasureDto = newLiveMeasure(componentDto, metricDto);
+    populator.accept(metricDto, new MeasureAdapter(liveMeasureDto));
+    return liveMeasureDto;
+  }
+
+  public static MeasureDto createProjectMeasure(MetricDto metricDto, SnapshotDto snapshotDto, ComponentDto projectComponentDto) {
+    BiConsumer<MetricDto, MeasureAdapter> populator = specificLiveMeasurePopulator.getOrDefault(metricDto.getKey(), defaultLiveMeasurePopulator);
+    MeasureDto measureDto = newMeasureDto(metricDto, projectComponentDto, snapshotDto);
+    populator.accept(metricDto, new MeasureAdapter(measureDto));
+    return measureDto;
+  }
+
+  private static final Consumer<MeasureAdapter> ratingMeasurePopulator =
+    m -> {
+      int rating = ThreadLocalRandom.current().nextInt(1, 5);
+      char textValue = (char) ('A' + rating - 1);
+      m.setValue((double) rating).setData("" + textValue);
+    };
+
+  private static final Map<String, BiConsumer<MetricDto, MeasureAdapter>> specificLiveMeasurePopulator = Map.of(
+    CoreMetrics.DEVELOPMENT_COST_KEY, (metric, m) -> m.setData("" + Math.round(ThreadLocalRandom.current().nextDouble(100, 10_000))),
+    CoreMetrics.LAST_COMMIT_DATE_KEY, (metric, m) -> m.setValue((double)
+      Instant.now().minusSeconds(Math.round(ThreadLocalRandom.current().nextDouble(100_000, 1_000_000))).toEpochMilli()),
+    CoreMetrics.SQALE_RATING_KEY, (metric, m) -> ratingMeasurePopulator.accept(m),
+    CoreMetrics.RELIABILITY_RATING_KEY, (metric, m) -> ratingMeasurePopulator.accept(m),
+    CoreMetrics.SECURITY_REVIEW_RATING_KEY, (metric, m) -> ratingMeasurePopulator.accept(m),
+    CoreMetrics.SECURITY_RATING_KEY, (metric, m) -> ratingMeasurePopulator.accept(m),
+    CoreMetrics.ALERT_STATUS_KEY, (metric, m) -> {
+      boolean isOk = ThreadLocalRandom.current().nextDouble() > 0.5;
+      m.setData(isOk ? "OK" : "ERROR");
+      m.setAlert(isOk ? "OK" : "ERROR");
+    },
+    CoreMetrics.QUALITY_GATE_DETAILS_KEY, (metric, m) -> m.setData("{\"level\":\"OK\"}")
+  );
+
+  private static final BiConsumer<MetricDto, MeasureAdapter> defaultLiveMeasurePopulator =
+    (metric, m) -> {
+      int min, max;
+      if (metric.getWorstValue() != null && metric.getBestValue() != null) {
+        min = (int) Math.min(metric.getBestValue(), metric.getWorstValue());
+        max = (int) Math.max(metric.getBestValue(), metric.getWorstValue());
+      } else if (metric.getDirection() != 0) {
+        int worst, best;
+        if (metric.getWorstValue() != null) {
+          worst = metric.getWorstValue().intValue();
+          best = -metric.getDirection() * 100;
+        } else if (metric.getBestValue() != null) {
+          best = metric.getBestValue().intValue();
+          worst = best - metric.getDirection() * 100;
+        } else {
+          worst = 0;
+          best = -metric.getDirection() * 100;
+        }
+        min = Math.min(best, worst);
+        max = Math.max(best, worst);
+      } else {
+        min = 0;
+        max = 100;
+      }
+
+      m.setValue((double) Math.round(ThreadLocalRandom.current().nextDouble(min, max)));
+    };
+
+  private static class MeasureAdapter {
+    private final MeasureDto projectMeasure;
+    private final LiveMeasureDto liveMeasure;
+
+    private MeasureAdapter(LiveMeasureDto liveMeasure) {
+      this.projectMeasure = null;
+      this.liveMeasure = liveMeasure;
+    }
+
+    private MeasureAdapter(MeasureDto projectMeasure) {
+      this.projectMeasure = projectMeasure;
+      this.liveMeasure = null;
+    }
+
+    public MeasureAdapter setValue(Double value) {
+      if (projectMeasure != null) {
+        projectMeasure.setValue(value);
+      } else if (liveMeasure != null) {
+        liveMeasure.setValue(value);
+      }
+      return this;
+    }
+
+    public MeasureAdapter setData(String data) {
+      if (projectMeasure != null) {
+        projectMeasure.setData(data);
+      } else if (liveMeasure != null) {
+        liveMeasure.setData(data);
+      }
+      return this;
+    }
+
+    public MeasureAdapter setAlert(String value) {
+      if (projectMeasure != null) {
+        projectMeasure.setAlertText(value).setAlertStatus(value);
+      }
+      return this;
+    }
+  }
 }