diff --git a/DocTest.cabal b/DocTest.cabal
--- a/DocTest.cabal
+++ b/DocTest.cabal
@@ -1,5 +1,5 @@
 name:                DocTest
-version:             0.0.6
+version:             0.1.0
 stability:           experimental
 synopsis:            Test interactive Haskell examples
 description:         DocTest checks examples in source code comments.
@@ -16,23 +16,52 @@
 build-type:          Simple
 cabal-version:       >= 1.6
 
+extra-source-files:
+    DocTest.cabal
+    LICENSE
+    Setup.lhs
+    tests/run.sh
+    tests/TestInterpreter.hs
+    tests/runtests.sh
+    tests/Main.hs
+    tests/bugfixMultipleStatements/Fib.hs
+    tests/testImport/ModuleB.hs
+    tests/testImport/ModuleA.hs
+    tests/run_interpreter_tests.sh
+    tests/bugfixMultipleModules/ModuleB.hs
+    tests/bugfixMultipleModules/ModuleA.hs
+    tests/selftest.sh
+    tests/testCommentLocation/Foo.hs
+    tests/runtests.bat
+    tests/bugfixOutputToStdErr/Fib.hs
+    tests/testFailOnMultiline/Fib.hs
+    tests/testPutStr/Fib.hs
+    tests/bugfixWorkingDirectory/examples/Fib.hs
+    tests/bugfixWorkingDirectory/description
+    tests/bugfixWorkingDirectory/Fib.hs
+    tests/bugfixImportHierarchical/ModuleB.hs
+    tests/bugfixImportHierarchical/ModuleA.hs
+    tests/Util.hs
+
 source-repository head
     type:            git
     location:        http://code.haskell.org/~sih/code/DocTest.git/
 
 executable doctest
+    ghc-options:     -Wall
+    hs-source-dirs:  src
     main-is:         Main.hs
     other-modules:
-                       Test.DocTest
-                     , Test.DocTest.Error
-                     , Test.DocTest.Parser
-                     , Test.DocTest.Util
+                      Interpreter
+                    , Options
+                    , HaddockBackend.Api
+                    , HaddockBackend.Markup
 
     build-depends:
