public override InjectableMethod Rate(InjectableType t, MethodDef m)
        {
            if (!m.HasBody || m.IsConstructor || m.IsStatic || !m.IsPublic || m.HasGenericParameters)
            {
                return(null);
            }
            var mask = t.TypeMapping.Source.Attributes &
                       (TypeAttributes.Abstract | TypeAttributes.Interface | TypeAttributes.Sealed);

            // static class is Abstract|Sealed, but we allow non-abstract Sealed
            if (mask != 0 && mask != TypeAttributes.Sealed)
            {
                return(null);
            }
            var td      = TD.Get(this, t);
            var rating  = td.Score;
            var parlist = new List <int>();

            foreach (var p in m.Parameters)
            {
                if (p.IsNormalMethodParameter)
                {
                    if (IsSuitableInput(p.Type))
                    {
                        parlist.Add(p.Index);
                    }
                }
            }

            return(new Injectable(t, m, rating + parlist.Count, parlist));
        }
Ejemplo n.º 2
0
        private static MethodArgumentDescriptor[] GetArgumentDescriptors(List <ParameterSyntax> argumentList, SemanticModel semanticModel, MockFramework mockFramework)
        {
            MethodArgumentDescriptor[] argumentDescriptors = new MethodArgumentDescriptor[argumentList.Count()];

            for (int i = 0; i < argumentDescriptors.Count(); i++)
            {
                string argumentName = argumentList[i].Identifier.Text;

                ParameterModifier modifier = GetArgumentModifier(argumentList[i]);

                var namedTypeSymbol = InjectableType.TryCreateInjectableTypeFromParameterNode(argumentList[i], semanticModel, mockFramework);

                if (namedTypeSymbol != null)
                {
                    argumentDescriptors[i] = new MethodArgumentDescriptor(namedTypeSymbol, argumentName, modifier);
                    continue;
                }

                var argumentType = argumentList[i].Type;

                string typeName = argumentType.ToString();

                argumentDescriptors[i] = new MethodArgumentDescriptor(new TypeDescriptor(typeName, null), argumentName, modifier);
            }

            return(argumentDescriptors);
        }
Ejemplo n.º 3
0
        private void WriteMockFieldInitializations(StringBuilder builder, TestGenerationContext context)
        {
            string template = context.Settings.GetTemplate(context.TestFramework, context.MockFramework, TemplateType.MockFieldInitialization);
            IDictionary <string, string> customMocks = context.Settings.CustomMocks;

            for (int i = 0; i < context.InjectedTypes.Count; i++)
            {
                InjectableType injectedType = context.InjectedTypes[i];
                string         line;
                if (customMocks.TryGetValue(injectedType.FullName, out string customMockClassFull))
                {
                    string customMockClassName = StringUtilities.GetShortNameFromFullTypeName(customMockClassFull);
                    line = ReplaceInterfaceTokensCustom(context.Settings.CustomMockFieldInitializationTemplate, injectedType, context, customMockClassName);
                }
                else
                {
                    line = ReplaceInterfaceTokens(template, injectedType, context);
                }

                builder.Append(line);

                if (i < context.InjectedTypes.Count - 1)
                {
                    builder.AppendLine();
                }
            }
        }
