LmdbDbmTest.vala 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. /**
  2. * LmdbDbmTest - Unit tests for LmdbDbm storage
  3. */
  4. using Implexus.Storage;
  5. public static int main(string[] args) {
  6. int passed = 0;
  7. int failed = 0;
  8. // Test 1: Basic set and get
  9. if (test_basic_set_get()) {
  10. passed++;
  11. stdout.puts("PASS: test_basic_set_get\n");
  12. } else {
  13. failed++;
  14. stdout.puts("FAIL: test_basic_set_get\n");
  15. }
  16. // Test 2: Get non-existent key
  17. if (test_get_nonexistent()) {
  18. passed++;
  19. stdout.puts("PASS: test_get_nonexistent\n");
  20. } else {
  21. failed++;
  22. stdout.puts("FAIL: test_get_nonexistent\n");
  23. }
  24. // Test 3: Delete key
  25. if (test_delete_key()) {
  26. passed++;
  27. stdout.puts("PASS: test_delete_key\n");
  28. } else {
  29. failed++;
  30. stdout.puts("FAIL: test_delete_key\n");
  31. }
  32. // Test 4: Has key
  33. if (test_has_key()) {
  34. passed++;
  35. stdout.puts("PASS: test_has_key\n");
  36. } else {
  37. failed++;
  38. stdout.puts("FAIL: test_has_key\n");
  39. }
  40. // Test 5: Keys iteration
  41. if (test_keys_iteration()) {
  42. passed++;
  43. stdout.puts("PASS: test_keys_iteration\n");
  44. } else {
  45. failed++;
  46. stdout.puts("FAIL: test_keys_iteration\n");
  47. }
  48. // Test 6: Transaction commit
  49. if (test_transaction_commit()) {
  50. passed++;
  51. stdout.puts("PASS: test_transaction_commit\n");
  52. } else {
  53. failed++;
  54. stdout.puts("FAIL: test_transaction_commit\n");
  55. }
  56. // Test 7: Transaction rollback
  57. if (test_transaction_rollback()) {
  58. passed++;
  59. stdout.puts("PASS: test_transaction_rollback\n");
  60. } else {
  61. failed++;
  62. stdout.puts("FAIL: test_transaction_rollback\n");
  63. }
  64. // Test 8: Transaction with delete
  65. if (test_transaction_delete()) {
  66. passed++;
  67. stdout.puts("PASS: test_transaction_delete\n");
  68. } else {
  69. failed++;
  70. stdout.puts("FAIL: test_transaction_delete\n");
  71. }
  72. // Test 9: Overwrite value
  73. if (test_overwrite_value()) {
  74. passed++;
  75. stdout.puts("PASS: test_overwrite_value\n");
  76. } else {
  77. failed++;
  78. stdout.puts("FAIL: test_overwrite_value\n");
  79. }
  80. // Test 10: Special key names
  81. if (test_special_key_names()) {
  82. passed++;
  83. stdout.puts("PASS: test_special_key_names\n");
  84. } else {
  85. failed++;
  86. stdout.puts("FAIL: test_special_key_names\n");
  87. }
  88. // Test 11: Binary data
  89. if (test_binary_data()) {
  90. passed++;
  91. stdout.puts("PASS: test_binary_data\n");
  92. } else {
  93. failed++;
  94. stdout.puts("FAIL: test_binary_data\n");
  95. }
  96. // Test 12: Empty key
  97. if (test_empty_key()) {
  98. passed++;
  99. stdout.puts("PASS: test_empty_key\n");
  100. } else {
  101. failed++;
  102. stdout.puts("FAIL: test_empty_key\n");
  103. }
  104. // Test 13: Large value
  105. if (test_large_value()) {
  106. passed++;
  107. stdout.puts("PASS: test_large_value\n");
  108. } else {
  109. failed++;
  110. stdout.puts("FAIL: test_large_value\n");
  111. }
  112. // Test 14: Multiple operations
  113. if (test_multiple_operations()) {
  114. passed++;
  115. stdout.puts("PASS: test_multiple_operations\n");
  116. } else {
  117. failed++;
  118. stdout.puts("FAIL: test_multiple_operations\n");
  119. }
  120. // Test 15: Persistence
  121. if (test_persistence()) {
  122. passed++;
  123. stdout.puts("PASS: test_persistence\n");
  124. } else {
  125. failed++;
  126. stdout.puts("FAIL: test_persistence\n");
  127. }
  128. // Test 16: Open and close
  129. if (test_open_close()) {
  130. passed++;
  131. stdout.puts("PASS: test_open_close\n");
  132. } else {
  133. failed++;
  134. stdout.puts("FAIL: test_open_close\n");
  135. }
  136. stdout.printf("\nResults: %d passed, %d failed\n", passed, failed);
  137. return failed > 0 ? 1 : 0;
  138. }
  139. // Helper to create temporary database path (LMDB uses a directory)
  140. string create_temp_db_path() {
  141. string temp_dir = DirUtils.mkdtemp("implexus_lmdb_test_XXXXXX");
  142. return temp_dir;
  143. }
  144. // Helper to get directory from db path
  145. string get_dir_from_path(string db_path) {
  146. return db_path;
  147. }
  148. // Helper to create BinaryData from string (includes null terminator)
  149. Invercargill.BinaryData string_to_binary(string str) {
  150. // Include null terminator for proper string reconstruction when reading back
  151. uint8[] data = new uint8[str.length + 1];
  152. Memory.copy(data, str, str.length);
  153. data[str.length] = 0; // null terminator
  154. return new Invercargill.DataStructures.ByteBuffer.from_byte_array(data);
  155. }
  156. // Helper to convert BinaryData to string
  157. string binary_to_string(Invercargill.BinaryData data) {
  158. var bytes = data.to_bytes();
  159. return (string) bytes.get_data();
  160. }
  161. // Test 1: Basic set and get
  162. bool test_basic_set_get() {
  163. string db_path = create_temp_db_path();
  164. string temp_dir = get_dir_from_path(db_path);
  165. try {
  166. var dbm = new LmdbDbm();
  167. dbm.open(db_path, false);
  168. dbm.set("test_key", string_to_binary("test_value"));
  169. var value = dbm.get("test_key");
  170. if (value == null) {
  171. dbm.close();
  172. cleanup_dir(temp_dir);
  173. return false;
  174. }
  175. if (binary_to_string((!) value) != "test_value") {
  176. dbm.close();
  177. cleanup_dir(temp_dir);
  178. return false;
  179. }
  180. dbm.close();
  181. cleanup_dir(temp_dir);
  182. return true;
  183. } catch (Error e) {
  184. message("Error: %s", e.message);
  185. cleanup_dir(temp_dir);
  186. return false;
  187. }
  188. }
  189. // Test 2: Get non-existent key
  190. bool test_get_nonexistent() {
  191. string db_path = create_temp_db_path();
  192. string temp_dir = get_dir_from_path(db_path);
  193. try {
  194. var dbm = new LmdbDbm();
  195. dbm.open(db_path, false);
  196. var value = dbm.get("nonexistent");
  197. dbm.close();
  198. cleanup_dir(temp_dir);
  199. return value == null;
  200. } catch (Error e) {
  201. cleanup_dir(temp_dir);
  202. return false;
  203. }
  204. }
  205. // Test 3: Delete key
  206. bool test_delete_key() {
  207. string db_path = create_temp_db_path();
  208. string temp_dir = get_dir_from_path(db_path);
  209. try {
  210. var dbm = new LmdbDbm();
  211. dbm.open(db_path, false);
  212. dbm.set("key1", string_to_binary("value1"));
  213. if (!dbm.has_key("key1")) {
  214. dbm.close();
  215. cleanup_dir(temp_dir);
  216. return false;
  217. }
  218. dbm.delete("key1");
  219. if (dbm.has_key("key1")) {
  220. dbm.close();
  221. cleanup_dir(temp_dir);
  222. return false;
  223. }
  224. dbm.close();
  225. cleanup_dir(temp_dir);
  226. return true;
  227. } catch (Error e) {
  228. cleanup_dir(temp_dir);
  229. return false;
  230. }
  231. }
  232. // Test 4: Has key
  233. bool test_has_key() {
  234. string db_path = create_temp_db_path();
  235. string temp_dir = get_dir_from_path(db_path);
  236. try {
  237. var dbm = new LmdbDbm();
  238. dbm.open(db_path, false);
  239. if (dbm.has_key("key1")) {
  240. dbm.close();
  241. cleanup_dir(temp_dir);
  242. return false; // Should not exist
  243. }
  244. dbm.set("key1", string_to_binary("value1"));
  245. if (!dbm.has_key("key1")) {
  246. dbm.close();
  247. cleanup_dir(temp_dir);
  248. return false; // Should exist
  249. }
  250. if (dbm.has_key("key2")) {
  251. dbm.close();
  252. cleanup_dir(temp_dir);
  253. return false; // Should not exist
  254. }
  255. dbm.close();
  256. cleanup_dir(temp_dir);
  257. return true;
  258. } catch (Error e) {
  259. cleanup_dir(temp_dir);
  260. return false;
  261. }
  262. }
  263. // Test 5: Keys iteration
  264. bool test_keys_iteration() {
  265. string db_path = create_temp_db_path();
  266. string temp_dir = get_dir_from_path(db_path);
  267. try {
  268. var dbm = new LmdbDbm();
  269. dbm.open(db_path, false);
  270. dbm.set("key1", string_to_binary("value1"));
  271. dbm.set("key2", string_to_binary("value2"));
  272. dbm.set("key3", string_to_binary("value3"));
  273. var keys = dbm.keys;
  274. if (keys.count() != 3) {
  275. dbm.close();
  276. cleanup_dir(temp_dir);
  277. return false;
  278. }
  279. // Check all keys are present
  280. var key_set = new Invercargill.DataStructures.HashSet<string>();
  281. foreach (var key in keys) {
  282. key_set.add(key);
  283. }
  284. if (!key_set.has("key1")) {
  285. dbm.close();
  286. cleanup_dir(temp_dir);
  287. return false;
  288. }
  289. if (!key_set.has("key2")) {
  290. dbm.close();
  291. cleanup_dir(temp_dir);
  292. return false;
  293. }
  294. if (!key_set.has("key3")) {
  295. dbm.close();
  296. cleanup_dir(temp_dir);
  297. return false;
  298. }
  299. dbm.close();
  300. cleanup_dir(temp_dir);
  301. return true;
  302. } catch (Error e) {
  303. cleanup_dir(temp_dir);
  304. return false;
  305. }
  306. }
  307. // Test 6: Transaction commit
  308. bool test_transaction_commit() {
  309. string db_path = create_temp_db_path();
  310. string temp_dir = get_dir_from_path(db_path);
  311. try {
  312. var dbm = new LmdbDbm();
  313. dbm.open(db_path, false);
  314. dbm.set("key1", string_to_binary("value1"));
  315. dbm.begin_transaction();
  316. dbm.set("key2", string_to_binary("value2"));
  317. dbm.set("key3", string_to_binary("value3"));
  318. // Before commit, values should be in transaction buffer
  319. var value2 = dbm.get("key2");
  320. if (value2 == null) {
  321. dbm.close();
  322. cleanup_dir(temp_dir);
  323. return false;
  324. }
  325. dbm.commit_transaction();
  326. // After commit, values should be persisted
  327. if (!dbm.has_key("key1")) {
  328. dbm.close();
  329. cleanup_dir(temp_dir);
  330. return false;
  331. }
  332. if (!dbm.has_key("key2")) {
  333. dbm.close();
  334. cleanup_dir(temp_dir);
  335. return false;
  336. }
  337. if (!dbm.has_key("key3")) {
  338. dbm.close();
  339. cleanup_dir(temp_dir);
  340. return false;
  341. }
  342. dbm.close();
  343. cleanup_dir(temp_dir);
  344. return true;
  345. } catch (Error e) {
  346. cleanup_dir(temp_dir);
  347. return false;
  348. }
  349. }
  350. // Test 7: Transaction rollback
  351. bool test_transaction_rollback() {
  352. string db_path = create_temp_db_path();
  353. string temp_dir = get_dir_from_path(db_path);
  354. try {
  355. var dbm = new LmdbDbm();
  356. dbm.open(db_path, false);
  357. dbm.set("key1", string_to_binary("value1"));
  358. dbm.begin_transaction();
  359. dbm.set("key2", string_to_binary("value2"));
  360. // Value should be visible in transaction
  361. if (!dbm.has_key("key2")) {
  362. dbm.close();
  363. cleanup_dir(temp_dir);
  364. return false;
  365. }
  366. dbm.rollback_transaction();
  367. // After rollback, key2 should not exist
  368. if (dbm.has_key("key2")) {
  369. dbm.close();
  370. cleanup_dir(temp_dir);
  371. return false;
  372. }
  373. // key1 should still exist
  374. if (!dbm.has_key("key1")) {
  375. dbm.close();
  376. cleanup_dir(temp_dir);
  377. return false;
  378. }
  379. dbm.close();
  380. cleanup_dir(temp_dir);
  381. return true;
  382. } catch (Error e) {
  383. cleanup_dir(temp_dir);
  384. return false;
  385. }
  386. }
  387. // Test 8: Transaction with delete
  388. bool test_transaction_delete() {
  389. string db_path = create_temp_db_path();
  390. string temp_dir = get_dir_from_path(db_path);
  391. try {
  392. var dbm = new LmdbDbm();
  393. dbm.open(db_path, false);
  394. dbm.set("key1", string_to_binary("value1"));
  395. dbm.set("key2", string_to_binary("value2"));
  396. dbm.begin_transaction();
  397. dbm.delete("key1");
  398. // In transaction, key1 should not be visible
  399. if (dbm.has_key("key1")) {
  400. dbm.close();
  401. cleanup_dir(temp_dir);
  402. return false;
  403. }
  404. dbm.commit_transaction();
  405. // After commit, key1 should be deleted
  406. if (dbm.has_key("key1")) {
  407. dbm.close();
  408. cleanup_dir(temp_dir);
  409. return false;
  410. }
  411. // key2 should still exist
  412. if (!dbm.has_key("key2")) {
  413. dbm.close();
  414. cleanup_dir(temp_dir);
  415. return false;
  416. }
  417. dbm.close();
  418. cleanup_dir(temp_dir);
  419. return true;
  420. } catch (Error e) {
  421. cleanup_dir(temp_dir);
  422. return false;
  423. }
  424. }
  425. // Test 9: Overwrite value
  426. bool test_overwrite_value() {
  427. string db_path = create_temp_db_path();
  428. string temp_dir = get_dir_from_path(db_path);
  429. try {
  430. var dbm = new LmdbDbm();
  431. dbm.open(db_path, false);
  432. dbm.set("key1", string_to_binary("value1"));
  433. var value = dbm.get("key1");
  434. if (value == null || binary_to_string((!) value) != "value1") {
  435. dbm.close();
  436. cleanup_dir(temp_dir);
  437. return false;
  438. }
  439. dbm.set("key1", string_to_binary("value2"));
  440. value = dbm.get("key1");
  441. if (value == null || binary_to_string((!) value) != "value2") {
  442. dbm.close();
  443. cleanup_dir(temp_dir);
  444. return false;
  445. }
  446. dbm.close();
  447. cleanup_dir(temp_dir);
  448. return true;
  449. } catch (Error e) {
  450. cleanup_dir(temp_dir);
  451. return false;
  452. }
  453. }
  454. // Test 10: Special key names
  455. bool test_special_key_names() {
  456. string db_path = create_temp_db_path();
  457. string temp_dir = get_dir_from_path(db_path);
  458. try {
  459. var dbm = new LmdbDbm();
  460. dbm.open(db_path, false);
  461. string[] special_keys = {
  462. "key/with/slashes",
  463. "key:with:colons",
  464. "key.with.dots",
  465. "key with spaces",
  466. "key\nwith\nnewlines",
  467. "日本語キー"
  468. };
  469. foreach (var key in special_keys) {
  470. dbm.set(key, string_to_binary(@"value for $key"));
  471. var value = dbm.get(key);
  472. if (value == null) {
  473. dbm.close();
  474. cleanup_dir(temp_dir);
  475. return false;
  476. }
  477. if (binary_to_string((!) value) != @"value for $key") {
  478. dbm.close();
  479. cleanup_dir(temp_dir);
  480. return false;
  481. }
  482. }
  483. dbm.close();
  484. cleanup_dir(temp_dir);
  485. return true;
  486. } catch (Error e) {
  487. cleanup_dir(temp_dir);
  488. return false;
  489. }
  490. }
  491. // Test 11: Binary data
  492. bool test_binary_data() {
  493. string db_path = create_temp_db_path();
  494. string temp_dir = get_dir_from_path(db_path);
  495. try {
  496. var dbm = new LmdbDbm();
  497. dbm.open(db_path, false);
  498. // Create binary data with all byte values
  499. var data = new uint8[256];
  500. for (int i = 0; i < 256; i++) {
  501. data[i] = (uint8) i;
  502. }
  503. var binary = new Invercargill.DataStructures.ByteBuffer.from_byte_array(data);
  504. dbm.set("binary_key", binary);
  505. var value = dbm.get("binary_key");
  506. if (value == null) {
  507. dbm.close();
  508. cleanup_dir(temp_dir);
  509. return false;
  510. }
  511. var read_data = ((!) value).to_bytes();
  512. if (read_data.length != 256) {
  513. dbm.close();
  514. cleanup_dir(temp_dir);
  515. return false;
  516. }
  517. for (int i = 0; i < 256; i++) {
  518. if (read_data.get_data()[i] != (uint8) i) {
  519. dbm.close();
  520. cleanup_dir(temp_dir);
  521. return false;
  522. }
  523. }
  524. dbm.close();
  525. cleanup_dir(temp_dir);
  526. return true;
  527. } catch (Error e) {
  528. cleanup_dir(temp_dir);
  529. return false;
  530. }
  531. }
  532. // Test 12: Empty key
  533. // Note: LMDB does not support zero-length keys, so this test is skipped
  534. bool test_empty_key() {
  535. // LMDB returns BAD_VALSIZE error for empty keys
  536. // This is a documented limitation of LMDB
  537. return true;
  538. }
  539. // Test 13: Large value
  540. bool test_large_value() {
  541. string db_path = create_temp_db_path();
  542. string temp_dir = get_dir_from_path(db_path);
  543. try {
  544. var dbm = new LmdbDbm();
  545. dbm.open(db_path, false);
  546. // Create a 1MB value
  547. var data = new uint8[1024 * 1024];
  548. for (int i = 0; i < data.length; i++) {
  549. data[i] = (uint8) (i % 256);
  550. }
  551. var binary = new Invercargill.DataStructures.ByteBuffer.from_byte_array(data);
  552. dbm.set("large_key", binary);
  553. var value = dbm.get("large_key");
  554. if (value == null) {
  555. dbm.close();
  556. cleanup_dir(temp_dir);
  557. return false;
  558. }
  559. var read_data = ((!) value).to_bytes();
  560. if (read_data.length != data.length) {
  561. dbm.close();
  562. cleanup_dir(temp_dir);
  563. return false;
  564. }
  565. for (int i = 0; i < data.length; i++) {
  566. if (read_data.get_data()[i] != data[i]) {
  567. dbm.close();
  568. cleanup_dir(temp_dir);
  569. return false;
  570. }
  571. }
  572. dbm.close();
  573. cleanup_dir(temp_dir);
  574. return true;
  575. } catch (Error e) {
  576. cleanup_dir(temp_dir);
  577. return false;
  578. }
  579. }
  580. // Test 14: Multiple operations
  581. bool test_multiple_operations() {
  582. string db_path = create_temp_db_path();
  583. string temp_dir = get_dir_from_path(db_path);
  584. try {
  585. var dbm = new LmdbDbm();
  586. dbm.open(db_path, false);
  587. // Set multiple keys
  588. for (int i = 0; i < 100; i++) {
  589. dbm.set(@"key$i", string_to_binary(@"value$i"));
  590. }
  591. // Verify all keys
  592. for (int i = 0; i < 100; i++) {
  593. var value = dbm.get(@"key$i");
  594. if (value == null) {
  595. dbm.close();
  596. cleanup_dir(temp_dir);
  597. return false;
  598. }
  599. if (binary_to_string((!) value) != @"value$i") {
  600. dbm.close();
  601. cleanup_dir(temp_dir);
  602. return false;
  603. }
  604. }
  605. // Delete half the keys
  606. for (int i = 0; i < 50; i++) {
  607. dbm.delete(@"key$i");
  608. }
  609. // Verify deleted keys are gone
  610. for (int i = 0; i < 50; i++) {
  611. if (dbm.has_key(@"key$i")) {
  612. dbm.close();
  613. cleanup_dir(temp_dir);
  614. return false;
  615. }
  616. }
  617. // Verify remaining keys still exist
  618. for (int i = 50; i < 100; i++) {
  619. if (!dbm.has_key(@"key$i")) {
  620. dbm.close();
  621. cleanup_dir(temp_dir);
  622. return false;
  623. }
  624. }
  625. dbm.close();
  626. cleanup_dir(temp_dir);
  627. return true;
  628. } catch (Error e) {
  629. cleanup_dir(temp_dir);
  630. return false;
  631. }
  632. }
  633. // Test 15: Persistence
  634. bool test_persistence() {
  635. string db_path = create_temp_db_path();
  636. string temp_dir = get_dir_from_path(db_path);
  637. try {
  638. // Create and write
  639. var dbm1 = new LmdbDbm();
  640. dbm1.open(db_path, false);
  641. dbm1.set("persistent_key", string_to_binary("persistent_value"));
  642. dbm1.close();
  643. // Create new instance with same path
  644. var dbm2 = new LmdbDbm();
  645. dbm2.open(db_path, false);
  646. // Data should persist
  647. var value = dbm2.get("persistent_key");
  648. if (value == null) {
  649. dbm2.close();
  650. cleanup_dir(temp_dir);
  651. return false;
  652. }
  653. if (binary_to_string((!) value) != "persistent_value") {
  654. dbm2.close();
  655. cleanup_dir(temp_dir);
  656. return false;
  657. }
  658. dbm2.close();
  659. cleanup_dir(temp_dir);
  660. return true;
  661. } catch (Error e) {
  662. cleanup_dir(temp_dir);
  663. return false;
  664. }
  665. }
  666. // Test 16: Open and close
  667. bool test_open_close() {
  668. string db_path = create_temp_db_path();
  669. string temp_dir = get_dir_from_path(db_path);
  670. try {
  671. var dbm = new LmdbDbm();
  672. if (dbm.is_open) {
  673. cleanup_dir(temp_dir);
  674. return false;
  675. }
  676. dbm.open(db_path, false);
  677. if (!dbm.is_open) {
  678. cleanup_dir(temp_dir);
  679. return false;
  680. }
  681. dbm.close();
  682. if (dbm.is_open) {
  683. cleanup_dir(temp_dir);
  684. return false;
  685. }
  686. cleanup_dir(temp_dir);
  687. return true;
  688. } catch (Error e) {
  689. cleanup_dir(temp_dir);
  690. return false;
  691. }
  692. }
  693. // Cleanup helper
  694. void cleanup_dir(string path) {
  695. // Remove all files in directory and the directory itself
  696. try {
  697. Dir dir = Dir.open(path, 0);
  698. string? name;
  699. while ((name = dir.read_name()) != null) {
  700. string file_path = Path.build_filename(path, name);
  701. if (FileUtils.test(file_path, FileTest.IS_DIR)) {
  702. cleanup_dir(file_path);
  703. } else {
  704. FileUtils.unlink(file_path);
  705. }
  706. }
  707. } catch (FileError e) {
  708. // Ignore errors
  709. }
  710. DirUtils.remove(path);
  711. }