MDBX Containers
Loading...
Searching...
No Matches
path_resolution_test.cpp
Go to the documentation of this file.
1// file: tests/path_resolution_test.cpp
2#if __cplusplus < 201703L
3#include <iostream>
4int main() {
5 std::cout << "path_resolution_test skipped for C++11" << std::endl;
6 return 0;
7}
8#else
9// build (пример):
10// g++ -std=gnu++17 -O2 -Wall -Wextra -Iinclude \
11// tests/path_resolution_test.cpp -o path_resolution_test \
12// -lmd b x (или нужные вам библиотеки)
13//
14// Тестирует политику разрешения путей и работу no_subdir.
15
16#include <cassert>
17#include <cstdint>
18#include <iostream>
19#include <fstream>
20#include <string>
21#include <vector>
22#include <chrono>
23#include <random>
24#include <filesystem>
25
26#ifdef _WIN32
27 #include <windows.h>
28#else
29 #include <unistd.h>
30 #ifdef __APPLE__
31 #include <mach-o/dyld.h>
32 #endif
33#endif
34
36
37// ----------------------- helpers -----------------------
38namespace fs = std::filesystem;
39
40static std::string exe_dir() {
41#ifdef _WIN32
42 std::vector<wchar_t> buf(32768);
43 DWORD n = GetModuleFileNameW(nullptr, buf.data(), static_cast<DWORD>(buf.size()));
44 if (n == 0 || n == buf.size()) throw std::runtime_error("GetModuleFileNameW failed");
45 fs::path p(buf.data(), buf.data() + n);
46 return p.parent_path().u8string();
47#elif defined(__APPLE__)
48 uint32_t size = 0;
49 _NSGetExecutablePath(nullptr, &size);
50 std::vector<char> path(size);
51 if (_NSGetExecutablePath(path.data(), &size) != 0) throw std::runtime_error("_NSGetExecutablePath failed");
52 fs::path p = fs::weakly_canonical(fs::path(path.data()));
53 return p.parent_path().u8string();
54#else
55 char path[4096];
56 ssize_t n = readlink("/proc/self/exe", path, sizeof(path)-1);
57 if (n <= 0) throw std::runtime_error("readlink /proc/self/exe failed");
58 path[n] = '\0';
59 fs::path p = fs::weakly_canonical(fs::path(path));
60 return p.parent_path().u8string();
61#endif
62}
63
64static std::string uniq_suffix() {
65 auto now = std::chrono::high_resolution_clock::now().time_since_epoch().count();
66 std::mt19937_64 rng(static_cast<uint64_t>(now));
67 std::uniform_int_distribution<uint64_t> dist;
68 uint64_t x = dist(rng);
69 char buf[32];
70 std::snprintf(buf, sizeof(buf), "%016llx", static_cast<unsigned long long>(x));
71 return std::string(buf);
72}
73
74static bool dir_nonempty(const fs::path& p) {
75 if (!fs::exists(p) || !fs::is_directory(p)) return false;
76 return fs::directory_iterator(p) != fs::directory_iterator{};
77}
78
79// Правило разрешения пути, которое должна соблюдать библиотека.
80static fs::path expected_path_from_policy(const std::string& pathname,
81 bool relative_to_exe,
82 const fs::path& exeDir,
83 const fs::path& cwd) {
84 auto explicitly_rel = [](const std::string& s){
85 auto sw = [&](const char* p){ return s.rfind(p, 0) == 0; };
86 return sw("./") || sw("../") || sw(".\\") || sw("..\\");
87 };
88 fs::path p = fs::u8path(pathname);
89 if (p.is_absolute()) {
90 // как есть
91 } else if (explicitly_rel(pathname)) {
92 p = cwd / p;
93 } else if (relative_to_exe) {
94 p = exeDir / p;
95 } else {
96 p = cwd / p;
97 }
98#if __cplusplus >= 202002L
99 p = fs::weakly_canonical(p);
100#else
101 p = p.lexically_normal();
102#endif
103 return p;
104}
105
106template <typename DoWriteVerify>
107static void run_case(const std::string& case_name,
108 const std::string& raw_pathname,
109 bool relative_to_exe,
110 bool no_subdir,
111 const fs::path& exeDir,
112 const fs::path& cwd,
113 DoWriteVerify&& doWriteVerify) {
114 // ожидаемое место БД
115 fs::path expect = expected_path_from_policy(raw_pathname, relative_to_exe, exeDir, cwd);
116
117 // сконфигурировать и создать соединение
118 mdbxc::Config cfg;
119 cfg.pathname = raw_pathname;
120 cfg.max_dbs = 14;
121 cfg.no_subdir = no_subdir;
122 cfg.relative_to_exe = relative_to_exe;
123 cfg.relative_to_exe = relative_to_exe;
124
125 auto conn = mdbxc::Connection::create(cfg);
126
127 // реальное использование БД: таблица и запись/чтение
128 {
129 // имя таблицы тоже уникализируем
130 std::string tbl = "kv_i8_i8_" + case_name + "_" + uniq_suffix();
132 doWriteVerify(kv); // пользовательская запись и проверка
133 }
134
135 // проверки на ФС
136 if (!no_subdir) {
137 // директория БД должна существовать и быть не пустой
138 assert(fs::exists(expect));
139 assert(fs::is_directory(expect));
140 assert(dir_nonempty(expect));
141 } else {
142 // файл БД должен существовать (lock-файл может быть рядом)
143 assert(fs::exists(expect));
144 assert(fs::is_regular_file(expect));
145 }
146}
147
148// ----------------------- main -----------------------
149int main() try {
150 // 1) exeDir (фиксированный), 2) staging CWD (меняем на уникальную временную)
151 const fs::path exeDir = fs::path(exe_dir());
152 const fs::path baseTmp = fs::temp_directory_path() / ("mdbxc_path_tests_" + uniq_suffix());
153 fs::create_directories(baseTmp);
154
155 // Сделаем CWD гарантированно другим, чем exeDir
156 const fs::path cwdA = baseTmp / "cwdA";
157 const fs::path cwdB = baseTmp / "cwdB";
158 fs::create_directories(cwdA);
159 fs::create_directories(cwdB);
160
161 // ---- Группа 1: no_subdir = false ----
162 fs::current_path(cwdA);
163
164 // 1.1 relative_to_exe = false → CWD
165 run_case("dir_rel_cwd",
166 "data/db_dir_cwd_" + uniq_suffix(),
167 /*relative_to_exe=*/false,
168 /*no_subdir=*/false,
169 exeDir, fs::current_path(),
170 [](auto& kv){
171 kv.insert_or_assign(1, (int8_t)42);
172#if __cplusplus >= 201703L
173 auto v = kv.find(1);
174 assert(v && *v == (int8_t)42);
175#else
176 auto v = kv.find_compat(1);
177 assert(v.first && v.second == (int8_t)42);
178#endif
179 });
180
181 // 1.2 relative_to_exe = true → exeDir
182 run_case("dir_rel_exe",
183 "data/db_dir_exe_" + uniq_suffix(),
184 /*relative_to_exe=*/true,
185 /*no_subdir=*/false,
186 exeDir, fs::current_path(),
187 [](auto& kv){
188 kv.insert_or_assign(1, (int8_t)7);
189#if __cplusplus >= 201703L
190 auto v = kv.find(1);
191 assert(v && *v == (int8_t)7);
192#else
193 auto v = kv.find_compat(1);
194 assert(v.first && v.second == (int8_t)7);
195#endif
196 });
197
198 // 1.3 Явно относительный "./..." → игнорируем relative_to_exe, берём CWD
199 run_case("dir_explicit_cwd",
200 "./data/db_dir_explicit_" + uniq_suffix(),
201 /*relative_to_exe=*/true, // флаг выставлен, но должен быть проигнорирован
202 /*no_subdir=*/false,
203 exeDir, fs::current_path(),
204 [](auto& kv){
205 kv.insert_or_assign(1, (int8_t)-5);
206#if __cplusplus >= 201703L
207 auto v = kv.find(1);
208 assert(v && *v == (int8_t)-5);
209#else
210 auto v = kv.find_compat(1);
211 assert(v.first && v.second == (int8_t)-5);
212#endif
213 });
214
215 // ---- Группа 2: no_subdir = true (файловый режим) ----
216 fs::current_path(cwdB);
217
218 // 2.1 relative_to_exe = false → CWD
219 run_case("file_rel_cwd",
220 "data/db_file_cwd_" + uniq_suffix(),
221 /*relative_to_exe=*/false,
222 /*no_subdir=*/true,
223 exeDir, fs::current_path(),
224 [](auto& kv){
225 kv.insert_or_assign(1, (int8_t)11);
226#if __cplusplus >= 201703L
227 auto v = kv.find(1);
228 assert(v && *v == (int8_t)11);
229#else
230 auto v = kv.find_compat(1);
231 assert(v.first && v.second == (int8_t)11);
232#endif
233 });
234
235 // 2.2 relative_to_exe = true → exeDir
236 run_case("file_rel_exe",
237 "data/db_file_exe_" + uniq_suffix(),
238 /*relative_to_exe=*/true,
239 /*no_subdir=*/true,
240 exeDir, fs::current_path(),
241 [](auto& kv){
242 kv.insert_or_assign(1, (int8_t)12);
243#if __cplusplus >= 201703L
244 auto v = kv.find(1);
245 assert(v && *v == (int8_t)12);
246#else
247 auto v = kv.find_compat(1);
248 assert(v.first && v.second == (int8_t)12);
249#endif
250 });
251
252 // 2.3 Явно относительный "./..." → CWD
253 run_case("file_explicit_cwd",
254 "./data/db_file_explicit_" + uniq_suffix(),
255 /*relative_to_exe=*/true, // игнорируем из-за "./"
256 /*no_subdir=*/true,
257 exeDir, fs::current_path(),
258 [](auto& kv){
259 kv.insert_or_assign(1, (int8_t)13);
260#if __cplusplus >= 201703L
261 auto v = kv.find(1);
262 assert(v && *v == (int8_t)13);
263#else
264 auto v = kv.find_compat(1);
265 assert(v.first && v.second == (int8_t)13);
266#endif
267 });
268
269 std::cout << "[result] path resolution tests passed\n";
270 return 0;
271}
272catch (const std::exception& e) {
273 std::cerr << "[error] " << e.what() << "\n";
274 return 1;
275}
276#endif // __cplusplus < 201703L
Declaration of the KeyValueTable class for managing key-value pairs in an MDBX database.
Parameters used by Connection to create the MDBX environment.
Definition Config.hpp:17
bool no_subdir
Whether to store the database in a single file instead of a directory.
Definition Config.hpp:30
std::string pathname
Path to the database file or directory containing the database.
Definition Config.hpp:19
bool relative_to_exe
Whether to resolve a relative path relative to the executable directory.
Definition Config.hpp:33
int64_t max_dbs
Maximum number of named databases (DBI) in the environment.
Definition Config.hpp:27
static std::shared_ptr< Connection > create(const Config &config)
Creates and connects a new shared Connection instance.
Template class for managing key-value pairs in an MDBX database.
int main()