Skip to content

Commit ddaf65c

Browse files
Add missing Scanl tests
1 parent 9082622 commit ddaf65c

4 files changed

Lines changed: 429 additions & 4 deletions

File tree

test/Streamly/Test/Data/Scanl/Combinators.hs

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ module Streamly.Test.Data.Scanl.Combinators (main) where
1212
import Data.Int (Int64)
1313
import Data.Semigroup (Sum(..))
1414
import qualified Streamly.Internal.Data.MutArray as MArray
15+
import qualified Streamly.Internal.Data.Pipe as Pipe
1516
import qualified Streamly.Internal.Data.Scanl as F
1617
import qualified Streamly.Internal.Data.Stream as Stream
18+
import qualified Streamly.Internal.Data.Unfold as Unfold
1719

1820
import qualified Prelude
1921
import Prelude hiding (maximum, minimum, product, sum, mconcat, foldMap, maybe)
@@ -26,13 +28,165 @@ import Test.QuickCheck (Gen, Property, choose, forAll, listOf1)
2628

2729
#include "Streamly/Test/Data/Scanl/CommonCombinators.hs"
2830

31+
-------------------------------------------------------------------------------
32+
-- Scanl-only tests (these combinators are not exported by the Fold module, or
33+
-- their scan output cannot be shared via the common 'check' harness).
34+
-------------------------------------------------------------------------------
35+
36+
-- 'compose' scans the input through the left scan and feeds each of its outputs
37+
-- (including the initial extract) to the right scan.
38+
composeS :: Expectation
39+
composeS =
40+
check (F.compose F.sum F.toList) ([1, 2, 3] :: [Int])
41+
[[0], [0, 1], [0, 1, 3], [0, 1, 3, 6]]
42+
43+
-- 'composeMany' restarts the left scan with a fresh state each time it
44+
-- terminates. Here the left scan (take 2 sum) emits a running sum of every two
45+
-- inputs which the right scan (sum) accumulates.
46+
composeManyS :: Expectation
47+
composeManyS =
48+
check (F.composeMany (F.take 2 F.sum) F.sum) ([1, 2, 3, 4, 5] :: [Int])
49+
[0, 1, 4, 7, 14, 19]
50+
51+
-- 'with' adapts a stateful combinator (here 'indexed') so that the supplied
52+
-- predicate also sees the state (the index). This keeps elements at even
53+
-- indices.
54+
withS :: Expectation
55+
withS =
56+
check (F.with F.indexed F.filter (even . fst) F.toList) "abcde"
57+
["", "a", "a", "ac", "ac", "ace"]
58+
59+
pipeS :: Expectation
60+
pipeS =
61+
check (F.pipe (Pipe.map (* 2)) F.sum) ([1, 2, 3] :: [Int]) [0, 2, 6, 12]
62+
63+
topByS :: Expectation
64+
topByS =
65+
check (F.rmapM MArray.toList (F.topBy compare 3)) ([5, 1, 4, 2, 3] :: [Int])
66+
[[], [5], [5, 1], [5, 4, 1], [5, 4, 2], [5, 4, 3]]
67+
68+
bottomByS :: Expectation
69+
bottomByS =
70+
check (F.rmapM MArray.toList (F.bottomBy compare 3)) ([5, 1, 4, 2, 3] :: [Int])
71+
[[], [5], [1, 5], [1, 4, 5], [1, 2, 4], [1, 2, 3]]
72+
73+
indexingWithS :: Expectation
74+
indexingWithS =
75+
check (F.indexingWith 0 (+ 2)) "abc"
76+
[Nothing, Just (0, 'a'), Just (2, 'b'), Just (4, 'c')]
77+
78+
indexingS :: Expectation
79+
indexingS =
80+
check F.indexing "abc"
81+
[Nothing, Just (0, 'a'), Just (1, 'b'), Just (2, 'c')]
82+
83+
indexingRevS :: Expectation
84+
indexingRevS =
85+
check (F.indexingRev 5) "abc"
86+
[Nothing, Just (5, 'a'), Just (4, 'b'), Just (3, 'c')]
87+
88+
takingEndByUS :: Expectation
89+
takingEndByUS =
90+
check (F.takingEndBy_ (== 3)) ([1, 2, 3, 4, 5] :: [Int])
91+
[Nothing, Just 1, Just 2, Nothing]
92+
93+
mapMaybeMS :: Expectation
94+
mapMaybeMS =
95+
check
96+
(F.mapMaybeM (\x -> return (if even x then Just x else Nothing)) F.toList)
97+
([1, 2, 3, 4] :: [Int])
98+
[[], [], [2], [2], [2, 4]]
99+
100+
-- An Unfold that streams the elements of an input list.
101+
unfoldList :: Monad m => Unfold.Unfold m [a] a
102+
unfoldList =
103+
Unfold.unfoldrM
104+
(\xs -> return (case xs of { [] -> Nothing; (y:ys) -> Just (y, ys) }))
105+
106+
unfoldEachS :: Expectation
107+
unfoldEachS =
108+
check (F.unfoldEach unfoldList F.toList) ([[1, 2], [3], [4, 5]] :: [[Int]])
109+
[[], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5]]
110+
111+
unfoldManyS :: Expectation
112+
unfoldManyS =
113+
check (F.unfoldMany unfoldList F.toList) ([[1, 2], [3], [4, 5]] :: [[Int]])
114+
[[], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5]]
115+
116+
-- 'defaultSalt' is the default salt used by 'rollingHash'. It is part of the
117+
-- output contract, so the test duplicates the constant rather than importing it.
118+
defaultSaltS :: Expectation
119+
defaultSaltS = F.defaultSalt `shouldBe` (-2578643520546668380 :: Int64)
120+
121+
teeS :: Expectation
122+
teeS =
123+
check (F.tee F.sum F.length) ([1, 2, 3] :: [Int])
124+
[(0, 0), (1, 1), (3, 2), (6, 3)]
125+
126+
-- Unlike the Fold 'partition' which returns the tuple of both branch results, a
127+
-- Scanl emits a single interleaved value per input: the just-updated branch.
128+
partitionByS :: Expectation
129+
partitionByS =
130+
check
131+
(F.partitionBy (\x -> if odd x then Left x else Right x) F.length F.length)
132+
([1, 2, 3, 4, 5] :: [Int])
133+
[0, 1, 1, 2, 2, 3]
134+
135+
partitionByMS :: Expectation
136+
partitionByMS =
137+
check
138+
(F.partitionByM
139+
(\x -> return (if odd x then Left x else Right x)) F.length F.length)
140+
([1, 2, 3, 4, 5] :: [Int])
141+
[0, 1, 1, 2, 2, 3]
142+
143+
partitionS :: Expectation
144+
partitionS =
145+
check (F.partition F.toList F.toList)
146+
([Left 1, Right 2, Left 3, Right 4] :: [Either Int Int])
147+
[[], [1], [2], [1, 3], [2, 4]]
148+
149+
-------------------------------------------------------------------------------
150+
-- Deprecated combinators (aliases for compose / composeMany)
151+
-------------------------------------------------------------------------------
152+
153+
scanlS :: Expectation
154+
scanlS =
155+
check (F.scanl F.sum F.toList) ([1, 2, 3] :: [Int])
156+
[[0], [0, 1], [0, 1, 3], [0, 1, 3, 6]]
157+
158+
scanlManyS :: Expectation
159+
scanlManyS =
160+
check (F.scanlMany (F.take 2 F.sum) F.sum) ([1, 2, 3, 4, 5] :: [Int])
161+
[0, 1, 4, 7, 14, 19]
162+
29163
moduleName :: String
30164
moduleName = "Data.Scanl.Combinators"
31165

