Ver Fonte

test: extract filter, fold, iota, join, to_container, transform

Sam Jaffe há 2 anos atrás
pai
commit
d285f2bed1

+ 2 - 0
include/stream/view/join.h

@@ -15,6 +15,8 @@ public:
   auto begin() const { return iterator::joining_iterator(stream_); }
   auto end() const { return iterator::sentinel; }
 };
+
+template <typename S> join_view(S &&) -> join_view<S>;
 }
 
 namespace stream::ranges::views {

+ 28 - 0
stream.xcodeproj/project.pbxproj

@@ -8,6 +8,13 @@
 
 /* Begin PBXBuildFile section */
 		CD52820429D3B1AB001A84DE /* stream in Headers */ = {isa = PBXBuildFile; fileRef = CDAA170121A3A738007BBA11 /* stream */; settings = {ATTRIBUTES = (Public, ); }; };
+		CD5A559629DE2B3400881E7E /* to_container_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD5A559529DE2B3400881E7E /* to_container_test.cxx */; };
+		CD5A559829DE2DB900881E7E /* transform_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD5A559729DE2DB900881E7E /* transform_test.cxx */; };
+		CD5A559A29DE34B800881E7E /* filter_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD5A559929DE34B800881E7E /* filter_test.cxx */; };
+		CD5A559C29DE353800881E7E /* fold_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD5A559B29DE353800881E7E /* fold_test.cxx */; };
+		CD5A559E29DE35E900881E7E /* join_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD5A559D29DE35E900881E7E /* join_test.cxx */; };
+		CD5A55A029DE375B00881E7E /* composed_views_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD5A559F29DE375B00881E7E /* composed_views_test.cxx */; };
+		CD5A55A229DE37B400881E7E /* iota_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD5A55A129DE37B400881E7E /* iota_test.cxx */; };
 		CDA37D4029D9D38E000A1F97 /* all_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDA37D3E29D9D38E000A1F97 /* all_test.cxx */; };
 		CDA54F9E29DA59C4006C0FAA /* common_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDA54F9D29DA59C4006C0FAA /* common_test.cxx */; };
 		CDEC1D7623514BEB0091D9F2 /* stream_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD9337281E3CD78B00699FF5 /* stream_test.cxx */; };
@@ -88,6 +95,13 @@
 		CD52828029D51166001A84DE /* identity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = identity.h; sourceTree = "<group>"; };
 		CD52828129D5133C001A84DE /* map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = map.h; sourceTree = "<group>"; };
 		CD5A559429DCD12300881E7E /* stream_helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stream_helpers.h; sourceTree = "<group>"; };
+		CD5A559529DE2B3400881E7E /* to_container_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = to_container_test.cxx; sourceTree = "<group>"; };
+		CD5A559729DE2DB900881E7E /* transform_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = transform_test.cxx; sourceTree = "<group>"; };
+		CD5A559929DE34B800881E7E /* filter_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = filter_test.cxx; sourceTree = "<group>"; };
+		CD5A559B29DE353800881E7E /* fold_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = fold_test.cxx; sourceTree = "<group>"; };
+		CD5A559D29DE35E900881E7E /* join_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = join_test.cxx; sourceTree = "<group>"; };
+		CD5A559F29DE375B00881E7E /* composed_views_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = composed_views_test.cxx; sourceTree = "<group>"; };
+		CD5A55A129DE37B400881E7E /* iota_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = iota_test.cxx; sourceTree = "<group>"; };
 		CD5A8D3529D63C05008C2A4F /* count.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = count.h; sourceTree = "<group>"; };
 		CD5A8D3B29D63DF5008C2A4F /* named_pair.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = named_pair.h; sourceTree = "<group>"; };
 		CD64CCB926232D6900770A30 /* xcode_gtest_helper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xcode_gtest_helper.h; sourceTree = "<group>"; };
@@ -221,6 +235,13 @@
 				CD9337281E3CD78B00699FF5 /* stream_test.cxx */,
 				CDA37D3E29D9D38E000A1F97 /* all_test.cxx */,
 				CDA54F9D29DA59C4006C0FAA /* common_test.cxx */,
