deka-tests (empty) → 0.6.0.2
raw patch · 33 files changed
+2036/−0 lines, 33 filesdep +QuickCheckdep +basedep +bytestringsetup-changed
Dependencies added: QuickCheck, base, bytestring, containers, deka, parsec, pipes, quickpull, transformers
Files
- ChangeLog +40/−0
- LICENSE +68/−0
- README.md +63/−0
- Setup.hs +2/−0
- current-versions.txt +53/−0
- dectest/Arity.hs +131/−0
- dectest/Conditions.hs +50/−0
- dectest/Directives.hs +142/−0
- dectest/NumTests.hs +61/−0
- dectest/Operand.hs +67/−0
- dectest/Parse.hs +99/−0
- dectest/Parse/Tokenizer.hs +220/−0
- dectest/Parse/Tokens.hs +70/−0
- dectest/Result.hs +74/−0
- dectest/Runner.hs +164/−0
- dectest/Specials.hs +85/−0
- dectest/TestHelpers.hs +35/−0
- dectest/TestLog.hs +64/−0
- dectest/Types.hs +36/−0
- dectest/Util.hs +28/−0
- dectest/deka-dectest.hs +7/−0
- deka-tests.cabal +135/−0
- lib/Deka/Dec/Coarbitrary.hs +15/−0
- lib/Deka/Dec/Generators.hs +10/−0
- lib/Deka/Dec/Shrinkers.hs +13/−0
- lib/Deka/Native/Abstract/Coarbitrary.hs +72/−0
- lib/Deka/Native/Abstract/Generators.hs +53/−0
- lib/Deka/Native/Abstract/Shrinkers.hs +75/−0
- lib/Deka/Tests/Util.hs +11/−0
- minimum-versions.txt +50/−0
- native/Decrees.hs +15/−0
- native/Properties.hs +21/−0
- native/deka-native.hs +7/−0
+ ChangeLog view
@@ -0,0 +1,40 @@+version 0.6.0.2:++* changed build system to use cartel.++* moved tests to separate package, deka-tests++version 0.6.0.0:++* switch to mpdecimal for the underlying C library. This allows+higher limits on exponents and precision. All arithmetic is now+(practically speaking) arbitrary precision. This also allows more+operations, such as logarithms and all exponents. As a possible+disadvantage, this might be slower than decNumber (but I have not+benchmarked this.)++version 0.4.0.4:++* test with GHC 7.8.2++* bundle in decNumber for easier building.++version 0.4.0.2:++* remove version upper bounds++* lower version lower bounds to allow building with GHC 7.4++* add Sunlight tests++version 0.4.0.0:++* removed bindings-dsl; see comments in git log++* made Quad an instance of Eq and Ord++version 0.2.0.2:++* updated .travis.yml for decnumber dependency++* Removed spurious dependencies from .cabal file
+ LICENSE view
@@ -0,0 +1,68 @@+License for deka:++Copyright (c) 2014, Omari Norman++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Omari Norman nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.++Deka includes code from the decNumber C library. The license+for decNumber is as follows:++COPYRIGHT AND PERMISSION NOTICE++Copyright (c) 1995-2005 International Business Machines Corporation and others+All rights reserved.++Permission is hereby granted, free of charge, to any person obtaining a+copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, and/or sell copies of the Software, and to permit persons+to whom the Software is furnished to do so, provided that the above+copyright notice(s) and this permission notice appear in all copies of+the Software and that both the above copyright notice(s) and this+permission notice appear in supporting documentation.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL+INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING+FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,+NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.++Except as contained in this notice, the name of a copyright holder+shall not be used in advertising or otherwise to promote the sale, use+or other dealings in this Software without prior written authorization+of the copyright holder.++--------------------------------------------------------------------------------+All trademarks and registered trademarks mentioned herein are the property of their respective owners.
+ README.md view
@@ -0,0 +1,63 @@+# deka++deka provides correctly rounded decimal arithmetic for Haskell.++The core of deka is a binding to the C library mpdecimal. You need+to install mpdecimal; otherwise, your executables will not link.+mpdecimal is available here:++http://www.bytereef.org/mpdecimal/index.html++mpdecimal has also been packaged for some Linux distributions, such+as Debian (libmpdec-dev) and Arch (mpdecimal). deka has been tested+with mpdecimal version 2.4.0.++As the author of deka, I have no association with the author of+mpdecimal, and any errors in this library are mine and should be+reported to omari@smileystation.com or to the Github tracker at++http://www.github.com/massysett/deka++You will want to understand the General Decimal Arithmetic+Specification in order to fully understand deka. The specification+is at++http://speleotrove.com/decimal/decarith.html++and more about decimal arithmetic generally at++http://speleotrove.com/decimal/++## Dependencies++The main deka library depends only on `base`, `bytestring`, and+`parsec`, so it shouldn't be difficult to build. The tests use+[tasty](http://documentup.com/feuerbach/tasty) and+[QuickCheck](http://hackage.haskell.org/package/QuickCheck).++## Test status++deka is tested using the tests available on the General Decimal+Arithmetic website:++http://speleotrove.com/decimal/dectest.html++Some of these tests currently fail. The failures are in edge cases+that should not affect most usage. Diagnosing these failures is on+the TODO list.++## More documentation++Much more documentation is available in the Haddock comments in the+source files. There is also a file of examples to get you started.+It has copious comments. It is written in literate Haskell, so the+compiler keeps me honest with the example code. Unfortunately+Haddock does not play very nice with literate Haskell. However, the+file is easy to view on Github:++[Examples](lib/Data/Deka/Docs/Examples.lhs)++## License++deka is licensed under the BSD license, see the LICENSE file.+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ current-versions.txt view
@@ -0,0 +1,53 @@+This package was tested to work with these dependency+versions and compiler version.+These are the default versions fetched by cabal install.+Tested as of: 2014-07-16 13:56:43.863793 UTC+Path to compiler: ghc-7.8.3+Compiler description: 7.8.3++/opt/ghc/7.8.3/lib/ghc-7.8.3/package.conf.d:+ Cabal-1.18.1.3+ array-0.5.0.0+ base-4.7.0.1+ bin-package-db-0.0.0.0+ binary-0.7.1.0+ rts-1.0+ bytestring-0.10.4.0+ containers-0.5.5.1+ deepseq-1.3.0.2+ directory-1.2.1.0+ filepath-1.3.0.2+ (ghc-7.8.3)+ ghc-prim-0.3.1.0+ haskeline-0.7.1.2+ (haskell2010-1.1.2.0)+ (haskell98-2.0.0.3)+ hoopl-3.10.0.1+ hpc-0.6.0.1+ integer-gmp-0.5.1.0+ old-locale-1.0.0.6+ old-time-1.1.0.2+ pretty-1.1.1.1+ process-1.2.0.0+ template-haskell-2.9.0.0+ terminfo-0.4.0.0+ time-1.4.2+ transformers-0.3.0.0+ unix-2.7.0.1+ xhtml-3000.2.1++/home/massysett/deka/tests/sunlight-19453/db:+ QuickCheck-2.7.6+ deka-0.6.0.2+ deka-tests-0.6.0.2+ mmorph-1.0.3+ mtl-2.2.1+ parsec-3.1.5+ pipes-4.1.2+ primitive-0.5.3.0+ quickpull-0.2.0.0+ random-1.0.1.1+ text-1.1.1.3+ tf-random-0.5+ transformers-0.4.1.0+
+ dectest/Arity.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE OverloadedStrings #-}++-- | Use functions in here to test any testcase except toSci,+-- toEng, or apply. Those have special rules for operand parsing.+module Arity where++import TestLog+import qualified Data.ByteString.Char8 as BS8+import qualified Deka.Dec as D+import qualified Deka.Context as C+import Control.Monad+import Data.Monoid+import Operand+import Result+import Types+import Directives+import TestHelpers++type Unary = D.Dec -> C.Ctx D.Dec+type Binary = D.Dec -> D.Dec -> C.Ctx D.Dec+type Ternary = D.Dec -> D.Dec -> D.Dec -> C.Ctx D.Dec++unary :: Unary -> Test+unary f dirs ops rslt conds = runTestLog $ do+ ic <- parseDirectives dirs+ op <- case ops of+ x:[] -> operand ic x+ _ -> flunk $ "one operand expected, got " <>+ (BS8.pack . show . length $ ops)+ mayRslt <- result rslt+ let k = ic >> f op+ (r, fl) = C.runCtxStatus k+ testConditions conds fl+ testDec r mayRslt+ pass "conditions and result match targets"++binary :: Binary -> Test+binary f dirs ops rslt conds = runTestLog $ do+ ic <- parseDirectives dirs+ (opX, opY) <- case ops of+ x:y:[] -> do+ ox <- operand ic x+ oy <- operand ic y+ return (ox, oy)+ _ -> flunk $ "two operands expected, got " <>+ (BS8.pack . show . length $ ops)+ mayRslt <- result rslt+ let k = ic >> f opX opY+ (r, fl) = C.runCtxStatus k+ testConditions conds fl+ testDec r mayRslt+ pass "conditions and result match targets"++comparer :: (D.Dec -> D.Dec -> Ordering) -> Test+comparer f dirs ops rslt conds = runTestLog $ do+ when (not . null $ conds) . flunk $+ "comparer: conditions not null, which makes no sense "+ <> "for this kind of test."+ ic <- parseDirectives dirs+ (opX, opY) <- case ops of+ x:y:[] -> do+ ox <- operand ic x+ oy <- operand ic y+ return (ox, oy)+ _ -> flunk $ "two operands expected, got " <>+ (BS8.pack . show . length $ ops)+ rsltO <- resultOrd rslt+ let r = f opX opY+ if rsltO == r+ then pass "conditions and result match targets"+ else flunk $ "expected: " <> (BS8.pack . show $ rsltO)+ <> " got: " <> (BS8.pack . show $ r)++binaryTest :: (D.Dec -> D.Dec -> Bool) -> Test+binaryTest f dirs ops rslt conds = runTestLog $ do+ when (not . null $ conds) . flunk $+ "binaryTest: conditions not null, which makes no sense "+ <> "for this kind of test."+ ic <- parseDirectives dirs+ (opX, opY) <- case ops of+ x:y:[] -> do+ ox <- operand ic x+ oy <- operand ic y+ return (ox, oy)+ _ -> flunk $ "two operands expected, got " <>+ (BS8.pack . show . length $ ops)+ rsltB <- resultBool rslt+ let r = f opX opY+ if rsltB == r+ then pass "conditions and result match targets"+ else flunk $ "expected: " <> (BS8.pack . show $ rsltB)+ <> " got: " <> (BS8.pack . show $ r)+++decAndIntegral+ :: (D.Dec -> D.Signed -> C.Ctx D.Dec) -> Test++decAndIntegral f dirs ops rslt conds = runTestLog $ do+ ic <- parseDirectives dirs+ (opX, opY) <- case ops of+ x:y:[] -> do+ ox <- operand ic x+ oy <- operandIntegral y+ return (ox, oy)+ _ -> flunk $ "two operands expected, got " <>+ (BS8.pack . show . length $ ops)+ let k = ic >> f opX opY+ (r, fl) = C.runCtxStatus k+ mayRslt <- result rslt+ testConditions conds fl+ testDec r mayRslt+ pass "conditions and results match targets"++ternary :: Ternary -> Test+ternary f dirs ops rslt conds = runTestLog $ do+ ic <- parseDirectives dirs+ (opX, opY, opZ) <- case ops of+ x:y:z:[] -> do+ ox <- operand ic x+ oy <- operand ic y+ oz <- operand ic z+ return (ox, oy, oz)+ _ -> flunk $ "three operands expected, got " <>+ (BS8.pack . show . length $ ops)+ mayRslt <- result rslt+ let k = ic >> f opX opY opZ+ (r, fl) = C.runCtxStatus k+ testConditions conds fl+ testDec r mayRslt+ pass "conditions and result match targets"+
+ dectest/Conditions.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE OverloadedStrings #-}++module Conditions where++import qualified Data.ByteString.Char8 as BS8+import TestLog+import Data.String+import qualified Deka.Context as C+import Data.Monoid+import Data.Char (toLower)+import Data.List (sort)++-- | Parses and sorts a list of conditions.++parseConditions :: [BS8.ByteString] -> TestLog [C.Flag]+parseConditions = fmap sort . mapM parseCondition++parseCondition :: BS8.ByteString -> TestLog C.Flag+parseCondition str = case lookup s allConditions of+ Nothing -> flunk $ "could not parse condition: " <> str+ Just f -> tell ("parsed condition: " <> str) >> return f+ where+ s = map toLower . BS8.unpack $ str++allConditions :: IsString a => [(a, C.Flag)]+allConditions =+ [ ("clamped", C.clamped)+ , ("conversion_syntax", C.conversionSyntax)+ , ("division_by_zero", C.divisionByZero)+ , ("division_impossible", C.divisionImpossible)+ , ("division_undefined", C.divisionUndefined)+ , ("inexact", C.inexact)++ -- insufficient_storage not present in mpdecimal - not used in the+ -- tests+ -- , ("insufficient_storage", C.insufficientStorage)++ , ("invalid_context", C.invalidContext)+ , ("invalid_operation", C.invalidOperation)++ -- lost_digits - not present in mpdecimal - but also not in the+ -- decNumber tests+ --, ("lost_digits", C.lostDigits)++ , ("overflow", C.overflow)+ , ("rounded", C.rounded)+ , ("subnormal", C.subnormal)+ , ("underflow", C.underflow)+ ]+
+ dectest/Directives.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE OverloadedStrings #-}+module Directives where++import qualified Parse as P+import qualified Deka.Context as C+import Data.Char (toLower)+import qualified Data.ByteString.Char8 as BS8+import Data.Monoid+import TestLog+import Util+import Data.String+import qualified Data.Sequence as S+import Data.Sequence(ViewR(..))++data Directives = Directives+ { dsPrecision :: Maybe P.Value+ , dsRounding :: Maybe P.Value+ , dsEmax :: Maybe P.Value+ , dsEmin :: Maybe P.Value+ , dsExtended :: Maybe P.Value+ , dsClamp :: Maybe P.Value+ , dsVersion :: Maybe P.Value+ } deriving Show++type Getter = Directives -> Maybe P.Value+type Setter = P.Value -> Directives -> Directives++dirFields+ :: IsString a+ => [(a, (Getter, Setter))]+dirFields =+ [ ("precision", (dsPrecision, \v d -> d { dsPrecision = Just v }))+ , ("rounding", (dsRounding, \v d -> d { dsRounding = Just v }))+ , ("maxexponent", (dsEmax, \v d -> d { dsEmax = Just v }))+ , ("minexponent", (dsEmin, \v d -> d { dsEmin = Just v }))+ , ("extended", (dsExtended, \v d -> d { dsExtended = Just v }))+ , ("clamp", (dsClamp, \v d -> d { dsClamp = Just v }))+ , ("version", (dsVersion, \v d -> d { dsVersion = Just v }))+ ]++emptyDirectives :: Directives+emptyDirectives = Directives Nothing Nothing Nothing Nothing+ Nothing Nothing Nothing++plusDirective+ :: (P.Keyword, P.Value)+ -> Directives+ -> TestLog Directives+plusDirective (kw, vl) d = case lookup k dirFields of+ Nothing -> flunk $ "could not find directive: " <> BS8.pack k+ Just p -> add p+ where+ (k, v) = (map toLower . BS8.unpack . P.unKeyword $ kw,+ P.unValue vl)+ add (get, set) = case get d of+ Nothing -> do+ tell $ "using directive " <> BS8.pack k <> " with value " <> v+ return $ set vl d+ Just _ -> do+ tell $ "ignoring old directive " <> BS8.pack k+ <> " with value " <> v+ return d++plusDirectives+ :: S.Seq (P.Keyword, P.Value)+ -> TestLog Directives+plusDirectives = go emptyDirectives+ where+ go ds sq = case S.viewr sq of+ EmptyR -> return ds+ sq' :> p -> do+ ds' <- plusDirective p ds+ go ds' sq'++directivesToCtx :: Directives -> TestLog (C.Ctx ())+directivesToCtx ds = do++ rnd <- case dsRounding ds of+ Nothing -> return (return ())+ Just (P.Value r) -> do+ let dflt = flunk $ "could not set rounding: " <> r+ mayRnd = lookup r allRounds+ rnd <- maybe dflt return mayRnd+ tell $ "setting rounding to " <> r+ return (C.setRound rnd)++ clmp <- case dsClamp ds of+ Nothing -> return (return ())+ Just (P.Value r) -> case readNumberBS r >>= parseBool of+ Nothing -> flunk $ "could not set clamp: " <> r+ Just p -> do+ tell $ "setting clamp to " <> BS8.pack (show p)+ return (C.setClamp p)++ tri <- do+ pcsn <- case dsPrecision ds of+ Nothing -> flunk "precision not present in directives"+ Just (P.Value r) -> case readNumberBS r >>= C.precision of+ Nothing -> flunk $ "could not set precision: " <> r+ Just p -> do+ tell $ "using for precision: " <> r+ return p++ emx <- case dsEmax ds of+ Nothing -> flunk "emax not present in directives"+ Just (P.Value r) -> case readNumberBS r >>= C.emax of+ Nothing -> flunk $ "could not set emax: " <> r+ Just x -> do+ tell $ "using for Emax: " <> r+ return x++ emn <- case dsEmin ds of+ Nothing -> flunk "emin not present in directives"+ Just (P.Value r) -> case readNumberBS r >>= C.emin of+ Nothing -> flunk $ "could not set emin: " <> r+ Just x -> do+ tell $ "using for Emin: " <> r+ return x++ case C.trio pcsn emx emn of+ Nothing -> flunk $ "failed to set trio"+ Just t -> return $ C.setTrio t++ return $ rnd >> clmp >> tri++parseDirectives+ :: S.Seq (P.Keyword, P.Value)+ -> TestLog (C.Ctx ())+parseDirectives sq = plusDirectives sq >>= directivesToCtx++allRounds :: IsString a => [(a, C.Round)]+allRounds =+ [ ("ceiling", C.roundCeiling)+ , ("up", C.roundUp)+ , ("half_up", C.roundHalfUp)+ , ("half_even", C.roundHalfEven)+ , ("half_down", C.roundHalfDown)+ , ("down", C.roundDown)+ , ("floor", C.roundFloor)+ , ("05up", C.round05Up)+ ]+
+ dectest/NumTests.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE OverloadedStrings, NoImplicitPrelude #-}++module NumTests where++import Arity+import Types+import Specials+import Deka.Dec+import qualified Data.ByteString.Char8 as BS8++testLookups :: [(BS8.ByteString, Test)]+testLookups =+ [ ("abs", unary abs)+ , ("add", binary add)+ , ("and", binary and)+ , ("apply", apply)+ -- skip: canonical+ , ("class", decClass)+ , ("compare", binary compare)+ , ("comparesig", binary compareSignal)+ , ("comparetotal", comparer compareTotal)+ , ("comparetotalmag", comparer compareTotalMag)+ -- skip: copy, copyabs, copynegate, copysign+ , ("divide", binary divide)+ , ("divideint", binary divideInteger)+ , ("exp", unary exp)+ , ("fma", ternary fma)+ , ("invert", unary invert)+ , ("ln", unary ln)+ , ("log10", unary log10)+ , ("logb", unary logB)+ , ("max", binary max)+ , ("min", binary min)+ , ("maxmag", binary maxMag)+ , ("minmag", binary minMag)+ , ("minus", unary minus)+ , ("multiply", binary multiply)+ , ("nextminus", unary nextMinus)+ , ("nextplus", unary nextPlus)+ , ("nexttoward", binary nextToward)+ , ("or", binary or)+ , ("plus", unary plus)+ , ("power", binary power)+ , ("quantize", binary quantize)+ , ("reduce", unary reduce)+ , ("remainder", binary remainder)+ , ("remaindernear", binary remainderNear)+ , ("rescale", decAndIntegral rescale)+ , ("rotate", binary rotate)+ , ("samequantum", binaryTest sameQuantum)+ , ("scaleb", binary scaleB)+ , ("shift", binary shift)+ , ("squareroot", unary squareRoot)+ , ("subtract", binary subtract)+ , ("toEng", toEng)+ , ("tointegral", unary toIntegralValue)+ , ("toIntegralx", unary toIntegralExact)+ , ("toSci", toSci)+ -- skip: trim+ , ("xor", binary xor)+ ]
+ dectest/Operand.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE OverloadedStrings #-}+module Operand where++import TestLog+import qualified Data.ByteString.Char8 as BS8+import qualified Deka.Dec as D+import qualified Deka.Context as C+import Data.Monoid+import Util++-- | Parses an operand. Do not use this function for @toSci@,+-- @toEng@, or @apply@ as those have special rules for handling the+-- context.+--+-- Bypasses if the operand contains any octothorpe.++operand+ :: C.Ctx ()+ -- ^ Initial context to use+ -> BS8.ByteString+ -- ^ Parse this token+ -> TestLog D.Dec+operand ic bs+ | '#' `BS8.elem` bs =+ bypass $ "operand contains an octothorpe: " <> bs+ | otherwise = do+ tell $ "parsing operand token: " <> bs+ let k = do+ ic+ p <- fmap C.unPrecision C.setMaxPrecision+ s <- D.fromByteString bs+ return (p, s)+ let (p, r) = C.runCtx k+ tell $ "operand parse result: " <> D.toByteString r+ <> " parsed at precision: " <> BS8.pack (show p)+ return r++-- | Parses an operand into a context. Use this function for+-- @toSci@, @toEng@, and @apply@. Bypasses if the operand contains+-- any octothorpe.+operandSciEngAp+ :: C.Ctx ()+ -- ^ Initial context to use+ -> BS8.ByteString+ -- ^ Parse this token+ -> TestLog (C.Ctx D.Dec)+operandSciEngAp ic bs+ | '#' `BS8.elem` bs =+ bypass $ "operand contains an octothorpe: " <> bs+ | otherwise = do+ tell $ "parsing operand token: " <> bs+ let k = ic >> D.fromByteString bs+ return k++-- | Parses a signed integral operand.+operandIntegral+ :: BS8.ByteString+ -> TestLog D.Signed+operandIntegral bs+ | '#' `BS8.elem` bs =+ bypass $ "operand contains an octothorpe: " <> bs++ | otherwise = case readNumberBS bs of+ Nothing -> flunk $ "could not parse integral operand: " <> bs+ Just r -> do+ tell $ "parsed integral operand: " <> bs+ return r
+ dectest/Parse.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE OverloadedStrings #-}+module Parse where++import Parse.Tokens+import qualified Data.ByteString.Char8 as BS8++-- | Remove the comments from a line of tokens. Any unquoted token that+-- starts with two dashes is removed. Also, any token that comes+-- after such a token is also removed.+removeComments :: [Token] -> [Token]+removeComments toks = go toks+ where+ go [] = []+ go (t:ts)+ | quoted t = t : go ts+ | BS8.take 2 (unToken t) == "--" = []+ | otherwise = t : go ts++newtype Keyword = Keyword { unKeyword :: BS8.ByteString }+ deriving (Eq, Show)++newtype Value = Value { unValue :: BS8.ByteString }+ deriving (Eq, Show)++data TestSpec = TestSpec+ { testId :: BS8.ByteString+ , testOperation :: BS8.ByteString+ , testOperands :: [BS8.ByteString]+ , testResult :: BS8.ByteString+ , testConditions :: [BS8.ByteString]+ } deriving Show++data Instruction+ = Blank+ | Directive Keyword Value+ | Test TestSpec+ deriving Show++data File = File+ { fileName :: BS8.ByteString+ , fileContents :: [Either File Instruction]+ } deriving Show++directive :: [Token] -> (Keyword, Value)+directive ts = case ts of+ x:y:[] -> ( Keyword . BS8.init . unToken $ x,+ Value (unToken y) )+ _ -> error "directive: bad token count"++lineToContent :: [Token] -> IO (Either File Instruction)+lineToContent ts+ | null ts = return . Right $ Blank+ | length ts == 2 && kw == "dectest" = fmap Left $ parseFile fn+ | length ts == 2 = return . Right $ Directive akw avl+ | otherwise = return . Right . Test . mkTestSpec $ ts+ where+ (akw@(Keyword kw), avl@(Value val)) = directive ts+ fn = val `BS8.append` ".decTest"+ +mkTestSpec :: [Token] -> TestSpec+mkTestSpec ts+ | length ts < 5 = error "mkTestSpec: list too short"+ | otherwise = TestSpec+ { testId = unToken . head $ ts+ , testOperation = unToken . head . drop 1 $ ts+ , testOperands = map unToken+ . takeWhile (not . resultsIn)+ . drop 2+ $ ts+ , testResult = unToken+ . safeHead+ . drop 1+ . dropWhile (not . resultsIn)+ . drop 2+ $ ts+ , testConditions = map unToken+ . drop 2+ . dropWhile (not . resultsIn)+ . drop 2+ $ ts+ }+ where+ resultsIn t = unToken t == "->" && not (quoted t)+ safeHead x = case x of+ [] -> error "mkTestSpec: list too short"+ y:_ -> y++rawToContent :: Raw -> IO [Either File Instruction]+rawToContent+ = mapM lineToContent+ . map removeComments+ . map processLine+ . splitLines++parseFile :: BS8.ByteString -> IO File+parseFile fn = do+ rw <- raw (BS8.unpack fn)+ ctnt <- rawToContent rw+ return $ File fn ctnt
+ dectest/Parse/Tokenizer.hs view
@@ -0,0 +1,220 @@+{-# LANGUAGE OverloadedStrings #-}+-- | A finite state machine to break a single line of text into+-- tokens.+--+-- The machine has three main states, some of which have sub-states:+--+-- * inside of a token. Here, the machine can be in a @plain@ (that+-- is, unquoted) word, or it can be inside of a quoted word. When+-- inside of a quoted word, if a closing quotation mark is seen, the+-- machine transitions to being in a @pending@ state. The next+-- character determines what happens next: if it's an identical+-- quotation mark, then a single quotation mark is added to the+-- current token, and the machine stays in a quoted word state. If+-- the character is anything else, the machine outputs the previous+-- token and begins a new one.+--+-- * between tokens.+--+-- * in a comment. Before a token is output, the tokenizer examines+-- it to determine whether it is both (1) unquoted, and (2) begins+-- with two dashes. If so, it's a comment token. The token is not+-- output, and the machine shifts to being in a comment state, which+-- swallows all remaining characters that are input.+--+-- To use the tokenizer:+--+-- * turn it on with 'start'.+--+-- * Feed it characters from the line using 'feed'. Occasionally+-- this will output a single 'Token'. It always outputs a new+-- 'Tokenizer' that will accept further characters.+--+-- * when done, be sure to turn it off with 'finish'. This will+-- eject any characters in the tokenizer that have not been spit out+-- yet.++module Parse.Tokenizer+ ( Tokenizer+ , Token(..)+ , start -- :: Tokenizer+ , feed -- :: Char -> Tokenizer -> (Tokenizer, Maybe Token)+ , finish -- :: Tokenizer -> Maybe Token+ ) where++import qualified Data.ByteString.Char8 as BS8++-- | Set to True when a single close quote character has been+-- parsed. Since double quote characters indicates an enclosed+-- quote, we don't know until the next character whether to close+-- the quote or just include a quote in the token.+type Pending = Bool++data QuoteType = Single | Double+ deriving (Eq, Show)++toQuot :: QuoteType -> Char+toQuot Single = '\''+toQuot Double = '"'++data InTok+ = PlainWord+ | Quoted QuoteType Pending+ deriving Show++data Accepting+ = InTok InTok BS8.ByteString+ | BetweenToks+ deriving Show++-- The Tokenizer holds a Maybe Accepting, which is Nothing if the+-- Tokenizer is no longer accepting new characters because it is in+-- an comment, or is Just if accepting new characters.++data Tokenizer = Tokenizer (Maybe Accepting)+ deriving Show++data Token = Token+ { unToken :: BS8.ByteString+ , quoted :: Bool+ } deriving (Eq, Ord, Show)++-- | Turns the tokenizer on.++start :: Tokenizer+start = Tokenizer . Just $ BetweenToks++-- | Feeds a character to the tokenizer.+feed :: Char -> Tokenizer -> (Tokenizer, Maybe Token)+feed c (Tokenizer t) = case t of+ Nothing -> (Tokenizer Nothing, Nothing)++ Just a -> case a of+ InTok tokType curr -> case tokType of++ PlainWord ->+ let mnt q = mint (InTok (Quoted q False) BS8.empty)+ curr False+ in case c of+ '"' -> mnt Double+ '\'' -> mnt Single+ ' ' -> mint BetweenToks curr False+ _ -> stayInTok c curr PlainWord++ Quoted qType pend+ | pend -> acceptQuotedWithPending c curr qType+ | otherwise -> acceptQuotedNoPending c curr qType++ BetweenToks -> case c of+ ' ' -> (Tokenizer (Just BetweenToks), Nothing)+ '"' -> newTok (Left Double)+ '\'' -> newTok (Left Single)+ _ -> newTok (Right c)++-- | Creates a new token, if called for. Checks to see if the token+-- that would be produced would be a comment token. If so, do not+-- emit a token, and return a Tokenizer that does not accept new+-- characters. Otherwise, emit the token, and return an accepting+-- Tokenizer.++mint+ :: Accepting+ -- ^ If a token is produced because the Tokenizer did not output a+ -- comment, this becomes the new state of the Tokenizer.+ -> BS8.ByteString+ -- ^ What to output+ -> Bool+ -- ^ True if the token is quoted; False if not+ -> (Tokenizer, Maybe Token)+mint a o q+ | q = nt+ | otherwise =+ if BS8.take 2 o == "--"+ then (Tokenizer Nothing, Nothing)+ else nt+ where+ nt = (Tokenizer (Just a), Just (Token o q))++-- | Accepts a new character without producing a token.++stayInTok+ :: Char+ -- ^ Character to add to the storehouse of characters in the+ -- current token+ -> BS8.ByteString+ -- ^ Storehouse of current characters+ -> InTok+ -> (Tokenizer, Maybe Token)+ -- ^ snd is always False, fst is a Tokenizer that is InTok+stayInTok c s i = (tzr, Nothing)+ where+ tzr = Tokenizer (Just ac)+ ac = InTok i (s `BS8.snoc` c)++-- | Accepts a character while within a quote and there is not a+-- pending character.++acceptQuotedNoPending+ :: Char+ -- ^ Character to accept+ -> BS8.ByteString+ -- ^ Storehouse of current characters+ -> QuoteType+ -- ^ Type of quote we're currently inside of+ -> (Tokenizer, Maybe Token)+ -- ^ snd is always False, fst is a Tokenizer that is InTok+acceptQuotedNoPending c s q = (tzr, Nothing)+ where+ tzr = Tokenizer (Just ac)+ ac = InTok (Quoted q pnd) s'+ (s', pnd)+ | c == toQuot q = (s, True)+ | otherwise = (s `BS8.snoc` c, False)++-- | Accepts a character while within a quote and there is a pending+-- character.+acceptQuotedWithPending+ :: Char+ -- ^ Character to accept+ -> BS8.ByteString+ -- ^ Storehouse of current characters+ -> QuoteType+ -- ^ Type of quote we're currently inside of+ -> (Tokenizer, Maybe Token)+ -- ^ snd is always False, fst is a Tokenizer that is InTok+acceptQuotedWithPending c s q+ | c == toQuot q = stayInTok c s (Quoted q False)+ | otherwise = case c of+ ' ' -> mnt BetweenToks+ '"' -> mnt (InTok (Quoted Double False) BS8.empty)+ '\'' -> mnt (InTok (Quoted Single False) BS8.empty)+ _ -> mnt (InTok PlainWord (BS8.singleton c))+ where+ mnt a = mint a s True++-- | Starts a new token.+newTok+ :: Either QuoteType Char+ -- ^ If Left, start a new quoted token. If Right, start a new+ -- plain token with the given character.+ -> (Tokenizer, Maybe Token)+ -- ^ snd is always False, fst if an accepting Tokenizer+newTok e = (Tokenizer (Just (InTok i s)), Nothing)+ where+ (i, s) = case e of+ Left q -> (Quoted q False, BS8.empty)+ Right c -> (PlainWord, BS8.singleton c)++-- | Finishes any remaining tokens in the machine. Applies 'error'+-- if there is a quoted token in the machine that has not been+-- finished yet.+finish :: Tokenizer -> Maybe Token+finish (Tokenizer ma) = case ma of+ Nothing -> Nothing+ Just a -> case a of+ BetweenToks -> Nothing+ InTok i s -> case i of+ PlainWord -> Just (Token s False)+ Quoted _ pnd+ | pnd -> Just (Token s True)+ | otherwise -> error "finish: quoted token still in machine"
+ dectest/Parse/Tokens.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE OverloadedStrings #-}+module Parse.Tokens+ ( Raw(unRaw)+ , Line(..)+ , T.Token(..)+ , splitLines+ , processLine+ , raw+ ) where++import qualified Data.ByteString.Char8 as BS8+import qualified Parse.Tokenizer as T++-- | Raw file, parsed in from disk.+newtype Raw = Raw { unRaw :: BS8.ByteString }+ deriving Show++raw :: FilePath -> IO Raw+raw = fmap Raw . BS8.readFile++-- | A line from a Raw. Does not contain any newlines.+newtype Line = Line { unLine :: BS8.ByteString }+ deriving Show++-- | Splits a Raw into a list of Line. First, eliminates any+-- MS-DOS carriage returns (ASCII character 0d). Then, uses the+-- ByteString lines function.+splitLines :: Raw -> [Line]+splitLines = map Line . BS8.lines . BS8.filter (/= '\r') . unRaw++--+-- Parsing a Line into Tokens+--++data HighLevelLine = HighLevelLine+ { llState :: T.Tokenizer+ , llToks :: [T.Token]+ } deriving Show++highLevelProc :: HighLevelLine -> Char -> HighLevelLine+highLevelProc h c = HighLevelLine l' ts'+ where+ (l', mayT) = T.feed c (llState h)+ ts' = case mayT of+ Nothing -> llToks h+ Just t' -> t' : llToks h++eject :: HighLevelLine -> [T.Token]+eject h = reverse toks+ where+ toks = case T.finish (llState h) of+ Nothing -> llToks h+ Just t -> t : llToks h++processLine :: Line -> [T.Token]+processLine+ = eject+ . BS8.foldl highLevelProc z+ . unLine+ where+ z = HighLevelLine T.start []++_testProcessLine :: String -> IO ()+_testProcessLine+ = mapM_ BS8.putStrLn+ . map T.unToken+ . processLine+ . Line+ . BS8.pack+
+ dectest/Result.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE OverloadedStrings #-}+module Result where++import TestLog+import qualified Data.ByteString.Char8 as BS8+import qualified Deka.Dec as D+import qualified Deka.Context as C+import Data.Monoid++-- | Parses a result token. Returns Nothing if the result token was+-- a question mark, as this indicates that the result is undefined.+-- Bypasses if the token contains an octothorpe. Otherwise, returns+-- the Dec.+--+-- Probably the easiest way to do the comparison is going to be to+-- take the output of the test function, apply @toByteString@ to it,+-- and then test that for equality with the result of applying+-- @toByteString@ to the result returned from here.++result+ :: BS8.ByteString+ -> TestLog (Maybe D.Dec)+result bs+ | '#' `BS8.elem` bs =+ bypass $ "result contains an octothorpe: " <> bs++ | '?' `BS8.elem` bs = do+ tell "result token contains a question mark"+ return Nothing++ | otherwise = do+ tell $ "parsing result token: " <> bs+ let r = C.runCtx (D.fromByteString bs)+ tell $ "result parse result: " <> D.toByteString r+ return . Just $ r++-- | Parses result token where the function is expecting an+-- Ordering.++resultOrd+ :: BS8.ByteString+ -> TestLog Ordering+resultOrd bs+ | '#' `BS8.elem` bs =+ bypass $ "result contains an octothorpe: " <> bs++ | '?' `BS8.elem` bs =+ flunk "result token contains a question mark"++ | otherwise = do+ tell $ "parsing result token: " <> bs+ case bs of+ "-1" -> return LT+ "0" -> return EQ+ "1" -> return GT+ _ -> flunk $ "unrecognized Ord result: " <> bs++resultBool+ :: BS8.ByteString+ -> TestLog Bool+resultBool bs+ | '#' `BS8.elem` bs =+ bypass $ "result contains an octothorpe: " <> bs++ | '?' `BS8.elem` bs =+ flunk "result token contains a question mark"++ | otherwise = do+ tell $ "parsing result token: " <> bs+ case bs of+ "1" -> return True+ "0" -> return False+ _ -> flunk $ "unrecognized Bool result: " <> bs+
+ dectest/Runner.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE OverloadedStrings, BangPatterns #-}+module Runner (runAndExit) where++import qualified Parse as P+import qualified Data.ByteString.Char8 as BS8+import Data.Monoid+import Data.List (intersperse)+import System.Exit+import Data.Foldable (toList)+import qualified Data.Sequence as S+import Data.Sequence ((|>))+import Pipes+import Pipes.Prelude (fold)+import NumTests (testLookups)+import Types++produceFile :: MonadIO m => Pipe BS8.ByteString P.File m ()+produceFile = do+ bs <- await+ f <- liftIO $ P.parseFile bs+ yield f++order :: Monad m => Pipe P.File Order m ()+order = do+ f <- await+ go S.empty (P.fileContents f)+ where+ go !sq ls = case ls of+ [] -> return ()+ x:xs -> case x of+ Left inner -> go S.empty (P.fileContents inner)+ Right i -> case i of+ P.Blank -> go sq xs+ P.Directive k v ->+ let sq' = sq |> (k, v)+ in go sq' xs+ P.Test spec -> do+ yield (Order sq spec)+ go sq xs++output :: Monad m => Pipe Order TestOutput m ()+output = do+ o <- await+ case lookup (P.testOperation . ordSpec $ o) testLookups of+ Nothing -> yield (noOperation . ordSpec $ o)+ Just t -> yield (runTest o t)+ output+++printTest+ :: MonadIO m+ => Pipe TestOutput (Maybe Bool) m ()+printTest = do+ o <- await+ liftIO . BS8.putStr . showResult $ o+ yield (outResult o)+ printTest++pipeline :: MonadIO m => Pipe BS8.ByteString (Maybe Bool) m ()+pipeline =+ produceFile+ >-> order+ >-> output+ >-> printTest++totals :: Monad m => Producer (Maybe Bool) m () -> m Counts+totals = fold tally mempty id++runAndExit :: [String] -> IO ()+runAndExit ss = do+ let bs = map BS8.pack ss+ pip = each bs >-> pipeline+ tot <- totals pip+ putStr . showCounts $ tot+ exit tot++noOperation :: P.TestSpec -> TestOutput+noOperation ts = TestOutput+ { outSpec = ts+ , outResult = Nothing+ , outLog = S.singleton "no matching operation; skipping"+ }++data Order = Order+ { _ordDirectives :: S.Seq (P.Keyword, P.Value)+ , ordSpec :: P.TestSpec+ }++runTest+ :: Order+ -> Test+ -> TestOutput+runTest (Order ds ts) t = TestOutput+ { outSpec = ts+ , outResult = r+ , outLog = l+ }+ where+ (r, l) = t ds (P.testOperands ts) (P.testResult ts)+ (P.testConditions ts)++data TestOutput = TestOutput+ { outSpec :: P.TestSpec+ , outResult :: Maybe Bool+ , outLog :: S.Seq BS8.ByteString+ }++data Counts = Counts+ { _nPass :: !Int+ , nFail :: !Int+ , _nSkip :: !Int+ } deriving Show++tally :: Counts -> Maybe Bool -> Counts+tally (Counts p f s) mb = case mb of+ Nothing -> Counts p f (s + 1)+ Just True -> Counts (p + 1) f s+ Just False -> Counts p (f + 1) s++instance Monoid Counts where+ mempty = Counts 0 0 0+ (Counts x1 y1 z1) `mappend` (Counts x2 y2 z2) =+ Counts (x1 + x2) (y1 + y2) (z1 + z2)++showCounts :: Counts -> String+showCounts (Counts p f s) = unlines+ [ "pass: " ++ show p+ , "fail: " ++ show f+ , "skip: " ++ show s+ , "total: " ++ show (p + f + s)+ ]++exit :: Counts -> IO ()+exit c+ | nFail c > 0 = exitFailure+ | otherwise = exitSuccess++showResult+ :: TestOutput+ -> BS8.ByteString+showResult (TestOutput t r sq) = BS8.unlines $ l1:lr+ where+ l1 = pf <+> showSpec t+ pf = case r of+ Nothing -> "[skip]"+ Just True -> "[pass]"+ Just False -> "[FAIL]"+ lr = map (" " <>) . toList $ sq++showSpec :: P.TestSpec -> BS8.ByteString+showSpec t =+ P.testId t+ <+> P.testOperation t+ <+> BS8.concat (intersperse " " . P.testOperands $ t)+ <+> "->"+ <+> P.testResult t+ <+> BS8.concat (intersperse " " . P.testConditions $ t)++(<+>) :: BS8.ByteString -> BS8.ByteString -> BS8.ByteString+l <+> r+ | BS8.null l && BS8.null r = ""+ | BS8.null l || BS8.null r = l <> r+ | otherwise = l <> " " <> r+
+ dectest/Specials.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE OverloadedStrings #-}+-- | Handling special cases: toSci, toEng, apply, class.++module Specials where++import qualified Data.ByteString.Char8 as BS8+import TestLog+import qualified Deka.Context as C+import qualified Deka.Dec as D+import Types+import Operand+import Directives+import TestHelpers+import Data.Monoid+import Result+import Data.Char (toLower)+import Control.Arrow (first)++toSciOrEng :: (D.Dec -> BS8.ByteString) -> Test+toSciOrEng fn dirs opS rsS cdS = runTestLog $ do+ ic <- parseDirectives dirs+ getOp <- case opS of+ x:[] -> operandSciEngAp ic x+ _ -> flunk $ "expected 1 operand, got " <>+ (BS8.pack . show . length $ opS)+ let (rConv, fl) = C.runCtxStatus getOp+ r = fn rConv+ testConditions cdS fl+ if r == rsS+ then pass $ "string " <> r <> " matches expected result"+ else flunk $ "string " <> r <> " does not match expected "+ <> "result of " <> rsS++toSci :: Test+toSci = toSciOrEng D.toByteString++toEng :: Test+toEng = toSciOrEng D.toEngByteString++apply :: Test+apply dirs opS rsS cdS = runTestLog $ do+ ic <- parseDirectives dirs+ getOp <- case opS of+ x:[] -> operandSciEngAp ic x+ _ -> flunk $ "expected 1 operand, got " <>+ (BS8.pack . show . length $ opS)+ let k = getOp >>= D.plus+ (rConv, fl) = C.runCtxStatus k+ r = D.toByteString rConv+ tgt <- result rsS+ testConditions cdS fl+ case tgt of+ Nothing -> flunk $ "target result of apply is undefined; "+ <> "this makes no sense"+ Just t+ | D.toByteString t == r -> pass "result is as expected"+ | otherwise -> flunk $ "result of " <> r+ <> " does not match expected result of "+ <> D.toByteString t++parseClass :: BS8.ByteString -> TestLog D.Class+parseClass bs = case lookup lwr ls of+ Nothing -> flunk $ "could not parse class: " <> bs+ Just r -> do+ tell $ "parsed class: " <> bs+ return r+ where+ lwr = map toLower . BS8.unpack $ bs+ ls = map (first (map toLower)) D.strToClass++decClass :: Test+decClass dirs opS rsS cdS = runTestLog $ do+ ic <- parseDirectives dirs+ op <- case opS of+ x:[] -> operand ic x+ _ -> flunk $ "expected 1 operand, got " <>+ (BS8.pack . show . length $ opS)+ tgt <- parseClass rsS+ let k = ic >> D.numClass op+ (r, fl) = C.runCtxStatus k+ testConditions cdS fl+ if r == tgt+ then pass "result is as expected"+ else flunk $ "unexpected class: result is " <>+ (BS8.pack . show $ r)
+ dectest/TestHelpers.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE OverloadedStrings #-}+module TestHelpers where++import TestLog+import qualified Data.ByteString.Char8 as BS8+import qualified Deka.Dec as D+import qualified Deka.Context as C+import Data.Monoid+import Conditions+import Data.List (sort)++testConditions+ :: [BS8.ByteString]+ -- ^ List of conditions+ -> C.Flags+ -- ^ Actual conditions from test+ -> TestLog ()+testConditions bs flgs = do+ cs <- parseConditions bs+ let fs = sort . C.unpackFlags $ flgs+ if sort fs == sort cs+ then tell "conditions are as expected"+ else flunk $ "conditions not as expected: " <>+ (BS8.pack . show $ fs)++testDec :: D.Dec -> Maybe D.Dec -> TestLog ()+testDec x y = case y of+ Nothing -> tell "target result is undefined" >> return ()+ Just r ->+ let sx = D.toByteString x+ sr = D.toByteString r+ res | sx == sr = tell "result is as expected"+ | otherwise =+ flunk $ "output: " <> sx <> " expected result: " <> sr+ in res
+ dectest/TestLog.hs view
@@ -0,0 +1,64 @@+module TestLog+ ( TestLog+ , tell+ , bypass+ , flunk+ , Done+ , pass+ , runTestLog+ ) where++import qualified Data.Sequence as S+import Control.Applicative+import Control.Monad+import qualified Data.ByteString.Char8 as BS8+import Data.Monoid++data State a+ = Good a+ | Failed+ | Bypass+ deriving Show++newtype TestLog a = TestLog+ { unTestLog :: (S.Seq BS8.ByteString, State a) }+ deriving Show++instance Monad TestLog where+ return a = TestLog (S.empty, Good a)+ (TestLog (ss, st)) >>= f = TestLog $ case st of+ Failed -> (ss, Failed)+ Bypass -> (ss, Bypass)+ Good a -> let (ss', st') = unTestLog $ f a in+ (ss <> ss', st')++instance Applicative TestLog where+ pure = return+ (<*>) = ap++instance Functor TestLog where+ fmap = liftM++tell :: BS8.ByteString -> TestLog ()+tell bs = TestLog (S.singleton bs, Good ())++bypass :: BS8.ByteString -> TestLog a+bypass bs = TestLog (S.singleton bs, Bypass)++flunk :: BS8.ByteString -> TestLog a+flunk bs = TestLog (S.singleton bs, Failed)++data Done = Done+ deriving Show++pass :: BS8.ByteString -> TestLog Done+pass bs = TestLog (S.singleton bs, Good Done)++runTestLog :: TestLog Done -> (Maybe Bool, S.Seq BS8.ByteString)+runTestLog (TestLog (ss, st)) = (r, ss)+ where+ r = case st of+ Good Done -> Just True+ Failed -> Just False+ Bypass -> Nothing+
+ dectest/Types.hs view
@@ -0,0 +1,36 @@+module Types where++import qualified Data.ByteString.Char8 as BS8+import qualified Parse as P+import Data.Sequence++-- | All tests must conform to this interface.++type Test++ = Seq (P.Keyword, P.Value)+ -- ^ All directives in force when the test is run. Oldest+ -- directives are on the left side of the Seq; newest ones, on+ -- the right side.++ -> [BS8.ByteString]+ -- ^ Operands++ -> BS8.ByteString+ -- ^ Result++ -> [BS8.ByteString]+ -- ^ Conditions. These are already sorted.++ -> (Maybe Bool, Seq BS8.ByteString)+ -- ^ The test returns a Maybe Bool to indicate the test result.+ -- Passage is Just True, failure is Just False, skip is Nothing.+ -- Use Nothing for inputs that the implementation does not+ -- support, such as null operands. For all other failures+ -- (including programmer errors or test configuration errors such+ -- as mismatches in the number of operands), use Just False.+ --+ -- Also returned is a list of ByteString. These are narratives.+ -- Put one pice of data in each line. For example you might+ -- include information on how the operands parsed, how the result+ -- parsed, what information the test function returned, etc.
+ dectest/Util.hs view
@@ -0,0 +1,28 @@+module Util where++import qualified Data.ByteString.Char8 as BS8++switch :: a -> [(Bool, a)] -> a+switch dflt [] = dflt+switch dflt ((bl, a):xs)+ | bl = a+ | otherwise = switch dflt xs++parseBool :: Int -> Maybe Bool+parseBool i+ | i == 0 = Just False+ | i == 1 = Just True+ | otherwise = Nothing++safeRead :: Read a => String -> Maybe a+safeRead a = case reads a of+ (x, ""):[] -> Just x+ _ -> Nothing++readNumber :: Read a => String -> Maybe a+readNumber a = case a of+ [] -> Nothing+ x:xs -> if x == '+' then safeRead xs else safeRead (x:xs)++readNumberBS :: Read a => BS8.ByteString -> Maybe a+readNumberBS bs = readNumber (BS8.unpack bs)
+ dectest/deka-dectest.hs view
@@ -0,0 +1,7 @@+module Main where++import Runner+import System.Environment++main :: IO ()+main = getArgs >>= runAndExit
+ deka-tests.cabal view
@@ -0,0 +1,135 @@+-- This Cabal file generated using the Cartel library.+-- Cartel is available at:+-- http://www.github.com/massysett/cartel+--+-- Script name used to generate: genCabal.hs+-- Generated on: 2014-07-16 09:29:20.116107 EDT+-- Cartel library version: 0.10.0.2+name: deka-tests+version: 0.6.0.2+cabal-version: >= 1.14+build-type: Simple+license: BSD3+license-file: LICENSE+copyright: Copyright 2014 Omari Norman+author: Omari Norman+maintainer: Omari Norman, omari@smileystation.com+stability: Experimental+homepage: https://github.com/massysett/deka+bug-reports: https://github.com/massysett/deka/issues+synopsis: Tests for deka, decimal floating point arithmetic+description:+ deka provides decimal floating point arithmetic. It is based on+ mpdecimal, the C library used to provide support for the Decimal+ module in Python 3.+ .+ You will need to install mpdecimal to use deka; otherwise your+ executables will not link. It is available at+ .+ <http://www.bytereef.org/mpdecimal/>+ .+ mpdecimal has also been packaged for some Linux distributions,+ such as Debian (libmpdec-dev - available in Jessie and later) and+ Arch (mpdecimal).+ .+ mpdecimal, in turn, implements the General Decimal Arithmetic+ Specification, which is available at+ .+ <http://speleotrove.com/decimal/>+ .+ For more on deka, please see the Github home page at+ .+ <https://github.com/massysett/deka>+ .+ This package contains only tests, so that other packages+ may also use the tests.+category: Math+tested-with: GHC == 7.4.1, GHC == 7.6.3, GHC == 7.8.3+extra-source-files:+ README.md+ , ChangeLog+ , current-versions.txt+ , minimum-versions.txt++source-repository head+ type: git+ location: https://github.com/massysett/deka.git++Library+ exposed-modules:+ Deka.Dec.Coarbitrary+ , Deka.Dec.Generators+ , Deka.Dec.Shrinkers+ , Deka.Native.Abstract.Coarbitrary+ , Deka.Native.Abstract.Generators+ , Deka.Native.Abstract.Shrinkers+ , Deka.Tests.Util+ hs-source-dirs:+ lib+ build-depends:+ base ((> 4.5.0.0 || == 4.5.0.0) && < 4.8)+ , bytestring ((> 0.9.2.1 || == 0.9.2.1) && < 0.11)+ , deka == 0.6.0.2+ , QuickCheck ((> 2.7.3 || == 2.7.3) && < 2.8)+ ghc-options:+ -Wall+ default-language: Haskell2010++Executable deka-dectest+ build-depends:+ base ((> 4.5.0.0 || == 4.5.0.0) && < 4.8)+ , bytestring ((> 0.9.2.1 || == 0.9.2.1) && < 0.11)+ , deka == 0.6.0.2+ , QuickCheck ((> 2.7.3 || == 2.7.3) && < 2.8)+ , transformers ((> 0.3.0.0 || == 0.3.0.0) && < 0.5)+ , parsec ((> 3.1.2 || == 3.1.2) && < 3.2)+ , containers ((> 0.4.2.1 || == 0.4.2.1) && < 0.6)+ , pipes ((> 4.1.1 || == 4.1.1) && < 4.2)+ hs-source-dirs:+ dectest+ other-modules:+ Arity+ , Conditions+ , Directives+ , NumTests+ , Operand+ , Parse+ , Parse.Tokenizer+ , Parse.Tokens+ , Result+ , Runner+ , Specials+ , TestHelpers+ , TestLog+ , Types+ , Util+ main-is: deka-dectest.hs+ default-language: Haskell2010+ ghc-options:+ -Wall++Test-Suite deka-native+ build-depends:+ base ((> 4.5.0.0 || == 4.5.0.0) && < 4.8)+ , bytestring ((> 0.9.2.1 || == 0.9.2.1) && < 0.11)+ , deka == 0.6.0.2+ , QuickCheck ((> 2.7.3 || == 2.7.3) && < 2.8)+ , quickpull ((> 0.2.0.0 || == 0.2.0.0) && < 0.3)+ type: exitcode-stdio-1.0+ main-is: deka-native.hs+ hs-source-dirs:+ native+ , lib+ other-modules:+ Deka.Dec.Coarbitrary+ , Deka.Dec.Generators+ , Deka.Dec.Shrinkers+ , Deka.Native.Abstract.Coarbitrary+ , Deka.Native.Abstract.Generators+ , Deka.Native.Abstract.Shrinkers+ , Deka.Tests.Util+ , Decrees+ , Properties+ default-language: Haskell2010+ ghc-options:+ -Wall
+ lib/Deka/Dec/Coarbitrary.hs view
@@ -0,0 +1,15 @@+module Deka.Dec.Coarbitrary where++import qualified Deka.Dec as D+import Test.QuickCheck+import Deka.Tests.Util++posNeg :: D.PosNeg -> Gen a -> Gen a+posNeg a = case a of+ D.Pos -> varInt 0+ D.Neg -> varInt 1++sign :: D.Sign -> Gen a -> Gen a+sign a = case a of+ D.Sign0 -> varInt 0+ D.Sign1 -> varInt 1
+ lib/Deka/Dec/Generators.hs view
@@ -0,0 +1,10 @@+module Deka.Dec.Generators where++import Test.QuickCheck+import qualified Deka.Dec as D++posNeg :: Gen D.PosNeg+posNeg = elements [ D.Pos, D.Neg ]++sign :: Gen D.Sign+sign = elements [ D.Sign0, D.Sign1 ]
+ lib/Deka/Dec/Shrinkers.hs view
@@ -0,0 +1,13 @@+module Deka.Dec.Shrinkers where++import qualified Deka.Dec as D++posNeg :: D.PosNeg -> [D.PosNeg]+posNeg x = case x of+ D.Neg -> [D.Pos]+ _ -> []++sign :: D.Sign -> [D.Sign]+sign x = case x of+ D.Sign1 -> [D.Sign0]+ _ -> []
+ lib/Deka/Native/Abstract/Coarbitrary.hs view
@@ -0,0 +1,72 @@+module Deka.Native.Abstract.Coarbitrary where++import qualified Deka.Native.Abstract as D+import Test.QuickCheck+import Deka.Tests.Util+import Deka.Dec.Coarbitrary+import Prelude hiding (exponent)++novem :: D.Novem -> Gen a -> Gen a+novem d = case d of+ D.D1 -> v 0+ D.D2 -> v 1+ D.D3 -> v 2+ D.D4 -> v 3+ D.D5 -> v 4+ D.D6 -> v 5+ D.D7 -> v 6+ D.D8 -> v 7+ D.D9 -> v 8+ where+ v = varInt++decem :: D.Decem -> Gen a -> Gen a+decem d = case d of+ D.D0 -> varInt 0+ D.Nonem n -> varInt 1 . novem n++decuple :: D.Decuple -> Gen a -> Gen a+decuple (D.Decuple n ds) = novem n . coarbitraryList decem ds++aut :: D.Aut -> Gen a -> Gen a+aut a = case a of+ D.Nil -> varInt 0+ D.Plenus d -> varInt 1 . decuple d++firmado :: D.Firmado -> Gen a -> Gen a+firmado a = case a of+ D.Cero -> varInt 0+ D.Completo p d -> varInt 1 . posNeg p . decuple d++coefficient :: D.Coefficient -> Gen a -> Gen a+coefficient (D.Coefficient c) = aut c++exponent :: D.Exponent -> Gen a -> Gen a+exponent (D.Exponent c) = firmado c++diagnostic :: D.Diagnostic -> Gen a -> Gen a+diagnostic (D.Diagnostic d) = decuple d++noisy :: D.Noisy -> Gen a -> Gen a+noisy d = case d of+ D.Quiet -> varInt 0+ D.Signaling -> varInt 1++nonNum :: D.NonNum -> Gen a -> Gen a+nonNum (D.NonNum n d) = noisy n . varD d+ where+ varD x = case x of+ Nothing -> varInt 0+ Just j -> varInt 1 . diagnostic j++value :: D.Value -> Gen a -> Gen a+value v = case v of+ D.Finite c e -> varInt 0 . coefficient c . exponent e+ D.Infinite -> varInt 1+ D.NotANumber n -> varInt 2 . nonNum n++abstract :: D.Abstract -> Gen a -> Gen a+abstract (D.Abstract s v) = sign s . value v++adjustedExp :: D.AdjustedExp -> Gen a -> Gen a+adjustedExp (D.AdjustedExp i) = variant i
+ lib/Deka/Native/Abstract/Generators.hs view
@@ -0,0 +1,53 @@+module Deka.Native.Abstract.Generators where++import Test.QuickCheck+import Deka.Dec.Generators+import qualified Deka.Native.Abstract as A+import Control.Monad+import Prelude hiding (exponent)++novem :: Gen A.Novem+novem = elements+ [ A.D1, A.D2, A.D3, A.D4, A.D5, A.D6, A.D7, A.D8, A.D9 ]++decem :: Gen A.Decem+decem = frequency [(1, return A.D0), (5, fmap A.Nonem novem)]++decuple :: Gen A.Decuple+decuple = liftM2 A.Decuple novem (listOf decem)++aut :: Gen A.Aut+aut = frequency [(1, return A.Nil), (5, fmap A.Plenus decuple)]++firmado :: Gen A.Firmado+firmado = frequency [ (1, return A.Cero),+ (5, liftM2 A.Completo posNeg decuple) ]++coefficient :: Gen A.Coefficient+coefficient = fmap A.Coefficient aut++exponent :: Gen A.Exponent+exponent = fmap A.Exponent firmado++diagnostic :: Gen A.Diagnostic+diagnostic = fmap A.Diagnostic decuple++noisy :: Gen A.Noisy+noisy = elements [ A.Quiet, A.Signaling ]++nonNum :: Gen A.NonNum+nonNum = liftM2 A.NonNum noisy+ (frequency [(1, return Nothing), (5, fmap Just diagnostic)])++value :: Gen A.Value+value = frequency+ [ (4, liftM2 A.Finite coefficient exponent)+ , (1, return A.Infinite)+ , (1, fmap A.NotANumber nonNum)+ ]++abstract :: Gen A.Abstract+abstract = liftM2 A.Abstract sign value++adjustedExp :: Gen A.AdjustedExp+adjustedExp = fmap A.AdjustedExp arbitrarySizedIntegral
+ lib/Deka/Native/Abstract/Shrinkers.hs view
@@ -0,0 +1,75 @@+module Deka.Native.Abstract.Shrinkers where++import Deka.Dec.Shrinkers+import qualified Deka.Native.Abstract as D+import Test.QuickCheck+import Prelude hiding (exponent)++novem :: D.Novem -> [D.Novem]+novem d = case d of+ D.D1 -> []+ D.D2 -> [D.D1]+ D.D3 -> [D.D1 .. D.D2]+ D.D4 -> [D.D1 .. D.D3]+ D.D5 -> [D.D1 .. D.D4]+ D.D6 -> [D.D1 .. D.D5]+ D.D7 -> [D.D1 .. D.D6]+ D.D8 -> [D.D1 .. D.D7]+ D.D9 -> [D.D1 .. D.D8]++decem :: D.Decem -> [D.Decem]+decem d = case d of+ D.D0 -> []+ D.Nonem nv -> D.D0 : map D.Nonem (novem nv)++decuple :: D.Decuple -> [D.Decuple]+decuple (D.Decuple n d) =+ [ D.Decuple n' d' | n' <- novem n,+ d' <- shrinkList decem d ]++aut :: D.Aut -> [D.Aut]+aut a = case a of+ D.Nil -> []+ D.Plenus d -> D.Nil : map D.Plenus (decuple d)++firmado :: D.Firmado -> [D.Firmado]+firmado a = case a of+ D.Cero -> []+ D.Completo p d ->+ [ D.Completo p' d' | p' <- posNeg p, d' <- decuple d ]++coefficient :: D.Coefficient -> [D.Coefficient]+coefficient = map D.Coefficient . aut . D.unCoefficient++exponent :: D.Exponent -> [D.Exponent]+exponent = map D.Exponent . firmado . D.unExponent++diagnostic :: D.Diagnostic -> [D.Diagnostic]+diagnostic = map D.Diagnostic . decuple . D.unDiagnostic++noisy :: D.Noisy -> [D.Noisy]+noisy a = case a of+ D.Quiet -> []+ D.Signaling -> [D.Quiet]++nonNum :: D.NonNum -> [D.NonNum]+nonNum (D.NonNum n d) = [ D.NonNum n' d'+ | n' <- noisy n, d' <- shrinkMaybe d ]+ where+ shrinkMaybe m = case m of+ Nothing -> []+ Just x -> Nothing : map Just (diagnostic x)++value :: D.Value -> [D.Value]+value a = case a of+ D.Finite c e -> [ D.Finite c' e' | c' <- coefficient c,+ e' <- exponent e ]+ D.Infinite -> []+ D.NotANumber n -> [ D.NotANumber n' | n' <- nonNum n ]++abstract :: D.Abstract -> [D.Abstract]+abstract (D.Abstract s v) =+ [ D.Abstract s' v' | s' <- sign s, v' <- value v ]++adjustedExp :: D.AdjustedExp -> [D.AdjustedExp]+adjustedExp (D.AdjustedExp i) = map D.AdjustedExp $ shrinkIntegral i
+ lib/Deka/Tests/Util.hs view
@@ -0,0 +1,11 @@+module Deka.Tests.Util where++import Test.QuickCheck++varInt :: Int -> Gen b -> Gen b+varInt = variant++coarbitraryList :: (a -> Gen b -> Gen b) -> [a] -> Gen b -> Gen b+coarbitraryList f ls = case ls of+ [] -> varInt 0+ x:xs -> varInt 1 . f x . coarbitraryList f xs
+ minimum-versions.txt view
@@ -0,0 +1,50 @@+This package was tested to work with these dependency+versions and compiler version.+These are the minimum versions given in the .cabal file.+Tested as of: 2014-07-16 13:56:43.863793 UTC+Path to compiler: ghc-7.4.1+Compiler description: 7.4.1++/opt/ghc/7.4.1/lib/ghc-7.4.1/package.conf.d:+ Cabal-1.14.0+ array-0.4.0.0+ base-4.5.0.0+ bin-package-db-0.0.0.0+ binary-0.5.1.0+ bytestring-0.9.2.1+ containers-0.4.2.1+ deepseq-1.3.0.0+ directory-1.1.0.2+ extensible-exceptions-0.1.1.4+ filepath-1.3.0.0+ (ghc-7.4.1)+ ghc-prim-0.2.0.0+ (haskell2010-1.1.0.1)+ (haskell98-2.0.0.1)+ hoopl-3.8.7.3+ hpc-0.5.1.1+ integer-gmp-0.4.0.0+ old-locale-1.0.0.4+ old-time-1.1.0.0+ pretty-1.1.1.0+ process-1.1.0.1+ rts-1.0+ template-haskell-2.7.0.0+ time-1.4+ unix-2.5.1.0++/home/massysett/deka/tests/sunlight-19453/db:+ QuickCheck-2.7.3+ deka-0.6.0.2+ deka-tests-0.6.0.2+ mmorph-1.0.3+ mtl-2.1.3.1+ parsec-3.1.2+ pipes-4.1.1+ primitive-0.5.3.0+ quickpull-0.2.0.0+ random-1.0.1.1+ text-0.11.3.1+ tf-random-0.5+ transformers-0.3.0.0+
+ native/Decrees.hs view
@@ -0,0 +1,15 @@+-- | This module generated by the Quickpull package.+-- Quickpull is available at:+-- <http://www.github.com/massysett/quickpull>++module Decrees where++import Quickpull+import qualified Properties++decrees :: [Decree]+decrees =++ [ Decree ( Meta {modDesc = ModDesc {modPath = "native/Properties.hs", modName = ["Properties"]}, linenum = 9, qName = "prop_abstractToStringToAbstract"} ) ( Single Properties.prop_abstractToStringToAbstract )+ , Decree ( Meta {modDesc = ModDesc {modPath = "native/Properties.hs", modName = ["Properties"]}, linenum = 15, qName = "prop_decToAbstractToDec"} ) ( Single Properties.prop_decToAbstractToDec )+ ]
+ native/Properties.hs view
@@ -0,0 +1,21 @@+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}+module Properties where++import qualified Deka.Native.Abstract.Generators as G+import qualified Deka.Native as N+import Test.QuickCheck+import qualified Deka.Dec as D++prop_abstractToStringToAbstract =+ forAll G.abstract $ \a ->+ case N.stringToAbstract . N.abstractToString $ a of+ Left _ -> property False+ Right a' -> a === a'++prop_decToAbstractToDec =+ forAll G.abstract $ \a ->+ let (d, flgs) = N.abstractToDec a+ a' = N.decToAbstract d+ (d'', flgs') = N.abstractToDec a'+ in flgs == D.emptyFlags && flgs' == D.emptyFlags+ ==> D.compareTotal d d'' == EQ
+ native/deka-native.hs view
@@ -0,0 +1,7 @@+module Main where++import Decrees+import Quickpull++main :: IO ()+main = defaultMain decrees