GCC Code Coverage Report


Directory: ./
File: unit_tests/test-framework/engine_test_helper.cpp
Date: 2025-10-24 14:26:41
Coverage Exec Excl Total
Lines: 96.1% 296 0 308
Functions: 93.5% 43 0 46
Branches: 60.6% 129 0 213
Decisions: 86.0% 43 - 50

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