Callstack deminification without parsing JS to support ES2015+ (#82)

* Enable deminifying call stack without parsing JS to support ES2015

* Move exception handling into FunctionMapGenerator
This commit is contained in:
Ian Craig 2020-10-13 14:46:34 -07:00 коммит произвёл GitHub
Родитель 263a3bd471
Коммит ef2988141b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 261 добавлений и 32 удалений

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

@ -17,7 +17,14 @@ namespace SourcemapToolkit.CallstackDeminifier
for (int i = 0; i < DeminifiedStackFrameResults.Count; i++)
{
StackFrame deminFrame = DeminifiedStackFrameResults[i].DeminifiedStackFrame;
StackFrame frame = string.IsNullOrEmpty(deminFrame.MethodName) ? MinifiedStackFrames[i] : deminFrame;
// Use deminified info wherever possible, merging if necessary so we always print a full frame
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
};
output += $"{Environment.NewLine} {frame}";
}

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

@ -20,11 +20,21 @@ namespace SourcemapToolkit.CallstackDeminifier
return null;
}
List<FunctionMapEntry> result = ParseSourceCode(sourceCodeStreamReader);
foreach (FunctionMapEntry functionMapEntry in result)
List<FunctionMapEntry> result;
try
{
functionMapEntry.DeminfifiedMethodName = GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
result = ParseSourceCode(sourceCodeStreamReader);
foreach (FunctionMapEntry functionMapEntry in result)
{
functionMapEntry.DeminfifiedMethodName = GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
}
}
catch
{
// Failed to parse JavaScript source. This is common as the JS parser does not support ES2015+.
// Continue to regular source map deminification.
result = null;
}
return result;

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

@ -6,6 +6,6 @@
/// This method will deminify a single stack from from a minified stack trace.
/// </summary>
/// <returns>Returns a StackFrameDeminificationResult that contains a stack trace that has been translated to the original source code. The DeminificationError Property indicates if the StackFrame could not be deminified. DeminifiedStackFrame will not be null, but any properties of DeminifiedStackFrame could be null if the value could not be extracted. </returns>
StackFrameDeminificationResult DeminifyStackFrame(StackFrame stackFrame);
StackFrameDeminificationResult DeminifyStackFrame(StackFrame stackFrame, string callerSymbolName);
}
}

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

