Skip to content

Commit

Permalink
Fix issue where the TestViewsDisposeCorrectly was not doing what it w…
Browse files Browse the repository at this point in the history
…as supposed to do (#2964)

* add Disposal Test and fix an ssue where the CopyClipboard test was failing

* Update ViewDisposalTest.cs

* Update ViewDisposalTest.cs: Some Formatting, and adding code comments.

* Fix ViewDisposalTests (Wasn't working the way it was supposed to)

* update test

* update test

* update test

* try to fix as many conflicts as possible

* make test output prettier

* fix formatting

* Fix Subviews not being empty after disposing on all views.

* The fail cause was Application.Top not being disposed.

* Fix others containers that weren't being removed.

* Revert "The fail cause was Application.Top not being disposed."

This reverts commit 0c2183e.

* Application.Top isn't null and need disposing.

* Fixes #2985. Application.RunState must be responsible for dispose the Toplevel property.

* Change the unit test with ans without Application.Shutdown method.

* Update ViewDisposeTests to actually check wether ALL views have been disposed (not just container)

* small additional check just to be safe

* Update ViewDisposalTest.cs: Formatting

* Update ViewDisposalTest.cs: Minor change to re-trigger Action

TestVKPacket is acting up again. Maybe the test is running async and is receiving scan codes from other instances?

---------

Co-authored-by: John Züchler <john.zuechler@eks-intec.de>
Co-authored-by: BDisp <bd.bdisp@gmail.com>
Co-authored-by: Tig <tig@users.noreply.github.com>
  • Loading branch information
4 people authored Nov 15, 2023
1 parent 91865ee commit c7942ae
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 50 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
Loading

0 comments on commit c7942ae

Please sign in to comment.