Example #1
0
        public void AsyncValidation_SeveralAsyncRules_AllExecutedBeforeValidationCompleted()
        {
            TestUtils.ExecuteWithDispatcher((dispatcher, completedAction) =>
            {
                var vm = new DummyViewModel();
                vm.Foo = null;

                var validation = new ValidationHelper();

                bool rule1Executed = false;

                validation.AddAsyncRule(() =>
                {
                    return(Task.Run(() =>
                    {
                        rule1Executed = true;
                        return RuleResult.Valid();
                    }));
                });

                bool rule2Executed = false;

                validation.AddAsyncRule(() =>
                {
                    return(Task.Run(() =>
                    {
                        rule2Executed = true;
                        return RuleResult.Valid();
                    }));
                });

                bool rule3Executed = false;

                validation.AddAsyncRule(() =>
                {
                    return(Task.Run(() =>
                    {
                        rule3Executed = true;
                        return RuleResult.Valid();
                    }));
                });

                validation.ValidateAllAsync().ContinueWith(r =>
                {
                    Assert.True(rule1Executed);
                    Assert.True(rule2Executed);
                    Assert.True(rule3Executed);
                    Assert.True(r.Result.IsValid);

                    completedAction();
                });
            });
        }
Example #2
0
        public void ValidatedAsync_AsyncRuleDoesnotCallCallback_ThrowsAnExceptionAfterTimeout()
        {
            TestUtils.ExecuteWithDispatcher((uiThreadDispatcher, completedAction) =>
            {
                // ARRANGE
                var validation = new ValidationHelper();
                validation.AsyncRuleExecutionTimeout = TimeSpan.FromSeconds(0.1);

                var dummy = new DummyViewModel();

                validation.AddAsyncRule(() => dummy.Foo,
                                        onCompleted =>
                {
                    // Do nothing
                });

                // ACT
                var ui = TaskScheduler.FromCurrentSynchronizationContext();

                validation.ValidateAllAsync().ContinueWith(result =>
                {
                    Assert.True(result.IsFaulted, "Validation task must fail.");
                    Assert.NotNull(result.Exception);

                    completedAction();
                }, ui);
            });
        }
Example #3
0
        public void ValidateAsync_WithCallback_ValidationOccuredAndCallbackIsCalledOnUIThread()
        {
            TestUtils.ExecuteWithDispatcher((dispatcher, completedAction) =>
            {
                var vm            = new DummyViewModel();
                vm.Foo            = null;
                bool ruleExecuted = false;

                var validation = new ValidationHelper();

                validation.AddAsyncRule(setResult =>
                {
                    ruleExecuted = true;
                    setResult(RuleResult.Invalid("Error1"));
                });

                validation.ValidateAllAsync(result =>
                {
                    Assert.True(ruleExecuted, "Validation rule must be executed before validation callback is called.");
                    Assert.Equal(dispatcher.Thread.ManagedThreadId, Thread.CurrentThread.ManagedThreadId);

                    completedAction();
                });
            });
        }
Example #4
0
        public void AsyncValidation_DependantProperties_IfOneInvalidSecondIsInvalidToo()
        {
            TestUtils.ExecuteWithDispatcher((dispatcher, testCompleted) =>
            {
                var vm = new DummyViewModel
                {
                    Foo = "abc",
                    Bar = "abc"
                };

                Func <bool> validCondition = () => vm.Foo != vm.Bar;

                var validation = new ValidationHelper();

                validation.AddAsyncRule(
                    () => vm.Foo,
                    () => vm.Bar,
                    setResult =>
                {
                    ThreadPool.QueueUserWorkItem(_ =>
                    {
                        setResult(RuleResult.Assert(validCondition(), "Foo must be different than bar"));
                    });
                });

                validation.ValidateAsync(() => vm.Bar).ContinueWith(r =>
                {
                    Assert.False(r.Result.IsValid, "Validation must fail");
                    Assert.True(r.Result.ErrorList.Count == 2, "There must be 2 errors: one for each dependant property");

                    testCompleted();
                });
            });
        }
