Files
mRemoteNG/mRemoteNGTests/Tools/TabColorConverterTests.cs
Dawie Joubert 100c2e3078 Fix issue #2907: Options panel freezing, disposal, and performance issues
This commit resolves multiple related issues with the Options dialog that caused freezing, crashes, and slow performance:

**Problem 1: Infinite Recursive Loop**
- Symptom: Options dialog would freeze when navigating between pages
- Cause: LstOptionPages_SelectedIndexChanged event handler triggering itself infinitely
- Fix: Added _isHandlingSelectionChange guard flag to prevent recursive calls

**Problem 2: Disposed Object Exception**
- Symptom: "Cannot access a disposed object" error after SSH connection workflow
- Cause: Static FrmOptions instance was disposed but still referenced
- Fix: Enhanced OptionsWindow.LoadOptionsForm() to detect disposal before use
- Fix: Added FrmMain.RecreateOptionsForm() to recreate disposed forms transparently

**Problem 3: Index Out of Range**
- Symptom: "index must be less than 0" when accessing empty lstOptionPages
- Cause: SetActivatedPage() tried to access Items[0] when collection was empty
- Fix: Added bounds checking before accessing lstOptionPages.Items

**Problem 4: NullReferenceException in OptionsPages**
- Symptom: NullReferenceException in LoadRegistrySettings() across all pages
- Cause: pageRegSettingsInstance was null when registry settings didn't exist
- Fix: Added null checks and default instance creation in 8 OptionsPages

**Problem 5: Slow Page Loading on Recreation**
- Symptom: Second Options dialog open showed staggered page loading (~2.2 seconds)
- Cause: Application.Idle async pattern loaded pages one-by-one
- Fix: Replaced async loading with synchronous batch loading using BeginUpdate/EndUpdate

**Files Modified:**
- mRemoteNG/UI/Forms/frmOptions.cs
- mRemoteNG/UI/Window/OptionsWindow.cs
- mRemoteNG/UI/Forms/frmMain.cs
- mRemoteNG/UI/Forms/OptionsPages/StartupExitPage.cs
- mRemoteNG/UI/Forms/OptionsPages/NotificationsPage.cs
- mRemoteNG/UI/Forms/OptionsPages/AppearancePage.cs
- mRemoteNG/UI/Forms/OptionsPages/SecurityPage.cs
- mRemoteNG/UI/Forms/OptionsPages/ConnectionsPage.cs
- mRemoteNG/UI/Forms/OptionsPages/CredentialsPage.cs
- mRemoteNG/UI/Forms/OptionsPages/TabsPanelsPage.cs
- mRemoteNG/UI/Forms/OptionsPages/UpdatesPage.cs

**Additional Changes:**
- Replaced all Debug.WriteLine with Logger.Instance.Log for consistent logging
- Added comprehensive debug logging throughout Options form lifecycle
- Improved defensive programming with guard flags and validation checks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 19:49:56 +02:00

168 lines
5.3 KiB
C#

using System;
using System.Drawing;
using mRemoteNG.Tools;
using NUnit.Framework;
namespace mRemoteNGTests.Tools
{
public class TabColorConverterTests
{
private MiscTools.TabColorConverter _converter;
[SetUp]
public void Setup()
{
_converter = new MiscTools.TabColorConverter();
}
[TestCase(typeof(string), true)]
[TestCase(typeof(Color), true)]
public void CanConvertFrom(Type typeToConvertFrom, bool expectedOutcome)
{
var actualOutcome = _converter.CanConvertFrom(typeToConvertFrom);
Assert.That(actualOutcome, Is.EqualTo(expectedOutcome));
}
[TestCase(typeof(string), true)]
[TestCase(typeof(Color), true)]
public void CanConvertTo(Type typeToConvertTo, bool expectedOutcome)
{
var actualOutcome = _converter.CanConvertTo(typeToConvertTo);
Assert.That(actualOutcome, Is.EqualTo(expectedOutcome));
}
[Test]
public void ConvertFromColorToStringNamedColor()
{
var color = Color.Red;
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("Red"));
}
[Test]
public void ConvertFromColorToStringCustomColor()
{
var color = Color.FromArgb(255, 128, 64, 32);
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("#80401F"));
}
[Test]
public void ConvertFromColorToStringCustomColorWithAlpha()
{
var color = Color.FromArgb(128, 255, 0, 0);
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("#80FF0000"));
}
[Test]
public void ConvertFromStringReturnsString()
{
var colorString = "Blue";
var result = _converter.ConvertFrom(colorString);
Assert.That(result, Is.EqualTo("Blue"));
}
[Test]
public void ConvertFromHexStringReturnsString()
{
var colorString = "#FF0000";
var result = _converter.ConvertFrom(colorString);
Assert.That(result, Is.EqualTo("#FF0000"));
}
[Test]
public void ConvertFromNullReturnsEmptyString()
{
var result = _converter.ConvertFrom(null);
Assert.That(result, Is.EqualTo(string.Empty));
}
[Test]
public void ConvertFromEmptyStringReturnsEmptyString()
{
var result = _converter.ConvertFrom("");
Assert.That(result, Is.EqualTo(string.Empty));
}
[Test]
public void ConvertToStringFromString()
{
var colorString = "Green";
var result = _converter.ConvertTo(colorString, typeof(string));
Assert.That(result, Is.EqualTo("Green"));
}
[Test]
public void ConvertToColorFromNamedString()
{
var colorString = "Red";
var result = _converter.ConvertTo(colorString, typeof(Color));
Assert.That(result, Is.EqualTo(Color.Red));
}
[Test]
public void ConvertToColorFromHexString()
{
var colorString = "#FF0000";
var result = _converter.ConvertTo(colorString, typeof(Color));
var expectedColor = Color.FromArgb(255, 255, 0, 0);
Assert.That(result, Is.EqualTo(expectedColor));
}
[Test]
public void ConvertToColorFromEmptyStringReturnsEmpty()
{
var result = _converter.ConvertTo("", typeof(Color));
Assert.That(result, Is.EqualTo(Color.Empty));
}
[Test]
public void ConvertToColorFromNullReturnsEmpty()
{
var result = _converter.ConvertTo(null, typeof(Color));
Assert.That(result, Is.EqualTo(Color.Empty));
}
[Test]
public void GetStandardValuesSupportedReturnsTrue()
{
var result = _converter.GetStandardValuesSupported(null);
Assert.That(result, Is.True);
}
[Test]
public void GetStandardValuesReturnsColorList()
{
var result = _converter.GetStandardValues(null);
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.GreaterThan(0));
}
[Test]
public void GetStandardValuesExclusiveReturnsFalse()
{
var result = _converter.GetStandardValuesExclusive(null);
Assert.That(result, Is.False);
}
[Test]
public void ConvertFromColorObjectDoesNotThrowException()
{
// This test verifies the fix for the "Object of type 'System.Drawing.Color' cannot be converted to type 'System.String'" error
var color = Color.FromArgb(255, 100, 150, 200);
Assert.DoesNotThrow(() => _converter.ConvertFrom(color));
}
[Test]
public void ColorPropertyUsesTabColorConverter()
{
// This test verifies that the Color property can properly handle Color objects
// by using TabColorConverter instead of System.Drawing.ColorConverter
var color = Color.Blue;
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("Blue"));
}
}
}