diff --git a/include/tcframe/runner/aggregator/MinAggregator.hpp b/include/tcframe/runner/aggregator/MinAggregator.hpp index b5c21cd5..5904e255 100644 --- a/include/tcframe/runner/aggregator/MinAggregator.hpp +++ b/include/tcframe/runner/aggregator/MinAggregator.hpp @@ -23,7 +23,13 @@ class MinAggregator : public TestCaseAggregator { aggregatedVerdict = max(aggregatedVerdict, testCaseVerdict.verdict()); if (testCaseVerdict.verdict() == Verdict::ok()) { - aggregatedPoints = min(aggregatedPoints, testCaseVerdict.points().value()); + double points = 0.0; + if (testCaseVerdict.percentage()) { + points = testCaseVerdict.percentage().value() * subtaskPoints / 100.0; + } else if (testCaseVerdict.points()) { + points = testCaseVerdict.points().value(); + } + aggregatedPoints = min(aggregatedPoints, points); } else if (!(testCaseVerdict.verdict() == Verdict::ac())) { aggregatedPoints = 0; } diff --git a/include/tcframe/runner/verdict/TestCaseVerdict.hpp b/include/tcframe/runner/verdict/TestCaseVerdict.hpp index ac089581..3221c858 100644 --- a/include/tcframe/runner/verdict/TestCaseVerdict.hpp +++ b/include/tcframe/runner/verdict/TestCaseVerdict.hpp @@ -16,6 +16,7 @@ struct TestCaseVerdict { private: Verdict verdict_; optional points_; + optional percentage_; public: TestCaseVerdict() @@ -29,6 +30,11 @@ struct TestCaseVerdict { : verdict_(move(status)) , points_(optional(points)) {} + TestCaseVerdict(Verdict status, optional points, optional percentage) + : verdict_(move(status)) + , points_(move(points)) + , percentage_(move(percentage)) {} + const Verdict& verdict() const { return verdict_; } @@ -37,8 +43,12 @@ struct TestCaseVerdict { return points_; } + const optional& percentage() const { + return percentage_; + } + bool operator==(const TestCaseVerdict& o) const { - return tie(verdict_, points_) == tie(o.verdict_, o.points_); + return tie(verdict_, points_, percentage_) == tie(o.verdict_, o.points_, percentage_); } string toBriefString() const { @@ -59,17 +69,27 @@ struct TestCaseVerdict { private: string pointsToString() const { - if (!points_) { - return ""; - } - string points = StringUtils::toString(points_.value(), 2); - while (points.back() == '0') { - points.pop_back(); + if (points_) { + string points = StringUtils::toString(points_.value(), 2); + while (points.back() == '0') { + points.pop_back(); + } + if (points.back() == '.') { + points.pop_back(); + } + return points; } - if (points.back() == '.') { - points.pop_back(); + if (percentage_) { + string points = StringUtils::toString(percentage_.value(), 2); + while (points.back() == '0') { + points.pop_back(); + } + if (points.back() == '.') { + points.pop_back(); + } + return points + "%"; } - return points; + return ""; } }; diff --git a/include/tcframe/runner/verdict/TestCaseVerdictParser.hpp b/include/tcframe/runner/verdict/TestCaseVerdictParser.hpp index 325c0f6c..9da14f32 100644 --- a/include/tcframe/runner/verdict/TestCaseVerdictParser.hpp +++ b/include/tcframe/runner/verdict/TestCaseVerdictParser.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -35,12 +36,21 @@ class TestCaseVerdictParser { if (!getline(*in, secondLine)) { throw runtime_error("Expected: on the second line"); } - string pointsString = StringUtils::split(secondLine, ' ')[0]; - optional points = StringUtils::toNumber(pointsString); - if (points) { + string result = StringUtils::split(secondLine, ' ')[0]; + + if (!result.empty() && result.back() == '%') { + optional percentage = StringUtils::toNumber(result.substr(0, result.size() - 1)); + if (!percentage) { + throw runtime_error("Unknown percentage format: " + result); + } + return {Verdict::ok(), optional(), percentage}; + } else { + optional points = StringUtils::toNumber(result); + if (!points) { + throw runtime_error("Unknown points format: " + result); + } return {Verdict::ok(), points.value()}; } - throw runtime_error("Unknown points format: " + pointsString); } throw runtime_error("Unknown verdict: " + verdictCode); diff --git a/test/unit/tcframe/runner/aggregator/MinAggregatorTests.cpp b/test/unit/tcframe/runner/aggregator/MinAggregatorTests.cpp index 222db0ea..e617c1ec 100644 --- a/test/unit/tcframe/runner/aggregator/MinAggregatorTests.cpp +++ b/test/unit/tcframe/runner/aggregator/MinAggregatorTests.cpp @@ -38,4 +38,13 @@ TEST_F(MinAggregatorTests, Aggregate_MinOKPoints) { EXPECT_THAT(aggregator.aggregate(verdicts, 70), Eq(SubtaskVerdict(Verdict::ok(), 20))); } +TEST_F(MinAggregatorTests, Aggregate_MinOKPercentage) { + vector verdicts = { + TestCaseVerdict(Verdict::ac()), + TestCaseVerdict(Verdict::ok(), optional(), optional(25)), + TestCaseVerdict(Verdict::ok(), optional(), optional(50))}; + + EXPECT_THAT(aggregator.aggregate(verdicts, 70), Eq(SubtaskVerdict(Verdict::ok(), 17.5))); +} + } diff --git a/test/unit/tcframe/runner/verdict/TestCaseVerdictParserTests.cpp b/test/unit/tcframe/runner/verdict/TestCaseVerdictParserTests.cpp index e722d863..f0abb511 100644 --- a/test/unit/tcframe/runner/verdict/TestCaseVerdictParserTests.cpp +++ b/test/unit/tcframe/runner/verdict/TestCaseVerdictParserTests.cpp @@ -25,14 +25,22 @@ TEST_F(TestCaseVerdictParserTests, ParseStream_WA) { EXPECT_THAT(parser.parseStream(new istringstream("WA\n")), Eq(TestCaseVerdict(Verdict::wa()))); } -TEST_F(TestCaseVerdictParserTests, ParseStream_OK) { +TEST_F(TestCaseVerdictParserTests, ParseStream_OK_Points) { EXPECT_THAT(parser.parseStream(new istringstream("OK\n70\n")), Eq(TestCaseVerdict(Verdict::ok(), 70))); } -TEST_F(TestCaseVerdictParserTests, ParseStream_OK_WithFeedback) { +TEST_F(TestCaseVerdictParserTests, ParseStream_OK_Points_WithFeedback) { EXPECT_THAT(parser.parseStream(new istringstream("OK\n70 text\n")), Eq(TestCaseVerdict(Verdict::ok(), 70))); } +TEST_F(TestCaseVerdictParserTests, ParseStream_OK_Percentage) { + EXPECT_THAT(parser.parseStream(new istringstream("OK\n25%\n")), Eq(TestCaseVerdict(Verdict::ok(), optional(), optional(25)))); +} + +TEST_F(TestCaseVerdictParserTests, ParseStream_OK_Percentage_WithFeedback) { + EXPECT_THAT(parser.parseStream(new istringstream("OK\n25% text\n")), Eq(TestCaseVerdict(Verdict::ok(), optional(), optional(25)))); +} + TEST_F(TestCaseVerdictParserTests, ParseStream_OK_Failed_EmptySecondLine) { try { parser.parseStream(new istringstream("OK\n")); diff --git a/test/unit/tcframe/runner/verdict/TestCaseVerdictTests.cpp b/test/unit/tcframe/runner/verdict/TestCaseVerdictTests.cpp index a97b55e5..eae8fc2b 100644 --- a/test/unit/tcframe/runner/verdict/TestCaseVerdictTests.cpp +++ b/test/unit/tcframe/runner/verdict/TestCaseVerdictTests.cpp @@ -9,7 +9,7 @@ namespace tcframe { class TestCaseVerdictTests : public Test {}; -TEST_F(TestCaseVerdictTests, ToString_WithoutPoints) { +TEST_F(TestCaseVerdictTests, ToString) { TestCaseVerdict verdict(Verdict::wa()); EXPECT_THAT(verdict.toString(), Eq(verdict.verdict().name())); } @@ -25,7 +25,14 @@ TEST_F(TestCaseVerdictTests, ToString_WithPoints) { EXPECT_THAT(verdict4.toString(), Eq(verdict4.verdict().name() + " [30.12]")); } -TEST_F(TestCaseVerdictTests, ToBriefString_WithoutPoints) { +TEST_F(TestCaseVerdictTests, ToString_WithPercentage) { + TestCaseVerdict verdict1(Verdict::ok(), optional(), optional(25)); + EXPECT_THAT(verdict1.toString(), Eq(verdict1.verdict().name() + " [25%]")); + TestCaseVerdict verdict2(Verdict::ok(), optional(), optional(25.5)); + EXPECT_THAT(verdict2.toString(), Eq(verdict2.verdict().name() + " [25.5%]")); +} + +TEST_F(TestCaseVerdictTests, ToBriefString) { TestCaseVerdict verdict(Verdict::wa()); EXPECT_THAT(verdict.toBriefString(), Eq(verdict.verdict().code())); } @@ -35,4 +42,9 @@ TEST_F(TestCaseVerdictTests, ToBriefString_WithPoints) { EXPECT_THAT(verdict.toBriefString(), Eq(verdict.verdict().code() + " 30")); } +TEST_F(TestCaseVerdictTests, ToBriefString_WithPercentage) { + TestCaseVerdict verdict(Verdict::ok(), optional(), optional(25)); + EXPECT_THAT(verdict.toBriefString(), Eq(verdict.verdict().code() + " 25%")); +} + }