Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;

/// <summary>
/// This interface holds additional values for a request handler when rewriting paths is used.
/// This is in case when --local-path and --remote-path parameters are provided and testhost is running
/// in a remote deployment. This interface is used only to avoid changes to public API of TestRequestHandler.
/// </summary>
internal interface IDeploymentAwareTestRequestHandler
{
string LocalPath { get; set; }
string RemotePath { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;

using System.Collections.Generic;
using System.Collections.ObjectModel;

using ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;

internal interface IPathConverter
{
string? UpdatePath(string? path, PathConversionDirection updateDirection);

IEnumerable<string?> UpdatePaths(IEnumerable<string?> paths, PathConversionDirection updateDirection);

TestCase UpdateTestCase(TestCase testCase, PathConversionDirection updateDirection);

IEnumerable<TestCase> UpdateTestCases(IEnumerable<TestCase> testCases, PathConversionDirection updateDirection);

TestRunCompleteEventArgs UpdateTestRunCompleteEventArgs(TestRunCompleteEventArgs testRunCompleteEventArgs, PathConversionDirection updateDirection);

TestRunChangedEventArgs UpdateTestRunChangedEventArgs(TestRunChangedEventArgs testRunChangedArgs, PathConversionDirection updateDirection);

Collection<AttachmentSet> UpdateAttachmentSets(Collection<AttachmentSet> attachmentSets, PathConversionDirection updateDirection);

ICollection<AttachmentSet> UpdateAttachmentSets(ICollection<AttachmentSet> attachmentSets, PathConversionDirection updateDirection);

DiscoveryCriteria UpdateDiscoveryCriteria(DiscoveryCriteria discoveryCriteria, PathConversionDirection updateDirection);

TestRunCriteriaWithSources UpdateTestRunCriteriaWithSources(TestRunCriteriaWithSources testRunCriteriaWithSources, PathConversionDirection updateDirection);

TestRunCriteriaWithTests UpdateTestRunCriteriaWithTests(TestRunCriteriaWithTests testRunCriteriaWithTests, PathConversionDirection updateDirection);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;

using System.Collections.Generic;
using ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using System.Collections.ObjectModel;
using System;

internal class NullPathConverter : IPathConverter
{
private static readonly Lazy<NullPathConverter> LazyInstance = new(() => new NullPathConverter());

private NullPathConverter() { }

public static NullPathConverter Instance => LazyInstance.Value;

Collection<AttachmentSet> IPathConverter.UpdateAttachmentSets(Collection<AttachmentSet> attachmentSets, PathConversionDirection _) => attachmentSets;

ICollection<AttachmentSet> IPathConverter.UpdateAttachmentSets(ICollection<AttachmentSet> attachmentSets, PathConversionDirection _) => attachmentSets;

DiscoveryCriteria IPathConverter.UpdateDiscoveryCriteria(DiscoveryCriteria discoveryCriteria, PathConversionDirection _) => discoveryCriteria;

string? IPathConverter.UpdatePath(string? path, PathConversionDirection _) => path;

IEnumerable<string?> IPathConverter.UpdatePaths(IEnumerable<string?> paths, PathConversionDirection _) => paths;

TestCase IPathConverter.UpdateTestCase(TestCase testCase, PathConversionDirection _) => testCase;

IEnumerable<TestCase> IPathConverter.UpdateTestCases(IEnumerable<TestCase> testCases, PathConversionDirection _) => testCases;

TestRunChangedEventArgs IPathConverter.UpdateTestRunChangedEventArgs(TestRunChangedEventArgs testRunChangedArgs, PathConversionDirection _) => testRunChangedArgs;

TestRunCompleteEventArgs IPathConverter.UpdateTestRunCompleteEventArgs(TestRunCompleteEventArgs testRunCompleteEventArgs, PathConversionDirection _) => testRunCompleteEventArgs;

TestRunCriteriaWithSources IPathConverter.UpdateTestRunCriteriaWithSources(TestRunCriteriaWithSources testRunCriteriaWithSources, PathConversionDirection _) => testRunCriteriaWithSources;

TestRunCriteriaWithTests IPathConverter.UpdateTestRunCriteriaWithTests(TestRunCriteriaWithTests testRunCriteriaWithTests, PathConversionDirection _) => testRunCriteriaWithTests;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;

internal enum PathConversionDirection
{
Receive,
Send
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.IO;

using ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;

/// <summary>
/// Converts paths in received and sent objects, to make testhost seem like it run a local test,
/// while it was in fact running a test on a remote system, in a totally different path. This is for UWP which
/// does testhost deployment.
/// The modifications here rely on combination of side-effects, and actually replacing the values, because
/// we cannot modify the properties on our public objects, and add setters.
/// </summary>
internal class PathConverter : IPathConverter
{
// The path on this computer to which we deployed the test dll and test runner
private readonly string _deploymentPath = "";
// The path on the remote system where test dll was originally placed, and from which we
// copied it to this system. For vstest.console, which is on the other side of this, the names
// are inverted, it sends us their local path, and thinks about our local path as remote.
private readonly string _originalPath = "";

public PathConverter(string originalPath!!, string deploymentPath!!, IFileHelper fileHelper!!)
{
_originalPath = fileHelper.GetFullPath(originalPath).TrimEnd('\\').TrimEnd('/') + Path.DirectorySeparatorChar;
_deploymentPath = fileHelper.GetFullPath(deploymentPath).TrimEnd('\\').TrimEnd('/') + Path.DirectorySeparatorChar;
}

public string? UpdatePath(string? path, PathConversionDirection updateDirection)
{
if (path == null)
return path;

string find;
string replaceWith;
if (updateDirection == PathConversionDirection.Receive)
{
// Request is incoming, the path that is local to the sender (for us that is "remote" path)
// needs to be replaced with our path
find = _originalPath;
replaceWith = _deploymentPath;
}
else
{
find = _deploymentPath;
replaceWith = _originalPath;
}

var result = path?.Replace(find, replaceWith);
return result;
}

public IEnumerable<string?> UpdatePaths(IEnumerable<string?> paths!!, PathConversionDirection updateDirection)
{
return paths.Select(i => UpdatePath(i, updateDirection)).ToList();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to match same style as other implementations:

Suggested change
return paths.Select(i => UpdatePath(i, updateDirection)).ToList();
paths.ToList().ForEach(i => UpdatePath(i, updateDirection));
return paths;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would not work, strings are immutable, so you would create a new string and output it to null, so the original string would not be updated.

}

public TestCase UpdateTestCase(TestCase testCase!!, PathConversionDirection updateDirection)
{
testCase.CodeFilePath = UpdatePath(testCase.CodeFilePath, updateDirection);
testCase.Source = UpdatePath(testCase.Source, updateDirection);
return testCase;
}

public IEnumerable<TestCase> UpdateTestCases(IEnumerable<TestCase> testCases!!, PathConversionDirection updateDirection)
{
testCases.ToList().ForEach(tc => UpdateTestCase(tc, updateDirection));
return testCases;
}

public TestRunCompleteEventArgs UpdateTestRunCompleteEventArgs(TestRunCompleteEventArgs testRunCompleteEventArgs!!, PathConversionDirection updateDirection)
{
UpdateAttachmentSets(testRunCompleteEventArgs.AttachmentSets, updateDirection);
return testRunCompleteEventArgs;
}

public TestRunChangedEventArgs UpdateTestRunChangedEventArgs(TestRunChangedEventArgs testRunChangedArgs!!, PathConversionDirection updateDirection)
{
UpdateTestResults(testRunChangedArgs.NewTestResults, updateDirection);
UpdateTestCases(testRunChangedArgs.ActiveTests, updateDirection);
return testRunChangedArgs;
}

public Collection<AttachmentSet> UpdateAttachmentSets(Collection<AttachmentSet> attachmentSets!!, PathConversionDirection updateDirection)
{
attachmentSets.ToList().ForEach(i => UpdateAttachmentSet(i, updateDirection));
return attachmentSets;
}

public ICollection<AttachmentSet> UpdateAttachmentSets(ICollection<AttachmentSet> attachmentSets!!, PathConversionDirection updateDirection)
{
attachmentSets.ToList().ForEach(i => UpdateAttachmentSet(i, updateDirection));
return attachmentSets;
}

private AttachmentSet UpdateAttachmentSet(AttachmentSet attachmentSet!!, PathConversionDirection updateDirection)
{
attachmentSet.Attachments.ToList().ForEach(a => UpdateAttachment(a, updateDirection));
return attachmentSet;
}

private UriDataAttachment UpdateAttachment(UriDataAttachment attachment!!, PathConversionDirection _)
{
// todo: convert uri? https://github.com/microsoft/vstest/issues/3367
return attachment;
}

private IEnumerable<TestResult> UpdateTestResults(IEnumerable<TestResult> testResults!!, PathConversionDirection updateDirection)
{
foreach (var tr in testResults)
{
UpdateAttachmentSets(tr.Attachments, updateDirection);
UpdateTestCase(tr.TestCase, updateDirection);
}
return testResults;
}

public DiscoveryCriteria UpdateDiscoveryCriteria(DiscoveryCriteria discoveryCriteria!!, PathConversionDirection updateDirection)
{
discoveryCriteria.Package = UpdatePath(discoveryCriteria.Package, updateDirection);
foreach (var adapter in discoveryCriteria.AdapterSourceMap.ToList())
{
var updatedPaths = UpdatePaths(adapter.Value, updateDirection);
discoveryCriteria.AdapterSourceMap[adapter.Key] = updatedPaths;
}
return discoveryCriteria;
}

public TestRunCriteriaWithSources UpdateTestRunCriteriaWithSources(TestRunCriteriaWithSources testRunCriteriaWithSources!!, PathConversionDirection updateDirection)
{
testRunCriteriaWithSources.AdapterSourceMap.ToList().ForEach(adapter => testRunCriteriaWithSources.AdapterSourceMap[adapter.Key] = UpdatePaths(adapter.Value, updateDirection));
var package = UpdatePath(testRunCriteriaWithSources.Package, updateDirection);
return new TestRunCriteriaWithSources(testRunCriteriaWithSources.AdapterSourceMap, package, testRunCriteriaWithSources.RunSettings, testRunCriteriaWithSources.TestExecutionContext);
}

public TestRunCriteriaWithTests UpdateTestRunCriteriaWithTests(TestRunCriteriaWithTests testRunCriteriaWithTests!!, PathConversionDirection updateDirection)
{
var tests = UpdateTestCases(testRunCriteriaWithTests.Tests, updateDirection);
var package = UpdatePath(testRunCriteriaWithTests.Package, updateDirection);
return new TestRunCriteriaWithTests(tests, package, testRunCriteriaWithTests.RunSettings, testRunCriteriaWithTests.TestExecutionContext);
}
}
Loading