From 4b09788441be2486783177bc2fc914ea5d096ea8 Mon Sep 17 00:00:00 2001 From: Manish Date: Sat, 25 Feb 2023 12:11:38 +1100 Subject: [PATCH] feat: metamorphic test and partial verification --- ...t_substring_with_k_distinct_characters.cpp | 79 ++++++++++++++++--- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/1_longest_substring_with_k_distinct_characters.cpp b/1_longest_substring_with_k_distinct_characters.cpp index c33466f..580dcec 100644 --- a/1_longest_substring_with_k_distinct_characters.cpp +++ b/1_longest_substring_with_k_distinct_characters.cpp @@ -4,12 +4,15 @@ Possible references include: https://stackoverflow.com/a/7304184 - custom delimeter for istream */ +#include "lib_random.h" +#include #include #include #include #include #include #include +#include #include struct semicolon_is_space : std::ctype { @@ -25,8 +28,8 @@ struct semicolon_is_space : std::ctype { class input { public: - std::string s; - unsigned long k; + std::string s = ""; + unsigned long k = 1; friend std::istream &operator>>(std::istream &in, input &i); friend std::ostream &operator<<(std::ostream &out, const input &i); }; @@ -121,7 +124,34 @@ result find(const std::string &s, const unsigned long k) { return r; } +void test(const test_case &t) { + std::cout << t; + if (find(t.i.s, t.i.k) == t.r) { + std::cout << "Test case with string \"" << t.i.s << "\" and k=" << t.i.k + << " passed.\n"; + } else { + std::cout << std::flush; + std::cerr << "TEST CASE WITH STRING \"" << t.i.s << "\" AND k=" << t.i.k + << " FAILED.\n"; + } + std::cout << "\n"; +} + +bool K_verify(const test_case &t) { + // Verifies if found solution has exactly K unique characters (one of the + // problem requirement) + if (!t.r.match_found) + return true; // Do not check if no match found + std::unordered_set unique_chars; + for (const char c : t.i.s.substr(t.r.longest_start, + t.r.longest_end - t.r.longest_start + 1)) { + unique_chars.insert(c); + } + return unique_chars.size() == t.i.k; +} + int main(int argc, char *argv[]) { + std::cout << "Processing static test cases from input file (if any)\n"; semicolon_is_space delimeter; for (int i = 1; i < argc; i++) { std::ifstream f(argv[i]); @@ -130,16 +160,43 @@ int main(int argc, char *argv[]) { test_case t; f >> t; if (t.i.s.length()) { // skip empty line/string inputs - std::cout << t; - if (find(t.i.s, t.i.k) == t.r) { - std::cout << "Test case with string \"" << t.i.s - << "\" and k=" << t.i.k << " passed.\n"; - } else { - std::cout << std::flush; - std::cerr << "TEST CASE WITH STRING \"" << t.i.s - << "\" AND k=" << t.i.k << " FAILED.\n"; + test(t); + } + } + + std::cout + << "\n\n\nPerforming metamorphic tests from randomly generated input " + "(string and K values)" + << std::endl; + random_number_generator random_ascii_char(97, 122); // a-z + random_number_generator rng; + for (unsigned int i = 0; i < 1; i++) { + test_case t; + t.i.s += (char)random_ascii_char(); + std::cout << t; + t.r = find(t.i.s, t.i.k); + for (unsigned int j = 0; j < 10; j++) { + for (unsigned int k = 0, increase_str_len_by = rng(100, 1000); + k < increase_str_len_by; k++) { + t.i.s += (char)random_ascii_char(); + std::cout << t; + result r = find(t.i.s, t.i.k); + if (r.match_found && t.r.match_found) + assert(r.longest_end - r.longest_start >= + t.r.longest_end - t.r.longest_start); + t.r = r; + assert(K_verify(t)); + } + while (t.r.match_found) { + std::cout << t; + result r = find(t.i.s, ++t.i.k); + if (r.match_found) + assert(r.longest_end - r.longest_start >= + t.r.longest_end - t.r.longest_start); + t.r = r; + assert(K_verify(t)); } - std::cout << "\n"; + t.i.k = rng(1, --t.i.k); } } }