/** * @file TriggerEngineGTest.cpp * @brief Unit tests for the StreamHub hub-side trigger FSM. * * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and * the Development of Fusion Energy ('Fusion for Energy'). * Licensed under the EUPL, Version 1.1 or - as soon they will be approved * by the European Commission - subsequent versions of the EUPL (the "Licence") * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl * * @warning Unless required by applicable law or agreed to in writing, * software distributed under the Licence is distributed on an "AS IS" * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence permissions and limitations under the Licence. */ #include "TriggerEngine.h" #include using namespace StreamHub; using MARTe::float64; static TriggerConfig MakeConfig(TrigEdge edge, float64 threshold, float64 windowSec, float64 prePercent, TrigAcqMode mode = kTrigNormal) { TriggerConfig cfg; cfg.signalKey = "src:sig"; cfg.edge = edge; cfg.threshold = threshold; cfg.windowSec = windowSec; cfg.prePercent = prePercent; cfg.mode = mode; return cfg; } TEST(TriggerEngineGTest, TestInitialState) { TriggerEngine eng; EXPECT_EQ(kTrigIdle, eng.GetState()); EXPECT_FALSE(eng.GetStopped()); float64 tt, pre, post; EXPECT_FALSE(eng.GetFiredWindow(tt, pre, post)); } TEST(TriggerEngineGTest, TestIdleIgnoresSamples) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeRising, 0.5, 1.0, 20.0)); eng.CheckSample(1.0, 0.0); eng.CheckSample(2.0, 1.0); /* would fire if armed */ EXPECT_EQ(kTrigIdle, eng.GetState()); } TEST(TriggerEngineGTest, TestRisingEdgeFires) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeRising, 0.5, 1.0, 20.0)); eng.Arm(); EXPECT_EQ(kTrigArmed, eng.GetState()); eng.CheckSample(1.0, 0.0); /* primes prevValue */ eng.CheckSample(2.0, 1.0); /* 0.0 < 0.5 <= 1.0 → fire */ EXPECT_EQ(kTrigCollecting, eng.GetState()); float64 tt, pre, post; ASSERT_TRUE(eng.GetFiredWindow(tt, pre, post)); EXPECT_DOUBLE_EQ(2.0, tt); EXPECT_DOUBLE_EQ(0.2, pre); /* 1.0 s × 20 % */ EXPECT_DOUBLE_EQ(0.8, post); } TEST(TriggerEngineGTest, TestFirstSampleNeverFires) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeRising, 0.5, 1.0, 20.0)); eng.Arm(); /* First sample above threshold must only prime the detector */ eng.CheckSample(1.0, 2.0); EXPECT_EQ(kTrigArmed, eng.GetState()); } TEST(TriggerEngineGTest, TestFallingEdgeFires) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeFalling, 0.5, 1.0, 50.0)); eng.Arm(); eng.CheckSample(1.0, 1.0); eng.CheckSample(2.0, 1.5); /* rising: must not fire */ EXPECT_EQ(kTrigArmed, eng.GetState()); eng.CheckSample(3.0, 0.2); /* 1.5 > 0.5 >= 0.2 → fire */ EXPECT_EQ(kTrigCollecting, eng.GetState()); float64 tt, pre, post; ASSERT_TRUE(eng.GetFiredWindow(tt, pre, post)); EXPECT_DOUBLE_EQ(3.0, tt); EXPECT_DOUBLE_EQ(0.5, pre); EXPECT_DOUBLE_EQ(0.5, post); } TEST(TriggerEngineGTest, TestBothEdgesFire) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeBoth, 0.0, 1.0, 0.0)); eng.Arm(); eng.CheckSample(1.0, -1.0); eng.CheckSample(2.0, 1.0); /* rising through 0 */ EXPECT_EQ(kTrigCollecting, eng.GetState()); eng.Arm(); eng.CheckSample(3.0, 1.0); eng.CheckSample(4.0, -1.0); /* falling through 0 */ EXPECT_EQ(kTrigCollecting, eng.GetState()); } TEST(TriggerEngineGTest, TestNoFireBelowThreshold) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeRising, 5.0, 1.0, 20.0)); eng.Arm(); eng.CheckSample(1.0, 0.0); eng.CheckSample(2.0, 1.0); eng.CheckSample(3.0, 4.9); EXPECT_EQ(kTrigArmed, eng.GetState()); } TEST(TriggerEngineGTest, TestConfigClamping) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeRising, 0.0, 100.0, 150.0)); TriggerConfig cfg = eng.GetConfig(); EXPECT_DOUBLE_EQ(10.0, cfg.windowSec); EXPECT_DOUBLE_EQ(100.0, cfg.prePercent); eng.SetConfig(MakeConfig(kEdgeRising, 0.0, 1.0e-6, -5.0)); cfg = eng.GetConfig(); EXPECT_DOUBLE_EQ(1.0e-4, cfg.windowSec); EXPECT_DOUBLE_EQ(0.0, cfg.prePercent); } TEST(TriggerEngineGTest, TestConfigEpochIncrements) { TriggerEngine eng; MARTe::uint32 e0 = eng.GetConfigEpoch(); eng.SetConfig(MakeConfig(kEdgeRising, 0.0, 1.0, 20.0)); EXPECT_EQ(e0 + 1u, eng.GetConfigEpoch()); eng.SetConfig(MakeConfig(kEdgeFalling, 1.0, 2.0, 30.0)); EXPECT_EQ(e0 + 2u, eng.GetConfigEpoch()); } TEST(TriggerEngineGTest, TestMarkTriggeredOnlyFromCollecting) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeRising, 0.5, 1.0, 20.0)); /* Not collecting: no-op */ eng.MarkTriggered(); EXPECT_EQ(kTrigIdle, eng.GetState()); eng.Arm(); eng.MarkTriggered(); EXPECT_EQ(kTrigArmed, eng.GetState()); /* Collecting → triggered */ eng.CheckSample(1.0, 0.0); eng.CheckSample(2.0, 1.0); ASSERT_EQ(kTrigCollecting, eng.GetState()); eng.MarkTriggered(); EXPECT_EQ(kTrigTriggered, eng.GetState()); } TEST(TriggerEngineGTest, TestDisarmResetsEverything) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeRising, 0.5, 1.0, 20.0)); eng.Arm(); eng.SetStopped(true); eng.CheckSample(1.0, 0.0); eng.CheckSample(2.0, 1.0); ASSERT_EQ(kTrigCollecting, eng.GetState()); eng.Disarm(); EXPECT_EQ(kTrigIdle, eng.GetState()); EXPECT_FALSE(eng.GetStopped()); float64 tt, pre, post; EXPECT_FALSE(eng.GetFiredWindow(tt, pre, post)); } TEST(TriggerEngineGTest, TestRearmResetsEdgeDetection) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeRising, 0.5, 1.0, 20.0)); eng.Arm(); eng.CheckSample(1.0, 0.0); eng.CheckSample(2.0, 1.0); eng.MarkTriggered(); ASSERT_EQ(kTrigTriggered, eng.GetState()); /* Re-arm: first sample only primes even if it crosses the threshold * relative to the value seen before the re-arm. */ eng.Arm(); EXPECT_EQ(kTrigArmed, eng.GetState()); eng.CheckSample(3.0, 0.0); EXPECT_EQ(kTrigArmed, eng.GetState()); eng.CheckSample(4.0, 1.0); EXPECT_EQ(kTrigCollecting, eng.GetState()); float64 tt, pre, post; ASSERT_TRUE(eng.GetFiredWindow(tt, pre, post)); EXPECT_DOUBLE_EQ(4.0, tt); } TEST(TriggerEngineGTest, TestStoppedFlag) { TriggerEngine eng; EXPECT_FALSE(eng.GetStopped()); eng.SetStopped(true); EXPECT_TRUE(eng.GetStopped()); eng.SetStopped(false); EXPECT_FALSE(eng.GetStopped()); } TEST(TriggerEngineGTest, TestWindowLatchedAtFireTime) { TriggerEngine eng; eng.SetConfig(MakeConfig(kEdgeRising, 0.5, 1.0, 20.0)); eng.Arm(); eng.CheckSample(1.0, 0.0); eng.CheckSample(2.0, 1.0); ASSERT_EQ(kTrigCollecting, eng.GetState()); /* Config edits after the fire must not change the latched window */ eng.SetConfig(MakeConfig(kEdgeRising, 0.5, 10.0, 50.0)); float64 tt, pre, post; ASSERT_TRUE(eng.GetFiredWindow(tt, pre, post)); EXPECT_DOUBLE_EQ(0.2, pre); EXPECT_DOUBLE_EQ(0.8, post); }