MSTest where constructor leads to asynchronous method - wpf

I have a viewmodel whose constructor leads to some asynchronous calls and I'm having trouble testing their result.
public ExamAcquireImageViewModel(ICore coreInstance, ExamManager examManager, Action cancelHandler) : base(examManager)
{
TemplateVm.OnSelectionChanged += StartAcquiringImages;
// BECAUSE OF PREVIOUS LINE, THIS CALLS StartAcquiringImages()
SelectedLocationInTemplate = SelectedLocationInTemplate ?? FindNextLowest();
}
private void StartAcquiringImages(LocationViewModel nextLocation = null)
{
new Thread(() =>
{
// expensive operation
_recon = _coreInstance.AcquireImageSet(Exam, SelectedLocationInTemplate.LocationModel);
int width = 1000;
int height = 1000;
// (less) expensive operation
AcquiredImage = _recon?.RenderImageAndGetCurrentFrame().ToWriteableBitmap(width, height);
SelectedLocationInTemplate = GetNextLocation();
}).Start();
}
The constructor assigns the OnSelectionChanged and then changes the selection, setting off the image acquisition process. I want to test that AcquireImages has been assigned to.
public void TestAcquisition()
{
ExamAcquireImageViewModel acqVm = new ExamAcquireImageViewModel(mockCore.Object, examManager, () => { });
Assert.IsNotNull(acqVm.AcquiredImage);
}
I have all my Moqs set up correctly. However because of the threaded/asynchronous operations, the test fails because the assertion runs before any AcquiredImage gets set (indeed, I imagine, before anything in the new Thread gets run).
I've tried ExamAcquireImageViewModel acqVm = await new ExamAcquireImageViewModel(mockCore.Object, examManager, () => { }); but that doesn't compile (no GetAwaiter etc).
How do I wait for this thread in my tests?
I'll also want to test that the SelectedLocationInTemplate "increments" automatically and each next image gets acquired (see last line in the Thread). I don't know where I'd intercept or "peek into" the whole process to see that happening.

For anyone who has a similar problem, I will answer my own question.
It's very very very simple. Thread.Sleep.
[TestMethod]
public void TestAcquisitionSucess()
{
ExamAcquireImageViewModel acqVm = GetAcqVm();
Thread.Sleep(30);
Assert.IsNotNull(acqVm.AcquiredImage);
}

Related

WPF how to dispose a running operation with Task

I've a method which check if a directory exists or not.
public static bool FileExists(string path, int timeout = 500)
{
Func<bool> func = () => File.Exists(path);
using (Task<bool> task = new Task<bool>(func))
{
task.Start();
return task.Wait(timeout) && task.Result;
}
}
It works fine but when I close my wpf application, sometimes I had this exception:
A task may only be disposed if it is in a completion state (RanToCompletion, Faulted or Canceled)
Any hints how to detect the exception? I tried to wrap my code into try...catch block but I think there's more elegand solution,
There is an overload of the Wait method accepts a CancellationToken. If you use this one, you could cancel it using a CancellationTokenSource as described in the docs.
You don't really need to dispose the task though:
public static bool FileExists(string path, int timeout = 500)
{
Func<bool> func = () => File.Exists(path);
Task<bool> task = Task.Run(func);
return task.Wait(timeout) && task.Result;
}
Stephan Toub explains more about this in his blog post:
Do I need to dispose of Tasks?

WPF waiting for the UI to finish

