diff --git a/lib/ansible/executor/powershell/async_watchdog.ps1 b/lib/ansible/executor/powershell/async_watchdog.ps1 index c33ff3a320b..9eef1efa960 100644 --- a/lib/ansible/executor/powershell/async_watchdog.ps1 +++ b/lib/ansible/executor/powershell/async_watchdog.ps1 @@ -4,6 +4,7 @@ using namespace Microsoft.Win32.SafeHandles using namespace System.Collections using namespace System.IO +using namespace System.Management.Automation using namespace System.Text using namespace System.Threading @@ -43,6 +44,15 @@ param([ScriptBlock]$ScriptBlock, $Param) 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 $waitHandle = [SafeWaitHandle]::new([IntPtr]$WaitHandleId, $true) $waitEvent = [ManualResetEvent]::new($false) @@ -52,7 +62,7 @@ $null = $waitEvent.Set() $jobOutput = $null $jobError = $null try { - $jobAsyncResult = $ps.BeginInvoke() + $jobAsyncResult = $ps.BeginInvoke($pipelineInput, $invocationSettings, $null, $null) $jobAsyncResult.AsyncWaitHandle.WaitOne($Timeout * 1000) > $null $result.finished = 1 diff --git a/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/plugins/module_utils/CollectionPwshCoverage.psm1 b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/plugins/module_utils/CollectionPwshCoverage.psm1 new file mode 100644 index 00000000000..01a83483e57 --- /dev/null +++ b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/plugins/module_utils/CollectionPwshCoverage.psm1 @@ -0,0 +1,9 @@ +Function Test-CollectionPwshCoverage { + <# + .SYNOPSIS + Test coverage for collection pwsh util. + #> + 'foo' +} + +Export-ModuleMember -Function Test-CollectionPwshCoverage diff --git a/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/plugins/modules/win_collection.ps1 b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/plugins/modules/win_collection.ps1 index 53b2f2da3b3..708e3fa65ef 100644 --- a/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/plugins/modules/win_collection.ps1 +++ b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/plugins/modules/win_collection.ps1 @@ -1,6 +1,8 @@ #!powershell #AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ..module_utils.CollectionPwshCoverage $module = [Ansible.Basic.AnsibleModule]::Create($args, @{}) +$module.Result.util = Test-CollectionPwshCoverage $module.ExitJson() diff --git a/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/library/test_win_collection.ps1 b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/library/test_win_collection_async.ps1 similarity index 100% rename from test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/library/test_win_collection.ps1 rename to test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/library/test_win_collection_async.ps1 diff --git a/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/library/test_win_collection_become.ps1 b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/library/test_win_collection_become.ps1 new file mode 100644 index 00000000000..53b2f2da3b3 --- /dev/null +++ b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/library/test_win_collection_become.ps1 @@ -0,0 +1,6 @@ +#!powershell + +#AnsibleRequires -CSharpUtil Ansible.Basic + +$module = [Ansible.Basic.AnsibleModule]::Create($args, @{}) +$module.ExitJson() diff --git a/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/library/test_win_collection_normal.ps1 b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/library/test_win_collection_normal.ps1 new file mode 100644 index 00000000000..ba11c1255a3 --- /dev/null +++ b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/library/test_win_collection_normal.ps1 @@ -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() diff --git a/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/module_utils/Ansible.ModuleUtils.AdjacentPwshCoverage.psm1 b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/module_utils/Ansible.ModuleUtils.AdjacentPwshCoverage.psm1 new file mode 100644 index 00000000000..36eada2d443 --- /dev/null +++ b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/module_utils/Ansible.ModuleUtils.AdjacentPwshCoverage.psm1 @@ -0,0 +1,9 @@ +Function Test-AdjacentPwshCoverage { + <# + .SYNOPSIS + Test coverage for module_util adjacent pwsh util. + #> + 'foo' +} + +Export-ModuleMember -Function Test-AdjacentPwshCoverage diff --git a/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/tasks/main.yml b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/tasks/main.yml index 6196b768c6b..b4c59aeeaed 100644 --- a/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/tasks/main.yml +++ b/test/integration/targets/ansible-test-coverage-windows/ansible_collections/ns/col/tests/integration/targets/win_collection/tasks/main.yml @@ -2,4 +2,21 @@ win_collection: - 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 diff --git a/test/integration/targets/ansible-test-coverage-windows/test-coverage.py b/test/integration/targets/ansible-test-coverage-windows/test-coverage.py index 98dbca7437c..b3471089ffe 100644 --- a/test/integration/targets/ansible-test-coverage-windows/test-coverage.py +++ b/test/integration/targets/ansible-test-coverage-windows/test-coverage.py @@ -3,24 +3,42 @@ from __future__ import annotations import json import os import os.path +import pathlib def main() -> None: - collection_root = os.getcwd() + collection_root = pathlib.Path(os.getcwd()) print(f"Running windows-integration coverage test in '{collection_root}'") - result_path = os.path.join(collection_root, "tests", "output", "coverage", "coverage-powershell") - module_path = os.path.join(collection_root, "plugins", "modules", "win_collection.ps1") - test_path = os.path.join(collection_root, "tests", "integration", "targets", "win_collection", "library", "test_win_collection.ps1") + result_path = collection_root / "tests" / "output" / "coverage" / "coverage-powershell" + adjacent_modules_path = collection_root / "tests" / "integration" / "targets" / "win_collection" / "library" + 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: data = json.load(fd) for path, result in data.items(): print(f"Testing result for path '{path}' -> {result!r}") - assert path in [module_path, test_path], 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" + assert path in expected_hits, f"Found unexpected coverage result path '{path}'" + + 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__':