Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a14985a

Browse files
committedAug 28, 2023
Introduce declarative test project definition
Test data is currently often in some 'testdata' directory in 'test/'. It can easily get quite messy with many files. Especially since 'lsp-test' should load only exactly what is needed to speed up tests and avoid test flakiness. A subdirectory per test file is quite overkill and increases the required boilerplate to write tests. Thus, we introduce a declarative test project specification that runs lsp-test in a temporary directory. The first advantage is that we can load only exactly what we need, and create more accurate projects.
1 parent 663c0e7 commit a14985a

File tree

4 files changed

+448
-9
lines changed

4 files changed

+448
-9
lines changed
 

‎hls-test-utils/hls-test-utils.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ library
2828
exposed-modules:
2929
Test.Hls
3030
Test.Hls.Util
31+
Test.Hls.FileSystem
3132

3233
hs-source-dirs: src
3334
build-depends:

‎hls-test-utils/src/Test/Hls.hs

Lines changed: 213 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,22 @@ module Test.Hls
2121
defaultTestRunner,
2222
goldenGitDiff,
2323
goldenWithHaskellDoc,
24+
goldenWithHaskellDocInTmpDir,
2425
goldenWithHaskellAndCaps,
26+
goldenWithHaskellAndCapsInTmpDir,
2527
goldenWithCabalDoc,
2628
goldenWithHaskellDocFormatter,
29+
goldenWithHaskellDocFormatterInTmpDir,
2730
goldenWithCabalDocFormatter,
31+
goldenWithCabalDocFormatterInTmpDir,
2832
def,
2933
-- * Running HLS for integration tests
3034
runSessionWithServer,
3135
runSessionWithServerAndCaps,
36+
runSessionWithServerInTmpDir,
37+
runSessionWithServerAndCapsInTmpDir,
3238
runSessionWithServer',
39+
runSessionWithServerInTmpDir',
3340
-- * Helpful re-exports
3441
PluginDescriptor,
3542
IdeState,
@@ -90,11 +97,13 @@ import GHC.Stack (emptyCallStack)
9097
import GHC.TypeLits
9198
import Ide.Logger (Doc, Logger (Logger),
9299
Pretty (pretty),
93-
Priority (Debug),
100+
Priority (..),
94101
Recorder (Recorder, logger_),
95102
WithPriority (WithPriority, priority),
96103
cfilter, cmapWithPrio,
97-
makeDefaultStderrRecorder)
104+
logWith,
105+
makeDefaultStderrRecorder,
106+
(<+>))
98107
import Ide.Types
99108
import Language.LSP.Protocol.Capabilities
100109
import Language.LSP.Protocol.Message
@@ -105,9 +114,12 @@ import System.Directory (getCurrentDirectory,
105114
setCurrentDirectory)
106115
import System.Environment (lookupEnv)
107116
import System.FilePath
117+
import System.IO.Extra (newTempDir, withTempDir)
108118
import System.IO.Unsafe (unsafePerformIO)
109119
import System.Process.Extra (createPipe)
110120
import System.Time.Extra
121+
import qualified Test.Hls.FileSystem as FS
122+
import Test.Hls.FileSystem
111123
import Test.Hls.Util
112124
import Test.Tasty hiding (Timeout)
113125
import Test.Tasty.ExpectedFailure
@@ -116,11 +128,26 @@ import Test.Tasty.HUnit
116128
import Test.Tasty.Ingredients.Rerun
117129
import Test.Tasty.Runners (NumThreads (..))
118130

119-
newtype Log = LogIDEMain IDEMain.Log
131+
data Log
132+
= LogIDEMain IDEMain.Log
133+
| LogTestHarness LogTestHarness
120134

121135
instance Pretty Log where
122136
pretty = \case
123-
LogIDEMain log -> pretty log
137+
LogIDEMain log -> pretty log
138+
LogTestHarness log -> pretty log
139+
140+
data LogTestHarness
141+
= LogTestDir FilePath
142+
| LogCleanup
143+
| LogNoCleanup
144+
145+
146+
instance Pretty LogTestHarness where
147+
pretty = \case
148+
LogTestDir dir -> "Test Project located in directory:" <+> pretty dir
149+
LogCleanup -> "Cleaned up temporary directory"
150+
LogNoCleanup -> "No cleanup of temporary directory"
124151

