Skip to content
This repository was archived by the owner on Oct 4, 2021. It is now read-only.

[Core][Tests] Add basic ProgressMonitor tests #7255

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
using System.Text;
using System.Collections.Concurrent;
using UnitTests;
using System.Linq;

namespace MonoDevelop.Core
{
Expand Down Expand Up @@ -154,6 +155,247 @@ public void TestLogSynchronizationContextThroughWrites ()
Assert.AreEqual (2, ctx.CallCount - offset);
}
}

[Test]
public void TestParentChildTask ()
{
var monitor = new ProgressMonitor ();

var mainTask = monitor.BeginTask ("Task", 1) as ProgressTask;

Assert.AreSame (mainTask, monitor.CurrentTask);
Assert.AreEqual (mainTask.Name, monitor.CurrentTaskName);
Assert.IsTrue (monitor.GetRootTasks ().Contains (mainTask));

var childTask = monitor.BeginTask ("ChildTask", 1) as ProgressTask;

// test task hierarchy
Assert.NotNull (mainTask);
Assert.NotNull (childTask);
Assert.AreSame (mainTask, childTask.ParentTask);
Assert.AreSame (childTask, monitor.CurrentTask);
Assert.AreEqual (childTask.Name, monitor.CurrentTaskName);
Assert.IsTrue (monitor.GetRootTasks ().Contains (mainTask));
Assert.IsTrue (mainTask.GetChildrenTasks ().Contains (childTask));

// ensure that the child is not a root task
Assert.IsFalse (monitor.GetRootTasks ().Contains (childTask));

monitor.EndTask ();

Assert.AreSame (mainTask, monitor.CurrentTask);
Assert.AreEqual (mainTask.Name, monitor.CurrentTaskName);
Assert.IsTrue (monitor.GetRootTasks ().Contains (mainTask));
Assert.IsFalse (mainTask.GetChildrenTasks ().Contains (childTask));

monitor.EndTask ();

Assert.IsNull (monitor.CurrentTask);
Assert.IsFalse (monitor.GetRootTasks ().Any ());
}

[Test, Combinatorial]
public void TestUsingMultipleParentChildTasks ([Range (1, 2)] int rootTasks, [Range (1, 2)] int childTasks)
{
var monitor = new ProgressMonitor ();

for (int i = 1; i <= rootTasks; i++) {
using (var mainTask = monitor.BeginTask ("Task" + i, 1) as ProgressTask) {
for (int j = 1; j <= rootTasks; j++) {
using (var childTask = monitor.BeginTask ("ChildTask" + j, 1) as ProgressTask) {
// test task hierarchy
Assert.AreSame (mainTask, childTask.ParentTask);
Assert.AreSame (childTask, monitor.CurrentTask);
Assert.IsTrue (monitor.GetRootTasks ().Contains (mainTask));
Assert.IsTrue (mainTask.GetChildrenTasks ().Contains (childTask));

// ensure that the child is not a root task
Assert.IsFalse (monitor.GetRootTasks ().Contains (childTask));
}
Assert.AreSame (mainTask, monitor.CurrentTask);
Assert.IsTrue (monitor.GetRootTasks ().Contains (mainTask));
Assert.IsFalse (mainTask.GetChildrenTasks ().Any ());
}
}

Assert.IsNull (monitor.CurrentTask);
Assert.IsFalse (monitor.GetRootTasks ().Any ());
}
}

static readonly double taskProgressTestPrecision = 0.0001;

[TestCase (true, TestName = "With Completion")]
[TestCase (true, TestName = "Without Completion")]
public void TestSingleTaskProgress (bool stepsToFinish)
{
int steps = 10;
// we test 6 steps + 1 for finish test,
// just make sure that we start the task with enough steps
Assert.Greater (steps, 7);

double stepProgress = 1d / steps;
var monitor = new ProgressMonitor ();
Assert.AreSame (monitor.BeginTask ("Task", steps), monitor.CurrentTask);

var task = monitor.CurrentTask;
Assert.NotNull (task);
Assert.AreEqual ("Task", monitor.CurrentTaskName);
Assert.AreEqual (0, monitor.Progress);
Assert.AreEqual (task.Progress, monitor.Progress);
Assert.AreEqual (steps, task.TotalWork);
Assert.IsTrue (string.IsNullOrEmpty (task.StatusMessage));

int stepped = 0;

// 1 step
monitor.Step ();
stepped += 1;
Assert.That (monitor.Progress, Is.EqualTo (stepProgress * stepped).Within (taskProgressTestPrecision));
Assert.AreEqual (monitor.Progress, task.Progress);

// 2 steps
monitor.Step (2);
stepped += 2;
Assert.That (monitor.Progress, Is.EqualTo (stepProgress * stepped).Within (taskProgressTestPrecision));
Assert.AreEqual (monitor.Progress, task.Progress);

// Begin/End 1 step
monitor.BeginStep ();
Assert.That (monitor.Progress, Is.EqualTo (stepProgress * stepped).Within (taskProgressTestPrecision), "Task progress changed before the task ended");
Assert.AreEqual (monitor.Progress, task.Progress);
monitor.EndStep ();
stepped += 1;
Assert.That (monitor.Progress, Is.EqualTo (stepProgress * stepped).Within (taskProgressTestPrecision));
Assert.AreEqual (monitor.Progress, task.Progress);

// Begin/End 2 steps
monitor.BeginStep (2);
Assert.That (monitor.Progress, Is.EqualTo (stepProgress * stepped).Within (taskProgressTestPrecision), "Task progress changed before the task ended");
Assert.AreEqual (monitor.Progress, task.Progress);
monitor.EndStep ();
stepped += 2;
Assert.That (monitor.Progress, Is.EqualTo (stepProgress * stepped).Within (taskProgressTestPrecision));
Assert.AreEqual (monitor.Progress, task.Progress);

// Run to end
if (stepsToFinish)
// Step to End
monitor.Step (steps - stepped);
else
// End Task
monitor.EndTask ();

Assert.That (monitor.Progress, Is.EqualTo (1).Within (taskProgressTestPrecision));
Assert.AreEqual (monitor.Progress, task.Progress);
}

