os-release 0.2.2 → 1.0.0
raw patch · 35 files changed
+1067/−314 lines, 35 filesdep +aesondep +bytestringdep +filepathdep −containersdep −hlintdep −parsecdep ~basedep ~hspecnew-uploader
Dependencies added: aeson, bytestring, filepath, hspec-megaparsec, megaparsec, pretty-simple, safe-exceptions, tasty, tasty-golden, tasty-hspec, text, unordered-containers
Dependencies removed: containers, hlint, parsec, process, regex-compat, temporary, transformers
Dependency ranges changed: base, hspec
Files
- ChangeLog.rst +5/−0
- library/System/OsRelease.hs +168/−90
- library/System/OsRelease/Megaparsec.hs +102/−0
- os-release.cabal +120/−83
- tests/Golden/Real.hs +35/−0
- tests/Golden/data/alpine.golden +24/−0
- tests/Golden/data/alpine.in +7/−0
- tests/Golden/data/arch.golden +29/−0
- tests/Golden/data/arch.in +11/−0
- tests/Golden/data/centos.golden +45/−0
- tests/Golden/data/centos.in +17/−0
- tests/Golden/data/debian.golden +24/−0
- tests/Golden/data/debian.in +8/−0
- tests/Golden/data/empty.golden +24/−0
- tests/Golden/data/empty.in +1/−0
- tests/Golden/data/exherbo.golden +24/−0
- tests/Golden/data/exherbo.in +9/−0
- tests/Golden/data/fedora.golden +49/−0
- tests/Golden/data/fedora.in +21/−0
- tests/Golden/data/gentoo.golden +24/−0
- tests/Golden/data/gentoo.in +8/−0
- tests/Golden/data/mint.golden +29/−0
- tests/Golden/data/mint.in +12/−0
- tests/Golden/data/red-hat.golden +41/−0
- tests/Golden/data/red-hat.in +17/−0
- tests/Golden/data/red-hat_parseErrors.golden +49/−0
- tests/Golden/data/red-hat_parseErrors.in +18/−0
- tests/Golden/data/ubuntu.golden +29/−0
- tests/Golden/data/ubuntu.in +13/−0
- tests/HLint.hs +0/−17
- tests/Haddock.hs +0/−30
- tests/Main.hs +16/−0
- tests/Specs/Megaparsec.hs +88/−0
- tests/unit/OsReleaseSpec.hs +0/−93
- tests/unit/Spec.hs +0/−1
ChangeLog.rst view
@@ -1,3 +1,8 @@+1.0.0+=====++- Redo of the entire API+ 0.2.2 =====
library/System/OsRelease.hs view
@@ -1,114 +1,192 @@-{-# LANGUAGE FlexibleContexts, TypeSynonymInstances, FlexibleInstances, GeneralizedNewtypeDeriving, CPP #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeOperators #-} +-- | A module to retrieve os-release information according to the+-- freedesktop standard:+-- https://www.freedesktop.org/software/systemd/man/os-release.html+--+-- Usage example:+--+-- @+-- do+-- Just (OsRelease {..}) <- fmap osRelease <$\> parseOsRelease+-- putStrLn name+-- @ module System.OsRelease- ( parseOs- , readOs- , readOs'- , OsReleaseValue (..)- , OsReleaseKey (..)- , OsReleaseLine- , OsRelease- , OsReleaseError- )+ (+ -- * data types+ OsReleaseResult(..)+ , OsRelease(..)++ -- * read/parse os-release+ , parseOsRelease+ , readOsRelease++ -- * defaults+ , defaultOsRelease+ , defaultAssignments++ -- * low-level+ , parseAssignments+ , parseAssignment+ , getAllAssignments+ , getOsRelease+ , parseOsRelease'+ ) where -import Text.ParserCombinators.Parsec-import Text.Parsec.Prim hiding (try)-import Data.Map.Lazy hiding (foldl)-import Data.Functor.Identity-import Data.String-import Data.Monoid-import Data.Either+import System.OsRelease.Megaparsec -#if MIN_VERSION_base(4,8,0)--- note: using hscpp it should be possible to use `#if not COND`-#else-import Control.Applicative ((<$>))-#endif+import Control.Applicative+import Control.Monad+import Control.Exception.Safe+import Data.Aeson+import Data.Aeson.TH+import Data.Char+import Data.Either+import Data.List+import Data.Maybe+import Data.Void+import GHC.Generics+import Prelude hiding ( id+ ) -import Control.Monad-import qualified Control.Exception as E+import qualified Data.HashMap.Strict as HM+import qualified Data.Text as T+import qualified Text.Megaparsec as MP -type OsReleaseLine = (OsReleaseKey, OsReleaseValue)-newtype OsReleaseKey = OsReleaseKey String- deriving (Ord, Eq, IsString, Show) -newtype OsReleaseValue = OsReleaseValue String- deriving (IsString, Eq, Show)+data OsReleaseResult = OsReleaseResult {+ osRelease :: !OsRelease+ , unknown_fields :: [(String, String)]+ , parse_errors :: [MP.ParseError String Void]+} deriving (Show) -type OsRelease = Map OsReleaseKey OsReleaseValue -class Parsable a where- parser :: ParsecT String () Identity a+-- | All the explicitly documented fields of @os-release@.+data OsRelease = OsRelease {+ name :: !(String)+ , version :: !(Maybe String)+ , id :: !(String)+ , id_like :: !(Maybe String)+ , version_codename :: !(Maybe String)+ , version_id :: !(Maybe String)+ , pretty_name :: !(String)+ , ansi_color :: !(Maybe String)+ , cpe_name :: !(Maybe String)+ , home_url :: !(Maybe String)+ , documentation_url :: !(Maybe String)+ , support_url :: !(Maybe String)+ , bug_report_url :: !(Maybe String)+ , privacy_policy_url :: !(Maybe String)+ , build_id :: !(Maybe String)+ , variant :: !(Maybe String)+ , variant_id :: !(Maybe String)+ , logo :: !(Maybe String)+} deriving (Generic, Show) -instance Parsable OsReleaseKey where- parser = OsReleaseKey <$> many1 (alphaNum <|> char '_') -instance Parsable OsReleaseValue where- parser = OsReleaseValue <$> (qVal <|> nqVal)- where- qVal :: Parser String- qVal = do- quote <- oneOf "'\""- value <- manyTill (qValInside quote) (char quote)- noJunk- return (foldl mappend "" value)+class GetRecords a where+ getRecords :: a -> [String] - noJunk = try . lookAhead $ eof <|> (void newline)+instance {-# OVERLAPPABLE #-} GetRecords (f p) => GetRecords (M1 i c f p) where+ getRecords (M1 x) = getRecords x - qValInside :: Char -> ParsecT String () Identity String- qValInside quote = (qSpecial quote) <|> (:[]) <$> (noneOf $ specials quote)+instance {-# OVERLAPPING #-} Selector c => GetRecords (M1 S c f p) where+ getRecords x = [selName x] - qSpecial :: Char -> ParsecT String () Identity String- qSpecial quote = (\x -> [x!!1]) <$>- foldl1 (<|>) [try (string $ "\\" <> [x]) | x <- specials quote]+instance (GetRecords (a p), GetRecords (b p)) => GetRecords ((a :*: b) p) where+ getRecords (a :*: b) = getRecords a ++ getRecords b - specials :: Char -> [Char]- specials quote =- [ quote- , '\\'- , '$'- , '`'- ] - nqVal :: Parser String- nqVal = do- x <- many alphaNum- noJunk- return x -instance Parsable OsReleaseLine where- parser = do- var <- parser :: ParsecT String () Identity OsReleaseKey- _ <- char '='- val <- parser :: ParsecT String () Identity OsReleaseValue- return (var, val)+-- | The defaults as per the spec:+--+-- @+-- NAME=Linux+-- ID=linux+-- PRETTY_NAME=Linux+-- @+defaultOsRelease :: OsRelease+defaultOsRelease = OsRelease { name = "Linux"+ , version = Nothing+ , id = "linux"+ , id_like = Nothing+ , version_codename = Nothing+ , version_id = Nothing+ , pretty_name = "Linux"+ , ansi_color = Nothing+ , cpe_name = Nothing+ , home_url = Nothing+ , documentation_url = Nothing+ , support_url = Nothing+ , bug_report_url = Nothing+ , privacy_policy_url = Nothing+ , build_id = Nothing+ , variant = Nothing+ , variant_id = Nothing+ , logo = Nothing+ } -instance Parsable OsRelease where- parser = fromList <$> (sepEndBy (parser :: ParsecT String () Identity OsReleaseLine) newline)+-- | Like `defaultOsRelease`, except as key-value pair.+defaultAssignments :: [(String, String)]+defaultAssignments =+ [("NAME", "Linux"), ("ID", "linux"), ("PRETTY_NAME", "Linux")] -readOs :: IO (Either OsReleaseError OsRelease)-readOs = do- xs <- readOs' ["/etc/os-release", "/usr/lib/os-release"]- case xs of- Left e -> return $ Left e- Right x -> return . h $ parseOs x- where- h (Left e) = Left $ OsReleaseParseError e- h (Right x) = Right x+-- | Get all allAssignments as @(key, val)@ from the @os-release@+-- file contents.+getAllAssignments :: String -- ^ file contents of os-release+ -> [Either (MP.ParseError String Void) (String, String)]+getAllAssignments = fromRight [] . MP.parse parseAssignments "os-release" -data OsReleaseError = OsReleaseError String- | OsReleaseParseError ParseError- deriving (Show) -readOs' :: [FilePath] -> IO (Either OsReleaseError String)-readOs' fs = do- xs <- mapM (E.try . readFile) fs :: IO [Either E.IOException String]- return . h $ rights xs- where- h [] = Left . OsReleaseError $ "Neither of " <> unwords fs <> " could be read"- h (x:_) = Right x+-- | Parse the assignments into `OsRelease`. This is merged with the+-- defaults as per the spec. In case of no assignments, also returns+-- the defaults.+getOsRelease :: [(String, String)] -- ^ assignments+ -> OsRelease+getOsRelease =+ (\case+ Error _ -> defaultOsRelease+ Success v -> v+ )+ . fromJSON+ . Object+ . (\x -> HM.union x (HM.fromList . aesonify $ defaultAssignments))+ . HM.fromList+ . aesonify+ where+ aesonify = fmap (\(k, v) -> (T.toLower . T.pack $ k, String . T.pack $ v)) -parseOs :: String -> Either ParseError OsRelease-parseOs xs = parse (parser :: ParsecT String () Identity OsRelease) "os-release" xs++-- | Tries to read @\"\/etc\/os-release\"@ and @\"\/usr\/lib\/os_release\"@ in order.+--+-- Throws @IOError@ if both files could not be read.+readOsRelease :: IO String+readOsRelease = readFile "/etc/os-release" <|> readFile "/usr/lib/os-release"+++-- | Tries to read @\"\/etc\/os-release\"@ and @\"\/usr\/lib\/os_release\"@ in order+-- and parses into `OsReleaseResult`. Returns @Nothing@ if both files could+-- not be read.+parseOsRelease :: IO (Maybe OsReleaseResult)+parseOsRelease =+ handleIO (\_ -> pure Nothing) . fmap (Just . parseOsRelease') $ readOsRelease+++-- | Like `parseOsRelease`, except taking the input String explicitly.+-- Primarily for tests.+parseOsRelease' :: String -> OsReleaseResult+parseOsRelease' s =+ let (errs, ass) = partitionEithers . getAllAssignments $ s+ osr = getOsRelease ass+ unknown_fields' =+ HM.toList+ . foldr (\x y -> HM.delete (fmap toUpper x) y) (HM.fromList ass)+ $ (init . getRecords . from $ defaultOsRelease)+ in OsReleaseResult osr unknown_fields' errs+++deriveJSON defaultOptions ''OsRelease
+ library/System/OsRelease/Megaparsec.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE CPP #-}++module System.OsRelease.Megaparsec where++import Control.Applicative+import Control.Monad+#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail ( MonadFail )+#endif+import Data.Char+import Data.Functor+import Data.Void++import qualified Text.Megaparsec as MP+import qualified Text.Megaparsec.Char as MP+++-- | Parse the entire file, handling newlines and comments gracefully.+--+-- This parser generally shouldn't fail, but instead report a failed+-- parsed line as @Left@ value.+parseAssignments :: MP.Parsec+ Void+ String+ [Either (MP.ParseError String Void) (String, String)]+parseAssignments =+ (\xs x -> join xs ++ x) <$> many (line MP.eol) <*> line MP.eof+ where+ line eol = choice'+ [ comment $> []+ , blank $> []+ , fmap+ (: [])+ ( MP.withRecovery (\e -> parseUntil eol $> Left e)+ . fmap Right+ $ (parseAssignment <* eol)+ )+ ]+ where+ comment = pWs *> MP.char '#' *> parseUntil eol *> eol+ blank = pWs *> eol+++-- | Parse a single line assignment and extract the right hand side.+-- This is only a subset of a shell parser, refer to the spec for+-- details.+parseAssignment :: MP.Parsec Void String (String, String)+parseAssignment =+ (,) <$> (pWs *> key) <*> (MP.char '=' *> (MP.try qval <|> mempty) <* pWs)+ where+ dropSpace :: String -> String+ dropSpace = reverse . dropWhile (\x -> x == ' ' || x == '\t') . reverse++ key :: MP.Parsec Void String String+ key = some (MP.try MP.alphaNumChar <|> MP.char '_')++ qval :: MP.Parsec Void String String+ qval = do+ c <- MP.lookAhead MP.printChar+ case c of+ ' ' -> pure ""+ '"' -> MP.char c *> val c <* MP.char c+ '\'' -> MP.char c *> val c <* MP.char c+ -- no quote, have to drop trailing spaces+ _ -> fmap+ dropSpace+ (some $ MP.satisfy (\x -> isAlphaNum x || (x `elem` ['_', '-', '.']))) -- this is more lax than the spec+ val :: Char -> MP.Parsec Void String String+ val !q = many (qspecial q <|> MP.noneOf (specials q)) -- noneOf may be too lax++ qspecial :: Char -> MP.Parsec Void String Char+ qspecial !q =+ fmap (!! 1)+ . (\xs -> choice' xs)+ . fmap (\s -> MP.try . MP.chunk $ ['\\', s])+ $ (specials q)++ specials :: Char -> [Char]+ specials !q = [q, '\\', '$', '`']+++parseUntil :: MP.Parsec Void String a -> MP.Parsec Void String String+parseUntil !p = do+ (MP.try (MP.lookAhead p) $> [])+ <|> (do+ c <- MP.anySingle+ c2 <- parseUntil p+ pure ([c] `mappend` c2)+ )+++-- | Parse one or more white spaces or tabs.+pWs :: MP.Parsec Void String ()+pWs = many (MP.satisfy (\x -> x == ' ' || x == '\t')) $> ()+++-- | Try all parses in order, failing if all failed. Also fails+-- on empty list.+choice' :: (MonadFail f, MP.MonadParsec e s f) => [f a] -> f a+choice' = \case+ [] -> fail "Empty list"+ xs -> foldr1 (\x y -> MP.try x <|> MP.try y) xs
os-release.cabal view
@@ -1,94 +1,131 @@-author: Jan Matějka-category: System-license-file: LICENSE-build-type: Simple-cabal-version: >= 1.16-copyright: 2014 Jan Matějka <yac@blesmrt.net>-license: BSD3-maintainer: <yac@blesmrt.net>-homepage: https://github.com/yaccz/os-release-name: os-release-synopsis: /etc/os-release helpers-version: 0.2.2-description: /etc/os-release helpers-extra-doc-files: ChangeLog.rst README.rst+cabal-version: 3.0+author: Jan Matějka+category: System+license-file: LICENSE+build-type: Simple+copyright: 2014 Jan Matějka <yac@blesmrt.net>+license: BSD-3-Clause+maintainer: Julian Ospald <hasufell@posteo.de>+homepage: https://github.com/yaccz/os-release+name: os-release+synopsis: /etc/os-release helpers+version: 1.0.0+description:+ \/etc\/os-release helpers as per the freedesktop spec: https://www.freedesktop.org/software/systemd/man/os-release.html -Flag devel- Description: Enables -Werror- Default: False- Manual: True+extra-doc-files:+ ChangeLog.rst+ README.rst +extra-source-files: tests/Golden/data/*.golden tests/Golden/data/*.in++flag devel+ description: Enables -Werror+ default: False+ manual: True+ source-repository head type: git location: https://github.com/yaccz/os-release.git -library- build-depends:- base < 5- , parsec- , containers- , parsec- , containers- , transformers- default-language: Haskell2010- exposed-modules: System.OsRelease+common base+ build-depends: base >=4.11 && <5++common bytestring+ build-depends: bytestring++common aeson+ build-depends: aeson >=1.4++common filepath+ build-depends: filepath >=1.4.2.1++common hspec+ build-depends: hspec >=2.7.1++common hspec-megaparsec+ build-depends: hspec-megaparsec >=2.1.0++common megaparsec+ build-depends: megaparsec >=8.0.0++common pretty-simple+ build-depends: pretty-simple >=1.0.0.0++common safe-exceptions+ build-depends: safe-exceptions >=0.1.7.0++common tasty+ build-depends: tasty >=1.3++common tasty-golden+ build-depends: tasty-golden >=2.3.4++common tasty-hspec+ build-depends: tasty-hspec >=1.1.5.1++common text+ build-depends: text >=1.2++common unordered-containers+ build-depends: unordered-containers >=0.2.10.0++common config+ default-language: Haskell2010++ if flag(devel) ghc-options:- -Wall- ghc-prof-options:- -auto-all- -prof- hs-source-dirs: library+ -Werror -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns+ -fwarn-incomplete-record-updates -test-suite tests- build-depends:- base < 5- , os-release- , hspec- , parsec- , containers- , transformers- , temporary- default-language:- Haskell2010+ else+ ghc-options:+ -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns+ -fwarn-incomplete-record-updates - if flag(devel)- ghc-options: -Wall -Werror- else- ghc-options: -Wall- hs-source-dirs:- library- tests/unit- main-is:- Spec.hs- other-modules:- System.OsRelease- OsReleaseSpec- type:- exitcode-stdio-1.0+ default-extensions:+ BangPatterns+ LambdaCase+ OverloadedStrings+ QuasiQuotes+ TemplateHaskell+ TupleSections+ TypeFamilies -test-suite documentation- build-depends:- base < 5- , process == 1.*- , regex-compat == 0.*- default-language:- Haskell2010- hs-source-dirs:- tests- main-is:- Haddock.hs- type:- exitcode-stdio-1.0+library+ import:+ config+ , base+ , aeson+ , megaparsec+ , safe-exceptions+ , text+ , unordered-containers -test-suite style- build-depends:- base < 5- , hlint == 1.*- default-language:- Haskell2010- hs-source-dirs:- tests- main-is:- HLint.hs- type:- exitcode-stdio-1.0+ exposed-modules: System.OsRelease+ other-modules: System.OsRelease.Megaparsec+ hs-source-dirs: library++test-suite tests+ import:+ config+ , base+ , bytestring+ , filepath+ , hspec+ , hspec-megaparsec+ , megaparsec+ , pretty-simple+ , tasty+ , tasty-golden+ , tasty-hspec+ , text++ build-depends: os-release+ hs-source-dirs: tests+ main-is: Main.hs+ other-modules:+ Golden.Real+ Specs.Megaparsec++ type: exitcode-stdio-1.0
+ tests/Golden/Real.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE CPP #-}++module Golden.Real where++import System.OsRelease++import System.FilePath+import Text.Pretty.Simple+import Test.Tasty+import Test.Tasty.Golden++import qualified Data.ByteString.Lazy as B+import qualified Data.Text.Lazy as L+import qualified Data.Text.Encoding as E+++goldenTests :: IO TestTree+goldenTests = do+ files <- findByExtension [".in"] (takeDirectory (__FILE__) </> "data")+ return $ testGroup+ "Parse os-release into OsRelease"+ (flip fmap files $ \file ->+ let out = replaceExtension file ".golden"+ in goldenVsString (takeBaseName file) out (parse file)+ )+ where+ parse f = do+ c <- readFile f+ pure+ . B.fromStrict+ . E.encodeUtf8+ . L.toStrict+ . pShowNoColor+ . parseOsRelease'+ $ c
+ tests/Golden/data/alpine.golden view
@@ -0,0 +1,24 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Alpine Linux"+ , version = Nothing+ , id = "alpine"+ , id_like = Nothing+ , version_codename = Nothing+ , version_id = Just "3.11.3"+ , pretty_name = "Alpine Linux v3.11"+ , ansi_color = Nothing+ , cpe_name = Nothing+ , home_url = Just "https://alpinelinux.org/"+ , documentation_url = Nothing+ , support_url = Nothing+ , bug_report_url = Just "https://bugs.alpinelinux.org/"+ , privacy_policy_url = Nothing+ , build_id = Nothing+ , variant = Nothing+ , variant_id = Nothing+ , logo = Nothing+ }+ , unknown_fields = []+ , parse_errors = []+ }
+ tests/Golden/data/alpine.in view
@@ -0,0 +1,7 @@+NAME="Alpine Linux"+ID=alpine+VERSION_ID=3.11.3+PRETTY_NAME="Alpine Linux v3.11"+HOME_URL="https://alpinelinux.org/"+BUG_REPORT_URL="https://bugs.alpinelinux.org/"+
+ tests/Golden/data/arch.golden view
@@ -0,0 +1,29 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Arch Linux"+ , version = Nothing+ , id = "arch"+ , id_like = Nothing+ , version_codename = Nothing+ , version_id = Nothing+ , pretty_name = "Arch Linux"+ , ansi_color = Just "38;2;23;147;209"+ , cpe_name = Nothing+ , home_url = Just "https://www.archlinux.org/"+ , documentation_url = Just "https://wiki.archlinux.org/"+ , support_url = Just "https://bbs.archlinux.org/"+ , bug_report_url = Just "https://bugs.archlinux.org/"+ , privacy_policy_url = Nothing+ , build_id = Just "rolling"+ , variant = Nothing+ , variant_id = Nothing+ , logo = Just "archlinux"+ }+ , unknown_fields =+ [+ ( "LOGO"+ , "archlinux"+ )+ ]+ , parse_errors = []+ }
+ tests/Golden/data/arch.in view
@@ -0,0 +1,11 @@+NAME="Arch Linux"+PRETTY_NAME="Arch Linux"+ID=arch+BUILD_ID=rolling+ANSI_COLOR="38;2;23;147;209"+HOME_URL="https://www.archlinux.org/"+DOCUMENTATION_URL="https://wiki.archlinux.org/"+SUPPORT_URL="https://bbs.archlinux.org/"+BUG_REPORT_URL="https://bugs.archlinux.org/"+LOGO=archlinux+
+ tests/Golden/data/centos.golden view
@@ -0,0 +1,45 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "CentOS Linux"+ , version = Just "8 (Core)"+ , id = "centos"+ , id_like = Just "rhel fedora"+ , version_codename = Nothing+ , version_id = Just "8"+ , pretty_name = "CentOS Linux 8 (Core)"+ , ansi_color = Just "0;31"+ , cpe_name = Just "cpe:/o:centos:centos:8"+ , home_url = Just "https://www.centos.org/"+ , documentation_url = Nothing+ , support_url = Nothing+ , bug_report_url = Just "https://bugs.centos.org/"+ , privacy_policy_url = Nothing+ , build_id = Nothing+ , variant = Nothing+ , variant_id = Nothing+ , logo = Nothing+ }+ , unknown_fields =+ [+ ( "REDHAT_SUPPORT_PRODUCT_VERSION"+ , "8"+ )+ ,+ ( "CENTOS_MANTISBT_PROJECT"+ , "CentOS-8"+ )+ ,+ ( "REDHAT_SUPPORT_PRODUCT"+ , "centos"+ )+ ,+ ( "PLATFORM_ID"+ , "platform:el8"+ )+ ,+ ( "CENTOS_MANTISBT_PROJECT_VERSION"+ , "8"+ )+ ]+ , parse_errors = []+ }
+ tests/Golden/data/centos.in view
@@ -0,0 +1,17 @@+NAME="CentOS Linux"+VERSION="8 (Core)"+ID="centos"+ID_LIKE="rhel fedora"+VERSION_ID="8"+PLATFORM_ID="platform:el8"+PRETTY_NAME="CentOS Linux 8 (Core)"+ANSI_COLOR="0;31"+CPE_NAME="cpe:/o:centos:centos:8"+HOME_URL="https://www.centos.org/"+BUG_REPORT_URL="https://bugs.centos.org/"++CENTOS_MANTISBT_PROJECT="CentOS-8"+CENTOS_MANTISBT_PROJECT_VERSION="8"+REDHAT_SUPPORT_PRODUCT="centos"+REDHAT_SUPPORT_PRODUCT_VERSION="8"+
+ tests/Golden/data/debian.golden view
@@ -0,0 +1,24 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Debian GNU/Linux"+ , version = Just "8 (jessie)"+ , id = "debian"+ , id_like = Nothing+ , version_codename = Nothing+ , version_id = Just "8"+ , pretty_name = "Debian GNU/Linux 8 (jessie)"+ , ansi_color = Nothing+ , cpe_name = Nothing+ , home_url = Just "http://www.debian.org/"+ , documentation_url = Nothing+ , support_url = Just "http://www.debian.org/support/"+ , bug_report_url = Just "https://bugs.debian.org/"+ , privacy_policy_url = Nothing+ , build_id = Nothing+ , variant = Nothing+ , variant_id = Nothing+ , logo = Nothing+ }+ , unknown_fields = []+ , parse_errors = []+ }
+ tests/Golden/data/debian.in view
@@ -0,0 +1,8 @@+PRETTY_NAME="Debian GNU/Linux 8 (jessie)"+NAME="Debian GNU/Linux"+VERSION_ID="8"+VERSION="8 (jessie)"+ID=debian+HOME_URL="http://www.debian.org/"+SUPPORT_URL="http://www.debian.org/support/"+BUG_REPORT_URL="https://bugs.debian.org/"
+ tests/Golden/data/empty.golden view
@@ -0,0 +1,24 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Linux"+ , version = Nothing+ , id = "linux"+ , id_like = Nothing+ , version_codename = Nothing+ , version_id = Nothing+ , pretty_name = "Linux"+ , ansi_color = Nothing+ , cpe_name = Nothing+ , home_url = Nothing+ , documentation_url = Nothing+ , support_url = Nothing+ , bug_report_url = Nothing+ , privacy_policy_url = Nothing+ , build_id = Nothing+ , variant = Nothing+ , variant_id = Nothing+ , logo = Nothing+ }+ , unknown_fields = []+ , parse_errors = []+ }
+ tests/Golden/data/empty.in view
@@ -0,0 +1,1 @@+
+ tests/Golden/data/exherbo.golden view
@@ -0,0 +1,24 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Exherbo"+ , version = Nothing+ , id = "exherbo"+ , id_like = Nothing+ , version_codename = Nothing+ , version_id = Nothing+ , pretty_name = "Exherbo Linux"+ , ansi_color = Just "0;32"+ , cpe_name = Nothing+ , home_url = Just "https://www.exherbo.org/"+ , documentation_url = Nothing+ , support_url = Just "irc://irc.freenode.net/#exherbo"+ , bug_report_url = Just "https://bugs.exherbo.org/"+ , privacy_policy_url = Nothing+ , build_id = Nothing+ , variant = Nothing+ , variant_id = Nothing+ , logo = Nothing+ }+ , unknown_fields = []+ , parse_errors = []+ }
+ tests/Golden/data/exherbo.in view
@@ -0,0 +1,9 @@+NAME="Exherbo"+PRETTY_NAME="Exherbo Linux"+ID="exherbo"+# comment+ANSI_COLOR="0;32"+HOME_URL="https://www.exherbo.org/"+SUPPORT_URL="irc://irc.freenode.net/#exherbo"+BUG_REPORT_URL="https://bugs.exherbo.org/"+
+ tests/Golden/data/fedora.golden view
@@ -0,0 +1,49 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Fedora"+ , version = Just "31 (Container Image)"+ , id = "fedora"+ , id_like = Nothing+ , version_codename = Just ""+ , version_id = Just "31"+ , pretty_name = "Fedora 31 (Container Image)"+ , ansi_color = Just "0;34"+ , cpe_name = Just "cpe:/o:fedoraproject:fedora:31"+ , home_url = Just "https://fedoraproject.org/"+ , documentation_url = Just "https://docs.fedoraproject.org/en-US/fedora/f31/system-administrators-guide/"+ , support_url = Just "https://fedoraproject.org/wiki/Communicating_and_getting_help"+ , bug_report_url = Just "https://bugzilla.redhat.com/"+ , privacy_policy_url = Just "https://fedoraproject.org/wiki/Legal:PrivacyPolicy"+ , build_id = Nothing+ , variant = Just "Container Image"+ , variant_id = Just "container"+ , logo = Just "fedora-logo-icon"+ }+ , unknown_fields =+ [+ ( "REDHAT_SUPPORT_PRODUCT_VERSION"+ , "31"+ )+ ,+ ( "REDHAT_BUGZILLA_PRODUCT"+ , "Fedora"+ )+ ,+ ( "REDHAT_SUPPORT_PRODUCT"+ , "Fedora"+ )+ ,+ ( "PLATFORM_ID"+ , "platform:f31"+ )+ ,+ ( "REDHAT_BUGZILLA_PRODUCT_VERSION"+ , "31"+ )+ ,+ ( "LOGO"+ , "fedora-logo-icon"+ )+ ]+ , parse_errors = []+ }
+ tests/Golden/data/fedora.in view
@@ -0,0 +1,21 @@+NAME=Fedora+VERSION="31 (Container Image)"+ID=fedora+VERSION_ID=31+VERSION_CODENAME=""+PLATFORM_ID="platform:f31"+PRETTY_NAME="Fedora 31 (Container Image)"+ANSI_COLOR="0;34"+LOGO=fedora-logo-icon+CPE_NAME="cpe:/o:fedoraproject:fedora:31"+HOME_URL="https://fedoraproject.org/"+DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f31/system-administrators-guide/"+SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help"+BUG_REPORT_URL="https://bugzilla.redhat.com/"+REDHAT_BUGZILLA_PRODUCT="Fedora"+REDHAT_BUGZILLA_PRODUCT_VERSION=31+REDHAT_SUPPORT_PRODUCT="Fedora"+REDHAT_SUPPORT_PRODUCT_VERSION=31+PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"+VARIANT="Container Image"+VARIANT_ID=container
+ tests/Golden/data/gentoo.golden view
@@ -0,0 +1,24 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Gentoo"+ , version = Nothing+ , id = "gentoo"+ , id_like = Nothing+ , version_codename = Nothing+ , version_id = Nothing+ , pretty_name = "Gentoo/Linux"+ , ansi_color = Just "1;32"+ , cpe_name = Nothing+ , home_url = Just "https://www.gentoo.org/"+ , documentation_url = Nothing+ , support_url = Just "https://www.gentoo.org/support/"+ , bug_report_url = Just "https://bugs.gentoo.org/"+ , privacy_policy_url = Nothing+ , build_id = Nothing+ , variant = Nothing+ , variant_id = Nothing+ , logo = Nothing+ }+ , unknown_fields = []+ , parse_errors = []+ }
+ tests/Golden/data/gentoo.in view
@@ -0,0 +1,8 @@+NAME=Gentoo+ID=gentoo+PRETTY_NAME="Gentoo/Linux"+ANSI_COLOR="1;32"+HOME_URL="https://www.gentoo.org/"+SUPPORT_URL="https://www.gentoo.org/support/"+BUG_REPORT_URL="https://bugs.gentoo.org/"+
+ tests/Golden/data/mint.golden view
@@ -0,0 +1,29 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Linux Mint"+ , version = Just "18.2 (Sonya)"+ , id = "linuxmint"+ , id_like = Just "ubuntu"+ , version_codename = Just "sonya"+ , version_id = Just "18.2"+ , pretty_name = "Linux Mint 18.2"+ , ansi_color = Nothing+ , cpe_name = Nothing+ , home_url = Just "http://www.linuxmint.com/"+ , documentation_url = Nothing+ , support_url = Just "http://forums.linuxmint.com/"+ , bug_report_url = Just "http://bugs.launchpad.net/linuxmint/"+ , privacy_policy_url = Nothing+ , build_id = Nothing+ , variant = Nothing+ , variant_id = Nothing+ , logo = Nothing+ }+ , unknown_fields =+ [+ ( "UBUNTU_CODENAME"+ , "xenial"+ )+ ]+ , parse_errors = []+ }
+ tests/Golden/data/mint.in view
@@ -0,0 +1,12 @@+NAME="Linux Mint"+VERSION="18.2 (Sonya)"+ID=linuxmint+ID_LIKE=ubuntu+PRETTY_NAME="Linux Mint 18.2"+VERSION_ID="18.2"+HOME_URL="http://www.linuxmint.com/"+SUPPORT_URL="http://forums.linuxmint.com/"+BUG_REPORT_URL="http://bugs.launchpad.net/linuxmint/"+VERSION_CODENAME=sonya+UBUNTU_CODENAME=xenial+
+ tests/Golden/data/red-hat.golden view
@@ -0,0 +1,41 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Red Hat Enterprise Linux Server"+ , version = Just "7.6 (Maipo)"+ , id = "rhel"+ , id_like = Just "fedora"+ , version_codename = Nothing+ , version_id = Just "7.6"+ , pretty_name = "Red Hat Enterprise Linux"+ , ansi_color = Just "0;31"+ , cpe_name = Just "cpe:/o:redhat:enterprise_linux:7.6:GA:server"+ , home_url = Just "https://www.redhat.com/"+ , documentation_url = Nothing+ , support_url = Nothing+ , bug_report_url = Just "https://bugzilla.redhat.com/"+ , privacy_policy_url = Nothing+ , build_id = Nothing+ , variant = Just "Server"+ , variant_id = Just "server"+ , logo = Nothing+ }+ , unknown_fields =+ [+ ( "REDHAT_SUPPORT_PRODUCT_VERSION"+ , "7.6"+ )+ ,+ ( "REDHAT_BUGZILLA_PRODUCT"+ , "Red Hat Enterprise Linux 7"+ )+ ,+ ( "REDHAT_SUPPORT_PRODUCT"+ , "Red Hat Enterprise Linux"+ )+ ,+ ( "REDHAT_BUGZILLA_PRODUCT_VERSION"+ , "7.6"+ )+ ]+ , parse_errors = []+ }
+ tests/Golden/data/red-hat.in view
@@ -0,0 +1,17 @@+NAME="Red Hat Enterprise Linux Server"+VERSION="7.6 (Maipo)"+ID="rhel"+ID_LIKE="fedora"+VARIANT="Server"+VARIANT_ID="server"+VERSION_ID="7.6"+PRETTY_NAME="Red Hat Enterprise Linux"+ANSI_COLOR="0;31"+CPE_NAME="cpe:/o:redhat:enterprise_linux:7.6:GA:server"+HOME_URL="https://www.redhat.com/"+BUG_REPORT_URL="https://bugzilla.redhat.com/"+ +REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 7"+REDHAT_BUGZILLA_PRODUCT_VERSION=7.6+REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"+REDHAT_SUPPORT_PRODUCT_VERSION="7.6"
+ tests/Golden/data/red-hat_parseErrors.golden view
@@ -0,0 +1,49 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Red Hat Enterprise Linux Server"+ , version = Just "7.6 (Maipo)"+ , id = "rhel"+ , id_like = Just "fedora"+ , version_codename = Nothing+ , version_id = Just "7.6"+ , pretty_name = "Red Hat Enterprise Linux"+ , ansi_color = Just "0;31"+ , cpe_name = Just "cpe:/o:redhat:enterprise_linux:7.6:GA:server"+ , home_url = Just "https://www.redhat.com/"+ , documentation_url = Nothing+ , support_url = Nothing+ , bug_report_url = Just "https://bugzilla.redhat.com/"+ , privacy_policy_url = Nothing+ , build_id = Nothing+ , variant = Just "Server"+ , variant_id = Just "server"+ , logo = Nothing+ }+ , unknown_fields =+ [+ ( "REDHAT_SUPPORT_PRODUCT_VERSION"+ , "7.6"+ )+ ,+ ( "REDHAT_BUGZILLA_PRODUCT"+ , "Red Hat Enterprise Linux 7"+ )+ ,+ ( "REDHAT_SUPPORT_PRODUCT"+ , "Red Hat Enterprise Linux"+ )+ ,+ ( "REDHAT_BUGZILLA_PRODUCT_VERSION"+ , "7.6"+ )+ ]+ , parse_errors =+ [ TrivialError 520+ ( Just+ ( Tokens ( '\'' :| "" ) )+ )+ ( fromList+ [ Label ( 'e' :| "nd of line" ) ]+ )+ ]+ }
+ tests/Golden/data/red-hat_parseErrors.in view
@@ -0,0 +1,18 @@+NAME="Red Hat Enterprise Linux Server"+VERSION="7.6 (Maipo)"+ID="rhel"+ID_LIKE="fedora"+VARIANT="Server"+VARIANT_ID="server"+VERSION_ID="7.6"+PRETTY_NAME="Red Hat Enterprise Linux"+ANSI_COLOR="0;31"+CPE_NAME="cpe:/o:redhat:enterprise_linux:7.6:GA:server"+HOME_URL="https://www.redhat.com/"+BUG_REPORT_URL="https://bugzilla.redhat.com/"+ +REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 7"+REDHAT_BUGZILLA_PRODUCT_VERSION=7.6+REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"+REDHAT_SUPPORT_PRODUCT_VERSION="7.6"+foo=1.'
+ tests/Golden/data/ubuntu.golden view
@@ -0,0 +1,29 @@+OsReleaseResult+ { osRelease = OsRelease+ { name = "Ubuntu"+ , version = Just "20.04 LTS (Focal Fossa)"+ , id = "ubuntu"+ , id_like = Just "debian"+ , version_codename = Just "focal"+ , version_id = Just "20.04"+ , pretty_name = "Ubuntu 20.04 LTS"+ , ansi_color = Nothing+ , cpe_name = Nothing+ , home_url = Just "https://www.ubuntu.com/"+ , documentation_url = Nothing+ , support_url = Just "https://help.ubuntu.com/"+ , bug_report_url = Just "https://bugs.launchpad.net/ubuntu/"+ , privacy_policy_url = Just "https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"+ , build_id = Nothing+ , variant = Nothing+ , variant_id = Nothing+ , logo = Nothing+ }+ , unknown_fields =+ [+ ( "UBUNTU_CODENAME"+ , "focal"+ )+ ]+ , parse_errors = []+ }
+ tests/Golden/data/ubuntu.in view
@@ -0,0 +1,13 @@+NAME="Ubuntu"+VERSION="20.04 LTS (Focal Fossa)"+ID=ubuntu+ID_LIKE=debian+PRETTY_NAME="Ubuntu 20.04 LTS"+VERSION_ID="20.04"+HOME_URL="https://www.ubuntu.com/"+SUPPORT_URL="https://help.ubuntu.com/"+BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"+PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"+VERSION_CODENAME=focal+UBUNTU_CODENAME=focal+
− tests/HLint.hs
@@ -1,17 +0,0 @@-module Main (main) where--import Language.Haskell.HLint (hlint)-import System.Exit (exitFailure, exitSuccess)--arguments :: [String]-arguments =- [- "library"- , "--cpp-file=dist/build/autogen/cabal_macros.h"- , "tests"- ]--main :: IO ()-main = do- hints <- hlint arguments- if null hints then exitSuccess else exitFailure
− tests/Haddock.hs
@@ -1,30 +0,0 @@-module Main (main) where--import Data.List (genericLength)-import Data.Maybe (catMaybes)-import System.Exit (exitFailure, exitSuccess)-import System.Process (readProcess)-import Text.Regex (matchRegex, mkRegex)--arguments :: [String]-arguments =- [ "haddock"- ]--average :: (Fractional a, Real b) => [b] -> a-average xs = realToFrac (sum xs) / genericLength xs--expected :: Fractional a => a-expected = 0--main :: IO ()-main = do- output <- readProcess "cabal" arguments ""- if average (match output) >= expected- then exitSuccess- else putStr output >> exitFailure--match :: String -> [Int]-match = fmap read . concat . catMaybes . fmap (matchRegex pattern) . lines- where- pattern = mkRegex "^ *([0-9]*)% "
+ tests/Main.hs view
@@ -0,0 +1,16 @@+import Golden.Real+import Specs.Megaparsec++import Test.Tasty+import Test.Tasty.Hspec+++main :: IO ()+main = do+ ms <- testSpec "megaparsec spec" megaparsecSpec+ gs <- goldenTests+ defaultMain (tests [ms, gs])++tests :: [TestTree] -> TestTree+tests ts = testGroup "Tests" (ts++[])+
+ tests/Specs/Megaparsec.hs view
@@ -0,0 +1,88 @@+module Specs.Megaparsec where++import System.OsRelease++import Data.Either+import Data.Void+import Test.Hspec+import Test.Hspec.Megaparsec++import qualified Text.Megaparsec as MP+++megaparsecSpec :: Spec+megaparsecSpec = do+ describe "parseAssignment" $ do+ it "parses simple value" $ shouldParse' "foo=bar" ("foo", "bar")+ it "parses single quoted value" $ shouldParse' "foo='bar'" ("foo", "bar")+ it "parses double quoted value" $ shouldParse' "foo=\"bar\"" ("foo", "bar")++ it "parses _ var" $ shouldParse' "f_x=''" ("f_x", "")++ -- this is not valid per spec, but many files do this+ it "parses ._- in unquoted assignment"+ $ shouldParse' "VERSION_ID=bar-1.9_rc2" ("VERSION_ID", "bar-1.9_rc2")++ it "parses quoted space" $ shouldParse' "f='a b'" ("f", "a b")+ it "parses quoted -" $ shouldParse' "f='a-b'" ("f", "a-b")++ it "parses special \\" $ shouldParse' "foo='ba\\\\r'" ("foo", "ba\\r")+ it "parses special `" $ shouldParse' "foo='ba\\`r'" ("foo", "ba`r")+ it "parses special '" $ shouldParse' "foo='ba\\'r'" ("foo", "ba'r")+ it "parses special $" $ shouldParse' "foo='ba\\$r'" ("foo", "ba$r")+ it "parses special \"" $ shouldParse' "foo=\"ba\\\"r\"" ("foo", "ba\"r")++ it "parses empty val noquotes" $ shouldParse' "foo=" ("foo", "")+ it "parses empty val quote \"" $ shouldParse' "foo=\"\"" ("foo", "")+ it "parses empty val quote '" $ shouldParse' "foo=''" ("foo", "")++ it "breaks on comments" $ shouldFail' "# foo=\"bar'"+ it "breaks on misquoting 1" $ shouldFail' "foo=\"bar'"+ it "breaks on misquoting 2" $ shouldFail' "foo='bar\""+ it "breaks on unquoted $" $ shouldFail' "foo='ba$r'"+ it "breaks on unquoted `" $ shouldFail' "foo='ba`r'"+ it "breaks on unquoted \"" $ shouldFail' "foo=\"ba\"r\""+ it "breaks on unquoted '" $ shouldFail' "foo='ba'r'"+ it "breaks on unquoted \\" $ shouldFail' "foo='ba\\r'"+ it "breaks on unquoted val with space" $ shouldFail' "foo=ba r"+ it "breaks on unquoted val with ;" $ shouldFail' "foo=ba;r"+ it "breaks on unquoted val with \\" $ shouldFail' "foo=ba\\r"+ it "breaks on trailing NL" $ shouldFail' "foo=bar\n"+++ describe "parseAssignments" $ do+ it "parses simple values" $ shouldParseMany "foo=bar" [("foo", "bar")]++ it "parses multiple values"+ $ shouldParseMany "foo=bar\nbar=baz" [("foo", "bar"), ("bar", "baz")]+ it "parses multiple values with comments" $ shouldParseMany+ "foo=bar\n# comment\nbar=baz"+ [("foo", "bar"), ("bar", "baz")]++ it "parses gracefully" $ shouldParseMany "foo=bar\nbar=baz\"" [("foo", "bar")]++ it "parses empty files" $ shouldParseMany "" []+ it "parses empty files with newlines" $ shouldParseMany "\n\n" []+ it "parses empty files with newlines and comments"+ $ shouldParseMany "\n\n#\n" []++ it "parses comments with leading spaces" $ shouldParseMany " #" []++parse :: String -> Either (MP.ParseErrorBundle String Void) (String, String)+parse = MP.parse (parseAssignment <* MP.eof) ""++shouldParse' :: String -> (String, String) -> Expectation+shouldParse' s s' = parse s `shouldParse` s'++shouldFail' :: String -> Expectation+shouldFail' s = parse `shouldFailOn` s++parseMany :: String+ -> Either (MP.ParseErrorBundle String Void) [(String, String)]+parseMany = fmap rights . MP.parse parseAssignments ""++shouldParseMany :: String -> [(String, String)] -> Expectation+shouldParseMany s s' = parseMany s `shouldParse` s'++shouldFailMany :: String -> Expectation+shouldFailMany s = parseMany `shouldFailOn` s
− tests/unit/OsReleaseSpec.hs
@@ -1,93 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module OsReleaseSpec (spec) where--import Test.Hspec-import System.OsRelease-import Data.Map.Lazy-import Data.Monoid-import OsReleaseSpec.Reals-import System.IO.Temp-import System.IO--spec :: Spec-spec = do- describe "parseOs" $ do- it "parses simple value" $ fooBar "foo=bar"- it "parses simple value with trailing NL" $ fooBar "foo=bar\n"- it "parses single quoted value" $ fooBar "foo='bar'"- it "parses double quoted value" $ fooBar "foo=\"bar\""-- it "parses _ var" $ parseCase "f_x=''" [("f_x", "")]-- it "parses quoted space" $ parseCase "f='a b'" [("f", "a b")]- it "parses quoted -" $ parseCase "f='a-b'" [("f", "a-b")]-- it "parses special \\" $ specialFooBar "foo='ba\\\\r'" "\\"- it "parses special `" $ specialFooBar "foo='ba\\`r'" "`"- it "parses special '" $ specialFooBar "foo='ba\\'r'" "'"- it "parses special $" $ specialFooBar "foo='ba\\$r'" "$"- it "parses special \"" $ specialFooBar "foo=\"ba\\\"r\"" "\""-- it "parses multiple values" $ parseCase- "foo=bar\nqux=quux"- [("foo", "bar"), ("qux", "quux")]-- it "parses empty val noquotes" $ parseCase "foo=" [("foo", "")]- it "parses empty val quote \"" $ parseCase "foo=\"\"" [("foo", "")]- it "parses empty val quote '" $ parseCase "foo=''" [("foo", "")]-- it "breaks on misquoting 1" $ errCase "foo=\"bar'"- it "breaks on misquoting 2" $ errCase "foo='bar\""- it "breaks on unquoted $" $ errCase "foo='ba$r'"- it "breaks on unquoted `" $ errCase "foo='ba`r'"- it "breaks on unquoted \"" $ errCase "foo=\"ba\"r\""- it "breaks on unquoted '" $ errCase "foo='ba'r'"- it "breaks on unquoted \\" $ errCase "foo='ba\\r'"- it "breaks on unquoted val with space" $ errCase "foo=ba r"- it "breaks on unquoted val with ;" $ errCase "foo=ba;r"- it "breaks on unquoted val with \\" $ errCase "foo=ba\\r"-- it "parses Gentoo os-release" $ realCase Gentoo- it "parses OpenSUSE Factory os-release" $ realCase OpenSUSEFactory- it "parses Arch os-release" $ realCase Arch-- describe "readOs'" $ do- it "reads 1st" $ do- withSystemTempFile "1st.txt" $ \f1 hf1 -> do- withSystemTempFile "2nd.txt" $ \f2 hf2 -> do- hPutStr hf1 "1"- hPutStr hf2 "2"- mapM_ hClose [hf1, hf2]-- xs <- readOs' [f1, f2]- case xs of- Right x -> x `shouldBe` "1"- Left e -> expectationFailure $ show e-- it "reads 2nd" $ do- withSystemTempFile "a-file.txt" $ \f h -> do- hPutStr h "1"- hClose h-- xs <- readOs' ["/oh-well-let's-hope-no-one-makes-this-file", f]- case xs of- Right x -> x `shouldBe` "1"- Left e -> expectationFailure $ show e-- where- fooBar x = parseCase x [("foo", "bar")]-- specialFooBar :: String -> String -> Expectation- specialFooBar x y = parseCase x [("foo", OsReleaseValue $ "ba" <> y <> "r")]-- realCase x = parseCase (input x) (output x)-- parseCase x y =- case parseOs x of- Left e -> expectationFailure $ show e- Right z -> z `shouldBe` (fromList y)-- errCase x =- case parseOs x of- Left _ -> ("1"::String) `shouldBe` "1"- Right z -> expectationFailure (show z)
− tests/unit/Spec.hs
@@ -1,1 +0,0 @@-{-# OPTIONS_GHC -F -pgmF hspec-discover #-}