private void ProcessEventDefinition(EventDefinition eventDefinition, IntertypeRelationGraph graph, TypeDefinition type) { if (eventDefinition.AddMethod != null) { ProcessMethodDefinition(eventDefinition.AddMethod, graph, type); } if (eventDefinition.RemoveMethod != null) { ProcessMethodDefinition(eventDefinition.RemoveMethod, graph, type); } if (eventDefinition.InvokeMethod != null) { ProcessMethodDefinition(eventDefinition.InvokeMethod, graph, type); } if (eventDefinition.HasOtherMethods) { foreach (var methodDef in eventDefinition.OtherMethods) { ProcessMethodDefinition(methodDef, graph, type); } } AddUseEdgeIfBothExist(type, eventDefinition.EventType, graph); if (eventDefinition.HasCustomAttributes) { foreach (var attribute in eventDefinition.CustomAttributes) { AddUseEdgeIfBothExist(type, attribute.AttributeType, graph); } } }
private void AddNodeIfNotExists(INamespaceOrTypeSymbol namedType, IntertypeRelationGraph graph, List <INamedTypeSymbol> typeSymbols) { if (namedType == null) { return; } if (namedType.IsType) { typeSymbols.Add((INamedTypeSymbol)namedType); var typeName = GetTypeIdentifier(namedType); if (!graph.Nodes.Contains(typeName)) { graph.Nodes.Add(typeName); } } else { foreach (var type in namedType.GetMembers()) { AddNodeIfNotExists(type as INamespaceOrTypeSymbol, graph, typeSymbols); } } }
private void ProcessAttributes(INamedTypeSymbol type, ISymbol symbol, IntertypeRelationGraph graph) { foreach (var attribute in symbol.GetAttributes()) { AddUseEdgeIfBothExist(type, attribute.AttributeClass, graph); } }
private void AddNodeIfNotAlreadyThere(TypeDefinition type, IntertypeRelationGraph graph, List <TypeDefinition> typeDefinitions) { if (!graph.Nodes.Contains(type.FullName) && !type.IsAnonymousType()) { typeDefinitions.Add(type); graph.Nodes.Add(type.FullName); } }
private void ProcessTypeDefinition(TypeDefinition type, IntertypeRelationGraph graph) { if (type.BaseType != null) { AddInheritanceEdgeIfBothExist(type, type.BaseType, graph); } if (type.HasInterfaces) { foreach (var interfaceDef in type.Interfaces) { AddInheritanceEdgeIfBothExist(type, interfaceDef.InterfaceType, graph); } } if (type.HasMethods) { foreach (var method in type.Methods) { ProcessMethodDefinition(method, graph, type); } } if (type.HasProperties) { foreach (var property in type.Properties) { ProcessPropertyDefinition(property, graph, type); } } if (type.HasFields) { foreach (var field in type.Fields) { ProcessFieldDefinition(field, graph, type); } } if (type.HasEvents) { foreach (var eventDef in type.Events) { ProcessEventDefinition(eventDef, graph, type); } } if (type.HasGenericParameters) { foreach (var genericParameter in type.GenericParameters) { ProcessGenericParameter(genericParameter, graph, type); } } if (type.HasCustomAttributes) { foreach (var attribute in type.CustomAttributes) { AddUseEdgeIfBothExist(type, attribute.AttributeType, graph); } } }
private void ProcessGenericParameter(GenericParameter genericParameter, IntertypeRelationGraph graph, TypeDefinition type) { if (genericParameter.HasConstraints) { foreach (var constraint in genericParameter.Constraints) { AddUseEdgeIfBothExist(type, constraint, graph); } } }
private void AddNestedTypes(TypeDefinition type, IntertypeRelationGraph graph, List <TypeDefinition> typeDefinitions) { if (type.HasNestedTypes) { foreach (var nestedType in type.NestedTypes) { AddNodeIfNotAlreadyThere(nestedType, graph, typeDefinitions); AddNestedTypes(nestedType, graph, typeDefinitions); } } }
private void ProcessFieldDefinition(FieldDefinition fieldDefinition, IntertypeRelationGraph graph, TypeDefinition type) { AddUseEdgeIfBothExist(type, fieldDefinition.FieldType, graph); if (fieldDefinition.HasCustomAttributes) { foreach (var attribute in fieldDefinition.CustomAttributes) { AddUseEdgeIfBothExist(type, attribute.AttributeType, graph); } } }
private void ProcessTypeSymbol(INamedTypeSymbol type, IntertypeRelationGraph graph) { if (type.BaseType != null) { AddInheritanceEdgeIfBothExist(type, type.BaseType, graph); } foreach (var typeInterface in type.Interfaces) { AddInheritanceEdgeIfBothExist(type, typeInterface, graph); } ProcessAttributes(type, type, graph); foreach (var symbol in type.GetMembers()) { ProcessAttributes(type, symbol, graph); foreach (var typeParameter in type.TypeParameters) { foreach (var constraint in typeParameter.ConstraintTypes) { AddUseEdgeIfBothExist(type, constraint as INamedTypeSymbol, graph); } } var method = symbol as IMethodSymbol; if (method != null) { ProcessMethodSymbol(type, method, graph); } var property = symbol as IPropertySymbol; if (property != null) { ProcessPropertySymbol(type, property, graph); } var field = symbol as IFieldSymbol; if (field != null) { ProcessFieldSymbol(type, field, graph); } var eventSymbol = symbol as IEventSymbol; if (eventSymbol != null) { ProcessEventSymbol(type, eventSymbol, graph); } } }
public Task <IntertypeRelationGraph> GetDataStructure(TCSharpModel model, CancellationToken cancellationToken) { var assemblies = assembliesArtefactAdapter.Parse(model.AbsoluteSolutionPath); var graph = new IntertypeRelationGraph(); var typeDefinitions = new List <TypeDefinition>(); //First Collect All Types as Nodes foreach (var assembly in assemblies) { if (!File.Exists(assembly.AbsolutePath)) { continue; } var moduleDefinition = ModuleDefinition.ReadModule(assembly.AbsolutePath); foreach (var type in moduleDefinition.Types) { cancellationToken.ThrowIfCancellationRequested(); if (type.Name == MonoModuleTyp) { continue; } AddNodeIfNotAlreadyThere(type, graph, typeDefinitions); AddNestedTypes(type, graph, typeDefinitions); } } //Second, Build Edges var parallelOptions = new ParallelOptions { CancellationToken = cancellationToken, MaxDegreeOfParallelism = Environment.ProcessorCount }; Parallel.ForEach(typeDefinitions, parallelOptions, type => { parallelOptions.CancellationToken.ThrowIfCancellationRequested(); ProcessTypeDefinition(type, graph); }); return(Task.FromResult(graph)); }
public async Task <IntertypeRelationGraph> GetDataStructure(TCSharpModel model, CancellationToken token) { var graph = new IntertypeRelationGraph(); var workspace = MSBuildWorkspace.Create(new Dictionary <string, string> { { "Configuration", settingsProvider.Configuration }, { "Platform", settingsProvider.Platform } }); var solution = await workspace.OpenSolutionAsync(model.AbsoluteSolutionPath, token); compilations = new List <Compilation>(); var typeSymbols = new List <INamedTypeSymbol>(); //Collect all types foreach (var project in solution.Projects) { token.ThrowIfCancellationRequested(); var compilation = await project.GetCompilationAsync(token); compilations.Add(compilation); foreach (var namespaceSymbol in compilation.Assembly.GlobalNamespace.GetNamespaceMembers()) { AddNodeIfNotExists(namespaceSymbol, graph, typeSymbols); } } //Build edges var parallelOptions = new ParallelOptions { CancellationToken = token, MaxDegreeOfParallelism = Environment.ProcessorCount }; Parallel.ForEach(typeSymbols, parallelOptions, type => { parallelOptions.CancellationToken.ThrowIfCancellationRequested(); ProcessTypeSymbol(type, graph); }); return(graph); }
private void ProcessMethodDefinition(MethodDefinition methodDefinition, IntertypeRelationGraph graph, TypeDefinition currentType) { if (methodDefinition.HasParameters) { //Uses all parameter types foreach (var parameter in methodDefinition.Parameters) { AddUseEdgeIfBothExist(currentType, parameter.ParameterType, graph); } } //Uses return type AddUseEdgeIfBothExist(currentType, methodDefinition.ReturnType, graph); if (methodDefinition.HasBody) { //Uses all local variable types foreach (var variableDefinition in methodDefinition.Body.Variables) { AddUseEdgeIfBothExist(currentType, variableDefinition.VariableType, graph); } foreach (var instruction in methodDefinition.Body.Instructions) { ProcessInstruction(instruction, graph, currentType); } } if (methodDefinition.HasGenericParameters) { foreach (var genericParameter in methodDefinition.GenericParameters) { ProcessGenericParameter(genericParameter, graph, currentType); } } if (methodDefinition.HasCustomAttributes) { foreach (var attribute in methodDefinition.CustomAttributes) { AddUseEdgeIfBothExist(currentType, attribute.AttributeType, graph); } } }
public ISet <TTestCase> SelectTests(IntertypeRelationGraph dataStructure, StructuralDelta <TestsModel <TTestCase>, TTestCase> testsDelta, StructuralDelta <CSharpClassesProgramModel, CSharpClassElement> programDelta, CancellationToken cancellationToken) { ISet <ImpactedTest <TTestCase> > impactedTests = new HashSet <ImpactedTest <TTestCase> >(); var changedTypes = new List <AffectedType>(); changedTypes.AddRange(programDelta.AddedElements.Select(x => new AffectedType { Id = x.Id, ImpactedDueTo = x.Id })); changedTypes.AddRange(programDelta.ChangedElements.Select(x => new AffectedType { Id = x.Id, ImpactedDueTo = x.Id })); var affectedTypes = new List <AffectedType>(changedTypes); foreach (var type in changedTypes) { cancellationToken.ThrowIfCancellationRequested(); ExtendAffectedTypesAndReportImpactedTests(type, dataStructure, affectedTypes, testsDelta.NewModel.TestSuite, impactedTests, cancellationToken); } testsDelta.AddedElements.ForEach(x => { if (impactedTests.All(y => y.TestCase.Id != x.Id)) { impactedTests.Add(new ImpactedTest <TTestCase> { TestCase = x, ImpactedDueTo = null }); } }); CorrespondenceModel = new CorrespondenceModel.Models.CorrespondenceModel { ProgramVersionId = programDelta.NewModel.VersionId, CorrespondenceModelLinks = impactedTests.ToDictionary( x => x.TestCase.Id, x => x.ImpactedDueTo == null ? new HashSet <string>() : new HashSet <string>(new[] { x.ImpactedDueTo })) }; return(new HashSet <TTestCase>(impactedTests.Select(x => x.TestCase))); }
private void ProcessInstruction(Instruction instruction, IntertypeRelationGraph graph, TypeDefinition currentType) { if (instruction.Operand == null) { return; } var typeRef = instruction.Operand as TypeReference; if (typeRef != null) { AddUseEdgeIfBothExist(currentType, typeRef, graph); } var memberRef = instruction.Operand as MemberReference; if (memberRef != null) { AddUseEdgeIfBothExist(currentType, memberRef.DeclaringType, graph); } }
private void AddUseEdgeIfBothExist(TypeDefinition from, TypeReference to, IntertypeRelationGraph graph) { AddEdgeIfBothExist(from, to, true, graph); }
private void AddInheritanceEdgeIfBothExist(TypeDefinition from, TypeReference to, IntertypeRelationGraph graph) { AddEdgeIfBothExist(from, to, false, graph); }
private void ProcessFieldSymbol(INamedTypeSymbol type, IFieldSymbol field, IntertypeRelationGraph graph) { AddUseEdgeIfBothExist(type, field.Type as INamedTypeSymbol, graph); }
private void AddEdgeIfBothExist(INamedTypeSymbol from, INamedTypeSymbol to, bool useEdge, IntertypeRelationGraph graph) { if (from == null || to == null || from.TypeKind == TypeKind.Error || to.TypeKind == TypeKind.Error || Equals(from, to)) { return; } if (to.IsGenericType) { foreach (var genericArugment in to.TypeArguments) { AddUseEdgeIfBothExist(from, genericArugment as INamedTypeSymbol, graph); } } var fromName = GetTypeIdentifier(from); var toName = GetTypeIdentifier(to); if (!graph.Nodes.Contains(fromName) || !graph.Nodes.Contains(toName)) { return; } if (useEdge) { graph.AddUseEdgeIfNotExists(fromName, toName); } else { graph.AddInheritanceEdgeIfNotExists(fromName, toName); } }
private void AddUseEdgeIfBothExist(INamedTypeSymbol from, INamedTypeSymbol to, IntertypeRelationGraph graph) { AddEdgeIfBothExist(from, to, true, graph); }
private void AddInheritanceEdgeIfBothExist(INamedTypeSymbol from, INamedTypeSymbol to, IntertypeRelationGraph graph) { AddEdgeIfBothExist(from, to, false, graph); }
private void ProcessOperations(SemanticModel semanticModel, INamedTypeSymbol type, IntertypeRelationGraph graph, SyntaxNode node) { IOperation operation = semanticModel.GetOperation(node); ProcessOperation(type, operation, graph); }
private void AddEdgeIfBothExist(TypeDefinition from, TypeReference to, bool useEdge, IntertypeRelationGraph graph) { if (from == null || to == null) { return; } var genericType = to as GenericInstanceType; if (genericType != null && genericType.HasGenericArguments) { foreach (var genericArgument in genericType.GenericArguments) { AddUseEdgeIfBothExist(from, genericArgument, graph); } } if (!graph.Nodes.Contains(from.FullName) || !graph.Nodes.Contains(to.FullName)) { return; } if (useEdge) { graph.AddUseEdgeIfNotExists(from.FullName, to.FullName); } else { graph.AddInheritanceEdgeIfNotExists(from.FullName, to.FullName); } }
private void ProcessPropertySymbol(INamedTypeSymbol type, IPropertySymbol property, IntertypeRelationGraph graph) { AddUseEdgeIfBothExist(type, property.Type as INamedTypeSymbol, graph); ProcessMethodSymbol(type, property.SetMethod, graph); ProcessMethodSymbol(type, property.GetMethod, graph); }
private void ProcessMethodSymbol(INamedTypeSymbol type, IMethodSymbol method, IntertypeRelationGraph graph) { if (method == null) //Get or Set Method e.g. are not necessarily implemented { return; } foreach (var methodParameter in method.Parameters) { AddUseEdgeIfBothExist(type, methodParameter.Type as INamedTypeSymbol, graph); } AddUseEdgeIfBothExist(type, method.ReturnType as INamedTypeSymbol, graph); foreach (var typeParameter in method.TypeParameters) { foreach (var constraint in typeParameter.ConstraintTypes) { AddUseEdgeIfBothExist(type, constraint as INamedTypeSymbol, graph); } } foreach (var methodDeclaringSyntaxReference in method.DeclaringSyntaxReferences) { var methodSyntax = methodDeclaringSyntaxReference.GetSyntax(); var compilation = compilations.First(x => Equals(x.Assembly, method.ContainingAssembly)); var semanticModel = compilation.GetSemanticModel(methodSyntax.SyntaxTree); var accessorDec = methodSyntax as AccessorDeclarationSyntax; //Properties if (accessorDec?.Body != null) { ProcessOperations(semanticModel, type, graph, accessorDec.Body); continue; } var arrowExpr = methodSyntax as ArrowExpressionClauseSyntax; //Properties with Arrow if (arrowExpr?.Expression != null) { ProcessOperations(semanticModel, type, graph, arrowExpr.Expression); continue; } var methodDec = methodSyntax as BaseMethodDeclarationSyntax; //Methods, Constructors if (methodDec?.Body != null) { ProcessOperations(semanticModel, type, graph, methodDec.Body); } } }
private void ProcessPropertyDefinition(PropertyDefinition propertyDefinition, IntertypeRelationGraph graph, TypeDefinition currentType) { if (propertyDefinition.SetMethod != null) { ProcessMethodDefinition(propertyDefinition.SetMethod, graph, currentType); } if (propertyDefinition.GetMethod != null) { ProcessMethodDefinition(propertyDefinition.GetMethod, graph, currentType); } AddUseEdgeIfBothExist(currentType, propertyDefinition.PropertyType, graph); if (propertyDefinition.HasCustomAttributes) { foreach (var attribute in propertyDefinition.CustomAttributes) { AddUseEdgeIfBothExist(currentType, attribute.AttributeType, graph); } } }
private void ProcessOperation(INamedTypeSymbol type, IOperation operation, IntertypeRelationGraph graph) { foreach (var child in operation.Children) { ProcessOperation(type, child, graph); } //AddUseEdgeIfBothExist(type, operation.Type as INamedTypeSymbol, graph); switch (operation.Kind) { case OperationKind.DynamicMemberReference: var dynmemberRef = (IDynamicMemberReferenceOperation)operation; AddUseEdgeIfBothExist(type, dynmemberRef.ContainingType as INamedTypeSymbol, graph); foreach (var typeArg in dynmemberRef.TypeArguments) { AddUseEdgeIfBothExist(type, typeArg as INamedTypeSymbol, graph); } break; case OperationKind.EventReference: case OperationKind.FieldReference: case OperationKind.MethodReference: case OperationKind.PropertyReference: var memberReference = (IMemberReferenceOperation)operation; AddUseEdgeIfBothExist(type, memberReference.Member.ContainingType, graph); AddUseEdgeIfBothExist(type, memberReference.Member.ContainingType, graph); break; case OperationKind.TypeOf: var typeOfOp = (ITypeOfOperation)operation; AddUseEdgeIfBothExist(type, typeOfOp.TypeOperand as INamedTypeSymbol, graph); break; case OperationKind.ObjectCreation: var objectCreation = (IObjectCreationOperation)operation; AddUseEdgeIfBothExist(type, objectCreation.Constructor?.ContainingType, graph); break; case OperationKind.IsType: var isType = (IIsTypeOperation)operation; AddUseEdgeIfBothExist(type, isType.TypeOperand as INamedTypeSymbol, graph); break; case OperationKind.Tuple: var tuple = (ITupleOperation)operation; AddUseEdgeIfBothExist(type, tuple.NaturalType as INamedTypeSymbol, graph); break; case OperationKind.SizeOf: var sizeOf = (ISizeOfOperation)operation; AddUseEdgeIfBothExist(type, sizeOf.TypeOperand as INamedTypeSymbol, graph); break; case OperationKind.FieldInitializer: var fieldInit = (IFieldInitializerOperation)operation; foreach (var initializedField in fieldInit.InitializedFields) { AddUseEdgeIfBothExist(type, initializedField.Type as INamedTypeSymbol, graph); } break; case OperationKind.PropertyInitializer: var propertyInit = (IPropertyInitializerOperation)operation; foreach (var initProperty in propertyInit.InitializedProperties) { AddUseEdgeIfBothExist(type, initProperty.Type as INamedTypeSymbol, graph); } break; case OperationKind.ParameterInitializer: var paramInit = (IParameterInitializerOperation)operation; AddUseEdgeIfBothExist(type, paramInit.Parameter.ContainingType, graph); break; case OperationKind.VariableDeclarator: var variableDeclarator = (IVariableDeclaratorOperation)operation; AddUseEdgeIfBothExist(type, variableDeclarator.Symbol.Type as INamedTypeSymbol, graph); break; case OperationKind.Argument: var argumentOperation = (IArgumentOperation)operation; AddUseEdgeIfBothExist(type, argumentOperation.Parameter.Type as INamedTypeSymbol, graph); break; case OperationKind.CatchClause: var catchClause = (ICatchClauseOperation)operation; AddUseEdgeIfBothExist(type, catchClause.ExceptionType as INamedTypeSymbol, graph); break; case OperationKind.DeclarationPattern: var declarationpattern = (IDeclarationPatternOperation)operation; AddUseEdgeIfBothExist(type, declarationpattern.DeclaredSymbol as INamedTypeSymbol, graph); break; case OperationKind.Invocation: var incovation = (IInvocationOperation)operation; AddUseEdgeIfBothExist(type, incovation.TargetMethod?.ContainingType, graph); break; } }
private void ExtendAffectedTypesAndReportImpactedTests(AffectedType type, IntertypeRelationGraph graph, List <AffectedType> affectedTypes, ISet <TTestCase> allTests, ISet <ImpactedTest <TTestCase> > impactedTests, CancellationToken cancellationToken) { foreach (var test in allTests.Where(x => x.AssociatedClasses.Contains(type.Id))) { if (impactedTests.All(y => y.TestCase.Id != test.Id)) { impactedTests.Add(new ImpactedTest <TTestCase> { TestCase = test, ImpactedDueTo = type.ImpactedDueTo }); } } var usedByTypes = graph.UseEdges.Where(x => x.Item2 == type.Id).Select(x => x.Item1); foreach (string usedByType in usedByTypes) { cancellationToken.ThrowIfCancellationRequested(); if (affectedTypes.All(x => x.Id != usedByType)) { var newAffectedType = new AffectedType { Id = usedByType, ImpactedDueTo = type.ImpactedDueTo }; affectedTypes.Add(newAffectedType); ExtendAffectedTypesAndReportImpactedTests(newAffectedType, graph, affectedTypes, allTests, impactedTests, cancellationToken); } } // https://dl.acm.org/citation.cfm?id=2950361 // Section 2.1 Class-Level Static RTS (ClassSRTS) // "Note that ClassSRTS need not include supertypes of the changed types (but must include all subtypes) // in the transitive closure because a test cannot be affected statically by the changes even if the // test reaches supertype(s) of the changed types unless the test also reaches a changed type or (one of) its subtypes." var subTypes = graph.InheritanceEdges.Where(x => x.Item2 == type.Id).Select(x => x.Item1); foreach (string subtype in subTypes) { cancellationToken.ThrowIfCancellationRequested(); if (affectedTypes.All(x => x.Id != subtype)) { var newAffectedType = new AffectedType { Id = subtype, ImpactedDueTo = type.ImpactedDueTo }; affectedTypes.Add(newAffectedType); ExtendAffectedTypesAndReportImpactedTests(newAffectedType, graph, affectedTypes, allTests, impactedTests, cancellationToken); } } //However, this might not be true if dependency injection is used: //Instances of objects are injected dynamically which happens out of scope of the static analysis //-> Pessimistic approach selects also all super types var superTypes = graph.InheritanceEdges.Where(x => x.Item1 == type.Id).Select(x => x.Item2); foreach (string superType in superTypes) { cancellationToken.ThrowIfCancellationRequested(); if (affectedTypes.All(x => x.Id != superType)) { var newAffectedType = new AffectedType { Id = superType, ImpactedDueTo = type.ImpactedDueTo }; affectedTypes.Add(newAffectedType); ExtendAffectedTypesAndReportImpactedTests(newAffectedType, graph, affectedTypes, allTests, impactedTests, cancellationToken); } } }
private void ProcessEventSymbol(INamedTypeSymbol type, IEventSymbol eventSymbol, IntertypeRelationGraph graph) { ProcessMethodSymbol(type, eventSymbol.AddMethod, graph); ProcessMethodSymbol(type, eventSymbol.RemoveMethod, graph); ProcessMethodSymbol(type, eventSymbol.RaiseMethod, graph); AddUseEdgeIfBothExist(type, eventSymbol.Type as INamedTypeSymbol, graph); }