Damian Hickey

Mostly software and .NET related. Mostly.

Getting ReSharper to natively support xUnit

So I started a little online campaign yesterday to get the JetBrains folks to natively support xUnit in ReSharper. It already supports NUnit and MSTest (ugh) so what are the business cases for supporting xUnit?

  1. xUnit was part created by the same guy who was also involed in crating NUnit, Jame NewKirk. He details his leasons learned here, and thus can be argued that xUnit is a more modern version of NUnit.
  2. On NuGet gallery, NUnit has ~75K downloads compared to xUnit's ~14K, about 19% of NUnit's total. Not an insignificant market share.
  3. xUnit stats show a slow, but steady increase in adoption rates.
  4. xUnit-contrib, a projects whose predominate purpose is to provide a plugin for the ReSharper runner, also shows steady adoption. This project always lags behind the ReSharper releases and completely unusable during EAP phases due to API churn.
  5. xUnit, current version being 1.9, has had a version independent runner since 1.6. Thus ReSharper and the developer won't be constrained to any particular xUnit version, as long as it's > 1.6.

I hypothsized that xUnit adoption is being held back because ReSharper doesn't natively support it. That is, people are using it because using something else is simply too much friction. JetBrains ran a (non-scientific) poll to see of the NUnit/MSTest user base, who would move to another unit test framework if the friction is removed. As of posting, this is the current result:

The interesting thing is that over 54% of respondents would make a move if there was no friction. And at 39%, I am concluding from that that native xUnit support is a desired feature by ReSharper's customers. I hope JetBrains arrive at the same conclusion.

To vote on the offical issue, do so here http://youtrack.jetbrains.com/issue/RSRP-205721

Decrapify your Visual Studio installation

Fed up with the Visual Studio spamming your Programs and Features list? Make your voice heard over here.

In mean time, a lot of MSIs are silently uninstallable so you can use PC Decrapifier to remove them in one go.

Rename a Visual Studio Project using PowerShell

I seem to be mostly posting about PowerShell these days... but it's because I'm find all these neat ways to make my life easier.

Renaming a project within Visual Studio is easy enough. If, like me, your project folder name is the same as the project and you'd like that to be renamed to. Doing it directly in Windows Explorer will result in a couple of problems: the solution won't find the project so you have to remove and re-add it; projects that referenced the renamed project will have to have their references re-added.

And then your root namespace, assembly name, and assembly title need to be fixed too (assuming these are the same as the project name, which is usually the case).

clickety-clickety-clickety...

The script below solve this problem. It:

  1. hg renames the project file
  2. hg renames the folder
  3. changes the assembly title in AssemblyInfo.cs
  4. changes the assembly root namespace in the project file
  5. changes the asssembly name in the project file
  6. updates all other .csproj file's reference path to the renamed project
  7. updates the path to the renamed project in the solution file
  8. is designed to be called from the same working folder as the .sln
  9. expects all project folders to be immediate child folders of the working folder

Note: This a definite 'works for me' script so will probably need some adjusting to work for you.

