Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add property based testing #11

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ Available options:
- [x] With different formats
- [x] Add unit tests(HUnit)
- [x] Remove "error" on parse issues, replace with "Maybe"
- [ ] Add -Weverything flag
- [x] Adding quickcheck testing(how to model strftime/log file inputs)
- [x] Add -Werror flag
- [ ] `fileembed` with actual data + setup of regression tests
- [ ] Add Github CI (optional)
- [ ] Checkpoint 3: Performance
- [ ] Use Vector/Text
- [ ] Use heap for low memory consumption/Use streaming library
- [ ] Benchmarks(optional)
- [ ] Checkpoint 4: Stretch goals
- [ ] Adding quickcheck testing(how to model strftime/log file inputs)
- [ ] Pull in remote files
- [ ] Terminal coloring
- [ ] Filename prefixing in output
Expand Down
3 changes: 3 additions & 0 deletions loggy.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ common common-options
-Wincomplete-uni-patterns
-Wincomplete-record-updates
-Wredundant-constraints
-Werror
default-language: Haskell2010

library
Expand All @@ -42,6 +43,8 @@ test-suite loggy-test
other-modules: Test.LoggyCore
build-depends: loggy
, hspec ^>= 2.7.4
, hspec-hedgehog
, hedgehog
ghc-options: -threaded
-rtsopts
-with-rtsopts=-N
63 changes: 59 additions & 4 deletions test/Test/LoggyCore.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
module Test.LoggyCore (loggycore) where

import Test.Hspec (shouldBe, Spec, describe, it)
import Test.Hspec.Hedgehog (hedgehog, (===), forAll, Gen)
import Data.Time
import Data.Time.Clock.System
import qualified Hedgehog.Range as Range
import qualified Hedgehog.Gen as Gen
import Data.List (sort)
import Control.Monad (msum)
import Data.Maybe (catMaybes)

import LoggyCore

Expand Down Expand Up @@ -30,14 +37,21 @@ loggycore = describe "LoggyCoreTest" $ do
mergeLogLinesTest

extractTimestampTest :: Spec
extractTimestampTest = describe "LoggyCoreTest: extractTimestamp" $ do
extractTimestampTest = describe "LoggyCoreTest: extractTimestampTest" $ do
it "simpleTimestamp" $ extractTsHelper inputTime `shouldBe` inputDiffTime
it "simpleTimestampWithSpaces" $ extractTsHelper (inputTimeWith " ") `shouldBe` inputDiffTime
it "simpleTimestampWithExtraChars" $ extractTsHelper (inputTimeWith " random extra chars") `shouldBe` inputDiffTime
it "simpleTimestampWithRepeatedTimestamp" $ extractTsHelper (inputTimeWith $ " " ++ inputTime) `shouldBe` inputDiffTime
it "invalidLogLine" $ extractTsHelper "invalid input" `shouldBe` Nothing
where
extractTsHelper tsLog = utctDayTime <$> extractTimestamp dateFormat tsLog
it "extractTimestampPropertyTest" $ hedgehog $ do
logText <- forAll $ Gen.string (Range.linear 0 1000) Gen.ascii
unixTimeSeconds <- forAll $ Gen.int64 (Range.linear 0 1000)
let utcTime = systemToUTCTime $ MkSystemTime unixTimeSeconds 0
let utcTimeStr = formatTime defaultTimeLocale dateFormat utcTime
let logLine = utcTimeStr ++ " " ++ logText
Just utcTime === extractTimestamp dateFormat logLine
where
extractTsHelper tsLog = utctDayTime <$> extractTimestamp dateFormat tsLog

mergeLogLinesTest :: Spec
mergeLogLinesTest = describe "LoggyCoreTest: mergeLogLines" $ do
Expand All @@ -64,4 +78,45 @@ mergeLogLinesTest = describe "LoggyCoreTest: mergeLogLines" $ do
it "invalidLogLine" $ testMergeLogs dateFormat [[" invalid log line"]]
`shouldBe` MkMergeResult [] InvalidLogLine
it "invalidLogLines" $ testMergeLogs dateFormat [[" invalid log line", inputTimeWith " from file 1"]]
`shouldBe` MkMergeResult [inputTimeWith " from file 1"] InvalidLogLine
`shouldBe` MkMergeResult [inputTimeWith " from file 1"] InvalidLogLine
it "mergeLogLinesPropertyTest" $ hedgehog $ do
numFiles <- forAll $ Gen.int (Range.linear 1 10)
fileDateFormats <- forAll $ genDateFormats numFiles
logFiles <- forAll $ mapM genFile fileDateFormats
let MkMergeResult mergedLines mergeStatus = mergeLogLines (zip fileDateFormats logFiles)
length mergedLines === sum (map length logFiles)
mergeStatus === NoWarning
let maybeTsPerLogLine = map (dateFormats `extractTimestamp'`) mergedLines
let tsPerLogLine = catMaybes maybeTsPerLogLine
length tsPerLogLine === length mergedLines
isSorted tsPerLogLine === True
where
dateFormats :: [DateFormat]
dateFormats = ["%H:%M:%S", "%H-%M-%S", "%H/%M/%S", "%H.%M.%S"]
genDateFormats ::Int -> Gen [DateFormat]
genDateFormats numFiles = Gen.list (Range.singleton numFiles) (Gen.element dateFormats)
genFile :: DateFormat -> Gen LogFileLines
genFile dFormat = do
numLogLines <- Gen.int (Range.linear 0 100)
logTxtLines <- Gen.list (Range.singleton numLogLines) (Gen.string (Range.linear 0 100) Gen.ascii)
logTsLst <- genSortedDates numLogLines
let logTsFormattedLst = map (formatTime defaultTimeLocale dFormat) logTsLst
let logLines = zipWith (\ts logLine -> ts ++ " " ++ logLine) logTsFormattedLst logTxtLines
return logLines
genSortedDates :: Int -> Gen [UTCTime]
genSortedDates numLines = do
unixTsLst <- Gen.list (Range.singleton numLines) (Gen.int64 (Range.linear 0 1000))
let sortedUnixTsLst = sort unixTsLst
let utcTimeLst = map (\ts -> systemToUTCTime $ MkSystemTime ts 0) sortedUnixTsLst
return utcTimeLst
extractTimestamp' :: [DateFormat] -> LogLine -> Maybe UTCTime
-- Brute force try all formats till one matches
extractTimestamp' dFs logLine = tsForLogLine
where
tsForLogLine = msum tsPerDateFormat
tsPerDateFormat = map (`extractTimestamp` logLine) dFs
isSorted :: (Ord a) => [a] -> Bool
isSorted [] = True
isSorted [_] = True
isSorted (x:y:xs) = x <= y && isSorted (y:xs)