Skip to content
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
14 changes: 9 additions & 5 deletions tests/fm_tone_presentation_contract_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,17 @@ def main() -> int:
("m_fmToneValueCmb", "m_fmToneRxValueCmb"),
)
rx_source = rx_path.read_text(encoding="utf-8")
radio_model_source = (source_root / "src/models/RadioModel.cpp").read_text(
slice_model_source = (source_root / "src/models/SliceModel.cpp").read_text(
encoding="utf-8"
)
if "&SliceModel::fmDtcsCommandIssued" not in radio_model_source:
fail("RadioModel no longer forwards normalized DTCS operator intent")
if "m_backend->setSliceFmDtcs(" not in radio_model_source:
fail("RadioModel no longer sends DTCS intent through IRadioBackend")
if "controlRequested({field, DtcsSetting{" not in slice_model_source:
fail("SliceModel no longer publishes normalized DTCS operator intent")
icom_source = (source_root / "src/core/backends/icom/IcomCivBackend.cpp").read_text(
encoding="utf-8"
)
dtcs_dispatch = re.compile(r"case Field::FmDtcs: \{(?:(?!case Field::).)*setSliceFmDtcs\(", re.S)
if not dtcs_dispatch.search(icom_source):
fail("the Icom backend no longer applies the typed DTCS request")
mode_visibility_edge = re.compile(
r"m_fmContainer->setVisible\(isFM\);\s*"
r"(?:\s*//[^\n]*\n)*\s*configureFmToneControls\(\);"
Expand Down
19 changes: 19 additions & 0 deletions tests/hl2_tci_signaling_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,18 @@ static void testSeamBackendCannotWedgeOnVfoB()
"a refused VFO-B tune leaves no route transition in flight");
}

namespace hl2 {
// Reuses an existing friend rather than adding a production test hook.
struct Hl2TxGateTestAccess {
// Link state only; no Metis client is started. Typed model controls
// dispatch only while the radio is up.
static void setConnected(Hl2Backend& backend, bool connected)
{
backend.m_connected = connected;
}
};
} // namespace hl2

// promoteTxSliceAndContinue() has the same shape of trap: `slice set N tx=1` is
// Flex text with no seam counterpart, and every caller opens a route transition
// around it. A continuation that never runs strands that transition, so the
Expand All @@ -666,6 +678,12 @@ static void testSeamBackendPromoteAlwaysAnswers()
check(!model.slice(0)->isTxSlice(),
"fixture precondition: the FIXTURE seeded tx=0 on this slice");

auto* hl2 = dynamic_cast<hl2::Hl2Backend*>(model.backend());
check(hl2 != nullptr, "fixture precondition: the HL2 backend is live");
if (!hl2) { return; }
// A TCI client promotes a slice on a connected radio.
hl2::Hl2TxGateTestAccess::setConnected(*hl2, true);

TciServer server(&model);
bool answered = false, selected = false;
Hl2TciSignalingTest::promote(server, 0, &answered, &selected);
Expand All @@ -691,6 +709,7 @@ static void testSeamBackendPromoteAlwaysAnswers()
Hl2TciSignalingTest::promote(server, 0, &answered, &selected);
check(answered && selected,
"an already-TX slice is selected with no command at all");
hl2::Hl2TxGateTestAccess::setConnected(*hl2, false);
}
#endif

Expand Down
30 changes: 28 additions & 2 deletions tests/icom_family_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "models/TransmitModel.h"
#include "core/RadioDiscovery.h"
#include "core/backends/icom/IcomCivBackend.h"
#include "core/backends/icom/IcomSession.h"
#include "core/backends/icom/IcomControls.h"
#include "core/backends/icom/IcomModels.h"
#include "core/backends/icom/CivCodec.h"
Expand All @@ -25,6 +26,7 @@

#include <cstdint>
#include <cstdio>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
Expand All @@ -45,6 +47,19 @@ struct IcomCivBackendTestAccess {
return backend.confirmationFor(frame);
}

// Typed model controls dispatch only while the radio is up. An unstarted
// IcomSession constructs no streams or sockets, so the frame is scheduled
// and then dropped by the unconnected transport.
static void setConnected(IcomCivBackend& backend, bool connected)
{
backend.m_connected = connected;
if (connected) {
backend.m_session = std::make_unique<IcomSession>();
} else {
backend.m_session.reset();
}
}

static void injectConnectedFrame(IcomCivBackend& backend, const CivFrame& frame)
{
backend.m_connected = true;
Expand Down Expand Up @@ -999,11 +1014,22 @@ int main(int argc, char** argv)
// phoneLevelFollowsNetworkInput, this would fail on the
// m_networkModLevelPercent < 0 refusal — a reason that has nothing
// to do with the gate this section pins (PR #5643 review).
//
// Operator TX intent is dispatched only to a connected radio with an
// open TX session. The connect attempt above never completes, so
// reopen admission the socket-free way and mark the link up.
}
check(model.rebuildBackendForTest(QStringLiteral("icom")),
"a socket-free Icom backend replaces the unconnected one");
auto* live = dynamic_cast<icom::IcomCivBackend*>(model.backend());
if (live) {
icom::IcomCivBackendTestAccess::setConnected(*live, true);
model.transmitModel().setMicLevel(70);
check(icom::IcomCivBackendTestAccess::micGainReported(*rebuilt)
&& icom::IcomCivBackendTestAccess::micGainPercent(*rebuilt)
check(icom::IcomCivBackendTestAccess::micGainReported(*live)
&& icom::IcomCivBackendTestAccess::micGainPercent(*live)
== 70,
"the operator moving the slider still reaches the Icom mic gain");
icom::IcomCivBackendTestAccess::setConnected(*live, false);
}
}

Expand Down
Loading