-
Notifications
You must be signed in to change notification settings - Fork 9
Implement take
, truncate
, skip
and drop
#209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
382db57
Implement `take`, `truncate`, `skip` and `drop`
abelbraaksma f959fe5
Add tests for `TaskSeq.take` and `truncate`
abelbraaksma 140f82b
Fix `TaskSeq.truncate` reading one too many from the input
abelbraaksma bd6dcf8
Fix `truncate`, do not `MoveNext` before we know we have enough elements
abelbraaksma 15f1e09
Rename source file and formatting
abelbraaksma 5495646
Add tests for `TaskSeq.skip` and `TaskSeq.drop`
abelbraaksma 6e6821e
Fix `TaskSeq.skip` skipped one too few, and simplify
abelbraaksma dd5b085
Fix and simplify code for `TaskSeq.drop`
abelbraaksma 9acc527
Temporarily ignore formatting for some files, see https://github.com/…
abelbraaksma 79f0bc4
Apply review comments, thanks @bartelink, fix naming and doc comments
abelbraaksma d199434
Revert "Temporarily ignore formatting for some files, see https://git…
abelbraaksma File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
module TaskSeq.Tests.Skip | ||
|
||
open System | ||
|
||
open Xunit | ||
open FsUnit.Xunit | ||
|
||
open FSharp.Control | ||
|
||
// | ||
// TaskSeq.skip | ||
// TaskSeq.drop | ||
// | ||
|
||
exception SideEffectPastEnd of string | ||
|
||
[<AutoOpen>] | ||
module With = | ||
/// Turns a sequence of numbers into a string, starting with A for '1' | ||
let verifyAsString expected = | ||
TaskSeq.map char | ||
>> TaskSeq.map ((+) '@') | ||
>> TaskSeq.toArrayAsync | ||
>> Task.map (String >> should equal expected) | ||
|
||
module EmptySeq = | ||
[<Theory; ClassData(typeof<TestEmptyVariants>)>] | ||
let ``TaskSeq-skip(0) has no effect on empty input`` variant = | ||
// no `task` block needed | ||
Gen.getEmptyVariant variant |> TaskSeq.skip 0 |> verifyEmpty | ||
|
||
[<Theory; ClassData(typeof<TestEmptyVariants>)>] | ||
let ``TaskSeq-skip(1) on empty input should throw InvalidOperation`` variant = | ||
fun () -> | ||
Gen.getEmptyVariant variant | ||
|> TaskSeq.skip 1 | ||
|> consumeTaskSeq | ||
|
||
|> should throwAsyncExact typeof<ArgumentException> | ||
|
||
[<Fact>] | ||
let ``TaskSeq-skip(-1) should throw ArgumentException on any input`` () = | ||
fun () -> TaskSeq.empty<int> |> TaskSeq.skip -1 |> consumeTaskSeq | ||
|> should throwAsyncExact typeof<ArgumentException> | ||
|
||
fun () -> TaskSeq.init 10 id |> TaskSeq.skip -1 |> consumeTaskSeq | ||
|> should throwAsyncExact typeof<ArgumentException> | ||
|
||
[<Fact>] | ||
let ``TaskSeq-skip(-1) should throw ArgumentException before awaiting`` () = | ||
fun () -> | ||
taskSeq { | ||
do! longDelay () | ||
|
||
if false then | ||
yield 0 // type inference | ||
} | ||
|> TaskSeq.skip -1 | ||
|> ignore // throws even without running the async. Bad coding, don't ignore a task! | ||
|
||
|> should throw typeof<ArgumentException> | ||
|
||
[<Theory; ClassData(typeof<TestEmptyVariants>)>] | ||
let ``TaskSeq-drop(0) has no effect on empty input`` variant = Gen.getEmptyVariant variant |> TaskSeq.drop 0 |> verifyEmpty | ||
|
||
[<Theory; ClassData(typeof<TestEmptyVariants>)>] | ||
let ``TaskSeq-drop(99) does not throw on empty input`` variant = | ||
Gen.getEmptyVariant variant | ||
|> TaskSeq.drop 99 | ||
|> verifyEmpty | ||
|
||
|
||
[<Fact>] | ||
let ``TaskSeq-drop(-1) should throw ArgumentException on any input`` () = | ||
fun () -> TaskSeq.empty<int> |> TaskSeq.drop -1 |> consumeTaskSeq | ||
|> should throwAsyncExact typeof<ArgumentException> | ||
|
||
fun () -> TaskSeq.init 10 id |> TaskSeq.drop -1 |> consumeTaskSeq | ||
|> should throwAsyncExact typeof<ArgumentException> | ||
|
||
[<Fact>] | ||
let ``TaskSeq-drop(-1) should throw ArgumentException before awaiting`` () = | ||
fun () -> | ||
taskSeq { | ||
do! longDelay () | ||
|
||
if false then | ||
yield 0 // type inference | ||
} | ||
|> TaskSeq.drop -1 | ||
|> ignore // throws even without running the async. Bad coding, don't ignore a task! | ||
|
||
|> should throw typeof<ArgumentException> | ||
|
||
module Immutable = | ||
|
||
[<Theory; ClassData(typeof<TestImmTaskSeq>)>] | ||
let ``TaskSeq-skip skips over exactly 'count' items`` variant = task { | ||
|
||
do! | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.skip 0 | ||
|> verifyAsString "ABCDEFGHIJ" | ||
|
||
do! | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.skip 1 | ||
|> verifyAsString "BCDEFGHIJ" | ||
|
||
do! | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.skip 5 | ||
|> verifyAsString "FGHIJ" | ||
|
||
do! | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.skip 10 | ||
|> verifyEmpty | ||
} | ||
|
||
[<Theory; ClassData(typeof<TestImmTaskSeq>)>] | ||
let ``TaskSeq-skip throws when there are not enough elements`` variant = | ||
fun () -> TaskSeq.init 1 id |> TaskSeq.skip 2 |> consumeTaskSeq | ||
|
||
|> should throwAsyncExact typeof<ArgumentException> | ||
|
||
fun () -> | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.skip 11 | ||
|> consumeTaskSeq | ||
|
||
|> should throwAsyncExact typeof<ArgumentException> | ||
|
||
fun () -> | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.skip 10_000_000 | ||
|> consumeTaskSeq | ||
|
||
|> should throwAsyncExact typeof<ArgumentException> | ||
|
||
[<Theory; ClassData(typeof<TestImmTaskSeq>)>] | ||
let ``TaskSeq-drop skips over at least 'count' items`` variant = task { | ||
do! | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.drop 0 | ||
|> verifyAsString "ABCDEFGHIJ" | ||
|
||
do! | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.drop 1 | ||
|> verifyAsString "BCDEFGHIJ" | ||
|
||
do! | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.drop 5 | ||
|> verifyAsString "FGHIJ" | ||
|
||
do! | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.drop 10 | ||
|> verifyEmpty | ||
|
||
do! | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.drop 11 // no exception | ||
|> verifyEmpty | ||
|
||
do! | ||
Gen.getSeqImmutable variant | ||
|> TaskSeq.drop 10_000_000 // no exception | ||
|> verifyEmpty | ||
} | ||
|
||
module SideEffects = | ||
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>] | ||
let ``TaskSeq-skip skips over enough items`` variant = | ||
Gen.getSeqWithSideEffect variant | ||
|> TaskSeq.skip 5 | ||
|> verifyAsString "FGHIJ" | ||
|
||
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>] | ||
let ``TaskSeq-drop skips over enough items`` variant = | ||
Gen.getSeqWithSideEffect variant | ||
|> TaskSeq.drop 5 | ||
|> verifyAsString "FGHIJ" | ||
|
||
[<Fact>] | ||
let ``TaskSeq-skip prove we do not skip side effects`` () = task { | ||
let mutable x = 42 // for this test, the potential mutation should not actually occur | ||
|
||
let items = taskSeq { | ||
yield x | ||
yield x * 2 | ||
x <- x + 1 // we are proving we never get here | ||
} | ||
|
||
let! first = items |> TaskSeq.skip 2 |> TaskSeq.toArrayAsync | ||
let! repeat = items |> TaskSeq.skip 2 |> TaskSeq.toArrayAsync | ||
|
||
first |> should equal Array.empty<int> | ||
repeat |> should equal Array.empty<int> | ||
x |> should equal 44 // expect: side-effect is executed twice by now | ||
} | ||
|
||
[<Fact>] | ||
let ``TaskSeq-skip prove that an exception from the taskseq is thrown instead of exception from function`` () = | ||
let items = taskSeq { | ||
yield 42 | ||
yield! [ 1; 2 ] | ||
do SideEffectPastEnd "at the end" |> raise // we SHOULD get here before ArgumentException is raised | ||
} | ||
|
||
fun () -> items |> TaskSeq.skip 4 |> consumeTaskSeq // this would raise ArgumentException normally | ||
|> should throwAsyncExact typeof<SideEffectPastEnd> | ||
|
||
|
||
[<Fact>] | ||
let ``TaskSeq-drop prove we do not skip side effects at the end`` () = task { | ||
let mutable x = 42 // for this test, the potential mutation should not actually occur | ||
|
||
let items = taskSeq { | ||
yield x | ||
yield x * 2 | ||
x <- x + 1 // we are proving we never get here | ||
} | ||
|
||
let! first = items |> TaskSeq.drop 2 |> TaskSeq.toArrayAsync | ||
let! repeat = items |> TaskSeq.drop 2 |> TaskSeq.toArrayAsync | ||
|
||
first |> should equal Array.empty<int> | ||
repeat |> should equal Array.empty<int> | ||
x |> should equal 44 // expect: side-effect at end is executed twice by now | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.