Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
6312812
Remove unnecessary tuple element
jfmengels Jan 1, 2026
f947943
Simplify
jfmengels Jan 1, 2026
3137637
Simplify
jfmengels Jan 1, 2026
6111af9
Simplify
jfmengels Jan 1, 2026
a722b83
Simplify
jfmengels Jan 1, 2026
7bac4c5
Simplify
jfmengels Jan 1, 2026
ace318e
Simplify
jfmengels Jan 1, 2026
77c6f96
Remove unused variables
jfmengels Jan 1, 2026
36ed8be
Remove unused parameters
jfmengels Jan 1, 2026
4d051f9
Remove unused parameter
jfmengels Jan 1, 2026
0c60906
Remove unused exports
jfmengels Jan 1, 2026
ccdddee
Make pattern explicit
jfmengels Jan 1, 2026
7ad1ad9
Apply NoPrematureLetComputation
jfmengels Jan 1, 2026
5ce3296
Apply NoSimpleLetBody
jfmengels Jan 1, 2026
f593f2a
Convert to dictionary early
jfmengels Jan 1, 2026
b12f8bb
Avoid tuple
jfmengels Jan 1, 2026
e51caa5
Use more lazy case expressions
jfmengels Jan 1, 2026
0ec3cd7
Apply simplifications
jfmengels Jan 1, 2026
da28b4b
Remove unused field
jfmengels Jan 1, 2026
3419471
Pattern match earlier
jfmengels Jan 1, 2026
b5f8474
Stop early
jfmengels Jan 1, 2026
8d265d4
Avoid Maybe.map
jfmengels Jan 1, 2026
6c43a7e
Use more lazy case expressions
jfmengels Jan 1, 2026
c32bfdb
Use more lazy case expressions
jfmengels Jan 1, 2026
0ab20eb
Avoid List.map
jfmengels Jan 1, 2026
1162036
Faster Dict creation
jfmengels Jan 1, 2026
28ba136
Avoid record
jfmengels Jan 1, 2026
ee6ef3d
Avoid double conversion from RandomRun to List
jfmengels Jan 1, 2026
8e18d8f
Extract function
jfmengels Jan 1, 2026
cb1d2a2
Compute possible bool indexes using a single list traversal
jfmengels Jan 1, 2026
96e4ec5
Compute minimizeFloatCmds using a single loop
jfmengels Jan 1, 2026
1492319
Remove unused declaration
jfmengels Feb 13, 2026
ca250ac
Remove unused custom type constructors
jfmengels Jan 1, 2026
3b66b4f
Remove record for TextTag
jfmengels Feb 13, 2026
110fb84
Make toLinesHelp tail-call recursive
jfmengels Feb 13, 2026
5153874
Faster withHtmlContext
jfmengels Feb 13, 2026
a83197a
Make joinAsList faster
jfmengels Feb 13, 2026
37a5d9c
Compute trimmedExtras in a single list iteration
jfmengels Feb 13, 2026
cd10e72
Compute styles in a single collection iteration
jfmengels Feb 13, 2026
3d1291d
Compute stringAttributes in a single collection iteration
jfmengels Feb 13, 2026
155dfd1
Compute boolAttributes in a single collection iteration
jfmengels Feb 13, 2026
6af438b
Extract openTag computation
jfmengels Feb 13, 2026
b369457
Extract value
jfmengels Feb 13, 2026
4faf694
Use explicit lambda
jfmengels Feb 13, 2026
8b6a78d
Avoid adding to the end
jfmengels Feb 13, 2026
6e3f2f1
Merge definitions
jfmengels Feb 14, 2026
0f3adf8
Rename function
jfmengels Feb 14, 2026
1afc2f4
Remove unnecessary parameters
jfmengels Feb 14, 2026
6db72ad
Add padding argument to Selector type
jfmengels Feb 14, 2026
4c166fb
Move helpers to top-level
jfmengels Feb 14, 2026
505a2b0
Make queryAll tail-call recursive
jfmengels Feb 14, 2026
71090f7
Make queryAllChildren tail-call recursive
jfmengels Feb 14, 2026
548b8da
Rename key to name
jfmengels Feb 14, 2026
880be54
Avoid recreating records
jfmengels Feb 14, 2026
7df50b3
Avoid Result pipeline
jfmengels Feb 14, 2026
2fcfaee
Avoid currying
jfmengels Feb 14, 2026
688bdc8
Merge Result.map and Result.andThen
jfmengels Feb 14, 2026
5d7ab59
Make traverseSelectors stop at the first error
jfmengels Feb 14, 2026
e64f325
Use a case expression
jfmengels Feb 14, 2026
aae2230
Add accumulator argument
jfmengels Feb 15, 2026
4ba3120
Reuse accumulator instead of appending
jfmengels Feb 15, 2026
e3c5243
Combine reverse and foldr into foldl
jfmengels Feb 15, 2026
62aec2e
Move function to top-level
jfmengels Feb 15, 2026
00c2e05
Remove redundant check
jfmengels Feb 15, 2026
4f21767
Use recursion for forOffset
jfmengels Feb 15, 2026
ed7b19c
Use List.foldr
jfmengels Feb 15, 2026
062942b
Reverse list order
jfmengels Feb 15, 2026
d3b00e6
Use foldr instead of fastConcatMap
jfmengels Feb 16, 2026
f3e3b88
Avoid redefining decoders in a lambda
jfmengels Feb 16, 2026
ffda455
Move map to the constants' declarations
jfmengels Feb 13, 2026
1e4f7a6
Use List.foldl by creating a reverse range
jfmengels Feb 17, 2026
87fd820
Use List.foldr
jfmengels Feb 17, 2026
37a3706
Use List.foldl by creating a reverse range
jfmengels Feb 17, 2026
dbc9204
Use lambda
jfmengels Feb 17, 2026
2a46268
Use plain concatenation
jfmengels Feb 17, 2026
c110a44
Remove unused code
jfmengels Feb 18, 2026
7fd74c1
Remove unused code
jfmengels Feb 14, 2026
f8f7039
Apply simplifications
jfmengels Feb 18, 2026
c15fc4b
Flatten Simplify.runCmd
jfmengels Aug 6, 2026
b6b20df
Reuse constant
jfmengels Feb 17, 2026
d9d8a68
Remove unnecessary parens
jfmengels Aug 11, 2026
48da5c9
Combine Maybe.traverse and List.all
jfmengels Aug 11, 2026
425ca21
Inline Dict.toList
jfmengels Aug 11, 2026
579a729
Inline List.filterMap
jfmengels Aug 11, 2026
3a3b173
Remove usage of maybeCons
jfmengels Aug 11, 2026
1855b09
Combine Dict.foldr and List.foldr
jfmengels Aug 11, 2026
3b01940
Extract isLabelSufficientlyCovered
jfmengels Aug 11, 2026
3def8ea
Combine Dict.foldr and List.all
jfmengels Aug 11, 2026
fe097fa
Remove pipes
jfmengels Aug 11, 2026
3ff4ce2
Avoid record update
jfmengels Aug 11, 2026
0e23074
Avoid creating new summary record
jfmengels Aug 11, 2026
9f97b1b
Merge concatenations
jfmengels Aug 11, 2026
187a433
Improve arity for fuzz2/fuzz3
jfmengels Aug 11, 2026
8d93c39
Avoid creating record in validatedFuzzTest
jfmengels Aug 11, 2026
369945d
Remove record for Pass
jfmengels Aug 11, 2026
90da6ca
Avoid record update
jfmengels Aug 11, 2026
bf2d97a
Avoid record update
jfmengels Aug 11, 2026
5898880
Pad NoDistribution to have the same shape as the other variants
jfmengels Aug 11, 2026
852d24f
Use boolean short-circuits in withinCompare
jfmengels Aug 11, 2026
78ea5e0
Inline Test.Expectation.fail to skip an intermediate record
jfmengels Aug 11, 2026
90f58f0
Avoid record update
jfmengels Aug 11, 2026
8e82ccf
Compute weightSum more lazily
jfmengels Aug 11, 2026
3f30298
Fix typo
jfmengels Aug 11, 2026
160507d
Use List.getAt
jfmengels Aug 11, 2026
0dc1552
Compute invalid fuzzer more lazily
jfmengels Aug 11, 2026
e16533d
Move List.map
jfmengels Aug 11, 2026
936bd79
Inline Dict.filter
jfmengels Aug 11, 2026
066a489
Inline Dict.foldr
jfmengels Aug 11, 2026
260fac8
Combine Dict.foldr and List.map
jfmengels Aug 11, 2026
3351da9
Combine Dict.foldr and Dict.foldl
jfmengels Aug 11, 2026
354f32d
Remove pipes
jfmengels Aug 11, 2026
60118ef
Avoid recreating Dict unnecessarily
jfmengels Aug 11, 2026
d29a17f
Combine if branches
jfmengels Aug 11, 2026
b5519d7
Avoid recreating Just
jfmengels Aug 11, 2026
93f4981
Check for list length more optimally
jfmengels Aug 11, 2026
863e763
Create label list only once
jfmengels Aug 11, 2026
d188835
Inline let declaration
jfmengels Aug 11, 2026
e78b035
Avoid record update
jfmengels Aug 11, 2026
3f97e7d
Speed up RandomRun comparison
jfmengels Aug 12, 2026
9b91727
Avoid allocating a Tuple in nextChoice when returning Nothing
jfmengels Aug 12, 2026
a5a6f6b
Combine Dict.values and List.sum
jfmengels Aug 12, 2026
e1e81f5
Combine Dict.filter and Dict.foldr
jfmengels Aug 12, 2026
2f09771
Remove pipes
jfmengels Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 76 additions & 49 deletions src/Expect.elm
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ Another example is comparing values that are on either side of zero. `0.0001` is
-}

