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/docs/learning-powershell/using-vscode.md b/docs/learning-powershell/using-vscode.md index 0441a53fbab..3623039153d 100644 --- a/docs/learning-powershell/using-vscode.md +++ b/docs/learning-powershell/using-vscode.md @@ -59,13 +59,13 @@ If you wish to use a specific installation of PowerShell with Visual Studio Code ```json // On Windows: - "powershell.powerShellExePath": "c:/Program Files/PowerShell//powershell.exe" + "powershell.powerShellExePath": "c:/Program Files/PowerShell//pwsh.exe" // On Linux: - "powershell.powerShellExePath": "/opt/microsoft/powershell//powershell" + "powershell.powerShellExePath": "/opt/microsoft/powershell//pwsh" // On macOS: - "powershell.powerShellExePath": "/usr/local/microsoft/powershell//powershell" + "powershell.powerShellExePath": "/usr/local/microsoft/powershell//pwsh" ``` 3. Replace the setting with the path to the desired PowerShell executable diff --git a/src/Microsoft.PowerShell.Security/security/CatalogCommands.cs b/src/Microsoft.PowerShell.Security/security/CatalogCommands.cs index 07f2dc9015c..722a8b07072 100644 --- a/src/Microsoft.PowerShell.Security/security/CatalogCommands.cs +++ b/src/Microsoft.PowerShell.Security/security/CatalogCommands.cs @@ -95,7 +95,7 @@ protected override void ProcessRecord() { foreach (PathInfo tempPath in SessionState.Path.GetResolvedPSPathFromPSPath(p)) { - if (ShouldProcess(tempPath.ProviderPath)) + if (ShouldProcess("Including path " + tempPath.ProviderPath, "", "")) { paths.Add(tempPath.ProviderPath); } @@ -103,17 +103,16 @@ protected override void ProcessRecord() } } - // We add 'paths.Count > 0' to support 'ShouldProcess()' - if (paths.Count > 0 ) - { - string drive = null; + string drive = null; - // resolve catalog destination Path - if (!SessionState.Path.IsPSAbsolute(catalogFilePath, out drive) && !System.IO.Path.IsPathRooted(catalogFilePath)) - { - catalogFilePath = SessionState.Path.GetUnresolvedProviderPathFromPSPath(catalogFilePath); - } + // resolve catalog destination Path + if (!SessionState.Path.IsPSAbsolute(catalogFilePath, out drive) && !System.IO.Path.IsPathRooted(catalogFilePath)) + { + catalogFilePath = SessionState.Path.GetUnresolvedProviderPathFromPSPath(catalogFilePath); + } + if (ShouldProcess(catalogFilePath)) + { PerformAction(paths, catalogFilePath); } } diff --git a/src/System.Management.Automation/engine/Attributes.cs b/src/System.Management.Automation/engine/Attributes.cs index 876f428bde8..d96a4284510 100644 --- a/src/System.Management.Automation/engine/Attributes.cs +++ b/src/System.Management.Automation/engine/Attributes.cs @@ -1865,11 +1865,40 @@ public ValidateUserDriveAttribute() #endregion #region NULL validation attributes + + /// + /// Base type of Null Validation attributes. + /// + public abstract class NullValidationAttributeBase : ValidateArgumentsAttribute + { + /// + /// Check if the argument type is a collection. + /// + protected bool IsArgumentCollection(Type argumentType, out bool isElementValueType) + { + isElementValueType = false; + var information = new ParameterCollectionTypeInformation(argumentType); + switch (information.ParameterCollectionType) + { + // If 'arguments' is an array, or implement 'IList', or implement 'ICollection<>' + // then we continue to check each element of the collection. + case ParameterCollectionType.Array: + case ParameterCollectionType.IList: + case ParameterCollectionType.ICollectionGeneric: + Type elementType = information.ElementType; + isElementValueType = elementType != null && elementType.IsValueType; + return true; + default: + return false; + } + } + } + /// /// Validates that the parameters's argument is not null /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public sealed class ValidateNotNullAttribute : ValidateArgumentsAttribute + public sealed class ValidateNotNullAttribute : NullValidationAttributeBase { /// /// Verifies the argument is not null and if it is a collection, that each @@ -1891,9 +1920,6 @@ public sealed class ValidateNotNullAttribute : ValidateArgumentsAttribute /// protected override void Validate(object arguments, EngineIntrinsics engineIntrinsics) { - IEnumerable ienum = null; - IEnumerator itor = null; - if (arguments == null || arguments == AutomationNull.Value) { throw new ValidationMetadataException( @@ -1901,10 +1927,16 @@ protected override void Validate(object arguments, EngineIntrinsics engineIntrin null, Metadata.ValidateNotNullFailure); } - else if ((ienum = arguments as IEnumerable) != null) + else if (IsArgumentCollection(arguments.GetType(), out bool isElementValueType)) { - foreach (object element in ienum) + // If the element of the collection is of value type, then no need to check for null + // because a value-type value cannot be null. + if (isElementValueType) { return; } + + IEnumerator ienum = LanguagePrimitives.GetEnumerator(arguments); + while (ienum.MoveNext()) { + object element = ienum.Current; if (element == null || element == AutomationNull.Value) { throw new ValidationMetadataException( @@ -1914,19 +1946,6 @@ protected override void Validate(object arguments, EngineIntrinsics engineIntrin } } } - else if ((itor = arguments as IEnumerator) != null) - { - for (; itor.MoveNext() == true;) - { - if (itor.Current == null || itor.Current == AutomationNull.Value) - { - throw new ValidationMetadataException( - "ArgumentIsNull", - null, - Metadata.ValidateNotNullCollectionFailure); - } - } - } } } @@ -1935,7 +1954,7 @@ protected override void Validate(object arguments, EngineIntrinsics engineIntrin /// an empty string, and is not an empty collection. /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public sealed class ValidateNotNullOrEmptyAttribute : ValidateArgumentsAttribute + public sealed class ValidateNotNullOrEmptyAttribute : NullValidationAttributeBase { /// /// Validates that the parameters's argument is not null, is not @@ -1954,10 +1973,6 @@ public sealed class ValidateNotNullOrEmptyAttribute : ValidateArgumentsAttribute /// protected override void Validate(object arguments, EngineIntrinsics engineIntrinsics) { - IEnumerable ienum = null; - IEnumerator itor = null; - string str = null; - if (arguments == null || arguments == AutomationNull.Value) { throw new ValidationMetadataException( @@ -1965,7 +1980,7 @@ protected override void Validate(object arguments, EngineIntrinsics engineIntrin null, Metadata.ValidateNotNullOrEmptyFailure); } - else if ((str = arguments as String) != null) + else if (arguments is string str) { if (String.IsNullOrEmpty(str)) { @@ -1975,34 +1990,40 @@ protected override void Validate(object arguments, EngineIntrinsics engineIntrin Metadata.ValidateNotNullOrEmptyFailure); } } - else if ((ienum = arguments as IEnumerable) != null) + else if (IsArgumentCollection(arguments.GetType(), out bool isElementValueType)) { - int validElements = 0; - foreach (object element in ienum) - { - validElements++; - if (element == null || element == AutomationNull.Value) - { - throw new ValidationMetadataException( - "ArgumentIsNull", - null, - Metadata.ValidateNotNullOrEmptyCollectionFailure); - } + bool isEmpty = true; + IEnumerator ienum = LanguagePrimitives.GetEnumerator(arguments); + if (ienum.MoveNext()) { isEmpty = false; } - string elementAsString = element as String; - if (elementAsString != null) - { - if (String.IsNullOrEmpty(elementAsString)) + // If the element of the collection is of value type, then no need to check for null + // because a value-type value cannot be null. + if (!isEmpty && !isElementValueType) + { + do { + object element = ienum.Current; + if (element == null || element == AutomationNull.Value) { throw new ValidationMetadataException( - "ArgumentCollectionContainsEmpty", + "ArgumentIsNull", null, - Metadata.ValidateNotNullOrEmptyFailure); + Metadata.ValidateNotNullOrEmptyCollectionFailure); } - } + + if (element is string elementAsString) + { + if (String.IsNullOrEmpty(elementAsString)) + { + throw new ValidationMetadataException( + "ArgumentCollectionContainsEmpty", + null, + Metadata.ValidateNotNullOrEmptyCollectionFailure); + } + } + } while (ienum.MoveNext()); } - if (validElements == 0) + if (isEmpty) { throw new ValidationMetadataException( "ArgumentIsEmpty", @@ -2010,21 +2031,9 @@ protected override void Validate(object arguments, EngineIntrinsics engineIntrin Metadata.ValidateNotNullOrEmptyCollectionFailure); } } - else if ((itor = arguments as IEnumerator) != null) + else if (arguments is IDictionary dict) { - int validElements = 0; - for (; itor.MoveNext() == true;) - { - validElements++; - if (itor.Current == null || itor.Current == AutomationNull.Value) - { - throw new ValidationMetadataException( - "ArgumentIsNull", - null, - Metadata.ValidateNotNullOrEmptyCollectionFailure); - } - } - if (validElements == 0) + if (dict.Count == 0) { throw new ValidationMetadataException( "ArgumentIsEmpty", diff --git a/src/System.Management.Automation/engine/CompiledCommandParameter.cs b/src/System.Management.Automation/engine/CompiledCommandParameter.cs index a8a4eb097d1..4a90b0849cf 100644 --- a/src/System.Management.Automation/engine/CompiledCommandParameter.cs +++ b/src/System.Management.Automation/engine/CompiledCommandParameter.cs @@ -698,7 +698,7 @@ internal ParameterCollectionTypeInformation(Type type) /// /// The type of the elements in the collection /// - internal Type ElementType { get; set; } + internal Type ElementType { get; private set; } } } diff --git a/src/System.Management.Automation/engine/ParameterBinderBase.cs b/src/System.Management.Automation/engine/ParameterBinderBase.cs index 4f3dc90e81e..94b15552f6c 100644 --- a/src/System.Management.Automation/engine/ParameterBinderBase.cs +++ b/src/System.Management.Automation/engine/ParameterBinderBase.cs @@ -787,18 +787,26 @@ private void ValidateNullOrEmptyArgument( // Ensure that each element abides by the metadata bool isEmpty = true; + Type elementType = parameterMetadata.CollectionTypeInformation.ElementType; + bool isElementValueType = elementType != null && elementType.IsValueType; + // Note - we explicitly don't pass the context here because we don't want // the overhead of the calls that check for stopping. - while (ParserOps.MoveNext(null, null, ienum)) + if (ParserOps.MoveNext(null, null, ienum)) { isEmpty = false; } + + // If the element of the collection is of value type, then no need to check for null + // because a value-type value cannot be null. + if (!isEmpty && !isElementValueType) { - object element = ParserOps.Current(null, ienum); - isEmpty = false; - ValidateNullOrEmptyArgument( - parameter, - parameterMetadata, - parameterMetadata.CollectionTypeInformation.ElementType, - element, - false); + do { + object element = ParserOps.Current(null, ienum); + ValidateNullOrEmptyArgument( + parameter, + parameterMetadata, + parameterMetadata.CollectionTypeInformation.ElementType, + element, + false); + } while (ParserOps.MoveNext(null, null, ienum)); } if (isEmpty && !parameterMetadata.AllowsEmptyCollectionArgument) diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 72934c33485..4f3c5ab80f8 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -594,7 +594,6 @@ public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, Dyna return (errorSuggestion ?? NullResult(target)).WriteToDebugLog(this); } - // In CORECLR System.Data.DataTable does not have the DataRowCollection IEnumerable, so disabling code. if (targetValue is DataTable) { // Generate: diff --git a/src/System.Management.Automation/utils/tracing/SysLogProvider.cs b/src/System.Management.Automation/utils/tracing/SysLogProvider.cs index d54392e607a..03d5d17e9eb 100755 --- a/src/System.Management.Automation/utils/tracing/SysLogProvider.cs +++ b/src/System.Management.Automation/utils/tracing/SysLogProvider.cs @@ -357,6 +357,7 @@ internal enum LogLevel : uint internal static class NativeMethods { + const string libpslnative = "libpsl-native"; /// /// Write a message to the system logger, which in turn writes the message to the system console, log files, etc. /// See man 3 syslog for more info. @@ -365,13 +366,13 @@ internal static class NativeMethods /// The OR of a priority and facility in the SysLogPriority enum indicating the the priority and facility of the log entry. /// /// The message to put in the log entry. - [DllImport("psl-native", CharSet = CharSet.Ansi, EntryPoint = "Native_SysLog")] + [DllImport(libpslnative, CharSet = CharSet.Ansi, EntryPoint = "Native_SysLog")] internal static extern void SysLog(SysLogPriority priority, string message); - [DllImport("psl-native", CharSet = CharSet.Ansi, EntryPoint = "Native_OpenLog")] + [DllImport(libpslnative, CharSet = CharSet.Ansi, EntryPoint = "Native_OpenLog")] internal static extern void OpenLog(IntPtr ident, SysLogPriority facility); - [DllImport("psl-native", EntryPoint = "Native_CloseLog")] + [DllImport(libpslnative, EntryPoint = "Native_CloseLog")] internal static extern void CloseLog(); [Flags] 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 78631251692..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; @@ -57,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] @@ -73,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] @@ -98,7 +119,7 @@ public void TestGetProperty() { if(property.Name == "IsReadOnly") { - Assert.Equal(property.Value,false); + Assert.False((bool)property.Value); } } } @@ -117,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 eec20153772..990242f69f1 100644 --- a/test/csharp/test_SessionState.cs +++ b/test/csharp/test_SessionState.cs @@ -12,12 +12,12 @@ 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); InitialSessionState iss = InitialSessionState.CreateDefault2(); 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/test/powershell/Modules/Microsoft.PowerShell.Security/FileCatalog.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Security/FileCatalog.Tests.ps1 index 85e4c9c8710..712310c8e52 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Security/FileCatalog.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Security/FileCatalog.Tests.ps1 @@ -233,11 +233,14 @@ Describe "Test suite for NewFileCatalogAndTestFileCatalogCmdlets" -Tags "CI" { try { copy-item "$testDataPath\UserConfigProv" $env:temp -Recurse -ErrorAction SilentlyContinue - $null = New-FileCatalog -Path $env:temp\UserConfigProv\ -CatalogFilePath $catalogPath -CatalogVersion 1.0 - $result = Test-FileCatalog -Path $env:temp\UserConfigProv\ -CatalogFilePath $catalogPath + Push-Location "$env:TEMP\UserConfigProv" + # When -Path is not specified, it should use current directory + $null = New-FileCatalog -CatalogFilePath $catalogPath -CatalogVersion 1.0 + $result = Test-FileCatalog -CatalogFilePath $catalogPath } finally { + Pop-Location Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue Remove-Item "$env:temp\UserConfigProv\" -Force -ErrorAction SilentlyContinue -Recurse } diff --git a/test/powershell/engine/Basic/ValidateAttributes.Tests.ps1 b/test/powershell/engine/Basic/ValidateAttributes.Tests.ps1 index 92a04c1497d..d95af1cd2b7 100644 --- a/test/powershell/engine/Basic/ValidateAttributes.Tests.ps1 +++ b/test/powershell/engine/Basic/ValidateAttributes.Tests.ps1 @@ -260,4 +260,124 @@ Describe 'Validate Attributes Tests' -Tags 'CI' { $ScriptBlock | Should Not Throw } } + + Context "ValidateNotNull, ValidateNotNullOrEmpty and Not-Null-Or-Empty check for Mandatory parameter" { + + BeforeAll { + function MandatoryFunc { + param( + [Parameter(Mandatory, ParameterSetName = "ByteArray")] + [byte[]] $ByteArray, + + [Parameter(Mandatory, ParameterSetName = "ByteList")] + [System.Collections.Generic.List[byte]] $ByteList, + + [Parameter(Mandatory, ParameterSetName = "ByteCollection")] + [System.Collections.ObjectModel.Collection[byte]] $ByteCollection, + + [Parameter(ParameterSetName = "Default")] + $Value + ) + } + + function NotNullFunc { + param( + [ValidateNotNull()] + $Value, + [string] $TestType + ) + + switch ($TestType) { + "COM-Enumerable" { $Value | ForEach-Object Name } + "Enumerator" { + $items = foreach ($i in $Value) { $i } + $items -join "," + } + } + } + + function NotNullOrEmptyFunc { + param( + [ValidateNotNullOrEmpty()] + $Value, + [string] $TestType + ) + + switch ($TestType) { + "COM-Enumerable" { $Value | ForEach-Object Name } + "Enumerator" { + $items = foreach ($i in $Value) { $i } + $items -join "," + } + } + } + + $filePath = Join-Path -Path $PSHOME -ChildPath System.Management.Automation.dll + $byteArray = [System.IO.File]::ReadAllBytes($filePath) + $byteList = [System.Collections.Generic.List[byte]] $byteArray + $byteCollection = [System.Collections.ObjectModel.Collection[byte]] $byteArray + ## Use the running time of 'MandatoryFunc -Value $byteArray' as the baseline time + $baseline = (Measure-Command { MandatoryFunc -Value $byteArray }).Milliseconds + ## Running time should be less than 'expected' + $expected = $baseline + 20 + + if ($IsWindows) { + $null = New-Item -Path $TESTDRIVE/file1 + } + + $testCases = @( + @{ ScriptBlock = { MandatoryFunc -ByteArray $byteArray } } + @{ ScriptBlock = { MandatoryFunc -ByteList $byteList } } + @{ ScriptBlock = { MandatoryFunc -ByteCollection $byteCollection } } + @{ ScriptBlock = { NotNullFunc -Value $byteArray } } + @{ ScriptBlock = { NotNullFunc -Value $byteList } } + @{ ScriptBlock = { NotNullFunc -Value $byteCollection } } + @{ ScriptBlock = { NotNullOrEmptyFunc -Value $byteArray } } + @{ ScriptBlock = { NotNullOrEmptyFunc -Value $byteList } } + @{ ScriptBlock = { NotNullOrEmptyFunc -Value $byteCollection } } + ) + } + + It "Validate running time ''" -TestCases $testCases { + param ($ScriptBlock) + (Measure-Command $ScriptBlock).Milliseconds | Should BeLessThan $expected + } + + It "COM enumerable argument should work with 'ValidateNotNull' and 'ValidateNotNullOrEmpty'" -Skip:(!$IsWindows) { + $shell = New-Object -ComObject "Shell.Application" + $folder = $shell.Namespace("$TESTDRIVE") + $items = $folder.Items() + + NotNullFunc -Value $items -TestType "COM-Enumerable" | Should Be "file1" + NotNullOrEmptyFunc -Value $items -TestType "COM-Enumerable" | Should Be "file1" + } + + It "Enumerator argument should work with 'ValidateNotNull' and 'ValidateNotNullOrEmpty'" { + $data = @(1,2,3) + NotNullFunc -Value $data.GetEnumerator() -TestType "Enumerator" | Should Be "1,2,3" + NotNullOrEmptyFunc -Value $data.GetEnumerator() -TestType "Enumerator" | Should Be "1,2,3" + } + + It "'ValidateNotNull' should throw on null element of a collection argument" { + ## Should throw on null element + { NotNullFunc -Value @("string", $null, 2) } | Should Throw + ## Should not throw on empty string element + { NotNullFunc -Value @("string", "", 2) } | Should Not Throw + ## Should not throw on an empty collection + { NotNullFunc -Value @() } | Should Not Throw + } + + It "'ValidateNotNullOrEmpty' should throw on null element of a collection argument or empty collection/dictionary" { + { NotNullOrEmptyFunc -Value @("string", $null, 2) } | Should Throw + { NotNullOrEmptyFunc -Value @("string", "", 2) } | Should Throw + { NotNullOrEmptyFunc -Value @() } | Should Throw + { NotNullOrEmptyFunc -Value @{} } | Should Throw + } + + It "Mandatory parameter should throw on empty collection" { + { MandatoryFunc -ByteArray ([byte[]]@()) } | Should Throw + { MandatoryFunc -ByteList ([System.Collections.Generic.List[byte]]@()) } | Should Throw + { MandatoryFunc -ByteList ([System.Collections.ObjectModel.Collection[byte]]@()) } | Should Throw + } + } } diff --git a/tools/appveyor.psm1 b/tools/appveyor.psm1 index 359f7c85079..6237ee61517 100644 --- a/tools/appveyor.psm1 +++ b/tools/appveyor.psm1 @@ -323,6 +323,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" @@ -356,6 +357,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 @( @@ -365,6 +370,8 @@ function Invoke-AppVeyorTest Test-PSPesterResults -TestResultsFile $_ } + $testPassResult = Test-XUnitTestResults -TestResultsFile $testResultsXUnitFile + Set-BuildVariable -Name TestPassed -Value True } diff --git a/tools/terms/PowerShell-Terms-Rules.mdb b/tools/terms/PowerShell-Terms-Rules.mdb index 91251e061a9..2a6abb2457a 100644 Binary files a/tools/terms/PowerShell-Terms-Rules.mdb and b/tools/terms/PowerShell-Terms-Rules.mdb differ diff --git a/tools/travis.ps1 b/tools/travis.ps1 index 79cdb304919..3cb81520a26 100644 --- a/tools/travis.ps1 +++ b/tools/travis.ps1 @@ -224,7 +224,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"