Skip to content

Commit 96b8dcd

Browse files
committed
VB.NET support
1 parent ad7a566 commit 96b8dcd

19 files changed

+755
-681
lines changed

Generator/CodeDomHelper.cs

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,118 @@
11
using System;
22
using System.CodeDom;
3-
using System.Collections.Generic;
4-
using System.Linq;
3+
using System.CodeDom.Compiler;
4+
using System.Globalization;
55
using System.Reflection;
6-
using System.Text;
76

87
namespace TechTalk.SpecFlow.Generator
98
{
10-
internal static class CodeDomHelper
9+
public enum GenerationTargetLanguage
1110
{
12-
static public CodeTypeReference CreateNestedTypeReference(CodeTypeDeclaration baseTypeDeclaration, string nestedTypeName)
11+
CSharp,
12+
VB,
13+
Other
14+
}
15+
16+
public interface ICodeDomHelperRequired
17+
{
18+
CodeDomHelper CodeDomHelper { get; set; }
19+
}
20+
21+
public class CodeDomHelper
22+
{
23+
public GenerationTargetLanguage TargetLanguage { get; private set; }
24+
25+
public CodeDomHelper(CodeDomProvider codeComProvider)
26+
{
27+
switch (codeComProvider.FileExtension.ToLower(CultureInfo.InvariantCulture))
28+
{
29+
case "cs":
30+
TargetLanguage = GenerationTargetLanguage.CSharp;
31+
break;
32+
case "vb":
33+
TargetLanguage = GenerationTargetLanguage.VB;
34+
break;
35+
default:
36+
TargetLanguage = GenerationTargetLanguage.Other;
37+
break;
38+
}
39+
}
40+
41+
public CodeDomHelper(GenerationTargetLanguage targetLanguage)
42+
{
43+
TargetLanguage = targetLanguage;
44+
}
45+
46+
public CodeTypeReference CreateNestedTypeReference(CodeTypeDeclaration baseTypeDeclaration, string nestedTypeName)
1347
{
1448
return new CodeTypeReference(baseTypeDeclaration.Name + "." + nestedTypeName);
1549
}
1650

17-
static public void SetTypeReferenceAsInterface(CodeTypeReference typeReference)
51+
public void SetTypeReferenceAsInterface(CodeTypeReference typeReference)
1852
{
1953
// this hack is necessary for VB.NET code generation
2054

21-
var isInterfaceField = typeReference.GetType().GetField("isInterface", BindingFlags.Instance | BindingFlags.NonPublic);
22-
if (isInterfaceField != null)
55+
if (TargetLanguage == GenerationTargetLanguage.VB)
56+
{
57+
var isInterfaceField = typeReference.GetType().GetField("isInterface",
58+
BindingFlags.Instance | BindingFlags.NonPublic);
59+
if (isInterfaceField == null)
60+
throw new InvalidOperationException("CodeDom version does not support VB.NET generation.");
61+
2362
isInterfaceField.SetValue(typeReference, true);
63+
}
64+
}
65+
66+
public void InjectIfRequired(object target)
67+
{
68+
ICodeDomHelperRequired codeDomHelperRequired = target as ICodeDomHelperRequired;
69+
if (codeDomHelperRequired != null)
70+
codeDomHelperRequired.CodeDomHelper = this;
71+
}
72+
73+
public void AddCommentStatement(CodeStatementCollection statements, string comment)
74+
{
75+
switch (TargetLanguage)
76+
{
77+
case GenerationTargetLanguage.CSharp:
78+
statements.Add(new CodeSnippetStatement("//" + comment));
79+
break;
80+
case GenerationTargetLanguage.VB:
81+
statements.Add(new CodeSnippetStatement("'" + comment));
82+
break;
83+
}
84+
}
85+
86+
public void BindTypeToSourceFile(CodeTypeDeclaration typeDeclaration, string fileName)
87+
{
88+
switch (TargetLanguage)
89+
{
90+
case GenerationTargetLanguage.CSharp:
91+
typeDeclaration.Members.Add(new CodeSnippetTypeMember(string.Format("#line 1 \"{0}\"", fileName)));
92+
typeDeclaration.Members.Add(new CodeSnippetTypeMember("#line hidden"));
93+
break;
94+
}
95+
}
96+
97+
public void AddSourceLinePragmaStatement(CodeStatementCollection statements, int lineNo, int colNo)
98+
{
99+
switch (TargetLanguage)
100+
{
101+
case GenerationTargetLanguage.CSharp:
102+
statements.Add(new CodeSnippetStatement(string.Format("#line {0}", lineNo)));
103+
AddCommentStatement(statements, string.Format("#indentnext {0}", colNo - 1));
104+
break;
105+
}
106+
}
107+
108+
public void AddDisableSourceLinePragmaStatement(CodeStatementCollection statements)
109+
{
110+
switch (TargetLanguage)
111+
{
112+
case GenerationTargetLanguage.CSharp:
113+
statements.Add(new CodeSnippetStatement("#line hidden"));
114+
break;
115+
}
24116
}
25117
}
26118
}

