Showing posts with label test tools. Show all posts
Showing posts with label test tools. Show all posts

Saturday, December 09, 2017

Announcing AltCover

Now I have time to myself, and the season doesn't lend itself to outdoor activities, I can start dusting off my various coding projects and devoting the time and energy to those that I used to expend for pay.

First off the block is AltCover, an alternative code coverage tool for .net and Mono.

This is a project I started back in the dark days in the spring of 2010, when changes in the profiling API of the new CLR 4.0 release meant that the old freeware NCover 1.5.x series no longer functioned. For some considerable while, the only FOSS alternative that worked with the new CLR (by instrumenting the code under test before execution, rather than on-the-fly) seemed to be an initial proof-of-concept on CodeProject.

At that point, I'd been looking for something non-trivial to work on that would provide the opportunity to use F# to build up my fluency in the language; and so the obvious thing to to was to re-implement from scratch and extend to cover such gaps as I found in its functionality when trying to use it as a near drop-in replacement for the now non-functional NCover version.

After some considerable interval and a failed dalliance with PartCover, including my first contribution to a real FOSS tool, but never a resolution to one real sticking problem, where it looked like JITting across assembly boundaries was causing executed lines to not appear in the coverage, I shelved my work in favour of the off-the-shelf OpenCover, where I could intermittently contribute enhancements to cover personal pain points.

Why have I dusted it off again now?

Well, for much the same reasons as before; a non-trivial project that does answer some pain-points (Mono and probably the new dotnet core amongst them) that OpenCover's necessarily intimate relationship with the runtime makes difficult. And it provides a reason to play with new toys that have grown up over the last few years, like Fake for builds, and the generous provision of CI tools to FOSS projects so people can take builds rather than having to roll their own.

Monday, February 25, 2013

FsCheck is great for for native code too!

I've just had call to write some good old-fashioned 'C' code recently, for use in an environment where my access to anything fancy like external libraries is limited. And a one part needs a simple transcoding function to take data in and manipulate it from the form convenient to provide into a form that's actually useful.

But of course, I want to test that bit before I start wiring it into place. There are, of course a whole slew of 'C' unit test frameworks out there, none of which I've used before, but really I wanted to just soak-test the algorithm quickly and simply, with a maximum of coverage and a minimum of distractions, so, I actually wanted a QuickCheck-equivalent tool that would "just work".

And this is where F#'s easy P/Invoke syntax (almost as simple as cutting and pasting out of the 'C' header file) and FsCheck were just the tools I was looking for -- write a property (or, in fact, a series of properties) that asserted that an input mapped by my function was the same as obtained using an equivalent .net library function already to hand as reference, invoke them with FsCheck.Check.Quick, and then TDD away, starting with the rough outlines as asserted by the early properties, and then filling in details as asserted by the later ones.


Monday, March 19, 2012

C# under the covers

More of what the compiler does without you realising it, only this time in a C# sample.

The absolutely simplest sort of method, which you can clearly completely cover by calling it once. Right?

That's what I thought until I tried running OpenCover over code rather like this, and it told me I had only covered one of two possible branches.

So naturally, I go "WTF?", and wonder whether it's the return or throw alternatives out of .First that it's alluding to and to be certain, crack open ILSpy, and see that first line expands to

which actually involves first caching the delegate corresponding to the lambda, if it hasn't been already; or using it on subsequent calls.

The release build is much the same; it lacks the initial nop, and the meaningless jump-to-next-instruction at offset 0x23.

Move the constant string out into the method, and make the lambda close over it

and the caching goes away : a new instance of ClassLibrary2.Class1/'<>c__DisplayClass1' gets created every time, debug or release. And with it goes the branch.

Monday, January 23, 2012

HTML reporting for StyleCop

Based on the outputs from the earlier script; and using an appearance inspired by the output from the XSL report approach from codecampserver; but with the source in-lined into the report, and violation messages inserted after the affected lines. Each file (and the nested violation messages) are hidden by default, and can be expanded by clicking the file header or the source marked as violation (where the hand cursor shows):

N.B. Defined as it is within a PowerShell here-string, the jQuery script needs its $ symbols to be escaped to keep the PowerShell parser happy.



You will need to adjust the script to find the appropriate StyleCop output files from your build process, and create suitably named/located reports based on those files.

Sunday, January 22, 2012

Adding the missing pieces to the Standalone StyleCop script

