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 |