Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: flow measure statuses not being delivered #16

Merged
merged 1 commit into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/flowmeasure/FlowMeasureStatusUpdates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace ECFMP::FlowMeasure {
if (!canonicalFlowMeasures.contains(canonicalInformation.Identifier())) {
canonicalFlowMeasures[canonicalInformation.Identifier()] = measure;
BroadcastStatusUpdate(measure);
return;
continue;
}

// Switch the measure we have stored out for the new one
Expand Down
100 changes: 100 additions & 0 deletions test/api/FlowMeasureDataParserTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,106 @@ namespace ECFMPTest::Api {
}
);

class FlowMeasureDataParserMultipleMeasuresTest : public testing::Test
{
public:
void SetUp() override
{
// Create the mock parsers
auto filterParser = std::make_unique<MockFlowMeasureFilterParser>();
filterParserRaw = filterParser.get();
auto measureParser = std::make_unique<MockFlowMeasureMeasureParser>();
measureParserRaw = measureParser.get();

eventBus = ECFMP::EventBus::MakeEventBus();
mockEventHandler = std::make_shared<MockFlowMeasuresUpdatedEventHandler>(2);
mockEventHandlerInternal = std::make_shared<MockInternalFlowMeasuresUpdatedEventHandler>(2);
eventBus->SubscribeSync<ECFMP::Plugin::FlowMeasuresUpdatedEvent>(mockEventHandler);
eventBus->SubscribeSync<ECFMP::Plugin::InternalFlowMeasuresUpdatedEvent>(mockEventHandlerInternal);

customFilters =
std::make_shared<std::vector<std::shared_ptr<ECFMP::FlowMeasure::CustomFlowMeasureFilter>>>();
parser = std::make_unique<ECFMP::Api::FlowMeasureDataParser>(
std::move(filterParser), std::move(measureParser), std::make_shared<Log::MockLogger>(), eventBus,
customFilters
);

firs.Add(std::make_shared<ECFMP::FlightInformationRegion::ConcreteFlightInformationRegion>(
1, "EGTT", "London"
));
firs.Add(std::make_shared<ECFMP::FlightInformationRegion::ConcreteFlightInformationRegion>(
2, "EGPX", "Scottish"
));

events.Add(std::make_shared<ECFMP::Event::ConcreteEvent>(
1, "Test event", now, plusOneHour, firs.Get(1), "abc",
std::vector<std::shared_ptr<ECFMP::Event::EventParticipant>>{}
));
}

std::shared_ptr<std::vector<std::shared_ptr<ECFMP::FlowMeasure::CustomFlowMeasureFilter>>> customFilters;
std::shared_ptr<MockFlowMeasuresUpdatedEventHandler> mockEventHandler;
std::shared_ptr<MockInternalFlowMeasuresUpdatedEventHandler> mockEventHandlerInternal;
std::shared_ptr<ECFMP::EventBus::InternalEventBus> eventBus;
MockFlowMeasureFilterParser* filterParserRaw;
MockFlowMeasureMeasureParser* measureParserRaw;
ECFMP::Api::InternalEventCollection events;
ECFMP::Api::InternalFlightInformationRegionCollection firs;
std::unique_ptr<ECFMP::Api::FlowMeasureDataParser> parser;
};

