From 600acb76c75686f2e1a3bba581365b51b04099c3 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sat, 4 Jun 2022 19:34:54 +0400 Subject: [PATCH 01/10] XML responses should be taskbarless by default Unit-tests of search results in XML format should work the same way with a server that would inject a taskbar into HTML responses. This small change actually validates that taskbar injection is disabled for XML responses. --- test/server_xml_search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/server_xml_search.cpp b/test/server_xml_search.cpp index 1f3568fbc..74ab948d7 100644 --- a/test/server_xml_search.cpp +++ b/test/server_xml_search.cpp @@ -473,7 +473,7 @@ 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) +TEST_F(ServerTest, searchResultsXML) { struct TestData { From 1c98b0012812ef215929d6da9bf2e2b25b0e97ff Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sat, 4 Jun 2022 19:50:51 +0400 Subject: [PATCH 02/10] Got rid of TaskbarlessServerTest Now ServerTest provides an optional taskbarless kiwix::Server. --- test/server_html_search.cpp | 6 +++--- test/server_testing_tools.h | 21 ++++++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/test/server_html_search.cpp b/test/server_html_search.cpp index 4e425da32..44d1ee383 100644 --- a/test/server_html_search.cpp +++ b/test/server_html_search.cpp @@ -507,7 +507,7 @@ 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 @@ -583,7 +583,7 @@ 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) +TEST_F(ServerTest, searchResults) { struct TestData { @@ -1301,7 +1301,7 @@ TEST_F(TaskbarlessServerTest, searchResults) }; for ( const auto& t : testData ) { - const auto r = zfs1_->GET(t.url().c_str()); + const auto r = taskbarlessZimFileServer().GET(t.url().c_str()); EXPECT_EQ(r->status, 200); t.check(r->body); } diff --git a/test/server_testing_tools.h b/test/server_testing_tools.h index 779b220ee..d6a4a23a9 100644 --- a/test/server_testing_tools.h +++ b/test/server_testing_tools.h @@ -134,6 +134,9 @@ ZimFileServer::~ZimFileServer() class ServerTest : public ::testing::Test { +private: + std::unique_ptr taskbarlessZfs_; + protected: std::unique_ptr zfs1_; @@ -149,16 +152,16 @@ protected: zfs1_.reset(new ZimFileServer(SERVER_PORT, /*withTaskbar=*/true, ZIMFILES)); } + ZimFileServer& taskbarlessZimFileServer() + { + if ( ! taskbarlessZfs_ ) { + taskbarlessZfs_.reset(new ZimFileServer(SERVER_PORT+1, /*withTaskbar=*/false, ZIMFILES)); + } + return *taskbarlessZfs_; + } + void TearDown() override { zfs1_.reset(); + taskbarlessZfs_.reset(); } }; - -class TaskbarlessServerTest : public ServerTest -{ -protected: - void SetUp() override { - zfs1_.reset(new ZimFileServer(SERVER_PORT, /*withTaskbar=*/false, ZIMFILES)); - } -}; - From ddd639eaa14ce0e7ae505ba4d35d31eb8cb4decc Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sat, 4 Jun 2022 20:42:41 +0400 Subject: [PATCH 03/10] Moved TestData out of ServerTest.searchResults Now that ServerTest.searchResults is in a separate cpp file, there are no reasons for hiding its test data definition inside the unit test function. The diff is much-much simpler if whitespace changes are ignored. --- test/server_html_search.cpp | 294 ++++++++++++++++++------------------ 1 file changed, 147 insertions(+), 147 deletions(-) diff --git a/test/server_html_search.cpp b/test/server_html_search.cpp index 44d1ee383..b4862916d 100644 --- a/test/server_html_search.cpp +++ b/test/server_html_search.cpp @@ -583,60 +583,58 @@ 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(ServerTest, 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 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("&")); + } + + std::string url() const + { + return makeUrl(query, start, resultsPerPage); + } + + std::string expectedHeader() 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 +644,129 @@ TEST_F(ServerTest, searchResults) )"; - 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; + 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 expectedResultsString() const + { + if ( results.empty() ) { + return "\n "; } - std::string expectedResultsString() const - { - if ( results.empty() ) { - return "\n "; + std::string s; + for ( const auto& r : results ) { + s += "\n
  • "; + s += maskSnippetsInSearchResults(r); + s += "
  • "; + } + return s; + } + + std::string expectedFooter() const + { + if ( pagination.empty() ) { + return "\n "; + } + + std::ostringstream oss; + oss << "\n "; + return oss.str(); + } - std::string expectedFooter() const + std::string expectedHtml() const + { + return makeSearchResultsHtml( + getPattern(), + expectedHeader(), + expectedResultsString(), + expectedFooter() + ); + } + + TestContext testContext() const + { + return TestContext{ { "url", url() } }; + } + + void check(const std::string& html) const + { + EXPECT_EQ(maskSnippetsInSearchResults(html), expectedHtml()) + << testContext(); + + checkSnippets(extractSearchResultSnippets(html)); + } + + typedef std::vector Snippets; + + static Snippets extractSearchResultSnippets(const std::string& html) + { + Snippets snippets; + const std::regex snippetRegex("(.*)"); + std::sregex_iterator snippetIt(html.begin(), html.end(), snippetRegex); + const std::sregex_iterator end; + for ( ; snippetIt != end; ++snippetIt) { - if ( pagination.empty() ) { - return "\n "; - } - - std::ostringstream oss; - oss << "\n "; - return oss.str(); + const std::smatch snippetMatch = *snippetIt; + snippets.push_back(snippetMatch[1].str()); } + return snippets; + } - std::string expectedHtml() const + void checkSnippets(const Snippets& snippets) const + { + ASSERT_EQ(snippets.size(), results.size()); + for ( size_t i = 0; i < results.size(); ++i ) { - return makeSearchResultsHtml( - getPattern(), - expectedHeader(), - expectedResultsString(), - expectedFooter() - ); - } + const auto& r = results[i]; + const auto expectedSnippet = extractSearchResultSnippets(r); + ASSERT_EQ(1u, expectedSnippet.size()) + << "Multiple snippets in test data:" + << "\n" << r; - TestContext testContext() const - { - return TestContext{ { "url", url() } }; - } - - void check(const std::string& html) const - { - EXPECT_EQ(maskSnippetsInSearchResults(html), expectedHtml()) - << testContext(); - - checkSnippets(extractSearchResultSnippets(html)); - } - - typedef std::vector Snippets; - - static Snippets extractSearchResultSnippets(const std::string& html) - { - Snippets snippets; - const std::regex snippetRegex("(.*)"); - 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; - } - - void checkSnippets(const Snippets& snippets) const - { - ASSERT_EQ(snippets.size(), results.size()); - for ( size_t i = 0; i < results.size(); ++i ) - { - const auto& r = results[i]; - const auto expectedSnippet = extractSearchResultSnippets(r); - ASSERT_EQ(1u, expectedSnippet.size()) - << "Multiple snippets in test data:" - << "\n" << r; - - if ( snippets[i] != expectedSnippet[0] ) { - std::cout << "Trying a weaker check for a mismatching snippet...\n"; - checkMismatchingSnippet(snippets[i], expectedSnippet[0]); - } + if ( snippets[i] != expectedSnippet[0] ) { + std::cout << "Trying a weaker check for a mismatching snippet...\n"; + checkMismatchingSnippet(snippets[i], expectedSnippet[0]); } } + } - void checkMismatchingSnippet(std::string actual, std::string expected) const - { - TestContext testContext{ - { "url", url() }, - { "actual snippet", actual }, - { "expected snippet", expected } - }; + void checkMismatchingSnippet(std::string actual, std::string expected) const + { + TestContext testContext{ + { "url", url() }, + { "actual snippet", actual }, + { "expected snippet", expected } + }; - ASSERT_TRUE(isValidSnippet(actual)) << testContext; - ASSERT_TRUE(isValidSnippet(expected)) << testContext; + ASSERT_TRUE(isValidSnippet(actual)) << testContext; + ASSERT_TRUE(isValidSnippet(expected)) << testContext; - if ( !isSubSnippet(actual, expected) ) { - EXPECT_EQ(actual, expected) << testContext; - } + if ( !isSubSnippet(actual, expected) ) { + EXPECT_EQ(actual, expected) << testContext; } - }; + } +}; +TEST_F(ServerTest, searchResults) +{ const TestData testData[] = { { /* query */ "pattern=velomanyunkan&books.id=" RAYCHARLESZIMID, From 75796ed6a55b997af755bffd26d6fb6e5cf998c5 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sat, 4 Jun 2022 20:57:59 +0400 Subject: [PATCH 04/10] Introduced struct SearchResult --- test/server_html_search.cpp | 46 +++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/test/server_html_search.cpp b/test/server_html_search.cpp index b4862916d..20873eaec 100644 --- a/test/server_html_search.cpp +++ b/test/server_html_search.cpp @@ -137,15 +137,32 @@ 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" +struct SearchResult +{ + std::string link; + std::string title; + std::string snippet; + std::string bookTitle; + std::string wordCount; -const std::vector LARGE_SEARCH_RESULTS = { + std::string getHtml() const + { + return std::string() + + "\n \n" + + " " + title + "\n" + + " \n" + + " " + snippet + "\n" + + "
    from " + bookTitle + "
    \n" + + "
    " + wordCount + " words
    \n"; + } +}; + +#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", @@ -597,7 +614,7 @@ struct TestData size_t resultsPerPage; size_t totalResultCount; size_t firstResultIndex; - std::vector results; + std::vector results; std::vector pagination; static std::string makeUrl(const std::string& query, int start, size_t resultsPerPage) @@ -661,7 +678,7 @@ struct TestData std::string s; for ( const auto& r : results ) { s += "\n
  • "; - s += maskSnippetsInSearchResults(r); + s += maskSnippetsInSearchResults(r.getHtml()); s += "
  • "; } return s; @@ -736,14 +753,9 @@ struct TestData for ( size_t i = 0; i < results.size(); ++i ) { const auto& r = results[i]; - const auto expectedSnippet = extractSearchResultSnippets(r); - ASSERT_EQ(1u, expectedSnippet.size()) - << "Multiple snippets in test data:" - << "\n" << r; - - if ( snippets[i] != expectedSnippet[0] ) { + if ( snippets[i] != r.snippet ) { std::cout << "Trying a weaker check for a mismatching snippet...\n"; - checkMismatchingSnippet(snippets[i], expectedSnippet[0]); + checkMismatchingSnippet(snippets[i], r.snippet); } } } From ed6aa5a89a0046d5f51ca6740816b2274fb3ba8f Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sat, 4 Jun 2022 21:04:20 +0400 Subject: [PATCH 05/10] Renamed some functions and variables Included the word "Html" in the names of those functions and variables which will get Xml siblings soon. --- test/server_html_search.cpp | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/test/server_html_search.cpp b/test/server_html_search.cpp index 20873eaec..11f9c0001 100644 --- a/test/server_html_search.cpp +++ b/test/server_html_search.cpp @@ -528,7 +528,7 @@ const std::vector LARGE_SEARCH_RESULTS = { // // 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 @@ -546,7 +546,7 @@ const std::vector LARGE_SEARCH_RESULTS = { // - Non-overlapping snippets can be joined with a " ... " in between. // -std::string maskSnippetsInSearchResults(std::string s) +std::string maskSnippetsInHtmlSearchResults(std::string s) { return replace(s, ".+", "SNIPPET TEXT WAS MASKED"); } @@ -645,7 +645,7 @@ struct TestData return makeUrl(query, start, resultsPerPage); } - std::string expectedHeader() const + std::string expectedHtmlHeader() const { if ( totalResultCount == 0 ) { return "\n No results were found for \"" + getPattern() + "\""; @@ -669,7 +669,7 @@ struct TestData return header; } - std::string expectedResultsString() const + std::string expectedHtmlResultsString() const { if ( results.empty() ) { return "\n "; @@ -678,13 +678,13 @@ struct TestData std::string s; for ( const auto& r : results ) { s += "\n
  • "; - s += maskSnippetsInSearchResults(r.getHtml()); + s += maskSnippetsInHtmlSearchResults(r.getHtml()); s += "
  • "; } return s; } - std::string expectedFooter() const + std::string expectedHtmlFooter() const { if ( pagination.empty() ) { return "\n "; @@ -712,9 +712,9 @@ struct TestData { return makeSearchResultsHtml( getPattern(), - expectedHeader(), - expectedResultsString(), - expectedFooter() + expectedHtmlHeader(), + expectedHtmlResultsString(), + expectedHtmlFooter() ); } @@ -723,17 +723,17 @@ struct TestData return TestContext{ { "url", url() } }; } - void check(const std::string& html) const + void checkHtml(const std::string& html) const { - EXPECT_EQ(maskSnippetsInSearchResults(html), expectedHtml()) + EXPECT_EQ(maskSnippetsInHtmlSearchResults(html), expectedHtml()) << testContext(); - checkSnippets(extractSearchResultSnippets(html)); + checkSnippets(extractSearchResultSnippetsFromHtml(html)); } typedef std::vector Snippets; - static Snippets extractSearchResultSnippets(const std::string& html) + static Snippets extractSearchResultSnippetsFromHtml(const std::string& html) { Snippets snippets; const std::regex snippetRegex("(.*)"); @@ -1313,8 +1313,9 @@ TEST_F(ServerTest, searchResults) }; for ( const auto& t : testData ) { - const auto r = taskbarlessZimFileServer().GET(t.url().c_str()); - EXPECT_EQ(r->status, 200); - t.check(r->body); + const std::string htmlSearchUrl = t.url(); + const auto htmlRes = taskbarlessZimFileServer().GET(htmlSearchUrl.c_str()); + EXPECT_EQ(htmlRes->status, 200); + t.checkHtml(htmlRes->body); } } From 0340a49780a4be8e2162a2f968b6c9f483e2ba70 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sat, 4 Jun 2022 21:16:37 +0400 Subject: [PATCH 06/10] git mv test/server_{html_,}search.cpp --- test/meson.build | 2 +- test/{server_html_search.cpp => server_search.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename test/{server_html_search.cpp => server_search.cpp} (100%) diff --git a/test/meson.build b/test/meson.build index 58d4d605d..f747a457f 100644 --- a/test/meson.build +++ b/test/meson.build @@ -17,7 +17,7 @@ tests = [ if build_machine.system() != 'windows' tests += [ 'server', - 'server_html_search', + 'server_search', 'server_xml_search' ] endif diff --git a/test/server_html_search.cpp b/test/server_search.cpp similarity index 100% rename from test/server_html_search.cpp rename to test/server_search.cpp From 7d4867194a4f4607c2bdd94f72cf00496a7833fa Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sun, 5 Jun 2022 00:32:04 +0400 Subject: [PATCH 07/10] Moved a function --- test/server_search.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/server_search.cpp b/test/server_search.cpp index 11f9c0001..5aed75c8a 100644 --- a/test/server_search.cpp +++ b/test/server_search.cpp @@ -546,11 +546,27 @@ const std::vector LARGE_SEARCH_RESULTS = { // - Non-overlapping snippets can be joined with a " ... " in between. // +typedef std::vector Snippets; + std::string maskSnippetsInHtmlSearchResults(std::string s) { return replace(s, ".+", "SNIPPET TEXT WAS MASKED"); } +Snippets extractSearchResultSnippetsFromHtml(const std::string& html) +{ + Snippets snippets; + const std::regex snippetRegex("(.*)"); + 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; +} + bool isValidSnippet(const std::string& s) { return s.size() >= 250 @@ -731,22 +747,6 @@ struct TestData checkSnippets(extractSearchResultSnippetsFromHtml(html)); } - typedef std::vector Snippets; - - static Snippets extractSearchResultSnippetsFromHtml(const std::string& html) - { - Snippets snippets; - const std::regex snippetRegex("(.*)"); - 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; - } - void checkSnippets(const Snippets& snippets) const { ASSERT_EQ(snippets.size(), results.size()); From f27976943569e1097b50e3aed586608c2ce5343b Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sun, 5 Jun 2022 00:33:36 +0400 Subject: [PATCH 08/10] Deduplicated the snippet regex --- test/server_search.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/server_search.cpp b/test/server_search.cpp index 5aed75c8a..6749ad323 100644 --- a/test/server_search.cpp +++ b/test/server_search.cpp @@ -548,15 +548,17 @@ const std::vector LARGE_SEARCH_RESULTS = { 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("(.*)"); + 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) From 06d7a2320fd81de0a57eb6fa0c8b00952c5cfc1e Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sat, 4 Jun 2022 21:18:59 +0400 Subject: [PATCH 09/10] test/server_search.cpp covers XML search too --- test/server_search.cpp | 149 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 2 deletions(-) diff --git a/test/server_search.cpp b/test/server_search.cpp index 6749ad323..16e4d7fd8 100644 --- a/test/server_search.cpp +++ b/test/server_search.cpp @@ -137,6 +137,25 @@ std::string makeSearchResultsHtml(const std::string& pattern, return html; } +std::string makeSearchResultsXml(const std::string& header, + const std::string& results) +{ + const char SEARCHRESULTS_XML_TEMPLATE[] = R"XML( + + + %HEADER%%RESULTS% + + +)XML"; + + std::string html = removeEOLWhitespaceMarkers(SEARCHRESULTS_XML_TEMPLATE); + html = replace(html, "%HEADER%", header); + html = replace(html, "%RESULTS%", results); + return html; +} + struct SearchResult { std::string link; @@ -155,6 +174,18 @@ struct SearchResult + "
    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) \ @@ -569,6 +600,27 @@ Snippets extractSearchResultSnippetsFromHtml(const std::string& html) 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) { return s.size() >= 250 @@ -650,19 +702,37 @@ struct TestData return url; } - std::string getPattern() const + std::string extractQueryValue(const std::string& key) const { - const std::string p = "pattern="; + 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 ) { @@ -736,11 +806,70 @@ struct TestData ); } + std::string expectedXmlHeader() const + { + std::string header = R"(Search: PATTERN + URL + Search result for PATTERN + RESULTCOUNT + FIRSTRESULT + ITEMCOUNT +