Просмотр исходного кода

Merge pull request #487 from nestabentum/max_metric_in_web_report

Add Max Metric To Report Viewer
Timur Sağlam 4 лет назад
Родитель
Сommit
33ecda9aea
27 измененных файлов с 376 добавлено и 250 удалено
  1. 1 1
      jplag/src/main/java/de/jplag/JPlag.java
  2. 27 9
      jplag/src/main/java/de/jplag/JPlagResult.java
  3. 4 4
      jplag/src/main/java/de/jplag/reporting/jsonfactory/JsonFactory.java
  4. 12 26
      jplag/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java
  5. 54 0
      jplag/src/main/java/de/jplag/reporting/reportobject/mapper/MetricMapper.java
  6. 1 12
      jplag/src/main/java/de/jplag/reporting/reportobject/model/JPlagReport.java
  7. 2 36
      jplag/src/main/java/de/jplag/reporting/reportobject/model/Metric.java
  8. 2 28
      jplag/src/main/java/de/jplag/reporting/reportobject/model/TopComparison.java
  9. 3 1
      jplag/src/main/resources/de/jplag/messages.properties
  10. 3 0
      jplag/src/main/resources/de/jplag/messages_de.properties
  11. 4 0
      jplag/src/main/resources/de/jplag/messages_en.properties
  12. 3 0
      jplag/src/main/resources/de/jplag/messages_es.properties
  13. 3 0
      jplag/src/main/resources/de/jplag/messages_fr.properties
  14. 3 0
      jplag/src/main/resources/de/jplag/messages_pt.properties
  15. 5 5
      jplag/src/test/java/de/jplag/BaseCodeTest.java
  16. 2 2
      jplag/src/test/java/de/jplag/NewJavaFeaturesTest.java
  17. 13 15
      jplag/src/test/java/de/jplag/NormalComparisonTest.java
  18. 9 11
      jplag/src/test/java/de/jplag/ParallelComparisonTest.java
  19. 2 3
      jplag/src/test/java/de/jplag/clustering/ClusteringAdapterTest.java
  20. 115 0
      jplag/src/test/java/de/jplag/reporting/reportobject/mapper/MetricMapperTest.java
  21. 2 2
      jplag/src/test/java/de/jplag/special/VolumeTest.java
  22. 1 0
      jplag/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
  23. 4 6
      report-viewer/src/components/DistributionDiagram.vue
  24. 39 26
      report-viewer/src/components/MetricButton.vue
  25. 1 0
      report-viewer/src/model/Metric.ts
  26. 60 61
      report-viewer/src/model/factories/OverviewFactory.ts
  27. 1 2
      report-viewer/src/views/OverviewView.vue

+ 1 - 1
jplag/src/main/java/de/jplag/JPlag.java

@@ -96,7 +96,7 @@ public class JPlag {
         JPlagResult result = comparisonStrategy.compareSubmissions(submissionSet);
         errorCollector.print("\nTotal time for comparing submissions: " + TimeUtil.formatDuration(result.getDuration()), null);
 
-        result.setClusteringResult(ClusteringFactory.getClusterings(result.getComparisons(), options.getClusteringOptions()));
+        result.setClusteringResult(ClusteringFactory.getClusterings(result.getAllComparisons(), options.getClusteringOptions()));
 
         return result;
     }

+ 27 - 9
jplag/src/main/java/de/jplag/JPlagResult.java

@@ -1,9 +1,11 @@
 package de.jplag;
 
 import java.util.List;
+import java.util.function.Function;
 
 import de.jplag.clustering.ClusteringResult;
 import de.jplag.options.JPlagOptions;
+import de.jplag.options.SimilarityMetric;
 
 /**
  * Encapsulates the results of a comparison of a set of source code submissions.
@@ -21,6 +23,7 @@ public class JPlagResult {
     private final int[] similarityDistribution; // 10-element array representing the similarity distribution of the detected matches.
 
     private List<ClusteringResult<Submission>> clusteringResult;
+    private final int SIMILARITY_DISTRIBUTION_SIZE = 10;
 
     public JPlagResult(List<JPlagComparison> comparisons, SubmissionSet submissions, long durationInMillis, JPlagOptions options) {
         this.comparisons = comparisons;
@@ -47,7 +50,7 @@ public class JPlagResult {
     /**
      * @return a list of all comparisons sorted by percentage (descending)
      */