import Dict exposing (Dict)
import Fuzz.Internal
import Set exposing (Set)
import Test.Distribution
import Test.Expectation
Expand Down Expand Up @@ -392,10 +393,12 @@ ok result =
pass

Err _ ->
{ description = "Expect.ok"
, reason = Comparison "Ok _" (Internal.toString result)
}
|> Test.Expectation.fail
Test.Expectation.Fail
{ given = Nothing
, distributionReport = Fuzz.Internal.noDistribution
, description = "Expect.ok"
, reason = Comparison "Ok _" (Internal.toString result)
}


{-| Passes if the
Expand Down Expand Up @@ -433,10 +436,12 @@ err : Result a b -> Expectation
err result =
case result of
Ok _ ->
{ description = "Expect.err"
, reason = Comparison "Err _" (Internal.toString result)
}
|> Test.Expectation.fail
Test.Expectation.Fail
{ given = Nothing
, distributionReport = Fuzz.Internal.noDistribution
, description = "Expect.err"
, reason = Comparison "Err _" (Internal.toString result)
}

Err _ ->
pass
Expand Down Expand Up @@ -475,10 +480,12 @@ equalLists expected actual =
pass

else
{ description = "Expect.equalLists"
, reason = ListDiff (List.map Internal.toString expected) (List.map Internal.toString actual)
}
|> Test.Expectation.fail
Test.Expectation.Fail
{ given = Nothing
, distributionReport = Fuzz.Internal.noDistribution
, description = "Expect.equalLists"
, reason = ListDiff (List.map Internal.toString expected) (List.map Internal.toString actual)
}


{-| Passes if the arguments are equal dicts.
Expand Down Expand Up @@ -595,7 +602,7 @@ equalSets expected actual =
-}
pass : Expectation
pass =
Test.Expectation.Pass { distributionReport = Test.Distribution.NoDistribution }
Test.Expectation.Pass (Test.Distribution.NoDistribution ())


{-| Fails with the given message.
Expand All @@ -617,7 +624,12 @@ pass =
-}
fail : String -> Expectation
fail str =
Test.Expectation.fail { description = str, reason = Custom }
Test.Expectation.Fail
{ given = Nothing
, distributionReport = Fuzz.Internal.noDistribution
, description = str
, reason = Custom
}


{-| If the given expectation fails, replace its failure message with a custom one.
Expand Down Expand Up @@ -671,8 +683,10 @@ which argument is which:
all : List (subject -> Expectation) -> subject -> Expectation
all list query =
if List.isEmpty list then
Test.Expectation.fail
{ reason = Invalid EmptyList
Test.Expectation.Fail
{ given = Nothing
, distributionReport = Fuzz.Internal.noDistribution
, reason = Invalid EmptyList
, description = "Expect.all was given an empty list. You must make at least one expectation to have a valid test!"
}

Expand Down Expand Up @@ -701,16 +715,18 @@ allHelp list query =

reportCollectionFailure : String -> a -> b -> List c -> List d -> Expectation
reportCollectionFailure comparison expected actual missingKeys extraKeys =
{ description = comparison
, reason =
{ expected = Internal.toString expected
, actual = Internal.toString actual
, extra = List.map Internal.toString extraKeys
, missing = List.map Internal.toString missingKeys
Test.Expectation.Fail
{ given = Nothing
, distributionReport = Fuzz.Internal.noDistribution
, description = comparison
, reason =
{ expected = Internal.toString expected
, actual = Internal.toString actual
, extra = List.map Internal.toString extraKeys
, missing = List.map Internal.toString missingKeys
}
|> CollectionDiff
}
|> CollectionDiff
}
|> Test.Expectation.fail


{-| String arg is label, e.g. "Expect.equal".
Expand All @@ -731,16 +747,13 @@ equateWith reason comparison b a =

usesFloats =
isFloat (Internal.toString a) || isFloat (Internal.toString b)

floatError =
if String.contains reason "not" then
"Do not use Expect.notEqual with floats. Use Expect.notWithin instead."

else
"Do not use Expect.equal with floats. Use Expect.within instead."
in
if usesFloats then
fail floatError
if String.contains reason "not" then
fail "Do not use Expect.notEqual with floats. Use Expect.notWithin instead."

else
fail "Do not use Expect.equal with floats. Use Expect.within instead."

else
testWith Equality reason comparison b a
Expand All @@ -757,10 +770,12 @@ testWith makeReason label runTest expected actual =
pass

else
{ description = label
, reason = makeReason (Internal.toString expected) (Internal.toString actual)
}
|> Test.Expectation.fail
Test.Expectation.Fail
{ given = Nothing
, distributionReport = Fuzz.Internal.noDistribution
, description = label
, reason = makeReason (Internal.toString expected) (Internal.toString actual)
}



Expand Down Expand Up @@ -796,26 +811,38 @@ relative tolerance =
nonNegativeToleranceError : FloatingPointTolerance -> String -> Expectation -> Expectation
nonNegativeToleranceError tolerance name result =
if absolute tolerance < 0 && relative tolerance < 0 then
Test.Expectation.fail { description = "Expect." ++ name ++ " was given negative absolute and relative tolerances", reason = Custom }
Test.Expectation.Fail
{ given = Nothing
, distributionReport = Fuzz.Internal.noDistribution
, description = "Expect." ++ name ++ " was given negative absolute and relative tolerances"
, reason = Custom
}

else if absolute tolerance < 0 then
Test.Expectation.fail { description = "Expect." ++ name ++ " was given a negative absolute tolerance", reason = Custom }
Test.Expectation.Fail
{ given = Nothing
, distributionReport = Fuzz.Internal.noDistribution
, description = "Expect." ++ name ++ " was given a negative absolute tolerance"
, reason = Custom
}

else if relative tolerance < 0 then
Test.Expectation.fail { description = "Expect." ++ name ++ " was given a negative relative tolerance", reason = Custom }
Test.Expectation.Fail
{ given = Nothing
, distributionReport = Fuzz.Internal.noDistribution
, description = "Expect." ++ name ++ " was given a negative relative tolerance"
, reason = Custom
}

else
result


withinCompare : FloatingPointTolerance -> Float -> Float -> Bool
withinCompare tolerance a b =
let
withinAbsoluteTolerance =
a - absolute tolerance <= b && b <= a + absolute tolerance

withinRelativeTolerance =
(a - abs (a * relative tolerance) <= b && b <= a + abs (a * relative tolerance))
|| (b - abs (b * relative tolerance) <= a && a <= b + abs (b * relative tolerance))
in
(a == b) || withinAbsoluteTolerance || withinRelativeTolerance
(a == b)
-- within absolute tolerance
|| (a - absolute tolerance <= b && b <= a + absolute tolerance)
-- within relative tolerance
|| (a - abs (a * relative tolerance) <= b && b <= a + abs (a * relative tolerance))
|| (b - abs (b * relative tolerance) <= a && a <= b + abs (b * relative tolerance))
78 changes: 38 additions & 40 deletions src/Fuzz.elm
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ can usually find the simplest input that reproduces a bug.

import Array exposing (Array)
import Bitwise
import Char
import Dict exposing (Dict)
import Fuzz.Float
import Fuzz.Internal exposing (Fuzzer(..))
Expand Down Expand Up @@ -1145,15 +1144,14 @@ frequencyHelp functionName fuzzers =
intFrequency (List.map (Tuple.mapFirst round) nonzeroFuzzers)

else
let
weightSum : Float
weightSum =
List.foldl (\( w, _ ) acc -> w + acc) 0 nonzeroFuzzers
in
percentage
|> andThen
(\p ->
let
weightSum : Float
weightSum =
List.foldl (\( w, _ ) acc -> w + acc) 0 nonzeroFuzzers

f : Float
f =
p * weightSum
Expand Down Expand Up @@ -1204,11 +1202,12 @@ intFrequency fuzzers =
rollDice (weightSum - 1) (intFrequencyGenerator n (List.map Tuple.first rest))
|> andThen
(\i ->
fuzzers
|> List.drop i
|> List.head
|> Maybe.map Tuple.second
|> Maybe.withDefault (invalid "elm-test bug: intFrequency index out of range")
case List.getAt i fuzzers of
Just ( _, fuzzer ) ->
fuzzer

Nothing ->
invalid "elm-test bug: intFrequency index out of range"
)

[] ->
Expand Down Expand Up @@ -1621,7 +1620,7 @@ rollDice maxValue diceGenerator =
else
Generated
{ value = hardcodedChoice
, prng = Hardcoded { h | unusedPart = restOfChoices }
, prng = Hardcoded { wholeRun = h.wholeRun, unusedPart = restOfChoices }
}


Expand All @@ -1640,7 +1639,7 @@ forcedChoice n =
Random r ->
Generated
{ value = n
, prng = Random { r | run = RandomRun.append n r.run }
, prng = Random { run = RandomRun.append n r.run, seed = r.seed }
}

Hardcoded h ->
Expand All @@ -1662,19 +1661,15 @@ forcedChoice n =
else
Generated
{ value = n
, prng = Hardcoded { h | unusedPart = restOfChoices }
, prng = Hardcoded { wholeRun = h.wholeRun, unusedPart = restOfChoices }
}


{-| We could golf this to ((/=) 0) but this is perhaps more readable.
-}
intToBool : Int -> Bool
intToBool n =
if n == 0 then
False

else
True
n /= 0


weightedBoolGenerator : Float -> Random.Generator Int
Expand Down Expand Up @@ -1795,46 +1790,49 @@ labelExamples n labels fuzzer =
Nothing
)
in
if List.isEmpty categories then
if List.isEmpty categories || Dict.member categories acc then
acc

else
acc
|> Dict.update categories
(\maybeExample ->
case maybeExample of
Nothing ->
Just item

Just original ->
Just original
)
Dict.insert categories item acc
)
Dict.empty

combinations : List ( List String, a )
combinations : List ( List String, Maybe a )
combinations =
foundExamples
|> Dict.filter (\k _ -> List.length k > 1)
|> Dict.toList
Dict.foldr
(\label example l ->
if List.hasMultipleItems label then
( label, Just example ) :: l

else
l
)
[]
foundExamples
in
List.filterMap
(\( label, _ ) ->
case Dict.get [ label ] foundExamples of
let
thisLabel : List String
thisLabel =
[ label ]
in
case Dict.get thisLabel foundExamples of
Nothing ->
if Dict.any (\k _ -> List.member label k) foundExamples then
-- don't show this example: all its occurences were included in combination with some other label
-- don't show this example: all its occurrences were included in combination with some other label
Nothing

else
-- show that we didn't find it (in any combination nor alone)
Just ( [ label ], Nothing )
Just ( thisLabel, Nothing )

Just example ->
Just ( [ label ], Just example )
(Just _) as justExample ->
Just ( thisLabel, justExample )
)
labels
++ List.map (\( label, example ) -> ( label, Just example )) combinations
++ combinations

Rejected _ ->
[]
Expand Down
8 changes: 7 additions & 1 deletion src/Fuzz/Internal.elm
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
module Fuzz.Internal exposing (Fuzzer(..), generate)
module Fuzz.Internal exposing (Fuzzer(..), generate, noDistribution)

{-| This module is here just to hide the `generate` function from the end users
of the library.
-}

import GenResult exposing (GenResult)
import PRNG exposing (PRNG)
import Test.Distribution exposing (DistributionReport)


type Fuzzer a
Expand All @@ -15,3 +16,8 @@ type Fuzzer a
generate : PRNG -> Fuzzer a -> GenResult a
generate prng (Fuzzer fuzzer) =
fuzzer prng


noDistribution : DistributionReport
noDistribution =
Test.Distribution.NoDistribution ()
Loading