sydtest 0.13.0.0 → 0.13.0.1
raw patch · 6 files changed
+1102/−231 lines, 6 filesdep +vectordep −Diffdep −split
Dependencies added: vector
Dependencies removed: Diff, split
Files
- CHANGELOG.md +9/−0
- src/Test/Syd/Diff.hs +527/−0
- src/Test/Syd/Output.hs +60/−15
- sydtest.cabal +6/−4
- test/Test/Syd/DiffSpec.hs +288/−0
- test_resources/output-test.txt +212/−212
CHANGELOG.md view
@@ -1,5 +1,14 @@ # Changelog +## [0.13.0.1] - 2023-01-13++### Changed++* Replaced the diffing algorithm by a custom one.+ This has sped up diffing at least 100x and let us get rid of the `Diff` and `split` dependencies.+* Reworded 'likely not flaky' to 'does not look flaky' to be more technically+ accurate as we know nothing about the likelihood of flakiness.+ ## [0.13.0.0] - 2022-10-14 ### Changed
+ src/Test/Syd/Diff.hs view
@@ -0,0 +1,527 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-unused-imports -Werror=name-shadowing #-}++module Test.Syd.Diff+ ( -- * Diffing+ Diff,+ PolyDiff (..),+ getTextDiff,+ getStringDiff,+ getGroupedStringDiff,+ getVectorDiff,+ getGroupedVectorDiff,+ getVectorDiffBy,+ getGroupedVectorDiffBy,++ -- ** Internals+ Edit (..),+ getEditScript,+ getEditScriptBy,+ computeDiffFromEditScript,+ computeGroupedDiffFromEditScript,++ -- ** Backwards compatibility with @Diff@+ getDiff,+ getDiffBy,+ getGroupedDiff,+ getGroupedDiffBy,+ )+where++import Control.Monad+import Control.Monad.ST+import Data.DList (DList)+import qualified Data.DList as DList+import Data.Maybe (fromJust)+import Data.STRef+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.Array as TA+import Data.Vector (Vector, (!))+import qualified Data.Vector as V+import Data.Vector.Mutable (MVector)+import qualified Data.Vector.Mutable as MV++type Diff a = PolyDiff a a++mapDiff :: (a -> b) -> Diff a -> Diff b+mapDiff f = bimapPolyDiff f f++-- | A value is either from the 'First' list, the 'Second' or from 'Both'.+-- 'Both' contains both the left and right values, in case you are using a form+-- of equality that doesn't check all data (for example, if you are using a+-- newtype to only perform equality on side of a tuple).+data PolyDiff a b = First a | Second b | Both a b+ deriving (Show, Eq)++bimapPolyDiff :: (a -> c) -> (b -> d) -> PolyDiff a b -> PolyDiff c d+bimapPolyDiff f g = \case+ First a -> First (f a)+ Second b -> Second (g b)+ Both a b -> Both (f a) (g b)++-- |+--+-- For backward compatibility with 'Diff', use more specific functions if you can.+getDiff :: Eq a => [a] -> [a] -> [Diff a]+getDiff = getDiffBy (==)++-- |+--+-- For backward compatibility with 'Diff', use more specific functions if you can.+getDiffBy :: (a -> b -> Bool) -> [a] -> [b] -> [PolyDiff a b]+getDiffBy eq as bs = V.toList (getVectorDiffBy eq (V.fromList as) (V.fromList bs))++-- |+--+-- For backward compatibility with 'Diff', use more specific functions if you can.+getGroupedDiff :: Eq a => [a] -> [a] -> [Diff [a]]+getGroupedDiff = getGroupedDiffBy (==)++-- |+--+-- For backward compatibility with 'Diff', use more specific functions if you can.+getGroupedDiffBy :: (a -> b -> Bool) -> [a] -> [b] -> [PolyDiff [a] [b]]+getGroupedDiffBy eq as bs = V.toList (V.map (bimapPolyDiff V.toList V.toList) (getGroupedVectorDiffBy eq (V.fromList as) (V.fromList bs)))++-- | Text diff+--+-- Uses pack and unpack, so does not roundtrip.+-- It uses pack and unpack because 'Text' is not the same as @Vector Char@;+-- You can't index a text in O(1) time, it takes O(n) time.+getTextDiff :: Text -> Text -> Vector (Diff Text)+getTextDiff expected actual = V.map (mapDiff packFromVector) $ getGroupedVectorDiff (unpackToVector expected) (unpackToVector actual)+ where+ packFromVector :: Vector Char -> Text+ packFromVector = T.pack . V.toList+ unpackToVector :: Text -> Vector Char+ unpackToVector = V.fromList . T.unpack++-- | 'String' diff+--+-- You probably want to use 'getTextDiff' with packed strings instead, but this+-- function doesn't have the roundtripping problem that 'getTextDiff' has.+getStringDiff :: String -> String -> [Diff Char]+getStringDiff actual expected = V.toList (getVectorDiff (V.fromList actual) (V.fromList expected))++-- | Grouped 'String' diff+--+-- Like 'getStringDiff' but with entire strings instead of individual characters.+getGroupedStringDiff :: String -> String -> [Diff String]+getGroupedStringDiff actual expected = V.toList $ V.map (mapDiff V.toList) $ getGroupedVectorDiff (V.fromList actual) (V.fromList expected)++-- | Diff two vectors+--+-- Prefer 'getGroupedVectorDiff' for performance reasons.+getVectorDiff :: Eq a => Vector a -> Vector a -> Vector (Diff a)+getVectorDiff = getVectorDiffBy (==)++-- | Diff two vectors with different types using a custom equality operator+--+-- Prefer 'getGroupedVectorDiffBy' for performance reasons.+getVectorDiffBy :: forall a b. (a -> b -> Bool) -> Vector a -> Vector b -> Vector (PolyDiff a b)+getVectorDiffBy eq old new = computeDiffFromEditScript old new (getEditScriptBy eq old new)++-- | Diff two vectors with grouped results+getGroupedVectorDiff :: Eq a => Vector a -> Vector a -> Vector (Diff (Vector a))+getGroupedVectorDiff = getGroupedVectorDiffBy (==)++-- | Diff two vectors with grouped results using a custom equality operator+getGroupedVectorDiffBy :: forall a b. (a -> b -> Bool) -> Vector a -> Vector b -> Vector (PolyDiff (Vector a) (Vector b))+getGroupedVectorDiffBy eq old new = computeGroupedDiffFromEditScript old new (getEditScriptBy eq old new)++-- | Compute the edit script to turn a given vector into the second given vector+getEditScript :: forall a. Eq a => Vector a -> Vector a -> Vector Edit+getEditScript = getEditScriptBy (==)++-- | Compute the edit script to turn a given vector into the second given vector with a custom equality operator+--+-- From https://blog.robertelder.org/diff-algorithm/+getEditScriptBy :: forall a b. (a -> b -> Bool) -> Vector a -> Vector b -> Vector Edit+getEditScriptBy eq old new = V.fromList $ DList.toList $ runST $ go old new 0 0+ where+ go :: forall s. Vector a -> Vector b -> Int -> Int -> ST s (DList Edit)+ go e f i j = do+ -- N,M,L,Z = len(e),len(f),len(e)+len(f),2*min(len(e),len(f))+2+ let upperN :: Int+ upperN = V.length e++ let upperM :: Int+ upperM = V.length f++ let upperL :: Int+ upperL = upperN + upperM++ let upperZ :: Int+ upperZ = 2 * min upperN upperM + 2++ -- if N > 0 and M > 0:+ if upperN > 0 && upperM > 0+ then do+ -- w,g,p = N-M,[0]*Z,[0]*Z+ let w :: Int+ w = upperN - upperM++ g <- MV.replicate upperZ 0 :: ST s (MVector s Int)++ p <- MV.replicate upperZ 0 :: ST s (MVector s Int)++ -- for h in range(0, (L//2+(L%2!=0))+1):+ let hs :: [Int]+ hs = [0 .. ((upperL `quot` 2) + (if odd upperL then 1 else 0))]++ mResult <- forUntilJust hs $ \h -> do+ -- for r in range(0, 2):+ forUntilJust [0, 1 :: Int] $ \r -> do+ -- c,d,o,m = (g,p,1,1) if r==0 else (p,g,0,-1)+ let (c, d, o, m) = if r == 0 then (g, p, 1, 1) else (p, g, 0, -1)++ -- for k in range(-(h-2*max(0,h-M)), h-2*max(0,h-N)+1, 2):+ let lo :: Int+ lo = -(h - 2 * max 0 (h - upperM))++ let hi :: Int+ hi = h - 2 * max 0 (h - upperN)++ let ks :: [Int]+ ks = [lo, lo + 2 .. hi]++ forUntilJust ks $ \k -> do+ -- a = c[(k+1)%Z] if (k==-h or k!=h and c[(k-1)%Z]<c[(k+1)%Z]) else c[(k-1)%Z]+1+ initAVal <- do+ let part1 = k == -h++ let part2 = k /= h++ -- (k+1)%Z+ let kp1Ix = (k + 1) `modPortable` upperZ++ -- (k-1)%Z+ let km1Ix = (k - 1) `modPortable` upperZ+ if part1+ then MV.unsafeRead c kp1Ix+ else do+ if part2+ then do+ -- c[(k-1)%Z]+ km1 <- MV.unsafeRead c km1Ix++ -- c[(k+1)%Z]+ kp1 <- MV.unsafeRead c kp1Ix+ let part3 = km1 < kp1+ pure $+ if part3+ then kp1+ else km1 + 1+ else do+ km1 <- MV.unsafeRead c km1Ix++ pure $ km1 + 1++ a <- newSTRef initAVal++ -- b = a-k+ let initBVal :: Int+ initBVal = initAVal - k++ b <- newSTRef initBVal++ -- s,t = a,b+ s <- newSTRef initAVal+ t <- newSTRef initBVal++ -- while a<N and b<M and e[(1-o)*N+m*a+(o-1)]==f[(1-o)*M+m*b+(o-1)]:+ let computeWhileCond = do+ aVal <- readSTRef a+ -- a<N+ let part1 = aVal < upperN+ if part1+ then do+ bVal <- readSTRef b+ -- b<M+ let part2 = bVal < upperM+ -- e[(1-o)*N+m*a+(o-1)]==f[(1-o)*M+m*b+(o-1)]:+ let mkPart3 = do+ let imo = 1 - o+ omi = o - 1+ -- e[(1-o)*N+m*a+(o-1)]+ leftVal <- do+ -- (1-o)*N+m*a+(o-1)+ let ix = imo * upperN + m * aVal + omi++ pure $ e ! ix+ -- f[(1-o)*M+m*b+(o-1)]+ rightVal <- do+ -- (1-o)*M+m*b+(o-1)+ let ix = imo * upperM + m * bVal + omi++ pure $ f ! ix+ pure $ leftVal `eq` rightVal+ part2 &&. mkPart3+ else pure False+ whileM_ computeWhileCond $ do+ -- a,b = a+1,b+1+ modifySTRef a (+ 1)+ modifySTRef b (+ 1)+ -- c[k%Z],z=a,-(k-w)+ do+ aVal <- readSTRef a++ MV.unsafeWrite c (k `modPortable` upperZ) aVal++ let z = -(k - w)++ -- if L%2==o and z>=-(h-o) and z<=h-o and c[k%Z]+d[z%Z] >= N:+ let -- L%2==o+ part1 = upperL `rem` 2 == o+ -- (h-o)+ hmo = h - o+ -- z>=-(h-o)+ part2 = z >= -hmo+ -- z<=h-o+ part3 = z <= hmo+ -- c[k%Z]+d[z%Z] >= N+ mkPart4 = do+ ck <- MV.unsafeRead c (k `modPortable` upperZ)++ dz <- MV.unsafeRead d (z `modPortable` upperZ)++ pure (ck + dz >= upperN)+ mkCondition = part1 &&. (part2 &&. (part3 &&. mkPart4))+ condition <- mkCondition+ if condition+ then do+ -- D,x,y,u,v = (2*h-1,s,t,a,b) if o==1 else (2*h,N-a,M-b,N-s,M-t)+ (upperD, x, y, u, v) <- do+ aVal <- readSTRef a+ bVal <- readSTRef b+ sVal <- readSTRef s+ tVal <- readSTRef t+ pure $+ if o == 1+ then (2 * h - 1, sVal, tVal, aVal, bVal)+ else (2 * h, upperN - aVal, upperM - bVal, upperN - sVal, upperM - tVal)++ -- if D > 1 or (x != u and y != v):+ if upperD > 1 || (x /= u && y /= v)+ then do+ -- return diff(e[0:x],f[0:y],i,j)+diff(e[u:N],f[v:M],i+u,j+v)+ -- diff(e[0:x],f[0:y],i,j)+ firstHalf <- go (V.slice 0 x e) (V.slice 0 y f) i j+ -- diff(e[u:N],f[v:M],i+u,j+v)+ secondHalf <- go (sliceIx u upperN e) (sliceIx v upperM f) (i + u) (j + v)+ pure (Just (firstHalf <> secondHalf))+ else -- elif M > N:++ if upperM > upperN+ then do+ -- return diff([],f[N:M],i+N,j+N)+ Just <$> go V.empty (sliceIx upperN upperM f) (i + upperN) (j + upperN)+ else -- elif M < N:++ if upperM < upperN+ then do+ -- return diff(e[M:N],[],i+M,j+M)+ Just <$> go (sliceIx upperM upperN e) V.empty (i + upperM) (j + upperM)+ else -- else:+ -- return []+ pure (Just mempty)+ else pure Nothing+ case mResult of+ Nothing -> error "Test.Syd.Diff: This is a bug, the diffing algorithm was supposed to terminate and it didn't."+ Just result -> pure result+ else do+ -- elif N > 0: # Modify the return statements below if you want a different edit+ if upperN > 0+ then do+ -- return [{"operation": "delete", "position_old": i+n} for n in range(0,N)]++ pure $ DList.singleton (Delete i upperN)+ else do+ -- return [{"operation": "insert", "position_old": i,"position_new":j+n} for n in range(0,M)]+ if upperM > 0+ then do+ pure $ DList.singleton (Insert i j upperM)+ else do+ pure DList.empty++-- | Compute a diff using an edit script.+--+-- Prefer `computeGroupedDiffFromEditScript` for performance reasons.+computeGroupedDiffFromEditScript :: Vector a -> Vector b -> Vector Edit -> Vector (PolyDiff (Vector a) (Vector b))+computeGroupedDiffFromEditScript old new editSteps = V.create $ do+ -- Computing the exact size is cumbersome, so we make enough space and cut down later.+ -- Enough space means: Space between every two edit steps, and one before and one after.+ let size = length editSteps * 2 + 1+ v <- MV.new size+ groupMarker <- newSTRef 0++ oldMarker <- newSTRef 0+ curMarker <- newSTRef 0+ newMarker <- newSTRef 0++ forM_ editSteps $ \editStep -> do+ -- Copy over the pieces between the last and current edit+ inbetweenIx <- readSTRef oldMarker+ let inbetweenLen = oldPosition editStep - inbetweenIx+ when (inbetweenLen > 0) $ do+ groupIx <- readSTRef groupMarker+ oldIx <- readSTRef oldMarker+ newIx <- readSTRef newMarker+ MV.unsafeWrite v groupIx (Both (V.slice oldIx inbetweenLen old) (V.slice newIx inbetweenLen new))+ modifySTRef groupMarker (+ 1)+ modifySTRef oldMarker (+ inbetweenLen)+ modifySTRef curMarker (+ inbetweenLen)+ modifySTRef newMarker (+ inbetweenLen)++ -- Apply the edit+ case editStep of+ Delete oldPosStart upperN -> do+ groupIx <- readSTRef groupMarker+ MV.unsafeWrite v groupIx (First (V.slice oldPosStart upperN old))+ modifySTRef groupMarker (+ 1)+ modifySTRef oldMarker (+ upperN)+ modifySTRef curMarker (+ upperN)+ Insert _ newPosStart upperM -> do+ groupIx <- readSTRef groupMarker+ MV.unsafeWrite v groupIx (Second (V.slice newPosStart upperM new))+ modifySTRef groupMarker (+ 1)+ modifySTRef curMarker (+ upperM)+ modifySTRef newMarker (+ upperM)++ oldIx <- readSTRef oldMarker+ let afterLen = V.length old - oldIx+ when (afterLen > 0) $ do+ newIx <- readSTRef newMarker+ groupIx <- readSTRef groupMarker+ MV.unsafeWrite v groupIx (Both (V.slice oldIx afterLen old) (V.slice newIx afterLen new))+ modifySTRef groupMarker (+ 1)+ modifySTRef oldMarker (+ 1)+ modifySTRef curMarker (+ 1)+ modifySTRef newMarker (+ 1)++ endGroupIx <- readSTRef groupMarker++ pure (MV.slice 0 endGroupIx v)++-- | Compute a diff using an edit script.+--+-- Prefer `computeGroupedDiffFromEditScript` for performance reasons.+computeDiffFromEditScript :: Vector a -> Vector b -> Vector Edit -> Vector (PolyDiff a b)+computeDiffFromEditScript old new editSteps = V.create $ do+ -- The total size of the diff is the size of the old vector plus the number+ -- of inserts that need to happen.+ -- Not minus the number of deletions, because they get a 'First' constructor and stay.+ let totalSize = V.length old + sum (V.map insertLength editSteps)++ v <- MV.new totalSize+ oldMarker <- newSTRef 0+ curMarker <- newSTRef 0+ newMarker <- newSTRef 0++ forM_ editSteps $ \editStep -> do+ let computeWhileCond1 = do+ oldIx <- readSTRef oldMarker+ pure $ oldPosition editStep > oldIx+ -- Copy over the pieces between the last and current edit+ whileM_ computeWhileCond1 $ do+ oldIx <- readSTRef oldMarker+ curIx <- readSTRef curMarker+ newIx <- readSTRef newMarker+ MV.unsafeWrite v curIx (Both (old ! oldIx) (new ! newIx))+ modifySTRef oldMarker (+ 1)+ modifySTRef curMarker (+ 1)+ modifySTRef newMarker (+ 1)++ -- Apply the edit+ case editStep of+ Delete oldPosStart upperN -> do+ curIx <- readSTRef curMarker+ forM_ [0 .. upperN - 1] $ \n -> do+ MV.unsafeWrite v (curIx + n) (First (old ! (oldPosStart + n)))+ modifySTRef oldMarker (+ upperN)+ modifySTRef curMarker (+ upperN)+ Insert _ newPosStart upperM -> do+ curIx <- readSTRef curMarker+ forM_ [0 .. upperM - 1] $ \n -> do+ MV.unsafeWrite v (curIx + n) (Second (new ! (newPosStart + n)))+ modifySTRef curMarker (+ upperM)+ modifySTRef newMarker (+ upperM)++ let computeWhileCond2 = do+ oldIx <- readSTRef oldMarker+ pure $ oldIx < V.length old++ -- Copy over the pieces between the last and current edit+ whileM_ computeWhileCond2 $ do+ oldIx <- readSTRef oldMarker+ curIx <- readSTRef curMarker+ newIx <- readSTRef newMarker++ MV.unsafeWrite v curIx (Both (old ! oldIx) (new ! newIx))+ modifySTRef oldMarker (+ 1)+ modifySTRef curMarker (+ 1)+ modifySTRef newMarker (+ 1)++ pure v++data Edit+ = -- | Delete from the old vector+ Delete+ Int+ -- ^ position in the old vector+ Int+ -- ^ number of items to delete+ | -- | Insert into the old vector+ Insert+ Int+ -- ^ position in the old vector+ Int+ -- ^ position in the new vector+ Int+ -- ^ number of items to insert+ deriving (Show, Eq, Ord)++oldPosition :: Edit -> Int+oldPosition = \case+ Delete i _ -> i+ Insert i _ _ -> i++insertLength :: Edit -> Int+insertLength = \case+ Delete _ _ -> 0+ Insert _ _ m -> m++modPortable :: Int -> Int -> Int+modPortable a b =+ let r = a `rem` b+ in if r >= 0 then r else r + b++sliceIx :: Int -> Int -> Vector a -> Vector a+sliceIx start end = V.slice start (end - start)++-- | Short-circuiting monadic (&&)+(&&.) :: Applicative m => Bool -> m Bool -> m Bool+(&&.) b1 mkB2 = do+ if b1+ then mkB2+ else pure False++forUntilJust :: Monad m => [a] -> (a -> m (Maybe b)) -> m (Maybe b)+forUntilJust [] _ = pure Nothing+forUntilJust (a : rest) func = do+ mRes <- func a+ case mRes of+ Nothing -> forUntilJust rest func+ Just res -> pure $ Just res++whileM_ :: (Monad m) => m Bool -> m a -> m ()+whileM_ p f = go+ where+ go = do+ x <- p+ if x+ then f >> go+ else return ()
src/Test/Syd/Output.hs view
@@ -8,9 +8,9 @@ module Test.Syd.Output where import Control.Exception-import Data.Algorithm.Diff import qualified Data.List as L-import Data.List.Split (splitWhen)+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NE import Data.Map (Map) import qualified Data.Map as M import Data.Maybe@@ -19,10 +19,12 @@ import qualified Data.Text.Lazy.Builder as LTB import qualified Data.Text.Lazy.Builder as Text import qualified Data.Text.Lazy.IO as LTIO+import qualified Data.Vector as V import Data.Word import GHC.Stack import Safe import Test.QuickCheck.IO ()+import Test.Syd.Diff import Test.Syd.OptParse import Test.Syd.Run import Test.Syd.SpecDef@@ -225,7 +227,7 @@ [ [["Retries: ", chunk (T.pack (show retries)), fore red " !!! FLAKY !!!"]], [[fore magenta $ chunk $ T.pack message] | message <- maybeToList mMessage] ]- else [["Retries: ", chunk (T.pack (show retries)), " (likely not flaky)"]]+ else [["Retries: ", chunk (T.pack (show retries)), " (does not look flaky)"]] labelsChunks :: Word -> Maybe (Map [String] Int) -> [[Chunk]] labelsChunks _ Nothing = []@@ -414,38 +416,81 @@ ExpectationFailed s -> stringChunks s Context a' context -> outputAssertion a' ++ stringChunks context +-- | Split a list of 'Chunk's into lines of [Chunks].+--+-- This is rather complicated because chunks may contain newlines, in which+-- case they need to be split into two chunks on separate lines but with the+-- same colour information.+-- However, separate chunks are not necessarily on separate lines because there+-- may not be a newline inbetween.+splitChunksIntoLines :: [Chunk] -> [[Chunk]]+splitChunksIntoLines =+ -- We maintain a list of 'currently traversing lines'.+ -- These are already split into newlines and therefore definitely belong on separate lines.+ -- We still need to keep the last of the current line though, because it+ -- does not end in a newline and should therefore not necessarily belong on+ -- a separate line by itself.+ go ([] :| []) -- Start with an empty current line.+ where+ -- CurrentlyTraversingLines -> ChunksToStillSplit -> SplitChunks+ go :: NonEmpty [Chunk] -> [Chunk] -> [[Chunk]]+ go cls cs = case NE.uncons cls of+ (currentLine, mRest) -> case mRest of+ -- If there's only one current line, that's the last one of the currently traversing lines.+ -- We split the next chunk into lines and append the first line of that to the current line.+ Nothing -> case cs of+ -- If there is only one current line, and no more chunks, it's the last line.+ [] -> [currentLine]+ -- If there are chunks left, split the first one into lines.+ (c : rest) -> case T.splitOn "\n" (chunkText c) of+ -- Should not happen, but would be fine, just skip this chunk+ [] -> go cls rest+ -- If the chunk had more than one lines+ (l : ls) -> case NE.nonEmpty ls of+ -- If there was only one line in the chunk, we continue with the+ -- same current line onto the rest of the chunks+ Nothing -> go ((currentLine <> [c {chunkText = l}]) :| []) rest+ -- If there was more than one line in that chunk, that line is now considered finished.+ -- We then make all the lines of this new chunk the new current lines, one chunk per line.+ Just ne -> (currentLine <> [c {chunkText = l}]) : go (NE.map (\l' -> [c {chunkText = l'}]) ne) rest+ -- If there is more than one current line, all but the last one are considered finished.+ -- We skip them one by one.+ Just ne -> currentLine : go ne cs+ outputEqualityAssertionFailed :: String -> String -> [[Chunk]] outputEqualityAssertionFailed actual expected =- let diff = getDiff actual expected -- TODO use 'getGroupedDiff' instead, but then we need to fix the 'splitWhen' below- splitLines = splitWhen ((== "\n") . chunkText)+ let diff = V.toList $ getTextDiff (T.pack actual) (T.pack expected)+ -- Add a header to a list of lines of chunks chunksLinesWithHeader :: Chunk -> [[Chunk]] -> [[Chunk]] chunksLinesWithHeader header = \case+ -- If there is only one line, put the header on that line. [cs] -> [header : cs]+ -- If there is more than one line, put the header on a separate line before cs -> [header] : cs actualChunks :: [[Chunk]] actualChunks = chunksLinesWithHeader (fore blue "Actual: ") $- splitLines $+ splitChunksIntoLines $ flip mapMaybe diff $ \case- First a -> Just $ fore red $ chunk (T.singleton a)+ First t -> Just $ fore red $ chunk t Second _ -> Nothing- Both a _ -> Just $ chunk (T.singleton a)+ Both t _ -> Just $ chunk t expectedChunks :: [[Chunk]] expectedChunks = chunksLinesWithHeader (fore blue "Expected: ") $- splitLines $+ splitChunksIntoLines $ flip mapMaybe diff $ \case First _ -> Nothing- Second a -> Just $ fore green $ chunk (T.singleton a)- Both a _ -> Just $ chunk (T.singleton a)+ Second t -> Just $ fore green $ chunk t+ Both t _ -> Just $ chunk t inlineDiffChunks :: [[Chunk]] inlineDiffChunks = if length (lines actual) == 1 && length (lines expected) == 1 then [] else chunksLinesWithHeader (fore blue "Inline diff: ") $- splitLines $+ splitChunksIntoLines $ flip map diff $ \case- First a -> fore red $ chunk (T.singleton a)- Second a -> fore green $ chunk (T.singleton a)- Both a _ -> chunk (T.singleton a)+ First t -> fore red $ chunk t+ Second t -> fore green $ chunk t+ Both t _ -> chunk t in concat [ [[chunk "Expected these values to be equal:"]], actualChunks,
sydtest.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: sydtest-version: 0.13.0.0+version: 0.13.0.1 synopsis: A modern testing framework for Haskell with good defaults and advanced testing features. description: A modern testing framework for Haskell with good defaults and advanced testing features. Sydtest aims to make the common easy and the hard possible. See https://github.com/NorfairKing/sydtest#readme for more information. category: Testing@@ -45,6 +45,7 @@ Test.Syd.Def.SetupFunc Test.Syd.Def.Specify Test.Syd.Def.TestDefM+ Test.Syd.Diff Test.Syd.Expectation Test.Syd.HList Test.Syd.Modify@@ -66,8 +67,7 @@ hs-source-dirs: src build-depends:- Diff- , MonadRandom+ MonadRandom , QuickCheck , async , autodocodec@@ -88,9 +88,9 @@ , random-shuffle , safe , safe-coloured-text- , split , stm , text+ , vector if os(windows) build-depends: ansi-terminal@@ -127,6 +127,7 @@ Test.Syd.AroundCombinationSpec Test.Syd.AroundSpec Test.Syd.DescriptionsSpec+ Test.Syd.DiffSpec Test.Syd.ExpectationSpec Test.Syd.FootgunSpec Test.Syd.GoldenSpec@@ -152,4 +153,5 @@ , stm , sydtest , text+ , vector default-language: Haskell2010
+ test/Test/Syd/DiffSpec.hs view
@@ -0,0 +1,288 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module Test.Syd.DiffSpec (spec) where++import Control.Monad+import Data.String+import qualified Data.Text as T+import Data.Vector (Vector)+import qualified Data.Vector as V+import Test.Syd+import Test.Syd.Diff++-- Just for this test+instance IsString (Vector Char) where+ fromString = V.fromList++spec :: Spec+spec = do+ describe "getEditScript" $ do+ let exampleSpec :: Vector Char -> Vector Char -> Vector Edit -> Spec+ exampleSpec old new expected =+ it+ ( unwords+ [ "works for",+ show old,+ show new,+ show expected+ ]+ )+ $ do+ let actual = getEditScript old new+ when (actual /= expected) $+ expectationFailure $+ unlines+ [ unwords ["actual:", show actual],+ unwords ["expected:", show expected]+ ]++ exampleSpec "" "" []+ exampleSpec "a" "a" []+ exampleSpec "a" "b" [Delete 0 1, Insert 1 0 1]+ exampleSpec "ab" "ac" [Delete 1 1, Insert 2 1 1]+ exampleSpec "b" "aba" [Insert 0 0 1, Insert 1 2 1]+ exampleSpec "abc" "acb" [Delete 1 1, Insert 3 2 1]+ exampleSpec "aaa" "aba" [Delete 0 1, Insert 2 1 1]+ exampleSpec "foofoo" "foo" [Delete 1 1, Delete 3 1, Delete 5 1]+ exampleSpec "foo" "foofoo" [Insert 1 1 1, Insert 2 3 1, Insert 3 5 1]++ describe "computeDiffFromEditScript" $ do+ let exampleSpec :: Vector Char -> Vector Char -> Vector Edit -> Vector (Diff Char) -> Spec+ exampleSpec old new editScript expected =+ it+ ( unwords+ [ "works for",+ show old,+ show new,+ show editScript,+ show expected+ ]+ )+ $ do+ let actual = computeDiffFromEditScript old new editScript+ when (actual /= expected) $+ expectationFailure $+ unlines+ [ unwords ["actual:", show actual],+ unwords ["expected:", show expected]+ ]++ exampleSpec "" "" [] []+ exampleSpec "a" "a" [] [Both 'a' 'a']+ exampleSpec "a" "b" [Delete 0 1, Insert 1 0 1] [First 'a', Second 'b']+ exampleSpec "ab" "ac" [Delete 1 1, Insert 2 1 1] [Both 'a' 'a', First 'b', Second 'c']+ exampleSpec "b" "aba" [Insert 0 0 1, Insert 1 2 1] [Second 'a', Both 'b' 'b', Second 'a']+ exampleSpec "abc" "acb" [Delete 1 1, Insert 3 2 1] [Both 'a' 'a', First 'b', Both 'c' 'c', Second 'b']+ exampleSpec "aaa" "aba" [Delete 0 1, Insert 2 1 1] [First 'a', Both 'a' 'a', Second 'b', Both 'a' 'a']+ exampleSpec "foofoo" "foo" [Delete 1 1, Delete 3 1, Delete 5 1] [Both 'f' 'f', First 'o', Both 'o' 'o', First 'f', Both 'o' 'o', First 'o']+ exampleSpec "foo" "foofoo" [Insert 1 1 1, Insert 2 3 1, Insert 3 5 1] [Both 'f' 'f', Second 'o', Both 'o' 'o', Second 'f', Both 'o' 'o', Second 'o']++ describe "getVectorDiff" $ do+ let exampleSpec :: Vector Char -> Vector Char -> Vector (Diff Char) -> Spec+ exampleSpec old new expected =+ it+ ( unwords+ [ "works for",+ show old,+ show new,+ show expected+ ]+ )+ $ do+ let actual = getVectorDiff old new+ when (actual /= expected) $+ expectationFailure $+ unlines+ [ unwords ["actual:", show actual],+ unwords ["expected:", show expected]+ ]+ exampleSpec "" "" []+ exampleSpec "a" "a" [Both 'a' 'a']+ exampleSpec "a" "b" [First 'a', Second 'b']+ exampleSpec "ab" "ac" [Both 'a' 'a', First 'b', Second 'c']+ exampleSpec "aaa" "aba" [First 'a', Both 'a' 'a', Second 'b', Both 'a' 'a']+ exampleSpec "abgdef" "gh" [First 'a', First 'b', Both 'g' 'g', First 'd', First 'e', Second 'h', First 'f']+ exampleSpec "foofoo" "foo" [Both 'f' 'f', First 'o', Both 'o' 'o', First 'f', Both 'o' 'o', First 'o']++ prop "says that any list is entirely different from the empty list (left)" $ \ls ->+ let v = V.fromList (ls :: String)+ in getVectorDiff v "" `shouldBe` V.map First v++ prop "says that any list is entirely different from the empty list (right)" $ \ls ->+ let v = V.fromList (ls :: String)+ in getVectorDiff "" v `shouldBe` V.map Second v++ prop "does not find diffs in identical strings" $ \ls ->+ let v = V.fromList (ls :: String)+ in getVectorDiff v v `shouldBe` V.map (\a -> Both a a) v++ prop "only puts equal characters in 'Both'" $ \(ls1, ls2) ->+ let v1 = V.fromList (ls1 :: String)+ v2 = V.fromList (ls2 :: String)+ diff = getVectorDiff v1 v2+ valid = \case+ First _ -> True+ Second _ -> True+ Both a b -> a == b+ in all valid diff++ let rebuildFirst :: Vector (PolyDiff a b) -> Vector a+ rebuildFirst = V.mapMaybe $ \case+ First a -> Just a+ Second _ -> Nothing+ Both a _ -> Just a+ prop "lets you rebuild the old vector" $ \(ls1, ls2) -> do+ let v1 = V.fromList (ls1 :: String)+ v2 = V.fromList (ls2 :: String)+ rebuildFirst (getVectorDiff v1 v2) `shouldBe` v1++ let rebuildSecond :: Vector (PolyDiff a b) -> Vector b+ rebuildSecond = V.mapMaybe $ \case+ First _ -> Nothing+ Second b -> Just b+ Both _ b -> Just b+ prop "lets you rebuild the new vector" $ \(ls1, ls2) -> do+ let v1 = V.fromList (ls1 :: String)+ v2 = V.fromList (ls2 :: String)+ rebuildSecond (getVectorDiff v1 v2) `shouldBe` v2++ let unrollGroupedDiff :: Vector (PolyDiff (Vector a) (Vector b)) -> Vector (PolyDiff a b)+ unrollGroupedDiff = foldMap unrollPolyDiff++ -- Not performant, only for testing+ unrollPolyDiff :: PolyDiff (Vector a) (Vector b) -> Vector (PolyDiff a b)+ unrollPolyDiff = \case+ First va -> V.map First va+ Second vb -> V.map Second vb+ Both va vb -> V.zipWith Both va vb++ prop "is the same thing as getGroupedVectorDiff, but unrolled (unroll grouped)" $ \(ls1, ls2) ->+ let v1 = V.fromList (ls1 :: String)+ v2 = V.fromList (ls2 :: String)+ grouped = getGroupedVectorDiff v1 v2+ individual = getVectorDiff v1 v2+ in unrollGroupedDiff grouped `shouldBe` individual++ describe "getGroupedVectorDiff" $ do+ prop "says that any list is entirely different from the empty list (left)" $ \ls ->+ let v = V.fromList (ls :: String)+ in getGroupedVectorDiff v "" `shouldBe` (if V.null v then V.empty else V.singleton (First v))++ prop "says that any list is entirely different from the empty list (right)" $ \ls ->+ let v = V.fromList (ls :: String)+ in getGroupedVectorDiff "" v `shouldBe` (if V.null v then V.empty else V.singleton (Second v))++ prop "does not find diffs in identical strings" $ \ls ->+ let v = V.fromList (ls :: String)+ in getGroupedVectorDiff v v `shouldBe` (if V.null v then V.empty else V.singleton (Both v v))++ prop "only puts equal vectors in 'Both'" $ \(ls1, ls2) ->+ let v1 = V.fromList (ls1 :: String)+ v2 = V.fromList (ls2 :: String)+ diff = getGroupedVectorDiff v1 v2+ valid = \case+ First _ -> True+ Second _ -> True+ Both a b -> a == b+ in all valid diff++ let rebuildFirst :: Vector (PolyDiff (Vector a) (Vector b)) -> Vector a+ rebuildFirst =+ V.concat . V.toList+ . V.mapMaybe+ ( \case+ First a -> Just a+ Second _ -> Nothing+ Both a _ -> Just a+ )+ prop "lets you rebuild the old vector" $ \(ls1, ls2) -> do+ let v1 = V.fromList (ls1 :: String)+ v2 = V.fromList (ls2 :: String)+ rebuildFirst (getGroupedVectorDiff v1 v2) `shouldBe` v1++ let rebuildSecond :: Vector (PolyDiff (Vector a) (Vector b)) -> Vector b+ rebuildSecond =+ V.concat . V.toList+ . V.mapMaybe+ ( \case+ First _ -> Nothing+ Second b -> Just b+ Both _ b -> Just b+ )+ prop "lets you rebuild the new vector" $ \(ls1, ls2) -> do+ let v1 = V.fromList (ls1 :: String)+ v2 = V.fromList (ls2 :: String)+ rebuildSecond (getGroupedVectorDiff v1 v2) `shouldBe` v2++ -- Not performant, only for testing.+ let rollupUngroupedDiff :: Vector (PolyDiff a b) -> Vector (PolyDiff (Vector a) (Vector b))+ rollupUngroupedDiff = V.fromList . go . V.toList+ where+ go = \case+ [] -> []+ (d : ds) -> case d of+ First _ ->+ let (fs, rest) = goFirsts (d : ds)+ in First (V.fromList fs) : go rest+ Second _ ->+ let (ss, rest) = goSeconds (d : ds)+ in Second (V.fromList ss) : go rest+ Both _ _ ->+ let (bs, rest) = goBoths (d : ds)+ in Both (V.fromList (map fst bs)) (V.fromList (map snd bs)) : go rest+ goFirsts = \case+ (First a : ds) ->+ let (fs, rest) = goFirsts ds+ in (a : fs, rest)+ rest -> ([], rest)+ goSeconds = \case+ (Second a : ds) ->+ let (fs, rest) = goSeconds ds+ in (a : fs, rest)+ rest -> ([], rest)+ goBoths = \case+ (Both a b : ds) ->+ let (fs, rest) = goBoths ds+ in ((a, b) : fs, rest)+ rest -> ([], rest)++ let squishGroupedDiff :: Vector (PolyDiff (Vector a) (Vector b)) -> Vector (PolyDiff (Vector a) (Vector b))+ squishGroupedDiff = V.fromList . go . V.toList+ where+ go = \case+ [] -> []+ [pd] -> [pd]+ (First a : First b : rest) -> go (First (a <> b) : rest)+ (Second a : Second b : rest) -> go (Second (a <> b) : rest)+ (Both a b : Both c d : rest) -> go (Both (a <> c) (b <> d) : rest)+ (d : rest) -> d : go rest+ prop "is the same thing as getVectorDiff, but rolled up and squished (rollup individual)" $ \(ls1, ls2) ->+ let v1 = V.fromList (ls1 :: String)+ v2 = V.fromList (ls2 :: String)+ grouped = getGroupedVectorDiff v1 v2+ individual = getVectorDiff v1 v2+ in squishGroupedDiff grouped `shouldBe` rollupUngroupedDiff individual++ describe "getStringDiff" $ do+ it "can output a large diff quickly enough if there is no diff" $+ let s = replicate 10000 'a'+ in length (getStringDiff s s) `shouldBe` 10000++ it "can output a large diff quickly enough if it's only diff" $+ length (getStringDiff (replicate 10000 'a') "b") `shouldBe` 10001++ describe "getTextDiff" $ do+ it "can output a large diff quickly enough if there is no diff" $+ let s = T.pack $ replicate 10000 'a'+ in length (getTextDiff s s) `shouldBe` 1++ it "can output a large diff quickly enough if it's only diff" $+ length (getTextDiff (T.pack (replicate 10000 'a')) "b") `shouldBe` 15++ describe "outputEqualityAssertionFailed" $ do+ it "can output a large diff quickly enough" $+ length (outputEqualityAssertionFailed (replicate 10000 'a') "b") `shouldBe` 3
test_resources/output-test.txt view
@@ -3,71 +3,71 @@ [32m✓ [m[32mPasses[m [32m 0.00 ms[m [33merror[m [31m✗ [m[31mPure error[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mImpure error[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mundefined[m [31m✗ [m[31mPure undefined[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mImpure undefined[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mExit code[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mexceptions[m [31m✗ [m[31mRecord construction error[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mRecord construction error[m [31m✗ [m[31mfails in IO, as the result[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in IO, as the action[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in pure code[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mRecord selection error[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mRecord selection error[m [31m✗ [m[31mfails in IO, as the result[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in IO, as the action[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in pure code[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mRecord update error[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mRecord update error[m [31m✗ [m[31mfails in IO, as the result[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in IO, as the action[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in pure code[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mPattern matching error[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mPattern matching error[m [31m✗ [m[31mfails in IO, as the result[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in IO, as the action[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in pure code[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mArithException[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mPattern matching error[m [31m✗ [m[31mfails in IO, as the result[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in IO, as the action[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in pure code[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mNoMethodError[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mPattern matching error[m [31m✗ [m[31mfails in IO, as the result[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in IO, as the action[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mfails in pure code[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mPrinting[m [32m✓ [m[32mprint[m [32m 0.00 ms[m [32m✓ [m[32mputStrLn[m [32m 0.00 ms[m@@ -76,14 +76,14 @@ [32m✓ [m[32mreversing a list twice is the same as reversing it once[m [32m 0.00 ms[m passed for all of [32m10[m inputs. [31m✗ [m[31mshould fail to show that sorting does nothing[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [32m✓ [m[32mshould work with custom generators too[m [32m 0.00 ms[m passed for all of [32m10[m inputs. [33mimpure[m [32m✓ [m[32mreversing a list twice is the same as reversing it once[m [32m 0.00 ms[m passed for all of [32m10[m inputs. [31m✗ [m[31mshould fail to show that sorting does nothing[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [32m✓ [m[32mshould work with custom generators too[m [32m 0.00 ms[m passed for all of [32m10[m inputs. [33mLong running tests[m@@ -99,88 +99,88 @@ [32m✓ [m[32mtakes a while (10)[m [32m 0.00 ms[m [33mDiff[m [31m✗ [m[31mshows nice multi-line diffs[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mshows nice multi-line diffs[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33massertions[m [31m✗ [m[31mshouldBe[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mshouldNotBe[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mshouldSatisfy[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mshouldNotSatisfy[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mshouldSatisfyNamed[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mshouldNotSatisfyNamed[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [35mpending test[m [33mGolden[m [31m✗ [m[31mdoes not fail the suite when an exception happens while reading[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mdoes not fail the suite when an exception happens while producing[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31mGolden output not found[m [31m✗ [m[31mdoes not fail the suite when an exception happens while writing[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31mGolden output not found[m [31m✗ [m[31mdoes not fail the suite when an exception happens while checking for equality[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33moutputResultForest[m [32m✓ [m[32moutputs the same as last time[m [32m 0.00 ms[m [33mAround[m [33mbefore[m [31m✗ [m[31mdoes not kill the test suite[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mbefore_[m [31m✗ [m[31mdoes not kill the test suite[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mafter[m [31m✗ [m[31mdoes not kill the test suite[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mafter_[m [31m✗ [m[31mdoes not kill the test suite[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33maround[m [31m✗ [m[31mdoes not kill the test suite[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33maround_[m [31m✗ [m[31mdoes not kill the test suite[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33maroundWith[m [31m✗ [m[31mdoes not kill the test suite[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33maroundWith'[m [31m✗ [m[31mdoes not kill the test suite[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mexpectationFailure[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mString[m [31m✗ [m[31mcompares strings[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mcompares strings[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mcompares texts[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mcompares texts[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mcompares bytestrings[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mContext[m [31m✗ [m[31mshows a nice context[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mshows a nice context multiple levels deep[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mshows a context when an exception is thrown as well[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mProperty[m [33m0 tests run[m [32m✓ [m[32mshows a red '0 tests' when no tests are run[m [32m 0.00 ms[m passed for all of [31m0[m inputs. [33mgenerated values[m [31m✗ [m[31mshows many generated values too[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mlabels[m [32m✓ [m[32mshows the labels in use on success[m [32m 0.00 ms[m passed for all of [32m100[m inputs.@@ -216,7 +216,7 @@ 2.00% "length of input is 7", "magnitude (digits) of sum of input is 1" 1.00% "length of input is 8", "magnitude (digits) of sum of input is 0" [31m✗ [m[31mshows the labels in use on failure[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Labels 100.00% "length of input is 0", "magnitude (digits) of sum of input is 0" [33mclasses[m@@ -231,7 +231,7 @@ 48.00% non-trivial 21.00% single element [31m✗ [m[31mshows the classes in use on failure[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Classes 100.00% empty [33mtables[m@@ -260,22 +260,22 @@ 1.38% 9 [33mShrinking[m [31m✗ [m[31mcan grab the mvar during shrinking[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mRetries[m [31m✗ [m[31mdoes not retry if the test is configured withoutRetries[m [32m 0.00 ms[m [31m✗ [m[31mRetries this five times[m [32m 0.00 ms[m- Retries: 5 (likely not flaky)+ Retries: 5 (does not look flaky) [33mFlakiness[m [32m✓ [m[32mAllows flakiness on True eventhough there is none (should succeed)[m [32m 0.00 ms[m [31m✗ [m[31mAllows flakiness on False eventhough there is none (should fail)[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [32m✓ [m[32mallows this intentionally flaky test with the default number of retries[m [32m 0.00 ms[m Retries: 2[31m !!! FLAKY !!![m [35mWe're on it![m [31m✗ [m[31mDoes not allow flakiness if flakiness is not allowed even if retries happen[m [32m 0.00 ms[m Retries: 2[31m !!! FLAKY !!![m [31m✗ [m[31mAllows flakiness in this boolean five times (should fail with 5 retries)[m [32m 0.00 ms[m- Retries: 5 (likely not flaky)+ Retries: 5 (does not look flaky) [32m✓ [m[32mallows this intentionally flaky test with up to four retries[m [32m 0.00 ms[m Retries: 2[31m !!! FLAKY !!![m [35mWe're on it![m@@ -291,61 +291,61 @@ [35mfour[m [33mcallstack[m [31m✗ [m[31mit[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mspecify[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mprop[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mdescribe[m [31m✗ [m[31mdescribe-it[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mdescribe-specify[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mexpectations[m [32m✓ [m[32mconsidered passing[m [32m 0.00 ms[m [32m✓ [m[32mconsidered passing[m [32m 0.00 ms[m [31m✗ [m[31mconsidered failing[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mconsidered failing[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mcombinators[m [31m✗ [m[31mshould fail[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [32m✓ [m[32mshould pass[m [32m 0.00 ms[m passed for all of [32m100[m inputs. [31m✗ [m[31mshould not crash (undefined value)[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mshould not crash (undefined generator)[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31m✗ [m[31mshould be even[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [32m✓ [m[32mshould be even[m [32m 0.00 ms[m [31m✗ [m[31mshould be even[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [33mrandomness[m [31m✗ [m[31malways outputs the same pseudorandomness[m [32m 0.00 ms[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [34mFailures:[m [36m output-test/Spec.hs:35[m [31m✗ [m[31m1 [m[31merror.Pure error[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) foobar CallStack (from HasCallStack): error, called at output-test/Spec.hs:35:28 in main:Spec [36m output-test/Spec.hs:36[m [31m✗ [m[31m2 [m[31merror.Impure error[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) foobar CallStack (from HasCallStack): error, called at output-test/Spec.hs:36:24 in main:Spec [36m output-test/Spec.hs:38[m [31m✗ [m[31m3 [m[31mundefined.Pure undefined[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Prelude.undefined CallStack (from HasCallStack): error, called at libraries/base/GHC/Err.hs:75:14 in base:GHC.Err@@ -353,7 +353,7 @@ [36m output-test/Spec.hs:39[m [31m✗ [m[31m4 [m[31mundefined.Impure undefined[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Prelude.undefined CallStack (from HasCallStack): error, called at libraries/base/GHC/Err.hs:75:14 in base:GHC.Err@@ -361,147 +361,147 @@ [36m output-test/Spec.hs:40[m [31m✗ [m[31m5 [m[31mExit code[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) ExitFailure 1 [36m output-test/Spec.hs:48[m [31m✗ [m[31m6 [m[31mexceptions.Record construction error[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) test [36m output-test/Spec.hs:45[m [31m✗ [m[31m7 [m[31mexceptions.Record construction error.fails in IO, as the result[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:49:57-64: Missing field in record construction field [36m output-test/Spec.hs:46[m [31m✗ [m[31m8 [m[31mexceptions.Record construction error.fails in IO, as the action[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:49:57-64: Missing field in record construction field [36m output-test/Spec.hs:47[m [31m✗ [m[31m9 [m[31mexceptions.Record construction error.fails in pure code[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:49:57-64: Missing field in record construction field [36m output-test/Spec.hs:50[m [31m✗ [m[31m10 [m[31mexceptions.Record selection error[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) test [36m output-test/Spec.hs:45[m [31m✗ [m[31m11 [m[31mexceptions.Record selection error.fails in IO, as the result[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) No match in record selector field [36m output-test/Spec.hs:46[m [31m✗ [m[31m12 [m[31mexceptions.Record selection error.fails in IO, as the action[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) No match in record selector field [36m output-test/Spec.hs:47[m [31m✗ [m[31m13 [m[31mexceptions.Record selection error.fails in pure code[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) No match in record selector field [36m output-test/Spec.hs:52[m [31m✗ [m[31m14 [m[31mexceptions.Record update error[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) test [36m output-test/Spec.hs:45[m [31m✗ [m[31m15 [m[31mexceptions.Record update error.fails in IO, as the result[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:53:60-88: Non-exhaustive patterns in record update [36m output-test/Spec.hs:46[m [31m✗ [m[31m16 [m[31mexceptions.Record update error.fails in IO, as the action[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:53:60-88: Non-exhaustive patterns in record update [36m output-test/Spec.hs:47[m [31m✗ [m[31m17 [m[31mexceptions.Record update error.fails in pure code[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:53:60-88: Non-exhaustive patterns in record update [36m output-test/Spec.hs:54[m [31m✗ [m[31m18 [m[31mexceptions.Pattern matching error[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) test [36m output-test/Spec.hs:45[m [31m✗ [m[31m19 [m[31mexceptions.Pattern matching error.fails in IO, as the result[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:55:50-64: Non-exhaustive patterns in Cons1 s [36m output-test/Spec.hs:46[m [31m✗ [m[31m20 [m[31mexceptions.Pattern matching error.fails in IO, as the action[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:55:50-64: Non-exhaustive patterns in Cons1 s [36m output-test/Spec.hs:47[m [31m✗ [m[31m21 [m[31mexceptions.Pattern matching error.fails in pure code[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:55:50-64: Non-exhaustive patterns in Cons1 s [36m output-test/Spec.hs:56[m [31m✗ [m[31m22 [m[31mexceptions.ArithException[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) arithmetic underflow [36m output-test/Spec.hs:45[m [31m✗ [m[31m23 [m[31mexceptions.Pattern matching error.fails in IO, as the result[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) divide by zero [36m output-test/Spec.hs:46[m [31m✗ [m[31m24 [m[31mexceptions.Pattern matching error.fails in IO, as the action[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) divide by zero [36m output-test/Spec.hs:47[m [31m✗ [m[31m25 [m[31mexceptions.Pattern matching error.fails in pure code[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) divide by zero [36m output-test/Spec.hs:58[m [31m✗ [m[31m26 [m[31mexceptions.NoMethodError[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) test [36m output-test/Spec.hs:45[m [31m✗ [m[31m27 [m[31mexceptions.Pattern matching error.fails in IO, as the result[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:29:10-19: No instance nor default method for class operation toUnit [36m output-test/Spec.hs:46[m [31m✗ [m[31m28 [m[31mexceptions.Pattern matching error.fails in IO, as the action[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:29:10-19: No instance nor default method for class operation toUnit [36m output-test/Spec.hs:47[m [31m✗ [m[31m29 [m[31mexceptions.Pattern matching error.fails in pure code[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) output-test/Spec.hs:29:10-19: No instance nor default method for class operation toUnit [36m output-test/Spec.hs:72[m [31m✗ [m[31m30 [m[31mProperty tests.pure.should fail to show that sorting does nothing[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Failed after 3 tests Generated: [33m[-5,3,11,-3,-13,0,19,-10,15,-13,1,-12,10,-18,-3,-2,6,-9,-6][m [36m output-test/Spec.hs:80[m [31m✗ [m[31m31 [m[31mProperty tests.impure.should fail to show that sorting does nothing[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Failed after 3 tests Generated: [33m[-5,3,11,-3,-13,0,19,-10,15,-13,1,-12,10,-18,-3,-2,6,-9,-6][m Expected these values to be equal: [34mActual: [m [ -[31m1[m[31m8[m- , [31m-[m[31m1[m3 , [31m-[m1[31m3[m- , -[31m1[m[31m2[m- , -10+ , -[31m1[m3+ , -1[31m2[m+ , [31m-1[m0 , [31m-[m9 , -[31m6[m , [31m-[m5@@ -512,18 +512,18 @@ , 1 , 3 , [31m6[m- [31m,[m[31m [m[31m1[m[31m0[m- , [31m1[m[31m1[m- , [31m1[m[31m5[m+ , [31m1[m[31m0[m+ , [31m11[m+ [31m, 1[m[31m5[m , [31m1[m[31m9[m ] [34mExpected: [m- [ -[32m5[m- , 3+ [ -[32m5[m[32m[m+ [32m[m[32m, [m[32m3[m , 1[32m1[m- , -[32m3[m+ , -3 , -1[32m3[m- [32m,[m[32m [m0+ , 0 , [32m1[m9 , -[32m1[m[32m0[m , [32m1[m5@@ -536,241 +536,241 @@ , [32m-[m[32m2[m , [32m6[m , [32m-[m[32m9[m- , [32m-[m[32m6[m+ , [32m-6[m ] [34mInline diff: [m- [ -[31m1[m[31m8[m[32m5[m- , [31m-[m[31m1[m3- , [31m-[m1[31m3[m[32m1[m- , -[31m1[m[31m2[m[32m3[m- , -1[32m3[m- [32m,[m[32m [m0+ [ -[32m5[m[32m[m+ [32m[m[32m, [m[31m1[m[32m3[m[31m8[m+ , [31m-[m1[32m1[m[31m3[m+ , -[31m1[m3+ , -1[32m3[m[31m2[m+ , [31m-1[m0 , [31m-[m[32m1[m9- , -[31m6[m[32m1[m[32m0[m- , [31m-[m[32m1[m5+ , -[32m1[m[31m6[m[32m0[m+ , [32m1[m[31m-[m5 , -[32m1[m3 , [31m-[m[31m3[m[32m1[m , -[32m1[m2 , [32m1[m0 , [32m-[m1[32m8[m , [32m-[m3- , [31m6[m- [31m,[m[31m [m[31m1[m[31m0[m[32m-[m[32m2[m- , [31m1[m[31m1[m[32m6[m- , [31m1[m[31m5[m[32m-[m[32m9[m- , [31m1[m[31m9[m[32m-[m[32m6[m+ , [32m-[m[31m6[m[32m2[m+ , [31m1[m[31m0[m[32m6[m+ , [32m-[m[31m11[m+ [31m, 1[m[31m5[m[32m9[m+ , [31m1[m[32m-6[m[31m9[m ] [36m output-test/Spec.hs:90[m [31m✗ [m[31m32 [m[31mDiff.shows nice multi-line diffs[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal: [34mActual: [m ( "foo"- , [ "quux" , "quux" , "quux" , "quux" , "quux" , "quux" [31m,[m[31m [m[31m"[m[31mq[m[31mu[m[31mu[m[31mx[m[31m"[m[31m [m]+ , [[31m [m[31m"qu[m[31mux[m[31m"[m [31m,[m[31m [m"quux" , "quux" , "quux" , "quux" , "quux" , "quux" ] , "ba[31mr[m" ) [34mExpected: [m- ( "foo[32mf[m[32mo[m[32mo[m"+ ( "[32mf[m[32mo[m[32mo[mfoo" , [ "quux" , "quux" , "quux" , "quux" , "quux" , "quux" ] , "ba[32mz[m" ) [34mInline diff: [m- ( "foo[32mf[m[32mo[m[32mo[m"- , [ "quux" , "quux" , "quux" , "quux" , "quux" , "quux" [31m,[m[31m [m[31m"[m[31mq[m[31mu[m[31mu[m[31mx[m[31m"[m[31m [m]- , "ba[31mr[m[32mz[m"+ ( "[32mf[m[32mo[m[32mo[mfoo"+ , [[31m [m[31m"qu[m[31mux[m[31m"[m [31m,[m[31m [m"quux" , "quux" , "quux" , "quux" , "quux" , "quux" ]+ , "ba[32mz[m[31mr[m" ) [36m output-test/Spec.hs:92[m [31m✗ [m[31m33 [m[31mDiff.shows nice multi-line diffs[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal: [34mActual: [m( "foo"[31m [m, [[31m][m , "ba[31mr[m"[31m [m) [34mExpected: [m- ( "foo[32mf[m[32mo[m[32mo[m"- , [ [32m"[m[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m, "[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m[32m,[m[32m [m[32m"[m[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m[32m,[m[32m [m[32m"[m[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m[32m,[m[32m [m[32m"[m[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m[32m,[m[32m [m[32m"[m[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m[32m][m- [32m,[m[32m [m[32m"[mba[32mz[m"- )+ ( "[32mf[m[32mo[m[32mo[mfoo"[32m[m+ [32m[m, [[32m "[m[32mquux" , [m[32m"quux" , "quux" [m[32m, "quux" , "quux[m[32m" , "quu[m[32mx"[m [32m][m+ [32m[m, "ba[32mz[m"[32m[m+ [32m[m) [34mInline diff: [m- ( "foo[32mf[m[32mo[m[32mo[m"[31m [m- , [[31m][m [32m"[m[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m, "[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m[32m,[m[32m [m[32m"[m[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m[32m,[m[32m [m[32m"[m[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m[32m,[m[32m [m[32m"[m[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m[32m,[m[32m [m[32m"[m[32mq[m[32mu[m[32mu[m[32mx[m[32m"[m[32m [m[32m][m- [32m,[m[32m [m[32m"[mba[31mr[m[32mz[m"[31m [m- )+ ( "[32mf[m[32mo[m[32mo[mfoo"[31m [m[32m[m+ [32m[m, [[31m][m[32m "[m[32mquux" , [m[32m"quux" , "quux" [m[32m, "quux" , "quux[m[32m" , "quu[m[32mx"[m [32m][m+ [32m[m, "ba[31mr[m[32mz[m"[31m [m[32m[m+ [32m[m) [36m output-test/Spec.hs:95[m [31m✗ [m[31m34 [m[31massertions.shouldBe[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal: [34mActual: [m[31m3[m [34mExpected: [m[32m4[m [36m output-test/Spec.hs:96[m [31m✗ [m[31m35 [m[31massertions.shouldNotBe[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Did not expect equality of the values but both were: 3 [36m output-test/Spec.hs:97[m [31m✗ [m[31m36 [m[31massertions.shouldSatisfy[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Predicate failed, but should have succeeded, on this value: 3 [36m output-test/Spec.hs:98[m [31m✗ [m[31m37 [m[31massertions.shouldNotSatisfy[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Predicate succeeded, but should have failed, on this value: 3 [36m output-test/Spec.hs:99[m [31m✗ [m[31m38 [m[31massertions.shouldSatisfyNamed[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Predicate failed, but should have succeeded, on this value: 3 Predicate: even [36m output-test/Spec.hs:100[m [31m✗ [m[31m39 [m[31massertions.shouldNotSatisfyNamed[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Predicate succeeded, but should have failed, on this value: 3 Predicate: odd [36m output-test/Spec.hs:103[m [31m✗ [m[31m40 [m[31mGolden.does not fail the suite when an exception happens while reading[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) ExitFailure 1 [36m output-test/Spec.hs:111[m [31m✗ [m[31m41 [m[31mGolden.does not fail the suite when an exception happens while producing[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31mGolden output not found[m [36m output-test/Spec.hs:119[m [31m✗ [m[31m42 [m[31mGolden.does not fail the suite when an exception happens while writing[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [31mGolden output not found[m [36m output-test/Spec.hs:126[m [31m✗ [m[31m43 [m[31mGolden.does not fail the suite when an exception happens while checking for equality[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) divide by zero [36m output-test/Spec.hs:149[m [31m✗ [m[31m44 [m[31mAround.before.does not kill the test suite[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) user error (test) [36m output-test/Spec.hs:154[m [31m✗ [m[31m45 [m[31mAround.before_.does not kill the test suite[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) user error (test) [36m output-test/Spec.hs:159[m [31m✗ [m[31m46 [m[31mAround.after.does not kill the test suite[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) user error (test) [36m output-test/Spec.hs:164[m [31m✗ [m[31m47 [m[31mAround.after_.does not kill the test suite[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) user error (test) [36m output-test/Spec.hs:169[m [31m✗ [m[31m48 [m[31mAround.around.does not kill the test suite[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) user error (test) [36m output-test/Spec.hs:174[m [31m✗ [m[31m49 [m[31mAround.around_.does not kill the test suite[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) user error (test) [36m output-test/Spec.hs:179[m [31m✗ [m[31m50 [m[31mAround.aroundWith.does not kill the test suite[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) user error (test) [36m output-test/Spec.hs:184[m [31m✗ [m[31m51 [m[31mAround.aroundWith'.does not kill the test suite[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) user error (test) [36m output-test/Spec.hs:187[m [31m✗ [m[31m52 [m[31mexpectationFailure[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) fails [36m output-test/Spec.hs:190[m [31m✗ [m[31m53 [m[31mString.compares strings[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal:- [34mActual: [m"fo[31mo[m\nba[31mr[m\tq[31mu[m[31mu[mx[31m [m"+ [34mActual: [m"f[31mo[mo\nba[31mr[m\tq[31muu[mx[31m [m" [34mExpected: [m"fo[32mq[m\nba[32mz[m\tq[32me[mx" [36m output-test/Spec.hs:191[m [31m✗ [m[31m54 [m[31mString.compares strings[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal: [34mActual: [m- fo[31mo[m- ba[31mr[m q[31mu[m[31mu[mx[31m [m+ f[31mo[mo+ ba[31mr[m q[31muu[mx[31m [m [34mExpected: [m fo[32mq[m ba[32mz[m q[32me[mx [34mInline diff: [m- fo[31mo[m[32mq[m- ba[31mr[m[32mz[m q[31mu[m[31mu[m[32me[mx[31m [m+ f[31mo[mo[32mq[m+ ba[31mr[m[32mz[m q[31muu[m[32me[mx[31m [m [36m output-test/Spec.hs:192[m [31m✗ [m[31m55 [m[31mString.compares texts[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal:- [34mActual: [m"fo[31mo[m\nba[31mr[m\tq[31mu[m[31mu[mx[31m [m"+ [34mActual: [m"f[31mo[mo\nba[31mr[m\tq[31muu[mx[31m [m" [34mExpected: [m"fo[32mq[m\nba[32mz[m\tq[32me[mx" [36m output-test/Spec.hs:193[m [31m✗ [m[31m56 [m[31mString.compares texts[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal: [34mActual: [m- fo[31mo[m- ba[31mr[m q[31mu[m[31mu[mx[31m [m+ f[31mo[mo+ ba[31mr[m q[31muu[mx[31m [m [34mExpected: [m fo[32mq[m ba[32mz[m q[32me[mx [34mInline diff: [m- fo[31mo[m[32mq[m- ba[31mr[m[32mz[m q[31mu[m[31mu[m[32me[mx[31m [m+ f[31mo[mo[32mq[m+ ba[31mr[m[32mz[m q[31muu[m[32me[mx[31m [m [36m output-test/Spec.hs:194[m [31m✗ [m[31m57 [m[31mString.compares bytestrings[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal:- [34mActual: [m"fo[31mo[m\nba[31mr[m\tq[31mu[m[31mu[mx[31m [m"+ [34mActual: [m"f[31mo[mo\nba[31mr[m\tq[31muu[mx[31m [m" [34mExpected: [m"fo[32mq[m\nba[32mz[m\tq[32me[mx" [36m output-test/Spec.hs:197[m [31m✗ [m[31m58 [m[31mContext.shows a nice context[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal:- [34mActual: [m[31mT[m[31mr[m[31mu[me- [34mExpected: [m[32mF[m[32ma[m[32ml[m[32ms[me+ [34mActual: [m[31mTr[m[31mu[me+ [34mExpected: [m[32mFals[me Context [36m output-test/Spec.hs:198[m [31m✗ [m[31m59 [m[31mContext.shows a nice context multiple levels deep[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal:- [34mActual: [m[31mT[m[31mr[m[31mu[me- [34mExpected: [m[32mF[m[32ma[m[32ml[m[32ms[me+ [34mActual: [m[31mTr[m[31mu[me+ [34mExpected: [m[32mFals[me Context3 Context2 Context1 [36m output-test/Spec.hs:203[m [31m✗ [m[31m60 [m[31mContext.shows a context when an exception is thrown as well[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Prelude.undefined CallStack (from HasCallStack): error, called at libraries/base/GHC/Err.hs:75:14 in base:GHC.Err@@ -779,7 +779,7 @@ [36m output-test/Spec.hs:210[m [31m✗ [m[31m61 [m[31mProperty.generated values.shows many generated values too[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Failed after 1 tests Generated: [33m0[m Generated: [33m0[m@@ -792,7 +792,7 @@ [36m output-test/Spec.hs:229[m [31m✗ [m[31m62 [m[31mProperty.labels.shows the labels in use on failure[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Failed after 1 tests Generated: [33m[][m Labels: "length of input is 0", "magnitude (digits) of sum of input is 0"@@ -802,7 +802,7 @@ [36m output-test/Spec.hs:246[m [31m✗ [m[31m63 [m[31mProperty.classes.shows the classes in use on failure[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Failed after 1 tests Generated: [33m[][m Class: empty@@ -812,7 +812,7 @@ [36m output-test/Spec.hs:269[m [31m✗ [m[31m64 [m[31mShrinking.can grab the mvar during shrinking[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Failed after 21 tests Generated: [33m20[m Predicate failed, but should have succeeded, on this value:@@ -823,11 +823,11 @@ [36m output-test/Spec.hs:278[m [31m✗ [m[31m66 [m[31mRetries.Retries this five times[m- Retries: 5 (likely not flaky)+ Retries: 5 (does not look flaky) [36m output-test/Spec.hs:283[m [31m✗ [m[31m67 [m[31mFlakiness.Allows flakiness on False eventhough there is none (should fail)[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [36m output-test/Spec.hs:292[m [31m✗ [m[31m68 [m[31mFlakiness.Does not allow flakiness if flakiness is not allowed even if retries happen[m@@ -838,46 +838,46 @@ [36m output-test/Spec.hs:296[m [31m✗ [m[31m69 [m[31mFlakiness.Allows flakiness in this boolean five times (should fail with 5 retries)[m- Retries: 5 (likely not flaky)+ Retries: 5 (does not look flaky) [36m output-test/Spec.hs:316[m [31m✗ [m[31m70 [m[31mcallstack.it[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [36m output-test/Spec.hs:317[m [31m✗ [m[31m71 [m[31mcallstack.specify[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [36m Unknown location[m [31m✗ [m[31m72 [m[31mcallstack.prop[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Failed after 1 tests [36m output-test/Spec.hs:320[m [31m✗ [m[31m73 [m[31mcallstack.describe.describe-it[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [36m output-test/Spec.hs:321[m [31m✗ [m[31m74 [m[31mcallstack.describe.describe-specify[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [36m output-test/Spec.hs:328[m [31m✗ [m[31m75 [m[31mexpectations.considered failing[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [36m output-test/Spec.hs:329[m [31m✗ [m[31m76 [m[31mexpectations.considered failing[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [36m output-test/Spec.hs:336[m [31m✗ [m[31m77 [m[31mcombinators.should fail[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Failed after 2 tests Generated: [33m-1[m [36m output-test/Spec.hs:338[m [31m✗ [m[31m78 [m[31mcombinators.should not crash (undefined value)[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Failed after 1 tests Generated: [33m0[m Prelude.undefined@@ -887,7 +887,7 @@ [36m output-test/Spec.hs:339[m [31m✗ [m[31m79 [m[31mcombinators.should not crash (undefined generator)[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Failed after 1 tests Generated: [33mException thrown while showing test case: Prelude.undefined@@ -902,11 +902,11 @@ [36m output-test/Spec.hs:342[m [31m✗ [m[31m80 [m[31mcombinators.should be even[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) [36m output-test/Spec.hs:342[m [31m✗ [m[31m81 [m[31mcombinators.should be even[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Prelude.undefined CallStack (from HasCallStack): error, called at libraries/base/GHC/Err.hs:75:14 in base:GHC.Err@@ -914,7 +914,7 @@ [36m output-test/Spec.hs:348[m [31m✗ [m[31m82 [m[31mrandomness.always outputs the same pseudorandomness[m- Retries: 3 (likely not flaky)+ Retries: 3 (does not look flaky) Expected these values to be equal: [34mActual: [m[31m4[m[31m9[m [34mExpected: [m[32m2[m