Ejemplo n.º 4
0
        private static string ReplaceInterfaceTokens(string template, InjectableType injectableType)
        {
            return(StringUtilities.ReplaceTokens(
                       template,
                       (tokenName, propertyIndex, builder) =>
            {
                switch (tokenName)
                {
                case "InterfaceName":
                    builder.Append(injectableType.TypeName);
                    break;

                case "InterfaceNameBase":
                    builder.Append(injectableType.TypeBaseName);
                    break;

                case "InterfaceType":
                    builder.Append(injectableType.ToString());
                    break;

                case "InterfaceMockName":
                    builder.Append(injectableType.MockName);
                    break;

                default:
                    // We didn't recognize it, just pass through.
                    builder.Append($"${tokenName}$");
                    break;
                }
            }));
        }
        private static bool WriteInterfaceContentToken(InjectableType injectableType, string tokenName, StringBuilder builder)
        {
            switch (tokenName)
            {
            case "InterfaceName":
                builder.Append(injectableType.TypeName);
                break;

            case "InterfaceNameBase":
                builder.Append(injectableType.TypeBaseName);
                break;

            case "InterfaceType":
                builder.Append(injectableType.ToString());
                break;

            case "InterfaceMockName":
                builder.Append(injectableType.MockName);
                break;

            default:
                return(false);
            }

            return(true);
        }
        private void WriteExplicitConstructor(StringBuilder builder, TestGenerationContext context, string currentIndent)
        {
            builder.Append($"new {context.ClassName}");

            if (context.ConstructorTypes.Count > 0)
            {
                builder.AppendLine("(");

                for (int i = 0; i < context.ConstructorTypes.Count; i++)
                {
                    string         mockReferenceStatement;
                    InjectableType constructorType = context.ConstructorTypes[i];
                    if (constructorType == null)
                    {
                        mockReferenceStatement = "TODO";
                    }
                    else
                    {
                        string template = this.Settings.GetTemplate(context.TestFramework, context.MockFramework, TemplateType.MockObjectReference);
                        mockReferenceStatement = ReplaceInterfaceTokens(template, constructorType, context);
                    }

                    builder.Append($"{currentIndent}    {mockReferenceStatement}");

                    if (i < context.ConstructorTypes.Count - 1)
                    {
                        builder.AppendLine(",");
                    }
                }

                builder.Append(")");
            }
            else if (context.Properties.Count == 0)
            {
                builder.Append("()");
            }

            if (context.Properties.Count > 0)
            {
                builder.AppendLine();
                builder.AppendLine("{");

                foreach (InjectableProperty property in context.Properties)
                {
                    string template = this.Settings.GetTemplate(context.TestFramework, context.MockFramework, TemplateType.MockObjectReference);
                    string mockReferenceStatement = ReplaceInterfaceTokens(template, property, context);

                    builder.AppendLine($"{property.PropertyName} = {mockReferenceStatement},");
                }

                builder.Append(@"}");
            }
        }