TEST_F(FlowMeasureDataParserMultipleMeasuresTest, ItParsesFlowMeasures)
{
// Mock the measure and filter parser returns
ON_CALL(*filterParserRaw, Parse(testing::_, testing::_))
.WillByDefault(testing::Invoke([&](const nlohmann::json& data,
const ECFMP::Api::InternalEventCollection& events) {
return std::make_unique<ECFMP::FlowMeasure::ConcreteFlowMeasureFilters>(
std::list<std::shared_ptr<ECFMP::FlowMeasure::AirportFilter>>(),
std::list<std::shared_ptr<ECFMP::FlowMeasure::EventFilter>>(),
std::list<std::shared_ptr<ECFMP::FlowMeasure::RouteFilter>>(),
std::list<std::shared_ptr<ECFMP::FlowMeasure::LevelRangeFilter>>(),
std::list<std::shared_ptr<ECFMP::FlowMeasure::MultipleLevelFilter>>(),
std::list<std::shared_ptr<ECFMP::FlowMeasure::RangeToDestinationFilter>>(),
std::make_shared<Euroscope::MockEuroscopeAircraftFactory>()
);
}));

ON_CALL(*measureParserRaw, Parse(testing::_)).WillByDefault(testing::Invoke([&](const nlohmann::json& data) {
return std::make_unique<ECFMP::FlowMeasure::ConcreteMeasure>(ECFMP::FlowMeasure::MeasureType::GroundStop);
}));

const auto measure1 = nlohmann::json{
{"id", 1},
{"event_id", 1},
{"ident", "EGTT-01A"},
{"reason", "reason 1"},
{"starttime", ECFMP::Date::DateStringFromTimePoint(plusOneHour)},
{"endtime", ECFMP::Date::DateStringFromTimePoint(plusTwoHours)},
{"withdrawn_at", nlohmann::json::value_t::null},
{"measure", {{"foo", "bar"}}},// Measure is mocked and handled elsewhere, so placeholder
{"filters", {{"foo", "baz"}}},// Filter is mocked and handled elsewhere, so placeholder
{"notified_flight_information_regions", nlohmann::json::array({1, 2})}};

const auto measure2 = nlohmann::json{
{"id", 2},
{"event_id", 1},
{"ident", "EGTT-01B"},
{"reason", "reason 1"},
{"starttime", ECFMP::Date::DateStringFromTimePoint(plusOneHour)},
{"endtime", ECFMP::Date::DateStringFromTimePoint(plusTwoHours)},
{"withdrawn_at", nlohmann::json::value_t::null},
{"measure", {{"foo", "bar"}}},// Measure is mocked and handled elsewhere, so placeholder
{"filters", {{"foo", "baz"}}},// Filter is mocked and handled elsewhere, so placeholder
{"notified_flight_information_regions", nlohmann::json::array({1, 2})}};

auto data = nlohmann::json{{"flow_measures", nlohmann::json::array({measure1, measure2})}};
auto flowMeasures = parser->ParseFlowMeasures(data, events, firs);
EXPECT_EQ(2, flowMeasures->Count());
EXPECT_EQ("EGTT-01A", flowMeasures->begin()->Identifier());
EXPECT_EQ("EGTT-01B", (++flowMeasures->begin())->Identifier());
}

