hpc-threshold 0.1.0.1 → 0.1.0.2
raw patch · 7 files changed
+131/−140 lines, 7 files
Files
- ChangeLog.md +4/−0
- app/Main.hs +1/−1
- hpc-threshold.cabal +5/−5
- src/HPCThreshold.hs +58/−0
- src/Lib.hs +0/−71
- test/HPCThresholdSpec.hs +63/−0
- test/LibSpec.hs +0/−63
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for hpc-threshold +## 0.1.0.2++Change the module name to HPCThreshold.+ ## 0.1.0.1 Added more documentation.
app/Main.hs view
@@ -1,6 +1,6 @@ module Main where -import Lib+import HPCThreshold import System.Exit main :: IO ()
hpc-threshold.cabal view
@@ -2,11 +2,11 @@ -- -- see: https://github.com/sol/hpack ----- hash: 430b6966ee845f290543a6c8947d35feda02426dfb190eec88e3f92020f042f0+-- hash: da7496f9be2296b4dff3d2e626512cf322514acefdbf3e55fa9cb287d4322641 name: hpc-threshold-version: 0.1.0.1-synopsis: Configurable code coverage threshold for Haskell+version: 0.1.0.2+synopsis: Ensure the code coverage is above configured thresholds description: Please see the README on Github at <https://github.com/eckyputrady/hpc-threshold#readme> category: Development homepage: https://github.com/eckyputrady/hpc-threshold#readme@@ -38,7 +38,7 @@ , interpolate , pcre-heavy exposed-modules:- Lib+ HPCThreshold other-modules: Paths_hpc_threshold default-language: Haskell2010@@ -69,6 +69,6 @@ , hpc-threshold , hspec other-modules:- LibSpec+ HPCThresholdSpec Paths_hpc_threshold default-language: Haskell2010
+ src/HPCThreshold.hs view
@@ -0,0 +1,58 @@+module HPCThreshold where++import Text.Regex.PCRE.Heavy+import Data.ByteString (ByteString)+import Data.String.Interpolate++data Threshold = Threshold+ { thresholdName :: String+ , thresholdRegex :: ByteString+ , thresholdValue :: Double+ } deriving (Read, Show, Eq)++type Coverage = Double+type ThresholdEvaluationResult = (Threshold, Coverage, Bool)++extractCoverage :: String -> ByteString -> Coverage+extractCoverage src regexStr =+ case compileM regexStr [] of+ Left e ->+ error $ "Unable to compile regex " ++ show regexStr ++ ": " ++ e + Right regex ->+ case scan regex src of+ (_, coverage:_):_ ->+ read coverage+ _ ->+ 0++evaluate :: String -> Threshold -> ThresholdEvaluationResult+evaluate src threshold =+ let + coverage =+ extractCoverage src (thresholdRegex threshold)+ isPass =+ coverage >= thresholdValue threshold+ in+ (threshold, coverage, isPass)++reportThreshold :: ThresholdEvaluationResult -> String+reportThreshold (Threshold tName _ tValue, coverage, isPass) =+ let+ remark = if isPass then "✓" else "·" :: String+ sign = if isPass then "≥" else "<" :: String+ in+ [i|#{remark} #{tName}: #{coverage}% (#{sign} #{tValue}%)|]++evaluateAndReport :: String -> [Threshold] -> (String, Bool)+evaluateAndReport src thresholds =+ let+ evalResults = map (evaluate src) thresholds+ isAllThresholdPass = all (\(_, _, isPass) -> isPass) evalResults+ reportSummary =+ if isAllThresholdPass+ then "Code coverage threshold check: PASS"+ else "Code coverage threshold check: FAIL"+ report = unlines $ reportSummary : map reportThreshold evalResults+ in+ (report, isAllThresholdPass)+
− src/Lib.hs
@@ -1,71 +0,0 @@-module Lib where--import Text.Regex.PCRE.Heavy-import Data.ByteString (ByteString)-import Data.String.Interpolate---- | Data structure for a single threshold configuration.-data Threshold = Threshold- { thresholdName :: String- , thresholdRegex :: ByteString- , thresholdValue :: Double- } deriving (Read, Show, Eq)--type Coverage = Double---- | The result of evaluation. The _3 indicates whether the coverage passes the threshold or not.-type ThresholdEvaluationResult = (Threshold, Coverage, Bool)---- | Extract the coverage from input string using a regex string-extractCoverage - :: String -- ^ The input string- -> ByteString -- ^ The regex to extract the coverage. The regex should contain `(\\d+)` capture otherwise the coverage is always be 0.- -> Coverage -- ^ The extracted coverage-extractCoverage src regexStr =- case compileM regexStr [] of- Left e ->- error $ "Unable to compile regex " ++ show regexStr ++ ": " ++ e - Right regex ->- case scan regex src of- (_, coverage:_):_ ->- read coverage- _ ->- 0---- | Evaluate the given string against the given threshold, producing an evaluation result-evaluate :: String -> Threshold -> ThresholdEvaluationResult-evaluate src threshold =- let - coverage =- extractCoverage src (thresholdRegex threshold)- isPass =- coverage >= thresholdValue threshold- in- (threshold, coverage, isPass)---- | Produce a human-friendly output of the evaluation result-reportThreshold :: ThresholdEvaluationResult -> String-reportThreshold (Threshold tName _ tValue, coverage, isPass) =- let- remark = if isPass then "✓" else "·" :: String- sign = if isPass then "≥" else "<" :: String- in- [i|#{remark} #{tName}: #{coverage}% (#{sign} #{tValue}%)|]---- | Evaluate a string against a list of thresholds configuration and produce a report-evaluateAndReport- :: String -- ^ The input string- -> [Threshold] -- ^ The list of thresholds- -> (String, Bool) -- ^ Pair of report and whether there is any coverage under thresholds-evaluateAndReport src thresholds =- let- evalResults = map (evaluate src) thresholds- isAllThresholdPass = all (\(_, _, isPass) -> isPass) evalResults- reportSummary =- if isAllThresholdPass- then "Code coverage threshold check: PASS"- else "Code coverage threshold check: FAIL"- report = unlines $ reportSummary : map reportThreshold evalResults- in- (report, isAllThresholdPass)-
+ test/HPCThresholdSpec.hs view
@@ -0,0 +1,63 @@+module HPCThresholdSpec where++import Test.Hspec+import qualified Control.Exception as E+import qualified Control.DeepSeq as D+import HPCThreshold++spec :: Spec+spec = do+ describe "Threshold" $+ it "should be parsable from string" $ do+ let threshold = Threshold "abc" "(\\d+)% abc" 80+ (read . show) threshold `shouldBe` threshold++ describe "extractCoverage" $ do+ it "should fail with invalid regexStr" $+ (E.evaluate . D.force) (extractCoverage "irrelevant" "ab(c")+ `shouldThrow` anyErrorCall+ it "should return 0 for regex with no digit capture" $+ extractCoverage "12% abc" "abc" `shouldBe` 0+ it "should return 0 if input string does not match regex" $+ extractCoverage "12% abc" "(\\d+) abc" `shouldBe` 0+ it "should capture the coverage" $+ extractCoverage "12% abc" "(\\d+)% abc" `shouldBe` 12++ describe "evaluate" $ do+ let threshold = Threshold "abc" "(\\d+)% abc" 80+ it "should correctly evaluate coverage < threshold as FAIL" $+ evaluate "10% abc" threshold `shouldBe` (threshold, 10, False)+ it "should correctly evaluate coverage == threshold as PASS" $+ evaluate "80% abc" threshold `shouldBe` (threshold, 80, True)+ it "should correctly evaluate coverage > threshold as PASS" $+ evaluate "100% abc" threshold `shouldBe` (threshold, 100, True)++ describe "reportThreshold" $ do+ it "should print passing result correctly" $ do+ let evalResult = (Threshold "abc" "(\\d+)% abc" 80, 80, True)+ reportThreshold evalResult `shouldBe` "✓ abc: 80.0% (≥ 80.0%)"+ it "should print failing result correctly" $ do+ let evalResult = (Threshold "abc" "(\\d+)% abc" 80, 70, False)+ reportThreshold evalResult `shouldBe` "· abc: 70.0% (< 80.0%)"++ describe "evaluateAndReport" $ do+ let thresholds =+ [ Threshold "abc" "(\\d+)% abc" 80+ , Threshold "def" "(\\d+)% def" 80+ ]+ it "should report passing result correctly" $+ evaluateAndReport "80% abc -- 80% def" thresholds `shouldBe`+ ( unlines [ "Code coverage threshold check: PASS"+ , "✓ abc: 80.0% (≥ 80.0%)"+ , "✓ def: 80.0% (≥ 80.0%)"+ ]+ , True+ )+ it "should report failing result correctly" $+ evaluateAndReport "70% abc -- 80% def" thresholds `shouldBe`+ ( unlines [ "Code coverage threshold check: FAIL"+ , "· abc: 70.0% (< 80.0%)"+ , "✓ def: 80.0% (≥ 80.0%)"+ ]+ , False+ )
− test/LibSpec.hs
@@ -1,63 +0,0 @@-module LibSpec where--import Test.Hspec-import qualified Control.Exception as E-import qualified Control.DeepSeq as D-import Lib--spec :: Spec-spec = do- describe "Threshold" $- it "should be parsable from string" $ do- let threshold = Threshold "abc" "(\\d+)% abc" 80- (read . show) threshold `shouldBe` threshold-- describe "extractCoverage" $ do- it "should fail with invalid regexStr" $- (E.evaluate . D.force) (extractCoverage "irrelevant" "ab(c")- `shouldThrow` anyErrorCall- it "should return 0 for regex with no digit capture" $- extractCoverage "12% abc" "abc" `shouldBe` 0- it "should return 0 if input string does not match regex" $- extractCoverage "12% abc" "(\\d+) abc" `shouldBe` 0- it "should capture the coverage" $- extractCoverage "12% abc" "(\\d+)% abc" `shouldBe` 12-- describe "evaluate" $ do- let threshold = Threshold "abc" "(\\d+)% abc" 80- it "should correctly evaluate coverage < threshold as FAIL" $- evaluate "10% abc" threshold `shouldBe` (threshold, 10, False)- it "should correctly evaluate coverage == threshold as PASS" $- evaluate "80% abc" threshold `shouldBe` (threshold, 80, True)- it "should correctly evaluate coverage > threshold as PASS" $- evaluate "100% abc" threshold `shouldBe` (threshold, 100, True)-- describe "reportThreshold" $ do- it "should print passing result correctly" $ do- let evalResult = (Threshold "abc" "(\\d+)% abc" 80, 80, True)- reportThreshold evalResult `shouldBe` "✓ abc: 80.0% (≥ 80.0%)"- it "should print failing result correctly" $ do- let evalResult = (Threshold "abc" "(\\d+)% abc" 80, 70, False)- reportThreshold evalResult `shouldBe` "· abc: 70.0% (< 80.0%)"-- describe "evaluateAndReport" $ do- let thresholds =- [ Threshold "abc" "(\\d+)% abc" 80- , Threshold "def" "(\\d+)% def" 80- ]- it "should report passing result correctly" $- evaluateAndReport "80% abc -- 80% def" thresholds `shouldBe`- ( unlines [ "Code coverage threshold check: PASS"- , "✓ abc: 80.0% (≥ 80.0%)"- , "✓ def: 80.0% (≥ 80.0%)"- ]- , True- )- it "should report failing result correctly" $- evaluateAndReport "70% abc -- 80% def" thresholds `shouldBe`- ( unlines [ "Code coverage threshold check: FAIL"- , "· abc: 70.0% (< 80.0%)"- , "✓ def: 80.0% (≥ 80.0%)"- ]- , False- )