Skip to content

Commit b897a1d

Browse files
authored
Merge pull request #200 from Microsoft/PerthCharern/DebugRuleTests
Re-enable the default rule count test case (disabled in #176) The problem was this: ValidateCustomExtension test calls ValidationRuleSet.DefaultRuleSet and makes changes to it directly. Since the DefaultRuleSet is cached, the underlying private _defaultRuleSet is actually modified. The reason we did not see the issue locally might be that the order of local test run and the order on AppVeyor are different. We see the issue only if the "Count" test is run after the custom extension test.
2 parents 4118028 + 17485f6 commit b897a1d

File tree

4 files changed

+66
-32
lines changed

4 files changed

+66
-32
lines changed

src/Microsoft.OpenApi/Validations/OpenApiValidator.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class OpenApiValidator : OpenApiVisitorBase, IValidationContext
2424
/// <param name="ruleSet"></param>
2525
public OpenApiValidator(ValidationRuleSet ruleSet = null)
2626
{
27-
_ruleSet = ruleSet ?? ValidationRuleSet.DefaultRuleSet;
27+
_ruleSet = ruleSet ?? ValidationRuleSet.GetDefaultRuleSet();
2828
}
2929

3030
/// <summary>

src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs

+45-23
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,22 @@ public sealed class ValidationRuleSet : IEnumerable<ValidationRule>
2424
/// <summary>
2525
/// Gets the default validation rule sets.
2626
/// </summary>
27-
public static ValidationRuleSet DefaultRuleSet
27+
/// <remarks>
28+
/// This is a method instead of a property to signal that a new default ruleset object is created
29+
/// per call. Making this a property may be misleading callers to think the returned rulesets from multiple calls
30+
/// are the same objects.
31+
/// </remarks>
32+
public static ValidationRuleSet GetDefaultRuleSet()
2833
{
29-
get
34+
// Reflection can be an expensive operation, so we cache the default rule set that has already been built.
35+
if (_defaultRuleSet == null)
3036
{
31-
if (_defaultRuleSet == null)
32-
{
33-
_defaultRuleSet = BuildDefaultRuleSet();
34-
}
35-
36-
return _defaultRuleSet;
37+
_defaultRuleSet = BuildDefaultRuleSet();
3738
}
39+
40+
// We create a new instance of ValidationRuleSet per call as a safeguard
41+
// against unintentional modification of the private _defaultRuleSet.
42+
return new ValidationRuleSet(_defaultRuleSet);
3843
}
3944

4045
/// <summary>
@@ -44,51 +49,68 @@ public ValidationRuleSet()
4449
{
4550
}
4651

52+
/// <summary>
53+
/// Initializes a new instance of the <see cref="ValidationRuleSet"/> class.
54+
/// </summary>
55+
/// <param name="ruleSet">Rule set to be copied from.</param>
56+
public ValidationRuleSet(ValidationRuleSet ruleSet)
57+
{
58+
if (ruleSet == null)
59+
{
60+
return;
61+
}
62+
63+
foreach (ValidationRule rule in ruleSet)
64+
{
65+
Add(rule);
66+
}
67+
}
68+
4769
/// <summary>
4870
/// Initializes a new instance of the <see cref="ValidationRuleSet"/> class.
4971
/// </summary>
5072
/// <param name="rules">Rules to be contained in this ruleset.</param>
5173
public ValidationRuleSet(IEnumerable<ValidationRule> rules)
5274
{
53-
if (rules != null)
75+
if (rules == null)
5476
{
55-
foreach (ValidationRule rule in rules)
56-
{
57-
Add(rule);
58-
}
77+
return;
78+
}
79+
80+
foreach (ValidationRule rule in rules)
81+
{
82+
Add(rule);
5983
}
6084
}
6185

6286
/// <summary>
6387
/// Gets the rules in this rule set.
6488
/// </summary>
65-
public IEnumerable<ValidationRule> Rules
89+
public IList<ValidationRule> Rules
6690
{
6791
get
6892
{
69-
return _rules.Values.SelectMany(v => v);
93+
return _rules.Values.SelectMany(v => v).ToList();
7094
}
7195
}
7296

7397
/// <summary>
74-
/// Add the new rule into rule set.
98+
/// Add the new rule into the rule set.
7599
/// </summary>
76100
/// <param name="rule">The rule.</param>
77101
public void Add(ValidationRule rule)
78102
{
79-
IList<ValidationRule> typeRules;
80-
if (!_rules.TryGetValue(rule.ElementType, out typeRules))
103+
if (!_rules.ContainsKey(rule.ElementType))
81104
{
82-
typeRules = new List<ValidationRule>();
83-
_rules[rule.ElementType] = typeRules;
105+
_rules[rule.ElementType] = new List<ValidationRule>();
84106
}
85107

86-
if (typeRules.Contains(rule))
108+
if (_rules[rule.ElementType].Contains(rule))
87109
{
88110
throw new OpenApiException(SRResource.Validation_RuleAddTwice);
89111
}
90112

91-
typeRules.Add(rule);
113+
_rules[rule.ElementType].Add(rule);
92114
}
93115

94116
/// <summary>
@@ -97,7 +119,7 @@ public void Add(ValidationRule rule)
97119
/// <returns>The enumerator.</returns>
98120
public IEnumerator<ValidationRule> GetEnumerator()
99121
{
100-
foreach (List<ValidationRule> ruleList in _rules.Values)
122+
foreach (var ruleList in _rules.Values)
101123
{
102124
foreach (var rule in ruleList)
103125
{

test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs

+10-3
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,20 @@
1111
using Microsoft.OpenApi.Validations;
1212
using Microsoft.OpenApi.Writers;
1313
using Xunit;
14+
using Xunit.Abstractions;
1415

1516
namespace Microsoft.OpenApi.Tests.Services
1617
{
1718
[Collection("DefaultSettings")]
1819
public class OpenApiValidatorTests
1920
{
21+
private readonly ITestOutputHelper _output;
22+
23+
public OpenApiValidatorTests(ITestOutputHelper output)
24+
{
25+
_output = output;
26+
}
27+
2028
[Fact]
2129
public void ResponseMustHaveADescription()
2230
{
@@ -90,8 +98,8 @@ public void ServersShouldBeReferencedByIndex()
9098
[Fact]
9199
public void ValidateCustomExtension()
92100
{
93-
94-
var ruleset = Validations.ValidationRuleSet.DefaultRuleSet;
101+
var ruleset = ValidationRuleSet.GetDefaultRuleSet();
102+
95103
ruleset.Add(
96104
new ValidationRule<FooExtension>(
97105
(context, item) =>
@@ -102,7 +110,6 @@ public void ValidateCustomExtension()
102110
}
103111
}));
104112

105-
106113
var openApiDocument = new OpenApiDocument();
107114
openApiDocument.Info = new OpenApiInfo()
108115
{

test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
using System.Linq;
54
using Xunit;
5+
using Xunit.Abstractions;
66

77
namespace Microsoft.OpenApi.Validations.Tests
88
{
99
public class ValidationRuleSetTests
1010
{
11+
private readonly ITestOutputHelper _output;
12+
13+
public ValidationRuleSetTests(ITestOutputHelper output)
14+
{
15+
_output = output;
16+
}
1117

1218
[Fact]
1319
public void DefaultRuleSetReturnsTheCorrectRules()
@@ -27,7 +33,7 @@ public void DefaultRuleSetReturnsTheCorrectRules()
2733
public void DefaultRuleSetPropertyReturnsTheCorrectRules()
2834
{
2935
// Arrange & Act
30-
var ruleSet = ValidationRuleSet.DefaultRuleSet;
36+
var ruleSet = ValidationRuleSet.GetDefaultRuleSet();
3137
Assert.NotNull(ruleSet); // guard
3238

3339
var rules = ruleSet.Rules;
@@ -36,9 +42,8 @@ public void DefaultRuleSetPropertyReturnsTheCorrectRules()
3642
Assert.NotNull(rules);
3743
Assert.NotEmpty(rules);
3844

39-
// Temporarily removing this test as we get inconsistent behaviour on AppVeyor
40-
// This needs to be investigated but it is currently holding up other work.
41-
// Assert.Equal(14, rules.ToList().Count); // please update the number if you add new rule.
45+
// Update the number if you add new default rule(s).
46+
Assert.Equal(14, rules.Count);
4247
}
4348
}
4449
}

0 commit comments

Comments
 (0)