diff --git a/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeCalculator.java b/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeCalculator.java index 8b3bc18f0..8fd222936 100644 --- a/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeCalculator.java +++ b/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeCalculator.java @@ -18,8 +18,10 @@ public int calculateFinalGrade(GradeValues gradeValues, GradeWeight weights) { * weights.getMetaTestsWeight(); float checkScore = codeChecksScore(gradeValues.getChecksPassed(), gradeValues.getTotalChecks()) * weights.getCodeChecksWeight(); + float qualityScore = qualityScore(gradeValues.getQualityScore()) + * weights.getQualityWeight(); - float finalDecimalGrade = branchScore + mutationScore + metaScore + checkScore; + float finalDecimalGrade = branchScore + mutationScore + metaScore + checkScore + qualityScore; int finalGrade = Math.round(finalDecimalGrade * 100); @@ -98,6 +100,16 @@ public float codeChecksScore(int checksPassed, int totalChecks) { return (float)checksPassed / totalChecks; } + /** + * For now, just a dummy method... + * @param qualityScore - a dummy score + * @return the dummy score + */ + private float qualityScore(int qualityScore) { + // dummy + return 1.0f; + } + /** * @param gradeValues the grade values * @param weights the weights diff --git a/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeValues.java b/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeValues.java index 11918ed57..792a26697 100644 --- a/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeValues.java +++ b/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeValues.java @@ -1,9 +1,6 @@ package nl.tudelft.cse1110.andy.grade; -import nl.tudelft.cse1110.andy.result.CodeChecksResult; -import nl.tudelft.cse1110.andy.result.CoverageResult; -import nl.tudelft.cse1110.andy.result.MetaTestsResult; -import nl.tudelft.cse1110.andy.result.MutationTestingResult; +import nl.tudelft.cse1110.andy.result.*; public class GradeValues { @@ -21,6 +18,8 @@ public class GradeValues { private int penalty; + private int qualityScore; + public int getCoveredBranches() { return coveredBranches; } @@ -86,12 +85,44 @@ public GradeValues setPenalty(int penalty) { return this; } + public int getQualityScore() { + return qualityScore; + } + + public GradeValues setQualityScore(int qualityScore) { + this.qualityScore = qualityScore; + return this; + } + public static GradeValues fromResults(CoverageResult coverageResults, CodeChecksResult codeCheckResults, MutationTestingResult mutationResults, MetaTestsResult metaTestResults, MetaTestsResult penaltyMetaTestResults, CodeChecksResult penaltyCodeCheckResults) { GradeValues grades = new GradeValues(); grades.setBranchGrade(coverageResults.getCoveredBranches(), coverageResults.getTotalNumberOfBranches()); grades.setCheckGrade(codeCheckResults.getNumberOfPassedChecks(), codeCheckResults.getTotalNumberOfChecks()); grades.setMutationGrade(mutationResults.getKilledMutants(), mutationResults.getTotalNumberOfMutants()); grades.setMetaGrade(metaTestResults.getPassedMetaTests(), metaTestResults.getTotalTests()); + grades.setQualityScore(0); // use alternative constructor for quality check (backward compatibility) + + // penalty is equal to the sum of the weights of all failed penalty code checks + grades.setPenalty(penaltyCodeCheckResults.getCheckResults().stream().mapToInt(check -> check.passed() ? 0 : check.getWeight()).sum() + + penaltyMetaTestResults.getMetaTestResults().stream().mapToInt(check -> check.succeeded() ? 0 : check.getWeight()).sum()); + + return grades; + } + + /* Alternative constructor for assignments with quality score */ + public static GradeValues fromResults(CoverageResult coverageResults, + CodeChecksResult codeCheckResults, + MutationTestingResult mutationResults, + MetaTestsResult metaTestResults, + MetaTestsResult penaltyMetaTestResults, + CodeChecksResult penaltyCodeCheckResults, + QualityResult qualityResult) { + GradeValues grades = new GradeValues(); + grades.setBranchGrade(coverageResults.getCoveredBranches(), coverageResults.getTotalNumberOfBranches()); + grades.setCheckGrade(codeCheckResults.getNumberOfPassedChecks(), codeCheckResults.getTotalNumberOfChecks()); + grades.setMutationGrade(mutationResults.getKilledMutants(), mutationResults.getTotalNumberOfMutants()); + grades.setMetaGrade(metaTestResults.getPassedMetaTests(), metaTestResults.getTotalTests()); + grades.setQualityScore(qualityResult.getScore()); // penalty is equal to the sum of the weights of all failed penalty code checks grades.setPenalty(penaltyCodeCheckResults.getCheckResults().stream().mapToInt(check -> check.passed() ? 0 : check.getWeight()).sum() diff --git a/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeWeight.java b/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeWeight.java index 9dd6d27cf..bdffd75b5 100644 --- a/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeWeight.java +++ b/andy/src/main/java/nl/tudelft/cse1110/andy/grade/GradeWeight.java @@ -8,12 +8,14 @@ public class GradeWeight { private final float mutationCoverageWeight; private final float metaTestsWeight; private final float codeChecksWeight; + private final float qualityWeight; public GradeWeight(float branchCoverageWeight, float mutationCoverageWeight, float metaTestsWeight, float codeChecksWeight) { this.branchCoverageWeight = branchCoverageWeight; this.mutationCoverageWeight = mutationCoverageWeight; this.metaTestsWeight = metaTestsWeight; this.codeChecksWeight = codeChecksWeight; + this.qualityWeight = 0.0f; // use alternative constructor for quality check (backward compatibility) // weights have to sum up to 1 float weightSum = branchCoverageWeight + mutationCoverageWeight + metaTestsWeight + codeChecksWeight; @@ -22,6 +24,21 @@ public GradeWeight(float branchCoverageWeight, float mutationCoverageWeight, flo throw new RuntimeException("The weight configuration is wrong! Call the teacher!"); } + /* Alternative constructor for assignments with quality check */ + public GradeWeight(float branchCoverageWeight, float mutationCoverageWeight, float metaTestsWeight, float codeChecksWeight, float qualityWeight) { + this.branchCoverageWeight = branchCoverageWeight; + this.mutationCoverageWeight = mutationCoverageWeight; + this.metaTestsWeight = metaTestsWeight; + this.codeChecksWeight = codeChecksWeight; + this.qualityWeight = qualityWeight; + + // weights have to sum up to 1 + float weightSum = branchCoverageWeight + mutationCoverageWeight + metaTestsWeight + codeChecksWeight + qualityWeight; + float epsilon = Math.abs(1 - weightSum); + if(epsilon > 0.001) + throw new RuntimeException("The weight configuration is wrong! Call the teacher!"); + } + public float getBranchCoverageWeight() { return branchCoverageWeight; } @@ -38,6 +55,10 @@ public float getCodeChecksWeight() { return codeChecksWeight; } + public float getQualityWeight() { + return qualityWeight; + } + public static GradeWeight fromConfig(Map weights) { float coverage = weights.getOrDefault("coverage", 0.0f); float mutation = weights.getOrDefault("mutation", 0.0f); diff --git a/andy/src/main/java/nl/tudelft/cse1110/andy/result/QualityResult.java b/andy/src/main/java/nl/tudelft/cse1110/andy/result/QualityResult.java new file mode 100644 index 000000000..b7dbb89f8 --- /dev/null +++ b/andy/src/main/java/nl/tudelft/cse1110/andy/result/QualityResult.java @@ -0,0 +1,37 @@ +package nl.tudelft.cse1110.andy.result; + +import java.util.Collections; +import java.util.List; + +public class QualityResult { + private int score; // between 0 and 1 + + public QualityResult(int score) { + // this.score = score; + // dummy: + this.score = 1; + } + + public static QualityResult build(int score) { + return new QualityResult(score); + } + + public static QualityResult empty() { + return new QualityResult(0); + } + + public int getScore() { + return score; + } + + public void setScore(int score) { + this.score = score; + } + + @Override + public String toString() { + return "QualityResult{" + + "score=" + score + + '}'; + } +} diff --git a/andy/src/main/java/nl/tudelft/cse1110/andy/result/Result.java b/andy/src/main/java/nl/tudelft/cse1110/andy/result/Result.java index 18d0bf374..bd1d6d31d 100644 --- a/andy/src/main/java/nl/tudelft/cse1110/andy/result/Result.java +++ b/andy/src/main/java/nl/tudelft/cse1110/andy/result/Result.java @@ -20,6 +20,7 @@ public class Result { private final double timeInSeconds; private final GradeWeight weights; private final String successMessage; + private final QualityResult qualityResult; public Result(CompilationResult compilation, UnitTestsResult tests, MutationTestingResult mutationTesting, CodeChecksResult codeChecks, CodeChecksResult penaltyCodeChecks, CoverageResult coverage, MetaTestsResult metaTests, MetaTestsResult penaltyMetaTests, int penalty, int finalGrade, GenericFailure genericFailure, double timeInSeconds, GradeWeight weights, String successMessage) { this.compilation = compilation; @@ -37,6 +38,30 @@ public Result(CompilationResult compilation, UnitTestsResult tests, MutationTest this.weights = weights; this.successMessage = successMessage; + this.qualityResult = QualityResult.empty(); + + if(finalGrade < 0 || finalGrade>100) + throw new RuntimeException("Invalid final grade"); + } + + public Result(CompilationResult compilation, UnitTestsResult tests, MutationTestingResult mutationTesting, CodeChecksResult codeChecks, CodeChecksResult penaltyCodeChecks, CoverageResult coverage, MetaTestsResult metaTests, MetaTestsResult penaltyMetaTests, int penalty, int finalGrade, GenericFailure genericFailure, double timeInSeconds, GradeWeight weights, String successMessage, + QualityResult qualityResult) { + this.compilation = compilation; + this.tests = tests; + this.mutationTesting = mutationTesting; + this.codeChecks = codeChecks; + this.penaltyCodeChecks = penaltyCodeChecks; + this.coverage = coverage; + this.metaTests = metaTests; + this.penaltyMetaTests = penaltyMetaTests; + this.penalty = penalty; + this.finalGrade = finalGrade; + this.genericFailure = genericFailure; + this.timeInSeconds = timeInSeconds; + this.weights = weights; + this.successMessage = successMessage; + this.qualityResult = qualityResult; + if(finalGrade < 0 || finalGrade>100) throw new RuntimeException("Invalid final grade"); } @@ -100,6 +125,10 @@ public GenericFailure getGenericFailure() { return genericFailure; } + public QualityResult getQualityResult() { + return qualityResult; + } + public boolean hasFailed() { return !compilation.successful() || tests.hasTestsFailingOrFailures() || hasGenericFailure(); } @@ -122,6 +151,7 @@ public String toString() { ", timeInSeconds=" + timeInSeconds + ", weights=" + weights + ", successMessage=" + successMessage + + ", qualityResult=" + qualityResult + '}'; } diff --git a/andy/src/main/java/nl/tudelft/cse1110/andy/result/ResultBuilder.java b/andy/src/main/java/nl/tudelft/cse1110/andy/result/ResultBuilder.java index 69856d294..10a41b6d4 100644 --- a/andy/src/main/java/nl/tudelft/cse1110/andy/result/ResultBuilder.java +++ b/andy/src/main/java/nl/tudelft/cse1110/andy/result/ResultBuilder.java @@ -50,6 +50,7 @@ public class ResultBuilder { private CoverageResult coverageResults = CoverageResult.empty(); private MetaTestsResult metaTestResults = MetaTestsResult.empty(); private MetaTestsResult penaltyMetaTestResults = MetaTestsResult.empty(); + private QualityResult qualityResult = QualityResult.empty(); public ResultBuilder(Context ctx, GradeCalculator gradeCalculator) { this.ctx = ctx; @@ -249,6 +250,13 @@ public void logPenaltyMetaTests(int score, int totalTests, List this.penaltyMetaTestResults.addResults(score, totalTests, metaTestResults); } + /* + * Quality + */ + public void logQuality() { + // dummy + } + /* * Generic failures */ diff --git a/andy/src/test/java/unit/grade/GradeCalculatorTest.java b/andy/src/test/java/unit/grade/GradeCalculatorTest.java index d7e8ea5eb..ced541697 100644 --- a/andy/src/test/java/unit/grade/GradeCalculatorTest.java +++ b/andy/src/test/java/unit/grade/GradeCalculatorTest.java @@ -89,6 +89,35 @@ private static Stream differentWeights() { ); } + @ParameterizedTest + @MethodSource("withQualityCheck") + void withQualityCheck(int coveredBranches, int totalBranches, + int detectedMutations, int totalMutations, + int metaTestsPassed, int totalMetaTests, + int checksPassed, int totalChecks, int qualityScore, int expectedGrade, + float branchCoverageWeight, float mutationCoverageWeight, + float metaTestsWeight, float codeChecksWeight, float qualityWeight) { + + GradeWeight weights = new GradeWeight(branchCoverageWeight, mutationCoverageWeight, metaTestsWeight, codeChecksWeight, qualityWeight); + + GradeValues grades = new GradeValues(); + grades.setBranchGrade(coveredBranches, totalBranches); + grades.setMutationGrade(detectedMutations, totalMutations); + grades.setMetaGrade(metaTestsPassed, totalMetaTests); + grades.setCheckGrade(checksPassed, totalChecks); + grades.setQualityScore(qualityScore); + + int finalGrade = new GradeCalculator().calculateFinalGrade(grades, weights); + + assertThat(finalGrade).isEqualTo(expectedGrade); + } + + private static Stream withQualityCheck() { + return Stream.of( + of(25, 25, 55, 55, 100, 100, 5, 5, 1, 100, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f) // 0.20*1 + 0.20*1 + 0.20*1 + 0.20*1 + 0.20*1 = 1.0 --> 100 + ); + } + /* * Test where the grade is between 99.5 and 100, should be rounded down to 99 and not * rounded up to 100, as 100 should only be achievable if everything diff --git a/andy/src/test/java/unit/grade/GradeWeightTest.java b/andy/src/test/java/unit/grade/GradeWeightTest.java index d3227dbe4..5ac113c8c 100644 --- a/andy/src/test/java/unit/grade/GradeWeightTest.java +++ b/andy/src/test/java/unit/grade/GradeWeightTest.java @@ -17,4 +17,15 @@ void preCondition() { new GradeWeight(0.33f, 0.33f, 0.33f, 0.01f); new GradeWeight(0.33f, 0.33f, 0.34f, 0f); } + + @Test + void preConditionWithQualityCheck() { + assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> + new GradeWeight(0.1f, 0.1f, 0.1f, 0.1f, 0.1f) + ).withMessageContaining("weight configuration is wrong"); + + new GradeWeight(0.20f, 0.20f, 0.20f, 0.20f, 0.20f); + new GradeWeight(0.30f, 0.30f, 0.30f, 0.09f, 0.01f); + new GradeWeight(0.25f, 0.25f, 0.25f, 0.25f, 0.0f); + } }