125152
-- | Run 'defaultMainWithRerun', limiting each single test case running at most 10 minutes
126153
defaultTestRunner :: TestTree -> IO ()
@@ -145,6 +172,19 @@ goldenWithHaskellDoc
145172
-> TestTree
146173
goldenWithHaskellDoc = goldenWithDoc "haskell"
147174

175+
goldenWithHaskellDocInTmpDir
176+
:: Pretty b
177+
=> Config
178+
-> PluginTestDescriptor b
179+
-> TestName
180+
-> VirtualFileTree
181+
-> FilePath
182+
-> FilePath
183+
-> FilePath
184+
-> (TextDocumentIdentifier -> Session ())
185+
-> TestTree
186+
goldenWithHaskellDocInTmpDir = goldenWithDocInTmpDir "haskell"
187+
148188
goldenWithHaskellAndCaps
149189
:: Pretty b
150190
=> Config
@@ -167,6 +207,28 @@ goldenWithHaskellAndCaps config clientCaps plugin title testDataDir path desc ex
167207
act doc
168208
documentContents doc
169209

210+
goldenWithHaskellAndCapsInTmpDir
211+
:: Pretty b
212+
=> Config
213+
-> ClientCapabilities
214+
-> PluginTestDescriptor b
215+
-> TestName
216+
-> VirtualFileTree
217+
-> FilePath
218+
-> FilePath
219+
-> FilePath
220+
-> (TextDocumentIdentifier -> Session ())
221+
-> TestTree
222+
goldenWithHaskellAndCapsInTmpDir config clientCaps plugin title tree path desc ext act =
223+
goldenGitDiff title (vftOriginalRoot tree </> path <.> desc <.> ext)
224+
$ runSessionWithServerAndCapsInTmpDir config plugin clientCaps tree
225+
$ TL.encodeUtf8 . TL.fromStrict
226+
<$> do
227+
doc <- openDoc (path <.> ext) "haskell"
228+
void waitForBuildQueue
229+
act doc
230+
documentContents doc
231+
170232
goldenWithCabalDoc
171233
:: Pretty b
172234
=> Config
@@ -202,6 +264,28 @@ goldenWithDoc fileType config plugin title testDataDir path desc ext act =
202264
act doc
203265
documentContents doc
204266

267+
goldenWithDocInTmpDir
268+
:: Pretty b
269+
=> T.Text
270+
-> Config
271+
-> PluginTestDescriptor b
272+
-> TestName
273+
-> VirtualFileTree
274+
-> FilePath
275+
-> FilePath
276+
-> FilePath
277+
-> (TextDocumentIdentifier -> Session ())
278+
-> TestTree
279+
goldenWithDocInTmpDir fileType config plugin title tree path desc ext act =
280+
goldenGitDiff title (vftOriginalRoot tree </> path <.> desc <.> ext)
281+
$ runSessionWithServerInTmpDir config plugin tree
282+
$ TL.encodeUtf8 . TL.fromStrict
283+
<$> do
284+
doc <- openDoc (path <.> ext) fileType
285+
void waitForBuildQueue
286+
act doc
287+
documentContents doc
288+
205289
-- ------------------------------------------------------------
206290
-- Helper function for initialising plugins under test
207291
-- ------------------------------------------------------------
@@ -298,6 +382,76 @@ runSessionWithServerAndCaps config plugin caps fp act = do
298382
recorder <- pluginTestRecorder
299383
runSessionWithServer' (plugin recorder) config def caps fp act
300384

