Files
zoneminder/tests/zm_monitorlink_score.cpp
Isaac ConnorandClaude Opus 5.5 514254b967 fix: report a linked monitor's alarm only from its alarm state
MonitorLink::score() reported an alarm whenever the linked monitor's
last_event_id changed (4356a6dea), to catch an alarm that went from ALARM
through ALERT to IDLE between two checks. A new event is not an alarm,
though. A monitor recording continuously starts a new event at every section
boundary, so each one gave its linked monitors a "Linked" alarm, and an
on-demand one a camera reconnect, with no motion anywhere. A monitor with
Recording=None never opens events, so for it the latch did nothing.

Report alarm status only: the frame score in ALARM, and 1 in ALERT so a linked
event stays open through the source's post-event frames (8fc1992e4). The state
stays in shared memory until the source analyses its next frame, and the
receiving monitor checks the link on every packet (every 100 ms while an
on-demand one is paused), so an alarm is still seen.

tests/zm_monitorlink_score.cpp points a MonitorLink at a local SharedData and
checks IDLE, ALARM and ALERT, and that a new event while idle scores 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 08:51:35 -04:00

84 lines
2.6 KiB
C++

/*
* This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "zm_catch2.h"
#include "zm_config.h"
#include "zm_monitor.h"
#include <memory>
namespace {
void EnsureConfig() {
if (!config.font_file_location) config.font_file_location = "";
if (!config.event_close_mode) config.event_close_mode = "idle";
}
// The linked (source) monitor. Its shared data is a local struct here.
class SourceMonitor : public Monitor {
public:
using Data = SharedData;
};
class TestLink : public Monitor::MonitorLink {
public:
explicit TestLink(const std::shared_ptr<Monitor> &source) : MonitorLink(source, 0) {}
void Attach(SourceMonitor::Data *data) {
shared_data = data;
connected = true;
}
};
} // namespace
// A link reports the source monitor's alarm status: its frame score while it
// is in ALARM, and 1 in ALERT so that a linked event stays open through the
// source's post-event frames. A new event on the source is not an alarm: one
// recording continuously starts a new event at every section boundary.
TEST_CASE("MonitorLink score follows the source's alarm state", "[MonitorLink]") {
EnsureConfig();
SourceMonitor::Data data = {};
data.valid = 1;
data.state = Monitor::IDLE;
TestLink link(std::make_shared<SourceMonitor>());
link.Attach(&data);
SECTION("idle") {
REQUIRE(link.score() == 0);
REQUIRE_FALSE(link.hasAlarmed());
}
SECTION("alarm: the source's frame score") {
data.state = Monitor::ALARM;
data.last_frame_score = 42;
REQUIRE(link.score() == 42);
REQUIRE(link.hasAlarmed());
}
SECTION("alert: keeps the linked event open") {
data.state = Monitor::ALERT;
data.last_frame_score = 0;
REQUIRE(link.score() == 1);
}
SECTION("a new event while idle is not an alarm") {
data.last_event_id = 1234;
REQUIRE(link.score() == 0);
data.last_frame_score = 17; // left over from the source's last alarm
data.last_event_id = 1235;
REQUIRE(link.score() == 0);
}
}