-                       base >= 3 && < 5
-                     , HUnit
-                     , parsec
-                     , haskell-src
-                     , directory
-                     , filepath
-                     , process
+                       base      >= 4.0.0.0 && < 4.3.0.0
+                     , containers == 0.3.*
+                     , HUnit     == 1.2.*
+                     , process   == 1.0.*
+                     , haddock   == 2.8.*
+                     , ghc-paths == 0.1.*
+                     , ghc       == 6.12.*
diff --git a/Main.hs b/Main.hs
deleted file mode 100644
--- a/Main.hs
+++ /dev/null
@@ -1,12 +0,0 @@
-module Main where
-
-import System.Environment
-import Test.DocTest
-import Test.DocTest.Parser
-import Test.HUnit
-
-main = do
-	args <- getArgs
-	docTests <- mapM parseModule args
-	tests <- mapM docTestToTestCase (concat docTests)
-	runTestTT (TestList tests)
diff --git a/Test/DocTest.hs b/Test/DocTest.hs
deleted file mode 100644
--- a/Test/DocTest.hs
+++ /dev/null
@@ -1,47 +0,0 @@
--- Copyright (c) 2009 Simon Hengel <simon.hengel@web.de>
-module Test.DocTest where
-
-import Test.HUnit
-import System.FilePath
-import System.Directory
-import Test.DocTest.Util
-import System.Process
-
-
-data DocTest = DocTest {
-	  source		:: String
-	, _module		:: String
-	, expression	:: String
-	, result		:: String
-	}
-	deriving (Show)
-
-
-_testModule :: DocTest -> String
-_testModule (DocTest source _module expression result) =
-	"import " ++ _module  ++ "\n" ++
-	"main = do\n" ++
-	"    putStr (show (" ++ expression ++ "))\n"
-
-docTestToTestCase :: DocTest -> IO Test
-docTestToTestCase test = do
-	canonicalModulePath <- canonicalizePath $ source test
-	let baseDir = packageBaseDir canonicalModulePath (_module test)
-
-	ret <- readProcess "runhaskell" ["-i" ++ baseDir] (_testModule test)
-	return (TestCase (assertEqual (source test) (result test) ret))
-
-
-
--- Maps a given source file and a corresponding module name to the base
--- directory of the package.
---
--- Example:
--- > packageBaseDir "/foo/bar/MyPackage/MyModule.hs" "MyPackage.MyModule"
--- "/foo/bar/"
-
--- > packageBaseDir "foo" "bar"
--- "foo*** Exception: Prelude.undefined
-
-packageBaseDir :: FilePath -> String -> FilePath
-packageBaseDir moduleSrcFile moduleName = stripPostfix (dropExtension moduleSrcFile) (replace "." [pathSeparator] moduleName)
diff --git a/Test/DocTest/Error.hs b/Test/DocTest/Error.hs
deleted file mode 100644
--- a/Test/DocTest/Error.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-module Test.DocTest.Error where
-
-import Data.List
-import Language.Haskell.Syntax
-import Text.ParserCombinators.Parsec
-import Text.ParserCombinators.Parsec.Error
-
-data Error = Error {
-	  location	:: SrcLoc
-	, message	:: String
-	}
-
-instance Show Error where
-	show (Error (SrcLoc filename line column) message) = intercalate ":" [filename, show line, show column, " " ++ message]
-
-reportError :: Error -> a
-reportError e = error (show e)
-
-
-_showErrorMessages = showErrorMessages "or" "unknown parse error" "expecting" "unexpected" "end of input"
-
-
-toSrcLoc :: SourcePos -> SrcLoc
-toSrcLoc x = SrcLoc (sourceName x) (sourceLine x) (sourceColumn x)
-
-toError :: ParseError -> Error
-toError e = Error (toSrcLoc (errorPos e)) (_showErrorMessages (errorMessages e))
diff --git a/Test/DocTest/Parser.hs b/Test/DocTest/Parser.hs
deleted file mode 100644
--- a/Test/DocTest/Parser.hs
+++ /dev/null
@@ -1,59 +0,0 @@
-module Test.DocTest.Parser (parseModule) where
-
-import System.IO
-import Language.Haskell.Parser hiding (parseModule)
-import Language.Haskell.Syntax
-import Text.ParserCombinators.Parsec
-import Test.DocTest
-import Test.DocTest.Error
-import Data.List
-
-parseModule :: FilePath -> IO [DocTest]
-parseModule fileName = do
-	moduleSource <- readFile fileName
-
-	let moduleName =
-		case (parseModuleWithMode (ParseMode fileName) moduleSource) of
-			ParseFailed l m								-> reportError (Error l m)
-			ParseOk (HsModule _ (Module name) _ _ _)	-> name
-
-	docTests <-
-		case (parse manyTestsParser fileName moduleSource) of
-			--Left  e -> error ("parse error at " ++ show e)
-			Left  e	-> reportError (toError e)
-			Right x	-> return x
-
-	return
-		(
-			map
-			(\(expression, result) -> DocTest fileName moduleName expression result)
-			docTests
-		)
-
--- Parser
-
-tillEndOfLine :: Parser String
-tillEndOfLine = manyTill anyChar newline
-
-commentLine :: Parser String
-commentLine = do
-	{
-		  string "-- "
-		; tillEndOfLine
-	}
-
-commentLines :: Parser [String]
-commentLines = many1 commentLine
-
-docTestStart = string "-- > "
-
-singelTestParser :: Parser (String, String) 
-singelTestParser = do
-	{
-		  manyTill anyChar (try docTestStart)
-		; expression	<- tillEndOfLine
-		; result		<- commentLines
-		; return (expression, (intercalate "\n" result))
-	}
-
-manyTestsParser = (many (try singelTestParser))
diff --git a/Test/DocTest/Util.hs b/Test/DocTest/Util.hs
deleted file mode 100644
--- a/Test/DocTest/Util.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module Test.DocTest.Util where
-
-import Data.List
-
-
--- Source: Posted from Chaddaï Fouché to Haskell-Cafe mailing list.
---
--- Example:
--- > replace "." "/" "Foo.Bar.Baz"
--- "Foo/Bar/Baz"
-replace :: (Eq a) => [a] -> [a] -> [a] -> [a]
-replace _ _ [] = []
-replace old new xs@(y:ys) =
-	case stripPrefix old xs of
-		Nothing -> y : replace old new ys
-		Just ys' -> new ++ replace old new ys' 
-
-
---removePost :: [a] -> [a] -> [a]
-
-stripPostfix [] ys = undefined
-stripPostfix (x:xs) ys
-	| xs == ys	= [x]
-	| otherwise	= x : (stripPostfix xs ys)
diff --git a/src/HaddockBackend/Api.hs b/src/HaddockBackend/Api.hs
new file mode 100644
--- /dev/null
+++ b/src/HaddockBackend/Api.hs
@@ -0,0 +1,57 @@
+module HaddockBackend.Api (
+  DocTest(..)
+, Interaction(..)
+, getDocTests
+) where
+
+import Module (moduleName, moduleNameString)
+import HaddockBackend.Markup (examplesFromInterface)
+
+import Documentation.Haddock(
+    Interface(ifaceMod, ifaceOrigFilename)
+  , exampleExpression
+  , exampleResult
+  , createInterfaces
+  , Flag
+  )
+
+
+data DocTest = DocExample {
+    source        :: String -- ^ source file
+  , module_       :: String -- ^ originating module
+  , interactions  :: [Interaction]
+} deriving Show
+
+
+data Interaction = Interaction {
+    expression :: String    -- ^ example expression
+  , result     :: [String]  -- ^ expected result
+} deriving Show
+
+
+-- | Extract 'DocTest's
+getDocTests :: [Flag]       -- ^ list of Haddock command-line flags
+            -> [String]     -- ^ file or module names
+            -> IO [DocTest] -- ^ extracted 'DocTest's
+getDocTests flags modules = do
+  interfaces <- createInterfaces flags modules
+  return $ concat $ map docTestsFromInterface interfaces
+
+
+-- | Get name of the module, that is  associated with given 'Interface'
+moduleNameFromInterface :: Interface -> String
+moduleNameFromInterface = moduleNameString . moduleName . ifaceMod
+
+
+-- | Get 'DocTest's from 'Interface'.
+docTestsFromInterface :: Interface -> [DocTest]
+docTestsFromInterface interface = map docTestFromExamples listOfExamples
+  where
+    listOfExamples    = examplesFromInterface interface
+    moduleName' = moduleNameFromInterface interface
+    fileName    = ifaceOrigFilename interface
+    docTestFromExamples examples =
+      DocExample fileName moduleName' $ map interactionFromExample examples
+      where
+        interactionFromExample e =
+          Interaction (exampleExpression e) (exampleResult e)
diff --git a/src/HaddockBackend/Markup.hs b/src/HaddockBackend/Markup.hs
new file mode 100644
--- /dev/null
+++ b/src/HaddockBackend/Markup.hs
@@ -0,0 +1,72 @@
+module HaddockBackend.Markup (examplesFromInterface) where
+
+import Name (Name)
+import qualified Data.Map as Map
+import Data.Map (Map)
+import Documentation.Haddock (
+    markup
+  , DocMarkup(..)
+  , Interface(ifaceRnDocMap, ifaceRnExportItems, ifaceRnDoc)
+  , Example
+  , DocForDecl
+  , Doc
+  , DocName
+  , ExportItem(ExportDoc)
+  )
+
+-- | Extract all 'Example's from given 'Interface'.
+examplesFromInterface :: Interface -> [[Example]]
+examplesFromInterface interface =
+  fromModuleHeader ++ fromExportItems ++ fromDeclarations
+  where
+    fromModuleHeader = case ifaceRnDoc interface of
+      Just doc  -> extract doc
+      Nothing   -> []
+    fromExportItems =
+      concatMap extractFromExportItem . ifaceRnExportItems $ interface
+      where
+        extractFromExportItem (ExportDoc doc) = extract doc
+        extractFromExportItem _ = []
+    fromDeclarations = fromDeclMap $ ifaceRnDocMap interface
+
+fromDeclMap :: Map Name (DocForDecl DocName) -> [[Example]]
+fromDeclMap docMap = concatMap docForDeclName $ Map.elems docMap
+
+docForDeclName :: DocForDecl name -> [[Example]]
+docForDeclName (declDoc, argsDoc)  = declExamples ++ argsExamples
+  where
+    declExamples = extractFromMap argsDoc
+    argsExamples = extractFromMaybe declDoc
+
+extractFromMaybe :: Maybe (Doc name) -> [[Example]]
+extractFromMaybe (Just doc) = extract doc
+extractFromMaybe Nothing    = []
+
+extractFromMap :: Map key (Doc name) -> [[Example]]
+extractFromMap m = concatMap extract $ Map.elems m
+
+-- | Extract all 'Example's from given 'Doc' node.
+extract :: Doc name -> [[Example]]
+extract = markup exampleMarkup
+  where
+    exampleMarkup :: DocMarkup name [[Example]]
+    exampleMarkup = Markup {
+      markupEmpty         = [],
+      markupString        = const [],
+      markupParagraph     = id,
+      markupAppend        = (++),
+      markupIdentifier    = const [],
+      markupModule        = const [],
+      markupEmphasis      = id,
+      markupMonospaced    = id,
+      markupUnorderedList = concat,
+      markupOrderedList   = concat,
+      markupDefList       = concat . map combineTuple,
+      markupCodeBlock     = id,
+      markupURL           = const [],
+      markupAName         = const [],
+      markupPic           = const [],
+      markupExample       = return
+      }
+      where
+        combineTuple = uncurry (++)
diff --git a/src/Interpreter.hs b/src/Interpreter.hs
new file mode 100644
--- /dev/null
+++ b/src/Interpreter.hs
@@ -0,0 +1,128 @@
+module Interpreter (
+  Interpreter
+, eval
+, withInterpreter
+) where
+
+import System.IO
+import System.Process
+import Control.Exception (bracket)
+import Data.Char
+import Data.List
+
+import GHC.Paths (ghc)
+
+-- | Truly random marker, used to separate expressions.
+--
+-- IMPORTANT: This module relies upon the fact that this marker is unique.  It
+-- has been obtained from random.org.  Do not expect this module to work
+-- properly, if you reuse it for any purpose!
+marker :: String
+marker = show "dcbd2a1e20ae519a1c7714df2859f1890581d57fac96ba3f499412b2f5c928a1"
+
+data Interpreter = Interpreter {
+    hIn  :: Handle
+  , hOut :: Handle
+  }
+
+newInterpreter :: [String] -> IO Interpreter
+newInterpreter flags = do
+  (Just stdin_, Just stdout_, Nothing, _) <- createProcess $ (proc ghc myFlags) {std_in = CreatePipe, std_out = CreatePipe, std_err = UseHandle stdout}
+  setMode stdin_
+  setMode stdout_
+  return Interpreter {hIn = stdin_, hOut = stdout_}
+  where
+    myFlags = ["-v0", "--interactive", "-ignore-dot-ghci"] ++ flags
+
+    setMode handle = do
+      hSetBinaryMode handle False
+      hSetBuffering handle LineBuffering
+      hSetEncoding handle utf8
+
+
+-- | Run an interpreter session.
+--
+-- Example:
+--
+-- >>> withInterpreter [] $ \i -> eval i "23 + 42"
+-- "65\n"
+withInterpreter
+  :: [String]               -- ^ List of flags, passed to GHC
+  -> (Interpreter -> IO a)  -- ^ Action to run
+  -> IO a                   -- ^ Result of action
+withInterpreter flags = bracket (newInterpreter flags) closeInterpreter
+
+
+closeInterpreter :: Interpreter -> IO ()
+closeInterpreter repl = do
+  hClose $ hIn repl
+  hClose $ hOut repl
+
+
+putExpression :: Interpreter -> String -> IO ()
+putExpression repl e = do
+  hPutStrLn stdin_ $ filterExpression e
+  hPutStrLn stdin_ marker
+  hFlush stdin_
+  return ()
+  where
+    stdin_ = hIn repl
+
+
+-- | Fail on unterminated multiline commands.
+--
+-- Examples:
+--
+-- >>> filterExpression ""
+-- ""
+--
+-- >>> filterExpression "foobar"
+-- "foobar"
+--
+-- >>> filterExpression ":{"
+-- "*** Exception: unterminated multiline command
+--
+-- >>> filterExpression "  :{  "
+-- "*** Exception: unterminated multiline command
+--
+-- >>> filterExpression "  :{  \nfoobar"
+-- "*** Exception: unterminated multiline command
+--
+-- >>> filterExpression "  :{  \nfoobar \n  :}  "
+-- "  :{  \nfoobar \n  :}  "
+--
+filterExpression :: String -> String
+filterExpression e =
+  case lines e of
+    [] -> e
+    l  -> if firstLine == ":{" && lastLine /= ":}" then fail_ else e
+      where
+        firstLine = strip $ head l
+        lastLine  = strip $ last l
+        fail_ = error "unterminated multiline command"
+  where
+    strip :: String -> String
+    strip = dropWhile isSpace . reverse . dropWhile isSpace . reverse
+
+
+getResult :: Interpreter -> IO String
+getResult repl = do
+  line <- hGetLine stdout_
+  if isSuffixOf marker line
+    then
+      return $ stripMarker line
+    else do
+      result <- getResult repl
+      return $ line ++ '\n' : result
+  where
+    stdout_ = hOut repl
+    stripMarker l = take (length l - length marker) l
+
+-- | Evaluate an expresion
+eval
+  :: Interpreter
+  -> String       -- Expression
+  -> IO String    -- Result
+eval repl expr = do
+  putExpression repl expr
+  getResult repl
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,45 @@
+module Main where
+
+import Test.HUnit (runTestTT, Test(..), assertEqual)
+
+import HaddockBackend.Api
+import Options
+
+import qualified Interpreter
+
+main :: IO ()
+main = do
+  (options, files) <- getOptions
+  let ghciArgs = ghcOptions options ++ files
+  Interpreter.withInterpreter ghciArgs $ \repl -> do
+
+    -- get examples from Haddock comments
+    let haddockFlags = haddockOptions options
+    docTests <- getDocTests haddockFlags files
+
+    if DumpOnly `elem` options
+      then do
+        -- dump to stdout
+        print docTests
+      else do
+        -- map to unit tests
+        let tests = TestList $ map (toTestCase repl) docTests
+        _ <- runTestTT tests
+        return ()
+
+toTestCase :: Interpreter.Interpreter -> DocTest -> Test
+toTestCase repl test = TestLabel sourceFile $ TestCase $ do
+  -- bring module into scope before running tests..
+  _ <- Interpreter.eval repl $ ":m *" ++ moduleName
+  _ <- Interpreter.eval repl $ ":reload"
+  mapM_ interactionToAssertion $ interactions test
+  where
+    moduleName = module_ test
+    sourceFile = source test
+    interactionToAssertion x = do
+      result' <- Interpreter.eval repl exampleExpression
+      assertEqual ("expression `" ++ exampleExpression ++ "'")
+        exampleResult $ lines result'
+      where
+        exampleExpression = expression x
+        exampleResult     = result x
diff --git a/src/Options.hs b/src/Options.hs
new file mode 100644
--- /dev/null
+++ b/src/Options.hs
@@ -0,0 +1,77 @@
+module Options (
+  Option(..)
+, getOptions
+, ghcOptions
+, haddockOptions
+) where
+
+import Control.Monad (when)
+import System.Environment (getArgs)
+import System.Exit
+
+import System.Console.GetOpt
+
+import qualified Documentation.Haddock as Haddock
+
+
+data Option = Help
+            | Verbose
+            | GhcOption String
+            | DumpOnly
+            deriving (Show, Eq)
+
+
+documentedOptions :: [OptDescr Option]
+documentedOptions = [
+    Option []     ["help"]        (NoArg Help)                    "display this help and exit"
+  , Option ['v']  ["verbose"]     (NoArg Verbose)                 "explain what is being done, enable Haddock warnings"
+  , Option []     ["optghc"]      (ReqArg GhcOption "OPTION")     "option to be forwarded to GHC"
+  ]
+
+undocumentedOptions :: [OptDescr Option]
+undocumentedOptions = [
+    Option []     ["dump-only"]   (NoArg DumpOnly)                "dump extracted test cases to stdout"
+  ]
+
+
+getOptions :: IO ([Option], [String])
+getOptions = do
+  args  <- getArgs
+  let (options, modules, errors) = getOpt Permute (documentedOptions ++ undocumentedOptions) args
+
+  when (Help `elem` options)
+    (printAndExit usage)
+
+  when ((not . null) errors)
+    (tryHelp $ head errors)
+
+  when (null modules)
+    (tryHelp "no input files\n")
+
+  return (options, modules)
+  where
+    printAndExit :: String -> IO a
+    printAndExit s = putStr s >> exitWith ExitSuccess
+
+    usage = usageInfo "Usage: doctest [OPTION]... MODULE...\n" documentedOptions
+
+    tryHelp message = printAndExit $ "doctest: " ++ message
+      ++ "Try `doctest --help' for more information.\n"
+
+
+-- | Extract all ghc options from given list of options.
+--
+-- Example:
+--
+-- >>> ghcOptions [Help, GhcOption "-foo", Verbose, GhcOption "-bar"]
+-- ["-foo","-bar"]
+ghcOptions :: [Option] -> [String]
+ghcOptions opts = [ option | GhcOption option <- opts ]
+
+
+-- | Format given list of options for Haddock.
+haddockOptions :: [Option] -> [Haddock.Flag]
+haddockOptions opts = verbosity : ghcOpts
+  where
+    verbosity = if (Verbose `elem` opts) then Haddock.Flag_Verbosity "3" else Haddock.Flag_NoWarnings
+    ghcOpts = map Haddock.Flag_OptGhc $ ghcOptions opts
diff --git a/tests/Main.hs b/tests/Main.hs
new file mode 100644
--- /dev/null
+++ b/tests/Main.hs
@@ -0,0 +1,73 @@
+module Main where
+
+import System.Directory (canonicalizePath)
+import System.Environment (getArgs)
+import Test.HUnit (Test(TestList), runTestTT)
+import Util
+
+main :: IO ()
+main = do
+  -- get path to doctest binary
+  [bin] <- getArgs
+  bin' <- canonicalizePath bin
+  _ <- runTestTT $ tests $ doctestTestCase bin'
+  return ();
+  where
+    tests doctest = TestList [
+
+    -- Tests
+    -- =====
+
+    --  * testImport
+        doctest "testImport" ["ModuleA.hs"]
+        (cases 2)
+      , doctest ".." ["--optghc=-itests/testImport", "tests/testImport/ModuleA.hs"]
+        (cases 2)
+
+    --  * testCommentLocation
+      , doctest "." ["testCommentLocation/Foo.hs"]
+        (cases 11)
+
+    -- * testPutStr
+      , doctest "testPutStr" ["Fib.hs"]
+        (cases 2)
+
+    -- * testFailOnMultiline
+      , doctest "testFailOnMultiline" ["Fib.hs"]
+        (cases 2) {errors = 2}
+
+    -- Bugfix tests
+    -- ============
+
+    --  * bugfixWorkingDirectory
+      , doctest "bugfixWorkingDirectory" ["Fib.hs"]
+        (cases 1)
+      , doctest "bugfixWorkingDirectory" ["examples/Fib.hs"]
+        (cases 2)
+
+    -- * bugfixOutputToStdErr
+      , doctest "bugfixOutputToStdErr" ["Fib.hs"]
+        (cases 1)
+
+    -- * bugfixMultipleStatements
+      , doctest "bugfixMultipleStatements" ["Fib.hs"]
+        (cases 1)
+
+    -- * bugfixImportHierarchical
+      , doctest "bugfixImportHierarchical" ["ModuleA.hs", "ModuleB.hs"]
+        (cases 2)
+
+    -- * bugfixMultipleModules
+      , doctest "bugfixMultipleModules" ["ModuleA.hs"]
+        (cases 3)
+
+    -- Open bugs
+    -- =========
+
+    {-
+    -- * bugFoo
+      , doctest "bugFoo" ["Foo.hs"]
+        (cases 3) {errors = 0, failures = 1}
+        -- expected: (cases 3)
+    -}
+      ]
diff --git a/tests/TestInterpreter.hs b/tests/TestInterpreter.hs
new file mode 100644
--- /dev/null
+++ b/tests/TestInterpreter.hs
@@ -0,0 +1,92 @@
+module Main where
+
+import Test.HUnit
+import Interpreter
+
+data InterpreterTest =
+  InterpreterTest
+    String        -- name
+    [Interaction] -- interactions
+
+data Interaction =
+  Interaction
+    String    -- expression
+    String    -- result
+
+ghci = Interaction
+
+tests :: [InterpreterTest]
+tests = [
+    InterpreterTest "testLocalDeclaration" [
+      ghci "let x = 10"
+      []
+    , ghci "x"
+      "10\n"
+  ]
+  , InterpreterTest "testAddition" [
+      ghci "23 + 42"
+      "65\n"
+    , ghci "putStrLn \"foo\" >> putStrLn \"bar\""
+      "foo\n\
+      \bar\n"
+  ]
+  , InterpreterTest "testImport" [
+      ghci "import Data.Maybe"
+      ""
+    , ghci "fromJust $ Just 20"
+      "20\n"
+  ]
+  , InterpreterTest "testNotInScope" [
+      ghci "foo"
+      "\n\
+      \<interactive>:1:0: Not in scope: `foo'\n"
+  ]
+  , InterpreterTest "testStdOutErr" [
+      ghci "import System.IO"
+      ""
+    , ghci "hPutStrLn stdout \"foo\""
+      "foo\n"
+    , ghci "hPutStrLn stderr \"bar\""
+      "bar\n"
+  ]
+  , InterpreterTest "testShowUnicode" [
+      ghci "\"λ\""
+      "\"\\955\"\n"
+  ]
+  , InterpreterTest "testOutputUnicode" [
+      ghci "putStrLn \"λ\""
+      "λ\n"
+  ]
+  , InterpreterTest "testSystemExit" [
+      ghci "import System.Exit"
+      ""
+    , ghci "exitWith $ ExitFailure 10"
+      "*** Exception: ExitFailure 10\n"
+  ]
+  , InterpreterTest "testPutEmptyLine" [
+      ghci "putStrLn \"\""
+      "\n"
+  ]
+  , InterpreterTest "testPutStr" [
+      ghci "putStr \"foo\""
+      "foo"
+  ]
+  ]
+
+main :: IO ()
+main = do
+  _ <- runTestTT $ TestList $ map testFromInterpreterTest tests
+  return ();
+
+testFromInterpreterTest :: InterpreterTest -> Test
+testFromInterpreterTest (InterpreterTest name expressions) =
+  TestLabel name $ TestCase $ assertionFromInteractions expressions
+  where
+    assertionFromInteractions :: [Interaction] -> Assertion
+    assertionFromInteractions l = do
+      withInterpreter [] $ \repl -> mapM_ (assertionFromInteraction repl) l
+      where
+        assertionFromInteraction :: Interpreter -> Interaction -> Assertion
+        assertionFromInteraction repl (Interaction expression result') = do
+          result <- eval repl expression
+          assertEqual expression result' result
diff --git a/tests/Util.hs b/tests/Util.hs
new file mode 100644
--- /dev/null
+++ b/tests/Util.hs
@@ -0,0 +1,73 @@
+module Util (
+    doctestTestCase
+  , Util.cases
+  , errors
+  , failures
+  ) where
+
+import System.Directory (getCurrentDirectory, setCurrentDirectory)
+import System.Exit (ExitCode(ExitSuccess))
+import System.Process (readProcessWithExitCode)
+import qualified Test.HUnit as HU
+import Test.HUnit (assertEqual, Counts(..), Test(TestCase), Assertion,
+                   showCounts)
+import Data.Char (isSpace)
+
+cases :: Int -> Counts
+cases n = Counts {
+    HU.cases = n
+  , tried    = n
+  , errors   = 0
+  , failures = 0
+  }
+
+
+-- | Construct a doctest specific 'TestCase'.
+doctestTestCase :: FilePath -- ^ absolute path to `doctest` binary
+                -> FilePath -- ^ current directory of forked `doctest` process
+                -> [String] -- ^ args, given to doctest
+                -> Counts   -- ^ expected test result
+                -> Test
+doctestTestCase doctest dir args counts = TestCase $ doctestAssert doctest dir args counts
+
+
+-- | Construct a doctest specific 'Assertion'.
+doctestAssert :: FilePath   -- ^ absolute path to `doctest` binary
+              -> FilePath   -- ^ current directory of forked `doctest` process
+              -> [String]   -- ^ args, given to `doctest`
+              -> Counts     -- ^ expected test result
+              -> Assertion
+doctestAssert doctest workingDir args counts = do
+  out <- runDoctest doctest workingDir args
+  assertEqual label (showCounts counts) (last . lines $ out)
+  where
+    label = workingDir ++ " " ++ show args
+
+
+-- | Fork and run a `doctest` process.
+runDoctest :: FilePath      -- ^ absolute path to `doctest` binary
+           -> FilePath      -- ^ current directory of forked `doctest` process
+           -> [String]      -- ^ args, given to `doctest`
+           -> IO String     -- ^ final result, as printed by `doctest`
+runDoctest doctest workingDir args = do
+  cwd <- getCurrentDirectory
+  setCurrentDirectory workingDir
+  (exit, out, err) <- readProcessWithExitCode doctest args ""
+  setCurrentDirectory cwd
+  case exit of
+    ExitSuccess -> return $ lastLine err
+    _           ->
+      error $ mayCat "STDERR:" (strip err) ++ mayCat "STDOUT:" (strip out)
+      where
+        mayCat x y = if null y then "" else unlines ["", x, y]
+  where
+    lastLine = reverse . takeWhile (/= '\r') . reverse
+
+-- | Remove leading and trailing whitespace from given string.
+--
+-- Example:
+--
+-- >>> strip "   \tfoo\nbar  \t\n "
+-- "foo\nbar"
+strip :: String -> String
+strip = dropWhile isSpace . reverse . dropWhile isSpace . reverse
diff --git a/tests/bugfixImportHierarchical/ModuleA.hs b/tests/bugfixImportHierarchical/ModuleA.hs
new file mode 100644
--- /dev/null
+++ b/tests/bugfixImportHierarchical/ModuleA.hs
@@ -0,0 +1,6 @@
+-- |
+-- >>> fib 10
+-- 55
+module ModuleA where
+
+import Foo.ModuleB
diff --git a/tests/bugfixImportHierarchical/ModuleB.hs b/tests/bugfixImportHierarchical/ModuleB.hs
new file mode 100644
--- /dev/null
+++ b/tests/bugfixImportHierarchical/ModuleB.hs
@@ -0,0 +1,12 @@
+module Foo.ModuleB (fib) where
+
+
+-- |
+-- >>> fib 10
+-- 55
+-- >>> fib 5
+-- 5
+fib :: Integer -> Integer
+fib 0 = 0
+fib 1 = 1
+fib n = fib (n - 1) + fib (n - 2)
diff --git a/tests/bugfixMultipleModules/ModuleA.hs b/tests/bugfixMultipleModules/ModuleA.hs
new file mode 100644
--- /dev/null
+++ b/tests/bugfixMultipleModules/ModuleA.hs
@@ -0,0 +1,6 @@
+-- |
+-- >>> fib 10
+-- 55
+module ModuleA where
+
+import ModuleB
diff --git a/tests/bugfixMultipleModules/ModuleB.hs b/tests/bugfixMultipleModules/ModuleB.hs
new file mode 100644
--- /dev/null
+++ b/tests/bugfixMultipleModules/ModuleB.hs
@@ -0,0 +1,20 @@
+module ModuleB (fib) where
+
+
+-- |
+-- >>> fib 10
+-- 55
+-- >>> fib 5
+-- 5
+fib :: Integer -> Integer
+fib = foo
+
+-- |
+-- >>> foo 10
+-- 55
+-- >>> foo 5
+-- 5
+foo :: Integer -> Integer
+foo 0 = 0
+foo 1 = 1
+foo n = foo (n - 1) + foo (n - 2)
diff --git a/tests/bugfixMultipleStatements/Fib.hs b/tests/bugfixMultipleStatements/Fib.hs
new file mode 100644
--- /dev/null
+++ b/tests/bugfixMultipleStatements/Fib.hs
@@ -0,0 +1,13 @@
+module Fib where
+
+import System
+
+-- | Calculate Fibonacci number of given 'Num'.
+--
+-- >>> let x = 10
+-- >>> x
+-- 10
+fib :: (Num t, Num t1) => t -> t1
+fib _ = undefined
+
+bar = 10
diff --git a/tests/bugfixOutputToStdErr/Fib.hs b/tests/bugfixOutputToStdErr/Fib.hs
new file mode 100644
--- /dev/null
+++ b/tests/bugfixOutputToStdErr/Fib.hs
@@ -0,0 +1,11 @@
+module Fib where
+
+import System
+
+-- | Calculate Fibonacci number of given 'Num'.
+--
+-- >>> import System.IO
+-- >>> hPutStrLn stderr "foobar"
+-- foobar
+fib :: (Num t, Num t1) => t -> t1
+fib _ = undefined
diff --git a/tests/bugfixWorkingDirectory/Fib.hs b/tests/bugfixWorkingDirectory/Fib.hs
new file mode 100644
--- /dev/null
+++ b/tests/bugfixWorkingDirectory/Fib.hs
@@ -0,0 +1,10 @@
+module Fib where
+
+-- | Calculate Fibonacci number of given 'Num'.
+--
+-- >>> bar
+-- 10
+fib :: (Num t, Num t1) => t -> t1
+fib _ = undefined
+
+bar = 10
diff --git a/tests/bugfixWorkingDirectory/description b/tests/bugfixWorkingDirectory/description
new file mode 100644
--- /dev/null
+++ b/tests/bugfixWorkingDirectory/description
@@ -0,0 +1,10 @@
+Put the following files in the current working directory:
+
+    ./Fib.hs
+    ./examples/Fib.hs
+
+Now run:
+
+    doctest examples/Fib.hs
+
+Erroneously `./Fib.hs` will be tested instead of `examples/Fib.hs`.
diff --git a/tests/bugfixWorkingDirectory/examples/Fib.hs b/tests/bugfixWorkingDirectory/examples/Fib.hs
new file mode 100644
--- /dev/null
+++ b/tests/bugfixWorkingDirectory/examples/Fib.hs
@@ -0,0 +1,16 @@
+module Fib where
+
+
+-- | Calculate Fibonacci number of given 'Num'.
+--
+-- Examples:
+--
+--    >>> fib 10
+--    55
+--
+--    >>> fib 5
+--    5
+fib :: (Num t, Num t1) => t -> t1
+fib 0 = 0
+fib 1 = 1
+fib n = fib (n - 1) + fib (n - 2)
diff --git a/tests/run.sh b/tests/run.sh
new file mode 100644
--- /dev/null
+++ b/tests/run.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -o nounset
+set -o errexit
+
+cd "`dirname $0`"
+
+./run_interpreter_tests.sh
+./runtests.sh
+./selftest.sh
diff --git a/tests/run_interpreter_tests.sh b/tests/run_interpreter_tests.sh
new file mode 100644
--- /dev/null
+++ b/tests/run_interpreter_tests.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+cd "`dirname $0`"
+runhaskell -i../src TestInterpreter.hs
diff --git a/tests/runtests.bat b/tests/runtests.bat
new file mode 100644
--- /dev/null
+++ b/tests/runtests.bat
@@ -0,0 +1,1 @@
+runhaskell -hide-all-packages -packagebase -packageHUnit -packageprocess -packagedirectory Main.hs ../dist/build/doctest/doctest
diff --git a/tests/runtests.sh b/tests/runtests.sh
new file mode 100644
--- /dev/null
+++ b/tests/runtests.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+cd "`dirname $0`"
+
+DOCTEST="${1:-"../dist/build/doctest/doctest"}"
+
+if [ -x "$DOCTEST" ]; then
+    runhaskell -hide-all-packages \
+               -packagebase \
+               -packageHUnit \
+               -packageprocess \
+               -packagedirectory \
+                Main.hs "$DOCTEST"
+else
+    echo "$DOCTEST is not executable!"
+fi
diff --git a/tests/selftest.sh b/tests/selftest.sh
new file mode 100644
--- /dev/null
+++ b/tests/selftest.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+cd "`dirname $0`"
+
+DOCTEST="${1:-"../dist/build/doctest/doctest"}"
+
+if [ -x "$DOCTEST" ]; then
+    SRCDIR="../src"
+    "$DOCTEST" --optghc=-packageghc --optghc=-i$SRCDIR $SRCDIR/Main.hs
+else
+    echo "$DOCTEST is not executable!"
+fi
diff --git a/tests/testCommentLocation/Foo.hs b/tests/testCommentLocation/Foo.hs
new file mode 100644
--- /dev/null
+++ b/tests/testCommentLocation/Foo.hs
@@ -0,0 +1,75 @@
+-- |
+-- Examples in various locations...
+--
+-- Some random text.  Some random text.  Some random text.  Some random text.
+-- Some random text.  Some random text.  Some random text.  Some random text.
+-- Some random text.
+--
+-- >>> let x = 10
+--
+-- Some random text.  Some random text.  Some random text.  Some random text.
+-- Some random text.  Some random text.  Some random text.  Some random text.
+-- Some random text.
+--
+--
+--   >>> baz
+--   "foobar"
+
+module Foo (
+  -- | Some documentation not attached to a particular Haskell entity
+  --
+  -- >>> test 10
+  -- *** Exception: Prelude.undefined
+  test,
+
+  -- |
+  -- >>> fib 10
+  -- 55
+  fib,
+
+  -- |
+  -- >>> bar
+  -- "bar"
+  bar
+  ) where
+
+
+-- | My test
+--
+-- >>> test 20
+-- *** Exception: Prelude.undefined
+test :: Integer -> Integer
+test = undefined
+
+-- |
+-- >>> foo
+-- "foo"
+
+{- |
+    Example:
+
+     >>> fib 10
+     55
+-}
+
+-- | Calculate Fibonacci number of given `n`.
+fib :: Integer  -- ^ given `n`
+                --
+                -- >>> fib 10
+                -- 55
+
+    -> Integer  -- ^ Fibonacci of given `n`
+                --
+                -- >>> baz
+                -- "foobar"
+fib 0 = 0
+fib 1 = 1
+fib n = fib (n - 1) + fib (n - 2)
+-- ^ Example:
+--
+--   >>> fib 5
+--   5
+
+foo = "foo"
+bar = "bar"
+baz = foo ++ bar
diff --git a/tests/testFailOnMultiline/Fib.hs b/tests/testFailOnMultiline/Fib.hs
new file mode 100644
--- /dev/null
+++ b/tests/testFailOnMultiline/Fib.hs
@@ -0,0 +1,13 @@
+module Fib where
+
+import System
+
+-- | Calculate Fibonacci number of given 'Num'.
+--
+-- The following interactions cause `doctest' to fail with an error:
+--
+-- >>> :{
+--
+-- >>>       :{
+fib :: (Num t, Num t1) => t -> t1
+fib _ = undefined
diff --git a/tests/testImport/ModuleA.hs b/tests/testImport/ModuleA.hs
new file mode 100644
--- /dev/null
+++ b/tests/testImport/ModuleA.hs
@@ -0,0 +1,6 @@
+-- |
+-- >>> fib 10
+-- 55
+module ModuleA where
+
+import ModuleB
diff --git a/tests/testImport/ModuleB.hs b/tests/testImport/ModuleB.hs
new file mode 100644
--- /dev/null
+++ b/tests/testImport/ModuleB.hs
@@ -0,0 +1,12 @@
+module ModuleB (fib) where
+
+
+-- |
+-- >>> fib 10
+-- 55
+-- >>> fib 5
+-- 5
+fib :: Integer -> Integer
+fib 0 = 0
+fib 1 = 1
+fib n = fib (n - 1) + fib (n - 2)
diff --git a/tests/testPutStr/Fib.hs b/tests/testPutStr/Fib.hs
new file mode 100644
--- /dev/null
+++ b/tests/testPutStr/Fib.hs
@@ -0,0 +1,15 @@
+module Fib where
+
+import System
+
+-- | Calculate Fibonacci number of given 'Num'.
+--
+-- >>> putStrLn "foo"
+-- foo
+-- >>> putStr "bar"
+-- bar
+--
+-- >>> putStrLn "baz"
+-- baz
+fib :: (Num t, Num t1) => t -> t1
+fib _ = undefined
