Skip to content

Commit

Permalink
Merge pull request #1 from BDisp/DisposeTests-fix
Browse files Browse the repository at this point in the history
Dispose tests fix
  • Loading branch information
a-usr authored Nov 14, 2023
2 parents 0d8ca6c + d6eb201 commit 965e65d
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 43 deletions.
12 changes: 4 additions & 8 deletions Terminal.Gui/Core/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public static List<Toplevel> MdiChildes {
/// </summary>
public static Toplevel MdiTop {
get {
if (Top.IsMdiContainer) {
if (Top?.IsMdiContainer == true) {
return Top;
}
return null;
Expand Down Expand Up @@ -516,11 +516,8 @@ public void Dispose ()
protected virtual void Dispose (bool disposing)
{
if (Toplevel != null && disposing) {
throw new InvalidOperationException ("You must clean up (Dispose) the Toplevel before calling Application.RunState.Dispose");
// BUGBUG: It's insidious that we call EndFirstTopLevel here so I moved it to End.
//EndFirstTopLevel (Toplevel);
//Toplevel.Dispose ();
//Toplevel = null;
Toplevel.Dispose ();
Toplevel = null;
}
}
}
Expand Down Expand Up @@ -1062,6 +1059,7 @@ public static void End (RunState runState)
// Set Current and Top to the next TopLevel on the stack
if (toplevels.Count == 0) {
Current = null;
Top = null;
} else {
Current = toplevels.Peek ();
if (toplevels.Count == 1 && Current == MdiTop) {
Expand All @@ -1074,8 +1072,6 @@ public static void End (RunState runState)
Refresh ();
}

runState.Toplevel?.Dispose ();
runState.Toplevel = null;
runState.Dispose ();
}

Expand Down
6 changes: 5 additions & 1 deletion Terminal.Gui/Core/Border.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,11 @@ public override void Remove (View view)

SetNeedsDisplay ();
var touched = view.Frame;
Border.Child.Remove (view);
if (view == Border.Child) {
base.Remove (view);
} else {
Border.Child.Remove (view);
}

if (Border.Child.InternalSubviews.Count < 1) {
CanFocus = false;
Expand Down
2 changes: 1 addition & 1 deletion Terminal.Gui/Core/View.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using NStack;

namespace Terminal.Gui {
Expand Down Expand Up @@ -2944,6 +2943,7 @@ protected override void Dispose (bool disposing)
subview.Dispose ();
}
base.Dispose (disposing);
System.Diagnostics.Debug.Assert (InternalSubviews.Count == 0);
}

/// <summary>
Expand Down
7 changes: 5 additions & 2 deletions Terminal.Gui/Core/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
// Any udpates done here should probably be done in FrameView as well; TODO: Merge these classes

using System;
using System.Collections;
using System.Linq;
using NStack;

Expand Down Expand Up @@ -271,7 +270,11 @@ public override void Remove (View view)
}

SetNeedsDisplay ();
contentView.Remove (view);
if (view == contentView) {
base.Remove (view);
} else {
contentView.Remove (view);
}

RemoveMenuStatusBar (view);
if (view != contentView && Focused == null) {
Expand Down
7 changes: 5 additions & 2 deletions Terminal.Gui/Views/FrameView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
// - Does not support IEnumerable
// Any udpates done here should probably be done in Window as well; TODO: Merge these classes

using System;
using System.Linq;
using NStack;

Expand Down Expand Up @@ -202,7 +201,11 @@ public override void Remove (View view)

SetNeedsDisplay ();
var touched = view.Frame;
contentView.Remove (view);
if (view == contentView) {
base.Remove (view);
} else {
contentView.Remove (view);
}

if (contentView.InternalSubviews.Count < 1)
this.CanFocus = false;
Expand Down
6 changes: 4 additions & 2 deletions Terminal.Gui/Views/TextView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1441,8 +1441,10 @@ void TextView_Initialized (object sender, EventArgs e)
{
Autocomplete.HostControl = this;

Application.Top.AlternateForwardKeyChanged += Top_AlternateForwardKeyChanged;
Application.Top.AlternateBackwardKeyChanged += Top_AlternateBackwardKeyChanged;
if (Application.Top != null) {
Application.Top.AlternateForwardKeyChanged += Top_AlternateForwardKeyChanged;
Application.Top.AlternateBackwardKeyChanged += Top_AlternateBackwardKeyChanged;
}
OnContentsChanged ();
}

Expand Down
6 changes: 5 additions & 1 deletion Terminal.Gui/Windows/Wizard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,11 @@ public override void Remove (View view)

SetNeedsDisplay ();
var touched = view.Frame;
contentView.Remove (view);
if (view == contentView || view.GetType().Name == "ContentView") {
base.Remove (view);
} else {
contentView.Remove (view);
}

if (contentView.InternalSubviews.Count < 1)
this.CanFocus = false;
Expand Down
2 changes: 1 addition & 1 deletion UICatalog/Scenarios/SingleBackgroundWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public override void Run ()

Application.Run<MainApp> ();

Application.Top.Dispose ();
System.Diagnostics.Debug.Assert (Application.Top == null);
}

public class MainApp : Toplevel {
Expand Down
11 changes: 6 additions & 5 deletions UnitTests/Application/ApplicationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,12 @@ public void Init_Begin_End_Cleans_Up ()
Application.End (runstate);

Assert.Null (Application.Current);
Assert.NotNull (Application.Top);
Assert.Null (Application.Top);
Assert.NotNull (Application.MainLoop);
Assert.NotNull (Application.Driver);

Shutdown ();

Assert.Null (Application.Top);
Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
Expand Down Expand Up @@ -203,13 +202,12 @@ public void InitWithTopLevelFactory_Begin_End_Cleans_Up ()
Application.End (runstate);

Assert.Null (Application.Current);
Assert.NotNull (Application.Top);
Assert.Null (Application.Top);
Assert.NotNull (Application.MainLoop);
Assert.NotNull (Application.Driver);

Shutdown ();

Assert.Null (Application.Top);
Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
Expand Down Expand Up @@ -529,7 +527,10 @@ public void SetCurrentAsTop_Run_A_Not_Modal_Toplevel_Make_It_The_Current_Applica

Application.Run (t1);

Assert.Equal (t1, Application.Top);
Assert.Null (Application.Top);
#if DEBUG_IDISPOSABLE
Assert.True (t1.WasDisposed);
#endif
}

[Fact]
Expand Down
54 changes: 46 additions & 8 deletions UnitTests/Application/RunStateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,11 @@ public void Dispose_Cleans_Up_RunState ()
rs = new Application.RunState (top);
Assert.NotNull (rs);

// Should throw because Toplevel was not cleaned up
Assert.Throws<InvalidOperationException> (() => rs.Dispose ());
// Should not throw because Toplevel was cleaned up
var exception = Record.Exception (() => rs.Dispose ());
Assert.Null (exception);

rs.Toplevel.Dispose ();
rs.Toplevel = null;
rs.Dispose ();
Assert.Null (rs.Toplevel);
#if DEBUG_IDISPOSABLE
Assert.True (rs.WasDisposed);
Assert.True (top.WasDisposed);
Expand All @@ -63,7 +62,7 @@ public void Dispose_Cleans_Up_RunState ()
void Init ()
{
Application.Init (new FakeDriver ());

Assert.NotNull (Application.Driver);
Assert.NotNull (Application.MainLoop);
Assert.NotNull (SynchronizationContext.Current);
Expand All @@ -74,7 +73,7 @@ void Shutdown ()
Application.Shutdown ();
#if DEBUG_IDISPOSABLE
// Validate there are no outstanding RunState-based instances left
foreach (var inst in Application.RunState.Instances) Assert.True (inst.WasDisposed);
foreach (var inst in Application.RunState.Instances) Assert.True (inst.WasDisposed);
#endif
}

Expand All @@ -94,7 +93,7 @@ public void Begin_End_Cleans_Up_RunState ()
Application.End (rs);

Assert.Null (Application.Current);
Assert.NotNull (Application.Top);
Assert.Null (Application.Top);
Assert.NotNull (Application.MainLoop);
Assert.NotNull (Application.Driver);

Expand All @@ -104,7 +103,46 @@ public void Begin_End_Cleans_Up_RunState ()
Assert.True (rs.WasDisposed);
#endif

Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}

WeakReference CreateToplevelInstance ()
{
// Setup Mock driver
Init ();

var top = new Toplevel ();
var rs = Application.Begin (top);

Assert.NotNull (rs);
Assert.Equal (top, Application.Current);
Assert.Equal (top, Application.Top);
Application.End (rs);
#if DEBUG_IDISPOSABLE
Assert.True (rs.WasDisposed);
Assert.True (top.WasDisposed);
#endif
Assert.Null (Application.Current);
Assert.Null (Application.Top);
Assert.NotNull (top);
Assert.NotNull (Application.MainLoop);
Assert.NotNull (Application.Driver);

return new WeakReference (top, true);
}

[Fact]
public void Begin_End_Cleans_Up_RunState_Without_Shutdown ()
{
WeakReference wrInstance = CreateToplevelInstance ();

GC.Collect ();
GC.WaitForPendingFinalizers ();
Assert.False (wrInstance.IsAlive);

// Shutdown Mock driver
Shutdown ();
Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
}
Expand Down
13 changes: 7 additions & 6 deletions UnitTests/Views/ButtonTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ public void Constructors_Defaults ()
{
var btn = new Button ();
Assert.Equal (string.Empty, btn.Text);
Application.Top.Add (btn);
var rs = Application.Begin (Application.Top);
var top = Application.Top;
top.Add (btn);
var rs = Application.Begin (top);

Assert.Equal ("[ ]", btn.TextFormatter.Text);
Assert.False (btn.IsDefault);
Expand All @@ -34,8 +35,8 @@ [ ]
Application.End (rs);
btn = new Button ("ARGS", true) { Text = "Test" };
Assert.Equal ("Test", btn.Text);
Application.Top.Add (btn);
rs = Application.Begin (Application.Top);
top.Add (btn);
rs = Application.Begin (top);

Assert.Equal ("[◦ Test ◦]", btn.TextFormatter.Text);
Assert.True (btn.IsDefault);
Expand All @@ -52,8 +53,8 @@ [ ]
Application.End (rs);
btn = new Button (3, 4, "Test", true);
Assert.Equal ("Test", btn.Text);
Application.Top.Add (btn);
rs = Application.Begin (Application.Top);
top.Add (btn);
rs = Application.Begin (top);

Assert.Equal ("[◦ Test ◦]", btn.TextFormatter.Text);
Assert.True (btn.IsDefault);
Expand Down
30 changes: 24 additions & 6 deletions UnitTests/Views/ViewDisposalTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ public ViewDisposalTest (ITestOutputHelper output)
}
}

