• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NeonGE / geEngineSDK / ac7673f3-08dc-4326-87b7-3c14ad7938bf

05 Mar 2026 04:29PM UTC coverage: 57.84% (-0.02%) from 57.862%
ac7673f3-08dc-4326-87b7-3c14ad7938bf

push

circleci

NeonGE
Refactor input mapping, improve exception safety & tasks

- Replaced SFML-to-ImGui key mapping switch with static map for clarity.
- Updated input event lambdas to avoid capturing `this`.
- Marked `swap` methods as `_NOEXCEPT` in Matrix4, Path, Vector2.
- Added assertions and removed redundant checks in frame allocator.
- Refactored platform termination logic for clarity and portability.
- Modernized task scheduler waits using condition variable predicates.
- Updated debug.css font stack for better cross-platform support.

6 of 11 new or added lines in 4 files covered. (54.55%)

8 existing lines in 3 files now uncovered.

5603 of 9687 relevant lines covered (57.84%)

9107.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.23
/sdk/geUtilities/src/gePath.cpp
1
/*****************************************************************************/
2
/**
3
* @file   gePath.cpp
4
* @author Samuel Prince (samuel.prince.quezada@gmail.com)
5
* @date   2016/03/06
6
* @brief  Class for storing and manipulating file paths
7
*
8
* Class for storing and manipulating file paths. Paths may be
9
* parsed from and to raw strings according to various platform
10
* specific path types.
11
*
12
* @bug           No known bugs.
13
*/
14
/*****************************************************************************/
15

16
/*****************************************************************************/
17
/**
18
* Includes
19
*/
20
/*****************************************************************************/
21
#include "gePrerequisitesUtilities.h"
22
#include "geException.h"
23
#include "geDebug.h"
24
#include "geUnicode.h"
25