@ -6,12 +6,12 @@ namespace SourcemapToolkit.CallstackDeminifier
/// <summary>
/// This class only deminfies the method name in a stack frame. It does not depend on having a source map available during runtime.
/// </summary>
internal class SimpleStackFrameDeminifier : IStackFrameDeminifier
internal class MethodNameStackFrameDeminifier : IStackFrameDeminifier
{
protected readonly IFunctionMapConsumer _functionMapConsumer;
protected readonly IFunctionMapStore _functionMapStore;
public SimpleStackFrameDeminifier(IFunctionMapStore functionMapStore, IFunctionMapConsumer functionMapConsumer)
public MethodNameStackFrameDeminifier(IFunctionMapStore functionMapStore, IFunctionMapConsumer functionMapConsumer)
{
_functionMapStore = functionMapStore;
_functionMapConsumer = functionMapConsumer;
@ -19,7 +19,7 @@ namespace SourcemapToolkit.CallstackDeminifier
/// <summary>
/// This method will deminify the method name of a single stack from from a minified stack trace.
/// </summary>
public virtual StackFrameDeminificationResult DeminifyStackFrame(StackFrame stackFrame)
public virtual StackFrameDeminificationResult DeminifyStackFrame(StackFrame stackFrame, string callerSymbolName)
{
StackFrameDeminificationResult result = new StackFrameDeminificationResult
{

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

@ -66,7 +66,7 @@
<Compile Include="IStackFrameDeminifier.cs" />
<Compile Include="IStackTraceParser.cs" />
<Compile Include="KeyValueCache.cs" />
<Compile Include="SimpleStackFrameDeminifier.cs" />
<Compile Include="MethodNameStackFrameDeminifier.cs" />
<Compile Include="SourceMapStore.cs" />
<Compile Include="StackFrame.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

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

@ -52,6 +52,11 @@ namespace SourcemapToolkit.CallstackDeminifier
/// </summary>
public StackFrame DeminifiedStackFrame { get; set; }
/// <summary>
/// The original name of the symbol at this frame's position
/// </summary>
public string DeminifiedSymbolName { get; set; }
/// <summary>
/// An enum indicating if any errors occured when deminifying the stack frame.
/// </summary>

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

@ -9,20 +9,26 @@ namespace SourcemapToolkit.CallstackDeminifier
/// Since source maps take up a large amount of memory, this class consumes considerably
/// more memory than SimpleStackFrame Deminifier during runtime.
/// </summary>
internal class StackFrameDeminifier : SimpleStackFrameDeminifier
internal class StackFrameDeminifier : IStackFrameDeminifier
{
private readonly ISourceMapStore _sourceMapStore;
private readonly MethodNameStackFrameDeminifier _methodNameDeminifier = null;
public StackFrameDeminifier(ISourceMapStore sourceMapStore, IFunctionMapStore functionMapStore, IFunctionMapConsumer functionMapConsumer) : base (functionMapStore, functionMapConsumer)
public StackFrameDeminifier(ISourceMapStore sourceMapStore)
{
_sourceMapStore = sourceMapStore;
}
public StackFrameDeminifier(ISourceMapStore sourceMapStore, IFunctionMapStore functionMapStore, IFunctionMapConsumer functionMapConsumer) : this(sourceMapStore)
{
_methodNameDeminifier = new MethodNameStackFrameDeminifier(functionMapStore, functionMapConsumer);
}
/// <summary>
/// This method will deminify a single stack from from a minified stack trace.
/// </summary>
/// <returns>Returns a StackFrameDeminificationResult that contains a stack trace that has been translated to the original source code. The DeminificationError Property indicates if the StackFrame could not be deminified. DeminifiedStackFrame will not be null, but any properties of DeminifiedStackFrame could be null if the value could not be extracted. </returns>
public override StackFrameDeminificationResult DeminifyStackFrame(StackFrame stackFrame)
public StackFrameDeminificationResult DeminifyStackFrame(StackFrame stackFrame, string callerSymbolName)
{
if (stackFrame == null)
{
@ -32,7 +38,21 @@ namespace SourcemapToolkit.CallstackDeminifier
SourceMap sourceMap = _sourceMapStore.GetSourceMapForUrl(stackFrame.FilePath);
SourcePosition generatedSourcePosition = stackFrame.SourcePosition;
StackFrameDeminificationResult result = base.DeminifyStackFrame(stackFrame);
StackFrameDeminificationResult result = null;
if (_methodNameDeminifier != null)
{
result = _methodNameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName);
}
if (result == null || result.DeminificationError == DeminificationError.NoSourceCodeProvided)
{
result = new StackFrameDeminificationResult
{
DeminificationError = DeminificationError.None,
DeminifiedStackFrame = new StackFrame { MethodName = callerSymbolName }
};
}
if (result.DeminificationError == DeminificationError.None)
{
MappingEntry generatedSourcePositionMappingEntry =
@ -53,9 +73,12 @@ namespace SourcemapToolkit.CallstackDeminifier
result.DeminificationError = DeminificationError.NoMatchingMapingInSourceMap;
}
}
result.DeminifiedStackFrame.FilePath = generatedSourcePositionMappingEntry?.OriginalFileName;
result.DeminifiedStackFrame.SourcePosition = generatedSourcePositionMappingEntry?.OriginalSourcePosition;
else
{
result.DeminifiedStackFrame.FilePath = generatedSourcePositionMappingEntry.OriginalFileName;
result.DeminifiedStackFrame.SourcePosition = generatedSourcePositionMappingEntry.OriginalSourcePosition;
result.DeminifiedSymbolName = generatedSourcePositionMappingEntry.OriginalName;
}
}
return result;

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

@ -49,7 +49,41 @@ namespace SourcemapToolkit.CallstackDeminifier
return new StackTraceDeminifier(stackFrameDeminifier, stackTraceParser);
}
/// <summary>
/// Creates a StackTraceDeminifier which does not depend on JS files, and is ES2015+ compatible.
/// StackTrace deminifiers created with this method will keep source maps cached, and thus use significantly more memory during runtime than the ones generated with GetMethodNameOnlyStackTraceDeminfier.
/// </summary>
/// <param name="sourceMapProvider">Consumers of the API should implement this interface, which provides the source map for a given JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
public static StackTraceDeminifier GetMapOnlyStackTraceDeminfier(ISourceMapProvider sourceMapProvider)
{
return GetMapOnlyStackTraceDeminfier(sourceMapProvider, new StackTraceParser());
}
/// <summary>
/// Creates a StackTraceDeminifier which does not depend on JS files, and is ES2015+ compatible.
/// StackTrace deminifiers created with this method will keep source maps cached, and thus use significantly more memory during runtime than the ones generated with GetMethodNameOnlyStackTraceDeminfier.
/// </summary>
/// <param name="sourceMapProvider">Consumers of the API should implement this interface, which provides the source map for a given JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
/// <param name="stackTraceParser">Consumers of the API should implement this interface, which provides a parser for the stacktrace. Throws ArgumentNullException if the parameter is set to null.</param>
public static StackTraceDeminifier GetMapOnlyStackTraceDeminfier(ISourceMapProvider sourceMapProvider, IStackTraceParser stackTraceParser)
{
if (sourceMapProvider == null)
{
throw new ArgumentNullException(nameof(sourceMapProvider));
}
if (stackTraceParser == null)
{
throw new ArgumentNullException(nameof(stackTraceParser));
}
ISourceMapStore sourceMapStore = new SourceMapStore(sourceMapProvider);
IStackFrameDeminifier stackFrameDeminifier = new StackFrameDeminifier(sourceMapStore);
return new StackTraceDeminifier(stackFrameDeminifier, stackTraceParser);
}
/// <summary>
/// Creates a StackTraceDeminifier that only deminifies the method names. StackTrace deminifiers created with this method will use significantly less memory during runtime than the
/// </summary>
@ -71,7 +105,7 @@ namespace SourcemapToolkit.CallstackDeminifier
ValidateArguments(sourceMapProvider, generatedCodeProvider, stackTraceParser);
SourceMapParser sourceMapParser = new SourceMapParser();
IStackFrameDeminifier stackFrameDeminifier = new SimpleStackFrameDeminifier(new FunctionMapStore(generatedCodeProvider, (url) => sourceMapParser.ParseSourceMap(sourceMapProvider.GetSourceMapContentsForCallstackUrl(url))), new FunctionMapConsumer());
IStackFrameDeminifier stackFrameDeminifier = new MethodNameStackFrameDeminifier(new FunctionMapStore(generatedCodeProvider, (url) => sourceMapParser.ParseSourceMap(sourceMapProvider.GetSourceMapContentsForCallstackUrl(url))), new FunctionMapConsumer());
return new StackTraceDeminifier(stackFrameDeminifier, stackTraceParser);
}

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

@ -28,9 +28,14 @@ namespace SourcemapToolkit.CallstackDeminifier
result.Message = message;
result.DeminifiedStackFrameResults = new List<StackFrameDeminificationResult>();
foreach (StackFrame minifiedStackFrame in result.MinifiedStackFrames)
// 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--)
{
result.DeminifiedStackFrameResults.Add(_stackFrameDeminifier.DeminifyStackFrame(minifiedStackFrame));
var frame = _stackFrameDeminifier.DeminifyStackFrame(result.MinifiedStackFrames[i], callerSymbolName);
callerSymbolName = frame?.DeminifiedSymbolName;
result.DeminifiedStackFrameResults.Insert(0, frame);
}
return result;

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

@ -97,6 +97,7 @@
<Compile Include="KeyValueCacheUnitTests.cs" />
<Compile Include="StackFrameDeminifierUnitTests.cs" />
<Compile Include="StackTraceDeminifierClosureEndToEndTests.cs" />
<Compile Include="StackTraceDeminifierMapOnlyEndToEndTests.cs" />
<Compile Include="StackTraceDeminifierWebpackEndToEndTests.cs" />
<Compile Include="StackTraceDeminifierEndToEndTests.cs" />
<Compile Include="StackTraceDeminifierUnitTests.cs" />

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

@ -29,7 +29,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
if (useSimpleStackFrameDeminier)
{
return new SimpleStackFrameDeminifier(functionMapStore, functionMapConsumer);
return new MethodNameStackFrameDeminifier(functionMapStore, functionMapConsumer);
}
else
{
@ -45,7 +45,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
StackFrame stackFrame = null;
// Act
Assert.Throws<ArgumentNullException>( ()=> stackFrameDeminifier.DeminifyStackFrame(stackFrame));
Assert.Throws<ArgumentNullException>( ()=> stackFrameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName: null));
}
[Fact]
@ -56,7 +56,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
IStackFrameDeminifier stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies();
// Act
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame);
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName: null);
// Assert
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.MethodName);
@ -77,7 +77,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
IStackFrameDeminifier stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies(functionMapStore: functionMapStore, useSimpleStackFrameDeminier:true);
// Act
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame);
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName: null);
// Assert
Assert.Equal(DeminificationError.NoSourceCodeProvided, stackFrameDeminification.DeminificationError);
@ -102,7 +102,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
IStackFrameDeminifier stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies(functionMapStore: functionMapStore, functionMapConsumer: functionMapConsumer, useSimpleStackFrameDeminier: true);
// Act
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame);
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName: null);
// Assert
Assert.Equal(DeminificationError.NoWrapingFunctionFound, stackFrameDeminification.DeminificationError);
@ -128,7 +128,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
IStackFrameDeminifier stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies(functionMapStore: functionMapStore, functionMapConsumer: functionMapConsumer, useSimpleStackFrameDeminier: true);
// Act
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame);
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName: null);
// Assert
Assert.Equal(DeminificationError.None, stackFrameDeminification.DeminificationError);
@ -155,7 +155,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
IStackFrameDeminifier stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies(functionMapStore: functionMapStore, functionMapConsumer: functionMapConsumer);
// Act
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame);
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName: null);
// Assert
Assert.Equal(DeminificationError.NoSourceMap, stackFrameDeminification.DeminificationError);
@ -183,7 +183,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
IStackFrameDeminifier stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies(sourceMapStore: sourceMapStore,functionMapStore: functionMapStore, functionMapConsumer: functionMapConsumer);
// Act
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame);
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName: null);
// Assert
Assert.Equal(DeminificationError.SourceMapFailedToParse, stackFrameDeminification.DeminificationError);
@ -213,7 +213,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
IStackFrameDeminifier stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies(sourceMapStore: sourceMapStore, functionMapStore: functionMapStore, functionMapConsumer: functionMapConsumer);
// Act
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame);
StackFrameDeminificationResult stackFrameDeminification = stackFrameDeminifier.DeminifyStackFrame(stackFrame, callerSymbolName: null);
// Assert
Assert.Equal(DeminificationError.NoMatchingMapingInSourceMap, stackFrameDeminification.DeminificationError);

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

