diff --git a/test/meson.build b/test/meson.build index 58d4d605..74de624d 100644 --- a/test/meson.build +++ b/test/meson.build @@ -17,8 +17,7 @@ tests = [ if build_machine.system() != 'windows' tests += [ 'server', - 'server_html_search', - 'server_xml_search' + 'server_search' ] endif diff --git a/test/server_html_search.cpp b/test/server_search.cpp similarity index 86% rename from test/server_html_search.cpp rename to test/server_search.cpp index 4e425da3..16e4d7fd 100644 --- a/test/server_html_search.cpp +++ b/test/server_search.cpp @@ -137,15 +137,63 @@ std::string makeSearchResultsHtml(const std::string& pattern, return html; } -#define SEARCH_RESULT(LINK, TITLE, SNIPPET, BOOK_TITLE, WORDCOUNT) \ -"\n \n"\ -" " TITLE "\n"\ -" \n"\ -" " SNIPPET "\n"\ -"
from " BOOK_TITLE "
\n"\ -"
" WORDCOUNT " words
\n" +std::string makeSearchResultsXml(const std::string& header, + const std::string& results) +{ + const char SEARCHRESULTS_XML_TEMPLATE[] = R"XML( + + + %HEADER%%RESULTS% + + +)XML"; -const std::vector LARGE_SEARCH_RESULTS = { + std::string html = removeEOLWhitespaceMarkers(SEARCHRESULTS_XML_TEMPLATE); + html = replace(html, "%HEADER%", header); + html = replace(html, "%RESULTS%", results); + return html; +} + +struct SearchResult +{ + std::string link; + std::string title; + std::string snippet; + std::string bookTitle; + std::string wordCount; + + std::string getHtml() const + { + return std::string() + + "\n \n" + + " " + title + "\n" + + " \n" + + " " + snippet + "\n" + + "
from " + bookTitle + "
\n" + + "
" + wordCount + " words
\n"; + } + + std::string getXml() const + { + return std::string() + + " " + title + "\n" + + " " + replace(link, "'", "'") + "\n" + + " " + snippet + "\n" + + " \n" + + " " + bookTitle + "\n" + + " \n" + + " " + wordCount + ""; + } +}; + +#define SEARCH_RESULT(LINK, TITLE, SNIPPET, BOOK_TITLE, WORDCOUNT) \ + SearchResult{LINK, TITLE, SNIPPET, BOOK_TITLE, WORDCOUNT} + + + +const std::vector LARGE_SEARCH_RESULTS = { SEARCH_RESULT( /*link*/ "/ROOT/zimfile/A/Genius_+_Soul_=_Jazz", /*title*/ "Genius + Soul = Jazz", @@ -507,11 +555,11 @@ const std::vector LARGE_SEARCH_RESULTS = { // // In order to be able to share the same expected output data // LARGE_SEARCH_RESULTS between multiple build platforms and test-points -// of the TaskbarlessServerTest.searchResults test-case +// of the ServerTest.searchResults test-case // // 1. Snippets are excluded from the plain-text comparison of actual and // expected HTML strings. This is done with the help of the -// function maskSnippetsInSearchResults() +// function maskSnippetsInHtmlSearchResults() // // 2. Snippets are checked separately. If a plain-text comparison fails // then a weaker comparison is attempted. Currently it works by testing @@ -529,9 +577,48 @@ const std::vector LARGE_SEARCH_RESULTS = { // - Non-overlapping snippets can be joined with a " ... " in between. // -std::string maskSnippetsInSearchResults(std::string s) +typedef std::vector Snippets; + +const char SNIPPET_REGEX_FOR_HTML[] = "(.+)"; + +std::string maskSnippetsInHtmlSearchResults(std::string s) { - return replace(s, ".+", "SNIPPET TEXT WAS MASKED"); + return replace(s, SNIPPET_REGEX_FOR_HTML, "SNIPPET TEXT WAS MASKED"); +} + +Snippets extractSearchResultSnippetsFromHtml(const std::string& html) +{ + Snippets snippets; + const std::regex snippetRegex(SNIPPET_REGEX_FOR_HTML); + std::sregex_iterator snippetIt(html.begin(), html.end(), snippetRegex); + const std::sregex_iterator end; + for ( ; snippetIt != end; ++snippetIt) + { + const std::smatch snippetMatch = *snippetIt; + snippets.push_back(snippetMatch[1].str()); + } + return snippets; +} + +const char SNIPPET_REGEX_FOR_XML[] = "(?!Search result for)(.+)"; + +std::string maskSnippetsInXmlSearchResults(std::string s) +{ + return replace(s, SNIPPET_REGEX_FOR_XML, "SNIPPET TEXT WAS MASKED"); +} + +Snippets extractSearchResultSnippetsFromXml(const std::string& xml) +{ + Snippets snippets; + const std::regex snippetRegex(SNIPPET_REGEX_FOR_XML); + std::sregex_iterator snippetIt(xml.begin(), xml.end(), snippetRegex); + const std::sregex_iterator end; + for ( ; snippetIt != end; ++snippetIt) + { + const std::smatch snippetMatch = *snippetIt; + snippets.push_back(snippetMatch[1].str()); + } + return snippets; } bool isValidSnippet(const std::string& s) @@ -583,60 +670,76 @@ bool isSubSnippet(std::string subSnippet, const std::string& superSnippet) #define RAYCHARLESZIMID "6f1d19d0-633f-087b-fb55-7ac324ff9baf" #define EXAMPLEZIMID "5dc0b3af-5df2-0925-f0ca-d2bf75e78af6" -TEST_F(TaskbarlessServerTest, searchResults) +struct TestData { - struct TestData + struct PaginationEntry { - struct PaginationEntry - { - std::string label; - size_t start; - bool selected; - }; + std::string label; + size_t start; + bool selected; + }; - std::string query; - int start; - size_t resultsPerPage; - size_t totalResultCount; - size_t firstResultIndex; - std::vector results; - std::vector pagination; + std::string query; + int start; + size_t resultsPerPage; + size_t totalResultCount; + size_t firstResultIndex; + std::vector results; + std::vector pagination; - static std::string makeUrl(const std::string& query, int start, size_t resultsPerPage) - { - std::string url = "/ROOT/search?" + query; + static std::string makeUrl(const std::string& query, int start, size_t resultsPerPage) + { + std::string url = "/ROOT/search?" + query; - if ( start >= 0 ) { - url += "&start=" + to_string(start); - } - - if ( resultsPerPage != 0 ) { - url += "&pageLength=" + to_string(resultsPerPage); - } - - return url; + if ( start >= 0 ) { + url += "&start=" + to_string(start); } - std::string getPattern() const - { - const std::string p = "pattern="; - const size_t i = query.find(p); - std::string r = query.substr(i + p.size()); - return r.substr(0, r.find("&")); + if ( resultsPerPage != 0 ) { + url += "&pageLength=" + to_string(resultsPerPage); } - std::string url() const - { - return makeUrl(query, start, resultsPerPage); + return url; + } + + std::string extractQueryValue(const std::string& key) const + { + const std::string p = key + "="; + const size_t i = query.find(p); + if (i == std::string::npos) { + return ""; + } + std::string r = query.substr(i + p.size()); + return r.substr(0, r.find("&")); + } + + std::string getPattern() const + { + return extractQueryValue("pattern"); + } + + std::string getLang() const + { + return extractQueryValue("books.filter.lang"); + } + + std::string url() const + { + return makeUrl(query, start, resultsPerPage); + } + + std::string xmlSearchUrl() const + { + return url() + "&format=xml"; + } + + std::string expectedHtmlHeader() const + { + if ( totalResultCount == 0 ) { + return "\n No results were found for \"" + getPattern() + "\""; } - std::string expectedHeader() const - { - if ( totalResultCount == 0 ) { - return "\n No results were found for \"" + getPattern() + "\""; - } - - std::string header = R"( Results + std::string header = R"( Results FIRSTRESULT-LASTRESULT of @@ -646,127 +749,175 @@ TEST_F(TaskbarlessServerTest, searchResults) )"; - const size_t lastResultIndex = std::min(totalResultCount, firstResultIndex + results.size() - 1); + const size_t lastResultIndex = std::min(totalResultCount, firstResultIndex + results.size() - 1); + header = replace(header, "FIRSTRESULT", to_string(firstResultIndex)); + header = replace(header, "LASTRESULT", to_string(lastResultIndex)); + header = replace(header, "RESULTCOUNT", to_string(totalResultCount)); + header = replace(header, "PATTERN", getPattern()); + return header; + } + + std::string expectedHtmlResultsString() const + { + if ( results.empty() ) { + return "\n "; + } + + std::string s; + for ( const auto& r : results ) { + s += "\n
  • "; + s += maskSnippetsInHtmlSearchResults(r.getHtml()); + s += "
  • "; + } + return s; + } + + std::string expectedHtmlFooter() const + { + if ( pagination.empty() ) { + return "\n "; + } + + std::ostringstream oss; + oss << "\n "; + return oss.str(); + } + + std::string expectedHtml() const + { + return makeSearchResultsHtml( + getPattern(), + expectedHtmlHeader(), + expectedHtmlResultsString(), + expectedHtmlFooter() + ); + } + + std::string expectedXmlHeader() const + { + std::string header = R"(Search: PATTERN + URL + Search result for PATTERN + RESULTCOUNT + FIRSTRESULT + ITEMCOUNT +