Giter VIP home page Giter VIP logo

nsubstitute.analyzers's Introduction

NSubstitute

Build status Nuget

Visit the NSubstitute website for more information.

What is it?

NSubstitute is designed as a friendly substitute for .NET mocking libraries.

It is an attempt to satisfy our craving for a mocking library with a succinct syntax that helps us keep the focus on the intention of our tests, rather than on the configuration of our test doubles. We've tried to make the most frequently required operations obvious and easy to use, keeping less usual scenarios discoverable and accessible, and all the while maintaining as much natural language as possible.

Perfect for those new to testing, and for others who would just like to to get their tests written with less noise and fewer lambdas.

Installation

Getting help

If you have questions, feature requests or feedback on NSubstitute please raise an issue on our project site. All questions are welcome via our project site, but for "how-to"-style questions you can also try StackOverflow with the [nsubstitute] tag, which often leads to very good answers from the larger programming community. StackOverflow is especially useful if your question also relates to other libraries that our team may not be as familiar with (e.g. NSubstitute with Entity Framework). You can also head on over to the NSubstitute discussion group if you prefer.

Basic use

Let's say we have a basic calculator interface:

public interface ICalculator
{
    int Add(int a, int b);
    string Mode { get; set; }
    event Action PoweringUp;
}

We can ask NSubstitute to create a substitute instance for this type. We could ask for a stub, mock, fake, spy, test double etc., but why bother when we just want to substitute an instance we have some control over?

_calculator = Substitute.For<ICalculator>();

โš ๏ธ Note: NSubstitute will only work properly with interfaces or with virtual members of classes. Be careful substituting for classes with non-virtual members. See Creating a substitute for more information.

Now we can tell our substitute to return a value for a call:

_calculator.Add(1, 2).Returns(3);
Assert.That(_calculator.Add(1, 2), Is.EqualTo(3));

We can check that our substitute received a call, and did not receive others:

_calculator.Add(1, 2);
_calculator.Received().Add(1, 2);
_calculator.DidNotReceive().Add(5, 7);

If our Received() assertion fails, NSubstitute tries to give us some help as to what the problem might be:

NSubstitute.Exceptions.ReceivedCallsException : Expected to receive a call matching:
    Add(1, 2)
Actually received no matching calls.
Received 2 non-matching calls (non-matching arguments indicated with '*' characters):
    Add(1, *5*)
    Add(*4*, *7*)

We can also work with properties using the Returns syntax we use for methods, or just stick with plain old property setters (for read/write properties):

_calculator.Mode.Returns("DEC");
Assert.That(_calculator.Mode, Is.EqualTo("DEC"));

_calculator.Mode = "HEX";
Assert.That(_calculator.Mode, Is.EqualTo("HEX"));

NSubstitute supports argument matching for setting return values and asserting a call was received:

_calculator.Add(10, -5);
_calculator.Received().Add(10, Arg.Any<int>());
_calculator.Received().Add(10, Arg.Is<int>(x => x < 0));