385+
runSessionWithServerInTmpDir :: Pretty b => Config -> PluginTestDescriptor b -> VirtualFileTree -> Session a -> IO a
386+
runSessionWithServerInTmpDir config plugin tree act = do
387+
recorder <- pluginTestRecorder
388+
runSessionWithServerInTmpDir' (plugin recorder) config def fullCaps tree act
389+
390+
runSessionWithServerAndCapsInTmpDir :: Pretty b => Config -> PluginTestDescriptor b -> ClientCapabilities -> VirtualFileTree -> Session a -> IO a
391+
runSessionWithServerAndCapsInTmpDir config plugin caps tree act = do
392+
recorder <- pluginTestRecorder
393+
runSessionWithServerInTmpDir' (plugin recorder) config def caps tree act
394+
395+
-- | Host a server, and run a test session on it.
396+
--
397+
-- Creates a temporary directory, and materializes the VirtualFileTree
398+
-- in the temporary directory.
399+
--
400+
-- To debug test cases and verify the file system is correctly set up,
401+
-- you should set the environment variable 'HLS_TEST_HARNESS_NO_TESTDIR_CLEANUP=1'.
402+
-- Further, we log the temporary directory location on startup. To view
403+
-- the logs, set the environment variable 'HLS_TEST_HARNESS_STDERR=1'.
404+
--
405+
-- Example invocation to debug test cases:
406+
--
407+
-- @
408+
-- HLS_TEST_HARNESS_NO_TESTDIR_CLEANUP=1 HLS_TEST_HARNESS_STDERR=1 cabal test <plugin-name>
409+
-- @
410+
--
411+
-- Don't forget to use 'TASTY_PATTERN' to debug only a subset of tests.
412+
--
413+
-- For plugin test logs, look at the documentation of 'mkPluginTestDescriptor'.
414+
--
415+
-- Note: cwd will be shifted into a temporary directory in @Session a@
416+
runSessionWithServerInTmpDir' ::
417+
-- | Plugins to load on the server.
418+
--
419+
-- For improved logging, make sure these plugins have been initalised with
420+
-- the recorder produced by @pluginTestRecorder@.
421+
IdePlugins IdeState ->
422+
-- | lsp config for the server
423+
Config ->
424+
-- | config for the test session
425+
SessionConfig ->
426+
ClientCapabilities ->
427+
VirtualFileTree ->
428+
Session a ->
429+
IO a
430+
runSessionWithServerInTmpDir' plugins conf sessConf caps tree act = withLock lockForTempDirs $ do
431+
(recorder, _) <- initialiseTestRecorder
432+
["LSP_TEST_LOG_STDERR", "HLS_TEST_HARNESS_STDERR", "HLS_TEST_LOG_STDERR"]
433+
434+
-- Do not clean up the temporary directory if this variable is set to anything but '0'.
435+
-- Aids debugging.
436+
cleanupTempDir <- lookupEnv "HLS_TEST_HARNESS_NO_TESTDIR_CLEANUP"
437+
let runTestInDir = case cleanupTempDir of
438+
Just val
439+
| val /= "0" -> \action -> do
440+
(tempDir, _) <- newTempDir
441+
a <- action tempDir
442+
logWith recorder Debug $ LogNoCleanup
443+
pure a
444+
445+
_ -> \action -> do
446+
a <- withTempDir action
447+
logWith recorder Debug $ LogCleanup
448+
pure a
449+
450+
runTestInDir $ \tmpDir -> do
451+
logWith recorder Info $ LogTestDir tmpDir
452+
_fs <- FS.materialiseVFT tmpDir tree
453+
runSessionWithServer' plugins conf sessConf caps tmpDir act
454+
301455
goldenWithHaskellDocFormatter
302456
:: Pretty b
303457
=> Config
@@ -346,6 +500,54 @@ goldenWithCabalDocFormatter config plugin formatter conf title testDataDir path
346500
act doc
347501
documentContents doc
348502

