Reduce memory pressure from sourcemaptoolkit (#84)

* DeminifyStackTrace perf improvements.

* Make ToString() on a DeminifyStackTraceResult use fewer allocations.

* Make ParseStackTrace use fewer allocations and have its result be immutable

* Make most fields on FunctionMapEntry be read-only

* Fix unit test warnings

* Make FunctionMapEntry immutable

* Make the SourcePosition class immutable

* Fix build warnings

* Make MappingEntry be a struct

* Make binding information be a struct

* Make sourcemap immutable

* Additional clean-up

* More clean-up.

* Make more things read-only

* Remove accidental Debugger.Launch()

* Leave comment about why the FunctionMap is stored in descending order

* Some more clean-up

* fix typo

* fix comment to remove reference to other project.

* Rename GetDeminifiedMethodNameFromSourceMap --> GetDeminifiedMethodName in unit tests.

* Add unit tests for new code.

* Remove duplicate StringExtensions classes.
This commit is contained in:
Rob Rolnick 2021-02-14 20:43:18 -08:00 коммит произвёл GitHub
Родитель ef2988141b
Коммит 38362c5ed1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
45 изменённых файлов: 1863 добавлений и 1392 удалений

Просмотреть файл

@ -1,19 +1,24 @@
using System;
using SourcemapToolkit.SourcemapParser;
using System.Collections.Generic;
using System.Text;
namespace SourcemapToolkit.CallstackDeminifier
{
public class DeminifyStackTraceResult
{
public string Message;
public List<StackFrame> MinifiedStackFrames;
public List<StackFrameDeminificationResult> DeminifiedStackFrameResults;
public string Message { get; }
public IReadOnlyList<StackFrame> MinifiedStackFrames { get; }
public IReadOnlyList<StackFrameDeminificationResult> DeminifiedStackFrameResults { get; }
public override string ToString()
{
string output = Message ?? string.Empty;
StringBuilder sb = new StringBuilder();
if (!string.IsNullOrEmpty(Message))
{
sb.Append(Message);
}
for (int i = 0; i < DeminifiedStackFrameResults.Count; i++)
{
StackFrame deminFrame = DeminifiedStackFrameResults[i].DeminifiedStackFrame;
@ -22,14 +27,26 @@ namespace SourcemapToolkit.CallstackDeminifier
StackFrame frame = new StackFrame()
{
MethodName = deminFrame.MethodName ?? MinifiedStackFrames[i].MethodName,
SourcePosition = deminFrame.SourcePosition ?? MinifiedStackFrames[i].SourcePosition,
FilePath = deminFrame.SourcePosition != null ? deminFrame.FilePath : MinifiedStackFrames[i].FilePath
SourcePosition = deminFrame.SourcePosition != SourcePosition.NotFound ? deminFrame.SourcePosition : MinifiedStackFrames[i].SourcePosition,
FilePath = deminFrame.SourcePosition != SourcePosition.NotFound ? deminFrame.FilePath : MinifiedStackFrames[i].FilePath
};
output += $"{Environment.NewLine} {frame}";
sb.AppendLine();
sb.Append(" ");
sb.Append(frame);
}
return output;
return sb.ToString();
}
public DeminifyStackTraceResult(
string message,
IReadOnlyList<StackFrame> minifiedStackFrames,
IReadOnlyList<StackFrameDeminificationResult> deminifiedStackFrameResults)
{
Message = message;
MinifiedStackFrames = minifiedStackFrames;
DeminifiedStackFrameResults = deminifiedStackFrameResults;
}
}
}

Просмотреть файл

@ -11,99 +11,82 @@ namespace SourcemapToolkit.CallstackDeminifier
/// </summary>
internal class FunctionFinderVisitor : TreeVisitor
{
internal readonly List<FunctionMapEntry> FunctionMap = new List<FunctionMapEntry>();
internal readonly List<FunctionMapEntry> FunctionMap;
internal readonly SourceMap SourceMap;
public FunctionFinderVisitor(SourceMap sourceMap)
{
FunctionMap = new List<FunctionMapEntry>();
SourceMap = sourceMap;
}
public override void Visit(FunctionObject node)
{
base.Visit(node);
List<BindingInformation> bindings = GetBindings(node);
var bindings = GetBindings(node);
if (bindings != null)
{
FunctionMapEntry functionMapEntry = new FunctionMapEntry
{
Bindings = bindings,
StartSourcePosition = new SourcePosition
{
ZeroBasedLineNumber = node.Body.Context.StartLineNumber - 1, // Souce maps work with zero based line and column numbers, the AST works with one based line numbers. We want to use zero-based everywhere.
ZeroBasedColumnNumber = node.Body.Context.StartColumn
},
EndSourcePosition = new SourcePosition
{
ZeroBasedLineNumber = node.Body.Context.EndLineNumber - 1, // Souce maps work with zero based line and column numbers, the AST works with one based line numbers. We want to use zero-based everywhere.
ZeroBasedColumnNumber = node.Body.Context.EndColumn
}
};
FunctionMapEntry functionMapEntry = new FunctionMapEntry(
bindings: bindings,
deminifiedMethodName: SourceMap.GetDeminifiedMethodName(bindings),
startSourcePosition: new SourcePosition(
zeroBasedLineNumber: node.Body.Context.StartLineNumber - 1, // Souce maps work with zero based line and column numbers, the AST works with one based line numbers. We want to use zero-based everywhere.
zeroBasedColumnNumber: node.Body.Context.StartColumn),
endSourcePosition: new SourcePosition(
zeroBasedLineNumber: node.Body.Context.EndLineNumber - 1, // Souce maps work with zero based line and column numbers, the AST works with one based line numbers. We want to use zero-based everywhere.
zeroBasedColumnNumber: node.Body.Context.EndColumn));
FunctionMap.Add(functionMapEntry);
}
}
}
/// <summary>
/// Gets the name and location information related to the function name binding for a FunctionObject node
/// </summary>
private List<BindingInformation> GetBindings(FunctionObject node)
private IReadOnlyList<BindingInformation> GetBindings(FunctionObject node)
{
List<BindingInformation> result = new List<BindingInformation>();
// Gets the name of an object property that a function is bound to, like the static method foo in the example "object.foo = function () {}"
BinaryOperator parentBinaryOperator = node.Parent as BinaryOperator;
if (parentBinaryOperator != null)
if (node.Parent is BinaryOperator parentBinaryOperator)
{
result.AddRange(ExtractBindingsFromBinaryOperator(parentBinaryOperator));
return result;
}
// Gets the name of an object property that a function is bound to against the prototype, like the instance method foo in the example "object.prototype = {foo: function () {}}"
ObjectLiteralProperty parentObjectLiteralProperty = node.Parent as ObjectLiteralProperty;
if (parentObjectLiteralProperty != null)
if (node.Parent is ObjectLiteralProperty parentObjectLiteralProperty)
{
// See if we can get the name of the object that this method belongs to
ObjectLiteral objectLiteralParent = parentObjectLiteralProperty.Parent?.Parent as ObjectLiteral;
if (objectLiteralParent != null && objectLiteralParent.Parent is BinaryOperator)
if (objectLiteralParent != null && objectLiteralParent.Parent is BinaryOperator binaryOperator)
{
result.AddRange(ExtractBindingsFromBinaryOperator((BinaryOperator)objectLiteralParent.Parent));
result.AddRange(ExtractBindingsFromBinaryOperator(binaryOperator));
}
result.Add(
new BindingInformation
{
Name = parentObjectLiteralProperty.Name.Name,
SourcePosition = new SourcePosition
{
ZeroBasedLineNumber = parentObjectLiteralProperty.Context.StartLineNumber - 1,
ZeroBasedColumnNumber = parentObjectLiteralProperty.Context.StartColumn
}
});
new BindingInformation(
name: parentObjectLiteralProperty.Name.Name,
sourcePosition: new SourcePosition(
zeroBasedLineNumber: parentObjectLiteralProperty.Context.StartLineNumber - 1,
zeroBasedColumnNumber: parentObjectLiteralProperty.Context.StartColumn)));
return result;
}
BindingIdentifier bindingIdentifier = null;
// Gets the name of a variable that a function is bound to, like foo in the example "var foo = function () {}"
VariableDeclaration parentVariableDeclaration = node.Parent as VariableDeclaration;
if (parentVariableDeclaration != null)
{
bindingIdentifier = parentVariableDeclaration.Binding as BindingIdentifier;
}
// Gets the name bound to the function, like foo in the example "function foo() {}
else
{
bindingIdentifier = node.Binding;
}
BindingIdentifier bindingIdentifier = (node.Parent is VariableDeclaration parentVariableDeclaration) ?
parentVariableDeclaration.Binding as BindingIdentifier :
node.Binding; // Gets the name bound to the function, like foo in the example "function foo() {}
if (bindingIdentifier != null)
{
result.Add(
new BindingInformation
{
Name = bindingIdentifier.Name,
SourcePosition = new SourcePosition
{
ZeroBasedLineNumber = bindingIdentifier.Context.StartLineNumber - 1,
new BindingInformation(
name: bindingIdentifier.Name,
sourcePosition: new SourcePosition(
zeroBasedLineNumber: bindingIdentifier.Context.StartLineNumber - 1,
// Souce maps work with zero based line and column numbers, the AST works with one based line numbers. We want to use zero-based everywhere.
ZeroBasedColumnNumber = bindingIdentifier.Context.StartColumn
}
});
zeroBasedColumnNumber: bindingIdentifier.Context.StartColumn)));
return result;
}
@ -125,15 +108,11 @@ namespace SourcemapToolkit.CallstackDeminifier
if (member.Name != "prototype")
{
int offset = member.NameContext.Code.StartsWith(".") ? 1 : 0;
yield return new BindingInformation
{
Name = member.Name,
SourcePosition = new SourcePosition
{
ZeroBasedLineNumber = member.NameContext.StartLineNumber - 1,
ZeroBasedColumnNumber = member.NameContext.StartColumn + offset
}
};
yield return new BindingInformation(
name: member.Name,
sourcePosition: new SourcePosition(
zeroBasedLineNumber: member.NameContext.StartLineNumber - 1,
zeroBasedColumnNumber: member.NameContext.StartColumn + offset));
}
}
else
@ -144,15 +123,11 @@ namespace SourcemapToolkit.CallstackDeminifier
private BindingInformation ExtractBindingsFromNode(AstNode node)
{
return new BindingInformation
{
Name = node.Context.Code,
SourcePosition = new SourcePosition
{
ZeroBasedLineNumber = node.Context.StartLineNumber - 1,
ZeroBasedColumnNumber = node.Context.StartColumn
}
};
return new BindingInformation(
name: node.Context.Code,
sourcePosition: new SourcePosition(
zeroBasedLineNumber: node.Context.StartLineNumber - 1,
zeroBasedColumnNumber: node.Context.StartColumn));
}
}
}

Просмотреть файл

@ -10,7 +10,7 @@ namespace SourcemapToolkit.CallstackDeminifier
/// </summary>
/// <param name="sourcePosition">The location of the code around which we wish to find a wrapping function</param>
/// <param name="functionMap">The function map, sorted in decreasing order by start source position, that represents the file containing the code of interest</param>
public FunctionMapEntry GetWrappingFunctionForSourceLocation(SourcePosition sourcePosition, List<FunctionMapEntry> functionMap)
public FunctionMapEntry GetWrappingFunctionForSourceLocation(SourcePosition sourcePosition, IReadOnlyList<FunctionMapEntry> functionMap)
{
foreach (FunctionMapEntry mapEntry in functionMap)
{

Просмотреть файл

@ -7,17 +7,23 @@ namespace SourcemapToolkit.CallstackDeminifier
/// Describes information regarding a binding that can be used for minification.
/// Examples include methods, functions, and object declarations.
/// </summary>
internal class BindingInformation
internal struct BindingInformation
{
/// <summary>
/// The name of the method or class
/// </summary>
public string Name;
public readonly string Name;
/// <summary>
/// The location of the function name or class declaration
/// </summary>
public SourcePosition SourcePosition;
public readonly SourcePosition SourcePosition;
public BindingInformation(string name, SourcePosition sourcePosition)
{
Name = name;
SourcePosition = sourcePosition;
}
}
/// <summary>
@ -30,22 +36,34 @@ namespace SourcemapToolkit.CallstackDeminifier
/// To get the complete name of the function associated with this mapping entry
/// append the names of each bindings with a "."
/// </summary>
public List<BindingInformation> Bindings { get; set; }
public IReadOnlyList<BindingInformation> Bindings { get; }
/// <summary>
/// If this entry represents a function whose name was minified, this value
/// may contain an associated deminfied name corresponding to the function.
/// </summary>
public string DeminfifiedMethodName { get; set; }
public string DeminifiedMethodName { get; }
/// <summary>
/// Denotes the location of the beginning of this function
/// </summary>
public SourcePosition StartSourcePosition { get; set; }
public SourcePosition StartSourcePosition { get; }
/// <summary>
/// Denotes the end location of this function
/// </summary>
public SourcePosition EndSourcePosition { get; set; }
public SourcePosition EndSourcePosition { get; }
public FunctionMapEntry(
IReadOnlyList<BindingInformation> bindings,
string deminifiedMethodName,
SourcePosition startSourcePosition,
SourcePosition endSourcePosition)
{
Bindings = bindings;
DeminifiedMethodName = deminifiedMethodName;
StartSourcePosition = startSourcePosition;
EndSourcePosition = endSourcePosition;
}
}
}

Просмотреть файл

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Ajax.Utilities;
using SourcemapToolkit.SourcemapParser;
@ -13,22 +12,17 @@ namespace SourcemapToolkit.CallstackDeminifier
/// Returns a FunctionMap describing the locations of every funciton in the source code.
/// The functions are to be sorted descending by start position.
/// </summary>
public List<FunctionMapEntry> GenerateFunctionMap(StreamReader sourceCodeStreamReader, SourceMap sourceMap)
public IReadOnlyList<FunctionMapEntry> GenerateFunctionMap(StreamReader sourceCodeStreamReader, SourceMap sourceMap)
{
if (sourceCodeStreamReader == null || sourceMap == null)
{
return null;
}
List<FunctionMapEntry> result;
IReadOnlyList<FunctionMapEntry> result;
try
{
result = ParseSourceCode(sourceCodeStreamReader);
foreach (FunctionMapEntry functionMapEntry in result)
{
functionMapEntry.DeminfifiedMethodName = GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
}
result = ParseSourceCode(sourceCodeStreamReader, sourceMap);
}
catch
{
@ -43,7 +37,7 @@ namespace SourcemapToolkit.CallstackDeminifier
/// <summary>
/// Iterates over all the code in the JavaScript file to get a list of all the functions declared in that file.
/// </summary>
internal List<FunctionMapEntry> ParseSourceCode(StreamReader sourceCodeStreamReader)
internal IReadOnlyList<FunctionMapEntry> ParseSourceCode(StreamReader sourceCodeStreamReader, SourceMap sourceMap)
{
if (sourceCodeStreamReader == null)
{
@ -60,68 +54,16 @@ namespace SourcemapToolkit.CallstackDeminifier
Block block = jsParser.Parse(sourceCode, codeSettings);
FunctionFinderVisitor functionFinderVisitor = new FunctionFinderVisitor();
FunctionFinderVisitor functionFinderVisitor = new FunctionFinderVisitor(sourceMap);
functionFinderVisitor.Visit(block);
// Sort in descending order by start position
// Sort in descending order by start position. This allows the first result found in a linear search to be the "closest function to the [consumer's] source position".
//
// ATTN: It may be possible to do this with an ascending order sort, followed by a series of binary searches on rows & columns.
// Our current profiles show the memory pressure being a bigger issue than the stack lookup, so I'm leaving this for now.
functionFinderVisitor.FunctionMap.Sort((x, y) => y.StartSourcePosition.CompareTo(x.StartSourcePosition));
return functionFinderVisitor.FunctionMap;
}
/// <summary>
/// Gets the original name corresponding to a function based on the information provided in the source map.
/// </summary>
internal static string GetDeminifiedMethodNameFromSourceMap(FunctionMapEntry wrappingFunction, SourceMap sourceMap)
{
if (wrappingFunction == null)
{
throw new ArgumentNullException(nameof(wrappingFunction));
}
if (sourceMap == null)
{
throw new ArgumentNullException(nameof(sourceMap));
}
string methodName = null;
if (wrappingFunction.Bindings != null && wrappingFunction.Bindings.Count > 0)
{
MappingEntry objectProtoypeMappingEntry = null;
if (wrappingFunction.Bindings.Count == 2)
{
objectProtoypeMappingEntry =
sourceMap.GetMappingEntryForGeneratedSourcePosition(wrappingFunction.Bindings[0].SourcePosition);
}
MappingEntry mappingEntry =
sourceMap.GetMappingEntryForGeneratedSourcePosition(wrappingFunction.Bindings.Last().SourcePosition);
if (mappingEntry?.OriginalName != null)
{
if (objectProtoypeMappingEntry?.OriginalName != null)
{
string objectName = objectProtoypeMappingEntry.OriginalName;
if (objectProtoypeMappingEntry.OriginalSourcePosition?.ZeroBasedColumnNumber == mappingEntry.OriginalSourcePosition?.ZeroBasedColumnNumber
&& objectProtoypeMappingEntry.OriginalSourcePosition?.ZeroBasedLineNumber == mappingEntry.OriginalSourcePosition?.ZeroBasedLineNumber
&& objectName.EndsWith($".{mappingEntry.OriginalName}"))
{
// The object name already contains the method name, so do not append it
methodName = objectName;
}
else
{
methodName = $"{objectName}.{mappingEntry.OriginalName}";
}
}
else
{
methodName = mappingEntry.OriginalName;
}
}
}
return methodName;
}
}
}

Просмотреть файл

@ -10,12 +10,12 @@ namespace SourcemapToolkit.CallstackDeminifier
internal class FunctionMapStore : IFunctionMapStore
{
private readonly IFunctionMapGenerator _functionMapGenerator;
private readonly KeyValueCache<string,List<FunctionMapEntry>> _functionMapCache;
private readonly KeyValueCache<string, IReadOnlyList<FunctionMapEntry>> _functionMapCache;
public FunctionMapStore(ISourceCodeProvider sourceCodeProvider, Func<string, SourceMap> sourceMapGetter)
{
_functionMapGenerator = new FunctionMapGenerator();
_functionMapCache = new KeyValueCache<string, List<FunctionMapEntry>>(sourceCodeUrl => _functionMapGenerator.GenerateFunctionMap(
_functionMapCache = new KeyValueCache<string, IReadOnlyList<FunctionMapEntry>>(sourceCodeUrl => _functionMapGenerator.GenerateFunctionMap(
sourceCodeProvider.GetSourceCode(sourceCodeUrl),
sourceMapGetter(sourceCodeUrl)));
}
@ -26,7 +26,7 @@ namespace SourcemapToolkit.CallstackDeminifier
/// Once a function map is generated, the value is cached in memory for future usages.
/// </summary>
/// <param name="sourceCodeUrl">The URL of the file for which a function map is required</param>
public List<FunctionMapEntry> GetFunctionMapForSourceCode(string sourceCodeUrl)
public IReadOnlyList<FunctionMapEntry> GetFunctionMapForSourceCode(string sourceCodeUrl)
{
return _functionMapCache.GetValue(sourceCodeUrl);
}

Просмотреть файл

@ -10,6 +10,6 @@ namespace SourcemapToolkit.CallstackDeminifier
/// </summary>
/// <param name="sourcePosition">The location of the code around which we wish to find a wrapping function</param>
/// <param name="functionMap">The function map, sorted in decreasing order by start source position, that represents the file containing the code of interest</param>
FunctionMapEntry GetWrappingFunctionForSourceLocation(SourcePosition sourcePosition, List<FunctionMapEntry> functionMap);
FunctionMapEntry GetWrappingFunctionForSourceLocation(SourcePosition sourcePosition, IReadOnlyList<FunctionMapEntry> functionMap);
}
}

Просмотреть файл

@ -10,6 +10,6 @@ namespace SourcemapToolkit.CallstackDeminifier
/// Returns a FunctionMap describing the locations of every funciton in the source code.
/// The functions are to be sorted in decreasing order by start position.
/// </summary>
List<FunctionMapEntry> GenerateFunctionMap(StreamReader sourceCodeStreamReader, SourceMap sourceMap);
IReadOnlyList<FunctionMapEntry> GenerateFunctionMap(StreamReader sourceCodeStreamReader, SourceMap sourceMap);
}
}