Rather than post the whole ~200 lines, just the salient bits so that the original can be tidied up (but without obscuring everything with obsessive error handling). Start by giving it parameters like

Find StyleCop in the default install location by


if (-not $StyleCopFolder) {
    $styleCopFolder =  (dir "$($env:programFiles)*\stylecop*" | Sort-object LastWriteTimeUtc | Select-Object -Last 1).FullName
}

Get the list of projects by either just the project, or using a regex on the project file from here: http://bytes.com/topic/c-sharp/answers/483959-enumerate-projects-solution

Find a Settings.StyleCop file by looking at the project folder and up, defaulting to one in the StyleCop folder; and then scan the C# files of interest by

and customise the output file as

Saturday, January 21, 2012

Standalone StyleCop : a rough draft

Unlike FxCop, the StyleCop tool out of the box only comes with Visual Studio and MSBuild integration; there is no stand-alone command-line tool. Still, as it's now open-source, we can look at the workings of the MSBuild task and see how to drive it directly.

Here is a proof-of-concept PowerShell script which can be used as the basis for a proper command-line tool or script; and as a preliminary "how to" to write unit tests for your rules. Well, they aren't going to be proper unit tests, since they'd have to touch the file system, but they can crawl over test source files in a dummy project in the StyleCop rule solution.

And that's all there is to it, really.


Thursday, August 12, 2010

Links for 12-Aug

Using the Reactive Extensions:

F# Silverlight template.