Ejemplo n.º 7
0
        private void WriteExplicitConstructor(StringBuilder builder, TestGenerationContext context, string currentIndent)
        {
            builder.Append($"new {context.ClassName}");

            if (context.ConstructorTypes.Count > 0)
            {
                builder.AppendLine("(");

                for (int i = 0; i < context.ConstructorTypes.Count; i++)
                {
                    string         mockReferenceStatement;
                    InjectableType constructorType = context.ConstructorTypes[i];
                    if (constructorType == null)
                    {
                        mockReferenceStatement = "TODO";
                    }
                    else
                    {
                        mockReferenceStatement = CreateMockReferenceStatement(context, constructorType);
                    }

                    builder.Append($"{currentIndent}    {mockReferenceStatement}");

                    if (i < context.ConstructorTypes.Count - 1)
                    {
                        builder.AppendLine(",");
                    }
                }

                builder.Append(")");
            }
            else if (context.Properties.Count == 0)
            {
                builder.Append("()");
            }

            if (context.Properties.Count > 0)
            {
                builder.AppendLine();
                builder.AppendLine("{");

                foreach (InjectableProperty property in context.Properties)
                {
                    builder.AppendLine($"{property.PropertyName} = {CreateMockReferenceStatement(context, property)},");
                }

                builder.Append(@"}");
            }
        }
        // Works for both field declarations and initializations.
        private static void WriteFieldLines(StringBuilder builder, TestGenerationContext context, string template)
        {
            for (int i = 0; i < context.InjectedTypes.Count; i++)
            {
                InjectableType injectedType = context.InjectedTypes[i];
                string         line         = ReplaceInterfaceTokens(template, injectedType, context);

                builder.Append(line);

                if (i < context.InjectedTypes.Count - 1)
                {
                    builder.AppendLine();
                }
            }
        }
            public TD(ValueManglingFeature f, InjectableType t)
            {
                Fields = new List <FieldDef>();
                foreach (var field in t.TypeMapping.Source.Fields)
                {
                    if (!field.IsStatic && !field.IsInitOnly && (field.IsPublic || field.IsFamilyOrAssembly))
                    {
                        var ft = field.FieldType;
                        if (f.IsSuitableInput(ft))
                        {
                            Fields.Add(field);
                        }
                    }
                }

                Score = t.Ctors.Count + Fields.Count;
            }
        private InjectableType CheckSuitable(TypeMapping tm)
        {
            var t = tm.Source;

            if (!t.IsPublic && !t.IsNestedPublic && !t.IsNestedAssembly)
            {
                return(NonInjectableType);
            }
            if (t.IsValueType)
            {
                return(NonInjectableType);
            }
            if (t.HasGenericParameters)
            {
                return(NonInjectableType);
            }
            var bt = t.BaseType;

            if (bt == null)
            {
                return(NonInjectableType);
            }
            InjectableType btr = null;

            if (bt.ToTypeSig().ElementType != ElementType.Object)
            {
                var k = bt.CreateKey();
                if (!Context.MappedTypes.TryGetValue(k, out var btm))
                {
                    return(NonInjectableType);
                }
                btr = GetInjectableType(btm);
                if (btr == NonInjectableType)
                {
                    return(NonInjectableType);
                }
            }

            var ctors  = t.FindInstanceConstructors().Where(IsSuitableCtor).ToList();
            var result = new InjectableType(tm, btr, ctors);

            _injectableTypes.Add(result);
            return(result);
        }
        private static string ReplaceInterfaceTokens(string template, InjectableType injectableType, TestGenerationContext context)
        {
            return(StringUtilities.ReplaceTokens(
                       template,
                       (tokenName, propertyIndex, builder) =>
            {
                if (WriteGlobalToken(tokenName, builder, context))
                {
                    return;
                }

                if (WriteInterfaceContentToken(injectableType, tokenName, builder))
                {
                    return;
                }

                WriteTokenPassthrough(tokenName, builder);
            }));
        }
Ejemplo n.º 12
0
        private static string CreateMockReferenceStatement(TestGenerationContext context, InjectableType injectedType)
        {
            string template;

            if (context.Settings.CustomMocks.ContainsKey(injectedType.FullName))
            {
                template = context.Settings.CustomMockObjectReferenceTemplate;
            }
            else
            {
                template = context.Settings.GetTemplate(context.TestFramework, context.MockFramework, TemplateType.MockObjectReference);
            }

            return(ReplaceInterfaceTokens(template, injectedType, context));
        }
Ejemplo n.º 13
0
 public InjectableId(InjectableType type, uint componentId)
 {
     this.type        = type;
     this.componentId = componentId;
 }
Ejemplo n.º 14
0
 public static TD Get(ValueManglingFeature f, InjectableType t) => t.Details(f, () => new TD(f, t));
Ejemplo n.º 15
0
 public InjectableIdAttribute(InjectableType type, uint componentId)
 {
     Id = new InjectableId(type, componentId);
 }