Просмотреть файл

@ -4,6 +4,6 @@ namespace SourcemapToolkit.CallstackDeminifier
{
internal interface IFunctionMapStore
{
List<FunctionMapEntry> GetFunctionMapForSourceCode(string sourceCodeUrl);
IReadOnlyList<FunctionMapEntry> GetFunctionMapForSourceCode(string sourceCodeUrl);
}
}

Просмотреть файл

@ -18,7 +18,7 @@ namespace SourcemapToolkit.CallstackDeminifier
/// <remarks>
/// This override drops the Message out param for backward compatibility
/// </remarks>
List<StackFrame> ParseStackTrace(string stackTraceString);
IReadOnlyList<StackFrame> ParseStackTrace(string stackTraceString);
/// <summary>
/// Generates a list of StackFrame objects based on the input stack trace.
@ -31,6 +31,6 @@ namespace SourcemapToolkit.CallstackDeminifier
/// Any parts of the stack trace that could not be parsed are excluded from
/// the result. Does not ever return null.
/// </returns>
List<StackFrame> ParseStackTrace(string stackTraceString, out string message);
IReadOnlyList<StackFrame> ParseStackTrace(string stackTraceString, out string message);
}
}

Просмотреть файл

@ -21,21 +21,22 @@ namespace SourcemapToolkit.CallstackDeminifier
/// </summary>
public virtual StackFrameDeminificationResult DeminifyStackFrame(StackFrame stackFrame, string callerSymbolName)
{
StackFrameDeminificationResult result = new StackFrameDeminificationResult
{
DeminificationError = DeminificationError.None
};
if (stackFrame == null)
{
throw new ArgumentNullException(nameof(stackFrame));
}
StackFrameDeminificationResult result = new StackFrameDeminificationResult
{
DeminificationError = DeminificationError.None
};
FunctionMapEntry wrappingFunction = null;
// This code deminifies the stack frame by finding the wrapping function in
// the generated code and then using the source map to find the name and
// and original source location.
List<FunctionMapEntry> functionMap = _functionMapStore.GetFunctionMapForSourceCode(stackFrame.FilePath);
IReadOnlyList<FunctionMapEntry> functionMap = _functionMapStore.GetFunctionMapForSourceCode(stackFrame.FilePath);
if (functionMap != null)
{
wrappingFunction =
@ -51,7 +52,7 @@ namespace SourcemapToolkit.CallstackDeminifier
result.DeminificationError = DeminificationError.NoSourceCodeProvided;
}
result.DeminifiedStackFrame = new StackFrame {MethodName = wrappingFunction?.DeminfifiedMethodName};
result.DeminifiedStackFrame = new StackFrame {MethodName = wrappingFunction?.DeminifiedMethodName};
return result;
}
}

Просмотреть файл

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
internal static class SourceMapExtensions
{
/// <summary>
/// Gets the original name corresponding to a function based on the information provided in the source map.
/// </summary>
internal static string GetDeminifiedMethodName(this SourceMap sourceMap, IReadOnlyList<BindingInformation> bindings)
{
if (sourceMap == null)
{
throw new ArgumentNullException(nameof(sourceMap));
}
string methodName = null;
if (bindings != null && bindings.Count > 0)
{
MappingEntry? objectProtoypeMappingEntry = null;
if (bindings.Count == 2)
{
objectProtoypeMappingEntry =
sourceMap.GetMappingEntryForGeneratedSourcePosition(bindings[0].SourcePosition);
}
MappingEntry? mappingEntry =
sourceMap.GetMappingEntryForGeneratedSourcePosition(bindings.Last().SourcePosition);
if (mappingEntry?.OriginalName != null)
{
if (objectProtoypeMappingEntry?.OriginalName != null)
{
string objectName = objectProtoypeMappingEntry.Value.OriginalName;
if (objectProtoypeMappingEntry.Value.OriginalSourcePosition.ZeroBasedColumnNumber == mappingEntry.Value.OriginalSourcePosition.ZeroBasedColumnNumber
&& objectProtoypeMappingEntry.Value.OriginalSourcePosition.ZeroBasedLineNumber == mappingEntry.Value.OriginalSourcePosition.ZeroBasedLineNumber
&& objectName.Length > mappingEntry.Value.OriginalName.Length
&& objectName.EndsWith(mappingEntry.Value.OriginalName)
&& (objectName[objectName.Length - 1 - mappingEntry.Value.OriginalName.Length] == '.'))
{
// The object name already contains the method name, so do not append it
methodName = objectName;
}
else
{
methodName = $"{objectName}.{mappingEntry.Value.OriginalName}";
}
}
else
{
methodName = mappingEntry.Value.OriginalName;
}
}
}
return methodName;
}
}
}

Просмотреть файл

@ -51,6 +51,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="SourceMapExtensions.cs" />
<Compile Include="StackFrameDeminificationResult.cs" />
<Compile Include="DeminifyStackTraceResult.cs" />
<Compile Include="FunctionFinderVisitor.cs" />

Просмотреть файл

@ -20,7 +20,7 @@ namespace SourcemapToolkit.CallstackDeminifier
/// <summary>
/// The zero-based position of this stack entry.
/// </summary>
public SourcePosition SourcePosition { get; set; }
public SourcePosition SourcePosition { get; set; } = SourcePosition.NotFound;
public override string ToString()
{
@ -28,7 +28,7 @@ namespace SourcemapToolkit.CallstackDeminifier
if (!string.IsNullOrWhiteSpace(FilePath))
{
output += $" in {FilePath}";
if (SourcePosition != null)
if (SourcePosition != SourcePosition.NotFound)
{
output += $":{SourcePosition.ZeroBasedLineNumber + 1}:{SourcePosition.ZeroBasedColumnNumber + 1}";
}

Просмотреть файл

@ -55,7 +55,7 @@ namespace SourcemapToolkit.CallstackDeminifier
if (result.DeminificationError == DeminificationError.None)
{
MappingEntry generatedSourcePositionMappingEntry =
MappingEntry? generatedSourcePositionMappingEntry =
sourceMap?.GetMappingEntryForGeneratedSourcePosition(generatedSourcePosition);
if (generatedSourcePositionMappingEntry == null)
@ -75,9 +75,9 @@ namespace SourcemapToolkit.CallstackDeminifier
}
else
{
result.DeminifiedStackFrame.FilePath = generatedSourcePositionMappingEntry.OriginalFileName;
result.DeminifiedStackFrame.SourcePosition = generatedSourcePositionMappingEntry.OriginalSourcePosition;
result.DeminifiedSymbolName = generatedSourcePositionMappingEntry.OriginalName;
result.DeminifiedStackFrame.FilePath = generatedSourcePositionMappingEntry.Value.OriginalFileName;
result.DeminifiedStackFrame.SourcePosition = generatedSourcePositionMappingEntry.Value.OriginalSourcePosition;
result.DeminifiedSymbolName = generatedSourcePositionMappingEntry.Value.OriginalName;
}
}

Просмотреть файл

@ -23,21 +23,23 @@ namespace SourcemapToolkit.CallstackDeminifier
/// </summary>
public DeminifyStackTraceResult DeminifyStackTrace(string stackTraceString)
{
DeminifyStackTraceResult result = new DeminifyStackTraceResult();
result.MinifiedStackFrames = _stackTraceParser.ParseStackTrace(stackTraceString, out string message);
result.Message = message;
result.DeminifiedStackFrameResults = new List<StackFrameDeminificationResult>();
var minifiedStackFrames = _stackTraceParser.ParseStackTrace(stackTraceString, out string message);
var deminifiedStackFrameResults = new List<StackFrameDeminificationResult>(minifiedStackFrames.Count);
// Deminify frames in reverse order so we can pass the symbol name from caller
// (i.e. the function name) into the next level's deminification.
string callerSymbolName = null;
for (int i = result.MinifiedStackFrames.Count - 1; i >= 0; i--)
for (int i = minifiedStackFrames.Count - 1; i >= 0; i--)
{
var frame = _stackFrameDeminifier.DeminifyStackFrame(result.MinifiedStackFrames[i], callerSymbolName);
var frame = _stackFrameDeminifier.DeminifyStackFrame(minifiedStackFrames[i], callerSymbolName);
callerSymbolName = frame?.DeminifiedSymbolName;
result.DeminifiedStackFrameResults.Insert(0, frame);
deminifiedStackFrameResults.Add(frame);
}
deminifiedStackFrameResults.Reverse();
var result = new DeminifyStackTraceResult(message, minifiedStackFrames, deminifiedStackFrameResults);
return result;
}
}

Просмотреть файл

