Skip to content

Commit

Permalink
Add tests (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
SeeminglyScience authored Oct 18, 2020
1 parent 7e1a7ce commit ccdac27
Show file tree
Hide file tree
Showing 11 changed files with 350 additions and 10 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,9 @@ jobs:
with:
name: ILAssembler-${{ matrix.os }}
path: ./Release/ILAssembler
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v2
with:
name: Unit Test Results (${{ matrix.os }})
path: ./TestResults/Pester.xml
6 changes: 6 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@ jobs:
run: ./build.ps1 -Force -Publish
env:
GALLERY_API_KEY: ${{ secrets.GALLERY_API_KEY }}
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v2
with:
name: Unit Test Results
path: ./TestResults/Pester.xml
48 changes: 45 additions & 3 deletions ILAssembler.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ param(

$ModuleName = 'ILAssembler'
$DesktopFramework = 'net471'
$CoreFramework = 'net5'
$CoreFramework = 'netcoreapp3.1'

$FailOnError = @{
ErrorAction = [System.Management.Automation.ActionPreference]::Stop
Expand Down Expand Up @@ -84,6 +84,46 @@ task CopyToRelease {
}
}

task DoTest {
$testResultsFolder = "$PSScriptRoot/TestResults"
$testResultsFile = "$testResultsFolder/Pester.xml"
if (-not (Test-Path $testResultsFolder)) {
$null = New-Item $testResultsFolder -ItemType Directory -ErrorAction Stop
}

if (Test-Path $testResultsFile) {
Remove-Item $testResultsFile -ErrorAction Stop
}

$pwsh = [Environment]::GetCommandLineArgs()[0] -replace '\.dll$', ''

$arguments = @(
'-NoProfile'
'-NonInteractive'
if (-not $IsUnix) {
'-ExecutionPolicy', 'Bypass'
})

# I know this parameter set is deprecated, but it was taking too much
# fiddling to use the new stuff.
$command = {
Import-Module Pester -RequiredVersion 5.0.4
$results = Invoke-Pester -OutputFormat NUnitXml -OutputFile '{0}' -WarningAction Ignore -PassThru
if ($results.Failed) {{
[Environment]::Exit(1)
}}
} -f $testResultsFile

$encodedCommand = [convert]::ToBase64String(
[Text.Encoding]::Unicode.GetBytes($command))

& $pwsh @arguments -OutputFormat Text -EncodedCommand $encodedCommand

if ($LASTEXITCODE -ne 0) {
throw 'Pester test failed!'
}
}

task DoInstall {
$installBase = $Home
if ($profile) {
Expand Down Expand Up @@ -117,8 +157,10 @@ task DoPublish {

task Build -Jobs Clean, BuildManaged, CopyToRelease, BuildMaml

task Install -Jobs Build, DoInstall
task Test -Jobs Build, DoTest

task Install -Jobs Build, DoTest, DoInstall

task Publish -Jobs Build, DoPublish
task Publish -Jobs Build, DoTest, DoPublish

task . Build
2 changes: 1 addition & 1 deletion build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ end {
if ($Publish) {
$ibTask = 'Publish'
} else {
$ibTask = 'Build'
$ibTask = 'Test'
}

$invokeBuildSplat = @{
Expand Down
10 changes: 7 additions & 3 deletions module/ILAssembler.psm1
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# .ExternalHelp ILAssembler-help.xml

if ($PSVersionTable.PSVersion.Major -eq 5) {
Microsoft.PowerShell.Core\Import-Module $PSScriptRoot\Desktop\ILAssembler.dll -ErrorAction Stop
if (-not ('ILAssembler.Commands.NewIlDelegateCommand' -as [type])) {
if ($PSVersionTable.PSVersion.Major -eq 5) {
Microsoft.PowerShell.Core\Import-Module $PSScriptRoot\Desktop\ILAssembler.dll -ErrorAction Stop
} else {
Microsoft.PowerShell.Core\Import-Module $PSScriptRoot\Core\ILAssembler.dll -ErrorAction Stop
}
} else {
Microsoft.PowerShell.Core\Import-Module $PSScriptRoot\Core\ILAssembler.dll -ErrorAction Stop
Microsoft.PowerShell.Core\Import-Module -Force -Assembly ([ILAssembler.Commands.NewIlDelegateCommand].Assembly)
}

$functionDrive = 'Microsoft.PowerShell.Core\Function'
Expand Down
1 change: 1 addition & 0 deletions src/ILAssembler/Commands/NewIlDelegateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace ILAssembler.Commands
{
[Cmdlet(VerbsCommon.New, "IlDelegate", DefaultParameterSetName = ByAstSignatureSet)]
[Alias("il")]
[OutputType(typeof(MulticastDelegate), typeof(Action))]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class NewIlDelegateCommand : PSCmdlet
{
Expand Down
4 changes: 2 additions & 2 deletions src/ILAssembler/ILAssembler.csproj
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net5;net471</TargetFrameworks>
<TargetFrameworks>netcoreapp3.1;net471</TargetFrameworks>
<LangVersion>Preview</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup Condition=" '$(TargetFramework)' == 'net5' ">
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
<DefineConstants>$(DefineConstants);CORE</DefineConstants>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion src/ILAssembler/OpCodes/CalliOpCodeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public override void Emit(CilAssemblyContext context, in InstructionArguments ar
|| callingConvention.Value == SignatureCallingConvention.VarArgs;
var parser = new MethodSignatureParser(
rejectCtor: true,
requireResolvableDeclaringType: isManaged);
requireResolvableDeclaringType: false);

scriptBlockExpression.ScriptBlock.Visit(parser);
signature = (MethodIdentifier)parser.GetMemberIdentifier(scriptBlockExpression.Extent);
Expand Down
106 changes: 106 additions & 0 deletions tests/Common.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
if (-not (Get-Module ILAssembler -ea Ignore)) {
$projectBase = $PSScriptRoot | Split-Path
$psd1File = (Get-ChildItem $projectBase/Release/ILAssembler/*/ILAssembler.psd1).FullName

Import-Module $psd1File -Global
}

function GetBodyAsShouldOperator {
param(
[Parameter(ValueFromPipeline)]
[System.Delegate] $Delegate
)
process {
$bytes = $Delegate | GetResolver | GetResolverField m_code
'| Should -HaveBody {0}' -f (
$(
if ($bytes.Length -gt 1) { '('}
$bytes.ForEach{ '0x{0:X2}' -f $PSItem } -join ', '
if ($bytes.Length -gt 1) { ')'}
) -join '')
}
}

function GetResolver {
param(
[Parameter(ValueFromPipeline)]
[Delegate] $Delegate
)
process {
$dynamicMethod = $Delegate.Method.GetType().
GetField('m_owner', 60).
GetValue($Delegate.Method)

$dynamicMethod.GetType().
GetField('m_resolver', 60).
GetValue($dynamicMethod)
}
}

function GetResolverField {
param(
[string] $FieldName,

[Parameter(ValueFromPipeline)]
[object] $Resolver
)
process {
,$Resolver.GetType().
GetField($FieldName, 60).
GetValue($Resolver)
}
}

function ShouldHaveBody {
param(
$ActualValue,
$ExpectedValue
)
end {

function GetFailureMessage {
param([string] $Reason)
$failureMessage = "Expected method body to match, but $Reason." +
[System.Environment]::NewLine + [System.Environment]::NewLine +
'Expected: {0}' + [System.Environment]::NewLine +
'Actual: {1}'

return $failureMessage -f (
($ExpectedValue.ForEach{ '0x{0:X2}' -f $PSItem } -join ', '),
($code.ForEach{ '0x{0:X2}' -f $PSItem } -join ', '))
}

$ExpectedValue = [byte[]]$ExpectedValue
$code = [byte[]]($ActualValue | GetResolver | GetResolverField m_code)

if ($ExpectedValue.Length -ne $code.Length) {
return [PSCustomObject]@{
Succeeded = $false
FailureMessage = GetFailureMessage 'the byte count did not match'
}
}

for ($i = 0; $i -lt $code.Length; $i++) {
if ($ExpectedValue[$i] -ne $code[$i]) {
return [PSCustomObject]@{
Succeeded = $false
FailureMessage = GetFailureMessage "the byte at offset $i did not match"
}
}
}

return [PSCustomObject]@{
Succeeded = $true
FailureMessage = [string]::Empty
}
}
}

$pesterModule = Get-Module Pester -ErrorAction Ignore
if (-not $pesterModule) {
$pesterModule = Import-Module Pester -ErrorAction Stop -PassThru -Global
}

if (-not (& $pesterModule { $AssertionOperators.ContainsKey('HaveBody') })) {
Add-ShouldOperator -Name HaveBody -Test $function:ShouldHaveBody
}
83 changes: 83 additions & 0 deletions tests/DelegateSignatures.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
Describe 'Delegate signatures' {
BeforeAll {
. "$($PSCommandPath | Split-Path)/Common.ps1"
}

It 'Array expression for no parameters' {
$d = il { [int]@() } {
ldc.i4.0
ret
}

$d | Should -BeOfType ([Func[int]])
}

It 'Parameters in paren expression' {
$d = il { [int]([int], [string]) } {
ldc.i4.0
ret
}

$d | Should -BeOfType ([Func[int, string, int]])
}

It 'Parameters in array expression' {
$d = il { [int]@([object], [string]) } {
ldc.i4.0
ret
}

$d | Should -BeOfType ([Func[object, string, int]])
}

It 'Named parameters' {
$d = il { [int]([object] $instance, [string] $name) } {
ldc.i4.0
ret
}

$d | Should -BeOfType ([Func[object, string, int]])
}

It 'Invoke member expression syntax' {
$d = il { [int] $_._([object] $instance, [string] $name) } {
ldc.i4.0
ret
}

$d | Should -BeOfType ([Func[object, string, int]])
}

It 'Explicit delegate type' {
$d = il ([Action[int]]) {
ret
}

$d | Should -BeOfType ([Action[int]])
}

It 'Explicit non-standard delegate type' {
$d = New-IlDelegate ([System.ResolveEventHandler]) {
ret
}

$d | Should -BeOfType ([System.ResolveEventHandler])
$method = $d.GetType().GetMethod('Invoke')
$method.ReturnType | Should -Be ([System.Reflection.Assembly])
$method.GetParameters().ParameterType | Should -Be ([object], [System.ResolveEventArgs])
}

It 'Generates delegate type when parameters cannot be generics' {
$d = il { [bool]([int], [ref] [string], [int+]) } {
ldc.i4.0
ret
}

$method = $d.GetType().GetMethod('Invoke')
$method.ReturnType | Should -Be ([bool])
$method.GetParameters().ParameterType | Should -Be (
[int],
[string].MakeByRefType(),
[int].MakePointerType())
}
}
Loading

0 comments on commit ccdac27

Please sign in to comment.