Skip to content

Commit

Permalink
[Debugging] Debugging features
Browse files Browse the repository at this point in the history
  • Loading branch information
BAndysc committed Feb 22, 2024
1 parent 3a14355 commit 4f3108d
Show file tree
Hide file tree
Showing 228 changed files with 9,893 additions and 476 deletions.
2 changes: 1 addition & 1 deletion AvaloniaStyles/Controls/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ static Extensions()
if (args.NewValue is not Type type)
return;

var values = Enum.GetValues(type).Cast<object>().Zip(Enum.GetNames(type), (val, name) => new Option(val, type, combo, name)).ToList();
var values = Enum.GetValuesAsUnderlyingType(type).Cast<object>().Zip(Enum.GetNames(type), (val, name) => new Option(val, type, combo, name)).ToList();
combo.AsyncPopulator = async (items, str, _) =>
{
if (string.IsNullOrEmpty(str))
Expand Down
1 change: 1 addition & 0 deletions AvaloniaStyles/Controls/SettingItem.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
Content="{TemplateBinding Content}" />
<TextBlock Text="{TemplateBinding Header}"
Margin="0,0,50,0"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Center"
TextWrapping="WrapWithOverflow" />
</Grid>
Expand Down
3 changes: 1 addition & 2 deletions AvaloniaStyles/Styles/Windows10/TabControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}">
<DockPanel>
<DockPanel DockPanel.Dock="{TemplateBinding TabStripPlacement}" KeyboardNavigation.TabNavigation="Local">
<DockPanel ZIndex="1" DockPanel.Dock="{TemplateBinding TabStripPlacement}" KeyboardNavigation.TabNavigation="Local">
<!-- Used in MySqlWorkbench -->
<ContentControl DockPanel.Dock="Right" Content="{TemplateBinding Tag}" TabIndex="2" />
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}"
Margin="0,0,0,-1"
ZIndex="1"
TabIndex="1"/>
</DockPanel>
<Border Name="PART_SelectedContentHostBorder" ZIndex="0"
Expand Down
1 change: 1 addition & 0 deletions AvaloniaStyles/Styles/Windows11/TextBox.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource TextControlBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource TextControlBorderThemeThickness}" />
<Setter Property="Padding" Value="{DynamicResource TextControlThemePadding}" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
</Style>

<!-- fluent -->
Expand Down
1 change: 1 addition & 0 deletions LoaderAvalonia/LoaderAvalonia.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<ItemGroup>
<ProjectReference Include="..\AvaloniaStyles\AvaloniaStyles.csproj" />
<ProjectReference Include="..\Modules\WDE.DatabaseDefinitionEditor\WDE.DatabaseDefinitionEditor.csproj" />
<ProjectReference Include="..\Modules\WDE.Debugger\WDE.Debugger.csproj" />
<ProjectReference Include="..\Modules\WDE.EventScriptsEditor\WDE.EventScriptsEditor.csproj" />
<ProjectReference Include="..\Modules\WDE.LootEditor\WDE.LootEditor.csproj" />
<ProjectReference Include="..\Modules\WDE.PathPreviewTool\WDE.PathPreviewTool.csproj" />
Expand Down
4 changes: 3 additions & 1 deletion LoaderAvalonia/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
using WDE.WoWHeadConnector;
using WDE.AnniversaryInfo;
using WDE.DatabaseDefinitionEditor;
using WDE.Debugger;
using WDE.EventAiEditor.Avalonia;
using WDE.EventScriptsEditor;
using WDE.MangosEventAiEditor;
Expand Down Expand Up @@ -93,7 +94,8 @@ public static void Main(string[] args)
typeof(ProfilesModule),
typeof(DatabaseDefinitionEditorModule),
typeof(LootEditorModule),
typeof(SqlWorkbenchModule)
typeof(SqlWorkbenchModule),
typeof(DebuggerModule)
};
WoWDatabaseEditorCore.Avalonia.Program.PreloadedModules = modules;
WoWDatabaseEditorCore.Avalonia.Program.Main(args);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NSubstitute;
using NUnit.Framework;
using Prism.Events;
using WDE.Common.Debugging;
using WDE.Common.Services;
using WDE.Debugger.Services;

