1 #!powershell
2 
3 #AnsibleRequires -CSharpUtil Ansible.Basic
4 #AnsibleRequires -CSharpUtil Ansible.Process
5 
6 $module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
7 
Assert-Equalsnull8 Function Assert-Equals {
9     param(
10         [Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
11         [Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
12     )
13 
14     $matched = $false
15     if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) {
16         $Actual.Count | Assert-Equals -Expected $Expected.Count
17         for ($i = 0; $i -lt $Actual.Count; $i++) {
18             $actual_value = $Actual[$i]
19             $expected_value = $Expected[$i]
20             Assert-Equals -Actual $actual_value -Expected $expected_value
21         }
22         $matched = $true
23     } else {
24         $matched = $Actual -ceq $Expected
25     }
26 
27     if (-not $matched) {
28         if ($Actual -is [PSObject]) {
29             $Actual = $Actual.ToString()
30         }
31 
32         $call_stack = (Get-PSCallStack)[1]
33         $module.Result.test = $test
34         $module.Result.actual = $Actual
35         $module.Result.expected = $Expected
36         $module.Result.line = $call_stack.ScriptLineNumber
37         $module.Result.method = $call_stack.Position.Text
38         $module.FailJson("AssertionError: actual != expected")
39     }
40 }
41 
42 $tests = @{
43     "ParseCommandLine empty string" = {
44         $expected = @((Get-Process -Id $pid).Path)
45         $actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("")
46         Assert-Equals -Actual $actual -Expected $expected
47     }
48 
49     "ParseCommandLine single argument" = {
50         $expected = @("powershell.exe")
51         $actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("powershell.exe")
52         Assert-Equals -Actual $actual -Expected $expected
53     }
54 
55     "ParseCommandLine multiple arguments" = {
56         $expected = @("powershell.exe", "-File", "C:\temp\script.ps1")
57         $actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("powershell.exe -File C:\temp\script.ps1")
58         Assert-Equals -Actual $actual -Expected $expected
59     }
60 
61     "ParseCommandLine comples arguments" = {
62         $expected = @('abc', 'd', 'ef gh', 'i\j', 'k"l', 'm\n op', 'ADDLOCAL=qr, s', 'tuv\', 'w''x', 'yz')
63         $actual = [Ansible.Process.ProcessUtil]::ParseCommandLine('abc d "ef gh" i\j k\"l m\\"n op" ADDLOCAL="qr, s" tuv\ w''x yz')
64         Assert-Equals -Actual $actual -Expected $expected
65     }
66 
67     "SearchPath normal" = {
68         $expected = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe"
69         $actual = [Ansible.Process.ProcessUtil]::SearchPath("powershell.exe")
70         $actual | Assert-Equals -Expected $expected
71     }
72 
73     "SearchPath missing" = {
74         $failed = $false
75         try {
76             [Ansible.Process.ProcessUtil]::SearchPath("fake.exe")
77         } catch {
78             $failed = $true
79             $_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "System.IO.FileNotFoundException"
80             $expected = 'Exception calling "SearchPath" with "1" argument(s): "Could not find file ''fake.exe''."'
81             $_.Exception.Message | Assert-Equals -Expected $expected
82         }
83         $failed | Assert-Equals -Expected $true
84     }
85 
86     "CreateProcess basic" = {
87         $actual = [Ansible.Process.ProcessUtil]::CreateProcess("whoami.exe")
88         $actual.GetType().FullName | Assert-Equals -Expected "Ansible.Process.Result"
89         $actual.StandardOut | Assert-Equals -Expected "$(&whoami.exe)`r`n"
90         $actual.StandardError | Assert-Equals -Expected ""
91         $actual.ExitCode | Assert-Equals -Expected 0
92     }
93 
94     "CreateProcess stderr" = {
95         $actual = [Ansible.Process.ProcessUtil]::CreateProcess("powershell.exe [System.Console]::Error.WriteLine('hi')")
96         $actual.StandardOut | Assert-Equals -Expected ""
97         $actual.StandardError | Assert-Equals -Expected "hi`r`n"
98         $actual.ExitCode | Assert-Equals -Expected 0
99     }
100 
101     "CreateProcess exit code" = {
102         $actual = [Ansible.Process.ProcessUtil]::CreateProcess("powershell.exe exit 10")
103         $actual.StandardOut | Assert-Equals -Expected ""
104         $actual.StandardError | Assert-Equals -Expected ""
105         $actual.ExitCode | Assert-Equals -Expected 10
106     }
107 
108     "CreateProcess bad executable" = {
109         $failed = $false
110         try {
111             [Ansible.Process.ProcessUtil]::CreateProcess("fake.exe")
112         } catch {
113             $failed = $true
114             $_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "Ansible.Process.Win32Exception"
115             $expected = 'Exception calling "CreateProcess" with "1" argument(s): "CreateProcessW() failed '
116             $expected += '(The system cannot find the file specified, Win32ErrorCode 2)"'
117             $_.Exception.Message | Assert-Equals -Expected $expected
118         }
119         $failed | Assert-Equals -Expected $true
120     }
121 
122     "CreateProcess with unicode" = {
123         $actual = [Ansible.Process.ProcessUtil]::CreateProcess("cmd.exe /c echo �� café")
124         $actual.StandardOut | Assert-Equals -Expected "�� café`r`n"
125         $actual.StandardError | Assert-Equals -Expected ""
126         $actual.ExitCode | Assert-Equals -Expected 0
127 
128         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, "cmd.exe /c echo �� café", $null, $null)
129         $actual.StandardOut | Assert-Equals -Expected "�� café`r`n"
130         $actual.StandardError | Assert-Equals -Expected ""
131         $actual.ExitCode | Assert-Equals -Expected 0
132     }
133 
134     "CreateProcess without working dir" = {
135         $expected = $pwd.Path + "`r`n"
136         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $pwd.Path', $null, $null)
137         $actual.StandardOut | Assert-Equals -Expected $expected
138         $actual.StandardError | Assert-Equals -Expected ""
139         $actual.ExitCode | Assert-Equals -Expected 0
140     }
141 
142     "CreateProcess with working dir" = {
143         $expected = "C:\Windows`r`n"
144         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $pwd.Path', "C:\Windows", $null)
145         $actual.StandardOut | Assert-Equals -Expected $expected
146         $actual.StandardError | Assert-Equals -Expected ""
147         $actual.ExitCode | Assert-Equals -Expected 0
148     }
149 
150     "CreateProcess without environment" = {
151         $expected = "$($env:USERNAME)`r`n"
152         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $env:TEST; $env:USERNAME', $null, $null)
153         $actual.StandardOut | Assert-Equals -Expected $expected
154         $actual.StandardError | Assert-Equals -Expected ""
155         $actual.ExitCode | Assert-Equals -Expected 0
156     }
157 
158     "CreateProcess with environment" = {
159         $env_vars = @{
160             TEST = "tesTing"
161             TEST2 = "Testing 2"
162         }
163         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'cmd.exe /c set', $null, $env_vars)
164         ("TEST=tesTing" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
165         ("TEST2=Testing 2" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
166         ("USERNAME=$($env:USERNAME)" -cnotin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
167         $actual.StandardError | Assert-Equals -Expected ""
168         $actual.ExitCode | Assert-Equals -Expected 0
169     }
170 
171     "CreateProcess with string stdin" = {
172         $expected = "input value`r`n`r`n"
173         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
174             $null, $null, "input value")
175         $actual.StandardOut | Assert-Equals -Expected $expected
176         $actual.StandardError | Assert-Equals -Expected ""
177         $actual.ExitCode | Assert-Equals -Expected 0
178     }
179 
180     "CreateProcess with string stdin and newline" = {
181         $expected = "input value`r`n`r`n"
182         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
183             $null, $null, "input value`r`n")
184         $actual.StandardOut | Assert-Equals -Expected $expected
185         $actual.StandardError | Assert-Equals -Expected ""
186         $actual.ExitCode | Assert-Equals -Expected 0
187     }
188 
189     "CreateProcess with byte stdin" = {
190         $expected = "input value`r`n"
191         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
192             $null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value"))
193         $actual.StandardOut | Assert-Equals -Expected $expected
194         $actual.StandardError | Assert-Equals -Expected ""
195         $actual.ExitCode | Assert-Equals -Expected 0
196     }
197 
198     "CreateProcess with byte stdin and newline" = {
199         $expected = "input value`r`n`r`n"
200         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
201             $null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value`r`n"))
202         $actual.StandardOut | Assert-Equals -Expected $expected
203         $actual.StandardError | Assert-Equals -Expected ""
204         $actual.ExitCode | Assert-Equals -Expected 0
205     }
206 
207     "CreateProcess with lpApplicationName" = {
208         $expected = "abc`r`n"
209         $full_path = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe"
210         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($full_path, "Write-Output 'abc'", $null, $null)
211         $actual.StandardOut | Assert-Equals -Expected $expected
212         $actual.StandardError | Assert-Equals -Expected ""
213         $actual.ExitCode | Assert-Equals -Expected 0
214 
215         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($full_path, "powershell.exe Write-Output 'abc'", $null, $null)
216         $actual.StandardOut | Assert-Equals -Expected $expected
217         $actual.StandardError | Assert-Equals -Expected ""
218         $actual.ExitCode | Assert-Equals -Expected 0
219     }
220 
221     "CreateProcess with unicode and us-ascii encoding" = {
222         $poop = [System.Char]::ConvertFromUtf32(0xE05A)  # Coverage breaks due to script parsing encoding issues with unicode chars, just use the code point instead
223         $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, "cmd.exe /c echo $poop café", $null, $null, '', 'us-ascii')
224         $actual.StandardOut | Assert-Equals -Expected "??? caf??`r`n"
225         $actual.StandardError | Assert-Equals -Expected ""
226         $actual.ExitCode | Assert-Equals -Expected 0
227     }
228 }
229 
230 foreach ($test_impl in $tests.GetEnumerator()) {
231     $test = $test_impl.Key
232     &$test_impl.Value
233 }
234 
235 $module.Result.data = "success"
236 $module.ExitJson()
237