Example #5
0
        public void AsyncValidation_SeveralAsyncRules_AllExecutedBeforeValidationCompleted()
        {
            TestUtils.ExecuteWithDispatcher((dispatcher, completedAction) =>
            {
                var vm = new DummyViewModel();
                vm.Foo = null;

                var validation = new ValidationHelper();

                bool rule1Executed = false;

                validation.AddAsyncRule(setResultDelegate => ThreadPool.QueueUserWorkItem(_ =>
                {
                    rule1Executed = true;
                    setResultDelegate(RuleResult.Valid());
                }));

                bool rule2Executed = false;

                validation.AddAsyncRule(setResultDelegate => ThreadPool.QueueUserWorkItem(_ =>
                {
                    rule2Executed = true;
                    setResultDelegate(RuleResult.Valid());
                }));

                bool rule3Executed = false;

                validation.AddAsyncRule(setResultDelegate => ThreadPool.QueueUserWorkItem(_ =>
                {
                    rule3Executed = true;
                    setResultDelegate(RuleResult.Valid());
                }));

                validation.ValidateAllAsync().ContinueWith(r =>
                {
                    Assert.True(rule1Executed);
                    Assert.True(rule2Executed);
                    Assert.True(rule3Executed);
                    Assert.True(r.Result.IsValid);

                    completedAction();
                });
            });
        }
Example #6
0
        public void AsyncValidation_ExecutedInsideUIThreadTask_ValidationRunsInBackgroundThread()
        {
            // This test checks the situation described here:
            // http://stackoverflow.com/questions/6800705/why-is-taskscheduler-current-the-default-taskscheduler
            // In short: if a continuation of a task runs on the UI thread, then the tasks that you start using
            // Task.Factory.StartNew will run also on the UI thread. This is unexpected an may cause a deadlock, or freezing UI.

            TestUtils.ExecuteWithDispatcher((uiThreadDispatcher, completedAction) =>
            {
                // ARRANGE
                var validation = new ValidationHelper();

                var dummy = new DummyViewModel();

                int uiThreadId         = Thread.CurrentThread.ManagedThreadId;
                int validationThreadId = uiThreadId;

                validation.AddAsyncRule(nameof(dummy.Foo),
                                        () =>
                {
                    return(Task.Run(() =>
                    {
                        validationThreadId = Thread.CurrentThread.ManagedThreadId;

                        return RuleResult.Valid();
                    }));
                });

                // ACT

                // Execute validation within a continuation of another task that runs on
                // current synchronization context, so that the TaskScheduler.Current is set to the
                // UI context.
                Task.Factory.StartNew(() => { }).ContinueWith(t =>
                {
                    Task <ValidationResult> task = validation.ValidateAllAsync();

                    task.ContinueWith(result =>
                    {
                        // VERIFY

                        // Schedule the varification code with the dispatcher in order to take it
                        // out of the ContinueWith continuation. Otherwise the exceptions will be stored inside the task and
                        // unit testing framework will not know about them.
                        uiThreadDispatcher.BeginInvoke(new Action(() =>
                        {
                            Assert.False(uiThreadId == validationThreadId,
                                         "Validation must be executed in a background thread, so that the UI thread is not blocked.");

                            completedAction();
                        }));
                    });
                }, TaskScheduler.FromCurrentSynchronizationContext());
            });
        }
Example #7
0
        public void AsyncValidation_GeneralSmokeTest()
        {
            TestUtils.ExecuteWithDispatcher((dispatcher, completedAction) =>
            {
                var vm = new DummyViewModel {
                    Foo = null
                };

                var validation = new ValidationHelper();

                bool ruleExecuted = false;

                // OK, this is really strange, but if Action<bool> is not mentioned anywhere in the project, then ReSharter would fail to build and run the test...
                // So including the following line to fix it.
                Action <RuleResult> dummy = null;
                Assert.Null(dummy); // Getting rid of the "unused variable" warning.

                validation.AddAsyncRule(async() =>
                {
                    return(await Task.Run(() =>
                    {
                        ruleExecuted = true;

                        return RuleResult.Invalid("Foo cannot be empty string.");
                    }));
                });

                validation.ResultChanged += (o, e) =>
                {
                    Assert.True(ruleExecuted,
                                "Validation rule must be executed before ValidationCompleted event is fired.");

                    var isUiThread = dispatcher.Thread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId;

                    Assert.True(isUiThread, "ValidationResultChanged must be executed on UI thread");
                };

                var ui = TaskScheduler.FromCurrentSynchronizationContext();

                validation.ValidateAllAsync().ContinueWith(r =>
                {
                    var isUiThread = dispatcher.Thread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId;

                    Assert.True(isUiThread, "Validation callback must be executed on UI thread");

                    Assert.False(r.Result.IsValid, "Validation must fail according to the validaton rule");
                    Assert.False(validation.GetResult().IsValid, "Validation must fail according to the validaton rule");

                    Assert.True(ruleExecuted, "Rule must be executed before validation completed callback is executed.");

                    completedAction();
                }, ui);
            });
        }
