From 20a66a5b18e68c3c0defc010aff9e4aceb451e37 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 13 Aug 2023 16:37:43 +0100 Subject: [PATCH 1/8] feat: airport filters can have descriptions --- include/ECFMP/flowmeasure/AirportFilter.h | 5 +++++ include/mock/AirportFilterMock.h | 1 + src/flowmeasure/ConcreteAirportFilter.cpp | 14 ++++++++++++++ src/flowmeasure/ConcreteAirportFilter.h | 4 +++- test/flowmeasure/ConcreteAirportFilterTest.cpp | 18 ++++++++++++++++-- 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/include/ECFMP/flowmeasure/AirportFilter.h b/include/ECFMP/flowmeasure/AirportFilter.h index 90bee73..e41e426 100644 --- a/include/ECFMP/flowmeasure/AirportFilter.h +++ b/include/ECFMP/flowmeasure/AirportFilter.h @@ -30,5 +30,10 @@ namespace ECFMP::FlowMeasure { * Returns the type of airport filter */ [[nodiscard]] virtual auto Type() const noexcept -> AirportFilterType = 0; + + /** + * Returns a string representation of the filter. + */ + [[nodiscard]] virtual auto FilterDescription() const noexcept -> std::string = 0; }; }// namespace ECFMP::FlowMeasure diff --git a/include/mock/AirportFilterMock.h b/include/mock/AirportFilterMock.h index 32d625c..c9a4610 100644 --- a/include/mock/AirportFilterMock.h +++ b/include/mock/AirportFilterMock.h @@ -11,6 +11,7 @@ namespace ECFMP::Mock::FlowMeasure { MOCK_METHOD(ECFMP::FlowMeasure::AirportFilterType, Type, (), (const, noexcept, override)); MOCK_METHOD(bool, ApplicableToAirport, (const std::string&), (const, noexcept, override)); MOCK_METHOD(bool, ApplicableToAircraft, (const Euroscope::EuroscopeAircraft&), (const, noexcept, override)); + MOCK_METHOD(std::string, FilterDescription, (), (const, noexcept, override)); }; }// namespace ECFMP::Mock::FlowMeasure diff --git a/src/flowmeasure/ConcreteAirportFilter.cpp b/src/flowmeasure/ConcreteAirportFilter.cpp index e2f33ee..04f3db5 100644 --- a/src/flowmeasure/ConcreteAirportFilter.cpp +++ b/src/flowmeasure/ConcreteAirportFilter.cpp @@ -39,4 +39,18 @@ namespace ECFMP::FlowMeasure { type == AirportFilterType::Departure ? aircraft.DepartureAirport() : aircraft.DestinationAirport() ); } + + std::string ConcreteAirportFilter::FilterDescription() const noexcept + { + std::string description = type == AirportFilterType::Departure ? "Departing: " : "Arriving: "; + + for (const auto& airportString: airportStrings) { + const auto isWildcard = airportString.find(WILDCARD_CHAR) != std::string::npos; + description += isWildcard ? "Any of: " + airportString : airportString; + description += ", "; + } + + // Trim the trailing ", " + return description.substr(0, description.length() - 2); + } }// namespace ECFMP::FlowMeasure diff --git a/src/flowmeasure/ConcreteAirportFilter.h b/src/flowmeasure/ConcreteAirportFilter.h index 1e29e93..cf2bf50 100644 --- a/src/flowmeasure/ConcreteAirportFilter.h +++ b/src/flowmeasure/ConcreteAirportFilter.h @@ -10,7 +10,9 @@ namespace ECFMP::FlowMeasure { [[nodiscard]] auto AirportStrings() const noexcept -> const std::set& override; [[nodiscard]] auto ApplicableToAirport(const std::string& airport) const noexcept -> bool override; [[nodiscard]] auto Type() const noexcept -> AirportFilterType override; - auto ApplicableToAircraft(const Euroscope::EuroscopeAircraft& aircraft) const noexcept -> bool override; + [[nodiscard]] auto ApplicableToAircraft(const Euroscope::EuroscopeAircraft& aircraft) const noexcept + -> bool override; + [[nodiscard]] auto FilterDescription() const noexcept -> std::string override; private: // The strings for the airport filter, may contain wildcards (*) diff --git a/test/flowmeasure/ConcreteAirportFilterTest.cpp b/test/flowmeasure/ConcreteAirportFilterTest.cpp index e82999a..a552d7f 100644 --- a/test/flowmeasure/ConcreteAirportFilterTest.cpp +++ b/test/flowmeasure/ConcreteAirportFilterTest.cpp @@ -14,7 +14,8 @@ namespace ECFMPTest::FlowMeasure { public: ConcreteAirportFilterTest() : airportFilter( - std::set{"EGKK", "LF", "EDD", "K"}, ECFMP::FlowMeasure::AirportFilterType::Destination + std::set{"EGKK", "LF**", "EDD*", "K***"}, + ECFMP::FlowMeasure::AirportFilterType::Destination ) {} @@ -23,7 +24,7 @@ namespace ECFMPTest::FlowMeasure { TEST_F(ConcreteAirportFilterTest, ItReturnsAirportStrings) { - EXPECT_EQ(std::set({"EGKK", "LF", "EDD", "K"}), airportFilter.AirportStrings()); + EXPECT_EQ(std::set({"EGKK", "LF**", "EDD*", "K***"}), airportFilter.AirportStrings()); } TEST_F(ConcreteAirportFilterTest, ItReturnsAirportFilterType) @@ -31,6 +32,19 @@ namespace ECFMPTest::FlowMeasure { EXPECT_EQ(ECFMP::FlowMeasure::AirportFilterType::Destination, airportFilter.Type()); } + TEST_F(ConcreteAirportFilterTest, ItHasADescriptionArrivingAt) + { + EXPECT_EQ("Arriving: Any of: EDD*, EGKK, Any of: K***, Any of: LF**", airportFilter.FilterDescription()); + } + + TEST_F(ConcreteAirportFilterTest, ItHasADescriptionDeparting) + { + ECFMP::FlowMeasure::ConcreteAirportFilter airportFilter2( + std::set{"EGKK", "LF**", "EDD*", "K***"}, ECFMP::FlowMeasure::AirportFilterType::Departure + ); + EXPECT_EQ("Departing: Any of: EDD*, EGKK, Any of: K***, Any of: LF**", airportFilter2.FilterDescription()); + } + class ConcreteAirportFilterApplicabilityTest : public testing::TestWithParam { public: From 7e5d47ab8b8501e924320b637d067c0e29241e8c Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 13 Aug 2023 16:41:54 +0100 Subject: [PATCH 2/8] feat: event filters can have descriptions --- include/ECFMP/flowmeasure/EventFilter.h | 5 +++++ include/mock/EventFilterMock.h | 1 + src/flowmeasure/ConcreteEventFilter.cpp | 5 +++++ src/flowmeasure/ConcreteEventFilter.h | 1 + test/flowmeasure/ConcreteEventFilterTest.cpp | 16 ++++++++++++++++ 5 files changed, 28 insertions(+) diff --git a/include/ECFMP/flowmeasure/EventFilter.h b/include/ECFMP/flowmeasure/EventFilter.h index db62ffe..f3fec8f 100644 --- a/include/ECFMP/flowmeasure/EventFilter.h +++ b/include/ECFMP/flowmeasure/EventFilter.h @@ -43,5 +43,10 @@ namespace ECFMP::FlowMeasure { * Returns true if the event participation is "Participating in" */ [[nodiscard]] virtual auto IsParticipating() const noexcept -> bool = 0; + + /** + * Returns a description of the filter. + */ + [[nodiscard]] virtual auto FilterDescription() const noexcept -> std::string = 0; }; }// namespace ECFMP::FlowMeasure diff --git a/include/mock/EventFilterMock.h b/include/mock/EventFilterMock.h index ca3a158..fba3cdf 100644 --- a/include/mock/EventFilterMock.h +++ b/include/mock/EventFilterMock.h @@ -12,6 +12,7 @@ namespace ECFMP::Mock::FlowMeasure { MOCK_METHOD(ECFMP::FlowMeasure::EventParticipation, Participation, (), (const, noexcept, override)); MOCK_METHOD(bool, IsParticipating, (), (const, noexcept, override)); MOCK_METHOD(bool, ApplicableToAircraft, (const Euroscope::EuroscopeAircraft&), (const, noexcept, override)); + MOCK_METHOD(std::string, FilterDescription, (), (const, noexcept, override)); }; }// namespace ECFMP::Mock::FlowMeasure diff --git a/src/flowmeasure/ConcreteEventFilter.cpp b/src/flowmeasure/ConcreteEventFilter.cpp index a7bf3cd..98f7f64 100644 --- a/src/flowmeasure/ConcreteEventFilter.cpp +++ b/src/flowmeasure/ConcreteEventFilter.cpp @@ -60,4 +60,9 @@ namespace ECFMP::FlowMeasure { return (IsParticipating() && isParticipating) || (!IsParticipating() && !isParticipating); } + + auto ConcreteEventFilter::FilterDescription() const noexcept -> std::string + { + return (IsParticipating() ? "Participating in event: " : "Not participating in event: ") + event->Name(); + } }// namespace ECFMP::FlowMeasure diff --git a/src/flowmeasure/ConcreteEventFilter.h b/src/flowmeasure/ConcreteEventFilter.h index 39fdc4c..70256af 100644 --- a/src/flowmeasure/ConcreteEventFilter.h +++ b/src/flowmeasure/ConcreteEventFilter.h @@ -18,6 +18,7 @@ namespace ECFMP::FlowMeasure { [[nodiscard]] auto Participation() const noexcept -> EventParticipation override; [[nodiscard]] auto IsParticipating() const noexcept -> bool override; auto ApplicableToAircraft(const Euroscope::EuroscopeAircraft& aircraft) const noexcept -> bool override; + [[nodiscard]] auto FilterDescription() const noexcept -> std::string override; private: // The event in question diff --git a/test/flowmeasure/ConcreteEventFilterTest.cpp b/test/flowmeasure/ConcreteEventFilterTest.cpp index 4bc5e79..90ff714 100644 --- a/test/flowmeasure/ConcreteEventFilterTest.cpp +++ b/test/flowmeasure/ConcreteEventFilterTest.cpp @@ -117,4 +117,20 @@ namespace ECFMPTest::FlowMeasure { ); EXPECT_FALSE(eventFilter2.ApplicableToAircraft(aircraft)); } + + TEST_F(ConcreteEventFilterTest, ItHasDescriptionParticipating) + { + ECFMP::FlowMeasure::ConcreteEventFilter eventFilter2( + event, ECFMP::FlowMeasure::EventParticipation::Participating + ); + EXPECT_EQ("Participating in event: TEST", eventFilter2.FilterDescription()); + } + + TEST_F(ConcreteEventFilterTest, ItHasDescriptionNotParticipating) + { + ECFMP::FlowMeasure::ConcreteEventFilter eventFilter2( + event, ECFMP::FlowMeasure::EventParticipation::NotParticipating + ); + EXPECT_EQ("Not participating in event: TEST", eventFilter2.FilterDescription()); + } }// namespace ECFMPTest::FlowMeasure From a087ff3fef29525ccbfaac40f783163eb48b4d00 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 13 Aug 2023 16:45:39 +0100 Subject: [PATCH 3/8] feat: level range filters have descriptions --- include/ECFMP/flowmeasure/LevelRangeFilter.h | 5 +++++ include/mock/LevelRangeFilterMock.h | 1 + src/flowmeasure/ConcreteLevelRangeFilter.cpp | 6 ++++++ src/flowmeasure/ConcreteLevelRangeFilter.h | 1 + test/flowmeasure/ConcreteLevelRangeFilterTest.cpp | 11 +++++++++++ 5 files changed, 24 insertions(+) diff --git a/include/ECFMP/flowmeasure/LevelRangeFilter.h b/include/ECFMP/flowmeasure/LevelRangeFilter.h index c79d40a..5201b25 100644 --- a/include/ECFMP/flowmeasure/LevelRangeFilter.h +++ b/include/ECFMP/flowmeasure/LevelRangeFilter.h @@ -41,5 +41,10 @@ namespace ECFMP::FlowMeasure { * Helper method to determine, given a particular altitude (e.g. 35000), does this filter apply. */ [[nodiscard]] virtual auto ApplicableToAltitude(int level) const noexcept -> bool = 0; + + /** + * Description of the filter. + */ + [[nodiscard]] virtual auto FilterDescription() const noexcept -> std::string = 0; }; }// namespace ECFMP::FlowMeasure diff --git a/include/mock/LevelRangeFilterMock.h b/include/mock/LevelRangeFilterMock.h index 0489a72..a911236 100644 --- a/include/mock/LevelRangeFilterMock.h +++ b/include/mock/LevelRangeFilterMock.h @@ -13,6 +13,7 @@ namespace ECFMP::Mock::FlowMeasure { MOCK_METHOD(bool, ApplicableToAltitude, (int), (const, noexcept, override)); MOCK_METHOD(bool, ApplicableToLevel, (int), (const, noexcept, override)); MOCK_METHOD(bool, ApplicableToAircraft, (const Euroscope::EuroscopeAircraft&), (const, noexcept, override)); + MOCK_METHOD(std::string, FilterDescription, (), (const, noexcept, override)); }; }// namespace ECFMP::Mock::FlowMeasure diff --git a/src/flowmeasure/ConcreteLevelRangeFilter.cpp b/src/flowmeasure/ConcreteLevelRangeFilter.cpp index 75fcada..bf9ce8a 100644 --- a/src/flowmeasure/ConcreteLevelRangeFilter.cpp +++ b/src/flowmeasure/ConcreteLevelRangeFilter.cpp @@ -36,4 +36,10 @@ namespace ECFMP::FlowMeasure { { return ApplicableToAltitude(aircraft.CruiseAltitude()); } + + auto ConcreteLevelRangeFilter::FilterDescription() const noexcept -> std::string + { + return type == LevelRangeFilterType::AtOrBelow ? "At or below: FL" + std::to_string(filterLevel) + : "At or above: FL" + std::to_string(filterLevel); + } }// namespace ECFMP::FlowMeasure diff --git a/src/flowmeasure/ConcreteLevelRangeFilter.h b/src/flowmeasure/ConcreteLevelRangeFilter.h index 0adeb03..1f09d48 100644 --- a/src/flowmeasure/ConcreteLevelRangeFilter.h +++ b/src/flowmeasure/ConcreteLevelRangeFilter.h @@ -13,6 +13,7 @@ namespace ECFMP::FlowMeasure { [[nodiscard]] auto ApplicableToLevel(int level) const noexcept -> bool override; [[nodiscard]] auto ApplicableToAltitude(int level) const noexcept -> bool override; auto ApplicableToAircraft(const Euroscope::EuroscopeAircraft& aircraft) const noexcept -> bool override; + [[nodiscard]] auto FilterDescription() const noexcept -> std::string override; private: // The type of level filter this is diff --git a/test/flowmeasure/ConcreteLevelRangeFilterTest.cpp b/test/flowmeasure/ConcreteLevelRangeFilterTest.cpp index 6e5d145..8f74823 100644 --- a/test/flowmeasure/ConcreteLevelRangeFilterTest.cpp +++ b/test/flowmeasure/ConcreteLevelRangeFilterTest.cpp @@ -50,6 +50,17 @@ namespace ECFMPTest::FlowMeasure { EXPECT_EQ(ECFMP::FlowMeasure::LevelRangeFilterType::AtOrBelow, levelFilter.Type()); } + TEST_F(ConcreteLevelFilterTest, ItReturnsFilterDescriptionAtOrBelow) + { + EXPECT_EQ("At or below: FL350", levelFilter.FilterDescription()); + } + + TEST_F(ConcreteLevelFilterTest, ItReturnsFilterDescriptionAtOrAbove) + { + ECFMP::FlowMeasure::ConcreteLevelRangeFilter filter(ECFMP::FlowMeasure::LevelRangeFilterType::AtOrAbove, 350); + EXPECT_EQ("At or above: FL350", filter.FilterDescription()); + } + class ConcreteLevelFilterApplicabilityTest : public testing::TestWithParam { }; From 40ce475eae99cee378b4b219e82d957e6d951eb7 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 13 Aug 2023 16:49:25 +0100 Subject: [PATCH 4/8] feat: multi level filters have description --- include/ECFMP/flowmeasure/MultipleLevelFilter.h | 5 +++++ include/mock/MultipleLevelFilterMock.h | 1 + src/flowmeasure/ConcreteMultipleLevelFilter.cpp | 10 ++++++++++ src/flowmeasure/ConcreteMultipleLevelFilter.h | 5 +++++ test/flowmeasure/ConcreteMultipleLevelFilterTest.cpp | 5 +++++ 5 files changed, 26 insertions(+) diff --git a/include/ECFMP/flowmeasure/MultipleLevelFilter.h b/include/ECFMP/flowmeasure/MultipleLevelFilter.h index 941bb57..8da4e1d 100644 --- a/include/ECFMP/flowmeasure/MultipleLevelFilter.h +++ b/include/ECFMP/flowmeasure/MultipleLevelFilter.h @@ -29,5 +29,10 @@ namespace ECFMP::FlowMeasure { * Helper method to determine, given a particular altitude (e.g. 35000), does this filter apply. */ [[nodiscard]] virtual auto ApplicableToAltitude(int level) const noexcept -> bool = 0; + + /** + * Description of the filter + */ + [[nodiscard]] virtual auto FilterDescription() const noexcept -> std::string = 0; }; }// namespace ECFMP::FlowMeasure diff --git a/include/mock/MultipleLevelFilterMock.h b/include/mock/MultipleLevelFilterMock.h index 079660c..50fc761 100644 --- a/include/mock/MultipleLevelFilterMock.h +++ b/include/mock/MultipleLevelFilterMock.h @@ -12,6 +12,7 @@ namespace ECFMP::Mock::FlowMeasure { MOCK_METHOD(bool, ApplicableToAltitude, (int), (const, noexcept, override)); MOCK_METHOD(bool, ApplicableToLevel, (int), (const, noexcept, override)); MOCK_METHOD(bool, ApplicableToAircraft, (const Euroscope::EuroscopeAircraft&), (const, noexcept, override)); + MOCK_METHOD(std::string, FilterDescription, (), (const, noexcept, override)); }; }// namespace ECFMP::Mock::FlowMeasure diff --git a/src/flowmeasure/ConcreteMultipleLevelFilter.cpp b/src/flowmeasure/ConcreteMultipleLevelFilter.cpp index 0fc754f..65f27a6 100644 --- a/src/flowmeasure/ConcreteMultipleLevelFilter.cpp +++ b/src/flowmeasure/ConcreteMultipleLevelFilter.cpp @@ -33,4 +33,14 @@ namespace ECFMP::FlowMeasure { { return ApplicableToAltitude(aircraft.CruiseAltitude()); } + + auto ConcreteMultipleLevelFilter::FilterDescription() const noexcept -> std::string + { + std::string description = "At Levels: "; + for (const auto& level: this->levels) { + description += std::to_string(level) + ", "; + } + + return description.substr(0, description.size() - 2); + } }// namespace ECFMP::FlowMeasure diff --git a/src/flowmeasure/ConcreteMultipleLevelFilter.h b/src/flowmeasure/ConcreteMultipleLevelFilter.h index d584159..292affc 100644 --- a/src/flowmeasure/ConcreteMultipleLevelFilter.h +++ b/src/flowmeasure/ConcreteMultipleLevelFilter.h @@ -34,6 +34,11 @@ namespace ECFMP::FlowMeasure { [[nodiscard]] auto ApplicableToAircraft(const Euroscope::EuroscopeAircraft& aircraft) const noexcept -> bool override; + /** + * Description of the filter + */ + [[nodiscard]] auto FilterDescription() const noexcept -> std::string override; + private: // The levels (e.g. 350) associated with this filter std::vector levels; diff --git a/test/flowmeasure/ConcreteMultipleLevelFilterTest.cpp b/test/flowmeasure/ConcreteMultipleLevelFilterTest.cpp index e8f1c23..27e3748 100644 --- a/test/flowmeasure/ConcreteMultipleLevelFilterTest.cpp +++ b/test/flowmeasure/ConcreteMultipleLevelFilterTest.cpp @@ -67,4 +67,9 @@ namespace ECFMPTest::FlowMeasure { ON_CALL(aircraft, CruiseAltitude).WillByDefault(testing::Return(35000)); EXPECT_FALSE(filter.ApplicableToAircraft(aircraft)); } + + TEST_F(ConcreteMultipleFlowLevelFilterTest, ItReturnsDescription) + { + EXPECT_EQ(filter.FilterDescription(), "At Levels: 340, 360, 390"); + } }// namespace ECFMPTest::FlowMeasure From 804cab251cc51f09bac71006ae6813c52d6f2a65 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 13 Aug 2023 16:52:49 +0100 Subject: [PATCH 5/8] feat: range to destination filters have description --- include/ECFMP/flowmeasure/RangeToDestinationFilter.h | 7 ++++++- include/mock/RangeToDestinationFilterMock.h | 1 + src/flowmeasure/ConcreteRangeToDestinationFilter.cpp | 5 +++++ src/flowmeasure/ConcreteRangeToDestinationFilter.h | 1 + test/flowmeasure/ConcreteRangeToDestinationFilterTest.cpp | 5 +++++ 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/ECFMP/flowmeasure/RangeToDestinationFilter.h b/include/ECFMP/flowmeasure/RangeToDestinationFilter.h index a6e2fa2..3e02060 100644 --- a/include/ECFMP/flowmeasure/RangeToDestinationFilter.h +++ b/include/ECFMP/flowmeasure/RangeToDestinationFilter.h @@ -8,11 +8,16 @@ namespace ECFMP::FlowMeasure { class RangeToDestinationFilter : public ChecksAircraftApplicability { public: - virtual ~RangeToDestinationFilter() = default; + ~RangeToDestinationFilter() override = default; /** * Returns the range to destination. */ [[nodiscard]] virtual auto Range() const noexcept -> int = 0; + + /** + * Description of the filter + */ + [[nodiscard]] virtual auto FilterDescription() const noexcept -> std::string = 0; }; }// namespace ECFMP::FlowMeasure diff --git a/include/mock/RangeToDestinationFilterMock.h b/include/mock/RangeToDestinationFilterMock.h index dcaa81c..4f55652 100644 --- a/include/mock/RangeToDestinationFilterMock.h +++ b/include/mock/RangeToDestinationFilterMock.h @@ -10,5 +10,6 @@ namespace ECFMP::Mock::FlowMeasure { bool, ApplicableToAircraft, (const ECFMP::Euroscope::EuroscopeAircraft&), (const, noexcept, override) ); MOCK_METHOD(int, Range, (), (const, noexcept, override)); + MOCK_METHOD(std::string, FilterDescription, (), (const, noexcept, override)); }; }// namespace ECFMP::Mock::FlowMeasure diff --git a/src/flowmeasure/ConcreteRangeToDestinationFilter.cpp b/src/flowmeasure/ConcreteRangeToDestinationFilter.cpp index 01d254e..6b6ca3f 100644 --- a/src/flowmeasure/ConcreteRangeToDestinationFilter.cpp +++ b/src/flowmeasure/ConcreteRangeToDestinationFilter.cpp @@ -15,4 +15,9 @@ namespace ECFMP::FlowMeasure { { return ((int) ceil(aircraft.RangeToDestination())) <= range; } + + auto ConcreteRangeToDestinationFilter::FilterDescription() const noexcept -> std::string + { + return "Range to Destination: <= " + std::to_string(range) + "nm"; + } }// namespace ECFMP::FlowMeasure diff --git a/src/flowmeasure/ConcreteRangeToDestinationFilter.h b/src/flowmeasure/ConcreteRangeToDestinationFilter.h index 0c12331..65c38bf 100644 --- a/src/flowmeasure/ConcreteRangeToDestinationFilter.h +++ b/src/flowmeasure/ConcreteRangeToDestinationFilter.h @@ -9,6 +9,7 @@ namespace ECFMP::FlowMeasure { explicit ConcreteRangeToDestinationFilter(int range) noexcept; auto Range() const noexcept -> int override; auto ApplicableToAircraft(const Euroscope::EuroscopeAircraft& aircraft) const noexcept -> bool override; + auto FilterDescription() const noexcept -> std::string override; private: // The range to destination. diff --git a/test/flowmeasure/ConcreteRangeToDestinationFilterTest.cpp b/test/flowmeasure/ConcreteRangeToDestinationFilterTest.cpp index d85efa3..cbc6210 100644 --- a/test/flowmeasure/ConcreteRangeToDestinationFilterTest.cpp +++ b/test/flowmeasure/ConcreteRangeToDestinationFilterTest.cpp @@ -33,4 +33,9 @@ namespace ECFMPTest::FlowMeasure { EXPECT_CALL(aircraft, RangeToDestination).WillOnce(testing::Return(123.1)); EXPECT_FALSE(filter.ApplicableToAircraft(aircraft)); } + + TEST_F(ConcreteRangeToDestinationFilterTest, FilterDescriptionReturnsCorrectString) + { + EXPECT_EQ(filter.FilterDescription(), "Range to Destination: <= 123nm"); + } }// namespace ECFMPTest::FlowMeasure From b5d224c47c5dbf87a8d3263f74fa54820f61bf04 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 13 Aug 2023 16:58:04 +0100 Subject: [PATCH 6/8] feat: route filters have description --- include/ECFMP/flowmeasure/RouteFilter.h | 10 ++++++++++ include/mock/RouteFilterMock.h | 1 + .../ConcreteRangeToDestinationFilter.cpp | 2 +- src/flowmeasure/ConcreteRouteFilter.cpp | 10 ++++++++++ src/flowmeasure/ConcreteRouteFilter.h | 1 + .../ConcreteRangeToDestinationFilterTest.cpp | 2 +- test/flowmeasure/ConcreteRouteFilterTest.cpp | 13 +++++++++---- 7 files changed, 33 insertions(+), 6 deletions(-) diff --git a/include/ECFMP/flowmeasure/RouteFilter.h b/include/ECFMP/flowmeasure/RouteFilter.h index 90741a0..1712ce7 100644 --- a/include/ECFMP/flowmeasure/RouteFilter.h +++ b/include/ECFMP/flowmeasure/RouteFilter.h @@ -9,6 +9,16 @@ namespace ECFMP::FlowMeasure { { public: virtual ~RouteFilter() = default; + + /** + * Returns the set of route strings that this filter pertains to. + * @return + */ [[nodiscard]] virtual auto RouteStrings() const noexcept -> const std::set& = 0; + + /** + * Description of the filter. + */ + [[nodiscard]] virtual auto FilterDescription() const noexcept -> std::string = 0; }; }// namespace ECFMP::FlowMeasure diff --git a/include/mock/RouteFilterMock.h b/include/mock/RouteFilterMock.h index a265cc8..dbaac02 100644 --- a/include/mock/RouteFilterMock.h +++ b/include/mock/RouteFilterMock.h @@ -9,6 +9,7 @@ namespace ECFMP::Mock::FlowMeasure { public: MOCK_METHOD(const std::set&, RouteStrings, (), (const, noexcept, override)); MOCK_METHOD(bool, ApplicableToAircraft, (const Euroscope::EuroscopeAircraft&), (const, noexcept, override)); + MOCK_METHOD(std::string, FilterDescription, (), (const, noexcept, override)); }; }// namespace ECFMP::Mock::FlowMeasure diff --git a/src/flowmeasure/ConcreteRangeToDestinationFilter.cpp b/src/flowmeasure/ConcreteRangeToDestinationFilter.cpp index 6b6ca3f..546a96e 100644 --- a/src/flowmeasure/ConcreteRangeToDestinationFilter.cpp +++ b/src/flowmeasure/ConcreteRangeToDestinationFilter.cpp @@ -18,6 +18,6 @@ namespace ECFMP::FlowMeasure { auto ConcreteRangeToDestinationFilter::FilterDescription() const noexcept -> std::string { - return "Range to Destination: <= " + std::to_string(range) + "nm"; + return "Range to Destination Less Than: " + std::to_string(range) + "nm"; } }// namespace ECFMP::FlowMeasure diff --git a/src/flowmeasure/ConcreteRouteFilter.cpp b/src/flowmeasure/ConcreteRouteFilter.cpp index abcc5eb..4cca00e 100644 --- a/src/flowmeasure/ConcreteRouteFilter.cpp +++ b/src/flowmeasure/ConcreteRouteFilter.cpp @@ -19,4 +19,14 @@ namespace ECFMP::FlowMeasure { } ) != routes.end(); } + + auto ConcreteRouteFilter::FilterDescription() const noexcept -> std::string + { + std::string description = "On route(s): "; + for (const auto& route: routes) { + description += route + ", "; + } + + return description.substr(0, description.size() - 2); + } }// namespace ECFMP::FlowMeasure diff --git a/src/flowmeasure/ConcreteRouteFilter.h b/src/flowmeasure/ConcreteRouteFilter.h index 45e8e2d..fe15b6f 100644 --- a/src/flowmeasure/ConcreteRouteFilter.h +++ b/src/flowmeasure/ConcreteRouteFilter.h @@ -9,6 +9,7 @@ namespace ECFMP::FlowMeasure { [[nodiscard]] auto RouteStrings() const noexcept -> const std::set& override; [[nodiscard]] auto ApplicableToAircraft(const Euroscope::EuroscopeAircraft& aircraft) const noexcept -> bool override; + [[nodiscard]] auto FilterDescription() const noexcept -> std::string override; private: // The route strings diff --git a/test/flowmeasure/ConcreteRangeToDestinationFilterTest.cpp b/test/flowmeasure/ConcreteRangeToDestinationFilterTest.cpp index cbc6210..2a135ea 100644 --- a/test/flowmeasure/ConcreteRangeToDestinationFilterTest.cpp +++ b/test/flowmeasure/ConcreteRangeToDestinationFilterTest.cpp @@ -36,6 +36,6 @@ namespace ECFMPTest::FlowMeasure { TEST_F(ConcreteRangeToDestinationFilterTest, FilterDescriptionReturnsCorrectString) { - EXPECT_EQ(filter.FilterDescription(), "Range to Destination: <= 123nm"); + EXPECT_EQ(filter.FilterDescription(), "Range to Destination Less Than: 123nm"); } }// namespace ECFMPTest::FlowMeasure diff --git a/test/flowmeasure/ConcreteRouteFilterTest.cpp b/test/flowmeasure/ConcreteRouteFilterTest.cpp index 6f08e88..92a7244 100644 --- a/test/flowmeasure/ConcreteRouteFilterTest.cpp +++ b/test/flowmeasure/ConcreteRouteFilterTest.cpp @@ -6,7 +6,7 @@ namespace ECFMPTest::FlowMeasure { class ConcreteRouteFilterTest : public testing::Test { public: - ConcreteRouteFilterTest() : routeFilter(std::set{"ABC", "DEF", "GHI"}) + ConcreteRouteFilterTest() : routeFilter(std::set{"ABC 123", "DEF", "GHI"}) {} ECFMP::FlowMeasure::ConcreteRouteFilter routeFilter; @@ -14,20 +14,25 @@ namespace ECFMPTest::FlowMeasure { TEST_F(ConcreteRouteFilterTest, ItReturnsRoutes) { - EXPECT_EQ(std::set({"ABC", "DEF", "GHI"}), routeFilter.RouteStrings()); + EXPECT_EQ(std::set({"ABC 123", "DEF", "GHI"}), routeFilter.RouteStrings()); } TEST_F(ConcreteRouteFilterTest, ItsApplicableToAircraftIfRouteStringIsFound) { testing::NiceMock aircraft; - EXPECT_CALL(aircraft, RouteString).WillOnce(testing::Return("LOL ABC LOL")); + EXPECT_CALL(aircraft, RouteString).WillOnce(testing::Return("LOL ABC 123 LOL")); EXPECT_TRUE(routeFilter.ApplicableToAircraft(aircraft)); } TEST_F(ConcreteRouteFilterTest, ItsNotApplicableToAircraftIfRouteStringIsNotFound) { testing::NiceMock aircraft; - EXPECT_CALL(aircraft, RouteString).WillRepeatedly(testing::Return("LOL ADG LOL")); + EXPECT_CALL(aircraft, RouteString).WillRepeatedly(testing::Return("LOL ADG 123 LOL")); EXPECT_FALSE(routeFilter.ApplicableToAircraft(aircraft)); } + + TEST_F(ConcreteRouteFilterTest, ItReturnsFilterDescription) + { + EXPECT_EQ("On route(s): ABC 123, DEF, GHI", routeFilter.FilterDescription()); + } }// namespace ECFMPTest::FlowMeasure From a3fcf8bc0e8ef981d215f265e9b0ed5ea8e812fb Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 13 Aug 2023 17:10:56 +0100 Subject: [PATCH 7/8] feat: filters can return descriptions --- .../ECFMP/flowmeasure/FlowMeasureFilters.h | 5 ++ include/mock/FlowMeasureFiltersMock.h | 1 + .../ConcreteFlowMeasureFilters.cpp | 48 +++++++++++++++++++ src/flowmeasure/ConcreteFlowMeasureFilters.h | 1 + .../ConcreteFlowMeasureFiltersTest.cpp | 11 +++++ 5 files changed, 66 insertions(+) diff --git a/include/ECFMP/flowmeasure/FlowMeasureFilters.h b/include/ECFMP/flowmeasure/FlowMeasureFilters.h index cd0088d..83400dc 100644 --- a/include/ECFMP/flowmeasure/FlowMeasureFilters.h +++ b/include/ECFMP/flowmeasure/FlowMeasureFilters.h @@ -71,5 +71,10 @@ namespace ECFMP::FlowMeasure { [[nodiscard]] virtual auto FirstRangeToDestinationFilter(const std::function& callback ) const noexcept -> std::shared_ptr = 0; + + /** + * Convenience method to return string description of the filters. + */ + [[nodiscard]] virtual auto FilterDescriptions() const noexcept -> std::vector = 0; }; }// namespace ECFMP::FlowMeasure diff --git a/include/mock/FlowMeasureFiltersMock.h b/include/mock/FlowMeasureFiltersMock.h index fb2d42d..67a0174 100644 --- a/include/mock/FlowMeasureFiltersMock.h +++ b/include/mock/FlowMeasureFiltersMock.h @@ -65,6 +65,7 @@ namespace ECFMP::Mock::FlowMeasure { (const std::function&), (const, noexcept, override) ); + MOCK_METHOD(std::vector, FilterDescriptions, (), (const, noexcept, override)); }; }// namespace ECFMP::Mock::FlowMeasure diff --git a/src/flowmeasure/ConcreteFlowMeasureFilters.cpp b/src/flowmeasure/ConcreteFlowMeasureFilters.cpp index 4fc2943..66c14f4 100644 --- a/src/flowmeasure/ConcreteFlowMeasureFilters.cpp +++ b/src/flowmeasure/ConcreteFlowMeasureFilters.cpp @@ -251,4 +251,52 @@ namespace ECFMP::FlowMeasure { return passesRangeToDestinationFilters; } + + auto ConcreteFlowMeasureFilters::FilterDescriptions() const noexcept -> std::vector + { + std::vector descriptions; + descriptions.reserve( + airportFilters.size() + eventFilters.size() + levelFilters.size() + multipleLevelFilters.size() + + routeFilters.size() + rangeToDestinationFilters.size() + ); + + std::transform( + airportFilters.cbegin(), airportFilters.cend(), std::back_inserter(descriptions), + [](const auto& filter) { + return filter->FilterDescription(); + } + ); + std::transform( + eventFilters.cbegin(), eventFilters.cend(), std::back_inserter(descriptions), + [](const auto& filter) { + return filter->FilterDescription(); + } + ); + std::transform( + levelFilters.cbegin(), levelFilters.cend(), std::back_inserter(descriptions), + [](const auto& filter) { + return filter->FilterDescription(); + } + ); + std::transform( + multipleLevelFilters.cbegin(), multipleLevelFilters.cend(), std::back_inserter(descriptions), + [](const auto& filter) { + return filter->FilterDescription(); + } + ); + std::transform( + routeFilters.cbegin(), routeFilters.cend(), std::back_inserter(descriptions), + [](const auto& filter) { + return filter->FilterDescription(); + } + ); + std::transform( + rangeToDestinationFilters.cbegin(), rangeToDestinationFilters.cend(), std::back_inserter(descriptions), + [](const auto& filter) { + return filter->FilterDescription(); + } + ); + + return descriptions; + } }// namespace ECFMP::FlowMeasure diff --git a/src/flowmeasure/ConcreteFlowMeasureFilters.h b/src/flowmeasure/ConcreteFlowMeasureFilters.h index 96f31e3..3830cb5 100644 --- a/src/flowmeasure/ConcreteFlowMeasureFilters.h +++ b/src/flowmeasure/ConcreteFlowMeasureFilters.h @@ -59,6 +59,7 @@ namespace ECFMP::FlowMeasure { [[nodiscard]] auto FirstRangeToDestinationFilter(const std::function& callback ) const noexcept -> std::shared_ptr override; + [[nodiscard]] auto FilterDescriptions() const noexcept -> std::vector override; private: // All the airport filters diff --git a/test/flowmeasure/ConcreteFlowMeasureFiltersTest.cpp b/test/flowmeasure/ConcreteFlowMeasureFiltersTest.cpp index b1d6df1..d7bd541 100644 --- a/test/flowmeasure/ConcreteFlowMeasureFiltersTest.cpp +++ b/test/flowmeasure/ConcreteFlowMeasureFiltersTest.cpp @@ -302,4 +302,15 @@ namespace ECFMPTest::FlowMeasure { ON_CALL(*mockAircraft, RangeToDestination).WillByDefault(testing::Return(101.0)); EXPECT_FALSE(filters.ApplicableToAircraft(EuroScopePlugIn::CFlightPlan(), EuroScopePlugIn::CRadarTarget())); } + + TEST_F(ConcreteFlowMeasureFiltersTest, ItReturnsDescriptionsOfAllFilters) + { + EXPECT_EQ( + std::vector( + {"Departing: EGLL", "Participating in event: Test", "At or below: FL150", "At Levels: 150, 200", + "On route(s): XAMAB", "Range to Destination Less Than: 100nm"} + ), + filters.FilterDescriptions() + ); + } }// namespace ECFMPTest::FlowMeasure From c1f7e674c6e8b12b914e9c9f72303c5d7d42d5b9 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 13 Aug 2023 17:41:01 +0100 Subject: [PATCH 8/8] feat: flow measures can have description --- include/ECFMP/flowmeasure/Measure.h | 5 + include/mock/MeasureMock.h | 1 + src/flowmeasure/ConcreteMeasure.cpp | 88 ++++++++++++++ src/flowmeasure/ConcreteMeasure.h | 3 + test/flowmeasure/ConcreteMeasureTest.cpp | 141 +++++++++++++++++++++++ 5 files changed, 238 insertions(+) diff --git a/include/ECFMP/flowmeasure/Measure.h b/include/ECFMP/flowmeasure/Measure.h index 192b91e..e9aa414 100644 --- a/include/ECFMP/flowmeasure/Measure.h +++ b/include/ECFMP/flowmeasure/Measure.h @@ -73,5 +73,10 @@ namespace ECFMP::FlowMeasure { * Throws an IllegalFlowMeasureValueException exception otherwise. */ [[nodiscard]] virtual auto SetValue() const -> const std::set& = 0; + + /** + * Returns a string representation of the measure. + */ + [[nodiscard]] virtual auto MeasureDescription() const noexcept -> std::string = 0; }; }// namespace ECFMP::FlowMeasure diff --git a/include/mock/MeasureMock.h b/include/mock/MeasureMock.h index d7640ad..040975c 100644 --- a/include/mock/MeasureMock.h +++ b/include/mock/MeasureMock.h @@ -11,6 +11,7 @@ namespace ECFMP::Mock::FlowMeasure { MOCK_METHOD(int, IntegerValue, (), (const, override)); MOCK_METHOD(double, DoubleValue, (), (const, override)); MOCK_METHOD(const std::set&, SetValue, (), (const, override)); + MOCK_METHOD(std::string, MeasureDescription, (), (const, noexcept, override)); }; }// namespace ECFMP::Mock::FlowMeasure diff --git a/src/flowmeasure/ConcreteMeasure.cpp b/src/flowmeasure/ConcreteMeasure.cpp index 4c8256d..6038749 100644 --- a/src/flowmeasure/ConcreteMeasure.cpp +++ b/src/flowmeasure/ConcreteMeasure.cpp @@ -1,4 +1,7 @@ #include "ConcreteMeasure.h" +#include "ECFMP/flowmeasure/Measure.h" +#include +#include namespace ECFMP::FlowMeasure { @@ -49,4 +52,89 @@ namespace ECFMP::FlowMeasure { throw IllegalFlowMeasureValueException(); } } + + auto ConcreteMeasure::MeasureDescription() const noexcept -> std::string + { + // No value type associated with these + if (type == MeasureType::GroundStop || type == MeasureType::Prohibit) { + return MeasureTypeToString(type); + } + + return MeasureTypeToString(type) + ": " + MeasureValueToString(type); + } + + auto ConcreteMeasure::MeasureTypeToString(MeasureType type) -> std::string + { + switch (type) { + case MeasureType::MinimumDepartureInterval: + return "Minimum Departure Interval"; + case MeasureType::AverageDepartureInterval: + return "Average Departure Interval"; + case MeasureType::PerHour: + return "Per Hour"; + case MeasureType::MilesInTrail: + return "Miles in Trail"; + case MeasureType::MaxIndicatedAirspeed: + return "Max IAS"; + case MeasureType::IndicatedAirspeedReduction: + return "IAS Reduction"; + case MeasureType::MaxMach: + return "Max Mach"; + case MeasureType::MachReduction: + return "Mach Reduction"; + case MeasureType::MandatoryRoute: + return "Mandatory Route(s)"; + case MeasureType::Prohibit: + return "Prohibit"; + case MeasureType::GroundStop: + return "Ground Stop"; + default: + return "Unknown"; + } + } + + auto ConcreteMeasure::MeasureValueToString(MeasureType measureType) const -> std::string + { + switch (measureType) { + case MeasureType::MinimumDepartureInterval: + case MeasureType::AverageDepartureInterval: { + const auto extraSeconds = intValue % 60; + const auto minutes = intValue / 60; + + if (minutes == 0) { + return std::to_string(extraSeconds) + " seconds"; + } + + if (extraSeconds == 0) { + return std::to_string(minutes) + " minutes"; + } + + return std::to_string(minutes) + " minutes " + std::to_string(extraSeconds) + " seconds"; + } + case MeasureType::PerHour: + case MeasureType::MilesInTrail: + return std::to_string(intValue); + case MeasureType::MaxIndicatedAirspeed: + case MeasureType::IndicatedAirspeedReduction: + return std::to_string(intValue) + "kts"; + case MeasureType::MaxMach: + case MeasureType::MachReduction: { + std::stringstream stream; + stream << std::fixed << std::setprecision(2) << doubleValue; + return stream.str(); + } + case MeasureType::MandatoryRoute: { + std::string routeString; + for (const auto& route: setValue) { + routeString += route + ", "; + } + return routeString.substr(0, routeString.size() - 2); + } + case MeasureType::Prohibit: + case MeasureType::GroundStop: + throw std::logic_error("MeasureType::Prohibit and MeasureType::GroundStop have no value"); + } + + return "Unknown"; + } }// namespace ECFMP::FlowMeasure diff --git a/src/flowmeasure/ConcreteMeasure.h b/src/flowmeasure/ConcreteMeasure.h index e5a7417..5e3a9d5 100644 --- a/src/flowmeasure/ConcreteMeasure.h +++ b/src/flowmeasure/ConcreteMeasure.h @@ -23,8 +23,11 @@ namespace ECFMP::FlowMeasure { [[nodiscard]] auto IntegerValue() const -> int override; [[nodiscard]] auto DoubleValue() const -> double override; [[nodiscard]] auto SetValue() const -> const std::set& override; + [[nodiscard]] auto MeasureDescription() const noexcept -> std::string override; private: + [[nodiscard]] static auto MeasureTypeToString(MeasureType type) -> std::string; + [[nodiscard]] auto MeasureValueToString(MeasureType type) const -> std::string; inline void AssertMeasureType(MeasureValueType allowedType) const; // Type of the measure diff --git a/test/flowmeasure/ConcreteMeasureTest.cpp b/test/flowmeasure/ConcreteMeasureTest.cpp index 48a91ac..318d1c7 100644 --- a/test/flowmeasure/ConcreteMeasureTest.cpp +++ b/test/flowmeasure/ConcreteMeasureTest.cpp @@ -1,4 +1,5 @@ #include "flowmeasure/ConcreteMeasure.h" +#include "ECFMP/flowmeasure/FlowMeasure.h" #include "flowmeasure/ConcreteMeasureFactory.h" namespace ECFMPTest::FlowMeasure { @@ -173,4 +174,144 @@ namespace ECFMPTest::FlowMeasure { return "type_" + std::to_string((int) info.param.measureType); } ); + + struct ConcreteMeasureDescriptionTestCase { + // Test description + std::string testDescription; + + // The type of measure + std::function measureGenerator; + + // The expected description + std::string expectedDescription; + }; + + class ConcreteMeasureDescriptionTest : public testing::TestWithParam + { + public: + }; + + TEST_P(ConcreteMeasureDescriptionTest, ItHasAnAppropriateDescription) + { + auto measure = GetParam().measureGenerator(); + EXPECT_EQ(GetParam().expectedDescription, measure.MeasureDescription()); + } + + INSTANTIATE_TEST_SUITE_P( + ConcreteMeasureDescriptionTestCases, ConcreteMeasureDescriptionTest, + testing::Values( + ConcreteMeasureDescriptionTestCase{ + "minimum_departure_interval_seconds_only", + []() { + return ECFMP::FlowMeasure::MinimumDepartureInterval(5); + }, + "Minimum Departure Interval: 5 seconds"}, + ConcreteMeasureDescriptionTestCase{ + "minimum_departure_interval_minutes_only", + []() { + return ECFMP::FlowMeasure::MinimumDepartureInterval(300); + }, + "Minimum Departure Interval: 5 minutes"}, + ConcreteMeasureDescriptionTestCase{ + "minimum_departure_interval_minutes_and_seconds", + []() { + return ECFMP::FlowMeasure::MinimumDepartureInterval(305); + }, + "Minimum Departure Interval: 5 minutes 5 seconds"}, + ConcreteMeasureDescriptionTestCase{ + "average_departure_interval_seconds_only", + []() { + return ECFMP::FlowMeasure::AverageDepartureInterval(5); + }, + "Average Departure Interval: 5 seconds"}, + ConcreteMeasureDescriptionTestCase{ + "average_departure_interval_minutes_only", + []() { + return ECFMP::FlowMeasure::AverageDepartureInterval(300); + }, + "Average Departure Interval: 5 minutes"}, + ConcreteMeasureDescriptionTestCase{ + "average_departure_interval_minutes_and_seconds", + []() { + return ECFMP::FlowMeasure::AverageDepartureInterval(305); + }, + "Average Departure Interval: 5 minutes 5 seconds"}, + ConcreteMeasureDescriptionTestCase{ + "per_hour", + []() { + return ECFMP::FlowMeasure::PerHour(5); + }, + "Per Hour: 5"}, + ConcreteMeasureDescriptionTestCase{ + "miles_in_trail", + []() { + return ECFMP::FlowMeasure::MilesInTrail(5); + }, + "Miles in Trail: 5"}, + // Max ias + ConcreteMeasureDescriptionTestCase{ + "max_ias", + []() { + return ECFMP::FlowMeasure::MaxIndicatedAirspeed(5); + }, + "Max IAS: 5kts"}, + ConcreteMeasureDescriptionTestCase{ + "ias_reduction", + []() { + return ECFMP::FlowMeasure::IndicatedAirspeedReduction(5); + }, + "IAS Reduction: 5kts"}, + ConcreteMeasureDescriptionTestCase{ + "max_mach", + []() { + return ECFMP::FlowMeasure::MaxMach(0.89123); + }, + "Max Mach: 0.89"}, + ConcreteMeasureDescriptionTestCase{ + "mach_reduction", + []() { + return ECFMP::FlowMeasure::MachReduction(0.89123); + }, + "Mach Reduction: 0.89"}, + ConcreteMeasureDescriptionTestCase{ + "mandatory_route", + []() { + return ECFMP::FlowMeasure::MandatoryRoute(std::set({"foo boo", "bar"})); + }, + "Mandatory Route(s): bar, foo boo"}, + ConcreteMeasureDescriptionTestCase{ + "mandatory_route_empty", + []() { + return ECFMP::FlowMeasure::MandatoryRoute(std::set()); + }, + "Mandatory Route(s): "}, + ConcreteMeasureDescriptionTestCase{ + "mandatory_route_single", + []() { + return ECFMP::FlowMeasure::MandatoryRoute(std::set({"foo boo"})); + }, + "Mandatory Route(s): foo boo"}, + ConcreteMeasureDescriptionTestCase{ + "mandatory_route_single_empty", + []() { + return ECFMP::FlowMeasure::MandatoryRoute(std::set({" "})); + }, + "Mandatory Route(s): "}, + ConcreteMeasureDescriptionTestCase{ + "prohibit", + []() { + return ECFMP::FlowMeasure::Prohibit(); + }, + "Prohibit"}, + ConcreteMeasureDescriptionTestCase{ + "ground_stop", + []() { + return ECFMP::FlowMeasure::GroundStop(); + }, + "Ground Stop"} + ), + [](const ::testing::TestParamInfo& info) { + return info.param.testDescription; + } + ); }// namespace ECFMPTest::FlowMeasure