diff --git a/hls-hlint-plugin.cabal b/hls-hlint-plugin.cabal
--- a/hls-hlint-plugin.cabal
+++ b/hls-hlint-plugin.cabal
@@ -1,6 +1,6 @@
-cabal-version: 2.2
+cabal-version: 2.4
 name:          hls-hlint-plugin
-version:       1.0.1.1
+version:       1.0.2.0
 synopsis:      Hlint integration plugin with Haskell Language Server
 description:
   Please see the README on GitHub at <https://github.com/haskell/haskell-language-server#readme>
@@ -8,10 +8,17 @@
 license:       Apache-2.0
 license-file:  LICENSE
 author:        The Haskell IDE Team
-maintainer:    alan.zimm@gmail.com
+maintainer:    atreyu.bbb@gmail.com
 copyright:     The Haskell IDE Team
 category:      Development
 build-type:    Simple
+extra-source-files:
+  LICENSE
+  test/testdata/**/*.yaml
+  -- this one is not matched by the previous glob
+  test/testdata/ignore/.hlint.yaml
+  test/testdata/**/*.hs
+  test/testdata/**/*.h
 
 flag pedantic
   description: Enable -Werror
@@ -48,7 +55,7 @@
     , extra
     , filepath
     , ghc-exactprint        >=0.6.3.4
-    , ghcide                ^>=1.4
+    , ghcide                ^>=1.4 || ^>=1.5
     , hashable
     , hlint
     , hls-plugin-api        >=1.1     && <1.3
@@ -71,8 +78,8 @@
     else
       build-depends:
         , ghc
-        , ghc-lib            ==9.0.*
-        , ghc-lib-parser-ex  ==9.0.*
+        , ghc-lib            ^>=9.0
+        , ghc-lib-parser-ex  ^>=9.0
 
       cpp-options:   -DHLINT_ON_GHC_LIB
 
@@ -85,7 +92,7 @@
     else
       build-depends:
         , ghc
-        , ghc-lib            ^>=8.10.4.20210206
+        , ghc-lib            ^>=8.10.7.20210828
         , ghc-lib-parser-ex  ^>=8.10
 
       cpp-options:   -DHLINT_ON_GHC_LIB
@@ -101,3 +108,21 @@
   default-extensions:
     DataKinds
     TypeOperators
