Skip to content

Commit

Permalink
Binding methods are executed using the culture of the feature file.
Browse files Browse the repository at this point in the history
  • Loading branch information
gasparnagy committed May 3, 2010
1 parent 4f604dc commit 851e6e0
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 37 deletions.
2 changes: 1 addition & 1 deletion Runtime/Bindings/StepArgumentTypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public object Convert(string value, Type typeToConvertTo, CultureInfo cultureInf
var stepTransformations = StepTransformations.Where(t => t.ReturnType == typeToConvertTo && t.Regex.IsMatch(value)).ToArray();
Debug.Assert(stepTransformations.Length <= 1, "You may not call Convert if CanConvert returns false");
if (stepTransformations.Length > 0)
return stepTransformations[0].Transform(value);
return stepTransformations[0].Transform(value, testTracer);

return ConvertSimple(typeToConvertTo, value, cultureInfo);
}
Expand Down
46 changes: 46 additions & 0 deletions Runtime/Bindings/StepMethodBinding.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using TechTalk.SpecFlow.Configuration;
using TechTalk.SpecFlow.ErrorHandling;
using TechTalk.SpecFlow.Tracing;

namespace TechTalk.SpecFlow.Bindings
{
Expand Down Expand Up @@ -106,6 +109,49 @@ private Delegate CreateFuncDelegate(MethodInfo method)
return lambda.Compile();
}

public object InvokeAction(object[] arguments, ITestTracer testTracer)
{
TimeSpan duration;
return InvokeAction(arguments, testTracer, out duration);
}

public object InvokeAction(object[] arguments, ITestTracer testTracer, out TimeSpan duration)
{
try
{
object result;
Stopwatch stopwatch = new Stopwatch();
using (new CultureInfoScope(FeatureContext.Current.FeatureInfo.Language))
{
stopwatch.Start();
result = BindingAction.DynamicInvoke(arguments);
stopwatch.Stop();
}

if (RuntimeConfiguration.Current.TraceTimings && stopwatch.Elapsed >= RuntimeConfiguration.Current.MinTracedDuration)
{
testTracer.TraceDuration(stopwatch.Elapsed, MethodInfo, arguments);
}

duration = stopwatch.Elapsed;
return result;
}
catch (ArgumentException ex)
{
throw errorProvider.GetCallError(MethodInfo, ex);
}
catch (TargetInvocationException invEx)
{
var ex = invEx.InnerException;
PreserveStackTrace(ex);
throw ex;
}
}

internal void PreserveStackTrace(Exception ex)
{
typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(ex, new object[0]);
}

#region extended action types
static readonly Type[] actionTypes = new Type[] { typeof(Action), typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>),
Expand Down
7 changes: 4 additions & 3 deletions Runtime/Bindings/StepTransformationBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using TechTalk.SpecFlow.Tracing;

namespace TechTalk.SpecFlow.Bindings
{
Expand All @@ -16,17 +17,17 @@ public StepTransformationBinding(string regexString, MethodInfo methodInfo)
Regex = regex;
}

public string[] GetStepTransformationArguments(string stepSnippet)
private object[] GetStepTransformationArguments(string stepSnippet)
{
var match = Regex.Match(stepSnippet);
var argumentStrings = match.Groups.Cast<Group>().Skip(1).Select(g => g.Value).ToArray();
return argumentStrings;
}

public object Transform(string value)
public object Transform(string value, ITestTracer testTracer)
{
var arguments = GetStepTransformationArguments(value);
return BindingAction.DynamicInvoke(arguments);
return InvokeAction(arguments, testTracer);
}
}
}
31 changes: 31 additions & 0 deletions Runtime/CultureInfoScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Globalization;
using System.Threading;
using TechTalk.SpecFlow.Tracing;

namespace TechTalk.SpecFlow
{
public class CultureInfoScope : IDisposable
{
private readonly CultureInfo originalCultureInfo;

public CultureInfoScope(CultureInfo cultureInfo)
{
if (cultureInfo != null && !cultureInfo.Equals(Thread.CurrentThread.CurrentCulture))
{
if (cultureInfo.IsNeutralCulture)
{
cultureInfo = LanguageHelper.GetSpecificCultureInfo(cultureInfo);
}
originalCultureInfo = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = cultureInfo;
}
}

public void Dispose()
{
if (originalCultureInfo != null)
Thread.CurrentThread.CurrentCulture = originalCultureInfo;
}
}
}
8 changes: 8 additions & 0 deletions Runtime/ObjectContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,5 +270,13 @@ private static TInterface GetOrCreate<TInterface>(ref TInterface storage, Func<T
}

#endregion

internal static void Reset()
{
foreach (var fieldInfo in typeof(ObjectContainer).GetFields(BindingFlags.Static | BindingFlags.NonPublic| BindingFlags.Public))
{
fieldInfo.SetValue(null, null);
}
}
}
}
1 change: 1 addition & 0 deletions Runtime/TechTalk.SpecFlow.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
</Compile>
<Compile Include="Bindings\StepDefinitionKeyword.cs" />
<Compile Include="Bindings\StepMethodBinding.cs" />
<Compile Include="CultureInfoScope.cs" />
<Compile Include="Steps.cs" />
<Compile Include="Bindings\StepTransformationBinding.cs" />
<Compile Include="Bindings\StepArgumentTypeConverter.cs" />
Expand Down
33 changes: 3 additions & 30 deletions Runtime/TestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ private void FireEvents(BindingEvent bindingEvent, IEnumerable<string> tags)
{
if (IsTagNeeded(eventBinding.Tags, tags))
{
InvokeAction(eventBinding.BindingAction, null, eventBinding.MethodInfo);
eventBinding.InvokeAction(null, testTracer);
}
}
}
Expand Down Expand Up @@ -402,7 +402,8 @@ internal void PreserveStackTrace(Exception ex)
private TimeSpan ExecuteStepMatch(BindingMatch match, object[] arguments)
{
OnStepStart();
TimeSpan duration = InvokeAction(match.StepBinding.BindingAction, arguments, match.StepBinding.MethodInfo);
TimeSpan duration;
match.StepBinding.InvokeAction(arguments, testTracer, out duration);
OnStepEnd();

return duration;
Expand All @@ -422,34 +423,6 @@ private void HandleBlockSwitch(ScenarioBlock block)
}
}

