From 0d1e1919f2c696b08531cf3b57466f36bf192352 Mon Sep 17 00:00:00 2001 From: "James Truher [MSFT]" Date: Wed, 16 Aug 2017 16:33:53 -0700 Subject: [PATCH] Improve test coverage for CDXML cmdlet infrastructure (#4537) Create custom CIM classes (via MOF) and CDXML cmdlets against the new CIM class Create tests for CRUD operations for CDXML cmdlets Coverage for Microsoft.PowerShell.Cmdletization namespace is nearly 50% --- test/powershell/engine/Cdxml/Cdxml.Tests.ps1 | 273 ++++++++++++++++++ .../Cdxml/assets/CimTest/CdxmlTest.psd1 | 14 + .../engine/Cdxml/assets/CimTest/CimTest.cdxml | 122 ++++++++ .../Cdxml/assets/CimTest/CreateCimTest.mof | 64 ++++ .../Cdxml/assets/CimTest/DeleteCimTest.mof | 5 + 5 files changed, 478 insertions(+) create mode 100644 test/powershell/engine/Cdxml/Cdxml.Tests.ps1 create mode 100644 test/powershell/engine/Cdxml/assets/CimTest/CdxmlTest.psd1 create mode 100644 test/powershell/engine/Cdxml/assets/CimTest/CimTest.cdxml create mode 100644 test/powershell/engine/Cdxml/assets/CimTest/CreateCimTest.mof create mode 100644 test/powershell/engine/Cdxml/assets/CimTest/DeleteCimTest.mof diff --git a/test/powershell/engine/Cdxml/Cdxml.Tests.ps1 b/test/powershell/engine/Cdxml/Cdxml.Tests.ps1 new file mode 100644 index 000000000..52e374cb8 --- /dev/null +++ b/test/powershell/engine/Cdxml/Cdxml.Tests.ps1 @@ -0,0 +1,273 @@ +$script:CimClassName = "PSCore_CimTest1" +$script:CimNamespace = "root/default" +$script:moduleDir = Join-Path -Path $PSScriptRoot -ChildPath assets -AdditionalChildPath CimTest +$script:deleteMof = Join-Path -Path $moduleDir -ChildPath DeleteCimTest.mof +$script:createMof = Join-Path -Path $moduleDir -ChildPath CreateCimTest.mof + +$CimCmdletArgs = @{ + Namespace = ${script:CimNamespace} + ClassName = ${script:CimClassName} + ErrorAction = "SilentlyContinue" + } + +$script:ItSkipOrPending = @{} + +function Test-CimTestClass { + $null -eq (Get-CimClass @CimCmdletArgs) +} + +function Test-CimTestInstance { + $null -eq (Get-CimInstance @CimCmdletArgs) +} + +Describe "Cdxml cmdlets are supported" -Tag CI,RequireAdminOnWindows { + BeforeAll { + $skipNotWindows = ! $IsWindows + if ( $skipNotWindows ) { + $script:ItSkipOrPending = @{ Skip = $true } + return + } + + # if MofComp does not exist, we shouldn't bother moving forward + # there is a possibility that we could be on Windows, but MofComp + # isn't present, in any event we will mark these tests as skipped + # since the environment won't support loading the test classes + if ( (Get-Command -ea SilentlyContinue Mofcomp.exe) -eq $null ) { + $script:ItSkipOrPending = @{ Skip = $true } + return + } + + # start from a clean slate, remove the instances and the + # classes if they exist + if ( Test-CimTestClass ) { + if ( Test-CimTestInstance ) { + Get-CimInstance @CimCmdletArgs | Remove-CimInstance + } + # if there's a failure with mofcomp then we will have trouble + # executing the tests. Keep track of the exit code + $result = MofComp.exe $deleteMof + $script:MofCompReturnCode = $LASTEXITCODE + if ( $script:MofCompReturnCode -ne 0 ) { + return + } + } + + # create the class and instances + # and track the exitcode for the compilation of the mof file + # if there's a problem, there's no reason to keep going + $result = MofComp.exe ${script:createMof} + $script:MofCompReturnCode = $LASTEXITCODE + if ( $script:MofCompReturnCode -ne 0 ) { + return + } + + # now load the cdxml module + if ( Get-Module CimTest ) { + Remove-Module -force CimTest + } + Import-Module -force ${script:ModuleDir} + } + + AfterAll { + if ( $skipNotWindows ) { + return + } + if ( get-module CimTest ) { + Remove-Module CimTest -Force + } + $null = MofComp.exe $deleteMof + if ( $LASTEXITCODE -ne 0 ) { + Write-Warning "Could not remove PSCore_CimTest class" + } + } + + BeforeEach { + If ( $script:MofCompReturnCode -ne 0 ) { + throw "MofComp.exe failed with exit code $MofCompReturnCode" + } + } + + Context "Module level tests" { + It "The CimTest module should have been loaded" @ItSkipOrPending { + $result = Get-Module CimTest + $result.ModuleBase | should be ${script:ModuleDir} + } + + It "The CimTest module should have the proper cmdlets" @ItSkipOrPending { + $result = Get-Command -Module CimTest + $result.Count | Should Be 4 + ($result.Name | sort-object ) -join "," | Should Be "Get-CimTest,New-CimTest,Remove-CimTest,Set-CimTest" + } + } + + Context "Get-CimTest cmdlet" { + It "The Get-CimTest cmdlet should return 4 objects" @ItSkipOrPending { + $result = Get-CimTest + $result.Count | should be 4 + ($result.id |sort-object) -join "," | should be "1,2,3,4" + } + + It "The Get-CimTest cmdlet should retrieve an object via id" @ItSkipOrPending { + $result = Get-CimTest -id 1 + @($result).Count | should be 1 + $result.field1 | Should be "instance 1" + } + + It "The Get-CimTest cmdlet should retrieve an object by piped id" @ItSkipOrPending { + $result = 1,2,4 | foreach-object { [pscustomobject]@{ id = $_ } } | Get-CimTest + @($result).Count | should be 3 + ( $result.id | sort-object ) -join "," | Should be "1,2,4" + } + + It "The Get-CimTest cmdlet should return the proper error if the instance does not exist" @ItSkipOrPending { + { Get-CimTest -ea stop -id "ThisIdDoesNotExist" } | ShouldBeErrorId "CmdletizationQuery_NotFound_Id,Get-CimTest" + } + + It "The Get-CimTest cmdlet should work as a job" @ItSkipOrPending { + try { + $job = Get-CimTest -AsJob + $result = $null + # wait up to 10 seconds, then the test will fail + # we need to wait long enough, but not too long + # the time can be adjusted + $null = Wait-Job -Job $job -timeout 10 + $result = $job | Receive-Job + $result.Count | should be 4 + ( $result.id | sort-object ) -join "," | Should be "1,2,3,4" + } + finally { + if ( $job ) { + $job | Remove-Job -force + } + } + } + + It "Should be possible to invoke a method on an object returned by Get-CimTest" @ItSkipOrPending { + $result = Get-CimTest | Select-Object -first 1 + $result.GetCimSessionInstanceId() | Should BeOfType [guid] + } + } + + Context "Remove-CimTest cmdlet" { + BeforeEach { + Get-CimTest | Remove-CimTest + 1..4 | Foreach-Object { New-CimInstance -namespace root/default -class PSCore_Test1 -property @{ + id = "$_" + field1 = "field $_" + field2 = 10 * $_ + } + } + } + + It "The Remote-CimTest cmdlet should remove objects by id" @ItSkipOrPending { + Remove-CimTest -id 1 + $result = Get-CimTest + $result.Count | should be 3 + ($result.id |sort-object) -join "," | should be "2,3,4" + } + + It "The Remove-CimTest cmdlet should remove piped objects" @ItSkipOrPending { + Get-CimTest -id 2 | Remove-CimTest + $result = Get-CimTest + @($result).Count | should be 3 + ($result.id |sort-object) -join "," | should be "1,3,4" + } + + It "The Remove-CimTest cmdlet should work as a job" @ItSkipOrPending { + try { + $job = Get-CimTest -id 3 | Remove-CimTest -asjob + $result = $null + # wait up to 10 seconds, then the test will fail + # we need to wait long enough, but not too long + # the time can be adjusted + $null = Wait-Job -Job $job -Timeout 10 + $result = Get-CimTest + @($result).Count | should be 3 + ($result.id |sort-object) -join "," | should be "1,2,4" + } + finally { + if ( $job ) { + $job | Remove-Job -force + } + } + } + } + + Context "New-CimTest operations" { + It "Should create a new instance" @ItSkipOrPending { + $instanceArgs = @{ + id = "telephone" + field1 = "television" + field2 = 0 + } + New-CimTest @instanceArgs + $result = Get-CimInstance -namespace root/default -class PSCore_Test1 | Where-Object {$_.id -eq "telephone"} + $result.field2 | should be 0 + $result.field1 | Should be $instanceArgs.field1 + } + + It "Should return the proper error if called with an improper value" @ItSkipOrPending { + $instanceArgs = @{ + Id = "error validation" + field1 = "a string" + field2 = "a bad string" # this needs to be an int + } + { New-CimTest @instanceArgs } | ShouldBeErrorId "ParameterArgumentTransformationError,New-CimTest" + # just make sure that it wasn't added + Get-CimTest -id $instanceArgs.Id -ea SilentlyContinue | Should BeNullOrEmpty + } + + It "Should support -whatif" @ItSkipOrPending { + $instanceArgs = @{ + Id = "1000" + field1 = "a string" + field2 = 111 + Whatif = $true + } + New-CimTest @instanceArgs + Get-CimTest -id $instanceArgs.Id -ea SilentlyContinue | Should BeNullOrEmpty + } + } + + Context "Set-CimTest operations" { + It "Should set properties on an instance" @ItSkipOrPending { + $instanceArgs = @{ + id = "updateTest1" + field1 = "updatevalue" + field2 = 100 + } + $newValues = @{ + id = "updateTest1" + field2 = 22 + field1 = "newvalue" + } + New-CimTest @instanceArgs + $result = Get-CimTest -id $instanceArgs.id + $result.field2 | should be $instanceArgs.field2 + $result.field1 | Should be $instanceArgs.field1 + Set-CimTest @newValues + $result = Get-CimTest -id $newValues.id + $result.field1 | Should be $newValues.field1 + $result.field2 | should be $newValues.field2 + } + + It "Should set properties on an instance via pipeline" @ItSkipOrPending { + $instanceArgs = @{ + id = "updateTest2" + field1 = "updatevalue" + field2 = 100 + } + New-CimTest @instanceArgs + $result = Get-CimTest -id $instanceArgs.id + $result.field2 | should be $instanceArgs.field2 + $result.field1 | Should be $instanceArgs.field1 + $result.field1 = "yet another value" + $result.field2 = 33 + $result | Set-CimTest + $result = Get-CimTest -id $instanceArgs.id + $result.field1 | Should be "yet another value" + $result.field2 | should be 33 + } + } + +} diff --git a/test/powershell/engine/Cdxml/assets/CimTest/CdxmlTest.psd1 b/test/powershell/engine/Cdxml/assets/CimTest/CdxmlTest.psd1 new file mode 100644 index 000000000..23214f45e --- /dev/null +++ b/test/powershell/engine/Cdxml/assets/CimTest/CdxmlTest.psd1 @@ -0,0 +1,14 @@ +@{ + GUID = '41486F7D-842F-40F1-ACE4-8405F9C2ED9B' + Author="Microsoft Corporation" + CompanyName="Microsoft Corporation" + Copyright="© Microsoft Corporation. All rights reserved." + ModuleVersion = '2.0.0.0' + PowerShellVersion = '3.0' + FormatsToProcess = @() + TypesToProcess = @() + NestedModules = @( 'CimTest.cdxml') + AliasesToExport = @() + CmdletsToExport = @() + FunctionsToExport = @( 'Get-CimTest', 'Remove-CimTest', 'New-CimTest', 'Set-CimTest' ) +} diff --git a/test/powershell/engine/Cdxml/assets/CimTest/CimTest.cdxml b/test/powershell/engine/Cdxml/assets/CimTest/CimTest.cdxml new file mode 100644 index 000000000..6bae19576 --- /dev/null +++ b/test/powershell/engine/Cdxml/assets/CimTest/CimTest.cdxml @@ -0,0 +1,122 @@ + + + 1.0.0.0 + CimTest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/powershell/engine/Cdxml/assets/CimTest/CreateCimTest.mof b/test/powershell/engine/Cdxml/assets/CimTest/CreateCimTest.mof new file mode 100644 index 000000000..570c52efe --- /dev/null +++ b/test/powershell/engine/Cdxml/assets/CimTest/CreateCimTest.mof @@ -0,0 +1,64 @@ +class PSCore_subclass +{ + [key] string id; + string subfield1; + sint32 subfield2; +}; + +instance of PSCore_subclass +{ + id = "s1"; + subfield1 = "sub thing"; + subfield2 = 100; +}; + +[HasClassRefs, Association] +class PSCore_Association +{ + [key, classref{ "PSCore_Test1", "PSCore_Test2" }] + object ref ep1; + [key] object ref ep2; +}; + +class PSCore_Test2 +{ + [key] string id; + string field1; + sint32 field2; +}; + +class PSCore_Test1 +{ + [key] string id; + string field1; + sint32 field2; + PSCore_subclass subclass; +}; + +instance of PSCore_Test1 +{ + id = "1"; + field1 = "instance 1"; + field2 = -1; + subclass = instance of PSCore_subclass{Id="10";subfield1="yup";subfield2=200;}; +}; + +instance of PSCore_Test1 +{ + id = "2"; + field1 = "instance 2"; + field2 = -2; +}; + +instance of PSCore_Test1 +{ + id = "3"; + field1 = "instance 3"; + field2 = -3; +}; +instance of PSCore_Test1 +{ + id = "4"; + field1 = "instance 4"; + field2 = -4; +}; diff --git a/test/powershell/engine/Cdxml/assets/CimTest/DeleteCimTest.mof b/test/powershell/engine/Cdxml/assets/CimTest/DeleteCimTest.mof new file mode 100644 index 000000000..cd84ab177 --- /dev/null +++ b/test/powershell/engine/Cdxml/assets/CimTest/DeleteCimTest.mof @@ -0,0 +1,5 @@ +#pragma namespace ("\\\\.\\root\\default") +#pragma deleteclass("PSCore_Test1", NOFAIL) +#pragma deleteclass("PSCore_Test2", NOFAIL) +#pragma deleteclass("PSCore_subclass", NOFAIL) +#pragma deleteclass("PSCore_Association", NOFAIL)