@ -29,9 +29,9 @@ namespace SourcemapToolkit.CallstackDeminifier
/// <remarks>
/// This override drops the Message out param for backward compatibility
/// </remarks>
public List<StackFrame> ParseStackTrace(string stackTraceString)
public IReadOnlyList<StackFrame> ParseStackTrace(string stackTraceString)
{
return ParseStackTrace(stackTraceString, out string message);
return ParseStackTrace(stackTraceString, out _);
}
/// <summary>
@ -45,7 +45,7 @@ namespace SourcemapToolkit.CallstackDeminifier
/// Any parts of the stack trace that could not be parsed are excluded from
/// the result. Does not ever return null.
/// </returns>
public virtual List<StackFrame> ParseStackTrace(string stackTraceString, out string message)
public virtual IReadOnlyList<StackFrame> ParseStackTrace(string stackTraceString, out string message)
{
if (stackTraceString == null)
{
@ -53,19 +53,23 @@ namespace SourcemapToolkit.CallstackDeminifier
}
message = null;
List<StackFrame> stackTrace = new List<StackFrame>();
List<string> stackFrameStrings = stackTraceString.Split('\n').ToList();
string[] stackFrameStrings = stackTraceString.SplitFast('\n');
int startingIndex = 0;
var firstFrame = stackFrameStrings.First();
if (!firstFrame.StartsWith(" ") && TryExtractMethodNameFromFrame(firstFrame) == null)
{
message = firstFrame.Trim();
stackFrameStrings.RemoveAt(0);
startingIndex = 1;
}
foreach (string frame in stackFrameStrings)
// Allocate assuming none of the frames are null.
List<StackFrame> stackTrace = new List<StackFrame>(stackFrameStrings.Length - startingIndex);
for (int i = startingIndex; i < stackFrameStrings.Length; i++)
{
StackFrame parsedStackFrame = TryParseSingleStackFrame(frame);
StackFrame parsedStackFrame = TryParseSingleStackFrame(stackFrameStrings[i]);
if (parsedStackFrame != null)
{
@ -119,7 +123,10 @@ namespace SourcemapToolkit.CallstackDeminifier
}
if (string.IsNullOrWhiteSpace(methodName))
{
methodName = null;
}
return methodName;
}
@ -145,14 +152,12 @@ namespace SourcemapToolkit.CallstackDeminifier
if (lineNumberMatch.Success)
{
result.FilePath = lineNumberMatch.Groups[1].Value;
result.SourcePosition = new SourcePosition
{
result.SourcePosition = new SourcePosition(
// The browser provides one-based line and column numbers, but the
// rest of this library uses zero-based values. Normalize to make
// the stack frames zero based.
ZeroBasedLineNumber = int.Parse(lineNumberMatch.Groups[2].Value, CultureInfo.InvariantCulture) - 1,
ZeroBasedColumnNumber = int.Parse(lineNumberMatch.Groups[3].Value, CultureInfo.InvariantCulture) -1
};
zeroBasedLineNumber: int.Parse(lineNumberMatch.Groups[2].Value, CultureInfo.InvariantCulture) - 1,
zeroBasedColumnNumber: int.Parse(lineNumberMatch.Groups[3].Value, CultureInfo.InvariantCulture) -1);
}
return result;

Просмотреть файл

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
namespace SourcemapToolkit.SourcemapParser
{
internal static class IReadOnlyListExtensions
{
public static int IndexOf<T>(this IReadOnlyList<T> input, T value)
{
EqualityComparer<T> equalityComparer = EqualityComparer<T>.Default;
for (int i = 0; i < input.Count; i++)
{
if (equalityComparer.Equals(input[i], value))
{
return i;
}
}
return -1;
}
/// <summary>
/// Copied from: https://referencesource.microsoft.com/#mscorlib/system/collections/generic/arraysorthelper.cs,63a9955a91f2b37b
/// </summary>
public static int BinarySearch<T>(this IReadOnlyList<T> input, T item, IComparer<T> comparer)
{
int lo = 0;
int hi = input.Count - 1;
while (lo <= hi)
{
int i = lo + ((hi - lo) >> 1);
int order = comparer.Compare(input[i], item);
if (order == 0)
{
return i;
}
if (order < 0)
{
lo = i + 1;
}
else
{
hi = i - 1;
}
}
return ~lo;
}
}
}

Просмотреть файл

@ -2,46 +2,81 @@
namespace SourcemapToolkit.SourcemapParser
{
public class MappingEntry
public struct MappingEntry
{
/// <summary>
/// The location of the line of code in the transformed code
/// </summary>
public SourcePosition GeneratedSourcePosition;
public readonly SourcePosition GeneratedSourcePosition;
/// <summary>
/// The location of the code in the original source code
/// </summary>
public SourcePosition OriginalSourcePosition;
public readonly SourcePosition OriginalSourcePosition;
/// <summary>
/// The original name of the code referenced by this mapping entry
/// </summary>
public string OriginalName;
public readonly string OriginalName;
/// <summary>
/// The name of the file that originally contained this code
/// </summary>
public string OriginalFileName;
public readonly string OriginalFileName;
public MappingEntry Clone()
public MappingEntry(
SourcePosition generatedSourcePosition,
SourcePosition? originalSourcePosition = null,
string originalName = null,
string originalFileName = null)
{
return new MappingEntry
{
GeneratedSourcePosition = this.GeneratedSourcePosition.Clone(),
OriginalSourcePosition = this.OriginalSourcePosition.Clone(),
OriginalFileName = this.OriginalFileName,
OriginalName = this.OriginalName
};
GeneratedSourcePosition = generatedSourcePosition;
OriginalSourcePosition = originalSourcePosition ?? SourcePosition.NotFound;
OriginalName = originalName;
OriginalFileName = originalFileName;
}
public Boolean IsValueEqual(MappingEntry anEntry)
public MappingEntry CloneWithResetColumnNumber()
{
return (
this.OriginalName == anEntry.OriginalName &&
this.OriginalFileName == anEntry.OriginalFileName &&
this.GeneratedSourcePosition.CompareTo(anEntry.GeneratedSourcePosition) == 0 &&
this.OriginalSourcePosition.CompareTo(anEntry.OriginalSourcePosition) == 0);
return new MappingEntry(
generatedSourcePosition: new SourcePosition(
zeroBasedLineNumber: GeneratedSourcePosition.ZeroBasedLineNumber,
zeroBasedColumnNumber: 0),
originalSourcePosition: new SourcePosition(
zeroBasedLineNumber: OriginalSourcePosition.ZeroBasedLineNumber,
zeroBasedColumnNumber: 0),
originalName: OriginalName,
originalFileName: OriginalFileName);
}
public bool Equals(MappingEntry that)
{
return this.OriginalName == that.OriginalName &&
this.OriginalFileName == that.OriginalFileName &&
this.GeneratedSourcePosition.Equals(that.GeneratedSourcePosition) &&
this.OriginalSourcePosition.Equals(that.OriginalSourcePosition);
}
public override bool Equals(object obj)
{
return (obj is MappingEntry mappingEntry) ? Equals(mappingEntry) : false;
}
/// <summary>
/// An implementation of Josh Bloch's hashing algorithm from Effective Java.
/// It is fast, offers a good distribution (with primes 23 and 31), and allocation free.
/// </summary>
public override int GetHashCode()
{
unchecked // Wrap to protect overflow
{
int hash = 23;
hash = hash * 31 + GeneratedSourcePosition.GetHashCode();
hash = hash * 31 + OriginalSourcePosition.GetHashCode();
hash = hash * 31 + (OriginalName ?? "").GetHashCode();
hash = hash * 31 + (OriginalFileName ?? "").GetHashCode();
return hash;
}
}
}
}

Просмотреть файл

@ -7,7 +7,7 @@ namespace SourcemapToolkit.SourcemapParser
/// Corresponds to a single parsed entry in the source map mapping string that is used internally by the parser.
/// The public API exposes the MappingEntry object, which is more useful to consumers of the library.
/// </summary>
internal class NumericMappingEntry
internal struct NumericMappingEntry
{
/// <summary>
/// The zero-based line number in the generated code that corresponds to this mapping segment.
@ -39,44 +39,40 @@ namespace SourcemapToolkit.SourcemapParser
/// </summary>
public int? OriginalNameIndex;
public MappingEntry ToMappingEntry(List<string> names, List<string> sources)
public MappingEntry ToMappingEntry(IReadOnlyList<string> names, IReadOnlyList<string> sources)
{
MappingEntry result = new MappingEntry
{
GeneratedSourcePosition = new SourcePosition
{
ZeroBasedColumnNumber = GeneratedColumnNumber,
ZeroBasedLineNumber = GeneratedLineNumber
}
};
SourcePosition originalSourcePosition;
if (OriginalColumnNumber.HasValue && OriginalLineNumber.HasValue)
{
result.OriginalSourcePosition = new SourcePosition
{
ZeroBasedColumnNumber = OriginalColumnNumber.Value,
ZeroBasedLineNumber = OriginalLineNumber.Value
};
originalSourcePosition = new SourcePosition(
zeroBasedLineNumber: OriginalLineNumber.Value,
zeroBasedColumnNumber: OriginalColumnNumber.Value);
}
else
{
originalSourcePosition = SourcePosition.NotFound;
}
string originalName = null;
if (OriginalNameIndex.HasValue)
{
try
{
result.OriginalName = names[OriginalNameIndex.Value];
originalName = names[OriginalNameIndex.Value];
}
catch (IndexOutOfRangeException e)
{
throw new IndexOutOfRangeException("Source map contains original name index that is outside the range of the provided names array" ,e);
throw new IndexOutOfRangeException("Source map contains original name index that is outside the range of the provided names array", e);
}
}
string originalFileName = null;
if (OriginalSourceFileIndex.HasValue)
{
try
{
result.OriginalFileName = sources[OriginalSourceFileIndex.Value];
originalFileName = sources[OriginalSourceFileIndex.Value];
}
catch (IndexOutOfRangeException e)
{
@ -84,6 +80,14 @@ namespace SourcemapToolkit.SourcemapParser
}
}
MappingEntry result = new MappingEntry(
generatedSourcePosition: new SourcePosition(
zeroBasedLineNumber: GeneratedLineNumber,
zeroBasedColumnNumber: GeneratedColumnNumber),
originalSourcePosition: originalSourcePosition,
originalName: originalName,
originalFileName: originalFileName);
return result;
}
}
@ -101,7 +105,8 @@ namespace SourcemapToolkit.SourcemapParser
public readonly int OriginalSourceStartingColumnBase;
public readonly int NamesListIndexBase;
public MappingsParserState(MappingsParserState previousMappingsParserState = new MappingsParserState(),
public MappingsParserState(
MappingsParserState previousMappingsParserState = new MappingsParserState(),
int? newGeneratedLineNumber = null,
int? newGeneratedColumnBase = null,
int? newSourcesListIndexBase = null,
@ -124,6 +129,8 @@ namespace SourcemapToolkit.SourcemapParser
/// </summary>
internal class MappingsListParser
{
private static readonly char[] LineDelimiter = new[] { ',' };
/// <summary>
/// Parses a single "segment" of the mapping field for a source map. A segment describes one piece of code in the generated source.
/// In the mapping string "AAaAA,CAACC;", AAaAA and CAACC are both segments. This method assumes the segments have already been decoded
@ -132,7 +139,7 @@ namespace SourcemapToolkit.SourcemapParser
/// <param name="segmentFields">The integer values for the segment fields</param>
/// <param name="mappingsParserState">The current state of the state variables for the parser</param>
/// <returns></returns>
internal NumericMappingEntry ParseSingleMappingSegment(List<int> segmentFields, MappingsParserState mappingsParserState)
internal NumericMappingEntry ParseSingleMappingSegment(IReadOnlyList<int> segmentFields, MappingsParserState mappingsParserState)
{
if (segmentFields == null)
{
@ -144,7 +151,7 @@ namespace SourcemapToolkit.SourcemapParser
throw new ArgumentOutOfRangeException(nameof(segmentFields));
}
NumericMappingEntry numericMappingEntry = new NumericMappingEntry
NumericMappingEntry numericMappingEntry = new NumericMappingEntry()
{
GeneratedLineNumber = mappingsParserState.CurrentGeneratedLineNumber,
GeneratedColumnNumber = mappingsParserState.CurrentGeneratedColumnBase + segmentFields[0]
@ -192,28 +199,34 @@ namespace SourcemapToolkit.SourcemapParser
/// Top level API that should be called for decoding the MappingsString element. It will convert the string containing Base64
/// VLQ encoded segments into a list of MappingEntries.
/// </summary>
internal List<MappingEntry> ParseMappings(string mappingString, List<string> names, List<string> sources)
internal List<MappingEntry> ParseMappings(string mappingString, IReadOnlyList<string> names, IReadOnlyList<string> sources)
{
List<MappingEntry> mappingEntries = new List<MappingEntry>();
MappingsParserState currentMappingsParserState = new MappingsParserState();
// The V3 source map format calls for all Base64 VLQ segments to be seperated by commas.
// Each line of generated code is separated using semicolons. The count of semicolons encountered gives the current line number.
string[] lines = mappingString.Split(';');
string[] lines = mappingString.SplitFast(';');
for (int lineNumber = 0; lineNumber < lines.Length; lineNumber += 1)
for (int lineNumber = 0; lineNumber < lines.Length; lineNumber++)
{
// The only value that resets when encountering a semicolon is the starting column.
currentMappingsParserState = new MappingsParserState(currentMappingsParserState, newGeneratedLineNumber:lineNumber, newGeneratedColumnBase: 0);
string[] segmentsForLine = lines[lineNumber].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
currentMappingsParserState = new MappingsParserState(
currentMappingsParserState,
newGeneratedLineNumber: lineNumber,
newGeneratedColumnBase: 0);
string[] segmentsForLine = lines[lineNumber].Split(LineDelimiter, StringSplitOptions.RemoveEmptyEntries);
foreach (string segment in segmentsForLine)
{
// Reuse the numericMappingEntry to ease GC allocations.
NumericMappingEntry numericMappingEntry = ParseSingleMappingSegment(Base64VlqDecoder.Decode(segment), currentMappingsParserState);
mappingEntries.Add(numericMappingEntry.ToMappingEntry(names, sources));
// Update the current MappingParserState based on the generated MappingEntry
currentMappingsParserState = new MappingsParserState(currentMappingsParserState,
currentMappingsParserState = new MappingsParserState(
currentMappingsParserState,
newGeneratedColumnBase: numericMappingEntry.GeneratedColumnNumber,
newSourcesListIndexBase: numericMappingEntry.OriginalSourceFileIndex,
newOriginalSourceStartingLineBase: numericMappingEntry.OriginalLineNumber,

Просмотреть файл

@ -35,4 +35,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
// Sees internals to share extension methods
[assembly: InternalsVisibleTo("SourcemapToolkit.CallstackDeminifier, PublicKey = 0024000004800000940000000602000000240000525341310004000001000100fb6b178a31a5d56c14ad5de5e8f471e4b37ef4291eb74544d7477a42ba0769e04230fada2df8722623a26b018ca2aeb66b01e5a456dc56194f0e9826837151906ca02f3158111e41c3dd770ca307825402ff75d9602929b5594fe9a91aff9093d19cb2b35313452b48ab8ef3b7a997a09ee5f8c7c5285c327a30e3f16ef422a4")]
[assembly: InternalsVisibleTo("SourcemapToolkit.SourcemapParser.UnitTests, PublicKey = 0024000004800000940000000602000000240000525341310004000001000100fb6b178a31a5d56c14ad5de5e8f471e4b37ef4291eb74544d7477a42ba0769e04230fada2df8722623a26b018ca2aeb66b01e5a456dc56194f0e9826837151906ca02f3158111e41c3dd770ca307825402ff75d9602929b5594fe9a91aff9093d19cb2b35313452b48ab8ef3b7a997a09ee5f8c7c5285c327a30e3f16ef422a4")]

Просмотреть файл

@ -2,64 +2,88 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace SourcemapToolkit.SourcemapParser
{
public class SourceMap
{
/// <summary>
/// Cache the comparer to save the allocation
/// </summary>
[JsonIgnore]
private static Comparer<MappingEntry> _comparer = Comparer<MappingEntry>.Create((a, b) => a.GeneratedSourcePosition.CompareTo(b.GeneratedSourcePosition));
/// <summary>
/// The version of the source map specification being used
/// </summary>
public int Version;
public int Version { get; }
/// <summary>
/// The name of the generated file to which this source map corresponds
/// </summary>
public string File;
public string File { get; }
/// <summary>
/// The raw, unparsed mappings entry of the soure map
/// </summary>
public string Mappings;
public string Mappings { get; }
/// <summary>
/// The list of source files that were the inputs used to generate this output file
/// </summary>
public List<string> Sources;
public IReadOnlyList<string> Sources { get; }
/// <summary>
/// A list of known original names for entries in this file
/// </summary>
public List<string> Names;
public IReadOnlyList<string> Names { get; }
/// <summary>
/// Parsed version of the mappings string that is used for getting original names and source positions
/// </summary>
public List<MappingEntry> ParsedMappings;
public IReadOnlyList<MappingEntry> ParsedMappings { get; }
/// <summary>
/// A list of content source files
/// </summary>
public List<string> SourcesContent;
public IReadOnlyList<string> SourcesContent { get; }
public SourceMap Clone()
{
return new SourceMap
{
Version = this.Version,
File = this.File,
Mappings = this.Mappings,
Sources = new List<string>(this.Sources),
Names = new List<string>(this.Names),
SourcesContent = new List<string>(this.SourcesContent),
ParsedMappings = new List<MappingEntry>(this.ParsedMappings.Select(m => m.Clone()))
};
}
public SourceMap(
int version,
string file,
string mappings,
IReadOnlyList<string> sources,
IReadOnlyList<string> names,
IReadOnlyList<MappingEntry> parsedMappings,
IReadOnlyList<string> sourcesContent)
{
Version = version;
File = file;
Mappings = mappings;
Sources = sources;
Names = names;
ParsedMappings = parsedMappings;
SourcesContent = sourcesContent;
}
public SourceMap Clone()
{
return new SourceMap(
version: Version,
file: File,
mappings: Mappings,
sources: Sources,
names: Names,
parsedMappings: ParsedMappings,
sourcesContent: SourcesContent);
}
/// <summary>
/// Applies the mappings of a sub source map to the current source map
/// Each mapping to the supplied source file is rewritten using the supplied source map
/// This is useful in situations where we have a to b to c, with mappings ba.map and cb.map
/// Calling cb.ApplySourceMap(ba) will return mappings from c to a (ca)
/// This is useful in situations where we have a to b to c, with mappings ba.map and cb.map
/// Calling cb.ApplySourceMap(ba) will return mappings from c to a (ca)
/// <param name="submap">The submap to apply</param>
/// <param name="sourceFile">The filename of the source file. If not specified, submap's File property will be used</param>
/// <returns>A new source map</returns>
@ -75,41 +99,33 @@ namespace SourcemapToolkit.SourcemapParser
{
if (submap.File == null)
{
throw new Exception("ApplySourceMap expects either the explicit source file to the map, or submap's 'file' property");
throw new Exception($"{nameof(ApplySourceMap)} expects either the explicit source file to the map, or submap's 'file' property");
}
sourceFile = submap.File;
}
SourceMap newSourceMap = new SourceMap
{
File = this.File,
Version = this.Version,
Sources = new List<string>(),
Names = new List<string>(),
SourcesContent = new List<string>(),
ParsedMappings = new List<MappingEntry>()
};
HashSet<string> sources = new HashSet<string>(StringComparer.Ordinal);
HashSet<string> names = new HashSet<string>(StringComparer.Ordinal);
List<MappingEntry> parsedMappings = new List<MappingEntry>(this.ParsedMappings.Count);
// transform mappings in this source map
foreach (MappingEntry mappingEntry in this.ParsedMappings)
{
MappingEntry newMappingEntry = mappingEntry.Clone();
MappingEntry newMappingEntry = mappingEntry;
if (mappingEntry.OriginalFileName == sourceFile && mappingEntry.OriginalSourcePosition != null)
if (mappingEntry.OriginalFileName == sourceFile && mappingEntry.OriginalSourcePosition != SourcePosition.NotFound)
{
MappingEntry correspondingSubMapMappingEntry = submap.GetMappingEntryForGeneratedSourcePosition(mappingEntry.OriginalSourcePosition);
MappingEntry? correspondingSubMapMappingEntry = submap.GetMappingEntryForGeneratedSourcePosition(mappingEntry.OriginalSourcePosition);
if (correspondingSubMapMappingEntry != null)
{
// Copy the mapping
newMappingEntry = new MappingEntry
{
GeneratedSourcePosition = mappingEntry.GeneratedSourcePosition.Clone(),
OriginalSourcePosition = correspondingSubMapMappingEntry.OriginalSourcePosition.Clone(),
OriginalName = correspondingSubMapMappingEntry.OriginalName?? mappingEntry.OriginalName,
OriginalFileName = correspondingSubMapMappingEntry.OriginalFileName?? mappingEntry.OriginalFileName
};
newMappingEntry = new MappingEntry(
generatedSourcePosition: mappingEntry.GeneratedSourcePosition,
originalSourcePosition: correspondingSubMapMappingEntry.Value.OriginalSourcePosition,
originalName: correspondingSubMapMappingEntry.Value.OriginalName ?? mappingEntry.OriginalName,
originalFileName: correspondingSubMapMappingEntry.Value.OriginalFileName ?? mappingEntry.OriginalFileName);
}
}
@ -117,55 +133,62 @@ namespace SourcemapToolkit.SourcemapParser
string originalFileName = newMappingEntry.OriginalFileName;
string originalName = newMappingEntry.OriginalName;
if (originalFileName != null && !newSourceMap.Sources.Contains(originalFileName))
if (originalFileName != null)
{
newSourceMap.Sources.Add(originalFileName);
sources.Add(originalFileName);
}
if (originalName != null && !newSourceMap.Names.Contains(originalName))
if (originalName != null)
{
newSourceMap.Names.Add(originalName);
names.Add(originalName);
}
newSourceMap.ParsedMappings.Add(newMappingEntry);
};
parsedMappings.Add(newMappingEntry);
}
SourceMap newSourceMap = new SourceMap(
version: Version,
file: File,
mappings: default,
sources: sources.ToList(),
names: names.ToList(),
parsedMappings: parsedMappings,
new List<string>());
return newSourceMap;
}
/// <summary>
/// Finds the mapping entry for the generated source position. If no exact match is found, it will attempt
/// to return a nearby mapping that should map to the same piece of code.
/// </summary>
/// <param name="generatedSourcePosition">The location in generated code for which we want to discover a mapping entry</param>
/// <returns>A mapping entry that is a close match for the desired generated code location</returns>
public virtual MappingEntry GetMappingEntryForGeneratedSourcePosition(SourcePosition generatedSourcePosition)
{
if (ParsedMappings == null)
{
return null;
}
/// <summary>
/// Finds the mapping entry for the generated source position. If no exact match is found, it will attempt
/// to return a nearby mapping that should map to the same piece of code.
/// </summary>
/// <param name="generatedSourcePosition">The location in generated code for which we want to discover a mapping entry</param>
/// <returns>A mapping entry that is a close match for the desired generated code location</returns>
public virtual MappingEntry? GetMappingEntryForGeneratedSourcePosition(SourcePosition generatedSourcePosition)
{
if (ParsedMappings == null)
{
return null;
}
MappingEntry mappingEntryToFind = new MappingEntry
{
GeneratedSourcePosition = generatedSourcePosition
};
MappingEntry mappingEntryToFind = new MappingEntry(generatedSourcePosition: generatedSourcePosition);
int index = ParsedMappings.BinarySearch(mappingEntryToFind,
Comparer<MappingEntry>.Create((a, b) => a.GeneratedSourcePosition.CompareTo(b.GeneratedSourcePosition)));
int index = ParsedMappings.BinarySearch(mappingEntryToFind, _comparer);
// If we didn't get an exact match, let's try to return the closest piece of code to the given line
if (index < 0)
{
// The BinarySearch method returns the bitwise complement of the nearest element that is larger than the desired element when there isn't a match.
// Based on tests with source maps generated with the Closure Compiler, we should consider the closest source position that is smaller than the target value when we don't have a match.
if (~index - 1 >= 0 && ParsedMappings[~index - 1].GeneratedSourcePosition.IsEqualish(generatedSourcePosition))
{
index = ~index - 1;
}
}
// If we didn't get an exact match, let's try to return the closest piece of code to the given line
if (index < 0)
{
// The BinarySearch method returns the bitwise complement of the nearest element that is larger than the desired element when there isn't a match.
// Based on tests with source maps generated with the Closure Compiler, we should consider the closest source position that is smaller than the target value when we don't have a match.
int correctIndex = ~index - 1;
return index >= 0 ? ParsedMappings[index] : null;
}
if (correctIndex >= 0 && ParsedMappings[correctIndex].GeneratedSourcePosition.IsEqualish(generatedSourcePosition))
{
index = correctIndex;
}
}
return index >= 0 ? (MappingEntry?)ParsedMappings[index] : null;
}
}
}

Просмотреть файл

@ -16,22 +16,22 @@ namespace SourcemapToolkit.SourcemapParser
/// <summary>
/// Last location of the code in the transformed code
/// </summary>
public readonly SourcePosition LastGeneratedPosition = new SourcePosition();
public SourcePosition LastGeneratedPosition { get; private set; } = default;
/// <summary>
/// Last location of the code in the source code
/// </summary>
public readonly SourcePosition LastOriginalPosition = new SourcePosition();
public SourcePosition LastOriginalPosition { get; set; } = default;
/// <summary>
/// List that contains the symbol names
/// </summary>
public readonly IList<string> Names;
public readonly IReadOnlyList<string> Names;
/// <summary>
/// List that contains the file sources
/// </summary>
public readonly IList<string> Sources;
public readonly IReadOnlyList<string> Sources;
/// <summary>
/// Index of last file source
@ -48,12 +48,26 @@ namespace SourcemapToolkit.SourcemapParser
/// </summary>
public bool IsFirstSegment { get; set; }
public MappingGenerateState(IList<string> names, IList<string> sources)
public MappingGenerateState(IReadOnlyList<string> names, IReadOnlyList<string> sources)
{
Names = names;
Sources = sources;
IsFirstSegment = true;
}
public void AdvanceLastGeneratedPositionLine()
{
LastGeneratedPosition = new SourcePosition(
zeroBasedLineNumber: LastGeneratedPosition.ZeroBasedLineNumber + 1,
zeroBasedColumnNumber: 0);
}
public void UpdateLastGeneratedPositionColumn(int zeroBasedColumnNumber)
{
LastGeneratedPosition = new SourcePosition(
zeroBasedLineNumber: LastGeneratedPosition.ZeroBasedLineNumber,
zeroBasedColumnNumber: zeroBasedColumnNumber);
}
}
public class SourceMapGenerator
@ -81,15 +95,7 @@ namespace SourcemapToolkit.SourcemapParser
throw new ArgumentNullException(nameof(sourceMap));
}
SourceMap mapToSerialize = new SourceMap()
{
File = sourceMap.File,
Names = sourceMap.Names,
Sources = sourceMap.Sources,
Version = sourceMap.Version,
SourcesContent = sourceMap.SourcesContent
};
string mappings = null;
if (sourceMap.ParsedMappings != null && sourceMap.ParsedMappings.Count > 0)
{
MappingGenerateState state = new MappingGenerateState(sourceMap.Names, sourceMap.Sources);
@ -102,9 +108,18 @@ namespace SourcemapToolkit.SourcemapParser
output.Append(';');
mapToSerialize.Mappings = output.ToString();
mappings = output.ToString();
}
SourceMap mapToSerialize = new SourceMap(
version: sourceMap.Version,
file: sourceMap.File,
mappings: mappings,
sources: sourceMap.Sources,
names: sourceMap.Names,
parsedMappings: default,
sourcesContent: sourceMap.SourcesContent);
return JsonConvert.SerializeObject(mapToSerialize,
jsonSerializerSettings ?? new JsonSerializerSettings
{
@ -126,8 +141,7 @@ namespace SourcemapToolkit.SourcemapParser
// Each line of generated code is separated using semicolons
while (entry.GeneratedSourcePosition.ZeroBasedLineNumber != state.LastGeneratedPosition.ZeroBasedLineNumber)
{
state.LastGeneratedPosition.ZeroBasedColumnNumber = 0;
state.LastGeneratedPosition.ZeroBasedLineNumber++;
state.AdvanceLastGeneratedPositionLine();
state.IsFirstSegment = true;
output.Append(';');
}
@ -163,7 +177,7 @@ namespace SourcemapToolkit.SourcemapParser
*/
Base64VlqEncoder.Encode(output, entry.GeneratedSourcePosition.ZeroBasedColumnNumber - state.LastGeneratedPosition.ZeroBasedColumnNumber);
state.LastGeneratedPosition.ZeroBasedColumnNumber = entry.GeneratedSourcePosition.ZeroBasedColumnNumber;
state.UpdateLastGeneratedPositionColumn(entry.GeneratedSourcePosition.ZeroBasedColumnNumber);
if (entry.OriginalFileName != null)
{
@ -177,10 +191,11 @@ namespace SourcemapToolkit.SourcemapParser
state.LastSourceIndex = sourceIndex;
Base64VlqEncoder.Encode(output, entry.OriginalSourcePosition.ZeroBasedLineNumber - state.LastOriginalPosition.ZeroBasedLineNumber);
state.LastOriginalPosition.ZeroBasedLineNumber = entry.OriginalSourcePosition.ZeroBasedLineNumber;
Base64VlqEncoder.Encode(output, entry.OriginalSourcePosition.ZeroBasedColumnNumber - state.LastOriginalPosition.ZeroBasedColumnNumber);
state.LastOriginalPosition.ZeroBasedColumnNumber = entry.OriginalSourcePosition.ZeroBasedColumnNumber;
state.LastOriginalPosition = new SourcePosition(
zeroBasedLineNumber: entry.OriginalSourcePosition.ZeroBasedLineNumber,
zeroBasedColumnNumber: entry.OriginalSourcePosition.ZeroBasedColumnNumber);
if (entry.OriginalName != null)
{

Просмотреть файл

@ -1,4 +1,6 @@
using System.IO;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
namespace SourcemapToolkit.SourcemapParser
@ -26,7 +28,22 @@ namespace SourcemapToolkit.SourcemapParser
JsonSerializer serializer = new JsonSerializer();
SourceMap result = serializer.Deserialize<SourceMap>(jsonTextReader);
result.ParsedMappings = _mappingsListParser.ParseMappings(result.Mappings, result.Names, result.Sources);
// Since SourceMap is immutable we need to allocate a new one and copy over all the information
List<MappingEntry> parsedMappings = _mappingsListParser.ParseMappings(result.Mappings, result.Names, result.Sources);
// Resize to free unused memory
parsedMappings.Capacity = parsedMappings.Count;
result = new SourceMap(
version: result.Version,
file: result.File,
mappings: result.Mappings,
sources: result.Sources,
names: result.Names,
parsedMappings: parsedMappings,
sourcesContent: result.SourcesContent);
sourceMapStream.Close();
return result;
}

Просмотреть файл

@ -2,44 +2,43 @@
namespace SourcemapToolkit.SourcemapParser
{
public static class SourceMapTransformer
{
/// <summary>
/// Removes column information from a source map
/// This can significantly reduce the size of source maps
/// If there is a tie between mapping entries, the first generated line takes priority
/// <returns>A new source map</returns>
/// </summary>
public static SourceMap Flatten(SourceMap sourceMap)
{
SourceMap newMap = new SourceMap
{
File = sourceMap.File,
Version = sourceMap.Version,
Mappings = sourceMap.Mappings,
Sources = sourceMap.Sources == null ? null : new List<string>(sourceMap.Sources),
SourcesContent = sourceMap.SourcesContent == null ? null : new List<string>(sourceMap.SourcesContent),
Names = sourceMap.Names == null ? null : new List<string>(sourceMap.Names),
ParsedMappings = new List<MappingEntry>()
};
public static class SourceMapTransformer
{
/// <summary>
/// Removes column information from a source map
/// This can significantly reduce the size of source maps
/// If there is a tie between mapping entries, the first generated line takes priority
/// <returns>A new source map</returns>
/// </summary>
public static SourceMap Flatten(SourceMap sourceMap)
{
HashSet<int> visitedLines = new HashSet<int>();
List<MappingEntry> parsedMappings = new List<MappingEntry>(sourceMap.ParsedMappings.Count); // assume each line will not have been visited before
HashSet<int> visitedLines = new HashSet<int>();
foreach (MappingEntry mapping in sourceMap.ParsedMappings)
{
int generatedLine = mapping.GeneratedSourcePosition.ZeroBasedLineNumber;
foreach (MappingEntry mapping in sourceMap.ParsedMappings)
{
int generatedLine = mapping.GeneratedSourcePosition.ZeroBasedLineNumber;
if (visitedLines.Add(generatedLine))
{
MappingEntry newMapping = mapping.CloneWithResetColumnNumber();
parsedMappings.Add(newMapping);
}
}
if (!visitedLines.Contains(generatedLine))
{
visitedLines.Add(generatedLine);
var newMapping = mapping.Clone();
newMapping.GeneratedSourcePosition.ZeroBasedColumnNumber = 0;
newMapping.OriginalSourcePosition.ZeroBasedColumnNumber = 0;
newMap.ParsedMappings.Add(newMapping);
}
}
// Free-up any unneeded space. This no-ops if we're already the right size.
parsedMappings.Capacity = parsedMappings.Count;
return newMap;
}
}
SourceMap newMap = new SourceMap(
version: sourceMap.Version,
file: sourceMap.File,
mappings: sourceMap.Mappings,
sources: sourceMap.Sources,
names: sourceMap.Names,
parsedMappings: parsedMappings,
sourcesContent: sourceMap.SourcesContent);
return newMap;
}
}
}

Просмотреть файл

@ -2,69 +2,93 @@
namespace SourcemapToolkit.SourcemapParser
{
/// <summary>
/// Identifies the location of a piece of code in a JavaScript file
/// </summary>
public class SourcePosition : IComparable<SourcePosition>
{
public int ZeroBasedLineNumber;
/// <summary>
/// Identifies the location of a piece of code in a JavaScript file
/// </summary>
public struct SourcePosition : IComparable<SourcePosition>, IEquatable<SourcePosition>
{
public static readonly SourcePosition NotFound = new SourcePosition(-1, -1);
public int ZeroBasedColumnNumber;
public readonly int ZeroBasedLineNumber;
public readonly int ZeroBasedColumnNumber;
public int CompareTo(SourcePosition other)
{
if (this.ZeroBasedLineNumber == other.ZeroBasedLineNumber)
{
return this.ZeroBasedColumnNumber.CompareTo(other.ZeroBasedColumnNumber);
}
public SourcePosition(int zeroBasedLineNumber, int zeroBasedColumnNumber)
{
ZeroBasedLineNumber = zeroBasedLineNumber;
ZeroBasedColumnNumber = zeroBasedColumnNumber;
}
return this.ZeroBasedLineNumber.CompareTo(other.ZeroBasedLineNumber);
}
public int CompareTo(SourcePosition other)
{
if (this.ZeroBasedLineNumber == other.ZeroBasedLineNumber)
{
return this.ZeroBasedColumnNumber.CompareTo(other.ZeroBasedColumnNumber);
}
public static bool operator <(SourcePosition x, SourcePosition y)
{
return x.CompareTo(y) < 0;
}
return this.ZeroBasedLineNumber.CompareTo(other.ZeroBasedLineNumber);
}
public static bool operator >(SourcePosition x, SourcePosition y)
{
return x.CompareTo(y) > 0;
}
public static bool operator <(SourcePosition x, SourcePosition y)
{
return x.CompareTo(y) < 0;
}
/// <summary>
/// Returns true if we think that the two source positions are close enough together that they may in fact be the referring to the same piece of code.
/// </summary>
public bool IsEqualish(SourcePosition other)
{
// If the column numbers differ by 1, we can say that the source code is approximately equal
if (this.ZeroBasedLineNumber == other.ZeroBasedLineNumber && Math.Abs(this.ZeroBasedColumnNumber - other.ZeroBasedColumnNumber) <= 1)
{
return true;
}
public static bool operator >(SourcePosition x, SourcePosition y)
{
return x.CompareTo(y) > 0;
}
// This handles the case where we are comparing code at the end of one line and the beginning of the next line.
// If the column number on one of the entries is zero, it is ok for the line numbers to differ by 1, so long as the one that has a column number of zero is the one with the larger line number.
// Since we don't have the number of columns in each line, we can't know for sure if these two pieces of code are actually near each other. This is an optimistic guess.
if (Math.Abs(this.ZeroBasedLineNumber - other.ZeroBasedLineNumber) == 1)
{
SourcePosition largerLineNumber = this.ZeroBasedLineNumber > other.ZeroBasedLineNumber ? this : other;
public static bool operator ==(SourcePosition x, SourcePosition y)
{
return x.Equals(y);
}
if (largerLineNumber.ZeroBasedColumnNumber == 0)
{
return true;
}
}
public static bool operator !=(SourcePosition x, SourcePosition y)
{
return !x.Equals(y);
}
return false;
}
public bool Equals(SourcePosition that)
{
return this.ZeroBasedLineNumber == that.ZeroBasedLineNumber
&& this.ZeroBasedColumnNumber == that.ZeroBasedColumnNumber;
}
public SourcePosition Clone()
{
return new SourcePosition
{
ZeroBasedLineNumber = this.ZeroBasedLineNumber,
ZeroBasedColumnNumber = this.ZeroBasedColumnNumber
};
}
}
public override bool Equals(object obj)
{
return (obj is SourcePosition otherSourcePosition) ? Equals(otherSourcePosition) : false;
}
public override int GetHashCode()
{
return ZeroBasedColumnNumber.GetHashCode() ^ ZeroBasedColumnNumber.GetHashCode();
}
/// <summary>
/// Returns true if we think that the two source positions are close enough together that they may in fact be the referring to the same piece of code.
/// </summary>
public bool IsEqualish(SourcePosition other)
{
// If the column numbers differ by 1, we can say that the source code is approximately equal
if (this.ZeroBasedLineNumber == other.ZeroBasedLineNumber && Math.Abs(this.ZeroBasedColumnNumber - other.ZeroBasedColumnNumber) <= 1)
{
return true;
}
// This handles the case where we are comparing code at the end of one line and the beginning of the next line.
// If the column number on one of the entries is zero, it is ok for the line numbers to differ by 1, so long as the one that has a column number of zero is the one with the larger line number.
// Since we don't have the number of columns in each line, we can't know for sure if these two pieces of code are actually near each other. This is an optimistic guess.
if (Math.Abs(this.ZeroBasedLineNumber - other.ZeroBasedLineNumber) == 1)
{
SourcePosition largerLineNumber = this.ZeroBasedLineNumber > other.ZeroBasedLineNumber ? this : other;
if (largerLineNumber.ZeroBasedColumnNumber == 0)
{
return true;
}
}
return false;
}
}
}

Просмотреть файл

@ -55,6 +55,7 @@
<Compile Include="Base64VlqConstants.cs" />
<Compile Include="Base64VlqDecoder.cs" />
<Compile Include="Base64VlqEncoder.cs" />
<Compile Include="IReadOnlyListExtensions.cs" />
<Compile Include="MappingEntry.cs" />
<Compile Include="MappingListParser.cs" />
<Compile Include="SourceMap.cs" />
@ -63,6 +64,7 @@
<Compile Include="SourceMapParser.cs" />
<Compile Include="SourceMapTransformer.cs" />
<Compile Include="SourcePosition.cs" />
<Compile Include="StringExtensions.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

Просмотреть файл

@ -0,0 +1,40 @@
namespace SourcemapToolkit.SourcemapParser
{
internal static class StringExtensions
{
/// <summary>
/// String.Split() allocates an O(input.Length) int array, and
/// is surprisingly expensive. For most cases this implementation
/// is faster and does fewer allocations.
/// </summary>
public static string[] SplitFast(this string input, char delimiter)
{
int segmentCount = 1;
for (int i = 0; i < input.Length; i++)
{
if (input[i] == delimiter)
{
segmentCount++;
}
}
int segmentId = 0;
int prevDelimiter = 0;
string[] segments = new string[segmentCount];
for (int i = 0; i < input.Length; i++)
{
if (input[i] == delimiter)
{
segments[segmentId] = input.Substring(prevDelimiter, i - prevDelimiter);
segmentId++;
prevDelimiter = i + 1;
}
}
segments[segmentId] = input.Substring(prevDelimiter);
return segments;
}
}
}

Просмотреть файл

@ -11,11 +11,9 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
public void GetWrappingFunctionForSourceLocation_EmptyFunctionMap_ReturnNull()
{
// Arrange
SourcePosition sourcePosition = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 3
};
SourcePosition sourcePosition = new SourcePosition(
zeroBasedLineNumber: 2,
zeroBasedColumnNumber: 3);
List<FunctionMapEntry> functionMap = new List<FunctionMapEntry>();
IFunctionMapConsumer functionMapConsumer = new FunctionMapConsumer();
@ -30,18 +28,17 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
public void GetWrappingFunctionForSourceLocation_SingleIrrelevantFunctionMapEntry_ReturnNull()
{
// Arrange
SourcePosition sourcePosition = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 3
};
SourcePosition sourcePosition = new SourcePosition(
zeroBasedLineNumber: 2,
zeroBasedColumnNumber: 3);
List<FunctionMapEntry> functionMap = new List<FunctionMapEntry>
{
new FunctionMapEntry
{
StartSourcePosition = new SourcePosition {ZeroBasedLineNumber = 40, ZeroBasedColumnNumber = 10},
EndSourcePosition = new SourcePosition {ZeroBasedLineNumber = 50, ZeroBasedColumnNumber = 10}
}
new FunctionMapEntry(
bindings: default,
deminifiedMethodName: default,
startSourcePosition: new SourcePosition(zeroBasedLineNumber: 40, zeroBasedColumnNumber: 10),
endSourcePosition: new SourcePosition(zeroBasedLineNumber: 50, zeroBasedColumnNumber: 10))
};
IFunctionMapConsumer functionMapConsumer = new FunctionMapConsumer();
@ -56,16 +53,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
public void GetWrappingFunctionForSourceLocation_SingleRelevantFunctionMapEntry_ReturnWrappingFunction()
{
// Arrange
SourcePosition sourcePosition = new SourcePosition
{
ZeroBasedLineNumber = 41,
ZeroBasedColumnNumber = 2
};
FunctionMapEntry functionMapEntry = new FunctionMapEntry
{
StartSourcePosition = new SourcePosition { ZeroBasedLineNumber = 40, ZeroBasedColumnNumber = 10 },
EndSourcePosition = new SourcePosition { ZeroBasedLineNumber = 50, ZeroBasedColumnNumber = 10 }
};
SourcePosition sourcePosition = new SourcePosition(
zeroBasedLineNumber: 41,
zeroBasedColumnNumber: 2);
FunctionMapEntry functionMapEntry = new FunctionMapEntry(
bindings: default,
deminifiedMethodName: default,
startSourcePosition: new SourcePosition(zeroBasedLineNumber: 40, zeroBasedColumnNumber: 10),
endSourcePosition: new SourcePosition(zeroBasedLineNumber: 50, zeroBasedColumnNumber: 10));
List<FunctionMapEntry> functionMap = new List<FunctionMapEntry>
{
functionMapEntry
@ -83,21 +78,19 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
public void GetWrappingFunctionForSourceLocation_MultipleFunctionMapEntriesSingleRelevantFunctionMapEntry_ReturnWrappingFunction()
{
// Arrange
SourcePosition sourcePosition = new SourcePosition
{
ZeroBasedLineNumber = 31,
ZeroBasedColumnNumber = 0
};
FunctionMapEntry functionMapEntry = new FunctionMapEntry
{
StartSourcePosition = new SourcePosition { ZeroBasedLineNumber = 10, ZeroBasedColumnNumber = 10 },
EndSourcePosition = new SourcePosition { ZeroBasedLineNumber = 20, ZeroBasedColumnNumber = 30 }
};
FunctionMapEntry functionMapEntry2 = new FunctionMapEntry
{
StartSourcePosition = new SourcePosition { ZeroBasedLineNumber = 30, ZeroBasedColumnNumber = 0 },
EndSourcePosition = new SourcePosition { ZeroBasedLineNumber = 40, ZeroBasedColumnNumber = 2 }
};
SourcePosition sourcePosition = new SourcePosition(
zeroBasedLineNumber: 31,
zeroBasedColumnNumber: 0);
FunctionMapEntry functionMapEntry = new FunctionMapEntry(
bindings: default,
deminifiedMethodName: default,
startSourcePosition: new SourcePosition(zeroBasedLineNumber: 10, zeroBasedColumnNumber: 10),
endSourcePosition: new SourcePosition(zeroBasedLineNumber: 20, zeroBasedColumnNumber: 30));
FunctionMapEntry functionMapEntry2 = new FunctionMapEntry(
bindings: default,
deminifiedMethodName: default,
startSourcePosition: new SourcePosition(zeroBasedLineNumber: 30, zeroBasedColumnNumber: 0),
endSourcePosition: new SourcePosition(zeroBasedLineNumber: 40, zeroBasedColumnNumber: 2));
List<FunctionMapEntry> functionMap = new List<FunctionMapEntry>
{
functionMapEntry,
@ -116,21 +109,19 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
public void GetWrappingFunctionForSourceLocation_MultipleFunctionMapEntriesMultipleRelevantFunctionMapEntry_ReturnClosestWrappingFunction()
{
// Arrange
SourcePosition sourcePosition = new SourcePosition
{
ZeroBasedLineNumber = 10,
ZeroBasedColumnNumber = 25
};
FunctionMapEntry functionMapEntry = new FunctionMapEntry
{
StartSourcePosition = new SourcePosition { ZeroBasedLineNumber = 5, ZeroBasedColumnNumber = 10 },
EndSourcePosition = new SourcePosition { ZeroBasedLineNumber = 20, ZeroBasedColumnNumber = 30 }
};
FunctionMapEntry functionMapEntry2 = new FunctionMapEntry
{
StartSourcePosition = new SourcePosition { ZeroBasedLineNumber = 9, ZeroBasedColumnNumber = 0 },
EndSourcePosition = new SourcePosition { ZeroBasedLineNumber = 15, ZeroBasedColumnNumber = 2 }
};
SourcePosition sourcePosition = new SourcePosition(
zeroBasedLineNumber: 10,
zeroBasedColumnNumber: 25);
FunctionMapEntry functionMapEntry = new FunctionMapEntry(
bindings: default,
deminifiedMethodName: default,
startSourcePosition: new SourcePosition(zeroBasedLineNumber: 5, zeroBasedColumnNumber: 10),
endSourcePosition: new SourcePosition(zeroBasedLineNumber: 20, zeroBasedColumnNumber: 30));
FunctionMapEntry functionMapEntry2 = new FunctionMapEntry(
bindings: default,
deminifiedMethodName: default,
startSourcePosition: new SourcePosition(zeroBasedLineNumber: 9, zeroBasedColumnNumber: 0),
endSourcePosition: new SourcePosition(zeroBasedLineNumber: 15, zeroBasedColumnNumber: 2));
List<FunctionMapEntry> functionMap = new List<FunctionMapEntry>
{
functionMapEntry2,

Просмотреть файл

@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using Xunit;
using SourcemapToolkit.SourcemapParser.UnitTests;
using Rhino.Mocks;
using SourcemapToolkit.SourcemapParser;
using SourcemapToolkit.SourcemapParser.UnitTests;
namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
@ -18,7 +21,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
string sourceCode = "";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode), null);
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode), null);
// Assert
Assert.Null(functionMap);
@ -30,9 +33,10 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(null);
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(null, sourceMap);
// Assert
Assert.Null(functionMap);
@ -44,12 +48,13 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "bar();";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(0, functionMap.Count);
Assert.Empty(functionMap);
}
[Fact]
@ -58,12 +63,13 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "function foo(){bar();}";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(1, functionMap.Count);
Assert.Single(functionMap);
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.ZeroBasedLineNumber);
Assert.Equal(9, functionMap[0].Bindings[0].SourcePosition.ZeroBasedColumnNumber);
@ -80,12 +86,13 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "function foo()" + Environment.NewLine + "{" + Environment.NewLine + "bar();" +
Environment.NewLine + "}";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(1, functionMap.Count);
Assert.Single(functionMap);
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.ZeroBasedLineNumber);
Assert.Equal(9, functionMap[0].Bindings[0].SourcePosition.ZeroBasedColumnNumber);
@ -101,9 +108,10 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "function foo(){bar();}function bar(){baz();}";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(2, functionMap.Count);
@ -131,9 +139,10 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "function foo(){function bar(){baz();}}";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(2, functionMap.Count);
@ -161,12 +170,13 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){bar();}";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(1, functionMap.Count);
Assert.Single(functionMap);
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.ZeroBasedLineNumber);
@ -183,9 +193,10 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){};foo.bar = function() { baz(); }";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(2, functionMap.Count);
@ -214,9 +225,10 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){} foo.prototype.bar = function () { baz(); }";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(2, functionMap.Count);
@ -245,9 +257,10 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){} foo.prototype = { bar: function () { baz(); } }";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(2, functionMap.Count);
@ -278,12 +291,13 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function myCoolFunctionName(){ bar(); }";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(1, functionMap.Count);
Assert.Single(functionMap);
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.ZeroBasedLineNumber);
@ -300,9 +314,10 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){};foo.bar = function myCoolFunctionName() { baz(); }";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(2, functionMap.Count);
@ -331,9 +346,10 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){} foo.prototype.bar = function myCoolFunctionName() { baz(); } }";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(2, functionMap.Count);
@ -362,9 +378,10 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){} foo.prototype = { bar: function myCoolFunctionName() { baz(); } }";
SourceMap sourceMap = CreateSourceMapMock();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
IReadOnlyList<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode), sourceMap);
// Assert
Assert.Equal(2, functionMap.Count);
@ -389,193 +406,16 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
Assert.Equal(22, functionMap[1].EndSourcePosition.ZeroBasedColumnNumber);
}
[Fact]
public void GetDeminifiedMethodNameFromSourceMap_NullFunctionMapEntry_ThrowsException()
private static SourceMap CreateSourceMapMock()
{
// Arrange
FunctionMapEntry functionMapEntry = null;
SourceMap sourceMap = MockRepository.GenerateStub<SourceMap>();
// Act
Assert.Throws<ArgumentNullException>( ()=> FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap));
}
[Fact]
public void GetDeminifiedMethodNameFromSourceMap_NullSourceMap_ThrowsException()
{
// Arrange
FunctionMapEntry functionMapEntry = new FunctionMapEntry();
SourceMap sourceMap = null;
// Act
Assert.Throws<ArgumentNullException>( ()=> FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap));
}
[Fact]
public void GetDeminifiedMethodNameFromSourceMap_NoBinding_ReturnNullMethodName()
{
// Arrange
FunctionMapEntry functionMapEntry = new FunctionMapEntry();
SourceMap sourceMap = MockRepository.GenerateStub<SourceMap>();
// Act
string result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
// Assert
Assert.Null(result);
sourceMap.VerifyAllExpectations();
}
[Fact]
public void GetDeminifiedMethodNameFromSourceMap_HasSingleBindingNoMatchingMapping_ReturnNullMethodName()
{
// Arrange
FunctionMapEntry functionMapEntry = new FunctionMapEntry
{
Bindings =
new List<BindingInformation>
{
new BindingInformation
{
SourcePosition = new SourcePosition {ZeroBasedLineNumber = 20, ZeroBasedColumnNumber = 15}
}
}
};
SourceMap sourceMap = MockRepository.GenerateStub<SourceMap>();
sourceMap.Stub(x => x.GetMappingEntryForGeneratedSourcePosition(Arg<SourcePosition>.Is.Anything)).Return(null);
// Act
string result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
// Assert
Assert.Null(result);
sourceMap.VerifyAllExpectations();
}
[Fact]
public void GetDeminifiedMethodNameFromSourceMap_HasSingleBindingMatchingMapping_ReturnsMethodName()
{
// Arrange
FunctionMapEntry functionMapEntry = new FunctionMapEntry
{
Bindings =
new List<BindingInformation>
{
new BindingInformation
{
SourcePosition = new SourcePosition {ZeroBasedLineNumber = 5, ZeroBasedColumnNumber = 8}
}
}
};
SourceMap sourceMap = MockRepository.GenerateStub<SourceMap>();
sourceMap.Stub(
x =>
x.GetMappingEntryForGeneratedSourcePosition(
Arg<SourcePosition>.Matches(y => y.ZeroBasedLineNumber == 5 && y.ZeroBasedColumnNumber == 8)))
.Return(new MappingEntry
{
OriginalName = "foo",
});
// Act
string result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
// Assert
Assert.Equal("foo", result);
sourceMap.VerifyAllExpectations();
}
[Fact]
public void GetDeminifiedMethodNameFromSourceMap_MatchingMappingMultipleBindingsMissingPrototypeMapping_ReturnsMethodName()
{
// Arrange
FunctionMapEntry functionMapEntry = new FunctionMapEntry
{
Bindings =
new List<BindingInformation>
{
new BindingInformation
{
SourcePosition = new SourcePosition {ZeroBasedLineNumber = 86, ZeroBasedColumnNumber = 52}
},
new BindingInformation
{
SourcePosition = new SourcePosition {ZeroBasedLineNumber = 88, ZeroBasedColumnNumber = 78}
}
}
};
SourceMap sourceMap = MockRepository.GenerateStub<SourceMap>();
sourceMap.Stub(
x =>
x.GetMappingEntryForGeneratedSourcePosition(
Arg<SourcePosition>.Matches(y => y.ZeroBasedLineNumber == 86 && y.ZeroBasedColumnNumber == 52)))
.Return(null);
sourceMap.Stub(
x =>
x.GetMappingEntryForGeneratedSourcePosition(
Arg<SourcePosition>.Matches(y => y.ZeroBasedLineNumber == 88 && y.ZeroBasedColumnNumber == 78)))
.Return(new MappingEntry
{
OriginalName = "baz",
});
// Act
string result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
// Assert
Assert.Equal("baz", result);
sourceMap.VerifyAllExpectations();
}
[Fact]
public void GetDeminifiedMethodNameFromSourceMap_MatchingMappingMultipleBindings_ReturnsMethodNameWithFullBinding()
{
// Arrange
FunctionMapEntry functionMapEntry = new FunctionMapEntry
{
Bindings =
new List<BindingInformation>
{
new BindingInformation
{
SourcePosition = new SourcePosition {ZeroBasedLineNumber = 5, ZeroBasedColumnNumber = 5}
},
new BindingInformation
{
SourcePosition = new SourcePosition {ZeroBasedLineNumber = 20, ZeroBasedColumnNumber = 10}
}
}
};
SourceMap sourceMap = MockRepository.GenerateStub<SourceMap>();
sourceMap.Stub(
x =>
x.GetMappingEntryForGeneratedSourcePosition(
Arg<SourcePosition>.Matches(y => y.ZeroBasedLineNumber == 5 && y.ZeroBasedColumnNumber == 5)))
.Return(new MappingEntry
{
OriginalName = "bar"
});
sourceMap.Stub(
x =>
x.GetMappingEntryForGeneratedSourcePosition(
Arg<SourcePosition>.Matches(y => y.ZeroBasedLineNumber == 20 && y.ZeroBasedColumnNumber == 10)))
.Return(new MappingEntry
{
OriginalName = "baz",
});
// Act
string result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
// Assert
Assert.Equal("bar.baz", result);
sourceMap.VerifyAllExpectations();
return MockRepository.GenerateStub<SourceMap>(
0 /* version */,
default(string) /* file */,
default(string) /* mappings */,
default(IReadOnlyList<string>) /* sources */,
default(IReadOnlyList<string>) /* names */,
default(IReadOnlyList<MappingEntry>) /* parsedMappings */,
default(IReadOnlyList<string>) /* sourcesContent */);
}
}
}

