1<?php 2 3function check_skip_any() { 4 if (dba_handlers() === []) { 5 die('skip no handlers installed'); 6 } 7 if (dba_handlers() === ['cdb']) { 8 die('skip only cdb installed which is not suitable'); 9 } 10 if (dba_handlers() === ['cdb_make']) { 11 die('skip only cdb_make installed which is not suitable'); 12 } 13} 14 15function check_skip(string $handler) { 16 $handlers = dba_handlers(); 17 if ($handlers === []) { 18 die('skip no handlers installed'); 19 } 20 if (!in_array($handler, $handlers)) { 21 $HND = strtoupper($handler); 22 die("skip $HND handler not available"); 23 } 24} 25 26function get_any_handler(): string { 27 foreach (dba_handlers() as $handler) { 28 // Those are weird 29 if ($handler !== 'cdb' && $handler !== 'cdb_make' && $handler !== 'inifile') { 30 echo 'Using handler: "', $handler, '"', \PHP_EOL; 31 return $handler; 32 } 33 } 34 return 'should_not_happen'; 35} 36function get_any_db(string $name) { 37 return dba_open($name, 'c', get_any_handler()); 38} 39 40enum LockFlag: string { 41 case FileLock = 'l'; 42 case DbLock = 'd'; 43 case NoLock = '-'; 44} 45 46function set_up_db_ex(string $handler, string $name, LockFlag $lock, bool $persistent = false) { 47 $lock_flag = $lock->value; 48 // Open file in creation/truncation mode 49 $func = $persistent ? 'dba_popen' : 'dba_open'; 50 51 $db_file = $func($name, 'n'.$lock_flag, $handler); 52 53 if ($db_file === false) { 54 die("Failed to create DB"); 55 } 56 57 // Insert some data 58 dba_insert("key1", "Content String 1", $db_file); 59 dba_insert("key2", "Content String 2", $db_file); 60 dba_insert("key3", "Third Content String", $db_file); 61 dba_insert("key4", "Another Content String", $db_file); 62 dba_insert("key5", "The last content string", $db_file); 63 64 // Insert date with array keys 65 dba_insert(["", "name9"], "Content String 9", $db_file); 66 dba_insert(["key10", "name10"] , "Content String 10", $db_file); 67 dba_insert("[key30]name30", "Content String 30", $db_file); 68 69 return $db_file; 70} 71 72function set_up_db(string $handler, string $name, LockFlag $lock = LockFlag::FileLock): void { 73 $db_file = set_up_db_ex($handler, $name, $lock); 74 // Close creation/truncation handler 75 dba_close($db_file); 76} 77 78function run_common_read_only_test($dbHandle): void { 79 $key = dba_firstkey($dbHandle); 80 $result = []; 81 while ($key) { 82 $result[$key] = dba_fetch($key, $dbHandle); 83 $key = dba_nextkey($dbHandle); 84 } 85 ksort($result); 86 var_dump($result); 87} 88 89function run_standard_tests_ex(string $handler, string $name, LockFlag $lock, bool $persistent = false): void 90{ 91 $lock_flag = $lock->value; 92 set_up_db($handler, $name, $lock); 93 $db_writer = dba_open($name, 'w'.$lock_flag, $handler); 94 if ($db_writer === false) { 95 die("Failed to open DB for write"); 96 } 97 98 echo 'Remove key 1 and 3', \PHP_EOL; 99 var_dump(dba_delete("key3", $db_writer)); 100 var_dump(dba_delete("key1", $db_writer)); 101 102 echo 'Try to remove key 1 again', \PHP_EOL; 103 var_dump(dba_delete("key1", $db_writer)); 104 105 // Fetch and sort data. We sort to guarantee that the output is 106 // consistent across invocations and architectures. When iterating 107 // with firstkey() and nextkey(), several engines (GDBM, LMDB, 108 // QDBM) make no promise about the iteration order. Others (TCADB, 109 // DBM) explicitly state that the order is arbitrary. With GDBM at 110 // least, the order appears platform-dependent -- we have a report 111 // in Github issue 14786. GDBM's own test suite sorts this output, 112 // suggesting that sorting is a reasonable workaround for the issue. 113 $output = []; 114 115 $key = dba_firstkey($db_writer); 116 $total_keys = 0; 117 while ($key) { 118 $output[] = $key . ': ' . dba_fetch($key, $db_writer) . \PHP_EOL; 119 $key = dba_nextkey($db_writer); 120 $total_keys++; 121 } 122 123 sort($output, SORT_STRING); 124 foreach ($output as $line) { 125 echo $line; 126 } 127 128 echo 'Total keys: ', $total_keys, \PHP_EOL; 129 for ($i = 1; $i < 6; $i++) { 130 echo "Key $i exists? ", dba_exists("key$i", $db_writer) ? 'Y' : 'N', \PHP_EOL; 131 } 132 133 echo 'Replace second key data', \PHP_EOL; 134 var_dump(dba_replace('key2', 'Content 2 replaced', $db_writer)); 135 echo dba_fetch('key2', $db_writer), \PHP_EOL; 136 137 // Check that read is possible when a lock is used 138 $test_flag = 't'; 139 if ($lock === LockFlag::NoLock) { 140 // No point testing when we don't use locks 141 $test_flag = ''; 142 } 143 $db_reader = @dba_open($name, 'r'.$lock_flag.$test_flag, $handler); 144 if ($db_reader === false) { 145 echo 'Read during write: not allowed', \PHP_EOL; 146 } else { 147 echo 'Read during write: allowed', \PHP_EOL; 148 dba_close($db_reader); 149 } 150 151 if (dba_insert('key number 6', 'The 6th value', $db_writer)) { 152 echo 'Expected: Added a new data entry', \PHP_EOL; 153 } else { 154 echo 'Unexpected: Failed to add a new data entry', \PHP_EOL; 155 } 156 157 if (dba_insert('key number 6', 'The 6th value inserted again would be an error', $db_writer)) { 158 echo 'Unexpected: Wrote data to already used key', \PHP_EOL; 159 } else { 160 echo 'Expected: Failed to insert data for already used key', \PHP_EOL; 161 } 162 163 echo 'Replace second key data', \PHP_EOL; 164 var_dump(dba_replace('key2', 'Content 2 replaced 2nd time', $db_writer)); 165 echo 'Delete "key4"', \PHP_EOL; 166 var_dump(dba_delete('key4', $db_writer)); 167 echo 'Fetch "key2": ', dba_fetch('key2', $db_writer), \PHP_EOL; 168 echo 'Fetch "key number 6": ', dba_fetch('key number 6', $db_writer), \PHP_EOL; 169 dba_close($db_writer); // when the writer is open at least db3 would fail because of buffered io. 170 171 $db_reader = dba_open($name, 'r'.$lock_flag, $handler); 172 run_common_read_only_test($db_reader); 173 dba_close($db_reader); 174 175 /* TODO popen test? Old code copied from the previous general test 176 if (($db_file = dba_popen($db_filename, 'r'.($lock_flag==''?'':'-'), $handler))!==FALSE) { 177 if ($handler == 'dbm' || $handler == "tcadb") { 178 dba_close($db_file); 179 } 180 } 181 */ 182} 183 184const MODES = ['r', 'w', 'c', 'n']; 185const LOCKS = ['l', 'd', '-', '' /* No lock flag is like 'd' */]; 186function run_creation_tests_ex(string $handler, string $file_suffix, string $pre_req): void 187{ 188 $db_name = $handler . $file_suffix; 189 foreach (MODES as $mode) { 190 foreach (LOCKS as $lock) { 191 eval($pre_req); 192 $arg = $mode.$lock; 193 echo 'Mode parameter is "', $arg, '":', \PHP_EOL; 194 $db = dba_open($db_name, $arg, $handler); 195 if ($db !== false) { 196 assert(file_exists($db_name)); 197 $status = dba_insert("key1", "This is a test insert", $db); 198 if ($status) { 199 $fetch = dba_fetch("key1", $db); 200 if ($fetch === false) { 201 echo 'Cannot fetch insertion', \PHP_EOL; 202 } else { 203 echo $fetch, \PHP_EOL; 204 } 205 } else { 206 echo 'Insertion failed', \PHP_EOL; 207 } 208 dba_close($db); 209 } else { 210 echo 'Opening DB failed', \PHP_EOL; 211 } 212 cleanup_standard_db($db_name); 213 } 214 } 215} 216 217function run_creation_tests(string $handler): void 218{ 219 $extension = $handler === 'tcadb' ? 'tch' : 'db'; 220 /* Trying to open a non-existing file */ 221 echo '=== OPENING NON-EXISTING FILE ===', \PHP_EOL; 222 run_creation_tests_ex($handler, '_not_existing.'.$extension, ''); 223 224 /* Trying to open an existing db file */ 225 echo '=== OPENING EXISTING DB FILE ===', \PHP_EOL; 226 run_creation_tests_ex($handler, '_existing.'.$extension, 'dba_open($db_name, "n", $handler);'); 227 228 /* Trying to open an existing random file */ 229 echo '=== OPENING EXISTING RANDOM FILE ===', \PHP_EOL; 230 run_creation_tests_ex($handler, '_random.txt', 'file_put_contents($db_name, "Dummy contents");'); 231} 232 233function clean_creation_tests(string $handler): void { 234 $db_name = $handler . '_not_existing.db'; 235 cleanup_standard_db($db_name); 236 $db_name = $handler . '_existing.db'; 237 cleanup_standard_db($db_name); 238 $db_name = $handler . '_random.txt'; 239 cleanup_standard_db($db_name); 240} 241 242function run_standard_tests(string $handler, string $name): void { 243 echo '=== RUNNING WITH FILE LOCK ===', \PHP_EOL; 244 ob_start(); 245 set_up_db($handler, $name, LockFlag::FileLock); 246 run_standard_tests_ex($handler, $name, LockFlag::FileLock); 247 cleanup_standard_db($name); 248 $run1_output = ob_get_flush(); 249 echo '=== RUNNING WITH DB LOCK (default) ===', \PHP_EOL; 250 ob_start(); 251 set_up_db($handler, $name, LockFlag::DbLock); 252 run_standard_tests_ex($handler, $name, LockFlag::DbLock); 253 cleanup_standard_db($name); 254 $run2_output = ob_get_clean(); 255 if ($run1_output === $run2_output) { 256 echo 'SAME OUTPUT AS PREVIOUS RUN', \PHP_EOL; 257 } else { 258 echo $run2_output; 259 } 260 261 echo '=== RUNNING WITH NO LOCK ===', \PHP_EOL; 262 ob_start(); 263 set_up_db($handler, $name, LockFlag::NoLock); 264 run_standard_tests_ex($handler, $name, LockFlag::NoLock); 265 $run3_output = ob_get_clean(); 266 if ($run2_output === $run3_output) { 267 echo 'SAME OUTPUT AS PREVIOUS RUN', \PHP_EOL; 268 } else if ($run2_output === str_replace( // If only the fact that the lock prevented reads 269 'Read during write: allowed', 270 'Read during write: not allowed', 271 $run3_output 272 ) 273 ) { 274 echo 'SAME OUTPUT AS PREVIOUS RUN (modulo read during write due to no lock)', \PHP_EOL; 275 } else { 276 echo $run3_output; 277 } 278} 279 280// TODO Array keys insertion 281// TODO Run all lock flags 282function set_up_cdb_db_and_run(string $name): void { 283 set_up_db('cdb', $name); 284 285 $db_file = dba_open($name, 'rl', 'cdb'); 286 if ($db_file === false) { 287 die("Failed to reopen DB"); 288 } 289 for ($i = 1; $i < 6; $i++) { 290 echo "Key $i exists? ", dba_exists("key$i", $db_file) ? 'Y' : 'N', \PHP_EOL; 291 } 292 run_common_read_only_test($db_file); 293 dba_close($db_file); 294 295 echo '--NO-LOCK--', \PHP_EOL; 296 cleanup_standard_db($name); 297 set_up_db('cdb', $name, LockFlag::NoLock); 298 $db_file = dba_open($name, 'r-', 'cdb'); 299 if ($db_file === false) { 300 die("Failed to reopen DB"); 301 } 302 for ($i = 1; $i < 6; $i++) { 303 echo "Key $i exists? ", dba_exists("key$i", $db_file) ? 'Y' : 'N', \PHP_EOL; 304 } 305 run_common_read_only_test($db_file); 306} 307 308function cleanup_standard_db(string $name): void { 309 @unlink($name); 310 @unlink($name.'.lck'); 311 @unlink($name.'-lock'); 312} 313