packages feed

language-puppet 1.3.6 → 1.3.7

raw patch · 11 files changed

+284/−49 lines, 11 files

Files

CHANGELOG.markdown view
@@ -1,4 +1,9 @@-# v1.3.6 (UNRELEASED)+# v1.3.7 (2017/03/14)++* Add puppet `sprintf` function+* Fix scientific2text (#196)++# v1.3.6 (2017/02/27)  * The `defined` function can now test variables * Fixed the `delete_at` function, added new tests, TBC
Puppet/Interpreter/IO.hs view
@@ -79,7 +79,6 @@             PuppetPaths                  -> runInstr (r ^. readerPuppetPaths)             GetNativeTypes               -> runInstr (r ^. readerNativeTypes)             ErrorThrow d                 -> return (Left d, s, mempty)-            ErrorCatch _ _               -> thpe "ErrorCatch"             GetNodeName                  -> runInstr (r ^. readerNodename)             HieraQuery scps q t          -> canFail ((r ^. readerHieraQuery) scps q t)             PDBInformation               -> pdbInformation pdb >>= runInstr@@ -96,3 +95,9 @@             TraceEvent e                 -> (r ^. readerIoMethods . ioTraceEvent) e >>= runInstr             IsIgnoredModule m            -> runInstr (r ^. readerIgnoredModules . contains m)             IsExternalModule m           -> runInstr (r ^. readerExternalModules . contains m)+            -- on error, the program state is RESET and the logged messages are dropped+            ErrorCatch atry ahandle      -> do+                (eres, s', w) <- interpretMonad r s atry+                case eres of+                    Left rr -> interpretMonad r s (ahandle rr >>= k)+                    Right x -> logStuff w (interpretMonad r s' (k x))
Puppet/Interpreter/Resolve.hs view
@@ -29,15 +29,6 @@       fixResourceName     ) where -import           Puppet.Interpreter.PrettyPrinter ()-import           Puppet.Interpreter.RubyRandom-import           Puppet.Interpreter.Types-import           Puppet.Interpreter.Utils-import           Puppet.Parser.Types-import           Puppet.Paths-import           Puppet.PP-import           Puppet.Utils- import           Control.Lens import           Control.Monad import           Control.Monad.Operational        (singleton)@@ -45,8 +36,8 @@ import           Data.Aeson                       hiding ((.=)) import           Data.Aeson.Lens                  hiding (key) import           Data.Bits-import           Data.ByteString (ByteString)-import           Data.ByteArray (convert)+import           Data.ByteArray                   (convert)+import           Data.ByteString                  (ByteString) import qualified Data.ByteString                  as BS import qualified Data.ByteString.Base16           as B16 import           Data.CaseInsensitive             (mk)@@ -54,18 +45,28 @@ import qualified Data.Foldable                    as F import qualified Data.HashMap.Strict              as HM import qualified Data.HashSet                     as HS-import           Data.Maybe                       (mapMaybe,fromMaybe)+import           Data.Maybe                       (fromMaybe, mapMaybe) import qualified Data.Maybe.Strict                as S import           Data.Scientific import qualified Data.Text                        as T import qualified Data.Text.Encoding               as T import           Data.Tuple.Strict                as S import qualified Data.Vector                      as V-import           Data.Version                     (parseVersion, Version(..))+import           Data.Version                     (Version (..), parseVersion) import           Text.ParserCombinators.ReadP     (readP_to_S) import qualified Text.PrettyPrint.ANSI.Leijen     as PP import           Text.Regex.PCRE.ByteString.Utils +import           Puppet.Interpreter.PrettyPrinter ()+import           Puppet.Interpreter.RubyRandom+import           Puppet.Interpreter.Types+import           Puppet.Interpreter.Utils+import           Puppet.Interpreter.Resolve.Sprintf (sprintf)+import           Puppet.Parser.Types+import           Puppet.Paths+import           Puppet.PP+import           Puppet.Utils+ sha1 :: ByteString -> ByteString sha1 = convert . (hash :: ByteString -> Digest SHA1) @@ -95,7 +96,7 @@         -- we can't use _PString, because of dependency cycles         toStr (k,v) = case v of                           PString x -> Just (k,x)-                          _ -> Nothing+                          _         -> Nothing         toplevels = map (_1 %~ ("::" <>)) $ getV "::"         locals = getV ctx         vars = HM.fromList (toplevels <> locals)@@ -191,7 +192,7 @@                  (PString sa, PString sb)      -> mk sa == mk sb                  -- TODO, check if array / hash equality should be recursed                  -- for case insensitive matching-                 _ -> ra == rb+                 _                             -> ra == rb  -- | The main resolution function : turns an 'Expression' into a 'PValue', -- if possible.@@ -244,7 +245,7 @@         PHash h -> do             ridx <- resolveExpressionString idx             case h ^. at ridx of-                Just _ -> return (PBoolean True)+                Just _  -> return (PBoolean True)                 Nothing -> return (PBoolean False)         PArray ar -> do             ridx <- resolveExpression idx@@ -294,9 +295,9 @@     ra <- resolveExpression a     rb <- resolveExpression b     case (ra, rb) of-        (PHash ha, PHash hb) -> return (PHash (ha <> hb))+        (PHash ha, PHash hb)   -> return (PHash (ha <> hb))         (PArray ha, PArray hb) -> return (PArray (ha <> hb))-        _ -> binaryOperation a b (+)+        _                      -> binaryOperation a b (+) resolveExpression (Substraction a b)   = binaryOperation a b (-) resolveExpression (Division a b)       = do     ra <- resolveExpressionNumber a@@ -380,10 +381,10 @@ -- | Turns a 'PValue' into a 'Bool', as explained in the reference -- documentation. pValue2Bool :: PValue -> Bool-pValue2Bool PUndef = False+pValue2Bool PUndef       = False pValue2Bool (PString "") = False pValue2Bool (PBoolean x) = x-pValue2Bool _ = True+pValue2Bool _            = True  -- | This resolve function calls at the expression level. resolveFunction :: T.Text -> V.Vector Expression -> InterpreterMonad PValue@@ -406,7 +407,7 @@ resolveFunction fname args = mapM resolveExpression (V.toList args) >>= resolveFunction' fname . map undefEmptyString     where         undefEmptyString PUndef = PString ""-        undefEmptyString x = x+        undefEmptyString x      = x  resolveFunction' :: T.Text -> [PValue] -> InterpreterMonad PValue resolveFunction' "defined" [PResourceReference "class" cn] = do@@ -426,7 +427,7 @@             scps <- use scopes             scp <- getScopeName             return $ PBoolean $ case getVariable scps scp (T.tail t) of-                Left _ -> False+                Left _  -> False                 Right _ -> True         else do -- resource test             -- case 1, nested thingie@@ -459,7 +460,7 @@                     Right x -> fmap PString (safeDecodeUtf8 x)     case ptarget of         PArray a -> fmap PArray (traverse sub a)-        s -> sub s+        s        -> sub s resolveFunction' "regsubst" _ = throwPosError "regsubst(): Expects 3 or 4 arguments" resolveFunction' "split" [psrc, psplt] = do     src  <- fmap T.encodeUtf8 (resolvePValueString psrc)@@ -507,15 +508,17 @@     b <- resolvePValueString pb     let parser x = case filter (null . Prelude.snd) (readP_to_S parseVersion (T.unpack x)) of                        ( (v, _) : _ ) -> v-                       _ -> Version [] [] -- fallback :(+                       _              -> Version [] [] -- fallback :(         va = parser a         vb = parser b     return $ PString $ case compare va vb of                            EQ -> "0"                            LT -> "-1"                            GT -> "1"- resolveFunction' "versioncmp" _ = throwPosError "versioncmp(): Expects two arguments"+-- | Simplified implementation of sprintf+resolveFunction' "sprintf" (PString str:args) = sprintf str args+resolveFunction' "sprintf" _ = throwPosError "sprintf(): Expects a string as its first argument" -- some custom functions resolveFunction' "pdbresourcequery" [q]   = pdbresourcequery q Nothing resolveFunction' "pdbresourcequery" [q,k] = fmap Just (resolvePValueString k) >>= pdbresourcequery q@@ -548,7 +551,7 @@                                          Nothing -> throwPosError ("pdbresourcequery strange error, could not find key" <+> ttext ky <+> "in" <+> pretty (PHash h))         extractSubHash _ x = throwPosError ("pdbresourcequery strange error, expected a hash, had" <+> pretty x)     case mkey of-        Nothing -> return (PArray rv)+        Nothing  -> return (PArray rv)         (Just k) -> fmap PArray (V.mapM (extractSubHash k) rv)  calcTemplate :: (T.Text -> Either T.Text T.Text) -> PValue -> InterpreterMonad T.Text@@ -583,9 +586,9 @@         mkSE (RNonEqualitySearch a b) = fmap QNot (mkSE (REqualitySearch a b))         mkSE (REqualitySearch a (PString b)) = [QEqual (mkFld a) b]         mkSE _ = []-        mkFld "tag" = RTag+        mkFld "tag"   = RTag         mkFld "title" = RTitle-        mkFld z = RParameter z+        mkFld z       = RParameter z  -- | Checks whether a given 'Resource' matches a 'RSearchExpression'. Note -- that the expression doesn't check for type, so you must filter the@@ -599,21 +602,21 @@ checkSearchExpression (REqualitySearch "title" v) r =     let nameequal = puppetEquality v (PString (r ^. rid . iname))         aliasequal = case r ^. rattributes . at "alias" of-                         Just a -> puppetEquality v a+                         Just a  -> puppetEquality v a                          Nothing -> False     in nameequal || aliasequal checkSearchExpression (REqualitySearch attributename v) r =     case r ^. rattributes . at attributename of-        Nothing -> False+        Nothing         -> False         Just (PArray x) -> F.any (flip puppetEquality v) x-        Just x -> puppetEquality x v+        Just x          -> puppetEquality x v checkSearchExpression (RNonEqualitySearch attributename v) r     | attributename  == "tag" = True     | attributename  == "title" = not (checkSearchExpression (REqualitySearch attributename v) r)     | otherwise = case r ^. rattributes . at attributename of-        Nothing -> True+        Nothing         -> True         Just (PArray x) -> not (F.all (flip puppetEquality v) x)-        Just x -> not (puppetEquality x v)+        Just x          -> not (puppetEquality x v)  -- | Generates variable associations for evaluation of blocks. Each item -- corresponds to an iteration in the calling block.
+ Puppet/Interpreter/Resolve/Sprintf.hs view
@@ -0,0 +1,144 @@+module Puppet.Interpreter.Resolve.Sprintf where++import           Control.Applicative+import           Control.Monad+import           Data.Attoparsec.Text+import           Data.Scientific (Scientific)+import qualified Data.Text                         as T+import qualified Data.Text.Lazy                    as TL+import qualified Data.Text.Lazy.Builder            as TB+import qualified Data.Text.Lazy.Builder.Int        as TB+import qualified Data.Text.Lazy.Builder.Scientific as TB+++import           Puppet.Interpreter.Types+import           Puppet.Interpreter.Utils+import           Puppet.Utils+import           Puppet.PP (pretty)+import           Puppet.Interpreter.PrettyPrinter()++data Flag = Minus | Plus | Space | Zero | Hash+          deriving (Show, Eq)++data FLen = Lhh | Lh | Ll | Lll | LL | Lz | Lj | Lt+           deriving (Show, Eq)++data FType = TPct | Td | Tu | Tf | TF | Te | TE | Tg | TG | Tx | TX | To | Ts | Tc | Tp | Ta | TA+           deriving (Show, Eq)++data PrintfFormat = PrintfFormat { _pfFlags :: [Flag]+                                 , _pfWidth :: Maybe Int+                                 , _pfPrec  :: Maybe Int+                                 , _pfLen   :: Maybe FLen+                                 , _pfType  :: FType+                                 } deriving (Show, Eq)++data FormatStringPart = Raw T.Text+                      | Format PrintfFormat+                      deriving (Show, Eq)++parseFormat :: T.Text -> [FormatStringPart]+parseFormat t | T.null t = []+              | T.null nxt = [Raw raw]+              | otherwise = Raw raw : rformat+  where+    (raw, nxt) = T.break (== '%') t+    tryNext = case parseFormat (T.tail nxt) of+                  (Raw nt : nxt') -> Raw (T.cons '%' nt) : nxt'+                  nxt' -> Raw (T.singleton '%') : nxt'+    rformat = case parse format nxt of+                  Fail _ _ _ -> tryNext+                  Partial _ -> tryNext+                  Done remaining f -> Format f : parseFormat remaining++flag :: Parser Flag+flag =   (Minus <$ char '-')+     <|> (Plus  <$ char '+')+     <|> (Space <$ char ' ')+     <|> (Zero  <$ char '0')+     <|> (Hash  <$ char '#')++lenModifier :: Parser FLen+lenModifier =   (Lhh <$ string "hh")+            <|> (Lh  <$ char 'h')+            <|> (Lll <$ string "ll")+            <|> (Ll  <$ char 'l')+            <|> (LL  <$ char 'L')+            <|> (Lz  <$ char 'z')+            <|> (Lj  <$ char 'j')+            <|> (Lt  <$ char 't')++ftype :: Parser FType+ftype =   (TPct <$ char '%')+      <|> (Td <$ char 'd')+      <|> (Td <$ char 'i')+      <|> (Tu <$ char 'u')+      <|> (Tf <$ char 'f')+      <|> (TF <$ char 'F')+      <|> (Te <$ char 'e')+      <|> (TE <$ char 'E')+      <|> (Tg <$ char 'g')+      <|> (TG <$ char 'G')+      <|> (Tx <$ char 'x')+      <|> (TX <$ char 'X')+      <|> (To <$ char 'o')+      <|> (Ts <$ char 's')+      <|> (Tc <$ char 'c')+      <|> (Ta <$ char 'a')+      <|> (Tp <$ char 'p')+      <|> (TA <$ char 'A')++format :: Parser PrintfFormat+format = do+    void $ char '%'+    flags <- many flag+    width <- optional decimal+    prec <- optional $ do+        void $ char '.'+        decimal+    len <- optional lenModifier+    ft <- ftype+    return (PrintfFormat flags width prec len ft)++sprintf :: T.Text -> [PValue] -> InterpreterMonad PValue+sprintf str oargs = PString . TL.toStrict . TB.toLazyText . mconcat <$> go (parseFormat str) oargs+  where+    go (Raw x : xs) args = (TB.fromText x :) <$> go xs args+    go (Format f : _) _ | Hash `elem` _pfFlags f = throwPosError "sprintf: the # modifier is not supported"+    go (Format f : xs) (arg : args) = do+        let numeric = case arg of+                          PNumber n -> pure n+                          PString s -> maybe (throwError "sprintf: Don't know how to convert this to a number") return (text2Scientific s)+                          _         -> throwError "sprintf: Don't know how to convert this to a number"+            flags = _pfFlags f+            sh mkBuilder n | has Minus            = TL.justifyLeft padlen ' ' (sprefix <> content)+                           | has Plus && has Zero = sprefix <> TL.justifyRight mpadlen '0' content+                           | has Plus             = TL.justifyRight padlen ' ' (sprefix <> content)+                           | has Zero             = TL.justifyRight padlen '0' content+                           | otherwise            = TL.justifyRight padlen ' ' content+                 where+                   (mpadlen, sprefix) | Plus  `elem` flags && n >= 0 = (padlen - 1, "+")+                                      | Space `elem` flags && n >= 0 = (padlen - 1, " ")+                                      | otherwise = (padlen, mempty)+                   padlen = maybe 0 fromIntegral (_pfWidth f)+                   has flg = flg `elem` flags+                   content = TB.toLazyText (mkBuilder n)+        baseString <- case _pfType f of+                          Td -> sh (TB.formatScientificBuilder TB.Fixed    (Just 0))      <$> numeric+                          Tf -> sh (TB.formatScientificBuilder TB.Fixed    (_pfPrec f))   <$> numeric+                          TF -> sh (TB.formatScientificBuilder TB.Fixed    (_pfPrec f))   <$> numeric+                          Tg -> sh (TB.formatScientificBuilder TB.Generic  (_pfPrec f))   <$> numeric+                          TG -> sh (TB.formatScientificBuilder TB.Generic  (_pfPrec f))   <$> numeric+                          Te -> sh (TB.formatScientificBuilder TB.Exponent (_pfPrec f))   <$> numeric+                          TE -> sh (TB.formatScientificBuilder TB.Exponent (_pfPrec f))   <$> numeric+                          Tx -> sh (TB.hexadecimal . (truncate :: Scientific -> Integer)) <$> numeric+                          TX -> sh (TB.hexadecimal . (truncate :: Scientific -> Integer)) <$> numeric+                          Ts -> return $ case arg of+                                             PString s -> TL.fromStrict s+                                             _ -> TL.pack (show (pretty arg))+                          _ -> throwPosError "sprintf: not yet supported"+        (TB.fromLazyText baseString :) <$> go xs args+    go [] [] = return []+    go _ [] = throwPosError "sprintf: not enough arguments"+    go [] _ = [] <$ let msg = "sprintf: too many arguments" in checkStrict msg msg+
Puppet/Interpreter/Types.hs view
@@ -74,6 +74,7 @@  , EdgeMap   -- * Classes  , MonadThrowPos(..)+ , MonadError(..)   -- * definitions  , metaparameters  , showPos@@ -137,6 +138,8 @@  instance Exception PrettyError +instance Pretty PrettyError where+  pretty = getError  data PValue = PBoolean !Bool             | PUndef@@ -180,9 +183,9 @@                 deriving (Show, Eq)  instance FromJSON Strictness where-  parseJSON (Bool True) = pure Strict+  parseJSON (Bool True)  = pure Strict   parseJSON (Bool False) = pure Permissive-  parseJSON _ = mzero+  parseJSON _            = mzero  data RSearchExpression = REqualitySearch !Text !PValue                        | RNonEqualitySearch !Text !PValue@@ -513,7 +516,7 @@         where             chk (Left x) = Left x             chk (Right x) = case fromJSON x of-                                Error rr -> Left rr+                                Error rr    -> Left rr                                  Success suc -> Right suc @@ -626,7 +629,7 @@       [ "<", flds, val ] -> QL     <$> parseJSON flds    <*> parseJSON val       [">=", flds, val ] -> QGE    <$> parseJSON flds    <*> parseJSON val       ["<=", flds, val ] -> QLE    <$> parseJSON flds    <*> parseJSON val-      x -> fail ("unknown query" ++ show x)+      x                  -> fail ("unknown query" ++ show x)     parseJSON _ = fail "Expected an array"  instance ToJSON FactField where@@ -641,13 +644,13 @@     parseJSON _          = fail "Can't parse fact field"  instance ToJSON NodeField where-    toJSON NName = "name"+    toJSON NName     = "name"     toJSON (NFact t) = toJSON [ "fact", t ]  instance FromJSON NodeField where     parseJSON (Array xs) = case V.toList xs of                                ["fact", x] -> NFact <$> parseJSON x-                               _ -> fail "Invalid field syntax"+                               _           -> fail "Invalid field syntax"     parseJSON (String "name") = pure NName     parseJSON _ = fail "invalid field" @@ -676,7 +679,7 @@  instance FromJSON RIdentifier where     parseJSON (Object v) = RIdentifier <$> v .: "type" <*> v .: "title"-    parseJSON _ = fail "invalid resource"+    parseJSON _          = fail "invalid resource"  instance ToJSON RIdentifier where     toJSON (RIdentifier t n) = object [("type", String t), ("title", String n)]
Puppet/Puppetlabs.hs view
@@ -27,6 +27,8 @@ extFun :: [(FilePath, Text, [PValue] -> InterpreterMonad PValue)] extFun =  [ ("/apache", "bool2httpd", apacheBool2httpd)           , ("/docker", "docker_run_flags", mockDockerRunFlags)+          , ("/jenkins", "jenkins_port", mockJenkinsPort)+          , ("/jenkins", "jenkins_prefix", mockJenkinsPrefix)           , ("/postgresql", "postgresql_acls_to_resources_hash", pgAclsToHash)           , ("/postgresql", "postgresql_password", pgPassword)           , ("/extlib", "random_password", randomPassword)@@ -65,6 +67,16 @@  randomPassword _ = throwPosError "expect one single string arguments" ++-- | To be implemented if needed+mockJenkinsPrefix :: MonadThrowPos m => [PValue] -> m PValue+mockJenkinsPrefix [] = return $ PString ""+mockJenkinsPrefix arg@_ = throwPosError $ "expect no argument" <+> pretty arg++-- | To be implemented if needed+mockJenkinsPort :: MonadThrowPos m => [PValue] -> m PValue+mockJenkinsPort [] = return $ PString "8080"+mockJenkinsPort arg@_ = throwPosError $ "expect no argument" <+> pretty arg  mockCacheData :: MonadThrowPos m => [PValue] -> m PValue mockCacheData [_, _, b] = return b
Puppet/Utils.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE RankNTypes #-} {-# LANGUAGE LambdaCase      #-} -- | Those are utility functions, most of them being pretty much self -- explanatory.@@ -30,7 +31,6 @@ import Data.Scientific import Control.Exception import Control.Lens-import Data.Aeson.Lens import qualified Data.Yaml as Y  text2Scientific :: T.Text -> Maybe Scientific@@ -39,9 +39,9 @@             Right s -> Just s  scientific2text :: Scientific -> T.Text-scientific2text n = T.pack $ case n ^? _Integer of-                                 Just i -> show i-                                 _      -> show n+scientific2text n = T.pack $ case floatingOrInteger n of+                                 Left r -> show (r :: Double)+                                 Right i -> show (i :: Integer)  strictifyEither :: Either a b -> S.Either a b strictifyEither (Left x) = S.Left x
PuppetDB/Dummy.hs view
@@ -16,4 +16,3 @@                     (const (return [] ))                     (throwError "not implemented")                     (\_ _ -> return [] )-
language-puppet.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                language-puppet-version:             1.3.6+version:             1.3.7 synopsis:            Tools to parse and evaluate the Puppet DSL. description:         This is a set of tools that is supposed to fill all your Puppet needs : syntax checks, catalog compilation, PuppetDB queries, simulationg of complex interactions between nodes, Puppet master replacement, and more ! homepage:            http://lpuppet.banquise.net/@@ -15,7 +15,7 @@ build-type:          Simple cabal-version:       >=1.8 -Tested-With:         GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1+Tested-With:         GHC == 7.10.3, GHC == 8.0.1  extra-source-files:     CHANGELOG.markdown@@ -45,6 +45,7 @@                        , Puppet.Interpreter.PrettyPrinter                        , Puppet.Interpreter.Pure                        , Puppet.Interpreter.Resolve+                       , Puppet.Interpreter.Resolve.Sprintf                        , Puppet.Interpreter.Types                        , Puppet.Interpreter.Utils                        , Puppet.Lens@@ -176,6 +177,7 @@   extensions:     OverloadedStrings   build-depends:  language-puppet,base,strict-base-types,lens,text,hspec,unordered-containers,megaparsec,vector,scientific,mtl   other-modules:  Function.ShellquoteSpec+                  Function.SprintfSpec                   Function.SizeSpec                   Function.MergeSpec                   Function.EachSpec
+ tests/Function/SprintfSpec.hs view
@@ -0,0 +1,60 @@+module Function.SprintfSpec (spec, main) where++import           Test.Hspec+import           Data.Text (Text)+import           Control.Monad+import qualified Data.Vector as V+import           Data.Monoid++import           Puppet.Interpreter.Pure+import           Puppet.Interpreter.Resolve+import           Puppet.Interpreter.Types+import           Puppet.Parser.Types+import           Puppet.PP++main :: IO ()+main = hspec spec++evalArgs :: [Expression] -> Either PrettyError Text+evalArgs = dummyEval . resolveValue . UFunctionCall "sprintf" . V.fromList+  >=> \pv -> case pv of+                PString s -> return s+                _ -> Left ("Expected a string, not " <> PrettyError (pretty pv))++checkSuccess :: [Expression] -> Text -> Expectation+checkSuccess args res =+  case evalArgs args of+    Left rr -> expectationFailure (show rr)+    Right res' -> res' `shouldBe` res+checkError :: [Expression] -> String -> Expectation+checkError args msg =+  case evalArgs args of+    Left rr -> show rr `shouldContain` msg+    Right r -> expectationFailure ("Should have errored, received this instead: " <> show r)++spec :: Spec+spec = do+    it "should fail with no argument" (checkError [] "Expects a string as its first argument")+    it "should succeed with one argument" (checkSuccess ["hello"] "hello") -- puppet sprintf accepts one arg+    it "should work with multiple arguments" (checkSuccess ["hello %s %s", "world", "!"] "hello world !")+    it "should work with one string argument" (checkSuccess ["hello %s", "world"] "hello world" )+    it "should work with one int argument" (checkSuccess ["hello %d", 10] "hello 10" )+    it "should fail if arg is not provided" (checkError ["hello %s"] "not enough arguments")+    it "should fail when a wrong format instruction is used" (checkError ["hello %d", "world"] "Don't know how to convert this to a number" )+    it "should fail when a wrong format instruction is used" (checkError ["hello %f", "world"] "Don't know how to convert this to a number" )+    it "should work with one int argument" (checkSuccess ["hello %f", 1.0] "hello 1.0" )+    it "should work with one int argument" (checkSuccess ["hello %.1f", 1.23] "hello 1.2" )+    it "should pad with zeroes" (checkSuccess ["hello %03d", 10] "hello 010")+    it "should pad with spaces" (checkSuccess ["hello % 3d", 10] "hello  10")+    it "should format integers" (checkSuccess ["%+05d", 23] "+0023")+    it "should format floats" (checkSuccess ["%+.2f", 2.7182818284590451] "+2.72")+    it "should format large floats" (pendingWith "Minor formatting difference" >> checkSuccess ["%+.2e", 27182818284590451] "+2.72e+16")+    it "should work with    " (checkSuccess ["%5d"   , 5] "    5")+    it "should work with   0" (checkSuccess ["%05d"  , 5] "00005")+    it "should work with  - " (checkSuccess ["%-5d"  , 5] "5    ")+    it "should work with  -0" (checkSuccess ["%-05d" , 5] "5    ")+    it "should work with +  " (checkSuccess ["%+5d"  , 5] "   +5")+    it "should work with + 0" (checkSuccess ["%+05d" , 5] "+0005")+    it "should work with +- " (checkSuccess ["%+-5d" , 5] "+5   ")+    it "should work with +-0" (checkSuccess ["%+-05d", 5] "+5   ")+    it "should perform more complex formatting" (pendingWith "# is not yet supported" >> checkSuccess [ "<%.8s:%#5o %#8X (%-8s)>", "overlongstring", 23, 48879, "foo" ] "<overlong:  027   0XBEEF (foo     )>")
tests/Spec.hs view
@@ -10,6 +10,7 @@ import qualified Function.JoinKeysToValuesSpec import qualified Function.DeleteAtSpec import qualified Interpreter.IfSpec+import qualified Function.SprintfSpec  main :: IO () main = hspec spec@@ -23,6 +24,7 @@     describe "If" Interpreter.IfSpec.spec   describe "Puppet functions" $ do     describe "The shellquote function" Function.ShellquoteSpec.spec+    describe "The sprintf function" Function.SprintfSpec.spec     describe "The each function" Function.EachSpec.spec   describe "stdlib functions" $ do     describe "The assert_private function" Function.AssertPrivateSpec.spec