From 1b011b739dbe7eb652d603f6a31f92d9ebfea0a2 Mon Sep 17 00:00:00 2001 From: Yanis Srairi Date: Mon, 5 Oct 2026 13:51:34 +0200 Subject: [PATCH] fix: clear[] crashed after a fire whose front had stopped When the last event is taken out of the TimeTable, getUpcomingEvent left head pointing at it. If that event then had an infinite time (its node stopped), insert() deleted it and head dangled, so clear() deleted it a second time. dropAtomEvents could likewise delete the event being processed. --- CHANGELOG.md | 3 ++ src/TimeTable.cpp | 1 + tests/unit/CMakeLists.txt | 5 ++-- tests/unit/test_timetable.cpp | 52 +++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 tests/unit/test_timetable.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d77f86..6e75a1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,8 @@ Merged since v2.5.0, not yet released. - `StringRepresentation` kept its output buffer, current level and GeoJSON cursor in file-scope globals shared by every instance. They are now members. ([#178]) +- `clear[]` no longer crashes after a simulation whose fire front stopped + everywhere, for instance an ignition in non-burnable fuel. ([#194]) ### Changed @@ -189,3 +191,4 @@ repository stays easy to return to. [#178]: https://github.com/forefireAPI/forefire/pull/178 [#180]: https://github.com/forefireAPI/forefire/pull/180 [#183]: https://github.com/forefireAPI/forefire/pull/183 +[#194]: https://github.com/forefireAPI/forefire/pull/194 diff --git a/src/TimeTable.cpp b/src/TimeTable.cpp index a7a83e9..2c32a61 100644 --- a/src/TimeTable.cpp +++ b/src/TimeTable.cpp @@ -71,6 +71,7 @@ FFEvent* TimeTable::getUpcomingEvent(){ decrement(); } else if ( size() == 1 ) { // this is the only event left + head = nullptr; decrement(); } else { // no events left to be treated (size=0) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 219e111..2d85a7b 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -12,7 +12,8 @@ add_executable(forefire_unit_tests test_flux_models.cpp test_http_server.cpp test_model_registry.cpp - test_propagation_models.cpp) + test_propagation_models.cpp + test_timetable.cpp) target_link_libraries(forefire_unit_tests PRIVATE forefireL) target_include_directories(forefire_unit_tests PRIVATE @@ -27,7 +28,7 @@ endif() # doctest groups its cases into suites; registering one CTest entry per suite # keeps `ctest` output useful without needing doctest's CMake integration. -foreach(_suite "model registry" "propagation models" "flux models" "http server") +foreach(_suite "model registry" "propagation models" "flux models" "http server" "timetable") string(REPLACE " " "_" _suite_id "${_suite}") add_test(NAME "unit.${_suite_id}" COMMAND forefire_unit_tests --test-suite=${_suite} --no-skipped-summary) diff --git a/tests/unit/test_timetable.cpp b/tests/unit/test_timetable.cpp new file mode 100644 index 0000000..f99fd47 --- /dev/null +++ b/tests/unit/test_timetable.cpp @@ -0,0 +1,52 @@ +/** + * @file test_timetable.cpp + * @brief The event queue once its last event has been taken out. + * @copyright Copyright (C) 2025 ForeFire, Fire Team, SPE, CNRS/Universita di Corsica. + * @license This program is free software; See LICENSE file for details. (See LICENSE file). + */ + +#include "doctest/doctest.h" + +#include "FFEvent.h" +#include "TimeTable.h" + +#include + +using libforefire::FFEvent; +using libforefire::TimeTable; + +TEST_SUITE("timetable") { + +TEST_CASE("clear() after the last event was dropped on reinsertion") { + TimeTable table; + table.insert(new FFEvent(nullptr, 1.0, "none")); + + // A node that stopped gets an infinite time, and insert() deletes its event. + FFEvent* last = table.getUpcomingEvent(); + last->setNewTime(std::numeric_limits::infinity()); + table.insert(last); + + table.clear(); + + FFEvent* next = new FFEvent(nullptr, 2.0, "none"); + table.insert(next); + CHECK(table.getUpcomingEvent() == next); + delete next; +} + +TEST_CASE("dropping an atom's events spares the event being processed") { + TimeTable table; + FFEvent* event = new FFEvent(nullptr, 1.0, "none"); + table.insert(event); + + FFEvent* upcoming = table.getUpcomingEvent(); + REQUIRE(upcoming == event); + table.dropAtomEvents(nullptr); + + upcoming->setNewTime(2.0); + table.insert(upcoming); + CHECK(table.getUpcomingEvent() == event); + delete event; +} + +}