GCC Code Coverage Report


Directory: ./
File: unit_tests/test-framework/engine_test_helper.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 96.1% 297 0 309
Functions: 93.5% 43 0 46
Branches: 60.9% 131 0 215
Decisions: 86.5% 45 - 52

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