Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -955,34 +955,43 @@ private string GetTestHostPath(string runtimeConfigDevPath, string depsFilePath,
{
#if NETCOREAPP
using var doc = JsonDocument.Parse(stream);
var runtimeOptions = doc.RootElement.GetProperty("runtimeOptions");
var additionalProbingPaths = runtimeOptions.GetProperty("additionalProbingPaths");
foreach (var x in additionalProbingPaths.EnumerateArray())

if (doc.RootElement.TryGetProperty("runtimeOptions", out var runtimeOptions) &&
runtimeOptions.TryGetProperty("additionalProbingPaths", out var additionalProbingPaths))
{
EqtTrace.Verbose("DotnetTestHostmanager: Looking for path {0} in folder {1}", testHostPath, x.GetString());
string testHostFullPath;
try
{
testHostFullPath = Path.Combine(x.GetString()!, testHostPath);
}
catch (ArgumentException)
foreach (var x in additionalProbingPaths.EnumerateArray())
{
// https://github.com/Microsoft/vstest/issues/847
// skip any invalid paths and continue checking the others
continue;
}
EqtTrace.Verbose("DotnetTestHostmanager: Looking for path {0} in folder {1}", testHostPath, x.GetString());
string testHostFullPath;
try
{
testHostFullPath = Path.Combine(x.GetString()!, testHostPath);
}
catch (ArgumentException)
{
// https://github.com/Microsoft/vstest/issues/847
// skip any invalid paths and continue checking the others
continue;
}

if (_fileHelper.Exists(testHostFullPath))
{
EqtTrace.Verbose("DotnetTestHostmanager: Found testhost.dll in {0}", testHostFullPath);
return testHostFullPath;
if (_fileHelper.Exists(testHostFullPath))
{
EqtTrace.Verbose("DotnetTestHostmanager: Found testhost.dll in {0}", testHostFullPath);
return testHostFullPath;
}
}
}
#else
using var reader = new StreamReader(stream);
var parsed = Json.Deserialize(reader) as IDictionary<string, object>;
var runtimeOpts = parsed?["runtimeOptions"] as IDictionary<string, object>;
var probingPaths = runtimeOpts?["additionalProbingPaths"] as IList<object>;
var runtimeOpts = parsed is not null && parsed.TryGetValue("runtimeOptions", out var runtimeOptsObj)
? runtimeOptsObj as IDictionary<string, object>
: null;

var probingPaths = runtimeOpts is not null && runtimeOpts.TryGetValue("additionalProbingPaths", out var probingPathsObj)
? probingPathsObj as IList<object>
: null;

if (probingPaths is not null)
{
foreach (var x in probingPaths)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,56 @@ public void GetTestHostProcessStartInfo_FindsTestHostDllViaRealDepsJsonAndRuntim
startInfo.Arguments.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar));
}

[TestMethod]
public void GetTestHostProcessStartInfo_DoesNotThrowWhenRuntimeConfigDevJsonHasNoAdditionalProbingPaths()
{
// Arrange — a runtimeconfig.dev.json that exists but only carries runtime options other than
// additionalProbingPaths (for example Hot Reload switches generated by the SDK for net6.0+ Debug builds).
// The manager must not fail reading it; it should fall back to finding testhost.dll next to the source.
string sourceDll = Path.Combine(_tempDir, "TestProject.dll");
string depsJsonPath = Path.Combine(_tempDir, "TestProject.deps.json");

File.WriteAllText(sourceDll, "fake test dll");

// Minimal deps.json — testhost not in it, so resolution falls through to the source directory.
string depsJson = """
{
"runtimeTarget": { "name": ".NETCoreApp,Version=v8.0" },
"compilationOptions": {},
"targets": { ".NETCoreApp,Version=v8.0": {} },
"libraries": {}
}
""";
File.WriteAllText(depsJsonPath, depsJson);

// runtimeconfig.dev.json without an "additionalProbingPaths" property.
string runtimeConfigDevJson = """
{
"runtimeOptions": {
"configProperties": {
"System.Reflection.Metadata.MetadataUpdater.IsSupported": true,
"System.StartupHookProvider.IsSupported": true
}
}
}
""";
File.WriteAllText(Path.Combine(_tempDir, "TestProject.runtimeconfig.dev.json"), runtimeConfigDevJson);

// Place testhost.dll next to source so the manager can resolve it without probing paths.
string testhostNextToSource = Path.Combine(_tempDir, "testhost.dll");
File.WriteAllText(testhostNextToSource, "fake testhost");

var manager = CreateManager(new FileHelper());
manager.Initialize(_mockMessageLogger.Object, "<RunSettings><RunConfiguration><TargetFrameworkVersion>.NETCoreApp,Version=v8.0</TargetFrameworkVersion></RunConfiguration></RunSettings>");

// Act — this previously threw KeyNotFoundException when additionalProbingPaths was missing.
var startInfo = manager.GetTestHostProcessStartInfo(new[] { sourceDll }, null, _connectionInfo);

// Assert — resolution succeeds and falls back to testhost.dll next to the source.
Assert.IsNotNull(startInfo.Arguments);
Assert.Contains("testhost.dll", startInfo.Arguments);
}

[TestMethod]
public void GetTestHostProcessStartInfo_PassesDepsFileArgWhenDepsJsonExists()
{
Expand Down
Loading