diff --git a/eng/pipelines/common/evaluate-default-paths.yml b/eng/pipelines/common/evaluate-default-paths.yml
index b5effcf62675a8..9d171b672427fe 100644
--- a/eng/pipelines/common/evaluate-default-paths.yml
+++ b/eng/pipelines/common/evaluate-default-paths.yml
@@ -109,6 +109,7 @@ jobs:
- src/tests/BuildWasmApps/*
- src/mono/wasm/build/*
- src/mono/wasm/runtime/*
+ - src/mono/wasm/templates/*
- src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/*
- src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/*
- src/mono/nuget/Microsoft.NET.Runtime.wasm.Sample.Mono/*
@@ -125,7 +126,9 @@ jobs:
include:
- src/mono/wasm/debugger/*
- src/mono/wasm/runtime/*
- - src/mono/wasm/BrowsersForTesting.props
+ - src/tests/BuildWasmApps/*
+ - eng/testing/ProvisioningVersions.props
+ - eng/testing/scenarios/WasmDebuggerTestsJobsList.txt
- src/mono/mono/*
- subset: allwasm
include:
diff --git a/src/mono/wasm/BrowsersForTesting.props b/eng/testing/ProvisioningVersions.props
similarity index 100%
rename from src/mono/wasm/BrowsersForTesting.props
rename to eng/testing/ProvisioningVersions.props
diff --git a/eng/testing/provisioning.targets b/eng/testing/provisioning.targets
new file mode 100644
index 00000000000000..38293a916706de
--- /dev/null
+++ b/eng/testing/provisioning.targets
@@ -0,0 +1,67 @@
+
+
+ $(ArtifactsBinDir)chrome\
+ $(ArtifactsBinDir)\
+ $(BrowserStampDir).install-chrome-$(ChromiumRevision).stamp
+ $(ArtifactsBinDir)firefox\
+ $(BrowserStampDir).install-firefox-$(FirefoxRevision).stamp
+
+
+
+
+
+
+
+ <_StampFile Include="$(BrowserStampDir).install-chrome*.stamp" />
+
+
+
+
+
+
+
+
+
+
+
+ <_ChromeBinaryPath>$([MSBuild]::NormalizePath($(ChromeDir), $(ChromiumDirName), $(ChromiumBinaryName)))
+
+
+
+
+
+
+
+
+
+
+
+ <_StampFile Include="$(BrowserStampDir).install-firefox*.stamp" />
+
+
+
+
+
+
+
+
+
+
+
+
+ <_FirefoxBinaryPath>$([MSBuild]::NormalizePath($(FirefoxDir), $(FirefoxBinaryName)))
+
+
+
+
+
+
+
+
+
diff --git a/src/libraries/sendtohelix-wasm.targets b/src/libraries/sendtohelix-wasm.targets
index 6aea5dfa3b797f..3486b253e48c64 100644
--- a/src/libraries/sendtohelix-wasm.targets
+++ b/src/libraries/sendtohelix-wasm.targets
@@ -115,7 +115,7 @@
-
+
diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets
index cf8b18508cd7a4..bc598307276bfc 100644
--- a/src/mono/wasm/build/WasmApp.targets
+++ b/src/mono/wasm/build/WasmApp.targets
@@ -305,6 +305,7 @@
+
diff --git a/src/mono/wasm/debugger/BrowserDebugHost/DebugProxyHost.cs b/src/mono/wasm/debugger/BrowserDebugHost/DebugProxyHost.cs
new file mode 100644
index 00000000000000..737869622e550b
--- /dev/null
+++ b/src/mono/wasm/debugger/BrowserDebugHost/DebugProxyHost.cs
@@ -0,0 +1,65 @@
+// 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.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+#nullable enable
+
+namespace Microsoft.WebAssembly.Diagnostics;
+
+public static class DebugProxyHost
+{
+ public static Task RunDebugProxyAsync(ProxyOptions options, string[] args, ILoggerFactory loggerFactory, CancellationToken token)
+ {
+ return Task.WhenAny(
+ RunFirefoxServerLoopAsync(options, args, loggerFactory, token),
+ RunDevToolsProxyAsync(options, args, loggerFactory, token)
+ )
+ .ContinueWith(t => Console.WriteLine($"Debug proxy server failed with {t.Exception}"),
+ token,
+ TaskContinuationOptions.OnlyOnFaulted,
+ TaskScheduler.Default);
+ }
+
+ public static Task RunFirefoxServerLoopAsync(ProxyOptions options, string[] args, ILoggerFactory loggerFactory, CancellationToken token)
+ => FirefoxDebuggerProxy.RunServerLoopAsync(browserPort: options.FirefoxDebugPort,
+ proxyPort: options.FirefoxProxyPort,
+ loggerFactory,
+ loggerFactory.CreateLogger("FirefoxMonoProxy"),
+ token,
+ autoSetBreakpointOnEntryPoint: options.AutoSetBreakpointOnEntryPoint);
+
+ public static async Task RunDevToolsProxyAsync(ProxyOptions options, string[] args, ILoggerFactory loggerFactory, CancellationToken token)
+ {
+ string proxyUrl = $"http://127.0.0.1:{options.DevToolsProxyPort}";
+ IWebHost host = new WebHostBuilder()
+ .UseSetting("UseIISIntegration", false.ToString())
+ .UseKestrel()
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .UseStartup()
+ .ConfigureServices(services =>
+ {
+ services.AddSingleton(loggerFactory);
+ services.AddLogging(configure => configure.AddSimpleConsole().AddFilter(null, LogLevel.Information));
+ services.AddSingleton(Options.Create(options));
+ services.AddRouting();
+ })
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config.AddCommandLine(args);
+ })
+ .UseUrls(proxyUrl)
+ .Build();
+
+ token.Register(async () => { Console.WriteLine($"-- token got cancelled, stopping host"); await host.StopAsync(); });
+ await host.RunAsync(token);
+ }
+}
diff --git a/src/mono/wasm/debugger/BrowserDebugHost/Program.cs b/src/mono/wasm/debugger/BrowserDebugHost/Program.cs
index 61100fb4dd7169..320e30deaea866 100644
--- a/src/mono/wasm/debugger/BrowserDebugHost/Program.cs
+++ b/src/mono/wasm/debugger/BrowserDebugHost/Program.cs
@@ -2,37 +2,24 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
-using System.Collections.Generic;
using System.IO;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
#nullable enable
namespace Microsoft.WebAssembly.Diagnostics
{
- public class ProxyOptions
- {
- public Uri DevToolsUrl { get; set; } = new Uri("http://localhost:9222");
-
- public int? OwnerPid { get; set; }
- }
-
public class Program
{
- public static void Main(string[] args)
+ public static async Task Main(string[] args)
{
IConfigurationRoot config = new ConfigurationBuilder().AddCommandLine(args).Build();
- int proxyPort = 0;
- if (config["proxy-port"] is not null && int.TryParse(config["proxy-port"], out int port))
- proxyPort = port;
- int firefoxDebugPort = 6000;
- if (config["firefox-debug-port"] is not null && int.TryParse(config["firefox-debug-port"], out int ffport))
- firefoxDebugPort = ffport;
- string? logPath = config["log-path"];
-
+ ProxyOptions options = new();
+ config.Bind(options);
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
@@ -40,30 +27,28 @@ public static void Main(string[] args)
{
options.TimestampFormat = "[HH:mm:ss] ";
})
- .AddFilter(null, LogLevel.Debug);
+ .AddFilter("DevToolsProxy", LogLevel.Information)
+ .AddFilter("FirefoxMonoProxy", LogLevel.Information)
+ .AddFilter(null, LogLevel.Warning);
- if (!string.IsNullOrEmpty(logPath))
- builder.AddFile(Path.Combine(logPath, "proxy.log"),
+ if (!string.IsNullOrEmpty(options.LogPath))
+ builder.AddFile(Path.Combine(options.LogPath, "proxy.log"),
minimumLevel: LogLevel.Trace,
outputTemplate: "{Timestamp:o} [{Level:u3}] {SourceContext}: {Message}{NewLine}{Exception}");
});
- ILogger logger = loggerFactory.CreateLogger("FirefoxMonoProxy");
- _ = FirefoxDebuggerProxy.Run(browserPort: firefoxDebugPort, proxyPort: proxyPort, loggerFactory, logger);
+ CancellationTokenSource cts = new();
+ _ = Task.Run(() => DebugProxyHost.RunDebugProxyAsync(options, args, loggerFactory, cts.Token))
+ .ConfigureAwait(false);
- IWebHost host = new WebHostBuilder()
- .UseSetting("UseIISIntegration", false.ToString())
- .UseKestrel()
- .UseContentRoot(Directory.GetCurrentDirectory())
- .UseStartup()
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config.AddCommandLine(args);
- })
- .UseUrls($"http://127.0.0.1:{proxyPort}")
- .Build();
+ TaskCompletionSource tcs = new();
+ Console.CancelKeyPress += (_, _) =>
+ {
+ tcs.SetResult();
+ cts.Cancel();
+ };
- host.Run();
+ await tcs.Task;
}
}
}
diff --git a/src/mono/wasm/debugger/BrowserDebugHost/ProxyOptions.cs b/src/mono/wasm/debugger/BrowserDebugHost/ProxyOptions.cs
new file mode 100644
index 00000000000000..5e0638e6266808
--- /dev/null
+++ b/src/mono/wasm/debugger/BrowserDebugHost/ProxyOptions.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+#nullable enable
+
+namespace Microsoft.WebAssembly.Diagnostics;
+
+public class ProxyOptions
+{
+ public Uri DevToolsUrl { get; set; } = new Uri($"http://localhost:9222");
+ public int? OwnerPid { get; set; }
+ public int FirefoxProxyPort { get; set; } = 6300;
+ public int FirefoxDebugPort { get; set; } = 6000;
+ public int DevToolsProxyPort { get; set; } = 9300;
+ public int DevToolsDebugPort
+ {
+ get => DevToolsUrl.Port;
+ set
+ {
+ var builder = new UriBuilder(DevToolsUrl)
+ {
+ Port = value
+ };
+ DevToolsUrl = builder.Uri;
+ }
+ }
+ public string? LogPath { get; set; }
+ public bool AutoSetBreakpointOnEntryPoint { get; set; }
+}
diff --git a/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs b/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs
index 6b58163104fd31..f76b96836ad86b 100644
--- a/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs
+++ b/src/mono/wasm/debugger/BrowserDebugHost/Startup.cs
@@ -10,7 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
@@ -24,12 +24,6 @@ namespace Microsoft.WebAssembly.Diagnostics
{
internal sealed class Startup
{
- // This method gets called by the runtime. Use this method to add services to the container.
- // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
- public void ConfigureServices(IServiceCollection services) =>
- services.AddRouting()
- .Configure(Configuration);
-
public Startup(IConfiguration configuration) =>
Configuration = configuration;
@@ -37,9 +31,9 @@ public Startup(IConfiguration configuration) =>
#pragma warning disable CA1822
// 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, IHostApplicationLifetime applicationLifetime)
+ public void Configure(IApplicationBuilder app, IOptions optionsContainer, ILogger logger, IHostApplicationLifetime applicationLifetime)
{
- ProxyOptions options = optionsAccessor.CurrentValue;
+ ProxyOptions options = optionsContainer.Value;
if (options.OwnerPid.HasValue)
{
@@ -54,15 +48,30 @@ public void Configure(IApplicationBuilder app, IOptionsMonitor opt
}
}
+ applicationLifetime.ApplicationStarted.Register(() =>
+ {
+ string ipAddress = app.ServerFeatures
+ .Get()?
+ .Addresses?
+ .Where(a => a.StartsWith("http:", StringComparison.InvariantCultureIgnoreCase))
+ .Select(a => new Uri(a))
+ .Select(uri => uri.ToString())
+ .FirstOrDefault();
+
+ Console.WriteLine($"{Environment.NewLine}Debug proxy for chrome now listening on {ipAddress}. And expecting chrome at {options.DevToolsUrl}");
+ });
+
app.UseDeveloperExceptionPage()
.UseWebSockets()
- .UseDebugProxy(options);
+ .UseDebugProxy(logger, options);
}
#pragma warning restore CA1822
}
internal static class DebugExtensions
{
+ private static readonly HttpClient s_httpClient = new();
+
public static Dictionary MapValues(Dictionary response, HttpContext context, Uri debuggerHost)
{
var filtered = new Dictionary();
@@ -88,11 +97,12 @@ public static Dictionary MapValues(Dictionary re
return filtered;
}
- public static IApplicationBuilder UseDebugProxy(this IApplicationBuilder app, ProxyOptions options) =>
- UseDebugProxy(app, options, MapValues);
+ public static IApplicationBuilder UseDebugProxy(this IApplicationBuilder app, ILogger logger, ProxyOptions options) =>
+ UseDebugProxy(app, logger, options, MapValues);
public static IApplicationBuilder UseDebugProxy(
this IApplicationBuilder app,
+ ILogger logger,
ProxyOptions options,
Func, HttpContext, Uri, Dictionary> mapFunc)
{
@@ -117,33 +127,54 @@ string GetEndpoint(HttpContext context)
async Task Copy(HttpContext context)
{
- using (var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) })
+ try
{
- HttpResponseMessage response = await httpClient.GetAsync(GetEndpoint(context));
+ HttpResponseMessage response = await s_httpClient.GetAsync(GetEndpoint(context));
context.Response.ContentType = response.Content.Headers.ContentType.ToString();
if ((response.Content.Headers.ContentLength ?? 0) > 0)
context.Response.ContentLength = response.Content.Headers.ContentLength;
byte[] bytes = await response.Content.ReadAsByteArrayAsync();
await context.Response.Body.WriteAsync(bytes);
}
+ catch (HostConnectionException hce)
+ {
+ logger.LogWarning(hce.Message);
+ context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
+ }
}
async Task RewriteSingle(HttpContext context)
{
- Dictionary version = await ProxyGetJsonAsync>(GetEndpoint(context));
- context.Response.ContentType = "application/json";
- await context.Response.WriteAsync(
- JsonSerializer.Serialize(mapFunc(version, context, devToolsHost)));
+ try
+ {
+ Dictionary version = await ProxyGetJsonAsync>(GetEndpoint(context));
+ context.Response.ContentType = "application/json";
+ await context.Response.WriteAsync(
+ JsonSerializer.Serialize(mapFunc(version, context, devToolsHost)));
+ }
+ catch (HostConnectionException hce)
+ {
+ logger.LogWarning(hce.Message);
+ context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
+ }
}
async Task RewriteArray(HttpContext context)
{
- Dictionary[] tabs = await ProxyGetJsonAsync[]>(GetEndpoint(context));
- Dictionary[] alteredTabs = tabs.Select(t => mapFunc(t, context, devToolsHost)).ToArray();
- context.Response.ContentType = "application/json";
- string text = JsonSerializer.Serialize(alteredTabs);
- context.Response.ContentLength = text.Length;
- await context.Response.WriteAsync(text);
+ try
+ {
+ Dictionary[] tabs = await ProxyGetJsonAsync[]>(GetEndpoint(context));
+ Dictionary[] alteredTabs = tabs.Select(t => mapFunc(t, context, devToolsHost)).ToArray();
+ context.Response.ContentType = "application/json";
+ string text = JsonSerializer.Serialize(alteredTabs);
+ context.Response.ContentLength = text.Length;
+ await context.Response.WriteAsync(text);
+ }
+ catch (HostConnectionException hce)
+ {
+ logger.LogWarning(hce.Message);
+ context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
+ }
}
async Task ConnectProxy(HttpContext context)
@@ -165,25 +196,19 @@ async Task ConnectProxy(HttpContext context)
CancellationTokenSource cts = new();
try
{
- using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
- builder.AddSimpleConsole(options =>
- {
- options.SingleLine = true;
- options.TimestampFormat = "[HH:mm:ss] ";
- })
- .AddFilter(null, LogLevel.Information)
- );
-
+ var loggerFactory = context.RequestServices.GetService();
context.Request.Query.TryGetValue("urlSymbolServer", out StringValues urlSymbolServerList);
var proxy = new DebuggerProxy(loggerFactory, urlSymbolServerList.ToList(), runtimeId);
System.Net.WebSockets.WebSocket ideSocket = await context.WebSockets.AcceptWebSocketAsync();
+ logger.LogInformation("Connection accepted from IDE. Starting debug proxy...");
await proxy.Run(endpoint, ideSocket, cts);
}
catch (Exception e)
{
- Console.WriteLine("got exception {0}", e);
+ logger.LogError($"Failed to start proxy: {e}");
+ context.Response.StatusCode = StatusCodes.Status500InternalServerError;
cts.Cancel();
}
}
@@ -193,11 +218,22 @@ async Task ConnectProxy(HttpContext context)
private static async Task ProxyGetJsonAsync(string url)
{
- using (var httpClient = new HttpClient())
+ try
{
- HttpResponseMessage response = await httpClient.GetAsync(url);
+ HttpResponseMessage response = await s_httpClient.GetAsync(url);
return await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync());
}
+ catch (HttpRequestException hre)
+ {
+ throw new HostConnectionException($"Failed to read from the host at {url}. Make sure the host is running. error: {hre.Message}", hre);
+ }
+ }
+ }
+
+ internal sealed class HostConnectionException : Exception
+ {
+ public HostConnectionException(string message, Exception innerException) : base(message, innerException)
+ {
}
}
}
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
index 47bc311b319057..25f47ca8da5e9b 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
@@ -17,10 +17,8 @@
using System.Reflection.PortableExecutable;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
-using Microsoft.CodeAnalysis.Debugging;
using System.IO.Compression;
using System.Reflection;
-using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
@@ -79,11 +77,6 @@ internal sealed class BreakpointRequest
public BreakpointRequest()
{ }
- public BreakpointRequest(string id, MethodInfo method)
- {
- Id = id;
- Method = method;
- }
public BreakpointRequest(string id, JObject request)
{
@@ -797,11 +790,13 @@ internal sealed class AssemblyInfo
internal string PdbName { get; }
internal bool PdbInformationAvailable { get; }
public bool TriedToLoadSymbolsOnDemand { get; set; }
+ public MethodInfo EntryPoint { get; private set; }
- public unsafe AssemblyInfo(MonoProxy monoProxy, SessionId sessionId, string url, byte[] assembly, byte[] pdb, CancellationToken token)
+ public unsafe AssemblyInfo(MonoProxy monoProxy, SessionId sessionId, string url, byte[] assembly, byte[] pdb, ILogger logger, CancellationToken token)
{
debugId = -1;
this.id = Interlocked.Increment(ref next_id);
+ this.logger = logger;
using var asmStream = new MemoryStream(assembly);
peReader = new PEReader(asmStream);
var entries = peReader.ReadDebugDirectory();
@@ -867,11 +862,6 @@ public bool EnC(byte[] meta, byte[] pdb)
return true;
}
- public AssemblyInfo(ILogger logger)
- {
- this.logger = logger;
- }
-
private void PopulateEnC(MetadataReader asmMetadataReaderParm, MetadataReader pdbMetadataReaderParm)
{
int i = 1;
@@ -909,6 +899,11 @@ SourceFile FindSource(DocumentHandle doc, int rowid, string documentName)
if (pdbMetadataReader != null)
ProcessSourceLink();
+ MethodDefinitionHandle entryPointHandle = default;
+ if (asmMetadataReader.DebugMetadataHeader is not null)
+ entryPointHandle = asmMetadataReader.DebugMetadataHeader.EntryPoint;
+ if (pdbMetadataReader is not null && pdbMetadataReader.DebugMetadataHeader is not null)
+ entryPointHandle = pdbMetadataReader.DebugMetadataHeader.EntryPoint;
foreach (TypeDefinitionHandle type in asmMetadataReader.TypeDefinitions)
{
var typeDefinition = asmMetadataReader.GetTypeDefinition(type);
@@ -936,6 +931,11 @@ SourceFile FindSource(DocumentHandle doc, int rowid, string documentName)
source.AddMethod(methodInfo);
typeInfo.Methods.Add(methodInfo);
+ if (entryPointHandle.IsNil || EntryPoint is not null)
+ continue;
+
+ if (method.Equals(entryPointHandle))
+ EntryPoint = methodInfo;
}
}
}
@@ -1129,8 +1129,7 @@ private async Task GetDataAsync(Uri uri, CancellationToken token)
}
else if (uri.Scheme == "http" || uri.Scheme == "https")
{
- using (var client = new HttpClient())
- using (Stream stream = await client.GetStreamAsync(uri, token))
+ using (Stream stream = await MonoProxy.HttpClient.GetStreamAsync(uri, token))
{
await stream.CopyToAsync(mem, token).ConfigureAwait(false);
mem.Position = 0;
@@ -1226,20 +1225,16 @@ public object ToScriptSource(int executionContextId, object executionContextAuxD
internal sealed class DebugStore
{
internal List assemblies = new List();
- private readonly HttpClient client;
private readonly ILogger logger;
private readonly MonoProxy monoProxy;
+ private MethodInfo _entryPoint;
- public DebugStore(MonoProxy monoProxy, ILogger logger, HttpClient client)
+ public DebugStore(MonoProxy monoProxy, ILogger logger)
{
- this.client = client;
this.logger = logger;
this.monoProxy = monoProxy;
}
- public DebugStore(MonoProxy monoProxy, ILogger logger) : this(monoProxy, logger, new HttpClient())
- { }
-
private sealed class DebugItem
{
public string Url { get; set; }
@@ -1261,7 +1256,7 @@ public IEnumerable Add(SessionId id, string name, byte[] assembly_da
AssemblyInfo assembly;
try
{
- assembly = new AssemblyInfo(monoProxy, id, name, assembly_data, pdb_data, token);
+ assembly = new AssemblyInfo(monoProxy, id, name, assembly_data, pdb_data, logger, token);
}
catch (Exception e)
{
@@ -1309,7 +1304,7 @@ public async IAsyncEnumerable Load(SessionId id, string[] loaded_fil
new DebugItem
{
Url = url,
- Data = Task.WhenAll(client.GetByteArrayAsync(url, token), pdb != null ? client.GetByteArrayAsync(pdb, token) : Task.FromResult(null))
+ Data = Task.WhenAll(MonoProxy.HttpClient.GetByteArrayAsync(url, token), pdb != null ? MonoProxy.HttpClient.GetByteArrayAsync(pdb, token) : Task.FromResult(null))
});
}
catch (Exception e)
@@ -1324,7 +1319,7 @@ public async IAsyncEnumerable Load(SessionId id, string[] loaded_fil
try
{
byte[][] bytes = await step.Data.ConfigureAwait(false);
- assembly = new AssemblyInfo(monoProxy, id, step.Url, bytes[0], bytes[1], token);
+ assembly = new AssemblyInfo(monoProxy, id, step.Url, bytes[0], bytes[1], logger, token);
}
catch (Exception e)
{
@@ -1350,7 +1345,13 @@ public async IAsyncEnumerable Load(SessionId id, string[] loaded_fil
public SourceFile GetFileById(SourceId id) => AllSources().SingleOrDefault(f => f.SourceId.Equals(id));
public AssemblyInfo GetAssemblyByName(string name) => assemblies.FirstOrDefault(a => a.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
+ public MethodInfo FindEntryPoint()
+ {
+ if (_entryPoint is null)
+ _entryPoint = assemblies.Where(asm => asm.EntryPoint is not null).Select(asm => asm.EntryPoint).FirstOrDefault();
+ return _entryPoint;
+ }
/*
V8 uses zero based indexing for both line and column.
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebuggerProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebuggerProxy.cs
index a33f3a2b58fddc..5a1b4cba1dd9a9 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/DebuggerProxy.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebuggerProxy.cs
@@ -18,9 +18,13 @@ public class DebuggerProxy : DebuggerProxyBase
{
internal MonoProxy MonoProxy { get; }
- public DebuggerProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList, int runtimeId = 0, string loggerId = "")
+ public DebuggerProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList, int runtimeId = 0, string loggerId = "", bool autoSetBreakpointOnEntryPoint = false)
{
- MonoProxy = new MonoProxy(loggerFactory, urlSymbolServerList, runtimeId, loggerId);
+ string suffix = loggerId.Length > 0 ? $"-{loggerId}" : string.Empty;
+ MonoProxy = new MonoProxy(loggerFactory.CreateLogger($"DevToolsProxy{suffix}"), urlSymbolServerList, runtimeId, loggerId)
+ {
+ AutoSetBreakpointOnEntryPoint = autoSetBreakpointOnEntryPoint
+ };
}
public Task Run(Uri browserUri, WebSocket ideSocket, CancellationTokenSource cts)
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
index df8e33460e8260..ff76100ea87471 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
@@ -301,6 +301,8 @@ internal sealed class MonoCommands
public static MonoCommands GetLoadedFiles(int runtimeId) => new MonoCommands($"getDotnetRuntime({runtimeId}).INTERNAL.mono_wasm_get_loaded_files()");
+ public static MonoCommands SetDebuggerAttached(int runtimeId) => new MonoCommands($"getDotnetRuntime({runtimeId}).INTERNAL.mono_wasm_debugger_attached()");
+
public static MonoCommands SendDebuggerAgentCommand(int runtimeId, int id, int command_set, int command, string command_parameters)
{
return new MonoCommands($"getDotnetRuntime({runtimeId}).INTERNAL.mono_wasm_send_dbg_command ({id}, {command_set}, {command},'{command_parameters}')");
@@ -435,6 +437,7 @@ public ExecutionContext(MonoSDBHelper sdbAgent, int id, object auxData)
private Dictionary perScopeCaches { get; } = new Dictionary();
internal int TempBreakpointForSetNextIP { get; set; }
+ internal bool FirstBreakpoint { get; set; }
public DebugStore Store
{
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs
index 806c05cbf40e20..69877bdb507d3e 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsProxy.cs
@@ -34,11 +34,10 @@ internal class DevToolsProxy
public bool IsRunning => Stopped is null;
public RunLoopExitState Stopped { get; private set; }
- public DevToolsProxy(ILoggerFactory loggerFactory, string loggerId)
+ public DevToolsProxy(ILogger logger, string loggerId)
{
_loggerId = loggerId;
- string loggerSuffix = string.IsNullOrEmpty(loggerId) ? string.Empty : $"-{loggerId}";
- logger = loggerFactory.CreateLogger($"DevToolsProxy{loggerSuffix}");
+ this.logger = logger;
var channel = Channel.CreateUnbounded(new UnboundedChannelOptions { SingleReader = true });
_channelWriter = channel.Writer;
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FireforDebuggerProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FireforDebuggerProxy.cs
index 7f0f6c7f70bef7..1da6e10b5fac65 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FireforDebuggerProxy.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FireforDebuggerProxy.cs
@@ -22,23 +22,23 @@ public class FirefoxDebuggerProxy : DebuggerProxyBase
internal FirefoxMonoProxy? FirefoxMonoProxy { get; private set; }
[MemberNotNull(nameof(s_tcpListener))]
- public static void StartListener(int proxyPort, ILogger logger)
+ public static void StartListener(int proxyPort, ILogger logger, int browserPort = -1)
{
if (s_tcpListener is null)
{
s_tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), proxyPort);
s_tcpListener.Start();
- logger.LogInformation($"Now listening for Firefox on: {s_tcpListener.LocalEndpoint}");
+ Console.WriteLine($"{Environment.NewLine}Debug proxy for firefox now listening on tcp://{s_tcpListener.LocalEndpoint}." +
+ (browserPort >= 0 ? $"And expecting firefox at port {browserPort}" : string.Empty));
}
}
- public static async Task Run(int browserPort, int proxyPort, ILoggerFactory loggerFactory, ILogger logger)
+ public static async Task RunServerLoopAsync(int browserPort, int proxyPort, ILoggerFactory loggerFactory, ILogger logger, CancellationToken token, bool autoSetBreakpointOnEntryPoint = false)
{
- StartListener(proxyPort, logger);
- logger.LogInformation($"Expecting firefox to be listening on {browserPort}");
- while (true)
+ StartListener(proxyPort, logger, browserPort);
+ while (!token.IsCancellationRequested)
{
- TcpClient ideClient = await s_tcpListener.AcceptTcpClientAsync();
+ TcpClient ideClient = await s_tcpListener.AcceptTcpClientAsync(token);
_ = Task.Run(async () =>
{
CancellationTokenSource cts = new();
@@ -46,29 +46,31 @@ public static async Task Run(int browserPort, int proxyPort, ILoggerFactory logg
{
int id = Interlocked.Increment(ref s_nextId);
logger.LogInformation($"IDE connected to the proxy, id: {id}");
- var monoProxy = new FirefoxMonoProxy(loggerFactory, id.ToString());
+ var monoProxy = new FirefoxMonoProxy(loggerFactory.CreateLogger($"{nameof(FirefoxMonoProxy)}-{id}"), id.ToString())
+ {
+ AutoSetBreakpointOnEntryPoint = autoSetBreakpointOnEntryPoint
+ };
await monoProxy.RunForFirefox(ideClient: ideClient, browserPort, cts);
}
catch (Exception ex)
{
logger.LogError($"{nameof(FirefoxMonoProxy)} crashed with {ex}");
- throw;
}
finally
{
cts.Cancel();
}
- }, CancellationToken.None)
+ }, token)
.ConfigureAwait(false);
}
}
public async Task RunForTests(int browserPort, int proxyPort, string testId, ILoggerFactory loggerFactory, ILogger logger, CancellationTokenSource cts)
{
- StartListener(proxyPort, logger);
+ StartListener(proxyPort, logger, browserPort);
TcpClient ideClient = await s_tcpListener.AcceptTcpClientAsync(cts.Token);
- FirefoxMonoProxy = new FirefoxMonoProxy(loggerFactory, testId);
+ FirefoxMonoProxy = new FirefoxMonoProxy(loggerFactory.CreateLogger($"FirefoxMonoProxy-{testId}"), testId);
FirefoxMonoProxy.RunLoopStopped += (_, args) => ExitState = args;
await FirefoxMonoProxy.RunForFirefox(ideClient: ideClient, browserPort, cts);
}
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FirefoxMonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FirefoxMonoProxy.cs
index 607a9be796bd5a..8451049cdd9e6c 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FirefoxMonoProxy.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/Firefox/FirefoxMonoProxy.cs
@@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using System.Net.Sockets;
+using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using BrowserDebugProxy;
@@ -16,7 +17,7 @@ namespace Microsoft.WebAssembly.Diagnostics;
internal sealed class FirefoxMonoProxy : MonoProxy
{
- public FirefoxMonoProxy(ILoggerFactory loggerFactory, string loggerId = null) : base(loggerFactory, null, loggerId: loggerId)
+ public FirefoxMonoProxy(ILogger logger, string loggerId = null) : base(logger, null, loggerId: loggerId)
{
}
@@ -42,7 +43,7 @@ public async Task RunForFirefox(TcpClient ideClient, int portBrowser, Cancellati
await StartRunLoop(ideConn, browserConn, cts);
if (Stopped?.reason == RunLoopStopReason.Exception)
- throw Stopped.exception;
+ ExceptionDispatchInfo.Capture(Stopped.exception).Throw();
}
finally
{
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
index 4dc9d03892dcac..14f06d45cda9e0 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
@@ -19,16 +19,19 @@ namespace Microsoft.WebAssembly.Diagnostics
internal class MonoProxy : DevToolsProxy
{
private IList urlSymbolServerList;
- private static HttpClient client = new HttpClient();
private HashSet sessions = new HashSet();
protected Dictionary contexts = new Dictionary();
private const string sPauseOnUncaught = "pause_on_uncaught";
private const string sPauseOnCaught = "pause_on_caught";
+
+ public static HttpClient HttpClient => new HttpClient();
+
// index of the runtime in a same JS page/process
public int RuntimeId { get; private init; }
public bool JustMyCode { get; private set; }
+ public bool AutoSetBreakpointOnEntryPoint { get; set; }
- public MonoProxy(ILoggerFactory loggerFactory, IList urlSymbolServerList, int runtimeId = 0, string loggerId = "") : base(loggerFactory, loggerId)
+ public MonoProxy(ILogger logger, IList urlSymbolServerList, int runtimeId = 0, string loggerId = "") : base(logger, loggerId)
{
this.urlSymbolServerList = urlSymbolServerList ?? new List();
RuntimeId = runtimeId;
@@ -1153,7 +1156,7 @@ internal async Task LoadSymbolsOnDemand(AssemblyInfo asm, int method
try
{
- using HttpResponseMessage response = await client.GetAsync(downloadURL, token);
+ using HttpResponseMessage response = await HttpClient.GetAsync(downloadURL, token);
if (!response.IsSuccessStatusCode)
{
Log("info", $"Unable to download symbols on demand url:{downloadURL} assembly: {asm.Name}");
@@ -1436,10 +1439,36 @@ internal async Task LoadStore(SessionId sessionId, CancellationToken
{
await OnSourceFileAdded(sessionId, source, context, token);
}
+
+ if (AutoSetBreakpointOnEntryPoint)
+ {
+ var entryPoint = context.store.FindEntryPoint();
+ if (entryPoint is not null)
+ {
+ var sourceFile = entryPoint.Assembly.Sources.Single(sf => sf.SourceId == entryPoint.SourceId);
+ string bpId = $"auto:{entryPoint.StartLocation.Line}:{entryPoint.StartLocation.Column}:{sourceFile.DotNetUrl}";
+ BreakpointRequest request = new(bpId, JObject.FromObject(new
+ {
+ lineNumber = entryPoint.StartLocation.Line,
+ columnNumber = entryPoint.StartLocation.Column,
+ url = sourceFile.Url
+ }));
+ logger.LogDebug($"Adding bp req {request}");
+ context.BreakpointRequests[bpId] = request;
+ request.TryResolve(sourceFile);
+ if (request.TryResolve(sourceFile))
+ await SetBreakpoint(sessionId, context.store, request, true, true, token);
+ }
+ else
+ {
+ logger.LogDebug($"No entrypoint found, for setting automatic breakpoint");
+ }
+ }
}
}
catch (Exception e)
{
+ logger.LogError($"failed: {e}");
context.Source.SetException(e);
}
@@ -1485,6 +1514,7 @@ protected async Task RuntimeReady(SessionId sessionId, CancellationT
DebugStore store = await LoadStore(sessionId, token);
context.ready.SetResult(store);
await SendEvent(sessionId, "Mono.runtimeReady", new JObject(), token);
+ await SendMonoCommand(sessionId, MonoCommands.SetDebuggerAttached(RuntimeId), token);
context.SdbAgent.ResetStore(store);
return store;
}
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BrowserLocator.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserLocator.cs
new file mode 100644
index 00000000000000..70a9bd87c56f93
--- /dev/null
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/BrowserLocator.cs
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace Wasm.Tests.Internal;
+
+internal static class BrowserLocator
+{
+ public static string FindChrome(string artifactsBinDir, string envVarName)
+ => GetBrowserPath(GetChromePathsToProbe(artifactsBinDir), envVarName);
+
+ public static string FindFirefox(string artifactsBinDir, string envVarName)
+ => GetBrowserPath(GetFirefoxPathsToProbe(artifactsBinDir), envVarName);
+
+ private static string GetBrowserPath(IEnumerable pathsToProbe, string envVarName)
+ {
+ string? browserPath = FindBrowserPath();
+ if (!string.IsNullOrEmpty(browserPath))
+ return Path.GetFullPath(browserPath);
+
+ throw new Exception($"Could not find an installed browser to use. Tried paths: {string.Join(", ", pathsToProbe)}");
+
+ string? FindBrowserPath()
+ {
+ if (!string.IsNullOrEmpty(envVarName) &&
+ Environment.GetEnvironmentVariable(envVarName) is string _browserPath_env_var &&
+ !string.IsNullOrEmpty(_browserPath_env_var))
+ {
+ if (File.Exists(_browserPath_env_var))
+ return _browserPath_env_var;
+
+ Console.WriteLine ($"warning: Could not find {envVarName}={_browserPath_env_var}");
+ }
+
+ return pathsToProbe.FirstOrDefault(p => File.Exists(p));
+ }
+ }
+
+ private static IEnumerable GetChromePathsToProbe(string artifactsBinDir)
+ {
+ List paths = new();
+ if (!string.IsNullOrEmpty(artifactsBinDir))
+ {
+ // Look for a browser installed in artifacts, for local runs
+ paths.Add(Path.Combine(artifactsBinDir, "chrome", "chrome-linux", "chrome"));
+ paths.Add(Path.Combine(artifactsBinDir, "chrome", "chrome-win", "chrome.exe"));
+ }
+
+ paths.AddRange(new[]
+ {
+ "/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",
+ "C:/Program Files/Google/Chrome/Application/chrome.exe",
+ "/usr/bin/chromium-browser"
+ });
+
+ return paths;
+ }
+
+ private static IEnumerable GetFirefoxPathsToProbe(string artifactsBinDir)
+ {
+ List paths = new();
+ if (!string.IsNullOrEmpty(artifactsBinDir))
+ {
+ // Look for a browser installed in artifacts, for local runs
+ paths.Add(Path.Combine(artifactsBinDir, "firefox", "firefox", "firefox"));
+ paths.Add(Path.Combine(artifactsBinDir, "firefox", "firefox", "firefox.exe"));
+ }
+
+ paths.AddRange(new[]
+ {
+ "C:/Program Files/Mozilla Firefox/firefox.exe",
+ "/Applications/Firefox.app/Contents/MacOS/firefox",
+ });
+
+ return paths;
+ }
+}
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ChromeProvider.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ChromeProvider.cs
index 42c2b812d289d0..c704d8699bd296 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/ChromeProvider.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/ChromeProvider.cs
@@ -14,6 +14,7 @@
using Microsoft.WebAssembly.Diagnostics;
using System.Threading;
using System.Collections.Generic;
+using Wasm.Tests.Internal;
#nullable enable
@@ -24,7 +25,11 @@ internal class ChromeProvider : WasmHostProvider
static readonly Regex s_parseConnection = new (@"listening on (ws?s://[^\s]*)");
private WebSocket? _ideWebSocket;
private DebuggerProxy? _debuggerProxy;
- private static readonly Lazy s_browserPath = new(() => GetBrowserPath(GetPathsToProbe()));
+ private static readonly Lazy s_browserPath = new(() =>
+ {
+ string artifactsBinDir = Path.Combine(Path.GetDirectoryName(typeof(ChromeProvider).Assembly.Location)!, "..", "..", "..");
+ return BrowserLocator.FindChrome(artifactsBinDir, "BROWSER_PATH_FOR_TESTS");
+ });
public ChromeProvider(string id, ILogger logger) : base(id, logger)
{
@@ -153,28 +158,5 @@ private static string GetInitParms(int port)
return str;
}
- private static IEnumerable GetPathsToProbe()
- {
- List paths = new();
- string? asmLocation = Path.GetDirectoryName(typeof(ChromeProvider).Assembly.Location);
- if (asmLocation is not null)
- {
- string baseDir = Path.Combine(asmLocation, "..", "..");
- paths.Add(Path.Combine(baseDir, "chrome", "chrome-linux", "chrome"));
- paths.Add(Path.Combine(baseDir, "chrome", "chrome-win", "chrome.exe"));
- }
-
- paths.AddRange(new[]
- {
- "/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",
- "C:/Program Files/Google/Chrome/Application/chrome.exe",
- "/usr/bin/chromium-browser"
- });
-
- return paths;
- }
}
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj
index 67ae17bc43ed5f..1490a4726a7442 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj
@@ -7,21 +7,12 @@
true
chrome
$(DefineConstants);RUN_IN_CHROME
- $(MSBuildThisFileDirectory)..\..\BrowsersForTesting.props
windows
- true
- true
+ true
+ true
-
-
-
- $(ArtifactsBinDir)DebuggerTestSuite\chrome\
- $(ArtifactsBinDir)DebuggerTestSuite\
- $(BrowserStampDir).install-chrome-$(ChromiumRevision).stamp
- $(ArtifactsBinDir)DebuggerTestSuite\firefox\
- $(BrowserStampDir).install-firefox-$(FirefoxRevision).stamp
-
+
@@ -51,59 +42,4 @@
-
-
-
- <_StampFile Include="$(BrowserStampDir).install-chrome*.stamp" />
-
-
-
-
-
-
-
-
-
-
-
- <_ChromeBinaryPath>$([MSBuild]::NormalizePath($(ChromeDir), $(ChromiumDirName), $(ChromiumBinaryName)))
-
-
-
-
-
-
-
-
-
-
-
- <_StampFile Include="$(BrowserStampDir).install-firefox*.stamp" />
-
-
-
-
-
-
-
-
-
-
-
-
- <_FirefoxBinaryPath>$([MSBuild]::NormalizePath($(FirefoxDir), $(FirefoxBinaryName)))
-
-
-
-
-
-
-
-
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/FirefoxProvider.cs b/src/mono/wasm/debugger/DebuggerTestSuite/FirefoxProvider.cs
index 08515c1a12ff34..3fcb4935a5e3ba 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/FirefoxProvider.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/FirefoxProvider.cs
@@ -11,6 +11,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.WebAssembly.Diagnostics;
+using Wasm.Tests.Internal;
#nullable enable
@@ -20,7 +21,11 @@ internal class FirefoxProvider : WasmHostProvider
{
private WebSocket? _ideWebSocket;
private FirefoxDebuggerProxy? _firefoxDebuggerProxy;
- private static readonly Lazy s_browserPath = new(() => GetBrowserPath(GetPathsToProbe()));
+ private static readonly Lazy s_browserPath = new(() =>
+ {
+ string artifactsBinDir = Path.Combine(Path.GetDirectoryName(typeof(ChromeProvider).Assembly.Location)!, "..", "..");
+ return BrowserLocator.FindFirefox(artifactsBinDir, "BROWSER_PATH_FOR_TESTS");
+ });
public FirefoxProvider(string id, ILogger logger) : base(id, logger)
{
@@ -142,24 +147,4 @@ private static string GetProfilePath(string Id)
return profilePath;
}
-
- private static IEnumerable GetPathsToProbe()
- {
- List paths = new();
- string? asmLocation = Path.GetDirectoryName(typeof(ChromeProvider).Assembly.Location);
- if (asmLocation is not null)
- {
- string baseDir = Path.Combine(asmLocation, "..", "..");
- paths.Add(Path.Combine(baseDir, "firefox", "firefox", "firefox"));
- paths.Add(Path.Combine(baseDir, "firefox", "firefox", "firefox.exe"));
- }
-
- paths.AddRange(new[]
- {
- "C:/Program Files/Mozilla Firefox/firefox.exe",
- "/Applications/Firefox.app/Contents/MacOS/firefox",
- });
-
- return paths;
- }
}
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs
index 58f36667cc54b1..cb826ee92d1de3 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs
@@ -12,6 +12,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.WebAssembly.Diagnostics;
using Newtonsoft.Json.Linq;
+using System.Runtime.ExceptionServices;
#nullable enable
@@ -235,10 +236,10 @@ public async Task LaunchBrowser(DateTime start, TimeSpan span)
string uriStr = $"ws://{TestHarnessProxy.Endpoint.Authority}/launch-host-and-connect/?test_id={Id}";
if (!DebuggerTestBase.RunningOnChrome)
{
- uriStr += "&host=firefox&firefox-proxy-port=6002";
+ uriStr += $"&host=firefox&firefox-proxy-port={DebuggerTestBase.FirefoxProxyPort}";
// Ensure the listener is running early, so trying to
// connect to that does not race with the starting of it
- FirefoxDebuggerProxy.StartListener(6002, _logger);
+ FirefoxDebuggerProxy.StartListener(DebuggerTestBase.FirefoxProxyPort, _logger);
}
await Client.Connect(new Uri(uriStr), OnMessage, _cancellationTokenSource);
@@ -330,7 +331,7 @@ public async Task OpenSessionAsync(Func
{
router.MapGet("launch-host-and-connect", async context =>
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/WasmHostProvider.cs b/src/mono/wasm/debugger/DebuggerTestSuite/WasmHostProvider.cs
index 89d63d1a13cbec..88279dac716b77 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/WasmHostProvider.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/WasmHostProvider.cs
@@ -117,27 +117,4 @@ public virtual void Dispose()
}
}
- protected static string GetBrowserPath(IEnumerable pathsToProbe)
- {
- string? browserPath = FindBrowserPath();
- if (!string.IsNullOrEmpty(browserPath))
- return Path.GetFullPath(browserPath);
-
- throw new Exception("Could not find an installed chrome to use");
-
- string? FindBrowserPath()
- {
- string? _browserPath_env_var = Environment.GetEnvironmentVariable("BROWSER_PATH_FOR_DEBUGGER_TESTS");
- if (!string.IsNullOrEmpty(_browserPath_env_var))
- {
- if (File.Exists(_browserPath_env_var))
- return _browserPath_env_var;
-
- Console.WriteLine ($"warning: Could not find BROWSER_PATH_FOR_DEBUGGER_TESTS={_browserPath_env_var}");
- }
-
- // Look for a browser installed in artifacts, for local runs
- return pathsToProbe.FirstOrDefault(p => File.Exists(p));
- }
- }
}
diff --git a/src/mono/wasm/runtime/debug.ts b/src/mono/wasm/runtime/debug.ts
index a6115177040283..7a702f2053e1ac 100644
--- a/src/mono/wasm/runtime/debug.ts
+++ b/src/mono/wasm/runtime/debug.ts
@@ -142,10 +142,26 @@ export function mono_wasm_raise_debug_event(event: WasmEvent, args = {}): void {
// Used by the debugger to enumerate loaded dlls and pdbs
export function mono_wasm_get_loaded_files(): string[] {
- cwraps.mono_wasm_set_is_debugger_attached(true);
return MONO.loaded_files;
}
+export function mono_wasm_wait_for_debugger(): Promise {
+ return new Promise((resolve) => {
+ const interval = setInterval(() => {
+ if (runtimeHelpers.wait_for_debugger != 1) {
+ return;
+ }
+ clearInterval(interval);
+ resolve();
+ }, 100);
+ });
+}
+
+export function mono_wasm_debugger_attached(): void {
+ runtimeHelpers.wait_for_debugger = 1;
+ cwraps.mono_wasm_set_is_debugger_attached(true);
+}
+
function _create_proxy_from_object_id(objectId: string, details: any) {
if (objectId.startsWith("dotnet:array:")) {
let ret: Array;
diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts
index 0a4a8d48e8e7f6..e31742188e500a 100644
--- a/src/mono/wasm/runtime/dotnet.d.ts
+++ b/src/mono/wasm/runtime/dotnet.d.ts
@@ -153,6 +153,7 @@ declare type MonoConfig = {
aot_profiler_options?: AOTProfilerOptions;
coverage_profiler_options?: CoverageProfilerOptions;
ignore_pdb_load_errors?: boolean;
+ wait_for_debugger?: number;
};
declare type MonoConfigError = {
isError: true;
diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts
index 064ddcfa214783..66fe698af5dc09 100644
--- a/src/mono/wasm/runtime/exports.ts
+++ b/src/mono/wasm/runtime/exports.ts
@@ -27,6 +27,7 @@ import {
mono_wasm_change_debugger_log_level,
mono_wasm_symbolicate_string,
mono_wasm_stringify_as_error_with_stack,
+ mono_wasm_debugger_attached,
} from "./debug";
import { ENVIRONMENT_IS_WEB, ExitStatusError, runtimeHelpers, setImportsAndExports } from "./imports";
import { DotnetModuleConfigImports, DotnetModule } from "./types";
@@ -387,6 +388,7 @@ const INTERNAL: any = {
mono_wasm_detach_debugger,
mono_wasm_raise_debug_event,
mono_wasm_change_debugger_log_level,
+ mono_wasm_debugger_attached,
mono_wasm_runtime_is_ready: runtimeHelpers.mono_wasm_runtime_is_ready,
};
diff --git a/src/mono/wasm/runtime/run.ts b/src/mono/wasm/runtime/run.ts
index 492188352e915a..85c4c8e4dfcd55 100644
--- a/src/mono/wasm/runtime/run.ts
+++ b/src/mono/wasm/runtime/run.ts
@@ -1,5 +1,6 @@
-import { ExitStatus, INTERNAL, Module, quit } from "./imports";
+import { ExitStatus, INTERNAL, Module, quit, runtimeHelpers } from "./imports";
import { mono_call_assembly_entry_point } from "./method-calls";
+import { mono_wasm_wait_for_debugger } from "./debug";
import { mono_wasm_set_main_args, runtime_is_initialized_reject } from "./startup";
export async function mono_run_main_and_exit(main_assembly_name: string, args: string[]): Promise {
@@ -14,8 +15,13 @@ export async function mono_run_main_and_exit(main_assembly_name: string, args: s
}
}
+
export async function mono_run_main(main_assembly_name: string, args: string[]): Promise {
mono_wasm_set_main_args(main_assembly_name, args);
+ if (runtimeHelpers.wait_for_debugger == -1) {
+ console.log("waiting for debugger...");
+ return await mono_wasm_wait_for_debugger().then(() => mono_call_assembly_entry_point(main_assembly_name, [args], "m"));
+ }
return mono_call_assembly_entry_point(main_assembly_name, [args], "m");
}
diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts
index c183e8823919a5..a24b168e8454ab 100644
--- a/src/mono/wasm/runtime/startup.ts
+++ b/src/mono/wasm/runtime/startup.ts
@@ -324,6 +324,7 @@ function finalize_startup(config: MonoConfig | MonoConfigError | undefined): voi
mono_wasm_globalization_init(config.globalization_mode!, config.diagnostic_tracing!);
cwraps.mono_wasm_load_runtime("unused", config.debug_level || 0);
+ runtimeHelpers.wait_for_debugger = config.wait_for_debugger;
} catch (err: any) {
Module.printErr("MONO_WASM: mono_wasm_load_runtime () failed: " + err);
Module.printErr("MONO_WASM: Stacktrace: \n");
diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts
index 2b85e899538164..db3834c11c034a 100644
--- a/src/mono/wasm/runtime/types.ts
+++ b/src/mono/wasm/runtime/types.ts
@@ -78,7 +78,8 @@ export type MonoConfig = {
runtime_options?: string[], // array of runtime options as strings
aot_profiler_options?: AOTProfilerOptions, // dictionary-style Object. If omitted, aot profiler will not be initialized.
coverage_profiler_options?: CoverageProfilerOptions, // dictionary-style Object. If omitted, coverage profiler will not be initialized.
- ignore_pdb_load_errors?: boolean
+ ignore_pdb_load_errors?: boolean,
+ wait_for_debugger ?: number
};
export type MonoConfigError = {
@@ -152,6 +153,7 @@ export type RuntimeHelpers = {
loaded_files: string[];
config: MonoConfig | MonoConfigError;
+ wait_for_debugger?: number;
fetch: (url: string) => Promise;
}
diff --git a/src/mono/wasm/templates/Microsoft.NET.Runtime.WebAssembly.Templates.csproj b/src/mono/wasm/templates/Microsoft.NET.Runtime.WebAssembly.Templates.csproj
index dfe18d9b0042fe..0a4927e350fe7a 100644
--- a/src/mono/wasm/templates/Microsoft.NET.Runtime.WebAssembly.Templates.csproj
+++ b/src/mono/wasm/templates/Microsoft.NET.Runtime.WebAssembly.Templates.csproj
@@ -15,11 +15,11 @@
content
$(NoWarn);NU5128
true
+ false
-
diff --git a/src/mono/wasm/templates/templates/browser/.template.config/template.json b/src/mono/wasm/templates/templates/browser/.template.config/template.json
index e9736c68a1ba9c..4209d083adbcc5 100644
--- a/src/mono/wasm/templates/templates/browser/.template.config/template.json
+++ b/src/mono/wasm/templates/templates/browser/.template.config/template.json
@@ -5,6 +5,7 @@
"identity": "WebAssembly.Browser",
"name": "WebAssembly Browser App",
"shortName": "wasmbrowser",
+ "sourceName": "browser.0",
"tags": {
"language": "C#",
"type": "project"
diff --git a/src/mono/wasm/templates/templates/browser/Program.cs b/src/mono/wasm/templates/templates/browser/Program.cs
index baa90549b70363..5bfeaa26ef7af9 100644
--- a/src/mono/wasm/templates/templates/browser/Program.cs
+++ b/src/mono/wasm/templates/templates/browser/Program.cs
@@ -1,7 +1,7 @@
using System;
using System.Runtime.CompilerServices;
-Console.WriteLine ("Hello, Console!");
+Console.WriteLine ("Hello, Browser!");
public class MyClass {
[MethodImpl(MethodImplOptions.NoInlining)]
diff --git a/src/mono/wasm/templates/templates/browser/browser.csproj b/src/mono/wasm/templates/templates/browser/browser.0.csproj
similarity index 100%
rename from src/mono/wasm/templates/templates/browser/browser.csproj
rename to src/mono/wasm/templates/templates/browser/browser.0.csproj
diff --git a/src/mono/wasm/templates/templates/browser/index.html b/src/mono/wasm/templates/templates/browser/index.html
index 3cc12e3212d6b5..6cc1ec48879d58 100644
--- a/src/mono/wasm/templates/templates/browser/index.html
+++ b/src/mono/wasm/templates/templates/browser/index.html
@@ -4,7 +4,7 @@
- Sample ES6
+ browser.0
diff --git a/src/mono/wasm/templates/templates/browser/main.js b/src/mono/wasm/templates/templates/browser/main.js
index 56c9b350313ec1..0a3d6e95c20ff2 100644
--- a/src/mono/wasm/templates/templates/browser/main.js
+++ b/src/mono/wasm/templates/templates/browser/main.js
@@ -2,11 +2,11 @@ import createDotnetRuntime from './dotnet.js'
try {
const { MONO, BINDING, Module, RuntimeBuildInfo } = await createDotnetRuntime();
- const managedMethod = BINDING.bind_static_method("[browser] MyClass:CallMeFromJS");
+ const managedMethod = BINDING.bind_static_method("[browser.0] MyClass:CallMeFromJS");
const text = managedMethod();
document.getElementById("out").innerHTML = `${text}`;
- await MONO.mono_run_main("browser.dll", []);
+ await MONO.mono_run_main("browser.0.dll", []);
} catch (err) {
console.log(`WASM ERROR ${err}`);
document.getElementById("out").innerHTML = `error: ${err}`;
diff --git a/src/mono/wasm/templates/templates/console/.template.config/template.json b/src/mono/wasm/templates/templates/console/.template.config/template.json
index 0a67c332bb7d7e..15f0c9041de09f 100644
--- a/src/mono/wasm/templates/templates/console/.template.config/template.json
+++ b/src/mono/wasm/templates/templates/console/.template.config/template.json
@@ -5,6 +5,7 @@
"identity": "WebAssembly.Console",
"name": "WebAssembly Console App",
"shortName": "wasmconsole",
+ "sourceName": "console.0",
"tags": {
"language": "C#",
"type": "project"
diff --git a/src/mono/wasm/templates/templates/console/console.csproj b/src/mono/wasm/templates/templates/console/console.0.csproj
similarity index 100%
rename from src/mono/wasm/templates/templates/console/console.csproj
rename to src/mono/wasm/templates/templates/console/console.0.csproj
diff --git a/src/mono/wasm/templates/templates/console/main.cjs b/src/mono/wasm/templates/templates/console/main.cjs
index 18823502c64fa7..cbc74495a4f2c3 100644
--- a/src/mono/wasm/templates/templates/console/main.cjs
+++ b/src/mono/wasm/templates/templates/console/main.cjs
@@ -3,6 +3,6 @@ const createDotnetRuntime = require("./dotnet.js");
async function main() {
const { MONO } = await createDotnetRuntime();
const app_args = process.argv.slice(2);
- await MONO.mono_run_main_and_exit("console.dll", app_args);
+ await MONO.mono_run_main_and_exit("console.0.dll", app_args);
};
main();
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs
index 1e86a328305f9d..afbf3250e85ecb 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Reflection;
using System.Runtime.InteropServices;
#nullable enable
@@ -43,7 +44,18 @@ public BuildEnvironment()
string? sdkForWorkloadPath = EnvironmentVariables.SdkForWorkloadTestingPath;
if (string.IsNullOrEmpty(sdkForWorkloadPath))
- throw new Exception($"Environment variable SDK_FOR_WORKLOAD_TESTING_PATH not set");
+ {
+ // Is this a "local run?
+ string probePath = Path.Combine(Path.GetDirectoryName(typeof(BuildEnvironment).Assembly.Location)!,
+ "..",
+ "..",
+ "..",
+ "dotnet-workload");
+ if (Directory.Exists(probePath))
+ sdkForWorkloadPath = Path.GetFullPath(probePath);
+ else
+ throw new Exception($"Environment variable SDK_FOR_WORKLOAD_TESTING_PATH not set, and could not find it at {probePath}");
+ }
if (!Directory.Exists(sdkForWorkloadPath))
throw new Exception($"Could not find SDK_FOR_WORKLOAD_TESTING_PATH={sdkForWorkloadPath}");
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
index 10350a9e069f1d..ddcc00f84eb5f3 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
@@ -12,6 +12,7 @@
using System.Text;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
+using System.Threading.Tasks;
using System.Threading;
using System.Xml;
using Xunit;
@@ -553,25 +554,16 @@ protected static void AssertFilesDontExist(string dir, string[] filenames, strin
protected static void AssertFilesExist(string dir, string[] filenames, string? label = null, bool expectToExist=true)
{
+ string prefix = label != null ? $"{label}: " : string.Empty;
Assert.True(Directory.Exists(dir), $"[{label}] {dir} not found");
foreach (string filename in filenames)
{
string path = Path.Combine(dir, filename);
+ if (expectToExist && !File.Exists(path))
+ throw new XunitException($"{prefix}Expected the file to exist: {path}");
- if (expectToExist)
- {
- Assert.True(File.Exists(path),
- label != null
- ? $"{label}: File exists: {path}"
- : $"File exists: {path}");
- }
- else
- {
- Assert.False(File.Exists(path),
- label != null
- ? $"{label}: {path} should not exist"
- : $"{path} should not exist");
- }
+ if (!expectToExist && File.Exists(path))
+ throw new XunitException($"{prefix}Expected the file to *not* exist: {path}");
}
}
@@ -586,10 +578,11 @@ protected static void AssertFile(string file0, string file1, string? label=null,
FileInfo finfo0 = new(file0);
FileInfo finfo1 = new(file1);
- if (same)
- Assert.True(finfo0.Length == finfo1.Length, $"{label}:{Environment.NewLine} File sizes don't match for {file0} ({finfo0.Length}), and {file1} ({finfo1.Length})");
- else
- Assert.True(finfo0.Length != finfo1.Length, $"{label}:{Environment.NewLine} File sizes should not match for {file0} ({finfo0.Length}), and {file1} ({finfo1.Length})");
+ if (same && finfo0.Length != finfo1.Length)
+ throw new XunitException($"{label}:{Environment.NewLine} File sizes don't match for {file0} ({finfo0.Length}), and {file1} ({finfo1.Length})");
+
+ if (!same && finfo0.Length == finfo1.Length)
+ throw new XunitException($"{label}:{Environment.NewLine} File sizes should not match for {file0} ({finfo0.Length}), and {file1} ({finfo1.Length})");
}
protected (int exitCode, string buildOutput) AssertBuild(string args, string label="build", bool expectSuccess=true, IDictionary? envVars=null, int? timeoutMs=null)
@@ -685,6 +678,20 @@ public static (int exitCode, string buildOutput) RunProcess(string path,
string? label = null,
bool logToXUnit = true,
int? timeoutMs = null)
+ {
+ var t = RunProcessAsync(path, _testOutput, args, envVars, workingDir, label, logToXUnit, timeoutMs);
+ t.Wait();
+ return t.Result;
+ }
+
+ public static async Task<(int exitCode, string buildOutput)> RunProcessAsync(string path,
+ ITestOutputHelper _testOutput,
+ string args = "",
+ IDictionary? envVars = null,
+ string? workingDir = null,
+ string? label = null,
+ bool logToXUnit = true,
+ int? timeoutMs = null)
{
_testOutput.WriteLine($"Running {path} {args}");
_testOutput.WriteLine($"WorkingDirectory: {workingDir}");
@@ -756,7 +763,7 @@ public static (int exitCode, string buildOutput) RunProcess(string path,
// this will ensure that all the async event handling
// has completed
// https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit?view=net-5.0#System_Diagnostics_Process_WaitForExit_System_Int32_
- process.WaitForExit();
+ await process.WaitForExitAsync();
}
process.ErrorDataReceived -= logStdErr;
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs
index 588228ac1845f9..84ccf2fa23ab1c 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs
@@ -10,11 +10,12 @@ public class DotNetCommand : ToolCommand
private BuildEnvironment _buildEnvironment;
private bool _useDefaultArgs;
- public DotNetCommand(BuildEnvironment buildEnv, ITestOutputHelper _testOutput, bool useDefaultArgs=true) : base(buildEnv.DotNet, _testOutput)
+ public DotNetCommand(BuildEnvironment buildEnv, ITestOutputHelper _testOutput, bool useDefaultArgs=true, string label="") : base(buildEnv.DotNet, _testOutput, label)
{
_buildEnvironment = buildEnv;
_useDefaultArgs = useDefaultArgs;
- WithEnvironmentVariables(buildEnv.EnvVars);
+ if (useDefaultArgs)
+ WithEnvironmentVariables(buildEnv.EnvVars);
}
protected override string GetFullArgs(params string[] args)
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs
index ca60d32d22a07d..9a5f87b6c4402a 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs
@@ -124,8 +124,6 @@ public static Task StartAndWaitForExitAsync(this Process subject)
subject.Exited += (s, a) =>
{
taskCompletionSource.SetResult(null);
-
- subject.Dispose();
};
subject.Start();
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs
index bebe40f5064a3c..642b190666d2b6 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/ToolCommand.cs
@@ -11,7 +11,7 @@
namespace Wasm.Build.Tests
{
- public class ToolCommand
+ public class ToolCommand : IDisposable
{
private string _label;
protected ITestOutputHelper _testOutput;
@@ -58,6 +58,18 @@ public ToolCommand WithEnvironmentVariables(IDictionary? extraEn
return this;
}
+ public ToolCommand WithOutputDataReceived(Action handler)
+ {
+ OutputDataReceived += (_, args) => handler(args.Data);
+ return this;
+ }
+
+ public ToolCommand WithErrorDataReceived(Action handler)
+ {
+ ErrorDataReceived += (_, args) => handler(args.Data);
+ return this;
+ }
+
public virtual CommandResult Execute(params string[] args)
{
return Task.Run(async () => await ExecuteAsync(args)).Result;
@@ -79,6 +91,16 @@ public virtual CommandResult ExecuteWithCapturedOutput(params string[] args)
return Task.Run(async () => await ExecuteAsyncInternal(resolvedCommand, fullArgs)).Result;
}
+ public virtual void Dispose()
+ {
+ if (CurrentProcess is not null && !CurrentProcess.HasExited)
+ {
+ CurrentProcess.KillTree();
+ CurrentProcess.Dispose();
+ CurrentProcess = null;
+ }
+ }
+
protected virtual string GetFullArgs(params string[] args) => string.Join(" ", args);
private async Task ExecuteAsyncInternal(string executable, string args)
@@ -110,7 +132,6 @@ private async Task ExecuteAsyncInternal(string executable, string
CurrentProcess.BeginErrorReadLine();
await completionTask;
- CurrentProcess.WaitForExit();
RemoveNullTerminator(output);
return new CommandResult(
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmTemplateTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmTemplateTests.cs
index 1ab4c48a65e902..0624a488667f97 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmTemplateTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmTemplateTests.cs
@@ -6,6 +6,7 @@
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
+using System.Threading.Tasks;
#nullable enable
@@ -23,9 +24,9 @@ public WasmTemplateTests(ITestOutputHelper output, SharedBuildPerTestClassFixtur
[InlineData("Release")]
public void BrowserBuildThenPublish(string config)
{
- string id = $"{config}_{Path.GetRandomFileName()}";
- string projectName = $"browser";
- CreateWasmTemplateProject(id, "wasmbrowser");
+ string id = $"browser_{config}_{Path.GetRandomFileName()}";
+ string projectFile = CreateWasmTemplateProject(id, "wasmbrowser");
+ string projectName = Path.GetFileNameWithoutExtension(projectFile);
var buildArgs = new BuildArgs(projectName, config, false, id, null);
buildArgs = ExpandBuildArgs(buildArgs);
@@ -67,8 +68,8 @@ public void BrowserBuildThenPublish(string config)
public void ConsoleBuildThenPublish(string config)
{
string id = $"{config}_{Path.GetRandomFileName()}";
- string projectName = $"console";
- CreateWasmTemplateProject(id, "wasmconsole");
+ string projectFile = CreateWasmTemplateProject(id, "wasmconsole");
+ string projectName = Path.GetFileNameWithoutExtension(projectFile);
var buildArgs = new BuildArgs(projectName, config, false, id, null);
buildArgs = ExpandBuildArgs(buildArgs);
diff --git a/src/tests/BuildWasmApps/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj b/src/tests/BuildWasmApps/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj
index 830456ded05918..45241cbdde3277 100644
--- a/src/tests/BuildWasmApps/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj
+++ b/src/tests/BuildWasmApps/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj
@@ -38,12 +38,12 @@
-
-
+
+
-
+