Просмотреть файл

@ -53,7 +53,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
string result = keyValueCache.GetValue("bar");
// Assert
Assert.Equal(null, result);
Assert.Null(result);
valueGetter.VerifyAllExpectations();
}

Просмотреть файл

@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using Xunit;
using Rhino.Mocks;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
public class SourceMapExtensionsUnitTests
{
[Fact]
public void GetDeminifiedMethodName_NullBindings_ReturnsNull()
{
// Arrange
IReadOnlyList<BindingInformation> bindings = null;
SourceMap sourceMap = CreateSourceMapMock();
// Act
string deminifiedMethodName = SourceMapExtensions.GetDeminifiedMethodName(sourceMap, bindings);
// Assert
Assert.Null(deminifiedMethodName);
}
[Fact]
public void GetDeminifiedMethodName_NullSourceMap_ThrowsException()
{
// Arrange
IReadOnlyList<BindingInformation> bindings = null;
SourceMap sourceMap = null;
// Act
Assert.Throws<ArgumentNullException>(() => SourceMapExtensions.GetDeminifiedMethodName(sourceMap, bindings));
}
[Fact]
public void GetDeminifiedMethodName_EmptyBinding_ReturnNullMethodName()
{
// Arrange
IReadOnlyList<BindingInformation> bindings = new List<BindingInformation>();
SourceMap sourceMap = CreateSourceMapMock();
// Act
string result = SourceMapExtensions.GetDeminifiedMethodName(sourceMap, bindings);
// Assert
Assert.Null(result);
sourceMap.VerifyAllExpectations();
}
[Fact]
public void GetDeminifiedMethodName_HasSingleBindingNoMatchingMapping_ReturnNullMethodName()
{
// Arrange
List<BindingInformation> bindings = new List<BindingInformation>()
{
new BindingInformation(
name: default,
sourcePosition: new SourcePosition(zeroBasedLineNumber: 20, zeroBasedColumnNumber: 15))
};
SourceMap sourceMap = CreateSourceMapMock();
sourceMap.Stub(x => x.GetMappingEntryForGeneratedSourcePosition(Arg<SourcePosition>.Is.Anything)).Return(null);
// Act
string result = SourceMapExtensions.GetDeminifiedMethodName(sourceMap, bindings);
// Assert
Assert.Null(result);
sourceMap.VerifyAllExpectations();
}
[Fact]
public void GetDeminifiedMethodName_HasSingleBindingMatchingMapping_ReturnsMethodName()
{
// Arrange
List<BindingInformation> bindings = new List<BindingInformation>()
{
new BindingInformation(
name: default,
sourcePosition: new SourcePosition(zeroBasedLineNumber: 5, zeroBasedColumnNumber: 8))
};
SourceMap sourceMap = CreateSourceMapMock();
sourceMap.Stub(
x =>
x.GetMappingEntryForGeneratedSourcePosition(
Arg<SourcePosition>.Matches(y => y.ZeroBasedLineNumber == 5 && y.ZeroBasedColumnNumber == 8)))
.Return(new MappingEntry(generatedSourcePosition: default, originalName: "foo"));
// Act
string result = SourceMapExtensions.GetDeminifiedMethodName(sourceMap, bindings);
// Assert
Assert.Equal("foo", result);
sourceMap.VerifyAllExpectations();
}
[Fact]
public void GetDeminifiedMethodName_MatchingMappingMultipleBindingsMissingPrototypeMapping_ReturnsMethodName()
{
// Arrange
List<BindingInformation> bindings = new List<BindingInformation>
{
new BindingInformation(
name: default,
sourcePosition: new SourcePosition(zeroBasedLineNumber: 86, zeroBasedColumnNumber: 52)),
new BindingInformation(
name: default,
sourcePosition: new SourcePosition(zeroBasedLineNumber: 88, zeroBasedColumnNumber: 78))
};
SourceMap sourceMap = CreateSourceMapMock();
sourceMap.Stub(
x =>
x.GetMappingEntryForGeneratedSourcePosition(
Arg<SourcePosition>.Matches(y => y.ZeroBasedLineNumber == 86 && y.ZeroBasedColumnNumber == 52)))
.Return(null);
sourceMap.Stub(
x =>
x.GetMappingEntryForGeneratedSourcePosition(
Arg<SourcePosition>.Matches(y => y.ZeroBasedLineNumber == 88 && y.ZeroBasedColumnNumber == 78)))
.Return(new MappingEntry(generatedSourcePosition: default, originalName: "baz"));
// Act
string result = SourceMapExtensions.GetDeminifiedMethodName(sourceMap, bindings);
// Assert
Assert.Equal("baz", result);
sourceMap.VerifyAllExpectations();
}
[Fact]
public void GetDeminifiedMethodName_MatchingMappingMultipleBindings_ReturnsMethodNameWithFullBinding()
{
// Arrange
List<BindingInformation> bindings = new List<BindingInformation>
{
new BindingInformation(
name: default,
sourcePosition: new SourcePosition(zeroBasedLineNumber: 5, zeroBasedColumnNumber: 5)),
new BindingInformation(
name: default,
sourcePosition: new SourcePosition(zeroBasedLineNumber: 20, zeroBasedColumnNumber: 10))
};
SourceMap sourceMap = CreateSourceMapMock();
sourceMap.Stub(
x =>
x.GetMappingEntryForGeneratedSourcePosition(
Arg<SourcePosition>.Matches(y => y.ZeroBasedLineNumber == 5 && y.ZeroBasedColumnNumber == 5)))
.Return(new MappingEntry(generatedSourcePosition: default, originalName: "bar"));
sourceMap.Stub(
x =>
x.GetMappingEntryForGeneratedSourcePosition(
Arg<SourcePosition>.Matches(y => y.ZeroBasedLineNumber == 20 && y.ZeroBasedColumnNumber == 10)))
.Return(new MappingEntry(generatedSourcePosition: default, originalName: "baz"));
// Act
string result = SourceMapExtensions.GetDeminifiedMethodName(sourceMap, bindings);
// Assert
Assert.Equal("bar.baz", result);
sourceMap.VerifyAllExpectations();
}
private static SourceMap CreateSourceMapMock()
{
return MockRepository.GenerateStub<SourceMap>(
0 /* version */,
default(string) /* file */,
default(string) /* mappings */,
default(IReadOnlyList<string>) /* sources */,
default(IReadOnlyList<string>) /* names */,
default(IReadOnlyList<MappingEntry>) /* parsedMappings */,
default(IReadOnlyList<string>) /* sourcesContent */);
}
}
}

