knob 0.1 → 0.1.1
raw patch · 6 files changed
+439/−5 lines, 6 filesdep ~basedep ~bytestringdep ~transformersPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, bytestring, transformers
API changes (from Hackage documentation)
Files
- knob.cabal +13/−5
- scripts/common.bash +23/−0
- scripts/run-coverage +23/−0
- scripts/run-tests +20/−0
- tests/KnobTests.hs +338/−0
- tests/knob-tests.cabal +22/−0
knob.cabal view
@@ -1,5 +1,5 @@ name: knob-version: 0.1+version: 0.1.1 license: MIT license-file: license.txt author: John Millikin <jmillikin@gmail.com>@@ -13,7 +13,7 @@ synopsis: Memory-backed handles description:- Create memory-backed 'IO.Handle's, referencing virtual files. This is+ Create memory-backed 'Handle's, referencing virtual files. This is mostly useful for testing 'Handle'-based APIs without having to interact with the filesystem. .@@ -29,6 +29,14 @@ > bytes <- Data.Knob.getContents knob > putStrLn ("Wrote bytes: " ++ show bytes) +extra-source-files:+ scripts/common.bash+ scripts/run-coverage+ scripts/run-tests+ --+ tests/knob-tests.cabal+ tests/KnobTests.hs+ source-repository head type: bazaar location: https://john-millikin.com/software/knob/@@ -36,7 +44,7 @@ source-repository this type: bazaar location: https://john-millikin.com/branches/knob/0.1/- tag: knob_0.1+ tag: knob_0.1.1 library hs-source-dirs: lib@@ -44,8 +52,8 @@ build-depends: base >= 4.2 && < 5.0- , bytestring >= 0.9 && < 0.10- , transformers >= 0.2 && < 0.3+ , bytestring >= 0.9+ , transformers >= 0.2 exposed-modules: Data.Knob
+ scripts/common.bash view
@@ -0,0 +1,23 @@+PATH="$PATH:$PWD/cabal-dev/bin/"++VERSION=$(awk '/^version:/{print $2}' knob.cabal)++CABAL_DEV=$(which cabal-dev)+XZ=$(which xz)++require_cabal_dev()+{+ if [ -z "$CABAL_DEV" ]; then+ echo "Can't find 'cabal-dev' executable; make sure it exists on your "'$PATH'+ echo "Cowardly refusing to fuck with the global package database"+ exit 1+ fi+}++clean_dev_install()+{+ require_cabal_dev+ + rm -rf dist+ $CABAL_DEV install || exit 1+}
+ scripts/run-coverage view
@@ -0,0 +1,23 @@+#!/bin/bash+if [ ! -f 'knob.cabal' ]; then+ echo -n "Can't find knob.cabal; please run this script as"+ echo -n " ./scripts/run-coverage from within the knob source"+ echo " directory"+ exit 1+fi++. scripts/common.bash++require_cabal_dev++pushd tests+$CABAL_DEV -s ../cabal-dev install --flags="coverage" || exit 1+popd++rm -f knob_tests.tix+cabal-dev/bin/knob_tests $@++EXCLUDES="--exclude=Main"++hpc markup --srcdir=src/ --srcdir=tests/ knob_tests.tix --destdir=hpc-markup $EXCLUDES > /dev/null+hpc report --srcdir=src/ --srcdir=tests/ knob_tests.tix $EXCLUDES
+ scripts/run-tests view
@@ -0,0 +1,20 @@+#!/bin/bash+if [ ! -f 'knob.cabal' ]; then+ echo -n "Can't find knob.cabal; please run this script as"+ echo -n " ./scripts/run-tests from within the knob source"+ echo " directory"+ exit 1+fi++. scripts/common.bash++require_cabal_dev++clean_dev_install++pushd tests+rm -rf dist+$CABAL_DEV -s ../cabal-dev install || exit 1+popd++cabal-dev/bin/knob_tests
+ tests/KnobTests.hs view
@@ -0,0 +1,338 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}++-- Copyright (C) 2011 John Millikin <jmillikin@gmail.com>+--+-- See license.txt for details+module Main+ ( tests+ , main+ ) where++import Control.Monad.IO.Class (liftIO)+import qualified Data.ByteString+import Data.ByteString.Char8 ()+import Data.ByteString.Unsafe (unsafePackCStringLen)+import Foreign (nullPtr)+import qualified GHC.IO.Exception as GHC+import System.IO+import Test.Chell++import Data.Knob++main :: IO ()+main = Test.Chell.defaultMain tests++tests :: [Suite]+tests = [test_File, test_Duplex]++test_File :: Suite+test_File = suite "file"+ [ suite "read"+ [ test_ReadFromStart+ , test_ReadFromOffset+ , test_ReadToEOF+ , test_ReadPastEOF+ ]+ , suite "write"+ [ test_WriteFromStart+ , test_WriteFromOffset+ , test_WritePastEOF+ , test_WriteAppended+ ]+ , suite "seek"+ [ test_SeekAbsolute+ , test_SeekRelative+ , test_SeekFromEnd+ , test_SeekBeyondMaxInt+ ]+ , suite "setSize"+ [ test_SetSize_Read+ , test_SetSize_Write+ , test_SetSize_ReadWrite+ , test_SetSize_Append+ ]+ + , test_Ready+ , test_Close+ , test_SetContents+ , test_WithFileHandle+ ]++test_ReadFromStart :: Suite+test_ReadFromStart = assertions "from-start" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" ReadMode+ + bytes <- liftIO $ Data.ByteString.hGet h 3+ $expect (equal bytes "abc")+ + off <- liftIO $ hTell h+ $expect (equal off 3)++test_ReadFromOffset :: Suite+test_ReadFromOffset = assertions "from-offset" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" ReadMode+ + liftIO $ hSeek h AbsoluteSeek 1+ bytes <- liftIO $ Data.ByteString.hGet h 3+ $expect (equal bytes "bcd")+ + off <- liftIO $ hTell h+ $expect (equal off 4)++test_ReadToEOF :: Suite+test_ReadToEOF = assertions "to-eof" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" ReadMode+ + bytes <- liftIO $ Data.ByteString.hGet h 10+ $expect (equal bytes "abcde")+ + off <- liftIO $ hTell h+ $expect (equal off 5)++test_ReadPastEOF :: Suite+test_ReadPastEOF = assertions "past-eof" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" ReadMode+ + liftIO $ hSeek h AbsoluteSeek 10+ bytes <- liftIO $ Data.ByteString.hGet h 10+ $expect (equal bytes "")+ + off <- liftIO $ hTell h+ $expect (equal off 10)++test_WriteFromStart :: Suite+test_WriteFromStart = assertions "from-start" $ do+ k <- newKnob ""+ h <- newFileHandle k "foo.txt" WriteMode+ liftIO $ hSetBuffering h NoBuffering+ + liftIO $ Data.ByteString.hPut h "abcde"+ bytes <- Data.Knob.getContents k+ $expect (equal bytes "abcde")++test_WriteFromOffset :: Suite+test_WriteFromOffset = assertions "from-offset" $ do+ k <- newKnob ""+ h <- newFileHandle k "foo.txt" WriteMode+ liftIO $ hSetBuffering h NoBuffering+ + liftIO $ Data.ByteString.hPut h "abcde"+ liftIO $ hSeek h AbsoluteSeek 2+ liftIO $ Data.ByteString.hPut h "abcde"+ + bytes <- Data.Knob.getContents k+ $expect (equal bytes "ababcde")++test_WritePastEOF :: Suite+test_WritePastEOF = assertions "past-eof" $ do+ k <- newKnob ""+ h <- newFileHandle k "foo.txt" WriteMode+ liftIO $ hSetBuffering h NoBuffering+ + liftIO $ hSeek h AbsoluteSeek 2+ liftIO $ Data.ByteString.hPut h "abcde"+ bytes <- Data.Knob.getContents k+ $expect (equal bytes "\0\0abcde")++test_WriteAppended :: Suite+test_WriteAppended = assertions "appended" $ do+ k <- newKnob "foo"+ h <- newFileHandle k "foo.txt" AppendMode+ liftIO $ hSetBuffering h NoBuffering+ + liftIO $ Data.ByteString.hPut h "bar"+ bytes <- Data.Knob.getContents k+ $expect (equal bytes "foobar")++test_SeekAbsolute :: Suite+test_SeekAbsolute = assertions "absolute" $ do+ k <- newKnob ""+ h <- newFileHandle k "foo.txt" ReadMode+ + before <- liftIO $ hTell h+ liftIO $ hSeek h AbsoluteSeek 2+ after <- liftIO $ hTell h+ + $expect (equal before 0)+ $expect (equal after 2)++test_SeekRelative :: Suite+test_SeekRelative = assertions "relative" $ do+ k <- newKnob ""+ h <- newFileHandle k "foo.txt" ReadMode+ + before <- liftIO $ hTell h+ liftIO $ hSeek h RelativeSeek 2+ after1 <- liftIO $ hTell h+ liftIO $ hSeek h RelativeSeek 2+ after2 <- liftIO $ hTell h+ + $expect (equal before 0)+ $expect (equal after1 2)+ $expect (equal after2 4)++test_SeekFromEnd :: Suite+test_SeekFromEnd = assertions "from-end" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" ReadMode+ + before <- liftIO $ hTell h+ liftIO $ hSeek h SeekFromEnd (- 2)+ after <- liftIO $ hTell h+ + $expect (equal before 0)+ $expect (equal after 3)++test_SeekBeyondMaxInt :: Suite+test_SeekBeyondMaxInt = assertions "beyond-max-int" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" ReadMode+ + let intPlusOne = toInteger (maxBound :: Int) + 1+ + $expect $ throwsEq+ (GHC.IOError (Just h) GHC.InvalidArgument "hSeek" "offset > (maxBound :: Int)" Nothing (Just "foo.txt"))+ (hSeek h AbsoluteSeek intPlusOne)+ $expect $ throwsEq+ (GHC.IOError (Just h) GHC.InvalidArgument "hSeek" "offset > (maxBound :: Int)" Nothing (Just "foo.txt"))+ (hSeek h RelativeSeek intPlusOne)+ + -- testing this with real contents is difficult/impossible on a+ -- 64-bit system, so use an unsafe function to corrupt the knob's+ -- internal buffer first.+ hugeBytes <- liftIO (unsafePackCStringLen (nullPtr, maxBound))+ liftIO $ hSeek h AbsoluteSeek (intPlusOne - 1)+ setContents k hugeBytes+ $expect $ throwsEq+ (GHC.IOError (Just h) GHC.InvalidArgument "hSeek" "offset > (maxBound :: Int)" Nothing (Just "foo.txt"))+ (hSeek h SeekFromEnd 2)++test_Ready :: Suite+test_Ready = assertions "ready" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" ReadMode+ + ready <- liftIO $ hReady h+ $expect ready+ + _ <- liftIO $ Data.ByteString.hGet h 10+ $expect $ throwsEq+ (GHC.IOError (Just h) GHC.EOF "hWaitForInput" "" Nothing (Just "foo.txt"))+ (hReady h)++test_Close :: Suite+test_Close = assertions "close" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" ReadMode+ + liftIO $ hClose h+ $expect $ throwsEq+ (GHC.IOError (Just h) GHC.IllegalOperation "hGetBuf" "handle is closed" Nothing (Just "foo.txt"))+ (Data.ByteString.hGet h 1)+ $expect $ throwsEq+ (GHC.IOError (Just h) GHC.IllegalOperation "hWaitForInput" "handle is closed" Nothing (Just "foo.txt"))+ (hReady h)++test_SetSize_Read :: Suite+test_SetSize_Read = assertions "ReadMode" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" ReadMode+ + let intPlusOne = toInteger (maxBound :: Int) + 1+ $expect $ throwsEq+ (GHC.IOError (Just h) GHC.InvalidArgument "hSetFileSize" "size > (maxBound :: Int)" Nothing (Just "foo.txt"))+ (hSetFileSize h intPlusOne)+ + $expect $ throwsEq+ (GHC.IOError (Just h) GHC.IllegalOperation "hSetFileSize" "handle in ReadMode" Nothing (Just "foo.txt"))+ (hSetFileSize h 2)++test_SetSize_Write :: Suite+test_SetSize_Write = assertions "WriteMode" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" WriteMode+ + let intPlusOne = toInteger (maxBound :: Int) + 1+ $expect $ throwsEq+ (GHC.IOError (Just h) GHC.InvalidArgument "hSetFileSize" "size > (maxBound :: Int)" Nothing (Just "foo.txt"))+ (hSetFileSize h intPlusOne)+ + -- Resets contents to all NULL, regardless of offset+ liftIO $ hSeek h AbsoluteSeek 2+ liftIO $ hSetFileSize h 4+ + bytes <- Data.Knob.getContents k+ $expect (equal bytes "\0\0\0\0")++test_SetSize_ReadWrite :: Suite+test_SetSize_ReadWrite = assertions "ReadWriteMode" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" ReadWriteMode+ + let intPlusOne = toInteger (maxBound :: Int) + 1+ $expect $ throwsEq+ (GHC.IOError (Just h) GHC.InvalidArgument "hSetFileSize" "size > (maxBound :: Int)" Nothing (Just "foo.txt"))+ (hSetFileSize h intPlusOne)+ + -- Truncates contents, regardless of offset+ do+ liftIO $ hSeek h AbsoluteSeek 2+ liftIO $ hSetFileSize h 4+ bytes <- Data.Knob.getContents k+ $expect (equal bytes "abcd")+ + do+ liftIO $ hSetFileSize h 6+ bytes <- Data.Knob.getContents k+ $expect (equal bytes "abcd\0\0")++test_SetSize_Append :: Suite+test_SetSize_Append = assertions "AppendMode" $ do+ k <- newKnob "abcde"+ h <- newFileHandle k "foo.txt" AppendMode+ + let intPlusOne = toInteger (maxBound :: Int) + 1+ $expect $ throwsEq+ (GHC.IOError (Just h) GHC.InvalidArgument "hSetFileSize" "size > (maxBound :: Int)" Nothing (Just "foo.txt"))+ (hSetFileSize h intPlusOne)+ + do+ liftIO $ hSetFileSize h 4+ bytes <- Data.Knob.getContents k+ $expect (equal bytes "abcd")+ + do+ liftIO $ hSetFileSize h 6+ bytes <- Data.Knob.getContents k+ $expect (equal bytes "abcd\0\0")++test_SetContents :: Suite+test_SetContents = assertions "setContents" $ do+ k <- newKnob "abcde"+ before <- Data.Knob.getContents k+ setContents k "foo"+ after <- Data.Knob.getContents k+ + $expect (equal before "abcde")+ $expect (equal after "foo")++test_WithFileHandle :: Suite+test_WithFileHandle = assertions "withFileHandle" $ do+ k <- newKnob ""+ h <- withFileHandle k "test.txt" WriteMode $ \h -> do+ Data.ByteString.hPut h "abcde"+ return h+ + bytes <- Data.Knob.getContents k+ $expect (equal bytes "abcde")+ + closed <- liftIO $ hIsClosed h+ $expect closed++test_Duplex :: Suite+test_Duplex = suite "duplex" []
+ tests/knob-tests.cabal view
@@ -0,0 +1,22 @@+name: knob-tests+version: 0+build-type: Simple+cabal-version: >= 1.6++flag coverage+ default: False+ manual: True++executable knob_tests+ main-is: KnobTests.hs+ ghc-options: -Wall+ hs-source-dirs: ../lib,.++ if flag(coverage)+ ghc-options: -fhpc++ build-depends:+ base >= 4.2 && < 5.0+ , bytestring >= 0.9 && < 0.10+ , chell >= 0.2 && < 0.3+ , transformers >= 0.2 && < 0.3