Scott Hanselman

Testing PowerShell scripts with NUnit

June 29, 2006 Comment on this post [4] Posted in PowerShell | NUnit
Sponsored By

We've been doing lots of PowerShell at work, but we're also a continuous integration shop and we try to do TDD so testing, specifically NUnit, is very important to us.

Here's how Jason Scheuerman from my team tests PowerShell scripts with NUnit.

using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;
using System.Management.Automation;
using NUnit.Framework;
using System.Security;

namespace PSUnitTestLibrary.Test
{
    [TestFixture]
    public class Program
    {
        private Runspace myRunSpace;
 
        [TestFixtureSetUp]
        public void PSSetup()
        {
            myRunSpace = RunspaceFactory.CreateRunspace();
            myRunSpace.Open();
            Pipeline cmd = myRunSpace.CreatePipeline(@"set-Location 'C:\dev\someproject");
            cmd.Invoke();
         } 

        [Test]
        public void PSTest()
        {
            Pipeline cmd = myRunSpace.CreatePipeline("get-location");
            Collection<PSObject> resultObject = cmd.Invoke();
            string currDir = resultObject[0].ToString();
            Assert.IsTrue(currDir == @"'C:\dev\someproject");

            cmd = myRunSpace.CreatePipeline(@".\new-securestring.ps1 password");
            resultObject = cmd.Invoke();
            SecureString ss = (SecureString)resultObject[0].ImmediateBaseObject;
            Assert.IsTrue(ss.Length == 8);

            myRunSpace.SessionStateProxy.SetVariable("ss", ss);
            cmd = myRunSpace.CreatePipeline(@".\getfrom-securestring.ps1 $ss");
            resultObject = cmd.Invoke();
            string clearText = (string)resultObject[0].ImmediateBaseObject;
            Assert.IsTrue(clearText == "password");
        }
    }
} 

The "CreatePipeline" calls are us telling PowerShell "do that!" The Pipeline of commands is created, passed into cmd then Invoke'd.

Also in this case we're assuming one PowerShell RunSpace per TestFixture and we're using the TestFixtureSetUp method to get that RunSpace going, but you could certainly move things around if you wanted different behavior or isolation.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

PowerShell, AnkhSVN and Subversion

June 29, 2006 Comment on this post [3] Posted in PowerShell | ASP.NET | Subversion | Tools
Sponsored By

I've been up to my ears in PowerShell (podcast) this last week at work. It continues to rock my world. I've been doing some automation work using Subversion not as source control but as a quasi-journaled file system.

There's a number of ways one could use Subversion from PowerShell, but I kind of want it as integrated as possible. I'm still trying out a few things. Here's what I've got so far...

Via SVN.exe

One could just use svn.exe, the command-line client. There's some obscure switches to remember, but Arild, the guy that wrote AnkhSVN (Subversion support within Visual Studio) has written a PowerShell tabcompletion script that gives one completion for svn.exe commands.

So you might type svn c[TAB] and get svn cat then [TAB] and get svn checkout, etc. Clever and cool but it just extends the command line tool with help.

Via TortoiseProc (the command line that comes with TortoiseSVN)

One could use the TortoiseProc without it being in the PATH with a script like this (stolen from Brad Wilson). This isn't that interesting of a script as the file could just be in the PATH, but it is an example of a decent pattern for calling tools that have discoverable paths (via the registry, etc) but aren't in the PATH proper.

if ($args.Length -lt 1) {
    write-host "usage: tsvn <command>"
    return
}

if ((test-path "HKLM:\Software\TortoiseSVN") -eq $false) {
 write-host -foregroundColor Red "Error: Could not find TortoiseProc.exe"
 return
}

$tortoiseKey = get-itemproperty "HKLM:\Software\TortoiseSVN"

if ($tortoiseKey -eq $null) {
 write-host -foregroundColor Red "Error: Could not find TortoiseProc.exe"
 return
}

$tortoise = $tortoiseKey.ProcPath

if ($tortoise -eq $null) {
 write-host -foregroundColor Red "Error: Could not find TortoiseProc.exe"
 return
}

$commandLine = '/command:' + $args[0] + ' /notempfile /path:"' + ((get-location).Path) + '"'
& $tortoise $commandLine

Use NSvn.Core directly, in-process, from PowerShell

The most attractive option to me was to find a way to talk to Subversion directly using something in-process. These last two options would require "shelling out" to non-PowerShell things. I was hoping to update Subversion from within ASP.NET for some Admin stuff, and while I could use System.EnterpriseServices to manage identity, I didn't want to have new processes firing up for a number of obvious reasons.