namespace WDE.Debugger.Test.Services;

public class DebuggerServiceSynchronizationTests
{
private class DemoPayload : IDebugPointPayload
{
public DemoPayload(int value)
{
Value = value;
}

public int Value { get; set; }
}

private IDebuggerDelayedSaveService delayedSaveService = null!;
private IUserSettings userSettings = null!;
private IDebugPointSynchronizer synchronizer = null!;
private IDebugPointSource source = null!;
private IRemoteConnectorService remoteConnectorService = null!;
private IEventAggregator eventAggregator = null!;
private IDebugPointSource[] sources = null!;
private DebuggerService service = null!;

[SetUp]
public void Setup()
{
delayedSaveService = Substitute.For<IDebuggerDelayedSaveService>();
userSettings = Substitute.For<IUserSettings>();
synchronizer = Substitute.For<IDebugPointSynchronizer>();
source = Substitute.For<IDebugPointSource>();
eventAggregator = Substitute.For<IEventAggregator>();
source.Synchronizer.Returns(synchronizer);
remoteConnectorService = Substitute.For<IRemoteConnectorService>();
sources = new[] { source };
remoteConnectorService.IsConnected.Returns(true);
eventAggregator.GetEvent<IdeBreakpointHitEvent>().Returns(new IdeBreakpointHitEvent());
eventAggregator.GetEvent<IdeBreakpointResumeEvent>().Returns(new IdeBreakpointResumeEvent());
service = new DebuggerService(remoteConnectorService, userSettings, delayedSaveService, eventAggregator, sources);
}

[Test]
public async Task Breakpoint_Create_Delete_No_Synchronize()
{
bool eventFired = false;
bool enabled = false;
bool generateStackTrace = false;
BreakpointState state = default;
bool suspendExecution = false;
bool deleteFired = false;
service.DebugPointAdded += p =>
{
eventFired = true;
enabled = service.GetEnabled(p);
generateStackTrace = service.GetGenerateStacktrace(p);
state = service.GetState(p);
suspendExecution = service.GetSuspendExecution(p);
};
service.DebugPointRemoving += p =>
{
// in the remove callback, the payload should be still valid
Assert.IsTrue(service.TryGetPayload<DemoPayload>(p, out DemoPayload? payload));
Assert.AreEqual(5, payload.Value);
deleteFired = true;
};

var debugPoint = service.CreateDebugPoint(source, new DemoPayload(5));
Assert.AreNotEqual(DebugPointId.Empty, debugPoint);
Assert.IsTrue(eventFired);
// test default values
Assert.IsTrue(enabled);
Assert.IsTrue(generateStackTrace);
Assert.AreEqual(BreakpointState.Pending, state);
Assert.IsFalse(suspendExecution);

// synchronize needs to be explicitly called
await synchronizer.DidNotReceiveWithAnyArgs().Synchronize(debugPoint);

await service.RemoveDebugPointAsync(debugPoint, false);

await synchronizer.Received().Delete(debugPoint);

Assert.IsTrue(deleteFired);
}

[Test]
public async Task Breakpoint_Exception_In_Synchronize()
{
var debugPoint = service.CreateDebugPoint(source, new DemoPayload(5));
Assert.AreEqual(BreakpointState.Pending, service.GetState(debugPoint));

synchronizer.Synchronize(debugPoint).ReturnsForAnyArgs(Task.FromException<SynchronizationResult>(new Exception("simulated error")));

Assert.ThrowsAsync<Exception>(() => service.Synchronize(debugPoint));

await synchronizer.Received().Synchronize(debugPoint);

Assert.AreEqual(BreakpointState.SynchronizationError, service.GetState(debugPoint));
}

[Test]
public async Task Breakpoint_Long_Synchronize_And_Delete_Before()
{
var debugPoint = service.CreateDebugPoint(source, new DemoPayload(5));
Assert.AreEqual(BreakpointState.Pending, service.GetState(debugPoint));

var synchronizeLatch = new TaskCompletionSource<SynchronizationResult>();

synchronizer.Synchronize(debugPoint).ReturnsForAnyArgs(synchronizeLatch.Task);

var synchronizeTask = service.Synchronize(debugPoint);

Assert.AreEqual(BreakpointState.Pending, service.GetState(debugPoint));

var deleteTask = service.RemoveDebugPointAsync(debugPoint, false);

synchronizeLatch.SetResult(SynchronizationResult.Ok);

await synchronizeTask;
await deleteTask;

Assert.AreEqual(0, service.DebugPoints.Count());
}

[Test]
public async Task Synchronize_UnchangedDebugPoint_DoesNotTriggerSynchronization()
{
bool debugPointChangedEventFired = false;
service.DebugPointChanged += p => debugPointChangedEventFired = true;

var debugPoint = service.CreateDebugPoint(source, new DemoPayload(10));
await service.Synchronize(debugPoint); // Assume this triggers the first synchronization

// Reset state to simulate no changes
debugPointChangedEventFired = false;
synchronizer.ClearReceivedCalls();

// Attempt to synchronize again without making changes
await service.Synchronize(debugPoint);

// Verify that synchronization was not triggered again
await synchronizer.DidNotReceiveWithAnyArgs().Synchronize(debugPoint);
Assert.IsFalse(debugPointChangedEventFired, "DebugPointChanged event should not fire if the debug point was not modified.");
}

[Test]
public async Task ModifyDebugPoint_AfterSynchronization_RequiresResynchronization()
{
bool debugPointChangedEventFired = false;
service.DebugPointChanged += p => debugPointChangedEventFired = true;

var debugPoint = service.CreateDebugPoint(source, new DemoPayload(20));
await service.Synchronize(debugPoint); // Synchronize the debug point initially

// Modify the debug point, triggering a need for resynchronization
service.SetLog(debugPoint, "New log message");

// Verify state change and event firing
Assert.IsTrue(debugPointChangedEventFired, "Modifying a debug point should trigger the DebugPointChanged event.");
Assert.AreEqual(BreakpointState.Pending, service.GetState(debugPoint), "Debug point state should be set to Pending after modification.");

// Verify that synchronization is expected again
await synchronizer.Received().Synchronize(debugPoint);
}

[Test]
public async Task SynchronizeAndRemoveDebugPoint_Sequence_HandlesCorrectly()
{
var removalLatch = new TaskCompletionSource<bool>();
bool debugPointRemovedEventFired = false;
service.DebugPointRemoved += p =>
{
debugPointRemovedEventFired = true;
};

// Create a new debug point
var debugPoint = service.CreateDebugPoint(source, new DemoPayload(5));

// Set up the mock to delay deletion until the latch is released
synchronizer.Delete(debugPoint).Returns(removalLatch.Task);

// Initiate removal in the background without awaiting completion immediately
var removeTask = service.RemoveDebugPointAsync(debugPoint, false);

// Immediately allow the removal operation to proceed, eliminating the wait
removalLatch.SetResult(true);

// Then, initiate synchronization
Assert.ThrowsAsync<InvalidOperationException>(async () => await service.Synchronize(debugPoint));

// Await the removal task to ensure it completes after synchronization
await removeTask;

// Ensure the debug point was removed as expected
Assert.IsTrue(debugPointRemovedEventFired, "DebugPointRemoved event should fire indicating successful removal.");
// Ensure cleanup actions were performed as expected
await synchronizer.Received().Delete(debugPoint);
}
}
Loading

0 comments on commit 4f3108d

Please sign in to comment.