Ejemplo n.º 16
0
 public Injectable(InjectableType type, MethodDef method, int score, IReadOnlyList <int> arglist)
     : base(type, method, score)
 {
     _arglist = arglist;
 }
        private async Task <TestGenerationContext> CollectTestGenerationContextAsync(
            ProjectItemSummary selectedFile,
            string targetProjectNamespace,
            TestFramework testFramework,
            MockFramework mockFramework)
        {
            Microsoft.CodeAnalysis.Solution solution = CreateUnitTestBoilerplateCommandPackage.VisualStudioWorkspace.CurrentSolution;
            DocumentId documentId = solution.GetDocumentIdsWithFilePath(selectedFile.FilePath).FirstOrDefault();

            if (documentId == null)
            {
                throw new InvalidOperationException("Could not find document in solution with file path " + selectedFile.FilePath);
            }

            var document = solution.GetDocument(documentId);

            SyntaxNode root = await document.GetSyntaxRootAsync();

            SemanticModel semanticModel = await document.GetSemanticModelAsync();

            SyntaxNode firstClassDeclaration = root.DescendantNodes().FirstOrDefault(node => node.Kind() == SyntaxKind.ClassDeclaration);

            if (firstClassDeclaration == null)
            {
                throw new InvalidOperationException("Could not find class declaration.");
            }

            if (firstClassDeclaration.ChildTokens().Any(node => node.Kind() == SyntaxKind.AbstractKeyword))
            {
                throw new InvalidOperationException("Cannot unit test an abstract class.");
            }

            SyntaxToken classIdentifierToken = firstClassDeclaration.ChildTokens().FirstOrDefault(n => n.Kind() == SyntaxKind.IdentifierToken);

            if (classIdentifierToken == default(SyntaxToken))
            {
                throw new InvalidOperationException("Could not find class identifier.");
            }

            NamespaceDeclarationSyntax namespaceDeclarationSyntax = null;

            if (!TypeUtilities.TryGetParentSyntax(firstClassDeclaration, out namespaceDeclarationSyntax))
            {
                throw new InvalidOperationException("Could not find class namespace.");
            }

            // Find property injection types
            var injectableProperties = new List <InjectableProperty>();

            string           classFullName = namespaceDeclarationSyntax.Name + "." + classIdentifierToken;
            INamedTypeSymbol classType     = semanticModel.Compilation.GetTypeByMetadataName(classFullName);

            foreach (ISymbol member in classType.GetBaseTypesAndThis().SelectMany(n => n.GetMembers()))
            {
                if (member.Kind == SymbolKind.Property)
                {
                    IPropertySymbol property = (IPropertySymbol)member;

                    foreach (AttributeData attribute in property.GetAttributes())
                    {
                        if (PropertyInjectionAttributeNames.Contains(attribute.AttributeClass.ToString()))
                        {
                            var injectableProperty = InjectableProperty.TryCreateInjectableProperty(property.Name, property.Type.ToString(), mockFramework);
                            if (injectableProperty != null)
                            {
                                injectableProperties.Add(injectableProperty);
                            }
                        }
                    }
                }
            }

            string className = classIdentifierToken.ToString();

            // Find constructor injection types
            List <InjectableType> constructorInjectionTypes = new List <InjectableType>();

            SyntaxNode constructorDeclaration = firstClassDeclaration.ChildNodes().FirstOrDefault(n => n.Kind() == SyntaxKind.ConstructorDeclaration);

            if (constructorDeclaration != null)
            {
                constructorInjectionTypes.AddRange(
                    GetParameterListNodes(constructorDeclaration)
                    .Select(node => InjectableType.TryCreateInjectableTypeFromParameterNode(node, semanticModel, mockFramework)));
            }

            // Find public method declarations
            IList <MethodDescriptor> methodDeclarations = new List <MethodDescriptor>();

            foreach (MethodDeclarationSyntax methodDeclaration in
                     firstClassDeclaration.ChildNodes().Where(
                         n => n.Kind() == SyntaxKind.MethodDeclaration &&
                         ((MethodDeclarationSyntax)n).Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword))))
            {
                var parameterList = GetParameterListNodes(methodDeclaration).ToList();

                var parameterTypes = GetArgumentDescriptors(parameterList, semanticModel, mockFramework);

                var isAsync =
                    methodDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.AsyncKeyword)) ||
                    DoesReturnTask(methodDeclaration);

                var hasReturnType = !DoesReturnNonGenericTask(methodDeclaration) && !DoesReturnVoid(methodDeclaration);

                methodDeclarations.Add(new MethodDescriptor(methodDeclaration.Identifier.Text, parameterTypes, isAsync, hasReturnType));
            }

            string unitTestNamespace;

            string relativePath = this.GetRelativePath(selectedFile);

            if (string.IsNullOrEmpty(relativePath))
            {
                unitTestNamespace = targetProjectNamespace;
            }
            else
            {
                List <string> defaultNamespaceParts  = targetProjectNamespace.Split('.').ToList();
                List <string> unitTestNamespaceParts = new List <string>(defaultNamespaceParts);
                unitTestNamespaceParts.AddRange(relativePath.Split('\\'));

                unitTestNamespace = string.Join(".", unitTestNamespaceParts);
            }

            List <InjectableType> injectedTypes = new List <InjectableType>(injectableProperties);

            injectedTypes.AddRange(constructorInjectionTypes.Where(t => t != null));

            GenerateMockNames(injectedTypes);

            return(new TestGenerationContext(
                       mockFramework,
                       testFramework,
                       document,
                       unitTestNamespace,
                       className,
                       namespaceDeclarationSyntax.Name.ToString(),
                       injectableProperties,
                       constructorInjectionTypes,
                       injectedTypes,
                       methodDeclarations));
        }
        private async Task <TestGenerationContext> CollectTestGenerationContextAsync(
            ProjectItemSummary selectedFile,
            string targetProjectNamespace,
            TestFramework testFramework,
            MockFramework mockFramework,
            IBoilerplateSettings settings)
        {
            Microsoft.CodeAnalysis.Solution solution = CreateUnitTestBoilerplateCommandPackage.VisualStudioWorkspace.CurrentSolution;
            DocumentId documentId = solution.GetDocumentIdsWithFilePath(selectedFile.FilePath).FirstOrDefault();

            if (documentId == null)
            {
                throw new InvalidOperationException("Could not find document in solution with file path " + selectedFile.FilePath);
            }

            var document = solution.GetDocument(documentId);

            SyntaxNode root = await document.GetSyntaxRootAsync();

            SemanticModel semanticModel = await document.GetSemanticModelAsync();

            SyntaxNode firstClassLikeDeclaration = root.DescendantNodes().FirstOrDefault(node =>
            {
                var kind = node.Kind();
                return(kind == SyntaxKind.ClassDeclaration || kind == SyntaxKind.StructDeclaration || kind == (SyntaxKind)9063);                // record - Cannot update CodeAnalysis library because it's not found in VS 2019
            });

            if (firstClassLikeDeclaration == null)
            {
                throw new InvalidOperationException("Could not find class, struct or record declaration.");
            }

            if (firstClassLikeDeclaration.ChildTokens().Any(node => node.Kind() == SyntaxKind.AbstractKeyword))
            {
                throw new InvalidOperationException("Cannot unit test an abstract class.");
            }

            SyntaxToken classIdentifierToken = firstClassLikeDeclaration.ChildTokens().FirstOrDefault(n => n.Kind() == SyntaxKind.IdentifierToken);

            if (classIdentifierToken == default(SyntaxToken))
            {
                throw new InvalidOperationException("Could not find class identifier.");
            }

            object namespaceDeclarationSyntax = null;

            // 8842 is NamespaceDeclaration
            // 8845 is FileScopedNamespaceDeclaration
            // We would normally look for a node descended from BaseNamespaceDeclarationSyntax, but we don't have that type defined in the v1 Microsoft.CodeAnalysis DLL.
            // We can fix this once we are building against a higher version and can drop support for VS 2019.
            if (!TypeUtilities.TryGetParentSyntax(firstClassLikeDeclaration, (syntaxNode) => { return(syntaxNode.RawKind == 8842 || syntaxNode.RawKind == 8845); }, out namespaceDeclarationSyntax))
            {
                throw new InvalidOperationException("Could not find class namespace.");
            }

            // Find property injection types
            var injectableProperties = new List <InjectableProperty>();

            // We need to get the name via reflection since the DLL we are building against does not have the BaseNamespaceDeclarationSyntax or FileScopedNamespaceDeclarationSyntax types.
            string qualifiedNamespaceString = namespaceDeclarationSyntax.GetType().GetProperty("Name").GetValue(namespaceDeclarationSyntax, null).ToString();

            string           classFullName = qualifiedNamespaceString + "." + classIdentifierToken;
            INamedTypeSymbol classType     = semanticModel.Compilation.GetTypeByMetadataName(classFullName);

            foreach (ISymbol member in classType.GetBaseTypesAndThis().SelectMany(n => n.GetMembers()))
            {
                if (member.Kind == SymbolKind.Property)
                {
                    IPropertySymbol property = (IPropertySymbol)member;

                    foreach (AttributeData attribute in property.GetAttributes())
                    {
                        if (PropertyInjectionAttributeNames.Contains(attribute.AttributeClass.ToString()))
                        {
                            var injectableProperty = InjectableProperty.TryCreateInjectableProperty(property.Name, property.Type.ToString(), mockFramework);
                            if (injectableProperty != null)
                            {
                                injectableProperties.Add(injectableProperty);
                            }
                        }
                    }
                }
            }

            string className = classIdentifierToken.ToString();

            // Find constructor injection types
            List <InjectableType> constructorInjectionTypes = new List <InjectableType>();

            SyntaxNode constructorDeclaration = firstClassLikeDeclaration.ChildNodes().FirstOrDefault(n => n.Kind() == SyntaxKind.ConstructorDeclaration);

            if (constructorDeclaration != null)
            {
                constructorInjectionTypes.AddRange(
                    GetParameterListNodes(constructorDeclaration)
                    .Select(node => InjectableType.TryCreateInjectableTypeFromParameterNode(node, semanticModel, mockFramework)));
            }

            // Find public method declarations
            IList <MethodDescriptor> methodDeclarations = new List <MethodDescriptor>();

            foreach (MethodDeclarationSyntax methodDeclaration in
                     firstClassLikeDeclaration.ChildNodes().Where(
                         n => n.Kind() == SyntaxKind.MethodDeclaration &&
                         ((MethodDeclarationSyntax)n).Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword))))
            {
                var parameterList  = GetParameterListNodes(methodDeclaration).ToList();
                var parameterTypes = GetArgumentDescriptors(parameterList, semanticModel, mockFramework);

                var attributeList = GetAttributeListNodes(methodDeclaration);

                var isAsync =
                    methodDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.AsyncKeyword)) ||
                    DoesReturnTask(methodDeclaration);

                var hasReturnType = !DoesReturnNonGenericTask(methodDeclaration) && !DoesReturnVoid(methodDeclaration);

                string returnType = methodDeclaration.ReturnType.ToFullString();

                methodDeclarations.Add(new MethodDescriptor(methodDeclaration.Identifier.Text, parameterTypes, isAsync, hasReturnType, returnType, attributeList));
            }

            string unitTestNamespace;

            string relativePath = this.GetRelativePath(selectedFile);

            if (string.IsNullOrEmpty(relativePath))
            {
                unitTestNamespace = targetProjectNamespace;
            }
            else
            {
                List <string> defaultNamespaceParts  = targetProjectNamespace.Split('.').ToList();
                List <string> unitTestNamespaceParts = new List <string>(defaultNamespaceParts);
                unitTestNamespaceParts.AddRange(relativePath.Split('\\'));

                unitTestNamespace = string.Join(".", unitTestNamespaceParts);
            }

            List <InjectableType> injectedTypes = new List <InjectableType>(injectableProperties);

            injectedTypes.AddRange(constructorInjectionTypes.Where(t => t != null));

            GenerateMockNames(injectedTypes);

            return(new TestGenerationContext(
                       mockFramework,
                       testFramework,
                       document,
                       settings,
                       unitTestNamespace,
                       className,
                       qualifiedNamespaceString,
                       injectableProperties,
                       constructorInjectionTypes,
                       injectedTypes,
                       methodDeclarations));
        }