32166
main :: IO ()
33167
main = hspec $
34-
describe moduleName $
168+
describe moduleName $ do
35169
describe "common" commonCombinatorsSpec
36170

37171
-- Before adding any tests here consider if it can be added to the
38172
-- common tests above.
173+
it "compose" composeS
174+
it "composeMany" composeManyS
175+
it "with" withS
176+
it "pipe" pipeS
177+
it "topBy" topByS
178+
it "bottomBy" bottomByS
179+
it "indexingWith" indexingWithS
180+
it "indexing" indexingS
181+
it "indexingRev" indexingRevS
182+
it "takingEndBy_" takingEndByUS
183+
it "mapMaybeM" mapMaybeMS
184+
it "unfoldEach" unfoldEachS
185+
it "unfoldMany" unfoldManyS
186+
it "defaultSalt" defaultSaltS
187+
it "tee" teeS
188+
it "partitionBy" partitionByS
189+
it "partitionByM" partitionByMS
190+
it "partition" partitionS
191+
it "scanl" scanlS
192+
it "scanlMany" scanlManyS

test/Streamly/Test/Data/Scanl/Container.hs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
module Streamly.Test.Data.Scanl.Container (main) where
1111

1212
import qualified Data.IntSet as IntSet
13+
import qualified Data.Map.Strict as Map
1314
import qualified Data.Set as Set
1415
import qualified Streamly.Internal.Data.Scanl as F
1516