Просмотреть файл

@ -95,6 +95,7 @@
<Compile Include="FunctionMapConsumerUnitTests.cs" />
<Compile Include="FunctionMapGeneratorUnitTests.cs" />
<Compile Include="KeyValueCacheUnitTests.cs" />
<Compile Include="SourceMapExtensionsUnitTests.cs" />
<Compile Include="StackFrameDeminifierUnitTests.cs" />
<Compile Include="StackTraceDeminifierClosureEndToEndTests.cs" />
<Compile Include="StackTraceDeminifierMapOnlyEndToEndTests.cs" />

Просмотреть файл

@ -3,6 +3,7 @@ using System.Collections.Generic;
using Xunit;
using Rhino.Mocks;
using SourcemapToolkit.SourcemapParser;
using System.Linq.Expressions;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
namespace SourcemapToolkit.CallstackDeminifier.UnitTests
@ -45,7 +46,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
StackFrame stackFrame = null;
// Act
Assert.Throws<ArgumentNullException>( ()=> stackFrameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName: null));
Assert.Throws<ArgumentNullException>(() => stackFrameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName: null));
}
[Fact]
@ -60,7 +61,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Assert
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@ -82,7 +83,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Assert
Assert.Equal(DeminificationError.NoSourceCodeProvided, stackFrameDeminification.DeminificationError);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@ -107,7 +108,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Assert
Assert.Equal(DeminificationError.NoWrapingFunctionFound, stackFrameDeminification.DeminificationError);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@ -116,7 +117,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
// Arrange
string filePath = "foo";
FunctionMapEntry wrapingFunctionMapEntry = new FunctionMapEntry {DeminfifiedMethodName = "DeminifiedFoo"};
FunctionMapEntry wrapingFunctionMapEntry = CreateFunctionMapEntry(deminifiedMethodName: "DeminifiedFoo");
StackFrame stackFrame = new StackFrame { FilePath = filePath };
IFunctionMapStore functionMapStore = MockRepository.GenerateStub<IFunctionMapStore>();
functionMapStore.Stub(c => c.GetFunctionMapForSourceCode(filePath))
@ -132,8 +133,8 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Assert
Assert.Equal(DeminificationError.None, stackFrameDeminification.DeminificationError);
Assert.Equal(wrapingFunctionMapEntry.DeminfifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Equal(wrapingFunctionMapEntry.DeminifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@ -143,7 +144,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
// Arrange
string filePath = "foo";
FunctionMapEntry wrapingFunctionMapEntry = new FunctionMapEntry { DeminfifiedMethodName = "DeminifiedFoo" };
FunctionMapEntry wrapingFunctionMapEntry = CreateFunctionMapEntry(deminifiedMethodName: "DeminifiedFoo");
StackFrame stackFrame = new StackFrame { FilePath = filePath };
IFunctionMapStore functionMapStore = MockRepository.GenerateStub<IFunctionMapStore>();
functionMapStore.Stub(c => c.GetFunctionMapForSourceCode(filePath))
@ -159,8 +160,8 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Assert
Assert.Equal(DeminificationError.NoSourceMap, stackFrameDeminification.DeminificationError);
Assert.Equal(wrapingFunctionMapEntry.DeminfifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Equal(wrapingFunctionMapEntry.DeminifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@ -169,7 +170,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
// Arrange
string filePath = "foo";
FunctionMapEntry wrapingFunctionMapEntry = new FunctionMapEntry { DeminfifiedMethodName = "DeminifiedFoo" };
FunctionMapEntry wrapingFunctionMapEntry = CreateFunctionMapEntry(deminifiedMethodName: "DeminifiedFoo");
StackFrame stackFrame = new StackFrame { FilePath = filePath };
IFunctionMapStore functionMapStore = MockRepository.GenerateStub<IFunctionMapStore>();
functionMapStore.Stub(c => c.GetFunctionMapForSourceCode(filePath))
@ -178,7 +179,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
functionMapConsumer.Stub(c => c.GetWrappingFunctionForSourceLocation(Arg<SourcePosition>.Is.Anything, Arg<List<FunctionMapEntry>>.Is.Anything))
.Return(wrapingFunctionMapEntry);
ISourceMapStore sourceMapStore = MockRepository.GenerateStub<ISourceMapStore>();
sourceMapStore.Stub(c => c.GetSourceMapForUrl(Arg<string>.Is.Anything)).Return(new SourceMap());
sourceMapStore.Stub(c => c.GetSourceMapForUrl(Arg<string>.Is.Anything)).Return(CreateSourceMap());
IStackFrameDeminifier stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies(sourceMapStore: sourceMapStore,functionMapStore: functionMapStore, functionMapConsumer: functionMapConsumer);
@ -187,8 +188,8 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Assert
Assert.Equal(DeminificationError.SourceMapFailedToParse, stackFrameDeminification.DeminificationError);
Assert.Equal(wrapingFunctionMapEntry.DeminfifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Equal(wrapingFunctionMapEntry.DeminifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@ -197,13 +198,13 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
// Arrange
string filePath = "foo";
FunctionMapEntry wrapingFunctionMapEntry = new FunctionMapEntry { DeminfifiedMethodName = "DeminifiedFoo" };
FunctionMapEntry wrapingFunctionMapEntry = CreateFunctionMapEntry(deminifiedMethodName: "DeminifiedFoo");
StackFrame stackFrame = new StackFrame { FilePath = filePath };
IFunctionMapStore functionMapStore = MockRepository.GenerateStub<IFunctionMapStore>();
functionMapStore.Stub(c => c.GetFunctionMapForSourceCode(filePath))
.Return(new List<FunctionMapEntry>());
ISourceMapStore sourceMapStore = MockRepository.GenerateStub<ISourceMapStore>();
SourceMap sourceMap = new SourceMap() {ParsedMappings = new List<MappingEntry>()};
SourceMap sourceMap = CreateSourceMap(parsedMappings: new List<MappingEntry>());
sourceMapStore.Stub(c => c.GetSourceMapForUrl(Arg<string>.Is.Anything)).Return(sourceMap);
IFunctionMapConsumer functionMapConsumer = MockRepository.GenerateStub<IFunctionMapConsumer>();
@ -217,10 +218,30 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
// Assert
Assert.Equal(DeminificationError.NoMatchingMapingInSourceMap, stackFrameDeminification.DeminificationError);
Assert.Equal(wrapingFunctionMapEntry.DeminfifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Equal(wrapingFunctionMapEntry.DeminifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
private static FunctionMapEntry CreateFunctionMapEntry(string deminifiedMethodName)
{
return new FunctionMapEntry(
bindings: default,
deminifiedMethodName,
startSourcePosition: default,
endSourcePosition: default);
}
private static SourceMap CreateSourceMap(List<MappingEntry> parsedMappings = default)
{
return new SourceMap(
version: default,
file: default,
mappings: default,
sources: default,
names: default,
parsedMappings: parsedMappings,
sourcesContent: default);
}
}
}

Просмотреть файл

@ -30,10 +30,10 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
at HTMLButtonElement.<anonymous> (http://localhost:19220/crashcauser.min.js:1:332)";
// Act
List<StackFrame> stackTrace = stackTraceParser.ParseStackTrace(browserStackTrace);
var stackTrace = stackTraceParser.ParseStackTrace(browserStackTrace);
// Assert
Assert.Equal(stackTrace.Count, 4);
Assert.Equal(4, stackTrace.Count);
}
[Fact]
@ -47,10 +47,10 @@ b@http://localhost:19220/crashcauser.min.js:1:14
window.onload/<@http://localhost:19220/crashcauser.min.js:1:332";
// Act
List<StackFrame> stackTrace = stackTraceParser.ParseStackTrace(browserStackTrace);
var stackTrace = stackTraceParser.ParseStackTrace(browserStackTrace);
// Assert
Assert.Equal(stackTrace.Count, 4);
Assert.Equal(4, stackTrace.Count);
}
[Fact]
@ -64,10 +64,10 @@ window.onload/<@http://localhost:19220/crashcauser.min.js:1:332";
at b (http://localhost:19220/crashcauser.min.js:1:14)";
// Act
List<StackFrame> stackTrace = stackTraceParser.ParseStackTrace(browserStackTrace);
var stackTrace = stackTraceParser.ParseStackTrace(browserStackTrace);
// Assert
Assert.Equal(stackTrace.Count, 3);
Assert.Equal(3, stackTrace.Count);
}
[Fact]
@ -78,7 +78,7 @@ window.onload/<@http://localhost:19220/crashcauser.min.js:1:332";
string frame = null;
// Act
Assert.Throws<ArgumentNullException>( ()=> stackTraceParser.TryParseSingleStackFrame(frame));
Assert.Throws<ArgumentNullException>(() => stackTraceParser.TryParseSingleStackFrame(frame));
}
[Fact]

Просмотреть файл

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using Xunit;
namespace SourcemapToolkit.SourcemapParser.UnitTests
{
public class IReadOnlyListExtensionsUnitTests
{
[Fact]
public void IndexOf_NullList_ThrowsNullReferenceException()
{
// Arrange, Act, and Assert
Assert.Throws<NullReferenceException>(() => IReadOnlyListExtensions.IndexOf(null, 1));
}
[Fact]
public void IndexOf_ValueInList_CorrectlyReturnsIndex()
{
// Arrange
IReadOnlyList<int> list = new[] { 6, 4, 1, 8 };
// Act
int index = IReadOnlyListExtensions.IndexOf(list, 1);
// Assert
Assert.Equal(2, index);
}
[Fact]
public void IndexOf_ValueNotInList_CorrectlyReturnsNegativeOne()
{
// Arrange
IReadOnlyList<int> list = new[] { 2, 4, 6, 8 };
// Act
int index = IReadOnlyListExtensions.IndexOf(list, 1);
// Assert
Assert.Equal(-1, index);
}
[Fact]
public void IndexOf_ValueAppearsMultipleTimes_CorrectlyReturnsFirstInstance()
{
// Arrange
IReadOnlyList<int> list = new[] { 2, 4, 6, 8, 4 };
// Act
int index = IReadOnlyListExtensions.IndexOf(list, 4);
// Assert
Assert.Equal(1, index);
}
[Fact]
public void BinarySearch_NullList_ThrowsNullReferenceException()
{
// Arrange
Comparer<int> comparer = Comparer<int>.Default;
// Act & Assert
Assert.Throws<NullReferenceException>(() => IReadOnlyListExtensions.BinarySearch(null, 1, comparer));
}
[Fact]
public void BinarySearch_EvenNumberOfElements_CorrectlyMatchesListImplementation()
{
// Arrange
// 6 elements total
const int minFillIndexInclusive = 1;
const int maxFillIndexInclusive = 4;
Comparer<int> comparer = Comparer<int>.Default;
List<int> list = new List<int>();
for (int i = minFillIndexInclusive; i <= maxFillIndexInclusive; i++)
{
list.Add(2 * i); // multiplying each entry by 2 to make sure there are gaps
}
// Act & Assert
for (int i = minFillIndexInclusive - 1; i <= maxFillIndexInclusive + 1; i++)
{
Assert.Equal(list.BinarySearch(i, comparer), IReadOnlyListExtensions.BinarySearch(list, i, comparer));
Assert.Equal(list.BinarySearch(i + 1, comparer), IReadOnlyListExtensions.BinarySearch(list, i + 1, comparer));
}
}
[Fact]
public void BinarySearch_OddNumberOfElements_CorrectlyMatchesListImplementation()
{
// Arrange
// 6 elements total
const int minFillIndexInclusive = 1;
const int maxFillIndexInclusive = 5;
Comparer<int> comparer = Comparer<int>.Default;
List<int> list = new List<int>();
for (int i = minFillIndexInclusive; i <= maxFillIndexInclusive; i++)
{
list.Add(2 * i); // multiplying each entry by 2 to make sure there are gaps
}
// Act & Assert
for (int i = minFillIndexInclusive - 1; i <= maxFillIndexInclusive + 1; i++)
{
Assert.Equal(list.BinarySearch(i, comparer), IReadOnlyListExtensions.BinarySearch(list, i, comparer));
Assert.Equal(list.BinarySearch(i + 1, comparer), IReadOnlyListExtensions.BinarySearch(list, i + 1, comparer));
}
}
}
}

Просмотреть файл

@ -24,7 +24,7 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
// Assert
Assert.Equal(12, mappingEntry.GeneratedSourcePosition.ZeroBasedColumnNumber);
Assert.Equal(13, mappingEntry.GeneratedSourcePosition.ZeroBasedLineNumber);
Assert.Null(mappingEntry.OriginalSourcePosition);
Assert.Equal(SourcePosition.NotFound, mappingEntry.OriginalSourcePosition);
Assert.Null(mappingEntry.OriginalFileName);
Assert.Null(mappingEntry.OriginalName);
}
@ -75,7 +75,7 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
// Assert
Assert.Equal(8, mappingEntry.GeneratedSourcePosition.ZeroBasedColumnNumber);
Assert.Equal(48, mappingEntry.GeneratedSourcePosition.ZeroBasedLineNumber);
Assert.Null(mappingEntry.OriginalSourcePosition);
Assert.Equal(SourcePosition.NotFound, mappingEntry.OriginalSourcePosition);
Assert.Equal("three", mappingEntry.OriginalFileName);
Assert.Equal("bar", mappingEntry.OriginalName);
}

Просмотреть файл

@ -15,15 +15,13 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
SourceMapGenerator sourceMapGenerator = new SourceMapGenerator();
MappingGenerateState state = new MappingGenerateState(new List<string>() { "Name" }, new List<string>() { "Source" });
state.LastGeneratedPosition.ZeroBasedColumnNumber = 1;
state.UpdateLastGeneratedPositionColumn(zeroBasedColumnNumber: 1);
MappingEntry entry = new MappingEntry()
{
GeneratedSourcePosition = new SourcePosition() { ZeroBasedLineNumber = 1, ZeroBasedColumnNumber = 0 },
OriginalFileName = state.Sources[0],
OriginalName = state.Names[0],
OriginalSourcePosition = new SourcePosition() { ZeroBasedLineNumber = 1, ZeroBasedColumnNumber = 0 },
};
MappingEntry entry = new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 1, zeroBasedColumnNumber: 0),
originalSourcePosition: new SourcePosition(zeroBasedLineNumber: 1, zeroBasedColumnNumber: 0),
originalName: state.Names[0],
originalFileName: state.Sources[0]);
// Act
var result = new StringBuilder();
@ -41,11 +39,9 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
MappingGenerateState state = new MappingGenerateState(new List<string>() { "Name" }, new List<string>() { "Source" });
MappingEntry entry = new MappingEntry()
{
GeneratedSourcePosition = new SourcePosition() { ZeroBasedLineNumber = 0, ZeroBasedColumnNumber = 10 },
OriginalSourcePosition = new SourcePosition() { ZeroBasedLineNumber = 0, ZeroBasedColumnNumber = 1 },
};
MappingEntry entry = new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 10),
originalSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 1));
// Act
var result = new StringBuilder();
@ -64,12 +60,10 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
MappingGenerateState state = new MappingGenerateState(new List<string>() { "Name" }, new List<string>() { "Source" });
state.IsFirstSegment = false;
MappingEntry entry = new MappingEntry()
{
GeneratedSourcePosition = new SourcePosition() { ZeroBasedColumnNumber = 10 },
OriginalFileName = state.Sources[0],
OriginalSourcePosition = new SourcePosition() { ZeroBasedColumnNumber = 5 },
};
MappingEntry entry = new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 10),
originalSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 5),
originalFileName: state.Sources[0]);
// Act
var result = new StringBuilder();
@ -86,15 +80,13 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
SourceMapGenerator sourceMapGenerator = new SourceMapGenerator();
MappingGenerateState state = new MappingGenerateState(new List<string>() { "Name" }, new List<string>() { "Source" });
state.LastGeneratedPosition.ZeroBasedLineNumber = 1;
state.AdvanceLastGeneratedPositionLine();
MappingEntry entry = new MappingEntry()
{
GeneratedSourcePosition = new SourcePosition() { ZeroBasedLineNumber = 1, ZeroBasedColumnNumber = 5 },
OriginalSourcePosition = new SourcePosition() { ZeroBasedLineNumber = 1, ZeroBasedColumnNumber = 6 },
OriginalFileName = state.Sources[0],
OriginalName = state.Names[0],
};
MappingEntry entry = new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 1, zeroBasedColumnNumber: 5),
originalSourcePosition: new SourcePosition(zeroBasedLineNumber: 1, zeroBasedColumnNumber: 6),
originalName: state.Names[0],
originalFileName: state.Sources[0]);
// Act
var result = new StringBuilder();
@ -120,73 +112,72 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
{
// Arrange
SourceMapGenerator sourceMapGenerator = new SourceMapGenerator();
SourceMap input = this.GetSimpleSourceMap();
SourceMap input = this.GetSimpleSourceMap();
// Act
string output = sourceMapGenerator.SerializeMapping(input);
// Act
string output = sourceMapGenerator.SerializeMapping(input);
// Assert
Assert.Equal("{\"version\":3,\"file\":\"CommonIntl\",\"mappings\":\"AACAA,aAAA,CAAc;\",\"sources\":[\"input/CommonIntl.js\"],\"names\":[\"CommonStrings\",\"afrikaans\"]}", output);
}
[Fact]
public void SerializeMappingIntoBast64_NullInput_ThrowsException()
{
// Arrange
SourceMapGenerator sourceMapGenerator = new SourceMapGenerator();
SourceMap input = null;
[Fact]
public void SerializeMappingIntoBast64_NullInput_ThrowsException()
{
// Arrange
SourceMapGenerator sourceMapGenerator = new SourceMapGenerator();
SourceMap input = null;
// Act
Assert.Throws<ArgumentNullException>(() => sourceMapGenerator.GenerateSourceMapInlineComment(input));
}
// Act
Assert.Throws<ArgumentNullException>(() => sourceMapGenerator.GenerateSourceMapInlineComment(input));
}
[Fact]
public void SerializeMappingBase64_SimpleSourceMap_CorrectlySerialized()
{
// Arrange
SourceMapGenerator sourceMapGenerator = new SourceMapGenerator();
SourceMap input = this.GetSimpleSourceMap();
[Fact]
public void SerializeMappingBase64_SimpleSourceMap_CorrectlySerialized()
{
// Arrange
SourceMapGenerator sourceMapGenerator = new SourceMapGenerator();
SourceMap input = this.GetSimpleSourceMap();
// Act
string output = sourceMapGenerator.GenerateSourceMapInlineComment(input);
// Act
string output = sourceMapGenerator.GenerateSourceMapInlineComment(input);
// Assert
Assert.Equal("//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29tbW9uSW50bCIsIm1hcHBpbmdzIjoiQUFDQUEsYUFBQSxDQUFjOyIsInNvdXJjZXMiOlsiaW5wdXQvQ29tbW9uSW50bC5qcyJdLCJuYW1lcyI6WyJDb21tb25TdHJpbmdzIiwiYWZyaWthYW5zIl19", output);
}
// Assert
Assert.Equal("//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29tbW9uSW50bCIsIm1hcHBpbmdzIjoiQUFDQUEsYUFBQSxDQUFjOyIsInNvdXJjZXMiOlsiaW5wdXQvQ29tbW9uSW50bC5qcyJdLCJuYW1lcyI6WyJDb21tb25TdHJpbmdzIiwiYWZyaWthYW5zIl19", output);
}
private SourceMap GetSimpleSourceMap()
{
SourceMap input = new SourceMap()
{
File = "CommonIntl",
Names = new List<string>() { "CommonStrings", "afrikaans" },
Sources = new List<string>() { "input/CommonIntl.js" },
Version = 3,
};
input.ParsedMappings = new List<MappingEntry>()
{
new MappingEntry
{
GeneratedSourcePosition = new SourcePosition() {ZeroBasedLineNumber = 0, ZeroBasedColumnNumber = 0 },
OriginalFileName = input.Sources[0],
OriginalName = input.Names[0],
OriginalSourcePosition = new SourcePosition() {ZeroBasedLineNumber = 1, ZeroBasedColumnNumber = 0 },
},
new MappingEntry
{
GeneratedSourcePosition = new SourcePosition() {ZeroBasedLineNumber = 0, ZeroBasedColumnNumber = 13 },
OriginalFileName = input.Sources[0],
OriginalSourcePosition = new SourcePosition() {ZeroBasedLineNumber = 1, ZeroBasedColumnNumber = 0 },
},
new MappingEntry
{
GeneratedSourcePosition = new SourcePosition() {ZeroBasedLineNumber = 0, ZeroBasedColumnNumber = 14 },
OriginalFileName = input.Sources[0],
OriginalSourcePosition = new SourcePosition() {ZeroBasedLineNumber = 1, ZeroBasedColumnNumber = 14 },
},
};
private SourceMap GetSimpleSourceMap()
{
List<string> sources = new List<string>() { "input/CommonIntl.js" };
List<string> names = new List<string>() { "CommonStrings", "afrikaans" };
return input;
}
}
var parsedMappings = new List<MappingEntry>()
{
new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 0),
originalSourcePosition: new SourcePosition(zeroBasedLineNumber: 1, zeroBasedColumnNumber: 0),
originalName: names[0],
originalFileName: sources[0]),
new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 13),
originalSourcePosition: new SourcePosition(zeroBasedLineNumber: 1, zeroBasedColumnNumber: 0),
originalFileName: sources[0]),
new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 14),
originalSourcePosition: new SourcePosition(zeroBasedLineNumber: 1, zeroBasedColumnNumber: 14),
originalFileName: sources[0]),
};
SourceMap input = new SourceMap(
version: 3,
file: "CommonIntl",
mappings: default,
sources: sources,
names: names,
parsedMappings: parsedMappings,
sourcesContent: default);
return input;
}
}
}