[Fact]
[AutoInitShutdown]
public void TestViewsDisposeCorrectly ()
[Theory]
[InlineData (true)]
[InlineData (false)]
public void TestViewsDisposeCorrectly (bool callShutdown)
{
var reference = DoTest ();
var reference = DoTest (callShutdown);
for (var i = 0; i < 10 && reference.IsAlive; i++) {
GC.Collect ();
GC.WaitForPendingFinalizers ();
Expand All @@ -43,6 +44,9 @@ public void TestViewsDisposeCorrectly ()
}
Assert.Fail ($"Some Views didnt get Garbage Collected: {alive}");
}
if (!callShutdown) {
Application.Shutdown ();
}
}

void getSpecialParams ()
Expand All @@ -51,8 +55,10 @@ void getSpecialParams ()
//special_params.Add (typeof (LineView), new object [] { Orientation.Horizontal });
}

WeakReference DoTest ()
WeakReference DoTest (bool callShutdown)
{
var driver = new FakeDriver ();
Application.Init (driver, new FakeMainLoop (driver));
getSpecialParams ();
View Container = new View ();
Container.Add (new View ());
Expand Down Expand Up @@ -80,9 +86,21 @@ WeakReference DoTest ()

top.Remove (Container);
Application.End (state);
top.Dispose ();
#if DEBUG_IDISPOSABLE
Assert.True (top.WasDisposed);
Assert.False (Container.WasDisposed);
#endif
Assert.Null (Application.Top);

WeakReference reference = new (Container, true);
Container.Dispose ();
#if DEBUG_IDISPOSABLE
Assert.True (Container.WasDisposed);
#endif
if (callShutdown) {
Application.Shutdown ();
}

return reference;
}

Expand Down

0 comments on commit 965e65d

Please sign in to comment.