How can I log HSpec test output through two formatters simultaneously? - haskell

I run my HSpec tests both locally and in the CI. The default specdoc formatter produces nice, colored stdio output. However, for the CI, I need the results in the XML format so that they can be presented on the web.
I added my XML format to the HSpec config, but that disabled the stdio output altogether. I've tried hacking the formatter so that it ran both formatting commands, but that just resulted in an XML file with mixed text and XML messages (since there's only one configOutputFile option).
A few options at this point are:
To run the tests twice, once with each formatter
To run the tests with silent formatter and then somehow try to run the fomatters on the results.
To hack my formatter output so that e.g. some commands go straight to stdio.
Neither of those sound particularly easy and straightforward. Is there a better way? Being able to use just one Formatter at once sounds like a rather annoying limitation.

In the end I've decided that it's not worth the effort, but I did make a working PoC of the workaround:
hspecCi :: String -> Spec -> IO ()
hspecCi filename spec = do
isCiBuild <- (== "true") <$> getEnv "CI" `catch` \(e :: SomeException) -> return ""
let ciConfig = defaultConfig
{ configFormatter = Just xmlFormatter
, configOutputFile = Right $ testResultsPath ++ filename ++ "/results.xml"
}
hspecWith (if isCiBuild then ciConfig else defaultConfig) spec
This will run stdio output in local builds and XML in CI. Not too hard to write, but maybe it'll help someone. xmlFormatter is something you need to get from somewhere or write yourself.

Related

Xmonad: Prompt with output of shell commands as completion options

