GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 111 / 0 / 111
Functions: 100.0% 16 / 0 / 16
Branches: 36.0% 62 / 0 / 172
Decisions: -% 0 / - / 0

unit_tests/tests/controllers/algo/rotational_idle/test_rotational_idle.cpp
Line Branch Decision Exec Source
1 /*
2 * @file test_rotational_idle.cpp
3 *
4 * @date: ene 18, 2026
5 * @author FDSoftware
6 */
7
8 #include "pch.h"
9 #include "rotational_idle.h"
10
11 class RotationalIdleTest : public ::testing::Test
12 {
13 protected:
14 5 void SetUp() override
15 {
16
1/1
✓ Branch 2 taken 5 times.
5 EngineTestHelper eth(engine_type_e::TEST_ENGINE);
17
18 // Reset configuration
19 5 engineConfiguration->rotationalIdleController.enabled = false;
20 5 engineConfiguration->rotationalIdleController.auto_engage = false;
21 5 engineConfiguration->rotationalIdleController.auto_engage_clt_enable = false;
22 5 engineConfiguration->rotationalIdleController.max_tps = 5;
23 5 engineConfiguration->rotationalIdleController.auto_engage_clt = 80;
24 10 }
25 };
26
27 4 TEST_F(RotationalIdleTest, disabledFeature)
28 {
29
1/1
✓ Branch 2 taken 1 time.
1 EngineTestHelper eth(engine_type_e::TEST_ENGINE);
30
1/1
✓ Branch 2 taken 1 time.
1 RotationalIdle rotIdle;
31
32 // Feature disabled
33 1 engineConfiguration->rotationalIdleController.enabled = false;
34
35
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldEngageRotationalIdle());
36 2 }
37
38 4 TEST_F(RotationalIdleTest, autoEngageByClt)
39 {
40
1/1
✓ Branch 2 taken 1 time.
1 EngineTestHelper eth(engine_type_e::TEST_ENGINE);
41
1/1
✓ Branch 2 taken 1 time.
1 RotationalIdle rotIdle;
42
43 1 engineConfiguration->rotationalIdleController.enabled = true;
44 1 engineConfiguration->rotationalIdleController.auto_engage_clt_enable = true;
45 1 engineConfiguration->rotationalIdleController.auto_engage_clt = 80;
46
47 // CLT below threshold - should not engage
48
1/1
✓ Branch 1 taken 1 time.
1 Sensor::setMockValue(SensorType::Clt, 70);
49
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldEngageRotationalIdle());
50
51 // CLT above threshold - should engage
52
1/1
✓ Branch 1 taken 1 time.
1 Sensor::setMockValue(SensorType::Clt, 85);
53
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_TRUE(rotIdle.shouldEngageRotationalIdle());
54 2 }
55
56 4 TEST_F(RotationalIdleTest, autoEngageByTps)
57 {
58
1/1
✓ Branch 2 taken 1 time.
1 EngineTestHelper eth(engine_type_e::TEST_ENGINE);
59
1/1
✓ Branch 2 taken 1 time.
1 RotationalIdle rotIdle;
60
61 1 engineConfiguration->rotationalIdleController.enabled = true;
62 1 engineConfiguration->rotationalIdleController.auto_engage = true;
63 1 engineConfiguration->rotationalIdleController.max_tps = 5;
64
65 // TPS above threshold - should not engage
66
1/1
✓ Branch 1 taken 1 time.
1 Sensor::setMockValue(SensorType::DriverThrottleIntent, 10);
67
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldEngageRotationalIdle());
68
69 // TPS below threshold - should engage
70
1/1
✓ Branch 1 taken 1 time.
1 Sensor::setMockValue(SensorType::DriverThrottleIntent, 3);
71
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_TRUE(rotIdle.shouldEngageRotationalIdle());
72
73 // TPS at threshold - should engage
74
1/1
✓ Branch 1 taken 1 time.
1 Sensor::setMockValue(SensorType::DriverThrottleIntent, 5);
75
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_TRUE(rotIdle.shouldEngageRotationalIdle());
76 2 }
77
78 4 TEST_F(RotationalIdleTest, shouldSkipSpark)
79 {
80
1/1
✓ Branch 2 taken 1 time.
1 EngineTestHelper eth(engine_type_e::TEST_ENGINE);
81
1/1
✓ Branch 2 taken 1 time.
1 RotationalIdle rotIdle;
82
83 // Should not skip when feature is disabled
84 1 engineConfiguration->rotationalIdleController.enabled = false;
85
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldSkipSparkRotationalIdle());
86
87 // Enable rotational idle
88 1 engineConfiguration->rotationalIdleController.enabled = true;
89 1 engineConfiguration->rotationalIdleController.auto_engage = true;
90 1 engineConfiguration->rotationalIdleController.max_tps = 5;
91
1/1
✓ Branch 1 taken 1 time.
1 Sensor::setMockValue(SensorType::DriverThrottleIntent, 3);
92
93 // Should not skip when acc_max is 0 (disabled)
94 1 engineConfiguration->rotationalIdleController.cut_mode = RotationalCutMode::Spark;
95
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldSkipSparkRotationalIdle());
96
97 // Test cut_mode = 1 (Fuel only) - should never skip spark
98 1 engineConfiguration->rotationalIdleController.accumulators[0].acc_max = 2;
99 1 engineConfiguration->rotationalIdleController.accumulators[0].acc_adder = 1;
100 1 engineConfiguration->rotationalIdleController.cut_mode = RotationalCutMode::Fuel;
101 1 engine->engineState.globalSparkCounter = 1;
102
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldSkipSparkRotationalIdle()); // Fuel mode never skips spark
103
104 // Test cut_mode = 0 (Spark mode) - should skip spark based on pattern
105 1 engineConfiguration->rotationalIdleController.cut_mode = RotationalCutMode::Spark;
106
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_TRUE(rotIdle.shouldSkipSparkRotationalIdle()); // counter=1: (1 % 2) + 1 = 2 >= 2
107
108 // Test cut_mode = 2 (Both mode) - should skip spark based on pattern
109 1 engineConfiguration->rotationalIdleController.cut_mode = RotationalCutMode::Both;
110
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_TRUE(rotIdle.shouldSkipSparkRotationalIdle()); // counter=1: (1 % 2) + 1 = 2 >= 2
111
112 // Configure skip pattern: test with various counter values (cut_mode = 0)
113 1 engineConfiguration->rotationalIdleController.cut_mode = RotationalCutMode::Spark;
114 1 engineConfiguration->rotationalIdleController.accumulators[0].acc_max = 2;
115 1 engineConfiguration->rotationalIdleController.accumulators[0].acc_adder = 0;
116 1 engineConfiguration->rotationalIdleController.accumulators[0].acc_offset = 0;
117
118 // Set global spark counter to test pattern
119 1 engine->engineState.globalSparkCounter = 0;
120
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldSkipSparkRotationalIdle()); // counter=0: (0 % 2) + 0 = 0 < 2
121
122 1 engine->engineState.globalSparkCounter = 1;
123
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldSkipSparkRotationalIdle()); // counter=1: (1 % 2) + 0 = 1 < 2
124
125 1 engine->engineState.globalSparkCounter = 2;
126
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldSkipSparkRotationalIdle()); // counter=2: (2 % 2) + 0 = 0 < 2
127
128 // Test with adder to create skip pattern
129 1 engineConfiguration->rotationalIdleController.accumulators[0].acc_adder = 1;
130
131 1 engine->engineState.globalSparkCounter = 0;
132
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldSkipSparkRotationalIdle()); // counter=0: (0 % 2) + 1 = 1 < 2
133
134 1 engine->engineState.globalSparkCounter = 1;
135
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_TRUE(rotIdle.shouldSkipSparkRotationalIdle()); // counter=1: (1 % 2) + 1 = 2 >= 2
136
137 1 engine->engineState.globalSparkCounter = 2;
138
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldSkipSparkRotationalIdle()); // counter=2: (2 % 2) + 1 = 1 < 2
139
140 2 }
141
142 4 TEST_F(RotationalIdleTest, multipleAccumulators)
143 {
144
1/1
✓ Branch 2 taken 1 time.
1 EngineTestHelper eth(engine_type_e::TEST_ENGINE);
145
1/1
✓ Branch 2 taken 1 time.
1 RotationalIdle rotIdle;
146
147 1 engineConfiguration->rotationalIdleController.enabled = true;
148 1 engineConfiguration->rotationalIdleController.auto_engage = true;
149 1 engineConfiguration->rotationalIdleController.max_tps = 5;
150
1/1
✓ Branch 1 taken 1 time.
1 Sensor::setMockValue(SensorType::DriverThrottleIntent, 3);
151 1 engineConfiguration->rotationalIdleController.cut_mode = RotationalCutMode::Spark;
152
153 // Accumulator 0: skip every 2nd (1, 3, 5...)
154 1 engineConfiguration->rotationalIdleController.accumulators[0].acc_max = 2;
155 1 engineConfiguration->rotationalIdleController.accumulators[0].acc_adder = 1;
156 1 engineConfiguration->rotationalIdleController.accumulators[0].acc_offset = 0;
157
158 // Accumulator 1: skip every 3rd (2, 5, 8...)
159 1 engineConfiguration->rotationalIdleController.accumulators[1].acc_max = 3;
160 1 engineConfiguration->rotationalIdleController.accumulators[1].acc_adder = 1;
161 1 engineConfiguration->rotationalIdleController.accumulators[1].acc_offset = 0;
162
163 // globalSparkCounter = 0
164 // Acc 0: (0%2)+1 = 1 < 2 (Fire)
165 // Acc 1: (0%3)+1 = 1 < 3 (Fire)
166 1 engine->engineState.globalSparkCounter = 0;
167
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldSkipSparkRotationalIdle());
168
169 // globalSparkCounter = 1
170 // Acc 0: (1%2)+1 = 2 >= 2 (Skip)
171 // Acc 1: (1%3)+1 = 2 < 3 (Fire)
172 // Result: Skip
173 1 engine->engineState.globalSparkCounter = 1;
174
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_TRUE(rotIdle.shouldSkipSparkRotationalIdle());
175
176 // globalSparkCounter = 2
177 // Acc 0: (2%2)+1 = 1 < 2 (Fire)
178 // Acc 1: (2%3)+1 = 3 >= 3 (Skip)
179 // Result: Skip
180 1 engine->engineState.globalSparkCounter = 2;
181
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_TRUE(rotIdle.shouldSkipSparkRotationalIdle());
182
183 // globalSparkCounter = 3
184 // Acc 0: (3%2)+1 = 2 >= 2 (Skip)
185 // Acc 1: (3%3)+1 = 1 < 3 (Fire)
186 // Result: Skip
187 1 engine->engineState.globalSparkCounter = 3;
188
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_TRUE(rotIdle.shouldSkipSparkRotationalIdle());
189
190 // globalSparkCounter = 4
191 // Acc 0: (4%2)+1 = 1 < 2 (Fire)
192 // Acc 1: (4%3)+1 = 2 < 3 (Fire)
193 // Result: Fire
194 1 engine->engineState.globalSparkCounter = 4;
195
2/7
✓ Branch 3 taken 1 time.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 time.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
1 EXPECT_FALSE(rotIdle.shouldSkipSparkRotationalIdle());
196 2 }
197