packages feed

yxdb-utils 0.1.0.0 → 0.1.0.1

raw patch · 12 files changed

+580/−2 lines, 12 filesbinary-added

Files

+ Tests/Codec/Compression/LZF/ByteString.hs view
@@ -0,0 +1,57 @@+module Tests.Codec.Compression.LZF.ByteString (lzfByteStringTests) where++import Codec.Compression.LZF.ByteString+    (+     compressByteString,+     compressLazyByteString,+     decompressByteString,+     decompressByteStringFixed,+     decompressLazyByteString+    )++import Control.Applicative((<$>))++import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL++import Test.Framework+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck+import Test.QuickCheck.Instances+import Test.QuickCheck.Monadic (assert, monadic, run)++_compare :: Eq t => (t -> t) -> (t -> t) -> t -> Property+_compare a b x = property $ x == (a $ b x)++prop_DecompressCompressInverses :: BS.ByteString -> Property+prop_DecompressCompressInverses x =+    _compare decompressByteString compressByteString x++prop_LazyDecompressCompressInverses :: BSL.ByteString -> Property+prop_LazyDecompressCompressInverses x =+    _compare decompressLazyByteString compressLazyByteString x++data NSmall = NSmall Int deriving (Show)+data BSSmall = BSSmall BS.ByteString deriving (Show)++instance Arbitrary NSmall where+    arbitrary = NSmall <$> choose (0,10000)++instance Arbitrary BSSmall where+    arbitrary = do+      size <- choose(0,10000)+      BSSmall <$> BS.pack <$> vector size++prop_DecompressingAnyGarbageDoesntCauseACrash :: NSmall -> BSSmall -> Property+prop_DecompressingAnyGarbageDoesntCauseACrash (NSmall n) (BSSmall bs) =+    property $+    case decompressByteStringFixed n bs of+      Nothing -> True+      Just x -> BS.length x >= 0++lzfByteStringTests =+    testGroup "Codec.Compression.LZF.ByteString" [+        testProperty "Decompress and compress inverses" prop_DecompressCompressInverses,+        testProperty "Lazy Decompress and compress inverses" prop_LazyDecompressCompressInverses,+        testProperty "Checking for crashes on arbitrary ByteStrings" prop_DecompressingAnyGarbageDoesntCauseACrash+    ]
+ Tests/Data/Binary/C.hs view
@@ -0,0 +1,30 @@+module Tests.Data.Binary.C (cBinaryTests) where++import Control.Applicative+import Data.Binary+import Data.Binary.C+import Foreign.C.Types+import GHC.Prim (coerce)+import Test.Framework+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck+import Test.QuickCheck.Instances+import Test.QuickCheck.Monadic (assert, monadic, run)++prop_CFloat :: CFloat -> Property+prop_CFloat x = property $ (decode $ encode x) == x++prop_CDouble :: CDouble -> Property+prop_CDouble x = property $ (decode $ encode x) == x++instance Arbitrary CFloat where+  arbitrary = realToFrac <$> (arbitrary :: Gen Float)++instance Arbitrary CDouble where+  arbitrary = realToFrac <$> (arbitrary :: Gen Double)++cBinaryTests =+    testGroup "Binary instances for C types" [+        testProperty "CFloat" prop_CFloat,+        testProperty "CDouble" prop_CDouble+    ]
+ Tests/Database/Alteryx.hs view
@@ -0,0 +1,282 @@+{-# LANGUAGE OverloadedStrings #-}++module Tests.Database.Alteryx (yxdbTests) where++import qualified Database.Alteryx.CLI.Csv2Yxdb as C2Y+import qualified Database.Alteryx.CLI.Yxdb2Csv as Y2C+import Database.Alteryx+import Tests.Database.Alteryx.Arbitrary+import Tests.Database.Utils++import Prelude hiding (readFile)++import Control.Applicative+import Control.Lens hiding (elements)+import Control.Monad (replicateM, when)+import Control.Monad.State (evalStateT)+import Control.Monad.Trans.Resource+import Data.Array.IArray (listArray)+import Data.Binary+import Data.Binary.Get (runGet)+import Data.Binary.Put (runPut)+import Data.ByteString as BS+import Data.ByteString.Char8 as BSC+import Data.ByteString.Lazy as BSL+import Data.Conduit+import Data.Conduit.Combinators+import Data.Conduit.Lift+import Data.Conduit.List as CL+import qualified Data.Conduit.Text as CT+import qualified Data.CSV.Conduit as CSVT+import Data.Text as T+import Data.Text.Encoding as T+import Data.Text.IO as T++import Test.Framework+import Test.Framework.Providers.HUnit (testCase)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.HUnit+import Test.QuickCheck++assertEq :: (Eq a, Show a) => a -> a -> Property+assertEq a b = let+    aStr = show a+    bStr = show b+    in printTestCase ("\nA: " ++ aStr ++ "\nB: " ++ bStr) (a == b)++numBytes :: Binary a => a -> Int+numBytes x = fromIntegral $ BSL.length $ runPut $ put x++prop_HeaderLength :: Header -> Property+prop_HeaderLength header =+    assertEq headerPageSize (numBytes header)++prop_MetadataLength :: YxdbFile -> Property+prop_MetadataLength yxdb =+    assertEq (numMetadataBytesHeader $ yxdb ^. yxdbFileHeader)+             (numMetadataBytesActual $ yxdb ^. yxdbFileMetadata)++-- prop_BlocksLength :: YxdbFile -> Property+-- prop_BlocksLength yxdb =+--     assertEq (numBlocksBytesHeader $ header yxdb) (numBlocksBytesActual $ blocks yxdb)++prop_ValueGetAndPutAreInverses :: PairedValue -> Property+prop_ValueGetAndPutAreInverses (PairedValue field value) =+    assertEq (runGet (getValue field) (runPut $ putValue field value)) value++prop_BlockIndexGetAndPutAreInverses :: BlockIndex -> Property+prop_BlockIndexGetAndPutAreInverses x = assertEq (decode $ encode x) x++prop_MetadataGetAndPutAreInverses :: RecordInfo -> Property+prop_MetadataGetAndPutAreInverses x = assertEq (decode $ encode x) x++prop_HeaderGetAndPutAreInverses :: Header -> Property+prop_HeaderGetAndPutAreInverses x = assertEq (decode $ encode x) x++prop_BlocksGetAndPutAreInverses :: Block -> Property+prop_BlocksGetAndPutAreInverses x = assertEq (decode $ encode x) x++prop_YxdbFileGetAndPutAreInverses :: YxdbFile -> Property+prop_YxdbFileGetAndPutAreInverses x = assertEq (decode $ encode x) x++test_LoadingSmallModule :: Assertion+test_LoadingSmallModule = do+  parsed <- decodeFileOrFail "test-data/samples_of_various_field_types.yxdb"+  case parsed of+    Left (bytes, msg) -> assertFailure (msg ++ " at " ++ show bytes ++ " bytes")+    Right yxdbFile -> const (return ()) (yxdbFile :: YxdbFile)++csv2yxdb2csv :: C2Y.Settings -> FilePath -> FilePath -> IO T.Text+csv2yxdb2csv settings inputFilename outputFilename = do+  let c2ySettings = settings & C2Y.settingFilename .~ inputFilename+                             & C2Y.settingOutput .~ outputFilename+  let y2cSettings = Y2C.defaultSettings & Y2C.settingFilename .~ outputFilename+  evalStateT C2Y.runCsv2Yxdb c2ySettings+  newCsv <- T.decodeUtf8 <$> (captureStdout $ evalStateT Y2C.runYxdb2Csv y2cSettings)++  return newCsv++readCsvRecords :: Maybe Text -> FilePath -> IO [Record]+readCsvRecords header filename =+    runResourceT $+      sourceCsvRecords filename header alteryxCsvSettings $$+      sinkList++test_csv2yxdb2csvIsIdentity :: Assertion+test_csv2yxdb2csvIsIdentity = do+  let inputFilename = "test-data/samples.csv"+  let outputFilename = "test-data/samples.yxdb"+  newCsv <- csv2yxdb2csv C2Y.defaultSettings inputFilename outputFilename+  originalCsv <- T.readFile inputFilename+  assertEqual "CSV to YXDB to CSV" originalCsv newCsv++test_csv2yxdbQuoteParsing :: Assertion+test_csv2yxdbQuoteParsing = do+  let inputFilename = "test-data/quote-parsing.csv"+  let outputFilename = "test-data/quote-parsing.yxdb"+  oldCsv <- T.readFile inputFilename+  newCsv <- csv2yxdb2csv C2Y.defaultSettings inputFilename outputFilename+  assertEqual "" oldCsv newCsv++test_csv2yxdbDateFormat :: Assertion+test_csv2yxdbDateFormat = do+  let inputFilename = "test-data/date-format.csv"+      outputFilename = "test-data/date-format.yxdb"+  oldCsv <- T.readFile inputFilename+  newCsv <- csv2yxdb2csv C2Y.defaultSettings inputFilename outputFilename+  assertEqual "" oldCsv newCsv++nonAnsiCodepointFieldValue :: Maybe FieldValue+nonAnsiCodepointFieldValue = Just $ FVString "plátano"++nonAnsiCodepointField :: Field+nonAnsiCodepointField = Field {+                          _fieldName = "derp",+                          _fieldType = FTString,+                          _fieldSize = Just 10,+                          _fieldScale = Nothing+                        }++test_renderNonAnsiCodepoint :: Assertion+test_renderNonAnsiCodepoint = do+  let fieldValue = nonAnsiCodepointFieldValue+      field = nonAnsiCodepointField+      bs = runPut (putValue field fieldValue)+      parsedValue = runGet (getValue field) bs+  assertEqual "Converting a non-ANSI character lost information" fieldValue parsedValue++test_renderNonAnsiCodepointRecord :: Assertion+test_renderNonAnsiCodepointRecord = do+    let fieldValue = nonAnsiCodepointFieldValue+        field = nonAnsiCodepointField++        record = Record [ fieldValue ]+        recordInfo = RecordInfo [ field ]++        record2recordConduit = record2csv recordInfo =$=+                         csv2bytes =$=+                         CT.decode CT.utf8 =$=+                         csv2records alteryxCsvSettings+    newRecords <- runResourceT $ CL.sourceList [ record ] =$= record2recordConduit $$ sinkList++    let result = Prelude.head newRecords+    assertEqual "Converting a non-ANSI character lost information" record result++test_renderNonAnsiCodepointCSVRecord :: Assertion+test_renderNonAnsiCodepointCSVRecord =+  let csvText = "derp:string(10)\nplátano\n"+      recordInfo = RecordInfo [ nonAnsiCodepointField ]+      csv2csvConduit =+          csv2records alteryxCsvSettings =$=+          record2csv recordInfo =$=+          csv2bytes =$=+          CT.decode CT.utf8++  in do+    newCsv <- runResourceT $ CL.sourceList [ csvText ] =$= csv2csvConduit $$ sinkList+    assertEqual "" csvText $ T.concat newCsv++test_renderNonAnsiCodepointCSV2Record2Block2Record2CSV :: Assertion+test_renderNonAnsiCodepointCSV2Record2Block2Record2CSV =+  let csvText = "derp:string(10)\nplátano\n"+      recordInfo = RecordInfo [ nonAnsiCodepointField ]+      csv2csvConduit =+          csv2records alteryxCsvSettings =$=+          evalStateLC defaultStatistics (recordsToBlocks recordInfo) =$=+          blocksToRecords recordInfo =$=+          record2csv recordInfo =$=+          csv2bytes =$=+          CT.decode CT.utf8+  in do+    newCsv <- runResourceT $ CL.sourceList [ csvText ] =$= csv2csvConduit $$ sinkList+    assertEqual "" csvText $ T.concat newCsv+++test_recordParsing :: Assertion+test_recordParsing = do+  let inputFilename = "test-data/samples.csv"+      outputFilename = "test-data/samples.yxdb"+      secondOutputFilename = "test-data/samples.yxdb.csv"+      expectedRecord =+          [+           Record [+            Just (FVByte 1),+            Just (FVInt16 256),+            Just (FVInt32 32768),+            Just (FVInt64 4294967296),+            Just (FVString "1.10000"),+            Just (FVFloat 1.1),+            Just (FVDouble 1.1),+            Just (FVString "plátano"),+            Just (FVWString "香蕉"),+            Just (FVString "plátano"),+            Just (FVWString "香蕉"),+            Just (FVString "2013-12-31"),+            Just (FVString "23:34:56"),+            Just (FVString "2013-12-31 23:34:56")+           ]+          ]+  records <- readCsvRecords Nothing inputFilename+  assertEqual "Parse from CSV" expectedRecord records++  newCsv <- csv2yxdb2csv C2Y.defaultSettings inputFilename outputFilename+  yxdbFile <- decodeFile outputFilename :: IO YxdbFile++  T.writeFile secondOutputFilename newCsv+  newRecords <- readCsvRecords Nothing secondOutputFilename++  assertEqual "Parse from YXDB" expectedRecord (yxdbFile ^. yxdbFileRecords)+  assertEqual "New CSV doesn't match old" expectedRecord newRecords++test_signedRecordParsing :: Assertion+test_signedRecordParsing = do+  let inputFilename = "test-data/signed.csv"+      outputFilename = "test-data/signed.yxdb"+      secondOutputFilename = "test-data/signed.yxdb.csv"+      expectedRecord =+          [+           Record [+            Just (FVByte $ -1),+            Just (FVInt16 $ -256),+            Just (FVInt32 $ -32768),+            Just (FVInt64 $ -4294967296),+            Just (FVString "1.10000"),+            Just (FVFloat $ -1.1),+            Just (FVDouble $ -1.1)+           ]+          ]+  records <- readCsvRecords Nothing inputFilename+  assertEqual "Parse from CSV" expectedRecord records++  newCsv <- csv2yxdb2csv C2Y.defaultSettings inputFilename outputFilename+  yxdbFile <- decodeFile outputFilename :: IO YxdbFile++  T.writeFile secondOutputFilename newCsv+  newRecords <- readCsvRecords Nothing secondOutputFilename++  assertEqual "Parse from YXDB" expectedRecord (yxdbFile ^. yxdbFileRecords)+  assertEqual "New CSV doesn't match old" expectedRecord newRecords++yxdbTests :: Test.Framework.Test+yxdbTests =+    testGroup "YXDB" [+        testProperty "Header length" prop_HeaderLength,+        testProperty "Metadata length" prop_MetadataLength,+--        testProperty "Blocks length" prop_BlocksLength, --+        testProperty "Block Index get & put inverses" prop_BlockIndexGetAndPutAreInverses,+        testProperty "Metadata get and put inverses" prop_MetadataGetAndPutAreInverses,+        testProperty "Header get & put inverses" prop_HeaderGetAndPutAreInverses,+        testProperty "Value get & put inverses" prop_ValueGetAndPutAreInverses,+        testProperty "Blocks get & put inverses" prop_BlocksGetAndPutAreInverses,+--        testProperty "Yxdb get & put inverses" prop_YxdbFileGetAndPutAreInverses,+        testCase "Loading small module" test_LoadingSmallModule,+        testCase "CSV to YXDB to CSV is the identity" test_csv2yxdb2csvIsIdentity,+        testCase "Quotes are not used to escape" test_csv2yxdbQuoteParsing,+        testCase "Can parse ISO style dates" test_csv2yxdbDateFormat,+        testCase "Example CSV parses into correct structures" test_recordParsing,+        testCase "Example CSV with signed values" test_signedRecordParsing,+        testCase "Rendering a non-ANSI codepoint" test_renderNonAnsiCodepoint,+        testCase "Rendering a non-ANSI codepoint at record level" test_renderNonAnsiCodepointRecord,+        testCase "Rendering a non-ANSI codepoint from CSV to CSV" test_renderNonAnsiCodepointCSVRecord,+        testCase "Rendering a non-ANSI codepoint from CSV to YXDB blocks to CSV" test_renderNonAnsiCodepointCSV2Record2Block2Record2CSV+    ]
+ Tests/Database/Alteryx/Arbitrary.hs view
@@ -0,0 +1,170 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Tests.Database.Alteryx.Arbitrary where++import Database.Alteryx++import Conduit+import Data.Conduit.Lift as CL+import Control.Applicative+import Control.Lens hiding (elements)+import Control.Monad+import Data.Array.IArray+import Data.Binary+import Data.Binary.Put+import Data.ByteString as BS+import Data.ByteString.Lazy as BSL+import Data.Maybe+import qualified Data.Text as T+import Data.Text.Encoding+import Data.Time.Clock.POSIX+import Data.Word+import Test.QuickCheck+import Test.QuickCheck.Instances()++instance Arbitrary YxdbFile where+  arbitrary = do+    fMetadata   <- arbitrary+    fBlocks     <- arbitraryBlocksMatching fMetadata+    fHeader     <- arbitraryHeaderMatching fMetadata fBlocks+    fBlockIndex <- arbitrary++    fRecords <- arbitraryRecordsMatching fMetadata++    return $ YxdbFile {+      _yxdbFileHeader     = fHeader,+      _yxdbFileRecords    = fRecords,+      _yxdbFileMetadata   = fMetadata,+      _yxdbFileBlockIndex = fBlockIndex+    }++arbitraryBlocksMatching :: RecordInfo -> Gen [Block]+arbitraryBlocksMatching recordInfo = do+  records <- arbitraryRecordsMatching recordInfo+  let blocks = fromJust $ yieldMany records $= CL.evalStateLC defaultStatistics (recordsToBlocks recordInfo) $$ sinkList+  return blocks++arbitraryHeaderMatching :: RecordInfo -> [Block] -> Gen Header+arbitraryHeaderMatching metadata blocks = do+  fDescription <- replicateM 64 $ choose(0,127) :: Gen [Word8]+  fFileId <- arbitrary+  fCreationDate <- posixSecondsToUTCTime <$> fromIntegral <$> (arbitrary :: Gen Word32)+  fFlags1 <- arbitrary+  fFlags2 <- arbitrary+  fMystery <- arbitrary+  fSpatialIndexPos <- arbitrary+  let numMetadataBytes = numMetadataBytesActual metadata+  let fMetaInfoLength = fromIntegral $ numMetadataBytes `div` 2+  let numBlockBytes = sum $ Prelude.map numBlockBytesActual blocks+  let startOfBlocks = fromIntegral $ headerPageSize + (fromIntegral $ numMetadataBytes)+  let fRecordBlockIndexPos = startOfBlocks + (fromIntegral numBlockBytes)+  fNumRecords <- arbitrary+  fCompressionVersion <- arbitrary+  fReservedSpace <- vector (512 - 64 - (4 * 7) - (8 * 3)) :: Gen [Word8]+  return $ Header {+               _description         = decodeUtf8 $ BS.pack $ fDescription,+               _fileId              = fFileId,+               _creationDate        = fCreationDate,+               _flags1              = fFlags1,+               _flags2              = fFlags2,+               _metaInfoLength      = fMetaInfoLength,+               _mystery             = fMystery,+               _spatialIndexPos     = fSpatialIndexPos,+               _recordBlockIndexPos = fRecordBlockIndexPos,+               _numRecords          = fNumRecords,+               _compressionVersion  = fCompressionVersion,+               _reservedSpace       = BS.pack fReservedSpace+             }++instance Arbitrary Header where+    arbitrary = do+      metadata <- arbitrary+      blocks <- arbitraryBlocksMatching metadata+      arbitraryHeaderMatching metadata blocks++arbitraryRecordsMatching :: RecordInfo -> Gen [Record]+arbitraryRecordsMatching metadata = do+  size <- choose(0,10000)+  vectorOf size (arbitraryRecordMatching metadata)++arbitraryRecordMatching :: RecordInfo -> Gen Record+arbitraryRecordMatching (RecordInfo fields) =+    Record <$> mapM arbitraryValueMatching fields++arbitraryValueMatching :: Field -> Gen (Maybe FieldValue)+arbitraryValueMatching field =+  let value =+        case field ^. fieldType of+          FTBool -> FVBool <$> arbitrary+          FTByte -> FVByte <$> arbitrary+          FTInt16 -> FVInt16 <$> arbitrary+          FTInt32 -> FVInt32 <$> arbitrary+          FTInt64 -> FVInt64 <$> arbitrary+          FTFloat -> FVFloat <$> arbitrary+          FTDouble -> FVDouble <$> arbitrary+  in do+    isNull <- arbitrary+    if isNull+       then return Nothing+       else Just <$> value++instance Arbitrary Block where+    arbitrary = Prelude.head <$> (arbitraryBlocksMatching =<< arbitrary)++instance Arbitrary FieldType where+    arbitrary = elements [+                 -- FTBool,+                 FTByte,+                 FTInt16,+                 FTInt32,+                 FTInt64,+                 -- FTFixedDecimal,+                 FTFloat,+                 FTDouble+                 -- FTString,+                 -- FTWString,+                 -- FTVString,+                 -- FTVWString,+                 -- FTDate,+                 -- FTTime,+                 -- FTDateTime,+                 -- FTBlob,+                 -- FTSpatialObject,+                 -- FTUnknown+                ]++instance Arbitrary Field where+    arbitrary = do+      fName <- arbitrary+      fType <- arbitrary+      fSize <- arbitrary+      fScale <- arbitrary+      return $ Field {+                   _fieldName = fName,+                   _fieldType = fType,+                   _fieldSize = fSize,+                   _fieldScale = fScale+                 }++instance Arbitrary Record where+    arbitrary = arbitraryRecordMatching =<< arbitrary++instance Arbitrary RecordInfo where+    arbitrary = do+      len <- choose(1,10)+      RecordInfo <$> vector len++instance Arbitrary BlockIndex where+    arbitrary =+        sized $+            \chunkSize -> do+                indices <- replicateM chunkSize arbitrary+                return $ BlockIndex $ listArray (0, chunkSize) indices++data PairedValue = PairedValue Field (Maybe FieldValue) deriving (Eq, Show)++instance Arbitrary PairedValue where+    arbitrary = do+      field <- arbitrary+      value <- arbitraryValueMatching field+      return $ PairedValue field value
+ Tests/Database/Utils.hs view
@@ -0,0 +1,19 @@+module Tests.Database.Utils where++import Data.ByteString as BS+import GHC.IO.Handle+import System.IO+import System.Directory++captureStdout :: IO () -> IO BS.ByteString+captureStdout f = do+  tmpd <- getTemporaryDirectory+  (tmpf, tmph) <- openTempFile tmpd "haskell_stdout"+  stdout_dup <- hDuplicate stdout+  hDuplicateTo tmph stdout+  hClose tmph+  f+  hDuplicateTo stdout_dup stdout+  bs <- BS.readFile tmpf+  removeFile tmpf+  return bs
+ test-data/date-format.csv view
@@ -0,0 +1,2 @@+f1:int(16)|f2:int(16)|f3:date|f4:time|f5:datetime+500|1|2014-01-01|06:00:00|2014-0-0 06:00:00
+ test-data/quote-parsing.csv view
@@ -0,0 +1,3 @@+f1:string(7)+aoeu+"aoeu"
+ test-data/samples.csv view
@@ -0,0 +1,2 @@+f1:int(8)|f2:int(16)|f3:int(32)|f4:int(64)|f5:decimal(7,5)|f6:float|f7:double|f8:string(8)|f9:wstring(2)|f10:string(8)|f11:wstring(2)|f12:date|f13:time|f14:datetime+1|256|32768|4294967296|1.10000|1.1000|1.1000|plátano|香蕉|plátano|香蕉|2013-12-31|23:34:56|2013-12-31 23:34:56
+ test-data/samples_of_various_field_types.yxdb view

binary file changed (absent → 2691 bytes)

+ test-data/samples_without_header.csv view
@@ -0,0 +1,1 @@+1|256|32768|4294967296|1.10000|1.1000|1.1000|plátano|香蕉|plátano|香蕉|2013-12-31|23:34:56|2013-12-31 23:34:56
+ test-data/signed.csv view
@@ -0,0 +1,2 @@+f1:int(8)|f2:int(16)|f3:int(32)|f4:int(64)|f5:decimal(7,5)|f6:float|f7:double+-1|-256|-32768|-4294967296|1.10000|-1.1000|-1.1000
yxdb-utils.cabal view
@@ -10,7 +10,7 @@ -- PVP summary:      +-+------- breaking API changes --                   | | +----- non-breaking API additions --                   | | | +--- code changes with no API change-version:             0.1.0.0+version:             0.1.0.1  -- A short (one-line) description of the package. synopsis:            Utilities for reading and writing Alteryx .yxdb files@@ -41,6 +41,10 @@ -- Constraint on the version of Cabal needed to build this package. cabal-version:       >=1.8 +extra-source-files:+  test-data/*.csv+  test-data/*.yxdb+ executable csv2yxdb   hs-source-dirs: bin-src   main-is: Csv2Yxdb.hs@@ -108,6 +112,13 @@ test-suite yxdb-tests   ghc-options: -O2 -fprof-auto   hs-source-dirs: src, .+  other-modules:+    Tests.Codec.Compression.LZF.ByteString+    Tests.Data.Binary.C+    Tests.Database.Alteryx+    Tests.Database.Alteryx.Arbitrary+    Tests.Database.Utils+    Tests.Main   type: exitcode-stdio-1.0   build-depends:     Codec-Compression-LZF,@@ -152,7 +163,6 @@   buildable: True   hs-source-dirs: src   ghc-options: -rtsopts -with-rtsopts=-M512m- source-repository head   type: git   location: https://github.com/MichaelBurge/yxdb-utils