sydtest 0.33.0.0 → 0.33.2.0
raw patch · 4 files changed
+133/−48 lines, 4 files
Files
- CHANGELOG.md +19/−0
- src/Test/Syd/MutationMode/Common.hs +8/−8
- src/Test/Syd/OptParse.hs +105/−39
- sydtest.cabal +1/−1
CHANGELOG.md view
@@ -1,5 +1,24 @@ # Changelog +## [0.33.2.0] - 2026-10-24++### Fixed++* The warning to compile with `-threaded -rtsopts -with-rtsopts=-N` also fires+ when the thread count was asked for, such as `--jobs 4` on one capability.++## [0.33.1.0] - 2026-10-24++### Added++* `NIX_BUILD_CORES` narrows the thread count to the cores nix gave the build,+ when nothing else says how parallel to run.++### Fixed++* Zero threads means every capability, rather than an asynchronous runner with+ no workers to take tests off the queue.+ ## [0.33.0.0] - 2026-10-01 ### Fixed
src/Test/Syd/MutationMode/Common.hs view
@@ -459,14 +459,14 @@ ] -- | Render the per-mutation progress line emitted as each mutation is--- tested. @index@\/@total@ are this mutation's 1-based position in the run--- and the total mutation count, shown as a coloured @[X\/Y]@ counter. The--- @verbose@ flag controls detail: in concise mode (the default) this is the--- single locating line @[X\/Y] Testing mutation \<operator\> at--- \<file\>:\<line\>:\<cols\>@, so a long run shows steady progress — including--- how far along it is — without flooding the log; in verbose\/debug mode the--- full source diff of the mutation follows — the same block the report prints--- for a survivor.+-- tested. @index@\/@total@ are how many mutations have started, this one+-- included, and how many the run tests in total, shown as a coloured+-- @[X\/Y]@ counter. The @verbose@ flag controls detail: in concise mode+-- (the default) this is the single locating line @[X\/Y] Testing mutation+-- \<operator\> at \<file\>:\<line\>:\<cols\>@, so a long run shows steady+-- progress — including how far along it is — without flooding the log; in+-- verbose\/debug mode the full source diff of the mutation follows — the+-- same block the report prints for a survivor. renderMutationProgressEvent :: Bool -> Int -> Int -> MutationProgressEvent -> [[Chunk]] renderMutationProgressEvent verbose index total (MutationProgressEvent rec) = let logLines = formatMutationLog (augmentedMutationRecordId rec) rec
src/Test/Syd/OptParse.hs view
@@ -12,7 +12,6 @@ import Autodocodec import Control.Applicative import Control.Concurrent (getNumCapabilities)-import Control.Monad import Control.Monad.IO.Class import Data.List.NonEmpty (NonEmpty (..)) import Data.Maybe@@ -170,35 +169,34 @@ Just True -> pure WithoutColours _ -> detectTerminalCapabilities - let threads =+ let configuredThreads = fromMaybe ( if flagDebug then Synchronous else d settingThreads ) flagThreads- case threads of- ByCapabilities -> do- i <- getNumCapabilities-- when (i == 1) $ do- let outputLine :: [Chunk] -> IO ()- outputLine lineChunks =- liftIO $- -- Emit UTF-8 bytes directly so output never depends on the- -- handle's locale encoding; the trailing newline is folded- -- into the chunks to keep this one byte-level write.- putChunksUtf8With terminalCapabilities (lineChunks <> [chunk "\n"])- mapM_- ( outputLine- . (: [])- . fore red- )- [ chunk "WARNING: Only one CPU core detected, make sure to compile your test suite with these ghc options:",- chunk " -threaded -rtsopts -with-rtsopts=-N",- chunk " (This is important for correctness as well as speed, as a parallel test suite can find thread safety problems.)"- ]- _ -> pure ()+ numCapabilities <- getNumCapabilities+ let threads = threadsWithNixBuildCores numCapabilities flagNixBuildCores configuredThreads+ case oneCapabilityWarning numCapabilities threads of+ WarnAboutOneCapability -> do+ let outputLine :: [Chunk] -> IO ()+ outputLine lineChunks =+ liftIO $+ -- Emit UTF-8 bytes directly so output never depends on the+ -- handle's locale encoding; the trailing newline is folded+ -- into the chunks to keep this one byte-level write.+ putChunksUtf8With terminalCapabilities (lineChunks <> [chunk "\n"])+ mapM_+ ( outputLine+ . (: [])+ . fore red+ )+ [ chunk "WARNING: This test suite can only use one capability, make sure to compile it with these ghc options:",+ chunk " -threaded -rtsopts -with-rtsopts=-N",+ chunk " (This is important for correctness as well as speed, as a parallel test suite can find thread safety problems.)"+ ]+ NoOneCapabilityWarning -> pure () errOrProgress <- case flagReportProgress of Nothing -> pure $@@ -377,6 +375,7 @@ { flagSeed :: !SeedSetting, flagRandomiseExecutionOrder :: !(Maybe Bool), flagThreads :: !(Maybe Threads),+ flagNixBuildCores :: !(Maybe Word), flagMaxSize :: !Int, flagMaxSuccess :: !Int, flagMaxDiscard :: !Int,@@ -420,6 +419,7 @@ name "randomize-execution-order" ] flagThreads <- optional settingsParser+ flagNixBuildCores <- nixBuildCoresParser flagMaxSize <- setting [ help "Maximum size parameter to pass to generators",@@ -702,13 +702,53 @@ Asynchronous !Word deriving (Show, Read, Eq, Generic) +-- | Whether to tell a test suite's author that it can only use one+-- capability.+data OneCapabilityWarning+ = WarnAboutOneCapability+ | NoOneCapabilityWarning+ deriving (Show, Eq)++-- | Whether a test suite that would run tests at the same time can do so.+--+-- A suite with one capability runs its tests one at a time, so it gets+-- neither the speed of running them at the same time nor the thread safety+-- problems that only tests running at the same time find. Compiling with+-- @-threaded@ and @-N@ is what raises the capability count, which is why+-- that is the advice, and it is advice about the suite rather than about the+-- machine it is on: a suite built without @-N@ has one capability on every+-- machine, so a single-processor machine is worth warning on too, even+-- though nothing would run at the same time there anyway.+--+-- Only a suite that would use more capabilities has anything to gain.+-- 'Synchronous' would not, and neither would an 'Asynchronous' bound of a+-- single test.+oneCapabilityWarning :: Int -> Threads -> OneCapabilityWarning+oneCapabilityWarning numCapabilities threads =+ if numCapabilities <= 1+ then case threads of+ Synchronous -> NoOneCapabilityWarning+ ByCapabilities -> WarnAboutOneCapability+ Asynchronous bound ->+ if bound > 1+ then WarnAboutOneCapability+ else NoOneCapabilityWarning+ else NoOneCapabilityWarning++-- | How parallel to run the test suite, from a number of threads.+--+-- 0 means every capability, both because zero threads would run no tests at+-- all, and because that is what nix means by 0 cores.+threadsFromWord :: Word -> Threads+threadsFromWord = \case+ 0 -> ByCapabilities+ 1 -> Synchronous+ w -> Asynchronous w+ instance HasCodec Threads where codec = dimapCodec f g codec where- f = \case- Nothing -> ByCapabilities- Just 1 -> Synchronous- Just n -> Asynchronous n+ f = maybe ByCapabilities threadsFromWord g = \case ByCapabilities -> Nothing Synchronous -> Just 1@@ -717,12 +757,9 @@ instance HasParser Threads where settingsParser = choice- [ ( \case- 1 -> Synchronous- w -> Asynchronous w- )+ [ threadsFromWord <$> setting- [ help "How many threads to use to execute tests in asynchronously",+ [ help "How many threads to use to execute tests in asynchronously, 0 means as many as you have capabilities", reader auto, option, long "jobs",@@ -745,14 +782,43 @@ ], setting [ help "How parallel to run the test suite",- confWith' "threads" $- let f = \case- Nothing -> Just ByCapabilities- Just 1 -> Just Synchronous- Just n -> Just $ Asynchronous n- in f <$> codec+ confWith' "threads" $ Just <$> codec ] ]++-- | Nix tells a build how many cores it may use with its @cores@ setting,+-- which reaches the build only as the @NIX_BUILD_CORES@ environment+-- variable.+nixBuildCoresParser :: Parser (Maybe Word)+nixBuildCoresParser =+ optional $+ setting+ [ help "How many threads to use to execute tests in asynchronously, if nothing else says",+ reader auto,+ unprefixedEnv "NIX_BUILD_CORES",+ metavar "INT"+ ]++-- | Narrow "as many threads as you have capabilities" down to the cores nix+-- gave the build.+--+-- Only that case is narrowed, so that anything configured explicitly still+-- decides.+--+-- Narrowed, never widened: more cores than the RTS has capabilities would+-- run more tests at once than it can overlap, which is contention without+-- any parallelism to show for it. A suite not compiled with @-N@ has a+-- single capability, and running it 64 ways because the builder has 64 cores+-- would be a change for the worse.+threadsWithNixBuildCores :: Int -> Maybe Word -> Threads -> Threads+threadsWithNixBuildCores numCapabilities mCores = \case+ ByCapabilities -> case mCores of+ Nothing -> ByCapabilities+ Just cores+ | cores >= fromIntegral numCapabilities -> ByCapabilities+ | otherwise -> threadsFromWord cores+ Synchronous -> Synchronous+ Asynchronous w -> Asynchronous w data Iterations = -- | Run the test suite once, the default
sydtest.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: sydtest-version: 0.33.0.0+version: 0.33.2.0 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