function Rename-Project
{
	# designed to run from the src folder
	param(
		[string]$projectName=$(throw "projectName required."),
		[string]$newProjectName=$(throw "newProjectName required.")
	)
	
	if(!(Test-Path $projectName)){
		Write-Error "No project folder '$projectName' found"
		return
	}
	
	if(!(Test-Path $projectName\$projectName.csproj)){
		Write-Error "No project '$projectName\$projectName.dll' found"
		return
	}
	
	if((Test-Path $newProjectName)){
		Write-Error "Project '$newProjectName' already exists"
		return
	}
	
	# project
	hg rename $projectName\$projectName.csproj $projectName\$newProjectName.csproj
	
	# folder
	hg rename $projectName $newProjectName
	
	# assembly title
	$assemblyInfoPath = "$newProjectName\Properties\AssemblyInfo.cs"
	(gc $assemblyInfoPath) -replace """$projectName""","""$newProjectName""" | sc $assemblyInfoPath
	
	# root namespace
	$projectFile = "$newProjectName\$newProjectName.csproj"
	(gc $projectFile) -replace "<RootNamespace>$projectName</RootNamespace>","<RootNamespace>$newProjectName</RootNamespace>" | sc $projectFile
	
	# assembly name
	(gc $projectFile) -replace "<AssemblyName>$projectName</AssemblyName>","<AssemblyName>$newProjectName</AssemblyName>" | sc $projectFile
	
	# other project references
	gci -Recurse -Include *.csproj |% { (gc $_) -replace "..\\$projectName\\$projectName.csproj", "..\$newProjectName\$newProjectName.csproj" | sc $_ }
	gci -Recurse -Include *.csproj |% { (gc $_) -replace "<Name>$projectName</Name>", "<Name>$newProjectName</Name>" | sc $_ }
	
	# solution 
	gci -Recurse -Include *.sln |% { (gc $_) -replace "\""$projectName\""", """$newProjectName""" | sc $_ }
	gci -Recurse -Include *.sln |% { (gc $_) -replace "\""$projectName\\$projectName.csproj\""", """$newProjectName\$newProjectName.csproj""" | sc $_ }
}

 

I'm not totally enamoured with the find/replace bits, but they seem to work.

Any improvements will of course be welcomed :)

Script to strip strong names from a project's references.

In my (never-ending) quest for true continous integrations nirvana, I find that version numbers in the a projects reference...

<Reference Include="System.Reactive, Version=1.0.2787.104, Culture=neutral, etc">
	<HintPath>..\..\libs\RxFramework\System.Reactive.dll</HintPath>
</Reference>

... sometimes obstruct me. Especially when dropping in a new version and attempting to build from a script.

This script below will recursively go through all the .csproj files from a base directory and strip out the strong name above, leaving:

<Reference Include="System.Reactive">
	<HintPath>..\..\libs\RxFramework\System.Reactive.dll</HintPath>
</Reference>

Le script:

param(
	[string]$path = $(throw "path required.")
)

[Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null
$csprojs = Get-ChildItem . -Recurse | Where-Object {$_.Extension -match "csproj"} | Select-Object FullName
$ns = "http://schemas.microsoft.com/developer/msbuild/2003"
foreach($csproj in $csprojs){
	$projectModified = $false
	$path = $csproj.FullName
	$csprojXmlDoc = [System.Xml.Linq.XDocument]::Load($path)
	$csprojXmlDoc.Descendants("{$ns}Reference") | 
		Where-Object {$_.Attribute("Include").Value.Contains(",") } | 
		ForEach { 
			$index = $_.Attribute("Include").Value.IndexOf(',')
			$newAttributeValue =  $_.Attribute("Include").Value.SubString(0, $index)
			Write-Host ($path + ": " + $_.Attribute("Include").Value + " -> $newAttributeValue")
			$_.Attribute("Include").Value = $newAttributeValue
			$projectModified = $true
			}
	if($projectModified -eq $true){
		$csprojXmlDoc.Save($path)
	}
}

A NuGet (for tools) / OpenWrap (for libs) hybrid Visual Studio solution template

I've created a solution template as a that may form the basis of reference solution to be used in my company. It's available to view here

Requirements & Design decisions:

  1. To be used in a corp environment.
  2. No packages / wraps / dependencies / tools are committed to version control, except the bare minimum to bootstrap the build and/or restore dependencies.
  3. Packages / wraps / dependencies / tools may come from a variety of sources - nuget galley, local nuget repo, local openwrap repo.
  4. Anyone should be able to download the source and single click build the solution with no additional effort, friction or setup. (i.e. just run Build.ps1 )
  5. Easily managed via CI without addition setup and minimal coupling (i.e. .\Build.ps1 -buildNumber 2 )
  6. Sticking with released version of tools.
  7. I am using NuGet as my 'tools' manager. OpenWrap does not appear to be suitable for this purpose.
  8. OpenWrap seems to want to be responsible for compiling, running tests and creating the package. I already have psake for that. In my mind, creating a packages is a post-build step. Anyway, build: none to the rescue. 
  9. A psake build task sets the version number (using a build number if supplied) and keeps SharedAssemblyInfo.cs and the version file in sync.
  10. We use code contracts and the contract assemblies must be shipped in the wrap.

Some of the things I don't like:

  1. There is a bit too much going on in the root of the project, but at least Build.ps1 is at the top.
  2. Would rather not have to commit warps\openwrap-1.0.0.53270092.wrap to  support the bootstrap.
  3. ... and I'd like to add wraps/* to .hgignore.
  4. Can't isolate XUnit to only the DH.SolutionTemplate.Tests project (I know something is in the pipeline for this).
  5. Explicitly defining what goes into the resulting wrap may be a place where mistakes can easily happen. Not sure how to mitigate that.
  6. Having to use ;-shellInstall none; everywhere. I think o.exe default behaviour should be 'do nothing' and installation is an explicit step. I don't know enough of OpenWraps's architecture or intention to hold that belief firmly though :)
  7. In RestoreDependencies.ps1 I am deliberately adding the NuGet gallery remote source. This will add it permanently to the users remote list which I'd prefer not to affect. (I know there is something in OpenWrap's pipeline for this).

So, if there are any places this could be improved, let me know.