diff --git a/shelly.cabal b/shelly.cabal
--- a/shelly.cabal
+++ b/shelly.cabal
@@ -1,6 +1,6 @@
 Name:       shelly
 
-Version:     1.6.2.1
+Version:     1.6.2.2
 Synopsis:    shell-like (systems) programming in Haskell
 
 Description: Shelly provides convenient systems programming in Haskell,
@@ -31,6 +31,8 @@
 Build-type:          Simple
 Cabal-version:       >=1.8
 
+-- for the sdist of the test suite
+extra-source-files: test/src/*.hs
 
 Library
   Exposed-modules: Shelly, Shelly.Lifted, Shelly.Pipe
@@ -68,7 +70,6 @@
   extensions:
     CPP
 
-
 source-repository head
   type:     git
   location: https://github.com/yesodweb/Shelly.hs
@@ -83,6 +84,7 @@
   main-is: TestMain.hs
   ghc-options: -O2 -Wall -fhpc -fwarn-tabs -funbox-strict-fields -threaded
                -fno-warn-unused-do-bind -fno-warn-type-defaults
+
 
   extensions: OverloadedStrings, ExtendedDefaultRules
 
diff --git a/test/src/CopySpec.hs b/test/src/CopySpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/CopySpec.hs
@@ -0,0 +1,85 @@
+{-# Language CPP #-}
+module CopySpec ( copySpec ) where
+
+import TestInit
+
+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 706
+import Prelude hiding ( FilePath, catch)
+#else
+import Prelude hiding ( FilePath)
+#endif
+import Control.Monad (forM_)
+import System.IO.Error
+import Help
+
+copySpec :: Spec
+copySpec = do
+  let b = "b" 
+  let c = "c"
+  describe "cp file" $ do
+    it "cp to same dir" $
+      forM_ [cp, cp_r] $ \copier -> do
+        res <- shelly $
+          within_dir "test/a" $ do
+            writefile b "testing"
+            copier b c
+            readfile c
+        res @?= "testing"
+
+    it "cp to other dir" $
+      forM_ [cp, cp_r] $ \copier -> do
+        res <- shelly $
+          within_dir "test/a" $ do
+            writefile b "testing"
+            mkdir c
+            copier b c
+            readfile "c/b"
+        res @?= "testing"
+
+  describe "cp dir" $ do
+    it "to dir does not exist: create the to dir" $ do
+      res <- shelly $
+        within_dir "test/a" $ do
+          mkdir b
+          writefile "b/d" ""
+          cp_r b c
+          cIsDir <- test_d c
+          liftIO $ assert $ cIsDir
+          test_f "c/d"
+      assert res
+
+    it "to dir exists: creates a nested directory, full to path given" $ do
+      res <- shelly $
+        within_dir "test/a" $ do
+          mkdir b
+          mkdir c
+          writefile "b/d" ""
+          cp_r b $ c</>b
+          cIsDir <- test_d c
+          liftIO $ assert $ cIsDir
+          bIsDir <- test_d $ c</>b
+          liftIO $ assert $ bIsDir
+          test_f "c/b/d"
+      assert res
+
+    it "to dir exists: creates a nested directory, partial to path given" $ do
+      res <- shelly $
+        within_dir "test/a" $ do
+          mkdir b
+          mkdir c
+          writefile "b/d" ""
+          cp_r b $ c
+          cIsDir <- test_d c
+          liftIO $ assert $ cIsDir
+          bIsDir <- test_d $ c</>b
+          liftIO $ assert $ bIsDir
+          test_f "c/b/d"
+      assert res
+
+    it "copies the same dir" $ do
+      shelly $
+        within_dir "test/a" $ do
+          mkdir b
+          writefile "b/d" ""
+          cp_r b b `catch_sh` (\e -> liftIO $ assert $ isUserError e)
+      assert True
diff --git a/test/src/EnvSpec.hs b/test/src/EnvSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/EnvSpec.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE CPP #-}
+module EnvSpec ( envSpec ) where
+
+import TestInit
+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 706
+import Prelude hiding ( FilePath, catch)
+#else
+import Prelude hiding ( FilePath)
+#endif
+import Data.Maybe
+
+envSpec :: Spec
+envSpec = do
+  describe "getting unset env variables" $ do
+    it "get_env" $ do
+      res <- shelly $ get_env "FOOBARSHELLY"
+      assert $ isNothing res
+
+    it "get_env_text" $ do
+      res <- shelly $ get_env_text "FOOBARSHELLY"
+      assert $ res == ""
+
+  describe "with SHELLY var set" $ do
+    it "get_env" $ do
+      res <- shelly $ do
+        setenv "SHELLY" "test"
+        get_env "SHELLY"
+      assert $ res == Just "test"
+
+    it "get_env_text" $ do
+      res <- shelly $ do
+        setenv "SHELLY" "test"
+        get_env_text "SHELLY"
+      assert $ res == "test"
+ 
+
diff --git a/test/src/FailureSpec.hs b/test/src/FailureSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/FailureSpec.hs
@@ -0,0 +1,29 @@
+module FailureSpec ( failureSpec ) where
+
+import TestInit
+
+failureSpec :: Spec
+failureSpec = do
+  let discardException action = shellyFailDir $ catchany_sh action (\_ -> return ())
+
+  describe "failure set to stderr" $
+    it "writes a failure message to stderr" $ do
+      shellyFailDir $ discardException $
+        liftIO $ shelly $ do
+          test_d ".shelly" >>= liftIO . assert . not
+          echo "testing"
+          error "bam!"
+      assert . not =<< shellyFailDir (test_d ".shelly")
+
+  describe "failure set to directory" $
+    it "writes a failure message to a .shelly directory" $ do
+      shellyFailDir $ discardException $
+        shellyFailDir $ do
+          test_d ".shelly" >>= liftIO . assert . not
+          echo "testing"
+          error "bam!"
+      assert =<< shellyFailDir ( do
+          exists <- test_d ".shelly"
+          rm_rf ".shelly"
+          return exists
+        )
diff --git a/test/src/FindSpec.hs b/test/src/FindSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/FindSpec.hs
@@ -0,0 +1,69 @@
+module FindSpec ( findSpec ) where
+
+import TestInit
+import Data.List (sort)
+
+findSpec :: Spec
+findSpec = do
+  describe "relativeTo" $ do
+    it "relative to non-existent dir" $ do
+      res <- shelly $ relativeTo "rel/" "rel/foo"
+      res @?= "foo"
+      res2 <- shelly $ relativeTo "rel" "rel/foo"
+      res2 @?= "foo"
+
+    it "relative to existing dir" $ do
+      res <- shelly $ relativeTo "test/" "test/drain.hs"
+      res @?= "drain.hs"
+      res2 <- shelly $ relativeTo "test" "test/drain.hs"
+      res2 @?= "drain.hs"
+
+    it "abs path relative to existing dir" $ do
+      res  <- shelly $ do
+        d <- pwd
+        relativeTo "test/" $ d </> "test/drain.hs"
+      res @?= "drain.hs"
+
+  describe "relative listing" $ do
+    it "lists relative files" $ do
+      res <- shelly $ cd "test/src" >> ls "."
+      sort res @?= ["./CopySpec.hs", "./EnvSpec.hs", "./FailureSpec.hs",
+                    "./FindSpec.hs", "./Help.hs", "./LiftedSpec.hs", "./LogWithSpec.hs", "./MoveSpec.hs",
+                    "./ReadFileSpec.hs", "./RmSpec.hs", "./RunSpec.hs",
+                    "./TestInit.hs", "./TestMain.hs",
+                    "./WhichSpec.hs", "./WriteSpec.hs", "./sleep.hs"]
+
+    it "finds relative files" $ do
+      res <- shelly $ cd "test/src" >> find "."
+      sort res @?= ["./CopySpec.hs", "./EnvSpec.hs", "./FailureSpec.hs",
+                    "./FindSpec.hs", "./Help.hs", "./LiftedSpec.hs", "./LogWithSpec.hs", "./MoveSpec.hs",
+                    "./ReadFileSpec.hs", "./RmSpec.hs", "./RunSpec.hs",
+                    "./TestInit.hs", "./TestMain.hs",
+                    "./WhichSpec.hs", "./WriteSpec.hs", "./sleep.hs"]
+
+  describe "find" $ do
+    it "empty list for empty dir" $ do
+      let d = "deleteme"
+      res <- shelly $ do
+        mkdir_p d
+        res <- find d
+        rm_rf d
+        return res
+      res @?= []
+
+    it "lists relative files" $ do
+      res <- shelly $ find "test/src"
+      sort res @?= ["test/src/CopySpec.hs", "test/src/EnvSpec.hs", "test/src/FailureSpec.hs",
+                    "test/src/FindSpec.hs", "test/src/Help.hs", "test/src/LiftedSpec.hs",
+                    "test/src/LogWithSpec.hs", "test/src/MoveSpec.hs", "test/src/ReadFileSpec.hs",
+                    "test/src/RmSpec.hs", "test/src/RunSpec.hs",
+                    "test/src/TestInit.hs", "test/src/TestMain.hs", "test/src/WhichSpec.hs", "test/src/WriteSpec.hs",
+                    "test/src/sleep.hs"]
+
+    it "lists absolute files" $ do
+      res <- shelly $ relPath "test/src" >>= find >>= mapM (relativeTo "test/src")
+      sort res @?= ["CopySpec.hs", "EnvSpec.hs", "FailureSpec.hs", "FindSpec.hs",
+                    "Help.hs", "LiftedSpec.hs", "LogWithSpec.hs", "MoveSpec.hs",
+                    "ReadFileSpec.hs", "RmSpec.hs", "RunSpec.hs",
+                    "TestInit.hs", "TestMain.hs",
+                    "WhichSpec.hs", "WriteSpec.hs", "sleep.hs"]
diff --git a/test/src/Help.hs b/test/src/Help.hs
new file mode 100644
--- /dev/null
+++ b/test/src/Help.hs
@@ -0,0 +1,24 @@
+module Help (
+  with_dir, within_dir,
+  (@==)
+) where
+
+import Shelly
+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 706
+import Prelude hiding ( catch, FilePath )
+#else
+import Prelude hiding ( FilePath )
+#endif
+import Test.HUnit
+import Control.Monad.Trans ( MonadIO )
+
+(@==) :: (Eq a, Show a, MonadIO m) => a -> a -> m ()
+(@==) a b = liftIO (a @?= b)
+
+with_dir :: FilePath -> Sh a -> Sh a
+with_dir d action =
+  mkdir_p d >> (action `finally_sh` rm_rf d)
+
+within_dir :: FilePath -> Sh a -> Sh a
+within_dir d action =
+  with_dir d $ chdir d action
diff --git a/test/src/LiftedSpec.hs b/test/src/LiftedSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/LiftedSpec.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module LiftedSpec ( liftedSpec ) where
+
+import Test.HUnit hiding (path)
+import Test.Hspec
+import Shelly.Lifted
+import Control.Concurrent.Async.Lifted
+import Control.Monad.Trans.Maybe
+import Test.Hspec.HUnit ()
+
+liftedSpec :: Spec
+liftedSpec =
+  describe "basic actions" $
+    it "lifted sub" $ do
+      xs <- shelly $
+          runMaybeT $ do
+              echo "Hello!"
+              sub $ withTmpDir $ \p -> wait =<< (async $ do
+                  writefile (p </> "test.txt") "hello"
+                  readfile (p </> "test.txt")
+                  )
+      xs @?= Just "hello"
diff --git a/test/src/LogWithSpec.hs b/test/src/LogWithSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/LogWithSpec.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE OverloadedStrings #-}
+module LogWithSpec ( logWithSpec ) where
+
+import TestInit
+import Prelude hiding (FilePath)
+
+import Control.Concurrent (newEmptyMVar, takeMVar, putMVar)
+import Data.Text (Text)
+default (Text)
+
+logWithSpec :: Spec
+logWithSpec =
+  describe "withOutputWriter" $
+    it "calls writer function with handler and stdout output" $ do
+      outputVar <- newEmptyMVar
+      shelly $ log_stdout_with (putMVar outputVar)
+        $ run_ "echo" ["single line output"]
+      result <- takeMVar outputVar
+      assertEqual "expecting output" "single line output" result
diff --git a/test/src/MoveSpec.hs b/test/src/MoveSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/MoveSpec.hs
@@ -0,0 +1,76 @@
+module MoveSpec (moveSpec) where
+
+import TestInit
+import Help
+
+moveSpec :: Spec
+moveSpec = do
+    let b = "b" 
+    let c = "c"
+    describe "mv file" $ do
+      it "to same dir" $ do
+        res <- shelly $
+          within_dir "test/a" $ do
+            writefile b "testing"
+            mv b c
+            readfile c
+        res @?= "testing"
+    
+      it "to other dir" $ do
+        res <- shelly $
+          within_dir "test/a" $ do
+            writefile b "testing"
+            mkdir c
+            mv b c
+            readfile "c/b"
+        res @?= "testing"
+    
+    describe "mv dir" $ do
+      it "to dir does not exist: create the to dir" $ do
+        res <- shelly $
+          within_dir "test/a" $ do
+            mkdir b
+            writefile "b/d" ""
+            mv b c
+            cIsDir <- test_d c
+            liftIO $ assert cIsDir
+            test_f "c/d"
+        assert res
+    
+      it "to dir exists: creates a nested directory, full to path given" $ do
+        res <- shelly $
+          within_dir "test/a" $ do
+            mkdir b
+            mkdir c
+            writefile "b/d" ""
+            mv b $ c</>b
+            cIsDir <- test_d c
+            liftIO $ assert cIsDir
+            bIsDir <- test_d $ c</>b
+            liftIO $ assert bIsDir
+            test_f "c/b/d"
+        assert res
+    
+      it "to dir exists: creates a nested directory, partial to path given" $ do
+        res <- shelly $
+          within_dir "test/a" $ do
+            mkdir b
+            mkdir c
+            writefile "b/d" ""
+            mv b $ c
+            cIsDir <- test_d c
+            liftIO $ assert cIsDir
+            bIsDir <- test_d $ c</>b
+            liftIO $ assert bIsDir
+            test_f "c/b/d"
+        assert res
+    
+      {-
+      it "mv the same dir" $ do
+        shelly $ do
+          within_dir "test/a" $ do
+            mkdir b
+            writefile "b/d" ""
+            mv b b `catch_sh` (\e -> liftIO $ assert $ isUserError e)
+        assert True
+        -}
diff --git a/test/src/ReadFileSpec.hs b/test/src/ReadFileSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/ReadFileSpec.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE CPP #-}
+
+module ReadFileSpec (readFileSpec) where
+
+import TestInit
+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 706
+import Prelude hiding ( FilePath, catch)
+#else
+import Prelude hiding ( FilePath)
+#endif
+import qualified Data.ByteString as BS
+import qualified Data.Text as T
+
+readFileSpec :: Spec
+readFileSpec = describe "file with invalid encoding" $ do
+    it "readBinary" $ do
+      res <- shelly $ readBinary "test/data/zshrc"
+      assert (BS.length res > 0)
+ 
+    it "readfile" $ do
+      res <- shelly $ readfile "test/data/zshrc"
+      assert (T.length res > 0)
+ 
diff --git a/test/src/RmSpec.hs b/test/src/RmSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/RmSpec.hs
@@ -0,0 +1,82 @@
+module RmSpec (rmSpec) where
+
+import TestInit
+import Help
+
+rmSpec :: Spec
+rmSpec = do
+  let b = "b"
+  let d = "dir"
+  describe "rm file" $ do
+    it "rm" $ do
+      res <- shelly $ do
+        writefile b "testing"
+        (True @==) =<< test_f b
+        rm b
+        test_f b
+      assert (not res)
+
+    it "rm_r" $ do
+      res <- shelly $ do
+        writefile b "testing"
+        (True @==) =<< test_f b
+        rm b
+        test_f b
+      assert $ not res
+
+    it "rm_f" $ do
+      res <- shelly $ do
+        (False @==) =<< test_f b
+        rm_f b
+        test_f b
+      assert $ not res
+
+  describe "rm_rf dir" $ do
+    it "empty dir" $ do
+      res <- shelly $ do
+        mkdir d
+        rm_rf d
+        test_d d
+      assert $ not res
+
+    it "dir with file" $ do
+      res <- shelly $ do
+        mkdir d
+        rm d `catchany_sh` (\_ -> return ())
+        (True @==) =<< test_d d
+        writefile (d </> b) "testing"
+        rm d `catchany_sh` (\_ -> return ())
+        (True @==) =<< test_d d
+        rm_rf d
+        test_d d
+      assert $ not res
+
+  describe "rm symlink" $ do
+    let l = "l"
+    it "rm" $ do
+      res <- shelly $ do
+        writefile b "b"
+        cmd "ln" "-s" b l
+        rm l
+        test_f b
+      assert res
+      shelly $ rm b
+
+    it "rm_f" $ do
+      res <- shelly $ do
+        writefile b "b"
+        cmd "ln" "-s" b l
+        rm_f l
+        test_f b
+      assert res
+      shelly $ rm_f b
+
+    it "rm_rf" $ do
+      res <- shelly $ do
+        mkdir d
+        writefile (d</>b) "b"
+        cmd "ln" "-s" (d</>b) l
+        rm_rf l
+        test_f (d</>b)
+      assert res
+      shelly $ rm_rf d
diff --git a/test/src/RunSpec.hs b/test/src/RunSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/RunSpec.hs
@@ -0,0 +1,26 @@
+module RunSpec ( runSpec ) where
+
+import TestInit
+
+import qualified Data.Text as T
+import System.IO
+
+runSpec :: Spec
+runSpec = do
+  describe "run" $ do
+    it "simple command" $ do
+      res <- shelly $ run "echo" [ "wibble" ]
+      res @?= "wibble\n"
+
+    it "with escaping" $ do
+      res <- shelly $ run "echo" [ "*" ]
+      res @?= "*\n"
+
+    it "without escaping" $ do
+      res <- shelly $ escaping False $ run "echo" [ "*" ]
+      assert $ "README.md" `elem` T.words res
+
+    it "with binary handle mode" $ do
+      res <- shelly $ onCommandHandles (initOutputHandles (flip hSetBinaryMode True))
+                    $ run "cat" [ "test/data/nonascii.txt" ]
+      res @?= "Selbstverst\228ndlich \252berraschend\n"
diff --git a/test/src/TestInit.hs b/test/src/TestInit.hs
new file mode 100644
--- /dev/null
+++ b/test/src/TestInit.hs
@@ -0,0 +1,10 @@
+module TestInit (module Export) where
+
+import Test.HUnit as Export hiding (path) 
+import Test.Hspec as Export
+#ifdef LIFTED
+import Shelly.Lifted as Export
+#else
+import Shelly as Export
+#endif
+import Test.Hspec.HUnit ()
diff --git a/test/src/WhichSpec.hs b/test/src/WhichSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/WhichSpec.hs
@@ -0,0 +1,17 @@
+module WhichSpec (whichSpec) where
+
+import TestInit
+
+whichSpec :: Spec
+whichSpec = describe "which" $ do
+    it "gives full path to cabal" $ do
+      Just _ <- shelly $ which "cabal"
+      assert True
+
+    it "recognizes cabal as a path executable" $ do
+      res <- shelly $ test_px "cabal"
+      True @?= res
+ 
+    it "cannot find missing exe" $ do
+      Nothing <- shelly $ which "alskjdf;ashlva;ousnva;nj"
+      assert True
diff --git a/test/src/WriteSpec.hs b/test/src/WriteSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/WriteSpec.hs
@@ -0,0 +1,36 @@
+module WriteSpec ( writeSpec ) where
+
+import TestInit
+import Prelude hiding (FilePath)
+
+import Data.Text (Text)
+default (Text)
+
+createsFile :: FilePath -> (FilePath -> IO ()) -> IO ()
+createsFile f action = do
+  exists <- shelly $ test_e f
+  when exists $ error "cleanup after yourself!"
+  action f
+  shelly $ rm f
+  return ()
+
+
+writeSpec :: Spec
+writeSpec = do
+  describe "writefile" $
+    it "creates and overwites a file" $ createsFile "foo" $ \f -> do
+      assert . (== "a") =<< (shelly $ writefile f "a" >> readfile f)
+      assert . (== "b") =<< (shelly $ writefile f "b" >> readfile f)
+
+  describe "appendfile" $
+    it "creates and appends a file" $ createsFile "foo" $ \f -> do
+      assert . (== "a")  =<< (shelly $ appendfile f "a" >> readfile f)
+      assert . (== "ab") =<< (shelly $ appendfile f "b" >> readfile f)
+
+  describe "touchfile" $
+    it "creates and updates a file" $ createsFile "foo" $ \f -> do
+      assert . (== "") =<< (shelly $ touchfile f >> readfile f)
+      assert . (== "") =<< (shelly $ touchfile f >> readfile f)
+
+      assert . (== "a") =<< (shelly $
+        writefile f "a" >> touchfile f >> readfile f)
diff --git a/test/src/sleep.hs b/test/src/sleep.hs
new file mode 100644
--- /dev/null
+++ b/test/src/sleep.hs
@@ -0,0 +1,9 @@
+{-# Language OverloadedStrings #-}
+import Shelly
+
+main :: IO ()
+main =
+  shelly $ do
+    echo "sleeping"
+    run "sleep" ["5"]
+    echo "all done"