Deep in AnkhSVN is a great little .NET library called NSvn that did just want I needed. It doesn't appear to be distributed outside of Ankh, but I just took it from the latest install.  It's got no docs but Reflector to the rescue got me this PowerShell script that I called get-fromsvn.ps1.

param ([string]$svnurl       = $(read-host "Please specify the path to SVN"),
       [string]$svnlocalpath = $(read-host "Please specify the local path")
      )

if ([System.IO.Path]::IsPathRooted($svnlocalpath) -eq $false)
{
  throw "Please specific a local absolute path"
}

[System.Reflection.Assembly]::LoadFrom((join-Path $GLOBAL:someGlobalPath -childPath NSvn.Common.dll))
[System.Reflection.Assembly]::LoadFrom((join-Path $GLOBAL:someGlobalPath -childPath NSvn.Core.dll))

$PRIVATE:svnclient = new-object NSvn.Core.Client
$PRIVATE:svnclient.AuthBaton.Add( [NSvn.Core.AuthenticationProvider]::GetWindowsSimpleProvider() )
if ((test-Path $svnlocalpath) -eq $true )
{
  write-progress -status "Updating from $svnurl" -activity "Updating Working Copy"
  $PRIVATE:svnclient.Update($svnlocalpath, [NSvn.Core.Revision]::Head, $true)
}
else
{
  write-progress -status "Checkout from $svnurl" -activity "Updating Working Copy"
  $PRIVATE:svnclient.Checkout($svnurl, $svnlocalpath, [NSvn.Core.Revision]::Head, $true)
}

This script lets me checkout/update from Subversion happily. There's still some voodoo I need to work out like getting progress updates...

(How do you do a delegate from within PowerShell if a .NET Assembly needs a delegate with a certain signature? This library will call me back with status updates I'd like to broker to write-progress)

...but it gets 90% of what I need from an in-process call to Subversion from PowerShell. I know Arild Fines is not only the author of this library but a fan of PowerShell as well. Perhaps he'll help me either update this script, or better yet, write a PSSnapin to use Subversion as first-class cmdlets with progress and detailed logging. It'd be as useful, if not more so, than the SVN PSDriveProvider he's been messing with.

UPDATE: Arild Fines posted a solution to my question. I agree that the syntax for delegates is poo and I hope the PowerShell team takes that feedback. ;) The syntax for events, however, is quite elegant.

The addition of two lines gives me the write-progress and event notification support I wanted:

param ([string]$svnurl       = $(read-host "Please specify the path to SVN"),
       [string]$svnlocalpath = $(read-host "Please specify the local path")
      )

if ([System.IO.Path]::IsPathRooted($svnlocalpath) -eq $false)
{
  throw "Please specific a local absolute path"
}

[System.Reflection.Assembly]::LoadFrom((join-Path $GLOBAL:someGlobalPath -childPath NSvn.Common.dll))
[System.Reflection.Assembly]::LoadFrom((join-Path $GLOBAL:someGlobalPath -childPath NSvn.Core.dll))

$PRIVATE:svnclient = new-object NSvn.Core.Client

$PRIVATE:notificationcallback = [NSvn.Core.NotificationDelegate]{
        Write-Progress -status ("{0}: {1}" -f ($_.Action, $_.Path)) -activity "Updating Working Copy"
    }
    
$PRIVATE:svnclient.add_Notification($notificationcallback)   

$PRIVATE:svnclient.AuthBaton.Add( [NSvn.Core.AuthenticationProvider]::GetWindowsSimpleProvider() )
if ((test-Path $svnlocalpath) -eq $true )
{
  write-progress -status "Updating from $svnurl" -activity "Updating Working Copy"
  $PRIVATE:svnclient.Update($svnlocalpath, [NSvn.Core.Revision]::Head, $true)
}
else
{
  write-progress -status "Checkout from $svnurl" -activity "Updating Working Copy"
  $PRIVATE:svnclient.Checkout($svnurl, $svnlocalpath, [NSvn.Core.Revision]::Head, $true)
}

I hope more people in the community start digging into PowerShell and seeing how truly powerful it is to have .NET at the command line.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Hanselminutes Podcast 22

June 29, 2006 Comment on this post [1] Posted in Podcast | ASP.NET | XML | Tools
Sponsored By

HanselminutesMy twenty-second Podcast is up. This episode is mostly about portable GPS units but we'll call it Hanselminutiae #2. Next episode we'll steer towards more development topics.

We're listed in the iTunes Podcast Directory, so I encourage you to subscribe with a single click (two in Firefox) with the button below. For those of you on slower connections there are lo-fi and torrent-based versions as well.