+
+test-suite tests
+  type:             exitcode-stdio-1.0
+  default-language: Haskell2010
+  hs-source-dirs:   test
+  main-is:          Main.hs
+  ghc-options:      -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      aeson
+    , base
+    , containers
+    , filepath
+    , hls-hlint-plugin
+    , hls-plugin-api
+    , hls-test-utils      >=1.0 && <1.2
+    , lens
+    , lsp-types
+    , text
diff --git a/src/Ide/Plugin/Hlint.hs b/src/Ide/Plugin/Hlint.hs
--- a/src/Ide/Plugin/Hlint.hs
+++ b/src/Ide/Plugin/Hlint.hs
@@ -9,7 +9,9 @@
 {-# LANGUAGE PackageImports        #-}
 {-# LANGUAGE PatternSynonyms       #-}
 {-# LANGUAGE ScopedTypeVariables   #-}
+{-# LANGUAGE TupleSections         #-}
 {-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE ViewPatterns          #-}
 {-# OPTIONS_GHC -Wno-orphans   #-}
 
 #ifdef HLINT_ON_GHC_LIB
@@ -33,7 +35,6 @@
 import           Data.Aeson.Types                                   (FromJSON (..),
                                                                      ToJSON (..),
                                                                      Value (..))
-import           Data.Binary
 import           Data.Default
 import qualified Data.HashMap.Strict                                as Map
 import           Data.Hashable
@@ -51,15 +52,18 @@
 
 #ifdef HLINT_ON_GHC_LIB
 import           Data.List                                          (nub)
-import           "ghc" DynFlags                                     as RealGHC.DynFlags (topDir)
-import qualified "ghc" EnumSet                                      as EnumSet
-import           "ghc" GHC                                          as RealGHC (DynFlags (..))
+import           Development.IDE.GHC.Compat.Core                    (BufSpan,
+                                                                     DynFlags,
+                                                                     extensionFlags,
+                                                                     ms_hspp_opts,
+                                                                     topDir)
+import qualified Development.IDE.GHC.Compat.Util                    as EnumSet
 import           "ghc-lib" GHC                                      hiding
                                                                     (DynFlags (..),
+                                                                     RealSrcSpan,
                                                                      ms_hspp_opts)
+import qualified "ghc-lib" GHC
 import           "ghc-lib-parser" GHC.LanguageExtensions            (Extension)
-import           "ghc" HscTypes                                     as RealGHC.HscTypes (hsc_dflags,
-                                                                                         ms_hspp_opts)
 import           Language.Haskell.GhclibParserEx.GHC.Driver.Session as GhclibParserEx (readExtension)
 import           System.FilePath                                    (takeFileName)
 import           System.IO                                          (IOMode (WriteMode),
@@ -72,9 +76,8 @@
                                                                      withFile)
 import           System.IO.Temp
 #else
-import           Development.IDE.GHC.Compat                         hiding
-                                                                    (DynFlags (..),
-                                                                     OldRealSrcSpan)
+import           Development.IDE.GHC.Compat.Core                    hiding
+                                                                    (setEnv)
 import           Language.Haskell.GHC.ExactPrint.Delta              (deltaOptions)
 import           Language.Haskell.GHC.ExactPrint.Parsers            (postParseTransform)
 import           Language.Haskell.GHC.ExactPrint.Types              (Rigidity (..))
@@ -105,14 +108,16 @@
                                                                      unsetEnv)
 -- ---------------------------------------------------------------------
 
+#ifdef HLINT_ON_GHC_LIB
 -- Reimplementing this, since the one in Development.IDE.GHC.Compat isn't for ghc-lib
-pattern OldRealSrcSpan :: RealSrcSpan -> SrcSpan
-#if MIN_GHC_API_VERSION(9,0,0)
-pattern OldRealSrcSpan span <- RealSrcSpan span _
+pattern RealSrcSpan :: GHC.RealSrcSpan -> Maybe BufSpan -> GHC.SrcSpan
+#if MIN_VERSION_ghc(9,0,0)
+pattern RealSrcSpan x y = GHC.RealSrcSpan x y
 #else
-pattern OldRealSrcSpan span <- RealSrcSpan span
+pattern RealSrcSpan x y <- ((,Nothing) -> (GHC.RealSrcSpan x, y))
 #endif
-{-# COMPLETE OldRealSrcSpan, UnhelpfulSpan #-}
+{-# COMPLETE RealSrcSpan, UnhelpfulSpan #-}
+#endif
 
 descriptor :: PluginId -> PluginDescriptor IdeState
 descriptor plId = (defaultPluginDescriptor plId)
@@ -134,7 +139,6 @@
     deriving (Eq, Show, Typeable, Generic)
 instance Hashable GetHlintDiagnostics
 instance NFData   GetHlintDiagnostics
-instance Binary   GetHlintDiagnostics
 
 type instance RuleResult GetHlintDiagnostics = ()
 
@@ -209,7 +213,7 @@
       -- This one is defined in Development.IDE.GHC.Error but here
       -- the types could come from ghc-lib or ghc
       srcSpanToRange :: SrcSpan -> LSP.Range
-      srcSpanToRange (OldRealSrcSpan span) = Range {
+      srcSpanToRange (RealSrcSpan span _) = Range {
           _start = LSP.Position {
                 _line = srcSpanStartLine span - 1
               , _character  = srcSpanStartCol span - 1}
@@ -280,7 +284,6 @@
 instance NFData ParseFlags where rnf = rwhnf
 instance Show Hint where show = const "<hint>"
 instance Show ParseFlags where show = const "<parseFlags>"
-instance Binary GetHlintSettings
 
 type instance RuleResult GetHlintSettings = (ParseFlags, [Classify], Hint)
 
@@ -482,7 +485,7 @@
                 ideaPos = (srcSpanStartLine &&& srcSpanStartCol) . toRealSrcSpan . ideaSpan
             in filter (\i -> ideaHint i == title' && ideaPos i == (l+1, c+1)) ideas
 
-          toRealSrcSpan (OldRealSrcSpan real) = real
+          toRealSrcSpan (RealSrcSpan real _) = real
           toRealSrcSpan (UnhelpfulSpan x) = error $ "No real source span: " ++ show x
 
           showParseError :: Hlint.ParseError -> String
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,331 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Main
+  ( main
+  ) where
+
+import           Control.Lens            ((^.))
+import           Data.Aeson              (Value (..), object, toJSON, (.=))
+import           Data.List               (find)
+import qualified Data.Map                as Map
+import           Data.Maybe              (fromJust, isJust)
+import qualified Data.Text               as T
+import           Ide.Plugin.Config       (Config (..), PluginConfig (..),
+                                          hlintOn)
+import qualified Ide.Plugin.Config       as Plugin
+import qualified Ide.Plugin.Hlint        as HLint
+import qualified Language.LSP.Types.Lens as L
+import           System.FilePath         ((</>))
+import           Test.Hls
+
+main :: IO ()
+main = defaultTestRunner tests
+
+hlintPlugin :: PluginDescriptor IdeState
+hlintPlugin = HLint.descriptor "hlint"
+
+tests :: TestTree
+tests = testGroup "hlint" [
+      suggestionsTests
+    , configTests
+    ]
+
+suggestionsTests :: TestTree
+suggestionsTests =
+  testGroup "hlint suggestions" [
+    testCase "provides 3.8 code actions including apply all" $ runHlintSession "" $ do
+        doc <- openDoc "Base.hs" "haskell"
+        diags@(reduceDiag:_) <- waitForDiagnosticsFromSource doc "hlint"
+
+        liftIO $ do
+            length diags @?= 2 -- "Eta Reduce" and "Redundant Id"
+            reduceDiag ^. L.range @?= Range (Position 1 0) (Position 1 12)
+            reduceDiag ^. L.severity @?= Just DsInfo
+            reduceDiag ^. L.code @?= Just (InR "refact:Eta reduce")
+            reduceDiag ^. L.source @?= Just "hlint"
+
+        cas <- map fromAction <$> getAllCodeActions doc
+
+        let applyAll = find (\ca -> "Apply all hints" `T.isSuffixOf` (ca ^. L.title)) cas
+        let redId = find (\ca -> "Redundant id" `T.isSuffixOf` (ca ^. L.title)) cas
+        let redEta = find (\ca -> "Eta reduce" `T.isSuffixOf` (ca ^. L.title)) cas
+
+        liftIO $ isJust applyAll @? "There is 'Apply all hints' code action"
+        liftIO $ isJust redId @? "There is 'Redundant id' code action"
+        liftIO $ isJust redEta @? "There is 'Eta reduce' code action"
+
+        executeCodeAction (fromJust redId)
+
+        contents <- skipManyTill anyMessage $ getDocumentEdit doc
+        liftIO $ contents @?= "main = undefined\nfoo x = x\n"
+
+    , testCase "falls back to pre 3.8 code actions" $ runSessionWithServer' [hlintPlugin] def def noLiteralCaps "test/testdata" $ do
+        doc <- openDoc "Base.hs" "haskell"
+
+        _ <- waitForDiagnosticsFromSource doc "hlint"
+
+        cars <- getAllCodeActions doc
+        etaReduce <- liftIO $ inspectCommand cars ["Eta reduce"]
+
+        executeCommand etaReduce
+
+        contents <- skipManyTill anyMessage $ getDocumentEdit doc
+        liftIO $ contents @?= "main = undefined\nfoo = id\n"
+
+    , testCase "changing document contents updates hlint diagnostics" $ runHlintSession "" $ do
+        doc <- openDoc "Base.hs" "haskell"
+        testHlintDiagnostics doc
+
+        let change = TextDocumentContentChangeEvent
+                        (Just (Range (Position 1 8) (Position 1 12)))
+                         Nothing "x"
+        changeDoc doc [change]
+        expectNoMoreDiagnostics 3 doc "hlint"
+
+        let change' = TextDocumentContentChangeEvent
+                        (Just (Range (Position 1 8) (Position 1 12)))
+                         Nothing "id x"
+        changeDoc doc [change']
+        testHlintDiagnostics doc
+
+    , knownBrokenForHlintOnGhcLib "hlint doesn't take in account cpp flag as ghc -D argument" $
+      testCase "[#554] hlint diagnostics works with CPP via ghc -XCPP argument" $ runHlintSession "cpp" $ do
+        doc <- openDoc "CppCond.hs" "haskell"
+        testHlintDiagnostics doc
+
+    , knownBrokenForHlintOnGhcLib "hlint doesn't take in account cpp flag as ghc -D argument" $
+      testCase "[#554] hlint diagnostics works with CPP via language pragma" $ runHlintSession "" $ do
+        doc <- openDoc "CppCond.hs" "haskell"
+        testHlintDiagnostics doc
+
+    , testCase "[#554] hlint diagnostics works with CPP via -XCPP argument and flag via #include header" $ runHlintSession "cpp" $ do
+        doc <- openDoc "CppHeader.hs" "haskell"
+        testHlintDiagnostics doc
+
+    , testCase "[#590] apply-refact works with -XLambdaCase argument" $ runHlintSession "lambdacase" $ do
+        testRefactor "LambdaCase.hs" "Redundant bracket"
+            expectedLambdaCase
+
+    , testCase "[#1242] apply-refact works with -XTypeApplications argument" $ runHlintSession "typeapps" $ do
+        testRefactor "TypeApplication.hs" "Redundant bracket"
+            expectedTypeApp
+
+    , testCase "apply hints works with LambdaCase via language pragma" $ runHlintSession "" $ do
+        testRefactor "LambdaCase.hs" "Redundant bracket"
+            ("{-# LANGUAGE LambdaCase #-}" : expectedLambdaCase)
+
+    , expectFailBecause "apply-refact doesn't work with cpp" $
+      testCase "apply hints works with CPP via -XCPP argument" $ runHlintSession "cpp" $ do
+        testRefactor "CppCond.hs" "Redundant bracket"
+            expectedCPP
+
+    , expectFailBecause "apply-refact doesn't work with cpp" $
+      testCase "apply hints works with CPP via language pragma" $ runHlintSession "" $ do
+        testRefactor "CppCond.hs" "Redundant bracket"
+            ("{-# LANGUAGE CPP #-}" : expectedCPP)
+
+    , testCase "hlint diagnostics ignore hints honouring .hlint.yaml" $ runHlintSession "ignore" $ do
+        doc <- openDoc "CamelCase.hs" "haskell"
+        expectNoMoreDiagnostics 3 doc "hlint"
+
+    , testCase "hlint diagnostics ignore hints honouring ANN annotations" $ runHlintSession "" $ do
+        doc <- openDoc "IgnoreAnn.hs" "haskell"
+        expectNoMoreDiagnostics 3 doc "hlint"
+
+    , knownBrokenForHlintOnRawGhc "[#838] hlint plugin doesn't honour HLINT annotations" $
+      testCase "hlint diagnostics ignore hints honouring HLINT annotations" $ runHlintSession "" $ do
+        doc <- openDoc "IgnoreAnnHlint.hs" "haskell"
+        expectNoMoreDiagnostics 3 doc "hlint"
+
+    , testCase "apply-refact preserve regular comments" $ runHlintSession "" $ do
+        testRefactor "Comments.hs" "Redundant bracket" expectedComments
+
+    , testCase "[#2290] apply all hints works with a trailing comment" $ runHlintSession "" $ do
+        testRefactor "TwoHintsAndComment.hs" "Apply all hints" expectedComments2
+
+    , testCase "applyAll is shown only when there is at least one diagnostic in range" $  runHlintSession "" $ do
+        doc <- openDoc "TwoHints.hs" "haskell"
+        _ <- waitForDiagnosticsFromSource doc "hlint"
+
+        firstLine <- map fromAction <$> getCodeActions doc (mkRange 0 0 0 0)
+        secondLine <- map fromAction <$> getCodeActions doc (mkRange 1 0 1 0)
+        thirdLine <- map fromAction <$> getCodeActions doc (mkRange 2 0 2 0)
+        multiLine <- map fromAction <$> getCodeActions doc (mkRange 0 0 2 0)
+
+        let hasApplyAll = isJust . find (\ca -> "Apply all hints" `T.isSuffixOf` (ca ^. L.title))
+
+        liftIO $ hasApplyAll firstLine @? "Missing apply all code action"
+        liftIO $ hasApplyAll secondLine @? "Missing apply all code action"
+        liftIO $ not (hasApplyAll thirdLine) @? "Unexpected apply all code action"
+        liftIO $ hasApplyAll multiLine @? "Missing apply all code action"
+
+    , knownBrokenForHlintOnRawGhc "[#2042] maybe hlint is ignoring pragmas" $
+      testCase "hlint should warn about unused extensions" $ runHlintSession "unusedext" $ do
+        doc <- openDoc "UnusedExtension.hs" "haskell"
+        diags@(unusedExt:_) <- waitForDiagnosticsFromSource doc "hlint"
+
+        liftIO $ do
+            length diags @?= 1
+            unusedExt ^. L.code @?= Just (InR "refact:Unused LANGUAGE pragma")
+
+    , knownBrokenForHlintOnGhcLib "[#1279] hlint uses a fixed set of extensions" $
+      testCase "hlint should not activate extensions like PatternSynonyms" $ runHlintSession "" $ do
+        doc <- openDoc "PatternKeyword.hs" "haskell"
+
+        waitForAllProgressDone
+        -- hlint will report a parse error if PatternSynonyms is enabled
+        expectNoMoreDiagnostics 3 doc "hlint"
+    , knownBrokenForHlintOnRawGhc "[#2280] maybe hlint is ignoring pragmas" $
+      testCase "hlint should not warn about redundant irrefutable pattern with LANGUAGE Strict" $ runHlintSession "" $ do
+        doc <- openDoc "StrictData.hs" "haskell"
+
+        waitForAllProgressDone
+
+        expectNoMoreDiagnostics 3 doc "hlint"
+    ]
+    where
+        testRefactor file caTitle expected = do
+            doc <- openDoc file "haskell"
+            testHlintDiagnostics doc
+
+            cas <- map fromAction <$> getAllCodeActions doc
+            let ca = find (\ca -> caTitle `T.isSuffixOf` (ca ^. L.title)) cas
+            liftIO $ isJust ca @? ("There is '" ++ T.unpack caTitle ++"' code action")
+
+            executeCodeAction (fromJust ca)
+
+            contents <- skipManyTill anyMessage $ getDocumentEdit doc
+            liftIO $ contents @?= T.unlines expected
+
+        expectedLambdaCase = [ "module LambdaCase where", ""
+                             , "f = \\case \"true\" -> True"
+                             , "          _ -> False"
+                             ]
+        expectedCPP =        [ "module CppCond where", ""
+                             , "#ifdef FLAG"
+                             , "f = 1"
+                             , "#else"
+                             , "g = 2"
+                             , "#endif", ""
+                             ]
+        expectedComments =   [ "-- comment before header"
+                             , "module Comments where", ""
+                             , "{-# standalone annotation #-}", ""
+                             , "-- standalone comment", ""
+                             , "-- | haddock comment"
+                             , "f = {- inline comment -}{- inline comment inside refactored code -} 1 -- ending comment", ""
+                             , "-- final comment"
+                             ]
+        expectedComments2 =  [ "module TwoHintsAndComment where"
+                             , "biggest = foldr1 max -- the line above will show two hlint hints, \"eta reduce\" and \"use maximum\""
+                             ]
+        expectedTypeApp =    [ "module TypeApplication where", ""
+                             , "a = id @Int 1"
+                             ]
+
+configTests :: TestTree
+configTests = testGroup "hlint plugin config" [
+
+      testCase "changing hlintOn configuration enables or disables hlint diagnostics" $ runHlintSession "" $ do
+        let config = def { hlintOn = True }
+        sendConfigurationChanged (toJSON config)
+
+        doc <- openDoc "Base.hs" "haskell"
+        testHlintDiagnostics doc
+
+        let config' = def { hlintOn = False }
+        sendConfigurationChanged (toJSON config')
+
+        diags' <- waitForDiagnosticsFrom doc
+
+        liftIO $ noHlintDiagnostics diags'
+
+    , testCase "changing hlint plugin configuration enables or disables hlint diagnostics" $ runHlintSession "" $ do
+        let config = def { hlintOn = True }
+        sendConfigurationChanged (toJSON config)
+
+        doc <- openDoc "Base.hs" "haskell"
+        testHlintDiagnostics doc
+
+        let config' = pluginGlobalOn config "hlint" False
+        sendConfigurationChanged (toJSON config')
+
+        diags' <- waitForDiagnosticsFrom doc
+
+        liftIO $ noHlintDiagnostics diags'
+
+    , testCase "adding hlint flags to plugin configuration removes hlint diagnostics" $ runHlintSession "" $ do
+        let config = def { hlintOn = True }
+        sendConfigurationChanged (toJSON config)
+
+        doc <- openDoc "Base.hs" "haskell"
+        testHlintDiagnostics doc
+
+        let config' = hlintConfigWithFlags ["--ignore=Redundant id", "--hint=test-hlint-config.yaml"]
+        sendConfigurationChanged (toJSON config')
+
+        diags' <- waitForDiagnosticsFrom doc
+
+        liftIO $ noHlintDiagnostics diags'
+
+    , testCase "adding hlint flags to plugin configuration adds hlint diagnostics" $ runHlintSession "" $ do
+        let config = def { hlintOn = True }
+        sendConfigurationChanged (toJSON config)
+
+        doc <- openDoc "Generalise.hs" "haskell"
+
+        expectNoMoreDiagnostics 3 doc "hlint"
+
+        let config' = hlintConfigWithFlags ["--with-group=generalise"]
+        sendConfigurationChanged (toJSON config')
+
+        diags' <- waitForDiagnosticsFromSource doc "hlint"
+        d <- liftIO $ inspectDiagnostic diags' ["Use <>"]
+
+        liftIO $ do
+            length diags' @?= 1
+            d ^. L.range @?= Range (Position 1 10) (Position 1 21)
+            d ^. L.severity @?= Just DsInfo
+    ]
+
+runHlintSession :: FilePath -> Session a -> IO a
+runHlintSession subdir  =
+    failIfSessionTimeout . runSessionWithServer hlintPlugin ("test/testdata" </> subdir)
+
+noHlintDiagnostics :: [Diagnostic] -> Assertion
+noHlintDiagnostics diags =
+    Just "hlint" `notElem` map (^. L.source) diags @? "There are no hlint diagnostics"
+
+testHlintDiagnostics :: TextDocumentIdentifier -> Session ()
+testHlintDiagnostics doc = do
+    diags <- waitForDiagnosticsFromSource doc "hlint"
+    liftIO $ length diags > 0 @? "There are hlint diagnostics"
+
+pluginGlobalOn :: Config -> T.Text -> Bool -> Config
+pluginGlobalOn config pid state = config'
+  where
+      pluginConfig = def { plcGlobalOn = state }
+      config' = def { plugins = Map.insert pid pluginConfig (plugins config) }
+
+hlintConfigWithFlags :: [T.Text] -> Config
+hlintConfigWithFlags flags =
+  def
+    { hlintOn = True
+    , Plugin.plugins = Map.fromList [("hlint",
+        def { Plugin.plcConfig = unObject $ object ["flags" .= flags] }
+    )] }
+  where
+    unObject (Object obj) = obj
+    unObject _            = undefined
+
+-- We have two main code paths in the plugin depending on how hlint interacts with ghc:
+-- * One when hlint uses ghc-lib (all ghc versions but the last version supported by hlint)
+-- * Another one when hlint uses directly ghc (only one version, which not have to be the last version supported by ghcide)
+-- As we always are using ghc through ghcide the code to get the ghc parsed AST differs
+-- So the issues and bugs usually only affects to one code path or the other.
+-- Although a given hlint version supports one direct ghc, we could use several versions of hlint
+-- each one supporting a different ghc version. It should be a temporary situation though.
+knownBrokenForHlintOnGhcLib :: String -> TestTree -> TestTree
+knownBrokenForHlintOnGhcLib = knownBrokenForGhcVersions [GHC88, GHC86]
+
+knownBrokenForHlintOnRawGhc :: String -> TestTree -> TestTree
+knownBrokenForHlintOnRawGhc = knownBrokenForGhcVersions [GHC810, GHC90]
diff --git a/test/testdata/Base.hs b/test/testdata/Base.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/Base.hs
@@ -0,0 +1,2 @@
+main = undefined
+foo x = id x
diff --git a/test/testdata/Comments.hs b/test/testdata/Comments.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/Comments.hs
@@ -0,0 +1,11 @@
+-- comment before header
+module Comments where
+
+{-# standalone annotation #-}
+
+-- standalone comment
+
+-- | haddock comment
+f = {- inline comment -} ({- inline comment inside refactored code -}1) -- ending comment
+
+-- final comment
diff --git a/test/testdata/CppCond.hs b/test/testdata/CppCond.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/CppCond.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE CPP #-}
+module CppCond where
+
+#ifdef FLAG
+f = (1)
+#else
+g = 2
+#endif
diff --git a/test/testdata/Generalise.hs b/test/testdata/Generalise.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/Generalise.hs
@@ -0,0 +1,2 @@
+main = undefined
+foo x y = [x, x] ++ y
diff --git a/test/testdata/IgnoreAnn.hs b/test/testdata/IgnoreAnn.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/IgnoreAnn.hs
@@ -0,0 +1,5 @@
+module IgnoreAnn where
+
+{-# ANN module "HLint: ignore Redundant bracket" #-}
+f = (1)
+
diff --git a/test/testdata/IgnoreAnnHlint.hs b/test/testdata/IgnoreAnnHlint.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/IgnoreAnnHlint.hs
@@ -0,0 +1,7 @@
+module IgnoreHlintAnn where
+
+{- HLINT ignore "Redundant bracket" -}
+f = (1)
+
+{-# HLINT ignore "Use camelCase" #-}
+camel_case = undefined
diff --git a/test/testdata/LambdaCase.hs b/test/testdata/LambdaCase.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/LambdaCase.hs
@@ -0,0 +1,5 @@
+{-# LANGUAGE LambdaCase #-}
+module LambdaCase where
+
+f = \case "true" -> (True)
+          _ -> False
diff --git a/test/testdata/PatternKeyword.hs b/test/testdata/PatternKeyword.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/PatternKeyword.hs
@@ -0,0 +1,3 @@
+module Foo (pattern) where
+
+pattern = 42
diff --git a/test/testdata/StrictData.hs b/test/testdata/StrictData.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/StrictData.hs
@@ -0,0 +1,2 @@
+{-# LANGUAGE Strict #-}
+f ~x = x
diff --git a/test/testdata/TwoHints.hs b/test/testdata/TwoHints.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/TwoHints.hs
@@ -0,0 +1,2 @@
+f = (1)
+g = (1)
diff --git a/test/testdata/TwoHintsAndComment.hs b/test/testdata/TwoHintsAndComment.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/TwoHintsAndComment.hs
@@ -0,0 +1,2 @@
+module TwoHintsAndComment where
+biggest items = foldr1 max items -- the line above will show two hlint hints, "eta reduce" and "use maximum"
diff --git a/test/testdata/cpp/CppCond.hs b/test/testdata/cpp/CppCond.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/cpp/CppCond.hs
@@ -0,0 +1,7 @@
+module ApplyRefact3 where
+
+#ifdef FLAG
+f = (1)
+#else
+g = 2
+#endif
diff --git a/test/testdata/cpp/CppHeader.hs b/test/testdata/cpp/CppHeader.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/cpp/CppHeader.hs
@@ -0,0 +1,9 @@
+module CppHeader where
+
+#include "test.h"
+
+#ifdef TEST
+f = (1)
+#else
+f = 1
+#endif
diff --git a/test/testdata/cpp/hie.yaml b/test/testdata/cpp/hie.yaml
new file mode 100644
--- /dev/null
+++ b/test/testdata/cpp/hie.yaml
@@ -0,0 +1,7 @@
+cradle:
+  direct:
+    arguments:
+      - "-XCPP"
+      - "-DFLAG"
+      - "CppCond"
+      - "CppHeader"
diff --git a/test/testdata/cpp/test.h b/test/testdata/cpp/test.h
new file mode 100644
--- /dev/null
+++ b/test/testdata/cpp/test.h
@@ -0,0 +1,1 @@
+#define TEST
diff --git a/test/testdata/hie.yaml b/test/testdata/hie.yaml
new file mode 100644
--- /dev/null
+++ b/test/testdata/hie.yaml
@@ -0,0 +1,16 @@
+cradle:
+  direct:
+    arguments:
+      - "-DFLAG"
+      - "-Wno-unrecognised-pragmas"
+      - "Base"
+      - "Comments"
+      - "CppCond"
+      - "Generalise"
+      - "IgnoreAnn"
+      - "IgnoreAnnHlint"
+      - "LambdaCase"
+      - "TwoHints"
+      - "PatternKeyword"
+      - "StrictData"
+      - "TwoHintsAndComment"
diff --git a/test/testdata/ignore/.hlint.yaml b/test/testdata/ignore/.hlint.yaml
new file mode 100644
--- /dev/null
+++ b/test/testdata/ignore/.hlint.yaml
@@ -0,0 +1,2 @@
+- ignore: { name: "Redundant bracket" }
+- ignore: { name: "Use camelCase" }
diff --git a/test/testdata/ignore/CamelCase.hs b/test/testdata/ignore/CamelCase.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/ignore/CamelCase.hs
@@ -0,0 +1,5 @@
+module CamelCase where
+
+f = (1)
+
+camel_case = undefined
diff --git a/test/testdata/ignore/hie.yaml b/test/testdata/ignore/hie.yaml
new file mode 100644
--- /dev/null
+++ b/test/testdata/ignore/hie.yaml
@@ -0,0 +1,4 @@
+cradle:
+  direct:
+    arguments:
+      - "CamelCase"
diff --git a/test/testdata/lambdacase/LambdaCase.hs b/test/testdata/lambdacase/LambdaCase.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/lambdacase/LambdaCase.hs
@@ -0,0 +1,4 @@
+module LambdaCase where
+
+f = \case "true" -> (True)
+          _ -> False
diff --git a/test/testdata/lambdacase/hie.yaml b/test/testdata/lambdacase/hie.yaml
new file mode 100644
--- /dev/null
+++ b/test/testdata/lambdacase/hie.yaml
@@ -0,0 +1,5 @@
+cradle:
+  direct:
+    arguments:
+      - "-XLambdaCase"
+      - "LambdaCase"
diff --git a/test/testdata/test-hlint-config.yaml b/test/testdata/test-hlint-config.yaml
new file mode 100644
--- /dev/null
+++ b/test/testdata/test-hlint-config.yaml
@@ -0,0 +1,1 @@
+- ignore: { name: Eta reduce }
diff --git a/test/testdata/typeapps/TypeApplication.hs b/test/testdata/typeapps/TypeApplication.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/typeapps/TypeApplication.hs
@@ -0,0 +1,3 @@
+module TypeApplication where
+
+a = (id @Int 1)
diff --git a/test/testdata/typeapps/hie.yaml b/test/testdata/typeapps/hie.yaml
new file mode 100644
--- /dev/null
+++ b/test/testdata/typeapps/hie.yaml
@@ -0,0 +1,5 @@
+cradle:
+  direct:
+    arguments:
+      - "-XTypeApplications"
+      - "TypeApplications"
diff --git a/test/testdata/unusedext/UnusedExtension.hs b/test/testdata/unusedext/UnusedExtension.hs
new file mode 100644
--- /dev/null
+++ b/test/testdata/unusedext/UnusedExtension.hs
@@ -0,0 +1,1 @@
+{-# LANGUAGE BangPatterns #-}
diff --git a/test/testdata/unusedext/hie.yaml b/test/testdata/unusedext/hie.yaml
new file mode 100644
--- /dev/null
+++ b/test/testdata/unusedext/hie.yaml
@@ -0,0 +1,4 @@
+cradle:
+  direct:
+    arguments:
+      - "UnusedExtension"