@ -0,0 +1,144 @@
using Xunit;
using Rhino.Mocks;
using SourcemapToolkit.SourcemapParser.UnitTests;
namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
public class StackTraceDeminifierMapOnlyEndToEndTests
{
private const string GeneratedCodeString = "function causeCrash(){function n(){var n=16;n+=2;t(n)}function t(n){n=n+2;i(n)}function i(n){(function(){var t;console.log(t.length+n)})()}window.onerror=function(n,t,i,r,u){u?document.getElementById(\"callstackdisplay\").innerText=u.stack:window.event.error&&(document.getElementById(\"callstackdisplay\").innerText=window.event.error.stack)};n()}window.onload=function(){document.getElementById(\"crashbutton\").addEventListener(\"click\",function(){causeCrash()})};";
private const string SourceMapString = "{\r\n\"version\":3,\r\n\"file\":\"crashcauser.min.js\",\r\n\"lineCount\":1,\r\n\"mappings\":\"AAAAA,SAASA,UAAU,CAAA,CACnB,CACIC,SAASA,CAAM,CAAA,CAAG,CACd,IAAIC,EAAwB,EAAE,CAC9BA,CAAsB,EAAG,CAAC,CAC1BC,CAAM,CAACD,CAAD,CAHQ,CAMlBC,SAASA,CAAM,CAACC,CAAD,CAAQ,CACnBA,CAAM,CAAEA,CAAM,CAAE,CAAC,CACjBC,CAAM,CAACD,CAAD,CAFa,CAKvBC,SAASA,CAAM,CAACD,CAAD,CAAQ,EAClB,QAAQ,CAAA,CAAG,CACR,IAAIE,CAAC,CACLC,OAAOC,IAAI,CAACF,CAACG,OAAQ,CAAEL,CAAZ,CAFH,EAGX,CAAA,CAJkB,CAOvBM,MAAMC,QAAS,CAAEC,QAAS,CAACC,CAAO,CAAEC,CAAM,CAAEC,CAAM,CAAEC,CAAK,CAAEC,CAAjC,CAAwC,CAC1DA,CAAJ,CACIC,QAAQC,eAAe,CAAC,kBAAD,CAAoBC,UAAW,CAAEH,CAAKI,MADjE,CAESX,MAAMY,MAAML,M,GACjBC,QAAQC,eAAe,CAAC,kBAAD,CAAoBC,UAAW,CAAEV,MAAMY,MAAML,MAAMI,OAJhB,C,CAOlEpB,CAAM,CAAA,CA1BV,CA6BAS,MAAMa,OAAQ,CAAEC,QAAS,CAAA,CAAQ,CAC7BN,QAAQC,eAAe,CAAC,aAAD,CAAeM,iBAAiB,CAAC,OAAO,CAAE,QAAS,CAAA,CAAG,CACzEzB,UAAU,CAAA,CAD+D,CAAtB,CAD1B,C\",\r\n\"sources\":[\"crashcauser.js\"],\r\n\"names\":[\"causeCrash\",\"level1\",\"longLocalVariableName\",\"level2\",\"input\",\"level3\",\"x\",\"console\",\"log\",\"length\",\"window\",\"onerror\",\"window.onerror\",\"message\",\"source\",\"lineno\",\"colno\",\"error\",\"document\",\"getElementById\",\"innerText\",\"stack\",\"event\",\"onload\",\"window.onload\",\"addEventListener\"]\r\n}";
private StackTraceDeminifier GetStackTraceDeminifierWithDependencies()
{
ISourceMapProvider sourceMapProvider = MockRepository.GenerateStrictMock<ISourceMapProvider>();
sourceMapProvider.Stub(x => x.GetSourceMapContentsForCallstackUrl("http://localhost:11323/crashcauser.min.js")).Return(UnitTestUtils.StreamReaderFromString(SourceMapString));
ISourceCodeProvider sourceCodeProvider = MockRepository.GenerateStrictMock<ISourceCodeProvider>();
sourceCodeProvider.Stub(x => x.GetSourceCode("http://localhost:11323/crashcauser.min.js")).Return(UnitTestUtils.StreamReaderFromString(GeneratedCodeString));
return StackTraceDeminfierFactory.GetMapOnlyStackTraceDeminfier(sourceMapProvider);
}
private static void ValidateDeminifyStackTraceResults(DeminifyStackTraceResult results)
{
Assert.Equal(6, results.DeminifiedStackFrameResults.Count);
Assert.Equal(DeminificationError.None, results.DeminifiedStackFrameResults[0].DeminificationError);
Assert.Equal(16, results.DeminifiedStackFrameResults[0].DeminifiedStackFrame.SourcePosition.ZeroBasedLineNumber);
Assert.Equal("level3", results.DeminifiedStackFrameResults[1].DeminifiedStackFrame.MethodName);
Assert.Equal("level2", results.DeminifiedStackFrameResults[2].DeminifiedStackFrame.MethodName);
Assert.Equal("level1", results.DeminifiedStackFrameResults[3].DeminifiedStackFrame.MethodName);
Assert.Equal("causeCrash", results.DeminifiedStackFrameResults[4].DeminifiedStackFrame.MethodName);
Assert.Equal(32, results.DeminifiedStackFrameResults[5].DeminifiedStackFrame.SourcePosition.ZeroBasedLineNumber);
}
[Fact]
public void DeminifyStackTrace_ChromeStackTraceString_CorrectDeminificationWhenPossible()
{
// Arrange
StackTraceDeminifier stackTraceDeminifier = GetStackTraceDeminifierWithDependencies();
string chromeStackTrace = @"TypeError: Cannot read property 'length' of undefined
at http://localhost:11323/crashcauser.min.js:1:125
at i (http://localhost:11323/crashcauser.min.js:1:137)
at t (http://localhost:11323/crashcauser.min.js:1:75)
at n (http://localhost:11323/crashcauser.min.js:1:50)
at causeCrash (http://localhost:11323/crashcauser.min.js:1:341)
at HTMLButtonElement.<anonymous> (http://localhost:11323/crashcauser.min.js:1:445)";
// Act
DeminifyStackTraceResult results = stackTraceDeminifier.DeminifyStackTrace(chromeStackTrace);
// Assert
ValidateDeminifyStackTraceResults(results);
}
[Fact]
public void DeminifyStackTrace_FireFoxStackTraceString_CorrectDeminificationWhenPossible()
{
// Arrange
StackTraceDeminifier stackTraceDeminifier = GetStackTraceDeminifierWithDependencies();
string fireFoxStackTrace = @"i/<@http://localhost:11323/crashcauser.min.js:1:112
i@http://localhost:11323/crashcauser.min.js:1:95
t@http://localhost:11323/crashcauser.min.js:1:75
n@http://localhost:11323/crashcauser.min.js:1:50
causeCrash@http://localhost:11323/crashcauser.min.js:1:341
window.onload/<@http://localhost:11323/crashcauser.min.js:1:445";
// Act
DeminifyStackTraceResult results = stackTraceDeminifier.DeminifyStackTrace(fireFoxStackTrace);
// Assert
ValidateDeminifyStackTraceResults(results);
}
[Fact]
public void DeminifyStackTrace_IE11StackTraceString_CorrectDeminificationWhenPossible()
{
// Arrange
StackTraceDeminifier stackTraceDeminifier = GetStackTraceDeminifierWithDependencies();
string ieStackTrace = @"TypeError: Unable to get property 'length' of undefined or null reference
at Anonymous function (http://localhost:11323/crashcauser.min.js:1:112)
at i (http://localhost:11323/crashcauser.min.js:1:95)
at t (http://localhost:11323/crashcauser.min.js:1:75)
at n (http://localhost:11323/crashcauser.min.js:1:50)
at causeCrash (http://localhost:11323/crashcauser.min.js:1:341)
at Anonymous function (http://localhost:11323/crashcauser.min.js:1:445)";
// Act
DeminifyStackTraceResult results = stackTraceDeminifier.DeminifyStackTrace(ieStackTrace);
// Assert
ValidateDeminifyStackTraceResults(results);
}
[Fact]
public void DeminifyStackTrace_EdgeStackTraceString_CorrectDeminificationWhenPossible()
{
// Arrange
StackTraceDeminifier stackTraceDeminifier = GetStackTraceDeminifierWithDependencies();
string dgeStackTrace = @"TypeError: Unable to get property 'length' of undefined or null reference
at Anonymous function (http://localhost:11323/crashcauser.min.js:1:112)
at i (http://localhost:11323/crashcauser.min.js:1:95)
at t (http://localhost:11323/crashcauser.min.js:1:75)
at n (http://localhost:11323/crashcauser.min.js:1:50)
at causeCrash (http://localhost:11323/crashcauser.min.js:1:341)
at Anonymous function (http://localhost:11323/crashcauser.min.js:1:445)";
// Act
DeminifyStackTraceResult results = stackTraceDeminifier.DeminifyStackTrace(dgeStackTrace);
// Assert
ValidateDeminifyStackTraceResults(results);
}
[Fact]
public void DeminifyResultToString_SuccessfullyDeminified_AllLinesDeminified()
{
// Arrange
StackTraceDeminifier stackTraceDeminifier = GetStackTraceDeminifierWithDependencies();
string ieStackTrace = @"TypeError: Unable to get property 'length' of undefined or null reference
at Anonymous function (http://localhost:11323/crashcauser.min.js:1:112)
at i (http://localhost:11323/crashcauser.min.js:1:95)
at t (http://localhost:11323/crashcauser.min.js:1:75)
at n (http://localhost:11323/crashcauser.min.js:1:50)
at causeCrash (http://localhost:11323/crashcauser.min.js:1:341)
at http://localhost:11323/crashcauser.min.js:1:445";
DeminifyStackTraceResult results = stackTraceDeminifier.DeminifyStackTrace(ieStackTrace);
string exectedResult = @"TypeError: Unable to get property 'length' of undefined or null reference
at Anonymous function in crashcauser.js:17:13
at level3 in crashcauser.js:15:10
at level2 in crashcauser.js:11:9
at level1 in crashcauser.js:6:9
at causeCrash in crashcauser.js:28:5
at ? in crashcauser.js:33:9";
// Act
string formatted = results.ToString();
// Assert
Assert.Equal(exectedResult.Replace("\r", ""), formatted.Replace("\r", ""));
}
}
}

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

@ -36,7 +36,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
stackTraceParser.Stub(x => x.ParseStackTrace(stackTraceString, out string message)).Return(minifiedStackFrames).OutRef("Error example");
IStackFrameDeminifier stackFrameDeminifier = MockRepository.GenerateStrictMock<IStackFrameDeminifier>();
stackFrameDeminifier.Stub(x => x.DeminifyStackFrame(minifiedStackFrames[0])).Return(null);
stackFrameDeminifier.Stub(x => x.DeminifyStackFrame(minifiedStackFrames[0], null)).Return(null);
StackTraceDeminifier stackTraceDeminifier = new StackTraceDeminifier(stackFrameDeminifier, stackTraceParser);
@ -60,7 +60,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
IStackFrameDeminifier stackFrameDeminifier = MockRepository.GenerateStrictMock<IStackFrameDeminifier>();
StackFrameDeminificationResult stackFrameDeminification = new StackFrameDeminificationResult();
stackFrameDeminifier.Stub(x => x.DeminifyStackFrame(minifiedStackFrames[0])).Return(stackFrameDeminification);
stackFrameDeminifier.Stub(x => x.DeminifyStackFrame(minifiedStackFrames[0], null)).Return(stackFrameDeminification);
StackTraceDeminifier stackTraceDeminifier = new StackTraceDeminifier(stackFrameDeminifier, stackTraceParser);