private TimeSpan InvokeAction(Delegate action, object[] arguments, MethodInfo methodInfo)
{
try
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
action.DynamicInvoke(arguments);
stopwatch.Stop();

if (RuntimeConfiguration.Current.TraceTimings && stopwatch.Elapsed >= RuntimeConfiguration.Current.MinTracedDuration)
{
testTracer.TraceDuration(stopwatch.Elapsed, methodInfo, arguments);
}

return stopwatch.Elapsed;
}
catch (ArgumentException ex)
{
throw errorProvider.GetCallError(methodInfo, ex);
}
catch (TargetInvocationException invEx)
{
var ex = invEx.InnerException;
PreserveStackTrace(ex);
throw ex;
}
}

private object[] GetExecuteArguments(BindingMatch match)
{
List<object> arguments = new List<object>();
Expand Down
1 change: 1 addition & 0 deletions Tests/RuntimeTests/RuntimeTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
<Compile Include="StepExecutionTests.cs" />
<Compile Include="StepExecutionTestsBase.cs" />
<Compile Include="StepExecutionTestsWithConversions.cs" />
<Compile Include="StepExecutionTestsWithConversionsInNonEnglishCulture.cs" />
<Compile Include="StepTransformationTests.cs" />
<Compile Include="StepArgumentTypeConverterTest.cs" />
<Compile Include="ConfigurationTest.cs" />
Expand Down
11 changes: 9 additions & 2 deletions Tests/RuntimeTests/StepExecutionTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,20 @@ public void TraceDuration(TimeSpan elapsed, string text)
}
#endregion

protected virtual CultureInfo GetLanguage()
{
return new CultureInfo("en-US");
}

[SetUp]
public void SetUp()
{
ObjectContainer.Reset();

MockRepository = new MockRepository();

// FeatureContext and ScenarioContext is needed, because the [Binding]-instances live there
Language = new CultureInfo("en-US");
Language = GetLanguage();
ObjectContainer.FeatureContext = new FeatureContext(new FeatureInfo(Language, "test feature", null));
ObjectContainer.ScenarioContext = new ScenarioContext(new ScenarioInfo("test scenario"));

Expand All @@ -82,7 +89,7 @@ public void SetUp()
ObjectContainer.TestTracer = new DummyTestTracer();
}

private TestRunner GetTestRunnerFor(params Type[] bindingTypes)
protected TestRunner GetTestRunnerFor(params Type[] bindingTypes)
{
bindingRegistry = new BindingRegistry();
foreach (var bindingType in bindingTypes)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Globalization;
using System.Threading;
using NUnit.Framework;
using Rhino.Mocks;

namespace TechTalk.SpecFlow.RuntimeTests
{
[Binding]
public class StepExecutionTestsBindingsForArgumentConvertInNonEnglishCulture
{
[Given("argument (.*) should be able to convert to (.*)")]
public virtual void InBindingConversion(string doubleParam, double expectedValue)
{
double value = Convert.ToDouble(doubleParam);
Assert.AreEqual(expectedValue, value);

Assert.AreEqual("de-DE", Thread.CurrentThread.CurrentCulture.Name);
}
}


[TestFixture]
public class StepExecutionTestsWithConversionsInNonEnglishCulture : StepExecutionTestsBase
{
protected override CultureInfo GetLanguage()
{
return new CultureInfo("de-DE");
}

[Test]
public void SholdCallBindingWithSimpleConvertParam()
{
StepExecutionTestsBindings bindingInstance;
TestRunner testRunner = GetTestRunnerFor(out bindingInstance);

bindingInstance.Expect(b => b.BindingWithSimpleConvertParam(1.23));

MockRepository.ReplayAll();

testRunner.Given("sample step with simple convert param: 1,23"); // German uses , as decimal separator

Assert.AreEqual(TestStatus.OK, ObjectContainer.ScenarioContext.TestStatus);
MockRepository.VerifyAll();
}

[Test]
public void ShouldExecuteBindingWithTheProperCulture()
{
TestRunner testRunner = GetTestRunnerFor(typeof(StepExecutionTestsBindingsForArgumentConvertInNonEnglishCulture));

MockRepository.ReplayAll();

testRunner.Given("argument 1,23 should be able to convert to 1,23"); // German uses , as decimal separator

Assert.AreEqual(TestStatus.OK, ObjectContainer.ScenarioContext.TestStatus);
MockRepository.VerifyAll();
}
}
}
5 changes: 4 additions & 1 deletion changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ New features:
+ SpecFlow feature files can be added also to VB.NET projects
+ Support for xUnit
+ Single installer for Visual Studio 2008 and 2010 (Issue 6, 10, 11)
+ SpecFlow Reporting doesn't work with Firefox (Issue 31)
+ Place GeneratedCodeAttribute and 'Designer generated code' region on generated code to
avoid having this code parsed by code analysis. (Issue 33)

Fixed issues:
+ SpecFlow Reporting doesn't work with Firefox (Issue 31)
+ Binding methods are executed using the culture of the feature file.

1.2.0 - 2009/11/25

New features:
Expand Down

0 comments on commit 851e6e0

Please sign in to comment.