Generator/SpecFlowGenerator.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public override Encoding Encoding
7878
get { return innerWriter.Encoding; }
7979
}
8080

81-
static public readonly Regex indentNextRe = new Regex(@"^[\s\/\']*#indentnext (?<ind>\d+)\s*$");
81+
static private readonly Regex indentNextRe = new Regex(@"^[\s\/\']*#indentnext (?<ind>\d+)\s*$");
8282

8383
public override void WriteLine(string text)
8484
{
@@ -115,7 +115,7 @@ public void GenerateTestFile(SpecFlowFeatureFile featureFile, CodeDomProvider co
115115
{
116116
outputWriter = new HackedWriter(outputWriter);
117117

118-
var codeNamespace = GenerateTestFileCode(featureFile, inputReader);
118+
var codeNamespace = GenerateTestFileCode(featureFile, inputReader, codeProvider);
119119
var options = new CodeGeneratorOptions
120120
{
121121
BracingStyle = "C"
@@ -126,15 +126,19 @@ public void GenerateTestFile(SpecFlowFeatureFile featureFile, CodeDomProvider co
126126
outputWriter.Flush();
127127
}
128128

129-
public CodeNamespace GenerateTestFileCode(SpecFlowFeatureFile featureFile, TextReader inputReader)
129+
public CodeNamespace GenerateTestFileCode(SpecFlowFeatureFile featureFile, TextReader inputReader, CodeDomProvider codeProvider)
130130
{
131131
string targetNamespace = GetTargetNamespace(featureFile);
132132

133133
SpecFlowLangParser parser = new SpecFlowLangParser(project.GeneratorConfiguration.FeatureLanguage);
134134
Feature feature = parser.Parse(inputReader, featureFile.GetFullPath(project));
135135

136+
CodeDomHelper codeDomHelper = new CodeDomHelper(codeProvider);
137+
136138
IUnitTestGeneratorProvider generatorProvider = ConfigurationServices.CreateInstance<IUnitTestGeneratorProvider>(project.GeneratorConfiguration.GeneratorUnitTestProviderType);
137-
ISpecFlowUnitTestConverter testConverter = new SpecFlowUnitTestConverter(generatorProvider, project.GeneratorConfiguration.AllowDebugGeneratedFiles);
139+
codeDomHelper.InjectIfRequired(generatorProvider);
140+
141+
ISpecFlowUnitTestConverter testConverter = new SpecFlowUnitTestConverter(generatorProvider, codeDomHelper, project.GeneratorConfiguration.AllowDebugGeneratedFiles);
138142

139143
var codeNamespace = testConverter.GenerateUnitTestFixture(feature, null, targetNamespace);
140144
return codeNamespace;

Generator/SpecFlowUnitTestConverter.cs

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@ public class SpecFlowUnitTestConverter : ISpecFlowUnitTestConverter
3232
private const string SPECFLOW_NAMESPACE = "TechTalk.SpecFlow";
3333

3434
private readonly IUnitTestGeneratorProvider testGeneratorProvider;
35+
private readonly CodeDomHelper codeDomHelper;
3536
private readonly bool allowDebugGeneratedFiles;
3637

37-
public SpecFlowUnitTestConverter(IUnitTestGeneratorProvider testGeneratorProvider, bool allowDebugGeneratedFiles)
38+
public SpecFlowUnitTestConverter(IUnitTestGeneratorProvider testGeneratorProvider, CodeDomHelper codeDomHelper, bool allowDebugGeneratedFiles)
3839
{
3940
this.testGeneratorProvider = testGeneratorProvider;
41+
this.codeDomHelper = codeDomHelper;
4042
this.allowDebugGeneratedFiles = allowDebugGeneratedFiles;
4143
}
4244

@@ -125,7 +127,7 @@ private CodeMemberMethod GenerateTestFixtureSetup(CodeTypeDeclaration testType,
125127
setupMethod.Statements.Add(
126128
new CodeVariableDeclarationStatement(FEATUREINFO_TYPE, "featureInfo",
127129
new CodeObjectCreateExpression(FEATUREINFO_TYPE,
128-
new CodeObjectCreateExpression(typeof(CultureInfo),
130+
new CodeObjectCreateExpression(typeof(CultureInfo),
129131
new CodePrimitiveExpression(feature.Language)),
130132
new CodePrimitiveExpression(feature.Title),
131133
new CodePrimitiveExpression(feature.Description),
@@ -152,7 +154,7 @@ private CodeExpression GetStringArrayExpression(Tags tags)
152154
items.Add(new CodePrimitiveExpression(tag.Name));
153155
}
154156

155-
return new CodeArrayCreateExpression(typeof (string[]), items.ToArray());
157+
return new CodeArrayCreateExpression(typeof(string[]), items.ToArray());
156158
}
157159

158160
private CodeExpression GetStringArrayExpression(IEnumerable<string> items, ParameterSubstitution paramToIdentifier)
@@ -163,7 +165,7 @@ private CodeExpression GetStringArrayExpression(IEnumerable<string> items, Param
163165
expressions.Add(GetSubstitutedString(item, paramToIdentifier));
164166
}
165167

166-
return new CodeArrayCreateExpression(typeof (string[]), expressions.ToArray());
168+
return new CodeArrayCreateExpression(typeof(string[]), expressions.ToArray());
167169
}
168170

169171
private CodeMemberMethod GenerateTestFixtureTearDown(CodeTypeDeclaration testType)
@@ -327,7 +329,7 @@ private void GenerateScenarioOutlineBody(Feature feature, ScenarioOutline scenar
327329

328330
foreach (var pair in paramToIdentifier)
329331
{
330-
testMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof (string), pair.Value));
332+
testMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), pair.Value));
331333
}
332334

333335
GenerateTestBody(feature, scenarioOutline, testMethod, testSetup, paramToIdentifier);
@@ -397,7 +399,7 @@ private CodeMemberMethod GetTestMethodDeclaration(CodeTypeDeclaration testType,
397399
{
398400
CodeMemberMethod testMethod = new CodeMemberMethod();
399401
testType.Members.Add(testMethod);
400-
402+
401403
testMethod.Attributes = MemberAttributes.Public;
402404
testMethod.Name = string.Format(TEST_FORMAT, scenario.Title.ToIdentifier());
403405

@@ -446,7 +448,7 @@ private CodeExpression GetSubstitutedString(string text, ParameterSubstitution p
446448
formatArguments.Add(new CodeVariableReferenceExpression(id));
447449

448450
return new CodeMethodInvokeExpression(
449-
new CodeTypeReferenceExpression(typeof (string)),
451+
new CodeTypeReferenceExpression(typeof(string)),
450452
"Format",
451453
formatArguments.ToArray());
452454
}
@@ -513,50 +515,41 @@ private CodeExpression GetMultilineTextArgExpression(string multiLineTextArgumen
513515

514516
private void AddLinePragmaInitial(CodeTypeDeclaration testType, Feature feature)
515517
{
516-
if (allowDebugGeneratedFiles)
517-
return;
518+
if (allowDebugGeneratedFiles)
519+
return;
518520

519-
testType.Members.Add(new CodeSnippetTypeMember(string.Format("#line 1 \"{0}\"", Path.GetFileName(feature.SourceFile))));
520-
testType.Members.Add(new CodeSnippetTypeMember("#line hidden"));
521+
codeDomHelper.BindTypeToSourceFile(testType, Path.GetFileName(feature.SourceFile));
521522
}
522523

523524
private void AddLineDirectiveHidden(CodeStatementCollection statements)
524525
{
525526
if (allowDebugGeneratedFiles)
526527
return;
527528

528-
statements.Add(new CodeSnippetStatement("#line hidden"));
529+
codeDomHelper.AddDisableSourceLinePragmaStatement(statements);
529530
}
530531

531532
private void AddLineDirective(CodeStatementCollection statements, Background background)
532533
{
533-
AddLineDirective(statements, null, background.FilePosition);
534+
AddLineDirective(statements, background.FilePosition);
534535
}
535536

536537
private void AddLineDirective(CodeStatementCollection statements, Scenario scenario)
537538
{
538-
AddLineDirective(statements, null, scenario.FilePosition);
539+
AddLineDirective(statements, scenario.FilePosition);
539540
}
540541

541542
private void AddLineDirective(CodeStatementCollection statements, ScenarioStep step)
542543
{
543-
AddLineDirective(statements, null, step.FilePosition);
544+
AddLineDirective(statements, step.FilePosition);
544545
}
545546

546-
private void AddLineDirective(CodeStatementCollection statements, string sourceFile, FilePosition filePosition)
547+
private void AddLineDirective(CodeStatementCollection statements, FilePosition filePosition)
547548
{
548549
if (filePosition == null || allowDebugGeneratedFiles)
549550
return;
550551

551-
if (sourceFile == null)
552-
statements.Add(new CodeSnippetStatement(
553-
string.Format("#line {0}", filePosition.Line)));
554-
else
555-
statements.Add(new CodeSnippetStatement(
556-
string.Format("#line {0} \"{1}\"", filePosition.Line, Path.GetFileName(sourceFile))));
557-
558-
statements.Add(new CodeSnippetStatement(
559-
string.Format("//#indentnext {0}", filePosition.Column - 1)));
552+
codeDomHelper.AddSourceLinePragmaStatement(statements, filePosition.Line, filePosition.Column);
560553
}
561554

562555
#endregion

Generator/UnitTestProvider/XUnitTestGeneratorProvider.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace TechTalk.SpecFlow.Generator.UnitTestProvider
77
{
8-
public class XUnitTestGeneratorProvider : IUnitTestGeneratorProvider
8+
public class XUnitTestGeneratorProvider : IUnitTestGeneratorProvider, ICodeDomHelperRequired
99
{
1010
private const string FEATURE_TITLE_KEY = "FeatureTitle";
1111
private const string FEATURE_TITLE_PROPERTY_NAME = "FeatureTitle";
@@ -18,6 +18,8 @@ public class XUnitTestGeneratorProvider : IUnitTestGeneratorProvider
1818
private CodeTypeDeclaration _currentTestTypeDeclaration = null;
1919
private CodeTypeDeclaration _currentFixtureTypeDeclaration = null;
2020

21+
public CodeDomHelper CodeDomHelper { get; set; }
22+
2123
public void SetTestFixture(CodeTypeDeclaration typeDeclaration, string title, string description)
2224
{
2325
// xUnit does not use an attribute for the TestFixture, all public classes are potential fixtures

0 commit comments

Comments
 (0)