When I try to put the output of a shell command into an xmonad prompt as a completion option, i.e. as something that can be chosen via the prompt, I keep running compile errors, no matter what I try.
After initially getting a basic custom prompt to work, the following sanity check:
mkXPrompt TestPrompt config (mkComplFunFromList config ["a", compltest) testFun
compltest = do
output <- "b"
return output
This may not be terribly idiomatic Haskell, but it works as expected, it compiles and "a" as well as "b" are available options in the prompt. But I just can't get compltest to return the output of a shell command.
I have extensively looked at the source of all instances of xmonad prompt I could find in xmonad.contrib, and I have also checked here and on other websites for questions regarding similar issues, found a few and read them thoroughly.
The problem is that in all these cases, people are either doing something FAR more complex than what I'm attempting to do, or something that is simply very different. If I was better at Haskell I could probably adapt something for my needs, but so far I have spend several hours cobbling together functions and going from one compile error (usually type errors) to the next, no matter what I tried.
What I could so far gather is that I cannot "extract" a string out of an IO String for security reasons, and so should use liftIO in some way. I have also understood that some of the magic should happen in a do block, and that for Xmonad, runProcessWithInput is supposed to work somewhat better than readProcess. But practically applying this knowledge is a different matter.
Here is a tiny subset of the mass of functions I have tried so far, using the command "date" as an example (i.e. the output of the date command at the time of invoking the prompt should be a completion option in the prompt):
compltest = do
output <- liftIO $ putStrLn $ runProcessWithInput "date" [] ""
return output
compltest = do
output <- liftIO $ runProcessWithInput "date" [] ""
return output
compltest = io $ (runProcessWithInput "date" [] "" >>= readIO)
I guess bind your key to something like this:
do
output <- runProcessWithInput "date" [] ""
mkXPrompt TestPrompt config (mkComplFunFromList config [output]) testFun
Of course there are more interesting things to do with output than making it a singleton list; if f is your favorite String -> [String] function, then replace [output] with (f output) in the last line.

Debugging haskell in VS Code

I am trying to debug a simple Haskell application in VS Code with the phoityne-vscode plugin.
I managed to configure the plugin and run the application - I can see the breakpoints being hit.
The problem is cannot figure out how to interact with the running application. I have a line where I expect user input
do
someValue <- getLine
Once the debugger reaches this line it stops and I cannot figure out how to pass arguments to the program. I would guess it is somewhere in the Debug Console but it looks like the prompt is only for driving the debugger.
I am sure I am missing something very simple - it's my first attempt at tempering with Haskell and I'm new to VS Code too.
As described in repository - You can't use STD[IN|OUT] methods;
Something like putStrLn will be ignored, but IN methods (getLine for example) just will get stuck;
For functions without STD[IN|OUT] methods You can use F10 - select function and send params (for example [1,2,3] for send list or "str" for send string):
With F5 You can run previous configuration or configuration from launch.json:
mainArgs - params, that You can get with getArgs (cmd params for your programm);
startupFunc - name of the function that will be call first;
startupArgs - params for that first function (for example "startupArgs": "666" will the same as <F10> -> 666 -> <Enter>)
stopOnEntry - boolean param for enable\disable breakpoint at the start of the function;
Also, if I understood correctly, F10 will rewrite startupFunc and startupArgs;
I'm really new in haskell so I'm confused a little bit when I can get value of constant in debug console, sometimes I have:
[DAP][ERROR] error occurred while runStmt.
Variable not in scope: <...>
Also look like where and let blocks are ignored :D
Use watch panel for better understanding when You can use some constant:
If You want to debug input\output methods You can use ghci debug commands (:h - Commands for debugging block);
For example, You have a program:
89| test :: IO ()
90| test = do
91| a <- getLine
92| b <- getLine
93| putStrLn $ a ++ b
Use :break 93 to add breakpoint at the 93'th line;
Then run your program in interpreter: test;
Enter values. Now You will stop at putStrLn $ a ++ b - if You type a or b in console - You'll get values of these constants;
Then :step for evaluate ++ and :step for putStrLn:
I hope it will be helpful for someone;

Implementing another kind of flags in Haskell

We have the classic flags in the command line tools, those that enable something (without arguments, e.g --help or --version) and others kind of flags that accept arguments (e.g. --output-dir=/home/ or --input-file="in.a", whatever).
But this time, I would like to implement the following kind of.
$ myprogram --GCC-option="--stdlib 11" --debug
In a general way, the flag is like "--PROGRAM-option=ARGUMENT". Then, I keep from this flag, PROGRAM and ARGUMENT values, they are variables. In the example above, we have PROG=GCC and ARGUMENT=--stdlib 11.
How should can I implement this feature in Haskell? I have some experience parsing options in the classic way.
In a recent project of mine I used an approach based on a 'Data.Tree' of option handling nodes. Of course, I haven't released this code so it's of very limited use, but I think the scheme might be helpful.
data OptHandle = Op { optSat :: String -> Bool
, opBuild :: [String] -> State Env [String]
}
The node fields: check to see if the argument satisfied the node; and incrementally built up an initial program environment based on the the remaining arguments (returning unused arguments to be processed by nodes lower in the tree.)
An option processing tree is then hard coded, such as below.
pgmOptTree :: [Tree OptHandle]
pgmOptTree = [mainHelpT,pgmOptT,dbgT]
mainHelpT :: Tree OptHandle
mainHelpT = Node (Op sat bld) []
where
sat "--help" = True
sat _ = False
bld _ = do
mySetEnvShowHelp
return []
pgmOptT :: Tree OptHandle
pgmOptT = Node (Op sat bld) [dbgT]
where
sat = functionOn . someParse
bld ss = do
let (d,ss') = parsePgmOption ss
mySetEnvPgmOpt d
return ss'
You will also need a function which feeds the command line arguments to the tree, checking satisfiability of each node in a forest, executing the opBuild, and calling subforests. After running the handler in the state monad, you should be returned an initial starting environment which can be used to tell main the functionality you want to call.
The option handler I used was actually a little more complicated than this, as my program communicated with Bash to perform tab completions, and included help for most major options. I found the benefit to the approach was that I could more easily keep in sync three command line concerns: enabling tab completions which could inform users the next available commands; providing help for incomplete commands; and actually running the program for complete commands.
Maintaining a tree like this is nice because you can reuse nodes at different points, and add options that work with the others fairly easily.

attoparsec: succeeding on part of the input instead of failing

I have an attoparsec parser, and tests for it, what annoys me is that if I comment part of the parser and run the tests, the parser doesn't return Left "parse error at line ..." but instead I get Right [].
Note that I'm using parseOnly to make it clear that there'll be no more input.
Otherwise it's nice to get the partially parsed input, it can definitely be useful and I'm glad to have it. However I'd like to be informed that the whole input was not consumed. Maybe to get a character offset of the last consumed letter, or if that's what it takes, at least an option to be returned Left.
If it's relevant, the parser can be found there.
If I comment for instance the line:
<|> PlainText <$> choice (string <$> ["[", "]", "*", "`"])
And run the tests, I get for instance:
1) notes parsing tests parses notes properly
simple test
expected: Right [NormalLine [PlainText "one line* # hello world"]]
but got: Right []
This is from that test.
Depending on if consuming the whole input should be the property of parseNoteDocument or just the tests, I'd extend one or the other with endOfInput or atEnd.
I'd suggest to define a proper Parser for your documents, like
parseNoteDocument' :: Text -> Parsec NoteDocument
parseNoteDocument' = many parseLine
and then define parseNoteDocument in terms of it. Then you can use parseNoteDocument' in the tests by defining a helper that parses a given piece of text using
parseNoteDocument' <* endOfInput
to ensure that the whole input is consumed.

How to tap into hpc information during execution

Consider the following use case:
I have a QuickCheck test suite consisting of two test cases prop_testCase1 and prop_testCase2. Using hpc I can observe the code coverage of my test suite.
However, I'd like to get separate results for the coverage of prop_testCase1 and prop_testCase2 (to then further process the results).
One way out could be to run the executable several times explicitly specifying the test case as a command line argument and to process the generated tix file after each run.
Yet, I would prefer to hide away this logic in a library. Also in reality I would like to run significantly more than just two test cases. Thus explicitly re-executing the binary for each test case seems rather inconvenient.
At the same time I'd imagine that hpc keeps the coverage data in some kind of intermediate data structure during the program execution.
Question: Is there a way to recognize that the program has been compiled with hpc symbols and to somehow access the data hpc generates at runtime?
I think I found a way to achieve the desired effect.
The required functions are provided by the Trace.Hpc.Reflect module.
Unfortunately the documentation is somewhat lacking.
Nevertheless, here is a minimal example that seems to work:
import Trace.Hpc.Reflect
main :: IO ()
main = do
print (show $ f 5)
tix <- examineTix
print (show tix)
print (show $ f 6)
print (show $ f 7)
tix <- examineTix
print (show tix)
return ()
f x = x*2
Compile and run this as follows:
$ ghc -fhpc Main
[1 of 1] Compiling Main ( Main.hs, Main.o )
Linking Main ...
$ ./Main
"10"
"Tix [TixModule \"Main\" 3889152622 31 [1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1]]"
"12"
"14"
"Tix [TixModule \"Main\" 3889152622 31 [3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, in1,1,1,1,0,0,0,0,0,1,1]]"
So using this you can tap into the coverage data provided by hpc while your program is executing.
If the executable is compiled without hpc support the program still works but the Tix object returned by examineTix contains the empty list.

Resources