[Test]
public void TestSingleTaskProgressWithSteps ([Values (1, 10, 100, 1000)] int stepsCount)
{
var monitor = new ProgressMonitor ();
var mainTask = monitor.BeginTask ("Task", stepsCount) as ProgressTask;

Assert.AreEqual (stepsCount, mainTask.TotalWork);

for (int i = 1; i <= stepsCount; i++) {
monitor.Step ();
var expectedProgress = (1d / stepsCount) * i;
Assert.That (mainTask.Progress, Is.EqualTo (expectedProgress).Within (taskProgressTestPrecision));
Assert.That (monitor.Progress, Is.EqualTo (expectedProgress).Within (taskProgressTestPrecision));
}

Assert.AreEqual (1, monitor.Progress);
Assert.AreEqual (mainTask.Progress, monitor.Progress);
}

[Test, Explicit ("This test makes only sense, if ending a child task would automatically step the parent")]
public void TestNestedTasksProgress ([Values (1, 10, 100)] int childrenCount)
{
int childSteps = 10;
double childStepProgress = 1d / childSteps;
var monitor = new ProgressMonitor ();

var mainTask = monitor.BeginTask ("ParentTask", childrenCount) as ProgressTask;

Assert.AreEqual (childrenCount, mainTask.TotalWork);

for (int i = 0; i < childrenCount; i++) {
var initialProgress = (1d / childrenCount) * i;
var childTask = monitor.BeginTask ("ChildTask" + i, childSteps) as ProgressTask;

// test initial progress state
Assert.That (mainTask.Progress, Is.EqualTo (initialProgress).Within (taskProgressTestPrecision));
Assert.That (monitor.Progress, Is.EqualTo (mainTask.Progress).Within (taskProgressTestPrecision));

monitor.EndTask ();
// EndTask should run the task to completion with Progress = 1
Assert.That (childTask.Progress, Is.EqualTo (1).Within (taskProgressTestPrecision));

var expectedMainProgress = (1d / childrenCount) * (i + 1);
Assert.That (monitor.Progress, Is.EqualTo (expectedMainProgress).Within (taskProgressTestPrecision));
}

// all child tasks ended and main task is the current task in finished state
Assert.AreSame (mainTask, monitor.CurrentTask);
Assert.AreEqual (1, monitor.Progress);
Assert.AreEqual (mainTask.Progress, monitor.Progress);
}

[TestCase (1, TestName = "Single Child Task")]
[TestCase (3, TestName = "Multiple Child Tasks")]
public void TestParentTaskTracksChildProgress (int childrenCount)
{
int childSteps = 4;
double childStepProgress = 1d / childSteps;
var monitor = new ProgressMonitor ();

var mainTask = monitor.BeginTask ("ParentTask", childrenCount) as ProgressTask;

Assert.AreEqual (childrenCount, mainTask.TotalWork);

for (int i = 0; i < childrenCount; i++) {
var expectedMainProgress = (1d / childrenCount) * i;
var childTask = monitor.BeginTask ("ChildTask" + i, childSteps) as ProgressTask;

// test initial progress state
Assert.AreEqual (expectedMainProgress, mainTask.Progress);
Assert.AreEqual (mainTask.Progress, monitor.Progress);
Assert.AreEqual (0, childTask.Progress);

int stepped = 0;

// 1 step
monitor.Step ();
stepped += 1;
Assert.That (childTask.Progress, Is.EqualTo (childStepProgress * stepped).Within (taskProgressTestPrecision));

expectedMainProgress += childStepProgress * stepped;
Assert.That (mainTask.Progress, Is.EqualTo (expectedMainProgress).Within (taskProgressTestPrecision));
Assert.That (monitor.Progress, Is.EqualTo (expectedMainProgress).Within (taskProgressTestPrecision));

// 2 steps
monitor.Step (2);
stepped += 2;
Assert.That (childTask.Progress, Is.EqualTo (childStepProgress * stepped).Within (taskProgressTestPrecision));

expectedMainProgress += childStepProgress * stepped;
Assert.That (mainTask.Progress, Is.EqualTo (expectedMainProgress).Within (taskProgressTestPrecision));
Assert.That (monitor.Progress, Is.EqualTo (expectedMainProgress).Within (taskProgressTestPrecision));

monitor.EndTask ();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a Step() call after EndTask()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a main part of the problem I described in #7256, in order to reflect child progress in the monitor, this step should not be required and is not obvious. If this is required, we need to find a way to synchronize that.

// EndTask should run the task to completion with Progress = 1
Assert.That (childTask.Progress, Is.EqualTo (1).Within (taskProgressTestPrecision));

expectedMainProgress = (1d / childrenCount) * (i + 1);
Assert.That (monitor.Progress, Is.EqualTo (expectedMainProgress).Within (taskProgressTestPrecision));
}

// all child tasks ended and main task is the current task in finished state
Assert.AreSame (mainTask, monitor.CurrentTask);
Assert.AreEqual (1, monitor.Progress);
Assert.AreEqual (mainTask.Progress, monitor.Progress);
}
}

class ChainedProgressMonitor : ProgressMonitor
Expand Down