GCC Code Coverage Report


Directory: ./
File: unit_tests/test-framework/engine_test_helper.cpp
Date: 2025-11-16 14:52:24
Coverage Exec Excl Total
Lines: 95.7% 309 0 323
Functions: 93.6% 44 0 47
Branches: 61.3% 136 0 222
Decisions: 84.5% 49 - 58

Line Branch Decision Exec Source
1 /**
2 * @file engine_test_helper.cpp
3 *
4 * @date Jun 26, 2014
5 * @author Andrey Belomutskiy, (c) 2012-2014
6 */
7
8 #include "pch.h"
9
10 #include "trigger_decoder.h"
11 #include "speed_density.h"
12 #include "fuel_math.h"
13 #include "accel_enrichment.h"
14 #include "advance_map.h"
15 #include "tooth_logger.h"
16 #include "logicdata.h"
17 #include "unit_test_logger.h"
18 #include "hardware.h"
19 // https://stackoverflow.com/questions/23427804/cant-find-mkdir-function-in-dirent-h-for-windows
20 #include <sys/types.h>
21 #include <sys/stat.h>
22
23 bool unitTestBusyWaitHack;
24 bool unitTestTaskPrecisionHack;
25 bool unitTestTaskNoFastCallWhileAdvancingTimeHack;
26
27 #if EFI_ENGINE_SNIFFER
28 #include "engine_sniffer.h"
29 extern WaveChart waveChart;
30 #endif /* EFI_ENGINE_SNIFFER */
31
32 #include "fw_configuration.h"
33
34 extern engine_configuration_s & activeConfiguration;
35 extern PinRepository pinRepository;
36 extern bool printTriggerDebug;
37 extern bool printTriggerTrace;
38 extern bool printFuelDebug;
39 extern int minCrankingRpm;
40
41 684 EngineTestHelperBase::EngineTestHelperBase(Engine * eng, engine_configuration_s * econfig, persistent_config_s * pers) {
42 // todo: make this not a global variable, we need currentTimeProvider interface on engine
43 684 setTimeNowUs(0);
44 684 minCrankingRpm = 0;
45 684 ButtonDebounce::resetForUnitTests();
46 684 unitTestBusyWaitHack = false;
47 684 EnableToothLogger();
48
3/6
✓ Branch 0 taken 684 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 684 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 684 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 684 times.
684 if (engine || engineConfiguration || config) {
49 firmwareError(ObdCode::OBD_PCM_Processor_Fault,
50 "Engine configuration not cleaned up by previous test");
51 }
52 684 engine = eng;
53 684 engineConfiguration = econfig;
54 684 config = pers;
55
56 684 setup_custom_fw_overrides();
57 684 }
58
59 684 EngineTestHelperBase::~EngineTestHelperBase() {
60 684 engine = nullptr;
61 684 engineConfiguration = nullptr;
62 684 config = nullptr;
63 684 }
64
65 565 EngineTestHelper::EngineTestHelper(engine_type_e engineType)
66 565 : EngineTestHelper(engineType, &emptyCallbackWithConfiguration) {
67 565 }
68
69 574 EngineTestHelper::EngineTestHelper(engine_type_e engineType, configuration_callback_t configurationCallback)
70
1/1
✓ Branch 3 taken 574 times.
574 : EngineTestHelper(engineType, configurationCallback, {}) {
71 574 }
72
73 16 EngineTestHelper::EngineTestHelper(engine_type_e engineType, const std::unordered_map<SensorType, float>& sensorValues)
74 16 : EngineTestHelper(engineType, &emptyCallbackWithConfiguration, sensorValues) {
75 16 }
76
77 41 warningBuffer_t *EngineTestHelper::recentWarnings() {
78 41 return getRecentWarnings();
79 }
80
81 int EngineTestHelper::getWarningCounter() {
82 return engine.engineState.warnings.warningCounter;
83 }
84
85 FILE *jsonTrace = nullptr;
86
87 590 EngineTestHelper::EngineTestHelper(engine_type_e engineType, configuration_callback_t configurationCallback, const std::unordered_map<SensorType, float>& sensorValues) :
88
1/1
✓ Branch 2 taken 590 times.
590 EngineTestHelperBase(&engine, &persistentConfig.engineConfiguration, &persistentConfig)
89 {
90 590 persistentConfig = decltype(persistentConfig){};
91
1/1
✓ Branch 2 taken 590 times.
590 pinRepository = decltype(pinRepository){};
92
93
2/2
✓ Branch 1 taken 590 times.
✓ Branch 4 taken 590 times.
590 auto testInfo = ::testing::UnitTest::GetInstance()->current_test_info();
94 extern bool hasInitGtest;
95
1/2
✓ Branch 0 taken 590 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 590 times.
✗ Decision 'false' not taken.
590 if (hasInitGtest) {
96 #if IS_WINDOWS_COMPILER
97 mkdir(TEST_RESULTS_DIR);
98 #else
99 590 mkdir(TEST_RESULTS_DIR, 0777);
100 #endif
101
1/1
✓ Branch 1 taken 590 times.
590 createUnitTestLog();
102
103
1/1
✓ Branch 2 taken 590 times.
590 std::stringstream filePath;
104
6/6
✓ Branch 1 taken 590 times.
✓ Branch 4 taken 590 times.
✓ Branch 8 taken 590 times.
✓ Branch 11 taken 590 times.
✓ Branch 15 taken 590 times.
✓ Branch 18 taken 590 times.
590 filePath << TEST_RESULTS_DIR << "/unittest_" << testInfo->test_case_name() << "_" << testInfo->name() << "_trace.json";
105 // fun fact: ASAN says not to extract 'fileName' into a variable, we must be doing something a bit not right?
106
2/2
✓ Branch 2 taken 590 times.
✓ Branch 6 taken 590 times.
590 jsonTrace = fopen(filePath.str().c_str(), "wb");
107
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 564 times.
2/2
✓ Decision 'true' taken 26 times.
✓ Decision 'false' taken 564 times.
590 if (jsonTrace == nullptr) {
108 // criticalError("Error creating file [%s]", filePath.str().c_str());
109 // TOOD handle config tests
110
2/2
✓ Branch 2 taken 26 times.
✓ Branch 6 taken 26 times.
26 printf("Error creating file [%s]\n", filePath.str().c_str());
111 } else {
112
1/1
✓ Branch 1 taken 564 times.
564 fprintf(jsonTrace, "{\"traceEvents\": [\n");
113
1/1
✓ Branch 1 taken 564 times.
564 fprintf(jsonTrace, "{\"name\":\"process_name\",\"ph\":\"M\",\"pid\":-16,\"tid\":0,\"args\":{\"name\":\"Main\"}}\n");
114 }
115 590 } else {
116 // todo: document why this branch even exists
117 jsonTrace = nullptr;
118 }
119
120
1/1
✓ Branch 1 taken 590 times.
590 Sensor::setMockValue(SensorType::Clt, 70);
121
1/1
✓ Branch 1 taken 590 times.
590 Sensor::setMockValue(SensorType::Iat, 30);
122
123
2/2
✓ Branch 9 taken 16 times.
✓ Branch 10 taken 590 times.
2/2
✓ Decision 'true' taken 16 times.
✓ Decision 'false' taken 590 times.
606 for (const auto& [s, v] : sensorValues) {
124
1/1
✓ Branch 1 taken 16 times.
16 Sensor::setMockValue(s, v);
125 }
126
127 590 activeConfiguration = engine_configuration_s{};
128
129
1/1
✓ Branch 1 taken 590 times.
590 enginePins.reset();
130
1/1
✓ Branch 1 taken 590 times.
590 enginePins.unregisterPins();
131
132
1/1
✓ Branch 1 taken 590 times.
590 waveChart.init();
133
134
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, -40, 1.5);
135
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, -30, 1.5);
136
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, -20, 1.42);
137
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, -10, 1.36);
138
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, 0, 1.28);
139
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, 10, 1.19);
140
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, 20, 1.12);
141
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, 30, 1.10);
142
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, 40, 1.06);
143
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, 50, 1.06);
144
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, 60, 1.03);
145
1/1
✓ Branch 1 taken 590 times.
590 setCurveValue(config->cltFuelCorrBins, config->cltFuelCorr, CLT_CURVE_SIZE, 70, 1.01);
146
147
1/1
✓ Branch 1 taken 590 times.
590 initDataStructures();
148
149
1/1
✓ Branch 1 taken 590 times.
590 resetConfigurationExt(configurationCallback, engineType);
150
151
1/1
✓ Branch 1 taken 590 times.
590 validateConfigOnStartUpOrBurn();
152
153
1/1
✓ Branch 1 taken 590 times.
590 enginePins.startPins();
154
155
1/1
✓ Branch 1 taken 590 times.
590 commonInitEngineController();
156
157 // this is needed to have valid CLT and IAT.
158 //todo: reuse initPeriodicEvents() method
159
1/1
✓ Branch 1 taken 590 times.
590 engine.periodicSlowCallback();
160
161 extern bool hasInitGtest;
162
1/2
✓ Branch 0 taken 590 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 590 times.
✗ Decision 'false' not taken.
590 if (hasInitGtest) {
163 // When built in unit tests mode UNSUPPORTED_ENUM_VALUE leads to acquiring of mockAirmassModel
164 590 engineConfiguration->fuelAlgorithm = engine_load_mode_e::UNSUPPORTED_ENUM_VALUE;
165
166
1/1
✓ Branch 2 taken 590 times.
590 mockAirmass = std::make_unique<::testing::NiceMock<MockAirmass>>();
167 590 engine.mockAirmassModel = mockAirmass.get();
168 }
169
170 590 memset(mockPinStates, 0, sizeof(mockPinStates));
171
172
1/1
✓ Branch 1 taken 590 times.
590 setVerboseTrigger(false);
173
174
1/1
✓ Branch 1 taken 590 times.
590 initHardware();
175
1/1
✓ Branch 1 taken 590 times.
590 rememberCurrentConfiguration();
176 590 }
177
178 112 static void writeEventsToFile(const char *fileName,
179 const std::vector<CompositeEvent> &events) {
180 112 FILE *ptr = fopen(fileName, "wb");
181 112 size_t count = events.size();
182
183 // todo: move magic keywords to something.txt and reuse magic constants from C and java, once we have java converter
184 112 fprintf(ptr, "count,%d\n", count);
185
186 #define numChannels 6 // todo: clean-up
187
188
2/2
✓ Branch 0 taken 83042 times.
✓ Branch 1 taken 112 times.
2/2
✓ Decision 'true' taken 83042 times.
✓ Decision 'false' taken 112 times.
83154 for (size_t i = 0; i < count; i++) {
189 83042 const CompositeEvent *event = &events[i];
190
191 83042 uint32_t ts = event->timestamp;
192 83042 fprintf(ptr, "timestamp,%d\n", ts);
193
194
2/2
✓ Branch 0 taken 498252 times.
✓ Branch 1 taken 83042 times.
2/2
✓ Decision 'true' taken 498252 times.
✓ Decision 'false' taken 83042 times.
581294 for (int ch = 0; ch < numChannels; ch++) {
195 498252 int chState = getChannelState(ch, event);
196 498252 fprintf(ptr, "state,%d,%d\n", ch, chState);
197
198 }
199
200 }
201
202
203 112 fclose(ptr);
204 112 }
205
206 590 EngineTestHelper::~EngineTestHelper() {
207 // Write history to file
208 extern bool hasInitGtest;
209 590 auto testInfo = ::testing::UnitTest::GetInstance()->current_test_info();
210
1/2
✓ Branch 0 taken 590 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 590 times.
✗ Decision 'false' not taken.
590 if (hasInitGtest) {
211 590 std::stringstream filePath;
212 590 filePath << TEST_RESULTS_DIR << "/unittest_" << testInfo->test_case_name() << "_" << testInfo->name() << ".logicdata";
213 590 writeEventsLogicData(filePath.str().c_str());
214 590 }
215
1/2
✓ Branch 0 taken 590 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 590 times.
✗ Decision 'false' not taken.
590 if (hasInitGtest) {
216 590 std::stringstream filePath;
217 590 filePath << TEST_RESULTS_DIR << "/unittest_" << testInfo->test_case_name() << "_" << testInfo->name() << ".events.txt";
218 590 writeEvents2(filePath.str().c_str());
219 590 }
220
221
2/2
✓ Branch 0 taken 564 times.
✓ Branch 1 taken 26 times.
2/2
✓ Decision 'true' taken 564 times.
✓ Decision 'false' taken 26 times.
590 if (jsonTrace != nullptr) {
222 564 fprintf(jsonTrace, "]}\n");
223 564 fclose(jsonTrace);
224 564 jsonTrace = nullptr;
225 }
226 590 closeUnitTestLog();
227
228 // Cleanup
229 // reset pin config state, will trigger isPinConfigurationChanged
230 590 enginePins.resetForUnitTest();
231 590 enginePins.reset();
232 590 enginePins.unregisterPins();
233 590 Sensor::resetRegistry();
234 590 memset(mockPinStates, 0, sizeof(mockPinStates));
235 590 unitTestTaskNoFastCallWhileAdvancingTimeHack = false;
236 590 }
237
238 590 void EngineTestHelper::writeEventsLogicData(const char *fileName) {
239 590 const auto& events = getCompositeEvents();
240
2/2
✓ Branch 1 taken 478 times.
✓ Branch 2 taken 112 times.
2/2
✓ Decision 'true' taken 478 times.
✓ Decision 'false' taken 112 times.
590 if (events.size() < 2) {
241 478 printf("Not enough data for %s\n", fileName);
242 478 return;
243 }
244 112 printf("Writing %d records to %s\n", events.size(), fileName);
245 112 writeLogicDataFile(fileName, events);
246 }
247
248 590 void EngineTestHelper::writeEvents2(const char *fileName) {
249 590 const auto& events = getCompositeEvents();
250
2/2
✓ Branch 1 taken 478 times.
✓ Branch 2 taken 112 times.
2/2
✓ Decision 'true' taken 478 times.
✓ Decision 'false' taken 112 times.
590 if (events.size() < 2) {
251 478 printf("Not enough data for %s\n", fileName);
252 478 return;
253 }
254 112 printf("Writing %d records to %s\n", events.size(), fileName);
255 112 writeEventsToFile(fileName, events);
256 }
257
258 /**
259 * mock a change of time and fire single RISE front event
260 * DEPRECATED many usages should be migrated to
261 */
262 1225 void EngineTestHelper::fireRise(float delayMs) {
263 1225 moveTimeForwardUs(MS2US(delayMs));
264 1225 firePrimaryTriggerRise();
265 1225 }
266
267 1098 void EngineTestHelper::smartFireRise(float delayMs) {
268 1098 moveTimeForwardAndInvokeEventsUs(MS2US(delayMs));
269 1098 firePrimaryTriggerRise();
270 1098 }
271
272 1260 void EngineTestHelper::fireFall(float delayMs) {
273 1260 moveTimeForwardUs(MS2US(delayMs));
274 1260 firePrimaryTriggerFall();
275 1260 }
276
277 1042 void EngineTestHelper::smartFireFall(float delayMs) {
278 1042 moveTimeForwardAndInvokeEventsUs(MS2US(delayMs));
279 1042 firePrimaryTriggerFall();
280 1042 }
281
282 /**
283 * fire single RISE front event
284 */
285 3896 void EngineTestHelper::firePrimaryTriggerRise() {
286 3896 efitick_t nowNt = getTimeNowNt();
287 3896 LogTriggerTooth(SHAFT_PRIMARY_RISING, nowNt);
288 3896 handleShaftSignal(0, true, nowNt);
289 3896 }
290
291 3868 void EngineTestHelper::firePrimaryTriggerFall() {
292 3868 efitick_t nowNt = getTimeNowNt();
293 3868 LogTriggerTooth(SHAFT_PRIMARY_FALLING, nowNt);
294 3868 handleShaftSignal(0, false, nowNt);
295 3868 }
296
297 46 void EngineTestHelper::fireTriggerEventsWithDuration(float durationMs) {
298 46 fireTriggerEvents2(/*count*/1, durationMs);
299 46 }
300
301 /**
302 * Sends specified number of rise/fall trigger events, with specified amount of time between those.
303 *
304 * This is helpful for TT_ONE trigger wheel decoder and probably other decoders as well.
305 */
306 66 void EngineTestHelper::fireTriggerEvents2(int count, float durationMs) {
307
2/2
✓ Branch 0 taken 449 times.
✓ Branch 1 taken 66 times.
2/2
✓ Decision 'true' taken 449 times.
✓ Decision 'false' taken 66 times.
515 for (int i = 0; i < count; i++) {
308 449 fireRise(durationMs);
309 449 fireFall(durationMs);
310 }
311 66 }
312
313 366 void EngineTestHelper::smartFireTriggerEvents2(int count, float durationMs) {
314
2/2
✓ Branch 0 taken 1037 times.
✓ Branch 1 taken 366 times.
2/2
✓ Decision 'true' taken 1037 times.
✓ Decision 'false' taken 366 times.
1403 for (int i = 0; i < count; i++) {
315 1037 smartFireRise(durationMs);
316 1037 smartFireFall(durationMs);
317 }
318 366 }
319
320 4 void EngineTestHelper::clearQueue() {
321 4 engine.scheduler.executeAll(99999999); // this is needed to clear 'isScheduled' flag
322
4/10
✓ Branch 3 taken 4 times.
✓ Branch 7 taken 4 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 4 times.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 24 not taken.
✗ Branch 27 not taken.
✓ Branch 34 taken 4 times.
✗ Branch 35 not taken.
4 ASSERT_EQ( 0, engine.scheduler.size()) << "Failed to clearQueue";
323 }
324
325 122 int EngineTestHelper::executeActions() {
326 122 return engine.scheduler.executeAll(getTimeNowUs());
327 }
328
329 195 void EngineTestHelper::moveTimeForwardMs(float deltaTimeMs) {
330 195 moveTimeForwardUs(MS2US(deltaTimeMs));
331 195 }
332
333 1001 void EngineTestHelper::moveTimeForwardSec(float deltaTimeSec) {
334 1001 moveTimeForwardUs(MS2US(1000 * deltaTimeSec));
335 1001 }
336
337 11279 void EngineTestHelper::moveTimeForwardUs(int deltaTimeUs) {
338
2/4
✓ Branch 0 taken 11279 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11279 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 11279 times.
11279 if (printTriggerDebug || printFuelDebug) {
339 printf("moveTimeForwardUs %.1fms\r\n", deltaTimeUs / 1000.0);
340 }
341 11279 advanceTimeUs(deltaTimeUs);
342 11279 }
343
344 8 void EngineTestHelper::moveTimeForwardAndInvokeEventsSec(int deltaTimeSeconds) {
345 8 moveTimeForwardAndInvokeEventsUs(MS2US(1000 * deltaTimeSeconds));
346 8 }
347
348 /**
349 * this method executes all pending events while moving time forward
350 */
351 6956 void EngineTestHelper::moveTimeForwardAndInvokeEventsUs(int deltaTimeUs) {
352
2/4
✓ Branch 0 taken 6956 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6956 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 6956 times.
6956 if (printTriggerDebug || printFuelDebug) {
353 printf("moveTimeForwardAndInvokeEventsUs %.1fms\r\n", deltaTimeUs / 1000.0);
354 }
355 6956 setTimeAndInvokeEventsUs(getTimeNowUs() + deltaTimeUs);
356 6956 }
357
358 314841 void EngineTestHelper::setTimeNtAndInvokeCallBacks(efitick_t nt)
359 {
360 // we need to call fast callback every FAST_CALLBACK_PERIOD_MS
361 314841 efitick_t step = MS2US(FAST_CALLBACK_PERIOD_MS) * US_TO_NT_MULTIPLIER;
362
363
2/2
✓ Branch 1 taken 360264 times.
✓ Branch 2 taken 44294 times.
2/2
✓ Decision 'true' taken 360264 times.
✓ Decision 'false' taken 44294 times.
404558 while (getTimeNowNt() < nt) {
364 // get next FAST_CALLBACK_PERIOD_MS tick time
365 360264 efitick_t nextStep = (getTimeNowNt() + step) / step * step;
366
367
2/2
✓ Branch 0 taken 270547 times.
✓ Branch 1 taken 89717 times.
2/2
✓ Decision 'true' taken 270547 times.
✓ Decision 'false' taken 89717 times.
360264 if (nextStep > nt) {
368 270547 setTimeNowNt(nt);
369 270547 return;
370 }
371
372 89717 setTimeNowNt(nextStep);
373 89717 engine.periodicFastCallback();
374 }
375 }
376
377 598547 void EngineTestHelper::setTimeAndInvokeEventsUs(int targetTimeUs) {
378 598547 int counter = 0;
379 while (true) {
380
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 619808 times.
619808 criticalAssertVoid(counter++ < 100'000, "EngineTestHelper: failing to setTimeAndInvokeEventsUs");
381 619808 scheduling_s* nextScheduledEvent = engine.scheduler.getHead();
382
2/2
✓ Branch 0 taken 523749 times.
✓ Branch 1 taken 96059 times.
2/2
✓ Decision 'true' taken 523749 times.
✓ Decision 'false' taken 96059 times.
619808 if (nextScheduledEvent == nullptr) {
383 // nothing pending - we are done here
384 523749 break;
385 }
386 96059 efitick_t nextEventNt = nextScheduledEvent->getMomentNt();
387
2/2
✓ Branch 0 taken 74798 times.
✓ Branch 1 taken 21261 times.
2/2
✓ Decision 'true' taken 74798 times.
✓ Decision 'false' taken 21261 times.
96059 if (nextEventNt > US2NT(targetTimeUs)) {
388 // next event is too far in the future
389 74798 break;
390 }
391 // see #8725 for details
392
2/2
✓ Branch 0 taken 6083 times.
✓ Branch 1 taken 15178 times.
2/2
✓ Decision 'true' taken 6083 times.
✓ Decision 'false' taken 15178 times.
21261 if (unitTestTaskNoFastCallWhileAdvancingTimeHack) {
393 6083 setTimeNowNt(nextEventNt);
394 } else {
395 15178 setTimeNtAndInvokeCallBacks(nextEventNt);
396 }
397 21261 engine.scheduler.executeAllNt(getTimeNowNt());
398 21261 }
399
2/2
✓ Branch 0 taken 298884 times.
✓ Branch 1 taken 299663 times.
2/2
✓ Decision 'true' taken 298884 times.
✓ Decision 'false' taken 299663 times.
598547 if (unitTestTaskNoFastCallWhileAdvancingTimeHack) {
400 298884 setTimeNowUs(targetTimeUs);
401 } else {
402 299663 setTimeNtAndInvokeCallBacks(US_TO_NT_MULTIPLIER * targetTimeUs);
403 }
404 }
405
406 4 void EngineTestHelper::fireTriggerEvents(int count) {
407 4 fireTriggerEvents2(count, 5); // 5ms
408 4 }
409
410 47 void EngineTestHelper::assertInjectorUpEvent(const char *msg, int eventIndex, efitimeus_t momentUs, long injectorIndex) {
411 47 InjectionEvent *event = &engine.injectionEvents.elements[injectorIndex];
412 47 auto const expected_action{ action_s::make<turnInjectionPinHigh>(uintptr_t{}) };
413
1/1
✓ Branch 1 taken 47 times.
47 assertEvent(msg, eventIndex, expected_action, momentUs, event);
414 47 }
415
416 53 void EngineTestHelper::assertInjectorDownEvent(const char *msg, int eventIndex, efitimeus_t momentUs, long injectorIndex) {
417 53 InjectionEvent *event = &engine.injectionEvents.elements[injectorIndex];
418 53 auto const expected_action{ action_s::make<turnInjectionPinLow>((InjectionEvent*){}) };
419
1/1
✓ Branch 1 taken 53 times.
53 assertEvent(msg, eventIndex, expected_action, momentUs, event);
420 53 }
421
422 120 scheduling_s * EngineTestHelper::assertEvent5(const char *msg, int index, action_s const& action_expected, efitimeus_t expectedTimestamp) {
423 120 TestExecutor *executor = &engine.scheduler;
424
2/9
✓ Branch 3 taken 120 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 120 times.
✗ Branch 12 not taken.
✗ Branch 15 not taken.
✗ Branch 18 not taken.
✗ Branch 23 not taken.
✗ Branch 27 not taken.
✗ Branch 30 not taken.
120 EXPECT_TRUE(executor->size() > index) << msg << " valid index";
425 120 scheduling_s *event = executor->getForUnitTest(index);
426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 assert(event != nullptr);
427
428 120 auto const& action_scheduled{ event->action };
429
430
2/8
✓ Branch 6 taken 120 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 120 times.
✗ Branch 15 not taken.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
✗ Branch 26 not taken.
✗ Branch 29 not taken.
120 EXPECT_EQ(action_scheduled.getCallback(), action_expected.getCallback()) << msg << " callback up/down";
431 120 efitimeus_t start = getTimeNowUs();
432
2/7
✓ Branch 3 taken 120 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 120 times.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
120 EXPECT_NEAR(expectedTimestamp, event->getMomentUs() - start,/*3us precision to address rounding etc*/3) << msg;
433 120 return event;
434 }
435
436 3 angle_t EngineTestHelper::timeToAngle(float timeMs) {
437 3 return MS2US(timeMs) / engine.rpmCalculator.oneDegreeUs;
438 }
439
440 14 const AngleBasedEvent * EngineTestHelper::assertTriggerEvent(const char *msg,
441 int index, AngleBasedEvent *expected,
442 action_s const& action_expected,
443 angle_t enginePhase) {
444 14 auto event = engine.module<TriggerScheduler>()->getElementAtIndexForUnitTest(index);
445
446
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 1 time.
2/2
✓ Decision 'true' taken 13 times.
✓ Decision 'false' taken 1 time.
14 if (action_expected) {
447 13 auto const& action_scheduled{ event->action };
448
2/7
✓ Branch 6 taken 13 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 13 times.
✗ Branch 15 not taken.
✗ Branch 18 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
13 EXPECT_EQ(action_scheduled.getCallback(), action_expected.getCallback()) << " callback up/down";
449 }
450
451
2/7
✓ Branch 3 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 14 times.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
14 EXPECT_NEAR(enginePhase, event->getAngle(), EPS4D) << " angle";
452 14 return event;
453 }
454
455 scheduling_s * EngineTestHelper::assertScheduling(const char *msg, int index, scheduling_s *expected, action_s const& action, efitimeus_t expectedTimestamp) {
456 scheduling_s * actual = assertEvent5(msg, index, action, expectedTimestamp);
457 return actual;
458 }
459
460 100 void EngineTestHelper::assertEvent(const char *msg, int index, action_s const& action, efitimeus_t momentUs, InjectionEvent *expectedEvent) {
461 100 scheduling_s *event = assertEvent5(msg, index, action, momentUs);
462
463 100 auto const actualEvent{ event->action.getArgument<InjectionEvent*>() };
464
465
3/9
✓ Branch 2 taken 100 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 100 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 20 not taken.
✓ Branch 27 taken 100 times.
✗ Branch 28 not taken.
100 ASSERT_EQ(expectedEvent->outputs[0], actualEvent->outputs[0]) << msg;
466 // but this would not work assertEqualsLM(msg, expectedPair, (long)eventPair);
467 }
468
469 4 bool EngineTestHelper::assertEventExistsAtEnginePhase(const char *msg, action_s const& action_expected, angle_t expectedEventEnginePhase){
470 4 TestExecutor *executor = &engine.scheduler;
471
472 //std::cout << "executor->size(): " << executor->size() << std::endl;
473 //std::cout << "expected_action.getCallback(): 0x" << std::hex << reinterpret_cast<size_t>(action_expected.getCallback()) << "; name: " << action_expected.getCallbackName() << std::endl;
474
475
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
1/2
✓ Decision 'true' taken 4 times.
✗ Decision 'false' not taken.
4 for (int i = 0; i < executor->size(); i++) {
476
1/1
✓ Branch 1 taken 4 times.
4 auto event = executor->getForUnitTest(i);
477
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 assert(event != nullptr);
478
479 4 auto const action_scheduled{ event->action };
480
481 // Uncomment next to see what was stored in executor queue
482 // std::cout << "action_scheduled.getCallback(): 0x" << std::hex << reinterpret_cast<size_t>(action_scheduled.getCallback()) << "; name: " << action_scheduled.getCallbackName() << std::endl;
483
484
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
1/2
✓ Decision 'true' taken 4 times.
✗ Decision 'false' not taken.
4 if(action_scheduled.getCallback() == action_expected.getCallback()) {
485
1/1
✓ Branch 1 taken 4 times.
4 efitimeus_t start = getTimeNowUs();
486 4 efitimeus_t expectedTimestamp = angleToTimeUs(expectedEventEnginePhase);
487 // after #7245 we can increase the resolution of this test for expect 0.5 or less
488
2/6
✓ Branch 4 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 11 not taken.
✗ Branch 15 not taken.
✗ Branch 18 not taken.
4 EXPECT_NEAR( expectedTimestamp, event->getMomentUs() - start, angleToTimeUs( 1 ) )
489 << "Expected angle: " << expectedEventEnginePhase << " but got " << (event->getMomentUs() - start) / engine.rpmCalculator.oneDegreeUs << " -- "
490
0/1
✗ Branch 1 not taken.
4 << msg;
491 4 return true;
492 }
493 }
494 return false;
495 }
496
497 3 void EngineTestHelper::spin60_2UntilDeg(struct testSpinEngineUntilData& spinInfo, int targetRpm, float targetDegree) {
498 3 volatile float tick_per_deg = 6000 * 60 / 360 / (float)targetRpm;
499 3 constexpr float tooth_per_deg = 360 / 60;
500
501 3 size_t targetTooth = (targetDegree - spinInfo.currentDegree) / tooth_per_deg;
502
503
2/2
✓ Branch 0 taken 360 times.
✓ Branch 1 taken 3 times.
2/2
✓ Decision 'true' taken 360 times.
✓ Decision 'false' taken 3 times.
363 for (size_t i = 0; i < targetTooth; i++) {
504
4/4
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 180 times.
✓ Branch 2 taken 168 times.
✓ Branch 3 taken 12 times.
2/2
✓ Decision 'true' taken 348 times.
✓ Decision 'false' taken 12 times.
360 if (spinInfo.currentTooth < 30 || spinInfo.currentTooth > 31) {
505 348 smartFireTriggerEvents2(1 /* count */, tick_per_deg /*ms*/);
506 }
507
508
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 354 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 354 times.
360 if (spinInfo.currentTooth == 30) {
509 // now fire missed tooth rise/fall
510 6 fireRise(tick_per_deg * 5 /*ms*/);
511 6 fireFall(tick_per_deg);
512 6 executeActions();
513 }
514
515
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 354 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 354 times.
360 if (spinInfo.currentTooth > 58) {
516 6 spinInfo.currentTooth = 0;
517 }
518
519 360 spinInfo.currentTooth++;
520 }
521 3 }
522
523 116 void EngineTestHelper::applyTriggerWaveform() {
524 116 engine.updateTriggerConfiguration();
525
526 116 incrementGlobalConfigurationVersion("helper");
527 116 }
528
529 // todo: open question if this is worth a helper method or should be inlined?
530 void EngineTestHelper::assertRpm(int expectedRpm, const char *msg) {
531 EXPECT_NEAR(expectedRpm, Sensor::getOrZero(SensorType::Rpm), 0.01) << msg;
532 }
533
534 24 void setupSimpleTestEngineWithMaf(EngineTestHelper *eth, injection_mode_e injectionMode,
535 trigger_type_e trigger) {
536 24 engineConfiguration->isIgnitionEnabled = false; // let's focus on injection
537 24 engineConfiguration->cylindersCount = 4;
538 // a bit of flexibility - the mode may be changed by some tests
539 24 engineConfiguration->injectionMode = injectionMode;
540 // set cranking mode (it's used by getCurrentInjectionMode())
541 24 engineConfiguration->crankingInjectionMode = IM_SIMULTANEOUS;
542
543 24 setArrayValues(config->cltFuelCorrBins, 1.0f);
544 24 setFlatInjectorLag(0.0);
545 // this is needed to update injectorLag
546 24 engine->updateSlowSensors();
547
548
4/10
✓ Branch 3 taken 24 times.
✓ Branch 7 taken 24 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 24 times.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 24 not taken.
✗ Branch 27 not taken.
✓ Branch 34 taken 24 times.
✗ Branch 35 not taken.
24 ASSERT_EQ( 0, engine->triggerCentral.isTriggerConfigChanged()) << "trigger #1";
549 24 eth->setTriggerType(trigger);
550 }
551
552 100 void EngineTestHelper::setTriggerType(trigger_type_e trigger) {
553 100 engineConfiguration->trigger.type = trigger;
554 100 incrementGlobalConfigurationVersion();
555
4/10
✓ Branch 3 taken 100 times.
✓ Branch 7 taken 100 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 100 times.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 24 not taken.
✗ Branch 27 not taken.
✓ Branch 34 taken 100 times.
✗ Branch 35 not taken.
100 ASSERT_EQ( 1, engine.triggerCentral.isTriggerConfigChanged()) << "trigger #2";
556 100 applyTriggerWaveform();
557 }
558
559 21 void setupSimpleTestEngineWithMafAndTT_ONE_trigger(EngineTestHelper *eth, injection_mode_e injectionMode) {
560 21 setCamOperationMode();
561 21 setupSimpleTestEngineWithMaf(eth, injectionMode, trigger_type_e::TT_HALF_MOON);
562 21 }
563
564 595 void setVerboseTrigger(bool isEnabled) {
565 595 printTriggerDebug = isEnabled;
566 595 printTriggerTrace = isEnabled;
567 595 }
568
569 67 warningBuffer_t * getRecentWarnings() {
570 67 return &engine->engineState.warnings.recentWarnings;
571 }
572