@@ -18,13 +19,98 @@ import Test.Hspec
1819

1920
#include "Streamly/Test/Data/Scanl/CommonContainer.hs"
2021

22+
-------------------------------------------------------------------------------
23+
-- Scanl-only tests: demultiplexing and classifying.
24+
-------------------------------------------------------------------------------
25+
26+
-- Each key's inner scan is @take 2 sum@; the demux/classify scan emits
27+
-- @Just (key, result)@ on the step at which a key's inner scan terminates and
28+
-- @Nothing@ otherwise.
29+
30+
demuxInput :: [(String, Int)]
31+
demuxInput = [("A", 1), ("A", 2), ("B", 3)]
32+
33+
demuxExpected :: [Maybe (String, Int)]
34+
demuxExpected = [Just ("A", 1), Just ("A", 3), Just ("B", 3)]
35+
36+
demuxGetScanl :: String -> IO (Maybe (F.Scanl IO (String, Int) Int))
37+
demuxGetScanl _ = return (Just (F.take 2 (F.lmap snd F.sum)))
38+
39+
demuxS :: Expectation
40+
demuxS = checkPostscanl (F.demux fst demuxGetScanl) demuxInput demuxExpected
41+
42+
demuxIOS :: Expectation
43+
demuxIOS = checkPostscanl (F.demuxIO fst demuxGetScanl) demuxInput demuxExpected
44+
45+
demuxGenericS :: Expectation
46+
demuxGenericS =
47+
checkPostscanl
48+
(fmap snd
49+
(F.demuxGeneric fst demuxGetScanl
50+
:: F.Scanl IO (String, Int)
51+
(IO (Map.Map String Int), Maybe (String, Int))))
52+
demuxInput demuxExpected
53+
54+
demuxGenericIOS :: Expectation
55+
demuxGenericIOS =
56+
checkPostscanl
57+
(fmap snd
58+
(F.demuxGenericIO fst demuxGetScanl
59+
:: F.Scanl IO (String, Int)
60+
(IO (Map.Map String Int), Maybe (String, Int))))
61+
demuxInput demuxExpected
62+
63+
classifyInput :: [(String, Int)]
64+
classifyInput = [("ONE", 1), ("TWO", 2), ("ONE", 3), ("TWO", 4), ("ONE", 5)]
65+
66+
classifyExpected :: [Maybe (String, Int)]
67+
classifyExpected =
68+
[Just ("ONE", 1), Just ("TWO", 2), Just ("ONE", 4), Just ("TWO", 6), Nothing]
69+
70+
classifyInner :: F.Scanl IO (String, Int) Int
71+
classifyInner = F.lmap snd (F.take 2 F.sum)
72+
73+
classifyS :: Expectation
74+
classifyS =
75+
checkPostscanl (F.classify fst classifyInner) classifyInput classifyExpected
76+
77+
classifyIOS :: Expectation
78+
classifyIOS =
79+
checkPostscanl (F.classifyIO fst classifyInner) classifyInput classifyExpected
80+
81+
classifyGenericS :: Expectation
82+
classifyGenericS =
83+
checkPostscanl
84+
(fmap snd
85+
(F.classifyGeneric fst classifyInner
86+
:: F.Scanl IO (String, Int)
87+
(IO (Map.Map String Int), Maybe (String, Int))))
88+
classifyInput classifyExpected
89+
90+
classifyGenericIOS :: Expectation
91+
classifyGenericIOS =
92+
checkPostscanl
93+
(fmap snd
94+
(F.classifyGenericIO fst classifyInner
95+
:: F.Scanl IO (String, Int)
96+
(IO (Map.Map String Int), Maybe (String, Int))))
97+
classifyInput classifyExpected
98+
2199
moduleName :: String
22100
moduleName = "Data.Scanl.Container"
23101

24102
main :: IO ()
25103
main = hspec $
26-
describe moduleName $
104+
describe moduleName $ do
27105
describe "common" commonContainerSpec
28106

29107
-- Before adding any tests here consider if it can be added to the
30108
-- common tests above.
109+
it "demux" demuxS
110+
it "demuxIO" demuxIOS
111+
it "demuxGeneric" demuxGenericS
112+
it "demuxGenericIO" demuxGenericIOS
113+
it "classify" classifyS
114+
it "classifyIO" classifyIOS
115+
it "classifyGeneric" classifyGenericS
116+
it "classifyGenericIO" classifyGenericIOS

0 commit comments

Comments
 (0)