diff --git a/src/file_contents.h b/src/file_contents.h index 5394a198..0a6cffb5 100644 --- a/src/file_contents.h +++ b/src/file_contents.h @@ -5,6 +5,7 @@ #include "optional.h" #include +#include #include struct FileContents { diff --git a/src/query.cc b/src/query.cc index ba75ac34..c6d71180 100644 --- a/src/query.cc +++ b/src/query.cc @@ -30,6 +30,15 @@ void VerifyUnique(const std::vector& values0) { #endif } +template +void RemoveRange(std::vector* dest, const std::vector& to_remove) { + std::unordered_set to_remove_set(to_remove.begin(), to_remove.end()); + dest->erase( + std::remove_if(dest->begin(), dest->end(), + [&](const T& t) { return to_remove_set.count(t) > 0; }), + dest->end()); +} + optional ToQuery(const IdMap& id_map, const IndexType::Def& type) { if (type.detailed_name.empty()) diff --git a/src/test.cc b/src/test.cc index 01d17613..c957d04a 100644 --- a/src/test.cc +++ b/src/test.cc @@ -5,8 +5,12 @@ #include "serializer.h" #include "utils.h" +#include +#include + #include #include +#include #include // The 'diff' utility is available and we can use dprintf(3). @@ -33,6 +37,155 @@ std::string ToString(const rapidjson::Document& document) { return UpdateToRnNewlines(output); } +void ParseTestExpectation( + const std::string& filename, + const std::vector& lines_with_endings, + TextReplacer* replacer, + std::vector* flags, + std::unordered_map* output_sections) { +#if false +#include "bar.h" + + void foo(); + + /* + // DOCS for TEXT_REPLACE: + // Each line under TEXT_REPLACE is a replacement, ie, the two entries will be + // considered equivalent. This is useful for USRs which vary across files. + + // DOCS for EXTRA_FLAGS: + // Additional flags to pass to clang. + + // DOCS for OUTPUT: + // If no name is given assume to be this file name. If there is not an output + // section for a file it is not checked. + + TEXT_REPLACE: + foo <===> bar + one <===> two + + EXTRA_FLAGS: + -std=c++14 + + OUTPUT: + {} + + OUTPUT: bar.cc + {} + + OUTPUT: bar.h + {} + */ +#endif + + // Scan for TEXT_REPLACE: + { + bool in_output = false; + for (std::string line : lines_with_endings) { + TrimInPlace(line); + + if (StartsWith(line, "TEXT_REPLACE:")) { + assert(!in_output && "multiple TEXT_REPLACE sections"); + in_output = true; + continue; + } + + if (in_output && line.empty()) + break; + + if (in_output) { + static const std::string kKey = " <===> "; + size_t index = line.find(kKey); + LOG_IF_S(FATAL, index == std::string::npos) + << " No '" << kKey << "' in replacement string '" << line << "'" + << ", index=" << index; + + TextReplacer::Replacement replacement; + replacement.from = line.substr(0, index); + replacement.to = line.substr(index + kKey.size()); + TrimInPlace(replacement.from); + TrimInPlace(replacement.to); + replacer->replacements.push_back(replacement); + } + } + } + + // Scan for EXTRA_FLAGS: + { + bool in_output = false; + for (std::string line : lines_with_endings) { + TrimInPlace(line); + + if (StartsWith(line, "EXTRA_FLAGS:")) { + assert(!in_output && "multiple EXTRA_FLAGS sections"); + in_output = true; + continue; + } + + if (in_output && line.empty()) + break; + + if (in_output) + flags->push_back(line); + } + } + + // Scan for OUTPUT: + { + std::string active_output_filename; + std::string active_output_contents; + + bool in_output = false; + for (std::string line_with_ending : lines_with_endings) { + if (StartsWith(line_with_ending, "*/")) + break; + + if (StartsWith(line_with_ending, "OUTPUT:")) { + // Terminate the previous output section if we found a new one. + if (in_output) { + (*output_sections)[active_output_filename] = active_output_contents; + } + + // Try to tokenize OUTPUT: based one whitespace. If there is more than + // one token assume it is a filename. + std::vector tokens = SplitString(line_with_ending, " "); + if (tokens.size() > 1) { + active_output_filename = tokens[1]; + TrimInPlace(active_output_filename); + } else { + active_output_filename = filename; + } + active_output_contents = ""; + + in_output = true; + } else if (in_output) + active_output_contents += line_with_ending; + } + + if (in_output) + (*output_sections)[active_output_filename] = active_output_contents; + } +} + +void UpdateTestExpectation(const std::string& filename, + const std::string& expectation, + const std::string& actual) { + // Read the entire file into a string. + std::ifstream in(filename); + std::string str; + str.assign(std::istreambuf_iterator(in), + std::istreambuf_iterator()); + in.close(); + + // Replace expectation + auto it = str.find(expectation); + assert(it != std::string::npos); + str.replace(it, expectation.size(), actual); + + // Write it back out. + WriteToFile(filename, str); +} + void DiffDocuments(std::string path, std::string path_section, rapidjson::Document& expected, @@ -309,3 +462,54 @@ bool RunIndexTests(const std::string& filter_path, bool enable_update) { // TODO: ctor/dtor, copy ctor // TODO: Always pass IndexFile by pointer, ie, search and remove all IndexFile& // refs. + +TEST_SUITE("ParseTestExpectation") { + TEST_CASE("Parse TEXT_REPLACE") { + // clang-format off + std::vector lines_with_endings = { + "/*\n", + "TEXT_REPLACE:\n", + " foo <===> \tbar \n", + "01 <===> 2\n", + "\n", + "*/\n"}; + // clang-format on + + TextReplacer text_replacer; + std::vector flags; + std::unordered_map all_expected_output; + ParseTestExpectation("foo.cc", lines_with_endings, &text_replacer, &flags, + &all_expected_output); + + REQUIRE(text_replacer.replacements.size() == 2); + REQUIRE(text_replacer.replacements[0].from == "foo"); + REQUIRE(text_replacer.replacements[0].to == "bar"); + REQUIRE(text_replacer.replacements[1].from == "01"); + REQUIRE(text_replacer.replacements[1].to == "2"); + } + + TEST_CASE("Apply TEXT_REPLACE") { + TextReplacer replacer; + replacer.replacements.push_back(TextReplacer::Replacement{"foo", "bar"}); + replacer.replacements.push_back(TextReplacer::Replacement{"01", "2"}); + replacer.replacements.push_back(TextReplacer::Replacement{"3", "456"}); + + // Equal-length. + REQUIRE(replacer.Apply("foo") == "bar"); + REQUIRE(replacer.Apply("bar") == "bar"); + + // Shorter replacement. + REQUIRE(replacer.Apply("01") == "2"); + REQUIRE(replacer.Apply("2") == "2"); + + // Longer replacement. + REQUIRE(replacer.Apply("3") == "456"); + REQUIRE(replacer.Apply("456") == "456"); + + // Content before-after replacement. + REQUIRE(replacer.Apply("aaaa01bbbb") == "aaaa2bbbb"); + + // Multiple replacements. + REQUIRE(replacer.Apply("foofoobar0123") == "barbarbar22456"); + } +} diff --git a/src/utils.cc b/src/utils.cc index 17722ad8..5d3cda96 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -374,155 +374,6 @@ std::string TextReplacer::Apply(const std::string& content) { return result; } -void ParseTestExpectation( - const std::string& filename, - const std::vector& lines_with_endings, - TextReplacer* replacer, - std::vector* flags, - std::unordered_map* output_sections) { -#if false -#include "bar.h" - - void foo(); - - /* - // DOCS for TEXT_REPLACE: - // Each line under TEXT_REPLACE is a replacement, ie, the two entries will be - // considered equivalent. This is useful for USRs which vary across files. - - // DOCS for EXTRA_FLAGS: - // Additional flags to pass to clang. - - // DOCS for OUTPUT: - // If no name is given assume to be this file name. If there is not an output - // section for a file it is not checked. - - TEXT_REPLACE: - foo <===> bar - one <===> two - - EXTRA_FLAGS: - -std=c++14 - - OUTPUT: - {} - - OUTPUT: bar.cc - {} - - OUTPUT: bar.h - {} - */ -#endif - - // Scan for TEXT_REPLACE: - { - bool in_output = false; - for (std::string line : lines_with_endings) { - TrimInPlace(line); - - if (StartsWith(line, "TEXT_REPLACE:")) { - assert(!in_output && "multiple TEXT_REPLACE sections"); - in_output = true; - continue; - } - - if (in_output && line.empty()) - break; - - if (in_output) { - static const std::string kKey = " <===> "; - size_t index = line.find(kKey); - LOG_IF_S(FATAL, index == std::string::npos) - << " No '" << kKey << "' in replacement string '" << line << "'" - << ", index=" << index; - - TextReplacer::Replacement replacement; - replacement.from = line.substr(0, index); - replacement.to = line.substr(index + kKey.size()); - TrimInPlace(replacement.from); - TrimInPlace(replacement.to); - replacer->replacements.push_back(replacement); - } - } - } - - // Scan for EXTRA_FLAGS: - { - bool in_output = false; - for (std::string line : lines_with_endings) { - TrimInPlace(line); - - if (StartsWith(line, "EXTRA_FLAGS:")) { - assert(!in_output && "multiple EXTRA_FLAGS sections"); - in_output = true; - continue; - } - - if (in_output && line.empty()) - break; - - if (in_output) - flags->push_back(line); - } - } - - // Scan for OUTPUT: - { - std::string active_output_filename; - std::string active_output_contents; - - bool in_output = false; - for (std::string line_with_ending : lines_with_endings) { - if (StartsWith(line_with_ending, "*/")) - break; - - if (StartsWith(line_with_ending, "OUTPUT:")) { - // Terminate the previous output section if we found a new one. - if (in_output) { - (*output_sections)[active_output_filename] = active_output_contents; - } - - // Try to tokenize OUTPUT: based one whitespace. If there is more than - // one token assume it is a filename. - std::vector tokens = SplitString(line_with_ending, " "); - if (tokens.size() > 1) { - active_output_filename = tokens[1]; - TrimInPlace(active_output_filename); - } else { - active_output_filename = filename; - } - active_output_contents = ""; - - in_output = true; - } else if (in_output) - active_output_contents += line_with_ending; - } - - if (in_output) - (*output_sections)[active_output_filename] = active_output_contents; - } -} - -void UpdateTestExpectation(const std::string& filename, - const std::string& expectation, - const std::string& actual) { - // Read the entire file into a string. - std::ifstream in(filename); - std::string str; - str.assign(std::istreambuf_iterator(in), - std::istreambuf_iterator()); - in.close(); - - // Replace expectation - auto it = str.find(expectation); - assert(it != std::string::npos); - str.replace(it, expectation.size(), actual); - - // Write it back out. - WriteToFile(filename, str); -} - void WriteToFile(const std::string& filename, const std::string& content) { std::ofstream file(filename, std::ios::out | std::ios::trunc | std::ios::binary); @@ -601,57 +452,6 @@ std::string UpdateToRnNewlines(std::string output) { return output; }; -TEST_SUITE("ParseTestExpectation") { - TEST_CASE("Parse TEXT_REPLACE") { - // clang-format off - std::vector lines_with_endings = { - "/*\n", - "TEXT_REPLACE:\n", - " foo <===> \tbar \n", - "01 <===> 2\n", - "\n", - "*/\n"}; - // clang-format on - - TextReplacer text_replacer; - std::vector flags; - std::unordered_map all_expected_output; - ParseTestExpectation("foo.cc", lines_with_endings, &text_replacer, &flags, - &all_expected_output); - - REQUIRE(text_replacer.replacements.size() == 2); - REQUIRE(text_replacer.replacements[0].from == "foo"); - REQUIRE(text_replacer.replacements[0].to == "bar"); - REQUIRE(text_replacer.replacements[1].from == "01"); - REQUIRE(text_replacer.replacements[1].to == "2"); - } - - TEST_CASE("Apply TEXT_REPLACE") { - TextReplacer replacer; - replacer.replacements.push_back(TextReplacer::Replacement{"foo", "bar"}); - replacer.replacements.push_back(TextReplacer::Replacement{"01", "2"}); - replacer.replacements.push_back(TextReplacer::Replacement{"3", "456"}); - - // Equal-length. - REQUIRE(replacer.Apply("foo") == "bar"); - REQUIRE(replacer.Apply("bar") == "bar"); - - // Shorter replacement. - REQUIRE(replacer.Apply("01") == "2"); - REQUIRE(replacer.Apply("2") == "2"); - - // Longer replacement. - REQUIRE(replacer.Apply("3") == "456"); - REQUIRE(replacer.Apply("456") == "456"); - - // Content before-after replacement. - REQUIRE(replacer.Apply("aaaa01bbbb") == "aaaa2bbbb"); - - // Multiple replacements. - REQUIRE(replacer.Apply("foofoobar0123") == "barbarbar22456"); - } -} - TEST_SUITE("Update \\n to \\r\\n") { TEST_CASE("all") { REQUIRE(UpdateToRnNewlines("\n") == "\r\n"); diff --git a/src/utils.h b/src/utils.h index 4bbb2e44..204d3467 100644 --- a/src/utils.h +++ b/src/utils.h @@ -7,8 +7,6 @@ #include #include #include -#include -#include #include // Trim from start (in place) @@ -111,17 +109,6 @@ struct TextReplacer { std::string Apply(const std::string& content); }; -void ParseTestExpectation( - const std::string& filename, - const std::vector& lines_with_endings, - TextReplacer* text_replacer, - std::vector* flags, - std::unordered_map* output_sections); - -void UpdateTestExpectation(const std::string& filename, - const std::string& expectation, - const std::string& actual); - void WriteToFile(const std::string& filename, const std::string& content); // note: this implementation does not disable this overload for array types @@ -143,15 +130,6 @@ void AddRange(std::vector* dest, std::vector&& to_add) { std::make_move_iterator(to_add.end())); } -template -void RemoveRange(std::vector* dest, const std::vector& to_remove) { - std::unordered_set to_remove_set(to_remove.begin(), to_remove.end()); - dest->erase( - std::remove_if(dest->begin(), dest->end(), - [&](const T& t) { return to_remove_set.count(t) > 0; }), - dest->end()); -} - // http://stackoverflow.com/a/38140932 // // struct SomeHashKey {