503+
goldenWithHaskellDocFormatterInTmpDir
504+
:: Pretty b
505+
=> Config
506+
-> PluginTestDescriptor b -- ^ Formatter plugin to be used
507+
-> String -- ^ Name of the formatter to be used
508+
-> PluginConfig
509+
-> TestName -- ^ Title of the test
510+
-> VirtualFileTree -- ^ Virtual representation of the test project
511+
-> FilePath -- ^ Path to the testdata to be used within the directory
512+
-> FilePath -- ^ Additional suffix to be appended to the output file
513+
-> FilePath -- ^ Extension of the output file
514+
-> (TextDocumentIdentifier -> Session ())
515+
-> TestTree
516+
goldenWithHaskellDocFormatterInTmpDir config plugin formatter conf title tree path desc ext act =
517+
let config' = config { formattingProvider = T.pack formatter , plugins = M.singleton (PluginId $ T.pack formatter) conf }
518+
in goldenGitDiff title (vftOriginalRoot tree </> path <.> desc <.> ext)
519+
$ runSessionWithServerInTmpDir config' plugin tree
520+
$ TL.encodeUtf8 . TL.fromStrict
521+
<$> do
522+
doc <- openDoc (path <.> ext) "haskell"
523+
void waitForBuildQueue
524+
act doc
525+
documentContents doc
526+
527+
goldenWithCabalDocFormatterInTmpDir
528+
:: Pretty b
529+
=> Config
530+
-> PluginTestDescriptor b -- ^ Formatter plugin to be used
531+
-> String -- ^ Name of the formatter to be used
532+
-> PluginConfig
533+
-> TestName -- ^ Title of the test
534+
-> VirtualFileTree -- ^ Virtual representation of the test project
535+
-> FilePath -- ^ Path to the testdata to be used within the directory
536+
-> FilePath -- ^ Additional suffix to be appended to the output file
537+
-> FilePath -- ^ Extension of the output file
538+
-> (TextDocumentIdentifier -> Session ())
539+
-> TestTree
540+
goldenWithCabalDocFormatterInTmpDir config plugin formatter conf title tree path desc ext act =
541+
let config' = config { cabalFormattingProvider = T.pack formatter , plugins = M.singleton (PluginId $ T.pack formatter) conf }
542+
in goldenGitDiff title (vftOriginalRoot tree </> path <.> desc <.> ext)
543+
$ runSessionWithServerInTmpDir config' plugin tree
544+
$ TL.encodeUtf8 . TL.fromStrict
545+
<$> do
546+
doc <- openDoc (path <.> ext) "cabal"
547+
void waitForBuildQueue
548+
act doc
549+
documentContents doc
550+
349551
-- | Restore cwd after running an action
350552
keepCurrentDirectory :: IO a -> IO a
351553
keepCurrentDirectory = bracket getCurrentDirectory setCurrentDirectory . const
@@ -355,6 +557,12 @@ keepCurrentDirectory = bracket getCurrentDirectory setCurrentDirectory . const
355557
lock :: Lock
356558
lock = unsafePerformIO newLock
357559