I have a code that needs to refresh the UI then wait for it to finish (refreshing may involve animations) and then keep going. Is there a way to call the Application.Current.Dispatcher.Invoke(new Action (()=> { PerformUpdateWithAnimations(); } in a syncrhonous way?
This is the overall look:
List<thingsThatMove> myThings = new List<ThingsThatMove>();
//normal code interacting with the data
// let's name this part of code A
foreach (thing t in myThings)
{
thing.currentPosition = SomePoint;
if(thing.wasRejectedBySystem) thing.needsToMove = true;
}
//As a result of A we have some impact in the UI
//That may need some animations (let's call this bloc B)
Application.Current.Dispatcher.Invoke(new Action(() => {
foreach(thing in myThings)
if(thing.needsToMove)
createNewAnimation(thing);
}));
//Here is some final code that needs the final position of some
//of the elements, so it cannot be executed until the B part has
// been finished. Let's call this bloc C
updateInternalValues(myThings);
cleanUp();
I tried encapsulating B into a BackgroundWoker. Setting the B bloc as the DoWork and listening to the completed but it doent work since the B blok "completes" after the Application.Current.Dispatcher is called, not after the dispatcher itself finishes everything
How can I make C wait until all the animations in B are finished ?
You can use async/await to call asynchrnously and continue after it has been executed..
private async void RefreshCode()
{
List<thingsThatMove> myThings = new List<ThingsThatMove>();
//normal code interacting with the data
// let's name this part of code A
foreach (thing t in myThings)
{
thing.currentPosition = SomePoint;
if(thing.wasRejectedBySystem) thing.needsToMove = true;
}
//As a result of A we have some impact in the UI
//That may need some animations (let's call this bloc B)
await Application.Current.Dispatcher.InvokeAsync(new Action(() => {
foreach(thing in myThings)
if(thing.needsToMove)
createNewAnimation(thing);
}));
//Here is some final code that needs the final position of some
//of the elements, so it cannot be executed until the B part has
// been finished. Let's call this bloc C
updateInternalValues(myThings);
cleanUp();
}

Awaiting IEnumerable<Task<T>> individually C#

In short, I have a Task enumerable, and I would like to run each Task within the array in an await fashion. Each Task will perform a slow network operation and from my end I simply need to update the WinForm UI once the task is finished.
Below is the code I'm currently using, however I think this is more of a hack than an actual solution:
private void btnCheckCredentials_Click(object sender, EventArgs e)
{
// GetNetCredentials() is irrelevant to the question...
List<NetworkCredential> netCredentials = GetNetCredentials();
// This call is not awaited. Displays warning!
netCredentials.ForEach(nc => AwaitTask(ValidateCredentials(nc)));
}
public async Task<bool> ValidateCredentials(NetworkCredential netCredential)
{
// Network-reliant, slow code here...
}
public async Task AwaitTask(Task<bool> task)
{
await task;
// Dumbed-down version of displaying the task results
txtResults.Text += task.Result.ToString();
}
2nd line of btnCheckCredentials_Click() warning is shown:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
This actually works the way I wanted to, since I do not want to wait for the operation to complete. Instead I just want to fire away the tasks, and then do something as soon as each one of them finishes.
The Task.WhenAny() or Task.WhenAll() methods do function as I expect, since I would like to know of every task finishing - as soon as it finishes. Task.WaitAll() or Task.WaitAny() are blocking and therefore undesirable as well.
Edit: All tasks should start simultaneously. They may then finish in any order.
Are you looking for Task.WhenAll?
await Task.WhenAll(netCredentials.Select(nc => AwaitTask(ValidateCredentials(nc)));
You can do all the completion processing you need in AwaitTask.
The await task; is a bit awkward. I'd do it like this:
public async Task AwaitTask(netCredential credential)
{
var result = await ValidateCredentails(credential);
// Dumbed-down version of displaying the task results
txtResults.Text += result.ToString();
}
You can do this by using Task.WhenAny marking it as async (async void here is fine since you're inside an event handler):
private async void btnCheckCredentials_Click(object sender, EventArgs e)
{
// GetNetCredentials() is irrelevant to the question...
List<NetworkCredential> netCredentials = GetNetCredentials();
var credentialTasks = netCredentials
.Select(cred => ValidateCredentialsAsync(cred))
.ToList();
while (credentialTasks.Count > 0)
{
var finishedTask = await Task.WhenAny(credentialTasks);
// Do stuff with finished task.
credentialTasks.Remove(finishedTask);
}
}
You can fire and forget each task and add callback when task is completed.
private async void btnCheckCredentials_Click(object sender, EventArgs e)
{
List<NetworkCredential> netCredentials = GetNetCredentials();
foreach (var credential in netCredentials)
{
ValidateCredentails(credential).ContinueWith(x=> ...) {
};
}
}
So instead of labda expression you can create callback method and know exactly when the particular task finished.

Windows Form UI Update issue with Task in C#

We are working on a windows application which caters to an engineering calculation which are essentially very long running. So we are basically trying to keep the calculation module separate and working in a separate worker thread and pass it an Action delegate in method signature which will be invoked to report the calculation progress in the UI. The delegate handler declared in the UI will be updating the UI. We found that while a huge loop is running in the calculation, the UI is not showing the periodic progress and only displaying the final result. If a Thread Sleep for 1 millisecond is introduced in the calculation loop, the UI is getting updated correctly. This is not expected behavior as we are executing the calculation using a separate Task and updating the UI using BeginInvoke calls.
I have created a simple application to demonstrate our approach and code so that it is easier to understand. It is obvious that we are missing something very simple but cannot quite pin it down. Will appreciate any insights.
Thanks for reading.
private void cmdStart_Click(object sender, EventArgs e)
{
txtResultDIsplay.Text = "";
var maxIterations = long.Parse(txtIterationNo.Text.Trim());
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task<double> calculationTask = Task.Factory.StartNew<double>(
() => SumRootN(maxIterations, UpdateProgress));
var handleResultTask = calculationTask.ContinueWith((t) => DisplayResult(t),
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, ui);
}
private void DisplayResult(Task<double> calculationTask)
{
txtResultDIsplay.Text = "Final Calculation Result : " + calculationTask.Result.ToString();
}
private void UpdateProgress(string msg)
{
this.BeginInvoke((MethodInvoker)delegate
{
txtResultDIsplay.Text = msg;
});
}
public double SumRootN(long maxIterations, Action<string> progressUpdateDelegate)
{
int root = 20;
double result = 0;
for (long i = 1; i < maxIterations; i++)
{
Thread.Sleep(1);
result += Math.Exp(Math.Log(i) / root);
progressUpdateDelegate(result.ToString("0.00000"));
}
return result;
}
It is possible you are flooding the UI thread with your progress updates. You need to find a way to prevent lots of updates occurring.
We can solve the problem using tasks!
Task progressTask = null;
private void UpdateProgress(string msg)
{
//only schedule work if the task if not running
if(progressTask == null || progressTask.IsCompleted) //updates will end if there is an exception!
{
//Create a task representing the update
progressTask = Task.Factory.FromAsync<object>(BeginInvoke(new Action(() => txtResultDIsplay.Text = msg)), this.EndInvoke)
.ContinueWith(() => System.Threading.Thread.Sleep(100)); //add a sleep on the end
}
}
Note that locking will not do here as you want to skip the update if there is already an update occurring.

Application Won't 'new' when called

I am calling a new xaml application with some parameter inputs using the word new, but it does not seem to be working. I am also attempting to use onclosing to set it to null. When launching it for the first time it works (everything is new), but launching it after it finished, it seems to continue its previous state (brings to finished score board). Here is the snipplet of the code . . .
quizUI = new QuizzUI.MainWindow(App.User, true);
quizUI.Closed += (o, s) =>
{
quizUI = null;
};
quizUI.LaunchQuiz(qSet);
this is hooked to a button event. does anyone know how i can absolutely new this object's state every time? the two paramters are user's info and second one is to shortcut without prompt screen/loading screen.
Here is the code for QuizzUI.MainWindow.LaunchQuizz:
public void LaunchQuiz(GameQuizzSet quiz)
{
this.Hide();
quizz = new QuizzContainer()
{
QSet = quiz,
};
if (isShortCutted)
{
bool? diag = quizz.ShowDialog();
if (diag.HasValue)
{
quizz.Close();
Close();
}
}
else
{
quizz.ShowDialog();
this.Show();
}
}
the QuizzUI.MainWindow allows the user to select their profile and which quiz to execute.

Resources