-
Notifications
You must be signed in to change notification settings - Fork 292
Expand file tree
/
Copy pathDeploymentUtility.cs
More file actions
249 lines (211 loc) · 11.4 KB
/
DeploymentUtility.cs
File metadata and controls
249 lines (211 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if !WINDOWS_UWP && !WIN_UI
#if NETFRAMEWORK
using System.Security;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
#endif
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
#if NETFRAMEWORK
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endif
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
internal sealed class DeploymentUtility : DeploymentUtilityBase
{
public DeploymentUtility()
: base()
{
}
public DeploymentUtility(DeploymentItemUtility deploymentItemUtility, AssemblyUtility assemblyUtility, FileUtility fileUtility)
: base(deploymentItemUtility, assemblyUtility, fileUtility)
{
}
public override void AddDeploymentItemsBasedOnMsTestSetting(string testSourceHandler, IList<DeploymentItem> deploymentItems, List<string> warnings)
{
#if NETFRAMEWORK
if (MSTestSettingsProvider.Settings.DeployTestSourceDependencies)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.Info("Adding the references and satellite assemblies to the deployment items list");
// Get the referenced assemblies.
ProcessNewStorage(testSourceHandler, deploymentItems, warnings);
// Get the satellite assemblies
IEnumerable<DeploymentItem> satelliteItems = GetSatellites(deploymentItems, testSourceHandler, warnings);
foreach (DeploymentItem satelliteItem in satelliteItems)
{
DeploymentItemUtility.AddDeploymentItem(deploymentItems, satelliteItem);
}
}
else
{
PlatformServiceProvider.Instance.AdapterTraceLogger.Info("Adding the test source directory to the deployment items list");
DeploymentItemUtility.AddDeploymentItem(deploymentItems, new DeploymentItem(Path.GetDirectoryName(testSourceHandler)));
}
#else
// It should add items from bin\debug but since deployment items in netcore are run from bin\debug only, so no need to implement it
#endif
}
/// <summary>
/// Get root deployment directory.
/// </summary>
/// <param name="baseDirectory">The base directory.</param>
/// <returns>Root deployment directory.</returns>
public override string GetRootDeploymentDirectory(string baseDirectory)
{
using var currentProcess = Process.GetCurrentProcess();
string dateTimeSuffix = $"{DateTime.Now.ToString("yyyyMMddTHHmmss", DateTimeFormatInfo.InvariantInfo)}_{currentProcess.Id}";
string directoryName = string.Format(CultureInfo.InvariantCulture, Resource.TestRunName, DeploymentFolderPrefix,
#if NETFRAMEWORK
Environment.UserName,
#else
Environment.GetEnvironmentVariable("USERNAME") ?? Environment.GetEnvironmentVariable("USER"),
#endif
dateTimeSuffix);
directoryName = FileUtility.ReplaceInvalidFileNameCharacters(directoryName);
return FileUtility.GetNextIterationDirectoryName(baseDirectory, directoryName);
}
protected override void AddDependenciesOfDeploymentItem(string deploymentItemFile, IList<string> filesToDeploy, IList<string> warnings)
{
#if NETFRAMEWORK
var dependencies = new List<DeploymentItem>();
AddDependencies(deploymentItemFile, null, dependencies, warnings);
foreach (DeploymentItem dependencyItem in dependencies)
{
DebugEx.Assert(Path.IsPathRooted(dependencyItem.SourcePath), "Path of the dependency " + dependencyItem.SourcePath + " is not rooted.");
// Add dependencies to filesToDeploy.
filesToDeploy.Add(dependencyItem.SourcePath);
}
#else
// Its implemented only in full framework project as dependent files are not fetched in netcore.
#endif
}
#if NETFRAMEWORK
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public void ProcessNewStorage(string testSourceHandler, IList<DeploymentItem> deploymentItems, IList<string> warnings)
{
// Add deployment items and process .config files only for storages we have not processed before.
if (!DeploymentItemUtility.IsValidDeploymentItem(testSourceHandler, string.Empty, out string? errorMessage))
{
warnings.Add(errorMessage);
return;
}
DeploymentItemUtility.AddDeploymentItem(deploymentItems, new DeploymentItem(testSourceHandler, string.Empty, DeploymentItemOriginType.TestStorage));
// Deploy .config file if exists, only for assemblies, i.e. DLL and EXE.
// First check <TestStorage>.config, then if not found check for App.Config
// and deploy AppConfig to <TestStorage>.config.
if (AssemblyUtility.IsAssemblyExtension(Path.GetExtension(testSourceHandler)))
{
string? configFile = AddTestSourceConfigFileIfExists(testSourceHandler, deploymentItems);
// Deal with test dependencies: update dependencyDeploymentItems and missingDependentAssemblies.
try
{
// We look for dependent assemblies only for DLL and EXE's.
AddDependencies(testSourceHandler, configFile, deploymentItems, warnings);
}
catch (Exception e)
{
string warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorFailedToDeployDependencies, testSourceHandler, e);
warnings.Add(warning);
}
}
}
public IEnumerable<DeploymentItem> GetSatellites(IEnumerable<DeploymentItem> deploymentItems, string testSourceHandler, IList<string> warnings)
{
List<DeploymentItem> satellites = [];
foreach (DeploymentItem item in deploymentItems)
{
// We do not care about deployment items which are directories because in that case we deploy all files underneath anyway.
string? path = null;
try
{
path = GetFullPathToDeploymentItemSource(item.SourcePath, testSourceHandler);
path = Path.GetFullPath(path);
if (StringEx.IsNullOrEmpty(path) || !AssemblyUtility.IsAssemblyExtension(Path.GetExtension(path))
|| !FileUtility.DoesFileExist(path) || !AssemblyUtility.IsAssembly(path))
{
continue;
}
}
catch (Exception ex) when (ex is ArgumentException or SecurityException or IOException or NotSupportedException)
{
// IOException covers PathTooLongException.
if (PlatformServiceProvider.Instance.AdapterTraceLogger.IsWarningEnabled)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.Warning("DeploymentManager.GetSatellites: {0}", ex);
}
}
// Note: now Path operations with itemPath should not result in any exceptions.
// path is already canonicalized.
// If we cannot access satellite due to security, etc, we report warning.
try
{
string itemDir = Path.GetDirectoryName(path);
List<string> itemSatellites = AssemblyUtility.GetSatelliteAssemblies(path!);
foreach (string satellite in itemSatellites)
{
DebugEx.Assert(!StringEx.IsNullOrEmpty(satellite), "DeploymentManager.DoDeployment: got empty satellite!");
DebugEx.Assert(
satellite.StartsWith(itemDir, StringComparison.OrdinalIgnoreCase),
"DeploymentManager.DoDeployment: Got satellite that does not start with original item path");
string satelliteDir = Path.GetDirectoryName(satellite);
string localeDir = itemDir.Length > satelliteDir.Length
? string.Empty
: satelliteDir.Substring(itemDir.Length + 1);
string relativeOutputDir = Path.Combine(item.RelativeOutputDirectory, localeDir);
// Now finally add the item!
DeploymentItem satelliteItem = new(satellite, relativeOutputDir, DeploymentItemOriginType.Satellite);
DeploymentItemUtility.AddDeploymentItem(satellites, satelliteItem);
}
}
catch (Exception ex) when (ex is ArgumentException or SecurityException or IOException)
{
// IOException covers PathTooLongException.
if (PlatformServiceProvider.Instance.AdapterTraceLogger.IsWarningEnabled)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.Warning("DeploymentManager.GetSatellites: {0}", ex);
}
string warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorGettingSatellite, item, ex.GetType(), ex.GetExceptionMessage());
warnings.Add(warning);
}
}
return satellites;
}
/// <summary>
/// Process test storage and add dependent assemblies to dependencyDeploymentItems.
/// </summary>
/// <param name="testSourceHandler">The test source.</param>
/// <param name="configFile">The config file.</param>
/// <param name="deploymentItems">Deployment items.</param>
/// <param name="warnings">Warnings.</param>
private void AddDependencies(string testSourceHandler, string? configFile, IList<DeploymentItem> deploymentItems, IList<string> warnings)
{
DebugEx.Assert(!StringEx.IsNullOrEmpty(testSourceHandler), "testSourceHandler should not be null or empty.");
// config file can be null.
DebugEx.Assert(deploymentItems != null, "deploymentItems should not be null.");
DebugEx.Assert(Path.IsPathRooted(testSourceHandler), "path should be rooted.");
var sw = Stopwatch.StartNew();
// Note: if this is not an assembly we simply return empty array, also:
// we do recursive search and report missing.
IReadOnlyList<string> references = AssemblyUtility.GetFullPathToDependentAssemblies(testSourceHandler, configFile, out IList<string>? warningList);
foreach (string warning in warningList)
{
warnings.Add(warning);
}
if (PlatformServiceProvider.Instance.AdapterTraceLogger.IsInfoEnabled)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.Info("DeploymentManager: Source:{0} has following references", testSourceHandler);
PlatformServiceProvider.Instance.AdapterTraceLogger.Info("DeploymentManager: Resolving dependencies took {0} ms", sw.ElapsedMilliseconds);
}
foreach (string reference in references)
{
DeploymentItem deploymentItem = new(reference, string.Empty, DeploymentItemOriginType.Dependency);
DeploymentItemUtility.AddDeploymentItem(deploymentItems, deploymentItem);
if (PlatformServiceProvider.Instance.AdapterTraceLogger.IsInfoEnabled)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.Info("DeploymentManager: Reference:{0} ", reference);
}
}
}
#endif
}
#endif