Source code checked in, #62439
Created Issue: Intermittent event monitoring/assertion failure [10211]
System.Collections.Generic.KeyNotFoundException : The given key was not present in the dictionary.
at FluentAssertions.Common.BaseDictionary`2.get_Item(TKey key)
at FluentAssertions.EventMonitoring.EventMonitoringExtensions.ShouldRaise(Object eventSource, String eventName, String reason, Object[] reasonParameters)
at FluentAssertions.EventMonitoring.EventMonitoringExtensions.ShouldRaisePropertyChangeFor[T](T eventSource, Expression`1 propertyExpression, String reason, Object[] reasonParameters)
at FluentAssertions.EventMonitoring.EventMonitoringExtensions.ShouldRaisePropertyChangeFor[T](T eventSource, Expression`1 propertyExpression)
at XXX.Tests.ViewModels.XXXXViewModelTests.WhenXXXIsSet_ThenPropertyChangedShouldBeRaised()
The problem seems to be located in the ShouldRaise extension method:
public static EventRecorder ShouldRaise(this object eventSource, string eventName, string reason, params object[] reasonParameters)
{
<>c__DisplayClass4 class2;
if (!eventRecordersMap.ContainsKey(eventSource))
{
throw new InvalidOperationException(string.Format("Object <{0}> is not being monitored for events. Use the MonitorEvents() extension method to start monitoring events.", eventSource));
}
EventRecorder source = Enumerable.FirstOrDefault<EventRecorder>(
eventRecordersMap[eventSource], //<= !!!Here the exception gets thrown!!!
new Func<EventRecorder, bool>(class2, (IntPtr) this.<ShouldRaise>b__3));
if (source == null)
{
throw new InvalidOperationException(string.Format("Object <{0}> does not expose an event named \"{1}\".", eventSource, eventName));
}
if (!source.Any<RecordedEvent>())
{
Verification.Fail("Expected object {1} to raise event {0}{2}, but it did not.", eventName, eventSource, reason, reasonParameters, new object[0]);
}
return source;
}
The eventRecordersMap instance member is a WeakDictionary<TKey, TValue>. What happens is that the ContainsKey and TryGetValue (used by the indexer) have a different way of evaluating. ContainsKey will verify that a key is present in the dictionary (it won't verify if the key is alive). TryGetValue will lookup the value associated with the key and verify if the value is alive. This is why the first if statement succeeds and the second one fails, causing the aforementioned exception.
All this must mean the garbage collector has kicked in and marked an instance of the event monitor (the thing that is the value in the WeakDictionary, an enumerable of event recorders if I'm not mistaken) as dead (not alive). How did this happen? I've setup a [SetUp] method where I put something along the lines "_viewModel.MonitorEvents();". Notice how I'm not holding on to the event monitor. By not holding on to it, it becomes eligible for garbage collection, hence the fact that only "sometimes" this failure manifests itself. I'm not saying FluentAssertions is at fault, but this is a nasty side-effect that can bite you later on (depending on how you've written your tests). Some may never even notice it (it works every time on my machine, but not on the build server).
Commented Issue: Intermittent event monitoring/assertion failure [10211]
System.Collections.Generic.KeyNotFoundException : The given key was not present in the dictionary.
at FluentAssertions.Common.BaseDictionary`2.get_Item(TKey key)
at FluentAssertions.EventMonitoring.EventMonitoringExtensions.ShouldRaise(Object eventSource, String eventName, String reason, Object[] reasonParameters)
at FluentAssertions.EventMonitoring.EventMonitoringExtensions.ShouldRaisePropertyChangeFor[T](T eventSource, Expression`1 propertyExpression, String reason, Object[] reasonParameters)
at FluentAssertions.EventMonitoring.EventMonitoringExtensions.ShouldRaisePropertyChangeFor[T](T eventSource, Expression`1 propertyExpression)
at XXX.Tests.ViewModels.XXXXViewModelTests.WhenXXXIsSet_ThenPropertyChangedShouldBeRaised()
The problem seems to be located in the ShouldRaise extension method:
public static EventRecorder ShouldRaise(this object eventSource, string eventName, string reason, params object[] reasonParameters)
{
<>c__DisplayClass4 class2;
if (!eventRecordersMap.ContainsKey(eventSource))
{
throw new InvalidOperationException(string.Format("Object <{0}> is not being monitored for events. Use the MonitorEvents() extension method to start monitoring events.", eventSource));
}
EventRecorder source = Enumerable.FirstOrDefault<EventRecorder>(
eventRecordersMap[eventSource], //<= !!!Here the exception gets thrown!!!
new Func<EventRecorder, bool>(class2, (IntPtr) this.<ShouldRaise>b__3));
if (source == null)
{
throw new InvalidOperationException(string.Format("Object <{0}> does not expose an event named \"{1}\".", eventSource, eventName));
}
if (!source.Any<RecordedEvent>())
{
Verification.Fail("Expected object {1} to raise event {0}{2}, but it did not.", eventName, eventSource, reason, reasonParameters, new object[0]);
}
return source;
}
The eventRecordersMap instance member is a WeakDictionary<TKey, TValue>. What happens is that the ContainsKey and TryGetValue (used by the indexer) have a different way of evaluating. ContainsKey will verify that a key is present in the dictionary (it won't verify if the key is alive). TryGetValue will lookup the value associated with the key and verify if the value is alive. This is why the first if statement succeeds and the second one fails, causing the aforementioned exception.
All this must mean the garbage collector has kicked in and marked an instance of the event monitor (the thing that is the value in the WeakDictionary, an enumerable of event recorders if I'm not mistaken) as dead (not alive). How did this happen? I've setup a [SetUp] method where I put something along the lines "_viewModel.MonitorEvents();". Notice how I'm not holding on to the event monitor. By not holding on to it, it becomes eligible for garbage collection, hence the fact that only "sometimes" this failure manifests itself. I'm not saying FluentAssertions is at fault, but this is a nasty side-effect that can bite you later on (depending on how you've written your tests). Some may never even notice it (it works every time on my machine, but not on the build server).
Comments: ** Comment from web user: dennisdoomen **
I should have known this weak stuff is not reliable enough when I decided to incorporate somebody else's code. I'm going to investigate an alternative solution.
Created Issue: NuGet FluentAssertions install complains [10212]
Commented Issue: NuGet FluentAssertions install complains [10212]
Comments: ** Comment from web user: dennisdoomen **
I've just tried it on two systems, one with NuGet 1.0 and the other with NuGet 1.1. Do you have more info?
Source code checked in, #62591
Closed Issue: Intermittent event monitoring/assertion failure [10211]
System.Collections.Generic.KeyNotFoundException : The given key was not present in the dictionary.
at FluentAssertions.Common.BaseDictionary`2.get_Item(TKey key)
at FluentAssertions.EventMonitoring.EventMonitoringExtensions.ShouldRaise(Object eventSource, String eventName, String reason, Object[] reasonParameters)
at FluentAssertions.EventMonitoring.EventMonitoringExtensions.ShouldRaisePropertyChangeFor[T](T eventSource, Expression`1 propertyExpression, String reason, Object[] reasonParameters)
at FluentAssertions.EventMonitoring.EventMonitoringExtensions.ShouldRaisePropertyChangeFor[T](T eventSource, Expression`1 propertyExpression)
at XXX.Tests.ViewModels.XXXXViewModelTests.WhenXXXIsSet_ThenPropertyChangedShouldBeRaised()
The problem seems to be located in the ShouldRaise extension method:
public static EventRecorder ShouldRaise(this object eventSource, string eventName, string reason, params object[] reasonParameters)
{
<>c__DisplayClass4 class2;
if (!eventRecordersMap.ContainsKey(eventSource))
{
throw new InvalidOperationException(string.Format("Object <{0}> is not being monitored for events. Use the MonitorEvents() extension method to start monitoring events.", eventSource));
}
EventRecorder source = Enumerable.FirstOrDefault<EventRecorder>(
eventRecordersMap[eventSource], //<= !!!Here the exception gets thrown!!!
new Func<EventRecorder, bool>(class2, (IntPtr) this.<ShouldRaise>b__3));
if (source == null)
{
throw new InvalidOperationException(string.Format("Object <{0}> does not expose an event named \"{1}\".", eventSource, eventName));
}
if (!source.Any<RecordedEvent>())
{
Verification.Fail("Expected object {1} to raise event {0}{2}, but it did not.", eventName, eventSource, reason, reasonParameters, new object[0]);
}
return source;
}
The eventRecordersMap instance member is a WeakDictionary<TKey, TValue>. What happens is that the ContainsKey and TryGetValue (used by the indexer) have a different way of evaluating. ContainsKey will verify that a key is present in the dictionary (it won't verify if the key is alive). TryGetValue will lookup the value associated with the key and verify if the value is alive. This is why the first if statement succeeds and the second one fails, causing the aforementioned exception.
All this must mean the garbage collector has kicked in and marked an instance of the event monitor (the thing that is the value in the WeakDictionary, an enumerable of event recorders if I'm not mistaken) as dead (not alive). How did this happen? I've setup a [SetUp] method where I put something along the lines "_viewModel.MonitorEvents();". Notice how I'm not holding on to the event monitor. By not holding on to it, it becomes eligible for garbage collection, hence the fact that only "sometimes" this failure manifests itself. I'm not saying FluentAssertions is at fault, but this is a nasty side-effect that can bite you later on (depending on how you've written your tests). Some may never even notice it (it works every time on my machine, but not on the build server).
Comments: Resolved with changeset 62591.
Source code checked in, #62593
Source code checked in, #62637
Source code checked in, #62640
New Post: Floating point precision
Are there any plans to be able to specify the precision with which floating point assertions are made? For instance,
double angleOfAttack = CalculatePitchAtStallPoint(double airSpeed, double liftCoefficient); angleOfAttack.Should().Be(35.4).Within(0.2);
You can achieve that now by combining BeGreaterThan and BeLessThan, but it's a bit of a hack.
New Post: Floating point precision
Sounds like a good idea. You shouldn't be able to compare floating point values anyway.
It is a breaking change though, because your proposal won't work. People expect the Be() to do the actual checking, but the Within() is required to determine the precision.
It should be something like this, though I don't particularly like this.
angleOfAttack.Should().BeWithin(35.4, 0.2)
Any better ideas?
New Post: ShouldNotRaise() on EventAssertions
I'm not sure you noticed, but I've included your proposal in the trunk:
http://fluentassertions.codeplex.com/SourceControl/changeset/changes/62359
Created Feature: Resolve name of event in ShouldRaise using lambda expression [10233]
Created Issue: Add extension ShouldNotRaise [10234]
New Post: ShouldNotRaise() on EventAssertions
Hi
I have saw it last friday. Just have checked-out the latest trunk and built it. It works great..
I also have seen that you have integrated to assertion extensions for byte values. tank a lot!
Have you an plans to release a offical build containg all these changes?
Keep up the good work
Kindly regards
Marco
New Post: Floating point precision
Your solution is probably the most logical given how the existing assertions are done. Another option could be:
angleOfAttack.Should().BeWithin(35.4, 1);
where 1 is the number of decimal places that you want to be accurate to. Or maybe:
angleOfAttack.Should().BeInRange(35.2, 35.6);
to specify the upper and lower bounds of acceptable values.
Commented Feature: Add extension ShouldNotRaise [10234]
Comments: ** Comment from web user: dennisdoomen **
It was already added a few weeks ago:
http://fluentassertions.codeplex.com/SourceControl/changeset/changes/62359
Commented Feature: Resolve name of event in ShouldRaise using lambda expression [10233]
Comments: ** Comment from web user: dennisdoomen **
I've looked at that, but couldn't find a syntax that allows referencing an event using a Lambda statement.
New Post: ShouldNotRaise() on EventAssertions
I'm waiting for a confirmation about a fix fo a bug somebody found in very particular case. If that's confirmed and I've updated the documentation, I'll release this version as 1.4.0.0.