File: FastLoggerSpec.hs

package info (click to toggle)
haskell-fast-logger 3.2.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 168 kB
  • sloc: haskell: 898; makefile: 3
file content (114 lines) | stat: -rw-r--r-- 3,448 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}

module FastLoggerSpec (spec) where

#if __GLASGOW_HASKELL__ < 709
import Control.Applicative ((<$>))
#endif
import Control.Exception (finally)
import Control.Concurrent (getNumCapabilities)
import Control.Concurrent.Async (forConcurrently_)
import Control.Monad (when, forM_)
import qualified Data.ByteString.Char8 as BS
import Data.List (sort)
#if !MIN_VERSION_base(4,11,0)
import Data.Monoid ((<>))
#endif
import Data.String (IsString(fromString))
import System.Directory (doesFileExist, removeFile)
import Text.Printf (printf)

import Test.Hspec
import Test.Hspec.QuickCheck (prop)

import System.Log.FastLogger

spec :: Spec
spec = do
  describe "instance Show LogStr" $ do
    prop "it should be consistent with instance IsString" $ \str ->
      let logstr :: LogStr
          logstr = fromString str
      in show logstr == show str

  describe "instance Eq LogStr" $ do
    prop "it should be consistent with instance IsString" $ \str1 str2 ->
      let logstr1, logstr2 :: LogStr
          logstr1 = fromString str1
          logstr2 = fromString str2
      in (logstr1 == logstr2) == (str1 == str2)

  describe "pushLogMsg" $ do
    it "is safe for a large message" $ safeForLarge [
        100
      , 1000
      , 10000
      , 100000
      , 1000000
      ]
    it "logs all messages" logAllMsgs

  describe "fastlogger 1" $ do
    it "maintains the ordering of log messages" logOrdering

tempFile :: FilePath
tempFile = "test/temp.txt"

safeForLarge :: [Int] -> IO ()
safeForLarge = mapM_ safeForLarge'

safeForLarge' :: Int -> IO ()
safeForLarge' n = flip finally (cleanup tempFile) $ do
    cleanup tempFile
    lgrset <- newFileLoggerSet defaultBufSize tempFile
    let xs = toLogStr $ BS.pack $ take (abs n) (cycle ['a'..'z'])
        lf = "x"
    pushLogStr lgrset $ xs <> lf
    flushLogStr lgrset
    rmLoggerSet lgrset
    bs <- BS.readFile tempFile
    bs `shouldBe` BS.pack (take (abs n) (cycle ['a'..'z']) <> "x")

cleanup :: FilePath -> IO ()
cleanup file = do
    exist <- doesFileExist file
    when exist $ removeFile file

logAllMsgs :: IO ()
logAllMsgs = logAll "LICENSE" `finally` cleanup tempFile
  where
    logAll file = do
        cleanup tempFile
        lgrset <- newFileLoggerSet 512 tempFile
        src <- BS.readFile file
        let bs = (<> "\n") . toLogStr <$> BS.lines src
        mapM_ (pushLogStr lgrset) bs
        flushLogStr lgrset
        rmLoggerSet lgrset
        dst <- BS.readFile tempFile
        dst `shouldBe` src

logOrdering :: IO ()
logOrdering = flip finally (cleanup tempFile) $ do
    cleanup tempFile
    -- 128 is small enough for out-of-ordering
    (pushlog, teardown) <- newFastLogger1 $ LogFileNoRotate tempFile 128
    numCapabilities <- getNumCapabilities
    let concurrency = numCapabilities * 200 :: Int
        logEntriesCount = 100 :: Int
    forConcurrently_ [0 .. concurrency - 1] $ \t ->
      forM_ [0 .. logEntriesCount - 1] $ \i -> do
        let tag = mktag t
            cnt = printf "%02d" i :: String
            logmsg = toLogStr tag <> "log line nr: " <> toLogStr cnt <> "\n"
        pushlog logmsg
    teardown
    xs <- BS.lines <$> BS.readFile tempFile
    forM_ [0 .. concurrency - 1] $ \t -> do
        let tag = BS.pack $ mktag t
            msgs = filter (tag `BS.isPrefixOf`) xs
        sort msgs `shouldBe` msgs
  where
    mktag :: Int -> String
    mktag t = "thread id: " <> show t <> " "