560+
561+
{-# NOINLINE lockForTempDirs #-}
562+
-- | Never run in parallel
563+
lockForTempDirs :: Lock
564+
lockForTempDirs = unsafePerformIO newLock
565+
358566
-- | Host a server, and run a test session on it
359567
-- Note: cwd will be shifted into @root@ in @Session a@
360568
runSessionWithServer' ::
@@ -371,7 +579,7 @@ runSessionWithServer' ::
371579
FilePath ->
372580
Session a ->
373581
IO a
374-
runSessionWithServer' plugins conf sconf caps root s = withLock lock $ keepCurrentDirectory $ do
582+
runSessionWithServer' plugins conf sconf caps root s = withLock lock $ keepCurrentDirectory $ do
375583
(inR, inW) <- createPipe
376584
(outR, outW) <- createPipe
377585

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
module Test.Hls.FileSystem
3+
( FileSystem(..)
4+
, VirtualFileTree(..)
5+
, FileTree
6+
, Content
7+
-- * init
8+
, materialise
9+
, materialiseVFT
10+
-- * Interaction
11+
, readFileFS
12+
, writeFileFS
13+
-- * Test helpers
14+
, mkVirtualFileTree
15+
, toNfp
16+
, toAbsFp
17+
-- * Builders
18+
, file
19+
, copy
20+
, directory
21+
, text
22+
, ref
23+
-- * Cradle helpers
24+
, directCradle
25+
, simpleCabalCradle
26+
-- * Full project setups
27+
, directProject
28+
, directProjectMulti
29+
, simpleCabalProject
30+
, simpleCabalProject'
31+
) where
32+
33+
import Data.Foldable (traverse_)
34+
import qualified Data.Text as T
35+
import qualified Data.Text.IO as T
36+
import Development.IDE (NormalizedFilePath)
37+
import Language.LSP.Protocol.Types (toNormalizedFilePath)
38+
import System.Directory
39+
import System.FilePath as FP
40+
41+
-- ----------------------------------------------------------------------------
42+
-- Top Level definitions
43+
-- ----------------------------------------------------------------------------
44+
45+
-- | Representation of a 'VirtualFileTree' that has been 'materialise'd to disk.
46+
--
47+
data FileSystem =
48+
FileSystem
49+
{ fsRoot :: FilePath
50+
, fsTree :: [FileTree]
51+
, fsOriginalRoot :: FilePath
52+
} deriving (Eq, Ord, Show)
53+
54+
-- | Virtual representation of a filesystem tree.
55+
--
56+
-- Operations of 'vftTree' are relative to 'vftOriginalRoot'.
57+
-- In particular, any 'copy' etc. operation looks for the sources in 'vftOriginalRoot'.
58+
--
59+
-- To persist a 'VirtualFileTree', look at 'materialise' and 'materialiseVFT'.
60+
data VirtualFileTree =
61+
VirtualFileTree
62+
{ vftTree :: [FileTree]
63+
, vftOriginalRoot :: FilePath
64+
} deriving (Eq, Ord, Show)
65+
66+
data FileTree
67+
= File FilePath Content
68+
| Directory FilePath [FileTree]
69+
deriving (Show, Eq, Ord)
70+
71+
data Content
72+
= Inline T.Text
73+
| Ref FilePath
74+
deriving (Show, Eq, Ord)
75+
76+
-- ----------------------------------------------------------------------------
77+
-- API with side effects
78+
-- ----------------------------------------------------------------------------
79+
80+
readFileFS :: FileSystem -> FilePath -> IO T.Text
81+
readFileFS fs fp = do
82+
T.readFile (fsRoot fs </> FP.normalise fp)
83+
84+
writeFileFS :: FileSystem -> FilePath -> Content -> IO ()
85+
writeFileFS fs fp content = do
86+
contents <- case content of
87+
Inline txt -> pure txt
88+
Ref path -> T.readFile (fsOriginalRoot fs </> FP.normalise path)
89+
T.writeFile (fsRoot fs </> FP.normalise fp) contents
90+
91+
-- | Materialise a virtual file tree in the 'rootDir' directory.
92+
--
93+
-- Synopsis: @'materialise' rootDir fileTree testDataDir@
94+
--
95+
-- File references in '[FileTree]' are resolved relative to the @testDataDir@.
96+
materialise :: FilePath -> [FileTree] -> FilePath -> IO FileSystem
97+
materialise rootDir' fileTree testDataDir' = do
98+
let testDataDir = FP.normalise testDataDir'
99+
rootDir = FP.normalise rootDir'
100+
101+
persist :: FilePath -> FileTree -> IO ()
102+
persist fp (File name cts) = case cts of
103+
Inline txt -> T.writeFile (fp </> name) txt
104+
Ref path -> copyFile (testDataDir </> FP.normalise path) (fp </> takeFileName name)
105+
persist fp (Directory name nodes) = do
106+
createDirectory (fp </> name)
107+
mapM_ (persist (fp </> name)) nodes
108+
109+
traverse_ (persist rootDir) fileTree
110+
pure $ FileSystem rootDir fileTree testDataDir
111+
112+
-- | Materialise a virtual file tree in the 'rootDir' directory.
113+
--
114+
-- Synopsis: @'materialiseVFT' rootDir virtualFileTree@
115+
--
116+
-- File references in 'virtualFileTree' are resolved relative to the @vftOriginalRoot@.
117+
materialiseVFT :: FilePath -> VirtualFileTree -> IO FileSystem
118+
materialiseVFT root fs =
119+
materialise root (vftTree fs) (vftOriginalRoot fs)
120+
121+
-- ----------------------------------------------------------------------------
122+
-- Test definition helpers
123+
-- ----------------------------------------------------------------------------
124+
125+
mkVirtualFileTree :: FilePath -> [FileTree] -> VirtualFileTree
126+
mkVirtualFileTree testDataDir tree =
127+
VirtualFileTree
128+
{ vftTree = tree
129+
, vftOriginalRoot = testDataDir
130+
}
131+
132+
toAbsFp :: FileSystem -> FilePath -> FilePath
133+
toAbsFp fs fp = fsRoot fs </> FP.normalise fp
134+
135+
toNfp :: FileSystem -> FilePath -> NormalizedFilePath
136+
toNfp fs fp =
137+
toNormalizedFilePath $ toAbsFp fs fp
138+
139+
-- ----------------------------------------------------------------------------
140+
-- Builders
141+
-- ----------------------------------------------------------------------------
142+
143+
-- | Create a file in the test project with some content.
144+
--
145+
-- Only the filename will be used, and any directory components are *not*
146+
-- reflected in the test project.
147+
file :: FilePath -> Content -> FileTree
148+
file fp cts = File fp cts
149+
150+
-- | Copy a filepath into a test project. The name of the file is also used
151+
-- in the test project.
152+
--
153+
-- The filepath is always resolved to the root of the test data dir.
154+
copy :: FilePath -> FileTree
155+
copy fp = File fp (Ref fp)
156+
157+
directory :: FilePath -> [FileTree] -> FileTree
158+
directory name nodes = Directory name nodes
159+
160+
-- | Write the given test directly into a file.
161+
text :: T.Text -> Content
162+
text = Inline
163+
164+
-- | Read the contents of the given file
165+
-- The filepath is always resolved to the root of the test data dir.
166+
ref :: FilePath -> Content
167+
ref = Ref
168+
169+
-- ----------------------------------------------------------------------------
170+
-- Cradle Helpers
171+
-- ----------------------------------------------------------------------------
172+
173+
-- | Set up a simple direct cradle.
174+
--
175+
-- All arguments are added to the direct cradle file.
176+
-- Arguments will not be escaped.
177+
directCradle :: [T.Text] -> FileTree
178+
directCradle args =
179+
file "hie.yaml"
180+
( Inline $ T.unlines $
181+
[ "cradle:"
182+
, " direct:"
183+
, " arguments:"
184+
] <>
185+
[ " - " <> arg | arg <- args]
186+
)
187+
188+
-- | Set up a simple cabal cradle.
189+
--
190+
-- Prefer simple cabal cradle, over custom multi cabal cradles if possible.
191+
simpleCabalCradle :: FileTree
192+
simpleCabalCradle =
193+
file "hie.yaml"
194+
(Inline $ T.unlines
195+
[ "cradle:"
196+
, " cabal:"
197+
]
198+
)
199+
200+
201+
-- ----------------------------------------------------------------------------
202+
-- Project setup builders
203+
-- ----------------------------------------------------------------------------
204+
205+
-- | Set up a test project with a single haskell file.
206+
directProject :: FilePath -> [FileTree]
207+
directProject fp =
208+
[ directCradle [T.pack fp]
209+
, file fp (Ref fp)
210+
]
211+
212+
-- | Set up a test project with multiple haskell files.
213+
--
214+
directProjectMulti :: [FilePath] -> [FileTree]
215+
directProjectMulti fps =
216+
[ directCradle $ fmap T.pack fps
217+
] <> fmap copy fps
218+
219+
-- | Set up a simple cabal cradle project and copy all the given filepaths
220+
-- into the test directory.
221+
simpleCabalProject :: [FilePath] -> [FileTree]
222+
simpleCabalProject fps =
223+
[ simpleCabalCradle
224+
] <> fmap copy fps
225+
226+
-- | Set up a simple cabal cradle project.
227+
simpleCabalProject' :: [FileTree] -> [FileTree]
228+
simpleCabalProject' fps =
229+
[ simpleCabalCradle
230+
] <> fps

‎plugins/hls-call-hierarchy-plugin/test/Main.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module Main (main) where
88

99
import Control.Lens (set, (^.))
1010
import Control.Monad.Extra
11-
import Data.Aeson
11+
import qualified Data.Aeson as Aeson
1212
import Data.Functor ((<&>))
1313
import Data.List (sort, tails)
1414
import qualified Data.Map as M
@@ -527,9 +527,9 @@ mkCallHierarchyItem' prefix name kind range selRange uri c@(CallHierarchyItem na
527527
assertHierarchyItem selRange selRange'
528528
case xdata' of
529529
Nothing -> assertFailure ("In " ++ show c ++ ", got Nothing for data but wanted " ++ show xdata)
530-
Just v -> case fromJSON v of
531-
Success v -> assertBool ("In " ++ show c ++ " wanted data prefix: " ++ show xdata) (xdata `T.isPrefixOf` v)
532-
Error err -> assertFailure ("In " ++ show c ++ " wanted data prefix: " ++ show xdata ++ " but json parsing failed with " ++ show err)
530+
Just v -> case Aeson.fromJSON v of
531+
Aeson.Success v -> assertBool ("In " ++ show c ++ " wanted data prefix: " ++ show xdata) (xdata `T.isPrefixOf` v)
532+
Aeson.Error err -> assertFailure ("In " ++ show c ++ " wanted data prefix: " ++ show xdata ++ " but json parsing failed with " ++ show err)
533533
where
534534
tags = Nothing
535535
detail = Just "Main"

0 commit comments

Comments
 (0)
Please sign in to comment.