Subscribe to my Podcast in iTunes

NEW COUPON CODE EXCLUSIVELY FOR HANSELMINUTES LISTENERS: The folks at XCeed are giving Hanselminutes listeners that is Coupon Code "hm-20-20." It'll work on their online shop or over the phone. This is an amazing deal, and I encourage you to check our their stuff. The coupon is good for 20% off any component or suite, with or without subscription, for 1 developer all the way up to a site license.

Our sponsors are XCeed, CodeSmith Tools, PeterBlum and the .NET Dev Journal. There's a $100 off CodeSmith coupon for Hanselminutes listeners - it's coupon code HM100. Spread the word, now's the time to buy.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

  • The basic MP3 feed is here, and the iPod friendly one is here. There's a number of other ways you can get it (streaming, straight download, etc) that are all up on the site just below the fold. I use iTunes, myself, to listen to most podcasts, but I also use FeedDemon and it's built in support.
  • Note that for now, because of bandwidth constraints, the feeds always have just the current show. If you want to get an old show (and because many Podcasting Clients aren't smart enough to not download the file more than once) you can always find them at http://www.hanselminutes.com.
  • I have, and will, also include the enclosures to this feed you're reading, so if you're already subscribed to ComputerZen and you're not interested in cluttering your life with another feed, you have the choice to get the 'cast as well.
  • If there's a topic you'd like to hear, perhaps one that is better spoken than presented on a blog, or a great tool you can't live without, contact me and I'll get it in the queue!

Enjoy. Who knows what'll happen in the next show?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Mom Graduates and Z Crawls

June 26, 2006 Comment on this post [15] Posted in Javascript | Parenting | Speaking | Z
Sponsored By

My Mom Graduates from College

A big week indeed for the Hanselman Family. My mom, Franci, age 60 (she doesn't mind saying) graduated from PSU with a Master's in Education. An amazing accomplishment at any age I say. She held her own against the 20-somethings and graduated Magna Cum Laude.

Correction - Dad says a 3.82 GPA is Summa Cum Laude, my bad!

She's been working at this for a very long time and we're all very proud of her. Congratulations Mom!

And speaking of life-altering forward motion, in a fantastic sequel to the Hit Video "Z Rolls" today Z created a soon-to-be-hit Z Crawls! Now we're frantically running around baby-proofing the house. Just kidding, it's been baby-proofed for months. ;)

Tip: We put a number of sheets of clear plexiglass in front of the multimedia/stereo equipment. These plastic pieces are L-shaped with one side sitting underneath the component with the other side shielding the front. This lets the remote control continue to work while preventing Z from inserting a sandwich into the VCR.

play video stop video indicatorhandleamount downloaded toggle sound launch in external player
Launch the streaming media file

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

DotNetKicks Flare for FeedBurner and DasBlog

June 24, 2006 Comment on this post [9] Posted in DasBlog | Javascript | Subversion | XML | Bugs
Sponsored By

A number of folks have asked about the "flare" on my site that appears at the bottom of each post. This is possible via a number of things.

First, the current version of DasBlog (get it via SubVersion) supports FeedBurner directly. FeedBurner will host your RSS Feed for you and DasBlog will automatically redirect folks who ask for your original feed to the new FeedBurner one, like http://feeds.feedburner.com/ScottHanselman. This will allow you to manipulate your feed in a number of new ways, including the addition of "feed flare."

DasBlogFeedBurner1

Gavin Joyce kindly created a FeedFlare for DotNetKicks.com that I put up at http://www.hanselman.com/blog/kickitflare.xml. I told FeedBurner, via their web-based configuration interface to use this new flare and it now appears automatically at the bottom of each of my posts along with a few other bits of flare.

DasBlogFeedBurner2

Then I added this line to my itemtemplate.blogtemplate file in my DasBlog specific theme folder:

<script src="http://feeds.feedburner.com/ScottHanselman?flareitem=<%PermalinkUrlRaw%>" type="text/javascript"></script>

This includes a bit of JavaScript from FeedBurner that creates the flare. I can have different flare in the feed and the site.

NOTE: Apparently there's some kind of known bug in FeedBurner on their side related to DasBlog's correct usage of the Description element in the RSS Feed, but they know about it and they are working on it. This causes, it seems, flare to not display in some aggregators.

I've been VERY happy with FeedBurner and it's cut my bandwidth in half and added a host of new abilities like more detailed statistics on the number of subscribers, where they're from and what aggregator they use.

So, the combination of DasBlog's auto-redirect, the FeedFlare API, modifying the DasBlog item template and their FeedFlare JavaScript make it so.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.