diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
 Ariadne
 =======
 
+**Ariadne [is not maintained][1]. If you want to take over, get in touch.**
+
+[1]: http://ro-che.info/articles/2014-11-01-rebalancing-open-source-portfolio
+
 Ariadne provides a "go-to-definition" functionality for Haskell.
 
 [Video demonstration](http://youtu.be/-sbGijbhxAc)
diff --git a/ariadne.cabal b/ariadne.cabal
--- a/ariadne.cabal
+++ b/ariadne.cabal
@@ -2,18 +2,20 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                ariadne
-version:             0.1.2.2
+version:             0.1.2.3
 synopsis:            Go-to-definition for Haskell
 description:         See <https://github.com/feuerbach/ariadne#ariadne>
 homepage:            https://github.com/feuerbach/ariadne
 license:             MIT
 license-file:        LICENSE
 author:              Roman Cheplyaka
-maintainer:          roma@ro-che.info
+maintainer:
 -- copyright:
 category:            Language
 build-type:          Simple
-extra-source-files:  README.md
+extra-source-files:
+  README.md
+  tests/test-sources/*.hs
 cabal-version:       >=1.10
 
 source-repository head
@@ -62,6 +64,9 @@
     tests .
   Main-is:
     test.hs
+  other-modules:
+    InvocationTests
+    SrcMapTests
   Build-depends:
       base >= 4 && < 5
     , tasty >= 0.7
diff --git a/tests/InvocationTests.hs b/tests/InvocationTests.hs
new file mode 100644
--- /dev/null
+++ b/tests/InvocationTests.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-}
+
+module InvocationTests (invocationTests) where
+
+import Data.BERT
+import Network.BERT.Client
+import Network.BERT.Transport
+import System.Directory
+import System.FilePath
+import qualified Data.ByteString.Lazy.UTF8 as UTF8
+import Control.Exception
+import Control.Monad
+import Test.Tasty
+import Test.Tasty.HUnit
+
+invocationTests =
+  withResource connect closeConnection $ \getTransport ->
+  withResource createRun (const cleanRun) $ \_ ->
+    testGroup "Tests"
+      [ mkTest "Local var, same file" getTransport
+        ("t1.hs", 5, 6)
+        (TupleTerm [AtomTerm "loc_known",BinaryTerm "t1.hs",IntTerm 4,IntTerm 7])
+        (return ())
+      , mkTest "Global var, different file" getTransport
+        ("t1.hs", 7, 9)
+        (TupleTerm [AtomTerm "loc_known",BinaryTerm "L.hs",IntTerm 3,IntTerm 1])
+        (return ())
+      , mkTest "Global var, not defined yet" getTransport
+        ("t1.hs", 9, 9)
+        (TupleTerm [AtomTerm "error"])
+        (return ())
+      , mkTest "Global var, just defined" getTransport
+        ("t1.hs", 9, 9)
+        (TupleTerm [AtomTerm "loc_known",BinaryTerm "L.hs",IntTerm 3,IntTerm 1])
+        (copyFile (runDir </> "Lv2.hs") (runDir </> "L.hs"))
+      , mkTest "Recursive modules" getTransport
+        ("Rec1.hs", 5, 5)
+        (TupleTerm [AtomTerm "loc_known",BinaryTerm "Rec2.hs",IntTerm 5,IntTerm 1])
+        (return ())
+      ]
+  where
+    connect =
+      tcpClient "localhost" 39014 `catch` \(e :: IOException) ->
+        throwIO $ ErrorCall "Failed to connect; is ariadne-server running?"
+
+testSourcesDir = "tests/test-sources"
+runDir = "tests/run"
+
+createRun = do
+  createDirectory runDir
+  files <- getDirectoryContents testSourcesDir
+  forM_ files $ \f -> do
+    let fp = testSourcesDir </> f
+    exists <- doesFileExist fp
+    when exists $
+      copyFile fp (runDir </> f)
+
+cleanRun = removeDirectoryRecursive runDir
+
+type Loc = (FilePath, Int {-line-}, Int {-col-})
+
+mkTest :: Transport t => String -> IO t -> Loc -> Term -> IO () -> TestTree
+mkTest name getTransport (f1, l1, c1) response prepare = testCase name $ do
+  t <- getTransport
+  prepare
+  f <- canonicalizePath $ runDir </> f1
+  r <- call t "ariadne" "find" [BinaryTerm (UTF8.fromString f), IntTerm l1, IntTerm c1]
+  case r of
+    Left err -> assertFailure $ show err
+    Right r -> relativize r @?= response
+
+relativize (TupleTerm [a@(AtomTerm "loc_known"), BinaryTerm path, l, c]) =
+  TupleTerm [a, BinaryTerm (UTF8.fromString . takeFileName . UTF8.toString $ path), l, c]
+relativize (TupleTerm [a@(AtomTerm "error"),BinaryTerm _]) = TupleTerm [a]
+relativize x = x
diff --git a/tests/SrcMapTests.hs b/tests/SrcMapTests.hs
new file mode 100644
--- /dev/null
+++ b/tests/SrcMapTests.hs
@@ -0,0 +1,19 @@
+module SrcMapTests (srcMapTests) where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import qualified Ariadne.SrcMap as SrcMap
+import Language.Haskell.Exts.SrcLoc
+
+srcMapTests = testGroup "SrcMap tests"
+  [ testCase "lookup 1" $ SrcMap.lookup (SrcLoc "" 1 3) m @?= Just ()
+  , testCase "lookup 2" $ SrcMap.lookup (SrcLoc "" 1 4) m @?= Just ()
+  , testCase "lookup 3" $ SrcMap.lookup (SrcLoc "" 1 5) m @?= Nothing
+  , testCase "lookup 4" $ SrcMap.lookup (SrcLoc "" 1 2) m @?= Nothing
+  , testCase "lookup 5" $ SrcMap.lookup (SrcLoc "" 2 3) m @?= Nothing
+  ]
+
+  where
+    span = SrcSpan "" 1 3 1 5
+    m = SrcMap.singleton span ()
diff --git a/tests/test-sources/L.hs b/tests/test-sources/L.hs
new file mode 100644
--- /dev/null
+++ b/tests/test-sources/L.hs
@@ -0,0 +1,3 @@
+module L where
+
+baz = baz
diff --git a/tests/test-sources/Lv2.hs b/tests/test-sources/Lv2.hs
new file mode 100644
--- /dev/null
+++ b/tests/test-sources/Lv2.hs
@@ -0,0 +1,3 @@
+module L where
+
+yyy = yyy
diff --git a/tests/test-sources/Rec1.hs b/tests/test-sources/Rec1.hs
new file mode 100644
--- /dev/null
+++ b/tests/test-sources/Rec1.hs
@@ -0,0 +1,5 @@
+module Rec1 where
+
+import Rec2
+
+a = b
diff --git a/tests/test-sources/Rec2.hs b/tests/test-sources/Rec2.hs
new file mode 100644
--- /dev/null
+++ b/tests/test-sources/Rec2.hs
@@ -0,0 +1,5 @@
+module Rec2 where
+
+import Rec1
+
+b = a
diff --git a/tests/test-sources/t1.hs b/tests/test-sources/t1.hs
new file mode 100644
--- /dev/null
+++ b/tests/test-sources/t1.hs
@@ -0,0 +1,9 @@
+import qualified L
+
+f x =
+  let y = x
+  in y
+
+bar = L.baz
+
+xxx = L.yyy