PartCover patched to avoid writing to HKLM (my first patch to someone else's FOSS project!)

Untangling expression trees.

Using Code coverage -- did the right tests get written?

How we approach unfamiliar code (study).

Cosmos (C# OS kit) reaches milestone 5 with Visual Studio integration.

Saturday, July 17, 2010

Building PartCover 4 on Vista

Updated again: PartCover Revision 35 includes a patch I provided that builds the same functionality in C#, transiently registering the COM component in HKCU during build, and also providing a --register command line option which is equivalent to the old nCover //r option.

Updated with new complete registry frobbing script, based on actually diffing the before and after states of HKCR when using regsvr32 on the PartCover dll.

Following on from earlier in the week, I pulled the current trunk from SourceForge, and set about building it then putting it to work on some F# code which had caused an earlier version (the most recent available at the end of last year) to balk.

Unzipping the tools (Boost, ATL server) into the appropriate libraries, guided by the include paths (and moving the Debug relative paths to match the Release ones) was trivial, and then it's just a case of firing up MSBuild. Or so I thought.

First hiccup -- it attempts to register the PartCover assembly during the build, and I don't run as administrator by default. So I replaced the

regsvr32 /s /c "$(TargetPath)"

by a manual registry frobbing trick (akin to what NCover 1.5.8 used for its //r option)

"$(ProgramFiles)\FSharp-2.0.0.0\bin\fsi.exe" "$(ProjectDir)\partcover.fsx" "$(TargetPath)"

where partcover.fsx is

This updated per-user registration permits both building and running the PartCover utility without needing full administrator privilege.

So emitting

...\PartCover.exe --target "..\..\..\_Tools\Microsoft Fxcop 10.0\FxCopCmd.exe"  --target-work-dir . --target-args "/q /s /console /f:Tinesware.Recorder.dll /f:Tinesware.Instrumentation.exe /f:Tinesware.Infrastructure.dll /f:Tinesware.Rules.dll /f:Tinesware.InfrastructureTests.dll /f:BaseTests.dll /rule:Tinesware.Rules .dll /o:fxcop.xml /dictionary:..\..\..\CustomDictionary.xml" --include [Tinesware.*]* --include [CSharpTests]* --output PartCoverage.xml

to monitor code coverage when using an FxCop rule written in F#, this reported

open driver pipe
modify target environment variables
create target process
wait for driver connection
[00000] [05724] Options dump:
[00000] [05724]   VerboseLevel: -842150451
[00016] [05724]   Log file: ...\Infrastructure\_Binaries\Tinesware.InfrastructureTe
sts4\Debug+AnyCPU\partcover.driver.log
[00016] [05724]   Log pipe: yes
[00016] [05724]   Count Coverage - ON
[00016] [05724]   Count Call Tree - OFF
[00016] [05724]   Exclude [mscorlib]*
[00016] [05724]   Exclude [System*]*
[00016] [05724]   Include [Tinesware.*]*
[00016] [05724]   Include [CSharpTests]*
Project : warning : CA0060 : The indirectly-referenced assembly 'Microsoft.VisualStudio.CodeAnalysis, Version=10.0.0.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' could not be found. This assembly is not required for analysis, howeve
r, analysis results could be incomplete. This assembly was referenced by: ...\Infra
structure\_Binaries\Tinesware.InfrastructureTests4\Debug+AnyCPU\FxCopSdk.dll.
[04774] [05724] CorProfiler is turned off
Target PageFaultCount: 33536
Target PagefileUsage: 84246528
Target PeakPagefileUsage: 87359488
Target PeakWorkingSetSize: 92827648
Target QuotaNonPagedPoolUsage: 10920
Target QuotaPagedPoolUsage: 328336
Target QuotaPeakNonPagedPoolUsage: 43160
Target QuotaPeakPagedPoolUsage: 374344
Target WorkingSetSize: 89481216
Total 0 bytes ...\Infrastructure\_Binaries\Tinesware.InfrastructureTests4\Debug+AnyCPU

which completed cleanly, unlike before, and gave a sane looking output file. There is now obvious code in the system for doing the short branch instruction fix-up, which seems to have resolved the issue I had last time I tried this tool.

So, it really does look good to go for .net 4 code -- and I just have to add hooks to consume that format as well as the old NCover style.

Monday, July 12, 2010

dotCover 1.0 beta -- first impressions

The beta of the coverage tool from the same people who brought you Resharper came out late last week, and I took the chance to have a quick play with what it offers.

As expected, it integrates smoothly with the R# unit test running -- you can see exactly what code your test or set of tests cover; marked in green overlay in the source view for covered by the test(s) you just ran, red elsewhere, in a manner reminiscent of NCoverExplorer.

There is also a command-line option

Usage: JetBrains.dotCover.ConsoleRunner.exe /Executable='executable' [/Arguments='arguments'] [/WorkingDir='working dir'] /Output='output' 

Not having R# at home -- because I rarely use C# even if I'm writing for .net -- it's this which interested me more. So, I gave it a try on my current F# project

>& 'C:\Program Files\JetBrains\dotCover\v1.0\Bin\JetBrains.dotCover.ConsoleRunner.exe' /executable='..\..\..\_Tools\Microsoft FxCop 1.36\FxCopCmd.exe' /Arguments='/q /s /console /f:Tinesware.Recorder.dll /f:Tinesware.Instrumentation.exe /f: Tinesware.Infrastructure.dll /f:Tinesware.Rules.dll /f:Tinesware.InfrastructureTests.dll /f:BaseTests.dll  /rule:Tinesware.Rules.dll /o:fxcop.xml  /rule:.\Rules /dictionary:..\..\..\CustomDictionary.xml' /workingdir=. /output=dotcover.xml

which yields a bald total coverage number

JetBrains dotCover Console Runner v1.0.56.11 JetBrains s.r.o.
Coverage session started [12/07/2010 20:14:16]
Coverage session finished [12/07/2010 20:14:28]
Index files: ...AppData\Local\Temp\ssc06AE2.tmp
Log files: ...AppData\Local\Temp\lgc02A14.tmp
Opening snapshot...
Done.
Generating report...
Done.
Total coverage: 78%

and an XML report file that makes it clear that that percentage is based on all assemblies dragged into the AppDomain, counting those which have no pdb information as containing no statements to cover, which means if you're running unit tests with Rhino.Mocks that it includes things like

<Assembly Name="021925b1-cbe9-42d3-ad44-a39206195c2a" CoveredStatements="0" TotalStatements="0" CoveragePercent="0">
    <Type Name="IsolatorProxyb66a653c9a984566ac790c4fa654cab5" CoveredStatements="0" TotalStatements="0" CoveragePercent="0">
      <Type Name="InvocationShouldCallOriginal_1" CoveredStatements="0" TotalStatements="0" CoveragePercent="0">
        <Member Name="HandleEvent" CoveredStatements="0" TotalStatements="0" CoveragePercent="0" />

emanating from dynamic assemblies. And it seems from simple experiment that the filters that you can apply in Visual Studio don't seem to carry over to the command-line version.

The report is formatted as shown above, with hierarchy

Root -> Assembly -> Namespace -> Type -> Nested Type or Member (repeating Type as often as necessary)

There’s also no granularity below Member, so even for code with .pdb information available the reports only look like

<Type Name="Local" CoveredStatements="166" TotalStatements="173" CoveragePercent="95">
...
        <Member Name="get_Mutex" CoveredStatements="0" TotalStatements="0" CoveragePercent="0" />
        <Member Name="ExpandFile" CoveredStatements="2" TotalStatements="4" CoveragePercent="50" />
        <Member Name="LoadFile" CoveredStatements="4" TotalStatements="7" CoveragePercent="57" />
        <Member Name="op_GreaterQmarkGreater" CoveredStatements="4" TotalStatements="4" CoveragePercent="100" />
        <Member Name="op_GreaterPlusQmarkQmark" CoveredStatements="1" TotalStatements="1" CoveragePercent="100" />
        <Member Name="op_GreaterMultiplyGreater" CoveredStatements="4" TotalStatements="5" CoveragePercent="80" />
        <Member Name="op_GreaterPlusQmark" CoveredStatements="1" TotalStatements="1" CoveragePercent="100" />
        <Member Name="op_GreaterPlus" CoveredStatements="1" TotalStatements="1" CoveragePercent="100" />
        <Member Name="GetXmlData" CoveredStatements="1" TotalStatements="1" CoveragePercent="100" />
        <Member Name="DoWithLock" CoveredStatements="1" TotalStatements="1" CoveragePercent="100" />
        <Member Name="op_Append" CoveredStatements="1" TotalStatements="1" CoveragePercent="100" />
        <Member Name="GetByName" CoveredStatements="1" TotalStatements="1" CoveragePercent="100" />
        <Member Name="SetByName" CoveredStatements="1" TotalStatements="1" CoveragePercent="100" />
        <Member Name="GetStatementRange" CoveredStatements="2" TotalStatements="2" CoveragePercent="100" />
        <Member Name="FindSignatureForMethod" CoveredStatements="8" TotalStatements="8" CoveragePercent="100" />
        <Member Name="FindXmlForMethod" CoveredStatements="7" TotalStatements="7" CoveragePercent="100" />
        <Member Name="get_DeclaredExempt" CoveredStatements="0" TotalStatements="0" CoveragePercent="0" />
        <Member Name="get_AutomaticExempt" CoveredStatements="0" TotalStatements="0" CoveragePercent="0" />
...
      </Type>

So there can be no useful equivalent of NCoverExplorer for after the fact detail – to get detailed line un-coverage information you actually have to run every one of your unit tests in Visual Studio in one big bang.

Its metric of what counts as a statement is subtly different from what NCover (free) and the other tools I've been working on recently. It reports an F# property like

let AutomaticExempt = "-2"

or an equivalent C# automatic property, for that matter, as having 0 lines, rather than N/A and 1 respectively; but that aside it seems to be counting the self-same list of sequence points.

So, it seems that this is primarily a tool for interactive use and positive coverage testing -- "Does this set of tests cover this line, and if so, which one(s)?"; in a build process environment the lack of ability to focus down to only the assemblies of interest in the report (discarding tool and system code), and of per-statement reporting it is less interesting.

However it did take the F# code which PartCover had balked at in its stride.

Meanwhile in other news

There is a branch of PartCover that supports .net 4 originally on GitHub and which has now been merged into the main-line at SourceForge, removing one of the objections to using this tool. There also seems to be rather more life in this than there was six months ago when I tried it, so I shall have to revisit it and see if the IL issue is also resolved.

Saturday, November 28, 2009

Working on F# with NDepend

Following up from the earlier post here, looking more in depth at some of the results out of NDepend for my own little code quality project, and in addition to the results noted there

First out of the gate, for sanity's sake, when working with F#, it would make sense to add !HasAttribute OPTIONAL:System.Runtime.CompilerServices.CompilerGeneratedAttribute onto every rule. That way, you are not left to contemplate how and why the generated CompareTo methods on a simple record type

can have an IL Cyclomatic complexity of 43.

A similar blanket exclusion should be applied to types whose names contain the sigil @ marking them as generated function objects. Unfortunately, compiler inserted write-once local variables do not have any such distinctive sigils by which they can be filtered, just unexciting normal names like str, str2.

It would be nice if the HasAttribute condition also allowed you to filter on properties of the attributes like !HasAttribute OPTIONAL:Microsoft.FSharp.Core.CompilationMappingAttribute WITH (SourceConstructFlags & 31 == 1) OR (SourceConstructFlags & 31 == 2) -- to filter record or sum types, which are rich in generated structure, where appropriate. For example, though the sum type

may be implemented into the CLR by making Either<'a, 'b> an abstract type with concrete inner types Either<'a, 'b>+Left and Either<'a, 'b>+Right, the headline Either<'a, 'b> type is not an abstract base class in the usual sense, that user written code will be expected to extend the type.

Similarly, while it is arguably an oversight of the F# code generation that none of the inner types within a sum type are marked as sealed -- there is no good case for extending them -- it would also be nice to be able to exclude all (implicitly, generated) types that derived from (or are an inner type of) a sum or record type from analysis; as well as all the content of types attributed with [CompilationMapping(SourceConstructFlags.NonpublicRepresentation | ...)].

Allowing names of the form |ActivePattern|_| is a trivial extension of the "names should be upper case" rule.

There is what looks like a bug in NDepend's analysis as it manages to make all the types I have derived from Microsoft.FxCop.Sdk.BaseIntrospectionRule pass the filter

DepthOfInheritance == 1 // Must derive directly from System.Object

The containing rule for that test Classes that are candidate to be turned into Structures could probably also benefit from having AND !IsStatic added, so as to exempt functional modules.

Saturday, November 21, 2009

F# under the covers IX -- the case of the missing coverage

As I'm driving my little set of FxCop rules and associated helpers to a usable state, I'm looking in more detail at how the code coverage in the automated system/integration test is going, trying to drive that towards 100%. And as usual, the F# code generation is throwing up some interesting results.

Take this method (lightly refactored from last time) which starts at line 167 in the actual source:


Although my tests give a match for every case in the pattern -- and nCover shows a visit for every line from 168 to 176 (2 to 9), and the expression body of each branch of the if/else, it also claims that a code point that spans from from line 168 col 5 to line 177 col 24 (everything from if to then inclusive) is not visited.

Unfortunately, the IL generated for this method is not back-compilable to C# in Reflector (yes, another problem report submitted), so I can't use that to analyse what is going on in this particular case : I shall have to inject some diagnostic code into the rule itself to see what FxCop thinks is going on as a statement which spans those lines.

But this isn't the only mysterious bit of uncoverage I've found in F# code -- in the previous state of the method (as showcased here), it was the identifier result in let result = match… that got the uncoverage, despite the named value being used as the final expression in the method.

In most cases, it is possible to refactor away such temporaries, and clear the spurious uncoverages that they give rise to; but, as this example shows, it is not always possible.

Later

By recursively dumping Statements in FxCop for this method (which inserted 2 lines in a routine above this one, thus displacing the line numbers), I get a lot of multiple hits on statements (same type, same source context range), but the only ones with the appropriate source context are

Block -> from 170 : 5 to 179 : 24
Nop -> from 170 : 5 to 179 : 24
Nop -> from 170 : 5 to 179 : 24
AssignmentStatement -> from 170 : 9 to 170 : 22
AssignmentStatement -> from 170 : 9 to 170 : 22
Branch -> from 170 : 9 to 170 : 22
Block -> from 170 : 9 to 170 : 22
Branch -> from 170 : 9 to 170 : 22
Block -> from 170 : 9 to 170 : 22
Branch -> from 170 : 9 to 170 : 22
Block -> from 171 : 27 to 171 : 35
ExpressionStatement -> from 171 : 27 to 171 : 35
Nop -> from 171 : 27 to 171 : 35

So it seems that first Nop pair, with no other statement occupying the the same range, may be what it being detected as uncovered (unlike the second Nop, which overlaps the immediately preceding ExpressionStatement exactly).

The equivalent IL looks like

which does indeed have a pair of Nop opcodes at the start (lines 21 and 22).

So, that looks like another heuristic to add - removing unvisited code point records that correspond to nothing but a Nop.

The February 2010 CTP generates identical code here.

Thursday, November 19, 2009

NDepend -- a belated "kicking the tyres" review

While there may be subtle details changed by the February 2010 CTP, the broad thrust of how F# looks to a C# directed tool remains. The mass of compiler inserted locals which skews the analysis certainly containues to hold.

First off, a little confession -- while I'm a C# coder by day, by night I use a whole variety of other languages instead; so I don't have a large corpus of my own code to use it against; and having installed my gift copy of NDepend v2.12.1.3123 (Pro Edition) on my personal development machine at home, there are certain ethical issues involved in getting it to run over code from the day job as-is.

I do have an on-going project in F#, which at least the tool will consume after a fashion (as the tool doesn't know .fsproj files from Greek, analysis has to proceed by selecting a number of assemblies to work with), so my experiences are based on that.

First impressions

At one extreme, tools such as FxCop or StyleCop provide a report that indicates a number of point changes to make to code with pretty much zero configuration to the tools; at the other extreme, nUnit and nCover are things that require you to configure (code) pretty much everything, and responding to the output is a matter of judgement. NDepend is somewhere in the middle -- you can run it with default settings and get some analysis of your code, but the settings are completely malleable; and the response can in places be a judgement call too.

There are some rules in the default set which overlap, and in places contradict, guidance from FxCop or StyleCop -- especially in matters like naming conventions. This latter is the stuff of which religious arguments are made (my own choice being to leave policing of names to the MSFT tools). Similarly, it invents a method-level NDepend.CQL.GeneratedAttribute to mark generated code when we already have System.Runtime.CompilerServices.CompilerGeneratedAttribute and the class-applicable System.CodeDom.Compiler.GeneratedCodeAttribute

Some of the conditions tested (setting aside the false positives out of F#) are useful complements to FxCop, such as flagging places where code visibility could beneficially be adjusted to better encapsulate code, and are essentially resolved by point fixes in many cases.

The real value comes in the rules that examine the structure of the code (even if, again, the precise thresholds used are potentially subjects for debate).

Live test

I ran NDepend against the latest drop of my FxCop rules project; this is possibly a small sample of code, and in some respects an edge case (with strong coupling to framework or FxCop classes, and very low abstraction).

Being F#, I have to just supply the assemblies, and cannot get source level analysis (such as cyclomatic complexity or comment density metrics).

Aside from the language difficulty the one place I have had trouble is making sense of the coverage file integration -- the nCover output I supply seems to get ignored; and the linked-to tutorial refers to such an earlier version that the UI doesn't match at all.

Interpretation (and F# peculiarities)

With no interfaces defined in the project, I seem to trigger a fencepost error in the report for class statistics

Stat# OccurrencesAvgStdDevMax
Properties on Interfaces 0 Interfaces 00-1 properties on
Methods on Interfaces 0 Interfaces 00-1 methods on
Arguments on Methods on Interfaces 0 Methods 00-1 arguments on

and there are some places where the names of F# methods may be problematic -- for a number of rules I get a "WARNING: The following CQL constraint is not satisfied. 1 methods on 358 tested match the condition.", but the name of the offending method I have to find for myself:













methods# lines of code (LOC)Full Name
Sum:2
Average:2
Minimum:2
Maximum:2
Standard deviation:0
Variance:0

As I've been posting in my "F# under the covers" series of posts, the IL that gets generated is, shall we say, interesting; and that is one of the things that gets shown up by rules tuned to C#. Indeed, the very first run I made was what provoked a one-shot into an on-going series. For example, this method

has 11 variables and a cyclomatic complexity (in IL) of 12

That method -- which is used in

where >?> and >+?? are combinators, gets detected as unused ("No Afferent Coupling -> The method is not used in the context of this application.")

A number of rules -- such as "A stateless type might be turned into a static type" -- show up quite how many compiler generated classes there are; both the ones with @-line-number names (e.g. Patterns+MapToTypes@41) for F# funs, and simple nested classes for algebraic types. In production use of NDepend on F#, these rules would have to be extended so such types could be filtered automatically.

For other false positives, such as the spurious numbers of locals, it would be beneficial to filter on a suppression attribute (along the lines of System.Diagnostics.CodeAnalysis.SuppressMessage) with a string property that the rule could match on.

Overall

Another useful and interesting tool in the general .net developer's armoury. Even out of the box, I could see that it would provide useful automation to supplement a manual code review process, though it would require a degree of customization to fit into an established build and coding standards regime.

Sunday, November 08, 2009

F# under the covers VIII

I'm in the tidying up stages for the little project I've been working on lately, a set of FxCop rules, mainly aimed at providing some complementary features for nCover-style code coverage, including static analysis for trivial methods -- or compiler generated ones. I've done the red -- add a set of methods that should give expected static analysis results -- and green -- write the simplest implementation that works when given the code base to analyse as a post-build activity. And now I'm on the refactor leg, abstracting out the common activities, taming sprawling methods into tighter ones, and reducing the level of imperative features (perhaps also making the code more idiomatic), even at times managing to delete whole methods.

And while I'm doing so, I'm running various tools over the code; FxCop and nCover over a small nUnit run every build, of course, but in turns Reflector and NDepend -- the results of which have been the inspiration for this series of posts.

And as we have seen, the code emitted by the F# compiler in its various iterations is unlike that coming from the more traditional .net langauges, in a way that doesn't always play nice with tools developed against C# or VB.net (and perhaps C++/CLI).

NDepend will deserve an essay of its own in due course -- part of the quid pro quo for the copy that Patrick Smacchia has generously donated -- but today's point of interest is that some IL code generated from F# cannot (at least with current Reflector builds) be easily folded back into C# source.

Take this active pattern used to match a property getter method that just returns a backing field with a name related to the property name, which is at a current intermediate stage of refactoring

where MapToTypes turns an FxCop StatementCollection into a list of their corresponding NodeTypes, and @ wraps String.StartsWith with ordinal comparison.

Confronted with a request to decompile into C#, Reflector 5.1.6.0 asks me to file a bug report (which I have done) for an exception "Invalid branching statement for condition expression with target offset 002A."

Now, as far as I can tell from the IL, this is part of line 2 where it is balking

Presumably -- I would have to build some equivalent C# code to validate -- the brfalse.s is not one that C# (or any of the other languages Reflector knows of) would actually emit. Certainly it seems that the new F# CTP compiler is fond of emitting it, and it breaks Reflector every time it does.

Another good reason for avoiding too many imperative constructs in your F# code, it would seem.


Later: this C# code that approximates the above F#

compiles to

which indeed, as I suspected, uses a brtrue.s for the final branch out of the if statement (though there is a brfalse.s to perform the short-circuiting.

Code generation here is unchanged in 1.9.9.9 from the previous CTP.

Friday, October 02, 2009

C# under the covers

In an earlier exploration of debug build code patterns I unearthed bits of debug assistance built into simple field-backed getter properties in C#.

That same pattern -- evaluate to a temporary, and then unconditionally branch to a return -- also shows up in non-property methods that return a constant literal or a field. With purely empty methods, there is no such intermediate value to expose to the debugger, so the method just looks like

i.e. a block with a Nop and a Return (with no expression).

The canonical stub left by Visual Studio implementing an interface looks like

which resolves to

i.e. a block with a Nop and a Throw (with a Construct expression).

Nop and a Throw alone will catch any method that simply throws as its one and only operation (even if the exception is passed in from somewhere else); if you want to determine what is being thrown, then dissecting the expression would be required.

In writing a simple static analysis tool, identifying anything that just throws -- and has no decision making logic -- is sufficient for my purposes; for anyone tempted to put massive lambdas in the throw expression, testing for the Construct may be worthwhile.

Wednesday, September 16, 2009

F# -- separating coverage sheep from goats

The recent flurry of F# related posts have achieved a first little goal -- the FxCop rule to separate out developer written code truly uncovered by unit tests from the extras that the compiler adds in that I was mentioning earlier is now roughly working.

More details on my projects blog.

Saturday, September 12, 2009

More F# under the covers

This is a bit of a follow up to

and was provoked by running NDepend over -- naturally -- my current .net project as a first test drive (and being a work in progress, the debug build). Which then it highlit a number of quite simple methods involving function chaining as being candidates for refactoring (as well as the tut-tutting at the code generated for the Either< 'a,'b > type).

Let's look that series of short-circuitable tests, mentioned in the first post, something that would look like

in C#.

Let's have a type that expresses the intent more directly than a general purpose boolean, and a couple of helpful combinators...

Then we can write the chained tests as

Reflector shows us that the IL that comes out of the process in a debug build corresponds to code that looks like

If the Test function being used is a member function of the class, you get temporaries like

defined and being passed into the local lambdas; and there is no re-use of these temporaries.

Fortunately, the code generated in a release build avoids creating one-shot variables for these intermediate input values (and optimizes away the call to ignore) -- but it still has a separate tuple value for each test, rather than re-using a single name. Chain enough calls together and the number of hidden variables will start to trigger metrics-based alarms.

It looks like F# code is going to be rather heavy going for tools to work with.

This is unchanged with the Feb 2010 CTP (1.9.9.9).

Thursday, December 18, 2008