diff --git a/build.psm1 b/build.psm1 index d770d53e530..9c97a4e65ef 100644 --- a/build.psm1 +++ b/build.psm1 @@ -1188,6 +1188,53 @@ function Show-PSPesterError } +function Test-XUnitTestResults +{ + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $TestResultsFile + ) + + if(-not (Test-Path $TestResultsFile)) + { + throw "File not found $TestResultsFile" + } + + try + { + $results = [xml] (Get-Content $TestResultsFile) + } + catch + { + throw "Cannot convert $TestResultsFile to xml : $($_.message)" + } + + $failedTests = $results.assemblies.assembly.collection | Where-Object failed -gt 0 + + if(-not $failedTests) + { + return $true + } + + foreach($failure in $failedTests) + { + $description = $failure.test.type + $name = $failure.test.method + $message = $failure.test.failure.message.'#cdata-section' + $stackTrace = $failure.test.failure.'stack-trace'.'#cdata-section' + + logerror ("Description: " + $description) + logerror ("Name: " + $name) + logerror "message:" + logerror $message + logerror "stack-trace:" + logerror $stackTrace + } + + throw "$($failedTests.failed) tests failed" +} + # # Read the test result file and # Throw if a test failed @@ -1251,46 +1298,67 @@ function Test-PSPesterResults function Start-PSxUnit { - [CmdletBinding()]param() - - log "xUnit tests are currently disabled pending fixes due to API and AssemblyLoadContext changes - @andschwa" - return - - if ($Environment.IsWindows) { - throw "xUnit tests are only currently supported on Linux / OS X" - } - - if ($Environment.IsMacOS) { - log "Not yet supported on OS X, pretending they passed..." - return - } + [CmdletBinding()]param( + [string] $TestResultsFile = "XUnitResults.xml" + ) # Add .NET CLI tools to PATH Find-Dotnet - $Arguments = "--configuration", "Linux", "-parallel", "none" - if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) { - $Arguments += "-verbose" - } - $Content = Split-Path -Parent (Get-PSOutput) if (-not (Test-Path $Content)) { throw "PowerShell must be built before running tests!" } + if(Test-Path $TestResultsFile) + { + Remove-Item $TestResultsFile -Force -ErrorAction SilentlyContinue + } + try { Push-Location $PSScriptRoot/test/csharp + # Path manipulation to obtain test project output directory - $Output = Join-Path $pwd ((Split-Path -Parent (Get-PSOutput)) -replace (New-PSOptions).Top) - Write-Verbose "Output is $Output" + dotnet restore - Copy-Item -ErrorAction SilentlyContinue -Recurse -Path $Content/* -Include Modules,libpsl-native* -Destination $Output - Start-NativeExecution { dotnet test $Arguments } + # --fx-version workaround required due to https://github.com/dotnet/cli/issues/7901#issuecomment-343323674 + if($Environment.IsWindows) + { + dotnet xunit --fx-version 2.0.0 -xml $TestResultsFile + } + else + { + if($Environment.IsMacOS) + { + $nativeLib = "$Content/libpsl-native.dylib" + } + else + { + $nativeLib = "$Content/libpsl-native.so" + } + + $requiredDependencies = @( + $nativeLib, + "$Content/Microsoft.Management.Infrastructure.dll", + "$Content/System.Text.Encoding.CodePages.dll" + ) + + if((Test-Path $requiredDependencies) -notcontains $false) + { + $options = New-PSOptions + $Destination = "bin/$($options.configuration)/$($options.framework)" + New-Item $Destination -ItemType Directory -Force > $null + Copy-Item -Path $requiredDependencies -Destination $Destination -Force + } + else + { + throw "Dependencies $requiredDependencies not met." + } - if ($LASTEXITCODE -ne 0) { - throw "$LASTEXITCODE xUnit tests failed" + dotnet xunit --fx-version 2.0.0 -configuration $Options.configuration -xml $TestResultsFile } - } finally { + } + finally { Pop-Location } } diff --git a/test/csharp/csharp.tests.csproj b/test/csharp/csharp.tests.csproj index 03eb9cf93af..6ecf16d2a44 100644 --- a/test/csharp/csharp.tests.csproj +++ b/test/csharp/csharp.tests.csproj @@ -1,21 +1,30 @@  - + - PowerShell On Linux xUnit Tests + PowerShell xUnit Tests powershell-tests win7-x86;win7-x64;osx.10.12-x64;linux-x64 + + true + ../../src/signing/visualstudiopublic.snk + true + + - + + + - - - + + + + diff --git a/test/csharp/fixture_AssemblyLoadContext.cs b/test/csharp/fixture_AssemblyLoadContext.cs deleted file mode 100644 index b24f41fc269..00000000000 --- a/test/csharp/fixture_AssemblyLoadContext.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Xunit; -using System; -using System.Management.Automation; - -// This collection fixture initializes Core PowerShell's AssemblyLoadContext once and only -// once. Attempting to initialize in a class level fixture will cause multiple -// initializations, resulting in test failure due to "Binding model is already locked for -// the AppDomain and cannot be reset". -namespace PSTests -{ - public class AssemblyLoadContextFixture - { - public AssemblyLoadContextFixture() - { - // Initialize the Core PowerShell AssemblyLoadContext - PowerShellAssemblyLoadContextInitializer.SetPowerShellAssemblyLoadContext(AppContext.BaseDirectory); - } - } - - [CollectionDefinition("AssemblyLoadContext")] - public class AssemblyLoadContextCollection : ICollectionFixture - { - // nothing to do but satisfy the interface - } -} diff --git a/test/csharp/test_Binders.cs b/test/csharp/test_Binders.cs index 91dc76d25b5..e73d6b44ca4 100644 --- a/test/csharp/test_Binders.cs +++ b/test/csharp/test_Binders.cs @@ -4,14 +4,13 @@ namespace PSTests { - [Collection("AssemblyLoadContext")] public static class PSEnumerableBinderTests { [Fact] - public static void TestIsComObject() + public static void TestIsStaticTypePossiblyEnumerable() { - // It just needs an arbitrary object - Assert.False(PSEnumerableBinder.IsComObject(42)); + // It just needs an arbitrary type + Assert.False(PSEnumerableBinder.IsStaticTypePossiblyEnumerable(42.GetType())); } } } diff --git a/test/csharp/test_CorePsPlatform.cs b/test/csharp/test_CorePsPlatform.cs index 33579d9ed41..11a744b4046 100644 --- a/test/csharp/test_CorePsPlatform.cs +++ b/test/csharp/test_CorePsPlatform.cs @@ -6,7 +6,6 @@ namespace PSTests { - [Collection("AssemblyLoadContext")] public static class PlatformTests { [Fact] @@ -15,6 +14,7 @@ public static void TestIsCoreCLR() Assert.True(Platform.IsCoreCLR); } +#if Unix [Fact] public static void TestGetUserName() { @@ -38,7 +38,7 @@ public static void TestGetUserName() } } - [Fact(Skip="Bad arguments for macOS")] + [Fact] public static void TestGetMachineName() { var startInfo = new ProcessStartInfo @@ -61,7 +61,7 @@ public static void TestGetMachineName() } } - [Fact(Skip="Bad arguments for macOS")] + [Fact] public static void TestGetFQDN() { var startInfo = new ProcessStartInfo @@ -84,7 +84,7 @@ public static void TestGetFQDN() } } - [Fact(Skip="Bad arguments for macOS")] + [Fact] public static void TestGetDomainName() { var startInfo = new ProcessStartInfo @@ -255,5 +255,6 @@ public static void TestFileIsSymLink() File.Delete(path); File.Delete(link); } +#endif } } diff --git a/test/csharp/test_ExtensionMethods.cs b/test/csharp/test_ExtensionMethods.cs index 134f94b2811..0b2273fb3d2 100644 --- a/test/csharp/test_ExtensionMethods.cs +++ b/test/csharp/test_ExtensionMethods.cs @@ -4,14 +4,12 @@ namespace PSTests { - [Collection("AssemblyLoadContext")] public static class PSTypeExtensionsTests { [Fact] - public static void TestIsComObject() + public static void TestIsNumeric() { - // It just needs an arbitrary type - Assert.False(PSTypeExtensions.IsComObject(42.GetType())); + Assert.True(PSTypeExtensions.IsNumeric(42.GetType())); } } } diff --git a/test/csharp/test_FileSystemProvider.cs b/test/csharp/test_FileSystemProvider.cs index 5ad95a6daf5..76d52137478 100644 --- a/test/csharp/test_FileSystemProvider.cs +++ b/test/csharp/test_FileSystemProvider.cs @@ -13,10 +13,10 @@ using System.Management.Automation.Runspaces; using Microsoft.PowerShell; using Microsoft.PowerShell.Commands; +using System.Reflection; namespace PSTests { - [Collection("AssemblyLoadContext")] public class FileSystemProviderTests: IDisposable { private string testPath; @@ -37,9 +37,8 @@ private ExecutionContext GetExecutionContext() { CultureInfo currentCulture = CultureInfo.CurrentCulture; PSHost hostInterface = new DefaultHost(currentCulture,currentCulture); - RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create(); InitialSessionState iss = InitialSessionState.CreateDefault2(); - AutomationEngine engine = new AutomationEngine(hostInterface, runspaceConfiguration, iss); + AutomationEngine engine = new AutomationEngine(hostInterface, iss); ExecutionContext executionContext = new ExecutionContext(engine, hostInterface, iss); return executionContext; } @@ -58,7 +57,14 @@ private ProviderInfo GetProvider() [Fact] public void TestCreateJunctionFails() { - Assert.False(InternalSymbolicLinkLinkCodeMethods.CreateJunction("","")); + if(!Platform.IsWindows) + { + Assert.False(InternalSymbolicLinkLinkCodeMethods.CreateJunction("","")); + } + else + { + Assert.Throws(delegate { InternalSymbolicLinkLinkCodeMethods.CreateJunction("",""); }); + } } [Fact] @@ -74,12 +80,26 @@ public void TestGetHelpMaml() public void TestMode() { Assert.Equal(FileSystemProvider.Mode(null),String.Empty); - FileSystemInfo directoryObject = new DirectoryInfo(@"/"); - FileSystemInfo fileObject = new FileInfo(@"/etc/hosts"); - FileSystemInfo executableObject = new FileInfo(@"/bin/echo"); - Assert.Equal(FileSystemProvider.Mode(PSObject.AsPSObject(directoryObject)).Replace("r","-"),"d-----"); - Assert.Equal(FileSystemProvider.Mode(PSObject.AsPSObject(fileObject)).Replace("r","-"),"------"); - Assert.Equal(FileSystemProvider.Mode(PSObject.AsPSObject(executableObject)).Replace("r","-"),"------"); + FileSystemInfo directoryObject = null; + FileSystemInfo fileObject = null; + FileSystemInfo executableObject = null; + + if(!Platform.IsWindows) + { + directoryObject = new DirectoryInfo(@"/"); + fileObject = new FileInfo(@"/etc/hosts"); + executableObject = new FileInfo(@"/bin/echo"); + } + else + { + directoryObject = new DirectoryInfo(System.Environment.CurrentDirectory); + fileObject = new FileInfo(System.Reflection.Assembly.GetEntryAssembly().Location); + executableObject = new FileInfo(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); + } + + Assert.Equal("d-----", FileSystemProvider.Mode(PSObject.AsPSObject(directoryObject)).Replace("r","-")); + Assert.Equal("------", FileSystemProvider.Mode(PSObject.AsPSObject(fileObject)).Replace("r","-").Replace("a","-")); + Assert.Equal("------", FileSystemProvider.Mode(PSObject.AsPSObject(executableObject)).Replace("r","-").Replace("a","-")); } [Fact] @@ -99,7 +119,7 @@ public void TestGetProperty() { if(property.Name == "IsReadOnly") { - Assert.Equal(property.Value,false); + Assert.False((bool)property.Value); } } } @@ -118,7 +138,7 @@ public void TestSetProperty() { if(property.Name == "Name") { - Assert.Equal(property.Value,"test"); + Assert.Equal("test", property.Value); } } } diff --git a/test/csharp/test_MshSnapinInfo.cs b/test/csharp/test_MshSnapinInfo.cs index ac755ec5afa..454a4ec0a60 100644 --- a/test/csharp/test_MshSnapinInfo.cs +++ b/test/csharp/test_MshSnapinInfo.cs @@ -5,22 +5,23 @@ namespace PSTests { // Not static because a test requires non-const variables - [Collection("AssemblyLoadContext")] public class MshSnapinInfoTests { // Test that it does not throw an exception - [Fact] + [SkippableFact] public void TestReadRegistryInfo() { + Skip.IfNot(Platform.IsWindows); Version someVersion = null; string someString = null; PSSnapInReader.ReadRegistryInfo(out someVersion, out someString, out someString, out someString, out someString, out someVersion); } // PublicKeyToken is null on Linux - [Fact] + [SkippableFact] public void TestReadCoreEngineSnapIn() { + Skip.IfNot(Platform.IsWindows); PSSnapInInfo pSSnapInInfo = PSSnapInReader.ReadCoreEngineSnapIn(); Assert.Contains("PublicKeyToken=31bf3856ad364e35", pSSnapInInfo.AssemblyName); } diff --git a/test/csharp/test_PSVersionInfo.cs b/test/csharp/test_PSVersionInfo.cs index 5bd7acb82e8..ef980475ebf 100644 --- a/test/csharp/test_PSVersionInfo.cs +++ b/test/csharp/test_PSVersionInfo.cs @@ -4,7 +4,6 @@ namespace PSTests { - [Collection("AssemblyLoadContext")] public static class PSVersionInfoTests { [Fact] diff --git a/test/csharp/test_Runspace.cs b/test/csharp/test_Runspace.cs index 9b42341962a..ee7877523ac 100644 --- a/test/csharp/test_Runspace.cs +++ b/test/csharp/test_Runspace.cs @@ -8,11 +8,10 @@ namespace PSTests // NOTE: do not call AddCommand("out-host") after invoking or MergeMyResults, // otherwise Invoke will not return any objects - [Collection("AssemblyLoadContext")] public class RunspaceTests { - private static int count = 3; - private static string script = String.Format($"get-command | select-object -first {count}"); + private static int count = 1; + private static string script = String.Format($"get-command get-command"); [Fact] public void TestRunspaceWithPipeline() @@ -63,7 +62,7 @@ public void TestRunspaceWithPowerShell() } - [Fact(Skip="Fails in Travis CI, need investigation")] + [Fact] public void TestRunspaceWithPowerShellAndInitialSessionState() { InitialSessionState iss = InitialSessionState.CreateDefault2(); @@ -76,11 +75,14 @@ public void TestRunspaceWithPowerShellAndInitialSessionState() using (PowerShell powerShell = PowerShell.Create()) { powerShell.Runspace = runspace; - + powerShell.AddScript("Import-Module Microsoft.PowerShell.Utility -Force"); powerShell.AddScript(script); int objCount = 0; - foreach (var result in powerShell.Invoke()) + + var results = powerShell.Invoke(); + + foreach (var result in results) { // this is how an object would be captured here and looked at, // each result is a PSObject with the data from the pipeline diff --git a/test/csharp/test_SecuritySupport.cs b/test/csharp/test_SecuritySupport.cs index 33c336308cc..aef3ac5ee8a 100644 --- a/test/csharp/test_SecuritySupport.cs +++ b/test/csharp/test_SecuritySupport.cs @@ -4,21 +4,12 @@ namespace PSTests { - [Collection("AssemblyLoadContext")] public static class SecuritySupportTests { [Fact] public static void TestScanContent() { - Assert.Equal(AmsiUtils.ScanContent("", ""), AmsiUtils.AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED); - } - - [Fact] - public static void TestCurrentDomain_ProcessExit() - { - Assert.Throws(delegate { - AmsiUtils.CurrentDomain_ProcessExit(null, EventArgs.Empty); - }); + Assert.Equal(AmsiUtils.AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED, AmsiUtils.ScanContent("", "")); } [Fact] diff --git a/test/csharp/test_SessionState.cs b/test/csharp/test_SessionState.cs index 7558c1b6789..990242f69f1 100644 --- a/test/csharp/test_SessionState.cs +++ b/test/csharp/test_SessionState.cs @@ -12,17 +12,16 @@ namespace PSTests { - [Collection("AssemblyLoadContext")] public class SessionStateTests { - [Fact] + [SkippableFact] public void TestDrives() { + Skip.IfNot(Platform.IsWindows); CultureInfo currentCulture = CultureInfo.CurrentCulture; PSHost hostInterface = new DefaultHost(currentCulture,currentCulture); - RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create(); InitialSessionState iss = InitialSessionState.CreateDefault2(); - AutomationEngine engine = new AutomationEngine(hostInterface, runspaceConfiguration, iss); + AutomationEngine engine = new AutomationEngine(hostInterface, iss); ExecutionContext executionContext = new ExecutionContext(engine, hostInterface, iss); SessionStateInternal sessionState = new SessionStateInternal(executionContext); Collection drives = sessionState.Drives(null); diff --git a/test/csharp/test_Utils.cs b/test/csharp/test_Utils.cs index 65ded34f637..be1c5cfa492 100644 --- a/test/csharp/test_Utils.cs +++ b/test/csharp/test_Utils.cs @@ -1,15 +1,16 @@ using Xunit; using System; using System.Management.Automation; +using System.Reflection; namespace PSTests { - [Collection("AssemblyLoadContext")] public static class UtilsTests { - [Fact] + [SkippableFact] public static void TestIsWinPEHost() { + Skip.IfNot(Platform.IsWindows); Assert.False(Utils.IsWinPEHost()); } } diff --git a/tools/appveyor.psm1 b/tools/appveyor.psm1 index a1401a1bb7e..dd7b069e2cc 100644 --- a/tools/appveyor.psm1 +++ b/tools/appveyor.psm1 @@ -325,6 +325,7 @@ function Invoke-AppVeyorTest Write-Host -Foreground Green 'Run CoreCLR tests' $testResultsNonAdminFile = "$pwd\TestsResultsNonAdmin.xml" $testResultsAdminFile = "$pwd\TestsResultsAdmin.xml" + $testResultsXUnitFile = "$pwd\TestResultsXUnit.xml" if(!(Test-Path "$env:CoreOutput\pwsh.exe")) { throw "CoreCLR pwsh.exe was not built" @@ -358,6 +359,10 @@ function Invoke-AppVeyorTest Write-Host -Foreground Green 'Upload CoreCLR Admin test results' Update-AppVeyorTestResults -resultsFile $testResultsAdminFile + Start-PSxUnit -TestResultsFile $testResultsXUnitFile + Write-Host -ForegroundColor Green 'Uploading PSxUnit test results' + Update-AppVeyorTestResults -resultsFile $testResultsXUnitFile + # # Fail the build, if tests failed @( @@ -367,6 +372,8 @@ function Invoke-AppVeyorTest Test-PSPesterResults -TestResultsFile $_ } + $testPassResult = Test-XUnitTestResults -TestResultsFile $testResultsXUnitFile + Set-BuildVariable -Name TestPassed -Value True } diff --git a/tools/travis.ps1 b/tools/travis.ps1 index 670b012693d..5e4ecf0a437 100644 --- a/tools/travis.ps1 +++ b/tools/travis.ps1 @@ -240,7 +240,10 @@ elseif($Stage -eq 'Build') } try { - Start-PSxUnit + $testResultsXUnitFile = "$pwd/TestResultsXUnit.xml" + Start-PSxUnit -TestResultsFile $testResultsXUnitFile + # If there are failures, Test-XUnitTestResults throws + $testPassResult = Test-XUnitTestResults -TestResultsFile $testResultsXUnitFile } catch { $result = "FAIL"