Просмотреть файл

@ -34,7 +34,7 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
Assert.Equal(3, output.Version);
Assert.Equal("CommonIntl", output.File);
Assert.Equal("AACAA,aAAA,CAAc", output.Mappings);
Assert.Equal(1, output.Sources.Count);
Assert.Single(output.Sources);
Assert.Equal("input/CommonIntl.js", output.Sources[0]);
Assert.Equal(2, output.Names.Count);
Assert.Equal("CommonStrings", output.Names[0]);

Просмотреть файл

@ -4,108 +4,111 @@ using Xunit;
namespace SourcemapToolkit.SourcemapParser.UnitTests
{
/// <summary>
/// Summary description for SourceMapTransformerUnitTests
/// </summary>
public class SourceMapTransformerUnitTests
{
/// <summary>
/// Summary description for SourceMapTransformerUnitTests
/// </summary>
public class SourceMapTransformerUnitTests
{
[Fact]
public void FlattenMap_ReturnsOnlyLineInformation()
{
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 2);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 2);
MappingEntry mappingEntry = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceOne.js");
[Fact]
public void FlattenMap_ReturnsOnlyLineInformation()
{
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 2);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 2);
MappingEntry mappingEntry = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceOne.js");
SourceMap map = new SourceMap
{
File = "generated.js",
Sources = new List<string> { "sourceOne.js" },
ParsedMappings = new List<MappingEntry> { mappingEntry },
SourcesContent = new List<string>{"var a = b"}
};
SourceMap map = new SourceMap(
version: default,
file: "generated.js",
mappings: default,
sources: new List<string>() { "sourceOne.js" },
names: default,
parsedMappings: new List<MappingEntry> { mappingEntry },
sourcesContent: new List<string> { "var a = b" });
// Act
SourceMap linesOnlyMap = SourceMapTransformer.Flatten(map);
// Act
SourceMap linesOnlyMap = SourceMapTransformer.Flatten(map);
// Assert
Assert.NotNull(linesOnlyMap);
Assert.Equal(1, linesOnlyMap.Sources.Count);
Assert.Equal(1, linesOnlyMap.SourcesContent.Count);
Assert.Equal(1, linesOnlyMap.ParsedMappings.Count);
Assert.Equal(1, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedColumnNumber);
Assert.Equal(2, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedColumnNumber);
}
// Assert
Assert.NotNull(linesOnlyMap);
Assert.Single(linesOnlyMap.Sources);
Assert.Single(linesOnlyMap.SourcesContent);
Assert.Single(linesOnlyMap.ParsedMappings);
Assert.Equal(1, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedColumnNumber);
Assert.Equal(2, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedColumnNumber);
}
[Fact]
public void FlattenMap_MultipleMappingsSameLine_ReturnsOnlyOneMappingPerLine()
{
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 2);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 2);
MappingEntry mappingEntry = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceOne.js");
[Fact]
public void FlattenMap_MultipleMappingsSameLine_ReturnsOnlyOneMappingPerLine()
{
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 2);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 2);
MappingEntry mappingEntry = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceOne.js");
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 5);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 5);
MappingEntry mappingEntry2 = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceOne.js");
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 5);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 5);
MappingEntry mappingEntry2 = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceOne.js");
SourceMap map = new SourceMap
{
File = "generated.js",
Sources = new List<string> { "sourceOne.js" },
ParsedMappings = new List<MappingEntry> { mappingEntry, mappingEntry2 },
SourcesContent = new List<string>{"var a = b"}
};
SourceMap map = new SourceMap(
version: default,
file: "generated.js",
mappings: default,
sources: new List<string>() { "sourceOne.js" },
names: default,
parsedMappings: new List<MappingEntry> { mappingEntry, mappingEntry2 },
sourcesContent: new List<string> { "var a = b" });
// Act
SourceMap linesOnlyMap = SourceMapTransformer.Flatten(map);
// Act
SourceMap linesOnlyMap = SourceMapTransformer.Flatten(map);
// Assert
Assert.NotNull(linesOnlyMap);
Assert.Equal(1, linesOnlyMap.Sources.Count);
Assert.Equal(1, linesOnlyMap.SourcesContent.Count);
Assert.Equal(1, linesOnlyMap.ParsedMappings.Count);
Assert.Equal(1, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedColumnNumber);
Assert.Equal(2, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedColumnNumber);
}
// Assert
Assert.NotNull(linesOnlyMap);
Assert.Single(linesOnlyMap.Sources);
Assert.Single(linesOnlyMap.SourcesContent);
Assert.Single(linesOnlyMap.ParsedMappings);
Assert.Equal(1, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedColumnNumber);
Assert.Equal(2, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedColumnNumber);
}
[Fact]
public void FlattenMap_MultipleOriginalLineToSameGeneratedLine_ReturnsFirstOriginalLine()
{
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 2);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 2);
MappingEntry mappingEntry = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceOne.js");
[Fact]
public void FlattenMap_MultipleOriginalLineToSameGeneratedLine_ReturnsFirstOriginalLine()
{
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 2);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 2);
MappingEntry mappingEntry = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceOne.js");
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 3);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber: 3, colNumber: 5);
MappingEntry mappingEntry2 = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceOne.js");
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 3);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber: 3, colNumber: 5);
MappingEntry mappingEntry2 = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceOne.js");
SourceMap map = new SourceMap
{
File = "generated.js",
Sources = new List<string> { "sourceOne.js" },
ParsedMappings = new List<MappingEntry> { mappingEntry, mappingEntry2 },
SourcesContent = new List<string>{"var a = b"}
};
SourceMap map = new SourceMap(
version: default,
file: "generated.js",
mappings: default,
sources: new List<string>() { "sourceOne.js" },
names: default,
parsedMappings: new List<MappingEntry> { mappingEntry, mappingEntry2 },
sourcesContent: new List<string> { "var a = b" });
// Act
SourceMap linesOnlyMap = SourceMapTransformer.Flatten(map);
// Act
SourceMap linesOnlyMap = SourceMapTransformer.Flatten(map);
// Assert
Assert.NotNull(linesOnlyMap);
Assert.Equal(1, linesOnlyMap.Sources.Count);
Assert.Equal(1, linesOnlyMap.SourcesContent.Count);
Assert.Equal(1, linesOnlyMap.ParsedMappings.Count);
Assert.Equal(1, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedColumnNumber);
Assert.Equal(2, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedColumnNumber);
}
}
// Assert
Assert.NotNull(linesOnlyMap);
Assert.Single(linesOnlyMap.Sources);
Assert.Single(linesOnlyMap.SourcesContent);
Assert.Single(linesOnlyMap.ParsedMappings);
Assert.Equal(1, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].GeneratedSourcePosition.ZeroBasedColumnNumber);
Assert.Equal(2, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedLineNumber);
Assert.Equal(0, linesOnlyMap.ParsedMappings[0].OriginalSourcePosition.ZeroBasedColumnNumber);
}
}
}