Example #8
0
        public async Task ValidateAsync_AsyncRuleRegisteredWithNewSyntax_RuleIsExecuted()
        {
            // ARRANGE
            var validator = new ValidationHelper();

            validator.AddAsyncRule(async() => await Task.Factory.StartNew(() => RuleResult.Invalid("error")));

            // ACT
            var result = await validator.ValidateAllAsync();

            // VERIFY
            Assert.False(result.IsValid);
        }
Example #9
0
        public void Validate_ThereAreAsyncRules_ThrowsException()
        {
            // ARRANGE
            var validation = new ValidationHelper();

            // Add a simple sync rule
            validation.AddRule(RuleResult.Valid);

            // Add an async rule
            validation.AddAsyncRule(() => Task.Run(() => RuleResult.Invalid("Error")));

            // ACT
            Assert.Throws <InvalidOperationException>(() => { validation.ValidateAll(); });
        }
		public void AsyncValidation_GeneralSmokeTest()
		{
			TestUtils.ExecuteWithDispatcher((dispatcher, completedAction) =>
			{
				var vm = new DummyViewModel {Foo = null};

				var validation = new ValidationHelper();
				
				bool ruleExecuted = false;

				// OK, this is really strange, but if Action<bool> is not mentioned anywhere in the project, then ReSharter would fail to build and run the test... 
				// So including the following line to fix it.
				Action<RuleResult> dummy = null;
				Assert.Null(dummy); // Getting rid of the "unused variable" warning.

				validation.AddAsyncRule(setResult => ThreadPool.QueueUserWorkItem(_ =>
				{
					ruleExecuted = true;

					setResult(RuleResult.Invalid("Foo cannot be empty string."));
				}));

				validation.ResultChanged += (o, e) =>
				{
					Assert.True(ruleExecuted, "Validation rule must be executed before ValidationCompleted event is fired.");

					var isUiThread = dispatcher.Thread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId;

					Assert.True(isUiThread, "ValidationResultChanged must be executed on UI thread");
				};

				var ui = TaskScheduler.FromCurrentSynchronizationContext();

				validation.ValidateAllAsync().ContinueWith(r =>
				{
					var isUiThread = dispatcher.Thread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId;

					Assert.True(isUiThread, "Validation callback must be executed on UI thread");

					Assert.False(r.Result.IsValid, "Validation must fail according to the validaton rule");
					Assert.False(validation.GetResult().IsValid, "Validation must fail according to the validaton rule");

					Assert.True(ruleExecuted, "Rule must be executed before validation completed callback is executed.");

					completedAction();
				}, ui);
			});
		}
Example #11
0
        public async void AsyncValidation_RuleThrowsException_ExceptionIsPropogated()
        {
            // ARRANGE
            var validator = new ValidationHelper();

            validator.AddAsyncRule(async() =>
            {
                await
                Task.Factory.StartNew(() => { throw new InvalidOperationException("Test"); }, CancellationToken.None,
                                      TaskCreationOptions.None, TaskScheduler.Default);

                return(RuleResult.Valid());
            });

            // ACT & VERIFY
            await Assert.ThrowsAsync <ValidationException>(() => validator.ValidateAllAsync());
        }
Example #12
0
        public void AsyncValidation_MultipleRulesForSameTarget_DoesNotExecuteRulesIfPerviousFailed()
        {
            TestUtils.ExecuteWithDispatcher((uiThreadDispatcher, completedAction) =>
            {
                // ARRANGE
                var validation = new ValidationHelper();
                var dummy      = new DummyViewModel();

                bool firstRuleExecuted  = false;
                bool secondRuleExecuted = false;

                validation.AddRule(nameof(dummy.Foo),
                                   () =>
                {
                    firstRuleExecuted = true;
                    return(RuleResult.Invalid("Error1"));
                });

                validation.AddAsyncRule(nameof(dummy.Foo),
                                        () =>
                {
                    return(Task.Run(() =>
                    {
                        secondRuleExecuted = true;
                        return RuleResult.Invalid("Error2");
                    }));
                });

                // ACT

                validation.ValidateAllAsync().ContinueWith(result =>
                {
                    // VERIFY

                    Assert.True(firstRuleExecuted, "First rule must have been executed");
                    Assert.False(secondRuleExecuted,
                                 "Second rule should not have been executed because first rule failed.");

                    completedAction();
                });
            });
        }
