GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 95.7% 308 / 0 / 322
Functions: 93.6% 44 / 0 / 47
Branches: 61.3% 136 / 0 / 222
Decisions: 84.5% 49 / - / 58

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