26
namespace geEngineSDK {
27
  const Path Path::BLANK = Path();
28

29
  Path::Path(const ANSICHAR* pathStr, PATH_TYPE::E type) {
51✔
30
    assign(pathStr, type);
51✔
31
  }
51✔
32

33
  Path::Path(const String& pathStr, PATH_TYPE::E type) {
82✔
34
    assign(pathStr, type);
82✔
35
  }
82✔
36

37
  Path::Path(const Path& other) {
189✔
38
    assign(other);
189✔
39
  }
189✔
40

41
  Path& Path::operator=(const Path& path) {
31✔
42
    assign(path);
31✔
43
    return *this;
31✔
44
  }
45

46
  Path& Path::operator=(const String& pathStr) {
10✔
47
    assign(pathStr);
10✔
48
    return *this;
10✔
49
  }
50

51
  Path& Path::operator=(const ANSICHAR* pathStr) {
×
52
    assign(pathStr);
×
53
    return *this;
×
54
  }
55

56
  void
NEW
57
  Path::swap(Path& path) _NOEXCEPT {
×
58
    std::swap(m_directories, path.m_directories);
×
59
    std::swap(m_filename, path.m_filename);
×
60
    std::swap(m_device, path.m_device);
×
61
    std::swap(m_node, path.m_node);
×
62
    std::swap(m_isAbsolute, path.m_isAbsolute);
×
63
  }
×
64

65
  void
66
  Path::assign(const Path& path) {
220✔
67
    m_directories = path.m_directories;
220✔
68
    m_filename = path.m_filename;
220✔
69
    m_device = path.m_device;
220✔
70
    m_node = path.m_node;
220✔
71
    m_isAbsolute = path.m_isAbsolute;
220✔
72
  }
220✔
73

74
  void
75
  Path::assign(const ANSICHAR* pathStr, PATH_TYPE::E type) {
51✔
76
    assign(pathStr, strlen(pathStr), type);
51✔
77
  }
51✔
78

79
  void
80
  Path::assign(const String& pathStr, PATH_TYPE::E type) {
92✔
81
    assign(pathStr.data(), pathStr.length(), type);
92✔
82
  }
92✔
83

84
  void
85
  Path::assign(const ANSICHAR* pathStr, SIZE_T numChars, PATH_TYPE::E type) {
143✔
86
    switch (type)
143✔
87
    {
88
      case PATH_TYPE::kWindows:
×
89
        parseWindows(pathStr, numChars);
×
90
        break;
×
91
      case PATH_TYPE::kUnix:
×
92
        parseUnix(pathStr, numChars);
×
93
        break;
×
94
      case PATH_TYPE::kDefault:
143✔
95
      default:
96
#if USING(GE_PLATFORM_WINDOWS)
97
        parseWindows(pathStr, numChars);
98
#elif USING(GE_PLATFORM_OSX) || USING(GE_PLATFORM_LINUX) || \
99
      USING(GE_PLATFORM_PS4) || USING(GE_PLATFORM_PS5)
100
        //TODO: Test parsing with PS4
101
        parseUnix(pathStr, numChars);
143✔
102
#else
103
        static_assert(false, "Unsupported platform for path.");
104
#endif
105
        break;
143✔
106
    }
107
  }
143✔
108

109
#if USING(GE_PLATFORM_WINDOWS)
110
  WString
111
  Path::toPlatformString() const {
112
    return UTF8::toWide(toString());
113
  }
114
#endif
115

116
  String
117
  Path::toString(PATH_TYPE::E type) const {
240✔
118
    switch (type)
240✔
119
    {
120
      case PATH_TYPE::kWindows:
×
121
        return buildWindows();
×
122
      case PATH_TYPE::kUnix:
×
123
        return buildUnix();
×
124
      case PATH_TYPE::kDefault:
240✔
125
      default:
126
#if USING(GE_PLATFORM_WINDOWS)
127
        return buildWindows();
128
#elif USING(GE_PLATFORM_OSX) || USING(GE_PLATFORM_LINUX) || USING(GE_PLATFORM_PS4) || USING(GE_PLATFORM_PS5)
129
        return buildUnix();
240✔
130
#else
131
        static_assert(false, "Unsupported platform for path.");
132
#endif
133
        break;
134
    }
135
  }
136

137
  Path
138
  Path::getParent() const {
×
139
    Path copy = *this;
×
140
    copy.makeParent();
×
141
    return copy;
×
142
  }
×
143

144
  Path
145
  Path::getAbsolute(const Path& base) const {
6✔
146
    Path copy = *this;
6✔
147
    copy.makeAbsolute(base);
6✔
148
    return copy;
6✔
149
  }
×
150

151
  Path
152
  Path::getRelative(const Path& base) const {
8✔
153
    Path copy = *this;
8✔
154
    copy.makeRelative(base);
8✔
155
    return copy;
8✔
156
  }
×
157

158
  Path
159
  Path::getDirectory() const {
6✔
160
    Path copy = *this;
6✔
161
    copy.m_filename.clear();
6✔
162
    return copy;
6✔
163
  }
164

165
  Path&
166
  Path::makeParent() {
×
167
    if (m_filename.empty()) {
×
168
      if (m_directories.empty()) {
×
169
        if (!m_isAbsolute) {
×
170
          m_directories.emplace_back("..");
×
171
        }
172
      }
173
      else {
174
        if (".." == m_directories.back()) {
×
175
          m_directories.emplace_back("..");
×
176
        }
177
        else {
178
          m_directories.pop_back();
×
179
        }
180
      }
181
    }
182
    else {
183
      m_filename.clear();
×
184
    }
185

186
    return *this;
×
187
  }
188

189
  Path&
190
  Path::makeAbsolute(const Path& base) {
6✔
191
    if (m_isAbsolute) {
6✔
192
      return *this;
×
193
    }
194

195
    Path absDir = base.getDirectory();
6✔
196
    if (base.isFile()) {
6✔
197
      absDir.pushDirectory(base.m_filename);
×
198
    }
199

200
    for (auto& dir : m_directories) {
7✔
201
      absDir.pushDirectory(dir);
1✔
202
    }
203

204
    absDir.setFilename(m_filename);
6✔
205
    *this = absDir;
6✔
206
    return *this;
6✔
207
  }
6✔
208

209
  Path&
210
  Path::makeRelative(const Path& base) {
8✔
211
    const SIZE_T n = base.m_directories.size();
8✔
212
    if (n > m_directories.size()) {
8✔
213
      return *this;
×
214
    }
215

216
    using Diff = decltype(m_directories)::difference_type;
217
    m_directories.erase(m_directories.begin(), m_directories.begin() + static_cast<Diff>(n));
8✔
218

219
    /**
220
     * Sometimes a directory name can be interpreted as a file and we're okay with that.
221
     * Check for that special case.
222
     */
223
    if (base.isFile()) {
8✔
224
      if (!m_directories.empty()) {
×
225
        m_directories.erase(m_directories.begin());
×
226
      }
227
      else {
228
        m_filename = "";
×
229
      }
230
    }
231

232
    m_device = "";
8✔
233
    m_node = "";
8✔
234
    m_isAbsolute = false;
8✔
235
    return *this;
8✔
236
  }
237

238
  bool
239
  Path::includes(const Path& child) const {
×
240
    if (m_device != child.m_device) {
×
241
      return false;
×
242
    }
243

244
    if (m_node != child.m_node) {
×
245
      return false;
×
246
    }
247

248
    auto iterParent = m_directories.begin();
×
249
    auto iterChild = child.m_directories.begin();
×
250

251
    for (; iterParent != m_directories.end(); ++iterChild, ++iterParent) {
×
252
      if (iterChild == child.m_directories.end()) {
×
253
        return false;
×
254
      }
255

256
      if (!comparePathElem(*iterChild, *iterParent)) {
×
257
        return false;
×
258
      }
259
    }
260

261
    if (!m_filename.empty()) {
×
262
      if (iterChild == child.m_directories.end()) {
×
263
        if (child.m_filename.empty()) {
×
264
          return false;
×
265
        }
266

267
        if (!comparePathElem(child.m_filename, m_filename)) {
×
268
          return false;
×
269
        }
270
      }
271
      else {
272
        if (!comparePathElem(*iterChild, m_filename)) {
×
273
          return false;
×
274
        }
275
      }
276
    }
277

278
    return true;
×
279
  }
280

281
  bool
282
  Path::equals(const Path& other) const {
×
283
    if (m_isAbsolute != other.m_isAbsolute) {
×
284
      return false;
×
285
    }
286

287
    if (m_isAbsolute) {
×
288
      if (!comparePathElem(m_device, other.m_device)) {
×
289
        return false;
×
290
      }
291
    }
292

293
    SIZE_T myNumElements = m_directories.size();
×
294
    SIZE_T otherNumElements = other.m_directories.size();
×
295

296
    if (!m_filename.empty()) {
×
297
      ++myNumElements;
×
298
    }
299

300
    if (!other.m_filename.empty()) {
×
301
      ++otherNumElements;
×
302
    }
303

304
    if (myNumElements != otherNumElements) {
×
305
      return false;
×
306
    }
307
    
308
    if (0 < myNumElements) {
×
309
      auto iterMe = m_directories.begin();
×
310
      auto iterOther = other.m_directories.begin();
×
311

312
      for (SIZE_T i = 0; i < (myNumElements - 1); ++i, ++iterMe, ++iterOther) {
×
313
        if (!comparePathElem(*iterMe, *iterOther)) {
×
314
          return false;
×
315
        }
316
      }
317

318
      if (!m_filename.empty()) {
×
319
        if (!other.m_filename.empty()) {
×
320
          if (!comparePathElem(m_filename, other.m_filename)) {
×
321
            return false;
×
322
          }
323
        }
324
        else {
325
          if (!comparePathElem(m_filename, *iterOther)) {
×
326
            return false;
×
327
          }
328
        }
329
      }
330
      else {
331
        if (!other.m_filename.empty()) {
×
332
          if (!comparePathElem(*iterMe, other.m_filename)) {
×
333
            return false;
×
334
          }
335
        }
336
        else {
337
          if (!comparePathElem(*iterMe, *iterOther)) {
×
338
            return false;
×
339
          }
340
        }
341
      }
342
    }
343

344
    return true;
×
345
  }
346

347
  Path&
348
  Path::append(const Path& path) {
29✔
349
    if (!m_filename.empty()) {
29✔
350
      pushDirectory(m_filename);
×
351
    }
352

353
    for (auto& dir : path.m_directories) {
53✔
354
      pushDirectory(dir);
24✔
355
    }
356

357
    m_filename = path.m_filename;
29✔
358
    return *this;
29✔
359
  }
360

361
  void
362
  Path::setBasename(const String& basename) {
×
363
    m_filename = basename + getExtension();
×
364
  }
×
365

366
  void
367
  Path::setExtension(const String& extension) {
×
368
    StringStream stream;
×
369
    stream << getFilename(false);
×
370
    stream << extension;
×
371
    m_filename = stream.str();
×
372
  }
×
373

374
  String
375
  Path::getFilename(bool extension) const {
×
376
    if (extension) {
×
377
      return m_filename;
×
378
    }
379
    else {
380
      WString::size_type pos = m_filename.rfind(L'.');
×
381
      if (pos != WString::npos) {
×
382
        return m_filename.substr(0, pos);
×
383
      }
384
      else {
385
        return m_filename;
×
386
      }
387
    }
388
  }
389

390
  String
391
  Path::getExtension() const {
×
392
    WString::size_type pos = m_filename.rfind(L'.');
×
393
    if (pos != WString::npos)
×
394
      return m_filename.substr(pos);
×
395
    else
396
      return String();
×
397
  }
398

399
  const String&
400
  Path::getDirectory(SIZE_T idx) const {
×
401
    if (idx >= m_directories.size()) {
×
402
      GE_EXCEPT(InvalidParametersException,
×
403
                "Index out of range: " + 
404
                geEngineSDK::toString(static_cast<uint32>(idx)) +
405
                ". Valid range: [0, " + 
406
                geEngineSDK::toString(static_cast<uint32>(m_directories.size() - 1)) +
407
                "]");
408
    }
409
    return m_directories[idx];
×
410
  }
411

412
  const String&
413
  Path::getTail() const {
×
414
    if (isFile()) {
×
415
      return m_filename;
×
416
    }
417
    else if (!m_directories.empty()) {
×
418
      return m_directories.back();
×
419
    }
420
    else {
421
      return StringUtil::BLANK;
×
422
    }
423
  }
424

425
  void
426
  Path::clear() {
143✔
427
    m_directories.clear();
143✔
428
    m_device.clear();
143✔
429
    m_filename.clear();
143✔
430
    m_node.clear();
143✔
431
    m_isAbsolute = false;
143✔
432
  }
143✔
433

434
  void
435
  Path::throwInvalidPathException(const String& path) {
×
436
    GE_EXCEPT(InvalidParametersException, "Incorrectly formatted path provided: " + path);
×
437
  }
438

439
  String
440
  Path::buildWindows() const {
×
441
    StringStream result;
×
442
    if (!m_node.empty()) {
×
443
      result << "\\\\";
×
444
      result << m_node;
×
445
      result << "\\";
×
446
    }
447
    else if (!m_device.empty()) {
×
448
      result << m_device;
×
449
      result << ":\\";
×
450
    }
451
    else if (m_isAbsolute) {
×
452
      result << "\\";
×
453
    }
454

455
    for (auto& dir : m_directories) {
×
456
      result << dir;
×
457
      result << "\\";
×
458
    }
459

460
    result << m_filename;
×
461
    return result.str();
×
462
  }
×
463

464
  String
465
  Path::buildUnix() const {
240✔
466
    StringStream result;
240✔
467
    auto dirIter = m_directories.begin();
240✔
468

469
    if (!m_device.empty()) {
240✔
470
      result << "/";
×
471
      result << m_device;
×
472
      result << ":/";
×
473
    }
474
    else if (m_isAbsolute) {
240✔
475
      if (dirIter != m_directories.end() && "~" == *dirIter) {
181✔
476
        result << "~";
×
477
        dirIter++;
×
478
      }
479
      result << "/";
181✔
480
    }
481

482
    for (; dirIter != m_directories.end(); ++dirIter) {
717✔
483
      result << *dirIter;
477✔
484
      result << "/";
477✔
485
    }
486

487
    result << m_filename;
240✔
488
    return result.str();
480✔
489
  }
240✔
490

491
  Path
492
  Path::operator+(const Path& rhs) const {
×
493
    return Path::combine(*this, rhs);
×
494
  }
495

496
  Path&
497
  Path::operator+=(const Path& rhs) {
×
498
    return append(rhs);
×
499
  }
500

501
  bool
502
  Path::comparePathElem(const String& left, const String& right) {
×
503
    if (left.size() != right.size()) {
×
504
      return false;
×
505
    }
506

507
    //TODO: Case sensitive/insensitive file path actually depends on used file-system
508
    for (SIZE_T i = 0; i<left.size(); ++i) {
×
509
      if (tolower(left[i]) != tolower(right[i])) {
×
510
        return false;
×
511
      }
512
    }
513

514
    return true;
×
515
  }
516

517
  Path
518
  Path::combine(const Path& left, const Path& right) {
×
519
    Path output = left;
×
520
    return output.append(right);
×
521
  }
×
522

523
  void
524
  Path::stripInvalid(String& path) {
×
525
    String illegalChars = "\\/:?\"<>|";
×
526

527
    for (auto& entry : path) {
×
528
      if (illegalChars.find(entry) != String::npos) {
×
529
        entry = ' ';
×
530
      }
531
    }
532
  }
×
533

534
  void
535
  Path::pushDirectory(const String& dir) {
273✔
536
    if (!dir.empty() && "." != dir) {
273✔
537
      if (dir == "..") {
266✔
538
        if (!m_directories.empty() && m_directories.back() != "..") {
×
539
          m_directories.pop_back();
×
540
        }
541
        else {
542
          m_directories.push_back(dir);
×
543
        }
544
      }
545
      else {
546
        m_directories.push_back(dir);
266✔
547
      }
548
    }
549
  }
273✔
550
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc