diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Revision history for ghc-prof
 
+## v1.4.1 - 2018-02-08
+
+* Relax upper version bound for tasty
+* Add a strict decoder (#13)
+
 ## v1.4.0.4 - 2017-12-12
 
 * Relax upper version bound for tasty-hunit
diff --git a/ghc-prof.cabal b/ghc-prof.cabal
--- a/ghc-prof.cabal
+++ b/ghc-prof.cabal
@@ -1,5 +1,5 @@
 name: ghc-prof
-version: 1.4.0.4
+version: 1.4.1
 synopsis: Library for parsing GHC time and allocation profiling reports
 description: Library for parsing GHC time and allocation profiling reports
 homepage: https://github.com/maoe/ghc-prof
@@ -68,7 +68,7 @@
     , filepath
     , ghc-prof
     , process
-    , tasty < 0.13
+    , tasty < 1.1
     , tasty-hunit >= 0.9.1 && < 0.11
     , temporary
     , text
diff --git a/src/GHC/Prof.hs b/src/GHC/Prof.hs
--- a/src/GHC/Prof.hs
+++ b/src/GHC/Prof.hs
@@ -1,5 +1,6 @@
 module GHC.Prof
   ( decode
+  , decode'
 
   -- * Parser
   , profile
@@ -29,8 +30,11 @@
   ) where
 
 import qualified Data.Attoparsec.Text.Lazy as ATL
+import qualified Data.Attoparsec.Text as AT
 import qualified Data.Text.Lazy as TL
+import qualified Data.Text as T
 
+import Control.Applicative ((<*))
 import GHC.Prof.CostCentreTree
 import GHC.Prof.Parser (profile)
 import GHC.Prof.Types
@@ -40,3 +44,7 @@
 decode text = case ATL.parse profile text of
   ATL.Fail _unconsumed _contexts reason -> Left reason
   ATL.Done _unconsumed prof -> Right prof
+
+-- | Decode a GHC time allocation profiling report from a strict 'AT.Text'
+decode' :: T.Text -> Either String Profile
+decode' text = AT.parseOnly (profile <* AT.endOfInput) text
diff --git a/tests/Regression.hs b/tests/Regression.hs
--- a/tests/Regression.hs
+++ b/tests/Regression.hs
@@ -18,6 +18,7 @@
 import qualified Data.Attoparsec.Text.Lazy as ATL
 import qualified Data.Set as Set
 import qualified Data.Text.Lazy.IO as TL
+import qualified Data.Text.IO as T
 
 import GHC.Prof
 
@@ -33,6 +34,11 @@
     for_ profiles $ \prof -> do
       step $ "Parsing " ++ prof
       assertProfile prof
+    for_ profiles $ \prof -> do
+      step $ "Decode  " ++ prof
+      assertDecode prof
+      step $ "Decode' " ++ prof
+      assertDecode' prof
 
 generateProfiles :: IO [FilePath]
 generateProfiles = do
@@ -57,17 +63,35 @@
   , ("full", "-pa")
   ]
 
+caseStudy :: Profile -> IO ()
+caseStudy prof = do
+  let actual = Set.fromList $ map Similar $ aggregatedCostCentres prof
+      expected = Set.fromList $ map Similar $ profileTopCostCentres prof
+  assertBool
+    ("Missing cost centre(s): " ++ show (Set.difference expected actual)) $
+      Set.isSubsetOf expected actual
+
+
 assertProfile :: FilePath -> Assertion
 assertProfile path = do
   text <- TL.readFile path
   case ATL.parse profile text of
-    ATL.Done _ prof -> do
-      let actual = Set.fromList $ map Similar $ aggregatedCostCentres prof
-          expected = Set.fromList $ map Similar $ profileTopCostCentres prof
-      assertBool
-        ("Missing cost centre(s): " ++ show (Set.difference expected actual)) $
-          Set.isSubsetOf expected actual
+    ATL.Done _ prof -> caseStudy prof
     ATL.Fail _ _ reason -> assertFailure reason
+
+assertDecode :: FilePath -> Assertion
+assertDecode path = do
+  text <- TL.readFile path
+  case decode text of
+    Right prof -> caseStudy prof
+    Left reason -> assertFailure reason
+
+assertDecode' :: FilePath -> Assertion
+assertDecode' path = do
+  text <- T.readFile path
+  case decode' text of
+    Right prof -> caseStudy prof
+    Left reason -> assertFailure reason
 
 newtype Similar = Similar AggregatedCostCentre
 
