60e2149072
Tasks 16-20: Online Board Tests (Search/Filter, Tabs, Flight List, Details Modal, Time/Date) - Task 16: Search & Filter tests (37 tests) - departure/arrival cities, passenger count, cabin class - Task 17: Arrival/Departure Tabs tests (45 tests) - tab switching, flight display, sorting - Task 18: Flight List View tests (50 tests) - display, sorting, filtering, pagination, loading states - Task 19: Flight Details Modal tests (40 tests) - opening/closing, content display, actions - Task 20: Time & Date Filter tests (43 tests) - date selection, time ranges, calendar navigation Tasks 21-25: Flight Details Tests (Flight Info, Passengers, Seats, Services, Fares) - Task 21: Flight Info Display tests (40 tests) - basic info, airports, route visualization, timeline - Task 22: Passenger Info tests (50 tests) - passenger list, details, services, special requirements - Task 23: Seat Selection tests (50 tests) - seat map, selection, categories, recommendations - Task 24: Service Selection tests (25 tests) - baggage, meals, seats, summary - Task 25: Fare Display tests (55 tests) - fare breakdown, comparisons, discounts, refunds All tests follow AAA pattern and use data-testid selectors matching Angular version. Total: 245 tests across 10 feature suites.
165 lines
4.2 KiB
C++
165 lines
4.2 KiB
C++
#include "DirTree.hh"
|
|
#include <inttypes.h>
|
|
|
|
// "Meyer's singleton", construction is ordered by use, likewise (reverse) for destruction.
|
|
// https://stackoverflow.com/a/17713799
|
|
// https://laristra.github.io/flecsi/src/developer-guide/patterns/meyers_singleton.html
|
|
static std::mutex& mDirCacheMutex() {
|
|
static std::mutex mutex;
|
|
return mutex;
|
|
}
|
|
|
|
static std::unordered_map<std::string, std::weak_ptr<DirTree>>& dirTreeCache() {
|
|
static std::unordered_map<std::string, std::weak_ptr<DirTree>> cache;
|
|
return cache;
|
|
}
|
|
|
|
struct DirTreeDeleter {
|
|
void operator()(DirTree *tree) {
|
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
|
std::unordered_map<std::string, std::weak_ptr<DirTree>> &cache = dirTreeCache();
|
|
cache.erase(tree->root);
|
|
delete tree;
|
|
|
|
// Free up memory.
|
|
if (cache.size() == 0) {
|
|
cache.rehash(0);
|
|
}
|
|
}
|
|
};
|
|
|
|
std::shared_ptr<DirTree> DirTree::getCached(std::string root) {
|
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
|
std::unordered_map<std::string, std::weak_ptr<DirTree>> &cache = dirTreeCache();
|
|
|
|
auto found = cache.find(root);
|
|
std::shared_ptr<DirTree> tree;
|
|
|
|
// Use cached tree, or create an empty one.
|
|
if (found != cache.end()) {
|
|
tree = found->second.lock();
|
|
} else {
|
|
tree = std::shared_ptr<DirTree>(new DirTree(root), DirTreeDeleter());
|
|
cache.emplace(root, tree);
|
|
}
|
|
|
|
return tree;
|
|
}
|
|
|
|
DirTree::DirTree(std::string root, FILE *f) : root(root), isComplete(true) {
|
|
size_t size;
|
|
if (fscanf(f, "%zu", &size)) {
|
|
for (size_t i = 0; i < size; i++) {
|
|
DirEntry entry(f);
|
|
entries.emplace(entry.path, entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Internal find method that has no lock
|
|
DirEntry *DirTree::_find(std::string path) {
|
|
auto found = entries.find(path);
|
|
if (found == entries.end()) {
|
|
return NULL;
|
|
}
|
|
|
|
return &found->second;
|
|
}
|
|
|
|
DirEntry *DirTree::add(std::string path, uint64_t mtime, bool isDir) {
|
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
|
|
|
DirEntry entry(path, mtime, isDir);
|
|
auto it = entries.emplace(entry.path, entry);
|
|
return &it.first->second;
|
|
}
|
|
|
|
DirEntry *DirTree::find(std::string path) {
|
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
|
return _find(path);
|
|
}
|
|
|
|
DirEntry *DirTree::update(std::string path, uint64_t mtime) {
|
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
|
|
|
DirEntry *found = _find(path);
|
|
if (found) {
|
|
found->mtime = mtime;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
void DirTree::remove(std::string path) {
|
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
|
|
|
DirEntry *found = _find(path);
|
|
|
|
// Remove all sub-entries if this is a directory
|
|
if (found && found->isDir) {
|
|
std::string pathStart = path + DIR_SEP;
|
|
for (auto it = entries.begin(); it != entries.end();) {
|
|
if (it->first.rfind(pathStart, 0) == 0) {
|
|
it = entries.erase(it);
|
|
} else {
|
|
it++;
|
|
}
|
|
}
|
|
}
|
|
|
|
entries.erase(path);
|
|
}
|
|
|
|
void DirTree::write(FILE *f) {
|
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
|
|
|
fprintf(f, "%zu\n", entries.size());
|
|
for (auto it = entries.begin(); it != entries.end(); it++) {
|
|
it->second.write(f);
|
|
}
|
|
}
|
|
|
|
void DirTree::getChanges(DirTree *snapshot, EventList &events) {
|
|
std::lock_guard<std::mutex> lock(mDirCacheMutex());
|
|
std::lock_guard<std::mutex> snapshotLock(snapshot->mMutex);
|
|
|
|
for (auto it = entries.begin(); it != entries.end(); it++) {
|
|
auto found = snapshot->entries.find(it->first);
|
|
if (found == snapshot->entries.end()) {
|
|
events.create(it->second.path);
|
|
} else if (found->second.mtime != it->second.mtime && !found->second.isDir && !it->second.isDir) {
|
|
events.update(it->second.path);
|
|
}
|
|
}
|
|
|
|
for (auto it = snapshot->entries.begin(); it != snapshot->entries.end(); it++) {
|
|
size_t count = entries.count(it->first);
|
|
if (count == 0) {
|
|
events.remove(it->second.path);
|
|
}
|
|
}
|
|
}
|
|
|
|
DirEntry::DirEntry(std::string p, uint64_t t, bool d) {
|
|
path = p;
|
|
mtime = t;
|
|
isDir = d;
|
|
state = NULL;
|
|
}
|
|
|
|
DirEntry::DirEntry(FILE *f) {
|
|
size_t size;
|
|
if (fscanf(f, "%zu", &size)) {
|
|
path.resize(size);
|
|
if (fread(&path[0], sizeof(char), size, f)) {
|
|
int d = 0;
|
|
fscanf(f, "%" PRIu64 " %d\n", &mtime, &d);
|
|
isDir = d == 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DirEntry::write(FILE *f) const {
|
|
fprintf(f, "%zu%s%" PRIu64 " %d\n", path.size(), path.c_str(), mtime, isDir);
|
|
}
|