diff --git a/src/System/Texrunner/Online.hs b/src/System/Texrunner/Online.hs
--- a/src/System/Texrunner/Online.hs
+++ b/src/System/Texrunner/Online.hs
@@ -63,12 +63,12 @@
 
 import           System.Texrunner.Parse
 
--- | Type for dealing with Tex's pipping interface, the current streams
---   are availble though the `MonadReader` instance.
+-- | Type for dealing with Tex's piping interface; the current streams
+--   are available though the 'MonadReader' instance.
 newtype OnlineTex a = OnlineTex {runOnlineTexT :: ReaderT TexStreams IO a}
   deriving (Functor, Applicative, Monad, MonadIO, MonadReader TexStreams)
 
--- Run a tex process, disguarding the resulting PDF.
+-- | Run a tex process, discarding the resulting PDF.
 runOnlineTex :: String      -- ^ tex command
              -> [String]    -- ^ tex command arguments
              -> ByteString  -- ^ preamble
@@ -77,8 +77,8 @@
 runOnlineTex command args preamble process =
   (\(a,_,_) -> a) <$> runOnlineTex' command args preamble process
 
--- Run a tex process, keeping the resulting PDF. The OnlineTex must receive
--- the terminating control sequence (\bye, \end{document}, \stoptext).
+-- | Run a tex process, keeping the resulting PDF. The OnlineTex must receive
+--   the terminating control sequence (\\bye, \\end{document}, \\stoptext).
 runOnlineTex' :: String
               -> [String]
               -> ByteString
@@ -144,7 +144,7 @@
 clearUnblocking :: OnlineTex ()
 clearUnblocking = getInStream >>= void . liftIO . Streams.read
 
--- | Uses a surface to open an interface with Tex,
+-- | Uses a surface to open an interface with Tex.
 mkTexHandles :: FilePath
              -> Maybe [(String, String)]
              -> String
diff --git a/src/System/Texrunner/Parse.hs b/src/System/Texrunner/Parse.hs
--- a/src/System/Texrunner/Parse.hs
+++ b/src/System/Texrunner/Parse.hs
@@ -35,7 +35,7 @@
 import           Data.ByteString.Char8            (ByteString, cons, pack)
 import qualified Data.ByteString.Char8            as B
 import           Data.Maybe
-import           Data.Monoid
+import           Data.Semigroup
 
 ------------------------------------------------------------------------
 -- Boxes
@@ -99,13 +99,16 @@
   deriving Show
 
 -- Make shift way to parse a log by combining it in this way.
-instance Monoid TexLog where
-  mempty = TexLog (TexInfo Nothing Nothing Nothing) Nothing []
-  TexLog prog pages1 errors1 `mappend` TexLog _ pages2 errors2 =
+instance Semigroup TexLog where
+  TexLog prog pages1 errors1 <> TexLog _ pages2 errors2 =
     case (pages1,pages2) of
       (Just a,_) -> TexLog prog (Just a) (errors1 ++ errors2)
       (_,b)      -> TexLog prog b (errors1 ++ errors2)
 
+instance Monoid TexLog where
+  mempty  = TexLog (TexInfo Nothing Nothing Nothing) Nothing []
+  mappend = (<>)
+
 infoParser :: Parser TexInfo
 infoParser
   = TexInfo
@@ -131,7 +134,7 @@
 -- the parse should never fail (I think)
 
 prettyPrintLog :: TexLog -> ByteString
-prettyPrintLog (TexLog {..}) =
+prettyPrintLog TexLog {..} =
   fromMaybe "unknown program" (texCommand texInfo)
   <> maybe "" (" version " <>) (texVersion texInfo)
   <> maybe "" (" " <>) (texDistribution texInfo)
@@ -173,7 +176,7 @@
   | UnknownError ByteString
   deriving (Show, Read, Eq)
 
