-
Notifications
You must be signed in to change notification settings - Fork 382
/
SourceRootTranslator.cs
182 lines (157 loc) · 7 KB
/
SourceRootTranslator.cs
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
// Copyright (c) Toni Solarin-Sodara
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Coverlet.Core.Abstractions;
namespace Coverlet.Core.Helpers
{
[DebuggerDisplay("ProjectPath = {ProjectPath} OriginalPath = {OriginalPath}")]
internal class SourceRootMapping
{
public string ProjectPath { get; set; }
public string OriginalPath { get; set; }
}
internal class SourceRootTranslator : ISourceRootTranslator
{
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly Dictionary<string, List<SourceRootMapping>> _sourceRootMapping;
private readonly Dictionary<string, List<string>> _sourceToDeterministicPathMapping;
private Dictionary<string, string> _resolutionCacheFiles;
public SourceRootTranslator(ILogger logger, IFileSystem fileSystem)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
_sourceRootMapping = new Dictionary<string, List<SourceRootMapping>>();
}
public SourceRootTranslator(string sourceMappingFile, ILogger logger, IFileSystem fileSystem)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
_sourceRootMapping = LoadSourceRootMapping(sourceMappingFile);
}
public SourceRootTranslator(string moduleTestPath, ILogger logger, IFileSystem fileSystem, IAssemblyAdapter assemblyAdapter)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
if (moduleTestPath is null)
{
throw new ArgumentNullException(nameof(moduleTestPath));
}
if (!_fileSystem.Exists(moduleTestPath))
{
throw new FileNotFoundException($"Module test path '{moduleTestPath}' not found", moduleTestPath);
}
string assemblyName = assemblyAdapter.GetAssemblyName(moduleTestPath);
string mappingFileName = $"CoverletSourceRootsMapping_{assemblyName}";
_logger.LogInformation($"_mapping file name: '{mappingFileName}'", true);
_sourceRootMapping = LoadSourceRootMapping(Path.Combine(Path.GetDirectoryName(moduleTestPath), mappingFileName));
_sourceToDeterministicPathMapping = LoadSourceToDeterministicPathMapping(_sourceRootMapping);
}
private static Dictionary<string, List<string>> LoadSourceToDeterministicPathMapping(Dictionary<string, List<SourceRootMapping>> sourceRootMapping)
{
if (sourceRootMapping is null)
{
throw new ArgumentNullException(nameof(sourceRootMapping));
}
var sourceToDeterministicPathMapping = new Dictionary<string, List<string>>();
foreach (KeyValuePair<string, List<SourceRootMapping>> sourceRootMappingEntry in sourceRootMapping)
{
foreach (SourceRootMapping originalPath in sourceRootMappingEntry.Value)
{
if (!sourceToDeterministicPathMapping.ContainsKey(originalPath.OriginalPath))
{
sourceToDeterministicPathMapping.Add(originalPath.OriginalPath, new List<string>());
}
sourceToDeterministicPathMapping[originalPath.OriginalPath].Add(sourceRootMappingEntry.Key);
}
}
return sourceToDeterministicPathMapping;
}
private Dictionary<string, List<SourceRootMapping>> LoadSourceRootMapping(string mappingFilePath)
{
var mapping = new Dictionary<string, List<SourceRootMapping>>();
if (!_fileSystem.Exists(mappingFilePath))
{
return mapping;
}
foreach (string mappingRecord in _fileSystem.ReadAllLines(mappingFilePath))
{
int projectFileSeparatorIndex = mappingRecord.IndexOf('|');
int pathMappingSeparatorIndex = mappingRecord.IndexOf('=');
if (projectFileSeparatorIndex == -1 || pathMappingSeparatorIndex == -1)
{
_logger.LogWarning($"Malformed mapping '{mappingRecord}'");
continue;
}
#pragma warning disable IDE0057 // Use range operator
string projectPath = mappingRecord.Substring(0, projectFileSeparatorIndex);
string originalPath = mappingRecord.Substring(projectFileSeparatorIndex + 1, pathMappingSeparatorIndex - projectFileSeparatorIndex - 1);
string mappedPath = mappingRecord.Substring(pathMappingSeparatorIndex + 1);
#pragma warning restore IDE0057 // Use range operator
if (!mapping.ContainsKey(mappedPath))
{
mapping.Add(mappedPath, new List<SourceRootMapping>());
}
foreach (string path in originalPath.Split(';'))
{
mapping[mappedPath].Add(new SourceRootMapping() { OriginalPath = path, ProjectPath = projectPath });
}
}
return mapping;
}
public bool AddMappingInCache(string originalFileName, string targetFileName)
{
if (_resolutionCacheFiles != null && _resolutionCacheFiles.ContainsKey(originalFileName))
{
return false;
}
(_resolutionCacheFiles ??= new Dictionary<string, string>()).Add(originalFileName, targetFileName);
return true;
}
public IReadOnlyList<SourceRootMapping> ResolvePathRoot(string pathRoot)
{
return _sourceRootMapping.TryGetValue(pathRoot, out List<SourceRootMapping> sourceRootMapping) ? sourceRootMapping.AsReadOnly() : null;
}
public string ResolveFilePath(string originalFileName)
{
if (_resolutionCacheFiles != null && _resolutionCacheFiles.ContainsKey(originalFileName))
{
return _resolutionCacheFiles[originalFileName];
}
foreach (KeyValuePair<string, List<SourceRootMapping>> mapping in _sourceRootMapping)
{
if (originalFileName.StartsWith(mapping.Key))
{
foreach (SourceRootMapping srm in mapping.Value)
{
string pathToCheck;
if (_fileSystem.Exists(pathToCheck = Path.GetFullPath(originalFileName.Replace(mapping.Key, srm.OriginalPath))))
{
(_resolutionCacheFiles ??= new Dictionary<string, string>()).Add(originalFileName, pathToCheck);
_logger.LogVerbose($"Mapping resolved: '{FileSystem.EscapeFileName(originalFileName)}' -> '{FileSystem.EscapeFileName(pathToCheck)}'");
return pathToCheck;
}
}
}
}
return originalFileName;
}
public string ResolveDeterministicPath(string originalFileName)
{
foreach (KeyValuePair<string, List<string>> originalPath in _sourceToDeterministicPathMapping)
{
if (originalFileName.StartsWith(originalPath.Key))
{
foreach (string deterministicPath in originalPath.Value)
{
originalFileName = originalFileName.Replace(originalPath.Key, deterministicPath).Replace('\\', '/');
}
}
}
return originalFileName;
}
}
}