You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
9.7 KiB
254 lines
9.7 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.Collections.ObjectModel; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Threading; |
|
using ICSharpCode.NRefactory.CSharp; |
|
using ICSharpCode.NRefactory.CSharp.Resolver; |
|
using ICSharpCode.NRefactory.Semantics; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
|
|
namespace ICSharpCode.NRefactory.ConsistencyCheck |
|
{ |
|
public class RandomizedOrderResolverTest |
|
{ |
|
static Random sharedRnd = new Random(); |
|
CSharpAstResolver resolver; |
|
CSharpAstResolver resolveAllResolver; |
|
|
|
public static void RunTest(CSharpFile file) |
|
{ |
|
int seed; |
|
lock (sharedRnd) { |
|
seed = sharedRnd.Next(); |
|
} |
|
Random rnd = new Random(seed); |
|
var test = new RandomizedOrderResolverTest(); |
|
// Resolve all nodes, but in a random order without using a navigator. |
|
test.resolver = new CSharpAstResolver(file.Project.Compilation, file.SyntaxTree, file.UnresolvedTypeSystemForFile); |
|
// For comparing whether the results are equivalent, we also use a normal 'resolve all' resolver: |
|
test.resolveAllResolver = new CSharpAstResolver(file.Project.Compilation, file.SyntaxTree, file.UnresolvedTypeSystemForFile); |
|
test.resolveAllResolver.ApplyNavigator(new ResolveAllNavigator(), CancellationToken.None); |
|
// Prepare list of actions that we need to verify: |
|
var actions = new List<Func<bool>>(); |
|
bool checkResults = rnd.Next(0, 2) == 0; |
|
bool checkStateBefore = rnd.Next(0, 2) == 0; |
|
bool checkStateAfter = rnd.Next(0, 2) == 0; |
|
bool checkConversion = rnd.Next(0, 2) == 0; |
|
bool checkExpectedType = rnd.Next(0, 2) == 0; |
|
foreach (var _node in file.SyntaxTree.DescendantsAndSelf) { |
|
var node = _node; |
|
if (CSharpAstResolver.IsUnresolvableNode(node)) |
|
continue; |
|
if (checkResults) |
|
actions.Add(() => test.CheckResult(node)); |
|
if (checkStateBefore) |
|
actions.Add(() => test.CheckStateBefore(node)); |
|
if (checkStateAfter) |
|
actions.Add(() => test.CheckStateAfter(node)); |
|
var expr = node as Expression; |
|
if (expr != null) { |
|
if (checkConversion) |
|
actions.Add(() => test.CheckConversion(expr)); |
|
if (checkExpectedType) |
|
actions.Add(() => test.CheckExpectedType(expr)); |
|
} |
|
} |
|
|
|
// Fisher-Yates shuffle |
|
for (int i = actions.Count - 1; i > 0; i--) { |
|
int j = rnd.Next(0, i); |
|
var tmp = actions[i]; |
|
actions[i] = actions[j]; |
|
actions[j] = tmp; |
|
} |
|
|
|
foreach (var action in actions) { |
|
if (!action()) { |
|
Console.WriteLine("Seed for this file was: " + seed); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
bool CheckResult(AstNode node) |
|
{ |
|
ResolveResult expectedResult = resolveAllResolver.Resolve(node); |
|
ResolveResult actualResult = resolver.Resolve(node); |
|
if (IsEqualResolveResult(expectedResult, actualResult)) |
|
return true; |
|
Console.WriteLine("Different resolve results for '{0}' at {1} in {2}:", node, node.StartLocation, node.GetRegion().FileName); |
|
Console.WriteLine(" expected: " + expectedResult); |
|
Console.WriteLine(" actual: " + actualResult); |
|
return false; |
|
} |
|
|
|
bool CheckStateBefore(AstNode node) |
|
{ |
|
var expectedState = resolveAllResolver.GetResolverStateBefore(node); |
|
var actualState = resolver.GetResolverStateBefore(node); |
|
if (IsEqualResolverState(expectedState, actualState)) |
|
return true; |
|
Console.WriteLine("Different resolver states before '{0}' at {1} in {2}.", node, node.StartLocation, node.GetRegion().FileName); |
|
return false; |
|
} |
|
|
|
bool CheckStateAfter(AstNode node) |
|
{ |
|
var expectedState = resolveAllResolver.GetResolverStateAfter(node); |
|
var actualState = resolver.GetResolverStateAfter(node); |
|
if (IsEqualResolverState(expectedState, actualState)) |
|
return true; |
|
Console.WriteLine("Different resolver states after '{0}' at {1} in {2}.", node, node.StartLocation, node.GetRegion().FileName); |
|
return false; |
|
} |
|
|
|
bool CheckConversion(Expression node) |
|
{ |
|
Conversion expectedConversion = resolveAllResolver.GetConversion(node); |
|
Conversion actualConversion = resolver.GetConversion(node); |
|
if (Compare(expectedConversion, actualConversion, typeof(Conversion))) |
|
return true; |
|
Console.WriteLine("Different conversions for '{0}' at {1} in {2}:", node, node.StartLocation, node.GetRegion().FileName); |
|
Console.WriteLine(" expected: " + expectedConversion); |
|
Console.WriteLine(" actual: " + actualConversion); |
|
return false; |
|
} |
|
|
|
bool CheckExpectedType(Expression node) |
|
{ |
|
IType expectedExpectedType = resolveAllResolver.GetExpectedType(node); |
|
IType actualExpectedType = resolver.GetExpectedType(node); |
|
if (expectedExpectedType.Equals(actualExpectedType)) |
|
return true; |
|
Console.WriteLine("Different expected types for '{0}' at {1} in {2}:", node, node.StartLocation, node.GetRegion().FileName); |
|
Console.WriteLine(" expected: " + expectedExpectedType); |
|
Console.WriteLine(" actual: " + actualExpectedType); |
|
return false; |
|
} |
|
|
|
internal static bool IsEqualResolveResult(ResolveResult rr1, ResolveResult rr2) |
|
{ |
|
if (rr1 == rr2) |
|
return true; |
|
if (rr1 == null || rr2 == null) |
|
return false; |
|
if (rr1.GetType() != rr2.GetType()) |
|
return false; |
|
bool eq = true; |
|
foreach (var property in rr1.GetType().GetProperties()) { |
|
object val1 = property.GetValue(rr1, null); |
|
object val2 = property.GetValue(rr2, null); |
|
eq &= Compare(val1, val2, property.PropertyType); |
|
} |
|
foreach (var field in rr1.GetType().GetFields()) { |
|
object val1 = field.GetValue(rr1); |
|
object val2 = field.GetValue(rr2); |
|
eq &= Compare(val1, val2, field.FieldType); |
|
} |
|
return eq; |
|
} |
|
|
|
static bool Compare(object val1, object val2, Type type) |
|
{ |
|
if (val1 == val2) |
|
return true; |
|
if (val1 == null || val2 == null) |
|
return false; |
|
if (type == typeof(ResolveResult)) { |
|
return IsEqualResolveResult((ResolveResult)val1, (ResolveResult)val2); |
|
} else if (type == typeof(IVariable) || type == typeof(IParameter)) { |
|
return IsEqualVariable((IVariable)val1, (IVariable)val2); |
|
} else if (type == typeof(MethodListWithDeclaringType)) { |
|
var l1 = (MethodListWithDeclaringType)val1; |
|
var l2 = (MethodListWithDeclaringType)val2; |
|
return object.Equals(l1.DeclaringType, l2.DeclaringType) |
|
&& Compare(l1, l2, type.BaseType); |
|
} else if (type.IsArray || type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>) || type.GetGenericTypeDefinition() == typeof(ReadOnlyCollection<>) || type.GetGenericTypeDefinition() == typeof(IList<>) || type.GetGenericTypeDefinition() == typeof(ICollection<>) || type.GetGenericTypeDefinition() == typeof(IEnumerable<>))) { |
|
Type elementType = type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0]; |
|
object[] arr1 = ((IEnumerable)val1).Cast<object>().ToArray(); |
|
object[] arr2 = ((IEnumerable)val2).Cast<object>().ToArray(); |
|
if (arr1.Length != arr2.Length) |
|
return false; |
|
for (int i = 0; i < arr1.Length; i++) { |
|
if (!Compare(arr1[i], arr2[i], elementType)) |
|
return false; |
|
} |
|
return true; |
|
} else { |
|
if (object.Equals(val1, val2)) |
|
return true; |
|
else if (val1 is Conversion && val2 is Conversion && ((Conversion)val1).IsAnonymousFunctionConversion && ((Conversion)val2).IsAnonymousFunctionConversion) |
|
return true; |
|
else |
|
return false; |
|
} |
|
} |
|
|
|
internal static bool IsEqualResolverState(CSharpResolver r1, CSharpResolver r2) |
|
{ |
|
if (r1.CheckForOverflow != r2.CheckForOverflow) |
|
return false; |
|
if (r1.Compilation != r2.Compilation) |
|
return false; |
|
if (!object.Equals(r1.CurrentMember, r2.CurrentMember)) |
|
return false; |
|
if (!object.Equals(r1.CurrentObjectInitializerType, r2.CurrentObjectInitializerType)) |
|
return false; |
|
if (!object.Equals(r1.CurrentTypeDefinition, r2.CurrentTypeDefinition)) |
|
return false; |
|
if (!object.Equals(r1.CurrentUsingScope, r2.CurrentUsingScope)) |
|
return false; |
|
if (r1.IsWithinLambdaExpression != r2.IsWithinLambdaExpression) |
|
return false; |
|
if (r1.LocalVariables.Count() != r2.LocalVariables.Count()) |
|
return false; |
|
return r1.LocalVariables.Zip(r2.LocalVariables, IsEqualVariable).All(_ => _); |
|
} |
|
|
|
internal static bool IsEqualVariable(IVariable v1, IVariable v2) |
|
{ |
|
return object.Equals(v1.ConstantValue, v2.ConstantValue) |
|
&& v1.IsConst == v2.IsConst |
|
&& v1.Name == v2.Name |
|
&& v1.Region == v2.Region |
|
&& object.Equals(v1.Type, v2.Type); |
|
} |
|
|
|
sealed class ResolveAllNavigator : IResolveVisitorNavigator |
|
{ |
|
public ResolveVisitorNavigationMode Scan(AstNode node) |
|
{ |
|
return ResolveVisitorNavigationMode.Resolve; |
|
} |
|
|
|
public void Resolved(AstNode node, ResolveResult result) |
|
{ |
|
} |
|
|
|
public void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) |
|
{ |
|
} |
|
} |
|
} |
|
}
|
|
|