/* * Copyright (C) 2019 Matthieu Gautier * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and * NON-INFRINGEMENT. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "gtest/gtest.h" #include const char * sampleOpdsStream = R"( 00000000-0000-0000-0000-000000000000 Encyclopédie de la Tunisie wikipedia_fr_tunisie_novid_2018-10 unforgettable urn:uuid:0c45160e-f917-760a-9159-dfe3c53cdcdd /meta?name=favicon&content=wikipedia_fr_tunisie_novid_2018-10 2018-10-08T00:00::00:Z 8 Oct 2018 fra Le meilleur de Wikipédia sur la Tunisie wikipedia;novid;_ftindex Wikipedia Wikipedia Publishing House 1100 172 Tania Louis urn:uuid:0d0bcd57-d3f6-cb22-44cc-a723ccb4e1b2 /meta?name=favicon&content=biologie-tout-compris_fr_all_2018-06 2018-06-23T00:00::00:Z fra Tania Louis videos youtube;_category:category_defined_via_tags_only Tania Louis Wikiquote urn:uuid:0ea1cde6-441d-6c58-f2c7-21c2838e659f /meta?name=favicon&content=wikiquote_fr_all_nopic_2019-06 2019-06-05T00:00::00:Z fra Une page de Wikiquote, le recueil des citations libres. category_defined_via_category_element_only wikiquote;nopic Wikiquote Géographie par Wikipédia urn:uuid:1123e574-6eef-6d54-28fc-13e4caeae474 /meta?name=favicon&content=wikipedia_fr_geography_nopic_2019-06 2019-06-02T00:00::00:Z Une sélection d'articles de Wikipédia sur la géographie fra category_element_overrides_tags wikipedia;nopic;_category:tags_override_category_element Wikipedia Mathématiques urn:uuid:14829621-c490-c376-0792-9de558b57efa /meta?name=favicon&content=wikipedia_fr_mathematics_nopic_2019-05 2019-05-13T00:00::00:Z fra Une wikipedia;nopic;_category:tags_override_category_element category_element_overrides_tags Wikipedia Granblue Fantasy Wiki urn:uuid:006cbd1b-16d8-b00d-a584-c1ae110a94ed /meta?name=favicon&content=granbluefantasy_en_all_all_nopic_2018-10 2018-10-14T00:00::00:Z eng Granblue Fantasy Wiki gbf;nopic;_ftindex Wiki Movies & TV Stack Exchange urn:uuid:00f37b00-f4da-0675-995a-770f9c72903e /meta?name=favicon&content=movies.stackexchange.com_en_all_2019-02 2019-02-03T00:00::00:Z eng Q&A for movie and tv enthusiasts stackexchange;_ftindex Movies & TV Stack Exchange TED talks - Business urn:uuid:0189d9be-2fd0-b4b6-7300-20fab0b5cdc8 /meta?name=favicon&content=ted_en_business_2018-07 2018-07-23T00:00::00:Z eng Ideas worth spreading TED Mythology & Folklore Stack Exchange urn:uuid:028055ac-4acc-1d54-65e0-a96de45e1b22 /meta?name=favicon&content=mythology.stackexchange.com_en_all_2019-02 2019-02-03T00:00::00:Z eng Q&A for enthusiasts and scholars of mythology and folklore stackexchange;_ftindex Mythology & Folklore Stack Exchange Islam Stack Exchange urn:uuid:02e9c7ff-36fc-9c6e-6ac7-cd7085989029 /meta?name=favicon&content=islam.stackexchange.com_en_all_2019-01 2019-01-31T00:00::00:Z eng Q&A for Muslims, experts in Islam, and those interested in learning more about Islam stackexchange;_ftindex Islam Stack Exchange )"; const char sampleLibraryXML[] = R"( )"; #include "../include/library.h" #include "../include/manager.h" #include "../include/bookmark.h" namespace { TEST(LibraryOpdsImportTest, allInOne) { kiwix::Library lib; kiwix::Manager manager(&lib); manager.readOpds(sampleOpdsStream, "library-opds-import.unittests.dev"); EXPECT_EQ(10U, lib.getBookCount(true, true)); { const kiwix::Book& book1 = lib.getBookById("0c45160e-f917-760a-9159-dfe3c53cdcdd"); EXPECT_EQ(book1.getTitle(), "Encyclopédie de la Tunisie"); EXPECT_EQ(book1.getName(), "wikipedia_fr_tunisie_novid_2018-10"); EXPECT_EQ(book1.getFlavour(), "unforgettable"); EXPECT_EQ(book1.getLanguage(), "fra"); EXPECT_EQ(book1.getDate(), "8 Oct 2018"); EXPECT_EQ(book1.getDescription(), "Le meilleur de Wikipédia sur la Tunisie"); EXPECT_EQ(book1.getCreator(), "Wikipedia"); EXPECT_EQ(book1.getPublisher(), "Wikipedia Publishing House"); EXPECT_EQ(book1.getTags(), "wikipedia;novid;_ftindex"); EXPECT_EQ(book1.getCategory(), ""); EXPECT_EQ(book1.getUrl(), "http://download.kiwix.org/zim/wikipedia/wikipedia_fr_tunisie_novid_2018-10.zim.meta4"); EXPECT_EQ(book1.getSize(), 90030080UL); EXPECT_EQ(book1.getMediaCount(), 1100U); // Roman MC (MediaCount) is 1100 EXPECT_EQ(book1.getArticleCount(), 172U); // Hex AC (ArticleCount) is 172 const auto illustration = book1.getIllustration(48); EXPECT_EQ(illustration->width, 48U); EXPECT_EQ(illustration->height, 48U); EXPECT_EQ(illustration->mimeType, "image/png"); EXPECT_EQ(illustration->url, "library-opds-import.unittests.dev/meta?name=favicon&content=wikipedia_fr_tunisie_novid_2018-10"); } { const kiwix::Book& book2 = lib.getBookById("0189d9be-2fd0-b4b6-7300-20fab0b5cdc8"); EXPECT_EQ(book2.getTitle(), "TED talks - Business"); EXPECT_EQ(book2.getName(), ""); EXPECT_EQ(book2.getFlavour(), ""); EXPECT_EQ(book2.getLanguage(), "eng"); EXPECT_EQ(book2.getDate(), "2018-07-23"); EXPECT_EQ(book2.getDescription(), "Ideas worth spreading"); EXPECT_EQ(book2.getCreator(), "TED"); EXPECT_EQ(book2.getPublisher(), ""); EXPECT_EQ(book2.getTags(), ""); EXPECT_EQ(book2.getCategory(), ""); EXPECT_EQ(book2.getUrl(), "http://download.kiwix.org/zim/ted/ted_en_business_2018-07.zim.meta4"); EXPECT_EQ(book2.getSize(), 8855827456UL); EXPECT_EQ(book2.getMediaCount(), 0U); EXPECT_EQ(book2.getArticleCount(), 0U); const auto illustration = book2.getIllustration(48); EXPECT_EQ(illustration->width, 48U); EXPECT_EQ(illustration->height, 48U); EXPECT_EQ(illustration->mimeType, "image/png"); EXPECT_EQ(illustration->url, "library-opds-import.unittests.dev/meta?name=favicon&content=ted_en_business_2018-07"); } } class LibraryTest : public ::testing::Test { protected: typedef kiwix::Library::BookIdCollection BookIdCollection; typedef std::vector TitleCollection; void SetUp() override { kiwix::Manager manager(&lib); manager.readOpds(sampleOpdsStream, "foo.urlHost"); manager.readXml(sampleLibraryXML, false, "./test/library.xml", true); } kiwix::Bookmark createBookmark(const std::string &id) { kiwix::Bookmark bookmark; bookmark.setBookId(id); return bookmark; }; TitleCollection ids2Titles(const BookIdCollection& ids) { TitleCollection titles; for ( const auto& bookId : ids ) { titles.push_back(lib.getBookById(bookId).getTitle()); } std::sort(titles.begin(), titles.end()); return titles; } kiwix::Library lib; }; TEST_F(LibraryTest, getBookMarksTest) { auto bookId1 = lib.getBooksIds()[0]; auto bookId2 = lib.getBooksIds()[1]; lib.addBookmark(createBookmark(bookId1)); lib.addBookmark(createBookmark("invalid-bookmark-id")); lib.addBookmark(createBookmark(bookId2)); auto onlyValidBookmarks = lib.getBookmarks(); auto allBookmarks = lib.getBookmarks(false); EXPECT_EQ(onlyValidBookmarks[0].getBookId(), bookId1); EXPECT_EQ(onlyValidBookmarks[1].getBookId(), bookId2); EXPECT_EQ(allBookmarks[0].getBookId(), bookId1); EXPECT_EQ(allBookmarks[1].getBookId(), "invalid-bookmark-id"); EXPECT_EQ(allBookmarks[2].getBookId(), bookId2); } TEST_F(LibraryTest, sanityCheck) { EXPECT_EQ(lib.getBookCount(true, true), 12U); EXPECT_EQ(lib.getBooksLanguages(), std::vector({"deu", "eng", "fra"}) ); EXPECT_EQ(lib.getBooksCreators(), std::vector({ "Islam Stack Exchange", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange", "TED", "Tania Louis", "Wiki", "Wikibooks", "Wikipedia", "Wikiquote" })); EXPECT_EQ(lib.getBooksPublishers(), std::vector({ "", "Kiwix", "Kiwix & Some Enthusiasts", "Wikipedia Publishing House" })); } TEST_F(LibraryTest, categoryHandling) { EXPECT_EQ("", lib.getBookById("0c45160e-f917-760a-9159-dfe3c53cdcdd").getCategory()); EXPECT_EQ("category_defined_via_tags_only", lib.getBookById("0d0bcd57-d3f6-cb22-44cc-a723ccb4e1b2").getCategory()); EXPECT_EQ("category_defined_via_category_element_only", lib.getBookById("0ea1cde6-441d-6c58-f2c7-21c2838e659f").getCategory()); EXPECT_EQ("category_element_overrides_tags", lib.getBookById("1123e574-6eef-6d54-28fc-13e4caeae474").getCategory()); EXPECT_EQ("category_element_overrides_tags", lib.getBookById("14829621-c490-c376-0792-9de558b57efa").getCategory()); } TEST_F(LibraryTest, emptyFilter) { const auto bookIds = lib.filter(kiwix::Filter()); EXPECT_EQ(bookIds, lib.getBooksIds()); } #define EXPECT_FILTER_RESULTS(f, ...) \ EXPECT_EQ( \ ids2Titles(lib.filter(f)), \ TitleCollection({ __VA_ARGS__ }) \ ) TEST_F(LibraryTest, filterLocal) { EXPECT_FILTER_RESULTS(kiwix::Filter().local(true), "An example ZIM archive", "Ray Charles" ); EXPECT_FILTER_RESULTS(kiwix::Filter().local(false), "Encyclopédie de la Tunisie", "Granblue Fantasy Wiki", "Géographie par Wikipédia", "Islam Stack Exchange", "Mathématiques", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange", "TED talks - Business", "Tania Louis", "Wikiquote" ); } TEST_F(LibraryTest, filterRemote) { EXPECT_FILTER_RESULTS(kiwix::Filter().remote(true), "Encyclopédie de la Tunisie", "Granblue Fantasy Wiki", "Géographie par Wikipédia", "Islam Stack Exchange", "Mathématiques", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange", "Ray Charles", "TED talks - Business", "Tania Louis", "Wikiquote" ); EXPECT_FILTER_RESULTS(kiwix::Filter().remote(false), "An example ZIM archive" ); } TEST_F(LibraryTest, filterByLanguage) { EXPECT_FILTER_RESULTS(kiwix::Filter().lang("eng"), "Granblue Fantasy Wiki", "Islam Stack Exchange", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange", "Ray Charles", "TED talks - Business" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("lang:eng"), "Granblue Fantasy Wiki", "Islam Stack Exchange", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange", "Ray Charles", "TED talks - Business" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("eng"), /* no results */ ); } TEST_F(LibraryTest, filterByTags) { EXPECT_FILTER_RESULTS(kiwix::Filter().acceptTags({"stackexchange"}), "Islam Stack Exchange", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange" ); // filtering by tags is case and diacritics insensitive EXPECT_FILTER_RESULTS(kiwix::Filter().acceptTags({"ståckEXÇhange"}), "Islam Stack Exchange", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange" ); // filtering by tags requires full match of the search term EXPECT_FILTER_RESULTS(kiwix::Filter().acceptTags({"stackexch"}), /* no results */ ); // in tags with values (tag:value form) the value is an inseparable // part of the tag EXPECT_FILTER_RESULTS(kiwix::Filter().acceptTags({"_category"}), /* no results */ ); EXPECT_FILTER_RESULTS(kiwix::Filter().acceptTags({"_category:category_defined_via_tags_only"}), "Tania Louis" ); EXPECT_FILTER_RESULTS(kiwix::Filter().acceptTags({"wikipedia"}), "Encyclopédie de la Tunisie", "Géographie par Wikipédia", "Mathématiques", "Ray Charles" ); EXPECT_FILTER_RESULTS(kiwix::Filter().acceptTags({"wikipedia", "nopic"}), "Géographie par Wikipédia", "Mathématiques" ); EXPECT_FILTER_RESULTS(kiwix::Filter().acceptTags({"wikipedia"}).rejectTags({"nopic"}), "Encyclopédie de la Tunisie", "Ray Charles" ); } TEST_F(LibraryTest, filterByQuery) { // filtering by query checks the title EXPECT_FILTER_RESULTS(kiwix::Filter().query("Exchange"), "Islam Stack Exchange", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange" ); // filtering by query checks the description/summary EXPECT_FILTER_RESULTS(kiwix::Filter().query("enthusiasts"), "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange" ); // filtering by query is case insensitive on titles EXPECT_FILTER_RESULTS(kiwix::Filter().query("ExcHANge"), "Islam Stack Exchange", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange" ); // filtering by query is diacritics insensitive on titles EXPECT_FILTER_RESULTS(kiwix::Filter().query("mathematiques"), "Mathématiques", ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("èxchângé"), "Islam Stack Exchange", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange" ); // filtering by query is case insensitive on description/summary EXPECT_FILTER_RESULTS(kiwix::Filter().query("enTHUSiaSTS"), "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange" ); // filtering by query is diacritics insensitive on description/summary EXPECT_FILTER_RESULTS(kiwix::Filter().query("selection"), "Géographie par Wikipédia" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("enthúsïåsts"), "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange" ); // by default, filtering by query assumes partial query EXPECT_FILTER_RESULTS(kiwix::Filter().query("Wiki"), "Encyclopédie de la Tunisie", "Granblue Fantasy Wiki", "Géographie par Wikipédia", "Ray Charles", "Wikiquote" ); // partial query can be disabled EXPECT_FILTER_RESULTS(kiwix::Filter().query("Wiki", false), "Granblue Fantasy Wiki" ); } TEST_F(LibraryTest, filteringByEmptyQueryReturnsAllEntries) { EXPECT_FILTER_RESULTS(kiwix::Filter().query(""), "An example ZIM archive", "Encyclopédie de la Tunisie", "Granblue Fantasy Wiki", "Géographie par Wikipédia", "Islam Stack Exchange", "Mathématiques", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange", "Ray Charles", "TED talks - Business", "Tania Louis", "Wikiquote" ); } TEST_F(LibraryTest, filterByCreator) { EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Wikipedia"), "Encyclopédie de la Tunisie", "Géographie par Wikipédia", "Mathématiques", "Ray Charles" ); // filtering by creator requires full match of the search term EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Wiki"), "Granblue Fantasy Wiki" ); // filtering by creator is case and diacritics insensitive EXPECT_FILTER_RESULTS(kiwix::Filter().creator("wIkï"), "Granblue Fantasy Wiki" ); // filtering by creator doesn't requires full match of the full creator name EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Stack"), "Islam Stack Exchange", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange" ); // filtering by creator requires a full phrase match (ignoring some non-word terms) EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Movies & TV Stack Exchange"), "Movies & TV Stack Exchange" ); EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Movies & TV"), "Movies & TV Stack Exchange" ); EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Movies TV"), "Movies & TV Stack Exchange" ); EXPECT_FILTER_RESULTS(kiwix::Filter().creator("TV & Movies"), /* no results */ ); EXPECT_FILTER_RESULTS(kiwix::Filter().creator("TV Movies"), /* no results */ ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("creator:Wikipedia"), "Encyclopédie de la Tunisie", "Géographie par Wikipédia", "Mathématiques", "Ray Charles" ); } TEST_F(LibraryTest, filterByPublisher) { EXPECT_FILTER_RESULTS(kiwix::Filter().publisher("Kiwix"), "An example ZIM archive", "Ray Charles" ); // filtering by publisher requires full match of the search term EXPECT_FILTER_RESULTS(kiwix::Filter().publisher("Kiwi"), /* no results */ ); // filtering by publisher requires a full phrase match EXPECT_FILTER_RESULTS(kiwix::Filter().publisher("Kiwix & Some Enthusiasts"), "An example ZIM archive" ); EXPECT_FILTER_RESULTS(kiwix::Filter().publisher("Some Enthusiasts & Kiwix"), /* no results */ ); // filtering by publisher is case and diacritics insensitive EXPECT_FILTER_RESULTS(kiwix::Filter().publisher("kîWIx"), "An example ZIM archive", "Ray Charles" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("publisher:kiwix"), "An example ZIM archive", "Ray Charles" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("kiwix"), /* no results */ ); } TEST_F(LibraryTest, filterByName) { EXPECT_FILTER_RESULTS(kiwix::Filter().name("wikibooks_de"), "An example ZIM archive" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("name:wikibooks_de"), "An example ZIM archive" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("wikibooks_de"), /* no results */ ); } TEST_F(LibraryTest, filterByCategory) { EXPECT_FILTER_RESULTS(kiwix::Filter().category("category_element_overrides_tags"), "Géographie par Wikipédia", "Mathématiques" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("category:category_element_overrides_tags"), "Géographie par Wikipédia", "Mathématiques" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("category_element_overrides_tags"), /* no results */ ); } TEST_F(LibraryTest, filterByMaxSize) { EXPECT_FILTER_RESULTS(kiwix::Filter().maxSize(200000), "An example ZIM archive" ); } TEST_F(LibraryTest, filterByMultipleCriteria) { EXPECT_FILTER_RESULTS(kiwix::Filter().query("Wiki").creator("Wikipedia"), "Encyclopédie de la Tunisie", "Géographie par Wikipédia", "Ray Charles" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("Wiki").creator("Wikipedia").maxSize(100000000UL), "Encyclopédie de la Tunisie", "Ray Charles" ); EXPECT_FILTER_RESULTS(kiwix::Filter().query("Wiki").creator("Wikipedia").maxSize(100000000UL).local(false), "Encyclopédie de la Tunisie" ); } TEST_F(LibraryTest, getBookByPath) { kiwix::Book book = lib.getBookById(lib.getBooksIds()[0]); #ifdef _WIN32 auto path = "C:\\some\\abs\\path.zim"; #else auto path = "/some/abs/path.zim"; #endif book.setPath(path); lib.addBook(book); EXPECT_EQ(lib.getBookByPath(path).getId(), book.getId()); EXPECT_THROW(lib.getBookByPath("non/existant/path.zim"), std::out_of_range); } TEST_F(LibraryTest, removeBookByIdRemovesTheBook) { const auto initialBookCount = lib.getBookCount(true, true); ASSERT_GT(initialBookCount, 0U); EXPECT_NO_THROW(lib.getBookById("raycharles")); lib.removeBookById("raycharles"); EXPECT_EQ(initialBookCount - 1, lib.getBookCount(true, true)); EXPECT_THROW(lib.getBookById("raycharles"), std::out_of_range); }; TEST_F(LibraryTest, removeBookByIdDropsTheReader) { EXPECT_NE(nullptr, lib.getArchiveById("raycharles")); lib.removeBookById("raycharles"); EXPECT_THROW(lib.getArchiveById("raycharles"), std::out_of_range); }; TEST_F(LibraryTest, removeBookByIdUpdatesTheSearchDB) { kiwix::Filter f; f.local(true).valid(true).query(R"(title:"ray charles")", false); EXPECT_NO_THROW(lib.getBookById("raycharles")); EXPECT_EQ(1U, lib.filter(f).size()); lib.removeBookById("raycharles"); EXPECT_THROW(lib.getBookById("raycharles"), std::out_of_range); EXPECT_EQ(0U, lib.filter(f).size()); // make sure that Library::filter() doesn't add an empty book with // an id surviving in the search DB EXPECT_THROW(lib.getBookById("raycharles"), std::out_of_range); }; TEST_F(LibraryTest, removeBooksNotUpdatedSince) { EXPECT_FILTER_RESULTS(kiwix::Filter(), "An example ZIM archive", "Encyclopédie de la Tunisie", "Granblue Fantasy Wiki", "Géographie par Wikipédia", "Islam Stack Exchange", "Mathématiques", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange", "Ray Charles", "TED talks - Business", "Tania Louis", "Wikiquote" ); const uint64_t rev = lib.getRevision(); for ( const auto& id : lib.filter(kiwix::Filter().query("exchange")) ) { lib.addBook(lib.getBookByIdThreadSafe(id)); } EXPECT_EQ(9u, lib.removeBooksNotUpdatedSince(rev)); EXPECT_FILTER_RESULTS(kiwix::Filter(), "Islam Stack Exchange", "Movies & TV Stack Exchange", "Mythology & Folklore Stack Exchange", ); }; };