Skip to content

Commit ccdac27

Browse files
Add tests (#44)
1 parent 7e1a7ce commit ccdac27

File tree

11 files changed

+350
-10
lines changed

11 files changed

+350
-10
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,9 @@ jobs:
4242
with:
4343
name: ILAssembler-${{ matrix.os }}
4444
path: ./Release/ILAssembler
45+
- name: Upload Test Results
46+
if: always()
47+
uses: actions/upload-artifact@v2
48+
with:
49+
name: Unit Test Results (${{ matrix.os }})
50+
path: ./TestResults/Pester.xml

.github/workflows/publish.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,9 @@ jobs:
2626
run: ./build.ps1 -Force -Publish
2727
env:
2828
GALLERY_API_KEY: ${{ secrets.GALLERY_API_KEY }}
29+
- name: Upload Test Results
30+
if: always()
31+
uses: actions/upload-artifact@v2
32+
with:
33+
name: Unit Test Results
34+
path: ./TestResults/Pester.xml

ILAssembler.build.ps1

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ param(
1111

1212
$ModuleName = 'ILAssembler'
1313
$DesktopFramework = 'net471'
14-
$CoreFramework = 'net5'
14+
$CoreFramework = 'netcoreapp3.1'
1515

1616
$FailOnError = @{
1717
ErrorAction = [System.Management.Automation.ActionPreference]::Stop
@@ -84,6 +84,46 @@ task CopyToRelease {
8484
}
8585
}
8686

87+
task DoTest {
88+
$testResultsFolder = "$PSScriptRoot/TestResults"
89+
$testResultsFile = "$testResultsFolder/Pester.xml"
90+
if (-not (Test-Path $testResultsFolder)) {
91+
$null = New-Item $testResultsFolder -ItemType Directory -ErrorAction Stop
92+
}
93+
94+
if (Test-Path $testResultsFile) {
95+
Remove-Item $testResultsFile -ErrorAction Stop
96+
}
97+
98+
$pwsh = [Environment]::GetCommandLineArgs()[0] -replace '\.dll$', ''
99+
100+
$arguments = @(
101+
'-NoProfile'
102+
'-NonInteractive'
103+
if (-not $IsUnix) {
104+
'-ExecutionPolicy', 'Bypass'
105+
})
106+
107+
# I know this parameter set is deprecated, but it was taking too much
108+
# fiddling to use the new stuff.
109+
$command = {
110+
Import-Module Pester -RequiredVersion 5.0.4
111+
$results = Invoke-Pester -OutputFormat NUnitXml -OutputFile '{0}' -WarningAction Ignore -PassThru
112+
if ($results.Failed) {{
113+
[Environment]::Exit(1)
114+
}}
115+
} -f $testResultsFile
116+
117+
$encodedCommand = [convert]::ToBase64String(
118+
[Text.Encoding]::Unicode.GetBytes($command))
119+
120+
& $pwsh @arguments -OutputFormat Text -EncodedCommand $encodedCommand
121+
122+
if ($LASTEXITCODE -ne 0) {
123+
throw 'Pester test failed!'
124+
}
125+
}
126+
87127
task DoInstall {
88128
$installBase = $Home
89129
if ($profile) {
@@ -117,8 +157,10 @@ task DoPublish {
117157

118158
task Build -Jobs Clean, BuildManaged, CopyToRelease, BuildMaml
119159

120-
task Install -Jobs Build, DoInstall
160+
task Test -Jobs Build, DoTest
161+
162+
task Install -Jobs Build, DoTest, DoInstall
121163

122-
task Publish -Jobs Build, DoPublish
164+
task Publish -Jobs Build, DoTest, DoPublish
123165

124166
task . Build

build.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ end {
4747
if ($Publish) {
4848
$ibTask = 'Publish'
4949
} else {
50-
$ibTask = 'Build'
50+
$ibTask = 'Test'
5151
}
5252

5353
$invokeBuildSplat = @{

module/ILAssembler.psm1

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# .ExternalHelp ILAssembler-help.xml
22

3-
if ($PSVersionTable.PSVersion.Major -eq 5) {
4-
Microsoft.PowerShell.Core\Import-Module $PSScriptRoot\Desktop\ILAssembler.dll -ErrorAction Stop
3+
if (-not ('ILAssembler.Commands.NewIlDelegateCommand' -as [type])) {
4+
if ($PSVersionTable.PSVersion.Major -eq 5) {
5+
Microsoft.PowerShell.Core\Import-Module $PSScriptRoot\Desktop\ILAssembler.dll -ErrorAction Stop
6+
} else {
7+
Microsoft.PowerShell.Core\Import-Module $PSScriptRoot\Core\ILAssembler.dll -ErrorAction Stop
8+
}
59
} else {
6-
Microsoft.PowerShell.Core\Import-Module $PSScriptRoot\Core\ILAssembler.dll -ErrorAction Stop
10+
Microsoft.PowerShell.Core\Import-Module -Force -Assembly ([ILAssembler.Commands.NewIlDelegateCommand].Assembly)
711
}
812

913
$functionDrive = 'Microsoft.PowerShell.Core\Function'

src/ILAssembler/Commands/NewIlDelegateCommand.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace ILAssembler.Commands
1010
{
1111
[Cmdlet(VerbsCommon.New, "IlDelegate", DefaultParameterSetName = ByAstSignatureSet)]
1212
[Alias("il")]
13+
[OutputType(typeof(MulticastDelegate), typeof(Action))]
1314
[EditorBrowsable(EditorBrowsableState.Never)]
1415
public sealed class NewIlDelegateCommand : PSCmdlet
1516
{

src/ILAssembler/ILAssembler.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net5;net471</TargetFrameworks>
4+
<TargetFrameworks>netcoreapp3.1;net471</TargetFrameworks>
55
<LangVersion>Preview</LangVersion>
66
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
77
<Nullable>enable</Nullable>
88
</PropertyGroup>
99

10-
<PropertyGroup Condition=" '$(TargetFramework)' == 'net5' ">
10+
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
1111
<DefineConstants>$(DefineConstants);CORE</DefineConstants>
1212
</PropertyGroup>
1313

src/ILAssembler/OpCodes/CalliOpCodeInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public override void Emit(CilAssemblyContext context, in InstructionArguments ar
110110
|| callingConvention.Value == SignatureCallingConvention.VarArgs;
111111
var parser = new MethodSignatureParser(
112112
rejectCtor: true,
113-
requireResolvableDeclaringType: isManaged);
113+
requireResolvableDeclaringType: false);
114114

115115
scriptBlockExpression.ScriptBlock.Visit(parser);
116116
signature = (MethodIdentifier)parser.GetMemberIdentifier(scriptBlockExpression.Extent);

tests/Common.ps1

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
if (-not (Get-Module ILAssembler -ea Ignore)) {
2+
$projectBase = $PSScriptRoot | Split-Path
3+
$psd1File = (Get-ChildItem $projectBase/Release/ILAssembler/*/ILAssembler.psd1).FullName
4+
5+
Import-Module $psd1File -Global
6+
}
7+
8+
function GetBodyAsShouldOperator {
9+
param(
10+
[Parameter(ValueFromPipeline)]
11+
[System.Delegate] $Delegate
12+
)
13+
process {
14+
$bytes = $Delegate | GetResolver | GetResolverField m_code
15+
'| Should -HaveBody {0}' -f (
16+
$(
17+
if ($bytes.Length -gt 1) { '('}
18+
$bytes.ForEach{ '0x{0:X2}' -f $PSItem } -join ', '
19+
if ($bytes.Length -gt 1) { ')'}
20+
) -join '')
21+
}
22+
}
23+
24+
function GetResolver {
25+
param(
26+
[Parameter(ValueFromPipeline)]
27+
[Delegate] $Delegate
28+
)
29+
process {
30+
$dynamicMethod = $Delegate.Method.GetType().
31+
GetField('m_owner', 60).
32+
GetValue($Delegate.Method)
33+
34+
$dynamicMethod.GetType().
35+
GetField('m_resolver', 60).
36+
GetValue($dynamicMethod)
37+
}
38+
}
39+
40+
function GetResolverField {
41+
param(
42+
[string] $FieldName,
43+
44+
[Parameter(ValueFromPipeline)]
45+
[object] $Resolver
46+
)
47+
process {
48+
,$Resolver.GetType().
49+
GetField($FieldName, 60).
50+
GetValue($Resolver)
51+
}
52+
}
53+
54+
function ShouldHaveBody {
55+
param(
56+
$ActualValue,
57+
$ExpectedValue
58+
)
59+
end {
60+
61+
function GetFailureMessage {
62+
param([string] $Reason)
63+
$failureMessage = "Expected method body to match, but $Reason." +
64+
[System.Environment]::NewLine + [System.Environment]::NewLine +
65+
'Expected: {0}' + [System.Environment]::NewLine +
66+
'Actual: {1}'
67+
68+
return $failureMessage -f (
69+
($ExpectedValue.ForEach{ '0x{0:X2}' -f $PSItem } -join ', '),
70+
($code.ForEach{ '0x{0:X2}' -f $PSItem } -join ', '))
71+
}
72+
73+
$ExpectedValue = [byte[]]$ExpectedValue
74+
$code = [byte[]]($ActualValue | GetResolver | GetResolverField m_code)
75+
76+
if ($ExpectedValue.Length -ne $code.Length) {
77+
return [PSCustomObject]@{
78+
Succeeded = $false
79+
FailureMessage = GetFailureMessage 'the byte count did not match'
80+
}
81+
}
82+
83+
for ($i = 0; $i -lt $code.Length; $i++) {
84+
if ($ExpectedValue[$i] -ne $code[$i]) {
85+
return [PSCustomObject]@{
86+
Succeeded = $false
87+
FailureMessage = GetFailureMessage "the byte at offset $i did not match"
88+
}
89+
}
90+
}
91+
92+
return [PSCustomObject]@{
93+
Succeeded = $true
94+
FailureMessage = [string]::Empty
95+
}
96+
}
97+
}
98+
99+
$pesterModule = Get-Module Pester -ErrorAction Ignore
100+
if (-not $pesterModule) {
101+
$pesterModule = Import-Module Pester -ErrorAction Stop -PassThru -Global
102+
}
103+
104+
if (-not (& $pesterModule { $AssertionOperators.ContainsKey('HaveBody') })) {
105+
Add-ShouldOperator -Name HaveBody -Test $function:ShouldHaveBody
106+
}

tests/DelegateSignatures.tests.ps1

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
Describe 'Delegate signatures' {
2+
BeforeAll {
3+
. "$($PSCommandPath | Split-Path)/Common.ps1"
4+
}
5+
6+
It 'Array expression for no parameters' {
7+
$d = il { [int]@() } {
8+
ldc.i4.0
9+
ret
10+
}
11+
12+
$d | Should -BeOfType ([Func[int]])
13+
}
14+
15+
It 'Parameters in paren expression' {
16+
$d = il { [int]([int], [string]) } {
17+
ldc.i4.0
18+
ret
19+
}
20+
21+
$d | Should -BeOfType ([Func[int, string, int]])
22+
}
23+
24+
It 'Parameters in array expression' {
25+
$d = il { [int]@([object], [string]) } {
26+
ldc.i4.0
27+
ret
28+
}
29+
30+
$d | Should -BeOfType ([Func[object, string, int]])
31+
}
32+
33+
It 'Named parameters' {
34+
$d = il { [int]([object] $instance, [string] $name) } {
35+
ldc.i4.0
36+
ret
37+
}
38+
39+
$d | Should -BeOfType ([Func[object, string, int]])
40+
}
41+
42+
It 'Invoke member expression syntax' {
43+
$d = il { [int] $_._([object] $instance, [string] $name) } {
44+
ldc.i4.0
45+
ret
46+
}
47+
48+
$d | Should -BeOfType ([Func[object, string, int]])
49+
}
50+
51+
It 'Explicit delegate type' {
52+
$d = il ([Action[int]]) {
53+
ret
54+
}
55+
56+
$d | Should -BeOfType ([Action[int]])
57+
}
58+
59+
It 'Explicit non-standard delegate type' {
60+
$d = New-IlDelegate ([System.ResolveEventHandler]) {
61+
ret
62+
}
63+
64+
$d | Should -BeOfType ([System.ResolveEventHandler])
65+
$method = $d.GetType().GetMethod('Invoke')
66+
$method.ReturnType | Should -Be ([System.Reflection.Assembly])
67+
$method.GetParameters().ParameterType | Should -Be ([object], [System.ResolveEventArgs])
68+
}
69+
70+
It 'Generates delegate type when parameters cannot be generics' {
71+
$d = il { [bool]([int], [ref] [string], [int+]) } {
72+
ldc.i4.0
73+
ret
74+
}
75+
76+
$method = $d.GetType().GetMethod('Invoke')
77+
$method.ReturnType | Should -Be ([bool])
78+
$method.GetParameters().ParameterType | Should -Be (
79+
[int],
80+
[string].MakeByRefType(),
81+
[int].MakePointerType())
82+
}
83+
}

0 commit comments

Comments
 (0)