We can use argument matching as well as passing a function to Returns() to get some more behaviour out of our substitute (possibly too much, but that's your call):

_calculator
   .Add(Arg.Any<int>(), Arg.Any<int>())
   .Returns(x => (int)x[0] + (int)x[1]);
Assert.That(_calculator.Add(5, 10), Is.EqualTo(15));

Returns() can also be called with multiple arguments to set up a sequence of return values.

_calculator.Mode.Returns("HEX", "DEC", "BIN");
Assert.That(_calculator.Mode, Is.EqualTo("HEX"));
Assert.That(_calculator.Mode, Is.EqualTo("DEC"));
Assert.That(_calculator.Mode, Is.EqualTo("BIN"));

Finally, we can raise events on our substitutes (unfortunately C# dramatically restricts the extent to which this syntax can be cleaned up):

bool eventWasRaised = false;
_calculator.PoweringUp += () => eventWasRaised = true;

_calculator.PoweringUp += Raise.Event<Action>();
Assert.That(eventWasRaised);

Building

NSubstitute and its tests can be compiled and run using Visual Studio, Visual Studio Code or any other editor with .NET support. Note that some tests are marked [Pending] and are not meant to pass at present, so it is a good idea to exclude tests in the Pending category from test runs.

There are also build scripts in the ./build directory for command line builds, and CI configurations in the project root.

To do full builds you'll also need Ruby, as the jekyll gem is used to generate the website.

Other libraries you may be interested in

  • Moq: the original Arrange-Act-Assert mocking library for .NET, and a big source of inspiration for NSubstitute.
  • FakeItEasy: another modern mocking library for .NET. If you're not sold on NSubstitute's syntax, try FIE!
  • substitute.js: a mocking library for TypeScript inspired by NSubstitute's syntax (@fluffy-spoon/substitute on NPM)

nsubstitute.analyzers's People

Contributors

bdovaz avatar dtchepak avatar tpodolak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

nsubstitute.analyzers's Issues

Detect Arg matcher misuse

People often use Arg.Any<int>() in real calls (rather than .Returns stub or .Received assertion) to represent "I don't mind what input is used for this test".

This case is show in the documentation. There is also an example in this post (search for "Another example").

This causes problems for NSubstitute because it tries to use these argument matchers the next time it gets a call (it will consider the next call to be an attempt to stub, rather than a real call), and it can cause confusing test behaviour or test errors.

Proposed title:

Argument matcher used without call to .Returns or .Received

Proposed description (from docs):

Argument matchers should only be used when setting return values or checking received calls. Using Arg.Is or Arg.Any without a call to .Returns or Received() can cause your tests to behave in unexpected ways.

Detect wrong usages of callInfo argument access

Analyzers should be able to detect common mistakes when accessing invocation arguments via CallInfo.

using System;
using NSubstitute;

namespace MyNamespace
{
    public interface Foo
    {
        int Bar(int x);
    }

    public class Bar
    {
    }

    public class FooTests
    {
        public void Test()
        {
            var substitute = NSubstitute.Substitute.For<Foo>();
            substitute.Bar(Arg.Any<int>()).Returns(callInfo =>
            {
                var x = (Bar)callInfo[0]; // invalid cast
                var xx = callInfo[0] as Bar; // invalid cast
                var xxx = (Bar)callInfo.Args()[0]; // invalid cast
                var xxxx = callInfo.Args()[0] as Bar; // invalid cast
                var xxxxx = callInfo[1]; // out of bounds
                var xxxxxx = callInfo.Args()[1]; // out of bounds
                var xxxxxxx = callInfo.ArgTypes()[1]; // out of bounds
                callInfo[0] = 1; // no ref or out method
                callInfo[0] = new object(); //  no ref or out method, wrong type assigned
                return 1;
            });
        }
    }
}

Revisit/refactor test cases approach

Usually, the analyzer has multiple test classes which test different usages of NSubstitute. For instance, in NonVirtualSetupAnalyzer we have separate test classes for usage of Returns method as an extension and other for an ordinary method(and many more). Test cases for that kind of stuff are usually the same, however currently TestCases attributes are copied/duplicated over all test classes for the given analyzer. This approach was originally chosen as different usages of NSubstitute might report a different location of diagnostic - so it was/is not possible to pass the same diagnostic location for all test classes.
Now as codebase is much larger than originally, it is difficult to maintain a proper set of tests - thus we should move test cases to the base test class of given analyzer and remove the duplication. This will require to mark the location of the diagnostic in the test itself (as we will have to drop expectedLine from the test cases) - "marker" approach is used for instance in Roslynator so we can take a look at its implementation

ArgumentException on ReEntrantSetupAnalyzer on first load

Using 1.0.0 C#.

On first adding the package, or first loading the project in VS, I get the following error (anonymised slightly). It seems to go away after running a build.

Error	AD0001	Analyzer 'NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantSetupAnalyzer' threw an exception of type 'System.ArgumentException' with message 'SyntaxTree is not part of the compilation
Parameter name: syntaxTree'.	SampleTests		1	Active	Analyzer 'NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantSetupAnalyzer' threw the following exception:
'Exception occurred with following context:
Compilation: SampleTests
SyntaxTree: C:\dev\git\sample\SampleTests\SampleFixture.cs
SyntaxNode: _sample.Actions ... [InvocationExpressionSyntax]@[4394..4580) (99,12)-(99,198)

System.ArgumentException: SyntaxTree is not part of the compilation
Parameter name: syntaxTree
   at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetSemanticModel(SyntaxTree syntaxTree, Boolean ignoreAccessibility)
   at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.CommonGetSemanticModel(SyntaxTree syntaxTree, Boolean ignoreAccessibility)
   at NSubstitute.Analyzers.Shared.DiagnosticAnalyzers.AbstractReEntrantCallFinder.<GetRelatedNodes>d__3.MoveNext()
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.VisitRelatedSymbols(SyntaxNode syntaxNode)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIdentifierName(IdentifierNameSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitPropertyDeclaration(PropertyDeclarationSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.PropertyDeclarationSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.InterfaceDeclarationSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.VisitRelatedSymbols(SyntaxNode syntaxNode)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIdentifierName(IdentifierNameSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitMethodDeclaration(MethodDeclarationSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.MethodDeclarationSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.VisitRelatedSymbols(SyntaxNode syntaxNode)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIdentifierName(IdentifierNameSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.VisitInvocationExpression(InvocationExpressionSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitEqualsValueClause(EqualsValueClauseSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.EqualsValueClauseSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitVariableDeclarator(VariableDeclaratorSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclaratorSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.VisitRelatedSymbols(SyntaxNode syntaxNode)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIdentifierName(IdentifierNameSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitInitializerExpression(InitializerExpressionSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitImplicitArrayCreationExpression(ImplicitArrayCreationExpressionSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.GetReEntrantSymbols(Compilation compilation, SyntaxNode rootNode)
   at NSubstitute.Analyzers.Shared.DiagnosticAnalyzers.AbstractReEntrantCallFinder.GetReEntrantCalls(Compilation compilation, SyntaxNode rootNode)
   at NSubstitute.Analyzers.Shared.DiagnosticAnalyzers.AbstractReEntrantSetupAnalyzer`2.AnalyzeInvocation(SyntaxNodeAnalysisContext syntaxNodeContext)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c__44`1.<ExecuteSyntaxNodeAction>b__44_0(ValueTuple`2 data)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info)
-----
'.

Suggestion: add information on how to fix NS002 Unused received check

Current NS002 message looks like:

Error | NS002 | Unused received check.

I'm not sure what the standard is for analyser messages in general, but I think it might be good to include some information on how to fix this in the message.

For example:

Error | NS002 | Unused received check. To fix, make sure there is a call after `Received`. 
Correct: `sub.Received().SomeCall();`. Incorrect: `sub.Received();`

Speed up appveyor build by disabling autoreporters

Even though automatic test runs are disabled in appveyor config, the tests are still being picked up by the appveyor which drastically increases the build time. We have to disable so called autoreporters in order for appveyor to stop analyzing test results. More info can be found here

Investigate possibility to infer "real" type of substitute to improve non-virtual member calls detection

As reported in here nsubstitute/NSubstitute#499 (comment) NSubstitute.Analyzers fails to find non-virtual member calls when substituting for class but assigning it to interface e.g

IUnitOfWorkCoreCqrs cqrsUow = Substitute.ForPartsOf<CqrsUow>();
cqrsUow.Received(1).RegisterAfterCommitCallBackItem(Arg.Any<Func<Task>>());

In order to correctly report non-virtual member calls, we have to find actual substitute assignment

IUnitOfWorkCoreCqrs cqrsUow = Substitute.ForPartsOf<CqrsUow>();

or

IUnitOfWorkCoreCqrs cqrsUow = null;
cqrsUow  = Substitute.ForPartsOf<CqrsUow>();

based on the type infered from expression, we should be able to enhance CanBeSetuped method.

Possible roslyn API to use

var dataFlowAnalysis = syntaxNodeContext.SemanticModel.AnalyzeDataFlow(accessedMember);

Possible problems

  • performace degradation, we might decide not to implement it

Add convention tests to enforce project conventions

As for today, two conventions are required to implement

  • making sure that all NSubstitute.Analyzers types are internal
  • making sure that all of the analyzers are properly registered with DiagnosticAnalyzerAttribute

Rule NS2003 does not detect InternalsVisibleTo correctly

I do have the attribute InternalsVisibleTo in the file AssemblyInfo.cs

[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

However, the analyser adds a warning and suggests a fix when I use Substitute.ForPartsOf<MyInternalType>(). The fix add the attribute in the file that contains the class MyInternalType. But this is not needed as the attribute is already declared in another file.

Expected behavior: it should not report a warning if there is [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] somewhere in the project.

Unify resource files

Currently, each analyzer library (C# or VB version) has its own Resources.resx file which contains messages for every diagnostic. As it was noted by @dtchepak
in #14 (comment) in most of the cases, the messages are the same and they only differ in some particular scenarios. Having that in mind we should find a way to share messages across analyzer libraries and override some of them when needed. Thanks to that approach we will avoid message duplication which will make the code-base cleaner and easier to maintain

Allow configuration of safe API misuses

In some cases the NSub API can be intentionally used in a questionable manner to get a specific result.

A common example I have seen is "mocking" extensions methods. For example:

public static class MyExtensions {
  public static ICounter Counter { get; set; }
  public static int GetCount(this object blah) {
    return Counter.GetCount(object);
  }
}

We can then "mock" this extension method by cheating:

MyExtensions.Counter = Substitute.For<ICounter>();
// ...
widget.GetCount().Returns(42);

This will work because MyExtensions.GetCount() invokes ICounter.GetCount(widget), so Returns will successfully mock the ICounter call (rather than the static, unmockable MyExtensions.GetCount()). NSubstitute.Analyzers correctly reports this as "NS001 | Member GetCount can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted." Which would normally be fine, but in this case we're using this to intercept the underlying call instead.

@tpodolak's suggestion is to use a configuration file to allow developers to whitelist specific calls in their project. We still need to flesh out this idea, but here are some rough notes:

  • Should be able to get analysers to ignore a specific error code for a specific member. e.g. ignore NS001 errors for MyProject.MyExtensions.GetCount.
  • How to handle overloads? Can probably just support member name for now and ignore the complexity of overloads.
  • Possibly support basic patterns/wildcards? e.g. ignore NS001 for MyProject.MyExtensions.Get*?
  • Possibly consider making the errors more fine-grained. For example, NS1xx series could be used for attempts to sub non-virtuals. NS101 can be for class methods, NS102 for class properties, NS111 for static class methods, NS120 for extension methods etc. Then we could specifically ignore NS111 for MyProject.MyClass.* in the case we know all static members delegate to an underlying virtual method, but we still want to catch any instances of trying to substitute non-virtual class methods. (This is just an idea, it is probably sufficient just to do the basic whitelist instead.)

NSubstitute.Analyzers.CSharp produces warnings about Microsoft.CodeAnalysis.CSharp in project, no analyzer output

Question
I have an ordinary Test project in my solution, targeting .Net Framework 4.6.2. When I added a Nuget reference to NSubstitute.Analyzers.CSharp, I got several warnings like this:

An instance of analyzer NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.NonVirtualSetupWhenAnalyzer cannot be created from C:\Users\erizzo.nuget\packages\nsubstitute.analyzers.csharp\1.0.6\analyzers\dotnet\cs\NSubstitute.Analyzers.CSharp.dll : Could not load file or assembly 'Microsoft.CodeAnalysis.CSharp, Version=2.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified..

I then tried adding Nuget reference to Microsoft.CodeAnalysis.CSharp (I tried both 2.8.0 and the latest stable version, 2.10.0), but it made no difference.

Obviously, the NSubstitute analyzers are not producing any warnings, either.

Any ideas what I can do?

VisualStudio version is 15.6.7 (VS 2017)

Incorrect NS3004 for derived types

First of all, the analyzer is really useful - thanks for creating the project!

I think I'm incorrectly getting the Could not set value of type โ€ฆ to argument ... because the types are incompatible error when trying to assign a Dictionary to a IReadOnlyDictionary argument and was able to reproduce it with this code:

using NSubstitute;
using Xunit;

public class ExampleTest
{
    public interface IExampleInterface
    {
        bool Method(out object value);
    }

    [Fact]
    public void DictionaryTest()
    {
        IExampleInterface substitute = Substitute.For<IExampleInterface>();
        substitute.Method(out Arg.Any<object>())
            .Returns(ci =>
            {
                ci[0] = "string";
                return true;
            });

        bool result = substitute.Method(out object value);

        Assert.True(result);
        Assert.Equal("string", value);
    }
}

The line with ci[0] = "string"; triggers the analyser error, however, the test succeeds in passing (i.e. NSubstitute does correctly set the parameter).

I think the problem is we're checking if the type is equal instead of if the assignment will work in AbstractCallInfoAnalyzer.cs - maybe Equals should be replaced with a call to CanCast?

Assign non-out/ref arg from `AndDoes`

Just realised that out and ref arguments can also be set via AndDoes (in the same way they can be set from Returns). This should probably also result in a NS3005 if a non-out/ref arg is set.

        public interface ILookup { bool Lookup(string key, out int i); }

        [Fact]
        public void Test() {
            var sub = Substitute.For<ILookup>();

            sub.Lookup("blah", out Arg.Any<int>())
               .Returns(true)
               .AndDoes(x => x[0] = 45); // <-- incorrectly setting x[0] instead of x[1]. Should give NS3005

            sub.Lookup("blah", out var value);
        }

Add basic documentation

Add documentation for every diagnostic rule, with a simple explanation of why it is reported and how to fix it

Detect substituting for `internal` members without `InternalsVisibleTo`

From nsubstitute/NSubstitute#496, having a test assembly with visibility of internal members of assembly under test, but without InternalsVisibleTo("DynamicProxyGenAssembly2"), causes errors when running the tests.

It would be good to catch this case, similar to NS2003.

From Robert's comment on NSubstitute@496:

Referring to NS2003, there are no checks on members, only on the type itself. This is the analyzer method in question AnalyzeTypeAccessability

The reproduction on that issue is a good test case to illustrate the problem (just having a type with internal members local to the test assembly with trigger the issue).

I'm not sure what category this should belong to. Could be NS5xxx as a general usage problem, although it is also sort of similar to NS1xxx which covers non-virtual substitution, although in this case it is a non-substitutable member due to internal. The easiest is probably to put it in NS5xxx, but maybe we should also consider adapting the NS1xxx category to be something to do with "non-substitutable members" and include this?

Draft rule page:

NS2003

CheckId NS????
Category ????

Cause

Substituting for an internal member of a class without proxies having visibility into internal members.

Rule description

A violation of this rule occurs when using NSubstitute features like Returns(), Received() etc. with an internal member of a class, but without giving NSubstitute's proxy assembly access to internal members.

How to fix violations

There are several ways to fix this:

  • change the visibility of the member being tested from internal to public
  • change the visibility of the member being tested from internal to protected internal
  • expose your type being tested to DynamicProxyGenAssembly2 using:
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

How to suppress violations

...

NS1002 higlights wrong member

var substitute = NSubstitute.Substitute.For<Foo>();
var x = substitute.When(x => x.Bar());

Similarly, as in #73, assuming that Bar is a non-virtual member, NS1002 is correctly reported, however the analyzer highlights x.Bar instead of x.Bar().

Analyzer 'NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantSetupAnalyzer' threw an exception of type 'System.ArgumentException' with message 'SyntaxTree is not part of the compilation'

Hi,
I got this exception on my project:

Severity	Code	Description	Project	File	Line	Suppression State	Detail Description
Warning	AD0001	Analyzer 'NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantSetupAnalyzer' threw an exception of type 'System.ArgumentException' with message 'SyntaxTree is not part of the compilation
Parameter name: syntaxTree'.	*****		1	Active	Analyzer 'NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantSetupAnalyzer' threw the following exception:
'Exception occurred with following context:
Compilation: *****
SyntaxTree: C:\Users\Tests.cs
SyntaxNode: preferenceCollection.Get(@"t:\a.csproj" ... [InvocationExpressionSyntax]@[474..540) (15,12)-(15,78)

System.ArgumentException: SyntaxTree is not part of the compilation
Parameter name: syntaxTree
   at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetSemanticModel(SyntaxTree syntaxTree, Boolean ignoreAccessibility)
   at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.CommonGetSemanticModel(SyntaxTree syntaxTree, Boolean ignoreAccessibility)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.VisitInvocationExpression(InvocationExpressionSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitArrowExpressionClause(ArrowExpressionClauseSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitAccessorDeclaration(AccessorDeclarationSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitAccessorList(AccessorListSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.AccessorListSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitPropertyDeclaration(PropertyDeclarationSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.PropertyDeclarationSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitClassDeclaration(ClassDeclarationSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.VisitRelatedSymbols(SyntaxNode syntaxNode)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIdentifierName(IdentifierNameSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.ReEntrantCallVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitObjectCreationExpression(ObjectCreationExpressionSyntax node)
   at Microsoft.CodeAnalysis.CSharp.Syntax.ObjectCreationExpressionSyntax.Accept(CSharpSyntaxVisitor visitor)
   at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
   at NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers.ReEntrantCallFinder.GetReEntrantSymbols(Compilation compilation, SyntaxNode rootNode)
   at NSubstitute.Analyzers.Shared.DiagnosticAnalyzers.AbstractReEntrantCallFinder.GetReEntrantCalls(Compilation compilation, SyntaxNode rootNode)
   at NSubstitute.Analyzers.Shared.DiagnosticAnalyzers.AbstractReEntrantSetupAnalyzer`2.AnalyzeInvocation(SyntaxNodeAnalysisContext syntaxNodeContext)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c__44`1.<ExecuteSyntaxNodeAction>b__44_0(ValueTuple`2 data)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info)

NS1002 - doesnt detect non-virtual calls from base classe

Repro

using NSubstitute;
namespace MyNamespace
{
    public class AbstractFoo
    {
        public int Bar()
        {
            return 1;
        }
    }

    public class Foo : AbstractFoo
    {
    }
    
    public class FooTests
    {
        public void Test()
        {
            var foo = new Foo();

            foo.When(xx => xx.Bar()); // xx.Bar() is not higlighted
        }
    }
}

Nuget metadata improvements

When searching for NSubstitute, the analyzers are listed pretty low in the search results, we should add some more tags to make the package more "recognizable". Additionally, as a part of this task, we should get rid of obsolete PackageLicenseUrl from the csproj, and replace it with the new tag

Incorrect NS4000 when using typeof expression

I think the analyzer is incorrectly firing NS4000: Returns() is set with a method that itself calls Returns for the following code (this is the simplest I could get the code to reproduce the warning I was getting):

public interface IExample
{
    object Method();
}

public class ExampleTest
{
    [Fact]
    public void TypeofTestClass()
    {
        IExample example = Substitute.For<IExample>();
        example.Method().Returns(typeof(ExampleTest));
        //                       ^^^^^^^^^^^^^^^^^^^
    }
}

This is under dotnet-core 2.1 with using NSubstitute version 4.0.0, NSubstitute.Analyzers.CSharp version 1.0.5 and xunit version 2.4.1

Granted, the above code doesn't look realistic (there's no asserts!), however, I was getting the warning in one of my actual tests and it seems to be the typeof(TheClassContainingTheTest) that triggers it.

Let me know if there's anything I can do to help.

Erroneous NS1002 errors for Arg.Any calls

I've got some code like this:

var myMock = Substitute.For<IDependancy>();
myMock.When(a => a.Foo(Arg.Any<SomeClass>())).Throw(new Exception());

Since updating to version 1.0.8 we're getting the following error on the .When line.

error NS1002: Member Any can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted.

ReEntrantSetupAnalyzer - syntax node is not within syntax tree

Originally reported in nsubstitute/NSubstitute#260 (comment)
The problem occurs when ReEntrantCallAnalyzer crosses boundries of given syntax tree when analyzing reentrant call.
Repro

using NSubstitute;
using NSubstitute.Core;
using System;
using Xunit;

namespace MyNamespace
{
    public class UnitTest1
    {
        [Fact]
        public void Test1()
        {
            var sub = Substitute.For<MyClass2>();
            sub.Foo().Returns(ClassInOtherFile.Foo());
        }

        
    }

    public class MyClass2
    {
        public virtual int Foo()
        {
            return 1;
        }
    }

    public interface MyClass
    {
        int Foo();
    }
}

NS1001 higlights wrong member

var substitute = NSubstitute.Substitute.For<Foo>();
var x = substitute.Received(1).Bar();

Assuming that Bar is non virtual member, NS1001 is correctly reported, however the analyzer highlight
substitute.Received(1) instead of Bar(). Alternatively it should higligt entire invocation expression substitute.Received(1).Bar(); if the previous one is not feasible

Blocks #70

Add support for linux operating system

Even though NSubstitute.Analyzers are written against NetStandard at the moment all tests are failing when run on Linux machine. This task is all about ensuring that it is possible to do a normal development on linux based machines.

Conflicting out/ref argument set

It is possible to set argument values from .Returns and .AndDoes. At present the one set in .Returns wins (not sure we should always rely on this for future versions), but this can result in confusing results.

        public interface ILookup { bool Lookup(string key, out int i); }

        [Fact]
        public void Test() {
            var sub = Substitute.For<ILookup>();

            sub.Lookup("blah", out Arg.Any<int>())
               .Returns(x => {
                   x[1] = 42;
                   return true;
                })
               .AndDoes(x => x[1] = 45);

            sub.Lookup("blah", out var value);

            Assert.Equal(42, value); // The final values is 42 from the Returns, not 45 from AndDoes
        }

Draft documentation:

NS3006

CheckId NS3006
Category Argument specification

Cause

Conflicting assignments to out/ref arguments.

Rule description

A violation of this rule occurs when an out or ref argument is assigned via CallInfo in both a Returns and an AndDoes call.

How to fix violations

To fix a violation of this rule remove one of the assignments, so the argument is only assigned in either the Returns or the AndDoes block (not in both).

// Incorrect, argument assigned in `Returns` and `AndDoes`:
dictionary.TryGetValue("sample", out Arg.Any<int>())
   .Returns(x => {
       x[1] = 42;
       return true;
    })
   .AndDoes(x => x[1] = 45);

// Correct, only assigned in `AndDoes`:
dictionary.TryGetValue("sample", out Arg.Any<int>())
   .Returns(true)
   .AndDoes(x => x[1] = 45);

// Correct, only assign in `Returns`:
dictionary.TryGetValue("sample", out Arg.Any<int>())
   .Returns(x => {
       x[1] = 42;
       return true;
    });

How to suppress violations

This warning can be suppressed by disabling the warning in the ruleset file for the project.
The warning can also be suppressed programmatically for an assembly:

[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Argument specification", "NS3006:Conflicting argument assignments.", Justification = "Reviewed")]

Or for a specific code block:

#pragma warning disable NS3006 // Conflicting argument assignments.
// the code which produces warning
#pragma warning disable NS3006 // Conflicting argument assignments.

Over-enthusiastic NS4000 error (Returns within Returns)

Found this case where the Analyzers are a little too enthusiastic detecting this error. ๐Ÿ˜„ (with 1.0.0 CSharp Analyzers)

using NSubstitute;
using NUnit.Framework;

namespace NSubTestWorkshop {

    public interface ISample { IValue GetValue(); }
    public interface IValue { int Value { get; } }

    [TestFixture]
    public class SampleTests {
        [Test]
        public void Sample() {
            var value1 = CreateValue();
            var sample = Substitute.For<ISample>();
            sample.GetValue().Returns(value1); // NS4000 error
            Assert.AreEqual(42, sample.GetValue().Value);
        }

        public IValue CreateValue() {
            var val = Substitute.For<IValue>();
            val.Value.Returns(42);
            return val;
        }
    }
}

~/dev/git/NSubTestWorkshop/UnitTest1.cs(39,39): Warning NS4000: Returns() is set with a method that itself calls Returns. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(x => value1). (NS4000) (NSubTestWorkshop)

This one should not cause a problem because value1 substitute is fully created and configured before being given to sample.GetValue().Returns(value1). This test passes if we disable the warning โœ… .


This would be a problem if we instead had this:

            sample.GetValue().Returns(CreateValue());

Because in that case we switch from configuring GetValue() to configuring Value. This modified test fails with CouldNotSetReturnDueToNoLastCallException:

        [Test]
        public void Sample() {
            var value1 = CreateValue();
            var sample = Substitute.For<ISample>();

#pragma warning disable NS4000
            sample.GetValue().Returns(CreateValue()); // <-- Analyzers correctly detect NS4000 here
#pragma warning restore NS4000

            Assert.AreEqual(42, sample.GetValue().Value);
        }

Detect non-virtual When...Do calls

Analyzers should be able to detect following wrong usages

using NSubstitute;

namespace MyNamespace
{
    public class Foo
    {
        public int Bar()
        {
            return 2;
        }
    }

    public class FooTests
    {
        public void Test()
        {
            int i = 0;
            var substitute = NSubstitute.Substitute.For<Foo>();

            void SubstituteCall(Foo sub)
            {
                sub.Bar();
            }

            substitute.When(SubstituteCall).Do(callInfo => i++);
            substitute.When(delegate(Foo sub) { sub.Bar() })
            substitute.When(sub => sub.Bar())
        }
    }
}

and of course usages of beforementioned examples executed as ordinal method and used with WhenForAnyArgs

NS2003 triggers a warning when InternalsVisibleTo using strong name

Describe the bug
We have to strong name all our assemblies.
NS2003 does not recognize InternalsVisibleTo syntax with publickey.

To Reproduce
our AssembyInfo.cs contains following line:
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

Expected behaviour
A clear and concise description of what you expected to happen, compared to what actually happened.

Currently i receive following warning:
Warning NS2003
Can not substitute for internal type. To substitute for internal type expose your type to DynamicProxyGenAssembly2 via [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

I'd expect not to receive any warning when using strong key.

Environment:

NSubstitute.Analyzers.CSharp 1.0.6
Platform: Windows10 - .net 4.6.2
Additional context
public static bool InternalsVisibleToProxyGenerator(this ISymbol typeSymbol)
{
return typeSymbol.ContainingAssembly != null &&
typeSymbol.ContainingAssembly.GetAttributes()
.Any(att => att.AttributeClass.ToString() == MetadataNames.InternalsVisibleToAttributeFullTypeName &&
att.ConstructorArguments.Any(arg => arg.Value.ToString() == MetadataNames.CastleDynamicProxyGenAssembly2Name));
}

arg.Value.ToString().StartsWith(MetadataNames.CastleDynamicProxyGenAssembly2Name) would quickly fix this issue ;-)

Add acknowledgements.md

Lets follow NSubstitute convention and add acknowledgments .md file with the stuff which was used to build the NSubstitute.Analyzers

NS4000 Unexpected warning in foreach loop

NSubstitute 4.0.0.0
NSubstitute.Analyzers.CSharp 1.0.9

Perhaps I'm missing something but this looks like a false positive to me.

using System.Linq;
using NSubstitute;

namespace ClassLibrary3
{
    public class Class1
    {
        public void Foo(IFoo foo, IEnumerable<Description> descriptions)
        {
            foreach (Description d in descriptions)
            {
                foo.Value.Returns(d.Value); // <- Warning	NS4000	Returns() is set with a method that itself calls Returns. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(x => d.Value).
            }
        }
    }

    public class Description
    {
        public int Value { get; set; }
    }

    public interface IFoo
    {
        int Value { get; }
    }
}

Detect non-virtual received calls

Analyzers should be able to detect following wrong usages

namespace MyNamespace
{
    public class Foo
    {
        public int Bar()
        {
            return 2;
        }
    }

    public class FooTests
    {
        public void Test()
        {
            var substitute = NSubstitute.Substitute.For<Foo>();
            substitute.Received().Bar();
            substitute.ReceivedWithAnyArgs().Bar()
            substitute.DidNotReceive().Bar();
            substitute.DidNotReceiveWithAnyArgs().Bar();
        }
    }
}

and of course usages of beforementioned examples executed as ordinal method

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.