-    public List<JPlagComparison> getComparisons() {
+    public List<JPlagComparison> getAllComparisons() {
         return comparisons;
     }
 
@@ -93,22 +96,34 @@ public class JPlagResult {
     }
 
     /**
-     * Returns the similarity distribution of detected matches in a 10-element array. Each entry represents the absolute
-     * frequency of matches whose similarity lies within the respective interval. Intervals: 0: [0% - 10%), 1: [10% - 20%),
-     * 2: [20% - 30%), ..., 9: [90% - 100%]
+     * For the {@link SimilarityMetric} JPlag was run with, this returns the similarity distribution of detected matches in
+     * a 10-element array. Each entry represents the absolute frequency of matches whose similarity lies within the
+     * respective interval. Intervals: 0: [0% - 10%), 1: [10% - 20%), 2: [20% - 30%), ..., 9: [90% - 100%]
      * @return the similarity distribution array.
      */
     public int[] getSimilarityDistribution() {
         return similarityDistribution;
     }
 
+    /**
+     * For the {@link SimilarityMetric#MAX} that is built in to every {@link JPlagComparison}, this returns the similarity
+     * distribution of detected matches in a 10-element array. Each entry represents the absolute frequency of matches whose
+     * similarity lies within the respective interval. Intervals: 0: [0% - 10%), 1: [10% - 20%), 2: [20% - 30%), ..., 9:
+     * [90% - 100%]
+     * @return the similarity distribution array. When JPlag was run with the {@link SimilarityMetric#MAX}, this will return
+     * the same distribution as {@link JPlagResult#getSimilarityDistribution()}
+     */
+    public int[] getMaxSimilarityDistribution() {
+        return calculateDistributionFor(comparisons, (JPlagComparison::maximalSimilarity));
+    }
+
     public List<ClusteringResult<Submission>> getClusteringResult() {
         return this.clusteringResult;
     }
 
     @Override
     public String toString() {
-        return String.format("JPlagResult { comparisons: %d, duration: %d ms, language: %s, submissions: %d }", getComparisons().size(),
+        return String.format("JPlagResult { comparisons: %d, duration: %d ms, language: %s, submissions: %d }", getAllComparisons().size(),
                 getDuration(), getOptions().getLanguageOption(), submissions.numberOfSubmissions());
     }
 
@@ -116,11 +131,14 @@ public class JPlagResult {
      * Note: Before, comparisons with a similarity below the given threshold were also included in the similarity matrix.
      */
     private int[] calculateSimilarityDistribution(List<JPlagComparison> comparisons) {
-        int[] similarityDistribution = new int[10];
-
-        comparisons.stream().map(JPlagComparison::similarity).map(percent -> percent / 10).map(Float::intValue).map(index -> index == 10 ? 9 : index)
-                .forEach(index -> similarityDistribution[index]++);
+        return calculateDistributionFor(comparisons, (JPlagComparison::similarity));
+    }
 
+    private int[] calculateDistributionFor(List<JPlagComparison> comparisons, Function<JPlagComparison, Float> similarityExtractor) {
+        int[] similarityDistribution = new int[SIMILARITY_DISTRIBUTION_SIZE];
+        comparisons.stream().map(similarityExtractor).map(percent -> percent / SIMILARITY_DISTRIBUTION_SIZE).map(Float::intValue)
+                .map(index -> index == SIMILARITY_DISTRIBUTION_SIZE ? SIMILARITY_DISTRIBUTION_SIZE - 1 : index)
+                .forEach(index -> similarityDistribution[SIMILARITY_DISTRIBUTION_SIZE - 1 - index]++);
         return similarityDistribution;
     }
 }

+ 4 - 4
jplag/src/main/java/de/jplag/reporting/jsonfactory/JsonFactory.java

@@ -31,8 +31,8 @@ public class JsonFactory {
     public static List<String> getJsonStrings(JPlagReport jPlagReport) {
         List<String> jsonReports = new ArrayList<>();
         try {
-            jsonReports.add(mapper.writeValueAsString(jPlagReport.getOverviewReport()));
-            for (ComparisonReport comparisonReport : jPlagReport.getComparisons()) {
+            jsonReports.add(mapper.writeValueAsString(jPlagReport.overviewReport()));
+            for (ComparisonReport comparisonReport : jPlagReport.comparisons()) {
                 jsonReports.add(mapper.writeValueAsString(comparisonReport));
             }
         } catch (JsonProcessingException e) {
@@ -48,8 +48,8 @@ public class JsonFactory {
     public static boolean saveJsonFiles(JPlagReport jPlagReport, String folderPath) {
         try {
             ObjectMapper mapper = new ObjectMapper();
-            mapper.writeValue(Path.of(folderPath, "overview.json").toFile(), jPlagReport.getOverviewReport());
-            for (ComparisonReport report : jPlagReport.getComparisons()) {
+            mapper.writeValue(Path.of(folderPath, "overview.json").toFile(), jPlagReport.overviewReport());
+            for (ComparisonReport report : jPlagReport.comparisons()) {
                 String name = report.getFirstSubmissionId().concat("-").concat(report.getSecondSubmissionId()).concat(".json");
                 mapper.writeValue(Path.of(folderPath, name).toFile(), report);
             }

+ 12 - 26
jplag/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java

@@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory;
 
 import de.jplag.*;
 import de.jplag.reporting.reportobject.mapper.ClusteringResultMapper;
+import de.jplag.reporting.reportobject.mapper.MetricMapper;
 import de.jplag.reporting.reportobject.model.*;
 import de.jplag.reporting.reportobject.model.Match;
 
@@ -23,6 +24,7 @@ public class ReportObjectFactory {
 
     private static final Logger logger = LoggerFactory.getLogger(ReportObjectFactory.class);
     private static final ClusteringResultMapper clusteringResultMapper = new ClusteringResultMapper();
+    private static final MetricMapper metricMapper = new MetricMapper();
 
     /**
      * Converts a JPlagResult to a JPlagReport.
@@ -38,8 +40,7 @@ public class ReportObjectFactory {
      * Generates an Overview DTO of a JPlagResult.
      */
     private static OverviewReport generateOverviewReport(JPlagResult result) {
-        int numberOfComparisons = result.getOptions().getMaximumNumberOfComparisons();
-        List<JPlagComparison> comparisons = result.getComparisons(numberOfComparisons);
+        List<JPlagComparison> comparisons = getComparisons(result);
         OverviewReport overviewReport = new OverviewReport();
 
         // TODO: Consider to treat entries that were checked differently from old entries with prior work.
@@ -72,8 +73,7 @@ public class ReportObjectFactory {
      */
     private static List<ComparisonReport> generateComparisonReports(JPlagResult result) {
         List<ComparisonReport> comparisons = new ArrayList<>();
-        int numberOfComparisons = result.getOptions().getMaximumNumberOfComparisons();
-        result.getComparisons(numberOfComparisons).forEach(comparison -> comparisons.add( //
+        getComparisons(result).forEach(comparison -> comparisons.add( //
                 new ComparisonReport(comparison.getFirstSubmission().getName(), //
                         comparison.getSecondSubmission().getName(), //
                         comparison.similarity(), //
@@ -84,6 +84,11 @@ public class ReportObjectFactory {
         return comparisons;
     }
 
+    private static List<JPlagComparison> getComparisons(JPlagResult result) {
+        int numberOfComparisons = result.getOptions().getMaximumNumberOfComparisons();
+        return result.getComparisons(numberOfComparisons);
+    }
+
     private static List<Match> convertMatchesToReportMatches(JPlagResult result, JPlagComparison comparison, List<de.jplag.Match> matches) {
         return matches.stream().map(match -> convertMatchToReportMatch(comparison, match, result.getOptions().getLanguage().usesIndex()))
                 .collect(Collectors.toList());
@@ -113,29 +118,13 @@ public class ReportObjectFactory {
         return names;
     }
 
-    // Currently, only one metric can be obtained.
-
     /**
-     * Gets the used metric in a JPlag comparison.
+     * Gets the used metrics in a JPlag comparison. As Max Metric is included in every JPlag run, this always include Max
+     * Metric.
      * @return A list contains Metric DTOs.
      */
     private static List<Metric> getMetrics(JPlagResult result) {
-        List<Metric> metrics = new ArrayList<>();
-        metrics.add(new Metric(result.getOptions().getSimilarityMetric().name(), result.getOptions().getSimilarityThreshold(),
-                intArrayToList(result.getSimilarityDistribution()), getTopComparisons(result.getComparisons())));
-        return metrics;
-    }
-
-    /**
-     * Converts JPlagComparison to a DTO for displaying only comparisons. See
-     * {@link #generateComparisonReports(JPlagResult)} for a more detailed representation of a comparison.
-     * @return List containing TopComparison DTOs.
-     */
-    private static List<TopComparison> getTopComparisons(List<JPlagComparison> comparisons) {
-        List<TopComparison> topComparisons = new ArrayList<>();
-        comparisons.forEach(comparison -> topComparisons.add(
-                new TopComparison(comparison.getFirstSubmission().getName(), comparison.getSecondSubmission().getName(), comparison.similarity())));
-        return topComparisons;
+        return List.of(metricMapper.getAverageMetric(result), metricMapper.getMaxMetric(result));
     }
 
     /**
@@ -189,7 +178,4 @@ public class ReportObjectFactory {
         return dateFormat.format(date);
     }
 
-    private static List<Integer> intArrayToList(int[] array) {
-        return Arrays.stream(array).boxed().collect(Collectors.toList());
-    }
 }

+ 54 - 0
jplag/src/main/java/de/jplag/reporting/reportobject/mapper/MetricMapper.java

@@ -0,0 +1,54 @@
+package de.jplag.reporting.reportobject.mapper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import de.jplag.JPlagComparison;
+import de.jplag.JPlagResult;
+import de.jplag.Messages;
+import de.jplag.options.SimilarityMetric;
+import de.jplag.reporting.reportobject.model.Metric;
+import de.jplag.reporting.reportobject.model.TopComparison;
+
+/**
+ * Extracts and maps metrics from the JPlagResult to the corresponding JSON DTO
+ */
+public class MetricMapper {
+    public Metric getAverageMetric(JPlagResult result) {
+        return new Metric(SimilarityMetric.AVG.name(), intArrayToList(result.getSimilarityDistribution()), getTopComparisons(getComparisons(result)),
+                Messages.getString("SimilarityMetric.Avg.Description"));
+    }
+
+    public Metric getMaxMetric(JPlagResult result) {
+        return new Metric(SimilarityMetric.MAX.name(), intArrayToList(result.getMaxSimilarityDistribution()),
+                getMaxSimilarityTopComparisons(getComparisons(result)), Messages.getString("SimilarityMetric.Max.Description"));
+    }
+
+    private List<JPlagComparison> getComparisons(JPlagResult result) {
+        int maxNumberOfComparisons = result.getOptions().getMaximumNumberOfComparisons();
+        return result.getComparisons(maxNumberOfComparisons);
+    }
+
+    private static List<Integer> intArrayToList(int[] array) {
+        return Arrays.stream(array).boxed().collect(Collectors.toList());
+    }
+
+    private static List<TopComparison> getTopComparisons(List<JPlagComparison> comparisons, Function<JPlagComparison, Float> similarityExtractor) {
+        List<TopComparison> topComparisons = new ArrayList<>();
+        comparisons.forEach(comparison -> topComparisons.add(new TopComparison(comparison.getFirstSubmission().getName(),
+                comparison.getSecondSubmission().getName(), similarityExtractor.apply(comparison))));
+        return topComparisons;
+    }
+
+    private static List<TopComparison> getTopComparisons(List<JPlagComparison> comparisons) {
+        return getTopComparisons(comparisons, JPlagComparison::similarity);
+    }
+
+    private static List<TopComparison> getMaxSimilarityTopComparisons(List<JPlagComparison> comparisons) {
+        return getTopComparisons(comparisons, JPlagComparison::maximalSimilarity);
+    }
+
+}

+ 1 - 12
jplag/src/main/java/de/jplag/reporting/reportobject/model/JPlagReport.java

@@ -2,20 +2,9 @@ package de.jplag.reporting.reportobject.model;
 
 import java.util.List;
 
-public class JPlagReport {
-    private final OverviewReport overviewReport;
-    private final List<ComparisonReport> comparisons;
-
+public record JPlagReport(OverviewReport overviewReport, List<ComparisonReport> comparisons) {
     public JPlagReport(OverviewReport overviewReport, List<ComparisonReport> comparisons) {
         this.overviewReport = overviewReport;
         this.comparisons = List.copyOf(comparisons);
     }
-
-    public OverviewReport getOverviewReport() {
-        return overviewReport;
-    }
-
-    public List<ComparisonReport> getComparisons() {
-        return comparisons;
-    }
 }

+ 2 - 36
jplag/src/main/java/de/jplag/reporting/reportobject/model/Metric.java

@@ -4,40 +4,6 @@ import java.util.List;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-public class Metric {
-
-    @JsonProperty("name")
-    private final String name;
-
-    @JsonProperty("threshold")
-    private final float threshold;
-
-    @JsonProperty("distribution")
-    private final List<Integer> distribution;
-
-    @JsonProperty("topComparisons")
-    private final List<TopComparison> topComparisons;
-
-    public Metric(String name, float threshold, List<Integer> distribution, List<TopComparison> topComparisons) {
-        this.name = name;
-        this.threshold = threshold;
-        this.distribution = List.copyOf(distribution);
-        this.topComparisons = List.copyOf(topComparisons);
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public float getThreshold() {
-        return threshold;
-    }
-
-    public List<Integer> getDistribution() {
-        return distribution;
-    }
-
-    public List<TopComparison> getTopComparisons() {
-        return topComparisons;
-    }
+public record Metric(@JsonProperty("name") String name, @JsonProperty("distribution") List<Integer> distribution,
+        @JsonProperty("topComparisons") List<TopComparison> topComparisons, @JsonProperty String description) {
 }

+ 2 - 28
jplag/src/main/java/de/jplag/reporting/reportobject/model/TopComparison.java

@@ -2,33 +2,7 @@ package de.jplag.reporting.reportobject.model;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-public class TopComparison {
-
-    @JsonProperty("first_submission")
-    private final String firstSubmission;
-
-    @JsonProperty("second_submission")
-    private final String secondSubmission;
-
-    @JsonProperty("match_percentage")
-    private final float matchPercentage;
-
-    public TopComparison(String firstSubmission, String secondSubmission, float matchPercentage) {
-        this.firstSubmission = firstSubmission;
-        this.secondSubmission = secondSubmission;
-        this.matchPercentage = matchPercentage;
-    }
-
-    public String getFirstSubmission() {
-        return firstSubmission;
-    }
-
-    public String getSecondSubmission() {
-        return secondSubmission;
-    }
-
-    public float getMatchPercentage() {
-        return matchPercentage;
-    }
+public record TopComparison(@JsonProperty("first_submission") String firstSubmission, @JsonProperty("second_submission") String secondSubmission,
+        @JsonProperty("match_percentage") float matchPercentage) {
 
 }

+ 3 - 1
jplag/src/main/resources/de/jplag/messages.properties

@@ -26,4 +26,6 @@ CommandLineArgument.ClusterAgglomerativeInterClusterSimilarity=How to measure th
 CommandLineArgument.ClusterPreprocessingNone=Do not use any preprocessing before clustering. Not recommended for spectral clustering.
 CommandLineArgument.ClusterPreprocessingCdf=Before clustering, the value of the cumulative distribution function of all similarities is estimated. The similarities are multiplied with these estimates. This has the effect of supressing similarities that are low compared to other similarities.
 CommandLineArgument.ClusterPreprocessingPercentile=Any similarity smaller than the given percentile will be suppressed during clustering.
-CommandLineArgument.ClusterPreprocessingThreshold=Any similarity smaller than the given threshold value will be suppressed during clustering.
+CommandLineArgument.ClusterPreprocessingThreshold=Any similarity smaller than the given threshold value will be suppressed during clustering.
+SimilarityMetric.Avg.Description = Average of both program coverages. This is the default similarity which works in most cases: Matches with a high average similarity indicate that the programs work in a very similar way.
+SimilarityMetric.Max.Description = Maximum of both program coverages. This ranking is especially useful if the programs are very different in size. This can happen when dead code was inserted to disguise the origin of the plagiarized program.

+ 3 - 0
jplag/src/main/resources/de/jplag/messages_de.properties

@@ -57,3 +57,6 @@ AllMatches.Distribution=Verteilung
 AllMatches.Length=L&auml;nge
 AllMatches.Number_of_matches=Anzahl der &Uuml;bereinstimmungen
 AllMatches.Basecode=Referenz&uuml;bereinstimmung
+
+SimilarityMetric.Avg.Description = Average of both program coverages. This is the default similarity which works in most cases: Matches with a high average similarity indicate that the programs work in a very similar way.
+SimilarityMetric.Max.Description = Maximum of both program coverages. This ranking is especially useful if the programs are very different in size. This can happen when dead code was inserted to disguise the origin of the plagiarized program.

+ 4 - 0
jplag/src/main/resources/de/jplag/messages_en.properties

@@ -35,6 +35,7 @@ Report.HELP=HELP
 Report.Distribution=Distribution
 Report.Token_Distribution=Token Distribution
 
+
 Clusters.MIN_single_link=MIN, single link
 Clusters.AVR_group_average=AVR, group average
 Clusters.MAX_complete_link=MAX, complete link
@@ -57,3 +58,6 @@ AllMatches.Distribution=Distribution
 AllMatches.Length=Length
 AllMatches.Number_of_matches=No of matches
 AllMatches.Basecode=Basecode
+
+SimilarityMetric.Avg.Description = Average of both program coverages. This is the default similarity which works in most cases: Matches with a high average similarity indicate that the programs work in a very similar way.
+SimilarityMetric.Max.Description = Maximum of both program coverages. This ranking is especially useful if the programs are very different in size. This can happen when dead code was inserted to disguise the origin of the plagiarized program.

+ 3 - 0
jplag/src/main/resources/de/jplag/messages_es.properties

@@ -57,3 +57,6 @@ AllMatches.Distribution=Distribuci
 AllMatches.Length=Longitud
 AllMatches.Number_of_matches=No de coincidencias
 AllMatches.Basecode=Código Base
+
+SimilarityMetric.Avg.Description = Average of both program coverages. This is the default similarity which works in most cases: Matches with a high average similarity indicate that the programs work in a very similar way.
+SimilarityMetric.Max.Description = Maximum of both program coverages. This ranking is especially useful if the programs are very different in size. This can happen when dead code was inserted to disguise the origin of the plagiarized program.

+ 3 - 0
jplag/src/main/resources/de/jplag/messages_fr.properties

@@ -57,3 +57,6 @@ AllMatches.Distribution=Distribution
 AllMatches.Length=Length
 AllMatches.Number_of_matches=No of matches
 AllMatches.Basecode=Code source de reference
+
+SimilarityMetric.Avg.Description = Average of both program coverages. This is the default similarity which works in most cases: Matches with a high average similarity indicate that the programs work in a very similar way.
+SimilarityMetric.Max.Description = Maximum of both program coverages. This ranking is especially useful if the programs are very different in size. This can happen when dead code was inserted to disguise the origin of the plagiarized program.

+ 3 - 0
jplag/src/main/resources/de/jplag/messages_pt.properties

@@ -57,3 +57,6 @@ AllMatches.Distribution=Distribution
 AllMatches.Length=Length
 AllMatches.Number_of_matches=No of matches
 AllMatches.Basecode=Basecode
+
+SimilarityMetric.Avg.Description = Average of both program coverages. This is the default similarity which works in most cases: Matches with a high average similarity indicate that the programs work in a very similar way.
+SimilarityMetric.Max.Description = Maximum of both program coverages. This ranking is especially useful if the programs are very different in size. This can happen when dead code was inserted to disguise the origin of the plagiarized program.

+ 5 - 5
jplag/src/test/java/de/jplag/BaseCodeTest.java

@@ -39,10 +39,10 @@ public class BaseCodeTest extends TestBase {
 
     private void verifyResults(JPlagResult result) {
         assertEquals(2, result.getNumberOfSubmissions());
-        assertEquals(1, result.getComparisons().size());
-        assertEquals(1, result.getComparisons().get(0).getMatches().size());
-        assertEquals(1, result.getSimilarityDistribution()[8]);
-        assertEquals(85f, result.getComparisons().get(0).similarity(), DELTA);
+        assertEquals(1, result.getAllComparisons().size());
+        assertEquals(1, result.getAllComparisons().get(0).getMatches().size());
+        assertEquals(1, result.getSimilarityDistribution()[1]);
+        assertEquals(85f, result.getAllComparisons().get(0).similarity(), DELTA);
     }
 
     @Test
@@ -52,7 +52,7 @@ public class BaseCodeTest extends TestBase {
     }
 
     @Test
-    void testInvalidRoot() throws ExitException {
+    void testInvalidRoot() {
         assertThrows(RootDirectoryException.class, () -> runJPlag("basecode", it -> it.setSubmissionDirectories(List.of("WrongRoot"))));
     }
 

+ 2 - 2
jplag/src/test/java/de/jplag/NewJavaFeaturesTest.java

@@ -23,10 +23,10 @@ public class NewJavaFeaturesTest extends TestBase {
         for (Submission submission : result.getSubmissions().getSubmissions()) {
             assertEquals(6, submission.getFiles().size(), String.format(CHANGE_MESSAGE, "Files"));
         }
-        assertEquals(1, result.getComparisons().size(), String.format(CHANGE_MESSAGE, "Comparisons"));
+        assertEquals(1, result.getAllComparisons().size(), String.format(CHANGE_MESSAGE, "Comparisons"));
 
         // Check similarity and number of matches:
-        var comparison = result.getComparisons().get(0);
+        var comparison = result.getAllComparisons().get(0);
         assertEquals(EXPECTED_SIMILARITY, comparison.similarity(), DELTA);
         assertEquals(EXPECTED_MATCHES, comparison.getMatches().size());
     }

+ 13 - 15
jplag/src/test/java/de/jplag/NormalComparisonTest.java

@@ -21,10 +21,10 @@ class NormalComparisonTest extends TestBase {
         JPlagResult result = runJPlagWithDefaultOptions("SimpleDuplicate");
 
         assertEquals(2, result.getNumberOfSubmissions());
-        assertEquals(1, result.getComparisons().size());
-        assertEquals(1, result.getComparisons().get(0).getMatches().size());
-        assertEquals(1, result.getSimilarityDistribution()[6]);
-        assertEquals(62.07f, result.getComparisons().get(0).similarity(), 0.1f);
+        assertEquals(1, result.getAllComparisons().size());
+        assertEquals(1, result.getAllComparisons().get(0).getMatches().size());
+        assertEquals(1, result.getSimilarityDistribution()[3]);
+        assertEquals(62.07f, result.getAllComparisons().get(0).similarity(), 0.1f);
     }
 
     /**
@@ -35,11 +35,9 @@ class NormalComparisonTest extends TestBase {
         JPlagResult result = runJPlagWithDefaultOptions("NoDuplicate");
 
         assertEquals(3, result.getNumberOfSubmissions());
-        assertEquals(3, result.getComparisons().size());
+        assertEquals(3, result.getAllComparisons().size());
 
-        result.getComparisons().forEach(comparison -> {
-            assertEquals(0f, comparison.similarity(), 0.1f);
-        });
+        result.getAllComparisons().forEach(comparison -> assertEquals(0f, comparison.similarity(), 0.1f));
     }
 
     /**
@@ -52,10 +50,10 @@ class NormalComparisonTest extends TestBase {
         JPlagResult result = runJPlagWithDefaultOptions("PartialPlagiarism");
 
         assertEquals(5, result.getNumberOfSubmissions());
-        assertEquals(10, result.getComparisons().size());
+        assertEquals(10, result.getAllComparisons().size());
 
         // All comparisons with E shall have no matches
-        result.getComparisons().stream()
+        result.getAllComparisons().stream()
                 .filter(comparison -> comparison.getSecondSubmission().getName().equals("E") || comparison.getFirstSubmission().getName().equals("E"))
                 .forEach(comparison -> assertEquals(0f, comparison.similarity(), DELTA));
 
@@ -81,7 +79,7 @@ class NormalComparisonTest extends TestBase {
     }
 
     private Optional<JPlagComparison> getSelectedComparison(JPlagResult result, String nameA, String nameB) {
-        return result.getComparisons().stream().filter(
+        return result.getAllComparisons().stream().filter(
                 comparison -> comparison.getFirstSubmission().getName().equals(nameA) && comparison.getSecondSubmission().getName().equals(nameB)
                         || comparison.getFirstSubmission().getName().equals(nameB) && comparison.getSecondSubmission().getName().equals(nameA))
                 .findFirst();
@@ -112,7 +110,7 @@ class NormalComparisonTest extends TestBase {
     }
 
     @Test
-    public void testMultiRootDirBasecodeName() throws ExitException {
+    public void testMultiRootDirBasecodeName() {
         List<String> paths = List.of(getBasePath("basecode"), getBasePath("SimpleDuplicate"));
         String basecodePath = "base"; // Should *not* find basecode/base
         assertThrows(BasecodeException.class, () -> runJPlag(paths, it -> it.setBaseCodeSubmissionName(basecodePath)));
@@ -125,7 +123,7 @@ class NormalComparisonTest extends TestBase {
         JPlagResult result = runJPlag(newDirectories, oldDirectories, it -> {
         });
         int numberOfExpectedComparison = 1 + 3 * 2;
-        assertEquals(numberOfExpectedComparison, result.getComparisons().size());
+        assertEquals(numberOfExpectedComparison, result.getAllComparisons().size());
     }
 
     @Test
@@ -135,7 +133,7 @@ class NormalComparisonTest extends TestBase {
         JPlagResult result = runJPlag(newDirectories, oldDirectories, it -> {
         });
         int numberOfExpectedComparison = 1;
-        assertEquals(numberOfExpectedComparison, result.getComparisons().size());
+        assertEquals(numberOfExpectedComparison, result.getAllComparisons().size());
     }
 
     @Test
@@ -145,6 +143,6 @@ class NormalComparisonTest extends TestBase {
         List<String> oldDirectories = List.of(getBasePath("basecode")); // 3 - 1 submissions
         JPlagResult result = runJPlag(newDirectories, oldDirectories, it -> it.setBaseCodeSubmissionName(basecodePath));
         int numberOfExpectedComparison = 1 + 2 * 2;
-        assertEquals(numberOfExpectedComparison, result.getComparisons().size());
+        assertEquals(numberOfExpectedComparison, result.getAllComparisons().size());
     }
 }

+ 9 - 11
jplag/src/test/java/de/jplag/ParallelComparisonTest.java

@@ -25,10 +25,10 @@ public class ParallelComparisonTest extends TestBase {
         JPlagResult result = runJPlag("SimpleDuplicate", it -> it.setComparisonMode(PARALLEL));
 
         assertEquals(2, result.getNumberOfSubmissions());
-        assertEquals(1, result.getComparisons().size());
-        assertEquals(1, result.getComparisons().get(0).getMatches().size());
-        assertEquals(1, result.getSimilarityDistribution()[6]);
-        assertEquals(62.07f, result.getComparisons().get(0).similarity(), DELTA);
+        assertEquals(1, result.getAllComparisons().size());
+        assertEquals(1, result.getAllComparisons().get(0).getMatches().size());
+        assertEquals(1, result.getSimilarityDistribution()[3]);
+        assertEquals(62.07f, result.getAllComparisons().get(0).similarity(), DELTA);
     }
 
     /**
@@ -39,11 +39,9 @@ public class ParallelComparisonTest extends TestBase {
         JPlagResult result = runJPlag("NoDuplicate", it -> it.setComparisonMode(PARALLEL));
 
         assertEquals(3, result.getNumberOfSubmissions());
-        assertEquals(3, result.getComparisons().size());
+        assertEquals(3, result.getAllComparisons().size());
 
-        result.getComparisons().forEach(comparison -> {
-            assertEquals(0f, comparison.similarity(), DELTA);
-        });
+        result.getAllComparisons().forEach(comparison -> assertEquals(0f, comparison.similarity(), DELTA));
     }
 
     /**
@@ -56,10 +54,10 @@ public class ParallelComparisonTest extends TestBase {
         JPlagResult result = runJPlag("PartialPlagiarism", it -> it.setComparisonMode(PARALLEL));
 
         assertEquals(5, result.getNumberOfSubmissions());
-        assertEquals(10, result.getComparisons().size());
+        assertEquals(10, result.getAllComparisons().size());
 
         // All comparisons with E shall have no matches
-        result.getComparisons().stream()
+        result.getAllComparisons().stream()
                 .filter(comparison -> comparison.getSecondSubmission().getName().equals("E") || comparison.getFirstSubmission().getName().equals("E"))
                 .forEach(comparison -> assertEquals(0f, comparison.similarity(), DELTA));
 
@@ -85,7 +83,7 @@ public class ParallelComparisonTest extends TestBase {
     }
 
     private Optional<JPlagComparison> getSelectedComparison(JPlagResult result, String nameA, String nameB) {
-        return result.getComparisons().stream().filter(
+        return result.getAllComparisons().stream().filter(
                 comparison -> comparison.getFirstSubmission().getName().equals(nameA) && comparison.getSecondSubmission().getName().equals(nameB)
                         || comparison.getFirstSubmission().getName().equals(nameB) && comparison.getSecondSubmission().getName().equals(nameA))
                 .findFirst();

+ 2 - 3
jplag/src/test/java/de/jplag/clustering/ClusteringAdapterTest.java

@@ -23,7 +23,7 @@ public class ClusteringAdapterTest {
 
     @Test
     public void testClustering() {
-        List<Submission> submissions = IntStream.range(0, 4).mapToObj(x -> mock(Submission.class)).collect(Collectors.toList());
+        List<Submission> submissions = IntStream.range(0, 4).mapToObj(x -> mock(Submission.class)).toList();
         List<JPlagComparison> comparisons = new ArrayList<>(6);
         for (int i = 0; i < submissions.size(); i++) {
             for (int j = i + 1; j < submissions.size(); j++) {
@@ -38,8 +38,7 @@ public class ClusteringAdapterTest {
         GenericClusteringAlgorithm algorithm = mock(GenericClusteringAlgorithm.class);
         when(algorithm.cluster(any(RealMatrix.class))).then((InvocationOnMock invocation) -> {
             RealMatrix arg = invocation.getArgument(0);
-            Collection<Collection<Integer>> result = List.of(IntStream.range(0, arg.getRowDimension()).boxed().collect(Collectors.toList()));
-            return result;
+            return List.of(IntStream.range(0, arg.getRowDimension()).boxed().collect(Collectors.toList()));
         });
 
         ClusteringAdapter clustering = new ClusteringAdapter(comparisons, x -> 0.f);

+ 115 - 0
jplag/src/test/java/de/jplag/reporting/reportobject/mapper/MetricMapperTest.java

@@ -0,0 +1,115 @@
+package de.jplag.reporting.reportobject.mapper;
+
+import static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.jplag.JPlagComparison;
+import de.jplag.JPlagResult;
+import de.jplag.Submission;
+import de.jplag.options.JPlagOptions;
+import de.jplag.reporting.reportobject.model.TopComparison;
+
+public class MetricMapperTest {
+    private final MetricMapper metricMapper = new MetricMapper();
+
+    @Test
+    public void test_getAverageMetric() {
+        // given
+        JPlagResult jPlagResult = createJPlagResult(MockMetric.AVG, distribution(2, 3, 5, 7, 11, 13, 17, 19, 23, 29),
+                comparison(submission("1"), submission("2"), .7f), comparison(submission("3"), submission("4"), .3f));
+        // when
+        var result = metricMapper.getAverageMetric(jPlagResult);
+
+        // then
+        Assertions.assertEquals("AVG", result.name());
+        Assertions.assertIterableEquals(List.of(2, 3, 5, 7, 11, 13, 17, 19, 23, 29), result.distribution());
+        Assertions.assertEquals(List.of(new TopComparison("1", "2", .7f), new TopComparison("3", "4", .3f)), result.topComparisons());
+        Assertions.assertEquals(
+                "Average of both program coverages. This is the default similarity which"
+                        + " works in most cases: Matches with a high average similarity indicate that the programs work " + "in a very similar way.",
+                result.description());
+    }
+
+    @Test
+    public void test_getMaxMetric() {
+        // given
+        JPlagResult jPlagResult = createJPlagResult(MockMetric.MAX, distribution(2, 3, 5, 7, 11, 13, 17, 19, 23, 29),
+                comparison(submission("00"), submission("01"), .7f), comparison(submission("10"), submission("11"), .3f));
+        // when
+        var result = metricMapper.getMaxMetric(jPlagResult);
+
+        // then
+        Assertions.assertEquals("MAX", result.name());
+        Assertions.assertIterableEquals(List.of(2, 3, 5, 7, 11, 13, 17, 19, 23, 29), result.distribution());
+        Assertions.assertEquals(List.of(new TopComparison("00", "01", .7f), new TopComparison("10", "11", .3f)), result.topComparisons());
+        Assertions.assertEquals(
+                "Maximum of both program coverages. This ranking is especially useful if the programs are very "
+                        + "different in size. This can happen when dead code was inserted to disguise the origin of the plagiarized program.",
+                result.description());
+    }
+
+    private int[] distribution(int... numbers) {
+        return numbers;
+    }
+
+    private CreateSubmission submission(String name) {
+        return new CreateSubmission(name);
+    }
+
+    private Comparison comparison(CreateSubmission submission1, CreateSubmission submission2, float similarity) {
+        return new Comparison(submission1, submission2, similarity);
+    }
+
+    private JPlagResult createJPlagResult(MockMetric metricToMock, int[] distribution, Comparison... createComparisonsDto) {
+        JPlagResult jPlagResult = mock(JPlagResult.class);
+
+        if (metricToMock.equals(MockMetric.AVG)) {
+            doReturn(distribution).when(jPlagResult).getSimilarityDistribution();
+        } else if (metricToMock.equals(MockMetric.MAX)) {
+            doReturn(distribution).when(jPlagResult).getMaxSimilarityDistribution();
+
+        }
+
+        JPlagOptions options = mock(JPlagOptions.class);
+        doReturn(createComparisonsDto.length).when(options).getMaximumNumberOfComparisons();
+        doReturn(options).when(jPlagResult).getOptions();
+
+        List<JPlagComparison> comparisonList = new ArrayList<>();
+        for (Comparison comparisonDto : createComparisonsDto) {
+            Submission submission1 = mock(Submission.class);
+            doReturn(comparisonDto.submission1.name).when(submission1).getName();
+            Submission submission2 = mock(Submission.class);
+            doReturn(comparisonDto.submission2.name).when(submission2).getName();
+
+            JPlagComparison mockedComparison = mock(JPlagComparison.class);
+            doReturn(submission1).when(mockedComparison).getFirstSubmission();
+            doReturn(submission2).when(mockedComparison).getSecondSubmission();
+            if (metricToMock.equals(MockMetric.AVG)) {
+                doReturn(comparisonDto.similarity).when(mockedComparison).similarity();
+            } else if (metricToMock.equals(MockMetric.MAX)) {
+                doReturn(comparisonDto.similarity).when(mockedComparison).maximalSimilarity();
+            }
+            comparisonList.add(mockedComparison);
+        }
+
+        doReturn(comparisonList).when(jPlagResult).getComparisons(anyInt());
+        return jPlagResult;
+    }
+
+    private enum MockMetric {
+        MAX,
+        AVG
+    }
+
+    private record Comparison(CreateSubmission submission1, CreateSubmission submission2, float similarity) {
+    }
+
+    private record CreateSubmission(String name) {
+    }
+
+}

+ 2 - 2
jplag/src/test/java/de/jplag/special/VolumeTest.java

@@ -51,10 +51,10 @@ public class VolumeTest extends TestBase {
 
         var csv = readCSVResults(String.format("%s/%s", this.getBasePath(), "matches_avg.csv"));
 
-        assertEquals(csv.size(), results.getComparisons().size());
+        assertEquals(csv.size(), results.getAllComparisons().size());
         System.out.println("Volume test size: " + csv.size());
 
-        results.getComparisons().forEach(result -> {
+        results.getAllComparisons().forEach(result -> {
             var key = result.getFirstSubmission().getName() + result.getSecondSubmission().getName();
 
             assertTrue(csv.containsKey(key));

+ 1 - 0
jplag/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker

@@ -0,0 +1 @@
+mock-maker-inline

+ 4 - 6
report-viewer/src/components/DistributionDiagram.vue

@@ -3,11 +3,11 @@
 -->
 <template>
   <div class="wrapper">
-    <BarChart :chartData="chartData" :options="options" class="chart"/>
+    <BarChart :chartData="chartData" :options="options" class="chart" />
   </div>
 </template>
 
-<script>
+<script lang="ts">
 import {defineComponent, ref, watch} from "vue";
 import {BarChart} from "vue-chart-3"
 import {Chart, registerables} from 'chart.js';
@@ -21,7 +21,7 @@ export default defineComponent({
   components: {BarChart},
   props: {
     distribution: {
-      type: Array,
+      type: Array<number>,
       required: true
     }
   },
@@ -111,11 +111,9 @@ export default defineComponent({
   box-shadow: #777777 2px 3px 3px;
   display: flex;
   height: 100%;
-
 }
 
 .chart {
   width: 100%;
-
 }
-</style>
+</style>

+ 39 - 26
report-viewer/src/components/MetricButton.vue

@@ -3,41 +3,37 @@
   metric the distribution and comparisons are displayed.
 -->
 <template>
-  <div class="wrapper" v-bind:class="{ selected : isSelected }">
-    <div class="metric">
-      <p class="metric-name">{{ metricName }}</p>
-      <img alt="?" src="@/assets/help_outline_black_18dp.svg"/>
-    </div>
-    <div class="threshold">
-      <p>Threshold: </p>
-      <p>{{ metricThreshold }}%</p>
+  <div class="wrapper" v-bind:class="{ selected: isSelected }">
+    <div>
+      <p class="metric-name">{{ metric.metricName }}</p>
+      <div class="tooltip">
+        <img alt="?" src="@/assets/help_outline_black_18dp.svg" />
+        <span class="tooltiptext">{{metric.description}}</span>
+      </div>
     </div>
   </div>
 </template>
 
-<script>
-import {defineComponent} from "vue";
+<script lang="ts">
+import { defineComponent, PropType } from "vue";
+import { Metric } from "@/model/Metric";
 
 export default defineComponent({
   name: "MetricButton",
   props: {
-    metricName: {
-      type: String,
-      required: true
-    },
-    metricThreshold: {
-      type: Number,
-      required: true
+    metric: {
+      type: Object as PropType<Metric>,
+      required: true,
     },
     isSelected: {
       type: Boolean,
-      default: false
+      default: false,
     },
   },
   setup() {
-    return {}
-  }
-})
+    return {};
+  },
+});
 </script>
 
 <style scoped>
@@ -51,7 +47,6 @@ export default defineComponent({
   margin-right: 5%;
 }
 
-
 .wrapper > div > * {
   margin: 0;
   padding: 0;
@@ -67,9 +62,7 @@ export default defineComponent({
   color: white;
 }
 
-.metric {
-  margin-bottom: 2%;
-}
+
 
 .metric-name {
   font-size: xx-large;
@@ -90,4 +83,24 @@ export default defineComponent({
 .selected p {
   color: white;
 }
-</style>
+.tooltip {
+  position: relative;
+  display: inline-block;
+}
+
+.tooltip .tooltiptext {
+  visibility: hidden;
+  width: 240px;
+  background-color: black;
+  color: #fff;
+  text-align: center;
+  padding: 10px ;
+  border-radius: 6px;
+  position: absolute;
+  z-index: 1;
+}
+
+.tooltip:hover .tooltiptext {
+  visibility: visible;
+}
+</style>

+ 1 - 0
report-viewer/src/model/Metric.ts

@@ -5,6 +5,7 @@ import {ComparisonListElement} from "./ComparisonListElement";
  */
 export type Metric = {
     metricName: string,
+    description: string,
     metricThreshold: number,
     distribution: Array<number>,
     comparisons: Array<ComparisonListElement>

+ 60 - 61
report-viewer/src/model/factories/OverviewFactory.ts

@@ -1,67 +1,66 @@
-import {Overview} from "../Overview";
-import {Metric} from "../Metric";
-import {ComparisonListElement} from "../ComparisonListElement";
-import {Cluster} from "@/model/Cluster";
+import { Overview } from "../Overview";
+import { Metric } from "../Metric";
+import { ComparisonListElement } from "../ComparisonListElement";
+import { Cluster } from "@/model/Cluster";
 
 export class OverviewFactory {
+  static getOverview(json: Record<string, unknown>): Overview {
+    const submissionFolder = json.submission_folder_path as Array<string>;
+    const baseCodeFolder = "";
+    const language = json.language as string;
+    const fileExtensions = json.file_extensions as Array<string>;
+    const matchSensitivity = json.match_sensitivity as number;
+    const submissions = json.submission_ids as Array<string>;
+    const dateOfExecution = json.date_of_execution as string;
+    const duration = json.execution_time as number as number;
+    const metrics = [] as Array<Metric>;
+    const clusters = [] as Array<Cluster>;
+    (json.metrics as Array<unknown>).forEach((jsonMetric) => {
+      const metric = jsonMetric as Record<string, unknown>;
+      const comparisons = [] as Array<ComparisonListElement>;
 
-    static getOverview(json: Record<string, unknown>): Overview {
-        const submissionFolder = json.submission_folder_path as Array<string>
-        const baseCodeFolder = ""
-        const language = json.language as string
-        const fileExtensions = json.file_extensions as Array<string>
-        const matchSensitivity = json.match_sensitivity as number
-        const submissions = json.submission_ids as Array<string>
-        const dateOfExecution = json.date_of_execution as string
-        const duration = json.execution_time as number as number
-        const metrics = [] as Array<Metric>
-        const clusters = [] as Array<Cluster>
-        (json.metrics as Array<unknown>).forEach((jsonMetric) => {
-            const metric = jsonMetric as Record<string, unknown>
-            const comparisons = [] as Array<ComparisonListElement>
-
-            (metric.topComparisons as Array<Record<string, unknown>>).forEach(jsonComparison => {
-                const comparison: ComparisonListElement = {
-                    firstSubmissionId: jsonComparison.first_submission as string,
-                    secondSubmissionId: jsonComparison.second_submission as string,
-                    matchPercentage: jsonComparison.match_percentage as number
-                }
-                comparisons.push(comparison)
-            })
-
-            const newMetric: Metric = {
-                metricName: metric.name as string,
-                metricThreshold: metric.threshold as number,
-                distribution: metric.distribution as Array<number>,
-                comparisons: comparisons
-            }
-            metrics.push(newMetric)
-        });
-
-        if (json.clusters) {
-            (json.clusters as Array<unknown>).forEach(jsonCluster => {
-                const cluster = jsonCluster as Record<string, unknown>
-                const newCluster: Cluster = {
-                    averageSimilarity: cluster.average_similarity as number,
-                    strength: cluster.strength as number,
-                    members: cluster.members as Array<string>
-                }
-                clusters.push(newCluster)
-            })
+      (metric.topComparisons as Array<Record<string, unknown>>).forEach(
+        (jsonComparison) => {
+          const comparison: ComparisonListElement = {
+            firstSubmissionId: jsonComparison.first_submission as string,
+            secondSubmissionId: jsonComparison.second_submission as string,
+            matchPercentage: jsonComparison.match_percentage as number,
+          };
+          comparisons.push(comparison);
         }
+      );
+      metrics.push({
+        metricName: metric.name as string,
+        metricThreshold: metric.threshold as number,
+        distribution: metric.distribution as Array<number>,
+        comparisons: comparisons,
+        description: metric.description as string
+      });
+    });
 
-
-        return new Overview(
-            submissionFolder,
-            baseCodeFolder,
-            language,
-            fileExtensions,
-            matchSensitivity,
-            submissions,
-            dateOfExecution,
-            duration,
-            metrics,
-            clusters
-        )
+    if (json.clusters) {
+      (json.clusters as Array<unknown>).forEach((jsonCluster) => {
+        const cluster = jsonCluster as Record<string, unknown>;
+        const newCluster: Cluster = {
+          averageSimilarity: cluster.average_similarity as number,
+          strength: cluster.strength as number,
+          members: cluster.members as Array<string>,
+        };
+        clusters.push(newCluster);
+      });
     }
-}
+
+    return new Overview(
+      submissionFolder,
+      baseCodeFolder,
+      language,
+      fileExtensions,
+      matchSensitivity,
+      submissions,
+      dateOfExecution,
+      duration,
+      metrics,
+      clusters
+    );
+  }
+}

+ 1 - 2
report-viewer/src/views/OverviewView.vue

@@ -64,8 +64,7 @@
             :id="metric.metricName"
             :key="metric.metricName"
             :is-selected="selectedMetric[index]"
-            :metric-name="metric.metricName"
-            :metric-threshold="metric.metricThreshold"
+            :metric="metric"
             @click="selectMetric(index)"
           />
         </div>