+				CD5A559F29DE375B00881E7E /* composed_views_test.cxx */,
+				CD5A559929DE34B800881E7E /* filter_test.cxx */,
+				CD5A559B29DE353800881E7E /* fold_test.cxx */,
+				CD5A55A129DE37B400881E7E /* iota_test.cxx */,
+				CD5A559D29DE35E900881E7E /* join_test.cxx */,
+				CD5A559529DE2B3400881E7E /* to_container_test.cxx */,
+				CD5A559729DE2DB900881E7E /* transform_test.cxx */,
 			);
 			path = test;
 			sourceTree = "<group>";
@@ -475,9 +496,16 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD5A559C29DE353800881E7E /* fold_test.cxx in Sources */,
 				CDA54F9E29DA59C4006C0FAA /* common_test.cxx in Sources */,
+				CD5A559A29DE34B800881E7E /* filter_test.cxx in Sources */,
 				CDEC1D7623514BEB0091D9F2 /* stream_test.cxx in Sources */,
+				CD5A55A229DE37B400881E7E /* iota_test.cxx in Sources */,
+				CD5A559629DE2B3400881E7E /* to_container_test.cxx in Sources */,
+				CD5A55A029DE375B00881E7E /* composed_views_test.cxx in Sources */,
+				CD5A559E29DE35E900881E7E /* join_test.cxx in Sources */,
 				CDA37D4029D9D38E000A1F97 /* all_test.cxx in Sources */,
