Fix up coverage with async on Windows (#84917)

Fixes the coverage collection for Windows and async tasks. This ensures
the async task still has access to the PSHost so that it can access the
runspace debugger tasks on the host.
pull/84796/merge
Jordan Borean 8 months ago committed by GitHub
parent 390e112822
commit b7d76a93b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -4,6 +4,7 @@
using namespace Microsoft.Win32.SafeHandles using namespace Microsoft.Win32.SafeHandles
using namespace System.Collections using namespace System.Collections
using namespace System.IO using namespace System.IO
using namespace System.Management.Automation
using namespace System.Text using namespace System.Text
using namespace System.Threading using namespace System.Threading
@ -43,6 +44,15 @@ param([ScriptBlock]$ScriptBlock, $Param)
Param = $execInfo.Parameters Param = $execInfo.Parameters
}) })
# It is important we run with the invocation settings so that it has access
# to the same PSHost. The pipeline input also needs to be marked as complete
# so the exec_wrapper isn't waiting for input indefinitely.
$pipelineInput = [PSDataCollection[object]]::new()
$pipelineInput.Complete()
$invocationSettings = [PSInvocationSettings]@{
Host = $host
}
# Signals async_wrapper that we are ready to start the job and to stop waiting # Signals async_wrapper that we are ready to start the job and to stop waiting
$waitHandle = [SafeWaitHandle]::new([IntPtr]$WaitHandleId, $true) $waitHandle = [SafeWaitHandle]::new([IntPtr]$WaitHandleId, $true)
$waitEvent = [ManualResetEvent]::new($false) $waitEvent = [ManualResetEvent]::new($false)
@ -52,7 +62,7 @@ $null = $waitEvent.Set()
$jobOutput = $null $jobOutput = $null
$jobError = $null $jobError = $null
try { try {
$jobAsyncResult = $ps.BeginInvoke() $jobAsyncResult = $ps.BeginInvoke($pipelineInput, $invocationSettings, $null, $null)
$jobAsyncResult.AsyncWaitHandle.WaitOne($Timeout * 1000) > $null $jobAsyncResult.AsyncWaitHandle.WaitOne($Timeout * 1000) > $null
$result.finished = 1 $result.finished = 1

@ -0,0 +1,9 @@
Function Test-CollectionPwshCoverage {
<#
.SYNOPSIS
Test coverage for collection pwsh util.
#>
'foo'
}
Export-ModuleMember -Function Test-CollectionPwshCoverage

@ -1,6 +1,8 @@
#!powershell #!powershell
#AnsibleRequires -CSharpUtil Ansible.Basic #AnsibleRequires -CSharpUtil Ansible.Basic
#AnsibleRequires -PowerShell ..module_utils.CollectionPwshCoverage
$module = [Ansible.Basic.AnsibleModule]::Create($args, @{}) $module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
$module.Result.util = Test-CollectionPwshCoverage
$module.ExitJson() $module.ExitJson()

@ -0,0 +1,6 @@
#!powershell
#AnsibleRequires -CSharpUtil Ansible.Basic
$module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
$module.ExitJson()

@ -0,0 +1,8 @@
#!powershell
#AnsibleRequires -CSharpUtil Ansible.Basic
#AnsibleRequires -PowerShell Ansible.ModuleUtils.AdjacentPwshCoverage
$module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
$module.Result.util = Test-AdjacentPwshCoverage
$module.ExitJson()

@ -0,0 +1,9 @@
Function Test-AdjacentPwshCoverage {
<#
.SYNOPSIS
Test coverage for module_util adjacent pwsh util.
#>
'foo'
}
Export-ModuleMember -Function Test-AdjacentPwshCoverage

@ -2,4 +2,21 @@
win_collection: win_collection:
- name: run module in library adjacent to test coverage for test plugins - name: run module in library adjacent to test coverage for test plugins
test_win_collection: test_win_collection_normal:
register: library_result
- name: assert run module with library adjacent module
assert:
that:
- library_result.util == 'foo'
- name: test coverage under async
test_win_collection_async:
async: 30
poll: 2
- name: test coverage under become
test_win_collection_become:
become: yes
become_method: runas
become_user: SYSTEM

@ -3,24 +3,42 @@ from __future__ import annotations
import json import json
import os import os
import os.path import os.path
import pathlib
def main() -> None: def main() -> None:
collection_root = os.getcwd() collection_root = pathlib.Path(os.getcwd())
print(f"Running windows-integration coverage test in '{collection_root}'") print(f"Running windows-integration coverage test in '{collection_root}'")
result_path = os.path.join(collection_root, "tests", "output", "coverage", "coverage-powershell") result_path = collection_root / "tests" / "output" / "coverage" / "coverage-powershell"
module_path = os.path.join(collection_root, "plugins", "modules", "win_collection.ps1") adjacent_modules_path = collection_root / "tests" / "integration" / "targets" / "win_collection" / "library"
test_path = os.path.join(collection_root, "tests", "integration", "targets", "win_collection", "library", "test_win_collection.ps1") adjacent_utils_path = collection_root / "tests" / "integration" / "targets" / "win_collection" / "module_utils"
collection_modules_path = collection_root / "plugins" / "modules"
collection_utils_path = collection_root / "plugins" / "module_utils"
expected_hits = {
str(adjacent_modules_path / 'test_win_collection_async.ps1'): {'5': 1, '6': 1},
str(adjacent_modules_path / 'test_win_collection_become.ps1'): {'5': 1, '6': 1},
str(adjacent_modules_path / 'test_win_collection_normal.ps1'): {'6': 1, '7': 1, '8': 1},
str(adjacent_utils_path / 'Ansible.ModuleUtils.AdjacentPwshCoverage.psm1'): {'6': 1, '9': 1},
str(collection_modules_path / 'win_collection.ps1'): {'6': 1, '7': 1, '8': 1},
str(collection_utils_path / 'CollectionPwshCoverage.psm1'): {'6': 1, '9': 1},
}
found_hits = set()
with open(result_path, mode="rb") as fd: with open(result_path, mode="rb") as fd:
data = json.load(fd) data = json.load(fd)
for path, result in data.items(): for path, result in data.items():
print(f"Testing result for path '{path}' -> {result!r}") print(f"Testing result for path '{path}' -> {result!r}")
assert path in [module_path, test_path], f"Found unexpected coverage result path '{path}'" assert path in expected_hits, f"Found unexpected coverage result path '{path}'"
assert result == {'5': 1, '6': 1}, "Coverage result did not pick up a hit on lines 5 and 6"
expected = expected_hits[path]
assert result == expected, f"Coverage result for {path} was {result!r} but was expecting {expected!r}"
found_hits.add(path)
assert len(data) == 2, f"Expected coverage results for 2 files but got {len(data)}" missing_hits = set(expected_hits.keys()).difference(found_hits)
assert not missing_hits, f"Expected coverage results for {', '.join(missing_hits)} but they were not present"
if __name__ == '__main__': if __name__ == '__main__':

Loading…
Cancel
Save