diff --git a/src/System.Management.Automation/engine/Attributes.cs b/src/System.Management.Automation/engine/Attributes.cs
index 5ac315d2ae..3250e626b9 100644
--- a/src/System.Management.Automation/engine/Attributes.cs
+++ b/src/System.Management.Automation/engine/Attributes.cs
@@ -899,6 +899,31 @@ namespace System.Management.Automation
}
}
+ ///
+ /// Predefined range kind to use with ValidateRangeAttribute.
+ ///
+ public enum ValidateRangeKind
+ {
+ ///
+ /// Range is greater than 0.
+ ///
+ Positive,
+
+ ///
+ /// Range is greater than or equal to 0.
+ ///
+ NonNegative,
+
+ ///
+ /// Range is less than 0.
+ ///
+ Negative,
+
+ ///
+ /// Range is less than or equal to 0.
+ ///
+ NonPositive
+ }
///
/// Validates that each parameter argument falls in the range
/// specified by MinRange and MaxRange
@@ -926,6 +951,8 @@ namespace System.Management.Automation
///
private Type _promotedType;
+ ValidateRangeKind? _rangeKind;
+
///
/// Validates that each parameter argument falls in the range
/// specified by MinRange and MaxRange
@@ -951,37 +978,14 @@ namespace System.Management.Automation
{
element = o.BaseObject;
}
-
- // minRange and maxRange have the same type, so we just need
- // to compare to one of them
- if (element.GetType() != _promotedType)
+
+ if (_rangeKind.HasValue)
{
- object resultValue;
- if (LanguagePrimitives.TryConvertTo(element, _promotedType, out resultValue))
- {
- element = resultValue;
- }
- else
- {
- throw new ValidationMetadataException("ValidationRangeElementType",
- null, Metadata.ValidateRangeElementType,
- element.GetType().Name, MinRange.GetType().Name);
- }
+ ValidateRange(element, (ValidateRangeKind)_rangeKind);
}
-
- // They are the same type and are all IComparable, so this should not throw
- if (_minComparable.CompareTo(element) > 0)
+ else
{
- throw new ValidationMetadataException("ValidateRangeTooSmall",
- null, Metadata.ValidateRangeSmallerThanMinRangeFailure,
- element.ToString(), MinRange.ToString());
- }
-
- if (_maxComparable.CompareTo(element) < 0)
- {
- throw new ValidationMetadataException("ValidateRangeTooBig",
- null, Metadata.ValidateRangeGreaterThanMaxRangeFailure,
- element.ToString(), MaxRange.ToString());
+ ValidateRange(element);
}
}
@@ -1058,6 +1062,139 @@ namespace System.Management.Automation
MaxRange = maxRange;
}
+ ///
+ /// Initializes a new instance of the ValidateRangeAttribute class
+ /// this constructor uses a predefined ranged
+ ///
+ public ValidateRangeAttribute(ValidateRangeKind kind) : base()
+ {
+ _rangeKind = kind;
+ }
+
+ private void ValidateRange(object element, ValidateRangeKind rangeKind)
+ {
+ Type commonType = GetCommonType(typeof(int),element.GetType());
+ if (commonType == null)
+ {
+ throw new ValidationMetadataException(
+ "ValidationRangeElementType",
+ null,
+ Metadata.ValidateRangeElementType,
+ element.GetType().Name,
+ typeof(int).Name);
+ }
+
+ object resultValue;
+ IComparable dynamicZero = 0;
+
+ if (LanguagePrimitives.TryConvertTo(element, commonType, out resultValue))
+ {
+ element = resultValue;
+
+ if (LanguagePrimitives.TryConvertTo(0, commonType, out resultValue))
+ {
+ dynamicZero = (IComparable)resultValue;
+ }
+ }
+ else
+ {
+ throw new ValidationMetadataException(
+ "ValidationRangeElementType",
+ null,
+ Metadata.ValidateRangeElementType,
+ element.GetType().Name,
+ commonType.Name);
+ }
+
+ switch (rangeKind)
+ {
+ case ValidateRangeKind.Positive:
+ if (dynamicZero.CompareTo(element) >= 0)
+ {
+ throw new ValidationMetadataException(
+ "ValidateRangePositiveFailure",
+ null,
+ Metadata.ValidateRangePositiveFailure,
+ element.ToString());
+ }
+ break;
+ case ValidateRangeKind.NonNegative:
+ if (dynamicZero.CompareTo(element) > 0)
+ {
+ throw new ValidationMetadataException(
+ "ValidateRangeNonNegativeFailure",
+ null,
+ Metadata.ValidateRangeNonNegativeFailure,
+ element.ToString());
+ }
+ break;
+ case ValidateRangeKind.Negative:
+ if (dynamicZero.CompareTo(element) <= 0)
+ {
+ throw new ValidationMetadataException(
+ "ValidateRangeNegativeFailure",
+ null,
+ Metadata.ValidateRangeNegativeFailure,
+ element.ToString());
+ }
+ break;
+ case ValidateRangeKind.NonPositive:
+ if (dynamicZero.CompareTo(element) < 0)
+ {
+ throw new ValidationMetadataException(
+ "ValidateRangeNonPositiveFailure",
+ null,
+ Metadata.ValidateRangeNonPositiveFailure,
+ element.ToString());
+ }
+ break;
+ }
+ }
+
+ private void ValidateRange(object element)
+ {
+ // MinRange and maxRange have the same type, so we just need
+ // to compare to one of them.
+ if (element.GetType() != _promotedType)
+ {
+ object resultValue;
+ if (LanguagePrimitives.TryConvertTo(element, _promotedType, out resultValue))
+ {
+ element = resultValue;
+ }
+ else
+ {
+ throw new ValidationMetadataException(
+ "ValidationRangeElementType",
+ null,
+ Metadata.ValidateRangeElementType,
+ element.GetType().Name,
+ MinRange.GetType().Name);
+ }
+ }
+
+ // They are the same type and are all IComparable, so this should not throw
+ if (_minComparable.CompareTo(element) > 0)
+ {
+ throw new ValidationMetadataException(
+ "ValidateRangeTooSmall",
+ null,
+ Metadata.ValidateRangeSmallerThanMinRangeFailure,
+ element.ToString(),
+ MinRange.ToString());
+ }
+
+ if (_maxComparable.CompareTo(element) < 0)
+ {
+ throw new ValidationMetadataException(
+ "ValidateRangeTooBig",
+ null,
+ Metadata.ValidateRangeGreaterThanMaxRangeFailure,
+ element.ToString(),
+ MaxRange.ToString());
+ }
+ }
+
private static Type GetCommonType(Type minType, Type maxType)
{
Type resultType = null;
diff --git a/src/System.Management.Automation/resources/Metadata.resx b/src/System.Management.Automation/resources/Metadata.resx
index aad8f90208..762f830677 100644
--- a/src/System.Management.Automation/resources/Metadata.resx
+++ b/src/System.Management.Automation/resources/Metadata.resx
@@ -123,6 +123,18 @@
The argument cannot be validated because its type "{0}" is not the same type ({1}) as the maximum and minimum limits of the parameter. Make sure the argument is of type {1} and then try the command again.
+
+ The argument "{0}" cannot be validated because its value is not greater than zero.
+
+
+ The argument "{0}" cannot be validated because its value is not greater than or equal to zero.
+
+
+ The argument "{0}" cannot be validated because its value is not less than zero.
+
+
+ The argument "{0}" cannot be validated because its value is not less than or equal to zero.
+
The specified minimum range ({0}) cannot be accepted because it is not the same type as the specified maximum range ({1}). Update the ValidateRange attribute for the parameter.
diff --git a/src/System.Management.Automation/utils/MetadataExceptions.cs b/src/System.Management.Automation/utils/MetadataExceptions.cs
index 733bf12766..69e4dad6fc 100644
--- a/src/System.Management.Automation/utils/MetadataExceptions.cs
+++ b/src/System.Management.Automation/utils/MetadataExceptions.cs
@@ -72,6 +72,10 @@ namespace System.Management.Automation
public class ValidationMetadataException : MetadataException
{
internal const string ValidateRangeElementType = "ValidateRangeElementType";
+ internal const string ValidateRangePositiveFailure = "ValidateRangePositiveFailure";
+ internal const string ValidateRangeNonNegativeFailure = "ValidateRangeNonNegativeFailure";
+ internal const string ValidateRangeNegativeFailure = "ValidateRangeNegativeFailure";
+ internal const string ValidateRangeNonPositiveFailure = "ValidateRangeNonPositiveFailure";
internal const string ValidateRangeMinRangeMaxRangeType = "ValidateRangeMinRangeMaxRangeType";
internal const string ValidateRangeNotIComparable = "ValidateRangeNotIComparable";
internal const string ValidateRangeMaxRangeSmallerThanMinRange = "ValidateRangeMaxRangeSmallerThanMinRange";
diff --git a/test/powershell/engine/ValidateAttributes.Tests.ps1 b/test/powershell/engine/ValidateAttributes.Tests.ps1
index 42989b3c45..92a04c1497 100644
--- a/test/powershell/engine/ValidateAttributes.Tests.ps1
+++ b/test/powershell/engine/ValidateAttributes.Tests.ps1
@@ -3,26 +3,261 @@ Describe 'Validate Attributes Tests' -Tags 'CI' {
Context "ValidateCount" {
BeforeAll {
$testCases = @(
- @{ sb = { function Local:foo { param([ValidateCount(-1,2)] [string[]] $bar) }; foo }; FullyQualifiedErrorId = "ExceptionConstructingAttribute"; InnerErrorId = "" }
- @{ sb = { function Local:foo { param([ValidateCount(1,-1)] [string[]] $bar) }; foo }; FullyQualifiedErrorId = "ExceptionConstructingAttribute"; InnerErrorId = "" }
- @{ sb = { function Local:foo { param([ValidateCount(2, 1)] [string[]] $bar) }; foo }; FullyQualifiedErrorId = "ValidateRangeMaxLengthSmallerThanMinLength"; InnerErrorId = "" }
- @{ sb = { function Local:foo { param([ValidateCount(2, 2)] [string[]] $bar) }; foo 1 }; FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"; InnerErrorId = "ValidateCountExactFailure" }
- @{ sb = { function Local:foo { param([ValidateCount(2, 3)] [string[]] $bar) }; foo 1 }; FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"; InnerErrorId = "ValidateCountMinMaxFailure" }
- @{ sb = { function Local:foo { param([ValidateCount(2, 3)] [string[]] $bar) }; foo 1,2,3,4 }; FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"; InnerErrorId = "ValidateCountMinMaxFailure" }
+ @{
+ ScriptBlock = { function foo { param([ValidateCount(-1,2)] [string[]] $bar) }; foo }
+ FullyQualifiedErrorId = "ExceptionConstructingAttribute"
+ InnerErrorId = ""
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateCount(1,-1)] [string[]] $bar) }; foo }
+ FullyQualifiedErrorId = "ExceptionConstructingAttribute"
+ InnerErrorId = ""
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateCount(2, 1)] [string[]] $bar) }; foo }
+ FullyQualifiedErrorId = "ValidateRangeMaxLengthSmallerThanMinLength"
+ InnerErrorId = ""
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateCount(2, 2)] [string[]] $bar) }; foo 1 }
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateCountExactFailure"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateCount(2, 3)] [string[]] $bar) }; foo 1 }
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateCountMinMaxFailure"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateCount(2, 3)] [string[]] $bar) }; foo 1,2,3,4 }
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateCountMinMaxFailure"
+ }
)
}
It 'Exception: :' -TestCases $testCases {
- param($sb, $FullyQualifiedErrorId, $InnerErrorId)
+ param($ScriptBlock, $FullyQualifiedErrorId, $InnerErrorId)
- $sb | ShouldBeErrorId $FullyQualifiedErrorId
+ $ScriptBlock | ShouldBeErrorId $FullyQualifiedErrorId
if ($InnerErrorId) {
$error[0].exception.innerexception.errorrecord.FullyQualifiedErrorId | Should Be $InnerErrorId
}
}
It 'No Exception: valid argument count' {
- { function Local:foo { param([ValidateCount(2, 4)] [string[]] $bar) }; foo 1,2,3,4 } | Should Not Throw
+ { function foo { param([ValidateCount(2, 4)] [string[]] $bar) }; foo 1,2,3,4 } | Should Not Throw
+ }
+ }
+
+ Context "ValidateRange - ParameterConstuctors" {
+ BeforeAll {
+ $testCases = @(
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('xPositive')] $bar) }; foo }
+ FullyQualifiedErrorId = "ExceptionConstructingAttribute"
+ InnerErrorId = "SubstringDisambiguationEnumParseThrewAnException"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange(2,1)] [int] $bar) }; foo }
+ FullyQualifiedErrorId = "MaxRangeSmallerThanMinRange"
+ InnerErrorId = ""
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange("one",10)] $bar) }; foo }
+ FullyQualifiedErrorId = "MinRangeNotTheSameTypeOfMaxRange"
+ InnerErrorId = ""
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange(1,"two")] $bar) }; foo }
+ FullyQualifiedErrorId = "MinRangeNotTheSameTypeOfMaxRange"
+ InnerErrorId = ""
+ }
+ )
+ }
+
+ It 'Exception: :' -TestCases $testCases {
+ param($ScriptBlock, $FullyQualifiedErrorId, $InnerErrorId)
+
+ $ScriptBlock | ShouldBeErrorId $FullyQualifiedErrorId
+ if ($InnerErrorId) {
+ $error[0].exception.innerexception.errorrecord.FullyQualifiedErrorId | Should Be $InnerErrorId
+ }
+ }
+ }
+ Context "ValidateRange - User Defined Range"{
+ BeforeAll {
+ $testCases = @(
+ @{
+ ScriptBlock = { function foo { param([ValidateRange(1,10)] [int] $bar) }; foo -1 }
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateRangeTooSmall"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange(1,10)] [int] $bar) }; foo 11 }
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateRangeTooBig"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange(1,10)] $bar) }; foo "one" }
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidationRangeElementType"
+ }
+ )
+
+ $validTestCases = @(
+ @{
+ ScriptBlock = { function foo { param([ValidateRange(1,10)] [int] $bar) }; foo 5 }
+ }
+ )
+ }
+
+ It 'Exception: :' -TestCases $testCases {
+ param($ScriptBlock, $FullyQualifiedErrorId, $InnerErrorId)
+
+ $ScriptBlock | ShouldBeErrorId $FullyQualifiedErrorId
+ if ($InnerErrorId) {
+ $error[0].exception.innerexception.errorrecord.FullyQualifiedErrorId | Should Be $InnerErrorId
+ }
+ }
+
+ It 'No Exception: value within range' -TestCases $validTestCases {
+ param($ScriptBlock)
+ $ScriptBlock | Should Not Throw
+ }
+ }
+
+ Context "ValidateRange - Predefined Range" {
+ BeforeAll {
+ $testCases = @(
+ @{
+ ScriptBlock = { function foo { param([ValidateRange("Positive")] [int] $bar) }; foo -1 }
+ RangeType = "Positive"
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateRangePositiveFailure"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange("Positive")] [int] $bar) }; foo 0 }
+ RangeType = "Positive"
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateRangePositiveFailure"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange("Positive")] $bar) }; foo "one" }
+ RangeType = "Positive"
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = ""
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('NonNegative')] [int] $bar) }; foo -1 }
+ RangeType = "NonNegative"
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateRangeNonNegativeFailure"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('NonNegative')] $bar) }; foo "one" }
+ RangeType = "NonNegative"
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = ""
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('Negative')] [int] $bar) }; foo 1 }
+ RangeType = "Negative"
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateRangeNegativeFailure"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('Negative')] [int] $bar) }; foo 0 }
+ RangeType = "Negative"
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateRangeNegativeFailure"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('Negative')] $bar) }; foo "one" }
+ RangeType = "Negative"
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = ""
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('NonPositive')] $bar) }; foo 1 }
+ RangeType = "NonPositive"
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = "ValidateRangeNonPositiveFailure"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('NonPositive')] $bar) }; foo "one" }
+ RangeType = "NonPositive"
+ FullyQualifiedErrorId = "ParameterArgumentValidationError,foo"
+ InnerErrorId = ""
+ }
+ )
+
+ $validTestCases = @(
+ @{
+ ScriptBlock = { function foo { param([ValidateRange("Positive")] [int] $bar) }; foo 15 }
+ RangeType = "Positive"
+ TestValue = 15
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange("Positive")] [double]$bar) }; foo ([double]::MaxValue) };
+ RangeType = "Positive"
+ TestValue = [double]::MaxValue
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('NonNegative')] [int] $bar) }; foo 0 }
+ RangeType = "NonNegative"
+ TestValue = 0
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('NonNegative')] [int] $bar) }; foo 15 }
+ RangeType = "NonNegative"
+ TestValue = 15
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('NonNegative')] [double]$bar) }; foo ([double]::MaxValue) };
+ RangeType = "NonNegative"
+ TestValue = [double]::MaxValue
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('Negative')] [int] $bar) }; foo -15 }
+ RangeType = "Negative"
+ TestValue = -15
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('Negative')] [double]$bar) }; foo ([double]::MinValue) };
+ TestValue = [double]::MinValue
+ RangeType = "Negative"
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('NonPositive')] [int] $bar) }; foo 0 }
+ RangeType = "NonPositive"
+ TestValue = 0
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('NonPositive')] [int] $bar) }; foo -15 }
+ RangeType = "NonPositive"
+ TestValue = -15
+ }
+ @{
+ ScriptBlock = { function foo { param([ValidateRange('NonPositive')] [double]$bar) }; foo ([double]::MinValue) }
+ RangeType = "NonPositive"
+ TestValue = [double]::MinValue
+ }
+ )
+ }
+
+ It 'Exception: :, RangeType: ' -TestCases $testCases {
+ param($ScriptBlock, $RangeType, $FullyQualifiedErrorId, $InnerErrorId)
+
+ $ScriptBlock | ShouldBeErrorId $FullyQualifiedErrorId
+ if ($InnerErrorId) {
+ $error[0].exception.innerexception.errorrecord.FullyQualifiedErrorId | Should Be $InnerErrorId
+ }
+ }
+
+ It 'No Exception: RangeType: - argument ""' -TestCases $validTestCases {
+ param($ScriptBlock, $RangeType, $testValue)
+ $ScriptBlock | Should Not Throw
}
}
}