--- Parse any line begining with "! ". Any unknown errors are returned as 'UnknownError'.
+-- | Parse any line begining with "! ". Any unknown errors are returned as 'UnknownError'.
 someError :: Parser TexError
 someError =  mark *> errors
   where
diff --git a/tests/Tex/LogParse.hs b/tests/Tex/LogParse.hs
new file mode 100644
--- /dev/null
+++ b/tests/Tex/LogParse.hs
@@ -0,0 +1,160 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
+module Tex.LogParse where
+
+import Data.ByteString.Lazy.Char8 as B (unlines, ByteString, writeFile)
+import Data.Monoid
+
+import Test.HUnit
+import Test.Framework.Providers.HUnit
+import Test.Framework as F
+
+import System.Texrunner
+import System.Texrunner.Parse
+import Control.Lens
+import Data.Foldable (for_)
+
+tests = texTests ++ latexTests ++ contextTests
+
+texTests = [checkErrors "tex error parse" tex]
+latexTests = [checkErrors "latex error parse" latex]
+contextTests = [checkErrors "context error parse" context]
+
+withHead :: Monad m => [a] -> (a -> m ()) -> m ()
+withHead (a:_) f = f a
+withHead _     _ = return ()
+
+tex e code = testCase ("tex" ++ show e) $ do
+  (exitCode, texLog, mPDF) <- runTex "pdftex" [] [] code
+  take 1 (map error' (texErrors texLog)) @?= [e]
+
+latexHeader, latexBye :: ByteString
+latexHeader = B.unlines
+  [ "\\documentclass{article}"
+  , "\\begin{document}"
+  ]
+latexBye = "\\end{document}"
+
+latex e code = testCase ("latex" ++ show e) $ do
+  (exitCode, texLog, mPDF) <- runTex "pdflatex" [] [] (latexHeader <> code)
+  head (map error' $ texErrors texLog) @?= e
+
+contextHeader, contextBye :: ByteString
+contextHeader = "\\starttext"
+contextBye = "\\stoptext"
+
+context e code = testCase ("context" ++ show e) $ do
+  (exitCode, texLog, mPDF) <- runTex "context" [] [] (contextHeader <> code)
+  take 1 (map error' (texErrors texLog)) @?= [e]
+  -- head (map error' $ texErrors texLog) @?= e
+  -- assertBool ("context" ++ show e) $ texLog `containsError` e
+
+-- Generating tex sample tex files -------------------------------------
+
+-- plain tex
+
+genTexFiles :: IO ()
+genTexFiles = for_ labeledErrors mkFile
+  where
+    mkFile (nm, (_err, xs)) = ifor_ xs $ \i x -> do
+      let doc = latexHeader <> x <> latexBye
+          name | length xs == 1 = nm
+               | otherwise      = nm <> "-" <> show (i+1)
+      B.writeFile ("tests/samples/tex/" <> name <> ".tex") doc
+
+-- latex
+
+-- pdflatex -draftmode --interaction=nonstopmode $i
+
+genLatexFiles :: IO ()
+genLatexFiles = for_ labeledErrors mkFile
+  where
+    mkFile (nm, (_err, xs)) = ifor_ xs $ \i x -> do
+      let doc = latexHeader <> x <> latexBye
+          name | length xs == 1 = nm
+               | otherwise      = nm <> "-" <> show (i+1)
+      B.writeFile ("tests/samples/latex/" <> name <> ".tex") doc
+
+-- context tex
+
+genContextFiles :: IO ()
+genContextFiles = for_ labeledErrors mkFile
+  where
+    mkFile (nm, (_err, xs)) = ifor_ xs $ \i x -> do
+      let doc = contextHeader <> x <> contextBye
+          name | length xs == 1 = nm
+               | otherwise      = nm <> "-" <> show (i+1)
+      B.writeFile ("tests/samples/context/" <> name <> ".tex") doc
+
+labeledErrors =
+  [ ("missing-dollar", missingDollar)
+  , ("dimention-too-large", dimensionTooLarge)
+  , ("illegal-unit", illegalUnit)
+  , ("missing-number", missingNumber)
+  , ("undefined-control-sequence", undefinedControlSequence)
+  ]
+
+-- Checking error parsing ----------------------------------------------
+
+containsError :: TexLog -> TexError -> Bool
+containsError log (TexError _ err) = err `elem` map error' (texErrors log)
+
+checkError :: (TexError' -> ByteString -> F.Test) -> (TexError', [ByteString]) -> F.Test
+checkError f (e, codes) = testGroup (show e) $ map (f e) codes
+
+checkErrors :: TestName -> (TexError' -> ByteString -> F.Test) ->  F.Test
+checkErrors name f = testGroup name $ map (checkError f) texErrs
+
+-- Sample errors -------------------------------------------------------
+
+texErrs =
+  [ missingDollar
+  , dimensionTooLarge
+  , illegalUnit
+  , missingNumber
+  , undefinedControlSequence
+  ]
+
+missingDollar = (,) (Missing '$')
+  [ "$x+1=2\n\n"
+  , "$$x+1=2\n\n"
+  ]
+
+dimensionTooLarge = (,) DimensionTooLarge
+  [ "\\hskip100000em"
+  ]
+
+illegalUnit = (,) IllegalUnit
+  [ "\\hskip1cn"
+  ]
+
+missingNumber = (,) MissingNumber
+  [ "\\hskip hi"
+  ]
+
+undefinedControlSequence = (,) (UndefinedControlSequence "\\hobx")
+  [ "\\hobx"
+  ]
+
+
+
+--
+-- missingDollarExample2= "x_1"
+--
+-- missingDollarExample3= "
+--
+-- numberTooBig = "10000000000"
+--
+-- overfull = "\\hbox to 1em{overfill box}"
+--
+-- underfill = "\\hbox to 20em{underfill box}"
+--
+-- illegalUnit = "\\hskip{1cn}"
+--
+-- undefinedControlSequence = "\\hobx"
+--
+-- missingNumber = "\\hskip"
+--
+--
+-- missingDollarTest = (texPutStrLn missingDollarExample, MissingDollar)
+--
diff --git a/texrunner.cabal b/texrunner.cabal
--- a/texrunner.cabal
+++ b/texrunner.cabal
@@ -1,5 +1,5 @@
 name:                texrunner
-version:             0.0.1.1
+version:             0.0.1.2
 synopsis:            Functions for running Tex from Haskell.
 description:
   texrunner is an interface to tex that attempts to parse errors and
@@ -18,6 +18,8 @@
 build-type:          Simple
 extra-source-files:  README.md, CHANGELOG.md
 cabal-version:       >=1.10
+tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
+
 source-repository head
   type:     git
   location: http://github.com/cchalmers/texrunner
@@ -28,7 +30,7 @@
     System.Texrunner.Online
     System.Texrunner.Parse
   build-depends:
-    base       >=4.6  && <4.10,
+    base       >=4.6  && <4.12,
     bytestring >=0.10 && <1.0,
     filepath,
     directory  >=1.2  && <2.0,
@@ -36,7 +38,8 @@
     process    >=1.2  && <2.0,
     mtl        >=2.1  && <3.0,
     attoparsec >=0.10 && <1.0,
-    io-streams >=1.1  && <2.0
+    io-streams >=1.1  && <2.0,
+    semigroups
   hs-source-dirs: src
   ghc-options:        -Wall
   default-language:    Haskell2010
@@ -45,10 +48,9 @@
   type:           exitcode-stdio-1.0
   hs-source-dirs: tests
   main-is:        Tests.hs
+  other-modules:  Tex.PDF, Tex.LogParse
   ghc-options:    -Wall
   default-language: Haskell2010
-  other-modules:
-    Tex.PDF
 
   build-depends:
     base,