Example #13
0
        public async void MixedValidation_SyncRuleThrowsExceptionAfterSuccesfullAsyncRule_ExceptionIsPropogated()
        {
            // ARRANGE
            var validator = new ValidationHelper();

            validator.AddAsyncRule(async() =>
            {
                return(await Task.Run(() => RuleResult.Valid()));
            });

            validator.AddRule(() => { throw new InvalidOperationException("Test"); });

            // ACT & VERIFY
            var task = Assert.ThrowsAsync <ValidationException>(() => validator.ValidateAllAsync());

            if (task != await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(5))))
            {
                Assert.True(false, "Looks like the validation is stuck (didn't complete in a timeout), which means it didn't handle the exception properly.");
            }
        }
		public void AsyncValidation_RuleThrowsException_ExceptionIsPropogated()
		{
			// ARRANGE
			var validator = new ValidationHelper();

			validator.AddAsyncRule(async () =>
			{
				await Task.Factory.StartNew(() =>
				{
					throw new InvalidOperationException("Test");
				}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);

				return RuleResult.Valid();
			});

			// ACT & VERIFY
			Assert.Throws<ValidationException>(validator.ValidateAllAsync());
		}
Example #15
0
        public void ValidateAllAsync_SimilteniousCalls_DoesNotFail()
        {
            TestUtils.ExecuteWithDispatcher((dispatcher, completedAction) =>
            {
                const int numThreadsPerIternation = 4;
                const int iterationCount          = 10;
                const int numThreads = numThreadsPerIternation * iterationCount;
                var resetEvent       = new ManualResetEvent(false);
                int toProcess        = numThreads;

                var validation = new ValidationHelper();

                for (int i = 0; i < iterationCount; i++)
                {
                    var target1 = new object();
                    var target2 = new object();

                    validation.AddAsyncRule(setResult =>
                    {
                        setResult(RuleResult.Invalid("Error1"));
                    });

                    validation.AddAsyncRule(target1, setResult =>
                    {
                        setResult(RuleResult.Valid());
                    });

                    validation.AddRule(target2, () =>
                    {
                        return(RuleResult.Invalid("Error2"));
                    });

                    validation.AddRule(target2, RuleResult.Valid);

                    Action <Action> testThreadBody = exercise =>
                    {
                        try
                        {
                            exercise();

                            if (Interlocked.Decrement(ref toProcess) == 0)
                            {
                                resetEvent.Set();
                            }
                        }
                        catch (Exception ex)
                        {
                            dispatcher.BeginInvoke(new Action(() =>
                            {
                                throw new AggregateException(ex);
                            }));
                        }
                    };

                    var thread1 = new Thread(() =>
                    {
                        testThreadBody(() =>
                        {
                            validation.ValidateAllAsync().Wait();
                        });
                    });

                    var thread2 = new Thread(() =>
                    {
                        testThreadBody(() =>
                        {
                            validation.ValidateAllAsync().Wait();
                        });
                    });

                    var thread3 = new Thread(() =>
                    {
                        testThreadBody(() =>
                        {
                            validation.Validate(target2);
                        });
                    });

                    var thread4 = new Thread(() =>
                    {
                        testThreadBody(() =>
                        {
                            validation.Validate(target2);
                        });
                    });

                    thread1.Start();
                    thread2.Start();
                    thread3.Start();
                    thread4.Start();
                }

                ThreadPool.QueueUserWorkItem(_ =>
                {
                    resetEvent.WaitOne();
                    completedAction();
                });
            });
        }
		public void AsyncValidation_MixedAsyncAndNotAsyncRules_AllExecutedBeforeValidationCompleted()
		{
			TestUtils.ExecuteWithDispatcher((dispatcher, completedAction) =>
			{
				var vm = new DummyViewModel();

				var validation = new ValidationHelper();

				bool rule1Executed = false;

				validation.AddAsyncRule(vm, setResultDelegate => ThreadPool.QueueUserWorkItem(_ =>
				{
					rule1Executed = true;
					setResultDelegate(RuleResult.Valid());
				}));

				bool rule2Executed = false;

				validation.AddRule(vm, () =>
				{
					rule2Executed = true;
					return RuleResult.Valid();
				});

				bool rule3Executed = false;

				validation.AddAsyncRule(vm, setResultDelegate => ThreadPool.QueueUserWorkItem(_ =>
				{
					rule3Executed = true;
					setResultDelegate(RuleResult.Valid());
				}));

				validation.ValidateAllAsync().ContinueWith(r =>
				{
					Assert.True(rule1Executed);
					Assert.True(rule2Executed);
					Assert.True(rule3Executed);
					Assert.True(r.Result.IsValid);

					completedAction();
				});
			});
		}
		public void ValidateAsync_MultipleRulesForSameTarget_DoesNotExecuteRulesIfPerviousFailed()
		{
			TestUtils.ExecuteWithDispatcher((uiThreadDispatcher, completedAction) =>
			{
				// ARRANGE
				var validation = new ValidationHelper();
				var dummy = new DummyViewModel();

				bool firstRuleExecuted = false;
				bool secondRuleExecuted = false;

				validation.AddRule(() => dummy.Foo,
				                   () =>
				                   {
				                   	firstRuleExecuted = true;
				                   	return RuleResult.Invalid("Error1");
				                   });

				validation.AddAsyncRule(() => dummy.Foo,
				                        onCompleted =>
				                        {
				                        	secondRuleExecuted = true;
				                        	onCompleted(RuleResult.Invalid("Error2"));
				                        });

				// ACT

				validation.ValidateAllAsync().ContinueWith(result =>
				{
					// VERIFY

					Assert.True(firstRuleExecuted, "First rule must have been executed");
					Assert.False(secondRuleExecuted, "Second rule should not have been executed because first rule failed.");

					completedAction();
				});
			});
		}
		public void ValidatedAsync_AsyncRuleDoesnotCallCallback_ThrowsAnExceptionAfterTimeout()
		{
			TestUtils.ExecuteWithDispatcher((uiThreadDispatcher, completedAction) =>
			{
				// ARRANGE
				var validation = new ValidationHelper();
				validation.AsyncRuleExecutionTimeout = TimeSpan.FromSeconds(0.1);

				var dummy = new DummyViewModel();
				
				validation.AddAsyncRule(() => dummy.Foo,
										onCompleted =>
										{
											// Do nothing
										});

				// ACT
				var ui = TaskScheduler.FromCurrentSynchronizationContext();

				validation.ValidateAllAsync().ContinueWith(result =>
				{
					Assert.True(result.IsFaulted, "Validation task must fail.");
					Assert.NotNull(result.Exception);

					completedAction();
				}, ui);
			});
		}
        public void Validate_ThereAreAsyncRules_ThrowsException()
        {
            // ARRANGE
            var validation = new ValidationHelper();

            // Add a simple sync rule
            validation.AddRule(RuleResult.Valid);

            // Add an async rule
            validation.AddAsyncRule(onCompleted => onCompleted(RuleResult.Invalid("Error")));

            // ACT
            Assert.Throws<InvalidOperationException>(() =>
                {
                    validation.ValidateAll();
                });
        }
		public void ValidatedAsync_ExecutedInsideUIThreadTask_ValidationRunsInBackgroundThread()
		{
			// This test checks the situation described here:
			// http://stackoverflow.com/questions/6800705/why-is-taskscheduler-current-the-default-taskscheduler
			// In short: if a continuation of a task runs on the UI thread, then the tasks that you start using
			// Task.Factory.StartNew will run also on the UI thread. This is unexpected an may cause a deadlock, or freezing UI.

			TestUtils.ExecuteWithDispatcher((uiThreadDispatcher, completedAction) =>
			{
				// ARRANGE
				var validation = new ValidationHelper();

				var dummy = new DummyViewModel();

				int uiThreadId = Thread.CurrentThread.ManagedThreadId;
				int validationThreadId = uiThreadId;
				
				validation.AddAsyncRule(() => dummy.Foo,
					onCompleted =>
					{
						validationThreadId = Thread.CurrentThread.ManagedThreadId;

						onCompleted(RuleResult.Valid());
					});

				// ACT

				// Execute validation within a continuation of another task that runs on
				// current synchronization context, so that the TaskScheduler.Current is set to the 
				// UI context.
				Task.Factory.StartNew(() => { }).ContinueWith(t =>
				{
					Task<ValidationResult> task = validation.ValidateAllAsync();

					task.ContinueWith(result =>
					{
						// VERIFY

						// Schedule the varification code with the dispatcher in order to take it
						// out of the ContinueWith continuation. Otherwise the exceptions will be stored inside the task and 
						// unit testing framework will not know about them.
						uiThreadDispatcher.BeginInvoke(new Action(() =>
						{
							Assert.False(uiThreadId == validationThreadId, "Validation must be executed in a background thread, so that the UI thread is not blocked.");

							completedAction();
						}));
					});
				}, TaskScheduler.FromCurrentSynchronizationContext());
			});
		}
		public void ValidateAsync_WithCallback_ValidationOccuredAndCallbackIsCalledOnUIThread()
		{
			TestUtils.ExecuteWithDispatcher((dispatcher, completedAction) =>
			{
				var vm = new DummyViewModel();
				vm.Foo = null;
				bool ruleExecuted = false;

				var validation = new ValidationHelper();

				validation.AddAsyncRule(setResult =>
				{
					ruleExecuted = true;
					setResult(RuleResult.Invalid("Error1"));
				});

				validation.ValidateAllAsync(result =>
				{
					Assert.True(ruleExecuted, "Validation rule must be executed before validation callback is called.");
					Assert.Equal(dispatcher.Thread.ManagedThreadId, Thread.CurrentThread.ManagedThreadId);

					completedAction();
				});
			});
		}
		public async Task ValidateAsync_AsyncRuleRegisteredWithNewSyntax_RuleIsExecuted()
		{
			// ARRANGE
			var validator = new ValidationHelper();

			validator.AddAsyncRule(async () => await Task.Factory.StartNew(() => RuleResult.Invalid("error")));

			// ACT
			var result = await validator.ValidateAllAsync();

			// VERIFY
			Assert.False(result.IsValid);
		}
		public void ValidateAllAsync_SimilteniousCalls_DoesNotFail()
		{
			TestUtils.ExecuteWithDispatcher((dispatcher, completedAction) =>
			{
				const int numThreadsPerIternation = 4;
				const int iterationCount = 10;
				const int numThreads = numThreadsPerIternation * iterationCount;
				var resetEvent = new ManualResetEvent(false);
				int toProcess = numThreads;

				var validation = new ValidationHelper();

				for (int i = 0; i < iterationCount; i++)
				{
					var target1 = new object();
					var target2 = new object();

					validation.AddAsyncRule(setResult =>
					{
						setResult(RuleResult.Invalid("Error1"));
					});

					validation.AddAsyncRule(target1, setResult =>
					{
						setResult(RuleResult.Valid());
					});

					validation.AddRule(target2, () =>
					{
						return RuleResult.Invalid("Error2");
					});

					validation.AddRule(target2, RuleResult.Valid);

					Action<Action> testThreadBody = exercise =>
					{
						try
						{
							exercise();

							if (Interlocked.Decrement(ref toProcess) == 0)
								resetEvent.Set();
						}
						catch (Exception ex)
						{
							dispatcher.BeginInvoke(new Action(() =>
							{
								throw new AggregateException(ex);
							}));
						}
					};

					var thread1 = new Thread(() =>
					{
						testThreadBody(() =>
						{
							validation.ValidateAllAsync().Wait();
						});
					});

					var thread2 = new Thread(() =>
					{
						testThreadBody(() =>
						{
							validation.ValidateAllAsync().Wait();
						});
					});

					var thread3 = new Thread(() =>
					{
						testThreadBody(() =>
						{
							validation.Validate(target2);
						});
					});

					var thread4 = new Thread(() =>
					{
						testThreadBody(() =>
						{
							validation.Validate(target2);
						});
					});

					thread1.Start();
					thread2.Start();
					thread3.Start();
					thread4.Start();
				}

				ThreadPool.QueueUserWorkItem(_ =>
				{
					resetEvent.WaitOne();
					completedAction();
				});

			});
		}
		public void AsyncValidation_DependantProperties_IfOneInvalidSecondIsInvalidToo()
		{
			TestUtils.ExecuteWithDispatcher((dispatcher, testCompleted) =>
			{
				var vm = new DummyViewModel
				{
					Foo = "abc", 
					Bar = "abc"
				};

				Func<bool> validCondition = () => vm.Foo != vm.Bar;

				var validation = new ValidationHelper();

				validation.AddAsyncRule(
					() => vm.Foo, 
					() => vm.Bar,
					setResult =>
					{
						ThreadPool.QueueUserWorkItem(_ =>
						{
							setResult(RuleResult.Assert(validCondition(), "Foo must be different than bar"));
						});
					});

				validation.ValidateAsync(() => vm.Bar).ContinueWith(r =>
				{
					Assert.False(r.Result.IsValid, "Validation must fail");
					Assert.True(r.Result.ErrorList.Count == 2, "There must be 2 errors: one for each dependant property");

					testCompleted();
				});
			});
		}