struct FlowMeasureDataParserBadDataTestCase {
std::string description;
nlohmann::json data;
Expand Down
87 changes: 86 additions & 1 deletion test/flowmeasure/FlowMeasureStatusUpdatesTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,44 @@ namespace ECFMPTest::FlowMeasure {
std::shared_ptr<ECFMP::FlowMeasure::FlowMeasure> expectedMeasure;
};

template<typename EventType>
class FlowMeasureStatusUpdatesMultipleEventListener : public ECFMP::EventBus::EventListener<EventType>
{
public:
FlowMeasureStatusUpdatesMultipleEventListener(
std::unordered_map<int, std::shared_ptr<ECFMP::FlowMeasure::FlowMeasure>> expectedMeasures
)
: expectedMeasures(std::move(expectedMeasures))
{}

void OnEvent(const EventType& event) override
{
callCount++;
EXPECT_TRUE(expectedMeasures.contains(event.flowMeasure->Id()));
expectedMeasures.erase(event.flowMeasure->Id());
}

void AssertCalledOnce()
{
EXPECT_EQ(callCount, 1);
}

void AssertCalledTwice()
{
EXPECT_EQ(callCount, 2);
}

void AssertNotCalled()
{
EXPECT_EQ(callCount, 0);
}

private:
int callCount = 0;

std::unordered_map<int, std::shared_ptr<ECFMP::FlowMeasure::FlowMeasure>> expectedMeasures;
};

class FlowMeasureReissuedEventListener
: public ECFMP::EventBus::EventListener<ECFMP::Plugin::FlowMeasureReissuedEvent>
{
Expand Down Expand Up @@ -87,12 +125,13 @@ namespace ECFMPTest::FlowMeasure {
{}

[[nodiscard]] static auto
GetMeasureMock(const std::string& identifier, const ECFMP::FlowMeasure::MeasureStatus status)
GetMeasureMock(const std::string& identifier, const ECFMP::FlowMeasure::MeasureStatus status, int id = 1)
-> std::shared_ptr<ECFMP::Mock::FlowMeasure::FlowMeasureMock>
{
auto measure = std::make_shared<ECFMP::Mock::FlowMeasure::FlowMeasureMock>();
auto canonicalInformation = std::make_shared<ECFMP::FlowMeasure::CanonicalFlowMeasureInfo>(identifier);

ON_CALL(*measure, Id).WillByDefault(testing::Return(id));
ON_CALL(*measure, CanonicalInformation)
.WillByDefault(testing::Invoke(
[canonicalInformation]() -> const ECFMP::FlowMeasure::CanonicalFlowMeasureInfo& {
Expand Down Expand Up @@ -265,6 +304,52 @@ namespace ECFMPTest::FlowMeasure {
listenerExpired->AssertCalledOnce();
}

TEST_F(FlowMeasureStatusUpdatesTest, ItBroadcastsMultipleMeasures)
{
// Set up the measure and collection
auto measure1 = GetMeasureMock("EGTT01A", ECFMP::FlowMeasure::MeasureStatus::Expired);
auto measure2 = GetMeasureMock("EGTT01B", ECFMP::FlowMeasure::MeasureStatus::Expired, 2);
std::shared_ptr<ECFMP::Api::InternalFlowMeasureCollection> flowMeasures =
std::make_shared<ECFMP::Api::InternalFlowMeasureCollection>();
flowMeasures->Add(measure1);
flowMeasures->Add(measure2);

// Set up event listeners
auto listenerNotified =
std::make_shared<FlowMeasureStatusUpdatesEventListener<ECFMP::Plugin ::FlowMeasureNotifiedEvent>>(
measure1
);
auto listenerActivated =
std::make_shared<FlowMeasureStatusUpdatesEventListener<ECFMP::Plugin::FlowMeasureActivatedEvent>>(
measure1
);
auto listenerWithdrawn =
std::make_shared<FlowMeasureStatusUpdatesEventListener<ECFMP::Plugin::FlowMeasureWithdrawnEvent>>(
measure1
);

const std::unordered_map<int, std::shared_ptr<ECFMP::FlowMeasure::FlowMeasure>> expectedMeasures = {
{measure1->Id(), measure1},
{measure2->Id(), measure2}};
auto listenerExpired =
std::make_shared<FlowMeasureStatusUpdatesMultipleEventListener<ECFMP::Plugin::FlowMeasureExpiredEvent>>(
expectedMeasures
);

eventBus->SubscribeSync<ECFMP::Plugin::FlowMeasureNotifiedEvent>(listenerNotified);
eventBus->SubscribeSync<ECFMP::Plugin::FlowMeasureActivatedEvent>(listenerActivated);
eventBus->SubscribeSync<ECFMP::Plugin::FlowMeasureWithdrawnEvent>(listenerWithdrawn);
eventBus->SubscribeSync<ECFMP::Plugin::FlowMeasureExpiredEvent>(listenerExpired);

// Run event and check the events broadcast
EXPECT_EQ(2, flowMeasures->GetUnderlyingCollection().size());
flowMeasureStatusUpdates->OnEvent({flowMeasures});
listenerNotified->AssertNotCalled();
listenerActivated->AssertNotCalled();
listenerWithdrawn->AssertNotCalled();
listenerExpired->AssertCalledTwice();
}

TEST_F(FlowMeasureStatusUpdatesTest, ItBroadcastsReissuedEventOnCanonicalUpodate)
{
// Set up the measure and collection
Expand Down
Loading