{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Test.Hspec.BenchGolden.CSV
(
csvHeader
, csvRow
, buildCSV
, writeSweepCSV
, getSweepCSVPath
) where
import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Lazy.Builder (Builder)
import qualified Data.Text.Lazy.Builder as B
import qualified Data.Text.Lazy.Builder.RealFloat as B
import qualified Data.Text.Lazy.IO as TL
import Data.Time.Format.ISO8601 (iso8601Show)
import System.Directory (createDirectoryIfMissing)
import System.FilePath ((</>), (<.>))
import Test.Hspec.BenchGolden.Arch (sanitizeForFilename)
import Test.Hspec.BenchGolden.Types (GoldenStats(..))
csvHeader :: Text -> Builder
Text
paramName = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ Builder
"timestamp,"
, Text -> Builder
B.fromText Text
paramName, Builder
","
, Builder
"mean_ms,stddev_ms,median_ms,min_ms,max_ms,"
, Builder
"trimmed_mean_ms,mad_ms,iqr_ms\n"
]
csvRow :: Text -> GoldenStats -> Builder
csvRow :: Text -> GoldenStats -> Builder
csvRow Text
paramValue GoldenStats{Double
[Double]
[(Int, Double)]
Text
UTCTime
statsMean :: Double
statsStddev :: Double
statsMedian :: Double
statsMin :: Double
statsMax :: Double
statsPercentiles :: [(Int, Double)]
statsArch :: Text
statsTimestamp :: UTCTime
statsTrimmedMean :: Double
statsMAD :: Double
statsIQR :: Double
statsOutliers :: [Double]
statsOutliers :: GoldenStats -> [Double]
statsIQR :: GoldenStats -> Double
statsMAD :: GoldenStats -> Double
statsTrimmedMean :: GoldenStats -> Double
statsTimestamp :: GoldenStats -> UTCTime
statsArch :: GoldenStats -> Text
statsPercentiles :: GoldenStats -> [(Int, Double)]
statsMax :: GoldenStats -> Double
statsMin :: GoldenStats -> Double
statsMedian :: GoldenStats -> Double
statsStddev :: GoldenStats -> Double
statsMean :: GoldenStats -> Double
..} = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ FilePath -> Builder
B.fromString (UTCTime -> FilePath
forall t. ISO8601 t => t -> FilePath
iso8601Show UTCTime
statsTimestamp), Builder
","
, Text -> Builder
B.fromText Text
paramValue, Builder
","
, Double -> Builder
forall a. RealFloat a => a -> Builder
B.realFloat Double
statsMean, Builder
","
, Double -> Builder
forall a. RealFloat a => a -> Builder
B.realFloat Double
statsStddev, Builder
","
, Double -> Builder
forall a. RealFloat a => a -> Builder
B.realFloat Double
statsMedian, Builder
","
, Double -> Builder
forall a. RealFloat a => a -> Builder
B.realFloat Double
statsMin, Builder
","
, Double -> Builder
forall a. RealFloat a => a -> Builder
B.realFloat Double
statsMax, Builder
","
, Double -> Builder
forall a. RealFloat a => a -> Builder
B.realFloat Double
statsTrimmedMean, Builder
","
, Double -> Builder
forall a. RealFloat a => a -> Builder
B.realFloat Double
statsMAD, Builder
","
, Double -> Builder
forall a. RealFloat a => a -> Builder
B.realFloat Double
statsIQR, Builder
"\n"
]
buildCSV :: Text -> [(Text, GoldenStats)] -> Builder
buildCSV :: Text -> [(Text, GoldenStats)] -> Builder
buildCSV Text
paramName [(Text, GoldenStats)]
rows =
Text -> Builder
csvHeader Text
paramName Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat (((Text, GoldenStats) -> Builder)
-> [(Text, GoldenStats)] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map ((Text -> GoldenStats -> Builder) -> (Text, GoldenStats) -> Builder
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Text -> GoldenStats -> Builder
csvRow) [(Text, GoldenStats)]
rows)
getSweepCSVPath :: FilePath -> Text -> String -> FilePath
getSweepCSVPath :: FilePath -> Text -> FilePath -> FilePath
getSweepCSVPath FilePath
outDir Text
archId FilePath
sweepName =
let sanitizedName :: FilePath
sanitizedName = Text -> FilePath
T.unpack (Text -> FilePath) -> Text -> FilePath
forall a b. (a -> b) -> a -> b
$ Text -> Text
sanitizeForFilename (FilePath -> Text
T.pack FilePath
sweepName)
sanitizedArch :: FilePath
sanitizedArch = Text -> FilePath
T.unpack (Text -> FilePath) -> Text -> FilePath
forall a b. (a -> b) -> a -> b
$ Text -> Text
sanitizeForFilename Text
archId
in FilePath
outDir FilePath -> FilePath -> FilePath
</> (FilePath
sanitizedName FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
"-" FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
sanitizedArch) FilePath -> FilePath -> FilePath
<.> FilePath
"csv"
writeSweepCSV :: FilePath -> Text -> String -> Text -> [(Text, GoldenStats)] -> IO ()
writeSweepCSV :: FilePath
-> Text -> FilePath -> Text -> [(Text, GoldenStats)] -> IO ()
writeSweepCSV FilePath
outDir Text
archId FilePath
sweepName Text
paramName [(Text, GoldenStats)]
rows = do
Bool -> FilePath -> IO ()
createDirectoryIfMissing Bool
True FilePath
outDir
let path :: FilePath
path = FilePath -> Text -> FilePath -> FilePath
getSweepCSVPath FilePath
outDir Text
archId FilePath
sweepName
content :: Text
content = Builder -> Text
B.toLazyText (Builder -> Text) -> Builder -> Text
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, GoldenStats)] -> Builder
buildCSV Text
paramName [(Text, GoldenStats)]
rows
FilePath -> Text -> IO ()
TL.writeFile FilePath
path Text
content