From f3d7fb3972bb73a72527629a04d9de7d1ce5f206 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 5 Nov 2020 18:50:10 -0500 Subject: [PATCH 01/21] [wasm][debugger] Move DevToolsQueue to a separate file --- .../BrowserDebugProxy/DevToolsProxy.cs | 44 --------------- .../BrowserDebugProxy/DevToolsQueue.cs | 55 +++++++++++++++++++ 2 files changed, 55 insertions(+), 44 deletions(-) create mode 100644 src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs index 204e9b7845b8be..bb8e5b1715b01e 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs @@ -16,50 +16,6 @@ namespace Microsoft.WebAssembly.Diagnostics { - internal class DevToolsQueue - { - private Task current_send; - private List pending; - - public WebSocket Ws { get; private set; } - public Task CurrentSend { get { return current_send; } } - public DevToolsQueue(WebSocket sock) - { - this.Ws = sock; - pending = new List(); - } - - public Task Send(byte[] bytes, CancellationToken token) - { - pending.Add(bytes); - if (pending.Count == 1) - { - if (current_send != null) - throw new Exception("current_send MUST BE NULL IF THERE'S no pending send"); - //logger.LogTrace ("sending {0} bytes", bytes.Length); - current_send = Ws.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, token); - return current_send; - } - return null; - } - - public Task Pump(CancellationToken token) - { - current_send = null; - pending.RemoveAt(0); - - if (pending.Count > 0) - { - if (current_send != null) - throw new Exception("current_send MUST BE NULL IF THERE'S no pending send"); - - current_send = Ws.SendAsync(new ArraySegment(pending[0]), WebSocketMessageType.Text, true, token); - return current_send; - } - return null; - } - } - internal class DevToolsProxy { private TaskCompletionSource side_exception = new TaskCompletionSource(); diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs new file mode 100644 index 00000000000000..3fd86f40eb9522 --- /dev/null +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.WebAssembly.Diagnostics +{ + internal class DevToolsQueue + { + private Task current_send; + private List pending; + + public WebSocket Ws { get; private set; } + public Task CurrentSend { get { return current_send; } } + public DevToolsQueue(WebSocket sock) + { + this.Ws = sock; + pending = new List(); + } + + public Task Send(byte[] bytes, CancellationToken token) + { + pending.Add(bytes); + if (pending.Count == 1) + { + if (current_send != null) + throw new Exception("current_send MUST BE NULL IF THERE'S no pending send"); + //logger.LogTrace ("sending {0} bytes", bytes.Length); + current_send = Ws.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, token); + return current_send; + } + return null; + } + + public Task Pump(CancellationToken token) + { + current_send = null; + pending.RemoveAt(0); + + if (pending.Count > 0) + { + if (current_send != null) + throw new Exception("current_send MUST BE NULL IF THERE'S no pending send"); + + current_send = Ws.SendAsync(new ArraySegment(pending[0]), WebSocketMessageType.Text, true, token); + return current_send; + } + return null; + } + } +} From 61d78de6b1b464d6674267bc062a509a97551561 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 5 Nov 2020 18:54:39 -0500 Subject: [PATCH 02/21] Move Inspector to separate file, mv Support.cs=>DebuggerTest.cs --- .../{Support.cs => DebuggerTestBase.cs} | 94 --------------- .../debugger/DebuggerTestSuite/Inspector.cs | 107 ++++++++++++++++++ 2 files changed, 107 insertions(+), 94 deletions(-) rename src/mono/wasm/debugger/DebuggerTestSuite/{Support.cs => DebuggerTestBase.cs} (91%) create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Support.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs similarity index 91% rename from src/mono/wasm/debugger/DebuggerTestSuite/Support.cs rename to src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index 4a0e50b40c7245..55d42ccba6ba70 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Support.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -19,100 +19,6 @@ namespace DebuggerTests { - class Inspector - { - // InspectorClient client; - Dictionary> notifications = new Dictionary>(); - Dictionary> eventListeners = new Dictionary>(); - - public const string PAUSE = "pause"; - public const string READY = "ready"; - - public Task WaitFor(string what) - { - if (notifications.ContainsKey(what)) - throw new Exception($"Invalid internal state, waiting for {what} while another wait is already setup"); - var n = new TaskCompletionSource(); - notifications[what] = n; - return n.Task; - } - - void NotifyOf(string what, JObject args) - { - if (!notifications.ContainsKey(what)) - throw new Exception($"Invalid internal state, notifying of {what}, but nobody waiting"); - notifications[what].SetResult(args); - notifications.Remove(what); - } - - public void On(string evtName, Func cb) - { - eventListeners[evtName] = cb; - } - - void FailAllWaitersWithException(JObject exception) - { - foreach (var tcs in notifications.Values) - tcs.SetException(new ArgumentException(exception.ToString())); - } - - async Task OnMessage(string method, JObject args, CancellationToken token) - { - //System.Console.WriteLine("OnMessage " + method + args); - switch (method) - { - case "Debugger.paused": - NotifyOf(PAUSE, args); - break; - case "Mono.runtimeReady": - NotifyOf(READY, args); - break; - case "Runtime.consoleAPICalled": - Console.WriteLine("CWL: {0}", args?["args"]?[0]?["value"]); - break; - } - if (eventListeners.ContainsKey(method)) - await eventListeners[method](args, token); - else if (String.Compare(method, "Runtime.exceptionThrown") == 0) - FailAllWaitersWithException(args); - } - - public async Task Ready(Func cb = null, TimeSpan? span = null) - { - using (var cts = new CancellationTokenSource()) - { - cts.CancelAfter(span?.Milliseconds ?? 60 * 1000); //tests have 1 minute to complete by default - var uri = new Uri($"ws://{TestHarnessProxy.Endpoint.Authority}/launch-chrome-and-connect"); - using var loggerFactory = LoggerFactory.Create( - builder => builder.AddConsole().AddFilter(null, LogLevel.Information)); - using (var client = new InspectorClient(loggerFactory.CreateLogger())) - { - await client.Connect(uri, OnMessage, async token => - { - Task[] init_cmds = { - client.SendCommand("Profiler.enable", null, token), - client.SendCommand("Runtime.enable", null, token), - client.SendCommand("Debugger.enable", null, token), - client.SendCommand("Runtime.runIfWaitingForDebugger", null, token), - WaitFor(READY), - }; - // await Task.WhenAll (init_cmds); - Console.WriteLine("waiting for the runtime to be ready"); - await init_cmds[4]; - Console.WriteLine("runtime ready, TEST TIME"); - if (cb != null) - { - Console.WriteLine("await cb(client, token)"); - await cb(client, token); - } - - }, cts.Token); - await client.Close(cts.Token); - } - } - } - } - public class DebuggerTestBase { protected Task startTask; diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs new file mode 100644 index 00000000000000..2809c8feaaeb23 --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.WebAssembly.Diagnostics; +using Newtonsoft.Json.Linq; + +namespace DebuggerTests +{ + class Inspector + { + // InspectorClient client; + Dictionary> notifications = new Dictionary>(); + Dictionary> eventListeners = new Dictionary>(); + + public const string PAUSE = "pause"; + public const string READY = "ready"; + + public Task WaitFor(string what) + { + if (notifications.ContainsKey(what)) + throw new Exception($"Invalid internal state, waiting for {what} while another wait is already setup"); + var n = new TaskCompletionSource(); + notifications[what] = n; + return n.Task; + } + + void NotifyOf(string what, JObject args) + { + if (!notifications.ContainsKey(what)) + throw new Exception($"Invalid internal state, notifying of {what}, but nobody waiting"); + notifications[what].SetResult(args); + notifications.Remove(what); + } + + public void On(string evtName, Func cb) + { + eventListeners[evtName] = cb; + } + + void FailAllWaitersWithException(JObject exception) + { + foreach (var tcs in notifications.Values) + tcs.SetException(new ArgumentException(exception.ToString())); + } + + async Task OnMessage(string method, JObject args, CancellationToken token) + { + //System.Console.WriteLine("OnMessage " + method + args); + switch (method) + { + case "Debugger.paused": + NotifyOf(PAUSE, args); + break; + case "Mono.runtimeReady": + NotifyOf(READY, args); + break; + case "Runtime.consoleAPICalled": + Console.WriteLine("CWL: {0}", args?["args"]); + break; + } + if (eventListeners.ContainsKey(method)) + await eventListeners[method](args, token); + else if (String.Compare(method, "Runtime.exceptionThrown") == 0) + FailAllWaitersWithException(args); + } + + public async Task Ready(Func cb = null, TimeSpan? span = null) + { + using (var cts = new CancellationTokenSource()) + { + cts.CancelAfter(span?.Milliseconds ?? 60 * 1000); //tests have 1 minute to complete by default + var uri = new Uri($"ws://{TestHarnessProxy.Endpoint.Authority}/launch-chrome-and-connect"); + using var loggerFactory = LoggerFactory.Create( + builder => builder.AddConsole().AddFilter(null, LogLevel.Information)); + using (var client = new InspectorClient(loggerFactory.CreateLogger())) + { + await client.Connect(uri, OnMessage, async token => + { + Task[] init_cmds = { + client.SendCommand("Profiler.enable", null, token), + client.SendCommand("Runtime.enable", null, token), + client.SendCommand("Debugger.enable", null, token), + client.SendCommand("Runtime.runIfWaitingForDebugger", null, token), + WaitFor(READY), + }; + // await Task.WhenAll (init_cmds); + Console.WriteLine("waiting for the runtime to be ready"); + await init_cmds[4]; + Console.WriteLine("runtime ready, TEST TIME"); + if (cb != null) + { + Console.WriteLine("await cb(client, token)"); + await cb(client, token); + } + + }, cts.Token); + await client.Close(cts.Token); + } + } + } + } +} From aa5eb6fffa31a4d092c3c1b93cb1991effd5604a Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 19 Nov 2020 16:44:49 -0500 Subject: [PATCH 03/21] [wasm][debugger][tests] Some improvements to DevToolsQueue - `Pump` -> `TryPump` - use a ConcurrentQueue instead of a List, for tracking the pending requests. DevToolsClient: - use DevToolsQueue, and remove the redundant code. - Wake up the runloop when a new send task gets added --- .../BrowserDebugProxy/DevToolsProxy.cs | 3 +- .../BrowserDebugProxy/DevToolsQueue.cs | 51 ++++++++++--------- .../DebuggerTestSuite.csproj | 2 + .../DebuggerTestSuite/DevToolsClient.cs | 46 ++++++----------- 4 files changed, 45 insertions(+), 57 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs index bb8e5b1715b01e..b395be679f2083 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs @@ -295,8 +295,7 @@ public async Task Run(Uri browserUri, WebSocket ideSocket) DevToolsQueue queue = GetQueueForTask(task); if (queue != null) { - Task tsk = queue.Pump(x.Token); - if (tsk != null) + if (queue.TryPump(x.Token, out Task tsk)) pending_ops.Add(tsk); } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs index 3fd86f40eb9522..806cca61b1d2d2 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs @@ -2,54 +2,55 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; +#nullable enable + namespace Microsoft.WebAssembly.Diagnostics { internal class DevToolsQueue { - private Task current_send; - private List pending; + private Task? current_send; + private ConcurrentQueue pending; public WebSocket Ws { get; private set; } - public Task CurrentSend { get { return current_send; } } + public Task? CurrentSend { get { return current_send; } } public DevToolsQueue(WebSocket sock) { this.Ws = sock; - pending = new List(); + pending = new ConcurrentQueue(); } - public Task Send(byte[] bytes, CancellationToken token) + public Task? Send(byte[] bytes, CancellationToken token) { - pending.Add(bytes); - if (pending.Count == 1) - { - if (current_send != null) - throw new Exception("current_send MUST BE NULL IF THERE'S no pending send"); - //logger.LogTrace ("sending {0} bytes", bytes.Length); - current_send = Ws.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, token); - return current_send; - } - return null; + if (bytes == null) + throw new ArgumentNullException(nameof(bytes)); + + pending.Enqueue(bytes); + TryPump(token, out Task? sendTask); + return sendTask; } - public Task Pump(CancellationToken token) + public bool TryPump(CancellationToken token, [NotNullWhen(true)] out Task? sendTask) { - current_send = null; - pending.RemoveAt(0); + sendTask = null; - if (pending.Count > 0) - { - if (current_send != null) - throw new Exception("current_send MUST BE NULL IF THERE'S no pending send"); + if (current_send?.IsCompleted == false) + return false; - current_send = Ws.SendAsync(new ArraySegment(pending[0]), WebSocketMessageType.Text, true, token); - return current_send; + current_send = null; + if (pending.TryDequeue(out byte[]? bytes)) + { + current_send = Ws.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, token); + sendTask = current_send; } - return null; + + return sendTask != null; } } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj index 4a057ed11853eb..a46fd059b01fe9 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj @@ -9,6 +9,8 @@ + + diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs index b8fbac85a14de3..e6ce53156fde2c 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs @@ -14,11 +14,11 @@ namespace Microsoft.WebAssembly.Diagnostics { internal class DevToolsClient : IDisposable { + DevToolsQueue _queue; ClientWebSocket socket; List pending_ops = new List(); TaskCompletionSource side_exit = new TaskCompletionSource(); - List pending_writes = new List(); - Task current_write; + TaskCompletionSource _pendingOpsChanged = new TaskCompletionSource(); protected readonly ILogger logger; public DevToolsClient(ILogger logger) @@ -48,22 +48,6 @@ protected virtual void Dispose(bool disposing) socket.Dispose(); } - Task Pump(Task task, CancellationToken token) - { - if (task != current_write) - return null; - current_write = null; - - pending_writes.RemoveAt(0); - - if (pending_writes.Count > 0) - { - current_write = socket.SendAsync(new ArraySegment(pending_writes[0]), WebSocketMessageType.Text, true, token); - return current_write; - } - return null; - } - async Task ReadOne(CancellationToken token) { byte[] buff = new byte[4000]; @@ -90,15 +74,9 @@ async Task ReadOne(CancellationToken token) protected void Send(byte[] bytes, CancellationToken token) { - pending_writes.Add(bytes); - if (pending_writes.Count == 1) - { - if (current_write != null) - throw new Exception("Internal state is bad. current_write must be null if there are no pending writes"); - - current_write = socket.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, token); - pending_ops.Add(current_write); - } + var sendTask = _queue.Send(bytes, token); + if (sendTask != null) + pending_ops.Add(sendTask); } async Task MarkCompleteAfterward(Func send, CancellationToken token) @@ -126,7 +104,10 @@ protected async Task ConnectWithMainLoops( this.socket.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan; await this.socket.ConnectAsync(uri, token); + _queue = new DevToolsQueue(socket); + pending_ops.Add(ReadOne(token)); + pending_ops.Add(_pendingOpsChanged.Task); pending_ops.Add(side_exit.Task); pending_ops.Add(MarkCompleteAfterward(send, token)); @@ -142,6 +123,12 @@ protected async Task ConnectWithMainLoops( pending_ops.Add(tsk); } else if (task == pending_ops[1]) + { + // pending ops changed + _pendingOpsChanged = new TaskCompletionSource(); + pending_ops[1] = _pendingOpsChanged.Task; + } + else if (task == pending_ops[2]) { var res = ((Task)task).Result; //it might not throw if exiting successfull @@ -150,9 +137,8 @@ protected async Task ConnectWithMainLoops( else { //must be a background task pending_ops.Remove(task); - var tsk = Pump(task, token); - if (tsk != null) - pending_ops.Add(tsk); + if (_queue.TryPump(token, out Task sendTask)) + pending_ops.Add(sendTask); } } From a9c23157a7649294ed28240f11721c17074061bb Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 19 Nov 2020 17:03:57 -0500 Subject: [PATCH 04/21] [wasm][debugger] Check Proxy's client_initiated_close early - this allows the proxy to shutdown quicker, and not have to wait for the other tasks to get scheduled. - Fixes the issue of chrome processes left around. --- .../BrowserDebugProxy/DevToolsProxy.cs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs index b395be679f2083..5ca0e3668b85e8 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs @@ -19,7 +19,7 @@ namespace Microsoft.WebAssembly.Diagnostics internal class DevToolsProxy { private TaskCompletionSource side_exception = new TaskCompletionSource(); - private TaskCompletionSource client_initiated_close = new TaskCompletionSource(); + private TaskCompletionSource client_initiated_close = new TaskCompletionSource(); private Dictionary> pending_cmds = new Dictionary>(); private ClientWebSocket browser; private WebSocket ide; @@ -54,14 +54,14 @@ private async Task ReadOne(WebSocket socket, CancellationToken token) if (socket.State != WebSocketState.Open) { Log("error", $"DevToolsProxy: Socket is no longer open."); - client_initiated_close.TrySetResult(true); + client_initiated_close.TrySetResult(); return null; } WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment(buff), token); if (result.MessageType == WebSocketMessageType.Close) { - client_initiated_close.TrySetResult(true); + client_initiated_close.TrySetResult(); return null; } @@ -258,6 +258,16 @@ public async Task Run(Uri browserUri, WebSocket ideSocket) while (!x.IsCancellationRequested) { Task task = await Task.WhenAny(pending_ops.ToArray()); + + if (client_initiated_close.Task.IsCompleted) + { + await client_initiated_close.Task.ConfigureAwait(false); + Log("verbose", $"DevToolsProxy: Client initiated close from {browserUri}"); + x.Cancel(); + + break; + } + //logger.LogTrace ("pump {0} {1}", task, pending_ops.IndexOf (task)); if (task == pending_ops[0]) { @@ -282,12 +292,6 @@ public async Task Run(Uri browserUri, WebSocket ideSocket) bool res = ((Task)task).Result; throw new Exception("side task must always complete with an exception, what's going on???"); } - else if (task == pending_ops[3]) - { - bool res = ((Task)task).Result; - Log("verbose", $"DevToolsProxy: Client initiated close from {browserUri}"); - x.Cancel(); - } else { //must be a background task From d9753dcf01eb68b20d952c2a911eb20cef871f85 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 19 Nov 2020 17:40:28 -0500 Subject: [PATCH 05/21] [wasm][debugger][tests] Inspector - handle event being received before a waiter .. gets added. This handles the case where `NotifyOf` gets invoked before a corresponding `WaitFor` gets called. --- .../DebuggerTestSuite/HarnessTests.cs | 41 +++++++++++++++++++ .../debugger/DebuggerTestSuite/Inspector.cs | 37 +++++++++++++---- 2 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs new file mode 100644 index 00000000000000..c0d559d29d664d --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading.Tasks; +using Microsoft.WebAssembly.Diagnostics; +using Newtonsoft.Json.Linq; +using Xunit; + +#nullable enable + +namespace DebuggerTests +{ + public class HarnessTests : DebuggerTestBase + { + [Fact] + public async Task InspectorWaitForAfterMessageAlreadyReceived() + { + var insp = new Inspector(); + var scripts = SubscribeToScripts(insp); + + await Ready(); + await insp.Ready(async (cli, token) => + { + ctx = new DebugTestContext(cli, insp, token, scripts); + Result res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); + Assert.True(res.IsOk, $"setBreakpoint failed with {res}"); + + res = await ctx.cli.SendCommand( + "Runtime.evaluate", + JObject.FromObject(new { expression = "window.setTimeout(function() { invoke_add(); }, 0);" }), + ctx.token); + Assert.True(res.IsOk, $"evaluating the function failed with {res}"); + + // delay, so that we can get the Debugger.pause event + await Task.Delay(1000); + + await insp.WaitFor(Inspector.PAUSE); + }); + } + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs index 2809c8feaaeb23..e19270accb12a4 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs @@ -22,19 +22,40 @@ class Inspector public Task WaitFor(string what) { - if (notifications.ContainsKey(what)) + if (notifications.TryGetValue(what, out TaskCompletionSource tcs)) + { + if (tcs.Task.IsCompleted) + { + notifications.Remove(what); + return tcs.Task; + } + throw new Exception($"Invalid internal state, waiting for {what} while another wait is already setup"); - var n = new TaskCompletionSource(); - notifications[what] = n; - return n.Task; + } + else + { + var n = new TaskCompletionSource(); + notifications[what] = n; + return n.Task; + } } void NotifyOf(string what, JObject args) { - if (!notifications.ContainsKey(what)) - throw new Exception($"Invalid internal state, notifying of {what}, but nobody waiting"); - notifications[what].SetResult(args); - notifications.Remove(what); + if (notifications.TryGetValue(what, out TaskCompletionSource tcs)) + { + if (tcs.Task.IsCompleted) + throw new Exception($"Invalid internal state. Notifying for {what} again, but the previous one hasn't been read."); + + notifications[what].SetResult(args); + notifications.Remove(what); + } + else + { + var n = new TaskCompletionSource(); + notifications[what] = n; + n.SetResult(args); + } } public void On(string evtName, Func cb) From da7954e5e5d690ba2e2e9771670396f9f03f01cd Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 19 Nov 2020 19:30:34 -0500 Subject: [PATCH 06/21] [wasm][debugger][tests] Refactor test harness to support simpler test code Problem: - Currently, test methods must call `insp.Ready(..)` with a callback. And `Ready` takes care of the init, and shutdown for the proxy, and browser. - But this means that we have to repeat that boiler plate code in all the test methods. And the stack traces are deeply nested. - Instead, we want to have the *test method* contain only the test specific code. Solution: - we want to do that by using xunit's `IAsyncLifetime`, with methods: - `InitializeAsync` - setup proxy+browser here - `DisposeAsync` - shutdown - This would mean that when the *test method* runs, it already has everything set up, so the code can be just specific to the actual test. Implementation: - Improve DevToolsClient's RunLoop, to support this - Tries to handle shutdown/close requests explicitly, and not throw errors in such cases - Adds a `RunLoopStopped` event, which gets invoked with the reason due to which it stopped. Eg - Exception getting thrown, cancellation, shutdown request etc. - With `insp.Ready(..)`, the whole setup (launch proxy, browser, run test code) happens from the same *test method*. But here we are doing it at separate times, from different methods, and it's all async. - IOW, there are multiple places where failure can happen, and we need to explicitly propogate that, and fail the appropriate listeners/tasks/etc -> causing the test to correctly fail. Note: This does not change *any* actual test code! The `Ready` method is adjusted to use the new init/shutdown bits. Subsequent commits will make the test changes. --- .../DebuggerTestSuite/DevToolsClient.cs | 196 ++++++++++++---- .../debugger/DebuggerTestSuite/Inspector.cs | 222 +++++++++++++++--- .../DebuggerTestSuite/InspectorClient.cs | 20 +- 3 files changed, 346 insertions(+), 92 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs index e6ce53156fde2c..37fafc5d6a4409 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs @@ -17,10 +17,13 @@ internal class DevToolsClient : IDisposable DevToolsQueue _queue; ClientWebSocket socket; List pending_ops = new List(); - TaskCompletionSource side_exit = new TaskCompletionSource(); + TaskCompletionSource _clientInitiatedClose = new TaskCompletionSource(); + TaskCompletionSource _shutdownRequested = new TaskCompletionSource(); TaskCompletionSource _pendingOpsChanged = new TaskCompletionSource(); protected readonly ILogger logger; + public event EventHandler<(RunLoopStopReason reason, Exception ex)> RunLoopStopped; + public DevToolsClient(ILogger logger) { this.logger = logger; @@ -36,69 +39,86 @@ public void Dispose() Dispose(true); } - public async Task Close(CancellationToken cancellationToken) - { - if (socket.State == WebSocketState.Open) - await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken); - } - protected virtual void Dispose(bool disposing) { if (disposing) socket.Dispose(); } + public async Task Shutdown(CancellationToken cancellationToken) + { + if (_shutdownRequested.Task.IsCompleted) + { + logger.LogDebug($"Shutdown was already requested once. socket: {socket.State}. Ignoring"); + return; + } + + try + { + _shutdownRequested.SetResult(); + + if (!cancellationToken.IsCancellationRequested && socket.State == WebSocketState.Open) + await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken); + } + catch (Exception ex) when (ex is IOException || ex is WebSocketException || ex is OperationCanceledException) + { + logger.LogDebug($"DevToolsClient.Shutdown: Close failed, but ignoring: {ex}"); + } + } + async Task ReadOne(CancellationToken token) { byte[] buff = new byte[4000]; var mem = new MemoryStream(); while (true) { - var result = await this.socket.ReceiveAsync(new ArraySegment(buff), token); + if (socket.State != WebSocketState.Open) + { + logger.LogDebug($"Socket is no longer open"); + _clientInitiatedClose.TrySetResult(); + return null; + } + + WebSocketReceiveResult result; + try + { + result = await socket.ReceiveAsync(new ArraySegment(buff), token).ConfigureAwait(false); + } + catch (Exception ex) + { + if (token.IsCancellationRequested || _shutdownRequested.Task.IsCompletedSuccessfully) + return null; + + logger.LogDebug($"DevToolsClient.ReadOne threw {ex.Message}, token: {token.IsCancellationRequested}, _shutdown: {_shutdownRequested.Task.Status}, clientInitiated: {_clientInitiatedClose.Task.Status}"); + throw; + } + if (result.MessageType == WebSocketMessageType.Close) { + _clientInitiatedClose.TrySetResult(); return null; } + mem.Write(buff, 0, result.Count); if (result.EndOfMessage) { - mem.Write(buff, 0, result.Count); return Encoding.UTF8.GetString(mem.GetBuffer(), 0, (int)mem.Length); } - else - { - mem.Write(buff, 0, result.Count); - } } } protected void Send(byte[] bytes, CancellationToken token) { - var sendTask = _queue.Send(bytes, token); + Task sendTask = _queue.Send(bytes, token); if (sendTask != null) pending_ops.Add(sendTask); } - async Task MarkCompleteAfterward(Func send, CancellationToken token) - { - try - { - await send(token); - side_exit.SetResult(true); - } - catch (Exception e) - { - side_exit.SetException(e); - } - } - - protected async Task ConnectWithMainLoops( + protected async Task ConnectWithMainLoops( Uri uri, Func receive, - Func send, CancellationToken token) { - logger.LogDebug("connecting to {0}", uri); this.socket = new ClientWebSocket(); this.socket.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan; @@ -106,43 +126,115 @@ protected async Task ConnectWithMainLoops( await this.socket.ConnectAsync(uri, token); _queue = new DevToolsQueue(socket); - pending_ops.Add(ReadOne(token)); - pending_ops.Add(_pendingOpsChanged.Task); - pending_ops.Add(side_exit.Task); - pending_ops.Add(MarkCompleteAfterward(send, token)); + var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(token); + + _ = Task.Run(async () => + { + try + { + RunLoopStopReason reason; + Exception exception; + + try + { + (reason, exception) = await RunLoop(uri, receive, linkedCts); + } + catch (Exception ex) + { + logger.LogDebug($"RunLoop threw an exception. (parentToken: {token.IsCancellationRequested}, linked: {linkedCts.IsCancellationRequested}): {ex} "); + RunLoopStopped?.Invoke(this, (RunLoopStopReason.Exception, ex)); + return; + } + + try + { + logger.LogDebug($"RunLoop stopped, reason: {reason}. (parentToken: {token.IsCancellationRequested}, linked: {linkedCts.IsCancellationRequested}): {exception?.Message}"); + RunLoopStopped?.Invoke(this, (reason, exception)); + } + catch (Exception ex) + { + logger.LogError(ex, $"Invoking RunLoopStopped event failed for (reason: {reason}, exception: {exception})"); + } + } + finally + { + logger.LogDebug($"Loop ended with pending_ops: {pending_ops?.Count}, socket: {socket.State}"); + linkedCts.Cancel(); + pending_ops = null; + } + }); + } + + private async Task<(RunLoopStopReason, Exception)> RunLoop( + Uri uri, + Func receive, + CancellationTokenSource linkedCts) + { + pending_ops.InsertRange(0, new[] + { + ReadOne(linkedCts.Token), + _pendingOpsChanged.Task, + _clientInitiatedClose.Task, + _shutdownRequested.Task + }); + + // In case we had a Send called already + if (_queue.TryPump(linkedCts.Token, out Task sendTask)) + pending_ops.Add(sendTask); - while (!token.IsCancellationRequested) + while (!linkedCts.IsCancellationRequested) { - var task = await Task.WhenAny(pending_ops); + var task = await Task.WhenAny(pending_ops.ToArray()).ConfigureAwait(false); + + if (task.IsCanceled && linkedCts.IsCancellationRequested) + return (RunLoopStopReason.Cancelled, null); + + if (_shutdownRequested.Task.IsCompleted) + return (RunLoopStopReason.Shutdown, null); + + if (_clientInitiatedClose.Task.IsCompleted) + return (RunLoopStopReason.ClientInitiatedClose, new TaskCanceledException("Proxy or the browser closed the connection")); + if (task == pending_ops[0]) - { //pending_ops[0] is for message reading - var msg = ((Task)task).Result; - pending_ops[0] = ReadOne(token); - Task tsk = receive(msg, token); - if (tsk != null) - pending_ops.Add(tsk); + { + //pending_ops[0] is for message reading + var msg = await (Task)task; + pending_ops[0] = ReadOne(linkedCts.Token); + + if (msg != null) + { + Task tsk = receive(msg, linkedCts.Token); + if (tsk != null) + pending_ops.Add(tsk); + } } else if (task == pending_ops[1]) { - // pending ops changed + // Just needed to wake up. the new task has already + // been added to pending_ops _pendingOpsChanged = new TaskCompletionSource(); pending_ops[1] = _pendingOpsChanged.Task; } - else if (task == pending_ops[2]) - { - var res = ((Task)task).Result; - //it might not throw if exiting successfull - return res; - } else { //must be a background task pending_ops.Remove(task); - if (_queue.TryPump(token, out Task sendTask)) - pending_ops.Add(sendTask); + if (task == _queue.CurrentSend && _queue.TryPump(linkedCts.Token, out Task tsk)) + pending_ops.Add(tsk); } } - return false; + if (linkedCts.IsCancellationRequested) + return (RunLoopStopReason.Cancelled, null); + + return (RunLoopStopReason.Exception, new InvalidOperationException($"This shouldn't ever get thrown. Unsure why the loop stopped")); } } + + internal enum RunLoopStopReason + { + Shutdown, + Cancelled, + Exception, + ClientInitiatedClose + } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs index e19270accb12a4..33e349560ab475 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs @@ -3,26 +3,50 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.WebAssembly.Diagnostics; using Newtonsoft.Json.Linq; +#nullable enable + namespace DebuggerTests { class Inspector { - // InspectorClient client; + private const int DefaultTestTimeoutMs = 1 * 60 * 1000; + Dictionary> notifications = new Dictionary>(); Dictionary> eventListeners = new Dictionary>(); public const string PAUSE = "pause"; public const string READY = "ready"; + public CancellationToken Token { get; } + public InspectorClient Client { get; } + + private CancellationTokenSource _cancellationTokenSource; + + protected ILoggerFactory _loggerFactory; + protected ILogger _logger; + + public Inspector() + { + _cancellationTokenSource = new CancellationTokenSource(); + Token = _cancellationTokenSource.Token; + + _loggerFactory = LoggerFactory.Create( + builder => builder.AddConsole().AddFilter(null, LogLevel.Trace)); + + Client = new InspectorClient(_loggerFactory.CreateLogger()); + _logger = _loggerFactory.CreateLogger(); + } public Task WaitFor(string what) { - if (notifications.TryGetValue(what, out TaskCompletionSource tcs)) + if (notifications.TryGetValue(what, out TaskCompletionSource? tcs)) { if (tcs.Task.IsCompleted) { @@ -42,7 +66,7 @@ public Task WaitFor(string what) void NotifyOf(string what, JObject args) { - if (notifications.TryGetValue(what, out TaskCompletionSource tcs)) + if (notifications.TryGetValue(what, out TaskCompletionSource? tcs)) { if (tcs.Task.IsCompleted) throw new Exception($"Invalid internal state. Notifying for {what} again, but the previous one hasn't been read."); @@ -63,15 +87,27 @@ public void On(string evtName, Func cb) eventListeners[evtName] = cb; } - void FailAllWaitersWithException(JObject exception) + void FailAllWaiters(Exception? exception=null) { - foreach (var tcs in notifications.Values) - tcs.SetException(new ArgumentException(exception.ToString())); + // Because we can create already completed tasks, + // when we get a NotifyOf before the corresponding + // WaitFor, it might already be completed. So, use + // *Try* methods + + if (exception != null) + { + foreach (var tcs in notifications.Values) + tcs.TrySetException(exception); + } + else + { + foreach (var tcs in notifications.Values) + tcs.TrySetCanceled(); + } } async Task OnMessage(string method, JObject args, CancellationToken token) { - //System.Console.WriteLine("OnMessage " + method + args); switch (method) { case "Debugger.paused": @@ -81,47 +117,159 @@ async Task OnMessage(string method, JObject args, CancellationToken token) NotifyOf(READY, args); break; case "Runtime.consoleAPICalled": - Console.WriteLine("CWL: {0}", args?["args"]); + _logger.LogInformation("CWL: {0}", args["args"]); break; } - if (eventListeners.ContainsKey(method)) - await eventListeners[method](args, token); + if (eventListeners.TryGetValue(method, out var listener)) + { + await listener(args, token).ConfigureAwait(false); + } else if (String.Compare(method, "Runtime.exceptionThrown") == 0) - FailAllWaitersWithException(args); + { + _logger.LogDebug($"Failing all waiters because: {method}: {args}"); + FailAllWaiters(new ArgumentException(args.ToString())); + } } - public async Task Ready(Func cb = null, TimeSpan? span = null) + public async Task Ready(Func? cb = null, TimeSpan? span = null) { - using (var cts = new CancellationTokenSource()) + try + { + Func)>> fn = (_client, _token) => + { + Func)> getInitCmdFn = (cmd_name) => (cmd_name, _client.SendCommand(cmd_name, null, _token)); + return new List<(string, Task)> + { + getInitCmdFn("Profiler.enable"), + getInitCmdFn("Runtime.enable"), + getInitCmdFn("Debugger.enable"), + getInitCmdFn("Runtime.runIfWaitingForDebugger") + }; + }; + + await OpenSessionAsync(fn, span); + if (cb != null) + await cb(Client, _cancellationTokenSource.Token).ConfigureAwait(false); + } catch (Exception ex) { + if (_logger != null) + _logger.LogError(ex.ToString()); + else + Console.WriteLine (ex); + throw; + } + finally { - cts.CancelAfter(span?.Milliseconds ?? 60 * 1000); //tests have 1 minute to complete by default + await ShutdownAsync().ConfigureAwait(false); + } + } + + public async Task OpenSessionAsync(Func)>> getInitCmds, TimeSpan? span = null) + { + var start = DateTime.Now; + try { + _cancellationTokenSource.CancelAfter(span?.Milliseconds ?? DefaultTestTimeoutMs); + var uri = new Uri($"ws://{TestHarnessProxy.Endpoint.Authority}/launch-chrome-and-connect"); - using var loggerFactory = LoggerFactory.Create( - builder => builder.AddConsole().AddFilter(null, LogLevel.Information)); - using (var client = new InspectorClient(loggerFactory.CreateLogger())) + + await Client.Connect(uri, OnMessage, _cancellationTokenSource.Token); + Client.RunLoopStopped += (_, args) => { - await client.Connect(uri, OnMessage, async token => + switch (args.reason) { - Task[] init_cmds = { - client.SendCommand("Profiler.enable", null, token), - client.SendCommand("Runtime.enable", null, token), - client.SendCommand("Debugger.enable", null, token), - client.SendCommand("Runtime.runIfWaitingForDebugger", null, token), - WaitFor(READY), - }; - // await Task.WhenAll (init_cmds); - Console.WriteLine("waiting for the runtime to be ready"); - await init_cmds[4]; - Console.WriteLine("runtime ready, TEST TIME"); - if (cb != null) - { - Console.WriteLine("await cb(client, token)"); - await cb(client, token); - } - - }, cts.Token); - await client.Close(cts.Token); + case RunLoopStopReason.Exception: + FailAllWaiters(args.ex); + break; + + case RunLoopStopReason.Cancelled when Token.IsCancellationRequested: + FailAllWaiters(new TaskCanceledException($"Test timed out (elapsed time: {(DateTime.Now - start).TotalSeconds}")); + break; + + default: + FailAllWaiters(); + break; + }; + }; + + var init_cmds = getInitCmds(Client, _cancellationTokenSource.Token); + + Task readyTask = Task.Run(async () => Result.FromJson(await WaitFor(READY))); + init_cmds.Add((READY, readyTask)); + + _logger.LogInformation("waiting for the runtime to be ready"); + while (!_cancellationTokenSource.IsCancellationRequested && init_cmds.Count > 0) + { + var cmd_tasks = init_cmds.Select(ct => ct.Item2); + Task t = await Task.WhenAny(cmd_tasks); + + int cmdIdx = init_cmds.FindIndex(ct => ct.Item2 == t); + string cmd_name = init_cmds[cmdIdx].Item1; + + if (t.IsCanceled) + { + throw new TaskCanceledException( + $"Command {cmd_name} timed out during init for the test." + + $"Remaining commands: {RemainingCommandsToString(cmd_name, init_cmds)}." + + $"Total time: {(DateTime.Now - start).TotalSeconds}"); + } + + if (t.IsFaulted) + { + _logger.LogError($"Command {cmd_name} failed with {t.Exception}. Remaining commands: {RemainingCommandsToString(cmd_name, init_cmds)}."); + throw t.Exception!; + } + + Result res = t.Result; + if (res.IsErr) + throw new ArgumentException($"Command {cmd_name} failed with: {res.Error}. Remaining commands: {RemainingCommandsToString(cmd_name, init_cmds)}"); + + init_cmds.RemoveAt(cmdIdx); + } + + _logger.LogInformation("runtime ready, TEST TIME"); + } catch (Exception ex) { + _logger.LogDebug(ex.ToString()); + throw; + } + + static string RemainingCommandsToString(string cmd_name, IList<(string, Task)> cmds) + { + var sb = new StringBuilder(); + for (int i = 0; i < cmds.Count; i ++) + { + var (name, task) = cmds[i]; + + if (cmd_name == name) + continue; + + sb.Append($"[{name}: {task.Status}], "); } + + if (sb.Length > 2) + sb.Length -= 2; + + return sb.ToString(); + } + } + + public async Task ShutdownAsync() + { + if (Client == null) + throw new InvalidOperationException($"InspectorClient is null. Duplicate Shutdown?"); + + try + { + _logger?.LogDebug($"- test done,. let's close the client"); + await Client.Shutdown(_cancellationTokenSource.Token).ConfigureAwait(false); + } catch (Exception ex) { + _logger?.LogError(ex.ToString()); + throw; + } + finally + { + _cancellationTokenSource.Cancel(); + Client.Dispose(); + _loggerFactory?.Dispose(); + _cancellationTokenSource.Dispose(); } } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs b/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs index e461300457da77..6f221e9a7e32ac 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs @@ -37,12 +37,26 @@ Task HandleMessage(string msg, CancellationToken token) public async Task Connect( Uri uri, Func onEvent, - Func send, CancellationToken token) { - this.onEvent = onEvent; - await ConnectWithMainLoops(uri, HandleMessage, send, token); + + RunLoopStopped += (_, args) => + { + logger.LogDebug($"Failing {pending_cmds.Count} pending cmds"); + if (args.reason == RunLoopStopReason.Exception) + { + foreach (var cmd in pending_cmds.Values) + cmd.SetException(args.ex); + } + else + { + foreach (var cmd in pending_cmds.Values) + cmd.SetCanceled(); + } + }; + + await ConnectWithMainLoops(uri, HandleMessage, token); } public Task SendCommand(string method, JObject args, CancellationToken token) From 92c712f55d278b0e9e7f3ebb55287f4e6b485814 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 19 Nov 2020 22:51:51 -0500 Subject: [PATCH 07/21] [wasm][debugger][tests] Port some tests to the simpler test harness bits We add a `SingleSessionTestBase` class, which implements `IAsyncLifetime` to init/shutdown the proxy+browser outside the test methods. And port `HarnessTests`, and `SteppingTests` to that. Added a few more tests to `HarnessTests`, to check for common cases like test methods throwing exceptions, or timing out, or commands failing etc. - This is needed because with the older `insp.Ready` API, the whole thing was called from the test method, so failures would correctly fail the test. - But now we do these things in separate steps, so failing the test requires a bit more work in the harness. Note: I didn't port all the tests in one go, to make the review a bit simpler. A subsequent PR can move rest of the tests over, and do some cleanup that it would afford. --- .../DebuggerTestSuite/AssertHelpers.cs | 27 + .../DebuggerTestSuite/BadHarnessInitTests.cs | 45 + .../DebuggerTestSuite/HarnessTests.cs | 65 +- .../SingleSessionTestBase.cs | 49 + .../DebuggerTestSuite/SteppingTests.cs | 1417 +++++++---------- 5 files changed, 740 insertions(+), 863 deletions(-) create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs b/src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs new file mode 100644 index 00000000000000..c4b4409bc76dfa --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading.Tasks; +using Xunit; + +namespace DebuggerTests +{ + static class AssertHelpers + { + public static async Task ThrowsAsync(Func testCode, Action testException) + where T: Exception + { + try + { + await testCode(); + Assert.True(false, $"Expected an exception of type {typeof(T)} to be thrown"); + } + catch (Exception exception) + { + Assert.Equal(typeof(T), exception.GetType()); + testException(exception); + } + } + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs new file mode 100644 index 00000000000000..4bdecc149cfa81 --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.WebAssembly.Diagnostics; +using Xunit; + +#nullable enable + +namespace DebuggerTests +{ + public class BadHarnessInitTests : DebuggerTestBase + { + internal Inspector insp; + protected Dictionary scripts; + + public BadHarnessInitTests(string driver = "debugger-driver.html") : base(driver) + { + insp = new Inspector(); + scripts = SubscribeToScripts(insp); + } + + [Fact] + public async Task InvalidInitCommands() + { + var bad_cmd_name = "non-existant.command"; + + Func)>> fn = (client, token) => + new List<(string, Task)> + { + ("Profiler.enable", client.SendCommand("Profiler.enable", null, token)), + (bad_cmd_name, client.SendCommand(bad_cmd_name, null, token)) + }; + + await Ready(); + + await AssertHelpers.ThrowsAsync( + async () => await insp.OpenSessionAsync(fn), + ae => ae.Message.Contains(bad_cmd_name+"Queue")); + } + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs index c0d559d29d664d..a89e5ceb533530 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Net.WebSockets; using System.Threading.Tasks; using Microsoft.WebAssembly.Diagnostics; using Newtonsoft.Json.Linq; @@ -10,32 +12,51 @@ namespace DebuggerTests { - public class HarnessTests : DebuggerTestBase + public class HarnessTests : SingleSessionTestBase { + [Fact] + public async Task TimedOutWaitingForInvalidBreakpoint() + { + await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 100, 0); + await AssertHelpers.ThrowsAsync( + async () => await EvaluateAndCheck("window.setTimeout(function() { invoke_add(); }, 1);", null, -1, -1, null), + ex => Assert.Contains("timed out", ex.Message)); + } + + [Fact] + public async Task ExceptionThrown() => await AssertHelpers.ThrowsAsync( + async () => await EvaluateAndCheck("window.setTimeout(function() { non_existant_fn(); }, 1);", null, -1, -1, null), + ex => Assert.Contains("non_existant_fn is not defined", ex.Message)); + + [Fact] + public async Task BrowserCrash() => await Assert.ThrowsAsync(async () => + await SendCommandAndCheck(null, "Browser.crash", null, -1, -1, null)); + + [Fact] + public async Task BrowserClose() => await Assert.ThrowsAsync(async () => + await SendCommandAndCheck(null, "Browser.close", null, -1, -1, null)); + [Fact] public async Task InspectorWaitForAfterMessageAlreadyReceived() { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - Result res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); - Assert.True(res.IsOk, $"setBreakpoint failed with {res}"); - - res = await ctx.cli.SendCommand( - "Runtime.evaluate", - JObject.FromObject(new { expression = "window.setTimeout(function() { invoke_add(); }, 0);" }), - ctx.token); - Assert.True(res.IsOk, $"evaluating the function failed with {res}"); - - // delay, so that we can get the Debugger.pause event - await Task.Delay(1000); - - await insp.WaitFor(Inspector.PAUSE); - }); + Result res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); + Assert.True(res.IsOk, $"setBreakpoint failed with {res}"); + + res = await ctx.cli.SendCommand( + "Runtime.evaluate", + JObject.FromObject(new { expression = "window.setTimeout(function() { invoke_add(); }, 0);" }), + ctx.token); + Assert.True(res.IsOk, $"evaluating the function failed with {res}"); + + // delay, so that we can get the Debugger.pause event + await Task.Delay(1000); + + await insp.WaitFor(Inspector.PAUSE); } + + [Fact] + public async Task InspectorWaitForMessageThatNeverArrives() => await AssertHelpers.ThrowsAsync( + async () => await insp.WaitFor("Message.that.never.arrives"), + ex => Assert.Contains("timed out", ex.Message)); } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs new file mode 100644 index 00000000000000..ec765d575bb6f3 --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.WebAssembly.Diagnostics; +using Xunit; + +#nullable enable + +namespace DebuggerTests +{ + public abstract class SingleSessionTestBase : DebuggerTestBase, IAsyncLifetime + { + internal Inspector insp; + protected Dictionary scripts; + + public SingleSessionTestBase(string driver = "debugger-driver.html") : base(driver) + { + insp = new Inspector(); + scripts = SubscribeToScripts(insp); + } + + public virtual async Task InitializeAsync() + { + Func)>> fn = (client, token) => + { + Func)> getInitCmdFn = (cmd) => (cmd, client.SendCommand(cmd, null, token)); + var init_cmds = new List<(string, Task)> + { + getInitCmdFn("Profiler.enable"), + getInitCmdFn("Runtime.enable"), + getInitCmdFn("Debugger.enable"), + getInitCmdFn("Runtime.runIfWaitingForDebugger") + }; + + return init_cmds; + }; + + await Ready(); + await insp.OpenSessionAsync(fn); + ctx = new DebugTestContext(insp.Client, insp, insp.Token, scripts); + } + + public virtual async Task DisposeAsync() => await insp.ShutdownAsync().ConfigureAwait(false); + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs index 68be3b4e17ff85..d966320ded0f6e 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs @@ -6,99 +6,79 @@ namespace DebuggerTests { - public class SteppingTests : DebuggerTestBase + public class SteppingTests : SingleSessionTestBase { [Fact] public async Task TrivalStepping() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - - var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); - - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_add(); }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, - "IntAdd", - wait_for_event_fn: (pause_location) => - { - //make sure we're on the right bp - Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_add(); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, + "IntAdd", + wait_for_event_fn: (pause_location) => + { + //make sure we're on the right bp + Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - var top_frame = pause_location["callFrames"][0]; - CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, top_frame["functionLocation"]); - return Task.CompletedTask; - } - ); + var top_frame = pause_location["callFrames"][0]; + CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, top_frame["functionLocation"]); + return Task.CompletedTask; + } + ); - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 11, 8, "IntAdd", - wait_for_event_fn: (pause_location) => - { - var top_frame = pause_location["callFrames"][0]; - CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, top_frame["functionLocation"]); - return Task.CompletedTask; - } - ); - }); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 11, 8, "IntAdd", + wait_for_event_fn: (pause_location) => + { + var top_frame = pause_location["callFrames"][0]; + CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, top_frame["functionLocation"]); + return Task.CompletedTask; + } + ); } [Fact] public async Task InspectLocalsDuringStepping() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs"; + await SetBreakpoint(debugger_test_loc, 10, 8); - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_add(); }, 1);", + debugger_test_loc, 10, 8, "IntAdd", + locals_fn: (locals) => + { + CheckNumber(locals, "a", 10); + CheckNumber(locals, "b", 20); + CheckNumber(locals, "c", 30); + CheckNumber(locals, "d", 0); + CheckNumber(locals, "e", 0); + } + ); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs"; - await SetBreakpoint(debugger_test_loc, 10, 8); + await StepAndCheck(StepKind.Over, debugger_test_loc, 11, 8, "IntAdd", + locals_fn: (locals) => + { + CheckNumber(locals, "a", 10); + CheckNumber(locals, "b", 20); + CheckNumber(locals, "c", 30); + CheckNumber(locals, "d", 50); + CheckNumber(locals, "e", 0); + } + ); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_add(); }, 1);", - debugger_test_loc, 10, 8, "IntAdd", - locals_fn: (locals) => - { - CheckNumber(locals, "a", 10); - CheckNumber(locals, "b", 20); - CheckNumber(locals, "c", 30); - CheckNumber(locals, "d", 0); - CheckNumber(locals, "e", 0); - } - ); - - await StepAndCheck(StepKind.Over, debugger_test_loc, 11, 8, "IntAdd", - locals_fn: (locals) => - { - CheckNumber(locals, "a", 10); - CheckNumber(locals, "b", 20); - CheckNumber(locals, "c", 30); - CheckNumber(locals, "d", 50); - CheckNumber(locals, "e", 0); - } - ); - - //step and get locals - await StepAndCheck(StepKind.Over, debugger_test_loc, 12, 8, "IntAdd", - locals_fn: (locals) => - { - CheckNumber(locals, "a", 10); - CheckNumber(locals, "b", 20); - CheckNumber(locals, "c", 30); - CheckNumber(locals, "d", 50); - CheckNumber(locals, "e", 60); - } - ); - }); + //step and get locals + await StepAndCheck(StepKind.Over, debugger_test_loc, 12, 8, "IntAdd", + locals_fn: (locals) => + { + CheckNumber(locals, "a", 10); + CheckNumber(locals, "b", 20); + CheckNumber(locals, "c", 30); + CheckNumber(locals, "d", 50); + CheckNumber(locals, "e", 60); + } + ); } [Theory] @@ -106,75 +86,66 @@ await StepAndCheck(StepKind.Over, debugger_test_loc, 12, 8, "IntAdd", [InlineData(true)] public async Task InspectLocalsInPreviousFramesDuringSteppingIn2(bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var dep_cs_loc = "dotnet://debugger-test.dll/dependency.cs"; - await SetBreakpoint(dep_cs_loc, 33, 8); + var dep_cs_loc = "dotnet://debugger-test.dll/dependency.cs"; + await SetBreakpoint(dep_cs_loc, 33, 8); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs"; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs"; - // Will stop in Complex.DoEvenMoreStuff - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_use_complex (); }, 1);", - dep_cs_loc, 33, 8, "DoEvenMoreStuff", - locals_fn: (locals) => - { - Assert.Single(locals); - CheckObject(locals, "this", "Simple.Complex"); - } - ); - - var props = await GetObjectOnFrame(pause_location["callFrames"][0], "this"); - Assert.Equal(3, props.Count()); - CheckNumber(props, "A", 10); - CheckString(props, "B", "xx"); - CheckString(props, "c", "20_xx"); - - // Check UseComplex frame - var locals_m1 = await GetLocalsForFrame(pause_location["callFrames"][3], debugger_test_loc, 23, 8, "UseComplex"); - Assert.Equal(7, locals_m1.Count()); - - CheckNumber(locals_m1, "a", 10); - CheckNumber(locals_m1, "b", 20); - CheckObject(locals_m1, "complex", "Simple.Complex"); - CheckNumber(locals_m1, "c", 30); - CheckNumber(locals_m1, "d", 50); - CheckNumber(locals_m1, "e", 60); - CheckNumber(locals_m1, "f", 0); - - props = await GetObjectOnFrame(pause_location["callFrames"][3], "complex"); - Assert.Equal(3, props.Count()); - CheckNumber(props, "A", 10); - CheckString(props, "B", "xx"); - CheckString(props, "c", "20_xx"); - - pause_location = await StepAndCheck(StepKind.Over, dep_cs_loc, 23, 8, "DoStuff", times: 2); - // Check UseComplex frame again - locals_m1 = await GetLocalsForFrame(pause_location["callFrames"][1], debugger_test_loc, 23, 8, "UseComplex"); - Assert.Equal(7, locals_m1.Count()); - - CheckNumber(locals_m1, "a", 10); - CheckNumber(locals_m1, "b", 20); - CheckObject(locals_m1, "complex", "Simple.Complex"); - CheckNumber(locals_m1, "c", 30); - CheckNumber(locals_m1, "d", 50); - CheckNumber(locals_m1, "e", 60); - CheckNumber(locals_m1, "f", 0); - - props = await GetObjectOnFrame(pause_location["callFrames"][1], "complex"); - Assert.Equal(3, props.Count()); - CheckNumber(props, "A", 10); - CheckString(props, "B", "xx"); - CheckString(props, "c", "20_xx"); - }); + // Will stop in Complex.DoEvenMoreStuff + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_use_complex (); }, 1);", + dep_cs_loc, 33, 8, "DoEvenMoreStuff", + locals_fn: (locals) => + { + Assert.Single(locals); + CheckObject(locals, "this", "Simple.Complex"); + } + ); + + var props = await GetObjectOnFrame(pause_location["callFrames"][0], "this"); + Assert.Equal(3, props.Count()); + CheckNumber(props, "A", 10); + CheckString(props, "B", "xx"); + CheckString(props, "c", "20_xx"); + + // Check UseComplex frame + var locals_m1 = await GetLocalsForFrame(pause_location["callFrames"][3], debugger_test_loc, 23, 8, "UseComplex"); + Assert.Equal(7, locals_m1.Count()); + + CheckNumber(locals_m1, "a", 10); + CheckNumber(locals_m1, "b", 20); + CheckObject(locals_m1, "complex", "Simple.Complex"); + CheckNumber(locals_m1, "c", 30); + CheckNumber(locals_m1, "d", 50); + CheckNumber(locals_m1, "e", 60); + CheckNumber(locals_m1, "f", 0); + + props = await GetObjectOnFrame(pause_location["callFrames"][3], "complex"); + Assert.Equal(3, props.Count()); + CheckNumber(props, "A", 10); + CheckString(props, "B", "xx"); + CheckString(props, "c", "20_xx"); + + pause_location = await StepAndCheck(StepKind.Over, dep_cs_loc, 23, 8, "DoStuff", times: 2); + // Check UseComplex frame again + locals_m1 = await GetLocalsForFrame(pause_location["callFrames"][1], debugger_test_loc, 23, 8, "UseComplex"); + Assert.Equal(7, locals_m1.Count()); + + CheckNumber(locals_m1, "a", 10); + CheckNumber(locals_m1, "b", 20); + CheckObject(locals_m1, "complex", "Simple.Complex"); + CheckNumber(locals_m1, "c", 30); + CheckNumber(locals_m1, "d", 50); + CheckNumber(locals_m1, "e", 60); + CheckNumber(locals_m1, "f", 0); + + props = await GetObjectOnFrame(pause_location["callFrames"][1], "complex"); + Assert.Equal(3, props.Count()); + CheckNumber(props, "A", 10); + CheckString(props, "B", "xx"); + CheckString(props, "c", "20_xx"); } [Theory] @@ -182,178 +153,159 @@ await insp.Ready(async (cli, token) => [InlineData(true)] public async Task InspectLocalsInPreviousFramesDuringSteppingIn(bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs"; + await SetBreakpoint(debugger_test_loc, 111, 12); + + // Will stop in InnerMethod + var wait_res = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_outer_method(); }, 1);", + debugger_test_loc, 111, 12, "InnerMethod", + locals_fn: (locals) => + { + Assert.Equal(4, locals.Count()); + CheckNumber(locals, "i", 5); + CheckNumber(locals, "j", 24); + CheckString(locals, "foo_str", "foo"); + CheckObject(locals, "this", "Math.NestedInMath"); + } + ); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs"; - await SetBreakpoint(debugger_test_loc, 111, 12); + var this_props = await GetObjectOnFrame(wait_res["callFrames"][0], "this"); + Assert.Equal(2, this_props.Count()); + CheckObject(this_props, "m", "Math"); + CheckValueType(this_props, "SimpleStructProperty", "Math.SimpleStruct"); - // Will stop in InnerMethod - var wait_res = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_outer_method(); }, 1);", - debugger_test_loc, 111, 12, "InnerMethod", - locals_fn: (locals) => - { - Assert.Equal(4, locals.Count()); - CheckNumber(locals, "i", 5); - CheckNumber(locals, "j", 24); - CheckString(locals, "foo_str", "foo"); - CheckObject(locals, "this", "Math.NestedInMath"); - } - ); - - var this_props = await GetObjectOnFrame(wait_res["callFrames"][0], "this"); - Assert.Equal(2, this_props.Count()); - CheckObject(this_props, "m", "Math"); - CheckValueType(this_props, "SimpleStructProperty", "Math.SimpleStruct"); - - var ss_props = await GetObjectOnLocals(this_props, "SimpleStructProperty"); - var dt = new DateTime(2020, 1, 2, 3, 4, 5); - await CheckProps(ss_props, new + var ss_props = await GetObjectOnLocals(this_props, "SimpleStructProperty"); + var dt = new DateTime(2020, 1, 2, 3, 4, 5); + await CheckProps(ss_props, new + { + dt = TDateTime(dt), + gs = TValueType("Math.GenericStruct") + }, "ss_props"); + + // Check OuterMethod frame + var locals_m1 = await GetLocalsForFrame(wait_res["callFrames"][1], debugger_test_loc, 87, 8, "OuterMethod"); + Assert.Equal(5, locals_m1.Count()); + // FIXME: Failing test CheckNumber (locals_m1, "i", 5); + // FIXME: Failing test CheckString (locals_m1, "text", "Hello"); + CheckNumber(locals_m1, "new_i", 0); + CheckNumber(locals_m1, "k", 0); + CheckObject(locals_m1, "nim", "Math.NestedInMath"); + + // step back into OuterMethod + await StepAndCheck(StepKind.Over, debugger_test_loc, 91, 8, "OuterMethod", times: 9, + locals_fn: (locals) => { - dt = TDateTime(dt), - gs = TValueType("Math.GenericStruct") - }, "ss_props"); - - // Check OuterMethod frame - var locals_m1 = await GetLocalsForFrame(wait_res["callFrames"][1], debugger_test_loc, 87, 8, "OuterMethod"); - Assert.Equal(5, locals_m1.Count()); - // FIXME: Failing test CheckNumber (locals_m1, "i", 5); - // FIXME: Failing test CheckString (locals_m1, "text", "Hello"); - CheckNumber(locals_m1, "new_i", 0); - CheckNumber(locals_m1, "k", 0); - CheckObject(locals_m1, "nim", "Math.NestedInMath"); - - // step back into OuterMethod - await StepAndCheck(StepKind.Over, debugger_test_loc, 91, 8, "OuterMethod", times: 9, - locals_fn: (locals) => - { - Assert.Equal(5, locals.Count()); + Assert.Equal(5, locals.Count()); - // FIXME: Failing test CheckNumber (locals_m1, "i", 5); - CheckString(locals, "text", "Hello"); - // FIXME: Failing test CheckNumber (locals, "new_i", 24); - CheckNumber(locals, "k", 19); - CheckObject(locals, "nim", "Math.NestedInMath"); - } - ); + // FIXME: Failing test CheckNumber (locals_m1, "i", 5); + CheckString(locals, "text", "Hello"); + // FIXME: Failing test CheckNumber (locals, "new_i", 24); + CheckNumber(locals, "k", 19); + CheckObject(locals, "nim", "Math.NestedInMath"); + } + ); - //await StepAndCheck (StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 81, 2, "OuterMethod", times: 2); + //await StepAndCheck (StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 81, 2, "OuterMethod", times: 2); - // step into InnerMethod2 - await StepAndCheck(StepKind.Into, "dotnet://debugger-test.dll/debugger-test.cs", 96, 4, "InnerMethod2", - locals_fn: (locals) => - { - Assert.Equal(3, locals.Count()); + // step into InnerMethod2 + await StepAndCheck(StepKind.Into, "dotnet://debugger-test.dll/debugger-test.cs", 96, 4, "InnerMethod2", + locals_fn: (locals) => + { + Assert.Equal(3, locals.Count()); - CheckString(locals, "s", "test string"); - //out var: CheckNumber (locals, "k", 0); - CheckNumber(locals, "i", 24); - } - ); + CheckString(locals, "s", "test string"); + //out var: CheckNumber (locals, "k", 0); + CheckNumber(locals, "i", 24); + } + ); - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 100, 4, "InnerMethod2", times: 4, - locals_fn: (locals) => - { - Assert.Equal(3, locals.Count()); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 100, 4, "InnerMethod2", times: 4, + locals_fn: (locals) => + { + Assert.Equal(3, locals.Count()); - CheckString(locals, "s", "test string"); - // FIXME: Failing test CheckNumber (locals, "k", 34); - CheckNumber(locals, "i", 24); - } - ); + CheckString(locals, "s", "test string"); + // FIXME: Failing test CheckNumber (locals, "k", 34); + CheckNumber(locals, "i", 24); + } + ); - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 92, 8, "OuterMethod", times: 2, - locals_fn: (locals) => - { - Assert.Equal(5, locals.Count()); - - CheckString(locals, "text", "Hello"); - // FIXME: failing test CheckNumber (locals, "i", 5); - CheckNumber(locals, "new_i", 22); - CheckNumber(locals, "k", 34); - CheckObject(locals, "nim", "Math.NestedInMath"); - } - ); - }); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 92, 8, "OuterMethod", times: 2, + locals_fn: (locals) => + { + Assert.Equal(5, locals.Count()); + + CheckString(locals, "text", "Hello"); + // FIXME: failing test CheckNumber (locals, "i", 5); + CheckNumber(locals, "new_i", 22); + CheckNumber(locals, "k", 34); + CheckObject(locals, "nim", "Math.NestedInMath"); + } + ); } [Fact] public async Task InspectLocalsDuringSteppingIn() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 86, 8); - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); + await EvaluateAndCheck("window.setTimeout(function() { invoke_outer_method(); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 86, 8, "OuterMethod", + locals_fn: (locals) => + { + Assert.Equal(5, locals.Count()); - await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 86, 8); + CheckObject(locals, "nim", "Math.NestedInMath"); + CheckNumber(locals, "i", 5); + CheckNumber(locals, "k", 0); + CheckNumber(locals, "new_i", 0); + CheckString(locals, "text", null); + } + ); - await EvaluateAndCheck("window.setTimeout(function() { invoke_outer_method(); }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 86, 8, "OuterMethod", - locals_fn: (locals) => - { - Assert.Equal(5, locals.Count()); - - CheckObject(locals, "nim", "Math.NestedInMath"); - CheckNumber(locals, "i", 5); - CheckNumber(locals, "k", 0); - CheckNumber(locals, "new_i", 0); - CheckString(locals, "text", null); - } - ); - - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 87, 8, "OuterMethod", - locals_fn: (locals) => - { - Assert.Equal(5, locals.Count()); - - CheckObject(locals, "nim", "Math.NestedInMath"); - // FIXME: Failing test CheckNumber (locals, "i", 5); - CheckNumber(locals, "k", 0); - CheckNumber(locals, "new_i", 0); - CheckString(locals, "text", "Hello"); - } - ); - - // Step into InnerMethod - await StepAndCheck(StepKind.Into, "dotnet://debugger-test.dll/debugger-test.cs", 105, 8, "InnerMethod"); - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 109, 12, "InnerMethod", times: 5, - locals_fn: (locals) => - { - Assert.Equal(4, locals.Count()); - - CheckNumber(locals, "i", 5); - CheckNumber(locals, "j", 15); - CheckString(locals, "foo_str", "foo"); - CheckObject(locals, "this", "Math.NestedInMath"); - } - ); - - // Step back to OuterMethod - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 88, 8, "OuterMethod", times: 6, - locals_fn: (locals) => - { - Assert.Equal(5, locals.Count()); - - CheckObject(locals, "nim", "Math.NestedInMath"); - // FIXME: Failing test CheckNumber (locals, "i", 5); - CheckNumber(locals, "k", 0); - CheckNumber(locals, "new_i", 24); - CheckString(locals, "text", "Hello"); - } - ); - }); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 87, 8, "OuterMethod", + locals_fn: (locals) => + { + Assert.Equal(5, locals.Count()); + + CheckObject(locals, "nim", "Math.NestedInMath"); + // FIXME: Failing test CheckNumber (locals, "i", 5); + CheckNumber(locals, "k", 0); + CheckNumber(locals, "new_i", 0); + CheckString(locals, "text", "Hello"); + } + ); + + // Step into InnerMethod + await StepAndCheck(StepKind.Into, "dotnet://debugger-test.dll/debugger-test.cs", 105, 8, "InnerMethod"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 109, 12, "InnerMethod", times: 5, + locals_fn: (locals) => + { + Assert.Equal(4, locals.Count()); + + CheckNumber(locals, "i", 5); + CheckNumber(locals, "j", 15); + CheckString(locals, "foo_str", "foo"); + CheckObject(locals, "this", "Math.NestedInMath"); + } + ); + + // Step back to OuterMethod + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", 88, 8, "OuterMethod", times: 6, + locals_fn: (locals) => + { + Assert.Equal(5, locals.Count()); + + CheckObject(locals, "nim", "Math.NestedInMath"); + // FIXME: Failing test CheckNumber (locals, "i", 5); + CheckNumber(locals, "k", 0); + CheckNumber(locals, "new_i", 24); + CheckString(locals, "text", "Hello"); + } + ); } [Theory] @@ -361,64 +313,55 @@ await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", [InlineData(true)] public async Task InspectLocalsInAsyncMethods(bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs"; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs"; - - await SetBreakpoint(debugger_test_loc, 120, 12); - await SetBreakpoint(debugger_test_loc, 135, 12); - - // Will stop in Asyncmethod0 - var wait_res = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_async_method_with_await(); }, 1);", - debugger_test_loc, 120, 12, "MoveNext", //FIXME: - locals_fn: (locals) => - { - Assert.Equal(4, locals.Count()); - CheckString(locals, "s", "string from js"); - CheckNumber(locals, "i", 42); - CheckString(locals, "local0", "value0"); - CheckObject(locals, "this", "Math.NestedInMath"); - } - ); - Console.WriteLine(wait_res); + await SetBreakpoint(debugger_test_loc, 120, 12); + await SetBreakpoint(debugger_test_loc, 135, 12); + + // Will stop in Asyncmethod0 + var wait_res = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_async_method_with_await(); }, 1);", + debugger_test_loc, 120, 12, "MoveNext", //FIXME: + locals_fn: (locals) => + { + Assert.Equal(4, locals.Count()); + CheckString(locals, "s", "string from js"); + CheckNumber(locals, "i", 42); + CheckString(locals, "local0", "value0"); + CheckObject(locals, "this", "Math.NestedInMath"); + } + ); + Console.WriteLine(wait_res); #if false // Disabled for now, as we don't have proper async traces - var locals = await GetProperties(wait_res["callFrames"][2]["callFrameId"].Value()); - Assert.Equal(4, locals.Count()); - CheckString(locals, "ls", "string from jstest"); - CheckNumber(locals, "li", 52); + var locals = await GetProperties(wait_res["callFrames"][2]["callFrameId"].Value()); + Assert.Equal(4, locals.Count()); + CheckString(locals, "ls", "string from jstest"); + CheckNumber(locals, "li", 52); #endif - // TODO: previous frames have async machinery details, so no point checking that right now + // TODO: previous frames have async machinery details, so no point checking that right now - var pause_loc = await SendCommandAndCheck(null, "Debugger.resume", debugger_test_loc, 135, 12, /*FIXME: "AsyncMethodNoReturn"*/ "MoveNext", - locals_fn: (locals) => - { - Assert.Equal(4, locals.Count()); - CheckString(locals, "str", "AsyncMethodNoReturn's local"); - CheckObject(locals, "this", "Math.NestedInMath"); - //FIXME: check fields - CheckValueType(locals, "ss", "Math.SimpleStruct"); - CheckArray(locals, "ss_arr", "Math.SimpleStruct[]", 0); - // TODO: struct fields - } - ); - - var this_props = await GetObjectOnFrame(pause_loc["callFrames"][0], "this"); - Assert.Equal(2, this_props.Count()); - CheckObject(this_props, "m", "Math"); - CheckValueType(this_props, "SimpleStructProperty", "Math.SimpleStruct"); - - // TODO: Check `this` properties - }); + var pause_loc = await SendCommandAndCheck(null, "Debugger.resume", debugger_test_loc, 135, 12, /*FIXME: "AsyncMethodNoReturn"*/ "MoveNext", + locals_fn: (locals) => + { + Assert.Equal(4, locals.Count()); + CheckString(locals, "str", "AsyncMethodNoReturn's local"); + CheckObject(locals, "this", "Math.NestedInMath"); + //FIXME: check fields + CheckValueType(locals, "ss", "Math.SimpleStruct"); + CheckArray(locals, "ss_arr", "Math.SimpleStruct[]", 0); + // TODO: struct fields + } + ); + + var this_props = await GetObjectOnFrame(pause_loc["callFrames"][0], "this"); + Assert.Equal(2, this_props.Count()); + CheckObject(this_props, "m", "Math"); + CheckValueType(this_props, "SimpleStructProperty", "Math.SimpleStruct"); + + // TODO: Check `this` properties } [Theory] @@ -426,159 +369,141 @@ await insp.Ready(async (cli, token) => [InlineData(true)] public async Task InspectValueTypeMethodArgsWhileStepping(bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; + await SetBreakpoint(debugger_test_loc, 36, 12); - await SetBreakpoint(debugger_test_loc, 36, 12); - - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.ValueTypesTest:TestStructsAsMethodArgs'); }, 1);", - debugger_test_loc, 36, 12, "MethodWithStructArgs"); - var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); - { - Assert.Equal(3, locals.Count()); - CheckString(locals, "label", "TestStructsAsMethodArgs#label"); - CheckValueType(locals, "ss_arg", "DebuggerTests.ValueTypesTest.SimpleStruct"); - CheckNumber(locals, "x", 3); - } + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.ValueTypesTest:TestStructsAsMethodArgs'); }, 1);", + debugger_test_loc, 36, 12, "MethodWithStructArgs"); + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + { + Assert.Equal(3, locals.Count()); + CheckString(locals, "label", "TestStructsAsMethodArgs#label"); + CheckValueType(locals, "ss_arg", "DebuggerTests.ValueTypesTest.SimpleStruct"); + CheckNumber(locals, "x", 3); + } - var dt = new DateTime(2025, 6, 7, 8, 10, 11); - var ss_local_as_ss_arg = new - { - V = TGetter("V"), - str_member = TString("ss_local#SimpleStruct#string#0#SimpleStruct#str_member"), - dt = TDateTime(dt), - gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), - Kind = TEnum("System.DateTimeKind", "Local") - }; - var ss_local_gs = new - { - StringField = TString("ss_local#SimpleStruct#string#0#SimpleStruct#gs#StringField"), - List = TObject("System.Collections.Generic.List"), - Options = TEnum("DebuggerTests.Options", "Option1") - }; + var dt = new DateTime(2025, 6, 7, 8, 10, 11); + var ss_local_as_ss_arg = new + { + V = TGetter("V"), + str_member = TString("ss_local#SimpleStruct#string#0#SimpleStruct#str_member"), + dt = TDateTime(dt), + gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), + Kind = TEnum("System.DateTimeKind", "Local") + }; + var ss_local_gs = new + { + StringField = TString("ss_local#SimpleStruct#string#0#SimpleStruct#gs#StringField"), + List = TObject("System.Collections.Generic.List"), + Options = TEnum("DebuggerTests.Options", "Option1") + }; - // Check ss_arg's properties - var ss_arg_props = await GetObjectOnFrame(pause_location["callFrames"][0], "ss_arg"); - await CheckProps(ss_arg_props, ss_local_as_ss_arg, "ss_arg"); + // Check ss_arg's properties + var ss_arg_props = await GetObjectOnFrame(pause_location["callFrames"][0], "ss_arg"); + await CheckProps(ss_arg_props, ss_local_as_ss_arg, "ss_arg"); - var res = await InvokeGetter(GetAndAssertObjectWithName(locals, "ss_arg"), "V"); - await CheckValue(res.Value["result"], TNumber(0xDEADBEEF + (uint)dt.Month), "ss_arg#V"); + var res = await InvokeGetter(GetAndAssertObjectWithName(locals, "ss_arg"), "V"); + await CheckValue(res.Value["result"], TNumber(0xDEADBEEF + (uint)dt.Month), "ss_arg#V"); - { - // Check ss_local.gs - await CompareObjectPropertiesFor(ss_arg_props, "gs", ss_local_gs); - } + { + // Check ss_local.gs + await CompareObjectPropertiesFor(ss_arg_props, "gs", ss_local_gs); + } - pause_location = await StepAndCheck(StepKind.Over, debugger_test_loc, 40, 8, "MethodWithStructArgs", times: 4, - locals_fn: (l) => { /* non-null to make sure that locals get fetched */ }); - locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); - { - Assert.Equal(3, locals.Count()); + pause_location = await StepAndCheck(StepKind.Over, debugger_test_loc, 40, 8, "MethodWithStructArgs", times: 4, + locals_fn: (l) => { /* non-null to make sure that locals get fetched */ }); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + { + Assert.Equal(3, locals.Count()); - CheckString(locals, "label", "TestStructsAsMethodArgs#label"); - CheckValueType(locals, "ss_arg", "DebuggerTests.ValueTypesTest.SimpleStruct"); - CheckNumber(locals, "x", 3); - } + CheckString(locals, "label", "TestStructsAsMethodArgs#label"); + CheckValueType(locals, "ss_arg", "DebuggerTests.ValueTypesTest.SimpleStruct"); + CheckNumber(locals, "x", 3); + } - var ss_arg_updated = new - { - V = TGetter("V"), - str_member = TString("ValueTypesTest#MethodWithStructArgs#updated#ss_arg#str_member"), - dt = TDateTime(dt), - gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), - Kind = TEnum("System.DateTimeKind", "Utc") - }; + var ss_arg_updated = new + { + V = TGetter("V"), + str_member = TString("ValueTypesTest#MethodWithStructArgs#updated#ss_arg#str_member"), + dt = TDateTime(dt), + gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), + Kind = TEnum("System.DateTimeKind", "Utc") + }; - ss_arg_props = await GetObjectOnFrame(pause_location["callFrames"][0], "ss_arg"); - await CheckProps(ss_arg_props, ss_arg_updated, "ss_arg"); + ss_arg_props = await GetObjectOnFrame(pause_location["callFrames"][0], "ss_arg"); + await CheckProps(ss_arg_props, ss_arg_updated, "ss_arg"); - res = await InvokeGetter(GetAndAssertObjectWithName(locals, "ss_arg"), "V"); - await CheckValue(res.Value["result"], TNumber(0xDEADBEEF + (uint)dt.Month), "ss_arg#V"); + res = await InvokeGetter(GetAndAssertObjectWithName(locals, "ss_arg"), "V"); + await CheckValue(res.Value["result"], TNumber(0xDEADBEEF + (uint)dt.Month), "ss_arg#V"); + { + // Check ss_local.gs + await CompareObjectPropertiesFor(ss_arg_props, "gs", new { - // Check ss_local.gs - await CompareObjectPropertiesFor(ss_arg_props, "gs", new - { - StringField = TString("ValueTypesTest#MethodWithStructArgs#updated#gs#StringField#3"), - List = TObject("System.Collections.Generic.List"), - Options = TEnum("DebuggerTests.Options", "Option1") - }); - } + StringField = TString("ValueTypesTest#MethodWithStructArgs#updated#gs#StringField#3"), + List = TObject("System.Collections.Generic.List"), + Options = TEnum("DebuggerTests.Options", "Option1") + }); + } - // Check locals on previous frame, same as earlier in this test - ss_arg_props = await GetObjectOnFrame(pause_location["callFrames"][1], "ss_local"); - await CheckProps(ss_arg_props, ss_local_as_ss_arg, "ss_local"); + // Check locals on previous frame, same as earlier in this test + ss_arg_props = await GetObjectOnFrame(pause_location["callFrames"][1], "ss_local"); + await CheckProps(ss_arg_props, ss_local_as_ss_arg, "ss_local"); - { - // Check ss_local.dt - await CheckDateTime(ss_arg_props, "dt", dt); + { + // Check ss_local.dt + await CheckDateTime(ss_arg_props, "dt", dt); - // Check ss_local.gs - var gs_props = await GetObjectOnLocals(ss_arg_props, "gs"); - CheckString(gs_props, "StringField", "ss_local#SimpleStruct#string#0#SimpleStruct#gs#StringField"); - CheckObject(gs_props, "List", "System.Collections.Generic.List"); - } + // Check ss_local.gs + var gs_props = await GetObjectOnLocals(ss_arg_props, "gs"); + CheckString(gs_props, "StringField", "ss_local#SimpleStruct#string#0#SimpleStruct#gs#StringField"); + CheckObject(gs_props, "List", "System.Collections.Generic.List"); + } - // ----------- Step back to the caller --------- + // ----------- Step back to the caller --------- - pause_location = await StepAndCheck(StepKind.Over, debugger_test_loc, 30, 12, "TestStructsAsMethodArgs", - times: 2, locals_fn: (l) => { /* non-null to make sure that locals get fetched */ }); - locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); - await CheckProps(locals, new - { - ss_local = TValueType("DebuggerTests.ValueTypesTest.SimpleStruct"), - ss_ret = TValueType("DebuggerTests.ValueTypesTest.SimpleStruct") - }, - "locals#0"); + pause_location = await StepAndCheck(StepKind.Over, debugger_test_loc, 30, 12, "TestStructsAsMethodArgs", + times: 2, locals_fn: (l) => { /* non-null to make sure that locals get fetched */ }); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + await CheckProps(locals, new + { + ss_local = TValueType("DebuggerTests.ValueTypesTest.SimpleStruct"), + ss_ret = TValueType("DebuggerTests.ValueTypesTest.SimpleStruct") + }, + "locals#0"); - ss_arg_props = await GetObjectOnFrame(pause_location["callFrames"][0], "ss_local"); - await CheckProps(ss_arg_props, ss_local_as_ss_arg, "ss_local"); + ss_arg_props = await GetObjectOnFrame(pause_location["callFrames"][0], "ss_local"); + await CheckProps(ss_arg_props, ss_local_as_ss_arg, "ss_local"); - { - // Check ss_local.gs - await CompareObjectPropertiesFor(ss_arg_props, "gs", ss_local_gs, label: "ss_local_gs"); - } + { + // Check ss_local.gs + await CompareObjectPropertiesFor(ss_arg_props, "gs", ss_local_gs, label: "ss_local_gs"); + } - // FIXME: check ss_local.gs.List's members - }); + // FIXME: check ss_local.gs.List's members } [Fact] public async Task CheckUpdatedValueTypeFieldsOnResume() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; - - var lines = new[] { 205, 208 }; - await SetBreakpoint(debugger_test_loc, lines[0], 12); - await SetBreakpoint(debugger_test_loc, lines[1], 12); + var lines = new[] { 205, 208 }; + await SetBreakpoint(debugger_test_loc, lines[0], 12); + await SetBreakpoint(debugger_test_loc, lines[1], 12); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.ValueTypesTest:MethodUpdatingValueTypeMembers'); }, 1);", - debugger_test_loc, lines[0], 12, "MethodUpdatingValueTypeMembers"); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.ValueTypesTest:MethodUpdatingValueTypeMembers'); }, 1);", + debugger_test_loc, lines[0], 12, "MethodUpdatingValueTypeMembers"); - await CheckLocals(pause_location, new DateTime(1, 2, 3, 4, 5, 6), new DateTime(4, 5, 6, 7, 8, 9)); + await CheckLocals(pause_location, new DateTime(1, 2, 3, 4, 5, 6), new DateTime(4, 5, 6, 7, 8, 9)); - // Resume - pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", debugger_test_loc, lines[1], 12, "MethodUpdatingValueTypeMembers"); - await CheckLocals(pause_location, new DateTime(9, 8, 7, 6, 5, 4), new DateTime(5, 1, 3, 7, 9, 10)); - }); + // Resume + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", debugger_test_loc, lines[1], 12, "MethodUpdatingValueTypeMembers"); + await CheckLocals(pause_location, new DateTime(9, 8, 7, 6, 5, 4), new DateTime(5, 1, 3, 7, 9, 10)); async Task CheckLocals(JToken pause_location, DateTime obj_dt, DateTime vt_dt) { @@ -610,64 +535,46 @@ async Task CheckLocals(JToken pause_location, DateTime obj_dt, DateTime vt_dt) [Fact] public async Task CheckUpdatedValueTypeLocalsOnResumeAsync() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; + var lines = new[] { 214, 216 }; + await SetBreakpoint(debugger_test_loc, lines[0], 12); + await SetBreakpoint(debugger_test_loc, lines[1], 12); - var lines = new[] { 214, 216 }; - await SetBreakpoint(debugger_test_loc, lines[0], 12); - await SetBreakpoint(debugger_test_loc, lines[1], 12); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.ValueTypesTest:MethodUpdatingValueTypeLocalsAsync'); }, 1);", + debugger_test_loc, lines[0], 12, "MoveNext"); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.ValueTypesTest:MethodUpdatingValueTypeLocalsAsync'); }, 1);", - debugger_test_loc, lines[0], 12, "MoveNext"); + var dt = new DateTime(1, 2, 3, 4, 5, 6); + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + await CheckDateTime(locals, "dt", dt); - var dt = new DateTime(1, 2, 3, 4, 5, 6); - var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); - await CheckDateTime(locals, "dt", dt); - - // Resume - dt = new DateTime(9, 8, 7, 6, 5, 4); - pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", debugger_test_loc, lines[1], 12, "MoveNext"); - locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); - await CheckDateTime(locals, "dt", dt); - }); + // Resume + dt = new DateTime(9, 8, 7, 6, 5, 4); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", debugger_test_loc, lines[1], 12, "MoveNext"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + await CheckDateTime(locals, "dt", dt); } [Fact] public async Task CheckUpdatedVTArrayMembersOnResume() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; - var lines = new[] { 225, 227 }; - await SetBreakpoint(debugger_test_loc, lines[0], 12); - await SetBreakpoint(debugger_test_loc, lines[1], 12); + var lines = new[] { 225, 227 }; + await SetBreakpoint(debugger_test_loc, lines[0], 12); + await SetBreakpoint(debugger_test_loc, lines[1], 12); - var dt = new DateTime(1, 2, 3, 4, 5, 6); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.ValueTypesTest:MethodUpdatingVTArrayMembers'); }, 1);", - debugger_test_loc, lines[0], 12, "MethodUpdatingVTArrayMembers"); - await CheckArrayElements(pause_location, dt); + var dt = new DateTime(1, 2, 3, 4, 5, 6); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.ValueTypesTest:MethodUpdatingVTArrayMembers'); }, 1);", + debugger_test_loc, lines[0], 12, "MethodUpdatingVTArrayMembers"); + await CheckArrayElements(pause_location, dt); - // Resume - dt = new DateTime(9, 8, 7, 6, 5, 4); - pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", debugger_test_loc, lines[1], 12, "MethodUpdatingVTArrayMembers"); - await CheckArrayElements(pause_location, dt); - }); + // Resume + dt = new DateTime(9, 8, 7, 6, 5, 4); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", debugger_test_loc, lines[1], 12, "MethodUpdatingVTArrayMembers"); + await CheckArrayElements(pause_location, dt); async Task CheckArrayElements(JToken pause_location, DateTime dt) { @@ -689,440 +596,268 @@ async Task CheckArrayElements(JToken pause_location, DateTime dt) [Fact] public async Task SteppingIntoMscorlib() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); + var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 83, 8); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] Math:OuterMethod'); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 83, 8, + "OuterMethod"); - var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 83, 8); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] Math:OuterMethod'); }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 83, 8, - "OuterMethod"); + //make sure we're on the right bp + Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - //make sure we're on the right bp - Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); + pause_location = await SendCommandAndCheck(null, $"Debugger.stepInto", null, -1, -1, null); + var top_frame = pause_location["callFrames"][0]; - pause_location = await SendCommandAndCheck(null, $"Debugger.stepInto", null, -1, -1, null); - var top_frame = pause_location["callFrames"][0]; - - AssertEqual("WriteLine", top_frame["functionName"]?.Value(), "Expected to be in WriteLine method"); - var script_id = top_frame["functionLocation"]["scriptId"].Value(); - Assert.Matches ("^dotnet://(mscorlib|System\\.Console)\\.dll/Console.cs", scripts[script_id]); - }); + AssertEqual("WriteLine", top_frame["functionName"]?.Value(), "Expected to be in WriteLine method"); + var script_id = top_frame["functionLocation"]["scriptId"].Value(); + Assert.Matches ("^dotnet://(mscorlib|System\\.Console)\\.dll/Console.cs", scripts[script_id]); } [Fact] public async Task CreateGoodBreakpointAndHitAndRemoveAndDontHit() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - - var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); - var bp2 = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 12, 8); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_add(); invoke_add()}, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, - "IntAdd"); - - Assert.Equal("other", pause_location["reason"]?.Value()); - Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - - await RemoveBreakpoint(bp.Value["breakpointId"]?.ToString()); - await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 12, 8, "IntAdd"); - await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 12, 8, "IntAdd"); - }); + var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); + var bp2 = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 12, 8); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_add(); invoke_add()}, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, + "IntAdd"); + + Assert.Equal("other", pause_location["reason"]?.Value()); + Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); + + await RemoveBreakpoint(bp.Value["breakpointId"]?.ToString()); + await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 12, 8, "IntAdd"); + await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 12, 8, "IntAdd"); } [Fact] public async Task CreateGoodBreakpointAndHitAndRemoveTwice() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - - var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); - var bp2 = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 12, 8); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_add(); invoke_add()}, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, - "IntAdd"); - - Assert.Equal("other", pause_location["reason"]?.Value()); - Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - - await RemoveBreakpoint(bp.Value["breakpointId"]?.ToString()); - await RemoveBreakpoint(bp.Value["breakpointId"]?.ToString()); - }); + var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); + var bp2 = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 12, 8); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_add(); invoke_add()}, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, + "IntAdd"); + + Assert.Equal("other", pause_location["reason"]?.Value()); + Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); + + await RemoveBreakpoint(bp.Value["breakpointId"]?.ToString()); + await RemoveBreakpoint(bp.Value["breakpointId"]?.ToString()); } [Fact] public async Task CreateGoodBreakpointAndHitAndRemoveAndDontHitAndCreateAgainAndHit() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - - var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); - var bp2 = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 12, 8); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_add(); invoke_add(); invoke_add(); invoke_add()}, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, - "IntAdd"); - - Assert.Equal("other", pause_location["reason"]?.Value()); - Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - - await RemoveBreakpoint(bp.Value["breakpointId"]?.ToString()); - await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 12, 8, "IntAdd"); - await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 12, 8, "IntAdd"); - bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); - await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, "IntAdd"); - - }); + var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); + var bp2 = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 12, 8); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_add(); invoke_add(); invoke_add(); invoke_add()}, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, + "IntAdd"); + + Assert.Equal("other", pause_location["reason"]?.Value()); + Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); + + await RemoveBreakpoint(bp.Value["breakpointId"]?.ToString()); + await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 12, 8, "IntAdd"); + await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 12, 8, "IntAdd"); + bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); + await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, "IntAdd"); } // [Fact] //https://github.com/dotnet/runtime/issues/42421 public async Task BreakAfterAwaitThenStepOverTillBackToCaller() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + var bp = await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "TestAsyncStepOut2", 2); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:TestAsyncStepOut'); }, 1);", + "dotnet://debugger-test.dll/debugger-async-step.cs", 21, 12, + "MoveNext"); - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - - var bp = await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "TestAsyncStepOut2", 2); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:TestAsyncStepOut'); }, 1);", - "dotnet://debugger-test.dll/debugger-async-step.cs", 21, 12, - "MoveNext"); - - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-async-step.cs", 23, 12, "MoveNext"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-async-step.cs", 23, 12, "MoveNext"); - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-async-step.cs", 24, 8, "MoveNext"); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-async-step.cs", 24, 8, "MoveNext"); - await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-async-step.cs", 15, 12, "MoveNext"); - }); + await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-async-step.cs", 15, 12, "MoveNext"); } // [Fact] //[ActiveIssue("https://github.com/dotnet/runtime/issues/42421")] public async Task StepOutOfAsyncMethod() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; + string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "TestAsyncStepOut2", 2); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:TestAsyncStepOut'); }, 1);", - "dotnet://debugger-test.dll/debugger-async-step.cs", 21, 12, - "MoveNext"); + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "TestAsyncStepOut2", 2); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:TestAsyncStepOut'); }, 1);", + "dotnet://debugger-test.dll/debugger-async-step.cs", 21, 12, + "MoveNext"); - await StepAndCheck(StepKind.Out, source_file, 15, 4, "TestAsyncStepOut"); - }); + await StepAndCheck(StepKind.Out, source_file, 15, 4, "TestAsyncStepOut"); } [Fact] public async Task ResumeOutOfAsyncMethodToAsyncCallerWithBreakpoint() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; - - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "TestAsyncStepOut2", 2); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:TestAsyncStepOut'); }, 1);", - "dotnet://debugger-test.dll/debugger-async-step.cs", 21, 12, - "MoveNext"); - - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "TestAsyncStepOut", 2); - await SendCommandAndCheck(null, "Debugger.resume", source_file, 16, 8, "MoveNext"); - }); + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "TestAsyncStepOut2", 2); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:TestAsyncStepOut'); }, 1);", + "dotnet://debugger-test.dll/debugger-async-step.cs", 21, 12, + "MoveNext"); + + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "TestAsyncStepOut", 2); + await SendCommandAndCheck(null, "Debugger.resume", source_file, 16, 8, "MoveNext"); } [Fact] public async Task StepOutOfNonAsyncMethod() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; + string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "OtherMethod0", 1); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:SimpleMethod'); }, 1);", - source_file, -1, -1, - "OtherMethod0"); + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "OtherMethod0", 1); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:SimpleMethod'); }, 1);", + source_file, -1, -1, + "OtherMethod0"); - await StepAndCheck(StepKind.Out, source_file, 29, 12, "SimpleMethod"); - }); + await StepAndCheck(StepKind.Out, source_file, 29, 12, "SimpleMethod"); } [Fact] public async Task BreakOnAwaitThenStepOverToNextAwaitCall() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; + string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "MethodWithTwoAwaitsAsync", 2); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:StepOverTestAsync'); }, 1);", - "dotnet://debugger-test.dll/debugger-async-step.cs", 53, 12, - "MoveNext"); + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "MethodWithTwoAwaitsAsync", 2); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:StepOverTestAsync'); }, 1);", + "dotnet://debugger-test.dll/debugger-async-step.cs", 53, 12, + "MoveNext"); - await StepAndCheck(StepKind.Over, source_file, 54, 12, "MoveNext"); - }); + await StepAndCheck(StepKind.Over, source_file, 54, 12, "MoveNext"); } [Fact] public async Task BreakOnAwaitThenStepOverToNextLine() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; - - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "StepOverTestAsync", 1); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:StepOverTestAsync'); }, 1);", - "dotnet://debugger-test.dll/debugger-async-step.cs", 46, 12, - "MoveNext"); - - // BUG: chrome: not able to show any bp line indicator - await StepAndCheck(StepKind.Over, source_file, 47, 12, "MoveNext"); - }); + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "StepOverTestAsync", 1); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:StepOverTestAsync'); }, 1);", + "dotnet://debugger-test.dll/debugger-async-step.cs", 46, 12, + "MoveNext"); + + // BUG: chrome: not able to show any bp line indicator + await StepAndCheck(StepKind.Over, source_file, 47, 12, "MoveNext"); } [Fact] public async Task BreakOnAwaitThenResumeToNextBreakpoint() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); + string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "StepOverTestAsync", 1); + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "StepOverTestAsync", 3); - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "StepOverTestAsync", 1); - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "StepOverTestAsync", 3); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:StepOverTestAsync'); }, 1);", + "dotnet://debugger-test.dll/debugger-async-step.cs", 46, 12, + "MoveNext"); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:StepOverTestAsync'); }, 1);", - "dotnet://debugger-test.dll/debugger-async-step.cs", 46, 12, - "MoveNext"); - - await StepAndCheck(StepKind.Resume, source_file, 48, 8, "MoveNext"); - }); + await StepAndCheck(StepKind.Resume, source_file, 48, 8, "MoveNext"); } [Fact] public async Task BreakOnAwaitThenResumeToNextBreakpointAfterSecondAwaitInSameMethod() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; + string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs"; - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "MethodWithTwoAwaitsAsync", 1); - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "MethodWithTwoAwaitsAsync", 5); + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "MethodWithTwoAwaitsAsync", 1); + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.AsyncStepClass", "MethodWithTwoAwaitsAsync", 5); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:StepOverTestAsync'); }, 1);", - "dotnet://debugger-test.dll/debugger-async-step.cs", 52, 12, - "MoveNext"); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method_async('[debugger-test] DebuggerTests.AsyncStepClass:StepOverTestAsync'); }, 1);", + "dotnet://debugger-test.dll/debugger-async-step.cs", 52, 12, + "MoveNext"); - await StepAndCheck(StepKind.Resume, source_file, 56, 12, "MoveNext"); - }); + await StepAndCheck(StepKind.Resume, source_file, 56, 12, "MoveNext"); } [Fact] public async Task BreakOnMethodCalledFromHiddenLine() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - - await SetBreakpointInMethod("debugger-test.dll", "HiddenSequencePointTest", "StepOverHiddenSP2", 0); + await SetBreakpointInMethod("debugger-test.dll", "HiddenSequencePointTest", "StepOverHiddenSP2", 0); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 546, 4, - "StepOverHiddenSP2"); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 546, 4, + "StepOverHiddenSP2"); - // Check previous frame - var top_frame = pause_location["callFrames"][1]; - Assert.Equal("StepOverHiddenSP", top_frame["functionName"].Value()); - Assert.Contains("debugger-test.cs", top_frame["url"].Value()); + // Check previous frame + var top_frame = pause_location["callFrames"][1]; + Assert.Equal("StepOverHiddenSP", top_frame["functionName"].Value()); + Assert.Contains("debugger-test.cs", top_frame["url"].Value()); - CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 537, 8, scripts, top_frame["location"]); - }); + CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 537, 8, scripts, top_frame["location"]); } [Fact] public async Task StepOverHiddenLinesShouldResumeAtNextAvailableLineInTheMethod() { - var insp = new Inspector(); + string source_loc = "dotnet://debugger-test.dll/debugger-test.cs"; + await SetBreakpoint(source_loc, 537, 8); - //Collect events - var scripts = SubscribeToScripts(insp); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 537, 8, + "StepOverHiddenSP"); - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - - string source_loc = "dotnet://debugger-test.dll/debugger-test.cs"; - await SetBreakpoint(source_loc, 537, 8); - - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 537, 8, - "StepOverHiddenSP"); - - await StepAndCheck(StepKind.Over, source_loc, 542, 8, "StepOverHiddenSP"); - }); + await StepAndCheck(StepKind.Over, source_loc, 542, 8, "StepOverHiddenSP"); } [Fact] async Task StepOverHiddenLinesInMethodWithNoNextAvailableLineShouldResumeAtCallSite() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - - string source_loc = "dotnet://debugger-test.dll/debugger-test.cs"; - await SetBreakpoint(source_loc, 552, 8); + string source_loc = "dotnet://debugger-test.dll/debugger-test.cs"; + await SetBreakpoint(source_loc, 552, 8); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 552, 8, - "MethodWithHiddenLinesAtTheEnd"); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 552, 8, + "MethodWithHiddenLinesAtTheEnd"); - await StepAndCheck(StepKind.Over, source_loc, 544, 4, "StepOverHiddenSP"); - }); + await StepAndCheck(StepKind.Over, source_loc, 544, 4, "StepOverHiddenSP"); } // [Fact] // Issue: https://github.com/dotnet/runtime/issues/42704 async Task BreakpointOnHiddenLineShouldStopAtEarliestNextAvailableLine() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - - await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 539, 8); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 546, 4, - "StepOverHiddenSP2"); - }); - } + await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 539, 8); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 546, 4, + "StepOverHiddenSP2"); + } [Fact] public async Task BreakpointOnHiddenLineOfMethodWithNoNextVisibleLineShouldNotPause() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - - await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 554, 12); + await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 554, 12); - string expression = "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);"; - await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); + string expression = "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);"; + await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); - Task pause_task = insp.WaitFor(Inspector.PAUSE); - Task t = await Task.WhenAny(pause_task, Task.Delay(2000)); - Assert.True(t != pause_task, "Debugger unexpectedly paused"); - }); + Task pause_task = insp.WaitFor(Inspector.PAUSE); + Task t = await Task.WhenAny(pause_task, Task.Delay(2000)); + Assert.True(t != pause_task, "Debugger unexpectedly paused"); } } } From eaccc4810f85a614036eacae4b3652e5eddc3cca Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 19 Nov 2020 23:37:18 -0500 Subject: [PATCH 08/21] [wasm][debugger][tests] Fix race in ExceptionThrownInJSOutOfBand test Fix the race where the `Runtime.exceptionThrown` message can be received before the test calls `WaitFor`, causing it to get missed. --- src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index 2ac3cb620e4689..84a313f5dd7f74 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -287,15 +287,15 @@ await insp.Ready(async (cli, token) => expression = "window.setTimeout(function() { invoke_bad_js_test(); }, 1);", }); + var task = insp.WaitFor("Runtime.exceptionThrown"); var eval_res = await cli.SendCommand("Runtime.evaluate", eval_req, token); // Response here will be the id for the timer from JS! Assert.True(eval_res.IsOk); - var ex = await Assert.ThrowsAsync(async () => await insp.WaitFor("Runtime.exceptionThrown")); + var ex = await Assert.ThrowsAsync(async () => await task); var ex_json = JObject.Parse(ex.Message); Assert.Equal(dicFileToUrl["/debugger-driver.html"], ex_json["exceptionDetails"]?["url"]?.Value()); }); - } [Theory] From fe09e6832f17b8ab0e60adf0b03641b494b2b50a Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 20 Nov 2020 00:36:15 -0500 Subject: [PATCH 09/21] [wasm][debugger] Formatting fixes on the changes --- .../DebuggerTestSuite/AssertHelpers.cs | 2 +- .../DebuggerTestSuite/BadHarnessInitTests.cs | 2 +- .../debugger/DebuggerTestSuite/Inspector.cs | 21 ++++++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs b/src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs index c4b4409bc76dfa..b3fb0c3c78545b 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs @@ -10,7 +10,7 @@ namespace DebuggerTests static class AssertHelpers { public static async Task ThrowsAsync(Func testCode, Action testException) - where T: Exception + where T : Exception { try { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs index 4bdecc149cfa81..9e09ade0dc5560 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs @@ -39,7 +39,7 @@ public async Task InvalidInitCommands() await AssertHelpers.ThrowsAsync( async () => await insp.OpenSessionAsync(fn), - ae => ae.Message.Contains(bad_cmd_name+"Queue")); + ae => ae.Message.Contains(bad_cmd_name + "Queue")); } } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs index 33e349560ab475..fe526ab06a3cd6 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs @@ -87,7 +87,7 @@ public void On(string evtName, Func cb) eventListeners[evtName] = cb; } - void FailAllWaiters(Exception? exception=null) + void FailAllWaiters(Exception? exception = null) { // Because we can create already completed tasks, // when we get a NotifyOf before the corresponding @@ -150,11 +150,13 @@ public async Task Ready(Func? cb = nul await OpenSessionAsync(fn, span); if (cb != null) await cb(Client, _cancellationTokenSource.Token).ConfigureAwait(false); - } catch (Exception ex) { + } + catch (Exception ex) + { if (_logger != null) _logger.LogError(ex.ToString()); else - Console.WriteLine (ex); + Console.WriteLine(ex); throw; } finally @@ -166,7 +168,8 @@ public async Task Ready(Func? cb = nul public async Task OpenSessionAsync(Func)>> getInitCmds, TimeSpan? span = null) { var start = DateTime.Now; - try { + try + { _cancellationTokenSource.CancelAfter(span?.Milliseconds ?? DefaultTestTimeoutMs); var uri = new Uri($"ws://{TestHarnessProxy.Endpoint.Authority}/launch-chrome-and-connect"); @@ -226,7 +229,9 @@ public async Task OpenSessionAsync(Func)> cmds) { var sb = new StringBuilder(); - for (int i = 0; i < cmds.Count; i ++) + for (int i = 0; i < cmds.Count; i++) { var (name, task) = cmds[i]; @@ -260,7 +265,9 @@ public async Task ShutdownAsync() { _logger?.LogDebug($"- test done,. let's close the client"); await Client.Shutdown(_cancellationTokenSource.Token).ConfigureAwait(false); - } catch (Exception ex) { + } + catch (Exception ex) + { _logger?.LogError(ex.ToString()); throw; } From 83a497c00377cf83d1c46e747a2e43a0d7a4dca5 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 23 Nov 2020 06:12:13 -0500 Subject: [PATCH 10/21] Remove unncessary AssertHelpers.ThrowsAsync Suggested by @lewing ! --- .../DebuggerTestSuite/AssertHelpers.cs | 27 ------------------- .../DebuggerTestSuite/BadHarnessInitTests.cs | 5 ++-- .../DebuggerTestSuite/HarnessTests.cs | 23 +++++++++------- 3 files changed, 16 insertions(+), 39 deletions(-) delete mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs b/src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs deleted file mode 100644 index b3fb0c3c78545b..00000000000000 --- a/src/mono/wasm/debugger/DebuggerTestSuite/AssertHelpers.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Threading.Tasks; -using Xunit; - -namespace DebuggerTests -{ - static class AssertHelpers - { - public static async Task ThrowsAsync(Func testCode, Action testException) - where T : Exception - { - try - { - await testCode(); - Assert.True(false, $"Expected an exception of type {typeof(T)} to be thrown"); - } - catch (Exception exception) - { - Assert.Equal(typeof(T), exception.GetType()); - testException(exception); - } - } - } -} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs index 9e09ade0dc5560..663da5f1c50a6e 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs @@ -37,9 +37,8 @@ public async Task InvalidInitCommands() await Ready(); - await AssertHelpers.ThrowsAsync( - async () => await insp.OpenSessionAsync(fn), - ae => ae.Message.Contains(bad_cmd_name + "Queue")); + var ae = await Assert.ThrowsAsync(async () => await insp.OpenSessionAsync(fn)); + Assert.Contains(bad_cmd_name, ae.Message); } } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs index a89e5ceb533530..576c461c18018d 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs @@ -18,15 +18,18 @@ public class HarnessTests : SingleSessionTestBase public async Task TimedOutWaitingForInvalidBreakpoint() { await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 100, 0); - await AssertHelpers.ThrowsAsync( - async () => await EvaluateAndCheck("window.setTimeout(function() { invoke_add(); }, 1);", null, -1, -1, null), - ex => Assert.Contains("timed out", ex.Message)); + var tce = await Assert.ThrowsAsync( + async () => await EvaluateAndCheck("window.setTimeout(function() { invoke_add(); }, 1);", null, -1, -1, null)); + Assert.Contains("timed out", tce.Message); } [Fact] - public async Task ExceptionThrown() => await AssertHelpers.ThrowsAsync( - async () => await EvaluateAndCheck("window.setTimeout(function() { non_existant_fn(); }, 1);", null, -1, -1, null), - ex => Assert.Contains("non_existant_fn is not defined", ex.Message)); + public async Task ExceptionThrown() + { + var ae = await Assert.ThrowsAsync( + async () => await EvaluateAndCheck("window.setTimeout(function() { non_existant_fn(); }, 1);", null, -1, -1, null)); + Assert.Contains("non_existant_fn is not defined", ae.Message); + } [Fact] public async Task BrowserCrash() => await Assert.ThrowsAsync(async () => @@ -55,8 +58,10 @@ public async Task InspectorWaitForAfterMessageAlreadyReceived() } [Fact] - public async Task InspectorWaitForMessageThatNeverArrives() => await AssertHelpers.ThrowsAsync( - async () => await insp.WaitFor("Message.that.never.arrives"), - ex => Assert.Contains("timed out", ex.Message)); + public async Task InspectorWaitForMessageThatNeverArrives() + { + var tce = await Assert.ThrowsAsync(async () => await insp.WaitFor("Message.that.never.arrives")); + Assert.Contains("timed out", tce.Message); + } } } From ac2a158dea444d86f046751e52771f3bf43687c3 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 23 Nov 2020 11:51:49 -0500 Subject: [PATCH 11/21] [wasm][debugger][tests] Fix a race when sending messages DevToolsClient.Send adds to `pending_ops`, which can happen while the list is being modified in `RunLoop`. Fix: Since, we need to modify it outside `RunLoop` only when a new send task is available, we can let `RunLoop` itself add it to the list. And use a taskCompletionSource to wake up the loop. --- .../BrowserDebugProxy/DevToolsProxy.cs | 2 +- .../BrowserDebugProxy/DevToolsQueue.cs | 4 +- .../DebuggerTestSuite/DevToolsClient.cs | 50 ++++++++++--------- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs index 5ca0e3668b85e8..8474a2dc21cdc1 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs @@ -299,7 +299,7 @@ public async Task Run(Uri browserUri, WebSocket ideSocket) DevToolsQueue queue = GetQueueForTask(task); if (queue != null) { - if (queue.TryPump(x.Token, out Task tsk)) + if (queue.TryPumpIfCurrentCompleted(x.Token, out Task tsk)) pending_ops.Add(tsk); } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs index 806cca61b1d2d2..7baa7ab847f83c 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsQueue.cs @@ -32,11 +32,11 @@ public DevToolsQueue(WebSocket sock) throw new ArgumentNullException(nameof(bytes)); pending.Enqueue(bytes); - TryPump(token, out Task? sendTask); + TryPumpIfCurrentCompleted(token, out Task? sendTask); return sendTask; } - public bool TryPump(CancellationToken token, [NotNullWhen(true)] out Task? sendTask) + public bool TryPumpIfCurrentCompleted(CancellationToken token, [NotNullWhen(true)] out Task? sendTask) { sendTask = null; diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs index 37fafc5d6a4409..7c5874df227171 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs @@ -16,10 +16,9 @@ internal class DevToolsClient : IDisposable { DevToolsQueue _queue; ClientWebSocket socket; - List pending_ops = new List(); TaskCompletionSource _clientInitiatedClose = new TaskCompletionSource(); TaskCompletionSource _shutdownRequested = new TaskCompletionSource(); - TaskCompletionSource _pendingOpsChanged = new TaskCompletionSource(); + TaskCompletionSource _newSendTaskAvailable = new (); protected readonly ILogger logger; public event EventHandler<(RunLoopStopReason reason, Exception ex)> RunLoopStopped; @@ -111,7 +110,7 @@ protected void Send(byte[] bytes, CancellationToken token) { Task sendTask = _queue.Send(bytes, token); if (sendTask != null) - pending_ops.Add(sendTask); + _newSendTaskAvailable.TrySetResult(); } protected async Task ConnectWithMainLoops( @@ -137,7 +136,7 @@ protected async Task ConnectWithMainLoops( try { - (reason, exception) = await RunLoop(uri, receive, linkedCts); + (reason, exception) = await RunLoop(receive, linkedCts); } catch (Exception ex) { @@ -158,33 +157,31 @@ protected async Task ConnectWithMainLoops( } finally { - logger.LogDebug($"Loop ended with pending_ops: {pending_ops?.Count}, socket: {socket.State}"); + logger.LogDebug($"Loop ended with socket: {socket.State}"); linkedCts.Cancel(); - pending_ops = null; } }); } private async Task<(RunLoopStopReason, Exception)> RunLoop( - Uri uri, Func receive, CancellationTokenSource linkedCts) { - pending_ops.InsertRange(0, new[] + var pending_ops = new List { ReadOne(linkedCts.Token), - _pendingOpsChanged.Task, + _newSendTaskAvailable.Task, _clientInitiatedClose.Task, _shutdownRequested.Task - }); + }; // In case we had a Send called already - if (_queue.TryPump(linkedCts.Token, out Task sendTask)) + if (_queue.TryPumpIfCurrentCompleted(linkedCts.Token, out Task sendTask)) pending_ops.Add(sendTask); while (!linkedCts.IsCancellationRequested) { - var task = await Task.WhenAny(pending_ops.ToArray()).ConfigureAwait(false); + var task = await Task.WhenAny(pending_ops).ConfigureAwait(false); if (task.IsCanceled && linkedCts.IsCancellationRequested) return (RunLoopStopReason.Cancelled, null); @@ -195,10 +192,21 @@ protected async Task ConnectWithMainLoops( if (_clientInitiatedClose.Task.IsCompleted) return (RunLoopStopReason.ClientInitiatedClose, new TaskCanceledException("Proxy or the browser closed the connection")); + if (_newSendTaskAvailable.Task.IsCompleted) + { + // Just needed to wake up. the new task has already + // been added to pending_ops + _newSendTaskAvailable = new (); + pending_ops[1] = _newSendTaskAvailable.Task; + + _queue.TryPumpIfCurrentCompleted(linkedCts.Token, out _); + if (_queue.CurrentSend != null) + pending_ops.Add(_queue.CurrentSend); + } + if (task == pending_ops[0]) { - //pending_ops[0] is for message reading - var msg = await (Task)task; + var msg = await (Task)pending_ops[0]; pending_ops[0] = ReadOne(linkedCts.Token); if (msg != null) @@ -208,18 +216,12 @@ protected async Task ConnectWithMainLoops( pending_ops.Add(tsk); } } - else if (task == pending_ops[1]) - { - // Just needed to wake up. the new task has already - // been added to pending_ops - _pendingOpsChanged = new TaskCompletionSource(); - pending_ops[1] = _pendingOpsChanged.Task; - } else - { //must be a background task + { + //must be a background task pending_ops.Remove(task); - if (task == _queue.CurrentSend && _queue.TryPump(linkedCts.Token, out Task tsk)) - pending_ops.Add(tsk); + if (task == _queue.CurrentSend && _queue.TryPumpIfCurrentCompleted(linkedCts.Token, out sendTask)) + pending_ops.Add(sendTask); } } From 7e4b43f7acbb1c1c7e664d2b841f4c715386e6a0 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 20 Nov 2020 15:20:52 -0500 Subject: [PATCH 12/21] [wasm][debugger][tests] Move all the tests to `SingleSessionTestBase` And thus move to remove the use to `insp.Ready`: So, old code: ```csharp [Fact] public async Task BrowserClose() { var insp = new Inspector(); var scripts = SubscribeToScripts(insp); await Ready(); await insp.Ready(async (cli, token) => { await Assert.ThrowsAsync(async () => await SendCommandAndCheck(null, "Browser.close", null, -1, -1, null)); }); } ``` .. becomes: ```csharp [Fact] public async Task BrowserClose() => await Assert.ThrowsAsync(async () => await SendCommandAndCheck(null, "Browser.close", null, -1, -1, null)); ``` --- .../debugger/DebuggerTestSuite/ArrayTests.cs | 50 +--- .../DebuggerTestSuite/CallFunctionOnTests.cs | 47 +--- .../DebuggerTestSuite/DateTimeTests.cs | 11 +- .../DebuggerTestSuite/DebuggerTestBase.cs | 32 +-- .../DebuggerTestSuite/DelegateTests.cs | 2 +- .../EvaluateOnCallFrameTests.cs | 183 +++++++------- .../DebuggerTestSuite/ExceptionTests.cs | 49 +--- .../DebuggerTestSuite/GetPropertiesTests.cs | 30 +-- .../debugger/DebuggerTestSuite/MonoJsTests.cs | 65 +---- .../DebuggerTestSuite/PointerTests.cs | 2 +- .../SingleSessionTestBase.cs | 1 + .../DebuggerTestSuite/SteppingTests.cs | 13 +- .../wasm/debugger/DebuggerTestSuite/Tests.cs | 239 +++--------------- 13 files changed, 162 insertions(+), 562 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs index 6e5e4a948dd41c..8fac95901215a3 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class ArrayTests : DebuggerTestBase + public class ArrayTests : SingleSessionTestBase { [Theory] @@ -202,14 +202,6 @@ async Task TestSimpleArrayLocals(int line, int col, string entry_method_name, st string local_var_name_prefix, object[] array, object[] array_elem_props, bool test_prev_frame = false, int frame_idx = 0, bool use_cfo = false) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; @@ -272,7 +264,6 @@ await insp.Ready(async (cli, token) => var props = await GetObjectOnFrame(pause_location["callFrames"][frame_idx], $"{local_var_name_prefix}_arr_empty"); await CheckProps(props, new object[0], "${local_var_name_prefix}_arr_empty"); - }); async Task GetObjectWithCFO(string objectId, JObject fn_args = null) { @@ -298,19 +289,12 @@ async Task GetObjectWithCFO(string objectId, JObject fn_args = null) [InlineData(true)] public async Task InspectObjectArrayMembers(bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); int line = 227; int col = 12; string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:ObjectArrayMembers"; string method_name = "PlaceholderMethod"; int frame_idx = 1; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; @@ -370,7 +354,6 @@ await CompareObjectPropertiesFor(c_props, "PointsField", TPoint(123, 0, "PointsField#Id#1", "Blue"), }, label: "c#PointsField"); - }); } [Theory] @@ -378,19 +361,12 @@ await CompareObjectPropertiesFor(c_props, "PointsField", [InlineData(true)] public async Task InspectValueTypeArrayLocalsStaticAsync(bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); int line = 157; int col = 12; string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:ValueTypeLocalsAsync"; string method_name = "MoveNext"; // BUG: this should be ValueTypeLocalsAsync int frame_idx = 0; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; @@ -441,7 +417,6 @@ await CompareObjectPropertiesFor(frame_locals, $"{local_var_name_prefix}_arr", ); await CompareObjectPropertiesFor(frame_locals, $"{local_var_name_prefix}_arr_empty", new object[0]); - }); } // TODO: Check previous frame too @@ -450,18 +425,12 @@ await CompareObjectPropertiesFor(frame_locals, $"{local_var_name_prefix}_arr_emp [InlineData(true)] public async Task InspectValueTypeArrayLocalsInstanceAsync(bool use_cfo) { - var insp = new Inspector(); //Collect events - var scripts = SubscribeToScripts(insp); int line = 170; int col = 12; string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:ValueTypeLocalsAsync"; int frame_idx = 0; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; @@ -501,7 +470,6 @@ await CompareObjectPropertiesFor(frame_locals, "point_arr", await CompareObjectPropertiesFor(frame_locals, "point", TPoint(45, 51, "point#Id", "Green")); - }); } [Theory] @@ -509,18 +477,11 @@ await CompareObjectPropertiesFor(frame_locals, "point", [InlineData(true)] public async Task InspectValueTypeArrayLocalsInAsyncStaticStructMethod(bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); int line = 244; int col = 12; string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:EntryPointForStructMethod"; int frame_idx = 0; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; @@ -541,7 +502,6 @@ await insp.Ready(async (cli, token) => local_i = TNumber(5), sc = TSimpleClass(10, 45, "sc#Id", "Blue") }, "InspectValueTypeArrayLocalsInAsyncStaticStructMethod#locals"); - }); } [Theory] @@ -549,18 +509,11 @@ await insp.Ready(async (cli, token) => [InlineData(true)] public async Task InspectValueTypeArrayLocalsInAsyncInstanceStructMethod(bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); int line = 251; int col = 12; string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:EntryPointForStructMethod"; int frame_idx = 0; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; @@ -598,7 +551,6 @@ await CompareObjectPropertiesFor(frame_locals, "sc_arg", await CompareObjectPropertiesFor(frame_locals, "this", TPoint(90, -4, "point#Id", "Green"), label: "this#0"); - }); } [Fact] diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs index 09094cea2212ff..5524d2b6186ea8 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class CallFunctionOnTests : DebuggerTestBase + public class CallFunctionOnTests : SingleSessionTestBase { // This tests `callFunctionOn` with a function that the vscode-js-debug extension uses @@ -442,14 +442,6 @@ public async Task RunOnArrayReturnArrayByValue(string eval_fn, string bp_loc, in [InlineData("invoke_static_method ('[debugger-test] DebuggerTests.CallFunctionOnTest:LocalsTest', 10);", "dotnet://debugger-test.dll/debugger-cfo-test.cs", 23, 12, true)] public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int line, int col, bool return_by_val) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); await SetBreakpoint(bp_loc, line, col); // callFunctionOn @@ -509,7 +501,6 @@ await insp.Ready(async (cli, token) => // callFunctionOn result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); await CheckValue(result.Value["result"], JObject.Parse("{ type: 'object', subtype: 'null', value: null }"), "cfo-res"); - }); } public static TheoryData SilentErrorsTestData(bool? silent) => new TheoryData @@ -523,14 +514,6 @@ await insp.Ready(async (cli, token) => [MemberData(nameof(SilentErrorsTestData), true)] public async Task CFOWithSilentReturnsErrors(string eval_fn, string bp_loc, int line, int col, bool? silent) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); await SetBreakpoint(bp_loc, line, col); // callFunctionOn @@ -561,7 +544,6 @@ await insp.Ready(async (cli, token) => var hasErrorMessage = result.Error["exceptionDetails"]?["exception"]?["description"]?.Value()?.Contains(error_msg); Assert.True((hasErrorMessage ?? false), "Exception message not found"); - }); } public static TheoryData, string, bool> GettersTestData(string local_name, bool use_cfo) => new TheoryData, string, bool> @@ -816,14 +798,6 @@ public async Task RunOnInvalidCfoId(string eval_fn, string bp_loc, int line, int [MemberData(nameof(NegativeTestsData), false)] public async Task RunOnInvalidThirdSegmentOfObjectId(string eval_fn, string bp_loc, int line, int col, bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; await SetBreakpoint(bp_loc, line, col); @@ -844,7 +818,6 @@ await insp.Ready(async (cli, token) => var res = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); Assert.True(res.IsErr); - }); } [Theory] @@ -852,14 +825,6 @@ await insp.Ready(async (cli, token) => [MemberData(nameof(NegativeTestsData), true)] public async Task InvalidPropertyGetters(string eval_fn, string bp_loc, int line, int col, bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); await SetBreakpoint(bp_loc, line, col); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; @@ -878,7 +843,6 @@ await insp.Ready(async (cli, token) => var getter_res = await InvokeGetter(JObject.FromObject(new { value = new { objectId = ptd_id } }), invalid_arg); AssertEqual("undefined", getter_res.Value["result"]?["type"]?.ToString(), $"Expected to get undefined result for non-existant accessor - {invalid_arg}"); } - }); } [Theory] @@ -922,14 +886,6 @@ public async Task ReturnNullFromCFO(string eval_fn, string bp_loc, int line, int async Task RunCallFunctionOn(string eval_fn, string fn_decl, string local_name, string bp_loc, int line, int col, int res_array_len = -1, Func test_fn = null, bool returnByValue = false, JArray fn_args = null, bool roundtrip = false) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); await SetBreakpoint(bp_loc, line, col); // callFunctionOn @@ -996,7 +952,6 @@ async Task CheckCFOResult(Result result) else await CheckValue(result.Value["result"], TArray("Array", res_array_len), $"cfo-res"); } - }); } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs index 40a156c4f31823..b1ec0a27fa30fb 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs @@ -8,7 +8,7 @@ namespace DebuggerTests { - public class DateTimeList : DebuggerTestBase + public class DateTimeList : SingleSessionTestBase { [Theory] @@ -18,13 +18,6 @@ public class DateTimeList : DebuggerTestBase [InlineData("de-DE", "dddd, d. MMMM yyyy HH:mm:ss", "dddd, d. MMMM yyyy", "HH:mm:ss", "dd.MM.yyyy", "HH:mm")] public async Task CheckDateTimeLocale(string locale, string fdtp, string ldp, string ltp, string sdp, string stp) { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); var debugger_test_loc = "dotnet://debugger-test.dll/debugger-datetime-test.cs"; await SetBreakpointInMethod("debugger-test", "DebuggerTests.DateTimeTest", "LocaleTest", 15); @@ -49,8 +42,6 @@ await insp.Ready(async (cli, token) => }, "locals", num_fields: 8); } ); - - }); } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index 55d42ccba6ba70..fdc7eb4e708d67 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -107,16 +107,8 @@ internal Dictionary SubscribeToScripts(Inspector insp) } internal async Task CheckInspectLocalsAtBreakpointSite(string url_key, int line, int column, string function_name, string eval_expression, - Action test_fn = null, Func wait_for_event_fn = null, bool use_cfo = false) + Action? test_fn = null, Func? wait_for_event_fn = null, bool use_cfo = false) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; var bp = await SetBreakpoint(url_key, line, column); @@ -130,9 +122,9 @@ await EvaluateAndCheck( Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - var top_frame = pause_location["callFrames"][0]; + var top_frame = pause_location!["callFrames"]?[0]; - var scope = top_frame["scopeChain"][0]; + var scope = top_frame!["scopeChain"]?[0]; if (wait_for_event_fn != null) await wait_for_event_fn(pause_location); else @@ -144,21 +136,12 @@ await EvaluateAndCheck( test_fn(locals); } ); - }); } // sets breakpoint by method name and line offset internal async Task CheckInspectLocalsAtBreakpointSite(string type, string method, int line_offset, string bp_function_name, string eval_expression, - Action locals_fn = null, Func wait_for_event_fn = null, bool use_cfo = false, string assembly = "debugger-test.dll", int col = 0) + Action? locals_fn = null, Func? wait_for_event_fn = null, bool use_cfo = false, string assembly = "debugger-test.dll", int col = 0) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; var bp = await SetBreakpointInMethod(assembly, type, method, line_offset, col); @@ -178,19 +161,18 @@ await insp.Ready(async (cli, token) => Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - var top_frame = pause_location["callFrames"][0]; + var top_frame = pause_location!["callFrames"]?[0]; - var scope = top_frame["scopeChain"][0]; + var scope = top_frame?["scopeChain"]?[0]; if (wait_for_event_fn != null) await wait_for_event_fn(pause_location); if (locals_fn != null) { - var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + var locals = await GetProperties(pause_location?["callFrames"]?[0]?["callFrameId"]?.Value()); locals_fn(locals); } - }); } internal void CheckLocation(string script_loc, int line, int column, Dictionary scripts, JToken location) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DelegateTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DelegateTests.cs index 2fc0fb52ce99f9..241058a9edbb97 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DelegateTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DelegateTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class DelegateTests : DebuggerTestBase + public class DelegateTests : SingleSessionTestBase { [Theory] diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 2218b9a7945114..00a5abfc457e4e 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -13,7 +13,7 @@ namespace DebuggerTests { // TODO: static async, static method args - public class EvaluateOnCallFrameTests : DebuggerTestBase + public class EvaluateOnCallFrameTests : SingleSessionTestBase { public static IEnumerable InstanceMethodsTestData(string type_name) { @@ -49,26 +49,26 @@ public async Task EvaluateTypeInstanceMembers(string prefix, int bias, string ty var dateTime = new DateTime(2010, 9, 8, 7, 6, 5 + bias); var DTProp = dateTime.AddMinutes(10); - foreach (var pad in new[] { String.Empty, " " }) - { - var padded_prefix = pad + prefix; - await EvaluateOnCallFrameAndCheck(id, - ($"{padded_prefix}a", TNumber(4)), - - // fields - ($"{padded_prefix}dateTime.TimeOfDay", TValueType("System.TimeSpan", dateTime.TimeOfDay.ToString())), - ($"{padded_prefix}dateTime", TDateTime(dateTime)), - ($"{padded_prefix}dateTime.TimeOfDay.Minutes", TNumber(dateTime.TimeOfDay.Minutes)), - - // properties - ($"{padded_prefix}DTProp.TimeOfDay.Minutes", TNumber(DTProp.TimeOfDay.Minutes)), - ($"{padded_prefix}DTProp", TDateTime(DTProp)), - ($"{padded_prefix}DTProp.TimeOfDay", TValueType("System.TimeSpan", DTProp.TimeOfDay.ToString())), - - ($"{padded_prefix}IntProp", TNumber(9)), - ($"{padded_prefix}NullIfAIsNotZero", TObject("DebuggerTests.EvaluateTestsClassWithProperties", is_null: true)) - ); - } + foreach (var pad in new[] { String.Empty, " " }) + { + var padded_prefix = pad + prefix; + await EvaluateOnCallFrameAndCheck(id, + ($"{padded_prefix}a", TNumber(4)), + + // fields + ($"{padded_prefix}dateTime.TimeOfDay", TValueType("System.TimeSpan", dateTime.TimeOfDay.ToString())), + ($"{padded_prefix}dateTime", TDateTime(dateTime)), + ($"{padded_prefix}dateTime.TimeOfDay.Minutes", TNumber(dateTime.TimeOfDay.Minutes)), + + // properties + ($"{padded_prefix}DTProp.TimeOfDay.Minutes", TNumber(DTProp.TimeOfDay.Minutes)), + ($"{padded_prefix}DTProp", TDateTime(DTProp)), + ($"{padded_prefix}DTProp.TimeOfDay", TValueType("System.TimeSpan", DTProp.TimeOfDay.ToString())), + + ($"{padded_prefix}IntProp", TNumber(9)), + ($"{padded_prefix}NullIfAIsNotZero", TObject("DebuggerTests.EvaluateTestsClassWithProperties", is_null: true)) + ); + } }); [Theory] @@ -145,13 +145,13 @@ public async Task EvaluateLocalsAsync() => await CheckInspectLocalsAtBreakpointS { var id = pause_location["callFrames"][0]["callFrameId"].Value(); - // sc_arg - { + // sc_arg + { var (sc_arg, _) = await EvaluateOnCallFrame(id, "sc_arg"); await CheckValue(sc_arg, TObject("DebuggerTests.SimpleClass"), nameof(sc_arg)); - // Check that we did get the correct object - var sc_arg_props = await GetProperties(sc_arg["objectId"]?.Value()); + // Check that we did get the correct object + var sc_arg_props = await GetProperties(sc_arg["objectId"]?.Value()); await CheckProps(sc_arg_props, new { X = TNumber(10), @@ -167,8 +167,8 @@ await EvaluateOnCallFrameAndCheck(id, ("sc_arg.Id + (sc_arg.X==10 ? \"_is_ten\" : \"_not_ten\")", TString($"sc#Id_is_ten"))); } - // local_gs - { + // local_gs + { var (local_gs, _) = await EvaluateOnCallFrame(id, "local_gs"); await CheckValue(local_gs, TValueType("DebuggerTests.SimpleGenericStruct"), nameof(local_gs)); @@ -221,28 +221,28 @@ public async Task InheritedAndPrivateMembersInAClass(string prefix) { var id = pause_location["callFrames"][0]["callFrameId"].Value(); - foreach(var pad in new [] { String.Empty, " "}) - { - var padded_prefix = pad + prefix; - await EvaluateOnCallFrameAndCheck(id, - // overridden - ($"{padded_prefix}FirstName + \"_foo\"", TString("DerivedClass#FirstName_foo")), - ($"{padded_prefix}DateTimeForOverride.Date.Year", TNumber(2190)), - ($"{padded_prefix}DateTimeForOverride.Date.Year - 10", TNumber(2180)), - ($"\"foo_\" + {padded_prefix}StringPropertyForOverrideWithAutoProperty", TString("foo_DerivedClass#StringPropertyForOverrideWithAutoProperty")), - - // private - ($"{padded_prefix}_stringField + \"_foo\"", TString("DerivedClass#_stringField_foo")), - ($"{padded_prefix}_stringField", TString("DerivedClass#_stringField")), - ($"{padded_prefix}_dateTime.Second + 4", TNumber(7)), - ($"{padded_prefix}_DTProp.Second + 4", TNumber(13)), - - // inherited public - ($"\"foo_\" + {padded_prefix}Base_AutoStringProperty", TString("foo_base#Base_AutoStringProperty")), - // inherited private - ($"{padded_prefix}_base_dateTime.Date.Year - 10", TNumber(2124)) - ); - } + foreach (var pad in new[] { String.Empty, " " }) + { + var padded_prefix = pad + prefix; + await EvaluateOnCallFrameAndCheck(id, + // overridden + ($"{padded_prefix}FirstName + \"_foo\"", TString("DerivedClass#FirstName_foo")), + ($"{padded_prefix}DateTimeForOverride.Date.Year", TNumber(2190)), + ($"{padded_prefix}DateTimeForOverride.Date.Year - 10", TNumber(2180)), + ($"\"foo_\" + {padded_prefix}StringPropertyForOverrideWithAutoProperty", TString("foo_DerivedClass#StringPropertyForOverrideWithAutoProperty")), + + // private + ($"{padded_prefix}_stringField + \"_foo\"", TString("DerivedClass#_stringField_foo")), + ($"{padded_prefix}_stringField", TString("DerivedClass#_stringField")), + ($"{padded_prefix}_dateTime.Second + 4", TNumber(7)), + ($"{padded_prefix}_DTProp.Second + 4", TNumber(13)), + + // inherited public + ($"\"foo_\" + {padded_prefix}Base_AutoStringProperty", TString("foo_base#Base_AutoStringProperty")), + // inherited private + ($"{padded_prefix}_base_dateTime.Date.Year - 10", TNumber(2124)) + ); + } }); [Fact] @@ -322,8 +322,8 @@ public async Task EvaluateOnPreviousFrames(string type_name, bool is_valuetype) var dt_local = new DateTime(2020, 3, 4, 5, 6, 7); var dt_this = new DateTime(2010, 9, 8, 7, 6, 5); - // At EvaluateShadow - { + // At EvaluateShadow + { var id0 = pause_location["callFrames"][0]["callFrameId"].Value(); await EvaluateOnCallFrameAndCheck(id0, ("dateTime", TDateTime(dt_local)), @@ -339,38 +339,38 @@ await EvaluateOnCallFrameFail(id1, ("dateTime", "ReferenceError"), ("this.dateTime", "ReferenceError")); - // obj available only on the -1 frame - await EvaluateOnCallFrameAndCheck(id1, ("obj.IntProp", TNumber(7))); + // obj available only on the -1 frame + await EvaluateOnCallFrameAndCheck(id1, ("obj.IntProp", TNumber(7))); } await SetBreakpointInMethod("debugger-test.dll", type_name, "SomeMethod", 1); pause_location = await SendCommandAndCheck(null, "Debugger.resume", null, 0, 0, "SomeMethod"); - // At SomeMethod + // At SomeMethod - // TODO: change types also.. so, that `this` is different! + // TODO: change types also.. so, that `this` is different! - // Check frame0 - { + // Check frame0 + { var id0 = pause_location["callFrames"][0]["callFrameId"].Value(); - // 'me' and 'dateTime' are reversed in this method - await EvaluateOnCallFrameAndCheck(id0, - ("dateTime", is_valuetype ? TValueType(type_name) : TObject(type_name)), - ("this.dateTime", TDateTime(dt_this)), - ("me", TDateTime(dt_local)), + // 'me' and 'dateTime' are reversed in this method + await EvaluateOnCallFrameAndCheck(id0, + ("dateTime", is_valuetype ? TValueType(type_name) : TObject(type_name)), + ("this.dateTime", TDateTime(dt_this)), + ("me", TDateTime(dt_local)), - // local variable shadows field, but isn't "live" yet - ("DTProp", TString(null)), + // local variable shadows field, but isn't "live" yet + ("DTProp", TString(null)), - // access field via `this.` - ("this.DTProp", TDateTime(dt_this.AddMinutes(10)))); + // access field via `this.` + ("this.DTProp", TDateTime(dt_this.AddMinutes(10)))); await EvaluateOnCallFrameFail(id0, ("obj", "ReferenceError")); } - // check frame1 - { + // check frame1 + { var id1 = pause_location["callFrames"][1]["callFrameId"].Value(); await EvaluateOnCallFrameAndCheck(id1, @@ -388,15 +388,15 @@ await EvaluateOnCallFrameAndCheck(id1, await EvaluateOnCallFrameFail(id1, ("obj", "ReferenceError")); } - // check frame2 - { + // check frame2 + { var id2 = pause_location["callFrames"][2]["callFrameId"].Value(); - // Only obj should be available - await EvaluateOnCallFrameFail(id2, - ("dateTime", "ReferenceError"), - ("this.dateTime", "ReferenceError"), - ("me", "ReferenceError")); + // Only obj should be available + await EvaluateOnCallFrameFail(id2, + ("dateTime", "ReferenceError"), + ("this.dateTime", "ReferenceError"), + ("me", "ReferenceError")); await EvaluateOnCallFrameAndCheck(id2, ("obj", is_valuetype ? TValueType(type_name) : TObject(type_name))); } @@ -405,18 +405,10 @@ await EvaluateOnCallFrameFail(id2, [Fact] public async Task JSEvaluate() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - var bp_loc = "/other.js"; var line = 76; var col = 1; - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); await SetBreakpoint(bp_loc, line, col); var eval_expr = "window.setTimeout(function() { eval_call_on_frame_test (); }, 1)"; @@ -430,7 +422,6 @@ await EvaluateOnCallFrameFail(id, ("obj.foo.bar", null)); await EvaluateOnCallFrame(id, "obj.foo", expect_ok: true); - }); } [Fact] @@ -441,23 +432,23 @@ public async Task NegativeTestsInInstanceMethod() => await CheckInspectLocalsAtB { var id = pause_location["callFrames"][0]["callFrameId"].Value(); - // Use '.' on a primitive member - await EvaluateOnCallFrameFail(id, - //BUG: TODO: - //("a)", "CompilationError"), + // Use '.' on a primitive member + await EvaluateOnCallFrameFail(id, + //BUG: TODO: + //("a)", "CompilationError"), - ("this.a.", "ReferenceError"), - ("a.", "ReferenceError"), + ("this.a.", "ReferenceError"), + ("a.", "ReferenceError"), - ("this..a", "CompilationError"), - (".a.", "ReferenceError"), + ("this..a", "CompilationError"), + (".a.", "ReferenceError"), - ("me.foo", "ReferenceError"), + ("me.foo", "ReferenceError"), - ("this.a + non_existant", "ReferenceError"), + ("this.a + non_existant", "ReferenceError"), - ("this.NullIfAIsNotZero.foo", "ReferenceError"), - ("NullIfAIsNotZero.foo", "ReferenceError")); + ("this.NullIfAIsNotZero.foo", "ReferenceError"), + ("NullIfAIsNotZero.foo", "ReferenceError")); }); [Fact] diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs index 6f339c28ea90fd..a07585aec71acd 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs @@ -11,20 +11,12 @@ namespace DebuggerTests { - public class ExceptionTests : DebuggerTestBase + public class ExceptionTests : SingleSessionTestBase { [Fact] public async Task ExceptionTestAll() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs"; await SetPauseOnException("all"); @@ -66,21 +58,11 @@ await CheckValue(pause_location["data"], JObject.FromObject(new exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); CheckString(exception_members, "message", "not implemented uncaught"); - }); } [Fact] public async Task JSExceptionTestAll() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - await SetPauseOnException("all"); var eval_expr = "window.setTimeout(function () { exceptions_test (); }, 1)"; @@ -111,7 +93,6 @@ await CheckValue(pause_location["data"], JObject.FromObject(new exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); CheckString(exception_members, "message", "exception uncaught"); - }); } // FIXME? BUG? We seem to get the stack trace for Runtime.exceptionThrown at `call_method`, @@ -119,16 +100,8 @@ await CheckValue(pause_location["data"], JObject.FromObject(new [Fact] public async Task ExceptionTestNone() { - var insp = new Inspector(); //Collect events - var scripts = SubscribeToScripts(insp); string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - await SetPauseOnException("none"); var eval_expr = "window.setTimeout(function() { invoke_static_method (" + @@ -157,21 +130,11 @@ await CheckValue(eo["exceptionDetails"]?["exception"], JObject.FromObject(new } Assert.True(false, "Expected to get an ArgumentException from the uncaught user exception"); - }); } [Fact] public async Task JSExceptionTestNone() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - await SetPauseOnException("none"); var eval_expr = "window.setTimeout(function () { exceptions_test (); }, 1)"; @@ -200,7 +163,6 @@ await CheckValue(eo["exceptionDetails"]?["exception"], JObject.FromObject(new } Assert.True(false, "Expected to get an ArgumentException from the uncaught user exception"); - }); } [Theory] @@ -211,14 +173,6 @@ await CheckValue(eo["exceptionDetails"]?["exception"], JObject.FromObject(new public async Task ExceptionTestUncaught(string eval_fn, string loc, int line, int col, string fn_name, string exception_type, string exception_message) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - await SetPauseOnException("uncaught"); var eval_expr = $"window.setTimeout({eval_fn}, 1);"; @@ -235,7 +189,6 @@ await CheckValue(pause_location["data"], JObject.FromObject(new var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); CheckString(exception_members, "message", exception_message); - }); } async Task WaitForManagedException(JObject pause_location) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs index 2f35e8cfdc0e09..dc9742103b0b84 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class GetPropertiesTests : DebuggerTestBase + public class GetPropertiesTests : SingleSessionTestBase { public static TheoryData, bool> ClassGetPropertiesTestData(bool is_async) { @@ -188,7 +188,7 @@ public async Task InspectTypeInheritedMembers(string type_name, bool? own_proper }); public static IEnumerable MembersForLocalNestedStructData(bool is_async) - => StructGetPropertiesTestData(false).Select (datum => datum [1..]); + => StructGetPropertiesTestData(false).Select(datum => datum[1..]); [Theory] [MemberData(nameof(MembersForLocalNestedStructData), parameters: false)] @@ -217,7 +217,7 @@ public async Task MembersForLocalNestedStruct(bool? own_properties, bool? access AssertEqual(expected_names.Length, cs_props.Count(), $"expected number of properties"); }); - public static TheoryData JSGetPropertiesTestData(bool test_js)=> new TheoryData + public static TheoryData JSGetPropertiesTestData(bool test_js) => new TheoryData { // default, no args set { @@ -282,15 +282,6 @@ public async Task MembersForLocalNestedStruct(bool? own_properties, bool? access // [MemberData(nameof(JSGetPropertiesTestData), parameters: false)] public async Task GetPropertiesTestJSAndManaged(bool test_js, bool? own_properties, bool? accessors_only, string[] expected_names) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); string eval_expr; if (test_js) { @@ -321,10 +312,10 @@ await insp.Ready(async (cli, token) => else { // we don't set `enumerable` right now - filtered_props = obj_props.Children().Where(jt=> true); + filtered_props = obj_props.Children().Where(jt => true); } - var expected_props = new Dictionary () + var expected_props = new Dictionary() { // own {"owner_name", (TString("foo"), true)}, @@ -338,11 +329,10 @@ await insp.Ready(async (cli, token) => await CheckExpectedProperties( expected_names, - name => filtered_props.Where(jt => jt["name"]?.Value () == name).SingleOrDefault(), + name => filtered_props.Where(jt => jt["name"]?.Value() == name).SingleOrDefault(), expected_props); AssertEqual(expected_names.Length, filtered_props.Count(), $"expected number of properties"); - }); } private async Task CheckExpectedProperties(string[] expected_names, Func get_actual_prop, Dictionary all_props) @@ -356,7 +346,7 @@ private async Task CheckExpectedProperties(string[] expected_names, Func () == true, $"{exp_name}#isOwn"); + AssertEqual(is_own, actual_prop["isOwn"]?.Value() == true, $"{exp_name}#isOwn"); if (exp_prop["__custom_type"]?.Value() == "getter") { @@ -373,14 +363,14 @@ private async Task CheckExpectedProperties(string[] expected_names, Func actual) + private static void AssertHasOnlyExpectedProperties(string[] expected_names, IEnumerable actual) { var exp = new HashSet(expected_names); foreach (var obj in actual) { - if (!exp.Contains(obj["name"]?.Value ())) - Console.WriteLine ($"Unexpected: {obj}"); + if (!exp.Contains(obj["name"]?.Value())) + Console.WriteLine($"Unexpected: {obj}"); } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs index 892fe613f591c2..f6079dec950aed 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + using System; using System.IO; using System.Linq; @@ -8,19 +11,11 @@ namespace DebuggerTests { - public class MonoJsTests : DebuggerTestBase + public class MonoJsTests : SingleSessionTestBase { [Fact] public async Task FixupNameValueObjectsWithMissingParts() { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); var names = new JObject[] @@ -55,7 +50,7 @@ await insp.Ready(async (cli, token) => JObject.DeepEquals(getters[1], res.Value["result"]["value"].Values().ToArray()[3]); - JObject GetterRes(string name) => JObject.FromObject(new + static JObject GetterRes(string name) => JObject.FromObject(new { get = new { @@ -64,20 +59,11 @@ JObject GetterRes(string name) => JObject.FromObject(new type = "function" } }); - }); } [Fact] public async Task GetParamsAndLocalsWithInvalidIndices() { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); var pause_location = await EvaluateAndCheck( "window.setTimeout(function() { invoke_static_method('[debugger-test] Math:IntAdd', 1, 2); })", @@ -104,20 +90,11 @@ await insp.Ready(async (cli, token) => bad0 = TSymbol(""), bad1 = TSymbol("") }, "results"); - }); } [Fact] public async Task InvalidScopeId() { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); await EvaluateAndCheck( "window.setTimeout(function() { invoke_static_method('[debugger-test] Math:IntAdd', 1, 2); })", @@ -137,20 +114,11 @@ await EvaluateAndCheck( expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), ctx.token); Assert.False(res.IsOk); - }); } [Fact] public async Task BadRaiseDebugEventsTest() { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var bad_expressions = new[] { "MONO.mono_wasm_raise_debug_event('')", @@ -171,7 +139,6 @@ await insp.Ready(async (cli, token) => }), ctx.token); Assert.False(res.IsOk, $"Expected to fail for {expression}"); } - }); } [Theory] @@ -180,16 +147,9 @@ await insp.Ready(async (cli, token) => [InlineData(null)] public async Task RaiseDebugEventTraceTest(bool? trace) { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var tcs = new TaskCompletionSource(); - insp.On("Runtime.consoleAPICalled", async (args, token) => { + insp.On("Runtime.consoleAPICalled", async (args, token) => + { if (args?["type"]?.Value() == "debug" && args?["args"]?.Type == JTokenType.Array && args?["args"]?[0]?["value"]?.Value()?.StartsWith("mono_wasm_debug_event_raised:") == true) @@ -211,7 +171,6 @@ await insp.Ready(async (cli, token) => Assert.True(tcs.Task == t, "Timed out waiting for the event to be logged"); else Assert.False(tcs.Task == t, "Event should not have been logged"); - }); } [Theory] @@ -250,8 +209,6 @@ public async Task DuplicateAssemblyLoadedEventWithEmbeddedPdbNotLoadedFromBundle async Task AssemblyLoadedEventTest(string asm_name, string asm_path, string pdb_path, string source_file, int expected_count) { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); int event_count = 0; var tcs = new TaskCompletionSource(); @@ -262,7 +219,7 @@ async Task AssemblyLoadedEventTest(string asm_name, string asm_path, string pdb_ var url = args["url"]?.Value(); if (url?.EndsWith(source_file) == true) { - event_count ++; + event_count++; if (event_count > expected_count) tcs.SetResult(false); } @@ -275,11 +232,6 @@ async Task AssemblyLoadedEventTest(string asm_name, string asm_path, string pdb_ await Task.CompletedTask; }); - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - byte[] bytes = File.ReadAllBytes(asm_path); string asm_base64 = Convert.ToBase64String(bytes); @@ -308,7 +260,6 @@ await insp.Ready(async (cli, token) => throw t.Exception; Assert.True(event_count <= expected_count, $"number of scriptParsed events received. Expected: {expected_count}, Actual: {event_count}"); - }); } } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/PointerTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/PointerTests.cs index ee703b29648bb1..6c792063a6c085 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/PointerTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/PointerTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class PointerTests : DebuggerTestBase + public class PointerTests : SingleSessionTestBase { public static TheoryData PointersTestData => diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs index ec765d575bb6f3..6030df6fb0e1bd 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.WebAssembly.Diagnostics; +using Newtonsoft.Json.Linq; using Xunit; #nullable enable diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs index d966320ded0f6e..76b9f1722ac590 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + using System; using System.Linq; using System.Threading.Tasks; @@ -610,7 +613,7 @@ public async Task SteppingIntoMscorlib() AssertEqual("WriteLine", top_frame["functionName"]?.Value(), "Expected to be in WriteLine method"); var script_id = top_frame["functionLocation"]["scriptId"].Value(); - Assert.Matches ("^dotnet://(mscorlib|System\\.Console)\\.dll/Console.cs", scripts[script_id]); + Assert.Matches("^dotnet://(mscorlib|System\\.Console)\\.dll/Console.cs", scripts[script_id]); } [Fact] @@ -620,7 +623,7 @@ public async Task CreateGoodBreakpointAndHitAndRemoveAndDontHit() var bp2 = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 12, 8); var pause_location = await EvaluateAndCheck( "window.setTimeout(function() { invoke_add(); invoke_add()}, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, + "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, "IntAdd"); Assert.Equal("other", pause_location["reason"]?.Value()); @@ -638,7 +641,7 @@ public async Task CreateGoodBreakpointAndHitAndRemoveTwice() var bp2 = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 12, 8); var pause_location = await EvaluateAndCheck( "window.setTimeout(function() { invoke_add(); invoke_add()}, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, + "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, "IntAdd"); Assert.Equal("other", pause_location["reason"]?.Value()); @@ -655,7 +658,7 @@ public async Task CreateGoodBreakpointAndHitAndRemoveAndDontHitAndCreateAgainAnd var bp2 = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 12, 8); var pause_location = await EvaluateAndCheck( "window.setTimeout(function() { invoke_add(); invoke_add(); invoke_add(); invoke_add()}, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, + "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, "IntAdd"); Assert.Equal("other", pause_location["reason"]?.Value()); @@ -845,7 +848,7 @@ await EvaluateAndCheck( "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);", "dotnet://debugger-test.dll/debugger-test.cs", 546, 4, "StepOverHiddenSP2"); - } + } [Fact] public async Task BreakpointOnHiddenLineOfMethodWithNoNextVisibleLineShouldNotPause() diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index 84a313f5dd7f74..e51191fbe10ca0 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -14,19 +14,12 @@ namespace DebuggerTests { - public class SourceList : DebuggerTestBase + public class SourceList : SingleSessionTestBase { [Fact] - public async Task CheckThatAllSourcesAreSent() + public void CheckThatAllSourcesAreSent() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - //all sources are sent before runtime ready is sent, nothing to check - await insp.Ready(); Assert.Contains("dotnet://debugger-test.dll/debugger-test.cs", scripts.Values); Assert.Contains("dotnet://debugger-test.dll/debugger-test2.cs", scripts.Values); Assert.Contains("dotnet://debugger-test.dll/dependency.cs", scripts.Values); @@ -35,16 +28,6 @@ public async Task CheckThatAllSourcesAreSent() [Fact] public async Task CreateGoodBreakpoint() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var bp1_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); Assert.EndsWith("debugger-test.cs", bp1_res.Value["breakpointId"].ToString()); @@ -56,22 +39,12 @@ await insp.Ready(async (cli, token) => Assert.Equal("dotnet://debugger-test.dll/debugger-test.cs", scripts[loc["scriptId"]?.Value()]); Assert.Equal(10, loc["lineNumber"]); Assert.Equal(8, loc["columnNumber"]); - }); } [Fact] public async Task CreateJSBreakpoint() { // Test that js breakpoints get set correctly - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); // 13 24 // 13 31 var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 24); @@ -95,22 +68,11 @@ await insp.Ready(async (cli, token) => Assert.NotNull(loc2["scriptId"]); Assert.Equal(13, loc2["lineNumber"]); Assert.Equal(31, loc2["columnNumber"]); - }); } [Fact] public async Task CreateJS0Breakpoint() { - // Test that js column 0 does as expected - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); // 13 24 // 13 31 var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 0); @@ -134,7 +96,6 @@ await insp.Ready(async (cli, token) => Assert.NotNull(loc2["scriptId"]); Assert.Equal(13, loc2["lineNumber"]); Assert.Equal(31, loc2["columnNumber"]); - }); } [Theory] @@ -142,15 +103,6 @@ await insp.Ready(async (cli, token) => [InlineData(50)] public async Task CheckMultipleBreakpointsOnSameLine(int col) { - var insp = new Inspector(); - - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var bp1_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-array-test.cs", 219, col); Assert.EndsWith("debugger-array-test.cs", bp1_res.Value["breakpointId"].ToString()); Assert.Equal(1, bp1_res.Value["locations"]?.Value()?.Count); @@ -166,20 +118,11 @@ await insp.Ready(async (cli, token) => var loc2 = bp2_res.Value["locations"]?.Value()[0]; CheckLocation("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 55, scripts, loc2); - }); } [Fact] public async Task CreateBadBreakpoint() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { var bp1_req = JObject.FromObject(new { lineNumber = 8, @@ -187,27 +130,16 @@ await insp.Ready(async (cli, token) => url = "dotnet://debugger-test.dll/this-file-doesnt-exist.cs", }); - var bp1_res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token); + var bp1_res = await ctx.cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, ctx.token); Assert.True(bp1_res.IsOk); Assert.Empty(bp1_res.Value["locations"].Values()); //Assert.Equal ((int)MonoErrorCodes.BpNotFound, bp1_res.Error ["code"]?.Value ()); - }); } [Fact] public async Task CreateGoodBreakpointAndHit() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); var eval_req = JObject.FromObject(new @@ -241,45 +173,24 @@ await EvaluateAndCheck( return Task.CompletedTask; } ); - - }); } [Fact] public async Task ExceptionThrownInJS() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { var eval_req = JObject.FromObject(new { expression = "invoke_bad_js_test();" }); - var eval_res = await cli.SendCommand("Runtime.evaluate", eval_req, token); + var eval_res = await ctx.cli.SendCommand("Runtime.evaluate", eval_req, ctx.token); Assert.True(eval_res.IsErr); Assert.Equal("Uncaught", eval_res.Error["exceptionDetails"]?["text"]?.Value()); - }); } [Fact] public async Task ExceptionThrownInJSOutOfBand() { - var insp = new Inspector(); - - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - await SetBreakpoint("/debugger-driver.html", 27, 2); var eval_req = JObject.FromObject(new @@ -288,14 +199,13 @@ await insp.Ready(async (cli, token) => }); var task = insp.WaitFor("Runtime.exceptionThrown"); - var eval_res = await cli.SendCommand("Runtime.evaluate", eval_req, token); + var eval_res = await ctx.cli.SendCommand("Runtime.evaluate", eval_req, ctx.token); // Response here will be the id for the timer from JS! Assert.True(eval_res.IsOk); var ex = await Assert.ThrowsAsync(async () => await task); var ex_json = JObject.Parse(ex.Message); Assert.Equal(dicFileToUrl["/debugger-driver.html"], ex_json["exceptionDetails"]?["url"]?.Value()); - }); } [Theory] @@ -409,25 +319,25 @@ public async Task InspectNullableLocals(string method_name, bool is_async) => aw var dt = new DateTime(2310, 1, 2, 3, 4, 5); await CheckProps(locals, new { - n_int = TNumber(5), - n_int_null = TObject("System.Nullable", null), + n_int = TNumber(5), + n_int_null = TObject("System.Nullable", null), - n_dt = TDateTime(dt), - n_dt_null = TObject("System.Nullable", null), + n_dt = TDateTime(dt), + n_dt_null = TObject("System.Nullable", null), - n_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), - n_gs_null = TObject("System.Nullable>", null), + n_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), + n_gs_null = TObject("System.Nullable>", null), }, "locals"); // check gs var n_gs = GetAndAssertObjectWithName(locals, "n_gs"); - var n_gs_props = await GetProperties(n_gs["value"]?["objectId"]?.Value ()); + var n_gs_props = await GetProperties(n_gs["value"]?["objectId"]?.Value()); await CheckProps(n_gs_props, new { - List = TObject("System.Collections.Generic.List", is_null: true), + List = TObject("System.Collections.Generic.List", is_null: true), StringField = TString("n_gs#StringField"), - Options = TEnum ("DebuggerTests.Options", "None") + Options = TEnum("DebuggerTests.Options", "None") }, nameof(n_gs)); }); @@ -459,15 +369,6 @@ await CheckInspectLocalsAtBreakpointSite( [Fact] public async Task RuntimeGetPropertiesWithInvalidScopeIdTest() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 49, 8); await EvaluateAndCheck( @@ -489,11 +390,10 @@ await EvaluateAndCheck( objectId = "dotnet:scope:23490871", }); - var frame_props = await cli.SendCommand("Runtime.getProperties", get_prop_req, token); + var frame_props = await ctx.cli.SendCommand("Runtime.getProperties", get_prop_req, ctx.token); Assert.True(frame_props.IsErr); } ); - }); } [Theory] @@ -501,14 +401,6 @@ await EvaluateAndCheck( [InlineData(true)] public async Task InspectLocalsWithStructs(bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; @@ -593,7 +485,6 @@ await CompareObjectPropertiesFor(vt_local_props, name, } // FIXME: check ss_local.gs.List's members - }); } [Theory] @@ -611,33 +502,33 @@ public async Task InspectBoxedLocals(string method_name, bool is_async) => await var dt = new DateTime(2310, 1, 2, 3, 4, 5); await CheckProps(locals, new { - n_i = TNumber(5), - o_i = TNumber(5), - o_n_i = TNumber(5), - o_s = TString("foobar"), - o_obj = TObject("Math"), - - n_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), - o_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), + n_i = TNumber(5), + o_i = TNumber(5), + o_n_i = TNumber(5), + o_s = TString("foobar"), + o_obj = TObject("Math"), + + n_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), + o_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), o_n_gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), - n_dt = TDateTime(dt), - o_dt = TDateTime(dt), + n_dt = TDateTime(dt), + o_dt = TDateTime(dt), o_n_dt = TDateTime(dt), o_null = TObject("object", is_null: true), - o_ia = TArray("int[]", 2), + o_ia = TArray("int[]", 2), }, "locals"); foreach (var name in new[] { "n_gs", "o_gs", "o_n_gs" }) { var gs = GetAndAssertObjectWithName(locals, name); - var gs_props = await GetProperties(gs["value"]?["objectId"]?.Value ()); + var gs_props = await GetProperties(gs["value"]?["objectId"]?.Value()); await CheckProps(gs_props, new { - List = TObject("System.Collections.Generic.List", is_null: true), + List = TObject("System.Collections.Generic.List", is_null: true), StringField = TString("n_gs#StringField"), - Options = TEnum ("DebuggerTests.Options", "None") + Options = TEnum("DebuggerTests.Options", "None") }, name); } @@ -688,14 +579,14 @@ public async Task InspectBoxedAsClassLocals(string method_name, bool is_async) = { var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); var dt = new DateTime(2310, 1, 2, 3, 4, 5); - Console.WriteLine (locals); + Console.WriteLine(locals); await CheckProps(locals, new { vt_dt = TDateTime(new DateTime(4819, 5, 6, 7, 8, 9)), vt_gs = TValueType("Math.GenericStruct"), - e = TEnum("System.IO.FileMode", "0"), - ee = TEnum("System.IO.FileMode", "Append") + e = TEnum("System.IO.FileMode", "0"), + ee = TEnum("System.IO.FileMode", "Append") }, "locals"); }); @@ -704,14 +595,6 @@ public async Task InspectBoxedAsClassLocals(string method_name, bool is_async) = [InlineData(true)] public async Task InspectLocalsWithStructsStaticAsync(bool use_cfo) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; @@ -769,7 +652,6 @@ await CompareObjectPropertiesFor(ss_local_props, "gs", }, "gs_local"); // FIXME: check ss_local.gs.List's members - }); } [Theory] @@ -779,16 +661,8 @@ await CompareObjectPropertiesFor(ss_local_props, "gs", [InlineData(182, 12, "MethodWithArgumentsForToStringTestAsync", false, true)] public async Task InspectLocalsForToStringDescriptions(int line, int col, string method_name, bool call_other, bool invoke_async) { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - string entry_method_name = $"[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalsForToStringTest{(invoke_async ? "Async" : String.Empty)}"; + string entry_method_name = $"[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalsForToStringTest{(invoke_async ? "Async" : String.Empty)}"; int frame_idx = 0; - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; await SetBreakpoint(debugger_test_loc, line, col); @@ -869,23 +743,13 @@ await CompareObjectPropertiesFor(frame_locals, "obj", Dec = TValueType("System.Decimal", "1239871"), Guid = TValueType("System.Guid", "3D36E07E-AC90-48C6-B7EC-A481E289D014") }, "sst_props"); - }); } [Fact] public async Task InspectLocals() { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - var wait_res = await RunUntil("locals_inner"); var locals = await GetProperties(wait_res["callFrames"][1]["callFrameId"].Value()); - }); } [Theory] @@ -1057,13 +921,6 @@ JObject FindFrame(JObject pause_location, string function_name) [Fact] public async Task DebugLazyLoadedAssemblyWithPdb() { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - int line = 9; await SetBreakpoint(".*/lazy-debugger-test.cs$", line, 0, use_regex: true); await LoadAssemblyDynamically( @@ -1080,20 +937,11 @@ await LoadAssemblyDynamically( var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); CheckNumber(locals, "a", 5); CheckNumber(locals, "b", 10); - }); } [Fact] public async Task DebugLazyLoadedAssemblyWithEmbeddedPdb() { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - await Ready(); - - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - int line = 9; await SetBreakpoint(".*/lazy-debugger-test-embedded.cs$", line, 0, use_regex: true); await LoadAssemblyDynamically( @@ -1110,19 +958,11 @@ await LoadAssemblyDynamically( var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); CheckNumber(locals, "a", 5); CheckNumber(locals, "b", 10); - }); } [Fact] public async Task CannotDebugLazyLoadedAssemblyWithoutPdb() { - var insp = new Inspector(); - var scripts = SubscribeToScripts(insp); - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); - int line = 9; await SetBreakpoint(".*/lazy-debugger-test.cs$", line, 0, use_regex: true); await LoadAssemblyDynamically( @@ -1134,7 +974,6 @@ await LoadAssemblyDynamically( var source_location = "dotnet://lazy-debugger-test.dll/lazy-debugger-test.cs"; Assert.DoesNotContain(source_location, scripts.Values); - }); } async Task LoadAssemblyDynamically(string asm_file, string pdb_file) @@ -1144,7 +983,8 @@ async Task LoadAssemblyDynamically(string asm_file, string pdb_file) string asm_base64 = Convert.ToBase64String(bytes); string pdb_base64 = null; - if (pdb_file != null) { + if (pdb_file != null) + { bytes = File.ReadAllBytes(pdb_file); pdb_base64 = Convert.ToBase64String(bytes); } @@ -1161,19 +1001,10 @@ async Task LoadAssemblyDynamically(string asm_file, string pdb_file) [Fact] public async Task BreakOnDebuggerBreak() { - var insp = new Inspector(); - //Collect events - var scripts = SubscribeToScripts(insp); - - await Ready(); - await insp.Ready(async (cli, token) => - { - ctx = new DebugTestContext(cli, insp, token, scripts); await EvaluateAndCheck( "window.setTimeout(function() { invoke_static_method_async('[debugger-test] UserBreak:BreakOnDebuggerBreakCommand'); }, 1);", "dotnet://debugger-test.dll/debugger-test2.cs", 56, 4, "BreakOnDebuggerBreakCommand"); - }); } //TODO add tests covering basic stepping behavior as step in/out/over } From ae1268fa414a11e79da602fecfb7e559e330cfaa Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 20 Nov 2020 15:21:48 -0500 Subject: [PATCH 13/21] [wasm][debugger][tests] Run `dotnet format` on the test files --- .../debugger/DebuggerTestSuite/ArrayTests.cs | 394 +++++----- .../DebuggerTestSuite/CallFunctionOnTests.cs | 304 ++++---- .../DebuggerTestSuite/DateTimeTests.cs | 42 +- .../DebuggerTestSuite/DebuggerTestBase.cs | 96 +-- .../EvaluateOnCallFrameTests.cs | 18 +- .../DebuggerTestSuite/ExceptionTests.cs | 230 +++--- .../DebuggerTestSuite/GetPropertiesTests.cs | 68 +- .../debugger/DebuggerTestSuite/MonoJsTests.cs | 214 ++--- .../wasm/debugger/DebuggerTestSuite/Tests.cs | 736 +++++++++--------- 9 files changed, 1051 insertions(+), 1051 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs index 8fac95901215a3..d9d0a85de4d120 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs @@ -202,68 +202,68 @@ async Task TestSimpleArrayLocals(int line, int col, string entry_method_name, st string local_var_name_prefix, object[] array, object[] array_elem_props, bool test_prev_frame = false, int frame_idx = 0, bool use_cfo = false) { - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - await SetBreakpoint(debugger_test_loc, line, col); + await SetBreakpoint(debugger_test_loc, line, col); - var eval_expr = "window.setTimeout(function() { invoke_static_method (" + - $"'{entry_method_name}', { (test_prev_frame ? "true" : "false") }" + - "); }, 1);"; + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + + $"'{entry_method_name}', { (test_prev_frame ? "true" : "false") }" + + "); }, 1);"; - var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, method_name); + var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, method_name); - var locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); - Assert.Equal(4, locals.Count()); - CheckArray(locals, $"{local_var_name_prefix}_arr", $"{etype_name}[]", array?.Length ?? 0); - CheckArray(locals, $"{local_var_name_prefix}_arr_empty", $"{etype_name}[]", 0); - CheckObject(locals, $"{local_var_name_prefix}_arr_null", $"{etype_name}[]", is_null: true); - CheckBool(locals, "call_other", test_prev_frame); + var locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); + Assert.Equal(4, locals.Count()); + CheckArray(locals, $"{local_var_name_prefix}_arr", $"{etype_name}[]", array?.Length ?? 0); + CheckArray(locals, $"{local_var_name_prefix}_arr_empty", $"{etype_name}[]", 0); + CheckObject(locals, $"{local_var_name_prefix}_arr_null", $"{etype_name}[]", is_null: true); + CheckBool(locals, "call_other", test_prev_frame); - var local_arr_name = $"{local_var_name_prefix}_arr"; + var local_arr_name = $"{local_var_name_prefix}_arr"; - JToken prefix_arr; - if (use_cfo) - { // Use `Runtime.callFunctionOn` to get the properties - var frame = pause_location["callFrames"][frame_idx]; - var name = local_arr_name; - var fl = await GetProperties(frame["callFrameId"].Value()); - var l_obj = GetAndAssertObjectWithName(locals, name); - var l_objectId = l_obj["value"]["objectId"]?.Value(); + JToken prefix_arr; + if (use_cfo) + { // Use `Runtime.callFunctionOn` to get the properties + var frame = pause_location["callFrames"][frame_idx]; + var name = local_arr_name; + var fl = await GetProperties(frame["callFrameId"].Value()); + var l_obj = GetAndAssertObjectWithName(locals, name); + var l_objectId = l_obj["value"]["objectId"]?.Value(); - Assert.True(!String.IsNullOrEmpty(l_objectId), $"No objectId found for {name}"); + Assert.True(!String.IsNullOrEmpty(l_objectId), $"No objectId found for {name}"); - prefix_arr = await GetObjectWithCFO(l_objectId); - } - else - { - prefix_arr = await GetObjectOnFrame(pause_location["callFrames"][frame_idx], local_arr_name); - } + prefix_arr = await GetObjectWithCFO(l_objectId); + } + else + { + prefix_arr = await GetObjectOnFrame(pause_location["callFrames"][frame_idx], local_arr_name); + } - await CheckProps(prefix_arr, array, local_arr_name); + await CheckProps(prefix_arr, array, local_arr_name); - if (array_elem_props?.Length > 0) + if (array_elem_props?.Length > 0) + { + for (int i = 0; i < array_elem_props.Length; i++) { - for (int i = 0; i < array_elem_props.Length; i++) + var i_str = i.ToString(); + var label = $"{local_var_name_prefix}_arr[{i}]"; + if (array_elem_props[i] == null) { - var i_str = i.ToString(); - var label = $"{local_var_name_prefix}_arr[{i}]"; - if (array_elem_props[i] == null) - { - var act_i = prefix_arr.FirstOrDefault(jt => jt["name"]?.Value() == i_str); - Assert.True(act_i != null, $"[{label}] Couldn't find array element [{i_str}]"); + var act_i = prefix_arr.FirstOrDefault(jt => jt["name"]?.Value() == i_str); + Assert.True(act_i != null, $"[{label}] Couldn't find array element [{i_str}]"); - await CheckValue(act_i["value"], TObject(etype_name, is_null: true), label); - } - else - { - await CompareObjectPropertiesFor(prefix_arr, i_str, array_elem_props[i], label: label); - } + await CheckValue(act_i["value"], TObject(etype_name, is_null: true), label); + } + else + { + await CompareObjectPropertiesFor(prefix_arr, i_str, array_elem_props[i], label: label); } } + } - var props = await GetObjectOnFrame(pause_location["callFrames"][frame_idx], $"{local_var_name_prefix}_arr_empty"); - await CheckProps(props, new object[0], "${local_var_name_prefix}_arr_empty"); + var props = await GetObjectOnFrame(pause_location["callFrames"][frame_idx], $"{local_var_name_prefix}_arr_empty"); + await CheckProps(props, new object[0], "${local_var_name_prefix}_arr_empty"); async Task GetObjectWithCFO(string objectId, JObject fn_args = null) { @@ -295,65 +295,65 @@ public async Task InspectObjectArrayMembers(bool use_cfo) string method_name = "PlaceholderMethod"; int frame_idx = 1; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; - await SetBreakpoint(debugger_test_loc, line, col); + await SetBreakpoint(debugger_test_loc, line, col); - var eval_expr = "window.setTimeout(function() { invoke_static_method (" + - $"'{entry_method_name}'" + - "); }, 1);"; + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + + $"'{entry_method_name}'" + + "); }, 1);"; - var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, method_name); - var locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); - Assert.Single(locals); - CheckObject(locals, "c", "DebuggerTests.Container"); + var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, method_name); + var locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); + Assert.Single(locals); + CheckObject(locals, "c", "DebuggerTests.Container"); - var c_props = await GetObjectOnFrame(pause_location["callFrames"][frame_idx], "c"); - await CheckProps(c_props, new - { - id = TString("c#id"), - ClassArrayProperty = TArray("DebuggerTests.SimpleClass[]", 3), - ClassArrayField = TArray("DebuggerTests.SimpleClass[]", 3), - PointsProperty = TArray("DebuggerTests.Point[]", 2), - PointsField = TArray("DebuggerTests.Point[]", 2) - }, - "c" - ); + var c_props = await GetObjectOnFrame(pause_location["callFrames"][frame_idx], "c"); + await CheckProps(c_props, new + { + id = TString("c#id"), + ClassArrayProperty = TArray("DebuggerTests.SimpleClass[]", 3), + ClassArrayField = TArray("DebuggerTests.SimpleClass[]", 3), + PointsProperty = TArray("DebuggerTests.Point[]", 2), + PointsField = TArray("DebuggerTests.Point[]", 2) + }, + "c" + ); - await CompareObjectPropertiesFor(c_props, "ClassArrayProperty", - new[] - { + await CompareObjectPropertiesFor(c_props, "ClassArrayProperty", + new[] + { TSimpleClass(5, -2, "ClassArrayProperty#Id#0", "Green"), TSimpleClass(30, 1293, "ClassArrayProperty#Id#1", "Green"), TObject("DebuggerTests.SimpleClass", is_null : true) - }, - label: "InspectLocalsWithStructsStaticAsync"); + }, + label: "InspectLocalsWithStructsStaticAsync"); - await CompareObjectPropertiesFor(c_props, "ClassArrayField", - new[] - { + await CompareObjectPropertiesFor(c_props, "ClassArrayField", + new[] + { TObject("DebuggerTests.SimpleClass", is_null : true), TSimpleClass(5, -2, "ClassArrayField#Id#1", "Blue"), TSimpleClass(30, 1293, "ClassArrayField#Id#2", "Green") - }, - label: "c#ClassArrayField"); + }, + label: "c#ClassArrayField"); - await CompareObjectPropertiesFor(c_props, "PointsProperty", - new[] - { + await CompareObjectPropertiesFor(c_props, "PointsProperty", + new[] + { TPoint(5, -2, "PointsProperty#Id#0", "Green"), TPoint(123, 0, "PointsProperty#Id#1", "Blue"), - }, - label: "c#PointsProperty"); + }, + label: "c#PointsProperty"); - await CompareObjectPropertiesFor(c_props, "PointsField", - new[] - { + await CompareObjectPropertiesFor(c_props, "PointsField", + new[] + { TPoint(5, -2, "PointsField#Id#0", "Green"), TPoint(123, 0, "PointsField#Id#1", "Blue"), - }, - label: "c#PointsField"); + }, + label: "c#PointsField"); } [Theory] @@ -367,40 +367,40 @@ public async Task InspectValueTypeArrayLocalsStaticAsync(bool use_cfo) string method_name = "MoveNext"; // BUG: this should be ValueTypeLocalsAsync int frame_idx = 0; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; - await SetBreakpoint(debugger_test_loc, line, col); + await SetBreakpoint(debugger_test_loc, line, col); - var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" + - $"'{entry_method_name}', false" // *false* here keeps us only in the static method - + - "); }, 1);"; + var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" + + $"'{entry_method_name}', false" // *false* here keeps us only in the static method + + + "); }, 1);"; - var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, method_name); - var frame_locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); - await CheckProps(frame_locals, new - { - call_other = TBool(false), - gvclass_arr = TArray("DebuggerTests.SimpleGenericStruct[]", 2), - gvclass_arr_empty = TArray("DebuggerTests.SimpleGenericStruct[]"), - gvclass_arr_null = TObject("DebuggerTests.SimpleGenericStruct[]", is_null: true), - gvclass = TValueType("DebuggerTests.SimpleGenericStruct"), - // BUG: this shouldn't be null! - points = TObject("DebuggerTests.Point[]", is_null: true) - }, "ValueTypeLocalsAsync#locals"); - - var local_var_name_prefix = "gvclass"; - await CompareObjectPropertiesFor(frame_locals, local_var_name_prefix, new - { - Id = TString(null), - Color = TEnum("DebuggerTests.RGB", "Red"), - Value = TPoint(0, 0, null, "Red") - }); + var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, method_name); + var frame_locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); + await CheckProps(frame_locals, new + { + call_other = TBool(false), + gvclass_arr = TArray("DebuggerTests.SimpleGenericStruct[]", 2), + gvclass_arr_empty = TArray("DebuggerTests.SimpleGenericStruct[]"), + gvclass_arr_null = TObject("DebuggerTests.SimpleGenericStruct[]", is_null: true), + gvclass = TValueType("DebuggerTests.SimpleGenericStruct"), + // BUG: this shouldn't be null! + points = TObject("DebuggerTests.Point[]", is_null: true) + }, "ValueTypeLocalsAsync#locals"); + + var local_var_name_prefix = "gvclass"; + await CompareObjectPropertiesFor(frame_locals, local_var_name_prefix, new + { + Id = TString(null), + Color = TEnum("DebuggerTests.RGB", "Red"), + Value = TPoint(0, 0, null, "Red") + }); - await CompareObjectPropertiesFor(frame_locals, $"{local_var_name_prefix}_arr", - new[] - { + await CompareObjectPropertiesFor(frame_locals, $"{local_var_name_prefix}_arr", + new[] + { new { Id = TString("gvclass_arr#1#Id"), @@ -413,10 +413,10 @@ await CompareObjectPropertiesFor(frame_locals, $"{local_var_name_prefix}_arr", Color = TEnum("DebuggerTests.RGB", "Blue"), Value = TPoint(10, 20, "gvclass_arr#2#Value#Id", "Green") } - } - ); - await CompareObjectPropertiesFor(frame_locals, $"{local_var_name_prefix}_arr_empty", - new object[0]); + } + ); + await CompareObjectPropertiesFor(frame_locals, $"{local_var_name_prefix}_arr_empty", + new object[0]); } // TODO: Check previous frame too @@ -431,45 +431,45 @@ public async Task InspectValueTypeArrayLocalsInstanceAsync(bool use_cfo) string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:ValueTypeLocalsAsync"; int frame_idx = 0; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; + + await SetBreakpoint(debugger_test_loc, line, col); - await SetBreakpoint(debugger_test_loc, line, col); + var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" + + $"'{entry_method_name}', true" + + "); }, 1);"; - var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" + - $"'{entry_method_name}', true" + - "); }, 1);"; + // BUG: Should be InspectValueTypeArrayLocalsInstanceAsync + var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, "MoveNext"); - // BUG: Should be InspectValueTypeArrayLocalsInstanceAsync - var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, "MoveNext"); + var frame_locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); + await CheckProps(frame_locals, new + { + t1 = TObject("DebuggerTests.SimpleGenericStruct"), + @this = TObject("DebuggerTests.ArrayTestsClass"), + point_arr = TArray("DebuggerTests.Point[]", 2), + point = TValueType("DebuggerTests.Point") + }, "InspectValueTypeArrayLocalsInstanceAsync#locals"); - var frame_locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); - await CheckProps(frame_locals, new + await CompareObjectPropertiesFor(frame_locals, "t1", + new { - t1 = TObject("DebuggerTests.SimpleGenericStruct"), - @this = TObject("DebuggerTests.ArrayTestsClass"), - point_arr = TArray("DebuggerTests.Point[]", 2), - point = TValueType("DebuggerTests.Point") - }, "InspectValueTypeArrayLocalsInstanceAsync#locals"); - - await CompareObjectPropertiesFor(frame_locals, "t1", - new - { - Id = TString("gvclass_arr#1#Id"), - Color = TEnum("DebuggerTests.RGB", "Red"), - Value = TPoint(100, 200, "gvclass_arr#1#Value#Id", "Red") - }); + Id = TString("gvclass_arr#1#Id"), + Color = TEnum("DebuggerTests.RGB", "Red"), + Value = TPoint(100, 200, "gvclass_arr#1#Value#Id", "Red") + }); - await CompareObjectPropertiesFor(frame_locals, "point_arr", - new[] - { + await CompareObjectPropertiesFor(frame_locals, "point_arr", + new[] + { TPoint(5, -2, "point_arr#Id#0", "Red"), TPoint(123, 0, "point_arr#Id#1", "Blue"), - } - ); + } + ); - await CompareObjectPropertiesFor(frame_locals, "point", - TPoint(45, 51, "point#Id", "Green")); + await CompareObjectPropertiesFor(frame_locals, "point", + TPoint(45, 51, "point#Id", "Green")); } [Theory] @@ -482,26 +482,26 @@ public async Task InspectValueTypeArrayLocalsInAsyncStaticStructMethod(bool use_ string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:EntryPointForStructMethod"; int frame_idx = 0; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; - await SetBreakpoint(debugger_test_loc, line, col); - //await SetBreakpoint (debugger_test_loc, 143, 3); + await SetBreakpoint(debugger_test_loc, line, col); + //await SetBreakpoint (debugger_test_loc, 143, 3); - var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" + - $"'{entry_method_name}', false" + - "); }, 1);"; + var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" + + $"'{entry_method_name}', false" + + "); }, 1);"; - // BUG: Should be InspectValueTypeArrayLocalsInstanceAsync - var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, "MoveNext"); + // BUG: Should be InspectValueTypeArrayLocalsInstanceAsync + var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, "MoveNext"); - var frame_locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); - await CheckProps(frame_locals, new - { - call_other = TBool(false), - local_i = TNumber(5), - sc = TSimpleClass(10, 45, "sc#Id", "Blue") - }, "InspectValueTypeArrayLocalsInAsyncStaticStructMethod#locals"); + var frame_locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); + await CheckProps(frame_locals, new + { + call_other = TBool(false), + local_i = TNumber(5), + sc = TSimpleClass(10, 45, "sc#Id", "Blue") + }, "InspectValueTypeArrayLocalsInAsyncStaticStructMethod#locals"); } [Theory] @@ -514,43 +514,43 @@ public async Task InspectValueTypeArrayLocalsInAsyncInstanceStructMethod(bool us string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:EntryPointForStructMethod"; int frame_idx = 0; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; + + await SetBreakpoint(debugger_test_loc, line, col); - await SetBreakpoint(debugger_test_loc, line, col); + var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" + + $"'{entry_method_name}', true" + + "); }, 1);"; - var eval_expr = "window.setTimeout(function() { invoke_static_method_async (" + - $"'{entry_method_name}', true" + - "); }, 1);"; + // BUG: Should be InspectValueTypeArrayLocalsInstanceAsync + var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, "MoveNext"); - // BUG: Should be InspectValueTypeArrayLocalsInstanceAsync - var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, "MoveNext"); + var frame_locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); + await CheckProps(frame_locals, new + { + sc_arg = TObject("DebuggerTests.SimpleClass"), + @this = TValueType("DebuggerTests.Point"), + local_gs = TValueType("DebuggerTests.SimpleGenericStruct") + }, + "locals#0"); - var frame_locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); - await CheckProps(frame_locals, new + await CompareObjectPropertiesFor(frame_locals, "local_gs", + new { - sc_arg = TObject("DebuggerTests.SimpleClass"), - @this = TValueType("DebuggerTests.Point"), - local_gs = TValueType("DebuggerTests.SimpleGenericStruct") + Id = TString("local_gs#Id"), + Color = TEnum("DebuggerTests.RGB", "Green"), + Value = TNumber(4) }, - "locals#0"); + label: "local_gs#0"); - await CompareObjectPropertiesFor(frame_locals, "local_gs", - new - { - Id = TString("local_gs#Id"), - Color = TEnum("DebuggerTests.RGB", "Green"), - Value = TNumber(4) - }, - label: "local_gs#0"); - - await CompareObjectPropertiesFor(frame_locals, "sc_arg", - TSimpleClass(10, 45, "sc_arg#Id", "Blue"), - label: "sc_arg#0"); - - await CompareObjectPropertiesFor(frame_locals, "this", - TPoint(90, -4, "point#Id", "Green"), - label: "this#0"); + await CompareObjectPropertiesFor(frame_locals, "sc_arg", + TSimpleClass(10, 45, "sc_arg#Id", "Blue"), + label: "sc_arg#0"); + + await CompareObjectPropertiesFor(frame_locals, "this", + TPoint(90, -4, "point#Id", "Green"), + label: "this#0"); } [Fact] diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs index 5524d2b6186ea8..f3b674ec42d2ec 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs @@ -442,65 +442,65 @@ public async Task RunOnArrayReturnArrayByValue(string eval_fn, string bp_loc, in [InlineData("invoke_static_method ('[debugger-test] DebuggerTests.CallFunctionOnTest:LocalsTest', 10);", "dotnet://debugger-test.dll/debugger-cfo-test.cs", 23, 12, true)] public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int line, int col, bool return_by_val) { - await SetBreakpoint(bp_loc, line, col); + await SetBreakpoint(bp_loc, line, col); - // callFunctionOn - var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + // callFunctionOn + var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; + var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); + var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); - // Um for js we get "scriptId": "6" - // CheckLocation (bp_loc, line, col, ctx.scripts, pause_location ["callFrames"][0]["location"]); + // Um for js we get "scriptId": "6" + // CheckLocation (bp_loc, line, col, ctx.scripts, pause_location ["callFrames"][0]["location"]); - // Check the object at the bp - var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); - var obj = GetAndAssertObjectWithName(frame_locals, "big"); - var obj_id = obj["value"]["objectId"].Value(); + // Check the object at the bp + var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); + var obj = GetAndAssertObjectWithName(frame_locals, "big"); + var obj_id = obj["value"]["objectId"].Value(); - var cfo_args = JObject.FromObject(new - { - functionDeclaration = "function () { return 5; }", - objectId = obj_id - }); + var cfo_args = JObject.FromObject(new + { + functionDeclaration = "function () { return 5; }", + objectId = obj_id + }); - // value of @returnByValue doesn't matter, as the returned value - // is a primitive - if (return_by_val) - cfo_args["returnByValue"] = return_by_val; + // value of @returnByValue doesn't matter, as the returned value + // is a primitive + if (return_by_val) + cfo_args["returnByValue"] = return_by_val; - // callFunctionOn - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); - await CheckValue(result.Value["result"], TNumber(5), "cfo-res"); + // callFunctionOn + result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + await CheckValue(result.Value["result"], TNumber(5), "cfo-res"); - cfo_args = JObject.FromObject(new - { - functionDeclaration = "function () { return 'test value'; }", - objectId = obj_id - }); + cfo_args = JObject.FromObject(new + { + functionDeclaration = "function () { return 'test value'; }", + objectId = obj_id + }); - // value of @returnByValue doesn't matter, as the returned value - // is a primitive - if (return_by_val) - cfo_args["returnByValue"] = return_by_val; + // value of @returnByValue doesn't matter, as the returned value + // is a primitive + if (return_by_val) + cfo_args["returnByValue"] = return_by_val; - // callFunctionOn - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); - await CheckValue(result.Value["result"], JObject.FromObject(new { type = "string", value = "test value" }), "cfo-res"); + // callFunctionOn + result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + await CheckValue(result.Value["result"], JObject.FromObject(new { type = "string", value = "test value" }), "cfo-res"); - cfo_args = JObject.FromObject(new - { - functionDeclaration = "function () { return null; }", - objectId = obj_id - }); + cfo_args = JObject.FromObject(new + { + functionDeclaration = "function () { return null; }", + objectId = obj_id + }); - // value of @returnByValue doesn't matter, as the returned value - // is a primitive - if (return_by_val) - cfo_args["returnByValue"] = return_by_val; + // value of @returnByValue doesn't matter, as the returned value + // is a primitive + if (return_by_val) + cfo_args["returnByValue"] = return_by_val; - // callFunctionOn - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); - await CheckValue(result.Value["result"], JObject.Parse("{ type: 'object', subtype: 'null', value: null }"), "cfo-res"); + // callFunctionOn + result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + await CheckValue(result.Value["result"], JObject.Parse("{ type: 'object', subtype: 'null', value: null }"), "cfo-res"); } public static TheoryData SilentErrorsTestData(bool? silent) => new TheoryData @@ -514,36 +514,36 @@ public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int l [MemberData(nameof(SilentErrorsTestData), true)] public async Task CFOWithSilentReturnsErrors(string eval_fn, string bp_loc, int line, int col, bool? silent) { - await SetBreakpoint(bp_loc, line, col); + await SetBreakpoint(bp_loc, line, col); - // callFunctionOn - var eval_expr = "window.setTimeout(function() { " + eval_fn + " }, 1);"; - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + // callFunctionOn + var eval_expr = "window.setTimeout(function() { " + eval_fn + " }, 1);"; + var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); + var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); - var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); - var obj = GetAndAssertObjectWithName(frame_locals, "big"); - var big_obj_id = obj["value"]["objectId"].Value(); - var error_msg = "#This is an error message#"; + var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); + var obj = GetAndAssertObjectWithName(frame_locals, "big"); + var big_obj_id = obj["value"]["objectId"].Value(); + var error_msg = "#This is an error message#"; - // Check the object at the bp - var cfo_args = JObject.FromObject(new - { - functionDeclaration = $"function () {{ throw Error ('{error_msg}'); }}", - objectId = big_obj_id - }); + // Check the object at the bp + var cfo_args = JObject.FromObject(new + { + functionDeclaration = $"function () {{ throw Error ('{error_msg}'); }}", + objectId = big_obj_id + }); - if (silent.HasValue) - cfo_args["silent"] = silent; + if (silent.HasValue) + cfo_args["silent"] = silent; - // callFunctionOn, Silent does not change the result, except that the error - // doesn't get reported, and the execution is NOT paused even with setPauseOnException=true - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); - Assert.False(result.IsOk, "result.IsOk"); - Assert.True(result.IsErr, "result.IsErr"); + // callFunctionOn, Silent does not change the result, except that the error + // doesn't get reported, and the execution is NOT paused even with setPauseOnException=true + result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + Assert.False(result.IsOk, "result.IsOk"); + Assert.True(result.IsErr, "result.IsErr"); - var hasErrorMessage = result.Error["exceptionDetails"]?["exception"]?["description"]?.Value()?.Contains(error_msg); - Assert.True((hasErrorMessage ?? false), "Exception message not found"); + var hasErrorMessage = result.Error["exceptionDetails"]?["exception"]?["description"]?.Value()?.Contains(error_msg); + Assert.True((hasErrorMessage ?? false), "Exception message not found"); } public static TheoryData, string, bool> GettersTestData(string local_name, bool use_cfo) => new TheoryData, string, bool> @@ -798,26 +798,26 @@ public async Task RunOnInvalidCfoId(string eval_fn, string bp_loc, int line, int [MemberData(nameof(NegativeTestsData), false)] public async Task RunOnInvalidThirdSegmentOfObjectId(string eval_fn, string bp_loc, int line, int col, bool use_cfo) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - await SetBreakpoint(bp_loc, line, col); + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + await SetBreakpoint(bp_loc, line, col); - // callFunctionOn - var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + // callFunctionOn + var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; + var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); + var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); - var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); - var ptd = GetAndAssertObjectWithName(frame_locals, "ptd"); - var ptd_id = ptd["value"]["objectId"].Value(); + var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); + var ptd = GetAndAssertObjectWithName(frame_locals, "ptd"); + var ptd_id = ptd["value"]["objectId"].Value(); - var cfo_args = JObject.FromObject(new - { - functionDeclaration = "function () { return 0; }", - objectId = ptd_id + "_invalid" - }); + var cfo_args = JObject.FromObject(new + { + functionDeclaration = "function () { return 0; }", + objectId = ptd_id + "_invalid" + }); - var res = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); - Assert.True(res.IsErr); + var res = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + Assert.True(res.IsErr); } [Theory] @@ -825,24 +825,24 @@ public async Task RunOnInvalidThirdSegmentOfObjectId(string eval_fn, string bp_l [MemberData(nameof(NegativeTestsData), true)] public async Task InvalidPropertyGetters(string eval_fn, string bp_loc, int line, int col, bool use_cfo) { - await SetBreakpoint(bp_loc, line, col); - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + await SetBreakpoint(bp_loc, line, col); + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - // callFunctionOn - var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; - await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr })); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + // callFunctionOn + var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; + await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr })); + var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); - var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); - var ptd = GetAndAssertObjectWithName(frame_locals, "ptd"); - var ptd_id = ptd["value"]["objectId"].Value(); + var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); + var ptd = GetAndAssertObjectWithName(frame_locals, "ptd"); + var ptd_id = ptd["value"]["objectId"].Value(); - var invalid_args = new object[] { "NonExistant", String.Empty, null, 12310 }; - foreach (var invalid_arg in invalid_args) - { - var getter_res = await InvokeGetter(JObject.FromObject(new { value = new { objectId = ptd_id } }), invalid_arg); - AssertEqual("undefined", getter_res.Value["result"]?["type"]?.ToString(), $"Expected to get undefined result for non-existant accessor - {invalid_arg}"); - } + var invalid_args = new object[] { "NonExistant", String.Empty, null, 12310 }; + foreach (var invalid_arg in invalid_args) + { + var getter_res = await InvokeGetter(JObject.FromObject(new { value = new { objectId = ptd_id } }), invalid_arg); + AssertEqual("undefined", getter_res.Value["result"]?["type"]?.ToString(), $"Expected to get undefined result for non-existant accessor - {invalid_arg}"); + } } [Theory] @@ -886,72 +886,72 @@ public async Task ReturnNullFromCFO(string eval_fn, string bp_loc, int line, int async Task RunCallFunctionOn(string eval_fn, string fn_decl, string local_name, string bp_loc, int line, int col, int res_array_len = -1, Func test_fn = null, bool returnByValue = false, JArray fn_args = null, bool roundtrip = false) { - await SetBreakpoint(bp_loc, line, col); + await SetBreakpoint(bp_loc, line, col); - // callFunctionOn - var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + // callFunctionOn + var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; + var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); + var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); - // Um for js we get "scriptId": "6" - // CheckLocation (bp_loc, line, col, ctx.scripts, pause_location ["callFrames"][0]["location"]); + // Um for js we get "scriptId": "6" + // CheckLocation (bp_loc, line, col, ctx.scripts, pause_location ["callFrames"][0]["location"]); - // Check the object at the bp - var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); - var obj = GetAndAssertObjectWithName(frame_locals, local_name); - var obj_id = obj["value"]["objectId"].Value(); + // Check the object at the bp + var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); + var obj = GetAndAssertObjectWithName(frame_locals, local_name); + var obj_id = obj["value"]["objectId"].Value(); - var cfo_args = JObject.FromObject(new - { - functionDeclaration = fn_decl, - objectId = obj_id - }); + var cfo_args = JObject.FromObject(new + { + functionDeclaration = fn_decl, + objectId = obj_id + }); - if (fn_args != null) - cfo_args["arguments"] = fn_args; + if (fn_args != null) + cfo_args["arguments"] = fn_args; - if (returnByValue) - cfo_args["returnByValue"] = returnByValue; + if (returnByValue) + cfo_args["returnByValue"] = returnByValue; - // callFunctionOn - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); - await CheckCFOResult(result); + // callFunctionOn + result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + await CheckCFOResult(result); - // If it wasn't `returnByValue`, then try to run a new function - // on that *returned* object - // This second function, just returns the object as-is, so the same - // test_fn is re-usable. - if (!returnByValue && roundtrip) + // If it wasn't `returnByValue`, then try to run a new function + // on that *returned* object + // This second function, just returns the object as-is, so the same + // test_fn is re-usable. + if (!returnByValue && roundtrip) + { + cfo_args = JObject.FromObject(new { - cfo_args = JObject.FromObject(new - { - functionDeclaration = "function () { return this; }", - objectId = result.Value["result"]["objectId"]?.Value() - }); + functionDeclaration = "function () { return this; }", + objectId = result.Value["result"]["objectId"]?.Value() + }); - if (fn_args != null) - cfo_args["arguments"] = fn_args; + if (fn_args != null) + cfo_args["arguments"] = fn_args; - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); - await CheckCFOResult(result); - } + await CheckCFOResult(result); + } - if (test_fn != null) - await test_fn(result); + if (test_fn != null) + await test_fn(result); - return; + return; - async Task CheckCFOResult(Result result) - { - if (returnByValue) - return; + async Task CheckCFOResult(Result result) + { + if (returnByValue) + return; - if (res_array_len < 0) - await CheckValue(result.Value["result"], TObject("Object"), $"cfo-res"); - else - await CheckValue(result.Value["result"], TArray("Array", res_array_len), $"cfo-res"); - } + if (res_array_len < 0) + await CheckValue(result.Value["result"], TObject("Object"), $"cfo-res"); + else + await CheckValue(result.Value["result"], TArray("Array", res_array_len), $"cfo-res"); + } } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs index b1ec0a27fa30fb..b63b0d516e89cd 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs @@ -18,30 +18,30 @@ public class DateTimeList : SingleSessionTestBase [InlineData("de-DE", "dddd, d. MMMM yyyy HH:mm:ss", "dddd, d. MMMM yyyy", "HH:mm:ss", "dd.MM.yyyy", "HH:mm")] public async Task CheckDateTimeLocale(string locale, string fdtp, string ldp, string ltp, string sdp, string stp) { - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-datetime-test.cs"; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-datetime-test.cs"; - await SetBreakpointInMethod("debugger-test", "DebuggerTests.DateTimeTest", "LocaleTest", 15); + await SetBreakpointInMethod("debugger-test", "DebuggerTests.DateTimeTest", "LocaleTest", 15); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.DateTimeTest:LocaleTest'," + - $"'{locale}'); }}, 1);", - debugger_test_loc, 25, 12, "LocaleTest", - locals_fn: async (locals) => - { - DateTimeFormatInfo dtfi = CultureInfo.GetCultureInfo(locale).DateTimeFormat; - CultureInfo.CurrentCulture = new CultureInfo(locale, false); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.DateTimeTest:LocaleTest'," + + $"'{locale}'); }}, 1);", + debugger_test_loc, 25, 12, "LocaleTest", + locals_fn: async (locals) => + { + DateTimeFormatInfo dtfi = CultureInfo.GetCultureInfo(locale).DateTimeFormat; + CultureInfo.CurrentCulture = new CultureInfo(locale, false); - await CheckProps(locals, new - { - fdtp = TString(fdtp), - ldp = TString(ldp), - ltp = TString(ltp), - sdp = TString(sdp), - stp = TString(stp), - dt = TDateTime(new DateTime(2020, 1, 2, 3, 4, 5)) - }, "locals", num_fields: 8); - } - ); + await CheckProps(locals, new + { + fdtp = TString(fdtp), + ldp = TString(ldp), + ltp = TString(ltp), + sdp = TString(sdp), + stp = TString(stp), + dt = TDateTime(new DateTime(2020, 1, 2, 3, 4, 5)) + }, "locals", num_fields: 8); + } + ); } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index fdc7eb4e708d67..c1b59fc41ffcf3 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -109,70 +109,70 @@ internal Dictionary SubscribeToScripts(Inspector insp) internal async Task CheckInspectLocalsAtBreakpointSite(string url_key, int line, int column, string function_name, string eval_expression, Action? test_fn = null, Func? wait_for_event_fn = null, bool use_cfo = false) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var bp = await SetBreakpoint(url_key, line, column); + var bp = await SetBreakpoint(url_key, line, column); - await EvaluateAndCheck( - eval_expression, url_key, line, column, - function_name, - wait_for_event_fn: async (pause_location) => - { + await EvaluateAndCheck( + eval_expression, url_key, line, column, + function_name, + wait_for_event_fn: async (pause_location) => + { //make sure we're on the right bp Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - var top_frame = pause_location!["callFrames"]?[0]; + var top_frame = pause_location!["callFrames"]?[0]; - var scope = top_frame!["scopeChain"]?[0]; - if (wait_for_event_fn != null) - await wait_for_event_fn(pause_location); - else - await Task.CompletedTask; - }, - locals_fn: (locals) => - { - if (test_fn != null) - test_fn(locals); - } - ); + var scope = top_frame!["scopeChain"]?[0]; + if (wait_for_event_fn != null) + await wait_for_event_fn(pause_location); + else + await Task.CompletedTask; + }, + locals_fn: (locals) => + { + if (test_fn != null) + test_fn(locals); + } + ); } // sets breakpoint by method name and line offset internal async Task CheckInspectLocalsAtBreakpointSite(string type, string method, int line_offset, string bp_function_name, string eval_expression, Action? locals_fn = null, Func? wait_for_event_fn = null, bool use_cfo = false, string assembly = "debugger-test.dll", int col = 0) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var bp = await SetBreakpointInMethod(assembly, type, method, line_offset, col); + var bp = await SetBreakpointInMethod(assembly, type, method, line_offset, col); - var args = JObject.FromObject(new { expression = eval_expression }); - var res = await ctx.cli.SendCommand("Runtime.evaluate", args, ctx.token); - if (!res.IsOk) - { - Console.WriteLine($"Failed to run command {method} with args: {args?.ToString()}\nresult: {res.Error.ToString()}"); - Assert.True(false, $"SendCommand for {method} failed with {res.Error.ToString()}"); - } + var args = JObject.FromObject(new { expression = eval_expression }); + var res = await ctx.cli.SendCommand("Runtime.evaluate", args, ctx.token); + if (!res.IsOk) + { + Console.WriteLine($"Failed to run command {method} with args: {args?.ToString()}\nresult: {res.Error.ToString()}"); + Assert.True(false, $"SendCommand for {method} failed with {res.Error.ToString()}"); + } - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); - if (bp_function_name != null) - Assert.Equal(bp_function_name, pause_location["callFrames"]?[0]?["functionName"]?.Value()); + if (bp_function_name != null) + Assert.Equal(bp_function_name, pause_location["callFrames"]?[0]?["functionName"]?.Value()); - Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); + Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - var top_frame = pause_location!["callFrames"]?[0]; + var top_frame = pause_location!["callFrames"]?[0]; - var scope = top_frame?["scopeChain"]?[0]; + var scope = top_frame?["scopeChain"]?[0]; - if (wait_for_event_fn != null) - await wait_for_event_fn(pause_location); + if (wait_for_event_fn != null) + await wait_for_event_fn(pause_location); - if (locals_fn != null) - { - var locals = await GetProperties(pause_location?["callFrames"]?[0]?["callFrameId"]?.Value()); - locals_fn(locals); - } + if (locals_fn != null) + { + var locals = await GetProperties(pause_location?["callFrames"]?[0]?["callFrameId"]?.Value()); + locals_fn(locals); + } } internal void CheckLocation(string script_loc, int line, int column, Dictionary scripts, JToken location) @@ -236,13 +236,13 @@ internal async Task CheckDateTime(JToken value, DateTime expected, string label await CheckDateTimeValue(value, expected, label); } - internal async Task CheckDateTime(JToken locals, string name, DateTime expected, string label="") + internal async Task CheckDateTime(JToken locals, string name, DateTime expected, string label = "") { var obj = GetAndAssertObjectWithName(locals, name, label); await CheckDateTimeValue(obj["value"], expected, label); } - internal async Task CheckDateTimeValue(JToken value, DateTime expected, string label="") + internal async Task CheckDateTimeValue(JToken value, DateTime expected, string label = "") { await CheckDateTimeMembers(value, expected, label); @@ -251,7 +251,7 @@ internal async Task CheckDateTimeValue(JToken value, DateTime expected, string l // FIXME: check some float properties too - async Task CheckDateTimeMembers(JToken v, DateTime exp_dt, string label="") + async Task CheckDateTimeMembers(JToken v, DateTime exp_dt, string label = "") { AssertEqual("System.DateTime", v["className"]?.Value(), $"{label}#className"); AssertEqual(exp_dt.ToString(), v["description"]?.Value(), $"{label}#description"); @@ -300,7 +300,7 @@ internal void CheckArray(JToken locals, string name, string class_name, int leng GetAndAssertObjectWithName(locals, name)["value"], TArray(class_name, length), name).Wait(); - internal JToken GetAndAssertObjectWithName(JToken obj, string name, string label="") + internal JToken GetAndAssertObjectWithName(JToken obj, string name, string label = "") { var l = obj.FirstOrDefault(jt => jt["name"]?.Value() == name); if (l == null) @@ -398,7 +398,7 @@ internal async Task SendCommandAndCheck(JObject args, string method, st AssertEqual(function_name, wait_res["callFrames"]?[0]?["functionName"]?.Value(), top_frame?.ToString()); } - Console.WriteLine (top_frame); + Console.WriteLine(top_frame); if (script_loc != null && line >= 0) CheckLocation(script_loc, line, column, ctx.scripts, top_frame["location"]); @@ -640,7 +640,7 @@ internal async Task CheckValue(JToken actual_val, JToken exp_val, string label) } catch { - Console.WriteLine ($"Expected: {exp_val}. Actual: {actual_val}"); + Console.WriteLine($"Expected: {exp_val}. Actual: {actual_val}"); throw; } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 00a5abfc457e4e..bdd5e0e9531f90 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -409,19 +409,19 @@ public async Task JSEvaluate() var line = 76; var col = 1; - await SetBreakpoint(bp_loc, line, col); + await SetBreakpoint(bp_loc, line, col); - var eval_expr = "window.setTimeout(function() { eval_call_on_frame_test (); }, 1)"; - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var eval_expr = "window.setTimeout(function() { eval_call_on_frame_test (); }, 1)"; + var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); + var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); - var id = pause_location["callFrames"][0]["callFrameId"].Value(); + var id = pause_location["callFrames"][0]["callFrameId"].Value(); - await EvaluateOnCallFrameFail(id, - ("me.foo", null), - ("obj.foo.bar", null)); + await EvaluateOnCallFrameFail(id, + ("me.foo", null), + ("obj.foo.bar", null)); - await EvaluateOnCallFrame(id, "obj.foo", expect_ok: true); + await EvaluateOnCallFrame(id, "obj.foo", expect_ok: true); } [Fact] diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs index a07585aec71acd..77a3282ed1d1a4 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs @@ -17,82 +17,82 @@ public class ExceptionTests : SingleSessionTestBase public async Task ExceptionTestAll() { string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs"; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs"; - await SetPauseOnException("all"); + await SetPauseOnException("all"); - var eval_expr = "window.setTimeout(function() { invoke_static_method (" + - $"'{entry_method_name}'" + - "); }, 1);"; + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + + $"'{entry_method_name}'" + + "); }, 1);"; - var pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, null); - //stop in the managed caught exception - pause_location = await WaitForManagedException(pause_location); + var pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, null); + //stop in the managed caught exception + pause_location = await WaitForManagedException(pause_location); - AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause0"); + AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause0"); - await CheckValue(pause_location["data"], JObject.FromObject(new - { - type = "object", - subtype = "error", - className = "DebuggerTests.CustomException", - uncaught = false - }), "exception0.data"); - - var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); - CheckString(exception_members, "message", "not implemented caught"); + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "DebuggerTests.CustomException", + uncaught = false + }), "exception0.data"); - pause_location = await WaitForManagedException(null); - AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause1"); + var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "not implemented caught"); - //stop in the uncaught exception - CheckLocation(debugger_test_loc, 28, 16, scripts, pause_location["callFrames"][0]["location"]); + pause_location = await WaitForManagedException(null); + AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause1"); - await CheckValue(pause_location["data"], JObject.FromObject(new - { - type = "object", - subtype = "error", - className = "DebuggerTests.CustomException", - uncaught = true - }), "exception1.data"); + //stop in the uncaught exception + CheckLocation(debugger_test_loc, 28, 16, scripts, pause_location["callFrames"][0]["location"]); - exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); - CheckString(exception_members, "message", "not implemented uncaught"); + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "DebuggerTests.CustomException", + uncaught = true + }), "exception1.data"); + + exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "not implemented uncaught"); } [Fact] public async Task JSExceptionTestAll() { - await SetPauseOnException("all"); + await SetPauseOnException("all"); - var eval_expr = "window.setTimeout(function () { exceptions_test (); }, 1)"; - var pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, "exception_caught_test", null, null); - - Assert.Equal("exception", pause_location["reason"]); - await CheckValue(pause_location["data"], JObject.FromObject(new - { - type = "object", - subtype = "error", - className = "TypeError", - uncaught = false - }), "exception0.data"); + var eval_expr = "window.setTimeout(function () { exceptions_test (); }, 1)"; + var pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, "exception_caught_test", null, null); - var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); - CheckString(exception_members, "message", "exception caught"); + Assert.Equal("exception", pause_location["reason"]); + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "TypeError", + uncaught = false + }), "exception0.data"); - pause_location = await SendCommandAndCheck(null, "Debugger.resume", null, 0, 0, "exception_uncaught_test"); + var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "exception caught"); - Assert.Equal("exception", pause_location["reason"]); - await CheckValue(pause_location["data"], JObject.FromObject(new - { - type = "object", - subtype = "error", - className = "RangeError", - uncaught = true - }), "exception1.data"); + pause_location = await SendCommandAndCheck(null, "Debugger.resume", null, 0, 0, "exception_uncaught_test"); - exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); - CheckString(exception_members, "message", "exception uncaught"); + Assert.Equal("exception", pause_location["reason"]); + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "RangeError", + uncaught = true + }), "exception1.data"); + + exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "exception uncaught"); } // FIXME? BUG? We seem to get the stack trace for Runtime.exceptionThrown at `call_method`, @@ -102,67 +102,67 @@ public async Task ExceptionTestNone() { //Collect events string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; - await SetPauseOnException("none"); + await SetPauseOnException("none"); - var eval_expr = "window.setTimeout(function() { invoke_static_method (" + - $"'{entry_method_name}'" + - "); }, 1);"; + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + + $"'{entry_method_name}'" + + "); }, 1);"; - try - { - await EvaluateAndCheck(eval_expr, null, 0, 0, "", null, null); - } - catch (ArgumentException ae) - { - var eo = JObject.Parse(ae.Message); + try + { + await EvaluateAndCheck(eval_expr, null, 0, 0, "", null, null); + } + catch (ArgumentException ae) + { + var eo = JObject.Parse(ae.Message); - // AssertEqual (line, eo ["exceptionDetails"]?["lineNumber"]?.Value (), "lineNumber"); - AssertEqual("Uncaught", eo["exceptionDetails"]?["text"]?.Value(), "text"); + // AssertEqual (line, eo ["exceptionDetails"]?["lineNumber"]?.Value (), "lineNumber"); + AssertEqual("Uncaught", eo["exceptionDetails"]?["text"]?.Value(), "text"); - await CheckValue(eo["exceptionDetails"]?["exception"], JObject.FromObject(new - { - type = "object", - subtype = "error", - className = "Error" // BUG?: "DebuggerTests.CustomException" - }), "exception"); + await CheckValue(eo["exceptionDetails"]?["exception"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "Error" // BUG?: "DebuggerTests.CustomException" + }), "exception"); - return; - } + return; + } - Assert.True(false, "Expected to get an ArgumentException from the uncaught user exception"); + Assert.True(false, "Expected to get an ArgumentException from the uncaught user exception"); } [Fact] public async Task JSExceptionTestNone() { - await SetPauseOnException("none"); + await SetPauseOnException("none"); - var eval_expr = "window.setTimeout(function () { exceptions_test (); }, 1)"; + var eval_expr = "window.setTimeout(function () { exceptions_test (); }, 1)"; - int line = 44; - try - { - await EvaluateAndCheck(eval_expr, null, 0, 0, "", null, null); - } - catch (ArgumentException ae) - { - Console.WriteLine($"{ae}"); - var eo = JObject.Parse(ae.Message); + int line = 44; + try + { + await EvaluateAndCheck(eval_expr, null, 0, 0, "", null, null); + } + catch (ArgumentException ae) + { + Console.WriteLine($"{ae}"); + var eo = JObject.Parse(ae.Message); - AssertEqual(line, eo["exceptionDetails"]?["lineNumber"]?.Value(), "lineNumber"); - AssertEqual("Uncaught", eo["exceptionDetails"]?["text"]?.Value(), "text"); + AssertEqual(line, eo["exceptionDetails"]?["lineNumber"]?.Value(), "lineNumber"); + AssertEqual("Uncaught", eo["exceptionDetails"]?["text"]?.Value(), "text"); - await CheckValue(eo["exceptionDetails"]?["exception"], JObject.FromObject(new - { - type = "object", - subtype = "error", - className = "RangeError" - }), "exception"); + await CheckValue(eo["exceptionDetails"]?["exception"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "RangeError" + }), "exception"); - return; - } + return; + } - Assert.True(false, "Expected to get an ArgumentException from the uncaught user exception"); + Assert.True(false, "Expected to get an ArgumentException from the uncaught user exception"); } [Theory] @@ -173,22 +173,22 @@ await CheckValue(eo["exceptionDetails"]?["exception"], JObject.FromObject(new public async Task ExceptionTestUncaught(string eval_fn, string loc, int line, int col, string fn_name, string exception_type, string exception_message) { - await SetPauseOnException("uncaught"); + await SetPauseOnException("uncaught"); - var eval_expr = $"window.setTimeout({eval_fn}, 1);"; - var pause_location = await EvaluateAndCheck(eval_expr, loc, line, col, fn_name); + var eval_expr = $"window.setTimeout({eval_fn}, 1);"; + var pause_location = await EvaluateAndCheck(eval_expr, loc, line, col, fn_name); - Assert.Equal("exception", pause_location["reason"]); - await CheckValue(pause_location["data"], JObject.FromObject(new - { - type = "object", - subtype = "error", - className = exception_type, - uncaught = true - }), "exception.data"); - - var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); - CheckString(exception_members, "message", exception_message); + Assert.Equal("exception", pause_location["reason"]); + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = exception_type, + uncaught = true + }), "exception.data"); + + var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", exception_message); } async Task WaitForManagedException(JObject pause_location) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs index dc9742103b0b84..5133a8eba7675b 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs @@ -282,40 +282,40 @@ public async Task MembersForLocalNestedStruct(bool? own_properties, bool? access // [MemberData(nameof(JSGetPropertiesTestData), parameters: false)] public async Task GetPropertiesTestJSAndManaged(bool test_js, bool? own_properties, bool? accessors_only, string[] expected_names) { - string eval_expr; - if (test_js) - { - await SetBreakpoint("/other.js", 93, 1); - eval_expr = "window.setTimeout(function() { get_properties_test (); }, 1)"; - } - else - { - await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.GetPropertiesTests.DerivedClassForJSTest", "run", 2); - eval_expr = "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.GetPropertiesTests.DerivedClassForJSTest:run'); }, 1)"; - } + string eval_expr; + if (test_js) + { + await SetBreakpoint("/other.js", 93, 1); + eval_expr = "window.setTimeout(function() { get_properties_test (); }, 1)"; + } + else + { + await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.GetPropertiesTests.DerivedClassForJSTest", "run", 2); + eval_expr = "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.GetPropertiesTests.DerivedClassForJSTest:run'); }, 1)"; + } - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); + var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); - var id = pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value(); + var id = pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value(); - var frame_locals = await GetProperties(id); - var obj = GetAndAssertObjectWithName(frame_locals, "obj"); - var obj_props = await GetProperties(obj["value"]?["objectId"]?.Value(), - own_properties: own_properties, accessors_only: accessors_only); + var frame_locals = await GetProperties(id); + var obj = GetAndAssertObjectWithName(frame_locals, "obj"); + var obj_props = await GetProperties(obj["value"]?["objectId"]?.Value(), + own_properties: own_properties, accessors_only: accessors_only); - IEnumerable filtered_props; - if (test_js) - { - filtered_props = obj_props.Children().Where(jt => jt["enumerable"]?.Value() == true); - } - else - { - // we don't set `enumerable` right now - filtered_props = obj_props.Children().Where(jt => true); - } + IEnumerable filtered_props; + if (test_js) + { + filtered_props = obj_props.Children().Where(jt => jt["enumerable"]?.Value() == true); + } + else + { + // we don't set `enumerable` right now + filtered_props = obj_props.Children().Where(jt => true); + } - var expected_props = new Dictionary() + var expected_props = new Dictionary() { // own {"owner_name", (TString("foo"), true)}, @@ -327,12 +327,12 @@ public async Task GetPropertiesTestJSAndManaged(bool test_js, bool? own_properti {"available", (TGetter("available"), false)}, }; - await CheckExpectedProperties( - expected_names, - name => filtered_props.Where(jt => jt["name"]?.Value() == name).SingleOrDefault(), - expected_props); + await CheckExpectedProperties( + expected_names, + name => filtered_props.Where(jt => jt["name"]?.Value() == name).SingleOrDefault(), + expected_props); - AssertEqual(expected_names.Length, filtered_props.Count(), $"expected number of properties"); + AssertEqual(expected_names.Length, filtered_props.Count(), $"expected number of properties"); } private async Task CheckExpectedProperties(string[] expected_names, Func get_actual_prop, Dictionary all_props) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs index f6079dec950aed..7c6bfea518c2c6 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs @@ -16,111 +16,111 @@ public class MonoJsTests : SingleSessionTestBase [Fact] public async Task FixupNameValueObjectsWithMissingParts() { - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); + var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); - var names = new JObject[] - { + var names = new JObject[] + { JObject.FromObject(new { name = "Abc" }), JObject.FromObject(new { name = "Def" }), JObject.FromObject(new { name = "Xyz" }) - }; + }; - var values = new JObject[] - { + var values = new JObject[] + { JObject.FromObject(new { value = TObject("testclass") }), JObject.FromObject(new { value = TString("test string") }), - }; + }; - var getters = new JObject[] - { + var getters = new JObject[] + { GetterRes("xyz"), GetterRes("unattached") - }; + }; - var list = new[] { names[0], names[1], values[0], names[2], getters[0], getters[1] }; - var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression = $"MONO._fixup_name_value_objects({JsonConvert.SerializeObject(list)})", returnByValue = true }), ctx.token); - Assert.True(res.IsOk); + var list = new[] { names[0], names[1], values[0], names[2], getters[0], getters[1] }; + var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression = $"MONO._fixup_name_value_objects({JsonConvert.SerializeObject(list)})", returnByValue = true }), ctx.token); + Assert.True(res.IsOk); - await CheckProps(res.Value["result"]["value"], new - { - Abc = TSymbol(""), - Def = TObject("testclass"), - Xyz = TGetter("xyz") - }, "#1", num_fields: 4); + await CheckProps(res.Value["result"]["value"], new + { + Abc = TSymbol(""), + Def = TObject("testclass"), + Xyz = TGetter("xyz") + }, "#1", num_fields: 4); - JObject.DeepEquals(getters[1], res.Value["result"]["value"].Values().ToArray()[3]); + JObject.DeepEquals(getters[1], res.Value["result"]["value"].Values().ToArray()[3]); - static JObject GetterRes(string name) => JObject.FromObject(new + static JObject GetterRes(string name) => JObject.FromObject(new + { + get = new { - get = new - { - className = "Function", - description = $"get {name} () {{}}", - type = "function" - } - }); + className = "Function", + description = $"get {name} () {{}}", + type = "function" + } + }); } [Fact] public async Task GetParamsAndLocalsWithInvalidIndices() { - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method('[debugger-test] Math:IntAdd', 1, 2); })", - null, -1, -1, "IntAdd"); + var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method('[debugger-test] Math:IntAdd', 1, 2); })", + null, -1, -1, "IntAdd"); - var scope_id = pause_location["callFrames"][0]["callFrameId"].Value(); - var scope = int.Parse(scope_id.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries)[2]); + var scope_id = pause_location["callFrames"][0]["callFrameId"].Value(); + var scope = int.Parse(scope_id.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries)[2]); - var var_ids = new[] - { + var var_ids = new[] + { new { index = 0, name = "one" }, new { index = -12, name = "bad0" }, new { index = 1231, name = "bad1" } }; - var expression = $"MONO.mono_wasm_get_variables({scope}, {JsonConvert.SerializeObject(var_ids)})"; + var expression = $"MONO.mono_wasm_get_variables({scope}, {JsonConvert.SerializeObject(var_ids)})"; - var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), ctx.token); - Assert.True(res.IsOk); + var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), ctx.token); + Assert.True(res.IsOk); - await CheckProps(res.Value["result"]?["value"], new - { - one = TNumber(3), - bad0 = TSymbol(""), - bad1 = TSymbol("") - }, "results"); + await CheckProps(res.Value["result"]?["value"], new + { + one = TNumber(3), + bad0 = TSymbol(""), + bad1 = TSymbol("") + }, "results"); } [Fact] public async Task InvalidScopeId() { - var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method('[debugger-test] Math:IntAdd', 1, 2); })", - null, -1, -1, "IntAdd"); + var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method('[debugger-test] Math:IntAdd', 1, 2); })", + null, -1, -1, "IntAdd"); - var var_ids = new[] - { + var var_ids = new[] + { new { index = 0, name = "one" }, }; - var scope_id = "-12"; - var expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; - var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), ctx.token); - Assert.False(res.IsOk); + var scope_id = "-12"; + var expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; + var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), ctx.token); + Assert.False(res.IsOk); - scope_id = "30000"; - expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; - res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), ctx.token); - Assert.False(res.IsOk); + scope_id = "30000"; + expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; + res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), ctx.token); + Assert.False(res.IsOk); } [Fact] public async Task BadRaiseDebugEventsTest() { - var bad_expressions = new[] - { + var bad_expressions = new[] + { "MONO.mono_wasm_raise_debug_event('')", "MONO.mono_wasm_raise_debug_event(undefined)", "MONO.mono_wasm_raise_debug_event({})", @@ -129,16 +129,16 @@ public async Task BadRaiseDebugEventsTest() "MONO.mono_wasm_raise_debug_event({eventName:'foo'}, 12)" }; - foreach (var expression in bad_expressions) - { - var res = await ctx.cli.SendCommand($"Runtime.evaluate", - JObject.FromObject(new - { - expression, - returnByValue = true - }), ctx.token); - Assert.False(res.IsOk, $"Expected to fail for {expression}"); - } + foreach (var expression in bad_expressions) + { + var res = await ctx.cli.SendCommand($"Runtime.evaluate", + JObject.FromObject(new + { + expression, + returnByValue = true + }), ctx.token); + Assert.False(res.IsOk, $"Expected to fail for {expression}"); + } } [Theory] @@ -147,30 +147,30 @@ public async Task BadRaiseDebugEventsTest() [InlineData(null)] public async Task RaiseDebugEventTraceTest(bool? trace) { - var tcs = new TaskCompletionSource(); - insp.On("Runtime.consoleAPICalled", async (args, token) => + var tcs = new TaskCompletionSource(); + insp.On("Runtime.consoleAPICalled", async (args, token) => + { + if (args?["type"]?.Value() == "debug" && + args?["args"]?.Type == JTokenType.Array && + args?["args"]?[0]?["value"]?.Value()?.StartsWith("mono_wasm_debug_event_raised:") == true) { - if (args?["type"]?.Value() == "debug" && - args?["args"]?.Type == JTokenType.Array && - args?["args"]?[0]?["value"]?.Value()?.StartsWith("mono_wasm_debug_event_raised:") == true) - { - tcs.SetResult(true); - } + tcs.SetResult(true); + } - await Task.CompletedTask; - }); + await Task.CompletedTask; + }); - var trace_str = trace.HasValue ? $"trace: {trace.ToString().ToLower()}" : String.Empty; - var expression = $"MONO.mono_wasm_raise_debug_event({{ eventName:'qwe' }}, {{ {trace_str} }})"; - var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); - Assert.True(res.IsOk, $"Expected to pass for {expression}"); + var trace_str = trace.HasValue ? $"trace: {trace.ToString().ToLower()}" : String.Empty; + var expression = $"MONO.mono_wasm_raise_debug_event({{ eventName:'qwe' }}, {{ {trace_str} }})"; + var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); + Assert.True(res.IsOk, $"Expected to pass for {expression}"); - var t = await Task.WhenAny(tcs.Task, Task.Delay(2000)); + var t = await Task.WhenAny(tcs.Task, Task.Delay(2000)); - if (trace == true) - Assert.True(tcs.Task == t, "Timed out waiting for the event to be logged"); - else - Assert.False(tcs.Task == t, "Event should not have been logged"); + if (trace == true) + Assert.True(tcs.Task == t, "Timed out waiting for the event to be logged"); + else + Assert.False(tcs.Task == t, "Event should not have been logged"); } [Theory] @@ -232,34 +232,34 @@ async Task AssemblyLoadedEventTest(string asm_name, string asm_path, string pdb_ await Task.CompletedTask; }); - byte[] bytes = File.ReadAllBytes(asm_path); - string asm_base64 = Convert.ToBase64String(bytes); + byte[] bytes = File.ReadAllBytes(asm_path); + string asm_base64 = Convert.ToBase64String(bytes); - string pdb_base64 = String.Empty; - if (pdb_path != null) - { - bytes = File.ReadAllBytes(pdb_path); - pdb_base64 = Convert.ToBase64String(bytes); - } + string pdb_base64 = String.Empty; + if (pdb_path != null) + { + bytes = File.ReadAllBytes(pdb_path); + pdb_base64 = Convert.ToBase64String(bytes); + } - var expression = $@"MONO.mono_wasm_raise_debug_event({{ + var expression = $@"MONO.mono_wasm_raise_debug_event({{ eventName: 'AssemblyLoaded', assembly_name: '{asm_name}', assembly_b64: '{asm_base64}', pdb_b64: '{pdb_base64}' }});"; - var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); - Assert.True(res.IsOk, $"Expected to pass for {expression}"); + var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); + Assert.True(res.IsOk, $"Expected to pass for {expression}"); - res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); - Assert.True(res.IsOk, $"Expected to pass for {expression}"); + res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); + Assert.True(res.IsOk, $"Expected to pass for {expression}"); - var t = await Task.WhenAny(tcs.Task, Task.Delay(2000)); - if (t.IsFaulted) - throw t.Exception; + var t = await Task.WhenAny(tcs.Task, Task.Delay(2000)); + if (t.IsFaulted) + throw t.Exception; - Assert.True(event_count <= expected_count, $"number of scriptParsed events received. Expected: {expected_count}, Actual: {event_count}"); + Assert.True(event_count <= expected_count, $"number of scriptParsed events received. Expected: {expected_count}, Actual: {event_count}"); } } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index e51191fbe10ca0..5f833a6516e2b2 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -28,74 +28,74 @@ public void CheckThatAllSourcesAreSent() [Fact] public async Task CreateGoodBreakpoint() { - var bp1_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); + var bp1_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); - Assert.EndsWith("debugger-test.cs", bp1_res.Value["breakpointId"].ToString()); - Assert.Equal(1, bp1_res.Value["locations"]?.Value()?.Count); + Assert.EndsWith("debugger-test.cs", bp1_res.Value["breakpointId"].ToString()); + Assert.Equal(1, bp1_res.Value["locations"]?.Value()?.Count); - var loc = bp1_res.Value["locations"]?.Value()[0]; + var loc = bp1_res.Value["locations"]?.Value()[0]; - Assert.NotNull(loc["scriptId"]); - Assert.Equal("dotnet://debugger-test.dll/debugger-test.cs", scripts[loc["scriptId"]?.Value()]); - Assert.Equal(10, loc["lineNumber"]); - Assert.Equal(8, loc["columnNumber"]); + Assert.NotNull(loc["scriptId"]); + Assert.Equal("dotnet://debugger-test.dll/debugger-test.cs", scripts[loc["scriptId"]?.Value()]); + Assert.Equal(10, loc["lineNumber"]); + Assert.Equal(8, loc["columnNumber"]); } [Fact] public async Task CreateJSBreakpoint() { // Test that js breakpoints get set correctly - // 13 24 - // 13 31 - var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 24); + // 13 24 + // 13 31 + var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 24); - Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString()); - Assert.Equal(1, bp1_res.Value["locations"]?.Value()?.Count); + Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString()); + Assert.Equal(1, bp1_res.Value["locations"]?.Value()?.Count); - var loc = bp1_res.Value["locations"]?.Value()[0]; + var loc = bp1_res.Value["locations"]?.Value()[0]; - Assert.NotNull(loc["scriptId"]); - Assert.Equal(13, loc["lineNumber"]); - Assert.Equal(24, loc["columnNumber"]); + Assert.NotNull(loc["scriptId"]); + Assert.Equal(13, loc["lineNumber"]); + Assert.Equal(24, loc["columnNumber"]); - var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 31); + var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 31); - Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString()); - Assert.Equal(1, bp2_res.Value["locations"]?.Value()?.Count); + Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString()); + Assert.Equal(1, bp2_res.Value["locations"]?.Value()?.Count); - var loc2 = bp2_res.Value["locations"]?.Value()[0]; + var loc2 = bp2_res.Value["locations"]?.Value()[0]; - Assert.NotNull(loc2["scriptId"]); - Assert.Equal(13, loc2["lineNumber"]); - Assert.Equal(31, loc2["columnNumber"]); + Assert.NotNull(loc2["scriptId"]); + Assert.Equal(13, loc2["lineNumber"]); + Assert.Equal(31, loc2["columnNumber"]); } [Fact] public async Task CreateJS0Breakpoint() { - // 13 24 - // 13 31 - var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 0); + // 13 24 + // 13 31 + var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 0); - Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString()); - Assert.Equal(1, bp1_res.Value["locations"]?.Value()?.Count); + Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString()); + Assert.Equal(1, bp1_res.Value["locations"]?.Value()?.Count); - var loc = bp1_res.Value["locations"]?.Value()[0]; + var loc = bp1_res.Value["locations"]?.Value()[0]; - Assert.NotNull(loc["scriptId"]); - Assert.Equal(13, loc["lineNumber"]); - Assert.Equal(24, loc["columnNumber"]); + Assert.NotNull(loc["scriptId"]); + Assert.Equal(13, loc["lineNumber"]); + Assert.Equal(24, loc["columnNumber"]); - var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 31); + var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 31); - Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString()); - Assert.Equal(1, bp2_res.Value["locations"]?.Value()?.Count); + Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString()); + Assert.Equal(1, bp2_res.Value["locations"]?.Value()?.Count); - var loc2 = bp2_res.Value["locations"]?.Value()[0]; + var loc2 = bp2_res.Value["locations"]?.Value()[0]; - Assert.NotNull(loc2["scriptId"]); - Assert.Equal(13, loc2["lineNumber"]); - Assert.Equal(31, loc2["columnNumber"]); + Assert.NotNull(loc2["scriptId"]); + Assert.Equal(13, loc2["lineNumber"]); + Assert.Equal(31, loc2["columnNumber"]); } [Theory] @@ -103,109 +103,109 @@ public async Task CreateJS0Breakpoint() [InlineData(50)] public async Task CheckMultipleBreakpointsOnSameLine(int col) { - var bp1_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-array-test.cs", 219, col); - Assert.EndsWith("debugger-array-test.cs", bp1_res.Value["breakpointId"].ToString()); - Assert.Equal(1, bp1_res.Value["locations"]?.Value()?.Count); + var bp1_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-array-test.cs", 219, col); + Assert.EndsWith("debugger-array-test.cs", bp1_res.Value["breakpointId"].ToString()); + Assert.Equal(1, bp1_res.Value["locations"]?.Value()?.Count); - var loc = bp1_res.Value["locations"]?.Value()[0]; + var loc = bp1_res.Value["locations"]?.Value()[0]; - CheckLocation("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 50, scripts, loc); + CheckLocation("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 50, scripts, loc); - var bp2_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 55); - Assert.EndsWith("debugger-array-test.cs", bp2_res.Value["breakpointId"].ToString()); - Assert.Equal(1, bp2_res.Value["locations"]?.Value()?.Count); + var bp2_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 55); + Assert.EndsWith("debugger-array-test.cs", bp2_res.Value["breakpointId"].ToString()); + Assert.Equal(1, bp2_res.Value["locations"]?.Value()?.Count); - var loc2 = bp2_res.Value["locations"]?.Value()[0]; + var loc2 = bp2_res.Value["locations"]?.Value()[0]; - CheckLocation("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 55, scripts, loc2); + CheckLocation("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 55, scripts, loc2); } [Fact] public async Task CreateBadBreakpoint() { - var bp1_req = JObject.FromObject(new - { - lineNumber = 8, - columnNumber = 2, - url = "dotnet://debugger-test.dll/this-file-doesnt-exist.cs", - }); + var bp1_req = JObject.FromObject(new + { + lineNumber = 8, + columnNumber = 2, + url = "dotnet://debugger-test.dll/this-file-doesnt-exist.cs", + }); - var bp1_res = await ctx.cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, ctx.token); + var bp1_res = await ctx.cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, ctx.token); - Assert.True(bp1_res.IsOk); - Assert.Empty(bp1_res.Value["locations"].Values()); - //Assert.Equal ((int)MonoErrorCodes.BpNotFound, bp1_res.Error ["code"]?.Value ()); + Assert.True(bp1_res.IsOk); + Assert.Empty(bp1_res.Value["locations"].Values()); + //Assert.Equal ((int)MonoErrorCodes.BpNotFound, bp1_res.Error ["code"]?.Value ()); } [Fact] public async Task CreateGoodBreakpointAndHit() { - var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); + var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); + + var eval_req = JObject.FromObject(new + { + expression = "window.setTimeout(function() { invoke_add(); }, 1);", + }); - var eval_req = JObject.FromObject(new + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_add(); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, + "IntAdd", + wait_for_event_fn: (pause_location) => { - expression = "window.setTimeout(function() { invoke_add(); }, 1);", - }); - - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_add(); }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 10, 8, - "IntAdd", - wait_for_event_fn: (pause_location) => - { - Assert.Equal("other", pause_location["reason"]?.Value()); - Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); + Assert.Equal("other", pause_location["reason"]?.Value()); + Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - var top_frame = pause_location["callFrames"][0]; - Assert.Equal("IntAdd", top_frame["functionName"].Value()); - Assert.Contains("debugger-test.cs", top_frame["url"].Value()); + var top_frame = pause_location["callFrames"][0]; + Assert.Equal("IntAdd", top_frame["functionName"].Value()); + Assert.Contains("debugger-test.cs", top_frame["url"].Value()); - CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, top_frame["functionLocation"]); + CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, top_frame["functionLocation"]); - //now check the scope - var scope = top_frame["scopeChain"][0]; - Assert.Equal("local", scope["type"]); - Assert.Equal("IntAdd", scope["name"]); + //now check the scope + var scope = top_frame["scopeChain"][0]; + Assert.Equal("local", scope["type"]); + Assert.Equal("IntAdd", scope["name"]); - Assert.Equal("object", scope["object"]["type"]); - CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, scope["startLocation"]); - CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 14, 4, scripts, scope["endLocation"]); - return Task.CompletedTask; - } - ); + Assert.Equal("object", scope["object"]["type"]); + CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, scope["startLocation"]); + CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 14, 4, scripts, scope["endLocation"]); + return Task.CompletedTask; + } + ); } [Fact] public async Task ExceptionThrownInJS() { - var eval_req = JObject.FromObject(new - { - expression = "invoke_bad_js_test();" - }); + var eval_req = JObject.FromObject(new + { + expression = "invoke_bad_js_test();" + }); - var eval_res = await ctx.cli.SendCommand("Runtime.evaluate", eval_req, ctx.token); - Assert.True(eval_res.IsErr); - Assert.Equal("Uncaught", eval_res.Error["exceptionDetails"]?["text"]?.Value()); + var eval_res = await ctx.cli.SendCommand("Runtime.evaluate", eval_req, ctx.token); + Assert.True(eval_res.IsErr); + Assert.Equal("Uncaught", eval_res.Error["exceptionDetails"]?["text"]?.Value()); } [Fact] public async Task ExceptionThrownInJSOutOfBand() { - await SetBreakpoint("/debugger-driver.html", 27, 2); + await SetBreakpoint("/debugger-driver.html", 27, 2); - var eval_req = JObject.FromObject(new - { - expression = "window.setTimeout(function() { invoke_bad_js_test(); }, 1);", - }); + var eval_req = JObject.FromObject(new + { + expression = "window.setTimeout(function() { invoke_bad_js_test(); }, 1);", + }); - var task = insp.WaitFor("Runtime.exceptionThrown"); - var eval_res = await ctx.cli.SendCommand("Runtime.evaluate", eval_req, ctx.token); - // Response here will be the id for the timer from JS! - Assert.True(eval_res.IsOk); + var task = insp.WaitFor("Runtime.exceptionThrown"); + var eval_res = await ctx.cli.SendCommand("Runtime.evaluate", eval_req, ctx.token); + // Response here will be the id for the timer from JS! + Assert.True(eval_res.IsOk); - var ex = await Assert.ThrowsAsync(async () => await task); - var ex_json = JObject.Parse(ex.Message); - Assert.Equal(dicFileToUrl["/debugger-driver.html"], ex_json["exceptionDetails"]?["url"]?.Value()); + var ex = await Assert.ThrowsAsync(async () => await task); + var ex_json = JObject.Parse(ex.Message); + Assert.Equal(dicFileToUrl["/debugger-driver.html"], ex_json["exceptionDetails"]?["url"]?.Value()); } [Theory] @@ -369,31 +369,31 @@ await CheckInspectLocalsAtBreakpointSite( [Fact] public async Task RuntimeGetPropertiesWithInvalidScopeIdTest() { - var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 49, 8); + var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 49, 8); - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_delegates_test (); }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 49, 8, - "DelegatesTest", - wait_for_event_fn: async (pause_location) => - { - //make sure we're on the right bp - Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_delegates_test (); }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", 49, 8, + "DelegatesTest", + wait_for_event_fn: async (pause_location) => + { + //make sure we're on the right bp + Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value()); - var top_frame = pause_location["callFrames"][0]; + var top_frame = pause_location["callFrames"][0]; - var scope = top_frame["scopeChain"][0]; + var scope = top_frame["scopeChain"][0]; - // Try to get an invalid scope! - var get_prop_req = JObject.FromObject(new - { - objectId = "dotnet:scope:23490871", - }); + // Try to get an invalid scope! + var get_prop_req = JObject.FromObject(new + { + objectId = "dotnet:scope:23490871", + }); - var frame_props = await ctx.cli.SendCommand("Runtime.getProperties", get_prop_req, ctx.token); - Assert.True(frame_props.IsErr); - } - ); + var frame_props = await ctx.cli.SendCommand("Runtime.getProperties", get_prop_req, ctx.token); + Assert.True(frame_props.IsErr); + } + ); } [Theory] @@ -401,90 +401,90 @@ await EvaluateAndCheck( [InlineData(true)] public async Task InspectLocalsWithStructs(bool use_cfo) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; - - await SetBreakpoint(debugger_test_loc, 24, 8); - - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_method_with_structs(); }, 1);", - debugger_test_loc, 24, 8, "MethodWithLocalStructs"); - - var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); - await CheckProps(locals, new - { - ss_local = TValueType("DebuggerTests.ValueTypesTest.SimpleStruct"), - gs_local = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), - vt_local = TObject("DebuggerTests.ValueTypesTest") - }, "locals"); + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; - var dt = new DateTime(2021, 2, 3, 4, 6, 7); - var vt_local_props = await GetObjectOnFrame(pause_location["callFrames"][0], "vt_local"); - Assert.Equal(5, vt_local_props.Count()); + await SetBreakpoint(debugger_test_loc, 24, 8); - CheckString(vt_local_props, "StringField", "string#0"); - CheckValueType(vt_local_props, "SimpleStructField", "DebuggerTests.ValueTypesTest.SimpleStruct"); - CheckValueType(vt_local_props, "SimpleStructProperty", "DebuggerTests.ValueTypesTest.SimpleStruct"); - await CheckDateTime(vt_local_props, "DT", new DateTime(2020, 1, 2, 3, 4, 5)); - CheckEnum(vt_local_props, "RGB", "DebuggerTests.RGB", "Blue"); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_method_with_structs(); }, 1);", + debugger_test_loc, 24, 8, "MethodWithLocalStructs"); - // Check ss_local's properties - var ss_local_props = await GetObjectOnFrame(pause_location["callFrames"][0], "ss_local"); - await CheckProps(ss_local_props, new - { - V = TGetter("V"), - str_member = TString("set in MethodWithLocalStructs#SimpleStruct#str_member"), - dt = TDateTime(dt), - gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), - Kind = TEnum("System.DateTimeKind", "Utc") - }, "ss_local"); + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + await CheckProps(locals, new + { + ss_local = TValueType("DebuggerTests.ValueTypesTest.SimpleStruct"), + gs_local = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), + vt_local = TObject("DebuggerTests.ValueTypesTest") + }, "locals"); + + var dt = new DateTime(2021, 2, 3, 4, 6, 7); + var vt_local_props = await GetObjectOnFrame(pause_location["callFrames"][0], "vt_local"); + Assert.Equal(5, vt_local_props.Count()); + + CheckString(vt_local_props, "StringField", "string#0"); + CheckValueType(vt_local_props, "SimpleStructField", "DebuggerTests.ValueTypesTest.SimpleStruct"); + CheckValueType(vt_local_props, "SimpleStructProperty", "DebuggerTests.ValueTypesTest.SimpleStruct"); + await CheckDateTime(vt_local_props, "DT", new DateTime(2020, 1, 2, 3, 4, 5)); + CheckEnum(vt_local_props, "RGB", "DebuggerTests.RGB", "Blue"); + + // Check ss_local's properties + var ss_local_props = await GetObjectOnFrame(pause_location["callFrames"][0], "ss_local"); + await CheckProps(ss_local_props, new + { + V = TGetter("V"), + str_member = TString("set in MethodWithLocalStructs#SimpleStruct#str_member"), + dt = TDateTime(dt), + gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), + Kind = TEnum("System.DateTimeKind", "Utc") + }, "ss_local"); - { - var gres = await InvokeGetter(GetAndAssertObjectWithName(locals, "ss_local"), "V"); - await CheckValue(gres.Value["result"], TNumber(0xDEADBEEF + 2), $"ss_local#V"); + { + var gres = await InvokeGetter(GetAndAssertObjectWithName(locals, "ss_local"), "V"); + await CheckValue(gres.Value["result"], TNumber(0xDEADBEEF + 2), $"ss_local#V"); - // Check ss_local.gs - var gs_props = await GetObjectOnLocals(ss_local_props, "gs"); - CheckString(gs_props, "StringField", "set in MethodWithLocalStructs#SimpleStruct#gs#StringField"); - CheckObject(gs_props, "List", "System.Collections.Generic.List"); - } + // Check ss_local.gs + var gs_props = await GetObjectOnLocals(ss_local_props, "gs"); + CheckString(gs_props, "StringField", "set in MethodWithLocalStructs#SimpleStruct#gs#StringField"); + CheckObject(gs_props, "List", "System.Collections.Generic.List"); + } - // Check gs_local's properties - var gs_local_props = await GetObjectOnFrame(pause_location["callFrames"][0], "gs_local"); - await CheckProps(gs_local_props, new - { - StringField = TString("gs_local#GenericStruct#StringField"), - List = TObject("System.Collections.Generic.List", is_null: true), - Options = TEnum("DebuggerTests.Options", "None") - }, "gs_local"); + // Check gs_local's properties + var gs_local_props = await GetObjectOnFrame(pause_location["callFrames"][0], "gs_local"); + await CheckProps(gs_local_props, new + { + StringField = TString("gs_local#GenericStruct#StringField"), + List = TObject("System.Collections.Generic.List", is_null: true), + Options = TEnum("DebuggerTests.Options", "None") + }, "gs_local"); - // Check vt_local's properties + // Check vt_local's properties - var exp = new[] - { + var exp = new[] + { ("SimpleStructProperty", 2, "Utc"), ("SimpleStructField", 5, "Local") }; - foreach (var (name, bias, dt_kind) in exp) - { - dt = new DateTime(2020 + bias, 1 + bias, 2 + bias, 3 + bias, 5 + bias, 6 + bias); - await CompareObjectPropertiesFor(vt_local_props, name, - new - { - V = TGetter("V"), - str_member = TString($"{name}#string#0#SimpleStruct#str_member"), - dt = TDateTime(dt), - gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), - Kind = TEnum("System.DateTimeKind", dt_kind) - }, - label: $"vt_local_props.{name}"); - - var gres = await InvokeGetter(GetAndAssertObjectWithName(vt_local_props, name), "V"); - await CheckValue(gres.Value["result"], TNumber(0xDEADBEEF + (uint)dt.Month), $"{name}#V"); - } + foreach (var (name, bias, dt_kind) in exp) + { + dt = new DateTime(2020 + bias, 1 + bias, 2 + bias, 3 + bias, 5 + bias, 6 + bias); + await CompareObjectPropertiesFor(vt_local_props, name, + new + { + V = TGetter("V"), + str_member = TString($"{name}#string#0#SimpleStruct#str_member"), + dt = TDateTime(dt), + gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), + Kind = TEnum("System.DateTimeKind", dt_kind) + }, + label: $"vt_local_props.{name}"); + + var gres = await InvokeGetter(GetAndAssertObjectWithName(vt_local_props, name), "V"); + await CheckValue(gres.Value["result"], TNumber(0xDEADBEEF + (uint)dt.Month), $"{name}#V"); + } - // FIXME: check ss_local.gs.List's members + // FIXME: check ss_local.gs.List's members } [Theory] @@ -595,63 +595,63 @@ public async Task InspectBoxedAsClassLocals(string method_name, bool is_async) = [InlineData(true)] public async Task InspectLocalsWithStructsStaticAsync(bool use_cfo) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; + ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; - await SetBreakpoint(debugger_test_loc, 54, 12); + await SetBreakpoint(debugger_test_loc, 54, 12); - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method_async (" + - "'[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalStructsStaticAsync'" + - "); }, 1);", - debugger_test_loc, 54, 12, "MoveNext"); //BUG: method name + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method_async (" + + "'[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalStructsStaticAsync'" + + "); }, 1);", + debugger_test_loc, 54, 12, "MoveNext"); //BUG: method name - var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); - await CheckProps(locals, new - { - ss_local = TObject("DebuggerTests.ValueTypesTest.SimpleStruct"), - gs_local = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), - result = TBool(true) - }, - "locals#0"); - - var dt = new DateTime(2021, 2, 3, 4, 6, 7); - // Check ss_local's properties - var ss_local_props = await GetObjectOnFrame(pause_location["callFrames"][0], "ss_local"); - await CheckProps(ss_local_props, new - { - V = TGetter("V"), - str_member = TString("set in MethodWithLocalStructsStaticAsync#SimpleStruct#str_member"), - dt = TDateTime(dt), - gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), - Kind = TEnum("System.DateTimeKind", "Utc") - }, "ss_local"); + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + await CheckProps(locals, new + { + ss_local = TObject("DebuggerTests.ValueTypesTest.SimpleStruct"), + gs_local = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), + result = TBool(true) + }, + "locals#0"); + + var dt = new DateTime(2021, 2, 3, 4, 6, 7); + // Check ss_local's properties + var ss_local_props = await GetObjectOnFrame(pause_location["callFrames"][0], "ss_local"); + await CheckProps(ss_local_props, new + { + V = TGetter("V"), + str_member = TString("set in MethodWithLocalStructsStaticAsync#SimpleStruct#str_member"), + dt = TDateTime(dt), + gs = TValueType("DebuggerTests.ValueTypesTest.GenericStruct"), + Kind = TEnum("System.DateTimeKind", "Utc") + }, "ss_local"); - { - var gres = await InvokeGetter(GetAndAssertObjectWithName(locals, "ss_local"), "V"); - await CheckValue(gres.Value["result"], TNumber(0xDEADBEEF + 2), $"ss_local#V"); - - // Check ss_local.gs - await CompareObjectPropertiesFor(ss_local_props, "gs", - new - { - StringField = TString("set in MethodWithLocalStructsStaticAsync#SimpleStruct#gs#StringField"), - List = TObject("System.Collections.Generic.List"), - Options = TEnum("DebuggerTests.Options", "Option1") - } - ); - } + { + var gres = await InvokeGetter(GetAndAssertObjectWithName(locals, "ss_local"), "V"); + await CheckValue(gres.Value["result"], TNumber(0xDEADBEEF + 2), $"ss_local#V"); - // Check gs_local's properties - var gs_local_props = await GetObjectOnFrame(pause_location["callFrames"][0], "gs_local"); - await CheckProps(gs_local_props, new - { - StringField = TString("gs_local#GenericStruct#StringField"), - List = TObject("System.Collections.Generic.List"), - Options = TEnum("DebuggerTests.Options", "Option2") - }, "gs_local"); + // Check ss_local.gs + await CompareObjectPropertiesFor(ss_local_props, "gs", + new + { + StringField = TString("set in MethodWithLocalStructsStaticAsync#SimpleStruct#gs#StringField"), + List = TObject("System.Collections.Generic.List"), + Options = TEnum("DebuggerTests.Options", "Option1") + } + ); + } - // FIXME: check ss_local.gs.List's members + // Check gs_local's properties + var gs_local_props = await GetObjectOnFrame(pause_location["callFrames"][0], "gs_local"); + await CheckProps(gs_local_props, new + { + StringField = TString("gs_local#GenericStruct#StringField"), + List = TObject("System.Collections.Generic.List"), + Options = TEnum("DebuggerTests.Options", "Option2") + }, "gs_local"); + + // FIXME: check ss_local.gs.List's members } [Theory] @@ -661,95 +661,95 @@ await CompareObjectPropertiesFor(ss_local_props, "gs", [InlineData(182, 12, "MethodWithArgumentsForToStringTestAsync", false, true)] public async Task InspectLocalsForToStringDescriptions(int line, int col, string method_name, bool call_other, bool invoke_async) { - string entry_method_name = $"[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalsForToStringTest{(invoke_async ? "Async" : String.Empty)}"; + string entry_method_name = $"[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalsForToStringTest{(invoke_async ? "Async" : String.Empty)}"; int frame_idx = 0; - var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; - await SetBreakpoint(debugger_test_loc, line, col); + await SetBreakpoint(debugger_test_loc, line, col); - var eval_expr = "window.setTimeout(function() {" + - (invoke_async ? "invoke_static_method_async (" : "invoke_static_method (") + - $"'{entry_method_name}'," + - (call_other ? "true" : "false") + - "); }, 1);"; - Console.WriteLine($"{eval_expr}"); + var eval_expr = "window.setTimeout(function() {" + + (invoke_async ? "invoke_static_method_async (" : "invoke_static_method (") + + $"'{entry_method_name}'," + + (call_other ? "true" : "false") + + "); }, 1);"; + Console.WriteLine($"{eval_expr}"); - var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, invoke_async ? "MoveNext" : method_name); + var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, invoke_async ? "MoveNext" : method_name); - var dt0 = new DateTime(2020, 1, 2, 3, 4, 5); - var dt1 = new DateTime(2010, 5, 4, 3, 2, 1); - var ts = dt0 - dt1; - var dto = new DateTimeOffset(dt0, new TimeSpan(4, 5, 0)); + var dt0 = new DateTime(2020, 1, 2, 3, 4, 5); + var dt1 = new DateTime(2010, 5, 4, 3, 2, 1); + var ts = dt0 - dt1; + var dto = new DateTimeOffset(dt0, new TimeSpan(4, 5, 0)); - var frame_locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); - await CheckProps(frame_locals, new + var frame_locals = await GetProperties(pause_location["callFrames"][frame_idx]["callFrameId"].Value()); + await CheckProps(frame_locals, new + { + call_other = TBool(call_other), + dt0 = TDateTime(dt0), + dt1 = TDateTime(dt1), + dto = TValueType("System.DateTimeOffset", dto.ToString()), + ts = TValueType("System.TimeSpan", ts.ToString()), + dec = TValueType("System.Decimal", "123987123"), + guid = TValueType("System.Guid", "3D36E07E-AC90-48C6-B7EC-A481E289D014"), + dts = TArray("System.DateTime[]", 2), + obj = TObject("DebuggerTests.ClassForToStringTests"), + sst = TObject("DebuggerTests.StructForToStringTests") + }, "locals#0"); + + var dts_0 = new DateTime(1983, 6, 7, 5, 6, 10); + var dts_1 = new DateTime(1999, 10, 15, 1, 2, 3); + var dts_elements = await GetObjectOnLocals(frame_locals, "dts"); + await CheckDateTime(dts_elements, "0", dts_0); + await CheckDateTime(dts_elements, "1", dts_1); + + // TimeSpan + await CompareObjectPropertiesFor(frame_locals, "ts", + new { - call_other = TBool(call_other), - dt0 = TDateTime(dt0), - dt1 = TDateTime(dt1), - dto = TValueType("System.DateTimeOffset", dto.ToString()), - ts = TValueType("System.TimeSpan", ts.ToString()), - dec = TValueType("System.Decimal", "123987123"), - guid = TValueType("System.Guid", "3D36E07E-AC90-48C6-B7EC-A481E289D014"), - dts = TArray("System.DateTime[]", 2), - obj = TObject("DebuggerTests.ClassForToStringTests"), - sst = TObject("DebuggerTests.StructForToStringTests") - }, "locals#0"); - - var dts_0 = new DateTime(1983, 6, 7, 5, 6, 10); - var dts_1 = new DateTime(1999, 10, 15, 1, 2, 3); - var dts_elements = await GetObjectOnLocals(frame_locals, "dts"); - await CheckDateTime(dts_elements, "0", dts_0); - await CheckDateTime(dts_elements, "1", dts_1); - - // TimeSpan - await CompareObjectPropertiesFor(frame_locals, "ts", - new - { - Days = TNumber(3530), - Minutes = TNumber(2), - Seconds = TNumber(4), - }, "ts_props", num_fields: 12); - - // DateTimeOffset - await CompareObjectPropertiesFor(frame_locals, "dto", - new - { - Day = TNumber(2), - Year = TNumber(2020), - DayOfWeek = TEnum("System.DayOfWeek", "Thursday") - }, "dto_props", num_fields: 22); + Days = TNumber(3530), + Minutes = TNumber(2), + Seconds = TNumber(4), + }, "ts_props", num_fields: 12); + + // DateTimeOffset + await CompareObjectPropertiesFor(frame_locals, "dto", + new + { + Day = TNumber(2), + Year = TNumber(2020), + DayOfWeek = TEnum("System.DayOfWeek", "Thursday") + }, "dto_props", num_fields: 22); - var DT = new DateTime(2004, 10, 15, 1, 2, 3); - var DTO = new DateTimeOffset(dt0, new TimeSpan(2, 14, 0)); + var DT = new DateTime(2004, 10, 15, 1, 2, 3); + var DTO = new DateTimeOffset(dt0, new TimeSpan(2, 14, 0)); - await CompareObjectPropertiesFor(frame_locals, "obj", - new - { - DT = TDateTime(DT), - DTO = TValueType("System.DateTimeOffset", DTO.ToString()), - TS = TValueType("System.TimeSpan", ts.ToString()), - Dec = TValueType("System.Decimal", "1239871"), - Guid = TValueType("System.Guid", "3D36E07E-AC90-48C6-B7EC-A481E289D014") - }, "obj_props"); - - DTO = new DateTimeOffset(dt0, new TimeSpan(3, 15, 0)); - var sst_props = await CompareObjectPropertiesFor(frame_locals, "sst", - new - { - DT = TDateTime(DT), - DTO = TValueType("System.DateTimeOffset", DTO.ToString()), - TS = TValueType("System.TimeSpan", ts.ToString()), - Dec = TValueType("System.Decimal", "1239871"), - Guid = TValueType("System.Guid", "3D36E07E-AC90-48C6-B7EC-A481E289D014") - }, "sst_props"); + await CompareObjectPropertiesFor(frame_locals, "obj", + new + { + DT = TDateTime(DT), + DTO = TValueType("System.DateTimeOffset", DTO.ToString()), + TS = TValueType("System.TimeSpan", ts.ToString()), + Dec = TValueType("System.Decimal", "1239871"), + Guid = TValueType("System.Guid", "3D36E07E-AC90-48C6-B7EC-A481E289D014") + }, "obj_props"); + + DTO = new DateTimeOffset(dt0, new TimeSpan(3, 15, 0)); + var sst_props = await CompareObjectPropertiesFor(frame_locals, "sst", + new + { + DT = TDateTime(DT), + DTO = TValueType("System.DateTimeOffset", DTO.ToString()), + TS = TValueType("System.TimeSpan", ts.ToString()), + Dec = TValueType("System.Decimal", "1239871"), + Guid = TValueType("System.Guid", "3D36E07E-AC90-48C6-B7EC-A481E289D014") + }, "sst_props"); } [Fact] public async Task InspectLocals() { - var wait_res = await RunUntil("locals_inner"); - var locals = await GetProperties(wait_res["callFrames"][1]["callFrameId"].Value()); + var wait_res = await RunUntil("locals_inner"); + var locals = await GetProperties(wait_res["callFrames"][1]["callFrameId"].Value()); } [Theory] @@ -921,59 +921,59 @@ JObject FindFrame(JObject pause_location, string function_name) [Fact] public async Task DebugLazyLoadedAssemblyWithPdb() { - int line = 9; - await SetBreakpoint(".*/lazy-debugger-test.cs$", line, 0, use_regex: true); - await LoadAssemblyDynamically( - Path.Combine(DebuggerTestAppPath, "lazy-debugger-test.dll"), - Path.Combine(DebuggerTestAppPath, "lazy-debugger-test.pdb")); - - var source_location = "dotnet://lazy-debugger-test.dll/lazy-debugger-test.cs"; - Assert.Contains(source_location, scripts.Values); - - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function () { invoke_static_method('[lazy-debugger-test] LazyMath:IntAdd', 5, 10); }, 1);", - source_location, line, 8, - "IntAdd"); - var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); - CheckNumber(locals, "a", 5); - CheckNumber(locals, "b", 10); + int line = 9; + await SetBreakpoint(".*/lazy-debugger-test.cs$", line, 0, use_regex: true); + await LoadAssemblyDynamically( + Path.Combine(DebuggerTestAppPath, "lazy-debugger-test.dll"), + Path.Combine(DebuggerTestAppPath, "lazy-debugger-test.pdb")); + + var source_location = "dotnet://lazy-debugger-test.dll/lazy-debugger-test.cs"; + Assert.Contains(source_location, scripts.Values); + + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function () { invoke_static_method('[lazy-debugger-test] LazyMath:IntAdd', 5, 10); }, 1);", + source_location, line, 8, + "IntAdd"); + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 5); + CheckNumber(locals, "b", 10); } [Fact] public async Task DebugLazyLoadedAssemblyWithEmbeddedPdb() { - int line = 9; - await SetBreakpoint(".*/lazy-debugger-test-embedded.cs$", line, 0, use_regex: true); - await LoadAssemblyDynamically( - Path.Combine(DebuggerTestAppPath, "lazy-debugger-test-embedded.dll"), - null); - - var source_location = "dotnet://lazy-debugger-test-embedded.dll/lazy-debugger-test-embedded.cs"; - Assert.Contains(source_location, scripts.Values); - - var pause_location = await EvaluateAndCheck( - "window.setTimeout(function () { invoke_static_method('[lazy-debugger-test-embedded] LazyMath:IntAdd', 5, 10); }, 1);", - source_location, line, 8, - "IntAdd"); - var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); - CheckNumber(locals, "a", 5); - CheckNumber(locals, "b", 10); + int line = 9; + await SetBreakpoint(".*/lazy-debugger-test-embedded.cs$", line, 0, use_regex: true); + await LoadAssemblyDynamically( + Path.Combine(DebuggerTestAppPath, "lazy-debugger-test-embedded.dll"), + null); + + var source_location = "dotnet://lazy-debugger-test-embedded.dll/lazy-debugger-test-embedded.cs"; + Assert.Contains(source_location, scripts.Values); + + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function () { invoke_static_method('[lazy-debugger-test-embedded] LazyMath:IntAdd', 5, 10); }, 1);", + source_location, line, 8, + "IntAdd"); + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 5); + CheckNumber(locals, "b", 10); } [Fact] public async Task CannotDebugLazyLoadedAssemblyWithoutPdb() { - int line = 9; - await SetBreakpoint(".*/lazy-debugger-test.cs$", line, 0, use_regex: true); - await LoadAssemblyDynamically( - Path.Combine(DebuggerTestAppPath, "lazy-debugger-test.dll"), - null); + int line = 9; + await SetBreakpoint(".*/lazy-debugger-test.cs$", line, 0, use_regex: true); + await LoadAssemblyDynamically( + Path.Combine(DebuggerTestAppPath, "lazy-debugger-test.dll"), + null); - // wait to bit to catch if the event might be raised a bit late - await Task.Delay(1000); + // wait to bit to catch if the event might be raised a bit late + await Task.Delay(1000); - var source_location = "dotnet://lazy-debugger-test.dll/lazy-debugger-test.cs"; - Assert.DoesNotContain(source_location, scripts.Values); + var source_location = "dotnet://lazy-debugger-test.dll/lazy-debugger-test.cs"; + Assert.DoesNotContain(source_location, scripts.Values); } async Task LoadAssemblyDynamically(string asm_file, string pdb_file) @@ -1001,10 +1001,10 @@ async Task LoadAssemblyDynamically(string asm_file, string pdb_file) [Fact] public async Task BreakOnDebuggerBreak() { - await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method_async('[debugger-test] UserBreak:BreakOnDebuggerBreakCommand'); }, 1);", - "dotnet://debugger-test.dll/debugger-test2.cs", 56, 4, - "BreakOnDebuggerBreakCommand"); + await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method_async('[debugger-test] UserBreak:BreakOnDebuggerBreakCommand'); }, 1);", + "dotnet://debugger-test.dll/debugger-test2.cs", 56, 4, + "BreakOnDebuggerBreakCommand"); } //TODO add tests covering basic stepping behavior as step in/out/over } From f2dee9c7eb54862f994e7644059582d9b88cfd84 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 20 Nov 2020 15:28:49 -0500 Subject: [PATCH 14/21] [wasm][debugger][tests] Merge SingleSessionTestBase=>DebuggerTestBase .. since it isn't required any more. --- .../debugger/DebuggerTestSuite/ArrayTests.cs | 2 +- .../DebuggerTestSuite/BadHarnessInitTests.cs | 9 +--- .../DebuggerTestSuite/CallFunctionOnTests.cs | 2 +- .../DebuggerTestSuite/DateTimeTests.cs | 2 +- .../DebuggerTestSuite/DebuggerTestBase.cs | 34 +++++++++++-- .../DebuggerTestSuite/DelegateTests.cs | 2 +- .../EvaluateOnCallFrameTests.cs | 2 +- .../DebuggerTestSuite/ExceptionTests.cs | 2 +- .../DebuggerTestSuite/GetPropertiesTests.cs | 2 +- .../DebuggerTestSuite/HarnessTests.cs | 2 +- .../debugger/DebuggerTestSuite/MonoJsTests.cs | 2 +- .../DebuggerTestSuite/PointerTests.cs | 2 +- .../SingleSessionTestBase.cs | 50 ------------------- .../DebuggerTestSuite/SteppingTests.cs | 2 +- .../wasm/debugger/DebuggerTestSuite/Tests.cs | 2 +- 15 files changed, 44 insertions(+), 73 deletions(-) delete mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs index d9d0a85de4d120..1ab9ce242b47e1 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class ArrayTests : SingleSessionTestBase + public class ArrayTests : DebuggerTestBase { [Theory] diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs index 663da5f1c50a6e..6d83a777253e40 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs @@ -14,14 +14,7 @@ namespace DebuggerTests { public class BadHarnessInitTests : DebuggerTestBase { - internal Inspector insp; - protected Dictionary scripts; - - public BadHarnessInitTests(string driver = "debugger-driver.html") : base(driver) - { - insp = new Inspector(); - scripts = SubscribeToScripts(insp); - } + public override async Task InitializeAsync() => await Task.CompletedTask; [Fact] public async Task InvalidInitCommands() diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs index f3b674ec42d2ec..ac847f73417998 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class CallFunctionOnTests : SingleSessionTestBase + public class CallFunctionOnTests : DebuggerTestBase { // This tests `callFunctionOn` with a function that the vscode-js-debug extension uses diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs index b63b0d516e89cd..2d3707fec1d8d8 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DateTimeTests.cs @@ -8,7 +8,7 @@ namespace DebuggerTests { - public class DateTimeList : SingleSessionTestBase + public class DateTimeList : DebuggerTestBase { [Theory] diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index c1b59fc41ffcf3..944b6d2868cf00 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -19,9 +19,11 @@ namespace DebuggerTests { - public class DebuggerTestBase + public class DebuggerTestBase : IAsyncLifetime { protected Task startTask; + protected Inspector insp; + protected Dictionary scripts; static string s_debuggerTestAppPath; protected static string DebuggerTestAppPath @@ -73,9 +75,35 @@ static string FindChromePath() public DebuggerTestBase(string driver = "debugger-driver.html") { + insp = new Inspector(); + scripts = SubscribeToScripts(insp); + startTask = TestHarnessProxy.Start(FindChromePath(), DebuggerTestAppPath, driver); } + public virtual async Task InitializeAsync() + { + Func)>> fn = (client, token) => + { + Func)> getInitCmdFn = (cmd) => (cmd, client.SendCommand(cmd, null, token)); + var init_cmds = new List<(string, Task)> + { + getInitCmdFn("Profiler.enable"), + getInitCmdFn("Runtime.enable"), + getInitCmdFn("Debugger.enable"), + getInitCmdFn("Runtime.runIfWaitingForDebugger") + }; + + return init_cmds; + }; + + await Ready(); + await insp.OpenSessionAsync(fn); + ctx = new DebugTestContext(insp.Client, insp, insp.Token, scripts); + } + + public virtual async Task DisposeAsync() => await insp.ShutdownAsync().ConfigureAwait(false); + public Task Ready() => startTask; internal DebugTestContext ctx; @@ -107,7 +135,7 @@ internal Dictionary SubscribeToScripts(Inspector insp) } internal async Task CheckInspectLocalsAtBreakpointSite(string url_key, int line, int column, string function_name, string eval_expression, - Action? test_fn = null, Func? wait_for_event_fn = null, bool use_cfo = false) + Action test_fn = null, Func wait_for_event_fn = null, bool use_cfo = false) { ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; @@ -140,7 +168,7 @@ await EvaluateAndCheck( // sets breakpoint by method name and line offset internal async Task CheckInspectLocalsAtBreakpointSite(string type, string method, int line_offset, string bp_function_name, string eval_expression, - Action? locals_fn = null, Func? wait_for_event_fn = null, bool use_cfo = false, string assembly = "debugger-test.dll", int col = 0) + Action locals_fn = null, Func wait_for_event_fn = null, bool use_cfo = false, string assembly = "debugger-test.dll", int col = 0) { ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DelegateTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DelegateTests.cs index 241058a9edbb97..2fc0fb52ce99f9 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DelegateTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DelegateTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class DelegateTests : SingleSessionTestBase + public class DelegateTests : DebuggerTestBase { [Theory] diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index bdd5e0e9531f90..b7ce8e575218e2 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -13,7 +13,7 @@ namespace DebuggerTests { // TODO: static async, static method args - public class EvaluateOnCallFrameTests : SingleSessionTestBase + public class EvaluateOnCallFrameTests : DebuggerTestBase { public static IEnumerable InstanceMethodsTestData(string type_name) { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs index 77a3282ed1d1a4..1be0b18f325b59 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class ExceptionTests : SingleSessionTestBase + public class ExceptionTests : DebuggerTestBase { [Fact] public async Task ExceptionTestAll() diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs index 5133a8eba7675b..bbc2494f1947d5 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class GetPropertiesTests : SingleSessionTestBase + public class GetPropertiesTests : DebuggerTestBase { public static TheoryData, bool> ClassGetPropertiesTestData(bool is_async) { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs index 576c461c18018d..65c965073e6c77 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs @@ -12,7 +12,7 @@ namespace DebuggerTests { - public class HarnessTests : SingleSessionTestBase + public class HarnessTests : DebuggerTestBase { [Fact] public async Task TimedOutWaitingForInvalidBreakpoint() diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs index 7c6bfea518c2c6..99de780d63f03a 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class MonoJsTests : SingleSessionTestBase + public class MonoJsTests : DebuggerTestBase { [Fact] public async Task FixupNameValueObjectsWithMissingParts() diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/PointerTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/PointerTests.cs index 6c792063a6c085..ee703b29648bb1 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/PointerTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/PointerTests.cs @@ -11,7 +11,7 @@ namespace DebuggerTests { - public class PointerTests : SingleSessionTestBase + public class PointerTests : DebuggerTestBase { public static TheoryData PointersTestData => diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs deleted file mode 100644 index 6030df6fb0e1bd..00000000000000 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SingleSessionTestBase.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.WebAssembly.Diagnostics; -using Newtonsoft.Json.Linq; -using Xunit; - -#nullable enable - -namespace DebuggerTests -{ - public abstract class SingleSessionTestBase : DebuggerTestBase, IAsyncLifetime - { - internal Inspector insp; - protected Dictionary scripts; - - public SingleSessionTestBase(string driver = "debugger-driver.html") : base(driver) - { - insp = new Inspector(); - scripts = SubscribeToScripts(insp); - } - - public virtual async Task InitializeAsync() - { - Func)>> fn = (client, token) => - { - Func)> getInitCmdFn = (cmd) => (cmd, client.SendCommand(cmd, null, token)); - var init_cmds = new List<(string, Task)> - { - getInitCmdFn("Profiler.enable"), - getInitCmdFn("Runtime.enable"), - getInitCmdFn("Debugger.enable"), - getInitCmdFn("Runtime.runIfWaitingForDebugger") - }; - - return init_cmds; - }; - - await Ready(); - await insp.OpenSessionAsync(fn); - ctx = new DebugTestContext(insp.Client, insp, insp.Token, scripts); - } - - public virtual async Task DisposeAsync() => await insp.ShutdownAsync().ConfigureAwait(false); - } -} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs index 76b9f1722ac590..3ec7f1383824fa 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs @@ -9,7 +9,7 @@ namespace DebuggerTests { - public class SteppingTests : SingleSessionTestBase + public class SteppingTests : DebuggerTestBase { [Fact] public async Task TrivalStepping() diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index 5f833a6516e2b2..ed506f880a79c6 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -14,7 +14,7 @@ namespace DebuggerTests { - public class SourceList : SingleSessionTestBase + public class SourceList : DebuggerTestBase { [Fact] From 6c1b7199087287c54b452324376413e06d39c9f7 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 20 Nov 2020 16:44:20 -0500 Subject: [PATCH 15/21] [wasm][debugger][tests] Remove DebugTestContext This isn't required anymore. --- .../debugger/DebuggerTestSuite/ArrayTests.cs | 14 +-- .../DebuggerTestSuite/CallFunctionOnTests.cs | 100 +++++++++--------- .../DebuggerTestSuite/DebuggerTestBase.cs | 71 +++++-------- .../EvaluateOnCallFrameTests.cs | 4 +- .../DebuggerTestSuite/GetPropertiesTests.cs | 4 +- .../DebuggerTestSuite/HarnessTests.cs | 4 +- .../debugger/DebuggerTestSuite/MonoJsTests.cs | 18 ++-- .../DebuggerTestSuite/SteppingTests.cs | 10 +- .../wasm/debugger/DebuggerTestSuite/Tests.cs | 18 ++-- 9 files changed, 114 insertions(+), 129 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs index 1ab9ce242b47e1..dfe99aed5a7694 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs @@ -203,7 +203,7 @@ async Task TestSimpleArrayLocals(int line, int col, string entry_method_name, st bool test_prev_frame = false, int frame_idx = 0, bool use_cfo = false) { var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; await SetBreakpoint(debugger_test_loc, line, col); @@ -278,7 +278,7 @@ async Task GetObjectWithCFO(string objectId, JObject fn_args = null) cfo_args["arguments"] = fn_args; // callFunctionOn - var result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + var result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); return await GetProperties(result.Value["result"]["objectId"]?.Value(), fn_args); } @@ -295,7 +295,7 @@ public async Task InspectObjectArrayMembers(bool use_cfo) string method_name = "PlaceholderMethod"; int frame_idx = 1; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; await SetBreakpoint(debugger_test_loc, line, col); @@ -367,7 +367,7 @@ public async Task InspectValueTypeArrayLocalsStaticAsync(bool use_cfo) string method_name = "MoveNext"; // BUG: this should be ValueTypeLocalsAsync int frame_idx = 0; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; await SetBreakpoint(debugger_test_loc, line, col); @@ -431,7 +431,7 @@ public async Task InspectValueTypeArrayLocalsInstanceAsync(bool use_cfo) string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:ValueTypeLocalsAsync"; int frame_idx = 0; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; await SetBreakpoint(debugger_test_loc, line, col); @@ -482,7 +482,7 @@ public async Task InspectValueTypeArrayLocalsInAsyncStaticStructMethod(bool use_ string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:EntryPointForStructMethod"; int frame_idx = 0; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; await SetBreakpoint(debugger_test_loc, line, col); @@ -514,7 +514,7 @@ public async Task InspectValueTypeArrayLocalsInAsyncInstanceStructMethod(bool us string entry_method_name = "[debugger-test] DebuggerTests.ArrayTestsClass:EntryPointForStructMethod"; int frame_idx = 0; - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-array-test.cs"; await SetBreakpoint(debugger_test_loc, line, col); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs index ac847f73417998..3b320494538dd7 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs @@ -30,12 +30,12 @@ await RunCallFunctionOn(eval_fn, vscode_fn0, "big", bp_loc, line, col, res_array { var is_js = bp_loc.EndsWith(".js", StringComparison.Ordinal); - var obj_accessors = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, ownProperties = false - }), ctx.token); + }), token); if (is_js) await CheckProps(obj_accessors.Value["result"], new { __proto__ = TIgnore() }, "obj_accessors"); else @@ -43,12 +43,12 @@ await RunCallFunctionOn(eval_fn, vscode_fn0, "big", bp_loc, line, col, res_array // Check for a __proto__ object // isOwn = true, accessorPropertiesOnly = false - var obj_own = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, ownProperties = true - }), ctx.token); + }), token); await CheckProps(obj_own.Value["result"], new { @@ -90,12 +90,12 @@ await RunCallFunctionOn(eval_fn, vscode_fn1, "big", bp_loc, line, col, var is_js = bp_loc.EndsWith(".js", StringComparison.Ordinal); // isOwn = false, accessorPropertiesOnly = true - var obj_accessors = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, ownProperties = false - }), ctx.token); + }), token); if (is_js) await CheckProps(obj_accessors.Value["result"], new { __proto__ = TIgnore() }, "obj_accessors"); else @@ -104,12 +104,12 @@ await RunCallFunctionOn(eval_fn, vscode_fn1, "big", bp_loc, line, col, // Ignoring the __proto__ property // isOwn = true, accessorPropertiesOnly = false - var obj_own = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, ownProperties = true - }), ctx.token); + }), token); var obj_own_val = obj_own.Value["result"]; var num_elems_recd = len == 0 ? 0 : num_elems_fetch; @@ -142,24 +142,24 @@ await RunCallFunctionOn(eval_fn, var is_js = bp_loc.EndsWith(".js", StringComparison.Ordinal); // getProperties (isOwn = false, accessorPropertiesOnly = true) - var obj_accessors = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, ownProperties = false - }), ctx.token); + }), token); if (is_js) await CheckProps(obj_accessors.Value["result"], new { __proto__ = TIgnore() }, "obj_accessors"); else AssertEqual(0, obj_accessors.Value["result"]?.Count(), "obj_accessors-count"); // getProperties (isOwn = true, accessorPropertiesOnly = false) - var obj_own = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, ownProperties = true - }), ctx.token); + }), token); await CheckProps(obj_own.Value["result"], new { @@ -188,12 +188,12 @@ await RunCallFunctionOn(eval_fn, var is_js = bp_loc.EndsWith(".js"); // getProperties (own=false) - var obj_accessors = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, ownProperties = false - }), ctx.token); + }), token); if (is_js) await CheckProps(obj_accessors.Value["result"], new { __proto__ = TIgnore() }, "obj_accessors"); @@ -202,12 +202,12 @@ await RunCallFunctionOn(eval_fn, // getProperties (own=true) // isOwn = true, accessorPropertiesOnly = false - var obj_own = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, ownProperties = true - }), ctx.token); + }), token); // AssertEqual (2, obj_own.Value ["result"].Count (), $"{label}-obj_own.count"); @@ -239,23 +239,23 @@ public async Task RunOnVTArray(bool roundtrip) => await RunCallFunctionOn( var ret_len = 5; // getProperties (own=false) - var obj_accessors = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, ownProperties = false - }), ctx.token); + }), token); AssertEqual(0, obj_accessors.Value["result"]?.Count(), "obj_accessors-count"); // getProperties (own=true) // isOwn = true, accessorPropertiesOnly = false - var obj_own = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, ownProperties = true - }), ctx.token); + }), token); var obj_own_val = obj_own.Value["result"]; await CheckProps(obj_own_val, new @@ -299,22 +299,22 @@ public async Task RunOnCFOValueTypeResult(bool roundtrip) => await RunCallFuncti { // getProperties (own=false) - var obj_accessors = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, ownProperties = false - }), ctx.token); + }), token); AssertEqual(0, obj_accessors.Value["result"].Count(), "obj_accessors-count"); // getProperties (own=true) // isOwn = true, accessorPropertiesOnly = false - var obj_own = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, ownProperties = true - }), ctx.token); + }), token); var obj_own_val = obj_own.Value["result"]; var dt = new DateTime(2020, 1, 2, 3, 4, 5); @@ -345,23 +345,23 @@ public async Task RunOnJSObject(bool roundtrip) => await RunCallFunctionOn( { // getProperties (own=false) - var obj_accessors = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, ownProperties = false - }), ctx.token); + }), token); await CheckProps(obj_accessors.Value["result"], new { __proto__ = TIgnore() }, "obj_accessors"); // getProperties (own=true) // isOwn = true, accessorPropertiesOnly = false - var obj_own = await ctx.cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, ownProperties = true - }), ctx.token); + }), token); var obj_own_val = obj_own.Value["result"]; await CheckProps(obj_own_val, new @@ -446,11 +446,11 @@ public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int l // callFunctionOn var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var pause_location = await insp.WaitFor(Inspector.PAUSE); // Um for js we get "scriptId": "6" - // CheckLocation (bp_loc, line, col, ctx.scripts, pause_location ["callFrames"][0]["location"]); + // CheckLocation (bp_loc, line, col, scripts, pause_location ["callFrames"][0]["location"]); // Check the object at the bp var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); @@ -469,7 +469,7 @@ public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int l cfo_args["returnByValue"] = return_by_val; // callFunctionOn - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); await CheckValue(result.Value["result"], TNumber(5), "cfo-res"); cfo_args = JObject.FromObject(new @@ -484,7 +484,7 @@ public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int l cfo_args["returnByValue"] = return_by_val; // callFunctionOn - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); await CheckValue(result.Value["result"], JObject.FromObject(new { type = "string", value = "test value" }), "cfo-res"); cfo_args = JObject.FromObject(new @@ -499,7 +499,7 @@ public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int l cfo_args["returnByValue"] = return_by_val; // callFunctionOn - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); await CheckValue(result.Value["result"], JObject.Parse("{ type: 'object', subtype: 'null', value: null }"), "cfo-res"); } @@ -518,8 +518,8 @@ public async Task CFOWithSilentReturnsErrors(string eval_fn, string bp_loc, int // callFunctionOn var eval_expr = "window.setTimeout(function() { " + eval_fn + " }, 1);"; - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var pause_location = await insp.WaitFor(Inspector.PAUSE); var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); var obj = GetAndAssertObjectWithName(frame_locals, "big"); @@ -538,7 +538,7 @@ public async Task CFOWithSilentReturnsErrors(string eval_fn, string bp_loc, int // callFunctionOn, Silent does not change the result, except that the error // doesn't get reported, and the execution is NOT paused even with setPauseOnException=true - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); Assert.False(result.IsOk, "result.IsOk"); Assert.True(result.IsErr, "result.IsErr"); @@ -753,7 +753,7 @@ await RunCallFunctionOn( async Task GetPropertiesAndCheckAccessors(JObject get_prop_req, int num_fields) { - var res = await ctx.cli.SendCommand("Runtime.getProperties", get_prop_req, ctx.token); + var res = await cli.SendCommand("Runtime.getProperties", get_prop_req, token); if (!res.IsOk) Assert.True(false, $"Runtime.getProperties failed for {get_prop_req.ToString()}, with Result: {res}"); @@ -790,7 +790,7 @@ public async Task RunOnInvalidCfoId(string eval_fn, string bp_loc, int line, int objectId = ptd_id + "_invalid" }); - var res = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + var res = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); Assert.True(res.IsErr); }); @@ -798,13 +798,13 @@ public async Task RunOnInvalidCfoId(string eval_fn, string bp_loc, int line, int [MemberData(nameof(NegativeTestsData), false)] public async Task RunOnInvalidThirdSegmentOfObjectId(string eval_fn, string bp_loc, int line, int col, bool use_cfo) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; await SetBreakpoint(bp_loc, line, col); // callFunctionOn var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var pause_location = await insp.WaitFor(Inspector.PAUSE); var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); var ptd = GetAndAssertObjectWithName(frame_locals, "ptd"); @@ -816,7 +816,7 @@ public async Task RunOnInvalidThirdSegmentOfObjectId(string eval_fn, string bp_l objectId = ptd_id + "_invalid" }); - var res = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + var res = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); Assert.True(res.IsErr); } @@ -826,12 +826,12 @@ public async Task RunOnInvalidThirdSegmentOfObjectId(string eval_fn, string bp_l public async Task InvalidPropertyGetters(string eval_fn, string bp_loc, int line, int col, bool use_cfo) { await SetBreakpoint(bp_loc, line, col); - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; // callFunctionOn var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr })); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var pause_location = await insp.WaitFor(Inspector.PAUSE); var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); var ptd = GetAndAssertObjectWithName(frame_locals, "ptd"); @@ -890,11 +890,11 @@ async Task RunCallFunctionOn(string eval_fn, string fn_decl, string local_name, // callFunctionOn var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var pause_location = await insp.WaitFor(Inspector.PAUSE); // Um for js we get "scriptId": "6" - // CheckLocation (bp_loc, line, col, ctx.scripts, pause_location ["callFrames"][0]["location"]); + // CheckLocation (bp_loc, line, col, scripts, pause_location ["callFrames"][0]["location"]); // Check the object at the bp var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); @@ -914,7 +914,7 @@ async Task RunCallFunctionOn(string eval_fn, string fn_decl, string local_name, cfo_args["returnByValue"] = returnByValue; // callFunctionOn - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); await CheckCFOResult(result); // If it wasn't `returnByValue`, then try to run a new function @@ -932,7 +932,7 @@ async Task RunCallFunctionOn(string eval_fn, string fn_decl, string local_name, if (fn_args != null) cfo_args["arguments"] = fn_args; - result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); await CheckCFOResult(result); } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index 944b6d2868cf00..247778b97454a5 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -21,9 +21,13 @@ namespace DebuggerTests { public class DebuggerTestBase : IAsyncLifetime { - protected Task startTask; - protected Inspector insp; + internal InspectorClient cli; + internal Inspector insp; + protected CancellationToken token; protected Dictionary scripts; + protected Task startTask; + + public bool UseCallFunctionOnBeforeGetProperties; static string s_debuggerTestAppPath; protected static string DebuggerTestAppPath @@ -76,6 +80,7 @@ static string FindChromePath() public DebuggerTestBase(string driver = "debugger-driver.html") { insp = new Inspector(); + cli = insp.Client; scripts = SubscribeToScripts(insp); startTask = TestHarnessProxy.Start(FindChromePath(), DebuggerTestAppPath, driver); @@ -83,7 +88,7 @@ public DebuggerTestBase(string driver = "debugger-driver.html") public virtual async Task InitializeAsync() { - Func)>> fn = (client, token) => + Func)>> fn = (client, token) => { Func)> getInitCmdFn = (cmd) => (cmd, client.SendCommand(cmd, null, token)); var init_cmds = new List<(string, Task)> @@ -99,14 +104,12 @@ public virtual async Task InitializeAsync() await Ready(); await insp.OpenSessionAsync(fn); - ctx = new DebugTestContext(insp.Client, insp, insp.Token, scripts); } public virtual async Task DisposeAsync() => await insp.ShutdownAsync().ConfigureAwait(false); public Task Ready() => startTask; - internal DebugTestContext ctx; internal Dictionary dicScriptsIdToUrl; internal Dictionary dicFileToUrl; internal Dictionary SubscribeToScripts(Inspector insp) @@ -137,7 +140,7 @@ internal Dictionary SubscribeToScripts(Inspector insp) internal async Task CheckInspectLocalsAtBreakpointSite(string url_key, int line, int column, string function_name, string eval_expression, Action test_fn = null, Func wait_for_event_fn = null, bool use_cfo = false) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var bp = await SetBreakpoint(url_key, line, column); @@ -170,19 +173,19 @@ await EvaluateAndCheck( internal async Task CheckInspectLocalsAtBreakpointSite(string type, string method, int line_offset, string bp_function_name, string eval_expression, Action locals_fn = null, Func wait_for_event_fn = null, bool use_cfo = false, string assembly = "debugger-test.dll", int col = 0) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var bp = await SetBreakpointInMethod(assembly, type, method, line_offset, col); var args = JObject.FromObject(new { expression = eval_expression }); - var res = await ctx.cli.SendCommand("Runtime.evaluate", args, ctx.token); + var res = await cli.SendCommand("Runtime.evaluate", args, token); if (!res.IsOk) { Console.WriteLine($"Failed to run command {method} with args: {args?.ToString()}\nresult: {res.Error.ToString()}"); Assert.True(false, $"SendCommand for {method} failed with {res.Error.ToString()}"); } - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var pause_location = await insp.WaitFor(Inspector.PAUSE); if (bp_function_name != null) Assert.Equal(bp_function_name, pause_location["callFrames"]?[0]?["functionName"]?.Value()); @@ -338,7 +341,7 @@ internal JToken GetAndAssertObjectWithName(JToken obj, string name, string label internal async Task SendCommand(string method, JObject args) { - var res = await ctx.cli.SendCommand(method, args, ctx.token); + var res = await cli.SendCommand(method, args, token); if (!res.IsOk) { Console.WriteLine($"Failed to run command {method} with args: {args?.ToString()}\nresult: {res.Error.ToString()}"); @@ -364,7 +367,7 @@ internal async Task RunUntil(string methodName) await SetBreakpointInMethod("debugger-test", "DebuggerTest", methodName); // This will run all the tests until it hits the bp await Evaluate("window.setTimeout(function() { invoke_run_all (); }, 1);"); - var wait_res = await ctx.insp.WaitFor(Inspector.PAUSE); + var wait_res = await insp.WaitFor(Inspector.PAUSE); AssertLocation(wait_res, "locals_inner"); return wait_res; } @@ -380,7 +383,7 @@ internal async Task InvokeGetter(JToken obj, object arguments, string fn if (returnByValue != null) req["returnByValue"] = returnByValue.Value; - var res = await ctx.cli.SendCommand("Runtime.callFunctionOn", req, ctx.token); + var res = await cli.SendCommand("Runtime.callFunctionOn", req, token); Assert.True(expect_ok == res.IsOk, $"InvokeGetter failed for {req} with {res}"); return res; @@ -412,14 +415,14 @@ internal async Task EvaluateAndCheck(string expression, string script_l internal async Task SendCommandAndCheck(JObject args, string method, string script_loc, int line, int column, string function_name, Func wait_for_event_fn = null, Action locals_fn = null, string waitForEvent = Inspector.PAUSE) { - var res = await ctx.cli.SendCommand(method, args, ctx.token); + var res = await cli.SendCommand(method, args, token); if (!res.IsOk) { Console.WriteLine($"Failed to run command {method} with args: {args?.ToString()}\nresult: {res.Error.ToString()}"); Assert.True(false, $"SendCommand for {method} failed with {res.Error.ToString()}"); } - var wait_res = await ctx.insp.WaitFor(waitForEvent); + var wait_res = await insp.WaitFor(waitForEvent); JToken top_frame = wait_res["callFrames"]?[0]; if (function_name != null) { @@ -428,7 +431,7 @@ internal async Task SendCommandAndCheck(JObject args, string method, st Console.WriteLine(top_frame); if (script_loc != null && line >= 0) - CheckLocation(script_loc, line, column, ctx.scripts, top_frame["location"]); + CheckLocation(script_loc, line, column, scripts, top_frame["location"]); if (wait_for_event_fn != null) await wait_for_event_fn(wait_res); @@ -675,7 +678,7 @@ internal async Task CheckValue(JToken actual_val, JToken exp_val, string label) internal async Task GetLocalsForFrame(JToken frame, string script_loc, int line, int column, string function_name) { - CheckLocation(script_loc, line, column, ctx.scripts, frame["location"]); + CheckLocation(script_loc, line, column, scripts, frame["location"]); Assert.Equal(function_name, frame["functionName"].Value()); return await GetProperties(frame["callFrameId"].Value()); @@ -717,7 +720,7 @@ internal async Task GetObjectOnLocals(JToken locals, string name) /* @fn_args is for use with `Runtime.callFunctionOn` only */ internal async Task GetProperties(string id, JToken fn_args = null, bool? own_properties = null, bool? accessors_only = null, bool expect_ok = true) { - if (ctx.UseCallFunctionOnBeforeGetProperties && !id.StartsWith("dotnet:scope:")) + if (UseCallFunctionOnBeforeGetProperties && !id.StartsWith("dotnet:scope:")) { var fn_decl = "function () { return this; }"; var cfo_args = JObject.FromObject(new @@ -728,7 +731,7 @@ internal async Task GetProperties(string id, JToken fn_args = null, bool if (fn_args != null) cfo_args["arguments"] = fn_args; - var result = await ctx.cli.SendCommand("Runtime.callFunctionOn", cfo_args, ctx.token); + var result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); AssertEqual(expect_ok, result.IsOk, $"Runtime.getProperties returned {result.IsOk} instead of {expect_ok}, for {cfo_args.ToString()}, with Result: {result}"); if (!result.IsOk) return null; @@ -748,7 +751,7 @@ internal async Task GetProperties(string id, JToken fn_args = null, bool get_prop_req["accessorPropertiesOnly"] = accessors_only.Value; } - var frame_props = await ctx.cli.SendCommand("Runtime.getProperties", get_prop_req, ctx.token); + var frame_props = await cli.SendCommand("Runtime.getProperties", get_prop_req, token); AssertEqual(expect_ok, frame_props.IsOk, $"Runtime.getProperties returned {frame_props.IsOk} instead of {expect_ok}, for {get_prop_req}, with Result: {frame_props}"); if (!frame_props.IsOk) return null; @@ -779,7 +782,7 @@ internal async Task GetProperties(string id, JToken fn_args = null, bool expression = expression }); - var res = await ctx.cli.SendCommand("Debugger.evaluateOnCallFrame", evaluate_req, ctx.token); + var res = await cli.SendCommand("Debugger.evaluateOnCallFrame", evaluate_req, token); AssertEqual(expect_ok, res.IsOk, $"Debugger.evaluateOnCallFrame ('{expression}', scope: {id}) returned {res.IsOk} instead of {expect_ok}, with Result: {res}"); if (res.IsOk) return (res.Value["result"], res); @@ -794,7 +797,7 @@ internal async Task RemoveBreakpoint(string id, bool expect_ok = true) breakpointId = id }); - var res = await ctx.cli.SendCommand("Debugger.removeBreakpoint", remove_bp, ctx.token); + var res = await cli.SendCommand("Debugger.removeBreakpoint", remove_bp, token); Assert.True(expect_ok ? res.IsOk : res.IsErr); return res; @@ -806,7 +809,7 @@ internal async Task SetBreakpoint(string url_key, int line, int column, JObject.FromObject(new { lineNumber = line, columnNumber = column, url = dicFileToUrl[url_key], }) : JObject.FromObject(new { lineNumber = line, columnNumber = column, urlRegex = url_key, }); - var bp1_res = await ctx.cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, ctx.token); + var bp1_res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token); Assert.True(expect_ok ? bp1_res.IsOk : bp1_res.IsErr); return bp1_res; @@ -814,7 +817,7 @@ internal async Task SetBreakpoint(string url_key, int line, int column, internal async Task SetPauseOnException(string state) { - var exc_res = await ctx.cli.SendCommand("Debugger.setPauseOnExceptions", JObject.FromObject(new { state = state }), ctx.token); + var exc_res = await cli.SendCommand("Debugger.setPauseOnExceptions", JObject.FromObject(new { state = state }), token); return exc_res; } @@ -823,7 +826,7 @@ internal async Task SetBreakpointInMethod(string assembly, string type, var req = JObject.FromObject(new { assemblyName = assembly, typeName = type, methodName = method, lineOffset = lineOffset }); // Protocol extension - var res = await ctx.cli.SendCommand("DotnetDebugger.getMethodLocation", req, ctx.token); + var res = await cli.SendCommand("DotnetDebugger.getMethodLocation", req, token); Assert.True(res.IsOk); var m_url = res.Value["result"]["url"].Value(); @@ -836,7 +839,7 @@ internal async Task SetBreakpointInMethod(string assembly, string type, url = m_url }); - res = await ctx.cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, ctx.token); + res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token); Assert.True(res.IsOk); return res; @@ -926,24 +929,6 @@ internal static JObject TDateTime(DateTime dt) => JObject.FromObject(new }); } - class DebugTestContext - { - public InspectorClient cli; - public Inspector insp; - public CancellationToken token; - public Dictionary scripts; - - public bool UseCallFunctionOnBeforeGetProperties; - - public DebugTestContext(InspectorClient cli, Inspector insp, CancellationToken token, Dictionary scripts) - { - this.cli = cli; - this.insp = insp; - this.token = token; - this.scripts = scripts; - } - } - class DotnetObjectId { public string Scheme { get; } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index b7ce8e575218e2..6462e86505d331 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -412,8 +412,8 @@ public async Task JSEvaluate() await SetBreakpoint(bp_loc, line, col); var eval_expr = "window.setTimeout(function() { eval_call_on_frame_test (); }, 1)"; - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var pause_location = await insp.WaitFor(Inspector.PAUSE); var id = pause_location["callFrames"][0]["callFrameId"].Value(); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs index bbc2494f1947d5..3215ef1184d89c 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs @@ -294,8 +294,8 @@ public async Task GetPropertiesTestJSAndManaged(bool test_js, bool? own_properti eval_expr = "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.GetPropertiesTests.DerivedClassForJSTest:run'); }, 1)"; } - var result = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), ctx.token); - var pause_location = await ctx.insp.WaitFor(Inspector.PAUSE); + var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var pause_location = await insp.WaitFor(Inspector.PAUSE); var id = pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value(); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs index 65c965073e6c77..ef36ace6624a16 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs @@ -45,10 +45,10 @@ public async Task InspectorWaitForAfterMessageAlreadyReceived() Result res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); Assert.True(res.IsOk, $"setBreakpoint failed with {res}"); - res = await ctx.cli.SendCommand( + res = await cli.SendCommand( "Runtime.evaluate", JObject.FromObject(new { expression = "window.setTimeout(function() { invoke_add(); }, 0);" }), - ctx.token); + token); Assert.True(res.IsOk, $"evaluating the function failed with {res}"); // delay, so that we can get the Debugger.pause event diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs index 99de780d63f03a..e2b796062e3086 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs @@ -38,7 +38,7 @@ public async Task FixupNameValueObjectsWithMissingParts() }; var list = new[] { names[0], names[1], values[0], names[2], getters[0], getters[1] }; - var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression = $"MONO._fixup_name_value_objects({JsonConvert.SerializeObject(list)})", returnByValue = true }), ctx.token); + var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression = $"MONO._fixup_name_value_objects({JsonConvert.SerializeObject(list)})", returnByValue = true }), token); Assert.True(res.IsOk); await CheckProps(res.Value["result"]["value"], new @@ -81,7 +81,7 @@ public async Task GetParamsAndLocalsWithInvalidIndices() var expression = $"MONO.mono_wasm_get_variables({scope}, {JsonConvert.SerializeObject(var_ids)})"; - var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), ctx.token); + var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); Assert.True(res.IsOk); await CheckProps(res.Value["result"]?["value"], new @@ -107,12 +107,12 @@ await EvaluateAndCheck( var scope_id = "-12"; var expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; - var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), ctx.token); + var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); Assert.False(res.IsOk); scope_id = "30000"; expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; - res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), ctx.token); + res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); Assert.False(res.IsOk); } @@ -131,12 +131,12 @@ public async Task BadRaiseDebugEventsTest() foreach (var expression in bad_expressions) { - var res = await ctx.cli.SendCommand($"Runtime.evaluate", + var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true - }), ctx.token); + }), token); Assert.False(res.IsOk, $"Expected to fail for {expression}"); } } @@ -162,7 +162,7 @@ public async Task RaiseDebugEventTraceTest(bool? trace) var trace_str = trace.HasValue ? $"trace: {trace.ToString().ToLower()}" : String.Empty; var expression = $"MONO.mono_wasm_raise_debug_event({{ eventName:'qwe' }}, {{ {trace_str} }})"; - var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); + var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); Assert.True(res.IsOk, $"Expected to pass for {expression}"); var t = await Task.WhenAny(tcs.Task, Task.Delay(2000)); @@ -249,10 +249,10 @@ async Task AssemblyLoadedEventTest(string asm_name, string asm_path, string pdb_ pdb_b64: '{pdb_base64}' }});"; - var res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); + var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); Assert.True(res.IsOk, $"Expected to pass for {expression}"); - res = await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); + res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); Assert.True(res.IsOk, $"Expected to pass for {expression}"); var t = await Task.WhenAny(tcs.Task, Task.Delay(2000)); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs index 3ec7f1383824fa..a5da24b3691031 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs @@ -89,7 +89,7 @@ await StepAndCheck(StepKind.Over, debugger_test_loc, 12, 8, "IntAdd", [InlineData(true)] public async Task InspectLocalsInPreviousFramesDuringSteppingIn2(bool use_cfo) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var dep_cs_loc = "dotnet://debugger-test.dll/dependency.cs"; await SetBreakpoint(dep_cs_loc, 33, 8); @@ -156,7 +156,7 @@ public async Task InspectLocalsInPreviousFramesDuringSteppingIn2(bool use_cfo) [InlineData(true)] public async Task InspectLocalsInPreviousFramesDuringSteppingIn(bool use_cfo) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs"; await SetBreakpoint(debugger_test_loc, 111, 12); @@ -316,7 +316,7 @@ await StepAndCheck(StepKind.Over, "dotnet://debugger-test.dll/debugger-test.cs", [InlineData(true)] public async Task InspectLocalsInAsyncMethods(bool use_cfo) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-test.cs"; await SetBreakpoint(debugger_test_loc, 120, 12); @@ -372,7 +372,7 @@ public async Task InspectLocalsInAsyncMethods(bool use_cfo) [InlineData(true)] public async Task InspectValueTypeMethodArgsWhileStepping(bool use_cfo) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; await SetBreakpoint(debugger_test_loc, 36, 12); @@ -856,7 +856,7 @@ public async Task BreakpointOnHiddenLineOfMethodWithNoNextVisibleLineShouldNotPa await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 554, 12); string expression = "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);"; - await ctx.cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), ctx.token); + await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); Task pause_task = insp.WaitFor(Inspector.PAUSE); Task t = await Task.WhenAny(pause_task, Task.Delay(2000)); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index ed506f880a79c6..1b7657ddb87cd0 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -130,7 +130,7 @@ public async Task CreateBadBreakpoint() url = "dotnet://debugger-test.dll/this-file-doesnt-exist.cs", }); - var bp1_res = await ctx.cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, ctx.token); + var bp1_res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token); Assert.True(bp1_res.IsOk); Assert.Empty(bp1_res.Value["locations"].Values()); @@ -183,7 +183,7 @@ public async Task ExceptionThrownInJS() expression = "invoke_bad_js_test();" }); - var eval_res = await ctx.cli.SendCommand("Runtime.evaluate", eval_req, ctx.token); + var eval_res = await cli.SendCommand("Runtime.evaluate", eval_req, token); Assert.True(eval_res.IsErr); Assert.Equal("Uncaught", eval_res.Error["exceptionDetails"]?["text"]?.Value()); } @@ -199,7 +199,7 @@ public async Task ExceptionThrownInJSOutOfBand() }); var task = insp.WaitFor("Runtime.exceptionThrown"); - var eval_res = await ctx.cli.SendCommand("Runtime.evaluate", eval_req, ctx.token); + var eval_res = await cli.SendCommand("Runtime.evaluate", eval_req, token); // Response here will be the id for the timer from JS! Assert.True(eval_res.IsOk); @@ -390,7 +390,7 @@ await EvaluateAndCheck( objectId = "dotnet:scope:23490871", }); - var frame_props = await ctx.cli.SendCommand("Runtime.getProperties", get_prop_req, ctx.token); + var frame_props = await cli.SendCommand("Runtime.getProperties", get_prop_req, token); Assert.True(frame_props.IsErr); } ); @@ -401,7 +401,7 @@ await EvaluateAndCheck( [InlineData(true)] public async Task InspectLocalsWithStructs(bool use_cfo) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; await SetBreakpoint(debugger_test_loc, 24, 8); @@ -595,7 +595,7 @@ public async Task InspectBoxedAsClassLocals(string method_name, bool is_async) = [InlineData(true)] public async Task InspectLocalsWithStructsStaticAsync(bool use_cfo) { - ctx.UseCallFunctionOnBeforeGetProperties = use_cfo; + UseCallFunctionOnBeforeGetProperties = use_cfo; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-valuetypes-test.cs"; await SetBreakpoint(debugger_test_loc, 54, 12); @@ -812,7 +812,7 @@ await CheckInspectLocalsAtBreakpointSite( async Task CreateNewId(string expr) { - var res = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), ctx.token); + var res = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), token); Assert.True(res.IsOk, "Expected Runtime.evaluate to succeed"); AssertEqual("string", res.Value["result"]?["type"]?.Value(), "Expected Runtime.evaluate to return a string type result"); return res.Value["result"]?["value"]?.Value(); @@ -821,7 +821,7 @@ async Task CreateNewId(string expr) async Task _invoke_getter(string obj_id, string property_name, bool expect_ok) { var expr = $"MONO._invoke_getter ('{obj_id}', '{property_name}')"; - var res = await ctx.cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), ctx.token); + var res = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), token); AssertEqual(expect_ok, res.IsOk, "Runtime.evaluate result not as expected for {expr}"); return res; @@ -994,7 +994,7 @@ async Task LoadAssemblyDynamically(string asm_file, string pdb_file) expression = $"{{ let asm_b64 = '{asm_base64}'; let pdb_b64 = '{pdb_base64}'; invoke_static_method('[debugger-test] LoadDebuggerTest:LoadLazyAssembly', asm_b64, pdb_b64); }}" }); - Result load_assemblies_res = await ctx.cli.SendCommand("Runtime.evaluate", load_assemblies, ctx.token); + Result load_assemblies_res = await cli.SendCommand("Runtime.evaluate", load_assemblies, token); Assert.True(load_assemblies_res.IsOk); } From de5f2f2e339a905f02309cfca5a854954de93cb9 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 20 Nov 2020 17:30:33 -0500 Subject: [PATCH 16/21] [wasm][debugger][tests] Remove unused insp.Ready method --- .../debugger/DebuggerTestSuite/Inspector.cs | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs index fe526ab06a3cd6..e7001473792669 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs @@ -131,40 +131,6 @@ async Task OnMessage(string method, JObject args, CancellationToken token) } } - public async Task Ready(Func? cb = null, TimeSpan? span = null) - { - try - { - Func)>> fn = (_client, _token) => - { - Func)> getInitCmdFn = (cmd_name) => (cmd_name, _client.SendCommand(cmd_name, null, _token)); - return new List<(string, Task)> - { - getInitCmdFn("Profiler.enable"), - getInitCmdFn("Runtime.enable"), - getInitCmdFn("Debugger.enable"), - getInitCmdFn("Runtime.runIfWaitingForDebugger") - }; - }; - - await OpenSessionAsync(fn, span); - if (cb != null) - await cb(Client, _cancellationTokenSource.Token).ConfigureAwait(false); - } - catch (Exception ex) - { - if (_logger != null) - _logger.LogError(ex.ToString()); - else - Console.WriteLine(ex); - throw; - } - finally - { - await ShutdownAsync().ConfigureAwait(false); - } - } - public async Task OpenSessionAsync(Func)>> getInitCmds, TimeSpan? span = null) { var start = DateTime.Now; From e88344638b31cf92b919ebf1c42208dd97f0578e Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 24 Nov 2020 16:14:40 -0500 Subject: [PATCH 17/21] [wasm][debugger][tests] Improve console logging output - single line - add timestamps --- .../wasm/debugger/DebuggerTestSuite/Inspector.cs | 12 ++++++++++-- .../debugger/DebuggerTestSuite/TestHarnessProxy.cs | 10 +++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs index e7001473792669..9c56d9552ce816 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; using Microsoft.WebAssembly.Diagnostics; using Newtonsoft.Json.Linq; @@ -37,8 +38,15 @@ public Inspector() _cancellationTokenSource = new CancellationTokenSource(); Token = _cancellationTokenSource.Token; - _loggerFactory = LoggerFactory.Create( - builder => builder.AddConsole().AddFilter(null, LogLevel.Trace)); + _loggerFactory = LoggerFactory.Create(builder => + { + builder.AddSimpleConsole(c => + { + c.ColorBehavior = LoggerColorBehavior.Enabled; + c.TimestampFormat = "[HH:mm:ss.fff] "; + c.SingleLine = true; + }); + }); Client = new InspectorClient(_loggerFactory.CreateLogger()); _logger = _loggerFactory.CreateLogger(); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessProxy.cs b/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessProxy.cs index d50a8454949311..71c61185a9cca4 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessProxy.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessProxy.cs @@ -2,15 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; namespace Microsoft.WebAssembly.Diagnostics { @@ -38,7 +37,12 @@ public static Task Start(string chromePath, string appPath, string pagePath) }) .ConfigureLogging(logging => { - logging.AddConsole(); + logging.AddSimpleConsole(c => + { + c.ColorBehavior = LoggerColorBehavior.Enabled; + c.TimestampFormat = "[HH:mm:ss.fff] "; + c.SingleLine = true; + }); }) .ConfigureServices((ctx, services) => { From e51f5b656cda87227f0cffbf23790b9f82041604 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 24 Nov 2020 16:49:47 -0500 Subject: [PATCH 18/21] [wasm][debugger][tests] Add testId to the logger names --- .../BrowserDebugProxy/DebuggerProxy.cs | 4 +- .../BrowserDebugProxy/DevToolsProxy.cs | 6 ++- .../debugger/BrowserDebugProxy/MonoProxy.cs | 2 +- .../DebuggerTestSuite/DebuggerTestBase.cs | 6 ++- .../DebuggerTestSuite/DevToolsClient.cs | 4 +- .../debugger/DebuggerTestSuite/Inspector.cs | 10 ++-- .../DebuggerTestSuite/InspectorClient.cs | 2 +- .../DebuggerTestSuite/TestHarnessStartup.cs | 53 +++++++++++-------- 8 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebuggerProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebuggerProxy.cs index 197f4dd6cea5db..3c07c17465fc97 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebuggerProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebuggerProxy.cs @@ -17,9 +17,9 @@ public class DebuggerProxy { private readonly MonoProxy proxy; - public DebuggerProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList) + public DebuggerProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList, string id = "default") { - proxy = new MonoProxy(loggerFactory, urlSymbolServerList); + proxy = new MonoProxy(loggerFactory, urlSymbolServerList, id); } public Task Run(Uri browserUri, WebSocket ideSocket) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs index 8474a2dc21cdc1..002219970f8a2a 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs @@ -28,10 +28,12 @@ internal class DevToolsProxy private List queues = new List(); protected readonly ILogger logger; + protected readonly string _id; - public DevToolsProxy(ILoggerFactory loggerFactory) + public DevToolsProxy(string id, ILoggerFactory loggerFactory) { - logger = loggerFactory.CreateLogger(); + _id = id; + logger = loggerFactory.CreateLogger($"{typeof(DevToolsProxy)}-{_id}"); } protected virtual Task AcceptEvent(SessionId sessionId, string method, JObject args, CancellationToken token) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 180fb8cc7439c3..67b8eb414f3a6c 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -26,7 +26,7 @@ internal class MonoProxy : DevToolsProxy private HashSet sessions = new HashSet(); private Dictionary contexts = new Dictionary(); - public MonoProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList) : base(loggerFactory) + public MonoProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList, string id = "unknown") : base(id, loggerFactory) { this.urlSymbolServerList = urlSymbolServerList ?? new List(); } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index 247778b97454a5..e860e413b4f5c2 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -30,6 +30,7 @@ public class DebuggerTestBase : IAsyncLifetime public bool UseCallFunctionOnBeforeGetProperties; static string s_debuggerTestAppPath; + static int s_nextId; protected static string DebuggerTestAppPath { get @@ -77,9 +78,12 @@ static string FindChromePath() throw new Exception("Could not find an installed Chrome to use"); } + public string Id { get; init; } + public DebuggerTestBase(string driver = "debugger-driver.html") { - insp = new Inspector(); + Id = Interlocked.Increment(ref s_nextId).ToString(); + insp = new Inspector(Id); cli = insp.Client; scripts = SubscribeToScripts(insp); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs index 7c5874df227171..addc4b2d47836a 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs @@ -20,12 +20,14 @@ internal class DevToolsClient : IDisposable TaskCompletionSource _shutdownRequested = new TaskCompletionSource(); TaskCompletionSource _newSendTaskAvailable = new (); protected readonly ILogger logger; + protected readonly string _id; public event EventHandler<(RunLoopStopReason reason, Exception ex)> RunLoopStopped; - public DevToolsClient(ILogger logger) + public DevToolsClient(string id, ILogger logger) { this.logger = logger; + _id = id; } ~DevToolsClient() diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs index 9c56d9552ce816..741b6eaf74ab7d 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs @@ -29,12 +29,14 @@ class Inspector public InspectorClient Client { get; } private CancellationTokenSource _cancellationTokenSource; + private readonly string _id; protected ILoggerFactory _loggerFactory; protected ILogger _logger; - public Inspector() + public Inspector(string id) { + _id = id; _cancellationTokenSource = new CancellationTokenSource(); Token = _cancellationTokenSource.Token; @@ -48,8 +50,8 @@ public Inspector() }); }); - Client = new InspectorClient(_loggerFactory.CreateLogger()); - _logger = _loggerFactory.CreateLogger(); + Client = new InspectorClient(_id, _loggerFactory.CreateLogger($"{typeof(InspectorClient)}-{_id}")); + _logger = _loggerFactory.CreateLogger($"{typeof(Inspector)}-{_id}"); } public Task WaitFor(string what) @@ -146,7 +148,7 @@ public async Task OpenSessionAsync(Func diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs b/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs index 6f221e9a7e32ac..d2033c7badc2f8 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs @@ -17,7 +17,7 @@ internal class InspectorClient : DevToolsClient Func onEvent; int next_cmd_id; - public InspectorClient(ILogger logger) : base(logger) { } + public InspectorClient(string id, ILogger logger) : base(id, logger) { } Task HandleMessage(string msg, CancellationToken token) { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessStartup.cs b/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessStartup.cs index e7be4958421bed..6454b09a57631d 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessStartup.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessStartup.cs @@ -20,6 +20,7 @@ using Microsoft.Extensions.Options; using Newtonsoft.Json.Linq; using System.Collections.Generic; +using Microsoft.Extensions.Primitives; namespace Microsoft.WebAssembly.Diagnostics { @@ -32,7 +33,7 @@ public TestHarnessStartup(IConfiguration configuration) } public IConfiguration Configuration { get; set; } - public ILogger Logger { get; private set; } + public ILogger ServerLogger { get; private set; } private ILoggerFactory _loggerFactory; @@ -46,7 +47,7 @@ public void ConfigureServices(IServiceCollection services) async Task SendNodeVersion(HttpContext context) { - Logger.LogTrace("hello chrome! json/version"); + ServerLogger.LogTrace("hello chrome! json/version"); var resp_obj = new JObject(); resp_obj["Browser"] = "node.js/v9.11.1"; resp_obj["Protocol-Version"] = "1.1"; @@ -57,7 +58,7 @@ async Task SendNodeVersion(HttpContext context) async Task SendNodeList(HttpContext context) { - Logger.LogTrace("webserver: hello chrome! json/list"); + ServerLogger.LogTrace("webserver: hello chrome! json/list"); try { var response = new JArray(JObject.FromObject(new @@ -71,13 +72,13 @@ async Task SendNodeList(HttpContext context) webSocketDebuggerUrl = "ws://localhost:9300/91d87807-8a81-4f49-878c-a5604103b0a4" })).ToString(); - Logger.LogTrace($"webserver: sending: {response}"); + ServerLogger.LogTrace($"webserver: sending: {response}"); await context.Response.WriteAsync(response, new CancellationTokenSource().Token); } - catch (Exception e) { Logger.LogError(e, "webserver: SendNodeList failed"); } + catch (Exception e) { ServerLogger.LogError(e, "webserver: SendNodeList failed"); } } - public async Task LaunchAndServe(ProcessStartInfo psi, HttpContext context, Func> extract_conn_url) + public async Task LaunchAndServe(ProcessStartInfo psi, HttpContext context, string testId, ILogger testLogger, Func> extract_conn_url) { if (!context.WebSockets.IsWebSocketRequest) @@ -94,7 +95,7 @@ public async Task LaunchAndServe(ProcessStartInfo psi, HttpContext context, Func proc.ErrorDataReceived += (sender, e) => { var str = e.Data; - Logger.LogTrace($"browser-stderr: {str}"); + testLogger.LogTrace($"browser-stderr: {str}"); if (tcs.Task.IsCompleted) return; @@ -108,7 +109,7 @@ public async Task LaunchAndServe(ProcessStartInfo psi, HttpContext context, Func proc.OutputDataReceived += (sender, e) => { - Logger.LogTrace($"browser-stdout: {e.Data}"); + testLogger.LogTrace($"browser-stdout: {e.Data}"); }; proc.BeginErrorReadLine(); @@ -116,24 +117,24 @@ public async Task LaunchAndServe(ProcessStartInfo psi, HttpContext context, Func if (await Task.WhenAny(tcs.Task, Task.Delay(5000)) != tcs.Task) { - Logger.LogError("Didnt get the con string after 5s."); + testLogger.LogError("Didnt get the con string after 5s."); throw new Exception("node.js timedout"); } var line = await tcs.Task; var con_str = extract_conn_url != null ? await extract_conn_url(line) : line; - Logger.LogInformation($"launching proxy for {con_str}"); + testLogger.LogInformation($"launching proxy for {con_str}"); - var proxy = new DebuggerProxy(_loggerFactory, null); + var proxy = new DebuggerProxy(_loggerFactory, null, testId); var browserUri = new Uri(con_str); var ideSocket = await context.WebSockets.AcceptWebSocketAsync(); await proxy.Run(browserUri, ideSocket); - Logger.LogInformation("Proxy done"); + testLogger.LogInformation("Proxy done"); } catch (Exception e) { - Logger.LogError("got exception {0}", e); + testLogger.LogError("got exception {0}", e); } finally { @@ -148,7 +149,7 @@ public async Task LaunchAndServe(ProcessStartInfo psi, HttpContext context, Func // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IOptionsMonitor optionsAccessor, IWebHostEnvironment env, ILogger logger, ILoggerFactory loggerFactory) { - this.Logger = logger; + this.ServerLogger = logger; this._loggerFactory = loggerFactory; app.UseWebSockets(); @@ -172,7 +173,13 @@ public void Configure(IApplicationBuilder app, IOptionsMonitor { - Logger.LogInformation("New test request"); + string testId = "unknown"; + if (context.Request.Query.TryGetValue("testId", out StringValues values)) + testId = values.ToString(); + + var testLogger = _loggerFactory.CreateLogger($"{typeof(TestHarnessProxy)}-{testId}"); + + testLogger.LogInformation("New test request"); try { var client = new HttpClient(); @@ -184,7 +191,7 @@ public void Configure(IApplicationBuilder app, IOptionsMonitor + await LaunchAndServe(psi, context, testId, testLogger, async (str) => { var start = DateTime.Now; JArray obj = null; @@ -198,7 +205,7 @@ await LaunchAndServe(psi, context, async (str) => await Task.Delay(100); var res = await client.GetStringAsync(new Uri(new Uri(str), "/json/list")); - Logger.LogTrace("res is {0}", res); + testLogger.LogTrace("res is {0}", res); if (!String.IsNullOrEmpty(res)) { @@ -211,29 +218,29 @@ await LaunchAndServe(psi, context, async (str) => var elapsed = DateTime.Now - start; if (elapsed.Milliseconds > 5000) { - Logger.LogError($"Unable to get DevTools /json/list response in {elapsed.Seconds} seconds, stopping"); + testLogger.LogError($"Unable to get DevTools /json/list response in {elapsed.Seconds} seconds, stopping"); return null; } } var wsURl = obj[0]?["webSocketDebuggerUrl"]?.Value(); - Logger.LogTrace(">>> {0}", wsURl); + testLogger.LogTrace(">>> {0}", wsURl); return wsURl; }); } catch (Exception ex) { - Logger.LogError($"launch-chrome-and-connect failed with {ex.ToString()}"); + testLogger.LogError($"launch-chrome-and-connect failed with {ex.ToString()}"); } }); }); if (options.NodeApp != null) { - Logger.LogTrace($"Doing the nodejs: {options.NodeApp}"); + ServerLogger.LogTrace($"Doing the nodejs: {options.NodeApp}"); var nodeFullPath = Path.GetFullPath(options.NodeApp); - Logger.LogTrace(nodeFullPath); + ServerLogger.LogTrace(nodeFullPath); var psi = new ProcessStartInfo(); psi.UseShellExecute = false; @@ -251,7 +258,7 @@ await LaunchAndServe(psi, context, async (str) => router.MapGet("json/version", SendNodeVersion); router.MapGet("launch-done-and-connect", async context => { - await LaunchAndServe(psi, context, null); + await LaunchAndServe(psi, context, null, null, null); }); }); } From 454ac4d428b865604f8a298b4c688833a243dabc Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 24 Nov 2020 21:07:19 -0500 Subject: [PATCH 19/21] use browser session stuff --- eng/Configurations.props | 1 + .../debugger/BrowserDebugProxy/MonoProxy.cs | 3 +- .../debugger/DebuggerTestSuite/ArrayTests.cs | 2 +- .../DebuggerTestSuite/BadHarnessInitTests.cs | 8 +- .../DebuggerTestSuite/BrowserCdpConnection.cs | 236 ++++++++++++++++++ .../DebuggerTestSuite/BrowserInstance.cs | 215 ++++++++++++++++ .../debugger/DebuggerTestSuite/BrowserPool.cs | 60 +++++ .../DebuggerTestSuite/BrowserSession.cs | 49 ++++ .../DebuggerTestSuite/BrowserTarget.cs | 45 ++++ .../DebuggerTestSuite/CallFunctionOnTests.cs | 54 ++-- .../DebuggerTestSuite/DebuggerTestBase.cs | 83 +++--- .../DebuggerTestSuite/DevToolsClient.cs | 29 ++- .../EvaluateOnCallFrameTests.cs | 2 +- .../DebuggerTestSuite/ExceptionTests.cs | 1 - .../DebuggerTestSuite/GetPropertiesTests.cs | 2 +- .../DebuggerTestSuite/HarnessTests.cs | 2 +- .../debugger/DebuggerTestSuite/Inspector.cs | 74 +++--- .../DebuggerTestSuite/InspectorClient.cs | 85 +++++-- .../debugger/DebuggerTestSuite/MonoJsTests.cs | 16 +- .../DebuggerTestSuite/ProxyLauncherData.cs | 13 + .../DebuggerTestSuite/SteppingTests.cs | 3 +- .../DebuggerTestSuite/TestHarnessProxy.cs | 34 ++- .../DebuggerTestSuite/TestHarnessStartup.cs | 222 +++++----------- .../wasm/debugger/DebuggerTestSuite/Tests.cs | 16 +- .../DebuggerTestSuite/appsettings.json | 6 +- 25 files changed, 931 insertions(+), 330 deletions(-) create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/BrowserCdpConnection.cs create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/BrowserPool.cs create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/BrowserSession.cs create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/BrowserTarget.cs create mode 100644 src/mono/wasm/debugger/DebuggerTestSuite/ProxyLauncherData.cs diff --git a/eng/Configurations.props b/eng/Configurations.props index c4bd905ddc6278..9da7fe86922370 100644 --- a/eng/Configurations.props +++ b/eng/Configurations.props @@ -73,6 +73,7 @@ <_runtimeOS>$(RuntimeOS) + <__DistroRid>osx-x64 <_parseDistroRid>$(__DistroRid) <_parseDistroRid Condition="'$(_parseDistroRid)' == ''">$([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier) <_distroRidIndex>$(_parseDistroRid.LastIndexOfAny("-")) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 67b8eb414f3a6c..9c83ce3fa7757b 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -195,6 +195,7 @@ private async Task IsRuntimeAlreadyReadyAlready(SessionId sessionId, Cance protected override async Task AcceptCommand(MessageId id, string method, JObject args, CancellationToken token) { + logger.LogDebug($"AcceptCommand {method}"); // Inspector doesn't use the Target domain or sessions // so we try to init immediately if (id == SessionId.Null) @@ -390,7 +391,7 @@ protected override async Task AcceptCommand(MessageId id, string method, J } case "DotnetDebugger.getMethodLocation": { - Console.WriteLine("set-breakpoint-by-method: " + id + " " + args); + logger.LogDebug($"set-breakpoint-by-method: {id} {args}"); DebugStore store = await RuntimeReady(id, token); string aname = args["assemblyName"]?.Value(); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs index dfe99aed5a7694..2a24d5648d1e46 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ArrayTests.cs @@ -278,7 +278,7 @@ async Task GetObjectWithCFO(string objectId, JObject fn_args = null) cfo_args["arguments"] = fn_args; // callFunctionOn - var result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); + var result = await SendCommand("Runtime.callFunctionOn", cfo_args, token); return await GetProperties(result.Value["result"]["objectId"]?.Value(), fn_args); } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs index 6d83a777253e40..e7424c85c98828 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BadHarnessInitTests.cs @@ -21,16 +21,16 @@ public async Task InvalidInitCommands() { var bad_cmd_name = "non-existant.command"; - Func)>> fn = (client, token) => + Func)>> fn = (session, token) => new List<(string, Task)> { - ("Profiler.enable", client.SendCommand("Profiler.enable", null, token)), - (bad_cmd_name, client.SendCommand(bad_cmd_name, null, token)) + ("Profiler.enable", session.SendCommand("Profiler.enable", null, token)), + (bad_cmd_name, session.SendCommand(bad_cmd_name, null, token)) }; await Ready(); - var ae = await Assert.ThrowsAsync(async () => await insp.OpenSessionAsync(fn)); + var ae = await Assert.ThrowsAsync(async () => await insp.OpenSessionAsync("/debugger-driver.html", fn)); Assert.Contains(bad_cmd_name, ae.Message); } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BrowserCdpConnection.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserCdpConnection.cs new file mode 100644 index 00000000000000..cb4a69a3a328cc --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserCdpConnection.cs @@ -0,0 +1,236 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; + +#nullable enable + +namespace Microsoft.WebAssembly.Diagnostics +{ + internal class BrowserCdpConnection : IAsyncDisposable, IDisposable + { + public ConcurrentDictionary Targets = new (); + public ConcurrentDictionary Sessions = new (); + + public InspectorClient InspectorClient { get; set; } + public ILogger Logger { get; private set; } + + private CancellationTokenSource _cts; + + private const int ConcurrentChromeConnections = 6; + private static SemaphoreSlim s_semaphore = new (1); + private static SemaphoreSlim s_connectionSemaphore = new (ConcurrentChromeConnections); + private static MethodInfo? threadpoolPump; + private static MethodInfo? timerPump; + + private BrowserCdpConnection(InspectorClient client, ILogger logger, CancellationTokenSource cts) + { + Logger = logger; + _cts = cts; + InspectorClient = client; + InspectorClient.RunLoopStopped += (_, _) => Dispose(); + } + + public static async Task Open(int browser_id, string testId, ILogger logger, CancellationTokenSource cts) + { + await s_connectionSemaphore.WaitAsync(); + // try + // { + var client = new InspectorClient(testId, logger); + var connection = new BrowserCdpConnection(client, logger, cts); + + // FIXME: use a per-test logger here + await client.Connect( + new Uri($"ws://{TestHarnessProxy.Endpoint.Authority}/connect-to-devtools/{browser_id}?testId={testId}"), + connection.MessageFallbackHandler, + cts.Token); + + return connection; + // } + // finally + // { + // s_connectionSemaphore.Release(); + // } + } + + public void RemoveSession(SessionId sessionId) + { + Sessions.Remove(sessionId.sessionId, out _); + InspectorClient.RemoveMessageHandlerForSession(sessionId); + } + + private async Task MessageFallbackHandler(JObject msg, CancellationToken token) + { + Logger.LogDebug($"Fallback: {msg}"); + // string? method = msg["method"]?.Value(); + // JObject? args = msg["params"]?.Value(); + // if (method == null) + // return; + + // switch (method) + // { + // case "Target.detachedFromTarget": + // { + // // this has sessionId + // // string? sessionId = args?["sessionId"]?.Value(); + // // // FIXME: remove the target, and the session message handler + // // // Console.WriteLine ($"\tsessionId: {sessionId}"); + // // if (sessionId != null && Sessions.TryGetValue(sessionId, out var session)) + // // { + // // // await session.DisposeAsync(); + // // } + // break; + // } + + // targetDestroyed, .. + // } + + await Task.CompletedTask; + } + + public async Task SendCommandNoSession(string method, JObject? args, CancellationToken token) + => await InspectorClient.SendCommand(method, args, token); + + public async Task SendCommand(SessionId sessionId, string method, JObject? args, CancellationToken token) + => await InspectorClient.SendCommand(sessionId, method, args, token); + + public async Task OpenSession( + string url, Func onMessage, + ILogger logger, string id, CancellationTokenSource cts, BrowserTarget? target=null) + { + Result res; + if (target == null) + { + res = await SendCommandNoSession("Target.createBrowserContext", JObject.FromObject(new + { + disposeOnDetach = true + }), cts.Token); + if (!res.IsOk) + throw new Exception($"Target.createBrowserContext failed with {res}"); + + string browserContextId = res.Value["browserContextId"]?.Value() ?? throw new Exception($"Missing browserContextId in {res}"); + + res = await SendCommandNoSession("Target.createTarget", JObject.FromObject(new + { + url, + browserContextId + }), cts.Token); + if (!res.IsOk) + throw new Exception($"Target.createTarget failed with {res}"); + + var targetId = res.Value["targetId"]?.Value(); + if (string.IsNullOrEmpty(targetId)) + throw new Exception($"Target.createTarget missing a targetId: {res}"); + + res = await SendCommandNoSession("Target.activateTarget", res.Value, cts.Token); + target = new BrowserTarget(browserContextId, targetId, url, this); + Targets[target.Id] = target; + } + + res = await SendCommandNoSession("Target.attachToTarget", JObject.FromObject(new + { + targetId = target.Id, + flatten = true + }), cts.Token); + // Console.WriteLine($"----> attachToTarget: {res}"); + if (res.IsErr) + throw new Exception($"Target.attachToTarget failed with {res}"); + + // TODO: um.. do this in response to attachedToTarget + if (string.IsNullOrEmpty(res.Value["sessionId"]?.Value())) + throw new Exception($"Target.attachToTarget didn't return any sessionId. {res}"); + + var sessionId = new SessionId(res.Value["sessionId"]?.Value()); + InspectorClient.AddMessageHandlerForSession(sessionId, onMessage); + var session = new BrowserSession(sessionId, target); + Sessions[sessionId.sessionId] = session; + + return session; + } + + public void Dispose() => CloseAsync().Wait(); + public async Task CloseAsync() => await DisposeAsync(); + + // bool _disposed = false; + bool _disposing = false; + bool _disposed = false; + public async ValueTask DisposeAsync() + { + // if (_disposed) + // throw new ObjectDisposedException(nameof(BrowserTarget)); + + if (_disposing || _disposed) + return; + + Logger.LogDebug($"--------------------- CdpConnection.DisposeAsync, _disposing: {_disposing}, InspectorClient.IsRunning: {InspectorClient.IsRunning} ---------------"); + _disposing = true; + // Console.WriteLine (Environment.StackTrace); + // close this + // this might not actually work.. eg. if devtoolsclient is no longer running!! + if (InspectorClient.IsRunning) + { + foreach (var session in Sessions.Values) + await session.DisposeAsync(); + + Sessions.Clear(); + + foreach (var target in Targets.Values) + await target.DisposeAsync(); + + Targets.Clear(); + //FIXME: + // Result res = await InspectorClient.SendCommand("Target.closeTarget", JObject.FromObject(new + // { + // targetId = Id + // }), new CancellationToken(false)); + // _logger.LogDebug($"-- BrowserTarget.DisposeAsync: closeTarget sent, result: {res}"); + + // FIXME: um if closeTarget failed, then also we need to stop the client, and close the socket! + // if (!res.IsOk) + // throw new Exception("Failed to close target {TargetId}"); + } + + var res = await InspectorClient.SendCommand("Target.getTargets", null, new CancellationToken()); + Logger.LogDebug("getTargets returned {res}"); + res = await InspectorClient.SendCommand("Target.getBrowserContexts", null, new CancellationToken()); + Logger.LogDebug("getBrowserContextx returned {res}"); + + // await InspectorClient.DisposeAsync(); + InspectorClient.Dispose(); + + await s_semaphore.WaitAsync(); + try + { + if (threadpoolPump == null || timerPump == null) + { + threadpoolPump = typeof(ThreadPool).GetMethod("PumpThreadPool", BindingFlags.NonPublic | BindingFlags.Static); + timerPump = Type.GetType("System.Threading.TimerQueue")?.GetMethod("PumpTimerQueue", BindingFlags.NonPublic | BindingFlags.Static); + } + + Logger.LogDebug($"CdpConnection.DisposeAsync: TP PendingWorkItemCount: {ThreadPool.PendingWorkItemCount}\n"); + threadpoolPump?.Invoke(this, null); + timerPump?.Invoke(this, null); + Logger.LogDebug($"CdpConnection.DisposeAsync AFTER: TP PendingWorkItemCount: {ThreadPool.PendingWorkItemCount}\n"); + } finally { + s_semaphore.Release(); + } + + s_connectionSemaphore.Release(); + + Logger.LogDebug($"-- BrowserTarget.DisposeAsync: done"); + _disposing = false; + _disposed = true; + // s_semaphore.Release(); + + await Task.CompletedTask; + } + } + +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs new file mode 100644 index 00000000000000..170332cabb3598 --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs @@ -0,0 +1,215 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; + +#nullable enable + +namespace Microsoft.WebAssembly.Diagnostics +{ + internal class BrowserInstance : IAsyncDisposable + { + static int s_nextId = 0; + + private Uri _remoteConnectionUri; + public int Id { get; private set; } + + private Process _process; + private ILogger _poolLogger; + + // private ConcurrentDictionary targets = new ConcurrentDictionary(); + public bool HasExited => _process.HasExited; + + private BrowserInstance(Uri remoteConnectionUri, Process process, ILogger logger) + { + _remoteConnectionUri = remoteConnectionUri; + _process = process; + _poolLogger = logger; + + Id = Interlocked.Increment(ref s_nextId); + + _poolLogger.LogDebug($"Adding [{Id}] = {remoteConnectionUri}"); + TestHarnessProxy.LauncherData.IdToDevToolsUrl[Id.ToString()] = remoteConnectionUri; + } + + public async Task OpenConnection(string testId, ILogger testLogger, CancellationTokenSource testCts) + { + return await BrowserCdpConnection.Open(Id, testId, testLogger, testCts); + } + + public static async Task StartAsync(ILogger logger, CancellationToken token, int port=0) + { + (var _process, var uri) = await LaunchChrome(new [] + { + "--headless", + "--no-first-run", + "--disable-gpu", + "--lang=en-US", + "--incognito", + $"--remote-debugging-port={port}", + "--user-data-dir=/tmp/asd", + "--enable" + + }, logger); + + return new BrowserInstance(uri, _process, logger); + } + + public async Task OpenSession(string relativeUrl, + Func onMessage, + Action<(RunLoopStopReason reason, Exception? ex)> onRunLoopFailedOrCanceled, + string testId, + ILogger logger, + CancellationTokenSource cts, + BrowserCdpConnection? connection=null, + BrowserTarget? target=null) + { + + if (connection == null) + { + connection = await OpenConnection(testId, logger, cts); + connection.InspectorClient!.RunLoopStopped += (_, args) => onRunLoopFailedOrCanceled(args); + } + + var session = await connection.OpenSession(//this, + $"{TestHarnessProxy.Endpoint.Scheme}://{TestHarnessProxy.Endpoint.Authority}{relativeUrl}", + onMessage, + logger, + testId, + cts, + target); + + return session; + } + + static async Task<(Process, Uri)> LaunchChrome(string[] args, ILogger logger) + { + var psi = new ProcessStartInfo + { + Arguments = string.Join(' ', args), + UseShellExecute = false, + FileName = FindChromePath(), + RedirectStandardError = true, + RedirectStandardOutput = true + }; + + var tcs = new TaskCompletionSource(); + + logger.LogDebug($"Launching browser with '{psi.FileName}' args: '{psi.Arguments}'"); + var process = Process.Start(psi); + try + { + process!.ErrorDataReceived += (sender, e) => + { + string? str = e.Data; + logger.LogTrace($"browser-stderr: {str}"); + + if (tcs.Task.IsCompleted) + return; + + var match = parseConnection.Match(str!); + if (match.Success) + { + tcs.TrySetResult(match.Groups[1].Captures[0].Value); + } + }; + + process.OutputDataReceived += (sender, e) => + { + logger.LogTrace($"browser-stdout: {e.Data}"); + }; + + process.Exited += (_, e) => logger.LogDebug($"browser exited with {process.ExitCode}"); + + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); + + if (await Task.WhenAny(tcs.Task, Task.Delay(5000)) != tcs.Task) + { + logger.LogError("Didnt get the con string after 5s."); + + process.CancelErrorRead(); + process.CancelOutputRead(); + process.Kill(); + process.WaitForExit(); + process.Close(); + + + throw new Exception("Didn't get the remote connection string after 5s"); + } + + var line = await tcs.Task; + logger.LogDebug($"Chrome devtools listening on {line}"); + + return (process, new Uri(line)); + } + catch (Exception e) + { + logger.LogError("got exception {0}", e); + throw; + } + } + + // public void Dispose() + // { + // } + + private bool _disposing = false; + private bool _disposed = false; + public async ValueTask DisposeAsync() + { + if (_disposed || _disposing) + return; + + _disposing = true; + //FIXME: um..this Id type, and the type in the dict need to match! + TestHarnessProxy.LauncherData.IdToDevToolsUrl.Remove(Id.ToString()); + await Task.CompletedTask; + // var tasks = new List(); + // foreach (var tgt in targets.Values) + // await tgt.DisposeAsync(); + // tasks.Add(tgt.DisposeAsync()); + + // await Task.WhenAll(tasks); + _disposing = false; + _disposed = true; + } + + static string[] PROBE_LIST = { + "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", + "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", + "/usr/bin/chromium", + "/usr/bin/chromium-browser", + }; + + static string? chrome_path; + + static string FindChromePath() + { + if (chrome_path != null) + return chrome_path; + + foreach (var s in PROBE_LIST) + { + if (File.Exists(s)) + { + chrome_path = s; + // Console.WriteLine($"Using chrome path: ${s}"); + return s; + } + } + throw new Exception("Could not find an installed Chrome to use"); + } + + static Regex parseConnection = new Regex(@"listening on (ws?s://[^\s]*)"); + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BrowserPool.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserPool.cs new file mode 100644 index 00000000000000..2e99c7845967b9 --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserPool.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +#nullable enable + +namespace Microsoft.WebAssembly.Diagnostics +{ + internal class BrowserPool + { + static BrowserInstance? s_browserInstance; + static SemaphoreSlim s_myLock = new SemaphoreSlim(1); + static Task? s_proxyTask; + + public static async Task GetInstanceAsync(ILogger logger, string debuggerTestPath, CancellationToken token) + { + if (s_browserInstance != null && !s_browserInstance.HasExited) + return s_browserInstance; + + //FIXME: um use some better way to do this.. we want to init just once + await s_myLock.WaitAsync(); + try { + if (s_browserInstance?.HasExited == true) + { + logger.LogError($"Chrome has crashed, cleaning up"); + await s_browserInstance.DisposeAsync(); + s_browserInstance = null; + } + + if (s_proxyTask == null) + s_proxyTask = TestHarnessProxy.Start(debuggerTestPath, logger, token); + + if (s_browserInstance == null) + { + Task browserTask = BrowserInstance.StartAsync(logger, token); + + await Task.WhenAll(s_proxyTask, browserTask); + if (s_proxyTask.IsCompletedSuccessfully && browserTask.IsCompletedSuccessfully) + { + s_browserInstance = await browserTask; + } + else + { + // -- umm.. throw!! + logger.LogError($"----- EEERRRRRRRRRRR.. proxy or browser launch failed"); + throw new Exception("proxy or brwoser failed"); + } + } + } finally { + s_myLock.Release(); + } + + return s_browserInstance; + } + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BrowserSession.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserSession.cs new file mode 100644 index 00000000000000..8e8f766ef73509 --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserSession.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; + +#nullable enable + +namespace Microsoft.WebAssembly.Diagnostics +{ + internal class BrowserSession : IAsyncDisposable + { + public SessionId Id { get; private set; } + public BrowserTarget Target { get; private set; } + public BrowserCdpConnection Connection => Target.Connection; + public DateTime StartTime { get; } = DateTime.Now; + + private bool _disposed = false; + //fIXME: disposing + + public BrowserSession(SessionId id, BrowserTarget target) + { + Id = id; + Target = target; + } + + public async Task SendCommand(string method, JObject? args, CancellationToken token) + => await Connection.SendCommand(Id, method, args, token); + + public async Task ShutdownConnection(CancellationToken token) + => await Connection.DisposeAsync(); + + public async ValueTask DisposeAsync() + { + if (_disposed) + return; + + // FIXME: remove this from Connection + + await Connection.SendCommandNoSession("Target.detachFromTarget", JObject.FromObject(new { sessionId = Id.sessionId }), new CancellationToken()); + Connection.RemoveSession(Id); + + //FIXME: check error + _disposed = true; + } + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BrowserTarget.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserTarget.cs new file mode 100644 index 00000000000000..74e1a35cc55b1d --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserTarget.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading.Tasks; + +#nullable enable + +namespace Microsoft.WebAssembly.Diagnostics +{ + internal class BrowserTarget : IAsyncDisposable + { + public string BrowserContextId { get; private set; } + public string Id { get; private set; } + public string Url { get; private set; } + public BrowserCdpConnection Connection { get; private set; } + private bool _disposed = false; + + public BrowserTarget(string browserContextId, string id, string url, BrowserCdpConnection connection) + { + BrowserContextId = browserContextId; + Id = id; + Url = url; + Connection = connection; + } + + public async ValueTask DisposeAsync() + { + if (_disposed) + return; + + // FIXME: remove this from Connection + // var res = await Connection.SendCommand( + // "Target.closeTarget", + // JObject.FromObject(new { targetId = Id }), + // new CancellationToken()); + //FIXME: and disposeBrowserContext? + + // if (res.IsErr) + // Connection.Logger.LogError($"Target.closeTarget failed with {res}"); + _disposed = true; + await Task.CompletedTask; + } + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs index 3b320494538dd7..01a342c36fcef7 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/CallFunctionOnTests.cs @@ -30,7 +30,7 @@ await RunCallFunctionOn(eval_fn, vscode_fn0, "big", bp_loc, line, col, res_array { var is_js = bp_loc.EndsWith(".js", StringComparison.Ordinal); - var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, @@ -43,7 +43,7 @@ await RunCallFunctionOn(eval_fn, vscode_fn0, "big", bp_loc, line, col, res_array // Check for a __proto__ object // isOwn = true, accessorPropertiesOnly = false - var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, @@ -90,7 +90,7 @@ await RunCallFunctionOn(eval_fn, vscode_fn1, "big", bp_loc, line, col, var is_js = bp_loc.EndsWith(".js", StringComparison.Ordinal); // isOwn = false, accessorPropertiesOnly = true - var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, @@ -104,7 +104,7 @@ await RunCallFunctionOn(eval_fn, vscode_fn1, "big", bp_loc, line, col, // Ignoring the __proto__ property // isOwn = true, accessorPropertiesOnly = false - var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, @@ -142,7 +142,7 @@ await RunCallFunctionOn(eval_fn, var is_js = bp_loc.EndsWith(".js", StringComparison.Ordinal); // getProperties (isOwn = false, accessorPropertiesOnly = true) - var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, @@ -154,7 +154,7 @@ await RunCallFunctionOn(eval_fn, AssertEqual(0, obj_accessors.Value["result"]?.Count(), "obj_accessors-count"); // getProperties (isOwn = true, accessorPropertiesOnly = false) - var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, @@ -188,7 +188,7 @@ await RunCallFunctionOn(eval_fn, var is_js = bp_loc.EndsWith(".js"); // getProperties (own=false) - var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, @@ -202,7 +202,7 @@ await RunCallFunctionOn(eval_fn, // getProperties (own=true) // isOwn = true, accessorPropertiesOnly = false - var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, @@ -239,7 +239,7 @@ public async Task RunOnVTArray(bool roundtrip) => await RunCallFunctionOn( var ret_len = 5; // getProperties (own=false) - var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, @@ -250,7 +250,7 @@ public async Task RunOnVTArray(bool roundtrip) => await RunCallFunctionOn( // getProperties (own=true) // isOwn = true, accessorPropertiesOnly = false - var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, @@ -299,7 +299,7 @@ public async Task RunOnCFOValueTypeResult(bool roundtrip) => await RunCallFuncti { // getProperties (own=false) - var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, @@ -309,7 +309,7 @@ public async Task RunOnCFOValueTypeResult(bool roundtrip) => await RunCallFuncti // getProperties (own=true) // isOwn = true, accessorPropertiesOnly = false - var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, @@ -345,7 +345,7 @@ public async Task RunOnJSObject(bool roundtrip) => await RunCallFunctionOn( { // getProperties (own=false) - var obj_accessors = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_accessors = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = true, @@ -356,7 +356,7 @@ public async Task RunOnJSObject(bool roundtrip) => await RunCallFunctionOn( // getProperties (own=true) // isOwn = true, accessorPropertiesOnly = false - var obj_own = await cli.SendCommand("Runtime.getProperties", JObject.FromObject(new + var obj_own = await SendCommand("Runtime.getProperties", JObject.FromObject(new { objectId = result.Value["result"]["objectId"].Value(), accessorPropertiesOnly = false, @@ -446,7 +446,7 @@ public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int l // callFunctionOn var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; - var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var result = await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); var pause_location = await insp.WaitFor(Inspector.PAUSE); // Um for js we get "scriptId": "6" @@ -469,7 +469,7 @@ public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int l cfo_args["returnByValue"] = return_by_val; // callFunctionOn - result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); + result = await SendCommand("Runtime.callFunctionOn", cfo_args, token); await CheckValue(result.Value["result"], TNumber(5), "cfo-res"); cfo_args = JObject.FromObject(new @@ -484,7 +484,7 @@ public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int l cfo_args["returnByValue"] = return_by_val; // callFunctionOn - result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); + result = await SendCommand("Runtime.callFunctionOn", cfo_args, token); await CheckValue(result.Value["result"], JObject.FromObject(new { type = "string", value = "test value" }), "cfo-res"); cfo_args = JObject.FromObject(new @@ -499,7 +499,7 @@ public async Task RunOnArrayReturnPrimitive(string eval_fn, string bp_loc, int l cfo_args["returnByValue"] = return_by_val; // callFunctionOn - result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); + result = await SendCommand("Runtime.callFunctionOn", cfo_args, token); await CheckValue(result.Value["result"], JObject.Parse("{ type: 'object', subtype: 'null', value: null }"), "cfo-res"); } @@ -518,7 +518,7 @@ public async Task CFOWithSilentReturnsErrors(string eval_fn, string bp_loc, int // callFunctionOn var eval_expr = "window.setTimeout(function() { " + eval_fn + " }, 1);"; - var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var result = await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); var pause_location = await insp.WaitFor(Inspector.PAUSE); var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); @@ -538,7 +538,7 @@ public async Task CFOWithSilentReturnsErrors(string eval_fn, string bp_loc, int // callFunctionOn, Silent does not change the result, except that the error // doesn't get reported, and the execution is NOT paused even with setPauseOnException=true - result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); + result = await SendCommand("Runtime.callFunctionOn", cfo_args, token); Assert.False(result.IsOk, "result.IsOk"); Assert.True(result.IsErr, "result.IsErr"); @@ -753,7 +753,7 @@ await RunCallFunctionOn( async Task GetPropertiesAndCheckAccessors(JObject get_prop_req, int num_fields) { - var res = await cli.SendCommand("Runtime.getProperties", get_prop_req, token); + var res = await SendCommand("Runtime.getProperties", get_prop_req, token); if (!res.IsOk) Assert.True(false, $"Runtime.getProperties failed for {get_prop_req.ToString()}, with Result: {res}"); @@ -790,7 +790,7 @@ public async Task RunOnInvalidCfoId(string eval_fn, string bp_loc, int line, int objectId = ptd_id + "_invalid" }); - var res = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); + var res = await SendCommand("Runtime.callFunctionOn", cfo_args, token); Assert.True(res.IsErr); }); @@ -803,7 +803,7 @@ public async Task RunOnInvalidThirdSegmentOfObjectId(string eval_fn, string bp_l // callFunctionOn var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; - var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var result = await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); var pause_location = await insp.WaitFor(Inspector.PAUSE); var frame_locals = await GetProperties(pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value()); @@ -816,7 +816,7 @@ public async Task RunOnInvalidThirdSegmentOfObjectId(string eval_fn, string bp_l objectId = ptd_id + "_invalid" }); - var res = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); + var res = await SendCommand("Runtime.callFunctionOn", cfo_args, token); Assert.True(res.IsErr); } @@ -890,7 +890,7 @@ async Task RunCallFunctionOn(string eval_fn, string fn_decl, string local_name, // callFunctionOn var eval_expr = $"window.setTimeout(function() {{ {eval_fn} }}, 1);"; - var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var result = await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); var pause_location = await insp.WaitFor(Inspector.PAUSE); // Um for js we get "scriptId": "6" @@ -914,7 +914,7 @@ async Task RunCallFunctionOn(string eval_fn, string fn_decl, string local_name, cfo_args["returnByValue"] = returnByValue; // callFunctionOn - result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); + result = await SendCommand("Runtime.callFunctionOn", cfo_args, token); await CheckCFOResult(result); // If it wasn't `returnByValue`, then try to run a new function @@ -932,7 +932,7 @@ async Task RunCallFunctionOn(string eval_fn, string fn_decl, string local_name, if (fn_args != null) cfo_args["arguments"] = fn_args; - result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); + result = await SendCommand("Runtime.callFunctionOn", cfo_args, token); await CheckCFOResult(result); } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index e860e413b4f5c2..a9912424378273 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -23,15 +23,19 @@ public class DebuggerTestBase : IAsyncLifetime { internal InspectorClient cli; internal Inspector insp; + + protected CancellationTokenSource _cancellationTokenSource = new (); protected CancellationToken token; protected Dictionary scripts; protected Task startTask; public bool UseCallFunctionOnBeforeGetProperties; + private const int DefaultTestTimeoutMs = 1 * 60 * 1000; + static string s_debuggerTestAppPath; static int s_nextId; - protected static string DebuggerTestAppPath + public static string DebuggerTestAppPath { get { @@ -52,49 +56,25 @@ static protected string FindTestPath() throw new Exception($"Could not figure out debugger-test app path ({test_app_path}) based on the test suite location ({asm_dir})"); } - static string[] PROBE_LIST = { - "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", - "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", - "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", - "/usr/bin/chromium", - "/usr/bin/chromium-browser", - }; - static string chrome_path; - - static string FindChromePath() - { - if (chrome_path != null) - return chrome_path; - - foreach (var s in PROBE_LIST) - { - if (File.Exists(s)) - { - chrome_path = s; - Console.WriteLine($"Using chrome path: ${s}"); - return s; - } - } - throw new Exception("Could not find an installed Chrome to use"); - } - public string Id { get; init; } - public DebuggerTestBase(string driver = "debugger-driver.html") + public DebuggerTestBase()//string driver = "debugger-driver.html") { Id = Interlocked.Increment(ref s_nextId).ToString(); - insp = new Inspector(Id); + insp = new Inspector(Id, _cancellationTokenSource); cli = insp.Client; scripts = SubscribeToScripts(insp); - startTask = TestHarnessProxy.Start(FindChromePath(), DebuggerTestAppPath, driver); + token = _cancellationTokenSource.Token; + + // startTask = TestHarnessProxy.Start(DebuggerTestAppPath, } public virtual async Task InitializeAsync() { - Func)>> fn = (client, token) => + Func)>> fn = (session, token) => { - Func)> getInitCmdFn = (cmd) => (cmd, client.SendCommand(cmd, null, token)); + Func)> getInitCmdFn = (cmd) => (cmd, session.SendCommand(cmd, null, token)); var init_cmds = new List<(string, Task)> { getInitCmdFn("Profiler.enable"), @@ -106,13 +86,18 @@ public virtual async Task InitializeAsync() return init_cmds; }; - await Ready(); - await insp.OpenSessionAsync(fn); + TimeSpan? span = null; //TODO + + _cancellationTokenSource.CancelAfter(span?.Milliseconds ?? DefaultTestTimeoutMs); + + // await Ready(); + await insp.OpenSessionAsync("/debugger-driver.html", fn); + cli = insp.Client; } public virtual async Task DisposeAsync() => await insp.ShutdownAsync().ConfigureAwait(false); - public Task Ready() => startTask; + public Task Ready() => Task.CompletedTask; internal Dictionary dicScriptsIdToUrl; internal Dictionary dicFileToUrl; @@ -182,7 +167,7 @@ internal async Task CheckInspectLocalsAtBreakpointSite(string type, string metho var bp = await SetBreakpointInMethod(assembly, type, method, line_offset, col); var args = JObject.FromObject(new { expression = eval_expression }); - var res = await cli.SendCommand("Runtime.evaluate", args, token); + var res = await SendCommand("Runtime.evaluate", args, token); if (!res.IsOk) { Console.WriteLine($"Failed to run command {method} with args: {args?.ToString()}\nresult: {res.Error.ToString()}"); @@ -343,9 +328,9 @@ internal JToken GetAndAssertObjectWithName(JToken obj, string name, string label return l; } - internal async Task SendCommand(string method, JObject args) + internal async Task SendCommand(string method, JObject args, CancellationToken? token = null) { - var res = await cli.SendCommand(method, args, token); + var res = await insp.Session?.SendCommand(method, args, token ?? _cancellationTokenSource.Token); if (!res.IsOk) { Console.WriteLine($"Failed to run command {method} with args: {args?.ToString()}\nresult: {res.Error.ToString()}"); @@ -356,7 +341,7 @@ internal async Task SendCommand(string method, JObject args) internal async Task Evaluate(string expression) { - return await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expression })); + return await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expression }), _cancellationTokenSource.Token); } internal void AssertLocation(JObject args, string methodName) @@ -387,7 +372,7 @@ internal async Task InvokeGetter(JToken obj, object arguments, string fn if (returnByValue != null) req["returnByValue"] = returnByValue.Value; - var res = await cli.SendCommand("Runtime.callFunctionOn", req, token); + var res = await SendCommand("Runtime.callFunctionOn", req, token); Assert.True(expect_ok == res.IsOk, $"InvokeGetter failed for {req} with {res}"); return res; @@ -419,7 +404,7 @@ internal async Task EvaluateAndCheck(string expression, string script_l internal async Task SendCommandAndCheck(JObject args, string method, string script_loc, int line, int column, string function_name, Func wait_for_event_fn = null, Action locals_fn = null, string waitForEvent = Inspector.PAUSE) { - var res = await cli.SendCommand(method, args, token); + var res = await SendCommand(method, args, token); if (!res.IsOk) { Console.WriteLine($"Failed to run command {method} with args: {args?.ToString()}\nresult: {res.Error.ToString()}"); @@ -735,7 +720,7 @@ internal async Task GetProperties(string id, JToken fn_args = null, bool if (fn_args != null) cfo_args["arguments"] = fn_args; - var result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token); + var result = await SendCommand("Runtime.callFunctionOn", cfo_args, token); AssertEqual(expect_ok, result.IsOk, $"Runtime.getProperties returned {result.IsOk} instead of {expect_ok}, for {cfo_args.ToString()}, with Result: {result}"); if (!result.IsOk) return null; @@ -755,7 +740,7 @@ internal async Task GetProperties(string id, JToken fn_args = null, bool get_prop_req["accessorPropertiesOnly"] = accessors_only.Value; } - var frame_props = await cli.SendCommand("Runtime.getProperties", get_prop_req, token); + var frame_props = await SendCommand("Runtime.getProperties", get_prop_req, token); AssertEqual(expect_ok, frame_props.IsOk, $"Runtime.getProperties returned {frame_props.IsOk} instead of {expect_ok}, for {get_prop_req}, with Result: {frame_props}"); if (!frame_props.IsOk) return null; @@ -786,7 +771,7 @@ internal async Task GetProperties(string id, JToken fn_args = null, bool expression = expression }); - var res = await cli.SendCommand("Debugger.evaluateOnCallFrame", evaluate_req, token); + var res = await SendCommand("Debugger.evaluateOnCallFrame", evaluate_req, token); AssertEqual(expect_ok, res.IsOk, $"Debugger.evaluateOnCallFrame ('{expression}', scope: {id}) returned {res.IsOk} instead of {expect_ok}, with Result: {res}"); if (res.IsOk) return (res.Value["result"], res); @@ -801,7 +786,7 @@ internal async Task RemoveBreakpoint(string id, bool expect_ok = true) breakpointId = id }); - var res = await cli.SendCommand("Debugger.removeBreakpoint", remove_bp, token); + var res = await SendCommand("Debugger.removeBreakpoint", remove_bp, token); Assert.True(expect_ok ? res.IsOk : res.IsErr); return res; @@ -813,7 +798,7 @@ internal async Task SetBreakpoint(string url_key, int line, int column, JObject.FromObject(new { lineNumber = line, columnNumber = column, url = dicFileToUrl[url_key], }) : JObject.FromObject(new { lineNumber = line, columnNumber = column, urlRegex = url_key, }); - var bp1_res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token); + var bp1_res = await SendCommand("Debugger.setBreakpointByUrl", bp1_req, token); Assert.True(expect_ok ? bp1_res.IsOk : bp1_res.IsErr); return bp1_res; @@ -821,7 +806,7 @@ internal async Task SetBreakpoint(string url_key, int line, int column, internal async Task SetPauseOnException(string state) { - var exc_res = await cli.SendCommand("Debugger.setPauseOnExceptions", JObject.FromObject(new { state = state }), token); + var exc_res = await SendCommand("Debugger.setPauseOnExceptions", JObject.FromObject(new { state = state }), token); return exc_res; } @@ -830,7 +815,7 @@ internal async Task SetBreakpointInMethod(string assembly, string type, var req = JObject.FromObject(new { assemblyName = assembly, typeName = type, methodName = method, lineOffset = lineOffset }); // Protocol extension - var res = await cli.SendCommand("DotnetDebugger.getMethodLocation", req, token); + var res = await SendCommand("DotnetDebugger.getMethodLocation", req, token); Assert.True(res.IsOk); var m_url = res.Value["result"]["url"].Value(); @@ -843,7 +828,7 @@ internal async Task SetBreakpointInMethod(string assembly, string type, url = m_url }); - res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token); + res = await SendCommand("Debugger.setBreakpointByUrl", bp1_req, token); Assert.True(res.IsOk); return res; diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs index addc4b2d47836a..13e024c9de1aaf 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DevToolsClient.cs @@ -16,12 +16,14 @@ internal class DevToolsClient : IDisposable { DevToolsQueue _queue; ClientWebSocket socket; - TaskCompletionSource _clientInitiatedClose = new TaskCompletionSource(); - TaskCompletionSource _shutdownRequested = new TaskCompletionSource(); + TaskCompletionSource _clientInitiatedClose = new (); + TaskCompletionSource _shutdownRequested = new (); TaskCompletionSource _newSendTaskAvailable = new (); protected readonly ILogger logger; protected readonly string _id; + // private bool IsCloseRequested => _clientInitiatedClose.Task.IsCompletedSuccessfully; + public bool IsRunning { get; private set; } public event EventHandler<(RunLoopStopReason reason, Exception ex)> RunLoopStopped; public DevToolsClient(string id, ILogger logger) @@ -46,6 +48,8 @@ protected virtual void Dispose(bool disposing) socket.Dispose(); } + //FIXME: DisposeAsync + public async Task Shutdown(CancellationToken cancellationToken) { if (_shutdownRequested.Task.IsCompleted) @@ -67,6 +71,25 @@ public async Task Shutdown(CancellationToken cancellationToken) } } + private void LogState() + { + var sb = new StringBuilder(); + sb.Append($"------------ DevtoolsClient.RunLoop [{_id}] state dump -----------\n"); + LogState(sb); + + // sb.Append($"Running time: {DateTime.Now - _startTime}"); + + // ThreadPool.GetAvailableThreads(out int workerThreads, out int completionPortThreads); + // sb.Append($"TP available threads: worker: {workerThreads}, compleition: {completionPortThreads}\n"); + // sb.Append($"TP PendingWorkItemCount: {ThreadPool.PendingWorkItemCount}\n"); + + sb.Append($"------------ DevtoolsClient.RunLoop -----------\n"); + logger.LogDebug(sb.ToString()); + } + + protected virtual void LogState(StringBuilder sb) => + sb.Append($"pending_ops: , IsRunning: {IsRunning}\n"); + async Task ReadOne(CancellationToken token) { byte[] buff = new byte[4000]; @@ -129,6 +152,7 @@ protected async Task ConnectWithMainLoops( var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(token); + IsRunning = true; _ = Task.Run(async () => { try @@ -159,6 +183,7 @@ protected async Task ConnectWithMainLoops( } finally { + IsRunning = false; logger.LogDebug($"Loop ended with socket: {socket.State}"); linkedCts.Cancel(); } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 6462e86505d331..7f8e9591565e16 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -412,7 +412,7 @@ public async Task JSEvaluate() await SetBreakpoint(bp_loc, line, col); var eval_expr = "window.setTimeout(function() { eval_call_on_frame_test (); }, 1)"; - var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var result = await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); var pause_location = await insp.WaitFor(Inspector.PAUSE); var id = pause_location["callFrames"][0]["callFrameId"].Value(); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs index 1be0b18f325b59..16346ac508b229 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs @@ -146,7 +146,6 @@ public async Task JSExceptionTestNone() } catch (ArgumentException ae) { - Console.WriteLine($"{ae}"); var eo = JObject.Parse(ae.Message); AssertEqual(line, eo["exceptionDetails"]?["lineNumber"]?.Value(), "lineNumber"); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs index 3215ef1184d89c..ed0184e74c21b5 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs @@ -294,7 +294,7 @@ public async Task GetPropertiesTestJSAndManaged(bool test_js, bool? own_properti eval_expr = "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.GetPropertiesTests.DerivedClassForJSTest:run'); }, 1)"; } - var result = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); + var result = await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = eval_expr }), token); var pause_location = await insp.WaitFor(Inspector.PAUSE); var id = pause_location["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value(); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs index ef36ace6624a16..f5caa5e830d13f 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs @@ -45,7 +45,7 @@ public async Task InspectorWaitForAfterMessageAlreadyReceived() Result res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8); Assert.True(res.IsOk, $"setBreakpoint failed with {res}"); - res = await cli.SendCommand( + res = await SendCommand( "Runtime.evaluate", JObject.FromObject(new { expression = "window.setTimeout(function() { invoke_add(); }, 0);" }), token); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs index 741b6eaf74ab7d..c0d1de22ca82af 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs @@ -18,27 +18,26 @@ namespace DebuggerTests { class Inspector { - private const int DefaultTestTimeoutMs = 1 * 60 * 1000; - Dictionary> notifications = new Dictionary>(); Dictionary> eventListeners = new Dictionary>(); public const string PAUSE = "pause"; public const string READY = "ready"; - public CancellationToken Token { get; } - public InspectorClient Client { get; } + public CancellationToken Token => _cancellationTokenSource.Token; + public InspectorClient? Client { get; private set; } + + public BrowserSession? Session { get; private set; } - private CancellationTokenSource _cancellationTokenSource; + private readonly CancellationTokenSource _cancellationTokenSource; private readonly string _id; protected ILoggerFactory _loggerFactory; protected ILogger _logger; - public Inspector(string id) + public Inspector(string id, CancellationTokenSource cts) { _id = id; - _cancellationTokenSource = new CancellationTokenSource(); - Token = _cancellationTokenSource.Token; + _cancellationTokenSource = cts; _loggerFactory = LoggerFactory.Create(builder => { @@ -47,10 +46,10 @@ public Inspector(string id) c.ColorBehavior = LoggerColorBehavior.Enabled; c.TimestampFormat = "[HH:mm:ss.fff] "; c.SingleLine = true; - }); + }).AddFilter(null, LogLevel.Trace); }); - Client = new InspectorClient(_id, _loggerFactory.CreateLogger($"{typeof(InspectorClient)}-{_id}")); + // Client = new InspectorClient(_id, _loggerFactory.CreateLogger($"{typeof(InspectorClient)}-{_id}")); _logger = _loggerFactory.CreateLogger($"{typeof(Inspector)}-{_id}"); } @@ -141,35 +140,28 @@ async Task OnMessage(string method, JObject args, CancellationToken token) } } - public async Task OpenSessionAsync(Func)>> getInitCmds, TimeSpan? span = null) + public async Task OpenSessionAsync(string relativeUrl, Func)>> getInitCmds) { var start = DateTime.Now; try { - _cancellationTokenSource.CancelAfter(span?.Milliseconds ?? DefaultTestTimeoutMs); + // var uri = new Uri($"ws://{TestHarnessProxy.Endpoint.Authority}/launch-chrome-and-connect?testId={_id}"); - var uri = new Uri($"ws://{TestHarnessProxy.Endpoint.Authority}/launch-chrome-and-connect?testId={_id}"); + BrowserInstance browser = await BrowserPool.GetInstanceAsync(_logger, DebuggerTestBase.DebuggerTestAppPath, Token); + Session = await browser.OpenSession( + relativeUrl, + OnMessage, + RunLoopStoppedHandler, + _id, + _logger, + _cancellationTokenSource); - await Client.Connect(uri, OnMessage, _cancellationTokenSource.Token); - Client.RunLoopStopped += (_, args) => - { - switch (args.reason) - { - case RunLoopStopReason.Exception: - FailAllWaiters(args.ex); - break; - - case RunLoopStopReason.Cancelled when Token.IsCancellationRequested: - FailAllWaiters(new TaskCanceledException($"Test timed out (elapsed time: {(DateTime.Now - start).TotalSeconds}")); - break; - - default: - FailAllWaiters(); - break; - }; - }; + Client = Session.Connection.InspectorClient; + + // await Client.Connect(uri, OnMessage, _cancellationTokenSource.Token); + // Client.RunLoopStopped += (_, args) => - var init_cmds = getInitCmds(Client, _cancellationTokenSource.Token); + var init_cmds = getInitCmds(Session, _cancellationTokenSource.Token); Task readyTask = Task.Run(async () => Result.FromJson(await WaitFor(READY))); init_cmds.Add((READY, readyTask)); @@ -212,6 +204,24 @@ public async Task OpenSessionAsync(Func)> cmds) { var sb = new StringBuilder(); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs b/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs index d2033c7badc2f8..2b55b9ee2ec5ab 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs @@ -13,57 +13,92 @@ namespace Microsoft.WebAssembly.Diagnostics { internal class InspectorClient : DevToolsClient { - Dictionary> pending_cmds = new Dictionary>(); - Func onEvent; + Dictionary> pending_cmds = new(); int next_cmd_id; + Func _catchAllMessageHandler; + Dictionary> onMessageHandlers = new(); - public InspectorClient(string id, ILogger logger) : base(id, logger) { } + public InspectorClient(string id, ILogger logger) : base (id, logger) {} Task HandleMessage(string msg, CancellationToken token) { - var res = JObject.Parse(msg); + try + { + var res = JObject.Parse(msg); + string method = res["method"]?.Value(); + + if (res["id"] == null) { + string sidStr = res["sessionId"]?.Value(); + // } + if (sidStr == null) + return _catchAllMessageHandler(res, token); + + var sessionId = new SessionId(sidStr); + if (onMessageHandlers.TryGetValue(sessionId, out var onMessage)) { + // Console.WriteLine($"\tcalling onMessage"); + return onMessage(res["method"].Value(), res["params"] as JObject, token); + } else { + return _catchAllMessageHandler(res, token); + } + } - if (res["id"] == null) - return onEvent(res["method"].Value(), res["params"] as JObject, token); + var id = res.ToObject(); + if (!pending_cmds.Remove(id, out var item)) + logger.LogError ($"Unable to find command {id}"); - var id = res.ToObject(); - if (!pending_cmds.Remove(id, out var item)) - logger.LogError($"Unable to find command {id}"); + item.SetResult(Result.FromJson(res)); + return null; + } catch (Exception ex) { + Console.WriteLine($"------- {ex} -------"); + logger.LogError(ex.ToString()); + // throw; - item.SetResult(Result.FromJson(res)); - return null; + return null; + } } public async Task Connect( Uri uri, - Func onEvent, + Func catchAllMessageHandler, CancellationToken token) { - this.onEvent = onEvent; - + _catchAllMessageHandler = catchAllMessageHandler; RunLoopStopped += (_, args) => { - logger.LogDebug($"Failing {pending_cmds.Count} pending cmds"); - if (args.reason == RunLoopStopReason.Exception) + logger.LogDebug($"InspectorClient: Let's fail all the pending cmds (nr: {pending_cmds.Count})!"); + if (args.reason == RunLoopStopReason.Cancelled) { foreach (var cmd in pending_cmds.Values) - cmd.SetException(args.ex); + cmd.SetCanceled(); } else { + //FIXME: um args.ex should be non-null foreach (var cmd in pending_cmds.Values) - cmd.SetCanceled(); + cmd.SetException(args.ex); } - }; + }; await ConnectWithMainLoops(uri, HandleMessage, token); } + public void AddMessageHandlerForSession(SessionId sessionId, Func onEvent) + { + // logger.LogDebug($">>> Adding handler for {sessionId}"); + onMessageHandlers.Add(sessionId, onEvent); + } + + public void RemoveMessageHandlerForSession(SessionId sessionId) + => onMessageHandlers.Remove(sessionId); + public Task SendCommand(string method, JObject args, CancellationToken token) => SendCommand(new SessionId(null), method, args, token); public Task SendCommand(SessionId sessionId, string method, JObject args, CancellationToken token) { + if (!IsRunning) + throw new InvalidOperationException($"DevToolsClient.RunLoop is not running cmd: {method}"); + int id = ++next_cmd_id; if (args == null) args = new JObject(); @@ -74,15 +109,27 @@ public Task SendCommand(SessionId sessionId, string method, JObject args method = method, @params = args }); + if (sessionId != SessionId.Null) + o["sessionId"] = sessionId.sessionId; var tcs = new TaskCompletionSource(); pending_cmds[new MessageId(sessionId.sessionId, id)] = tcs; var str = o.ToString(); + logger.LogDebug($"SendCommand: id: {id} method: {method} params: {args}"); + // Console.WriteLine($"SendCommand: id: {id} method: {method} params: {args}"); var bytes = Encoding.UTF8.GetBytes(str); Send(bytes, token); return tcs.Task; } + + protected override void LogState(StringBuilder sb) + { + base.LogState(sb); + sb.Append($"Commands waiting for response: {pending_cmds.Count}\n"); + foreach (MessageId cmd_id in pending_cmds.Keys) + sb.Append($"\t{cmd_id}\n"); + } } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs index e2b796062e3086..e91eef96b4adad 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MonoJsTests.cs @@ -38,7 +38,7 @@ public async Task FixupNameValueObjectsWithMissingParts() }; var list = new[] { names[0], names[1], values[0], names[2], getters[0], getters[1] }; - var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression = $"MONO._fixup_name_value_objects({JsonConvert.SerializeObject(list)})", returnByValue = true }), token); + var res = await SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression = $"MONO._fixup_name_value_objects({JsonConvert.SerializeObject(list)})", returnByValue = true }), token); Assert.True(res.IsOk); await CheckProps(res.Value["result"]["value"], new @@ -81,7 +81,7 @@ public async Task GetParamsAndLocalsWithInvalidIndices() var expression = $"MONO.mono_wasm_get_variables({scope}, {JsonConvert.SerializeObject(var_ids)})"; - var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); + var res = await SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); Assert.True(res.IsOk); await CheckProps(res.Value["result"]?["value"], new @@ -107,12 +107,12 @@ await EvaluateAndCheck( var scope_id = "-12"; var expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; - var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); + var res = await SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); Assert.False(res.IsOk); scope_id = "30000"; expression = $"MONO.mono_wasm_get_variables({scope_id}, {JsonConvert.SerializeObject(var_ids)})"; - res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); + res = await SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, returnByValue = true }), token); Assert.False(res.IsOk); } @@ -131,7 +131,7 @@ public async Task BadRaiseDebugEventsTest() foreach (var expression in bad_expressions) { - var res = await cli.SendCommand($"Runtime.evaluate", + var res = await SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression, @@ -162,7 +162,7 @@ public async Task RaiseDebugEventTraceTest(bool? trace) var trace_str = trace.HasValue ? $"trace: {trace.ToString().ToLower()}" : String.Empty; var expression = $"MONO.mono_wasm_raise_debug_event({{ eventName:'qwe' }}, {{ {trace_str} }})"; - var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); + var res = await SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); Assert.True(res.IsOk, $"Expected to pass for {expression}"); var t = await Task.WhenAny(tcs.Task, Task.Delay(2000)); @@ -249,10 +249,10 @@ async Task AssemblyLoadedEventTest(string asm_name, string asm_path, string pdb_ pdb_b64: '{pdb_base64}' }});"; - var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); + var res = await SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); Assert.True(res.IsOk, $"Expected to pass for {expression}"); - res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); + res = await SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); Assert.True(res.IsOk, $"Expected to pass for {expression}"); var t = await Task.WhenAny(tcs.Task, Task.Delay(2000)); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ProxyLauncherData.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ProxyLauncherData.cs new file mode 100644 index 00000000000000..40a3ba378263e1 --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ProxyLauncherData.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +namespace Microsoft.WebAssembly.Diagnostics +{ + public class ProxyLauncherData + { + public Dictionary IdToDevToolsUrl { get; } = new (); + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs index a5da24b3691031..351c99556a6ce7 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs @@ -335,7 +335,6 @@ public async Task InspectLocalsInAsyncMethods(bool use_cfo) CheckObject(locals, "this", "Math.NestedInMath"); } ); - Console.WriteLine(wait_res); #if false // Disabled for now, as we don't have proper async traces var locals = await GetProperties(wait_res["callFrames"][2]["callFrameId"].Value()); @@ -856,7 +855,7 @@ public async Task BreakpointOnHiddenLineOfMethodWithNoNextVisibleLineShouldNotPa await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 554, 12); string expression = "window.setTimeout(function() { invoke_static_method ('[debugger-test] HiddenSequencePointTest:StepOverHiddenSP'); }, 1);"; - await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); + await SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression }), token); Task pause_task = insp.WaitFor(Inspector.PAUSE); Task t = await Task.WhenAny(pause_task, Task.Delay(2000)); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessProxy.cs b/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessProxy.cs index 71c61185a9cca4..0427d528e5f8b9 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessProxy.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessProxy.cs @@ -2,31 +2,37 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Console; +#nullable enable + namespace Microsoft.WebAssembly.Diagnostics { public class TestHarnessProxy { - static IWebHost host; - static Task hostTask; + static IWebHost? host; + static Task? hostTask; static CancellationTokenSource cts = new CancellationTokenSource(); static object proxyLock = new object(); - public static readonly Uri Endpoint = new Uri("http://localhost:9400"); + private static Uri? s_endpoint = null; + public static Uri Endpoint => s_endpoint ?? throw new ArgumentException($"Cannot access `{nameof(Endpoint)}` before `{nameof(TestHarnessProxy)}` has been started"); + public static ProxyLauncherData LauncherData { get; } = new (); - public static Task Start(string chromePath, string appPath, string pagePath) + public static Task Start(string appPath, ILogger logger, CancellationToken token) { lock (proxyLock) { - if (host != null) + if (host != null && hostTask != null) return hostTask; host = WebHost.CreateDefaultBuilder() @@ -49,20 +55,26 @@ public static Task Start(string chromePath, string appPath, string pagePath) services.Configure(ctx.Configuration); services.Configure(options => { - options.ChromePath = options.ChromePath ?? chromePath; options.AppPath = appPath; - options.PagePath = pagePath; options.DevToolsUrl = new Uri("http://localhost:0"); }); }) .UseStartup() - .UseUrls(Endpoint.ToString()) + .UseUrls("http://127.0.0.1:0") .Build(); - hostTask = host.StartAsync(cts.Token); + + logger.LogDebug("Starting webserver, and the proxy launcher"); + hostTask = host.StartAsync(cts.Token).ContinueWith(t => + { + s_endpoint = new Uri(host.ServerFeatures + .Get() + .Addresses + .First()); + }, TaskContinuationOptions.OnlyOnRanToCompletion); } - Console.WriteLine("WebServer Ready!"); + logger.LogDebug("WebServer Ready!"); return hostTask; } } -} \ No newline at end of file +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessStartup.cs b/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessStartup.cs index 6454b09a57631d..afb8485f85b335 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessStartup.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/TestHarnessStartup.cs @@ -2,10 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Diagnostics; using System.IO; -using System.Net.Http; -using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; @@ -18,15 +15,13 @@ using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; using Microsoft.Extensions.Primitives; +using Newtonsoft.Json.Linq; namespace Microsoft.WebAssembly.Diagnostics { public class TestHarnessStartup { - static Regex parseConnection = new Regex(@"listening on (ws?s://[^\s]*)"); public TestHarnessStartup(IConfiguration configuration) { Configuration = configuration; @@ -34,7 +29,6 @@ public TestHarnessStartup(IConfiguration configuration) public IConfiguration Configuration { get; set; } public ILogger ServerLogger { get; private set; } - private ILoggerFactory _loggerFactory; // This method gets called by the runtime. Use this method to add services to the container. @@ -78,190 +72,102 @@ async Task SendNodeList(HttpContext context) catch (Exception e) { ServerLogger.LogError(e, "webserver: SendNodeList failed"); } } - public async Task LaunchAndServe(ProcessStartInfo psi, HttpContext context, string testId, ILogger testLogger, Func> extract_conn_url) - { - - if (!context.WebSockets.IsWebSocketRequest) - { - context.Response.StatusCode = 400; - return; - } - - var tcs = new TaskCompletionSource(); - - var proc = Process.Start(psi); - try - { - proc.ErrorDataReceived += (sender, e) => - { - var str = e.Data; - testLogger.LogTrace($"browser-stderr: {str}"); - - if (tcs.Task.IsCompleted) - return; - - var match = parseConnection.Match(str); - if (match.Success) - { - tcs.TrySetResult(match.Groups[1].Captures[0].Value); - } - }; - - proc.OutputDataReceived += (sender, e) => - { - testLogger.LogTrace($"browser-stdout: {e.Data}"); - }; - - proc.BeginErrorReadLine(); - proc.BeginOutputReadLine(); - - if (await Task.WhenAny(tcs.Task, Task.Delay(5000)) != tcs.Task) - { - testLogger.LogError("Didnt get the con string after 5s."); - throw new Exception("node.js timedout"); - } - var line = await tcs.Task; - var con_str = extract_conn_url != null ? await extract_conn_url(line) : line; - - testLogger.LogInformation($"launching proxy for {con_str}"); - - var proxy = new DebuggerProxy(_loggerFactory, null, testId); - var browserUri = new Uri(con_str); - var ideSocket = await context.WebSockets.AcceptWebSocketAsync(); - - await proxy.Run(browserUri, ideSocket); - testLogger.LogInformation("Proxy done"); - } - catch (Exception e) - { - testLogger.LogError("got exception {0}", e); - } - finally - { - proc.CancelErrorRead(); - proc.CancelOutputRead(); - proc.Kill(); - proc.WaitForExit(); - proc.Close(); - } - } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IOptionsMonitor optionsAccessor, IWebHostEnvironment env, ILogger logger, ILoggerFactory loggerFactory) { this.ServerLogger = logger; this._loggerFactory = loggerFactory; + // _launcherData = app.ApplicationServices.GetRequiredService(); + app.UseWebSockets(); - app.UseStaticFiles(); + // app.UseStaticFiles(); TestHarnessOptions options = optionsAccessor.CurrentValue; var provider = new FileExtensionContentTypeProvider(); provider.Mappings[".wasm"] = "application/wasm"; + foreach (var extn in new string[] { ".dll", ".pdb", ".dat", ".blat" }) + { + provider.Mappings[extn] = "application/octet-stream"; + } + + ServerLogger.LogInformation($"Starting webserver with appPath: {options.AppPath}"); + app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(options.AppPath), ServeUnknownFileTypes = true, //Cuz .wasm is not a known file type :cry: - RequestPath = "", + // RequestPath = "", ContentTypeProvider = provider }); var devToolsUrl = options.DevToolsUrl; app.UseRouter(router => { - router.MapGet("launch-chrome-and-connect", async context => + router.MapGet("/connect-to-devtools/{browserInstanceId:int:required}", async context => { - string testId = "unknown"; - if (context.Request.Query.TryGetValue("testId", out StringValues values)) - testId = values.ToString(); - - var testLogger = _loggerFactory.CreateLogger($"{typeof(TestHarnessProxy)}-{testId}"); - - testLogger.LogInformation("New test request"); try { - var client = new HttpClient(); - var psi = new ProcessStartInfo(); - - psi.Arguments = $"--headless --disable-gpu --lang=en-US --incognito --remote-debugging-port={devToolsUrl.Port} http://{TestHarnessProxy.Endpoint.Authority}/{options.PagePath}"; - psi.UseShellExecute = false; - psi.FileName = options.ChromePath; - psi.RedirectStandardError = true; - psi.RedirectStandardOutput = true; - - await LaunchAndServe(psi, context, testId, testLogger, async (str) => - { - var start = DateTime.Now; - JArray obj = null; - - while (true) - { - // Unfortunately it does look like we have to wait - // for a bit after getting the response but before - // making the list request. We get an empty result - // if we make the request too soon. - await Task.Delay(100); - - var res = await client.GetStringAsync(new Uri(new Uri(str), "/json/list")); - testLogger.LogTrace("res is {0}", res); - - if (!String.IsNullOrEmpty(res)) - { - // Sometimes we seem to get an empty array `[ ]` - obj = JArray.Parse(res); - if (obj != null && obj.Count >= 1) - break; - } - - var elapsed = DateTime.Now - start; - if (elapsed.Milliseconds > 5000) - { - testLogger.LogError($"Unable to get DevTools /json/list response in {elapsed.Seconds} seconds, stopping"); - return null; - } - } - - var wsURl = obj[0]?["webSocketDebuggerUrl"]?.Value(); - testLogger.LogTrace(">>> {0}", wsURl); - - return wsURl; - }); + var id = context.Request.RouteValues["browserInstanceId"].ToString(); + string testId = "unknown"; + if (context.Request.Query.TryGetValue("testId", out StringValues values)) + testId = values.ToString(); + + var testLogger = _loggerFactory.CreateLogger($"{typeof(TestHarnessProxy)}-{testId}"); + + testLogger.LogDebug($"New test request for browserId: {id}, test_id: {testId}, with kestrel connection id: {context.Connection.Id}"); + if (!TestHarnessProxy.LauncherData.IdToDevToolsUrl.TryGetValue(id, out Uri remoteConnectionUri)) + throw new Exception($"Unknown browser id {id}"); + + // string logFilename = $"{testId}-proxy.log"; + // var proxyLoggerFactory = LoggerFactory.Create( + // builder => builder + // // .AddFile(logFilename, minimumLevel: LogLevel.Debug) + // .AddFilter(null, LogLevel.Trace)); + + var proxy = new DebuggerProxy(_loggerFactory, null, testId); + var browserUri = remoteConnectionUri;// options.RemoteConnectionUri; + var ideSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); + + await proxy.Run(browserUri, ideSocket).ConfigureAwait(false); + // Console.WriteLine("Proxy done"); + testLogger.LogDebug($"Closing proxy for browser {context.Request.Path}{context.Request.QueryString}"); } catch (Exception ex) { - testLogger.LogError($"launch-chrome-and-connect failed with {ex.ToString()}"); + ServerLogger.LogError($"{context.Request.Path}{context.Request.QueryString} failed with {ex}"); } }); }); - if (options.NodeApp != null) - { - ServerLogger.LogTrace($"Doing the nodejs: {options.NodeApp}"); - var nodeFullPath = Path.GetFullPath(options.NodeApp); - ServerLogger.LogTrace(nodeFullPath); - var psi = new ProcessStartInfo(); - - psi.UseShellExecute = false; - psi.RedirectStandardError = true; - psi.RedirectStandardOutput = true; - - psi.Arguments = $"--inspect-brk=localhost:0 {nodeFullPath}"; - psi.FileName = "node"; - - app.UseRouter(router => - { - //Inspector API for using chrome devtools directly - router.MapGet("json", SendNodeList); - router.MapGet("json/list", SendNodeList); - router.MapGet("json/version", SendNodeVersion); - router.MapGet("launch-done-and-connect", async context => - { - await LaunchAndServe(psi, context, null, null, null); - }); - }); - } + // if (options.NodeApp != null) + // { + // Logger.LogTrace($"Doing the nodejs: {options.NodeApp}"); + // var nodeFullPath = Path.GetFullPath(options.NodeApp); + // Logger.LogTrace(nodeFullPath); + // var psi = new ProcessStartInfo(); + + // psi.UseShellExecute = false; + // psi.RedirectStandardError = true; + // psi.RedirectStandardOutput = true; + + // psi.Arguments = $"--inspect-brk=localhost:0 {nodeFullPath}"; + // psi.FileName = "node"; + + // app.UseRouter(router => + // { + // //Inspector API for using chrome devtools directly + // router.MapGet("json", SendNodeList); + // router.MapGet("json/list", SendNodeList); + // router.MapGet("json/version", SendNodeVersion); + // router.MapGet("launch-done-and-connect", async context => + // { + // await LaunchAndServe(psi, context, null); + // }); + // }); + // } } } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index 1b7657ddb87cd0..5207da11f5154c 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -130,7 +130,7 @@ public async Task CreateBadBreakpoint() url = "dotnet://debugger-test.dll/this-file-doesnt-exist.cs", }); - var bp1_res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token); + var bp1_res = await SendCommand("Debugger.setBreakpointByUrl", bp1_req, token); Assert.True(bp1_res.IsOk); Assert.Empty(bp1_res.Value["locations"].Values()); @@ -183,7 +183,7 @@ public async Task ExceptionThrownInJS() expression = "invoke_bad_js_test();" }); - var eval_res = await cli.SendCommand("Runtime.evaluate", eval_req, token); + var eval_res = await SendCommand("Runtime.evaluate", eval_req, token); Assert.True(eval_res.IsErr); Assert.Equal("Uncaught", eval_res.Error["exceptionDetails"]?["text"]?.Value()); } @@ -199,7 +199,7 @@ public async Task ExceptionThrownInJSOutOfBand() }); var task = insp.WaitFor("Runtime.exceptionThrown"); - var eval_res = await cli.SendCommand("Runtime.evaluate", eval_req, token); + var eval_res = await SendCommand("Runtime.evaluate", eval_req, token); // Response here will be the id for the timer from JS! Assert.True(eval_res.IsOk); @@ -390,7 +390,7 @@ await EvaluateAndCheck( objectId = "dotnet:scope:23490871", }); - var frame_props = await cli.SendCommand("Runtime.getProperties", get_prop_req, token); + var frame_props = await SendCommand("Runtime.getProperties", get_prop_req, token); Assert.True(frame_props.IsErr); } ); @@ -579,7 +579,6 @@ public async Task InspectBoxedAsClassLocals(string method_name, bool is_async) = { var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); var dt = new DateTime(2310, 1, 2, 3, 4, 5); - Console.WriteLine(locals); await CheckProps(locals, new { @@ -672,7 +671,6 @@ public async Task InspectLocalsForToStringDescriptions(int line, int col, string $"'{entry_method_name}'," + (call_other ? "true" : "false") + "); }, 1);"; - Console.WriteLine($"{eval_expr}"); var pause_location = await EvaluateAndCheck(eval_expr, debugger_test_loc, line, col, invoke_async ? "MoveNext" : method_name); @@ -812,7 +810,7 @@ await CheckInspectLocalsAtBreakpointSite( async Task CreateNewId(string expr) { - var res = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), token); + var res = await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), token); Assert.True(res.IsOk, "Expected Runtime.evaluate to succeed"); AssertEqual("string", res.Value["result"]?["type"]?.Value(), "Expected Runtime.evaluate to return a string type result"); return res.Value["result"]?["value"]?.Value(); @@ -821,7 +819,7 @@ async Task CreateNewId(string expr) async Task _invoke_getter(string obj_id, string property_name, bool expect_ok) { var expr = $"MONO._invoke_getter ('{obj_id}', '{property_name}')"; - var res = await cli.SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), token); + var res = await SendCommand("Runtime.evaluate", JObject.FromObject(new { expression = expr }), token); AssertEqual(expect_ok, res.IsOk, "Runtime.evaluate result not as expected for {expr}"); return res; @@ -994,7 +992,7 @@ async Task LoadAssemblyDynamically(string asm_file, string pdb_file) expression = $"{{ let asm_b64 = '{asm_base64}'; let pdb_b64 = '{pdb_base64}'; invoke_static_method('[debugger-test] LoadDebuggerTest:LoadLazyAssembly', asm_b64, pdb_b64); }}" }); - Result load_assemblies_res = await cli.SendCommand("Runtime.evaluate", load_assemblies, token); + Result load_assemblies_res = await SendCommand("Runtime.evaluate", load_assemblies, token); Assert.True(load_assemblies_res.IsOk); } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/appsettings.json b/src/mono/wasm/debugger/DebuggerTestSuite/appsettings.json index 4d7c82ac0ee05b..8bafa23e224c4b 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/appsettings.json +++ b/src/mono/wasm/debugger/DebuggerTestSuite/appsettings.json @@ -4,9 +4,9 @@ "Default": "Error", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information", - "Microsoft.WebAssembly.Diagnostics.TestHarnessProxy": "Information", - "Microsoft.WebAssembly.Diagnostics.DevToolsProxy": "Information", - "DebuggerTests.Inspector": "Information" + "Microsoft.WebAssembly.Diagnostics.TestHarnessProxy": "Trace", + "Microsoft.WebAssembly.Diagnostics.DevToolsProxy": "Trace", + "DebuggerTests.Inspector": "Trace" } } } From 1be8fb3454d2844bae7d80cbacecfcaa0ce14060 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 24 Nov 2020 22:16:49 -0500 Subject: [PATCH 20/21] tests are running --- .../DebuggerTestSuite/BrowserInstance.cs | 10 +++++++--- .../DebuggerTestSuite/DebuggerTestBase.cs | 12 ++++++++---- .../debugger/DebuggerTestSuite/HarnessTests.cs | 12 ++++++------ .../debugger/DebuggerTestSuite/Inspector.cs | 17 ++++++++++++----- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs index 170332cabb3598..a1b07c21b9742a 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs @@ -28,11 +28,15 @@ internal class BrowserInstance : IAsyncDisposable // private ConcurrentDictionary targets = new ConcurrentDictionary(); public bool HasExited => _process.HasExited; + public CancellationToken CancellationToken => _cancellationTokenSource.Token; + private CancellationTokenSource _cancellationTokenSource; + private BrowserInstance(Uri remoteConnectionUri, Process process, ILogger logger) { _remoteConnectionUri = remoteConnectionUri; _process = process; _poolLogger = logger; + _cancellationTokenSource = new CancellationTokenSource(); Id = Interlocked.Increment(ref s_nextId); @@ -40,9 +44,9 @@ private BrowserInstance(Uri remoteConnectionUri, Process process, ILogger logger TestHarnessProxy.LauncherData.IdToDevToolsUrl[Id.ToString()] = remoteConnectionUri; } - public async Task OpenConnection(string testId, ILogger testLogger, CancellationTokenSource testCts) + private async Task OpenConnection(string testId, ILogger testLogger) { - return await BrowserCdpConnection.Open(Id, testId, testLogger, testCts); + return await BrowserCdpConnection.Open(Id, testId, testLogger, _cancellationTokenSource); } public static async Task StartAsync(ILogger logger, CancellationToken token, int port=0) @@ -75,7 +79,7 @@ public async Task OpenSession(string relativeUrl, if (connection == null) { - connection = await OpenConnection(testId, logger, cts); + connection = await OpenConnection(testId, logger); connection.InspectorClient!.RunLoopStopped += (_, args) => onRunLoopFailedOrCanceled(args); } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index a9912424378273..e0672d6ab79ce9 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -21,7 +21,7 @@ namespace DebuggerTests { public class DebuggerTestBase : IAsyncLifetime { - internal InspectorClient cli; + // internal InspectorClient cli; internal Inspector insp; protected CancellationTokenSource _cancellationTokenSource = new (); @@ -62,7 +62,7 @@ public DebuggerTestBase()//string driver = "debugger-driver.html") { Id = Interlocked.Increment(ref s_nextId).ToString(); insp = new Inspector(Id, _cancellationTokenSource); - cli = insp.Client; + // cli = insp.Client; scripts = SubscribeToScripts(insp); token = _cancellationTokenSource.Token; @@ -92,10 +92,14 @@ public virtual async Task InitializeAsync() // await Ready(); await insp.OpenSessionAsync("/debugger-driver.html", fn); - cli = insp.Client; + // cli = insp.Client; } - public virtual async Task DisposeAsync() => await insp.ShutdownAsync().ConfigureAwait(false); + public virtual async Task DisposeAsync() + { + Console.WriteLine ($"-- [{Id}] TestBase.DisposeAsync"); + await insp.DisposeAsync(true); + } public Task Ready() => Task.CompletedTask; diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs index f5caa5e830d13f..1e289dd6458c3f 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/HarnessTests.cs @@ -31,13 +31,13 @@ public async Task ExceptionThrown() Assert.Contains("non_existant_fn is not defined", ae.Message); } - [Fact] - public async Task BrowserCrash() => await Assert.ThrowsAsync(async () => - await SendCommandAndCheck(null, "Browser.crash", null, -1, -1, null)); + // [Fact] + // public async Task BrowserCrash() => await Assert.ThrowsAsync(async () => + // await SendCommandAndCheck(null, "Browser.crash", null, -1, -1, null)); - [Fact] - public async Task BrowserClose() => await Assert.ThrowsAsync(async () => - await SendCommandAndCheck(null, "Browser.close", null, -1, -1, null)); + // [Fact] + // public async Task BrowserClose() => await Assert.ThrowsAsync(async () => + // await SendCommandAndCheck(null, "Browser.close", null, -1, -1, null)); [Fact] public async Task InspectorWaitForAfterMessageAlreadyReceived() diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs index c0d1de22ca82af..ca2156569c2056 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs @@ -242,15 +242,22 @@ static string RemainingCommandsToString(string cmd_name, IList<(string, Task Date: Thu, 26 Nov 2020 18:07:52 -0500 Subject: [PATCH 21/21] Bp works! - App.init called from Start - regular runtime-test.js - added .pdb files to mono-config.js,. and copied the files over - new CustomAssemblyResolver, else we get dotnet.xunitextensions cannot be resolved! problems: - not able to see value for a concurrentdict, can see local int though --- eng/testing/WasmRunnerTemplate.sh | 8 + eng/testing/tests.mobile.targets | 2 +- mono-config.js | 717 ++++++++++++++++++ runtime.js | 409 ++++++++++ .../tests/WasmTestRunner/WasmTestRunner.cs | 21 +- src/mono/wasm/Makefile | 2 +- .../wasm/debugger/BrowserDebugHost/Program.cs | 19 +- .../wasm/debugger/BrowserDebugHost/Startup.cs | 2 +- .../debugger/BrowserDebugProxy/DebugStore.cs | 34 +- .../debugger/BrowserDebugProxy/MonoProxy.cs | 5 +- .../DebuggerTestSuite/BrowserInstance.cs | 4 +- .../debugger/DebuggerTestSuite/Inspector.cs | 9 +- .../DebuggerTestSuite/InspectorClient.cs | 2 +- .../DebuggerTestSuite/appsettings.json | 4 +- src/mono/wasm/runtime-test.js | 24 +- src/mono/wasm/runtime/library_mono.js | 3 +- .../WasmAppBuilder/WasmAppBuilder.cs | 2 +- 17 files changed, 1233 insertions(+), 34 deletions(-) create mode 100644 mono-config.js create mode 100644 runtime.js diff --git a/eng/testing/WasmRunnerTemplate.sh b/eng/testing/WasmRunnerTemplate.sh index e2e3996cab58c0..fbe0d22e91975e 100644 --- a/eng/testing/WasmRunnerTemplate.sh +++ b/eng/testing/WasmRunnerTemplate.sh @@ -18,6 +18,14 @@ if [ -z "$XHARNESS_COMMAND" ]; then XHARNESS_COMMAND="test" fi +DEBUG_PROXY_PROJ=$HOME/dev/runtime/src/mono/wasm/debugger/BrowserDebugHost/BrowserDebugHost.csproj +DEBUG_PROXY_PORT=9300 +BROWSER_DEBUG_PORT=9222 +XHARNESS_ARGS="$XHARNESS_ARGS --debug-port=$BROWSER_DEBUG_PORT" + +dotnet run -p $DEBUG_PROXY_PROJ $BROWSER_DEBUG_PORT $DEBUG_PROXY_PORT & +echo $? + # RunCommands defined in tests.mobile.targets [[RunCommands]] diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index ef94fd017269ac..f84674c91edaf8 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -10,7 +10,7 @@ - $HARNESS_RUNNER wasm $XHARNESS_COMMAND --app=. --engine=$(JSEngine) $(JSEngineArgs) --js-file=runtime.js --output-directory=$XHARNESS_OUT -- $(RunTestsJSArguments) --run WasmTestRunner.dll $(AssemblyName).dll + $HARNESS_RUNNER wasm $XHARNESS_COMMAND --app=. --engine=$(JSEngine) $(JSEngineArgs) --js-file=runtime.js --output-directory=$XHARNESS_OUT $XHARNESS_ARGS -- $(RunTestsJSArguments) --run WasmTestRunner.dll $(AssemblyName).dll diff --git a/mono-config.js b/mono-config.js new file mode 100644 index 00000000000000..b83ae367c24223 --- /dev/null +++ b/mono-config.js @@ -0,0 +1,717 @@ +config = { + "assembly_root": "managed", + "debug_level": 1, + "diagnostic_tracing": true, + "assets": [ + { + "behavior": "assembly", + "name": "Microsoft.Diagnostics.Runtime.dll" + }, + { + "behavior": "assembly", + "name": "Microsoft.DotNet.RemoteExecutor.dll" + }, + { + "behavior": "assembly", + "name": "Microsoft.DotNet.XHarness.TestRunners.Common.dll" + }, + { + "behavior": "assembly", + "name": "Microsoft.DotNet.XHarness.TestRunners.Xunit.dll" + }, + { + "behavior": "assembly", + "name": "xunit.abstractions.dll" + }, + { + "behavior": "assembly", + "name": "xunit.assert.dll" + }, + { + "behavior": "assembly", + "name": "xunit.core.dll" + }, + { + "behavior": "assembly", + "name": "xunit.execution.dotnet.dll" + }, + { + "behavior": "assembly", + "name": "xunit.runner.utility.netcoreapp10.dll" + }, + + { + "behavior": "assembly", + "name": "Microsoft.DotNet.XUnitExtensions.dll" + }, + { + "behavior": "assembly", + "name": "Microsoft.TestPlatform.CoreUtilities.dll" + }, + { + "behavior": "assembly", + "name": "Microsoft.TestPlatform.PlatformAbstractions.dll" + }, + { + "behavior": "assembly", + "name": "Microsoft.VisualStudio.TestPlatform.ObjectModel.dll" + }, + { + "behavior": "assembly", + "name": "Microsoft.Win32.Primitives.dll" + }, + { + "behavior": "assembly", + "name": "Microsoft.Win32.Registry.dll" + }, + { + "behavior": "assembly", + "name": "Mono.Options.dll" + }, + { + "behavior": "assembly", + "name": "mscorlib.dll" + }, + { + "behavior": "assembly", + "name": "netstandard.dll" + }, + { + "behavior": "assembly", + "name": "NuGet.Frameworks.dll" + }, + { + "behavior": "assembly", + "name": "System.Collections.dll" + }, + { + "behavior": "assembly", + "name": "System.Collections.Concurrent.dll" + }, + { + "behavior": "assembly", + "name": "System.Collections.Concurrent.Tests.dll" + }, + { + "behavior": "assembly", + "name": "System.Collections.Concurrent.Tests.pdb" + }, + { + "behavior": "assembly", + "name": "System.Collections.Immutable.dll" + }, + { + "behavior": "assembly", + "name": "System.Collections.NonGeneric.dll" + }, + { + "behavior": "assembly", + "name": "System.Collections.Specialized.dll" + }, + { + "behavior": "assembly", + "name": "System.ComponentModel.dll" + }, + { + "behavior": "assembly", + "name": "System.ComponentModel.EventBasedAsync.dll" + }, + { + "behavior": "assembly", + "name": "System.ComponentModel.Primitives.dll" + }, + { + "behavior": "assembly", + "name": "System.ComponentModel.TypeConverter.dll" + }, + { + "behavior": "assembly", + "name": "System.Console.dll" + }, + { + "behavior": "assembly", + "name": "System.Data.Common.dll" + }, + { + "behavior": "assembly", + "name": "System.Diagnostics.Contracts.dll" + }, + { + "behavior": "assembly", + "name": "System.Diagnostics.Debug.dll" + }, + { + "behavior": "assembly", + "name": "System.Diagnostics.DiagnosticSource.dll" + }, + { + "behavior": "assembly", + "name": "System.Diagnostics.FileVersionInfo.dll" + }, + { + "behavior": "assembly", + "name": "System.Diagnostics.Process.dll" + }, + { + "behavior": "assembly", + "name": "System.Diagnostics.StackTrace.dll" + }, + { + "behavior": "assembly", + "name": "System.Diagnostics.TextWriterTraceListener.dll" + }, + { + "behavior": "assembly", + "name": "System.Diagnostics.TraceSource.dll" + }, + { + "behavior": "assembly", + "name": "System.Diagnostics.Tracing.dll" + }, + { + "behavior": "assembly", + "name": "System.Drawing.dll" + }, + { + "behavior": "assembly", + "name": "System.Drawing.Primitives.dll" + }, + { + "behavior": "assembly", + "name": "System.Formats.Asn1.dll" + }, + { + "behavior": "assembly", + "name": "System.Globalization.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.Compression.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.Compression.Brotli.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.Compression.ZipFile.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.FileSystem.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.FileSystem.AccessControl.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.FileSystem.DriveInfo.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.FileSystem.Watcher.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.IsolatedStorage.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.MemoryMappedFiles.dll" + }, + { + "behavior": "assembly", + "name": "System.IO.Pipes.dll" + }, + { + "behavior": "assembly", + "name": "System.Linq.dll" + }, + { + "behavior": "assembly", + "name": "System.Linq.Expressions.dll" + }, + { + "behavior": "assembly", + "name": "System.Linq.Parallel.dll" + }, + { + "behavior": "assembly", + "name": "System.Linq.Queryable.dll" + }, + { + "behavior": "assembly", + "name": "System.Memory.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.Http.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.HttpListener.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.Mail.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.NameResolution.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.NetworkInformation.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.Ping.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.Primitives.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.Quic.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.Requests.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.Security.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.ServicePoint.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.Sockets.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.WebClient.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.WebHeaderCollection.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.WebProxy.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.WebSockets.dll" + }, + { + "behavior": "assembly", + "name": "System.Net.WebSockets.Client.dll" + }, + { + "behavior": "assembly", + "name": "System.Numerics.Vectors.dll" + }, + { + "behavior": "assembly", + "name": "System.ObjectModel.dll" + }, + { + "behavior": "assembly", + "name": "System.Private.CoreLib.dll" + }, + { + "behavior": "assembly", + "name": "System.Private.DataContractSerialization.dll" + }, + { + "behavior": "assembly", + "name": "System.Private.Runtime.InteropServices.JavaScript.dll" + }, + { + "behavior": "assembly", + "name": "System.Private.Uri.dll" + }, + { + "behavior": "assembly", + "name": "System.Private.Xml.dll" + }, + { + "behavior": "assembly", + "name": "System.Private.Xml.Linq.dll" + }, + { + "behavior": "assembly", + "name": "System.Reflection.dll" + }, + { + "behavior": "assembly", + "name": "System.Reflection.DispatchProxy.dll" + }, + { + "behavior": "assembly", + "name": "System.Reflection.Emit.dll" + }, + { + "behavior": "assembly", + "name": "System.Reflection.Emit.ILGeneration.dll" + }, + { + "behavior": "assembly", + "name": "System.Reflection.Emit.Lightweight.dll" + }, + { + "behavior": "assembly", + "name": "System.Reflection.Extensions.dll" + }, + { + "behavior": "assembly", + "name": "System.Reflection.Metadata.dll" + }, + { + "behavior": "assembly", + "name": "System.Reflection.Primitives.dll" + }, + { + "behavior": "assembly", + "name": "System.Reflection.TypeExtensions.dll" + }, + { + "behavior": "assembly", + "name": "System.Resources.ResourceManager.dll" + }, + { + "behavior": "assembly", + "name": "System.Resources.Writer.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.CompilerServices.Unsafe.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.CompilerServices.VisualC.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.Extensions.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.InteropServices.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.InteropServices.RuntimeInformation.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.Loader.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.Numerics.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.Serialization.Formatters.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.Serialization.Json.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.Serialization.Primitives.dll" + }, + { + "behavior": "assembly", + "name": "System.Runtime.Serialization.Xml.dll" + }, + { + "behavior": "assembly", + "name": "System.Security.AccessControl.dll" + }, + { + "behavior": "assembly", + "name": "System.Security.Claims.dll" + }, + { + "behavior": "assembly", + "name": "System.Security.Cryptography.Algorithms.dll" + }, + { + "behavior": "assembly", + "name": "System.Security.Cryptography.Csp.dll" + }, + { + "behavior": "assembly", + "name": "System.Security.Cryptography.Encoding.dll" + }, + { + "behavior": "assembly", + "name": "System.Security.Cryptography.Primitives.dll" + }, + { + "behavior": "assembly", + "name": "System.Security.Cryptography.X509Certificates.dll" + }, + { + "behavior": "assembly", + "name": "System.Security.Principal.Windows.dll" + }, + { + "behavior": "assembly", + "name": "System.Text.Encoding.dll" + }, + { + "behavior": "assembly", + "name": "System.Text.Encoding.Extensions.dll" + }, + { + "behavior": "assembly", + "name": "System.Text.RegularExpressions.dll" + }, + { + "behavior": "assembly", + "name": "System.Threading.dll" + }, + { + "behavior": "assembly", + "name": "System.Threading.Channels.dll" + }, + { + "behavior": "assembly", + "name": "System.Threading.Overlapped.dll" + }, + { + "behavior": "assembly", + "name": "System.Threading.Tasks.dll" + }, + { + "behavior": "assembly", + "name": "System.Threading.Tasks.Parallel.dll" + }, + { + "behavior": "assembly", + "name": "System.Threading.Thread.dll" + }, + { + "behavior": "assembly", + "name": "System.Threading.ThreadPool.dll" + }, + { + "behavior": "assembly", + "name": "System.Transactions.Local.dll" + }, + { + "behavior": "assembly", + "name": "System.Web.HttpUtility.dll" + }, + { + "behavior": "assembly", + "name": "System.Xml.ReaderWriter.dll" + }, + { + "behavior": "assembly", + "name": "System.Xml.XDocument.dll" + }, + { + "behavior": "assembly", + "name": "System.Xml.XmlSerializer.dll" + }, + { + "behavior": "assembly", + "name": "System.Xml.XPath.dll" + }, + { + "behavior": "assembly", + "name": "System.Xml.XPath.XDocument.dll" + }, + { + "behavior": "assembly", + "name": "TestUtilities.dll" + }, + { + "behavior": "assembly", + "name": "WasmTestRunner.dll" + }, + { + "behavior": "assembly", + "name": "WasmTestRunner.pdb" + }, + { + "culture": "cs", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "de", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "es", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "fr", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "it", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "ja", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "ko", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "pl", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "pt-BR", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "ru", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "tr", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "zh-Hans", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "culture": "zh-Hant", + "behavior": "resource", + "name": "Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "System.Collections.Concurrent.Tests.deps.json", + "behavior": "vfs", + "name": "supportFiles/0_System.Collections.Concurrent.Tests.deps.json" + }, + { + "virtual_path": "System.Collections.Concurrent.Tests.runtimeconfig.json", + "behavior": "vfs", + "name": "supportFiles/1_System.Collections.Concurrent.Tests.runtimeconfig.json" + }, + { + "virtual_path": "TestUtilities.dll", + "behavior": "vfs", + "name": "supportFiles/2_TestUtilities.dll" + }, + { + "virtual_path": "TestUtilities.pdb", + "behavior": "vfs", + "name": "supportFiles/3_TestUtilities.pdb" + }, + { + "virtual_path": "cs/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/4_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "de/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/5_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "es/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/6_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "fr/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/7_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "it/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/8_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "ja/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/9_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "ko/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/10_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "pl/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/11_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "pt-BR/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/12_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "ru/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/13_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "tr/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/14_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "zh-Hans/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/15_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "virtual_path": "zh-Hant/Microsoft.VisualStudio.TraceDataCollector.resources.dll", + "behavior": "vfs", + "name": "supportFiles/16_Microsoft.VisualStudio.TraceDataCollector.resources.dll" + }, + { + "load_remote": false, + "behavior": "icu", + "name": "icudt.dat" + }, + { + "virtual_path": "/usr/share/zoneinfo/", + "behavior": "vfs", + "name": "dotnet.timezones.blat" + } + ], + "remote_sources": [] +}; diff --git a/runtime.js b/runtime.js new file mode 100644 index 00000000000000..06df15f6817286 --- /dev/null +++ b/runtime.js @@ -0,0 +1,409 @@ +// -*- mode: js; js-indent-level: 4; -*- +// +// Run runtime tests under a JS shell or a browser +// + +//glue code to deal with the differences between chrome, ch, d8, jsc and sm. +var is_browser = typeof window != "undefined"; +var consoleWebSocket; +var print; + +if (is_browser) { + // We expect to be run by tests/runtime/run.js which passes in the arguments using http parameters + window.real_print = console.log; + print = function(_msg) { window.real_print(_msg); }; + console.log = print; + console.debug = print; + console.error = print; + console.trace = print; + console.warn = print; + console.info = print; + + //const consoleUrl = `${window.location.origin}/console`.replace('http://', 'ws://'); + + //consoleWebSocket = new WebSocket(consoleUrl); + //consoleWebSocket.onopen = function(event) { + //consoleWebSocket.send("browser: Console websocket connected."); + + //window.real_print = function(msg) { + //consoleWebSocket.send(msg); + //}; + //}; + //consoleWebSocket.onerror = function(event) { + //console.log(`websocket error: ${event}`); + //}; + + var url = new URL (decodeURI (window.location)); + arguments = []; + for (var v of url.searchParams) { + if (v [0] == "arg") { + console.log ("URL ARG: " + v [0] + "=" + v [1]); + arguments.push (v [1]); + } + } +} + +// JavaScript core does not have a console defined +if (typeof console === "undefined") { + var Console = function () { + this.log = function(msg){ print(msg) }; + this.clear = function() { }; + }; + console = new Console(); +} + +if (typeof console !== "undefined") { + if (!console.debug) + console.debug = console.log; + if (!console.trace) + console.trace = console.log; + if (!console.warn) + console.warn = console.log; + if (!console.error) + console.error = console.log; +} + +if (typeof crypto === 'undefined') { + // **NOTE** this is a simple insecure polyfill for testing purposes only + // /dev/random doesn't work on js shells, so define our own + // See library_fs.js:createDefaultDevices () + var crypto = { + getRandomValues: function (buffer) { + for (var i = 0; i < buffer.length; i++) + buffer [i] = (Math.random () * 256) | 0; + } + } +} + +if (typeof performance == 'undefined') { + // performance.now() is used by emscripten and doesn't work in JSC + var performance = { + now: function () { + return Date.now (); + } + } +} + +try { + if (typeof arguments == "undefined") + arguments = WScript.Arguments; + load = WScript.LoadScriptFile; + read = WScript.LoadBinaryFile; +} catch (e) { +} + +try { + if (typeof arguments == "undefined") { + if (typeof scriptArgs !== "undefined") + arguments = scriptArgs; + } +} catch (e) { +} + +if (arguments === undefined) + arguments = []; + +//end of all the nice shell glue code. + +// set up a global variable to be accessed in App.init +var testArguments = arguments; + +function test_exit (exit_code) { + if (is_browser) { + // Notify the selenium script + Module.exit_code = exit_code; + print ("WASM EXIT " + exit_code); + var tests_done_elem = document.createElement ("label"); + tests_done_elem.id = "tests_done"; + tests_done_elem.innerHTML = exit_code.toString (); + document.body.appendChild (tests_done_elem); + } else { + Module.wasm_exit (exit_code); + } +} + +function fail_exec (reason) { + print (reason); + test_exit (1); +} + +function inspect_object (o) { + var r = ""; + for(var p in o) { + var t = typeof o[p]; + r += "'" + p + "' => '" + t + "', "; + } + return r; +} + +// Preprocess arguments +var args = testArguments; +print("Arguments: " + testArguments); +profilers = []; +setenv = {}; +runtime_args = []; +enable_gc = true; +enable_zoneinfo = false; +working_dir='/'; +while (args !== undefined && args.length > 0) { + if (args [0].startsWith ("--profile=")) { + var arg = args [0].substring ("--profile=".length); + + profilers.push (arg); + + args = args.slice (1); + } else if (args [0].startsWith ("--setenv=")) { + var arg = args [0].substring ("--setenv=".length); + var parts = arg.split ('='); + if (parts.length != 2) + fail_exec ("Error: malformed argument: '" + args [0]); + setenv [parts [0]] = parts [1]; + args = args.slice (1); + } else if (args [0].startsWith ("--runtime-arg=")) { + var arg = args [0].substring ("--runtime-arg=".length); + runtime_args.push (arg); + args = args.slice (1); + } else if (args [0] == "--disable-on-demand-gc") { + enable_gc = false; + args = args.slice (1); + } else if (args [0].startsWith ("--working-dir=")) { + var arg = args [0].substring ("--working-dir=".length); + working_dir = arg; + args = args.slice (1); + } else { + break; + } +} +testArguments = args; + +// cheap way to let the testing infrastructure know we're running in a browser context (or not) +setenv["IsBrowserDomSupported"] = is_browser.toString().toLowerCase(); + +function writeContentToFile(content, path) +{ + var stream = FS.open(path, 'w+'); + FS.write(stream, content, 0, content.length, 0); + FS.close(stream); +} + +function loadScript (url) +{ + if (is_browser) { + var script = document.createElement ("script"); + script.src = url; + document.head.appendChild (script); + } else { + load (url); + } +} + +loadScript ("mono-config.js"); + +var Module = { + mainScriptUrlOrBlob: "dotnet.js", + + print: print, + printErr: function(x) { print ("WASM-ERR: " + x) }, + + onAbort: function(x) { + print ("ABORT: " + x); + var err = new Error(); + print ("Stacktrace: \n"); + print (err.stack); + test_exit (1); + }, + + onRuntimeInitialized: function () { + // Have to set env vars here to enable setting MONO_LOG_LEVEL etc. + for (var variable in setenv) { + MONO.mono_wasm_setenv (variable, setenv [variable]); + } + + if (!enable_gc) { + Module.ccall ('mono_wasm_enable_on_demand_gc', 'void', ['number'], [0]); + } + + MONO.mono_wasm_setenv ("MONO_LOG_LEVEL", "debug"); + MONO.mono_wasm_setenv ("MONO_LOG_MASK", "all"); + + config.loaded_cb = function () { + let wds = FS.stat (working_dir); + if (wds === undefined || !FS.isDir (wds.mode)) { + fail_exec (`Could not find working directory ${working_dir}`); + return; + } + + FS.chdir (working_dir); + //App.init (); + }; + config.fetch_file_cb = function (asset) { + // console.log("fetch_file_cb('" + asset + "')"); + // for testing purposes add BCL assets to VFS until we special case File.Open + // to identify when an assembly from the BCL is being open and resolve it correctly. + /* + var content = new Uint8Array (read (asset, 'binary')); + var path = asset.substr(config.deploy_prefix.length); + writeContentToFile(content, path); + */ + + if (typeof window != 'undefined') { + return fetch (asset, { credentials: 'same-origin' }); + } else { + // The default mono_load_runtime_and_bcl defaults to using + // fetch to load the assets. It also provides a way to set a + // fetch promise callback. + // Here we wrap the file read in a promise and fake a fetch response + // structure. + return new Promise ((resolve, reject) => { + var bytes = null, error = null; + try { + bytes = read (asset, 'binary'); + } catch (exc) { + error = exc; + } + var response = { ok: (bytes && !error), url: asset, + arrayBuffer: function () { + return new Promise ((resolve2, reject2) => { + if (error) + reject2 (error); + else + resolve2 (new Uint8Array (bytes)); + } + )} + } + resolve (response); + }) + } + }; + + MONO.mono_load_runtime_and_bcl_args (config); + }, +}; + +loadScript ("dotnet.js"); + +const IGNORE_PARAM_COUNT = -1; + +var App = { + init: function () { + + var assembly_load = Module.cwrap ('mono_wasm_assembly_load', 'number', ['string']) + var runtime_invoke = Module.cwrap ('mono_wasm_invoke_method', 'number', ['number', 'number', 'number', 'number']); + var string_from_js = Module.cwrap ('mono_wasm_string_from_js', 'number', ['string']); + var assembly_get_entry_point = Module.cwrap ('mono_wasm_assembly_get_entry_point', 'number', ['number']); + var string_get_utf8 = Module.cwrap ('mono_wasm_string_get_utf8', 'string', ['number']); + var string_array_new = Module.cwrap ('mono_wasm_string_array_new', 'number', ['number']); + var obj_array_set = Module.cwrap ('mono_wasm_obj_array_set', 'void', ['number', 'number', 'number']); + var wasm_set_main_args = Module.cwrap ('mono_wasm_set_main_args', 'void', ['number', 'number']); + var wasm_strdup = Module.cwrap ('mono_wasm_strdup', 'number', ['string']); + var unbox_int = Module.cwrap ('mono_unbox_int', 'number', ['number']); + + Module.wasm_exit = Module.cwrap ('mono_wasm_exit', 'void', ['number']); + + Module.print("Initializing....."); + + for (var i = 0; i < profilers.length; ++i) { + var init = Module.cwrap ('mono_wasm_load_profiler_' + profilers [i], 'void', ['string']) + + init (""); + } + + if (args.length == 0) { + fail_exec ("Missing required --run argument"); + return; + } + + if (args[0] == "--regression") { + var exec_regression = Module.cwrap ('mono_wasm_exec_regression', 'number', ['number', 'string']) + + var res = 0; + try { + res = exec_regression (10, args[1]); + Module.print ("REGRESSION RESULT: " + res); + } catch (e) { + Module.print ("ABORT: " + e); + print (e.stack); + res = 1; + } + + if (res) + fail_exec ("REGRESSION TEST FAILED"); + + return; + } + + if (runtime_args.length > 0) + MONO.mono_wasm_set_runtime_options (runtime_args); + + if (args[0] == "--run") { + // Run an exe + if (args.length == 1) { + fail_exec ("Error: Missing main executable argument."); + return; + } + + main_assembly_name = args[1]; + var app_args = string_array_new (args.length - 2); + for (var i = 2; i < args.length; ++i) { + obj_array_set (app_args, i - 2, string_from_js (args [i])); + } + + var main_argc = args.length - 2 + 1; + var main_argv = Module._malloc (main_argc * 4); + aindex = 0; + Module.setValue (main_argv + (aindex * 4), wasm_strdup (args [1]), "i32") + aindex += 1; + for (var i = 2; i < args.length; ++i) { + Module.setValue (main_argv + (aindex * 4), wasm_strdup (args [i]), "i32"); + aindex += 1; + } + wasm_set_main_args (main_argc, main_argv); + + function isThenable (js_obj) { + // When using an external Promise library the Promise.resolve may not be sufficient + // to identify the object as a Promise. + return Promise.resolve (js_obj) === js_obj || + ((typeof js_obj === "object" || typeof js_obj === "function") && typeof js_obj.then === "function") + } + + try { + // Automatic signature isn't working correctly + let exit_code = Module.mono_call_assembly_entry_point (main_assembly_name, [app_args], "m"); + + if (isThenable (exit_code)) + { + exit_code.then ( + (result) => { + test_exit (result); + }, + (reason) => { + console.error (reason); + test_exit (1); + }); + } else { + test_exit (exit_code); + return; + } + } catch (ex) { + print ("JS exception: " + ex); + print (ex.stack); + test_exit (1); + return; + } + } else { + fail_exec ("Unhandled argument: " + args [0]); + } + }, + call_test_method: function (method_name, args) { + if (arguments.length > 2) + throw new Error("Invalid number of arguments for call_test_method"); + + var fqn = "[System.Private.Runtime.InteropServices.JavaScript.Tests]System.Runtime.InteropServices.JavaScript.Tests.HelperMarshal:" + method_name; + try { + return BINDING.call_static_method(fqn, args || []); + } catch (exc) { + console.error("exception thrown in", fqn); + throw exc; + } + } +}; diff --git a/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs b/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs index 46a942a0f9cde1..afabbcf789aa10 100644 --- a/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs +++ b/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs @@ -11,14 +11,29 @@ public class SimpleWasmTestRunner : WasmApplicationEntryPoint { public static async Task Main(string[] args) { - var testAssembly = args[0]; + return await RunMain(args ?? new string[]{}); + } + + public static async Task RunMain(string[] args) + { + if (args == null) + { + args = new string[0]; + + } + Console.WriteLine ($"RunMain: args: {args?.Length}"); + if (args != null) + foreach (var arg in args) + Console.WriteLine ($" arg: {arg}"); + + var testAssembly = args?[0]; var excludedTraits = new List(); var includedTraits = new List(); var includedNamespaces = new List(); var includedClasses = new List(); var includedMethods = new List(); - for (int i = 1; i < args.Length; i++) + for (int i = 1; i < args?.Length; i++) { var option = args[i]; switch (option) @@ -50,7 +65,7 @@ public static async Task Main(string[] args) var runner = new SimpleWasmTestRunner() { - TestAssembly = testAssembly, + TestAssembly = testAssembly ?? String.Empty, ExcludedTraits = excludedTraits, IncludedTraits = includedTraits, IncludedNamespaces = includedNamespaces, diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index 9a7cb150088104..00c6fef46191fc 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -146,7 +146,7 @@ build: EMSDK_PATH=$(EMSDK_PATH) $(TOP)/build.sh mono+libs.pretest -os Browser -c $(CONFIG) /p:ContinueOnError=false /p:StopOnFirstFailure=true build-all: - EMSDK_PATH=$(EMSDK_PATH) $(TOP)/build.sh mono+libs -os Browser -c $(CONFIG) /p:ContinueOnError=false /p:StopOnFirstFailure=true + EMSDK_PATH=$(EMSDK_PATH) $(TOP)/build.sh mono+libs -os Browser -c $(CONFIG) /p:ContinueOnError=false /p:StopOnFirstFailure=true /bl runtime: EMSDK_PATH=$(EMSDK_PATH) $(TOP)/build.sh mono.runtime+mono.wasmruntime+libs.native+libs.pretest -os Browser -c $(CONFIG) /p:ContinueOnError=false /p:StopOnFirstFailure=true diff --git a/src/mono/wasm/debugger/BrowserDebugHost/Program.cs b/src/mono/wasm/debugger/BrowserDebugHost/Program.cs index 6c4a79dfeb238e..d2b5c3c91bbaf9 100644 --- a/src/mono/wasm/debugger/BrowserDebugHost/Program.cs +++ b/src/mono/wasm/debugger/BrowserDebugHost/Program.cs @@ -24,6 +24,16 @@ public class Program { public static void Main(string[] args) { + Uri devToolsUri = new ($"http://localhost:9222"); + if (args.Length >= 1) + devToolsUri = new Uri($"http://localhost:{args[0]}"); + + string proxyUrl = "http://127.0.0.1:0"; + if (args.Length >= 2) + proxyUrl = $"http://127.0.0.1:{args[1]}"; + + Console.WriteLine ($"Chrome devtools: {devToolsUri}"); + IWebHost host = new WebHostBuilder() .UseSetting("UseIISIntegration", false.ToString()) .UseKestrel() @@ -33,7 +43,14 @@ public static void Main(string[] args) { config.AddCommandLine(args); }) - .UseUrls("http://127.0.0.1:0") + .ConfigureServices(services => + { + services.Configure(options => + { + options.DevToolsUrl = devToolsUri; + }); + }) + .UseUrls(proxyUrl) .Build(); host.Run(); diff --git a/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs b/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs index 0a28e88096272d..4119cbf750bd76 100644 --- a/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs +++ b/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs @@ -154,7 +154,7 @@ async Task ConnectProxy(HttpContext context) try { using ILoggerFactory loggerFactory = LoggerFactory.Create( - builder => builder.AddConsole().AddFilter(null, LogLevel.Information)); + builder => builder.AddConsole().AddFilter(null, LogLevel.Debug)); context.Request.Query.TryGetValue("urlSymbolServer", out StringValues urlSymbolServerList); var proxy = new DebuggerProxy(loggerFactory, urlSymbolServerList.ToList()); diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 14c6dbefb1f7ed..6da6086e5c86b2 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -378,6 +378,19 @@ public TypeInfo(AssemblyInfo assembly, TypeDefinition type) public override string ToString() => "TypeInfo('" + FullName + "')"; } + internal class CustomAssemblyResolver : DefaultAssemblyResolver + { + private ILogger _logger; + public CustomAssemblyResolver(ILogger logger) + => _logger = logger; + + public void Register(AssemblyDefinition asm_def) + { + _logger.LogDebug($"CustomAssemblyResolver:Register: {asm_def}"); + RegisterAssembly(asm_def); + } + } + internal class AssemblyInfo { private static int next_id; @@ -439,6 +452,8 @@ public AssemblyInfo(IAssemblyResolver resolver, string url, byte[] assembly, byt this.image = ModuleDefinition.ReadModule(new MemoryStream(assembly), rp); } + ((CustomAssemblyResolver)resolver).Register(this.image.Assembly); + Populate(); } @@ -739,7 +754,19 @@ public DebugStore(ILogger logger, HttpClient client) { this.client = client; this.logger = logger; - this.resolver = new DefaultAssemblyResolver(); + this.resolver = new CustomAssemblyResolver(logger); + ((DefaultAssemblyResolver)resolver).ResolveFailure += (_, name) => + { + logger.LogDebug($"AssemblyResolve failed: {name}"); + + foreach (var dir in ((DefaultAssemblyResolver)resolver).GetSearchDirectories()) + { + logger.LogDebug($"\tsdir: {dir}"); + foreach (var f in Directory.GetFiles(dir)) + Console.WriteLine ($"\t\tf: {f}"); + } + return null; + }; } public DebugStore(ILogger logger) : this(logger, new HttpClient()) @@ -792,6 +819,7 @@ public async IAsyncEnumerable Load(SessionId sessionId, string[] loa asm_files.Add(file_name); } + List steps = new List(); foreach (string url in asm_files) { @@ -819,11 +847,12 @@ public async IAsyncEnumerable Load(SessionId sessionId, string[] loa try { byte[][] bytes = await step.Data.ConfigureAwait(false); + logger.LogDebug($"Loading {step.Url}, pdb: {bytes[1]?.Length > 0}"); assembly = new AssemblyInfo(this.resolver, step.Url, bytes[0], bytes[1]); } catch (Exception e) { - logger.LogDebug($"Failed to load {step.Url} ({e.Message})"); + logger.LogDebug($"Failed to load {step.Url} ({e.ToString()})"); } if (assembly == null) continue; @@ -833,6 +862,7 @@ public async IAsyncEnumerable Load(SessionId sessionId, string[] loa logger.LogDebug($"Skipping loading {assembly.Name} into the debug store, as it already exists"); continue; } + logger.LogDebug($"Loaded {step.Url}, {assembly.AssemblyNameUnqualified} {assembly.Name}"); assemblies.Add(assembly); foreach (SourceFile source in assembly.Sources) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 9c83ce3fa7757b..6927ec83da6a75 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -50,6 +50,7 @@ private bool UpdateContext(SessionId sessionId, ExecutionContext executionContex protected override async Task AcceptEvent(SessionId sessionId, string method, JObject args, CancellationToken token) { + //logger.LogDebug($"AcceptEvent: {method}"); switch (method) { case "Runtime.consoleAPICalled": @@ -159,7 +160,7 @@ protected override async Task AcceptEvent(SessionId sessionId, string meth case var _ when url == "": case var _ when url.StartsWith("wasm://", StringComparison.Ordinal): { - Log("verbose", $"ignoring wasm: Debugger.scriptParsed {url}"); + Log("verbose", $"ignoring wasm: Debugger.scriptParsed url: {url}"); return true; } } @@ -195,7 +196,7 @@ private async Task IsRuntimeAlreadyReadyAlready(SessionId sessionId, Cance protected override async Task AcceptCommand(MessageId id, string method, JObject args, CancellationToken token) { - logger.LogDebug($"AcceptCommand {method}"); + //logger.LogDebug($"AcceptCommand {method}"); // Inspector doesn't use the Target domain or sessions // so we try to init immediately if (id == SessionId.Null) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs index a1b07c21b9742a..932d11cc9de737 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserInstance.cs @@ -69,7 +69,7 @@ public static async Task StartAsync(ILogger logger, Cancellatio public async Task OpenSession(string relativeUrl, Func onMessage, - Action<(RunLoopStopReason reason, Exception? ex)> onRunLoopFailedOrCanceled, + Action<(RunLoopStopReason reason, Exception? ex)> onRunLoopStopped, string testId, ILogger logger, CancellationTokenSource cts, @@ -80,7 +80,7 @@ public async Task OpenSession(string relativeUrl, if (connection == null) { connection = await OpenConnection(testId, logger); - connection.InspectorClient!.RunLoopStopped += (_, args) => onRunLoopFailedOrCanceled(args); + connection.InspectorClient!.RunLoopStopped += (_, args) => onRunLoopStopped(args); } var session = await connection.OpenSession(//this, diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs index ca2156569c2056..aa0a2862602b3b 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs @@ -18,13 +18,13 @@ namespace DebuggerTests { class Inspector { - Dictionary> notifications = new Dictionary>(); - Dictionary> eventListeners = new Dictionary>(); + Dictionary> notifications = new (); + Dictionary> eventListeners = new (); public const string PAUSE = "pause"; public const string READY = "ready"; public CancellationToken Token => _cancellationTokenSource.Token; - public InspectorClient? Client { get; private set; } + // public InspectorClient? Client { get; private set; } public BrowserSession? Session { get; private set; } @@ -156,7 +156,7 @@ public async Task OpenSessionAsync(string relativeUrl, Func @@ -206,6 +206,7 @@ public async Task OpenSessionAsync(string relativeUrl, Func SendCommand(SessionId sessionId, string method, JObject args pending_cmds[new MessageId(sessionId.sessionId, id)] = tcs; var str = o.ToString(); - logger.LogDebug($"SendCommand: id: {id} method: {method} params: {args}"); + // logger.LogDebug($"SendCommand: id: {id} method: {method} params: {args}"); // Console.WriteLine($"SendCommand: id: {id} method: {method} params: {args}"); var bytes = Encoding.UTF8.GetBytes(str); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/appsettings.json b/src/mono/wasm/debugger/DebuggerTestSuite/appsettings.json index 8bafa23e224c4b..b18e86ec689b35 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/appsettings.json +++ b/src/mono/wasm/debugger/DebuggerTestSuite/appsettings.json @@ -4,8 +4,8 @@ "Default": "Error", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information", - "Microsoft.WebAssembly.Diagnostics.TestHarnessProxy": "Trace", - "Microsoft.WebAssembly.Diagnostics.DevToolsProxy": "Trace", + "Microsoft.WebAssembly.Diagnostics.TestHarnessProxy": "Debug", + "Microsoft.WebAssembly.Diagnostics.DevToolsProxy": "Information", "DebuggerTests.Inspector": "Trace" } } diff --git a/src/mono/wasm/runtime-test.js b/src/mono/wasm/runtime-test.js index 14a59c43c6da87..2f50ad3082dd7b 100644 --- a/src/mono/wasm/runtime-test.js +++ b/src/mono/wasm/runtime-test.js @@ -19,19 +19,19 @@ if (is_browser) { console.warn = print; console.info = print; - const consoleUrl = `${window.location.origin}/console`.replace('http://', 'ws://'); + //const consoleUrl = `${window.location.origin}/console`.replace('http://', 'ws://'); - consoleWebSocket = new WebSocket(consoleUrl); - consoleWebSocket.onopen = function(event) { - consoleWebSocket.send("browser: Console websocket connected."); + //consoleWebSocket = new WebSocket(consoleUrl); + //consoleWebSocket.onopen = function(event) { + //consoleWebSocket.send("browser: Console websocket connected."); - window.real_print = function(msg) { - consoleWebSocket.send(msg); - }; - }; - consoleWebSocket.onerror = function(event) { - console.log(`websocket error: ${event}`); - }; + //window.real_print = function(msg) { + //consoleWebSocket.send(msg); + //}; + //}; + //consoleWebSocket.onerror = function(event) { + //console.log(`websocket error: ${event}`); + //}; var url = new URL (decodeURI (window.location)); arguments = []; @@ -231,7 +231,7 @@ var Module = { } FS.chdir (working_dir); - App.init (); + //App.init (); }; config.fetch_file_cb = function (asset) { // console.log("fetch_file_cb('" + asset + "')"); diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 734e5cdc2c855c..a99182f295821a 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -1662,6 +1662,7 @@ var MonoSupportLib = { // "auto" (default): if "icu" behavior assets are present, use ICU, otherwise invariant. // diagnostic_tracing: (optional) enables diagnostic log messages during startup mono_load_runtime_and_bcl_args: function (args) { + console.log(`-- mono_load_runtime_and_bcl_args: ${JSON.stringify(args)}`); try { return this._load_assets_and_runtime (args); } catch (exc) { @@ -1753,7 +1754,7 @@ var MonoSupportLib = { throw new Error ("loaded_cb not provided"); var ctx = { - tracing: args.diagnostic_tracing || false, + tracing: true, pending_count: args.assets.length, mono_wasm_add_assembly: Module.cwrap ('mono_wasm_add_assembly', 'number', ['string', 'number', 'number']), mono_wasm_add_satellite_assembly: Module.cwrap ('mono_wasm_add_satellite_assembly', 'void', ['string', 'string', 'number', 'number']), diff --git a/tools-local/tasks/mobile.tasks/WasmAppBuilder/WasmAppBuilder.cs b/tools-local/tasks/mobile.tasks/WasmAppBuilder/WasmAppBuilder.cs index fce387d996751c..8b77253425aa68 100644 --- a/tools-local/tasks/mobile.tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/tools-local/tasks/mobile.tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -189,7 +189,7 @@ public override bool Execute () File.Copy(Path.Join (runtimeSourceDir, f), Path.Join(AppDir, f), true); File.Copy(MainJS!, Path.Join(AppDir, "runtime.js"), true); - var html = @""; + var html = @""; File.WriteAllText(Path.Join(AppDir, "index.html"), html); foreach (var assembly in _assemblies.Values) {