Просмотреть файл

@ -11,11 +11,11 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void GetMappingEntryForGeneratedSourcePosition_NullMappingList_ReturnNull()
{
// Arrange
SourceMap sourceMap = new SourceMap();
SourcePosition sourcePosition = new SourcePosition {ZeroBasedColumnNumber = 3, ZeroBasedLineNumber = 4};
SourceMap sourceMap = CreateSourceMap();
SourcePosition sourcePosition = new SourcePosition(zeroBasedLineNumber: 4, zeroBasedColumnNumber: 3);
// Act
MappingEntry result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
MappingEntry? result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
// Asset
Assert.Null(result);
@ -25,18 +25,17 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void GetMappingEntryForGeneratedSourcePosition_NoMatchingEntryInMappingList_ReturnNull()
{
// Arrange
SourceMap sourceMap = new SourceMap();
sourceMap.ParsedMappings = new List<MappingEntry>
List<MappingEntry> parsedMappings = new List<MappingEntry>
{
new MappingEntry
{
GeneratedSourcePosition = new SourcePosition {ZeroBasedLineNumber = 0, ZeroBasedColumnNumber = 0}
}
new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 0))
};
SourcePosition sourcePosition = new SourcePosition { ZeroBasedColumnNumber = 3, ZeroBasedLineNumber = 4 };
SourceMap sourceMap = CreateSourceMap(parsedMappings: parsedMappings);
SourcePosition sourcePosition = new SourcePosition(zeroBasedLineNumber: 4, zeroBasedColumnNumber: 3);
// Act
MappingEntry result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
MappingEntry? result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
// Asset
Assert.Null(result);
@ -46,23 +45,20 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void GetMappingEntryForGeneratedSourcePosition_MatchingEntryInMappingList_ReturnMatchingEntry()
{
// Arrange
SourceMap sourceMap = new SourceMap();
MappingEntry matchingMappingEntry = new MappingEntry
MappingEntry matchingMappingEntry = new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 8, zeroBasedColumnNumber: 13));
List<MappingEntry> parsedMappings = new List<MappingEntry>
{
GeneratedSourcePosition = new SourcePosition {ZeroBasedLineNumber = 8, ZeroBasedColumnNumber = 13}
};
sourceMap.ParsedMappings = new List<MappingEntry>
{
new MappingEntry
{
GeneratedSourcePosition = new SourcePosition {ZeroBasedLineNumber = 0, ZeroBasedColumnNumber = 0}
},
new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 0)),
matchingMappingEntry
};
SourcePosition sourcePosition = new SourcePosition { ZeroBasedLineNumber = 8, ZeroBasedColumnNumber = 13 };
SourceMap sourceMap = CreateSourceMap(parsedMappings: parsedMappings);
SourcePosition sourcePosition = new SourcePosition(zeroBasedLineNumber: 8, zeroBasedColumnNumber: 13);
// Act
MappingEntry result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
MappingEntry? result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
// Asset
Assert.Equal(matchingMappingEntry, result);
@ -72,70 +68,60 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void GetMappingEntryForGeneratedSourcePosition_NoExactMatchHasSimilarOnSameLine_ReturnSimilarEntry()
{
// Arrange
SourceMap sourceMap = new SourceMap();
MappingEntry matchingMappingEntry = new MappingEntry
MappingEntry matchingMappingEntry = new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 10, zeroBasedColumnNumber: 13));
List<MappingEntry> parsedMappings = new List<MappingEntry>
{
GeneratedSourcePosition = new SourcePosition { ZeroBasedLineNumber = 10, ZeroBasedColumnNumber = 13 }
};
sourceMap.ParsedMappings = new List<MappingEntry>
{
new MappingEntry
{
GeneratedSourcePosition = new SourcePosition {ZeroBasedLineNumber = 0, ZeroBasedColumnNumber = 0}
},
new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 0)),
matchingMappingEntry
};
SourcePosition sourcePosition = new SourcePosition { ZeroBasedLineNumber = 10, ZeroBasedColumnNumber = 14 };
SourceMap sourceMap = CreateSourceMap(parsedMappings: parsedMappings);
SourcePosition sourcePosition = new SourcePosition(zeroBasedLineNumber: 10, zeroBasedColumnNumber: 14);
// Act
MappingEntry result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
MappingEntry? result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
// Asset
Assert.Equal(matchingMappingEntry, result);
}
[Fact]
public void GetMappingEntryForGeneratedSourcePosition_NoExactMatchHasSimilarOnDifferentLinesLine_ReturnSimilarEntry()
{
// Arrange
SourceMap sourceMap = new SourceMap();
MappingEntry matchingMappingEntry = new MappingEntry
{
GeneratedSourcePosition = new SourcePosition { ZeroBasedLineNumber = 23, ZeroBasedColumnNumber = 15 }
};
sourceMap.ParsedMappings = new List<MappingEntry>
{
new MappingEntry
{
GeneratedSourcePosition = new SourcePosition {ZeroBasedLineNumber = 0, ZeroBasedColumnNumber = 0}
},
matchingMappingEntry
};
SourcePosition sourcePosition = new SourcePosition { ZeroBasedLineNumber = 24, ZeroBasedColumnNumber = 0 };
[Fact]
public void GetMappingEntryForGeneratedSourcePosition_NoExactMatchHasSimilarOnDifferentLinesLine_ReturnSimilarEntry()
{
// Arrange
MappingEntry matchingMappingEntry = new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 23, zeroBasedColumnNumber: 15));
List<MappingEntry> parsedMappings = new List<MappingEntry>
{
new MappingEntry(
generatedSourcePosition: new SourcePosition(zeroBasedLineNumber: 0, zeroBasedColumnNumber: 0)),
matchingMappingEntry
};
SourceMap sourceMap = CreateSourceMap(parsedMappings: parsedMappings);
SourcePosition sourcePosition = new SourcePosition(zeroBasedLineNumber: 24, zeroBasedColumnNumber: 0);
// Act
MappingEntry result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
// Act
MappingEntry? result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
// Asset
Assert.Equal(matchingMappingEntry, result);
}
// Asset
Assert.Equal(matchingMappingEntry, result);
}
[Fact]
public void GetRootMappingEntryForGeneratedSourcePosition_NoChildren_ReturnsSameEntry()
{
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber:2, colNumber: 5);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber:1, colNumber: 5);
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 5);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 5);
MappingEntry mappingEntry = UnitTestUtils.getSimpleEntry(generated1, original1, "generated.js");
SourceMap sourceMap = new SourceMap
{
Sources = new List<string> { "generated.js" },
ParsedMappings = new List<MappingEntry> { mappingEntry }
};
SourceMap sourceMap = CreateSourceMap(
sources: new List<string>() { "generated.js" },
parsedMappings: new List<MappingEntry> { mappingEntry });
// Act
MappingEntry rootEntry = sourceMap.GetMappingEntryForGeneratedSourcePosition(generated1);
MappingEntry? rootEntry = sourceMap.GetMappingEntryForGeneratedSourcePosition(generated1);
// Assert
Assert.Equal(rootEntry, mappingEntry);
@ -145,16 +131,14 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void ApplyMap_NullSubmap_ThrowsException()
{
// Arrange
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber:3, colNumber: 5);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber:2, colNumber: 5);
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber: 3, colNumber: 5);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 5);
MappingEntry mapping = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceOne.js");
SourceMap map = new SourceMap
{
File = "generated.js",
Sources = new List<string> { "sourceOne.js" },
ParsedMappings = new List<MappingEntry> { mapping }
};
SourceMap map = CreateSourceMap(
file: "generated.js",
sources: new List<string>() { "sourceOne.js" },
parsedMappings: new List<MappingEntry> { mapping });
// Act
Assert.Throws<ArgumentNullException>(() => map.ApplySourceMap(null));
@ -166,27 +150,23 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void ApplyMap_NoMatchingSources_ReturnsSameMap()
{
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber:2, colNumber: 3);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber:1, colNumber: 2);
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 3);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 2);
MappingEntry childMapping = UnitTestUtils.getSimpleEntry(generated1, original1, "someOtherSource.js");
SourceMap childMap = new SourceMap
{
File = "notSourceOne.js",
Sources = new List<string> { "someOtherSource.js" },
ParsedMappings = new List<MappingEntry> { childMapping }
};
SourceMap childMap = CreateSourceMap(
file: "notSourceOne.js",
sources: new List<string>() { "someOtherSource.js" },
parsedMappings: new List<MappingEntry> { childMapping });
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber:3, colNumber: 7);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber:2, colNumber: 3);
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber: 3, colNumber: 7);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 3);
MappingEntry parentMapping = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceOne.js");
SourceMap parentMap = new SourceMap
{
File = "generated.js",
Sources = new List<string> { "sourceOne.js" },
ParsedMappings = new List<MappingEntry> { parentMapping }
};
SourceMap parentMap = CreateSourceMap(
file: "generated.js",
sources: new List<string>() { "sourceOne.js" },
parsedMappings: new List<MappingEntry> { parentMapping });
// Act
SourceMap combinedMap = parentMap.ApplySourceMap(childMap);
@ -194,115 +174,103 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
// Assert
Assert.NotNull(combinedMap);
MappingEntry firstMapping = combinedMap.ParsedMappings[0];
Assert.True(firstMapping.IsValueEqual(parentMapping));
Assert.True(firstMapping.Equals(parentMapping));
}
[Fact]
public void ApplyMap_NoMatchingMappings_ReturnsSameMap()
{
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber:2);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber:10);
MappingEntry childMapping = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceTwo.js");
SourceMap childMap = new SourceMap
{
File = "sourceOne.js",
Sources = new List<string> { "sourceTwo.js" },
ParsedMappings = new List<MappingEntry> { childMapping }
};
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber:3, colNumber:4);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber:2, colNumber:5);
MappingEntry parentMapping = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceOne.js");
SourceMap parentMap = new SourceMap
{
File = "generated.js",
Sources = new List<string> { "sourceOne.js" },
ParsedMappings = new List<MappingEntry> { parentMapping }
};
// Act
SourceMap combinedMap = parentMap.ApplySourceMap(childMap);
// Assert
Assert.NotNull(combinedMap);
MappingEntry firstMapping = combinedMap.ParsedMappings[0];
Assert.True(firstMapping.IsValueEqual(parentMapping));
}
[Fact]
public void ApplyMap_MatchingSources_ReturnsCorrectMap()
[Fact]
public void ApplyMap_NoMatchingMappings_ReturnsSameMap()
{
// Expect mapping with same source filename as the applied source-map to be replaced
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber:2, colNumber:4);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber:1, colNumber:3);
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 2);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 10);
MappingEntry childMapping = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceTwo.js");
SourceMap childMap = new SourceMap
{
File = "sourceOne.js",
Sources = new List<string> { "sourceTwo.js" },
ParsedMappings = new List<MappingEntry> { childMapping }
};
SourceMap childMap = CreateSourceMap(
file: "sourceOne.js",
sources: new List<string>() { "sourceTwo.js" },
parsedMappings: new List<MappingEntry> { childMapping });
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber:3, colNumber: 5);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber:2, colNumber: 4);
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber: 3, colNumber: 4);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 5);
MappingEntry parentMapping = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceOne.js");
SourceMap parentMap = new SourceMap
{
File = "generated.js",
Sources = new List<string> { "sourceOne.js" },
ParsedMappings = new List<MappingEntry> { parentMapping }
};
SourceMap parentMap = CreateSourceMap(
file: "generated.js",
sources: new List<string>() { "sourceOne.js" },
parsedMappings: new List<MappingEntry> { parentMapping });
// Act
SourceMap combinedMap = parentMap.ApplySourceMap(childMap);
// Assert
Assert.NotNull(combinedMap);
Assert.Equal(1, combinedMap.ParsedMappings.Count);
Assert.Equal(1, combinedMap.Sources.Count);
MappingEntry rootMapping = combinedMap.GetMappingEntryForGeneratedSourcePosition(generated2);
Assert.Equal(0, rootMapping.OriginalSourcePosition.CompareTo(childMapping.OriginalSourcePosition));
MappingEntry firstMapping = combinedMap.ParsedMappings[0];
Assert.True(firstMapping.Equals(parentMapping));
}
[Fact]
public void ApplyMap_MatchingSources_ReturnsCorrectMap()
{
// Expect mapping with same source filename as the applied source-map to be replaced
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 4);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 3);
MappingEntry childMapping = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceTwo.js");
SourceMap childMap = CreateSourceMap(
file: "sourceOne.js",
sources: new List<string>() { "sourceTwo.js" },
parsedMappings: new List<MappingEntry> { childMapping });
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber: 3, colNumber: 5);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 4);
MappingEntry parentMapping = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceOne.js");
SourceMap parentMap = CreateSourceMap(
file: "generated.js",
sources: new List<string>() { "sourceOne.js" },
parsedMappings: new List<MappingEntry> { parentMapping });
// Act
SourceMap combinedMap = parentMap.ApplySourceMap(childMap);
// Assert
Assert.NotNull(combinedMap);
Assert.Single(combinedMap.ParsedMappings);
Assert.Single(combinedMap.Sources);
MappingEntry? rootMapping = combinedMap.GetMappingEntryForGeneratedSourcePosition(generated2);
Assert.Equal(0, rootMapping.Value.OriginalSourcePosition.CompareTo(childMapping.OriginalSourcePosition));
}
[Fact]
public void ApplyMap_PartialMatchingSources_ReturnsCorrectMap()
{
// Expect mappings with same source filename as the applied source-map to be replaced
// mappings with a different source filename should stay the same
// Expect mappings with same source filename as the applied source-map to be replaced
// mappings with a different source filename should stay the same
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber:2, colNumber:10);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber:1, colNumber:5);
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 10);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 1, colNumber: 5);
MappingEntry childMapping = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceTwo.js");
SourceMap childMap = new SourceMap
{
File = "sourceOne.js",
Sources = new List<string> { "sourceTwo.js" },
ParsedMappings = new List<MappingEntry> { childMapping }
};
SourceMap childMap = CreateSourceMap(
file: "sourceOne.js",
sources: new List<string>() { "sourceTwo.js" },
parsedMappings: new List<MappingEntry> { childMapping });
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber:3, colNumber:2);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber:2, colNumber: 10);
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber: 3, colNumber: 2);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 10);
MappingEntry mapping = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceOne.js");
SourcePosition generated3 = UnitTestUtils.generateSourcePosition(lineNumber:4, colNumber:3);
SourcePosition original3 = UnitTestUtils.generateSourcePosition(lineNumber:3, colNumber:2);
SourcePosition generated3 = UnitTestUtils.generateSourcePosition(lineNumber: 4, colNumber: 3);
SourcePosition original3 = UnitTestUtils.generateSourcePosition(lineNumber: 3, colNumber: 2);
MappingEntry mapping2 = UnitTestUtils.getSimpleEntry(generated3, original3, "noMapForThis.js");
SourceMap parentMap = new SourceMap
{
File = "generated.js",
Sources = new List<string> { "sourceOne.js", "noMapForThis.js" },
ParsedMappings = new List<MappingEntry> { mapping, mapping2 }
};
SourceMap parentMap = CreateSourceMap(
file: "generated.js",
sources: new List<string> { "sourceOne.js", "noMapForThis.js" },
parsedMappings: new List<MappingEntry> { mapping, mapping2 });
// Act
SourceMap combinedMap = parentMap.ApplySourceMap(childMap);
@ -311,48 +279,42 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
Assert.NotNull(combinedMap);
Assert.Equal(2, combinedMap.ParsedMappings.Count);
Assert.Equal(2, combinedMap.Sources.Count);
MappingEntry firstCombinedMapping = combinedMap.GetMappingEntryForGeneratedSourcePosition(generated3);
Assert.True(firstCombinedMapping.IsValueEqual(mapping2));
MappingEntry secondCombinedMapping = combinedMap.GetMappingEntryForGeneratedSourcePosition(generated2);
Assert.Equal(0, secondCombinedMapping.OriginalSourcePosition.CompareTo(childMapping.OriginalSourcePosition));
MappingEntry? firstCombinedMapping = combinedMap.GetMappingEntryForGeneratedSourcePosition(generated3);
Assert.True(firstCombinedMapping.Equals(mapping2));
MappingEntry? secondCombinedMapping = combinedMap.GetMappingEntryForGeneratedSourcePosition(generated2);
Assert.Equal(0, secondCombinedMapping.Value.OriginalSourcePosition.CompareTo(childMapping.OriginalSourcePosition));
}
[Fact]
public void ApplyMap_ExactMatchDeep_ReturnsCorrectMappingEntry()
{
// Arrange
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber:3, colNumber:5);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber:2, colNumber:10);
SourcePosition generated1 = UnitTestUtils.generateSourcePosition(lineNumber: 3, colNumber: 5);
SourcePosition original1 = UnitTestUtils.generateSourcePosition(lineNumber: 2, colNumber: 10);
MappingEntry mapLevel2 = UnitTestUtils.getSimpleEntry(generated1, original1, "sourceThree.js");
SourceMap grandChildMap = new SourceMap
{
File = "sourceTwo.js",
Sources = new List<string> { "sourceThree.js" },
ParsedMappings = new List<MappingEntry> { mapLevel2 }
};
SourceMap grandChildMap = CreateSourceMap(
file: "sourceTwo.js",
sources: new List<string>() { "sourceThree.js" },
parsedMappings: new List<MappingEntry> { mapLevel2 });
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber:4, colNumber:3);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber:3, colNumber:5);
SourcePosition generated2 = UnitTestUtils.generateSourcePosition(lineNumber: 4, colNumber: 3);
SourcePosition original2 = UnitTestUtils.generateSourcePosition(lineNumber: 3, colNumber: 5);
MappingEntry mapLevel1 = UnitTestUtils.getSimpleEntry(generated2, original2, "sourceTwo.js");
SourceMap childMap = new SourceMap
{
File = "sourceOne.js",
Sources = new List<string> { "sourceTwo.js" },
ParsedMappings = new List<MappingEntry> { mapLevel1 }
};
SourceMap childMap = CreateSourceMap(
file: "sourceOne.js",
sources: new List<string>() { "sourceTwo.js" },
parsedMappings: new List<MappingEntry> { mapLevel1 });
SourcePosition generated3 = UnitTestUtils.generateSourcePosition(lineNumber:5, colNumber:5);
SourcePosition original3 = UnitTestUtils.generateSourcePosition(lineNumber:4, colNumber:3);
SourcePosition generated3 = UnitTestUtils.generateSourcePosition(lineNumber: 5, colNumber: 5);
SourcePosition original3 = UnitTestUtils.generateSourcePosition(lineNumber: 4, colNumber: 3);
MappingEntry mapLevel0 = UnitTestUtils.getSimpleEntry(generated3, original3, "sourceOne.js");
SourceMap parentMap = new SourceMap
{
File = "generated.js",
Sources = new List<string> { "sourceOne.js" },
ParsedMappings = new List<MappingEntry> { mapLevel0 }
};
SourceMap parentMap = CreateSourceMap(
file: "generated.js",
sources: new List<string>() { "sourceOne.js" },
parsedMappings: new List<MappingEntry> { mapLevel0 });
// Act
SourceMap firstCombinedMap = parentMap.ApplySourceMap(childMap);
@ -361,8 +323,27 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
Assert.NotNull(firstCombinedMap);
SourceMap secondCombinedMap = firstCombinedMap.ApplySourceMap(grandChildMap);
Assert.NotNull(secondCombinedMap);
MappingEntry rootMapping = secondCombinedMap.GetMappingEntryForGeneratedSourcePosition(generated3);
Assert.Equal(0, rootMapping.OriginalSourcePosition.CompareTo(mapLevel2.OriginalSourcePosition));
MappingEntry? rootMapping = secondCombinedMap.GetMappingEntryForGeneratedSourcePosition(generated3);
Assert.Equal(0, rootMapping.Value.OriginalSourcePosition.CompareTo(mapLevel2.OriginalSourcePosition));
}
}
private static SourceMap CreateSourceMap(
int version = default,
string file = default,
string mappings = default,
IReadOnlyList<string> sources = default,
IReadOnlyList<string> names = default,
IReadOnlyList<MappingEntry> parsedMappings = default,
IReadOnlyList<string> sourcesContent = default)
{
return new SourceMap(
version: version,
file: file,
mappings: mappings,
sources: sources,
names: names,
parsedMappings: parsedMappings,
sourcesContent: sourcesContent);
}
}
}

