Skip to content

Instantly share code, notes, and snippets.

@megamorf
Last active August 30, 2015 17:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save megamorf/e7ede79fa94c89b4c625 to your computer and use it in GitHub Desktop.
Save megamorf/e7ede79fa94c89b4c625 to your computer and use it in GitHub Desktop.
New-FileSystemWatcher function
$psake.use_exit_on_error = $true
properties {
$currentDir = resolve-path .
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
$baseDir = $psake.build_script_dir
}
Task watch {
$WatchParams = @{
Path = $basedir
IncludeSubDirectories = $true
}
New-FileSystemWatcher @WatchParams | % {
Write-Host ("File [{0}] has been [{1}]" -f $_.Path,$_.OperationResult)
Set-Location "$baseDir"
Invoke-Pester
Set-Location $currentDir
}
}
Set-StrictMode -Version 2
Function New-FileSystemWatcher
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias('PSPath','LiteralPath')]
[ValidateNotNullOrEmpty()]
[String] $Path,
[ValidateSet('Change','Create','Rename','Delete','All')]
[string[]] $TriggerOn = 'all',
[switch] $Recurse
)
If(Test-Path $Path)
{
if((Resolve-Path $Path).Provider.ToString() -ne 'Microsoft.PowerShell.Core\FileSystem')
{
throw "Please specify a valid FileSystem path."
}
}
else
{
throw "Item [$Path] not found"
}
Write-Verbose "[$($myinvocation.mycommand)] :: Path is [$Path]"
$watcher = New-Object -TypeName System.IO.FileSystemWatcher -Property @{
Path = $Path
IncludeSubdirectories = $Recurse
EnableRaisingEvents = $false
NotifyFilter = [System.IO.NotifyFilters]::LastWrite -bor [System.IO.NotifyFilters]::FileName
}
$Conditions = 0
switch ($TriggerOn)
{
{$TriggerOn -contains 'All' } { $Conditions = 'All'; break }
{$TriggerOn -contains 'Change'} { $Conditions = $Conditions -bor [System.IO.WatcherChangeTypes]::Changed }
{$TriggerOn -contains 'Delete'} { $Conditions = $Conditions -bor [System.IO.WatcherChangeTypes]::Deleted }
{$TriggerOn -contains 'Rename'} { $Conditions = $Conditions -bor [System.IO.WatcherChangeTypes]::Renamed }
{$TriggerOn -contains 'Create'} { $Conditions = $Conditions -bor [System.IO.WatcherChangeTypes]::Created }
Default { throw "you broke it somehow" }
}
Write-Verbose ("[$($myinvocation.mycommand)] :: Invoking Watcher - Recurse={0} OnTriggers={1} ..." -f [bool]$Recurse, "$TriggerOn")
while ($true)
{
$result = $watcher.WaitForChanged($conditions, 1000)
if ($result.TimedOut -eq $false)
{
New-Object psobject -Property @{
Path = (Join-Path -Path $Path -ChildPath $result.Name)
OperationResult = $result.ChangeType.ToString()
}
}
}
}
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
. "$here\$sut"
Describe "New-FileSystemWatcher" {
It "should throw with valid registry path" {
{ New-FileSystemWatcher -Path 'HKCU:\Software' } | Should Throw "Please specify a valid FileSystem path"
}
It "should throw with invalid registry path" {
$Path = 'HKCU:\ShouldNotExist'
{ New-FileSystemWatcher -Path $Path } | Should Throw "Item [$Path] not found"
}
It "should throw with non-existent path" {
$Path = 'C:\ShouldNotExist'
{ New-FileSystemWatcher -Path $Path } | Should Throw "Item [$Path] not found"
}
}
@dlwyatt
Copy link

dlwyatt commented Aug 30, 2015

As written, I don't think that function's going to be testable. As you say, it just runs forever. Even if it had an exit condition, you would also need to have two bits of code executing in parallel, one to run New-FileSystemWatcher and another to make some sort of change to the directory it's watching (which is possible, but can make the testing logic tricky due to timing).

One easy way to make this testable is to add a duration or timeout parameter to the function. It can default to run forever (perhaps with a value of 0 or -1), but if your caller passes in a value, then the function will only run as long as specified. Then it's easier for your Pester tests to work. Something along these lines:

Describe 'New-FileSystemWatcher' {
    It 'Works' {
        $job = Start-Job -ScriptBlock { Start-Sleep -Milliseconds 500; New-Item -Path "$($args[0])\file.txt" } -ArgumentList $TestDrive
        $result = New-FileSystemWatcher -Path $TestDrive -Timeout 1000  # Using the -Timeout parameter which doesn't exist yet.
        $job | Wait-Job | Remove-Job
    
        $matchingResult = $result | Where { $_.Path -eq (Join-Path $TestDrive file.txt) -and $_.OperationResult -eq 'Created' }
        $matchingResult | Should Not Be $null
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment