Merge pull request #32 from christiang88/ReduceMemoryConsumption

Reduce memory consumption
This commit is contained in:
Christian Gonzalez 2016-11-16 12:02:27 -08:00 коммит произвёл GitHub
Родитель fc99702287 d2a78353b0
Коммит 0ff79ec40a
16 изменённых файлов: 384 добавлений и 391 удалений

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

@ -31,14 +31,18 @@ TypeError: Cannot read property 'length' of undefined
SourcePosition.ZeroBasedLineNumber: 5
```
### Usage
The top level API for call stack deminification is the `StackTraceDeminifier.DeminifyStackTrace` method. For each url that appears in a JavaScript callstack, the library requires the contents of the JavaScript file and corresponding source map in order to determine the original method name and code location. This information is provided by the consumer of the API by implementing the `ISourceMapProvider` and `ISourceCodeProvider` interfaces. These interfaces are expected to return a `StreamReader` that can be used to access the contents of the requested JavaScript code or corresponding source map. A sample usage of the library is shown below
The top level API for call stack deminification is the `StackTraceDeminifier.DeminifyStackTrace` method. For each url that appears in a JavaScript callstack, the library requires the contents of the JavaScript file and corresponding source map in order to determine the original method name and code location. This information is provided by the consumer of the API by implementing the `ISourceMapProvider` and `ISourceCodeProvider` interfaces. These interfaces are expected to return a `StreamReader` that can be used to access the contents of the requested JavaScript code or corresponding source map. A `StackTraceDeminifier` can be instantiated using one of the methods on `StackTraceDeminfierFactory`. A sample usage of the library is shown below.
```csharp
StackTraceDeminifier sourceMapCallstackDeminifier = new StackTraceDeminifier(new SourceMapProvider(), new SourceCodeProvider());
StackTraceDeminifier sourceMapCallstackDeminifier = StackTraceDeminfierFactory.GetStackTraceDeminfier(new SourceMapProvider(), new SourceCodeProvider());
DeminifyStackTraceResult deminifyStackTraceResult = sourceMapCallstackDeminifier.DeminifyStackTrace(callstack)
```
The result of `DeminifyStackTrace` is a `DeminifyStackTraceResult`, which is an object that contains a list of the parsed minified `StackFrame` objects in the `MinifiedStackFrame` property. The `DeminifiedStackFrame` property contains the best guess `StackFrame` object that maps to the `MinifiedStackFrame` element with the same index. Note that any of the properties on a `StackTrace` object may be null if no value could be extracted from the input callstack string or source map.
#### Memory Consumption
Parsed soure maps can take up a lot of memory for large JavaScript files. In order to allow for the `StackTraceDeminifier` to be used on servers with limited memory resources, the `StackTraceDeminfierFactory` exposes a `GetMethodNameOnlyStackTraceDeminfier` method that returns a `StackTraceDeminifier` that does not keep source maps in memory. Since the `StackTraceDeminifier` returned from this method only reads the source map once, the deminified stack frames will only contain the deminified method name and will not contain the original source location.
## Remarks
Browsers return one based line and column numbers, while the source map spec calls for zero based line and column numbers. In order to minimize confusion, line and column numbers are normalized to be zero based throughout the library.

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

@ -30,7 +30,13 @@ 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 List<BindingInformation> Bindings { get; set; }
/// <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; }
/// <summary>
/// Denotes the location of the beginning of this function

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

@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Ajax.Utilities;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
@ -10,7 +12,27 @@ 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)
public List<FunctionMapEntry> GenerateFunctionMap(StreamReader sourceCodeStreamReader, SourceMap sourceMap)
{
if (sourceCodeStreamReader == null || sourceMap == null)
{
return null;
}
List<FunctionMapEntry> result = ParseSourceCode(sourceCodeStreamReader);
foreach (FunctionMapEntry functionMapEntry in result)
{
functionMapEntry.DeminfifiedMethodName = GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
}
return result;
}
/// <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)
{
if (sourceCodeStreamReader == null)
{
@ -32,7 +54,47 @@ namespace SourcemapToolkit.CallstackDeminifier
// Sort in descending order by start position
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>
/// <returns></returns>
internal static string GetDeminifiedMethodNameFromSourceMap(FunctionMapEntry wrappingFunction, SourceMap sourceMap)
{
string methodName = null;
if (wrappingFunction?.Bindings != null && wrappingFunction.Bindings.Count > 0)
{
if (wrappingFunction.Bindings.Count == 2)
{
MappingEntry objectProtoypeMappingEntry =
sourceMap?.GetMappingEntryForGeneratedSourcePosition(wrappingFunction.Bindings[0].SourcePosition);
methodName = objectProtoypeMappingEntry?.OriginalName;
}
MappingEntry mappingEntry =
sourceMap?.GetMappingEntryForGeneratedSourcePosition(wrappingFunction.Bindings.Last().SourcePosition);
if (mappingEntry != null)
{
if (mappingEntry.OriginalName != null)
{
if (methodName != null)
{
methodName = methodName + "." + mappingEntry.OriginalName;
}
else
{
methodName = mappingEntry.OriginalName;
}
}
}
}
return methodName;
}
}
}

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

@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
@ -7,16 +9,15 @@ namespace SourcemapToolkit.CallstackDeminifier
/// </summary>
internal class FunctionMapStore : IFunctionMapStore
{
private readonly ISourceCodeProvider _sourceCodeProvider;
private readonly IFunctionMapGenerator _functionMapGenerator;
private readonly KeyValueCache<string,List<FunctionMapEntry>> _functionMapCache;
public FunctionMapStore(ISourceCodeProvider sourceCodeProvider)
public FunctionMapStore(ISourceCodeProvider sourceCodeProvider, Func<string, SourceMap> sourceMapGetter)
{
_sourceCodeProvider = sourceCodeProvider;
_functionMapGenerator = new FunctionMapGenerator();
_functionMapCache = new KeyValueCache<string, List<FunctionMapEntry>>(sourceCodeUrl => _functionMapGenerator.GenerateFunctionMap(
_sourceCodeProvider.GetSourceCode(sourceCodeUrl)));
sourceCodeProvider.GetSourceCode(sourceCodeUrl),
sourceMapGetter(sourceCodeUrl)));
}
/// <summary>

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

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
@ -9,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);
List<FunctionMapEntry> GenerateFunctionMap(StreamReader sourceCodeStreamReader, SourceMap sourceMap);
}
}

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

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
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
{
protected readonly IFunctionMapConsumer _functionMapConsumer;
protected readonly IFunctionMapStore _functionMapStore;
public SimpleStackFrameDeminifier(IFunctionMapStore functionMapStore, IFunctionMapConsumer functionMapConsumer)
{
_functionMapStore = functionMapStore;
_functionMapConsumer = functionMapConsumer;
}
/// <summary>
/// This method will deminify the method name of a single stack from from a minified stack trace.
/// </summary>
public virtual StackFrame DeminifyStackFrame(StackFrame stackFrame)
{
if (stackFrame == null)
{
throw new ArgumentNullException(nameof(stackFrame));
}
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);
if (functionMap != null)
{
wrappingFunction =
_functionMapConsumer.GetWrappingFunctionForSourceLocation(stackFrame.SourcePosition, functionMap);
}
return new StackFrame {MethodName = wrappingFunction?.DeminfifiedMethodName};
}
}
}

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

@ -65,10 +65,12 @@
<Compile Include="IStackFrameDeminifier.cs" />
<Compile Include="IStackTraceParser.cs" />
<Compile Include="KeyValueCache.cs" />
<Compile Include="SimpleStackFrameDeminifier.cs" />
<Compile Include="SourceMapStore.cs" />
<Compile Include="StackFrame.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StackFrameDeminifier.cs" />
<Compile Include="StackTraceDeminfierFactory.cs" />
<Compile Include="StackTraceDeminifier.cs" />
<Compile Include="StackTraceParser.cs" />
</ItemGroup>

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

@ -1,94 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
/// <summary>
/// Class responsible for deminifying a single stack frame in a minified stack trace.
/// This method of deminification relies on a source map being available at runtime.
/// 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 : IStackFrameDeminifier
internal class StackFrameDeminifier : SimpleStackFrameDeminifier
{
private readonly IFunctionMapConsumer _functionMapConsumer;
private readonly IFunctionMapStore _functionMapStore;
private readonly ISourceMapStore _sourceMapStore;
public StackFrameDeminifier(ISourceMapStore sourceMapStore, IFunctionMapStore functionMapStore, IFunctionMapConsumer functionMapConsumer)
public StackFrameDeminifier(ISourceMapStore sourceMapStore, IFunctionMapStore functionMapStore, IFunctionMapConsumer functionMapConsumer) : base (functionMapStore, functionMapConsumer)
{
_functionMapStore = functionMapStore;
_sourceMapStore = sourceMapStore;
_functionMapConsumer = functionMapConsumer;
}
/// <summary>
/// This method will deminify a single stack from from a minified stack trace.
/// </summary>
/// <returns>Returns a stack trace that has been translated to a best guess of the original source code. Any of the fields in the stack frame may be null</returns>
public StackFrame DeminifyStackFrame(StackFrame stackFrame)
public override StackFrame DeminifyStackFrame(StackFrame stackFrame)
{
if (stackFrame == null)
{
throw new ArgumentNullException(nameof(stackFrame));
}
FunctionMapEntry wrappingFunction = null;
SourceMap sourceMap = _sourceMapStore.GetSourceMapForUrl(stackFrame.FilePath);
SourcePosition generatedSourcePosition = stackFrame.SourcePosition;
// 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);
if (functionMap != null)
{
wrappingFunction =
_functionMapConsumer.GetWrappingFunctionForSourceLocation(stackFrame.SourcePosition, functionMap);
}
return ExtractFrameInformationFromSourceMap(wrappingFunction, sourceMap, stackFrame.SourcePosition);
}
/// <summary>
/// Gets the information necessary for a deminified stack frame from the relevant source map.
/// </summary>
/// <param name="wrappingFunction">The function that wraps the current stack frame location</param>
/// <param name="sourceMap">The relevant source map for this generated code</param>
/// <param name="generatedSourcePosition">The location that should be translated to original source code location in the deminified stack frame.</param>
/// <returns>Returns a StackFrame object with best guess values for each property. Any of the properties may be null if no match was found.</returns>
internal static StackFrame ExtractFrameInformationFromSourceMap(FunctionMapEntry wrappingFunction, SourceMap sourceMap, SourcePosition generatedSourcePosition)
{
StackFrame result = new StackFrame();
if (wrappingFunction?.Bindings != null && wrappingFunction.Bindings.Count > 0)
{
string methodName = null;
if (wrappingFunction.Bindings.Count == 2)
{
MappingEntry objectProtoypeMappingEntry =
sourceMap?.GetMappingEntryForGeneratedSourcePosition(wrappingFunction.Bindings[0].SourcePosition);
methodName = objectProtoypeMappingEntry?.OriginalName;
}
MappingEntry mappingEntry =
sourceMap?.GetMappingEntryForGeneratedSourcePosition(wrappingFunction.Bindings.Last().SourcePosition);
if (mappingEntry != null)
{
if (mappingEntry.OriginalName != null)
{
if (methodName != null)
{
methodName = methodName + "." + mappingEntry.OriginalName;
}
else
{
methodName = mappingEntry.OriginalName;
}
}
result.MethodName = methodName;
}
}
StackFrame result = base.DeminifyStackFrame(stackFrame);
MappingEntry generatedSourcePositionMappingEntry = sourceMap?.GetMappingEntryForGeneratedSourcePosition(generatedSourcePosition);
result.FilePath = generatedSourcePositionMappingEntry?.OriginalFileName;

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

@ -0,0 +1,56 @@
using System;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
public class StackTraceDeminfierFactory
{
private static void ValidateArguments(ISourceMapProvider sourceMapProvider, ISourceCodeProvider generatedCodeProvider)
{
if (sourceMapProvider == null)
{
throw new ArgumentNullException(nameof(sourceMapProvider));
}
if (generatedCodeProvider == null)
{
throw new ArgumentNullException(nameof(generatedCodeProvider));
}
}
/// <summary>
/// Creates a StackTraceDeminifier with full capabilities. 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="generatedCodeProvider">Consumers of the API should implement this interface, which provides the contents of a JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
public static StackTraceDeminifier GetStackTraceDeminfier(ISourceMapProvider sourceMapProvider, ISourceCodeProvider generatedCodeProvider)
{
ValidateArguments(sourceMapProvider, generatedCodeProvider);
ISourceMapStore sourceMapStore = new SourceMapStore(sourceMapProvider);
IStackFrameDeminifier stackFrameDeminifier = new StackFrameDeminifier(sourceMapStore,
new FunctionMapStore(generatedCodeProvider, sourceMapStore.GetSourceMapForUrl), new FunctionMapConsumer());
IStackTraceParser stackTraceParser = new StackTraceParser();
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>
/// <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="generatedCodeProvider">Consumers of the API should implement this interface, which provides the contents of a JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
public static StackTraceDeminifier GetMethodNameOnlyStackTraceDeminfier(ISourceMapProvider sourceMapProvider, ISourceCodeProvider generatedCodeProvider)
{
ValidateArguments(sourceMapProvider, generatedCodeProvider);
SourceMapParser sourceMapParser = new SourceMapParser();
IStackFrameDeminifier stackFrameDeminifier = new SimpleStackFrameDeminifier(new FunctionMapStore(generatedCodeProvider, (url) => sourceMapParser.ParseSourceMap(sourceMapProvider.GetSourceMapContentsForCallstackUrl(url))), new FunctionMapConsumer());
IStackTraceParser stackTraceParser = new StackTraceParser();
return new StackTraceDeminifier(stackFrameDeminifier, stackTraceParser);
}
}
}

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

@ -1,38 +1,17 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace SourcemapToolkit.CallstackDeminifier
{
/// <summary>
/// Public API for parsing and deminifying browser stack traces.
/// This class is responsible for parsing a callstack string into
/// a list of StackFrame objects and providing the deminified version
/// of the stack frame.
/// </summary>
public class StackTraceDeminifier
{
private readonly IStackFrameDeminifier _stackFrameDeminifier;
private readonly IStackTraceParser _stackTraceParser;
/// <summary>
/// This class is responsible for parsing a callstack string into
/// a list of StackFrame objects and providing the deminified version
/// of the stack frame.
/// </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="generatedCodeProvider">Consumers of the API should implement this interface, which provides the contents of a JavaScript file. Throws ArgumentNullException if the parameter is set to null.</param>
public StackTraceDeminifier(ISourceMapProvider sourceMapProvider, ISourceCodeProvider generatedCodeProvider)
: this(new StackFrameDeminifier(new SourceMapStore(sourceMapProvider),
new FunctionMapStore(generatedCodeProvider), new FunctionMapConsumer()), new StackTraceParser())
{
if (sourceMapProvider == null)
{
throw new ArgumentNullException(nameof(sourceMapProvider));
}
if (generatedCodeProvider == null)
{
throw new ArgumentNullException(nameof(generatedCodeProvider));
}
}
internal StackTraceDeminifier(IStackFrameDeminifier stackFrameDeminifier, IStackTraceParser stackTraceParser)
{
_stackFrameDeminifier = stackFrameDeminifier;

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

@ -2,6 +2,8 @@
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SourcemapToolkit.SourcemapParser.UnitTests;
using Rhino.Mocks;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
@ -9,41 +11,56 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
public class FunctionMapGeneratorUnitTests
{
[TestMethod]
public void GenerateFunctionMap_NullInput_ReturnsNull()
public void GenerateFunctionMap_NullSourceMap_ReturnsNull()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(null);
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode), null);
// Assert
Assert.IsNull(functionMap);
}
[TestMethod]
public void ParseSourceCode_NullInput_ReturnsNull()
{
// Arrange
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(null);
// Assert
Assert.IsNull(functionMap);
}
[TestMethod]
public void GenerateFunctionMap_NoFunctionsInSource_EmptyFunctionList()
public void ParseSourceCode_NoFunctionsInSource_EmptyFunctionList()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "bar();";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(0, functionMap.Count);
}
[TestMethod]
public void GenerateFunctionMap_SingleLineFunctionInSource_CorrectZeroBasedColumnNumbers()
public void ParseSourceCode_SingleLineFunctionInSource_CorrectZeroBasedColumnNumbers()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "function foo(){bar();}";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(1, functionMap.Count);
@ -57,15 +74,15 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_MultiLineFunctionInSource_CorrectColumnAndZeroBasedLineNumbers()
public void ParseSourceCode_MultiLineFunctionInSource_CorrectColumnAndZeroBasedLineNumbers()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "function foo()" + Environment.NewLine + "{" + Environment.NewLine + "bar();" +
Environment.NewLine + "}";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(1, functionMap.Count);
@ -79,14 +96,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_TwoSingleLineFunctions_TwoFunctionMapEntries()
public void ParseSourceCode_TwoSingleLineFunctions_TwoFunctionMapEntries()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "function foo(){bar();}function bar(){baz();}";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(2, functionMap.Count);
@ -109,14 +126,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_TwoNestedSingleLineFunctions_TwoFunctionMapEntries()
public void ParseSourceCode_TwoNestedSingleLineFunctions_TwoFunctionMapEntries()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "function foo(){function bar(){baz();}}";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(2, functionMap.Count);
@ -139,14 +156,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_FunctionAssignedToVariable_FunctionMapEntryGenerated()
public void ParseSourceCode_FunctionAssignedToVariable_FunctionMapEntryGenerated()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){bar();}";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(1, functionMap.Count);
@ -161,14 +178,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_StaticMethod_FunctionMapEntryGenerated()
public void ParseSourceCode_StaticMethod_FunctionMapEntryGenerated()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){};foo.bar = function() { baz(); }";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(2, functionMap.Count);
@ -191,14 +208,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_InstanceMethod_FunctionMapEntryGenerated()
public void ParseSourceCode_InstanceMethod_FunctionMapEntryGenerated()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){} foo.prototype.bar = function () { baz(); }";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(2, functionMap.Count);
@ -221,14 +238,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_InstanceMethodInObjectInitializer_FunctionMapEntryGenerated()
public void ParseSourceCode_InstanceMethodInObjectInitializer_FunctionMapEntryGenerated()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){} foo.prototype = { bar: function () { baz(); } }";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(2, functionMap.Count);
@ -254,14 +271,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_FunctionAssignedToVariableAndHasName_FunctionMapEntryGeneratedForVariableName()
public void ParseSourceCode_FunctionAssignedToVariableAndHasName_FunctionMapEntryGeneratedForVariableName()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function myCoolFunctionName(){ bar(); }";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(1, functionMap.Count);
@ -276,14 +293,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_StaticMethodAndFunctionHasName_FunctionMapEntryGeneratedForPropertyName()
public void ParseSourceCode_StaticMethodAndFunctionHasName_FunctionMapEntryGeneratedForPropertyName()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){};foo.bar = function myCoolFunctionName() { baz(); }";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(2, functionMap.Count);
@ -306,14 +323,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_InstanceMethodAndFunctionHasName_FunctionMapEntryGeneratedForObjectPrototype()
public void ParseSourceCode_InstanceMethodAndFunctionHasName_FunctionMapEntryGeneratedForObjectPrototype()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){} foo.prototype.bar = function myCoolFunctionName() { baz(); } }";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(2, functionMap.Count);
@ -336,14 +353,14 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
}
[TestMethod]
public void GenerateFunctionMap_InstanceMethodWithObjectInitializerAndFunctionHasName_FunctionMapEntryGeneratedForObjectPrototype()
public void ParseSourceCode_InstanceMethodWithObjectInitializerAndFunctionHasName_FunctionMapEntryGeneratedForObjectPrototype()
{
// Arrange
IFunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
FunctionMapGenerator functionMapGenerator = new FunctionMapGenerator();
string sourceCode = "var foo = function(){} foo.prototype = { bar: function myCoolFunctionName() { baz(); } }";
// Act
List<FunctionMapEntry> functionMap = functionMapGenerator.GenerateFunctionMap(UnitTestUtils.StreamReaderFromString(sourceCode));
List<FunctionMapEntry> functionMap = functionMapGenerator.ParseSourceCode(UnitTestUtils.StreamReaderFromString(sourceCode));
// Assert
Assert.AreEqual(2, functionMap.Count);
@ -367,5 +384,128 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
Assert.AreEqual(20, functionMap[1].StartSourcePosition.ZeroBasedColumnNumber);
Assert.AreEqual(22, functionMap[1].EndSourcePosition.ZeroBasedColumnNumber);
}
[TestMethod]
public void GetDeminifiedMethodNameFromSourceMap_NoBinding_ReturnNullMethodName()
{
// Arrange
FunctionMapEntry functionMapEntry = new FunctionMapEntry();
SourceMap sourceMap = MockRepository.GenerateStub<SourceMap>();
// Act
string result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
// Assert
Assert.IsNull(result);
sourceMap.VerifyAllExpectations();
}
[TestMethod]
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.IsNull(result);
sourceMap.VerifyAllExpectations();
}
[TestMethod]
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.AreEqual("foo", result);
sourceMap.VerifyAllExpectations();
}
[TestMethod]
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.AreEqual("bar.baz", result);
sourceMap.VerifyAllExpectations();
}
}
}

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

@ -70,7 +70,6 @@
<Compile Include="StackTraceDeminifierUnitTests.cs" />
<Compile Include="StackTraceParserUnitTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StackFrameDeminifierUnitTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SourceMapToolkit.CallstackDeminifier\SourcemapToolkit.CallstackDeminifier.csproj">

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

@ -1,220 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier.UnitTests
{
[TestClass]
public class StackFrameDeminifierUnitTests
{
private IStackFrameDeminifier GetStackFrameDeminifierWithMockDependencies(ISourceMapStore sourceMapStore = null, IFunctionMapStore functionMapStore = null, IFunctionMapConsumer functionMapConsumer = null)
{
if (sourceMapStore == null)
{
sourceMapStore = MockRepository.GenerateStrictMock<ISourceMapStore>();
}
if (functionMapStore == null)
{
functionMapStore = MockRepository.GenerateStrictMock<IFunctionMapStore>();
}
if (functionMapConsumer == null)
{
functionMapConsumer = MockRepository.GenerateStrictMock<IFunctionMapConsumer>();
}
return new StackFrameDeminifier(sourceMapStore, functionMapStore, functionMapConsumer);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void DeminifyStackFrame_NullInputStackFrame_ThrowsException()
{
// Arrange
IStackFrameDeminifier stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies();
StackFrame stackFrame = null;
// Act
StackFrame deminifiedStackFrame = stackFrameDeminifier.DeminifyStackFrame(stackFrame);
}
[TestMethod]
public void ExtractFrameInformationFromSourceMap_NullInputs_DoesNotThrowException()
{
// Arrange
FunctionMapEntry functionMapEntry = null;
SourceMap sourceMap = null;
SourcePosition generatedSourcePosition = null;
// Act
StackFrame deminifiedStackFrame = StackFrameDeminifier.ExtractFrameInformationFromSourceMap(functionMapEntry, sourceMap, generatedSourcePosition);
// Assert
Assert.IsNull(deminifiedStackFrame.MethodName);
Assert.IsNull(deminifiedStackFrame.SourcePosition);
Assert.IsNull(deminifiedStackFrame.FilePath);
}
[TestMethod]
public void ExtractFrameInformationFromSourceMap_NoBinding_ReturnNullMethodName()
{
// Arrange
FunctionMapEntry functionMapEntry = new FunctionMapEntry();
SourceMap sourceMap = MockRepository.GenerateStub<SourceMap>();
SourcePosition generatedSourcePosition = new SourcePosition();
// Act
StackFrame deminifiedStackFrame = StackFrameDeminifier.ExtractFrameInformationFromSourceMap(functionMapEntry, sourceMap, generatedSourcePosition);
// Assert
Assert.IsNull(deminifiedStackFrame.MethodName);
sourceMap.VerifyAllExpectations();
}
[TestMethod]
public void ExtractFrameInformationFromSourceMap_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);
SourcePosition generatedSourcePosition = new SourcePosition();
// Act
StackFrame deminifiedStackFrame = StackFrameDeminifier.ExtractFrameInformationFromSourceMap(functionMapEntry, sourceMap, generatedSourcePosition);
// Assert
Assert.IsNull(deminifiedStackFrame.MethodName);
sourceMap.VerifyAllExpectations();
}
[TestMethod]
public void ExtractFrameInformationFromSourceMap_HasSingleBindingMatchingMapping_ReturnsStackFrameWithMethodName()
{
// Arrange
FunctionMapEntry functionMapEntry = new FunctionMapEntry
{
Bindings =
new List<BindingInformation>
{
new BindingInformation
{
SourcePosition = new SourcePosition {ZeroBasedLineNumber = 5, ZeroBasedColumnNumber = 8}
}
}
};
SourcePosition generatedSourcePosition = new SourcePosition
{
ZeroBasedColumnNumber = 25,
ZeroBasedLineNumber = 85
};
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
StackFrame deminifiedStackFrame = StackFrameDeminifier.ExtractFrameInformationFromSourceMap(functionMapEntry, sourceMap, generatedSourcePosition);
// Assert
Assert.AreEqual("foo", deminifiedStackFrame.MethodName);
sourceMap.VerifyAllExpectations();
}
[TestMethod]
public void ExtractFrameInformationFromSourceMap_MatchingMappingMultipleBindings_ReturnsStackFrameWithFullBinding()
{
// 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}
}
}
};
SourcePosition generatedSourcePosition = new SourcePosition {ZeroBasedColumnNumber = 39, ZeroBasedLineNumber = 31};
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
StackFrame deminifiedStackFrame = StackFrameDeminifier.ExtractFrameInformationFromSourceMap(functionMapEntry, sourceMap, generatedSourcePosition);
// Assert
Assert.AreEqual("bar.baz", deminifiedStackFrame.MethodName);
sourceMap.VerifyAllExpectations();
}
[TestMethod]
public void ExtractFrameInformationFromSourceMap_HasMatchingGeneratedPositionMapping_ReturnsStackFrameWithSourcePositionAndFileName()
{
// Arrange
FunctionMapEntry functionMapEntry = null;
SourcePosition generatedSourcePosition = new SourcePosition
{
ZeroBasedColumnNumber = 25,
ZeroBasedLineNumber = 85
};
SourceMap sourceMap = MockRepository.GenerateStub<SourceMap>();
sourceMap.Stub(x => x.GetMappingEntryForGeneratedSourcePosition(generatedSourcePosition)).Return(new MappingEntry
{
OriginalSourcePosition = new SourcePosition { ZeroBasedColumnNumber = 10, ZeroBasedLineNumber = 20 }
});
// Act
StackFrame deminifiedStackFrame = StackFrameDeminifier.ExtractFrameInformationFromSourceMap(functionMapEntry, sourceMap, generatedSourcePosition);
// Assert
Assert.AreEqual(10, deminifiedStackFrame.SourcePosition.ZeroBasedColumnNumber);
Assert.AreEqual(20, deminifiedStackFrame.SourcePosition.ZeroBasedLineNumber);
}
}
}

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

@ -20,7 +20,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
ISourceCodeProvider sourceCodeProvider = MockRepository.GenerateStrictMock<ISourceCodeProvider>();
sourceCodeProvider.Stub(x => x.GetSourceCode("http://localhost:11323/crashcauser.js")).Return(UnitTestUtils.StreamReaderFromString(GeneratedCodeString));
return new StackTraceDeminifier(sourceMapProvider, sourceCodeProvider);
return StackTraceDeminfierFactory.GetStackTraceDeminfier(sourceMapProvider, sourceCodeProvider);
}
private void ValidateDeminifyStackTraceResults(DeminifyStackTraceResult results)

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

@ -18,8 +18,8 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
ISourceCodeProvider sourceCodeProvider = MockRepository.GenerateStrictMock<ISourceCodeProvider>();
sourceCodeProvider.Stub(x => x.GetSourceCode("http://localhost:11323/crashcauser.min.js")).Return(UnitTestUtils.StreamReaderFromString(GeneratedCodeString));
return new StackTraceDeminifier(sourceMapProvider, sourceCodeProvider);
}
return StackTraceDeminfierFactory.GetStackTraceDeminfier(sourceMapProvider, sourceCodeProvider);
}
private static void ValidateDeminifyStackTraceResults(DeminifyStackTraceResult results)
{

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

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
@ -8,30 +7,6 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
[TestClass]
public class StackTraceDeminifierUnitTests
{
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void StackTraceDeminifier_NullSourceMapProvider_ThrowsException()
{
// Arrange
ISourceCodeProvider sourceCodeProvider = MockRepository.GenerateStrictMock<ISourceCodeProvider>();
ISourceMapProvider sourceMapProvider = null;
// Act
StackTraceDeminifier stackTraceDeminifier = new StackTraceDeminifier(sourceMapProvider, sourceCodeProvider);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void StackTraceDeminifier_NullSourceCodeProvider_ThrowsException()
{
// Arrange
ISourceCodeProvider sourceCodeProvider = null;
ISourceMapProvider sourceMapProvider = MockRepository.GenerateStrictMock<ISourceMapProvider>();
// Act
StackTraceDeminifier stackTraceDeminifier = new StackTraceDeminifier(sourceMapProvider, sourceCodeProvider);
}
[TestMethod]
public void DeminifyStackTrace_UnableToParseStackTraceString_ReturnsEmptyList()
{