Просмотреть файл

@ -9,16 +9,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void CompareTo_SameLineAndColumn_Equal()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 6
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 6
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 6);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 6);
// Act
int result = x.CompareTo(y);
@ -31,16 +27,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void CompareTo_LargerZeroBasedLineNumber_ReturnLarger()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 4
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 8
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 2,
zeroBasedColumnNumber: 4);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 8);
// Act
int result = x.CompareTo(y);
@ -53,16 +45,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void CompareTo_SmallerZeroBasedLineNumber_ReturnSmaller()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 4
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 3,
ZeroBasedColumnNumber = 8
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 4);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 3,
zeroBasedColumnNumber: 8);
// Act
int result = x.CompareTo(y);
@ -75,16 +63,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void CompareTo_SameLineLargerColumn_ReturnLarger()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 8
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 6
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 8);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 6);
// Act
int result = x.CompareTo(y);
@ -97,16 +81,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void CompareTo_SameLineSmallerColumn_ReturnSmaller()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 4
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 8
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 4);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 8);
// Act
int result = x.CompareTo(y);
@ -119,16 +99,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void LessThanOverload_SameZeroBasedLineNumberXColumnSmaller_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 4
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 8
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 4);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 8);
// Act
bool result = x < y;
@ -141,16 +117,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void LessThanOverload_XZeroBasedLineNumberSmallerX_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 10
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 8
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 10);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 2,
zeroBasedColumnNumber: 8);
// Act
bool result = x < y;
@ -163,16 +135,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void LessThanOverload_Equal_ReturnFalse()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 0,
ZeroBasedColumnNumber = 0
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 0,
ZeroBasedColumnNumber = 0
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 0,
zeroBasedColumnNumber: 0);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 0,
zeroBasedColumnNumber: 0);
// Act
bool result = x < y;
@ -185,16 +153,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void LessThanOverload_XColumnLarger_ReturnFalse()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 0,
ZeroBasedColumnNumber = 10
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 0,
ZeroBasedColumnNumber = 0
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 0,
zeroBasedColumnNumber: 10);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 0,
zeroBasedColumnNumber: 0);
// Act
bool result = x < y;
@ -207,16 +171,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void GreaterThanOverload_SameLineXColumnLarger_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 10
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 3
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 2,
zeroBasedColumnNumber: 10);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 2,
zeroBasedColumnNumber: 3);
// Act
bool result = x > y;
@ -229,16 +189,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void GreaterThanOverload_XZeroBasedLineNumberLarger_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 3,
ZeroBasedColumnNumber = 10
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 30
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 3,
zeroBasedColumnNumber: 10);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 2,
zeroBasedColumnNumber: 30);
// Act
bool result = x > y;
@ -251,16 +207,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void GreaterThanOverload_Equal_ReturnFalse()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 3,
ZeroBasedColumnNumber = 10
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 3,
ZeroBasedColumnNumber = 10
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 3,
zeroBasedColumnNumber: 10);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 3,
zeroBasedColumnNumber: 10);
// Act
bool result = x > y;
@ -273,16 +225,12 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
public void GreaterThanOverload_XSmaller_ReturnFalse()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 3,
ZeroBasedColumnNumber = 9
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 3,
ZeroBasedColumnNumber = 10
};
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 3,
zeroBasedColumnNumber: 9);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 3,
zeroBasedColumnNumber: 10);
// Act
bool result = x > y;
@ -291,158 +239,130 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
Assert.False(result);
}
[Fact]
public void IsEqualish_XEqualsY_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 13,
ZeroBasedColumnNumber = 5
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 13,
ZeroBasedColumnNumber = 5
};
[Fact]
public void IsEqualish_XEqualsY_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 13,
zeroBasedColumnNumber: 5);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 13,
zeroBasedColumnNumber: 5);
// Act
bool result = x.IsEqualish(y);
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.True(result);
}
// Assert
Assert.True(result);
}
[Fact]
public void IsEqualish_XColumnNumberBiggerByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 8
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 7
};
[Fact]
public void IsEqualish_XColumnNumberBiggerByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 2,
zeroBasedColumnNumber: 8);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 2,
zeroBasedColumnNumber: 7);
// Act
bool result = x.IsEqualish(y);
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.True(result);
}
// Assert
Assert.True(result);
}
[Fact]
public void IsEqualish_YColumnNumberBiggerByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 10
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 11
};
[Fact]
public void IsEqualish_YColumnNumberBiggerByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 10);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 1,
zeroBasedColumnNumber: 11);
// Act
bool result = x.IsEqualish(y);
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.True(result);
}
// Assert
Assert.True(result);
}
[Fact]
public void IsEqualish_YColumnNumberBiggerByTwo_ReturnFalse()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 155,
ZeroBasedColumnNumber = 100
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 155,
ZeroBasedColumnNumber = 102
};
[Fact]
public void IsEqualish_YColumnNumberBiggerByTwo_ReturnFalse()
{
// Arrange
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 155,
zeroBasedColumnNumber: 100);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 155,
zeroBasedColumnNumber: 102);
// Act
bool result = x.IsEqualish(y);
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.False(result);
}
// Assert
Assert.False(result);
}
[Fact]
public void IsEqualish_XColumnNumberZeroLineNumbersDifferByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 235,
ZeroBasedColumnNumber = 0
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 234,
ZeroBasedColumnNumber = 102
};
[Fact]
public void IsEqualish_XColumnNumberZeroLineNumbersDifferByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 235,
zeroBasedColumnNumber: 0);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 234,
zeroBasedColumnNumber: 102);
// Act
bool result = x.IsEqualish(y);
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.True(result);
}
// Assert
Assert.True(result);
}
[Fact]
public void IsEqualish_YColumnNumberZeroLineNumbersDifferByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 458,
ZeroBasedColumnNumber = 13
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 459,
ZeroBasedColumnNumber = 0
};
[Fact]
public void IsEqualish_YColumnNumberZeroLineNumbersDifferByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 458,
zeroBasedColumnNumber: 13);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 459,
zeroBasedColumnNumber: 0);
// Act
bool result = x.IsEqualish(y);
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.True(result);
}
// Assert
Assert.True(result);
}
[Fact]
public void IsEqualish_YColumnNumberZeroLineNumbersDifferByTwo_ReturnFalse()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 5456,
ZeroBasedColumnNumber = 13
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 5458,
ZeroBasedColumnNumber = 0
};
[Fact]
public void IsEqualish_YColumnNumberZeroLineNumbersDifferByTwo_ReturnFalse()
{
// Arrange
SourcePosition x = new SourcePosition(
zeroBasedLineNumber: 5456,
zeroBasedColumnNumber: 13);
SourcePosition y = new SourcePosition(
zeroBasedLineNumber: 5458,
zeroBasedColumnNumber: 0);
// Act
bool result = x.IsEqualish(y);
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.False(result);
}
}
// Assert
Assert.False(result);
}
}
}

Просмотреть файл

@ -91,6 +91,7 @@
<Compile Include="Base64ConverterUnitTests.cs" />
<Compile Include="Base64VlqDecoderUnitTests.cs" />
<Compile Include="Base64VlqEncoderUnitTests.cs" />
<Compile Include="IReadOnlyListExtensionsUnitTests.cs" />
<Compile Include="NumericMappingEntryUnitTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MappingsListParserUnitTests.cs" />
@ -99,6 +100,7 @@
<Compile Include="SourceMapTransformerUnitTests.cs" />
<Compile Include="SourceMapUnitTests.cs" />
<Compile Include="SourcePositionUnitTests.cs" />
<Compile Include="StringExtensionsUnitTests.cs" />
<Compile Include="UnitTestUtils.cs" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -0,0 +1,182 @@
using System;
using Xunit;
namespace SourcemapToolkit.SourcemapParser.UnitTests
{
public class StringExtensionsUnitTests
{
[Fact]
public void SplitFast_NullInput_Throws()
{
// Arrange
const string input = null;
const char delimiter = ',';
// Act
Action testAction = () => StringExtensions.SplitFast(input, delimiter);
//Throws Assert
Assert.Throws<NullReferenceException>(testAction);
}
[Fact]
public void SplitFast_EmptyInput_Matches()
{
// Arrange
const string input = "";
const char delimiter = ',';
// Act
string[] fastSplit = StringExtensions.SplitFast(input, delimiter);
string[] normalSplit = input.Split(delimiter);
// Assert
Assert.Equal(normalSplit.Length, fastSplit.Length);
for (int i = 0; i < normalSplit.Length; i++)
{
Assert.Equal(normalSplit[i], fastSplit[i]);
}
}
[Fact]
public void SplitFast_OneCharacterNotSplit_Matches()
{
// Arrange
const string input = "A";
const char delimiter = ',';
// Act
string[] fastSplit = StringExtensions.SplitFast(input, delimiter);
string[] normalSplit = input.Split(delimiter);
// Assert
Assert.Equal(normalSplit.Length, fastSplit.Length);
for (int i = 0; i < normalSplit.Length; i++)
{
Assert.Equal(normalSplit[i], fastSplit[i]);
}
}
[Fact]
public void SplitFast_OneCharacterDelimiter_Matches()
{
// Arrange
const string input = ",";
const char delimiter = ',';
// Act
string[] fastSplit = StringExtensions.SplitFast(input, delimiter);
string[] normalSplit = input.Split(delimiter);
// Assert
Assert.Equal(normalSplit.Length, fastSplit.Length);
for (int i = 0; i < normalSplit.Length; i++)
{
Assert.Equal(normalSplit[i], fastSplit[i]);
}
}
[Fact]
public void SplitFast_DelimiterAtStart_Matches()
{
// Arrange
const string input = ",Hello";
const char delimiter = ',';
// Act
string[] fastSplit = StringExtensions.SplitFast(input, delimiter);
string[] normalSplit = input.Split(delimiter);
// Assert
Assert.Equal(normalSplit.Length, fastSplit.Length);
for (int i = 0; i < normalSplit.Length; i++)
{
Assert.Equal(normalSplit[i], fastSplit[i]);
}
}
[Fact]
public void SplitFast_DelimiterAtEnd_Matches()
{
// Arrange
const string input = "Hello,";
const char delimiter = ',';
// Act
string[] fastSplit = StringExtensions.SplitFast(input, delimiter);
string[] normalSplit = input.Split(delimiter);
// Assert
Assert.Equal(normalSplit.Length, fastSplit.Length);
for (int i = 0; i < normalSplit.Length; i++)
{
Assert.Equal(normalSplit[i], fastSplit[i]);
}
}
[Fact]
public void SplitFast_BackToBack_Matches()
{
// Arrange
const string input = "Hello,,World";
const char delimiter = ',';
// Act
string[] fastSplit = StringExtensions.SplitFast(input, delimiter);
string[] normalSplit = input.Split(delimiter);
// Assert
Assert.Equal(normalSplit.Length, fastSplit.Length);
for (int i = 0; i < normalSplit.Length; i++)
{
Assert.Equal(normalSplit[i], fastSplit[i]);
}
}
[Fact]
public void SplitFast_LongSentence_Matches()
{
// Arrange
const string input = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci. Aenean nec lorem. In porttitor. Donec laoreet nonummy augue. Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.";
const char delimiter = ',';
// Act
string[] fastSplit = StringExtensions.SplitFast(input, delimiter);
string[] normalSplit = input.Split(delimiter);
// Assert
Assert.Equal(normalSplit.Length, fastSplit.Length);
for (int i = 0; i < normalSplit.Length; i++)
{
Assert.Equal(normalSplit[i], fastSplit[i]);
}
}
[Fact]
public void SplitFast_Complex_Matches()
{
// Arrange
const string input = ",,Hello,World,How,,Are,You,Doing,,";
const char delimiter = ',';
// Act
string[] fastSplit = StringExtensions.SplitFast(input, delimiter);
string[] normalSplit = input.Split(delimiter);
// Assert
Assert.Equal(normalSplit.Length, fastSplit.Length);
for (int i = 0; i < normalSplit.Length; i++)
{
Assert.Equal(normalSplit[i], fastSplit[i]);
}
}
}
}

Просмотреть файл

@ -11,23 +11,19 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
return new StreamReader(new MemoryStream(byteArray));
}
public static MappingEntry getSimpleEntry(SourcePosition generatedSourcePosition, SourcePosition originalSourcePosition, string originalFileName)
{
return new MappingEntry
{
GeneratedSourcePosition = generatedSourcePosition,
OriginalSourcePosition = originalSourcePosition,
OriginalFileName = originalFileName
};
}
public static MappingEntry getSimpleEntry(SourcePosition generatedSourcePosition, SourcePosition originalSourcePosition, string originalFileName)
{
return new MappingEntry(
generatedSourcePosition: generatedSourcePosition,
originalSourcePosition: originalSourcePosition,
originalFileName: originalFileName);
}
public static SourcePosition generateSourcePosition(int lineNumber, int colNumber = 0)
{
return new SourcePosition
{
ZeroBasedLineNumber = lineNumber,
ZeroBasedColumnNumber = colNumber
};
}
}
public static SourcePosition generateSourcePosition(int lineNumber, int colNumber = 0)
{
return new SourcePosition(
zeroBasedLineNumber: lineNumber,
zeroBasedColumnNumber: colNumber);
}
}
}