From 4402dce48f8af9fbd46ec98864f7b591ea2d4d40 Mon Sep 17 00:00:00 2001 From: alx9r Date: Fri, 4 Aug 2017 07:00:28 -0700 Subject: [PATCH 01/11] replace Get- and Get-Normalized with Test- --- Functions/processPersistentItem.Tests.ps1 | 77 +++++++++-------------- Functions/processPersistentItem.ps1 | 26 +++----- 2 files changed, 35 insertions(+), 68 deletions(-) diff --git a/Functions/processPersistentItem.Tests.ps1 b/Functions/processPersistentItem.Tests.ps1 index 2a7bb34..9982dc1 100644 --- a/Functions/processPersistentItem.Tests.ps1 +++ b/Functions/processPersistentItem.Tests.ps1 @@ -16,9 +16,8 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { Getter = 'Get-PersistentItem' Adder = 'Add-PersistentItem' Remover = 'Remove-PersistentItem' - PropertyGetter = 'Get-PersistentItemProperty' - PropertySetter = 'Set-PersistentItemProperty' - PropertyNormalizer = 'Get-NormalizedPersistentItemProperty' + PropertySetter = 'Set-Property' + PropertyTester = 'Test-Property' } $coreDelegates = @{ Getter = 'Get-PersistentItem' @@ -44,9 +43,8 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { $Mode -eq 'Set' -and $_Keys.Key -eq 'key value' -and $Properties.P -eq 'P desired' -and - $PropertyGetter -eq 'Get-PersistentItemProperty' -and - $PropertySetter -eq 'Set-PersistentItemProperty' -and - $PropertyNormalizer -eq 'Get-NormalizedPersistentItemProperty' + $PropertySetter -eq 'Set-Property' -and + $PropertyTester -eq 'Test-Property' } } } @@ -198,46 +196,38 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { } -function Get-PersistentItemProperty { param ($Key,$PropertyName) } -function Set-PersistentItemProperty { param ($Key,$PropertyName,$Value) } -function Get-NormalizedPersistentItemProperty { param ($PropertyName,$Value) } +function Set-Property { param ($Key,$PropertyName,$Value) } +function Test-Property { param ($Key,$PropertyName,$Value) } Describe 'Invoke-ProcessPersistentItemProperty' { - Mock Get-NormalizedPersistentItemProperty -Verifiable - Mock Get-PersistentItemProperty -Verifiable - Mock Set-PersistentItemProperty { 'junk' } -Verifiable + Mock Set-Property { 'junk' } -Verifiable + Mock Test-Property { $true } -Verifiable $delegates = @{ - PropertyGetter = 'Get-PersistentItemProperty' - PropertySetter = 'Set-PersistentItemProperty' - PropertyNormalizer = 'Get-NormalizedPersistentItemProperty' + PropertySetter = 'Set-Property' + PropertyTester = 'Test-Property' } Context 'Set, property already correct' { - Mock Get-NormalizedPersistentItemProperty { 'already correct' } -Verifiable - Mock Get-PersistentItemProperty { 'already correct' } -Verifiable + Mock Test-Property { $true } -Verifiable It 'returns nothing' { $splat = @{ Keys = @{ Key = 'key value' } - Properties = @{ P = 'already correct' } + Properties = @{ P = 'correct' } } $r = Invoke-ProcessPersistentItemProperty Set @splat @delegates $r | Should beNullOrEmpty } It 'correctly invokes functions' { - Assert-MockCalled Get-NormalizedPersistentItemProperty 1 { - $PropertyName -eq 'P' -and - $Value -eq 'already correct' - } - Assert-MockCalled Get-PersistentItemProperty 1 { + Assert-MockCalled Test-Property 1 { $Key -eq 'key value' -and - $PropertyName -eq 'P' + $PropertyName -eq 'P' -and + $Value -eq 'correct' } - Assert-MockCalled Set-PersistentItemProperty 0 -Exactly + Assert-MockCalled Set-Property 0 -Exactly } } Context 'Test, property correct' { - Mock Get-NormalizedPersistentItemProperty { 'correct' } -Verifiable - Mock Get-PersistentItemProperty { 'correct' } -Verifiable + Mock Test-Property { $true } -Verifiable It 'returns true' { $splat = @{ Keys = @{ Key = 'key value' } @@ -247,20 +237,16 @@ Describe 'Invoke-ProcessPersistentItemProperty' { $r | Should be $true } It 'correctly invokes functions' { - Assert-MockCalled Get-NormalizedPersistentItemProperty 1 { + Assert-MockCalled Test-Property 1 { + $Key -eq 'key value' -and $PropertyName -eq 'P' -and $Value -eq 'correct' } - Assert-MockCalled Get-PersistentItemProperty 1 { - $Key -eq 'key value' -and - $PropertyName -eq 'P' - } - Assert-MockCalled Set-PersistentItemProperty 0 -Exactly + Assert-MockCalled Set-Property 0 -Exactly } } Context 'Set, correcting property' { - Mock Get-NormalizedPersistentItemProperty { 'normalized' } -Verifiable - Mock Get-PersistentItemProperty { 'original' } -Verifiable + Mock Test-Property { $false } -Verifiable It 'returns nothing' { $splat = @{ Keys = @{ Key = 'key value' } @@ -270,15 +256,12 @@ Describe 'Invoke-ProcessPersistentItemProperty' { $r | Should beNullOrEmpty } It 'correctly invokes functions' { - Assert-MockCalled Get-NormalizedPersistentItemProperty 1 { + Assert-MockCalled Set-Property 1 -Exactly { + $Key -eq 'key value' -and $PropertyName -eq 'P' -and $Value -eq 'desired' } - Assert-MockCalled Get-PersistentItemProperty 1 { - $Key -eq 'key value' -and - $PropertyName -eq 'P' - } - Assert-MockCalled Set-PersistentItemProperty 1 -Exactly { + Assert-MockCalled Set-Property 1 -Exactly { $Key -eq 'key value' -and $PropertyName -eq 'P' -and $Value -eq 'desired' @@ -286,8 +269,7 @@ Describe 'Invoke-ProcessPersistentItemProperty' { } } Context 'Test, property incorrect' { - Mock Get-NormalizedPersistentItemProperty { 'normalized' } -Verifiable - Mock Get-PersistentItemProperty { 'original' } -Verifiable + Mock Test-Property { $false } -Verifiable It 'returns false' { $splat = @{ Keys = @{ Key = 'key value' } @@ -297,15 +279,12 @@ Describe 'Invoke-ProcessPersistentItemProperty' { $r | Should be $false } It 'correctly invokes functions' { - Assert-MockCalled Get-NormalizedPersistentItemProperty 1 { + Assert-MockCalled Test-Property 1 { + $Key -eq 'key value' -and $PropertyName -eq 'P' -and $Value -eq 'desired' } - Assert-MockCalled Get-PersistentItemProperty 1 { - $Key -eq 'key value' -and - $PropertyName -eq 'P' - } - Assert-MockCalled Set-PersistentItemProperty 0 -Exactly + Assert-MockCalled Set-Property 0 -Exactly } } } diff --git a/Functions/processPersistentItem.ps1 b/Functions/processPersistentItem.ps1 index 246160b..bf4ae15 100644 --- a/Functions/processPersistentItem.ps1 +++ b/Functions/processPersistentItem.ps1 @@ -39,20 +39,16 @@ function Invoke-ProcessPersistentItem [hashtable] $Properties, - [Parameter(ParameterSetName = 'with_properties', - Mandatory = $true)] - [string] - $PropertyGetter, - [Parameter(ParameterSetName = 'with_properties', Mandatory = $true)] [string] $PropertySetter, + [Parameter(ParameterSetName = 'with_properties', Mandatory = $true)] [string] - $PropertyNormalizer + $PropertyTester ) process { @@ -103,9 +99,8 @@ function Invoke-ProcessPersistentItem Mode = $Mode Keys = $_Keys Properties = $Properties - PropertyGetter = $PropertyGetter PropertySetter = $PropertySetter - PropertyNormalizer = $PropertyNormalizer + PropertyTester = $PropertyTester } Invoke-ProcessPersistentItemProperty @splat } @@ -129,17 +124,13 @@ function Invoke-ProcessPersistentItemProperty [hashtable] $Properties, - [Parameter(Mandatory = $true)] - [string] - $PropertyGetter, - [Parameter(Mandatory = $true)] [string] $PropertySetter, [Parameter(Mandatory = $true)] [string] - $PropertyNormalizer + $PropertyTester ) process { @@ -149,13 +140,10 @@ function Invoke-ProcessPersistentItemProperty # this is the desired value provided by the user $desired = $Properties.$propertyName - # normalize the desired value - $normalized = & $PropertyNormalizer -PropertyName $propertyName -Value $desired - - # get the existing value - $existing = & $PropertyGetter @_Keys -PropertyName $propertyName + # test for the desired value + $alreadyCorrect = & $PropertyTester @_Keys -PropertyName $propertyName -Value $desired - if ( $existing -ne $normalized ) + if ( -not $alreadyCorrect ) { if ( $Mode -eq 'Test' ) { From 712a0cf818499d036fb3b763890e108a685594fd Mon Sep 17 00:00:00 2001 From: alx9r Date: Fri, 4 Aug 2017 07:01:28 -0700 Subject: [PATCH 02/11] use terser command names in fixture --- Functions/processPersistentItem.Tests.ps1 | 98 +++++++++++------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/Functions/processPersistentItem.Tests.ps1 b/Functions/processPersistentItem.Tests.ps1 index 9982dc1..0fa0150 100644 --- a/Functions/processPersistentItem.Tests.ps1 +++ b/Functions/processPersistentItem.Tests.ps1 @@ -2,31 +2,31 @@ Import-Module WindowsShell -Force InModuleScope WindowsShell { -function Get-PersistentItem { param ($Key) } -function Add-PersistentItem { param ($Key) } -function Remove-PersistentItem { param ($Key) } +function Get-Item { param ($Key) } +function Add-Item { param ($Key) } +function Remove-Item { param ($Key) } Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { - Mock Get-PersistentItem -Verifiable - Mock Add-PersistentItem -Verifiable - Mock Remove-PersistentItem { 'junk' } -Verifiable + Mock Get-Item -Verifiable + Mock Add-Item -Verifiable + Mock Remove-Item { 'junk' } -Verifiable Mock Invoke-ProcessPersistentItemProperty -Verifiable $delegates = @{ - Getter = 'Get-PersistentItem' - Adder = 'Add-PersistentItem' - Remover = 'Remove-PersistentItem' + Getter = 'Get-Item' + Adder = 'Add-Item' + Remover = 'Remove-Item' PropertySetter = 'Set-Property' PropertyTester = 'Test-Property' } $coreDelegates = @{ - Getter = 'Get-PersistentItem' - Adder = 'Add-PersistentItem' - Remover = 'Remove-PersistentItem' + Getter = 'Get-Item' + Adder = 'Add-Item' + Remover = 'Remove-Item' } Context '-Ensure Present: absent, Set' { - Mock Add-PersistentItem { 'item' } + Mock Add-Item { 'item' } It 'returns nothing' { $splat = @{ Keys = @{ Key = 'key value' } @@ -36,9 +36,9 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { $r | Should beNullOrEmpty } It 'correctly invokes functions' { - Assert-MockCalled Get-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Add-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Remove-PersistentItem 0 -Exactly + Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Add-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Remove-Item 0 -Exactly Assert-MockCalled Invoke-ProcessPersistentItemProperty 1 { $Mode -eq 'Set' -and $_Keys.Key -eq 'key value' -and @@ -49,16 +49,16 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { } } Context '-Ensure Present: absent, Set - omitting properties skips setting properties' { - Mock Add-PersistentItem { 'item' } + Mock Add-Item { 'item' } It 'returns nothing' { $splat = @{ Keys = @{ Key = 'key value' } } $r = Invoke-ProcessPersistentItem Set Present @splat @coreDelegates $r | Should beNullOrEmpty } It 'correctly invokes functions' { - Assert-MockCalled Get-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Add-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Remove-PersistentItem 0 -Exactly + Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Add-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Remove-Item 0 -Exactly Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly } } @@ -72,14 +72,14 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { $r | Should be $false } It 'correctly invokes functions' { - Assert-MockCalled Get-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Add-PersistentItem 0 -Exactly - Assert-MockCalled Remove-PersistentItem 0 -Exactly + Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Add-Item 0 -Exactly + Assert-MockCalled Remove-Item 0 -Exactly Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly } } Context '-Ensure Present: present, Set' { - Mock Get-PersistentItem { 'item' } -Verifiable + Mock Get-Item { 'item' } -Verifiable It 'returns nothing' { $splat = @{ Keys = @{ Key = 'key value' } @@ -89,14 +89,14 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { $r | Should beNullOrEmpty } It 'correctly invokes functions' { - Assert-MockCalled Get-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Add-PersistentItem 0 -Exactly - Assert-MockCalled Remove-PersistentItem 0 -Exactly + Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Add-Item 0 -Exactly + Assert-MockCalled Remove-Item 0 -Exactly Assert-MockCalled Invoke-ProcessPersistentItemProperty 1 } } Context '-Ensure Present: present, Test' { - Mock Get-PersistentItem { 'item' } -Verifiable + Mock Get-Item { 'item' } -Verifiable Mock Invoke-ProcessPersistentItemProperty { 'property test result' } -Verifiable It 'returns result of properties test' { $splat = @{ @@ -107,23 +107,23 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { $r | Should be 'property test result' } It 'correctly invokes functions' { - Assert-MockCalled Get-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Add-PersistentItem 0 -Exactly - Assert-MockCalled Remove-PersistentItem 0 -Exactly + Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Add-Item 0 -Exactly + Assert-MockCalled Remove-Item 0 -Exactly Assert-MockCalled Invoke-ProcessPersistentItemProperty 1 } } Context '-Ensure Present: present, Test - omitting properties skips setting properties' { - Mock Get-PersistentItem { 'item' } -Verifiable + Mock Get-Item { 'item' } -Verifiable It 'returns result of properties test' { $splat = @{ Keys = @{ Key = 'key value' } } $r = Invoke-ProcessPersistentItem Test Present @splat @coreDelegates $r | Should be $true } It 'correctly invokes functions' { - Assert-MockCalled Get-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Add-PersistentItem 0 -Exactly - Assert-MockCalled Remove-PersistentItem 0 -Exactly + Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Add-Item 0 -Exactly + Assert-MockCalled Remove-Item 0 -Exactly Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly } } @@ -137,9 +137,9 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { $r | Should beNullOrEmpty } It 'correctly invokes functions' { - Assert-MockCalled Get-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Add-PersistentItem 0 -Exactly - Assert-MockCalled Remove-PersistentItem 0 -Exactly + Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Add-Item 0 -Exactly + Assert-MockCalled Remove-Item 0 -Exactly Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly } } @@ -153,14 +153,14 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { $r | Should be $true } It 'correctly invokes functions' { - Assert-MockCalled Get-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Add-PersistentItem 0 -Exactly - Assert-MockCalled Remove-PersistentItem 0 -Exactly + Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Add-Item 0 -Exactly + Assert-MockCalled Remove-Item 0 -Exactly Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly } } Context '-Ensure Absent: present, Set' { - Mock Get-PersistentItem { 'item' } -Verifiable + Mock Get-Item { 'item' } -Verifiable It 'returns nothing' { $splat = @{ Keys = @{ Key = 'key value' } @@ -170,14 +170,14 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { $r | Should beNullOrEmpty } It 'correctly invokes functions' { - Assert-MockCalled Get-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Add-PersistentItem 0 -Exactly - Assert-MockCalled Remove-PersistentItem 1 { $Key -eq 'key value' } + Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Add-Item 0 -Exactly + Assert-MockCalled Remove-Item 1 { $Key -eq 'key value' } Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly } } Context '-Ensure Absent: present, Test' { - Mock Get-PersistentItem { 'item' } -Verifiable + Mock Get-Item { 'item' } -Verifiable It 'returns false' { $splat = @{ Keys = @{ Key = 'key value' } @@ -187,9 +187,9 @@ Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { $r | Should be $false } It 'correctly invokes functions' { - Assert-MockCalled Get-PersistentItem 1 { $Key -eq 'key value' } - Assert-MockCalled Add-PersistentItem 0 -Exactly - Assert-MockCalled Remove-PersistentItem 0 -Exactly + Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } + Assert-MockCalled Add-Item 0 -Exactly + Assert-MockCalled Remove-Item 0 -Exactly Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly } } From e444205d86775d41194f95a355024bcba683ccde Mon Sep 17 00:00:00 2001 From: alx9r Date: Fri, 4 Aug 2017 07:16:12 -0700 Subject: [PATCH 03/11] use Test- instead of Get- --- Functions/processShellLibrary.ps1 | 3 +-- Functions/processShortcut.ps1 | 3 +-- Functions/shellLibrary.ps1 | 19 ++++++++++++------- Functions/shortcut.ps1 | 28 ++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/Functions/processShellLibrary.ps1 b/Functions/processShellLibrary.ps1 index 2b3ea67..4fd658d 100644 --- a/Functions/processShellLibrary.ps1 +++ b/Functions/processShellLibrary.ps1 @@ -102,9 +102,8 @@ function Invoke-ProcessShellLibrary Getter = 'Get-ShellLibrary' Adder = 'Add-ShellLibrary' Remover = 'Remove-ShellLibrary' - PropertyGetter = 'Get-ShellLibraryProperty' PropertySetter = 'Set-ShellLibraryProperty' - PropertyNormalizer = 'Get-NormalizedShellLibraryProperty' + PropertyTester = 'Test-ShellLibraryProperty' } Invoke-ProcessPersistentItem @splat } diff --git a/Functions/processShortcut.ps1 b/Functions/processShortcut.ps1 index 7604218..e34fc7e 100644 --- a/Functions/processShortcut.ps1 +++ b/Functions/processShortcut.ps1 @@ -92,9 +92,8 @@ function Invoke-ProcessShortcut Getter = 'Get-ShortCut' Adder = 'Add-ShortCut' Remover = 'Remove-ShortCut' - PropertyGetter = 'Get-ShortCutProperty' PropertySetter = 'Set-ShortCutProperty' - PropertyNormalizer = 'Get-NormalizedShortCutProperty' + PropertyTest = 'Test-ShortCutProperty' } Invoke-ProcessPersistentItem @splat } diff --git a/Functions/shellLibrary.ps1 b/Functions/shellLibrary.ps1 index 6235217..da46b83 100644 --- a/Functions/shellLibrary.ps1 +++ b/Functions/shellLibrary.ps1 @@ -235,25 +235,30 @@ function Set-ShellLibraryProperty } } -function Get-NormalizedShellLibraryProperty +function Test-ShellLibraryProperty { [CmdletBinding()] param ( [Parameter(Mandatory = $true, - Position = 1)] + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true)] + [string] + [Alias('Key','Name')] + $LibraryName, + + [Parameter(Mandatory = $true, + position = 1)] + [ValidateSet('TypeName','IconReferencePath')] [string] $PropertyName, [Parameter(Mandatory = $true, - Position = 2, - ValueFromPipeline = $true)] - [AllowNull()] - [AllowEmptyString()] + position = 2)] $Value ) process { - $Value + $Value -eq (Get-ShellLibraryProperty -LibraryName $LibraryName -PropertyName $PropertyName) } } diff --git a/Functions/shortcut.ps1 b/Functions/shortcut.ps1 index 1f0f8df..6f62363 100644 --- a/Functions/shortcut.ps1 +++ b/Functions/shortcut.ps1 @@ -219,3 +219,31 @@ function Get-NormalizedShortcutProperty return $shortcut.$PropertyName } } + +function Test-ShortcutProperty +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true)] + [string] + $Path, + + [Parameter(Mandatory = $true, + position = 1)] + [string] + $PropertyName, + + [Parameter(Mandatory = $true, + position = 2)] + $Value + ) + process + { + $actualValue = Get-ShortcutProperty -Path $Path -PropertyName $PropertyName + $normalizedValue = Get-NormalizedShortcutProperty -PropertyName $PropertyName -Value $Value + return $actualValue -eq $normalizedValue + } +} From f70922e0add4c2b99dc39b5a9c4b4ac417a43d4b Mon Sep 17 00:00:00 2001 From: alx9r Date: Fri, 4 Aug 2017 09:00:51 -0700 Subject: [PATCH 04/11] move processPersistentItem.ps1 to ToolFoundations --- .../processPersistentItem-564a734 .ps1 | 0 Functions/processPersistentItem.Tests.ps1 | 291 ------------------ 2 files changed, 291 deletions(-) rename Functions/processPersistentItem.ps1 => External/processPersistentItem-564a734 .ps1 (100%) delete mode 100644 Functions/processPersistentItem.Tests.ps1 diff --git a/Functions/processPersistentItem.ps1 b/External/processPersistentItem-564a734 .ps1 similarity index 100% rename from Functions/processPersistentItem.ps1 rename to External/processPersistentItem-564a734 .ps1 diff --git a/Functions/processPersistentItem.Tests.ps1 b/Functions/processPersistentItem.Tests.ps1 deleted file mode 100644 index 0fa0150..0000000 --- a/Functions/processPersistentItem.Tests.ps1 +++ /dev/null @@ -1,291 +0,0 @@ -Import-Module WindowsShell -Force - -InModuleScope WindowsShell { - -function Get-Item { param ($Key) } -function Add-Item { param ($Key) } -function Remove-Item { param ($Key) } - -Describe 'Invoke-ProcessPersistentItem -Ensure Present: ' { - Mock Get-Item -Verifiable - Mock Add-Item -Verifiable - Mock Remove-Item { 'junk' } -Verifiable - Mock Invoke-ProcessPersistentItemProperty -Verifiable - - $delegates = @{ - Getter = 'Get-Item' - Adder = 'Add-Item' - Remover = 'Remove-Item' - PropertySetter = 'Set-Property' - PropertyTester = 'Test-Property' - } - $coreDelegates = @{ - Getter = 'Get-Item' - Adder = 'Add-Item' - Remover = 'Remove-Item' - } - - Context '-Ensure Present: absent, Set' { - Mock Add-Item { 'item' } - It 'returns nothing' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{ P = 'P desired' } - } - $r = Invoke-ProcessPersistentItem Set Present @splat @delegates - $r | Should beNullOrEmpty - } - It 'correctly invokes functions' { - Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Add-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Remove-Item 0 -Exactly - Assert-MockCalled Invoke-ProcessPersistentItemProperty 1 { - $Mode -eq 'Set' -and - $_Keys.Key -eq 'key value' -and - $Properties.P -eq 'P desired' -and - $PropertySetter -eq 'Set-Property' -and - $PropertyTester -eq 'Test-Property' - } - } - } - Context '-Ensure Present: absent, Set - omitting properties skips setting properties' { - Mock Add-Item { 'item' } - It 'returns nothing' { - $splat = @{ Keys = @{ Key = 'key value' } } - $r = Invoke-ProcessPersistentItem Set Present @splat @coreDelegates - $r | Should beNullOrEmpty - } - It 'correctly invokes functions' { - Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Add-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Remove-Item 0 -Exactly - Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly - } - } - Context '-Ensure Present: absent, Test' { - It 'returns false' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{} - } - $r = Invoke-ProcessPersistentItem Test Present @splat @delegates - $r | Should be $false - } - It 'correctly invokes functions' { - Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Add-Item 0 -Exactly - Assert-MockCalled Remove-Item 0 -Exactly - Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly - } - } - Context '-Ensure Present: present, Set' { - Mock Get-Item { 'item' } -Verifiable - It 'returns nothing' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{} - } - $r = Invoke-ProcessPersistentItem Set Present @splat @delegates - $r | Should beNullOrEmpty - } - It 'correctly invokes functions' { - Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Add-Item 0 -Exactly - Assert-MockCalled Remove-Item 0 -Exactly - Assert-MockCalled Invoke-ProcessPersistentItemProperty 1 - } - } - Context '-Ensure Present: present, Test' { - Mock Get-Item { 'item' } -Verifiable - Mock Invoke-ProcessPersistentItemProperty { 'property test result' } -Verifiable - It 'returns result of properties test' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{} - } - $r = Invoke-ProcessPersistentItem Test Present @splat @delegates - $r | Should be 'property test result' - } - It 'correctly invokes functions' { - Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Add-Item 0 -Exactly - Assert-MockCalled Remove-Item 0 -Exactly - Assert-MockCalled Invoke-ProcessPersistentItemProperty 1 - } - } - Context '-Ensure Present: present, Test - omitting properties skips setting properties' { - Mock Get-Item { 'item' } -Verifiable - It 'returns result of properties test' { - $splat = @{ Keys = @{ Key = 'key value' } } - $r = Invoke-ProcessPersistentItem Test Present @splat @coreDelegates - $r | Should be $true - } - It 'correctly invokes functions' { - Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Add-Item 0 -Exactly - Assert-MockCalled Remove-Item 0 -Exactly - Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly - } - } - Context '-Ensure Absent: absent, Set' { - It 'returns nothing' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{} - } - $r = Invoke-ProcessPersistentItem Set Absent @splat @delegates - $r | Should beNullOrEmpty - } - It 'correctly invokes functions' { - Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Add-Item 0 -Exactly - Assert-MockCalled Remove-Item 0 -Exactly - Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly - } - } - Context '-Ensure Absent: absent, Test' { - It 'returns true' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{} - } - $r = Invoke-ProcessPersistentItem Test Absent @splat @delegates - $r | Should be $true - } - It 'correctly invokes functions' { - Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Add-Item 0 -Exactly - Assert-MockCalled Remove-Item 0 -Exactly - Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly - } - } - Context '-Ensure Absent: present, Set' { - Mock Get-Item { 'item' } -Verifiable - It 'returns nothing' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{} - } - $r = Invoke-ProcessPersistentItem Set Absent @splat @delegates - $r | Should beNullOrEmpty - } - It 'correctly invokes functions' { - Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Add-Item 0 -Exactly - Assert-MockCalled Remove-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly - } - } - Context '-Ensure Absent: present, Test' { - Mock Get-Item { 'item' } -Verifiable - It 'returns false' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{} - } - $r = Invoke-ProcessPersistentItem Test Absent @splat @delegates - $r | Should be $false - } - It 'correctly invokes functions' { - Assert-MockCalled Get-Item 1 { $Key -eq 'key value' } - Assert-MockCalled Add-Item 0 -Exactly - Assert-MockCalled Remove-Item 0 -Exactly - Assert-MockCalled Invoke-ProcessPersistentItemProperty 0 -Exactly - } - } -} - - -function Set-Property { param ($Key,$PropertyName,$Value) } -function Test-Property { param ($Key,$PropertyName,$Value) } - -Describe 'Invoke-ProcessPersistentItemProperty' { - Mock Set-Property { 'junk' } -Verifiable - Mock Test-Property { $true } -Verifiable - - $delegates = @{ - PropertySetter = 'Set-Property' - PropertyTester = 'Test-Property' - } - Context 'Set, property already correct' { - Mock Test-Property { $true } -Verifiable - It 'returns nothing' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{ P = 'correct' } - } - $r = Invoke-ProcessPersistentItemProperty Set @splat @delegates - $r | Should beNullOrEmpty - } - It 'correctly invokes functions' { - Assert-MockCalled Test-Property 1 { - $Key -eq 'key value' -and - $PropertyName -eq 'P' -and - $Value -eq 'correct' - } - Assert-MockCalled Set-Property 0 -Exactly - } - } - Context 'Test, property correct' { - Mock Test-Property { $true } -Verifiable - It 'returns true' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{ P = 'correct' } - } - $r = Invoke-ProcessPersistentItemProperty Test @splat @delegates - $r | Should be $true - } - It 'correctly invokes functions' { - Assert-MockCalled Test-Property 1 { - $Key -eq 'key value' -and - $PropertyName -eq 'P' -and - $Value -eq 'correct' - } - Assert-MockCalled Set-Property 0 -Exactly - } - } - Context 'Set, correcting property' { - Mock Test-Property { $false } -Verifiable - It 'returns nothing' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{ P = 'desired' } - } - $r = Invoke-ProcessPersistentItemProperty Set @splat @delegates - $r | Should beNullOrEmpty - } - It 'correctly invokes functions' { - Assert-MockCalled Set-Property 1 -Exactly { - $Key -eq 'key value' -and - $PropertyName -eq 'P' -and - $Value -eq 'desired' - } - Assert-MockCalled Set-Property 1 -Exactly { - $Key -eq 'key value' -and - $PropertyName -eq 'P' -and - $Value -eq 'desired' - } - } - } - Context 'Test, property incorrect' { - Mock Test-Property { $false } -Verifiable - It 'returns false' { - $splat = @{ - Keys = @{ Key = 'key value' } - Properties = @{ P = 'desired' } - } - $r = Invoke-ProcessPersistentItemProperty Test @splat @delegates - $r | Should be $false - } - It 'correctly invokes functions' { - Assert-MockCalled Test-Property 1 { - $Key -eq 'key value' -and - $PropertyName -eq 'P' -and - $Value -eq 'desired' - } - Assert-MockCalled Set-Property 0 -Exactly - } - } -} -} From 63dbe4201e42466a8a89a998fe2105e81e0b2f6a Mon Sep 17 00:00:00 2001 From: alx9r Date: Sun, 13 Aug 2017 13:55:58 -0700 Subject: [PATCH 05/11] test resource plumbing fix obvious bugs found in testing --- Functions/processShellLibrary.ps1 | 2 +- Functions/processShellLibraryFolder.ps1 | 6 +- .../shellLibraryFolderResource.Tests.ps1 | 2 +- .../shellLibraryResource.Tests.ps1 | 2 +- .../zeroDsc.shellLibrary.Tests.ps1 | 2 +- IntegrationTests/zeroDsc.shortcut.Tests.ps1 | 2 +- TestFunctions/resourcePlumbing.ps1 | 361 ++++++++++++++++++ resourcePlumbing.Tests.ps1 | 15 + 8 files changed, 385 insertions(+), 7 deletions(-) create mode 100644 TestFunctions/resourcePlumbing.ps1 create mode 100644 resourcePlumbing.Tests.ps1 diff --git a/Functions/processShellLibrary.ps1 b/Functions/processShellLibrary.ps1 index 4fd658d..965ebc1 100644 --- a/Functions/processShellLibrary.ps1 +++ b/Functions/processShellLibrary.ps1 @@ -46,7 +46,6 @@ function Invoke-ProcessShellLibrary Position = 3, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] - [ValidateScript({ $_ | Test-ValidShellLibraryName })] [Alias('LibraryName')] [string] $Name, @@ -70,6 +69,7 @@ function Invoke-ProcessShellLibrary process { # validate parameters + $Name | ? {$_} | Test-ValidShellLibraryName -ea Stop | Out-Null $IconFilePath | ? {$_} | Test-ValidFilePath -ea Stop | Out-Null $TypeName | ? {$_} | Test-ValidShellLibraryTypeName -ea Stop | Out-Null $StockIconName | ? {$_} | Test-ValidStockIconName -ea Stop | Out-Null diff --git a/Functions/processShellLibraryFolder.ps1 b/Functions/processShellLibraryFolder.ps1 index 032604f..dc31d09 100644 --- a/Functions/processShellLibraryFolder.ps1 +++ b/Functions/processShellLibraryFolder.ps1 @@ -17,7 +17,6 @@ function Invoke-ProcessShellLibraryFolder [Parameter(Mandatory = $true, Position = 3, ValueFromPipelineByPropertyName = $true)] - [ValidateScript({ $_ | Test-ValidShellLibraryName })] [Alias('Name')] [string] $LibraryName, @@ -26,12 +25,15 @@ function Invoke-ProcessShellLibraryFolder Position = 4, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] - [ValidateScript({ $_ | Test-ValidFilePath })] [string] $FolderPath ) process { + # validate parameters + $LibraryName | ? {$_} | Test-ValidShellLibraryName -ea Stop | Out-Null + $FolderPath | ? {$_} | Test-ValidFilePath -ea Stop | Out-Null + $splat = @{ Mode = $Mode Ensure = $Ensure diff --git a/IntegrationTests/shellLibraryFolderResource.Tests.ps1 b/IntegrationTests/shellLibraryFolderResource.Tests.ps1 index 0990099..f826866 100644 --- a/IntegrationTests/shellLibraryFolderResource.Tests.ps1 +++ b/IntegrationTests/shellLibraryFolderResource.Tests.ps1 @@ -1,4 +1,4 @@ -Remove-Module WindowsShell -fo -ea si; Import-Module WindowsShell +Import-Module WindowsShell -Force Import-Module PSDesiredStateConfiguration Describe 'ShellLibraryFolder Resource' { diff --git a/IntegrationTests/shellLibraryResource.Tests.ps1 b/IntegrationTests/shellLibraryResource.Tests.ps1 index 6efa106..2619cd7 100644 --- a/IntegrationTests/shellLibraryResource.Tests.ps1 +++ b/IntegrationTests/shellLibraryResource.Tests.ps1 @@ -1,4 +1,4 @@ -Remove-Module WindowsShell -fo -ea si; Import-Module WindowsShell +Import-Module WindowsShell -Force Import-Module PSDesiredStateConfiguration Describe 'ShellLibrary Resource' { diff --git a/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 b/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 index 9a6d4c8..e3f3f4d 100644 --- a/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 +++ b/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 @@ -3,7 +3,7 @@ if ( -not (Get-Module ZeroDsc -ListAvailable) ) return } -Remove-Module WindowsShell -fo -ea si; Import-Module WindowsShell +Import-Module WindowsShell -Force Import-Module PSDesiredStateConfiguration, ZeroDsc Describe 'Invoke with ZeroDsc (ShellLibrary)' { diff --git a/IntegrationTests/zeroDsc.shortcut.Tests.ps1 b/IntegrationTests/zeroDsc.shortcut.Tests.ps1 index ced1e12..5d095c6 100644 --- a/IntegrationTests/zeroDsc.shortcut.Tests.ps1 +++ b/IntegrationTests/zeroDsc.shortcut.Tests.ps1 @@ -3,7 +3,7 @@ if ( -not (Get-Module ZeroDsc -ListAvailable) ) return } -Remove-Module WindowsShell -fo -ea si; Import-Module WindowsShell +Import-Module WindowsShell -Force Import-Module PSDesiredStateConfiguration, ZeroDsc Describe 'Invoke with ZeroDsc (Shortcut)' { diff --git a/TestFunctions/resourcePlumbing.ps1 b/TestFunctions/resourcePlumbing.ps1 new file mode 100644 index 0000000..ac31168 --- /dev/null +++ b/TestFunctions/resourcePlumbing.ps1 @@ -0,0 +1,361 @@ +function Test-ResourceObject +{ + param + ( + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 1)] + $ResourceName, + + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 2)] + $ModuleName + ) + Describe "resource object $ResourceName in module $ModuleName" { + Context 'module and resource registration' { + It "$ModuleName is a module available with Get-Module" { + $r = Get-Module $ModuleName -ListAvailable + $r | Should not beNullOrEmpty + } + It "$ModuleName successfully imports" { + Import-Module $ModuleName + } + It "$ResourceName is a nested module of $ModuleName" { + $r = (Get-Module $ModuleName).NestedModules | + ? { $_.Name -eq $ResourceName } + $r | Should not beNullOrEmpty + $r.Count | Should be 1 + } + It "$ResourceName in $ModuleName is a DscResource available with Get-DscResource" { + $r = Get-DscResource $ResourceName $ModuleName + $r | Should not beNullOrEmpty + } + } + $modulePath = (Get-Module $ModuleName).NestedModules | + ? { $_.Name -eq $ResourceName } | + select -First 1 | + % Path + $h = @{} + Context 'object' { + It "import $modulePath" { + Import-Module $modulePath + } + It "module $ResourceName is a module availabe with Get-Module" { + $r = Get-Module $ResourceName + $r | Should not beNullOrEmpty + } + It "an object of type $ResourceName can be created inside $ResourceName module" { + $h.o = & (Get-Module $ResourceName).NewBoundScriptBlock( + [scriptblock]::Create("New-Object $ResourceName") + ) + $h.o | Should not beNullOrEmpty + } + It 'the object''s type has the DscResourceAttribute' { + $r = $h.o.GetType().CustomAttributes | + ? { $_.AttributeType -eq ([System.Management.Automation.DscResourceAttribute]) } + $r | Should not beNullOrEmpty + } + } + Context 'member functions' { + foreach ( $values in @( + @('Get',"$ResourceName Get()"), + @('Set',"void Set()"), + @('Test',"bool Test()") + )) + { + $methodName,$signature = $values + It "the object has a .$methodName() method" { + $r = Get-Member -InputObject $h.o -MemberType Method -Name $methodName + $r | Should not beNullOrEmpty + } + It "the method signature is `"$signature`"" { + $r = Get-Member -InputObject $h.o -MemberType Method -Name $methodName | + % Definition + $r | Should be $signature + } + } + } + Context 'member variables' { + It 'has member variables' { + $r = $h.o.GetType().GetProperties() + $r | Should not beNullOrEmpty + } + It 'has member variables with the DscProperty() attribute' { + $r = $h.o.GetType().GetProperties() | + % CustomAttributes | + ? { $_.AttributeType -eq ([System.Management.Automation.DscPropertyAttribute]) } + $r | Should not beNullOrEmpty + } + It 'has at least one DSC property that is a key' { + $r = $h.o.GetType().GetProperties() | + % CustomAttributes | + ? { $_.AttributeType -eq ([System.Management.Automation.DscPropertyAttribute]) } | + % NamedArguments | + ? { $_.MemberName -eq 'Key' } + $r | Should not beNullOrEmpty + } + } + if ( Get-Member -InputObject $h.o -MemberType Property -Name 'Ensure' ) + { + Context 'Ensure member variable' { + It 'has a default value of "Present"' { + $h.o.Ensure | Should be 'Present' + } + } + } + Context 'cleanup' { + # Removing module causing problems with mocking later. Should test with removal again + # once PowerShell GH-2505 is fixed. + It "remove module $ResourceName" { + #Remove-Module $ResourceName -ea Stop + } + It "module $ResourceName is no longer available with Get-Module" { + #$r = Get-Module $ResourceName + #$r | Should beNullOrEmpty + } + } + } +} + +function Test-ProcessFunction +{ + param + ( + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 1)] + $ModuleName, + + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 2)] + $FunctionName + ) + + Describe "function $FunctionName in module $ModuleName" { + Context 'module and function registration' { + It "$ModuleName is a module available with Get-Module" { + $r = Get-Module $ModuleName -ListAvailable + $r | Should not beNullOrEmpty + } + It "$ModuleName successfully imports" { + Import-Module $ModuleName + } + It "$ModuleName exports function $FunctionName" { + $r = (Get-Module $ModuleName).ExportedFunctions.$FunctionName + $r | Should not beNullOrEmpty + } + } + $function = (Get-Module $ModuleName).ExportedFunctions.$FunctionName + foreach ( $values in @( + @('Mode', 'design_requires', 'mandatory', $null, 1, @('Set','Test')), + @('Ensure', $null, $null, 'Present',2, @('Present','Absent')) + )) + { + $name,$designRequires,$mandatory,$defaultValue,$position,$validValues = $values + + if ( $designRequires -or + $function.Parameters.get_keys() -contains $name ) + { + Context "$name parameter" { + It "the function has a $name parameter" { + $r = $function.Parameters.$name + $r | Should not beNullOrEmpty + } + if ($mandatory) { + It 'is mandatory' { + $r = $function.Parameters.$name.Attributes | + ? { $_.TypeId.Name -eq 'ParameterAttribute' } | + % Mandatory + $r | Should be $true + } + } + if ( $defaultValue ) + { + It "has default value $defaultValue" { + $r = $function.ScriptBlock.Ast.Body.ParamBlock.Parameters. + Where({$_.Name.VariablePath.UserPath -eq $name}). + DefaultValue.SafeGetValue() + $r | Should be $defaultValue + } + } + It "is a positional argument" { + $r = $function.Parameters.$name.Attributes | + ? { $_.TypeId.Name -eq 'ParameterAttribute' } | + % Position + $r | Should BeGreaterThan -1 + } + It "takes position $position" { + $positions = $function.Parameters.Values | + % Attributes | + ? {$_.TypeId.Name -eq 'ParameterAttribute' } | + % Position | + ? { $_ -ge 0 } | + Sort + $expected = $positions[$position-1] + + $actual = $function.Parameters.$name.Attributes | + ? { $_.TypeId.Name -eq 'ParameterAttribute' } | + % Position + + $actual | Should be $expected + } + It 'has a ValidateSet attribute' { + $r = $function.Parameters.$name.Attributes | + ? {$_.TypeId.Name -eq 'ValidateSetAttribute' } + $r | Should not beNullOrEmpty + } + It "valid values are $validValues" { + $r = $function.Parameters.$name.Attributes | + ? {$_.TypeId.Name -eq 'ValidateSetAttribute' } | + % ValidValues + $r | Should be $validValues + } + } + } + } + foreach ( $parameter in ($function.Parameters.Values | ? {$_.Name -notin (Get-CommonParameterNames)}) ) + { + Context "All Parameters: $($parameter.Name)" { + It 'does not bind to values from pipeline' { + $r = $parameter.Attributes | + ? { $_.TypeId.Name -eq 'ParameterAttribute' } + $r.ValueFromPipeline | Should be $false + } + It 'binds to properties of objects in pipeline by property name' { + $r = $parameter.Attributes | + ? { $_.TypeId.Name -eq 'ParameterAttribute' } + $r.ValueFromPipelineByPropertyName | Should be $true + } + It 'does not use a validation script' { + $r = $parameter.Attributes | + ? {$_.TypeId.Name -eq 'ValidateScriptAttribute'} + $r | Should beNullOrEmpty + } + } + } + } +} + +function Test-ResourcePlumbing +{ + param + ( + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 1)] + $ResourceName, + + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 2)] + $ModuleName, + + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 3)] + $FunctionName + ) + # each of these operations should have already been tested outside this function + Import-Module $ModuleName + $modulePath = (Get-Module $ModuleName).NestedModules | + ? { $_.Name -eq $ResourceName } | + select -First 1 | + % Path + Import-Module $modulePath + $object = & (Get-Module $ResourceName).NewBoundScriptBlock( + [scriptblock]::Create("New-Object $ResourceName") + ) + $function = (Get-Module $ModuleName).ExportedFunctions.$FunctionName + + Describe "resource object $ResourceName in module $ModuleName to function $FunctionName" { + It 'the member variables names and parameter names match' { + $memberNames = Get-Member -InputObject $object -MemberType Property | + % Name | + Sort + $parameterNames = $function.Parameters.get_keys() | + ? { $_ -notin (Get-CommonParameterNames) } | + ? { $_ -ne 'Mode' } | + Sort + $memberNames | Should be $parameterNames + } + + # compose the parameters + $parameters = @{} + foreach ( $variable in $object.GetType().GetProperties() ) + { + # if it's an enum, use the zero value + if ( $variable.PropertyType.BaseType -eq [System.Enum] ) + { + $parameters.$($variable.Name) = 0 + } + # if there is a default value, use it + elseif ( $object.$($variable.Name) ) + { + $parameters.$($variable.Name) = $object.$($variable.Name) + } + else + { + switch ($variable.PropertyType ) + { + ([bool]) { $parameters.$($variable.Name) = $true } + ([string]) { $parameters.$($variable.Name) = $variable.Name.ToUpper() } + ([string[]]) { $parameters.$($variable.Name) = $variable.Name.ToUpper() } + ([int]) { + $parameters.$($variable.Name) = $variable.GetHashCode() + } + Default { + throw "Unhandled Type $($variable.GetType())" + } + } + } + } + + @{ + ResourceName = $ResourceName + ModuleName = $ModuleName + FunctionName = $FunctionName + Parameters = $parameters + } | + Export-CliXml TestDrive:/values.xml + + InModuleScope $ResourceName { + $v = Import-Clixml TestDrive:/values.xml + foreach ( $values in @( + @('Test',$true), + @('Set',$null) + )) + { + $mode,$retVal = $values + $expectedMode = $mode + Context "Invoke .$mode()" { + Mock $v.FunctionName { $retVal } -Verifiable + It 'passes return value' { + $o = New-Object $v.ResourceName -Property $v.Parameters + + $r = $o.$Mode() + + $r | Should be $retVal + } + It 'invokes command' { + $sb = [scriptblock]::Create( + (($v.Parameters.get_keys() | % { "`$$_ -eq '$($v.Parameters.$_)'" }) -join ' -and ') + ) + Assert-MockCalled $v.FunctionName 1 $sb + } + } + } + } + } + # Removing module causing problems with mocking later. Should test with removal again + # once PowerShell GH-2505 is fixed. + #Remove-Module $ResourceName +} + +function Get-CommonParameterNames +{ + [CmdletBinding()] + param() + $MyInvocation.MyCommand.Parameters.Keys +} \ No newline at end of file diff --git a/resourcePlumbing.Tests.ps1 b/resourcePlumbing.Tests.ps1 new file mode 100644 index 0000000..eeaf457 --- /dev/null +++ b/resourcePlumbing.Tests.ps1 @@ -0,0 +1,15 @@ +Import-Module WindowsShell -Force + +. "$PSScriptRoot\TestFunctions\resourcePlumbing.ps1" + +foreach ( $resourceName in 'ShellLibrary','ShellLibraryFolder' ) +{ + $params = New-Object psobject -Property @{ + ModuleName = 'WindowsShell' + ResourceName = $resourceName + FunctionName = "Invoke-Process$resourceName" + } + $params | Test-ResourceObject + $params | Test-ProcessFunction + $params | Test-ResourcePlumbing +} \ No newline at end of file From a150676e546fda407fca43613066beb8c4c46bc1 Mon Sep 17 00:00:00 2001 From: alx9r Date: Sun, 13 Aug 2017 14:21:38 -0700 Subject: [PATCH 06/11] test for extraneous static-typing in resource plumbing fix non-compliant function parameters --- Functions/processShellLibrary.ps1 | 6 ------ Functions/processShellLibraryFolder.ps1 | 3 --- IntegrationTests/shellLibraryFolderResource.Tests.ps1 | 8 ++++---- IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 | 4 ++-- ShellLibraryFolder.psm1 | 6 +++--- TestFunctions/resourcePlumbing.ps1 | 5 +++++ 6 files changed, 14 insertions(+), 18 deletions(-) diff --git a/Functions/processShellLibrary.ps1 b/Functions/processShellLibrary.ps1 index 965ebc1..3746926 100644 --- a/Functions/processShellLibrary.ps1 +++ b/Functions/processShellLibrary.ps1 @@ -44,26 +44,20 @@ function Invoke-ProcessShellLibrary [Parameter(Mandatory = $true, Position = 3, - ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('LibraryName')] - [string] $Name, [Parameter(ValueFromPipelineByPropertyName = $true)] - [string] $TypeName, [Parameter(ValueFromPipelineByPropertyName = $true)] - [string] $StockIconName, [Parameter(ValueFromPipelineByPropertyName = $true)] - [string] $IconFilePath, [Parameter(ValueFromPipelineByPropertyName = $true)] - [int] $IconResourceId=0 ) process diff --git a/Functions/processShellLibraryFolder.ps1 b/Functions/processShellLibraryFolder.ps1 index dc31d09..5634c4a 100644 --- a/Functions/processShellLibraryFolder.ps1 +++ b/Functions/processShellLibraryFolder.ps1 @@ -18,14 +18,11 @@ function Invoke-ProcessShellLibraryFolder Position = 3, ValueFromPipelineByPropertyName = $true)] [Alias('Name')] - [string] $LibraryName, [Parameter(Mandatory = $true, Position = 4, - ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] - [string] $FolderPath ) process diff --git a/IntegrationTests/shellLibraryFolderResource.Tests.ps1 b/IntegrationTests/shellLibraryFolderResource.Tests.ps1 index f826866..4b653b8 100644 --- a/IntegrationTests/shellLibraryFolderResource.Tests.ps1 +++ b/IntegrationTests/shellLibraryFolderResource.Tests.ps1 @@ -27,8 +27,8 @@ Describe 'ShellLibraryFolder Resource' { %{ $_ | Should be $true } } It 'create the library' { - $libraryName | Invoke-ProcessShellLibrary Set - $r = $libraryName | Invoke-ProcessShellLibrary Test + Invoke-ProcessShellLibrary Set -Name $libraryName + $r = Invoke-ProcessShellLibrary Test -Name $libraryName $r | Should be $true } } @@ -86,8 +86,8 @@ Describe 'ShellLibraryFolder Resource' { Test-Path $folderPath1 | Should be $false } It 'remove the library' { - $libraryName | Invoke-ProcessShellLibrary Set Absent - $r = $libraryName | Invoke-ProcessShellLibrary Test Absent + Invoke-ProcessShellLibrary Set Absent -Name $libraryName + $r = Invoke-ProcessShellLibrary Test Absent -Name $libraryName $r | Should be $true } } diff --git a/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 b/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 index e3f3f4d..7677d96 100644 --- a/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 +++ b/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 @@ -85,8 +85,8 @@ Describe 'Invoke with ZeroDsc (ShellLibrary)' { Test-Path $folderPath | Should be $false } It 'remove the libraries' { - $libraryName1,$libraryName2 | Invoke-ProcessShellLibrary Set Absent - ( $libraryName1,$libraryName2 | Invoke-ProcessShellLibrary Test Absent ) -ne + $libraryName1,$libraryName2 | % { Invoke-ProcessShellLibrary Set Absent -Name $_ } + ( $libraryName1,$libraryName2 | % { Invoke-ProcessShellLibrary Test Absent -Name $_ } ) -ne $true | Should beNullOrEmpty } diff --git a/ShellLibraryFolder.psm1 b/ShellLibraryFolder.psm1 index 2dcc436..5585886 100644 --- a/ShellLibraryFolder.psm1 +++ b/ShellLibraryFolder.psm1 @@ -21,13 +21,13 @@ class ShellLibraryFolder [void] Set() { - $this.FolderPath | - Invoke-ProcessShellLibraryFolder Set $this.Ensure $this.LibraryName + $this.FolderPath | + % { $this | Invoke-ProcessShellLibraryFolder Set -FolderPath $_ } } [bool] Test() { $numSucceeded = $this.FolderPath | - Invoke-ProcessShellLibraryFolder Test $this.Ensure $this.LibraryName | + % { $this | Invoke-ProcessShellLibraryFolder Test -FolderPath $_ } | ? { $_ -eq $true } | Measure-Object | % Count diff --git a/TestFunctions/resourcePlumbing.ps1 b/TestFunctions/resourcePlumbing.ps1 index ac31168..1603f1a 100644 --- a/TestFunctions/resourcePlumbing.ps1 +++ b/TestFunctions/resourcePlumbing.ps1 @@ -233,6 +233,11 @@ function Test-ProcessFunction ? {$_.TypeId.Name -eq 'ValidateScriptAttribute'} $r | Should beNullOrEmpty } + It 'does not have a type' { + $r = $function.ScriptBlock.Ast.Body.ParamBlock.Parameters. + Where({$_.Name.VariablePath.UserPath -eq $parameter.Name}).StaticType + $r | Should be ([Object]) + } } } } From 51cf5825cfc9f33583da789ef53ce0618a81628b Mon Sep 17 00:00:00 2001 From: alx9r Date: Mon, 14 Aug 2017 07:17:07 -0700 Subject: [PATCH 07/11] use new ZeroDsc local variable capability --- .../zeroDsc.shellLibrary.Tests.ps1 | 48 ++++++++----------- IntegrationTests/zeroDsc.shortcut.Tests.ps1 | 14 +++--- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 b/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 index 7677d96..a7d6acc 100644 --- a/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 +++ b/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 @@ -18,14 +18,12 @@ Describe 'Invoke with ZeroDsc (ShellLibrary)' { } } Context ShellLibrary { - $document = [scriptblock]::Create(@" - Get-DscResource ShellLibrary WindowsShell | Import-DscResource - ShellLibrary MyLib @{ Name = '$libraryName1' } -"@ - ) $h = @{} It 'create instructions' { - $h.i = ConfigInstructions SomeName $document + $h.i = ConfigInstructions SomeName { + Get-DscResource ShellLibrary WindowsShell | Import-DscResource + ShellLibrary MyLib @{ Name = $libraryName1 } + } } foreach ( $step in $h.i ) { @@ -36,17 +34,15 @@ Describe 'Invoke with ZeroDsc (ShellLibrary)' { } } Context ShellLibraryFolder { - $document = [scriptblock]::Create(@" - Get-DscResource ShellLibraryFolder WindowsShell | Import-DscResource - ShellLibraryFolder MyDir @{ - LibraryName = '$libraryName1' - FolderPath = '$folderPath' - } -"@ - ) $h = @{} It 'create instructions' { - $h.i = ConfigInstructions SomeName $document + $h.i = ConfigInstructions SomeName { + Get-DscResource ShellLibraryFolder WindowsShell | Import-DscResource + ShellLibraryFolder MyDir @{ + LibraryName = $libraryName1 + FolderPath = $folderPath + } + } } foreach ( $step in $h.i ) { @@ -57,19 +53,17 @@ Describe 'Invoke with ZeroDsc (ShellLibrary)' { } } Context 'combined' { - $document = [scriptblock]::Create(@" - Get-DscResource -Module WindowsShell | Import-DscResource - ShellLibraryFolder MyDir @{ - LibraryName = '$libraryName2' - FolderPath = '$folderPath' - DependsOn = '[ShellLibrary]MyLib' - } - ShellLibrary MyLib @{ Name = '$libraryName2' } -"@ - ) - $h = @{} + $h = @{} It 'create instructions' { - $h.i = ConfigInstructions SomeName $document + $h.i = ConfigInstructions SomeName { + Get-DscResource -Module WindowsShell | Import-DscResource + ShellLibraryFolder MyDir @{ + LibraryName = $libraryName2 + FolderPath = $folderPath + DependsOn = '[ShellLibrary]MyLib' + } + ShellLibrary MyLib @{ Name = $libraryName2 } + } } foreach ( $step in $h.i ) { diff --git a/IntegrationTests/zeroDsc.shortcut.Tests.ps1 b/IntegrationTests/zeroDsc.shortcut.Tests.ps1 index 5d095c6..8acc0c5 100644 --- a/IntegrationTests/zeroDsc.shortcut.Tests.ps1 +++ b/IntegrationTests/zeroDsc.shortcut.Tests.ps1 @@ -13,14 +13,14 @@ Describe 'Invoke with ZeroDsc (Shortcut)' { $shortcutPath = Join-Path $tempPath $shortcutFilename $tests = [ordered]@{ - basic = @" + basic = { Get-DscResource Shortcut WindowsShell | Import-DscResource - Shortcut MyShortcut @{ Path = "$shortcutPath" } -"@ - full = @" + Shortcut MyShortcut @{ Path = $shortcutPath } + } + full = { Get-DscResource Shortcut WindowsShell | Import-DscResource Shortcut MyShortcut @{ - Path = "$shortcutPath" + Path = $shortcutPath Arguments = 'arguments' Hotkey = 'Ctrl+Alt+f' StockIconName = 'AudioFiles' @@ -29,12 +29,12 @@ Describe 'Invoke with ZeroDsc (Shortcut)' { WorkingDirectory = 'c:\temp' Description = 'some description' } -"@ + } } foreach ( $testName in $tests.Keys ) { Context $testName { - $document = [scriptblock]::Create($tests.$testName) + $document = $tests.$testName $h = @{} It 'create instructions' { $h.i = ConfigInstructions SomeName $document From 97bfc309c9b629b8f8374086391a8f31d69f9e88 Mon Sep 17 00:00:00 2001 From: alx9r Date: Tue, 15 Aug 2017 10:13:43 -0700 Subject: [PATCH 08/11] use .Net enums instead of PowerShell enums --- Functions/loadTypes.ps1 | 1 - Functions/processShellLibrary.ps1 | 4 +- Functions/processShellLibraryFolder.ps1 | 4 +- Functions/processShortcut.Tests.ps1 | 4 +- Functions/processShortcut.ps1 | 6 +-- .../zeroDsc.shellLibrary.Tests.ps1 | 3 +- ShellLibrary.psm1 | 34 -------------- ShellLibraryFolder.psm1 | 6 --- Shortcut.psm1 | 30 ------------- TestFunctions/resourcePlumbing.ps1 | 44 ++++++++++--------- WindowsShell.psd1 | 11 ++++- dotNetTypes/ensure.ps1 | 1 + dotNetTypes/libraryTypeName.ps1 | 12 +++++ dotNetTypes/mode.ps1 | 1 + dotNetTypes/stockIconName.ps1 | 18 ++++++++ .../windowStyle.ps1 | 9 ++-- import.Tests.ps1 | 35 +++++++++++++++ resourcePlumbing.Tests.ps1 | 2 +- 18 files changed, 117 insertions(+), 108 deletions(-) create mode 100644 dotNetTypes/ensure.ps1 create mode 100644 dotNetTypes/libraryTypeName.ps1 create mode 100644 dotNetTypes/mode.ps1 create mode 100644 dotNetTypes/stockIconName.ps1 rename Functions/shortcutType.ps1 => dotNetTypes/windowStyle.ps1 (86%) create mode 100644 import.Tests.ps1 diff --git a/Functions/loadTypes.ps1 b/Functions/loadTypes.ps1 index 462ca1d..4e827a5 100644 --- a/Functions/loadTypes.ps1 +++ b/Functions/loadTypes.ps1 @@ -3,6 +3,5 @@ 'shellLibraryType.ps1' 'shellLibraryFolderType.ps1' 'stockIconInfoType.ps1' - 'shortcutType.ps1' ) | % { . "$($PSCommandPath | Split-Path -Parent)\$_" } diff --git a/Functions/processShellLibrary.ps1 b/Functions/processShellLibrary.ps1 index 3746926..f1cf104 100644 --- a/Functions/processShellLibrary.ps1 +++ b/Functions/processShellLibrary.ps1 @@ -34,12 +34,12 @@ function Invoke-ProcessShellLibrary [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)] - [ValidateSet('Set','Test')] + [Mode] $Mode, [Parameter(Position = 2, ValueFromPipelineByPropertyName = $true)] - [ValidateSet('Present','Absent')] + [Ensure] $Ensure = 'Present', [Parameter(Mandatory = $true, diff --git a/Functions/processShellLibraryFolder.ps1 b/Functions/processShellLibraryFolder.ps1 index 5634c4a..309cebd 100644 --- a/Functions/processShellLibraryFolder.ps1 +++ b/Functions/processShellLibraryFolder.ps1 @@ -6,12 +6,12 @@ function Invoke-ProcessShellLibraryFolder [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)] - [ValidateSet('Set','Test')] + [Mode] $Mode, [Parameter(Position = 2, ValueFromPipelineByPropertyName = $true)] - [ValidateSet('Present','Absent')] + [Ensure] $Ensure = 'Present', [Parameter(Mandatory = $true, diff --git a/Functions/processShortcut.Tests.ps1 b/Functions/processShortcut.Tests.ps1 index a80427c..93bad3d 100644 --- a/Functions/processShortcut.Tests.ps1 +++ b/Functions/processShortcut.Tests.ps1 @@ -18,7 +18,7 @@ Describe 'Invoke-ProcessShortcut' { WorkingDirectory = 'working directory' WindowStyle = 'Normal' Hotkey = 'hotkey' - StockIconName = 'stock icon name' + StockIconName = 'Folder' IconFilePath = 'icon file path' IconResourceId = 1 Description = 'description' @@ -29,7 +29,7 @@ Describe 'Invoke-ProcessShortcut' { } It 'correctly invokes functions' { Assert-MockCalled Get-IconReferencePath 1 { - $StockIconName -eq 'stock icon name' -and + $StockIconName -eq 'Folder' -and $IconFilePath -eq 'icon file path' -and $IconResourceId -eq 1 } diff --git a/Functions/processShortcut.ps1 b/Functions/processShortcut.ps1 index e34fc7e..3f68d72 100644 --- a/Functions/processShortcut.ps1 +++ b/Functions/processShortcut.ps1 @@ -6,12 +6,12 @@ function Invoke-ProcessShortcut [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)] - [ValidateSet('Set','Test')] + [Mode] $Mode, [Parameter(Position = 2, ValueFromPipelineByPropertyName = $true)] - [ValidateSet('Present','Absent')] + [Ensure] $Ensure = 'Present', [Parameter(Mandatory = $true, @@ -46,7 +46,7 @@ function Invoke-ProcessShortcut $Hotkey, [Parameter(ValueFromPipelineByPropertyName = $true)] - [string] + [StockIconName] $StockIconName, [Parameter(ValueFromPipelineByPropertyName = $true)] diff --git a/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 b/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 index a7d6acc..f935bfb 100644 --- a/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 +++ b/IntegrationTests/zeroDsc.shellLibrary.Tests.ps1 @@ -21,7 +21,8 @@ Describe 'Invoke with ZeroDsc (ShellLibrary)' { $h = @{} It 'create instructions' { $h.i = ConfigInstructions SomeName { - Get-DscResource ShellLibrary WindowsShell | Import-DscResource + $r = Get-DscResource ShellLibrary WindowsShell + $r | Import-DscResource ShellLibrary MyLib @{ Name = $libraryName1 } } } diff --git a/ShellLibrary.psm1 b/ShellLibrary.psm1 index 18d5cf6..315c30e 100644 --- a/ShellLibrary.psm1 +++ b/ShellLibrary.psm1 @@ -1,37 +1,3 @@ -enum Ensure -{ - Present - Absent -} - -enum LibraryTypeName -{ - Generic - Documents - Music - Pictures - Videos - - DoNotSet -} - -enum StockIconName -{ - DocumentNotAssociated; DocumentAssociated; Application; Folder; FolderOpen; Drive525; Drive35; DriveRemove; - DriveFixed; DriveNetwork; DriveNetworkDisabled; DriveCD; DriveRam; World; Server; Printer; MyNetwork; Find; Help; - Share; Link; SlowFile; Recycler; RecyclerFull; MediaCDAudio; Lock; AutoList; PrinterNet; ServerShare; PrinterFax; - PrinterFaxNet; PrinterFile; Stack; MediaSvcd; StuffedFolder; DriveUnknown; DriveDvd; MediaDvd; MediaDvdRam; - MediaDvdRW; MediaDvdR; MediaDvdRom; MediaCDAudioPlus; MediaCDRW; MediaCDR; MediaCDBurn; MediaBlankCD; MediaCDRom; - AudioFiles; ImageFiles; VideoFiles; MixedFiles; FolderBack; FolderFront; Shield; Warning; Info; Error; Key; - Software; Rename; Delete; MediaAudioDvd; MediaMovieDvd; MediaEnhancedCD; MediaEnhancedDvd; MediaHDDvd; - MediaBluRay; MediaVcd; MediaDvdPlusR; MediaDvdPlusRW; DesktopPC; MobilePC; Users; MediaSmartMedia; - MediaCompactFlash; DeviceCellPhone; DeviceCamera; DeviceVideoCamera; DeviceAudioPlayer; NetworkConnect; Internet; - ZipFile; Settings; DriveHDDVD; DriveBluRay; MediaHDDVDROM; MediaHDDVDR; MediaHDDVDRAM; MediaBluRayROM; - MediaBluRayR; MediaBluRayRE; ClusteredDisk; - - DoNotSet -} - [DscResource()] class ShellLibrary { diff --git a/ShellLibraryFolder.psm1 b/ShellLibraryFolder.psm1 index 5585886..7765808 100644 --- a/ShellLibraryFolder.psm1 +++ b/ShellLibraryFolder.psm1 @@ -1,9 +1,3 @@ -enum Ensure -{ - Present - Absent -} - [DscResource()] class ShellLibraryFolder { diff --git a/Shortcut.psm1 b/Shortcut.psm1 index 2dda092..ffb69c5 100644 --- a/Shortcut.psm1 +++ b/Shortcut.psm1 @@ -1,33 +1,3 @@ -enum Ensure -{ - Present - Absent -} - -enum WindowStyle -{ - Normal = 1 - Maximized = 3 - Minimized = 7 -} - -enum StockIconName -{ - DocumentNotAssociated; DocumentAssociated; Application; Folder; FolderOpen; Drive525; Drive35; DriveRemove; - DriveFixed; DriveNetwork; DriveNetworkDisabled; DriveCD; DriveRam; World; Server; Printer; MyNetwork; Find; Help; - Share; Link; SlowFile; Recycler; RecyclerFull; MediaCDAudio; Lock; AutoList; PrinterNet; ServerShare; PrinterFax; - PrinterFaxNet; PrinterFile; Stack; MediaSvcd; StuffedFolder; DriveUnknown; DriveDvd; MediaDvd; MediaDvdRam; - MediaDvdRW; MediaDvdR; MediaDvdRom; MediaCDAudioPlus; MediaCDRW; MediaCDR; MediaCDBurn; MediaBlankCD; MediaCDRom; - AudioFiles; ImageFiles; VideoFiles; MixedFiles; FolderBack; FolderFront; Shield; Warning; Info; Error; Key; - Software; Rename; Delete; MediaAudioDvd; MediaMovieDvd; MediaEnhancedCD; MediaEnhancedDvd; MediaHDDvd; - MediaBluRay; MediaVcd; MediaDvdPlusR; MediaDvdPlusRW; DesktopPC; MobilePC; Users; MediaSmartMedia; - MediaCompactFlash; DeviceCellPhone; DeviceCamera; DeviceVideoCamera; DeviceAudioPlayer; NetworkConnect; Internet; - ZipFile; Settings; DriveHDDVD; DriveBluRay; MediaHDDVDROM; MediaHDDVDR; MediaHDDVDRAM; MediaBluRayROM; - MediaBluRayR; MediaBluRayRE; ClusteredDisk; - - DoNotSet -} - [DscResource()] class Shortcut { diff --git a/TestFunctions/resourcePlumbing.ps1 b/TestFunctions/resourcePlumbing.ps1 index 1603f1a..a17631a 100644 --- a/TestFunctions/resourcePlumbing.ps1 +++ b/TestFunctions/resourcePlumbing.ps1 @@ -149,11 +149,11 @@ function Test-ProcessFunction } $function = (Get-Module $ModuleName).ExportedFunctions.$FunctionName foreach ( $values in @( - @('Mode', 'design_requires', 'mandatory', $null, 1, @('Set','Test')), - @('Ensure', $null, $null, 'Present',2, @('Present','Absent')) + @('Mode', 'design_requires', 'mandatory', $null, 1, 'Mode'), + @('Ensure', $null, $null, 'Present',2, 'Ensure') )) { - $name,$designRequires,$mandatory,$defaultValue,$position,$validValues = $values + $name,$designRequires,$mandatory,$defaultValue,$position,$typeName = $values if ( $designRequires -or $function.Parameters.get_keys() -contains $name ) @@ -201,16 +201,9 @@ function Test-ProcessFunction $actual | Should be $expected } - It 'has a ValidateSet attribute' { - $r = $function.Parameters.$name.Attributes | - ? {$_.TypeId.Name -eq 'ValidateSetAttribute' } - $r | Should not beNullOrEmpty - } - It "valid values are $validValues" { - $r = $function.Parameters.$name.Attributes | - ? {$_.TypeId.Name -eq 'ValidateSetAttribute' } | - % ValidValues - $r | Should be $validValues + It "is of type $typeName" { + $r = $function.Parameters.$name.ParameterType + $r | Should be $typeName } } } @@ -233,11 +226,6 @@ function Test-ProcessFunction ? {$_.TypeId.Name -eq 'ValidateScriptAttribute'} $r | Should beNullOrEmpty } - It 'does not have a type' { - $r = $function.ScriptBlock.Ast.Body.ParamBlock.Parameters. - Where({$_.Name.VariablePath.UserPath -eq $parameter.Name}).StaticType - $r | Should be ([Object]) - } } } } @@ -275,7 +263,7 @@ function Test-ResourcePlumbing $function = (Get-Module $ModuleName).ExportedFunctions.$FunctionName Describe "resource object $ResourceName in module $ModuleName to function $FunctionName" { - It 'the member variables names and parameter names match' { + It 'the member variable and parameter names match' { $memberNames = Get-Member -InputObject $object -MemberType Property | % Name | Sort @@ -285,15 +273,29 @@ function Test-ResourcePlumbing Sort $memberNames | Should be $parameterNames } + Context 'the member and parameter types match' { + $members = $object.GetType().GetMembers() | + ? {$_.MemberType -eq 'Property' } + foreach ( $member in $members ) + { + $name = $member.Name + It $name { + $memberType = $member.PropertyType + $parameterType = $function.Parameters.$name.ParameterType + + $memberType | Should be $parameterType + } + } + } # compose the parameters $parameters = @{} foreach ( $variable in $object.GetType().GetProperties() ) { - # if it's an enum, use the zero value + # if it's an enum, use the first value if ( $variable.PropertyType.BaseType -eq [System.Enum] ) { - $parameters.$($variable.Name) = 0 + $parameters.$($variable.Name) = [System.Enum]::GetValues($variable.PropertyType)[0] } # if there is a default value, use it elseif ( $object.$($variable.Name) ) diff --git a/WindowsShell.psd1 b/WindowsShell.psd1 index ec2ae07..cfc27a5 100644 --- a/WindowsShell.psd1 +++ b/WindowsShell.psd1 @@ -2,7 +2,14 @@ # Script module or binary module file associated with this manifest. RootModule = 'WindowsShell.psm1' -NestedModules = 'ShellLibrary.psm1','ShellLibraryFolder.psm1','Shortcut.psm1' +NestedModules = 'Shortcut.psm1','ShellLibrary.psm1','ShellLibraryFolder.psm1' +ScriptsToProcess = @( + '.\dotNetTypes\ensure.ps1' + '.\dotNetTypes\mode.ps1' + '.\dotNetTypes\libraryTypeName.ps1' + '.\dotNetTypes\stockIconName.ps1' + '.\dotNetTypes\windowStyle.ps1' +) DscResourcesToExport = '*' @@ -26,4 +33,4 @@ PowerShellVersion = '5.0' # Name of the Windows PowerShell host required by this module # PowerShellHostName = '' -} \ No newline at end of file +} diff --git a/dotNetTypes/ensure.ps1 b/dotNetTypes/ensure.ps1 new file mode 100644 index 0000000..4dc93d1 --- /dev/null +++ b/dotNetTypes/ensure.ps1 @@ -0,0 +1 @@ +Add-Type "public enum Ensure { Present, Absent }" \ No newline at end of file diff --git a/dotNetTypes/libraryTypeName.ps1 b/dotNetTypes/libraryTypeName.ps1 new file mode 100644 index 0000000..f41a8b4 --- /dev/null +++ b/dotNetTypes/libraryTypeName.ps1 @@ -0,0 +1,12 @@ +Add-Type @' +public enum LibraryTypeName +{ + Generic, + Documents, + Music, + Pictures, + Videos, + + DoNotSet +} +'@ \ No newline at end of file diff --git a/dotNetTypes/mode.ps1 b/dotNetTypes/mode.ps1 new file mode 100644 index 0000000..711f7da --- /dev/null +++ b/dotNetTypes/mode.ps1 @@ -0,0 +1 @@ +Add-Type "public enum Mode { Set, Test }" \ No newline at end of file diff --git a/dotNetTypes/stockIconName.ps1 b/dotNetTypes/stockIconName.ps1 new file mode 100644 index 0000000..c101ede --- /dev/null +++ b/dotNetTypes/stockIconName.ps1 @@ -0,0 +1,18 @@ +Add-Type @' +public enum StockIconName +{ + DocumentNotAssociated, DocumentAssociated, Application, Folder, FolderOpen, Drive525, Drive35, DriveRemove, + DriveFixed, DriveNetwork, DriveNetworkDisabled, DriveCD, DriveRam, World, Server, Printer, MyNetwork, Find, Help, + Share, Link, SlowFile, Recycler, RecyclerFull, MediaCDAudio, Lock, AutoList, PrinterNet, ServerShare, PrinterFax, + PrinterFaxNet, PrinterFile, Stack, MediaSvcd, StuffedFolder, DriveUnknown, DriveDvd, MediaDvd, MediaDvdRam, + MediaDvdRW, MediaDvdR, MediaDvdRom, MediaCDAudioPlus, MediaCDRW, MediaCDR, MediaCDBurn, MediaBlankCD, MediaCDRom, + AudioFiles, ImageFiles, VideoFiles, MixedFiles, FolderBack, FolderFront, Shield, Warning, Info, Error, Key, + Software, Rename, Delete, MediaAudioDvd, MediaMovieDvd, MediaEnhancedCD, MediaEnhancedDvd, MediaHDDvd, + MediaBluRay, MediaVcd, MediaDvdPlusR, MediaDvdPlusRW, DesktopPC, MobilePC, Users, MediaSmartMedia, + MediaCompactFlash, DeviceCellPhone, DeviceCamera, DeviceVideoCamera, DeviceAudioPlayer, NetworkConnect, Internet, + ZipFile, Settings, DriveHDDVD, DriveBluRay, MediaHDDVDROM, MediaHDDVDR, MediaHDDVDRAM, MediaBluRayROM, + MediaBluRayR, MediaBluRayRE, ClusteredDisk, + + DoNotSet +} +'@ \ No newline at end of file diff --git a/Functions/shortcutType.ps1 b/dotNetTypes/windowStyle.ps1 similarity index 86% rename from Functions/shortcutType.ps1 rename to dotNetTypes/windowStyle.ps1 index 7ddca21..9b07a96 100644 --- a/Functions/shortcutType.ps1 +++ b/dotNetTypes/windowStyle.ps1 @@ -10,9 +10,12 @@ intWindowStyle Description 3 Activates the window and displays it as a maximized window. 7 Minimizes the window and activates the next top-level window. #> -enum WindowStyle + +Add-Type @' +public enum WindowStyle { - Normal = 1 - Maximized = 3 + Normal = 1, + Maximized = 3, Minimized = 7 } +'@ \ No newline at end of file diff --git a/import.Tests.ps1 b/import.Tests.ps1 new file mode 100644 index 0000000..d28d249 --- /dev/null +++ b/import.Tests.ps1 @@ -0,0 +1,35 @@ +Describe 'import WindowsShell module' { + It 'import' { + Import-Module WindowsShell + } + It 'get' { + $r = Get-Module WindowsShell + $r | Should not beNullOrEmpty + } + It 'an instance from this folder was loaded' { + $r = (Get-Module WindowsShell).ModuleBase + $PSScriptRoot | Should be $r + } +} +Describe 'loading DSC Resources' { + foreach ( $name in 'Shortcut','ShellLibrary','ShellLibraryFolder' ) + { + Context $name { + It 'get' { + $r = Get-DscResource $name WindowsShell + $r | Should not beNullOrEmpty + } + It 'an instance from this folder was loaded' { + $r = Get-DscResource $name WindowsShell | + ? { $PSScriptRoot -eq $_.Module.ModuleBase } + $r | Should be $true + } + It 'the instance with the latest version is from this folder' { + $r = Get-DscResource $name WindowsShell | + Sort Version | + Select -Last 1 + $r.Module.ModuleBase | Should be $PSScriptRoot + } + } + } +} \ No newline at end of file diff --git a/resourcePlumbing.Tests.ps1 b/resourcePlumbing.Tests.ps1 index eeaf457..170f372 100644 --- a/resourcePlumbing.Tests.ps1 +++ b/resourcePlumbing.Tests.ps1 @@ -2,7 +2,7 @@ . "$PSScriptRoot\TestFunctions\resourcePlumbing.ps1" -foreach ( $resourceName in 'ShellLibrary','ShellLibraryFolder' ) +foreach ( $resourceName in 'Shortcut')#'ShellLibrary','ShellLibraryFolder','Shortcut' ) { $params = New-Object psobject -Property @{ ModuleName = 'WindowsShell' From 1e443cbb297619dad86770523fd7d617ebd6fab1 Mon Sep 17 00:00:00 2001 From: alx9r Date: Tue, 15 Aug 2017 11:00:22 -0700 Subject: [PATCH 09/11] use $null instead of 'DoNotSet' --- Functions/processShellLibrary.Tests.ps1 | 1 - Functions/processShellLibrary.ps1 | 8 ++++++-- Functions/processShellLibraryFolder.ps1 | 2 ++ Functions/processShortcut.ps1 | 8 ++++---- ShellLibrary.psm1 | 8 ++++---- Shortcut.psm1 | 8 ++++---- TestFunctions/resourcePlumbing.ps1 | 6 ++++++ dotNetTypes/libraryTypeName.ps1 | 4 +--- dotNetTypes/stockIconName.ps1 | 4 +--- resourcePlumbing.Tests.ps1 | 2 +- 10 files changed, 29 insertions(+), 22 deletions(-) diff --git a/Functions/processShellLibrary.Tests.ps1 b/Functions/processShellLibrary.Tests.ps1 index 3778f94..b820d3d 100644 --- a/Functions/processShellLibrary.Tests.ps1 +++ b/Functions/processShellLibrary.Tests.ps1 @@ -63,7 +63,6 @@ Describe 'Invoke-ProcessShellLibrary' { Mode = 'Set' Ensure = 'Present' Name = 'name' - TypeName = 'DoNotSet' } $r = $params | Invoke-ProcessShellLibrary $r.Count | Should be 1 diff --git a/Functions/processShellLibrary.ps1 b/Functions/processShellLibrary.ps1 index f1cf104..973866d 100644 --- a/Functions/processShellLibrary.ps1 +++ b/Functions/processShellLibrary.ps1 @@ -45,19 +45,24 @@ function Invoke-ProcessShellLibrary [Parameter(Mandatory = $true, Position = 3, ValueFromPipelineByPropertyName = $true)] + [string] [Alias('LibraryName')] $Name, [Parameter(ValueFromPipelineByPropertyName = $true)] + [System.Nullable[LibraryTypeName]] $TypeName, [Parameter(ValueFromPipelineByPropertyName = $true)] + [System.Nullable[StockIconName]] $StockIconName, [Parameter(ValueFromPipelineByPropertyName = $true)] + [string] $IconFilePath, [Parameter(ValueFromPipelineByPropertyName = $true)] + [int] $IconResourceId=0 ) process @@ -71,8 +76,7 @@ function Invoke-ProcessShellLibrary # pass through properties $properties = @{} 'TypeName' | - ? { $_ -in $PSCmdlet.MyInvocation.BoundParameters.Keys } | - ? { (Get-Variable $_ -ValueOnly) -ne 'DoNotSet' } | + ? { $null -ne (Get-Variable $_ -ValueOnly) } | % { $properties.$_ = Get-Variable $_ -ValueOnly } diff --git a/Functions/processShellLibraryFolder.ps1 b/Functions/processShellLibraryFolder.ps1 index 309cebd..e80135b 100644 --- a/Functions/processShellLibraryFolder.ps1 +++ b/Functions/processShellLibraryFolder.ps1 @@ -17,12 +17,14 @@ function Invoke-ProcessShellLibraryFolder [Parameter(Mandatory = $true, Position = 3, ValueFromPipelineByPropertyName = $true)] + [string] [Alias('Name')] $LibraryName, [Parameter(Mandatory = $true, Position = 4, ValueFromPipelineByPropertyName = $true)] + [string[]] $FolderPath ) process diff --git a/Functions/processShortcut.ps1 b/Functions/processShortcut.ps1 index 3f68d72..eb578a7 100644 --- a/Functions/processShortcut.ps1 +++ b/Functions/processShortcut.ps1 @@ -37,7 +37,7 @@ function Invoke-ProcessShortcut [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('Run')] - [WindowStyle] + [System.Nullable[WindowStyle]] $WindowStyle, [Parameter(ValueFromPipelineByPropertyName = $true)] @@ -46,7 +46,7 @@ function Invoke-ProcessShortcut $Hotkey, [Parameter(ValueFromPipelineByPropertyName = $true)] - [StockIconName] + [System.Nullable[StockIconName]] $StockIconName, [Parameter(ValueFromPipelineByPropertyName = $true)] @@ -55,7 +55,7 @@ function Invoke-ProcessShortcut [Parameter(ValueFromPipelineByPropertyName = $true)] [int] - $IconResourceId=0, + $IconResourceId, [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('Comment')] @@ -68,7 +68,7 @@ function Invoke-ProcessShortcut $properties = @{} 'TargetPath','Arguments','WorkingDirectory', 'WindowStyle','Hotkey','Description' | - ? { $_ -in $PSCmdlet.MyInvocation.BoundParameters.Keys } | + ? { Get-Variable $_ -ValueOnly } | % { $properties.$_ = Get-Variable $_ -ValueOnly } diff --git a/ShellLibrary.psm1 b/ShellLibrary.psm1 index 315c30e..ac17ae0 100644 --- a/ShellLibrary.psm1 +++ b/ShellLibrary.psm1 @@ -10,12 +10,12 @@ class ShellLibrary $Ensure [DscProperty()] - [LibraryTypeName] - $TypeName = [LibraryTypeName]::DoNotSet + [System.Nullable[LibraryTypeName]] + $TypeName [DscProperty()] - [StockIconName] - $StockIconName = [StockIconName]::DoNotSet + [System.Nullable[StockIconName]] + $StockIconName [DscProperty()] [string] diff --git a/Shortcut.psm1 b/Shortcut.psm1 index ffb69c5..114a02c 100644 --- a/Shortcut.psm1 +++ b/Shortcut.psm1 @@ -22,16 +22,16 @@ class Shortcut $WorkingDirectory [DscProperty()] - [WindowStyle] - $WindowStyle=[WindowStyle]::Normal + [System.Nullable[WindowStyle]] + $WindowStyle [DscProperty()] [string] $Hotkey [DscProperty()] - [StockIconName] - $StockIconName = [StockIconName]::DoNotSet + [System.Nullable[StockIconName]] + $StockIconName [DscProperty()] [string] diff --git a/TestFunctions/resourcePlumbing.ps1 b/TestFunctions/resourcePlumbing.ps1 index a17631a..6dd87b9 100644 --- a/TestFunctions/resourcePlumbing.ps1 +++ b/TestFunctions/resourcePlumbing.ps1 @@ -297,6 +297,12 @@ function Test-ResourcePlumbing { $parameters.$($variable.Name) = [System.Enum]::GetValues($variable.PropertyType)[0] } + # if it's a nullable enum, use the first value + elseif ( $variable.PropertyType.Name -eq 'Nullable`1' -and + $variable.PropertyType.GenericTypeArguments.BaseType -eq [System.Enum] ) + { + $parameters.$($variable.Name) = [System.Enum]::GetValues($variable.PropertyType.GenericTypeArguments[0])[0] + } # if there is a default value, use it elseif ( $object.$($variable.Name) ) { diff --git a/dotNetTypes/libraryTypeName.ps1 b/dotNetTypes/libraryTypeName.ps1 index f41a8b4..916abea 100644 --- a/dotNetTypes/libraryTypeName.ps1 +++ b/dotNetTypes/libraryTypeName.ps1 @@ -5,8 +5,6 @@ public enum LibraryTypeName Documents, Music, Pictures, - Videos, - - DoNotSet + Videos } '@ \ No newline at end of file diff --git a/dotNetTypes/stockIconName.ps1 b/dotNetTypes/stockIconName.ps1 index c101ede..10a7673 100644 --- a/dotNetTypes/stockIconName.ps1 +++ b/dotNetTypes/stockIconName.ps1 @@ -11,8 +11,6 @@ public enum StockIconName MediaBluRay, MediaVcd, MediaDvdPlusR, MediaDvdPlusRW, DesktopPC, MobilePC, Users, MediaSmartMedia, MediaCompactFlash, DeviceCellPhone, DeviceCamera, DeviceVideoCamera, DeviceAudioPlayer, NetworkConnect, Internet, ZipFile, Settings, DriveHDDVD, DriveBluRay, MediaHDDVDROM, MediaHDDVDR, MediaHDDVDRAM, MediaBluRayROM, - MediaBluRayR, MediaBluRayRE, ClusteredDisk, - - DoNotSet + MediaBluRayR, MediaBluRayRE, ClusteredDisk } '@ \ No newline at end of file diff --git a/resourcePlumbing.Tests.ps1 b/resourcePlumbing.Tests.ps1 index 170f372..bd3e804 100644 --- a/resourcePlumbing.Tests.ps1 +++ b/resourcePlumbing.Tests.ps1 @@ -2,7 +2,7 @@ . "$PSScriptRoot\TestFunctions\resourcePlumbing.ps1" -foreach ( $resourceName in 'Shortcut')#'ShellLibrary','ShellLibraryFolder','Shortcut' ) +foreach ( $resourceName in 'Shortcut','ShellLibrary','ShellLibraryFolder') { $params = New-Object psobject -Property @{ ModuleName = 'WindowsShell' From 334dbf2b759da7dec96c0b2ff9b87d706ec37612 Mon Sep 17 00:00:00 2001 From: alx9r Date: Wed, 16 Aug 2017 09:11:25 -0700 Subject: [PATCH 10/11] test for default properties test for default parameter values --- Functions/processShellLibrary.ps1 | 2 +- TestFunctions/resourcePlumbing.ps1 | 31 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Functions/processShellLibrary.ps1 b/Functions/processShellLibrary.ps1 index 973866d..99f14d4 100644 --- a/Functions/processShellLibrary.ps1 +++ b/Functions/processShellLibrary.ps1 @@ -63,7 +63,7 @@ function Invoke-ProcessShellLibrary [Parameter(ValueFromPipelineByPropertyName = $true)] [int] - $IconResourceId=0 + $IconResourceId ) process { diff --git a/TestFunctions/resourcePlumbing.ps1 b/TestFunctions/resourcePlumbing.ps1 index 6dd87b9..2a50c7b 100644 --- a/TestFunctions/resourcePlumbing.ps1 +++ b/TestFunctions/resourcePlumbing.ps1 @@ -95,6 +95,12 @@ ? { $_.MemberName -eq 'Key' } $r | Should not beNullOrEmpty } + It 'no default property values evaluates to true' { + $r = $h.o.GetType().GetProperties() | + % Name | + ? { $h.o.$_ } + $r | Should beNullOrEmpty + } } if ( Get-Member -InputObject $h.o -MemberType Property -Name 'Ensure' ) { @@ -180,6 +186,15 @@ function Test-ProcessFunction $r | Should be $defaultValue } } + else + { + It 'does not have a default value' { + $r = $function.ScriptBlock.Ast.Body.ParamBlock.Parameters. + Where({$_.Name.VariablePath.UserPath -eq $name}). + DefaultValue + $r | Should beNullOrEmpty + } + } It "is a positional argument" { $r = $function.Parameters.$name.Attributes | ? { $_.TypeId.Name -eq 'ParameterAttribute' } | @@ -228,6 +243,22 @@ function Test-ProcessFunction } } } + foreach ( $parameter in ( + $function.Parameters.Values | + ? {$_.Name -notin (Get-CommonParameterNames)} | + ? {$_.Name -notin 'Mode','Ensure'} + ) + ) + { + Context "Resource Parameters: $($parameter.Name)" { + It 'has no default value' { + $r = $function.ScriptBlock.Ast.Body.ParamBlock.Parameters. + Where({$_.Name.VariablePath.UserPath -eq $($parameter.name)}). + DefaultValue + $r | Should beNullOrEmpty + } + } + } } } From 2e7111d8d2ec7540967c8e114405ac3c1455424a Mon Sep 17 00:00:00 2001 From: alx9r Date: Thu, 17 Aug 2017 09:14:38 -0700 Subject: [PATCH 11/11] more public interface testing --- Functions/processShellLibrary.Tests.ps1 | 26 ++++++ Functions/processShellLibrary.ps1 | 8 +- Functions/processShellLibraryFolder.ps1 | 4 +- Functions/processShortcut.Tests.ps1 | 32 ++++++++ Functions/processShortcut.ps1 | 13 +-- ShellLibrary.psm1 | 8 +- ShellLibraryFolder.psm1 | 6 +- Shortcut.psm1 | 8 +- TestFunctions/resourcePlumbing.ps1 | 102 ++++++++++++++++++++---- 9 files changed, 163 insertions(+), 44 deletions(-) diff --git a/Functions/processShellLibrary.Tests.ps1 b/Functions/processShellLibrary.Tests.ps1 index b820d3d..937c37b 100644 --- a/Functions/processShellLibrary.Tests.ps1 +++ b/Functions/processShellLibrary.Tests.ps1 @@ -74,6 +74,32 @@ Describe 'Invoke-ProcessShellLibrary' { $Ensure -eq 'Present' -and $_Keys.Name -eq 'name' -and + #Properties + $Properties.Count -eq 0 + } + } + } + Context 'null optional' { + It 'returns exactly one item' { + $params = New-Object psobject -Property @{ + Mode = 'Set' + Ensure = 'Present' + Name = 'name' + TypeName = $null + StockIconName = $null + IconFilePath = $null + IconResourceId = $null + } + $r = $params | Invoke-ProcessShellLibrary + $r.Count | Should be 1 + $r | Should be 'return value' + } + It 'correctly invokes functions' { + Assert-MockCalled Invoke-ProcessPersistentItem 1 { + $Mode -eq 'Set' -and + $Ensure -eq 'Present' -and + $_Keys.Name -eq 'name' -and + #Properties $Properties.Count -eq 0 } diff --git a/Functions/processShellLibrary.ps1 b/Functions/processShellLibrary.ps1 index 99f14d4..529d418 100644 --- a/Functions/processShellLibrary.ps1 +++ b/Functions/processShellLibrary.ps1 @@ -34,18 +34,17 @@ function Invoke-ProcessShellLibrary [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)] - [Mode] + [System.Nullable[Mode]] $Mode, [Parameter(Position = 2, ValueFromPipelineByPropertyName = $true)] - [Ensure] + [System.Nullable[Ensure]] $Ensure = 'Present', [Parameter(Mandatory = $true, Position = 3, ValueFromPipelineByPropertyName = $true)] - [string] [Alias('LibraryName')] $Name, @@ -58,11 +57,10 @@ function Invoke-ProcessShellLibrary $StockIconName, [Parameter(ValueFromPipelineByPropertyName = $true)] - [string] $IconFilePath, [Parameter(ValueFromPipelineByPropertyName = $true)] - [int] + [System.Nullable[int]] $IconResourceId ) process diff --git a/Functions/processShellLibraryFolder.ps1 b/Functions/processShellLibraryFolder.ps1 index e80135b..b0f7d68 100644 --- a/Functions/processShellLibraryFolder.ps1 +++ b/Functions/processShellLibraryFolder.ps1 @@ -6,12 +6,12 @@ function Invoke-ProcessShellLibraryFolder [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)] - [Mode] + [System.Nullable[Mode]] $Mode, [Parameter(Position = 2, ValueFromPipelineByPropertyName = $true)] - [Ensure] + [System.Nullable[Ensure]] $Ensure = 'Present', [Parameter(Mandatory = $true, diff --git a/Functions/processShortcut.Tests.ps1 b/Functions/processShortcut.Tests.ps1 index 93bad3d..9a2bf11 100644 --- a/Functions/processShortcut.Tests.ps1 +++ b/Functions/processShortcut.Tests.ps1 @@ -66,6 +66,38 @@ Describe 'Invoke-ProcessShortcut' { $Ensure -eq 'Present' -and $_Keys.Path -eq 'path' -and + #Properties + $Properties.Count -eq 0 + } + } + } + Context 'null optional' { + Mock Invoke-ProcessPersistentItem { 'return value' } -Verifiable + It 'returns exactly one item' { + $params = New-Object psobject -Property @{ + Mode = 'Set' + Ensure = 'Present' + Path = 'path' + TargetPath = $null + Arguments = $null + WorkingDirectory = $null + WindowStyle = $null + Hotkey = $null + StockIconName = $null + IconFilePath = $null + IconResourceId = $null + Description = $null + } + $r = $params | Invoke-ProcessShortcut + $r.Count | Should be 1 + $r | Should be 'return value' + } + It 'correctly invokes functions' { + Assert-MockCalled Invoke-ProcessPersistentItem 1 { + $Mode -eq 'Set' -and + $Ensure -eq 'Present' -and + $_Keys.Path -eq 'path' -and + #Properties $Properties.Count -eq 0 } diff --git a/Functions/processShortcut.ps1 b/Functions/processShortcut.ps1 index eb578a7..e334600 100644 --- a/Functions/processShortcut.ps1 +++ b/Functions/processShortcut.ps1 @@ -6,33 +6,29 @@ function Invoke-ProcessShortcut [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)] - [Mode] + [System.Nullable[Mode]] $Mode, [Parameter(Position = 2, ValueFromPipelineByPropertyName = $true)] - [Ensure] + [System.Nullable[Ensure]] $Ensure = 'Present', [Parameter(Mandatory = $true, Position = 3, ValueFromPipelineByPropertyName = $true)] [Alias('FullName')] - [string] $Path, [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('Target')] - [string] $TargetPath, [Parameter(ValueFromPipelineByPropertyName = $true)] - [string] $Arguments, [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('StartIn','WorkingFolder')] - [string] $WorkingDirectory, [Parameter(ValueFromPipelineByPropertyName = $true)] @@ -42,7 +38,6 @@ function Invoke-ProcessShortcut [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('ShortcutKey')] - [string] $Hotkey, [Parameter(ValueFromPipelineByPropertyName = $true)] @@ -50,16 +45,14 @@ function Invoke-ProcessShortcut $StockIconName, [Parameter(ValueFromPipelineByPropertyName = $true)] - [string] $IconFilePath, [Parameter(ValueFromPipelineByPropertyName = $true)] - [int] + [System.Nullable[int]] $IconResourceId, [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('Comment')] - [string] $Description ) process diff --git a/ShellLibrary.psm1 b/ShellLibrary.psm1 index ac17ae0..3b606c3 100644 --- a/ShellLibrary.psm1 +++ b/ShellLibrary.psm1 @@ -1,13 +1,13 @@ [DscResource()] class ShellLibrary { - [DscProperty(Key)] + [DscProperty(Key,Mandatory)] [string] $Name [DscProperty()] - [Ensure] - $Ensure + [System.Nullable[Ensure]] + $Ensure = 'Present' [DscProperty()] [System.Nullable[LibraryTypeName]] @@ -22,7 +22,7 @@ class ShellLibrary $IconFilePath [DscProperty()] - [int] + [System.Nullable[int]] $IconResourceId [void] Set() { $this | Invoke-ProcessShellLibrary Set } diff --git a/ShellLibraryFolder.psm1 b/ShellLibraryFolder.psm1 index 7765808..97b4a32 100644 --- a/ShellLibraryFolder.psm1 +++ b/ShellLibraryFolder.psm1 @@ -1,7 +1,7 @@ [DscResource()] class ShellLibraryFolder { - [DscProperty(Key)] + [DscProperty(Key,Mandatory)] [string] $LibraryName @@ -10,8 +10,8 @@ class ShellLibraryFolder $FolderPath [DscProperty()] - [Ensure] - $Ensure + [System.Nullable[Ensure]] + $Ensure = 'Present' [void] Set() { diff --git a/Shortcut.psm1 b/Shortcut.psm1 index 114a02c..bd79bef 100644 --- a/Shortcut.psm1 +++ b/Shortcut.psm1 @@ -1,13 +1,13 @@ [DscResource()] class Shortcut { - [DscProperty(Key)] + [DscProperty(Key,Mandatory)] [string] $Path [DscProperty()] - [Ensure] - $Ensure + [System.Nullable[Ensure]] + $Ensure = 'Present' [DscProperty()] [string] @@ -38,7 +38,7 @@ class Shortcut $IconFilePath [DscProperty()] - [int] + [System.Nullable[int]] $IconResourceId [DscProperty()] diff --git a/TestFunctions/resourcePlumbing.ps1 b/TestFunctions/resourcePlumbing.ps1 index 2a50c7b..7aea1d5 100644 --- a/TestFunctions/resourcePlumbing.ps1 +++ b/TestFunctions/resourcePlumbing.ps1 @@ -95,11 +95,20 @@ ? { $_.MemberName -eq 'Key' } $r | Should not beNullOrEmpty } - It 'no default property values evaluates to true' { - $r = $h.o.GetType().GetProperties() | - % Name | - ? { $h.o.$_ } - $r | Should beNullOrEmpty + foreach ( $propertyName in $h.o | Get-Member -MemberType Property | % Name ) + { + if ( $propertyName -eq 'Ensure' ) + { + It 'Ensure : has default value of "Present"' { + $h.o.Ensure | Should be 'Present' + } + } + else + { + It "$propertyName : default value is null" { + $null -eq $h.o.$propertyName | Should be $true + } + } } } if ( Get-Member -InputObject $h.o -MemberType Property -Name 'Ensure' ) @@ -155,8 +164,8 @@ function Test-ProcessFunction } $function = (Get-Module $ModuleName).ExportedFunctions.$FunctionName foreach ( $values in @( - @('Mode', 'design_requires', 'mandatory', $null, 1, 'Mode'), - @('Ensure', $null, $null, 'Present',2, 'Ensure') + @('Mode', 'design_requires', 'mandatory', $null, 1, 'System.Nullable[Mode]'), + @('Ensure', $null, $null, 'Present',2, 'System.Nullable[Ensure]') )) { $name,$designRequires,$mandatory,$defaultValue,$position,$typeName = $values @@ -236,11 +245,44 @@ function Test-ProcessFunction ? { $_.TypeId.Name -eq 'ParameterAttribute' } $r.ValueFromPipelineByPropertyName | Should be $true } - It 'does not use a validation script' { - $r = $parameter.Attributes | - ? {$_.TypeId.Name -eq 'ValidateScriptAttribute'} - $r | Should beNullOrEmpty + + $parameter_ = $function.ScriptBlock.Ast.Body.ParamBlock.Parameters. + Where({$_.Name.VariablePath.UserPath -eq $($parameter.name)}) + + if ( $parameter_.StaticType.IsValueType ) + { + It 'is a Nullable value type' { + $r = $parameter_.StaticType.Name + $r | Should be 'Nullable`1' + } + } + elseif ( $function.Parameters.$($parameter.Name).Attributes | + ? { $_.TypeId.Name -eq 'ParameterAttribute' } | + % Mandatory ) + { + It 'has a static type' { + $parameter_.StaticType | + Should not beNullOrEmpty + } } + else + { + It 'does not declare a type' { + $parameter_.StaticType | + Should be 'System.Object' + } + It 'omit [AllowNull()]' { + $r = $parameter.Attributes | + ? {$_.TypeId -match 'AllowNull' } + $r | Should beNullOrEmpty + } + It 'does not use a validation script' { + $r = $parameter.Attributes | + ? {$_.TypeId.Name -eq 'ValidateScriptAttribute'} + $r | Should beNullOrEmpty + } + } + } } foreach ( $parameter in ( @@ -304,17 +346,39 @@ function Test-ResourcePlumbing Sort $memberNames | Should be $parameterNames } - Context 'the member and parameter types match' { + Context 'the parameter/member correlation' { $members = $object.GetType().GetMembers() | ? {$_.MemberType -eq 'Property' } foreach ( $member in $members ) { $name = $member.Name - It $name { - $memberType = $member.PropertyType - $parameterType = $function.Parameters.$name.ParameterType + + $parameter = $function.Parameters.$name + $parameter_ = $function.ScriptBlock.Ast.Body.ParamBlock.Parameters. + Where({$_.Name.VariablePath.UserPath -eq $name}) + + if ( $parameter_.StaticType.IsValueType ) + { + It "$name : the parameter type matches the member type" { + $memberType = $member.PropertyType + $parameterType = $parameter.ParameterType - $memberType | Should be $parameterType + $parameterType | Should be $memberType + } + } + It "$name : the parameter mandatoriness matches the member mandatoriness" { + $parameterMandatory = [bool]($function.Parameters.$name.Attributes | + ? { $_.TypeId.Name -eq 'ParameterAttribute' } | + % Mandatory) + $memberMandatory = [bool]($object.GetType().DeclaredMembers | + ? {$_.Name -eq $name } | + % CustomAttributes | + ? {$_.AttributeType -match 'DscProperty' } | + % NamedArguments | + ? {$_.MemberName -eq 'Mandatory' } | + % TypedValue | + % Value) + $parameterMandatory | Should be $memberMandatory } } } @@ -334,6 +398,12 @@ function Test-ResourcePlumbing { $parameters.$($variable.Name) = [System.Enum]::GetValues($variable.PropertyType.GenericTypeArguments[0])[0] } + # if it's a nullable int, use 0 + elseif ( $variable.PropertyType.Name -eq 'Nullable`1' -and + $variable.PropertyType.BaseType -eq [System.ValueType] ) + { + $parameters.$($variable.Name) = 0 + } # if there is a default value, use it elseif ( $object.$($variable.Name) ) {