#include #include "helpers.hpp" #include #include #include #include "catch.hpp" using iter::groupby; namespace { int length(const std::string& s) { return s.size(); } struct Sizer { int operator()(const std::string& s) { return s.size(); } }; const std::vector vec = { "hi", "ab", "ho", "abc", "def", "abcde", "efghi"}; } TEST_CASE("groupby: works with lambda, callable, and function pointer") { std::vector keys; std::vector> groups; SECTION("Function pointer") { for (auto&& gb : groupby(vec, length)) { keys.push_back(gb.first); groups.emplace_back(std::begin(gb.second), std::end(gb.second)); } } SECTION("Callable object") { for (auto&& gb : groupby(vec, Sizer{})) { keys.push_back(gb.first); groups.emplace_back(std::begin(gb.second), std::end(gb.second)); } } SECTION("lambda function") { for (auto&& gb : groupby(vec, [](const std::string& s) { return s.size(); })) { keys.push_back(gb.first); groups.emplace_back(std::begin(gb.second), std::end(gb.second)); } } const std::vector kc = {2, 3, 5}; REQUIRE(keys == kc); const std::vector> gc = { {"hi", "ab", "ho"}, {"abc", "def"}, {"abcde", "efghi"}, }; REQUIRE(groups == gc); } TEST_CASE("groupby: groups can be skipped completely", "[groupby]") { std::vector keys; std::vector> groups; for (auto&& gb : groupby(vec, &length)) { if (gb.first == 3) { continue; } keys.push_back(gb.first); groups.emplace_back(std::begin(gb.second), std::end(gb.second)); } const std::vector kc = {2, 5}; REQUIRE(keys == kc); const std::vector> gc = { {"hi", "ab", "ho"}, {"abcde", "efghi"}, }; REQUIRE(groups == gc); } TEST_CASE("groupby: groups can be skipped partially", "[groupby]") { std::vector keys; std::vector> groups; for (auto&& gb : groupby(vec, &length)) { keys.push_back(gb.first); if (gb.first == 3) { std::vector cut_short = {*std::begin(gb.second)}; groups.push_back(cut_short); } else { groups.emplace_back(std::begin(gb.second), std::end(gb.second)); } } const std::vector kc = {2, 3, 5}; REQUIRE(keys == kc); const std::vector> gc = { {"hi", "ab", "ho"}, {"abc"}, {"abcde", "efghi"}, }; REQUIRE(groups == gc); } TEST_CASE("groupby: single argument uses elements as keys", "[groupby]") { std::vector ivec = {5, 5, 6, 6, 19, 19, 19, 19, 69, 0, 10, 10}; std::vector keys; std::vector> groups; for (auto&& gb : groupby(ivec)) { keys.push_back(gb.first); groups.emplace_back(std::begin(gb.second), std::end(gb.second)); } const std::vector kc = {5, 6, 19, 69, 0, 10}; REQUIRE(keys == kc); std::vector> gc = { {5, 5}, {6, 6}, {19, 19, 19, 19}, {69}, {0}, {10, 10}, }; REQUIRE(groups == gc); } TEST_CASE("groupby: empty iterable yields nothing", "[groupby]") { std::vector ivec{}; auto g = groupby(ivec); REQUIRE(std::begin(g) == std::end(g)); } TEST_CASE("groupby: inner iterator (group) not used", "[groupby]") { std::vector keys; for (auto&& gb : groupby(vec, length)) { keys.push_back(gb.first); } std::vector kc = {2, 3, 5}; REQUIRE(keys == kc); } TEST_CASE("groupby: doesn't double dereference", "[groupby]") { itertest::InputIterable seq; for (auto&& kg : groupby(seq, [](int i) { return i < 3; })) { for (auto&& e : kg.second) { (void)e; } } } TEST_CASE("grouby: iterator doesn't need to be dereferenced before advanced", "[groupby]") { std::vector ns = {2, 4, 7}; auto g = groupby(ns); auto it = std::begin(g); ++it; REQUIRE((*it).first == 4); } TEST_CASE("groupby: iterator can be dereferenced multiple times", "[groupby]") { std::vector ns = {2, 4, 7}; auto g = groupby(ns); auto it = std::begin(g); auto k1 = (*it).first; auto k2 = (*it).first; REQUIRE(k1 == k2); } TEST_CASE( "groupby: copy constructed iterators behave as expected", "[groupby]") { std::vector ns = {2, 3, 4, 5}; auto g = groupby(ns); auto it = std::begin(g); REQUIRE(it->first == 2); { auto it2 = it; REQUIRE(it2->first == 2); ++it; REQUIRE(it->first == 3); REQUIRE(*std::begin(it->second) == 3); } REQUIRE(it->first == 3); REQUIRE(*std::begin(it->second) == 3); } TEST_CASE("groupby: operator-> on both iterator types", "[groupby]") { std::vector ns = {"a", "abc"}; auto g = groupby(ns, [](const std::string& str) { return str.size(); }); auto it = std::begin(g); REQUIRE(it->first == 1); auto it2 = std::begin(it->second); REQUIRE(it2->size() == 1); } TEST_CASE("groupby: iterator and groupiterator are correct", "[groupby]") { std::string s{"abc"}; auto c = groupby(s); auto it = std::begin(c); REQUIRE(itertest::IsIterator::value); auto&& gp = (*it).second; auto it2 = std::begin(gp); REQUIRE(itertest::IsIterator::value); } template using ImpT = decltype(groupby(std::declval(), std::declval())); TEST_CASE("groupby: has correct ctor and assign ops", "[groupby]") { using T1 = ImpT; auto lam = [](char) { return false; }; using T2 = ImpT; REQUIRE(itertest::IsMoveConstructibleOnly::value); REQUIRE(itertest::IsMoveConstructibleOnly::value); }