Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/tests/InternalTest.cpp
169678 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file InternalTest.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Mar 2025
17
///
18
// Class used for internal tests
19
/****************************************************************************/
20
#include <config.h>
21
22
#include <fstream>
23
#include <utils/common/MsgHandler.h>
24
25
#include "InternalTest.h"
26
#include "InternalTestStep.h"
27
28
#ifdef _MSC_VER
29
// disable using unsecure functions (getenv)
30
#pragma warning(disable:4996)
31
#endif
32
33
// define number of points to interpolate
34
#define numPointsInterpolation 100
35
36
// ===========================================================================
37
// member method definitions
38
// ===========================================================================
39
40
// ---------------------------------------------------------------------------
41
// InternalTest::ViewPosition - public methods
42
// ---------------------------------------------------------------------------
43
44
InternalTest::ViewPosition::ViewPosition() {}
45
46
47
InternalTest::ViewPosition::ViewPosition(const int x, const int y) :
48
myX(x),
49
myY(y) {
50
}
51
52
53
InternalTest::ViewPosition::ViewPosition(const std::string& x, const std::string& y) :
54
myX(StringUtils::toInt(x)),
55
myY(StringUtils::toInt(y)) {
56
}
57
58
59
int
60
InternalTest::ViewPosition::getX() const {
61
return myX;
62
}
63
64
65
int
66
InternalTest::ViewPosition::getY() const {
67
return myY;
68
}
69
70
// ---------------------------------------------------------------------------
71
// InternalTest::ContextualMenu - public methods
72
// ---------------------------------------------------------------------------
73
74
InternalTest::ContextualMenu::ContextualMenu() {}
75
76
77
InternalTest::ContextualMenu::ContextualMenu(const std::string& mainMenuValue,
78
const std::string& subMenuAValue, const std::string& subMenuBValue) :
79
myMainMenu(StringUtils::toInt(mainMenuValue)),
80
mySubMenuA(StringUtils::toInt(subMenuAValue)),
81
mySubMenuB(StringUtils::toInt(subMenuBValue)) {
82
}
83
84
85
int
86
InternalTest::ContextualMenu::getMainMenuPosition() const {
87
return myMainMenu;
88
}
89
90
91
int
92
InternalTest::ContextualMenu::getSubMenuAPosition() const {
93
return mySubMenuA;
94
}
95
96
97
int
98
InternalTest::ContextualMenu::getSubMenuBPosition() const {
99
return mySubMenuB;
100
}
101
102
// ---------------------------------------------------------------------------
103
// InternalTest::Movement - public methods
104
// ---------------------------------------------------------------------------
105
106
InternalTest::Movement::Movement() {}
107
108
109
InternalTest::Movement::Movement(const std::string& up, const std::string& down,
110
const std::string& left, const std::string& right) :
111
myUp(StringUtils::toInt(up)),
112
myDown(StringUtils::toInt(down)),
113
myLeft(StringUtils::toInt(left)),
114
myRight(StringUtils::toInt(right)) {
115
}
116
117
118
int
119
InternalTest::Movement::getUp() const {
120
return myUp;
121
}
122
123
124
int
125
InternalTest::Movement::getDown() const {
126
return myDown;
127
}
128
129
130
int
131
InternalTest::Movement::getLeft() const {
132
return myLeft;
133
}
134
135
136
int
137
InternalTest::Movement::getRight() const {
138
return myRight;
139
}
140
141
// ---------------------------------------------------------------------------
142
// InternalTest - public methods
143
// ---------------------------------------------------------------------------
144
145
InternalTest::InternalTest(const std::string& testFile) {
146
// locate sumo home directory
147
const auto sumoHome = std::string(getenv("SUMO_HOME"));
148
// load data files
149
myAttributesEnum = parseAttributesEnumFile(sumoHome + "/data/tests/attributesEnum.txt");
150
myContextualMenuOperations = parseContextualMenuOperationsFile(sumoHome + "/data/tests/contextualMenuOperations.txt");
151
myViewPositions = parseViewPositionsFile(sumoHome + "/data/tests/viewPositions.txt");
152
myMovements = parseMovementsFile(sumoHome + "/data/tests/movements.txt");
153
// open file
154
std::ifstream strm(testFile);
155
// check if file can be opened
156
if (!strm.good()) {
157
std::cout << "Could not open test file '" + testFile + "'." << std::endl;
158
throw ProcessError();
159
} else if (myAttributesEnum.empty() || myContextualMenuOperations.empty() || myViewPositions.empty() || myMovements.empty()) {
160
std::cout << "Error loading test data files" << std::endl;
161
throw ProcessError();
162
} else {
163
std::string line;
164
std::vector<std::pair<bool, std::string> > linesRaw;
165
// read full lines until end of file
166
while (std::getline(strm, line)) {
167
// filter lines
168
if (!line.empty() && // emty lines
169
!(line[0] == '#') && // comments
170
!startWith(line, "import") && // imports
171
!startWith(line, "time.") && // time calls
172
!startWith(line, "sys.")) { // sys calls
173
linesRaw.push_back(std::make_pair(startWith(line, "netedit."), line));
174
}
175
}
176
// clean lines
177
const auto lines = cleanLines(linesRaw);
178
// create steps
179
new InternalTestStep(this, "netedit.setupAndStart");
180
for (const auto& clearLine : lines) {
181
new InternalTestStep(this, clearLine);
182
}
183
new InternalTestStep(this, "netedit.finish");
184
}
185
}
186
187
188
InternalTest::~InternalTest() {
189
// delete all test steps
190
while (myInitialTestStep != nullptr) {
191
// store next step
192
auto nextStep = myInitialTestStep->getNextStep();
193
// delete current step
194
delete myInitialTestStep;
195
// set next step as initial step
196
myInitialTestStep = nextStep;
197
}
198
}
199
200
201
void
202
InternalTest::addTestSteps(InternalTestStep* internalTestStep) {
203
if (myLastTestStep == nullptr) {
204
// set initial step
205
myInitialTestStep = internalTestStep;
206
myLastTestStep = internalTestStep;
207
myCurrentTestStep = internalTestStep;
208
} else {
209
// set next step
210
myLastTestStep->setNextStep(internalTestStep);
211
myLastTestStep = internalTestStep;
212
}
213
}
214
215
216
InternalTestStep*
217
InternalTest::getCurrentStep() const {
218
return myCurrentTestStep;
219
}
220
221
222
InternalTestStep*
223
InternalTest::setNextStep() {
224
const auto currentStep = myCurrentTestStep;
225
myCurrentTestStep = myCurrentTestStep->getNextStep();
226
return currentStep;
227
}
228
229
230
bool
231
InternalTest::isRunning() const {
232
return myRunning;
233
}
234
235
236
void
237
InternalTest::stopTests() {
238
myRunning = false;
239
}
240
241
242
FXint
243
InternalTest::getTime() const {
244
return static_cast<FXuint>(
245
std::chrono::duration_cast<std::chrono::milliseconds>(
246
std::chrono::steady_clock::now().time_since_epoch()
247
).count());
248
}
249
250
251
const std::map<std::string, int>&
252
InternalTest::getAttributesEnum() const {
253
return myAttributesEnum;
254
}
255
256
257
const std::map<std::string, InternalTest::ContextualMenu>&
258
InternalTest::getContextualMenuOperations() const {
259
return myContextualMenuOperations;
260
}
261
262
263
const std::map<std::string, InternalTest::ViewPosition>&
264
InternalTest::getViewPositions() const {
265
return myViewPositions;
266
}
267
268
269
const std::map<std::string, InternalTest::Movement>&
270
InternalTest::getMovements() const {
271
return myMovements;
272
}
273
274
275
const InternalTest::ViewPosition&
276
InternalTest::getLastMovedPosition() const {
277
return myLastMovedPosition;
278
}
279
280
281
void
282
InternalTest::updateLastMovedPosition(const int x, const int y) {
283
myLastMovedPosition = InternalTest::ViewPosition(x, y);
284
}
285
286
287
std::vector<InternalTest::ViewPosition>
288
InternalTest::interpolateViewPositions(const InternalTest::ViewPosition& viewStartPosition,
289
const int offsetStartX, const int offsetStartY,
290
const InternalTest::ViewPosition& viewEndPosition,
291
const int offsetEndX, const int offsetEndY) const {
292
// declare trajectory vector
293
std::vector<InternalTest::ViewPosition> trajectory;
294
trajectory.reserve(numPointsInterpolation);
295
// calulate from using offsets
296
const auto from = InternalTest::ViewPosition(viewStartPosition.getX() + offsetStartX, viewStartPosition.getY() + offsetStartY);
297
const auto to = InternalTest::ViewPosition(viewEndPosition.getX() + offsetEndX, viewEndPosition.getY() + offsetEndY);
298
// itearte over the number of points to interpolate
299
for (int i = 0; i < numPointsInterpolation; i++) {
300
const double t = static_cast<double>(i) / (numPointsInterpolation - 1); // t in [0, 1]
301
// calculate interpolated position
302
const int interpolatedX = int(from.getX() + t * (to.getX() - from.getX()));
303
const int interpolatedY = int(from.getY() + t * (to.getY() - from.getY()));
304
// add interpolated position
305
trajectory.push_back(ViewPosition(interpolatedX, interpolatedY));
306
}
307
return trajectory;
308
}
309
310
311
std::map<std::string, int>
312
InternalTest::parseAttributesEnumFile(const std::string filePath) const {
313
std::map<std::string, int> solution;
314
// open file
315
std::ifstream strm(filePath);
316
// check if file can be opened
317
if (!strm.good()) {
318
WRITE_ERRORF(TL("Could not open attributes enum file '%'."), filePath);
319
} else {
320
std::string line;
321
// read full lines until end of file
322
while (std::getline(strm, line)) {
323
// use stringstream for
324
std::stringstream ss(line);
325
// read key and value
326
std::string key;
327
std::string value;
328
std::getline(ss, key, ' ');
329
std::getline(ss, value, '\n');
330
// check that int can be parsed
331
if (!StringUtils::isInt(value)) {
332
WRITE_ERRORF(TL("In internal test file, value '%' cannot be parsed to int."), value);
333
} else {
334
solution[key] = StringUtils::toInt(value);
335
}
336
}
337
}
338
return solution;
339
}
340
341
342
std::map<std::string, InternalTest::ContextualMenu>
343
InternalTest::parseContextualMenuOperationsFile(const std::string filePath) const {
344
std::map<std::string, InternalTest::ContextualMenu> solution;
345
// open file
346
std::ifstream strm(filePath);
347
// check if file can be opened
348
if (!strm.good()) {
349
WRITE_ERRORF(TL("Could not open view positions file '%'."), filePath);
350
} else {
351
std::string line;
352
// read full lines until end of file
353
while (std::getline(strm, line)) {
354
// read key and value
355
std::string mainMenuKey;
356
std::string mainMenuValue;
357
std::string subMenuAKey;
358
std::string subMenuAValue;
359
std::string subMenuBKey;
360
std::string subMenuBValue;
361
// parse first line
362
std::stringstream mainMenuSS(line);
363
std::getline(mainMenuSS, mainMenuKey, ' ');
364
std::getline(mainMenuSS, mainMenuValue, '\n');
365
// parse second line
366
std::getline(strm, line);
367
std::stringstream subMenuASS(line);
368
std::getline(subMenuASS, subMenuAKey, ' ');
369
std::getline(subMenuASS, subMenuAValue, '\n');
370
// parse third line
371
std::getline(strm, line);
372
std::stringstream subMenuBSS(line);
373
std::getline(subMenuBSS, subMenuBKey, ' ');
374
std::getline(subMenuBSS, subMenuBValue, '\n');
375
// check that int can be parsed
376
if (!StringUtils::isInt(mainMenuValue)) {
377
WRITE_ERRORF(TL("In internal test file, mainMenu value '%' cannot be parsed to int."), mainMenuValue);
378
} else if (!StringUtils::isInt(subMenuAValue)) {
379
WRITE_ERRORF(TL("In internal test file, subMenuA value '%' cannot be parsed to int."), subMenuAValue);
380
} else if (!StringUtils::isInt(subMenuBValue)) {
381
WRITE_ERRORF(TL("In internal test file, subMenuB value '%' cannot be parsed to int."), subMenuBValue);
382
} else {
383
// remove '.mainMenuPosition' from mainMenuKey
384
solution[mainMenuKey.erase(mainMenuKey.size() - 17)] = InternalTest::ContextualMenu(mainMenuValue, subMenuAValue, subMenuBValue);
385
}
386
}
387
}
388
return solution;
389
}
390
391
392
std::map<std::string, InternalTest::ViewPosition>
393
InternalTest::parseViewPositionsFile(const std::string filePath) const {
394
std::map<std::string, InternalTest::ViewPosition> solution;
395
// open file
396
std::ifstream strm(filePath);
397
// check if file can be opened
398
if (!strm.good()) {
399
WRITE_ERRORF(TL("Could not open view positions file '%'."), filePath);
400
} else {
401
std::string line;
402
// read full lines until end of file
403
while (std::getline(strm, line)) {
404
// use stringstream for
405
std::stringstream ss(line);
406
// read key and value
407
std::string key;
408
std::string xValue;
409
std::string yValue;
410
std::getline(ss, key, ' ');
411
std::getline(ss, xValue, ' ');
412
std::getline(ss, yValue, '\n');
413
// check that int can be parsed
414
if (!StringUtils::isInt(xValue)) {
415
WRITE_ERRORF(TL("In internal test file, x value '%' cannot be parsed to int."), xValue);
416
} else if (!StringUtils::isInt(yValue)) {
417
WRITE_ERRORF(TL("In internal test file, y value '%' cannot be parsed to int."), yValue);
418
} else {
419
solution[key] = InternalTest::ViewPosition(xValue, yValue);
420
}
421
}
422
}
423
return solution;
424
}
425
426
427
std::map<std::string, InternalTest::Movement>
428
InternalTest::parseMovementsFile(const std::string filePath) const {
429
std::map<std::string, InternalTest::Movement> solution;
430
// open file
431
std::ifstream strm(filePath);
432
// check if file can be opened
433
if (!strm.good()) {
434
WRITE_ERRORF(TL("Could not open view positions file '%'."), filePath);
435
} else {
436
std::string line;
437
// read full lines until end of file
438
while (std::getline(strm, line)) {
439
// use stringstream for
440
std::stringstream ss(line);
441
// read key and value
442
std::string key;
443
std::string upValue;
444
std::string downValue;
445
std::string leftValue;
446
std::string rightValue;
447
std::getline(ss, key, ' ');
448
std::getline(ss, upValue, ' ');
449
std::getline(ss, downValue, ' ');
450
std::getline(ss, leftValue, ' ');
451
std::getline(ss, rightValue, '\n');
452
// check that int can be parsed
453
if (!StringUtils::isInt(upValue)) {
454
WRITE_ERRORF(TL("In internal test file, x value '%' cannot be parsed to int."), upValue);
455
} else if (!StringUtils::isInt(downValue)) {
456
WRITE_ERRORF(TL("In internal test file, y value '%' cannot be parsed to int."), downValue);
457
} else if (!StringUtils::isInt(leftValue)) {
458
WRITE_ERRORF(TL("In internal test file, y value '%' cannot be parsed to int."), leftValue);
459
} else if (!StringUtils::isInt(rightValue)) {
460
WRITE_ERRORF(TL("In internal test file, y value '%' cannot be parsed to int."), rightValue);
461
} else {
462
solution[key] = InternalTest::Movement(upValue, downValue, leftValue, rightValue);
463
}
464
}
465
}
466
return solution;
467
}
468
469
470
std::vector<std::string>
471
InternalTest::cleanLines(const std::vector<std::pair<bool, std::string> >& linesRaw) const {
472
std::vector<std::string> results;
473
for (const auto& lineRaw : linesRaw) {
474
if (lineRaw.first) {
475
results.push_back(lineRaw.second);
476
} else if (results.size() > 0) {
477
results.back().append(lineRaw.second);
478
}
479
}
480
return results;
481
}
482
483
484
bool
485
InternalTest::startWith(const std::string& str, const std::string& prefix) const {
486
if (prefix.size() > str.size()) {
487
return false;
488
} else {
489
for (int i = 0; i < (int)prefix.size(); i++) {
490
if (str[i] != prefix[i]) {
491
return false;
492
}
493
}
494
return true;
495
}
496
}
497
498
/****************************************************************************/
499
500