diff --git a/include/spelling_correction.h b/include/spelling_correction.h index 438035fa..f79353c2 100644 --- a/include/spelling_correction.h +++ b/include/spelling_correction.h @@ -20,6 +20,7 @@ #ifndef KIWIX_SPELLING_CORRECTION_H #define KIWIX_SPELLING_CORRECTION_H +#include #include #include #include @@ -40,7 +41,7 @@ namespace kiwix class SpellingsDB { public: // functions - explicit SpellingsDB(const zim::Archive& archive, std::string path); + SpellingsDB(const zim::Archive& archive, std::filesystem::path path); ~SpellingsDB(); SpellingsDB(const SpellingsDB& ) = delete; diff --git a/src/spelling_correction.cpp b/src/spelling_correction.cpp index dfc97ab6..d4767a44 100644 --- a/src/spelling_correction.cpp +++ b/src/spelling_correction.cpp @@ -39,19 +39,37 @@ std::vector getAllTitles(const zim::Archive& a) return result; } +void createXapianDB(std::string path, const zim::Archive& archive) +{ + const int flags = Xapian::DB_BACKEND_GLASS|Xapian::DB_CREATE; + const auto tmpDbPath = path + ".tmp"; + Xapian::WritableDatabase db(tmpDbPath, flags); + for (const auto& t : getAllTitles(archive)) { + db.add_spelling(t); + } + db.commit(); + db.compact(path, Xapian::DBCOMPACT_SINGLE_FILE); + db.close(); + std::filesystem::remove_all(tmpDbPath); +} + std::unique_ptr openOrCreateXapianDB(std::string path, const zim::Archive& archive) { - auto db(std::make_unique(path, Xapian::DB_BACKEND_GLASS)); - for (const auto& t : getAllTitles(archive)) { - db->add_spelling(t); + try + { + return std::make_unique(path); + } + catch (const Xapian::DatabaseOpeningError& ) + { + createXapianDB(path, archive); + return std::make_unique(path); } - return std::move(db); } } // unnamed namespace -SpellingsDB::SpellingsDB(const zim::Archive& archive, std::string path) - : impl_(openOrCreateXapianDB(path, archive)) +SpellingsDB::SpellingsDB(const zim::Archive& archive, std::filesystem::path path) + : impl_(openOrCreateXapianDB(path.string(), archive)) { } diff --git a/test/spelling_correction.cpp b/test/spelling_correction.cpp index 13b1ac53..bfcb6c98 100644 --- a/test/spelling_correction.cpp +++ b/test/spelling_correction.cpp @@ -19,27 +19,45 @@ #include "gtest/gtest.h" #include "../include/spelling_correction.h" +#include "../src/tools/pathTools.h" #include "zim/archive.h" #include +#include + const std::string TEST_DB_PATH = "./spellings.db"; class SpellingCorrectionTest : public ::testing::Test { - void removeDb() - { - std::filesystem::remove_all(TEST_DB_PATH); - } - protected: void SetUp() override { - removeDb(); + tmpDirPath = makeTmpDirectory(); + archive = std::make_unique("./test/spelling_correction_test.zim"); } void TearDown() override { - removeDb(); + std::filesystem::permissions( + tmpDirPath, + std::filesystem::perms::owner_write, + std::filesystem::perm_options::add + ); + std::filesystem::remove_all(tmpDirPath); } + + void makeTmpDirReadOnly() { + using std::filesystem::perms; + + std::filesystem::permissions( + tmpDirPath, + perms::owner_write | perms::group_write | perms::others_write, + std::filesystem::perm_options::remove + ); + } + +protected: + std::filesystem::path tmpDirPath; + std::unique_ptr archive; }; void testSpellingCorrections(const kiwix::SpellingsDB& spellingsDB) @@ -169,9 +187,29 @@ void testSpellingCorrections(const kiwix::SpellingsDB& spellingsDB) EXPECT_THROW(spellingsDB.getSpellingCorrections("Kung", 2), std::runtime_error); } +TEST_F(SpellingCorrectionTest, SpellingsDBCannotBeCreatedInAReadOnlyDirectory) +{ + makeTmpDirReadOnly(); + + EXPECT_THROW( + const kiwix::SpellingsDB spellingsDB(*archive, tmpDirPath / "spellings.db"), + Xapian::DatabaseCreateError + ); +} + TEST_F(SpellingCorrectionTest, allInOne) { - const auto archive = zim::Archive("./test/spelling_correction_test.zim"); - kiwix::SpellingsDB spellingsDB(archive, TEST_DB_PATH); - testSpellingCorrections(spellingsDB); + const auto spellingsDbPath = tmpDirPath / "spellings.db"; + + { + const kiwix::SpellingsDB spellingsDB(*archive, spellingsDbPath); + testSpellingCorrections(spellingsDB); + } + + makeTmpDirReadOnly(); + + { + const kiwix::SpellingsDB spellingsDB(*archive, spellingsDbPath); + testSpellingCorrections(spellingsDB); + } }