3
3
namespace Xunit
4
4
{
5
5
using System ;
6
+ using System . Collections . Generic ;
7
+ using System . Globalization ;
6
8
7
9
/// <summary>
8
10
/// Specifies which range of values for this parameter should be used for running the test method.
@@ -11,116 +13,87 @@ namespace Xunit
11
13
public class CombinatorialRandomAttribute : Attribute
12
14
{
13
15
/// <summary>
14
- /// Default quantity of values to generate .
16
+ /// Special seed value to create System.Random class without seed .
15
17
/// </summary>
16
- public const int DefaultCount = 5 ;
18
+ public const int NoSeed = 0 ;
17
19
18
- /// <summary>
19
- /// Default minValue for System.Random.Next method.
20
- /// </summary>
21
- public const int DefaultMinValue = 0 ;
20
+ private object [ ] values ;
22
21
23
22
/// <summary>
24
- /// Default maxValue for System.Random.Next method .
23
+ /// Gets or sets the number of values to generate. Must be positive .
25
24
/// </summary>
26
- public const int DefaultMaxValue = int . MaxValue ;
25
+ /// <value>The default value is 5.</value>
26
+ public int Count { get ; set ; } = 5 ;
27
27
28
28
/// <summary>
29
- /// Special seed value to create System.Random class without seed .
29
+ /// Gets or sets the minimum value (inclusive) that may be randomly generated .
30
30
/// </summary>
31
- public const int NoSeed = int . MinValue ;
31
+ /// <value>The default value is 0.</value>
32
+ public int Minimum { get ; set ; }
32
33
33
34
/// <summary>
34
- /// Initializes a new instance of the <see cref="CombinatorialRandomAttribute"/> class .
35
+ /// Gets or sets the maximum value (inclusive) that may be randomly generated .
35
36
/// </summary>
36
- public CombinatorialRandomAttribute ( )
37
- : this ( DefaultCount , DefaultMinValue , DefaultMaxValue , NoSeed )
38
- {
39
- }
37
+ /// <value>The default value is <c><see cref="int.MaxValue"/> - 1</c>, which is the maximum allowable value.</value>
38
+ public int Maximum { get ; set ; } = int . MaxValue - 1 ;
40
39
41
40
/// <summary>
42
- /// Initializes a new instance of the <see cref="CombinatorialRandomAttribute"/> class .
41
+ /// Gets or sets the seed to use for random number generation .
43
42
/// </summary>
44
- /// <param name="count">
45
- /// The quantity of values to generate.
46
- /// Cannot be less than 1, which would conceptually result in zero test cases.
47
- /// </param>
48
- public CombinatorialRandomAttribute ( int count )
49
- : this ( count , DefaultMinValue , DefaultMaxValue , NoSeed )
50
- {
51
- }
43
+ /// <value>The default value of <see cref="NoSeed"/> allows for a new seed to be used each time.</value>
44
+ public int Seed { get ; set ; } = NoSeed ;
52
45
53
46
/// <summary>
54
- /// Initializes a new instance of the <see cref="CombinatorialRandomAttribute"/> class .
47
+ /// Gets the values that should be passed to this parameter on the test method .
55
48
/// </summary>
56
- /// <param name="count">
57
- /// The quantity of values to generate.
58
- /// Cannot be less than 1, which would conceptually result in zero test cases.
59
- /// </param>
60
- /// <param name="maxValue">
61
- /// maxValue for System.Random.Next method.
62
- /// </param>
63
- public CombinatorialRandomAttribute ( int count , int maxValue )
64
- : this ( count , DefaultMinValue , maxValue , NoSeed )
65
- {
66
- }
49
+ /// <value>An array of values.</value>
50
+ public object [ ] Values => this . values ??= this . GenerateValues ( ) ;
67
51
68
- /// <summary>
69
- /// Initializes a new instance of the <see cref="CombinatorialRandomAttribute"/> class.
70
- /// </summary>
71
- /// <param name="count">
72
- /// The quantity of values to generate.
73
- /// Cannot be less than 1, which would conceptually result in zero test cases.
74
- /// </param>
75
- /// <param name="minValue">
76
- /// minValue for System.Random.Next method.
77
- /// </param>
78
- /// <param name="maxValue">
79
- /// maxValue for System.Random.Next method.
80
- /// </param>
81
- public CombinatorialRandomAttribute ( int count , int minValue , int maxValue )
82
- : this ( count , minValue , maxValue , NoSeed )
52
+ private object [ ] GenerateValues ( )
83
53
{
84
- }
54
+ if ( this . Count < 1 )
55
+ {
56
+ throw new InvalidOperationException ( string . Format ( CultureInfo . CurrentCulture , Strings . ValueMustBePositive , nameof ( this . Count ) ) ) ;
57
+ }
85
58
86
- /// <summary>
87
- /// Initializes a new instance of the <see cref="CombinatorialRandomAttribute"/> class.
88
- /// </summary>
89
- /// <param name="count">
90
- /// The quantity of values to generate.
91
- /// Cannot be less than 1, which would conceptually result in zero test cases.
92
- /// </param>
93
- /// <param name="minValue">
94
- /// minValue for System.Random.Next method.
95
- /// </param>
96
- /// <param name="maxValue">
97
- /// maxValue for System.Random.Next method.
98
- /// </param>
99
- /// <param name="seed">
100
- /// Seed for System.Random constructor. If Seed equal to NoSeed value then System.Random constructor whithout seed used.
101
- /// </param>
102
- public CombinatorialRandomAttribute ( int count , int minValue , int maxValue , int seed )
103
- {
104
- if ( count < 1 )
59
+ if ( this . Minimum > this . Maximum )
60
+ {
61
+ throw new InvalidOperationException ( string . Format ( CultureInfo . CurrentCulture , Strings . XMustNotBeGreaterThanY , nameof ( this . Minimum ) , nameof ( this . Maximum ) ) ) ;
62
+ }
63
+
64
+ int maxPossibleValues = this . Maximum - this . Minimum + 1 ;
65
+ if ( this . Count > maxPossibleValues )
105
66
{
106
- throw new ArgumentOutOfRangeException ( nameof ( count ) ) ;
67
+ throw new InvalidOperationException ( string . Format ( CultureInfo . CurrentCulture , Strings . MoreRandomValuesRequestedThanPossibleOnes , nameof ( this . Count ) , nameof ( this . Minimum ) , nameof ( this . Maximum ) ) ) ;
107
68
}
108
69
109
- var random = seed == NoSeed ? new Random ( ) : new Random ( seed ) ;
70
+ Random random = this . Seed != NoSeed ? new Random ( this . Seed ) : new Random ( ) ;
110
71
111
- object [ ] values = new object [ count ] ;
112
- for ( int i = 0 ; i < count ; i ++ )
72
+ HashSet < int > collisionChecker = new HashSet < int > ( ) ;
73
+ object [ ] values = new object [ this . Count ] ;
74
+ int collisionCount = 0 ;
75
+ int i = 0 ;
76
+ while ( collisionChecker . Count < this . Count )
113
77
{
114
- values [ i ] = random . Next ( minValue , maxValue ) ;
78
+ int value = random . Next ( this . Minimum , this . Maximum + 1 ) ;
79
+ if ( collisionChecker . Add ( value ) )
80
+ {
81
+ values [ i ++ ] = value ;
82
+ }
83
+ else
84
+ {
85
+ collisionCount ++ ;
86
+ }
87
+
88
+ if ( collisionCount > collisionChecker . Count * 5 )
89
+ {
90
+ // We have collided in random values far more than we have successfully generated values.
91
+ // Rather than spin in this loop, throw.
92
+ throw new InvalidOperationException ( Strings . TooManyRandomCollisions ) ;
93
+ }
115
94
}
116
95
117
- this . Values = values ;
96
+ return values ;
118
97
}
119
-
120
- /// <summary>
121
- /// Gets the values that should be passed to this parameter on the test method.
122
- /// </summary>
123
- /// <value>An array of values.</value>
124
- public object [ ] Values { get ; }
125
98
}
126
99
}
0 commit comments