+				CD5A559829DE2DB900881E7E /* transform_test.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 20 - 0
test/composed_views_test.cxx

@@ -0,0 +1,20 @@
+//
+//  composed_views_test.cxx
+//  stream-test
+//
+//  Created by Sam Jaffe on 4/5/23.
+//
+
+#include "stream/streams.hpp"
+
+#include "stream_helpers.h"
+#include "stream_matchers.h"
+
+TEST(ComposedView, FilterAndTransform) {
+  std::vector<int> input{1, 2, 3, 4, 5};
+
+  auto range = input | views::filter([](int i) { return i % 2 == 0; }) |
+               views::transform([](int i) { return i / 2; });
+
+  EXPECT_THAT(range, RangesEq(std::vector{1, 2}));
+}

+ 38 - 0
test/filter_test.cxx

@@ -0,0 +1,38 @@
+//
+//  filter_test.cxx
+//  stream-test
+//
+//  Created by Sam Jaffe on 4/5/23.
+//
+
+#include "stream/view/filter.h"
+
+#include "stream_helpers.h"
+#include "stream_matchers.h"
+
+TEST(FilterView, DoesNotPropagatesSize) {
+  Range<std::vector<int>, Sentinel, Sized> input{0, 1, 2, 3, 4};
+
+  auto range = input | views::filter([](int) { return true; });
+  static_assert(!stream::detail::has_size_v<decltype(range)>);
+}
+
+TEST(FilterView, DoesNotPropagatesEmpty) {
+  Range<std::vector<int>, Sentinel, Sized> input{0, 1, 2, 3, 4};
+
+  auto range = input | views::filter([](int) { return true; });
+  static_assert(!stream::detail::has_empty_v<decltype(range)>);
+}
+
+TEST(FilterView, NoOpFilterReturnOriginal) {
+  std::vector<int> const input{1, 2, 3, 4, 5};
+
+  EXPECT_THAT(input | views::filter([](int) { return true; }), RangesEq(input));
+}
+
+TEST(FilterView, CanFilterOutElements) {
+  std::vector<int> const input{1, 2, 3, 4, 5};
+
+  EXPECT_THAT(input | views::filter([](int i) { return i % 2 == 0; }),
+              RangesEq(std::vector{2, 4}));
+}

+ 18 - 0
test/fold_test.cxx

@@ -0,0 +1,18 @@
+//
+//  fold_test.cxx
+//  stream-test
+//
+//  Created by Sam Jaffe on 4/5/23.
+//
+
+#include "stream/algorithm/fold.h"
+
+#include "stream_helpers.h"
+#include "stream_matchers.h"
+
+TEST(FoldLeft, DifferentInitMakesDifferentResult) {
+  std::vector<int> const input{1, 2, 3, 4, 5};
+
+  EXPECT_THAT(ranges::fold_left(input, 0, std::multiplies<>()), 0);
+  EXPECT_THAT(ranges::fold_left(input, 1, std::multiplies<>()), 120);
+}

+ 25 - 0
test/iota_test.cxx

@@ -0,0 +1,25 @@
+//
+//  iota_test.cxx
+//  stream-test
+//
+//  Created by Sam Jaffe on 4/5/23.
+//
+
+#include "stream/view/iota.h"
+
+#include "stream_helpers.h"
+#include "stream_matchers.h"
+
+TEST(IotaView, ConcreteHasEmpty) {
+  auto range = views::iota(0, 4);
+  static_assert(stream::detail::has_empty_v<decltype(range)>);
+}
+
+TEST(IotaView, ConcreteHasSize) {
+  auto range = views::iota(0, 4);
+  static_assert(stream::detail::has_size_v<decltype(range)>);
+}
+
+TEST(IotaView, CountsFromStartToEnd) {
+  EXPECT_THAT(views::iota(0, 4), RangesEq(std::vector{0, 1, 2, 3}));
+}

+ 31 - 0
test/join_test.cxx

@@ -0,0 +1,31 @@
+//
+//  join_test.cxx
+//  stream-test
+//
+//  Created by Sam Jaffe on 4/5/23.
+//
+
+#include "stream/view/join.h"
+
+#include "stream/view/transform.h"
+
+#include "stream_helpers.h"
+#include "stream_matchers.h"
+
+TEST(JoinView, CanJoinCollectionOfCollections) {
+  std::vector<std::vector<int>> const input{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}};
+
+  EXPECT_THAT(input | views::join(),
+              RangesEq(std::vector{1, 2, 3, 2, 3, 4, 3, 4, 5}));
+}
+
+TEST(JoinView, CanJoinTemporaryCollections) {
+  std::vector<int> const input{1, 2, 3};
+
+  auto const range = input | views::transform([](int i) {
+                       return std::vector{i, i + 1, i + 2};
+                     }) |
+                     views::join();
+
+  EXPECT_THAT(range, RangesEq(std::vector{1, 2, 3, 2, 3, 4, 3, 4, 5}));
+}

+ 0 - 161
test/stream_test.cxx

@@ -20,83 +20,6 @@ using ::testing::Eq;
 namespace views = stream::views;
 namespace ranges = stream::ranges;
 
-// Workaround for OSX and pointer-to-member-functions
-template class std::basic_string<char>;
-
-TEST(StreamTest, IteratorPreservesElements) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto s = views::all(input);
-  std::vector<int> out{s.begin(), s.end()};
-
-  EXPECT_THAT(out, Eq(input));
-}
-
-TEST(MapStreamTest, IteratorPreservesElements) {
-  std::map<int, int> input{{1, 1}, {2, 2}};
-  auto s = views::all(input);
-  std::map<int, int> out{s.begin(), s.end()};
-
-  EXPECT_THAT(out, Eq(input));
-}
-
-TEST(StreamTest, CollectPreservesElements) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  std::vector<int> out = views::all(input) | ranges::to_vector();
-
-  EXPECT_THAT(out, Eq(input));
-}
-
-TEST(StreamTest, CollectToObjectPreservesElements) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto s = views::all(input);
-  std::set<int> out = s | ranges::to_set();
-
-  EXPECT_THAT(out, ElementsAreArray(input));
-}
-
-TEST(StreamTest, MapToSelfIsSelfs) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto identity = [](int i) { return i; };
-  auto out = input | views::transform(identity) | ranges::to_vector();
-
-  EXPECT_THAT(out, Eq(input));
-}
-
-TEST(StreamTest, MapCanAlterValues) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  std::vector<int> expected{3, 5, 7, 9, 11};
-  auto fmap = [](int i) { return 2 * i + 1; };
-  auto out = input | views::transform(fmap) | ranges::to_vector();
-
-  EXPECT_THAT(out, Eq(expected));
-}
-
-template <typename T> struct nocopy {
-  T value;
-  nocopy(T const & val) : value(val) {}
-  nocopy(nocopy const &) = delete;
-  nocopy & operator=(nocopy const &) = delete;
-  nocopy(nocopy &&) = default;
-  nocopy & operator=(nocopy &&) = default;
-
-  operator T() const { return value; }
-};
-
-TEST(MapStreamTest, MapToValue) {
-  auto const input = []() {
-    std::map<int, nocopy<int>> tmp;
-    tmp.emplace(0, 1);
-    tmp.emplace(2, 2);
-    return tmp;
-  }();
-  auto fmap = [](auto & pair) -> auto & { return pair.second; };
-  auto s = input | views::transform(fmap);
-  std::vector<int> out(s.begin(), s.end());
-  std::vector<int> const expected{1, 2};
-
-  EXPECT_THAT(out, Eq(expected));
-}
-
 TEST(StreamTest, CanBuildFromSingleElement) {
   int value = 11;
   auto even = [](int i) { return i % 2 == 0; };
@@ -115,63 +38,6 @@ TEST(StreamTest, CanBuildFromIterators) {
   EXPECT_THAT(out, Eq(expected));
 }
 
-TEST(StreamTest, NoOpFilterReturnOriginal) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto pass = [](int) { return true; };
-  auto out = input | views::filter(pass) | ranges::to_vector();
-
-  EXPECT_THAT(out, Eq(input));
-}
-
-TEST(StreamTest, CanFilterOutElements) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  std::vector<int> expected{2, 4};
-  auto even = [](int i) { return i % 2 == 0; };
-  auto out = input | views::filter(even) | ranges::to_vector();
-
-  EXPECT_THAT(out, Eq(expected));
-}
-
-TEST(StreamTest, CanTransformFilteredElements) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-
-  auto range = input | views::filter([](int i) { return i % 2 == 0; }) |
-               views::transform([](int i) { return i / 2; });
-
-  auto out = range | ranges::to_vector();
-  EXPECT_THAT(out, ElementsAre(1, 2));
-
-  ranges::for_each(range, [&](int i) { out.push_back(i); });
-  EXPECT_THAT(out, ElementsAre(1, 2, 1, 2));
-}
-
-TEST(StreamTest, AccumulateDefaultsToAdd) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto even = [](int i) { return i % 2 == 0; };
-  auto s = input | views::filter(even) | ranges::to_vector();
-
-  EXPECT_THAT(ranges::fold_left(s, 0, std::plus<>()), Eq(6));
-}
-
-TEST(StreamTest, AccumulateCanTakeCustomAccumulator) {
-  std::vector<int> input{1, 2, 3, 4, 5};
-  auto even = [](int i) { return i % 2 == 0; };
-  auto prod = [](int lhs, int rhs) { return lhs * rhs; };
-  auto s = input | views::filter(even) | ranges::to_vector();
-
-  EXPECT_THAT(ranges::fold_left(s, 0, prod), Eq(0));
-  EXPECT_THAT(ranges::fold_left(s, 1, prod), Eq(8));
-}
-
-TEST(StreamTest, FlatmapJoinsIterableOutputs) {
-  std::vector<int> vv{1, 2, 3, 4, 5};
-  auto next3 = [](int i) { return std::vector<int>{i, i + 1, i + 2}; };
-  std::vector<int> expected{1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7};
-  auto out = vv | views::transform(next3) | views::join() | ranges::to_vector();
-
-  EXPECT_THAT(out, Eq(expected));
-}
-
 TEST(StreamTest, CanDereferenceElements) {
   int val = 5;
   std::vector<int *> input{&val};
@@ -188,24 +54,6 @@ TEST(StreamTest, CanForEachConsume) {
   EXPECT_THAT(hits, Eq(5));
 }
 
-TEST(StreamTest, CanFetchMemPtr) {
-  struct test {
-    int val;
-  };
-  std::vector<test> input{{1}, {3}, {2}};
-  std::vector<int> expected{1, 3, 2};
-  auto out = input | views::transform(&test::val) | ranges::to_vector();
-
-  EXPECT_THAT(out, Eq(expected));
-}
-
-TEST(StreamTest, CanMapToMemFn) {
-  std::vector<std::string> input{"hello", "goodbye"};
-  std::vector<std::string::size_type> expected{5, 7};
-  auto out = input | views::transform(&std::string::size) | ranges::to_vector();
-
-  EXPECT_THAT(out, Eq(expected));
-}
 //
 // TEST(StreamTest, CastStreamToParentType) {
 //  struct base {
@@ -225,12 +73,3 @@ TEST(StreamTest, CanMapToMemFn) {
 //
 //  EXPECT_THAT(first, second);
 //}
-
-TEST(StreamTest, Iota) {
-  auto out = views::iota(0, 4) |
-             views::transform([](size_t i) { return std::vector(i, i); }) |
-             views::join() | ranges::to_vector();
-
-  std::vector<size_t> expected{1, 2, 2, 3, 3, 3};
-  EXPECT_THAT(out, expected);
-}

+ 49 - 0
test/to_container_test.cxx

@@ -0,0 +1,49 @@
+//
+//  to_container_test.cxx
+//  stream-test
+//
+//  Created by Sam Jaffe on 4/5/23.
+//
+
+#include "stream/to_container.h"
+
+#include "stream_helpers.h"
+#include "stream_matchers.h"
+
+using testing::ElementsAre;
+using testing::Pair;
+using testing::StaticAssertTypeEq;
+
+TEST(ToContainer, ConvertsToSet) {
+  std::vector<int> const input{0, 1, 2, 1, 2};
+
+  auto output = input | ranges::to_set();
+  StaticAssertTypeEq<decltype(output), std::set<int>>();
+  EXPECT_THAT(output, ElementsAre(0, 1, 2));
+}
+
+TEST(ToContainer, ConvertsToVector) {
+  std::set<int> const input{0, 1, 2};
+
+  auto output = input | ranges::to_vector();
+  StaticAssertTypeEq<decltype(output), std::vector<int>>();
+  EXPECT_THAT(output, ElementsAre(0, 1, 2));
+}
+
+TEST(ToContainer, CanConvertPairContainerToAssoc) {
+  std::vector<std::pair<int, std::string>> const input{{0, "hello"},
+                                                       {1, "goodbye"}};
+
+  auto output = input | ranges::to_map();
+  StaticAssertTypeEq<decltype(output), std::map<int, std::string>>();
+  EXPECT_THAT(output, ElementsAre(Pair(0, "hello"), Pair(1, "goodbye")));
+}
+
+TEST(ToContainer, CanConvertAssocToPairContainer) {
+  std::map<int, std::string> const input{{0, "hello"}, {1, "goodbye"}};
+
+  auto output = input | ranges::to_vector();
+  StaticAssertTypeEq<decltype(output),
+                     std::vector<std::pair<int const, std::string>>>();
+  EXPECT_THAT(output, ElementsAre(Pair(0, "hello"), Pair(1, "goodbye")));
+}

+ 71 - 0
test/transform_test.cxx

@@ -0,0 +1,71 @@
+//
+//  transform_test.cxx
+//  stream-test
+//
+//  Created by Sam Jaffe on 4/5/23.
+//
+
+#include "stream/view/transform.h"
+
+#include "stream_helpers.h"
+#include "stream_matchers.h"
+
+// Workaround for OSX and pointer-to-member-functions
+template class std::basic_string<char>;
+
+TEST(TransformView, NotRequiredToProvideSizeOrEmpty) {
+  Range<std::vector<int>, Sentinel> input{0, 1, 2, 3, 4};
+
+  auto range = input | views::transform([](int i) { return i; });
+
+  static_assert(!stream::detail::has_size_v<decltype(range)>);
+  static_assert(!stream::detail::has_empty_v<decltype(range)>);
+}
+
+TEST(TransformView, PropagatesSize) {
+  Range<std::vector<int>, Sentinel, Sized> input{0, 1, 2, 3, 4};
+
+  auto range = input | views::transform([](int i) { return i; });
+
+  static_assert(stream::detail::has_size_v<decltype(range)>);
+  EXPECT_THAT(range.size(), input.size());
+}
+
+TEST(TransformView, PropagatesEmpty) {
+  Range<std::vector<int>, Sentinel, Sized> input{0, 1, 2, 3, 4};
+
+  auto range = input | views::transform([](int i) { return i; });
+
+  static_assert(stream::detail::has_empty_v<decltype(range)>);
+  EXPECT_THAT(range.empty(), input.empty());
+}
+
+TEST(TransformView, ReturnsNewElements) {
+  std::vector<int> const input{1, 2, 3, 4, 5};
+  EXPECT_THAT(input | views::transform([](int i) { return 2 * i + 1; }),
+              RangesEq(std::vector{3, 5, 7, 9, 11}));
+}
+
+TEST(TransformView, CanReturnReference) {
+  std::vector<int> const input{1, 2, 3, 4, 5};
+
+  auto range = input | views::transform([](auto & i) -> auto & { return i; });
+  EXPECT_THAT(&*range.begin(), &*input.begin());
+}
+
+TEST(TransformView, CanUseMemberPtr) {
+  struct test {
+    int val;
+  };
+  std::vector<test> input{{1}, {3}, {2}};
+
+  EXPECT_THAT(input | views::transform(&test::val),
+              RangesEq(std::vector{1, 3, 2}));
+}
+
+TEST(TransformView, CanUseMemberFn) {
+  std::vector<std::string> input{"hello", "goodbye"};
+
+  EXPECT_THAT(input | views::transform(&std::string::size),
+              RangesEq(std::vector{5, 7}));
+}