packages feed

aihc-cabal-syntax-1.0.0.1: test/Compliance/Compare.hs

module Compliance.Compare
  ( Outcome (..), Comparison (..), compareBytes, comparePackage, outcomeName ) where

import qualified Data.ByteString as BS
import qualified Aihc.Cabal as A
import Compliance.Adapter (runResult, toCabal)
import qualified Distribution.PackageDescription as C
import qualified Distribution.PackageDescription.Parsec as C

data Outcome = Match | Mismatch | ParserError | ReferenceError | BothRejected | ConversionError
  deriving (Eq, Ord, Show, Enum, Bounded)

data Comparison = Comparison
  { outcome :: Outcome
  , parserAccepted :: Bool
  , referenceAccepted :: Bool
  , parserWarnings :: Int
  , referenceWarnings :: Int
  , detail :: String
  } deriving (Eq, Show)

outcomeName :: Outcome -> String
outcomeName value = case value of
  Match -> "match"
  Mismatch -> "mismatch"
  ParserError -> "parser_error"
  ReferenceError -> "reference_error"
  BothRejected -> "both_rejected"
  ConversionError -> "conversion_error"

compareBytes :: BS.ByteString -> Comparison
compareBytes bytes = case (A.parseValue ours, reference) of
  (Left errors, Left refErrors) -> report BothRejected False False (show errors ++ "\n" ++ show refErrors)
  (Left errors, Right _) -> report ParserError False True (show errors)
  (Right _, Left errors) -> report ReferenceError True False (show errors)
  (Right pkg, Right ref) -> let (status, message) = comparePackage pkg ref
                          in report status True True message
  where
    ours = A.parsePackage bytes
    (warnings, reference) = runResult (C.parseGenericPackageDescription bytes)
    report status accepted refAccepted message = Comparison status accepted refAccepted
      (length (A.parseWarnings ours)) (length warnings) message

comparePackage :: A.Package -> C.GenericPackageDescription -> (Outcome, String)
comparePackage pkg reference = case toCabal pkg of
  Left errors -> (ConversionError, errors)
  Right converted
    | converted == reference -> (Match, "")
    | otherwise -> (Mismatch, "Different fields: " ++ show (differences converted reference))

-- These labels explain failures. Equality always uses the complete value.
differences :: C.GenericPackageDescription -> C.GenericPackageDescription -> [String]
differences a b = [name | (name, different) <-
  [ ("packageDescription", C.packageDescription a /= C.packageDescription b)
  , ("gpdScannedVersion", C.gpdScannedVersion a /= C.gpdScannedVersion b)
  , ("genPackageFlags", C.genPackageFlags a /= C.genPackageFlags b)
  , ("condLibrary", C.condLibrary a /= C.condLibrary b)
  , ("condSubLibraries", C.condSubLibraries a /= C.condSubLibraries b)
  , ("condForeignLibs", C.condForeignLibs a /= C.condForeignLibs b)
  , ("condExecutables", C.condExecutables a /= C.condExecutables b)
  , ("condTestSuites", C.condTestSuites a /= C.condTestSuites b)
  , ("condBenchmarks", C.condBenchmarks a /= C.condBenchmarks b)
  ], different]