├── .gitignore ├── Documentation ├── DeveloperDocumentation.md └── Tutorial.md ├── LICENSE ├── NuGet ├── NuGetPack.bat ├── SimpleStubs.API.nuspec ├── SimpleStubs.json └── SimpleStubs.nuspec ├── README.md ├── SECURITY.md ├── SimpleStubs.Test ├── Properties │ └── AssemblyInfo.cs ├── SimpleStubs.Test.csproj └── StubContainerTest.cs ├── SimpleStubs.sln ├── Targets └── SimpleStubs.targets ├── nuget.config ├── src ├── SimpleStubs.CodeGen │ ├── CodeGen │ │ ├── EventStubber.cs │ │ ├── IInterfaceStubber.cs │ │ ├── IMethodStubber.cs │ │ ├── IProjectStubber.cs │ │ ├── IPropertyStubber.cs │ │ ├── InterfaceStubber.cs │ │ ├── OrdinaryMethodStubber.cs │ │ ├── ProjectStubber.cs │ │ ├── PropertyStubber.cs │ │ ├── SimpleStubsGenerator.cs │ │ ├── StubProjectResult.cs │ │ ├── StubbingDelegateGenerator.cs │ │ └── UsingDirectiveEqualityComparer.cs │ ├── CommandLineParser.cs │ ├── Config │ │ ├── ConfigLoader.cs │ │ └── SimpleStubsConfig.cs │ ├── DI │ │ └── DIModule.cs │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── SimpleStubs.CodeGen.csproj │ ├── Utils │ │ ├── NamingUtils.cs │ │ ├── RoslynExtensions.cs │ │ ├── RoslynUtils.cs │ │ └── StubbingUtils.cs │ ├── app.config │ └── packages.config └── SimpleStubs │ ├── MockBehavior.cs │ ├── SequenceContainer.cs │ ├── SequenceContainerException.cs │ ├── SimpleStubs.csproj │ ├── SimpleStubsException.cs │ ├── StubContainer.cs │ ├── StubsUtils.cs │ └── Times.cs └── test ├── TestClassLibrary ├── ITestInterface.cs ├── Properties │ └── AssemblyInfo.cs └── TestClassLibrary.csproj └── TestClassLibraryTest ├── AsyncMethodStubsTest.cs ├── Etg.SimpleStubs.targets ├── EventStubsTest.cs ├── IndexerStubsTest.cs ├── MethodStubsTest.cs ├── Properties └── AssemblyInfo.cs ├── PropertyStubsTest.cs ├── SimpleStubs.json ├── StubGeneratorTest.cs ├── TestClassLibraryTest.csproj ├── app.config └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | *.lock.json 10 | *.generated.cs 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | build/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | 27 | SimpleStubsDlls 28 | 29 | # Visual Studo 2015 cache/options directory 30 | .vs/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding addin-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | # TODO: Comment the next line if you want to checkin your web deploy settings 137 | # but database connection strings (with potential passwords) will be unencrypted 138 | *.pubxml 139 | *.publishproj 140 | 141 | # NuGet Packages 142 | *.nupkg 143 | # The packages folder can be ignored because of Package Restore 144 | **/packages/* 145 | # except build/, which is used as an MSBuild target. 146 | !**/packages/build/ 147 | # Uncomment if necessary however generally it will be regenerated when needed 148 | #!**/packages/repositories.config 149 | 150 | # Windows Azure Build Output 151 | csx/ 152 | *.build.csdef 153 | 154 | # Windows Store app package directory 155 | AppPackages/ 156 | 157 | # Others 158 | *.[Cc]ache 159 | ClientBin/ 160 | [Ss]tyle[Cc]op.* 161 | ~$* 162 | *~ 163 | *.dbmdl 164 | *.dbproj.schemaview 165 | *.pfx 166 | *.publishsettings 167 | node_modules/ 168 | bower_components/ 169 | 170 | # RIA/Silverlight projects 171 | Generated_Code/ 172 | 173 | # Backup & report files from converting an old project file 174 | # to a newer Visual Studio version. Backup files are not needed, 175 | # because we have git ;-) 176 | _UpgradeReport_Files/ 177 | Backup*/ 178 | UpgradeLog*.XML 179 | UpgradeLog*.htm 180 | 181 | # SQL Server files 182 | *.mdf 183 | *.ldf 184 | 185 | # Business Intelligence projects 186 | *.rdl.data 187 | *.bim.layout 188 | *.bim_*.settings 189 | 190 | # Microsoft Fakes 191 | FakesAssemblies/ 192 | 193 | # Node.js Tools for Visual Studio 194 | .ntvs_analysis.dat 195 | 196 | # Visual Studio 6 build log 197 | *.plg 198 | 199 | # Visual Studio 6 workspace options file 200 | *.opt 201 | -------------------------------------------------------------------------------- /Documentation/DeveloperDocumentation.md: -------------------------------------------------------------------------------- 1 | # Developer Documentation 2 | 3 | This document is targeted for developers who would like to understand and potentially contribute to the SimpleStubs source code. 4 | 5 | ## Build & Code Generation 6 | SimpleStubs used Roslyn to generate stubs. The code is generated during the build (right before the build actually). 7 | 8 | When SimpleStubs NuGet package is installed to a given project, the [Etg.SimpleStubs.targets](https://github.com/Microsoft/SimpleStubs/blob/master/Targets/Etg.SimpleStubs.targets) file is installed and imported in the `ProjectName.nuget.targets` file (which gets added to the target project). The [Etg.SimpleStubs.targets](https://github.com/Microsoft/SimpleStubs/blob/master/Targets/Etg.SimpleStubs.targets) file contains the necessary MSBuild tasks needed to generate the stubs. These tasks basically invoke the `Etg.SimpleStubs.CodeGen.exe` and add the output to the `"$(IntermediateOutputPath)SimpleStubs.generated.cs"` file (the `$(IntermediateOutputPath)` is the `obj` folder). 9 | 10 | ## Method and Property Stubbers 11 | 12 | If you follow the source code starting at [SimpleStubsGenerator](https://github.com/Microsoft/SimpleStubs/blob/master/src/SimpleStubs.CodeGen/CodeGen/SimpleStubsGenerator.cs), you'll notice that the [InterfaceStubber](https://github.com/Microsoft/SimpleStubs/blob/master/src/SimpleStubs.CodeGen/CodeGen/InterfaceStubber.cs) class is used to generate a stub for a given interface. The constructor of [InterfaceStubber](https://github.com/Microsoft/SimpleStubs/blob/master/src/SimpleStubs.CodeGen/CodeGen/InterfaceStubber.cs) is as follows: 13 | 14 | ```csharp 15 | public InterfaceStubber(IEnumerable methodStubbers, IEnumerable propertyStubbers) 16 | { 17 | _propertyStubbers = propertyStubbers; 18 | _methodStubbers = new List(methodStubbers); 19 | } 20 | ``` 21 | 22 | When an interface is stubbed, the `InterfaceStubber` walks through all the method/property stubbers and execute them all on each method/property in the interface. This means that you can easily add your own implementation of `IMethodStubber`/`IPropertyStubber` and it will be invoked as part of the build. To inject your own `IMethodStubber`/`IPropertyStubber`, simply update the [DIModule](https://github.com/Microsoft/SimpleStubs/blob/master/src/SimpleStubs.CodeGen/DI/DIModule.cs) code and add an additional entry where the constructor of `InterfaceStubber` is being invoked. You can also modify existing implementations of `IMethodStubber`/`IPropertyStubber` to fix bugs or add behaviour. 23 | 24 | ## Testing 25 | 26 | The [test](https://github.com/Microsoft/SimpleStubs/tree/master/test) folder contains a class library project and a corresponding test project. The test project is setup in a way that it will invoke SimpleStubs with the current version of the `Etg.SimpleStubs.CodeGen.exe` that is available in the build directory (see [Etg.SimpleStubs.targets](https://github.com/Microsoft/SimpleStubs/blob/master/test/TestClassLibraryTest/Etg.SimpleStubs.targets)). This will allow developers to modify the SimpleStubs code and run the unit tests without having to re-generate the NuGet every time. 27 | 28 | ## Debugging 29 | 30 | To debug SimpleStubs, simply run `SimpleStubs.CodeGen` console app with the appropriate parameters (`-ProjectPath` and `-OutputPath`). 31 | 32 | 33 | -------------------------------------------------------------------------------- /Documentation/Tutorial.md: -------------------------------------------------------------------------------- 1 | # SimpleStubs tutorial 2 | 3 | In this tutorial we will show how to install and use SimpleStubs. 4 | 5 | ## Installation 6 | 7 | To install SimpleStubs, simply install the `Etg.SimpleStubs` NuGet package to your unit test project (also see *Tips and Tricks* section for installation tips). 8 | 9 | Once installed, SimpleStubs will create stubs for all interfaces (`public` interfaces and optionally `internal` interfaces) in all referenced projects. 10 | 11 | The stubs will be automatically generated in the intermediate output path and compiled as part of the build process. Because the stubs are generated, modifying the stubs manually has no effect (if you really want to modify a stub, copy it to a different file). 12 | 13 | ## Api 14 | 15 | Let's consider the following interface and look at how we can use SimpleStubs to create stubs for it. 16 | 17 | ```csharp 18 | public interface IPhoneBook 19 | { 20 | long GetContactPhoneNumber(string firstName, string lastName); 21 | 22 | long MyNumber { get; set; } 23 | 24 | event EventHandler PhoneNumberChanged; 25 | } 26 | ``` 27 | 28 | ### Stubbing methods 29 | ```csharp 30 | // for any number of calls 31 | var stub = new StubIPhoneBook().GetContactPhoneNumber((firstName, lastName) => 6041234567); 32 | ``` 33 | 34 | or 35 | 36 | ```csharp 37 | // For one call; an exception will be thrown if more calls occur 38 | var stub = new StubIPhoneBook().GetContactPhoneNumber((firstName, lastName) => 6041234567, Times.Once); 39 | ``` 40 | 41 | or 42 | 43 | ```csharp 44 | // For a specific number of calls 45 | var stub = new StubIPhoneBook().GetContactPhoneNumber((firstName, lastName) => 6041234567, count:5); 46 | ``` 47 | 48 | You can also copy and verify the parameters values: 49 | ```csharp 50 | string firstName = null; 51 | string lastName = null; 52 | var stub = new StubIPhoneBook().GetContactPhoneNumber((fn, ln) => 53 | { 54 | firstName = fn; 55 | lastName = ln; 56 | return number; 57 | }); 58 | 59 | ClassUnderTest obj = new ClassUnderTest(stub); 60 | 61 | Assert.AreEqual(expectedValue, obj.Foo()); 62 | // parameters verification 63 | Assert.AreEqual("John", firstName); 64 | Assert.AreEqual("Smith", lastName); 65 | ``` 66 | 67 | ### Out parameters 68 | ```csharp 69 | object someObj = new Foo(); 70 | var stub = new StubIContainer() 71 | .GetElement((int index, out object value) => 72 | { 73 | value = someObj; 74 | return true; 75 | }); 76 | ``` 77 | 78 | ### Ref parameters 79 | ```csharp 80 | var stub = new StubIRefUtils() 81 | .Swap((ref int v1, ref int v2) => 82 | { 83 | int temp = v1; 84 | v1 = v2; 85 | v2 = temp; 86 | }); 87 | ``` 88 | 89 | ### Generic methods 90 | ```csharp 91 | int value = -1; 92 | var stub = new StubIContainer() 93 | .GetElement(index => value) 94 | .SetElement((i, v) => { value = v; }); 95 | ``` 96 | 97 | ## Stubbing properties 98 | 99 | ```csharp 100 | long myNumber = 6041234567; 101 | var stub = new StubIPhoneBook() 102 | .MyNumber_Get(() => myNumber) 103 | .MyNumber_Set(value => newNumber = value); 104 | ``` 105 | 106 | ## Stubbing indexers 107 | ```csharp 108 | var stub = new StubIGenericContainer(); 109 | 110 | // stubbing indexer getter 111 | stub.Item_Get(index => 112 | { 113 | // we're expecting the code under test to get index 5 114 | if (index != 5) throw new IndexOutOfRangeException(); 115 | return 99; 116 | }); 117 | 118 | // stubbing indexer setter 119 | int res = -1; 120 | stub.Item_Set((index, value) => 121 | { 122 | // we're expecting the code under test to only set index 7 123 | if (index != 7) throw new IndexOutOfRangeException(); 124 | res = value; 125 | }); 126 | ``` 127 | 128 | ## Stubbing events 129 | 130 | ```csharp 131 | var stub = new StubIPhoneBook(); 132 | 133 | // Pass the stub to the code under test 134 | var obj = new ClassUnderTest(stub); 135 | 136 | // Raise the event 137 | stub.PhoneNumberChanged_Raise(stub, 55); 138 | 139 | // Verify the state of obj to ensure that it has reacted to the event 140 | ``` 141 | 142 | ## Stubbing a sequence of calls 143 | 144 | In some cases, it might be useful to have a stub behave differently when it's called several times. SimpleStubs offers supports for stubbing a sequence of calls. 145 | 146 | ```csharp 147 | var stub = new StubIPhoneBook() 148 | .GetContactPhoneNumber((p1, p2) => 12345678, Times.Once) // first call 149 | .GetContactPhoneNumber((p1, p2) => 11122233, Times.Twice) // next two calls 150 | .GetContactPhoneNumber((p1, p2) => 22233556, Times.Forever); // rest of the calls 151 | ``` 152 | 153 | ## Overwriting stubs 154 | 155 | It's possible to overwrite a stubbed method or property as follows: 156 | ```csharp 157 | var stub = new StubIPhoneBook().GetContactPhoneNumber((p1, p2) => 12345678); 158 | 159 | // test code 160 | 161 | // overwrite the stub 162 | stub.GetContactPhoneNumber((p1, p2) => 11122233, overwrite:true); 163 | 164 | // other test code 165 | ``` 166 | 167 | ## Default behaviors 168 | 169 | In the default ```MockBehavior.Loose``` mode, stubs that are called before their behaviors have been set will return ```null``` or a default value. This can be changed—so that uninitialized stubs will throw an exception if called—by using ```MockBehavior.Strict```: 170 | 171 | ```csharp 172 | var stub = new StubIPhoneBook(MockBehavior.Strict); 173 | IPhoneBook phoneBook = stub; 174 | phoneBook.GetContactPhoneNumber("John", "Smith"); // Throws a SimpleStubsException 175 | ``` 176 | 177 | 178 | ## Configuration 179 | 180 | SimpleStubs also supports an optional configuration file that can be added to the root of your test project. The configuration file (named `SimpleStubs.json`) has the following structure: 181 | 182 | ```json 183 | { 184 | "IgnoredProjects": [ 185 | "IgnoredProject1", 186 | "IgnoredProject2" 187 | ], 188 | "IgnoredInterfaces": [ 189 | "MyNamespace.IFooInterface", 190 | "MyNamespace.IBarInterface", 191 | "MyNamespace.IGenericInterface" 192 | ], 193 | "StubInternalInterfaces": false, 194 | "StubCurrentProject": false, 195 | "DefaultMockBehavior": "Loose" 196 | } 197 | ``` 198 | 199 | The configuration file allows you to instruct SimpleStubs to omit creating stubs for a given project or interface. 200 | **Note** that this very useful to exclude interfaces that are causing SimpleStubs to generate stubs that don't compile (this can happen in some edge cases). If you encounter such a case, exclude the interface in question and report the problem so we can fix it. 201 | 202 | It's also possible to instruct SimpleStubs to create stubs for internal interfaces (by default only public interfaces are stubbed) as shown in the configuration sample above. 203 | 204 | It's also possible to generate stubs for interfaces from the current project (not only referenced projects) as shown in the configuration sample above. This is useful if you'd like to generate stubs for interfaces that are defined in your test project or in a shared project. 205 | 206 | Strict mode can be turned on for all stubs by changing the DefaultMockBehavior setting to "Strict". The default setting if omitted is "Loose". 207 | 208 | ## Tips and Tricks 209 | 210 | ### Reduce compile time by adding SimpleStubs to only one project in your solution 211 | 212 | If you have a solution composed of multiple projects, instead of installing SimpleStubs to each of your test projects, 213 | * Create a new project that will contain the stubs, call it something like *GeneratedStubs* 214 | * Install SimpleStubs to the *GeneratedStubs* project. 215 | * Reference all the projects in the solution from the *GeneratedStubs* project (or at least the projects that contain interfaces to be stubbed). 216 | * Reference the *GeneratedStubs* project in your test projects to access the stubs. 217 | 218 | With this approach, each stub is generated only once and only when there are code changes. 219 | 220 | ### Set location for generated file 221 | 222 | By default, SimpleStubs will generate an output file in the obj folder. 223 | If the *GeneratedStubs* project is referenced from another test project, Intellisense may not work. 224 | 225 | To resolve this, you can modify the location of the generated file to be included in the project root. 226 | Just add the following to the .csproj file: 227 | 228 | `.\SimpleStubs.generated.cs` 229 | 230 | ### Generate stubs for external interfaces 231 | 232 | By default, SimpleStubs ignores external interfaces because there can be too many of them to generate stubs for. One simple way of generating a stub for an external interface is to create an internal interface that inherits from it (you don't need to use the internal fake interface anywhere in the code). Because SimpleStubs handles inheritance, the generated stub can be used as a stub of the original external interface. 233 | 234 | ## Current limitations 235 | * Only interfaces are stubbed. 236 | * No stubs will be generated for external libraries (dlls). See *Tips and Tricks* section for a workaround. 237 | 238 | ## What if some stubs don't compile? 239 | 240 | Exclude the interface that is causing the problem (using the `SimpleStubs.json` configuration file) and report the problem by opening an issue. 241 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | SimpleStubs 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | All rights reserved. 6 | 7 | MIT License 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /NuGet/NuGetPack.bat: -------------------------------------------------------------------------------- 1 | nuget pack SimpleStubs.nuspec 2 | nuget pack SimpleStubs.API.nuspec -------------------------------------------------------------------------------- /NuGet/SimpleStubs.API.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SimpleStubs.API 5 | 1.0.0 6 | SimpleStubs API 7 | Nehme Bilal 8 | Nehme Bilal 9 | https://github.com/Microsoft/SimpleStubs 10 | MIT 11 | false 12 | SimpleStubs API is a set of classes used by the ETG.SimpleStubs NuGet package. 13 | 14 | Copyright 2016 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /NuGet/SimpleStubs.json: -------------------------------------------------------------------------------- 1 | { 2 | "IgnoredProjects": [ 3 | ], 4 | "IgnoredInterfaces": [ 5 | ], 6 | "StubInternalInterfaces": false, 7 | "DefaultMockBehavior": "Loose" 8 | } -------------------------------------------------------------------------------- /NuGet/SimpleStubs.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SimpleStubs 5 | 2.4.8.3 6 | SimpleStubs Mocking Framework (C#). 7 | Nehme Bilal 8 | Nehme Bilal 9 | MIT 10 | https://github.com/Microsoft/SimpleStubs 11 | false 12 | SimpleStubs is a simple C# mocking framework that supports Universal Windows Platform (UWP), .NET Core and .NET framework. 13 | 14 | Copyright 2016 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SimpleStubs 2 | ======= 3 | 4 | [![Join the chat at https://gitter.im/Microsoft/SimpleStubs](https://badges.gitter.im/Microsoft/SimpleStubs.svg)](https://gitter.im/Microsoft/SimpleStubs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | [![Build status](https://ci.appveyor.com/api/projects/status/wt0rjp0bkyk9l9gi?svg=true)](https://ci.appveyor.com/project/NehmeBilal/simplestubs) 6 | [![NuGet](https://img.shields.io/nuget/v/SimpleStubs.svg?style=flat)](https://www.nuget.org/packages/SimpleStubs/) 7 | 8 | *SimpleStubs* is a simple mocking framework that supports Universal Windows Platform (UWP), .NET Core and .NET framework. SimpleStubs was developed and is maintained by Nehme Bilal, a software engineer at Microsoft Vancouver. 9 | 10 | The framework can be installed to your test project using [SimpleStubs NuGet package](https://www.nuget.org/packages/SimpleStubs/). 11 | 12 | Please read the documentation below to learn more about how to install and use SimpleStubs. 13 | 14 | Documentation 15 | ======= 16 | * [Tutorial](Documentation/Tutorial.md). 17 | * [Developers Documentation](Documentation/DeveloperDocumentation.md) (for contributors) 18 | 19 | Contribute! 20 | ======= 21 | We welcome contributions of all sorts including pull requests, suggestions, documentation, etc. Please feel free to open an issue to discuss any matter. 22 | 23 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 24 | 25 | License 26 | ======= 27 | This project is licensed under the [MIT license](LICENSE). 28 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SimpleStubs.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SimpleStubs.Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SimpleStubs.Test")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("42d8b4ef-c779-4966-af96-46f51e15c88e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /SimpleStubs.Test/SimpleStubs.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {42D8B4EF-C779-4966-AF96-46F51E15C88E} 7 | Library 8 | Properties 9 | SimpleStubs.Test 10 | SimpleStubs.Test 11 | v4.7.2 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | {56be6aef-6db7-4fa6-89bb-b50c5dbbc675} 60 | SimpleStubs 61 | 62 | 63 | 64 | 65 | 66 | 67 | False 68 | 69 | 70 | False 71 | 72 | 73 | False 74 | 75 | 76 | False 77 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | -------------------------------------------------------------------------------- /SimpleStubs.Test/StubContainerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Etg.SimpleStubs; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace SimpleStubs.Test 6 | { 7 | [TestClass] 8 | public class StubContainerTest 9 | { 10 | private delegate void BasicDelegate(); 11 | private delegate void BasicDelegate2(); 12 | 13 | private delegate T GenericDelegate(); 14 | 15 | [TestMethod] 16 | public void TestSetGetMethodStub() 17 | { 18 | StubContainer container = new StubContainer(); 19 | BasicDelegate del = () => { }; 20 | container.SetMethodStub(del, 1, true); 21 | 22 | Assert.AreEqual(del, container.GetMethodStub("Foo")); 23 | } 24 | 25 | [TestMethod] 26 | public void TestThatSetStubOverridesExistingStub() 27 | { 28 | StubContainer container = new StubContainer(); 29 | BasicDelegate del = () => { }; 30 | BasicDelegate del2 = () => { throw new Exception();}; 31 | container.SetMethodStub(del, Times.Forever, true); 32 | container.SetMethodStub(del2, Times.Forever, true); 33 | 34 | Assert.AreEqual(del2, container.GetMethodStub("Foo")); 35 | } 36 | 37 | [TestMethod] 38 | public void TestThatDelegatesWithTheSameSignatureDontConflict() 39 | { 40 | StubContainer container = new StubContainer(); 41 | BasicDelegate del = () => { }; 42 | BasicDelegate2 del2 = () => { throw new Exception(); }; 43 | container.SetMethodStub(del, Times.Forever, true); 44 | container.SetMethodStub(del2, Times.Forever, true); 45 | 46 | Assert.AreEqual(del, container.GetMethodStub("Foo1")); 47 | Assert.AreEqual(del2, container.GetMethodStub("Foo2")); 48 | } 49 | 50 | [TestMethod] 51 | public void TestThatGenericDelegatesWithDifferentGenericTypeDontConflict() 52 | { 53 | StubContainer container = new StubContainer(); 54 | GenericDelegate del = () => 1; 55 | GenericDelegate del2 = () => "text"; 56 | container.SetMethodStub(del, Times.Forever, true); 57 | container.SetMethodStub(del2, Times.Forever, true); 58 | 59 | Assert.AreEqual(del, container.GetMethodStub>("Foo1")); 60 | Assert.AreEqual(del2, container.GetMethodStub>("Foo2")); 61 | } 62 | 63 | [TestMethod] 64 | [ExpectedException(typeof(SimpleStubsException))] 65 | public void TestGetMethodStubWhenNotSetup() 66 | { 67 | StubContainer container = new StubContainer(); 68 | container.GetMethodStub("Foo"); 69 | } 70 | 71 | private interface IFoo 72 | { 73 | }; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /SimpleStubs.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2026 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleStubs.CodeGen", "src\SimpleStubs.CodeGen\SimpleStubs.CodeGen.csproj", "{3E9C520A-94CF-46D0-864B-4293D439C92A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestClassLibrary", "test\TestClassLibrary\TestClassLibrary.csproj", "{ECBCBAE6-949E-4E9C-84E1-614D97909B6C}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestClassLibraryTest", "test\TestClassLibraryTest\TestClassLibraryTest.csproj", "{CB81F60F-1374-4B46-BB64-D848B5103A58}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleStubs.Test", "SimpleStubs.Test\SimpleStubs.Test.csproj", "{42D8B4EF-C779-4966-AF96-46F51E15C88E}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleStubs", "src\SimpleStubs\SimpleStubs.csproj", "{56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Debug|ARM = Debug|ARM 20 | Debug|x64 = Debug|x64 21 | Debug|x86 = Debug|x86 22 | Release|Any CPU = Release|Any CPU 23 | Release|ARM = Release|ARM 24 | Release|x64 = Release|x64 25 | Release|x86 = Release|x86 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Debug|ARM.ActiveCfg = Debug|Any CPU 31 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Debug|ARM.Build.0 = Debug|Any CPU 32 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Debug|x64.ActiveCfg = Debug|Any CPU 33 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Debug|x64.Build.0 = Debug|Any CPU 34 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Debug|x86.ActiveCfg = Debug|Any CPU 35 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Debug|x86.Build.0 = Debug|Any CPU 36 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Release|ARM.ActiveCfg = Release|Any CPU 39 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Release|ARM.Build.0 = Release|Any CPU 40 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Release|x64.ActiveCfg = Release|Any CPU 41 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Release|x64.Build.0 = Release|Any CPU 42 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Release|x86.ActiveCfg = Release|Any CPU 43 | {3E9C520A-94CF-46D0-864B-4293D439C92A}.Release|x86.Build.0 = Release|Any CPU 44 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Debug|ARM.ActiveCfg = Debug|Any CPU 47 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Debug|ARM.Build.0 = Debug|Any CPU 48 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Debug|x64.ActiveCfg = Debug|Any CPU 49 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Debug|x64.Build.0 = Debug|Any CPU 50 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Debug|x86.ActiveCfg = Debug|Any CPU 51 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Debug|x86.Build.0 = Debug|Any CPU 52 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Release|ARM.ActiveCfg = Release|Any CPU 55 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Release|ARM.Build.0 = Release|Any CPU 56 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Release|x64.ActiveCfg = Release|Any CPU 57 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Release|x64.Build.0 = Release|Any CPU 58 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Release|x86.ActiveCfg = Release|Any CPU 59 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C}.Release|x86.Build.0 = Release|Any CPU 60 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Debug|ARM.ActiveCfg = Debug|Any CPU 63 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Debug|ARM.Build.0 = Debug|Any CPU 64 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Debug|x64.ActiveCfg = Debug|Any CPU 65 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Debug|x64.Build.0 = Debug|Any CPU 66 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Debug|x86.ActiveCfg = Debug|Any CPU 67 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Debug|x86.Build.0 = Debug|Any CPU 68 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Release|ARM.ActiveCfg = Release|Any CPU 71 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Release|ARM.Build.0 = Release|Any CPU 72 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Release|x64.ActiveCfg = Release|Any CPU 73 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Release|x64.Build.0 = Release|Any CPU 74 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Release|x86.ActiveCfg = Release|Any CPU 75 | {CB81F60F-1374-4B46-BB64-D848B5103A58}.Release|x86.Build.0 = Release|Any CPU 76 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 77 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Debug|Any CPU.Build.0 = Debug|Any CPU 78 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Debug|ARM.ActiveCfg = Debug|Any CPU 79 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Debug|ARM.Build.0 = Debug|Any CPU 80 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Debug|x64.ActiveCfg = Debug|Any CPU 81 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Debug|x64.Build.0 = Debug|Any CPU 82 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Debug|x86.ActiveCfg = Debug|Any CPU 83 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Debug|x86.Build.0 = Debug|Any CPU 84 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Release|Any CPU.ActiveCfg = Release|Any CPU 85 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Release|Any CPU.Build.0 = Release|Any CPU 86 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Release|ARM.ActiveCfg = Release|Any CPU 87 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Release|ARM.Build.0 = Release|Any CPU 88 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Release|x64.ActiveCfg = Release|Any CPU 89 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Release|x64.Build.0 = Release|Any CPU 90 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Release|x86.ActiveCfg = Release|Any CPU 91 | {42D8B4EF-C779-4966-AF96-46F51E15C88E}.Release|x86.Build.0 = Release|Any CPU 92 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 93 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Debug|Any CPU.Build.0 = Debug|Any CPU 94 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Debug|ARM.ActiveCfg = Debug|Any CPU 95 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Debug|ARM.Build.0 = Debug|Any CPU 96 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Debug|x64.ActiveCfg = Debug|Any CPU 97 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Debug|x64.Build.0 = Debug|Any CPU 98 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Debug|x86.ActiveCfg = Debug|Any CPU 99 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Debug|x86.Build.0 = Debug|Any CPU 100 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Release|Any CPU.ActiveCfg = Release|Any CPU 101 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Release|Any CPU.Build.0 = Release|Any CPU 102 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Release|ARM.ActiveCfg = Release|Any CPU 103 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Release|ARM.Build.0 = Release|Any CPU 104 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Release|x64.ActiveCfg = Release|Any CPU 105 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Release|x64.Build.0 = Release|Any CPU 106 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Release|x86.ActiveCfg = Release|Any CPU 107 | {56BE6AEF-6DB7-4FA6-89BB-B50C5DBBC675}.Release|x86.Build.0 = Release|Any CPU 108 | EndGlobalSection 109 | GlobalSection(SolutionProperties) = preSolution 110 | HideSolutionNode = FALSE 111 | EndGlobalSection 112 | GlobalSection(ExtensibilityGlobals) = postSolution 113 | SolutionGuid = {0D89DE17-FFB0-4221-A07B-5C19176CC099} 114 | EndGlobalSection 115 | EndGlobal 116 | -------------------------------------------------------------------------------- /Targets/SimpleStubs.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CoreCompile;XamlPreCompile 5 | ResolveReferences 6 | $(IntermediateOutputPath)SimpleStubs.generated.cs 7 | true 8 | 9 | 10 | 11 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/EventStubber.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | using Etg.SimpleStubs.CodeGen.Utils; 7 | 8 | namespace Etg.SimpleStubs.CodeGen 9 | { 10 | using SF = SyntaxFactory; 11 | 12 | /// 13 | /// Generate the code needed to stub an event 14 | /// 15 | internal class EventStubber : IMethodStubber 16 | { 17 | public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol, INamedTypeSymbol stubbedInterface, SemanticModel semanticModel) 18 | { 19 | // only handle EventAdd and ignore EventRemove because we only need to stub the event once 20 | if (!methodSymbol.IsEventAdd()) 21 | { 22 | return classDclr; 23 | } 24 | 25 | // add the event implementation to the stub 26 | IEventSymbol eventSymbol = (IEventSymbol) methodSymbol.AssociatedSymbol; 27 | EventFieldDeclarationSyntax eventDclr = ToEventDclr(eventSymbol); 28 | classDclr = classDclr.AddMembers(eventDclr); 29 | 30 | string eventName = eventSymbol.Name; 31 | 32 | bool isCustomDelegateEvent = IsCustomDelegateBasedEvent(eventSymbol, semanticModel); 33 | ParameterSyntax[] parameters = GetEventParameters(eventSymbol, isCustomDelegateEvent); 34 | string onEventArgs; 35 | string eventTriggerArgs; 36 | string methodReturnType = "void"; 37 | string customDelegateReturnType = ""; 38 | bool hasReturnType = false; 39 | 40 | if (isCustomDelegateEvent) 41 | { 42 | IMethodSymbol delegateInvokeMethodSymbol = ((INamedTypeSymbol)(eventSymbol.OriginalDefinition).Type).DelegateInvokeMethod; 43 | onEventArgs = StubbingUtils.FormatParameters(delegateInvokeMethodSymbol); 44 | eventTriggerArgs = onEventArgs; 45 | if (!delegateInvokeMethodSymbol.ReturnsVoid) 46 | { 47 | hasReturnType = true; 48 | customDelegateReturnType = delegateInvokeMethodSymbol.ReturnType.GetFullyQualifiedName(); 49 | methodReturnType = $"global::System.Collections.Generic.IEnumerable<{customDelegateReturnType}>"; 50 | } 51 | } 52 | else 53 | { 54 | onEventArgs = "sender"; 55 | eventTriggerArgs = "sender"; 56 | if (parameters.Count() == 2) 57 | { 58 | onEventArgs += ", args"; 59 | eventTriggerArgs += ", args"; 60 | } 61 | else if (parameters.Count() == 1) 62 | { 63 | onEventArgs += ", null"; 64 | } 65 | } 66 | 67 | string eventType = GetEventType(eventSymbol); 68 | string onEventMethodName = "On_" + eventName; 69 | 70 | // Create OnEvent method 71 | BlockSyntax onEventMethodDclrBlock; 72 | if (hasReturnType) 73 | { 74 | onEventMethodDclrBlock = SF.Block( 75 | SF.ParseStatement($"{eventType} handler = {eventName};\n"), 76 | SF.ParseStatement("if (handler == null) {{ yield break; }}\n"), 77 | SF.ParseStatement($"foreach (var listener in handler.GetInvocationList()){{ if (listener.DynamicInvoke({onEventArgs}) is {customDelegateReturnType} ret){{ yield return ret; }} }}")); 78 | } 79 | else 80 | { 81 | onEventMethodDclrBlock = SF.Block( 82 | SF.ParseStatement($"{eventType} handler = {eventName};\n"), 83 | SF.ParseStatement($"if (handler != null) {{ handler({onEventArgs}); }}\n")); 84 | } 85 | 86 | MethodDeclarationSyntax onEventMethodDclr = SF.MethodDeclaration(SF.ParseTypeName(methodReturnType), onEventMethodName) 87 | .AddModifiers(SF.Token(SyntaxKind.ProtectedKeyword)) 88 | .AddParameterListParameters(parameters) 89 | .WithBody(onEventMethodDclrBlock); 90 | 91 | classDclr = classDclr.AddMembers(onEventMethodDclr); 92 | 93 | // Create event trigger method 94 | string eventTriggerMethodName = eventName + "_Raise"; 95 | 96 | BlockSyntax eventTriggerMethodBlock; 97 | if (hasReturnType) 98 | { 99 | eventTriggerMethodBlock = SF.Block(SF.ParseStatement($"return {onEventMethodName}({eventTriggerArgs});\n")); 100 | } 101 | else 102 | { 103 | eventTriggerMethodBlock = SF.Block(SF.ParseStatement($"{onEventMethodName}({eventTriggerArgs});\n")); 104 | } 105 | 106 | MethodDeclarationSyntax eventTriggerMethod = SF.MethodDeclaration(SF.ParseTypeName(methodReturnType), 107 | eventTriggerMethodName) 108 | .AddModifiers(SF.Token(SyntaxKind.PublicKeyword)) 109 | .AddParameterListParameters(parameters) 110 | .WithBody(eventTriggerMethodBlock); 111 | classDclr = classDclr.AddMembers(eventTriggerMethod); 112 | 113 | return classDclr; 114 | } 115 | 116 | private static ParameterSyntax[] GetEventParameters(IEventSymbol eventSymbol, bool isCustomDelegateEvent) 117 | { 118 | 119 | var parameters = new List(); 120 | INamedTypeSymbol type = (INamedTypeSymbol) (eventSymbol.Type); 121 | 122 | if (isCustomDelegateEvent) 123 | { 124 | IMethodSymbol delegateInvokeMethodSymbol = ((INamedTypeSymbol)(eventSymbol).Type).DelegateInvokeMethod; 125 | parameters.AddRange(RoslynUtils.GetMethodParameterSyntaxList(delegateInvokeMethodSymbol).ToArray()); 126 | } 127 | else 128 | { 129 | parameters.Add(SF.Parameter(SF.Identifier("sender")).WithType(SF.ParseTypeName("object"))); 130 | if (type.TypeArguments.Any()) 131 | { 132 | parameters.Add(SF.Parameter(SF.Identifier("args")) 133 | .WithType(SF.ParseTypeName(type.TypeArguments[0].GetFullyQualifiedName()))); 134 | } 135 | } 136 | 137 | return parameters.ToArray(); 138 | } 139 | 140 | private static EventFieldDeclarationSyntax ToEventDclr(IEventSymbol eventSymbol) 141 | { 142 | string eventName = eventSymbol.Name; 143 | string eventType = GetEventType(eventSymbol); 144 | EventFieldDeclarationSyntax eventDclr = SF.EventFieldDeclaration( 145 | SF.VariableDeclaration(SF.IdentifierName(eventType), 146 | SF.SeparatedList(new[] {SF.VariableDeclarator(eventName)}))) 147 | .AddModifiers(SF.Token(SyntaxKind.PublicKeyword)); 148 | return eventDclr; 149 | } 150 | 151 | private static string GetEventType(IEventSymbol eventSymbol) 152 | { 153 | return eventSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 154 | } 155 | 156 | private static bool IsCustomDelegateBasedEvent(IEventSymbol eventSymbol, SemanticModel semanticModel) 157 | { 158 | var genericEventType = semanticModel.Compilation.GetTypeByMetadataName("System.EventHandler`1"); 159 | var eventType = semanticModel.Compilation.GetTypeByMetadataName("System.EventHandler"); 160 | 161 | if (eventSymbol.Type.MetadataName.Equals(genericEventType.MetadataName) || eventSymbol.Type.MetadataName.Equals(eventType.MetadataName)) 162 | { 163 | return false; 164 | } 165 | 166 | return true; 167 | } 168 | } 169 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/IInterfaceStubber.cs: -------------------------------------------------------------------------------- 1 | using Etg.SimpleStubs.CodeGen.Config; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Etg.SimpleStubs.CodeGen 6 | { 7 | internal interface IInterfaceStubber 8 | { 9 | CompilationUnitSyntax StubInterface(CompilationUnitSyntax cu, InterfaceDeclarationSyntax interfaceDclr, SemanticModel semanticModel, SimpleStubsConfig config); 10 | } 11 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/IMethodStubber.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Etg.SimpleStubs.CodeGen 5 | { 6 | internal interface IMethodStubber 7 | { 8 | ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol, INamedTypeSymbol stubbedInterface, SemanticModel semanticModel); 9 | } 10 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/IProjectStubber.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | namespace Etg.SimpleStubs.CodeGen.CodeGen 6 | { 7 | internal interface IProjectStubber 8 | { 9 | Task StubProject(Project project, CompilationUnitSyntax cu); 10 | } 11 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/IPropertyStubber.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Etg.SimpleStubs.CodeGen 5 | { 6 | internal interface IPropertyStubber 7 | { 8 | ClassDeclarationSyntax StubProperty(ClassDeclarationSyntax classDclr, IPropertySymbol propertySymbol, INamedTypeSymbol stubbedInterface, SemanticModel semanticModel); 9 | } 10 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/InterfaceStubber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using Etg.SimpleStubs.CodeGen.Config; 8 | using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 9 | using Etg.SimpleStubs.CodeGen.Utils; 10 | 11 | namespace Etg.SimpleStubs.CodeGen 12 | { 13 | internal class InterfaceStubber : IInterfaceStubber 14 | { 15 | private readonly IEnumerable _methodStubbers; 16 | private readonly IEnumerable _propertyStubbers; 17 | 18 | public InterfaceStubber(IEnumerable methodStubbers, IEnumerable propertyStubbers) 19 | { 20 | _propertyStubbers = propertyStubbers; 21 | _methodStubbers = new List(methodStubbers); 22 | } 23 | 24 | public CompilationUnitSyntax StubInterface(CompilationUnitSyntax cu, InterfaceDeclarationSyntax interfaceDclr, SemanticModel semanticModel, SimpleStubsConfig config) 25 | { 26 | INamedTypeSymbol interfaceType = semanticModel.GetDeclaredSymbol(interfaceDclr); 27 | NamespaceDeclarationSyntax namespaceNode = GetNamespaceNode(interfaceDclr); 28 | string interfaceName = interfaceType.GetGenericName(); 29 | string stubName = NamingUtils.GetStubName(interfaceName); 30 | ClassDeclarationSyntax classDclr = SF.ClassDeclaration(SF.Identifier(stubName)) 31 | .AddModifiers(SF.Token(RoslynUtils.GetVisibilityKeyword(interfaceType))) 32 | .WithBaseList(RoslynUtils.BaseList(interfaceName)) 33 | .AddAttributeLists(AttributeListList(Attribute("CompilerGenerated")).ToArray()); 34 | 35 | classDclr = RoslynUtils.CopyGenericConstraints(interfaceType, classDclr); 36 | classDclr = AddStubContainerField(classDclr, stubName); 37 | classDclr = AddMockBehaviorProperty(classDclr); 38 | classDclr = StubProperties(interfaceType, classDclr, semanticModel); 39 | classDclr = StubMethods(interfaceType, classDclr, semanticModel); 40 | classDclr = AddConstructor(interfaceType, classDclr, config); 41 | 42 | string fullNameSpace = semanticModel.GetDeclaredSymbol(namespaceNode).ToString(); 43 | NamespaceDeclarationSyntax namespaceDclr = SF.NamespaceDeclaration(SF.IdentifierName(fullNameSpace)) 44 | .WithUsings(namespaceNode.Usings); 45 | namespaceDclr = namespaceDclr.AddMembers(classDclr); 46 | cu = cu.AddMembers(namespaceDclr); 47 | return cu; 48 | } 49 | 50 | private ClassDeclarationSyntax AddConstructor(INamedTypeSymbol interfaceType, ClassDeclarationSyntax classDclr, SimpleStubsConfig config) 51 | { 52 | string ctorName = NamingUtils.GetStubName(interfaceType.Name); 53 | string defaultMockBehavior = GetValidMockBehaviorEnumValue(config.DefaultMockBehavior); 54 | 55 | var ctorParameter = 56 | SF.Parameter(SF.Identifier("mockBehavior")) 57 | .WithType(SF.ParseTypeName("MockBehavior")) 58 | .WithDefault(SF.EqualsValueClause(SF.ParseExpression($"MockBehavior.{defaultMockBehavior}"))); 59 | 60 | classDclr = classDclr.AddMembers(SF.ConstructorDeclaration(ctorName) 61 | .WithModifiers(SF.TokenList(SF.Token(SyntaxKind.PublicKeyword))) 62 | .WithParameterList(SF.ParameterList().AddParameters(ctorParameter)) 63 | .WithBody(SF.Block().AddStatements(SF.ExpressionStatement( 64 | SF.AssignmentExpression( 65 | SyntaxKind.SimpleAssignmentExpression, 66 | SF.IdentifierName("MockBehavior"), 67 | SF.IdentifierName("mockBehavior"))) 68 | ))); 69 | return classDclr; 70 | } 71 | 72 | private ClassDeclarationSyntax StubProperties(INamedTypeSymbol interfaceType, ClassDeclarationSyntax classDclr, SemanticModel semanticModel) 73 | { 74 | IEnumerable propertiesToStub = RoslynUtils.GetAllMembers(interfaceType); 75 | foreach (IPropertySymbol propertySymbol in propertiesToStub) 76 | { 77 | foreach (IPropertyStubber propertyStubber in _propertyStubbers) 78 | { 79 | classDclr = propertyStubber.StubProperty(classDclr, propertySymbol, interfaceType, semanticModel); 80 | } 81 | } 82 | return classDclr; 83 | } 84 | 85 | private ClassDeclarationSyntax StubMethods(INamedTypeSymbol interfaceType, ClassDeclarationSyntax classDclr, SemanticModel semanticModel) 86 | { 87 | IEnumerable methodsToStub = RoslynUtils.GetAllMembers(interfaceType); 88 | foreach (IMethodSymbol methodSymbol in methodsToStub) 89 | { 90 | foreach (IMethodStubber methodStubber in _methodStubbers) 91 | { 92 | 93 | classDclr = methodStubber.StubMethod(classDclr, methodSymbol, interfaceType, semanticModel); 94 | } 95 | } 96 | return classDclr; 97 | } 98 | 99 | private static ClassDeclarationSyntax AddStubContainerField(ClassDeclarationSyntax classDclr, string stubName) 100 | { 101 | classDclr = classDclr.AddMembers( 102 | SF.FieldDeclaration( 103 | SF.VariableDeclaration(SF.ParseTypeName($"StubContainer<{stubName}>"), 104 | SF.SeparatedList(new[] 105 | { 106 | SF.VariableDeclarator(SF.Identifier("_stubs"), null, 107 | SF.EqualsValueClause(SF.ParseExpression($"new StubContainer<{stubName}>()"))) 108 | }))) 109 | .AddModifiers(SF.Token(SyntaxKind.PrivateKeyword), SF.Token(SyntaxKind.ReadOnlyKeyword))); 110 | return classDclr; 111 | } 112 | 113 | private static ClassDeclarationSyntax AddMockBehaviorProperty(ClassDeclarationSyntax classDclr) 114 | { 115 | classDclr = classDclr.AddMembers( 116 | SF.PropertyDeclaration(SF.ParseTypeName("MockBehavior"), "MockBehavior") 117 | .AddAccessorListAccessors( 118 | SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken)), 119 | SF.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken))) 120 | .WithModifiers(SF.TokenList(SF.Token(SyntaxKind.PublicKeyword)))); 121 | 122 | return classDclr; 123 | } 124 | 125 | private static string GetValidMockBehaviorEnumValue(string suppliedValue) 126 | { 127 | if ("Strict".Equals(suppliedValue, StringComparison.OrdinalIgnoreCase)) 128 | { 129 | return "Strict"; 130 | } 131 | 132 | return "Loose"; 133 | } 134 | 135 | private static NamespaceDeclarationSyntax GetNamespaceNode(InterfaceDeclarationSyntax interfaceNode) 136 | { 137 | var namespaceNode = interfaceNode.Parent as NamespaceDeclarationSyntax; 138 | if (namespaceNode == null) 139 | { 140 | throw new Exception("A grain interface must be declared inside a namespace"); 141 | } 142 | 143 | return namespaceNode; 144 | } 145 | 146 | private static SyntaxList AttributeListList(params AttributeSyntax[] attributes) 147 | { 148 | var list = new SyntaxList(); 149 | foreach (AttributeSyntax attributeSyntax in attributes) 150 | { 151 | list = list.Add(AttributeList(attributeSyntax)); 152 | } 153 | return list; 154 | } 155 | 156 | private static AttributeListSyntax AttributeList(params AttributeSyntax[] attributes) 157 | { 158 | SeparatedSyntaxList separatedList = SF.SeparatedList(); 159 | foreach (var attributeSyntax in attributes) 160 | { 161 | separatedList = separatedList.Add(attributeSyntax); 162 | } 163 | return SF.AttributeList(separatedList); 164 | } 165 | 166 | private static AttributeSyntax Attribute(string attributeName) 167 | { 168 | return SF.Attribute(SF.IdentifierName(attributeName)); 169 | } 170 | } 171 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/OrdinaryMethodStubber.cs: -------------------------------------------------------------------------------- 1 | using Etg.SimpleStubs.CodeGen.Utils; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; 5 | using System.Linq; 6 | using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 7 | 8 | namespace Etg.SimpleStubs.CodeGen 9 | { 10 | internal class OrdinaryMethodStubber : IMethodStubber 11 | { 12 | public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol, INamedTypeSymbol stubbedInterface, SemanticModel semanticModel) 13 | { 14 | if (!methodSymbol.IsOrdinaryMethod()) 15 | { 16 | return classDclr; 17 | } 18 | 19 | MethodDeclarationSyntax methodDclr = SF.MethodDeclaration( 20 | SF.ParseTypeName(methodSymbol.ReturnType.GetFullyQualifiedName()), methodSymbol.GetGenericName()); 21 | methodDclr = methodDclr.WithParameterList(methodDclr.ParameterList.AddParameters( 22 | RoslynUtils.GetMethodParameterSyntaxList(methodSymbol).ToArray())); 23 | methodDclr = methodDclr.WithSemicolonToken(SF.Token(SyntaxKind.None)) 24 | .WithExplicitInterfaceSpecifier( 25 | SF.ExplicitInterfaceSpecifier( 26 | SF.IdentifierName(methodSymbol.GetContainingInterfaceGenericQualifiedName()))); 27 | 28 | string delegateTypeName = NamingUtils.GetDelegateTypeName(methodSymbol, stubbedInterface); 29 | string parameters = StubbingUtils.FormatParameters(methodSymbol); 30 | var outParameters = methodSymbol.Parameters.Where(p => p.RefKind == RefKind.Out); 31 | 32 | var methodBlock = StubbingUtils.GetInvocationBlockSyntax(delegateTypeName, methodSymbol.GetGenericName(), 33 | parameters, outParameters, methodSymbol.ReturnType, semanticModel); 34 | 35 | classDclr = classDclr.AddMembers(methodDclr.WithBody(methodBlock)); 36 | 37 | return classDclr; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/ProjectStubber.cs: -------------------------------------------------------------------------------- 1 | using Etg.SimpleStubs.CodeGen.Config; 2 | using Etg.SimpleStubs.CodeGen.Utils; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | 12 | namespace Etg.SimpleStubs.CodeGen.CodeGen 13 | { 14 | internal class ProjectStubber : IProjectStubber 15 | { 16 | private readonly IInterfaceStubber _interfaceStubber; 17 | private readonly SimpleStubsConfig _config; 18 | 19 | public ProjectStubber(IInterfaceStubber interfaceStubber, SimpleStubsConfig config) 20 | { 21 | _interfaceStubber = interfaceStubber; 22 | _config = config; 23 | } 24 | 25 | public async Task StubProject(Project project, CompilationUnitSyntax cu) 26 | { 27 | var usings = new HashSet(new UsingDirectiveEqualityComparer()); 28 | foreach (Document document in project.Documents) 29 | { 30 | SyntaxTree syntaxTree = await document.GetSyntaxTreeAsync(); 31 | SemanticModel semanticModel = await document.GetSemanticModelAsync(); 32 | IEnumerable interfaces = 33 | syntaxTree.GetRoot() 34 | .DescendantNodes() 35 | .OfType() 36 | .Where(SatisfiesVisibilityConstraints); 37 | if (!interfaces.Any()) 38 | { 39 | continue; 40 | } 41 | 42 | foreach (var interfaceDclr in interfaces) 43 | { 44 | try 45 | { 46 | INamedTypeSymbol interfaceType = semanticModel.GetDeclaredSymbol(interfaceDclr); 47 | if (!_config.IgnoredInterfaces.Contains(interfaceType.GetQualifiedName())) 48 | { 49 | LogWarningsIfAny(semanticModel); 50 | cu = _interfaceStubber.StubInterface(cu, interfaceDclr, semanticModel, _config); 51 | } 52 | } 53 | catch (Exception e) 54 | { 55 | Trace.TraceError($"Could not generate stubs for interface {interfaceDclr}, Exception: {e}"); 56 | } 57 | } 58 | usings.UnionWith(syntaxTree.GetCompilationUnitRoot().Usings.Select( 59 | usingDirective => usingDirective.WithLeadingTrivia(SyntaxTriviaList.Empty).WithTrailingTrivia(SyntaxTriviaList.Empty))); 60 | } 61 | 62 | return new StubProjectResult(cu, usings); 63 | } 64 | 65 | private bool SatisfiesVisibilityConstraints(InterfaceDeclarationSyntax i) 66 | { 67 | return i.IsPublic() || (_config.StubInternalInterfaces && i.IsInternal()); 68 | } 69 | 70 | private void LogWarningsIfAny(SemanticModel semanticModel) 71 | { 72 | foreach (var diagnostic in semanticModel.GetDiagnostics()) 73 | { 74 | Trace.TraceInformation(diagnostic.ToString()); 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/PropertyStubber.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Etg.SimpleStubs.CodeGen.Utils; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | 7 | namespace Etg.SimpleStubs.CodeGen 8 | { 9 | using SF = SyntaxFactory; 10 | 11 | internal class PropertyStubber : IPropertyStubber 12 | { 13 | public ClassDeclarationSyntax StubProperty(ClassDeclarationSyntax classDclr, IPropertySymbol propertySymbol, INamedTypeSymbol stubbedInterface, SemanticModel semanticModel) 14 | { 15 | string indexerType = propertySymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 16 | BasePropertyDeclarationSyntax propDclr = null; 17 | 18 | if (propertySymbol.GetMethod != null) 19 | { 20 | IMethodSymbol getMethodSymbol = propertySymbol.GetMethod; 21 | string parameters = StubbingUtils.FormatParameters(getMethodSymbol); 22 | 23 | string delegateTypeName = NamingUtils.GetDelegateTypeName(getMethodSymbol, stubbedInterface); 24 | var accessorDclr = SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, SF.Block( 25 | SF.List(new[] 26 | { 27 | StubbingUtils.GetInvocationBlockSyntax(delegateTypeName, getMethodSymbol.Name, parameters, 28 | Enumerable.Empty(), getMethodSymbol.ReturnType, semanticModel) 29 | }))); 30 | 31 | propDclr = CreatePropertyDclr(getMethodSymbol, indexerType); 32 | propDclr = propDclr.AddAccessorListAccessors(accessorDclr); 33 | } 34 | 35 | if (propertySymbol.SetMethod != null) 36 | { 37 | var voidType = semanticModel.Compilation.GetTypeByMetadataName("System.Void"); 38 | IMethodSymbol setMethodSymbol = propertySymbol.SetMethod; 39 | string parameters = $"{StubbingUtils.FormatParameters(setMethodSymbol)}"; 40 | string delegateTypeName = NamingUtils.GetDelegateTypeName(setMethodSymbol, stubbedInterface); 41 | var accessorDclr = SF.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, SF.Block( 42 | SF.List(new[] 43 | { 44 | StubbingUtils.GetInvocationBlockSyntax(delegateTypeName, setMethodSymbol.Name, parameters, 45 | Enumerable.Empty(), voidType, semanticModel) 46 | }))); 47 | if (propDclr == null) 48 | { 49 | propDclr = CreatePropertyDclr(setMethodSymbol, indexerType); 50 | } 51 | propDclr = propDclr.AddAccessorListAccessors(accessorDclr); 52 | } 53 | 54 | classDclr = classDclr.AddMembers(propDclr); 55 | return classDclr; 56 | } 57 | 58 | private BasePropertyDeclarationSyntax CreatePropertyDclr(IMethodSymbol methodSymbol, string propType) 59 | { 60 | if (methodSymbol.IsIndexerAccessor()) 61 | { 62 | IndexerDeclarationSyntax indexerDclr = SF.IndexerDeclaration( 63 | SF.ParseTypeName(propType)) 64 | .WithExplicitInterfaceSpecifier(SF.ExplicitInterfaceSpecifier( 65 | SF.IdentifierName(methodSymbol.GetContainingInterfaceGenericQualifiedName()))); 66 | indexerDclr = indexerDclr.AddParameterListParameters( 67 | RoslynUtils.GetMethodParameterSyntaxList(methodSymbol).ToArray()); 68 | return indexerDclr; 69 | } 70 | 71 | string propName = methodSymbol.AssociatedSymbol.Name; 72 | PropertyDeclarationSyntax propDclr = SF.PropertyDeclaration(SF.ParseTypeName(propType), SF.Identifier(propName)) 73 | .WithExplicitInterfaceSpecifier(SF.ExplicitInterfaceSpecifier( 74 | SF.IdentifierName(methodSymbol.GetContainingInterfaceGenericQualifiedName()))); 75 | return propDclr; 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/SimpleStubsGenerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | using Microsoft.CodeAnalysis.MSBuild; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using Microsoft.CodeAnalysis.Formatting; 8 | using System.Threading.Tasks; 9 | using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 10 | using Etg.SimpleStubs.CodeGen.Config; 11 | using Etg.SimpleStubs.CodeGen.CodeGen; 12 | using System.Text; 13 | using Microsoft.Build.Locator; 14 | 15 | namespace Etg.SimpleStubs.CodeGen 16 | { 17 | internal class SimpleStubsGenerator 18 | { 19 | private readonly IProjectStubber _projectStubber; 20 | private readonly SimpleStubsConfig _config; 21 | 22 | public SimpleStubsGenerator(IProjectStubber projectStubber, SimpleStubsConfig config) 23 | { 24 | _projectStubber = projectStubber; 25 | _config = config; 26 | } 27 | 28 | public async Task GenerateStubs(string testProjectPath, string configuration, string platform, string visualStudioVersion) 29 | { 30 | RegisterVisualStudioInstance(visualStudioVersion); 31 | 32 | using (var workspace = MSBuildWorkspace.Create(new Dictionary { 33 | { "Configuration", configuration }, 34 | { "Platform", platform} 35 | })) 36 | { 37 | Project currentProject = await workspace.OpenProjectAsync(testProjectPath); 38 | 39 | if (workspace.Diagnostics.Any(d => d.Kind == WorkspaceDiagnosticKind.Failure)) 40 | { 41 | StringBuilder stringBuilder = new StringBuilder(); 42 | foreach (var diagnostic in workspace.Diagnostics) 43 | { 44 | stringBuilder.AppendLine(diagnostic.ToString()); 45 | } 46 | Console.WriteLine("Simplestubs encountered errors when opening the workspace; Stubs will be generated only for projects that successfully opened. Errors: " + stringBuilder.ToString()); 47 | } 48 | else 49 | { 50 | Console.WriteLine("MSBuildWorkspace loaded with no errors!"); 51 | } 52 | 53 | if (currentProject == null) 54 | { 55 | throw new ArgumentException("Could not open the project located at " + testProjectPath); 56 | } 57 | 58 | List projectsToStub = GetListOfProjectsToStub(workspace, currentProject); 59 | if (!projectsToStub.Any()) 60 | { 61 | return string.Empty; 62 | } 63 | 64 | CompilationUnitSyntax cu = SF.CompilationUnit(); 65 | var usings = new HashSet(new UsingDirectiveEqualityComparer()); 66 | usings.Add(ToUsingDirective(" System")); 67 | usings.Add(ToUsingDirective(" System.Runtime.CompilerServices")); 68 | usings.Add(ToUsingDirective(" Etg.SimpleStubs")); 69 | 70 | foreach (Project project in projectsToStub) 71 | { 72 | var res = await _projectStubber.StubProject(project, cu); 73 | cu = res.CompilationUnit; 74 | usings.UnionWith(res.Usings); 75 | } 76 | 77 | cu = cu.AddUsings(usings.ToArray()); 78 | return Formatter.Format(cu, workspace).ToString(); 79 | } 80 | } 81 | 82 | private static void RegisterVisualStudioInstance(string visualStudioVersion) 83 | { 84 | Version version = new Version(visualStudioVersion); 85 | string major = version.Major.ToString(); 86 | 87 | IEnumerable vsInstances = MSBuildLocator.QueryVisualStudioInstances(); 88 | VisualStudioInstance currentInstance = vsInstances.FirstOrDefault(i => i.Version.ToString().StartsWith(major)); 89 | if (currentInstance == null) 90 | { 91 | throw new Exception($"Could not find a visual studio instance that matches the version {visualStudioVersion}"); 92 | } 93 | MSBuildLocator.RegisterInstance(currentInstance); 94 | } 95 | 96 | UsingDirectiveSyntax ToUsingDirective(string nameSpace) 97 | { 98 | return SF.UsingDirective(SF.IdentifierName(nameSpace)).WithLeadingTrivia(); 99 | } 100 | 101 | private List GetListOfProjectsToStub(MSBuildWorkspace workspace, Project currentProject) 102 | { 103 | var projectsToStub = new List(); 104 | 105 | if (_config.StubCurrentProject) 106 | { 107 | projectsToStub.Add(currentProject); 108 | } 109 | 110 | foreach (ProjectReference projectRef in currentProject.ProjectReferences) 111 | { 112 | Project project = workspace.CurrentSolution.GetProject(projectRef.ProjectId); 113 | if (!_config.IgnoredProjects.Contains(project.Name)) 114 | { 115 | projectsToStub.Add(project); 116 | } 117 | } 118 | 119 | return projectsToStub; 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/StubProjectResult.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp.Syntax; 2 | using System.Collections.Generic; 3 | 4 | namespace Etg.SimpleStubs.CodeGen.CodeGen 5 | { 6 | internal class StubProjectResult 7 | { 8 | public StubProjectResult(CompilationUnitSyntax cu, IEnumerable usings) 9 | { 10 | CompilationUnit = cu; 11 | Usings = usings; 12 | } 13 | 14 | public CompilationUnitSyntax CompilationUnit { get; } 15 | 16 | public IEnumerable Usings { get; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/StubbingDelegateGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | using Etg.SimpleStubs.CodeGen.Utils; 5 | using Microsoft.CodeAnalysis.Editing; 6 | 7 | namespace Etg.SimpleStubs.CodeGen 8 | { 9 | using Microsoft.CodeAnalysis.CSharp; 10 | using System.Collections.Generic; 11 | using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 12 | 13 | internal class StubbingDelegateGenerator : IMethodStubber 14 | { 15 | public ClassDeclarationSyntax StubMethod(ClassDeclarationSyntax classDclr, IMethodSymbol methodSymbol, INamedTypeSymbol stubbedInterface, SemanticModel semanticModel) 16 | { 17 | if (methodSymbol.IsPropertyAccessor() || methodSymbol.IsOrdinaryMethod()) 18 | { 19 | string delegateTypeName = NamingUtils.GetDelegateTypeName(methodSymbol, stubbedInterface); 20 | string setupMethodName = NamingUtils.GetSetupMethodName(methodSymbol); 21 | 22 | DelegateDeclarationSyntax delegateDclr = GenerateDelegateDclr(methodSymbol, delegateTypeName, 23 | stubbedInterface); 24 | MethodDeclarationSyntax propDclr = GenerateSetupMethod(methodSymbol, setupMethodName, delegateTypeName, 25 | stubbedInterface, classDclr); 26 | classDclr = classDclr.AddMembers(delegateDclr, propDclr); 27 | } 28 | 29 | return classDclr; 30 | } 31 | 32 | private static MethodDeclarationSyntax GenerateSetupMethod(IMethodSymbol methodSymbol, string setupMethodName, string delegateTypeName, 33 | INamedTypeSymbol stubbedInterface, 34 | ClassDeclarationSyntax stub) 35 | { 36 | SyntaxKind visibility = RoslynUtils.GetVisibilityKeyword(stubbedInterface); 37 | MethodDeclarationSyntax methodDclr = SF.MethodDeclaration(SF.ParseTypeName(stub.Identifier.Text), setupMethodName) 38 | .AddModifiers(SF.Token(visibility)).WithSemicolonToken(SF.Token(SyntaxKind.SemicolonToken)) 39 | .AddParameterListParameters( 40 | SF.Parameter(SF.Identifier("del")).WithType(SF.ParseTypeName(delegateTypeName)), 41 | SF.Parameter(SF.Identifier("count")).WithType(SF.ParseTypeName("int")).WithDefault(SF.EqualsValueClause(SF.ParseExpression("Times.Forever"))), 42 | SF.Parameter(SF.Identifier("overwrite")).WithType(SF.ParseTypeName("bool")).WithDefault(SF.EqualsValueClause(SF.ParseExpression("false"))) 43 | ) 44 | .WithBody(SF.Block( 45 | SF.ParseStatement("_stubs.SetMethodStub(del, count, overwrite);\n"), 46 | SF.ParseStatement("return this;\n") 47 | )) 48 | .WithSemicolonToken(SF.Token(SyntaxKind.None)); 49 | 50 | return RoslynUtils.CopyGenericConstraints(methodSymbol, methodDclr); 51 | } 52 | 53 | private static DelegateDeclarationSyntax GenerateDelegateDclr(IMethodSymbol methodSymbol, string delegateName, 54 | INamedTypeSymbol stubbedInterface) 55 | { 56 | SyntaxKind visibility = RoslynUtils.GetVisibilityKeyword(stubbedInterface); 57 | List paramsSyntaxList = RoslynUtils.GetMethodParameterSyntaxList(methodSymbol); 58 | DelegateDeclarationSyntax delegateDeclaration = SF.DelegateDeclaration(SF.ParseTypeName(methodSymbol.ReturnType.GetFullyQualifiedName()), 59 | delegateName) 60 | .AddModifiers(SF.Token(visibility)).AddParameterListParameters(paramsSyntaxList.ToArray()); 61 | 62 | delegateDeclaration = RoslynUtils.CopyGenericConstraints(methodSymbol, delegateDeclaration); 63 | return delegateDeclaration; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CodeGen/UsingDirectiveEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | 4 | namespace Etg.SimpleStubs.CodeGen.CodeGen 5 | { 6 | internal class UsingDirectiveEqualityComparer : IEqualityComparer 7 | { 8 | public bool Equals(UsingDirectiveSyntax x, UsingDirectiveSyntax y) 9 | { 10 | return x.ToString() == y.ToString(); 11 | } 12 | 13 | public int GetHashCode(UsingDirectiveSyntax obj) 14 | { 15 | return obj.ToString().GetHashCode(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/CommandLineParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Etg.SimpleStubs.CodeGen 8 | { 9 | class CommandLineParser 10 | { 11 | public CommandLineParser() 12 | { 13 | Arguments = new Dictionary(); 14 | } 15 | 16 | public IDictionary Arguments { get; private set; } 17 | 18 | public void Parse(string[] args) 19 | { 20 | foreach (var arg in args) 21 | { 22 | if (arg.StartsWith("-")) 23 | { 24 | var colon = arg.IndexOf(':'); 25 | 26 | if (colon != -1) 27 | { 28 | this.Arguments.Add(arg.Substring(0, colon), arg.Substring(colon + 1).Trim('\'', '\"')); 29 | } 30 | else 31 | { 32 | this.Arguments.Add(arg, string.Empty); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/Config/ConfigLoader.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Newtonsoft.Json; 3 | using System.IO; 4 | 5 | namespace Etg.SimpleStubs.CodeGen.Config 6 | { 7 | internal class ConfigLoader 8 | { 9 | public SimpleStubsConfig LoadConfig(string configFilePath) 10 | { 11 | if (File.Exists(configFilePath)) 12 | { 13 | return JsonConvert.DeserializeObject(File.ReadAllText(configFilePath)); 14 | } 15 | 16 | return new SimpleStubsConfig(new string[] {}, new string[] {}, false, false, "Loose"); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/Config/SimpleStubsConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Etg.SimpleStubs.CodeGen.Config 4 | { 5 | internal class SimpleStubsConfig 6 | { 7 | public SimpleStubsConfig(IEnumerable ignoredProjects, 8 | IEnumerable ignoredInterfaces, 9 | bool stubInternalInterfaces, 10 | bool stubCurrentProject, 11 | string defaultMockBehavior) 12 | { 13 | IgnoredProjects = new HashSet(ignoredProjects); 14 | IgnoredInterfaces = new HashSet(ignoredInterfaces); 15 | StubInternalInterfaces = stubInternalInterfaces; 16 | StubCurrentProject = stubCurrentProject; 17 | DefaultMockBehavior = defaultMockBehavior; 18 | } 19 | 20 | public ISet IgnoredProjects { get; } 21 | 22 | public ISet IgnoredInterfaces { get; } 23 | 24 | public bool StubInternalInterfaces { get; } 25 | 26 | public bool StubCurrentProject { get; } 27 | 28 | public string DefaultMockBehavior { get; } 29 | } 30 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/DI/DIModule.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Etg.SimpleStubs.CodeGen.CodeGen; 3 | using Etg.SimpleStubs.CodeGen.Config; 4 | using System.IO; 5 | 6 | namespace Etg.SimpleStubs.CodeGen.DI 7 | { 8 | internal class DiModule 9 | { 10 | private readonly IContainer _container; 11 | 12 | public DiModule(string testProjectPath, string stubsFilePath) 13 | { 14 | _container = RegisterTypes(testProjectPath, stubsFilePath).Build(); 15 | } 16 | 17 | public static ContainerBuilder RegisterTypes(string testProjectPath, string stubsFilePath) 18 | { 19 | ContainerBuilder cb = new ContainerBuilder(); 20 | 21 | string configFilePath = Path.Combine(Path.GetDirectoryName(testProjectPath), "SimpleStubs.json"); 22 | SimpleStubsConfig config = new ConfigLoader().LoadConfig(configFilePath); 23 | 24 | cb.RegisterInstance(config); 25 | 26 | cb.Register((c) => 27 | { 28 | IInterfaceStubber interfaceStubber = new InterfaceStubber(new IMethodStubber[] 29 | { 30 | new OrdinaryMethodStubber(), 31 | new EventStubber(), 32 | new StubbingDelegateGenerator() 33 | }, 34 | new IPropertyStubber[] 35 | { 36 | new PropertyStubber() 37 | }); 38 | return interfaceStubber; 39 | }).As().SingleInstance(); 40 | 41 | cb.RegisterType().As().SingleInstance(); 42 | cb.RegisterType().AsSelf(); 43 | 44 | return cb; 45 | } 46 | 47 | public SimpleStubsGenerator StubsGenerator => _container.Resolve(); 48 | } 49 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Text; 5 | using Etg.SimpleStubs.CodeGen.DI; 6 | 7 | namespace Etg.SimpleStubs.CodeGen 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | CommandLineParser parser = new CommandLineParser(); 14 | parser.Parse(args); 15 | 16 | string projectPath = parser.Arguments["-ProjectPath"]; 17 | if (string.IsNullOrEmpty(projectPath)) 18 | { 19 | Console.WriteLine(DecorateMessage($"ProjectPath cannot be empty")); 20 | return; 21 | } 22 | if (!File.Exists(projectPath)) 23 | { 24 | Console.WriteLine(DecorateMessage($"{projectPath} does not exist")); 25 | return; 26 | } 27 | 28 | string outputPath = parser.Arguments["-OutputPath"]; 29 | if (string.IsNullOrEmpty(outputPath)) 30 | { 31 | Console.WriteLine(DecorateMessage($"OutputPath cannot be empty")); 32 | return; 33 | } 34 | 35 | string configuration = parser.Arguments["-Configuration"]; 36 | if (string.IsNullOrEmpty(configuration)) 37 | { 38 | Console.WriteLine(DecorateMessage($"Configuration cannot be empty")); 39 | return; 40 | } 41 | 42 | string platform = parser.Arguments["-Platform"]; 43 | if (string.IsNullOrEmpty(platform)) 44 | { 45 | Console.WriteLine(DecorateMessage($"Platform cannot be empty")); 46 | return; 47 | } 48 | 49 | string visualStudioVersion = parser.Arguments["-VisualStudioVersion"]; 50 | if (string.IsNullOrWhiteSpace(visualStudioVersion)) 51 | { 52 | Console.WriteLine(DecorateMessage($"VisualStudioVersion cannot be empty")); 53 | return; 54 | } 55 | 56 | DiModule diModule = new DiModule(projectPath, outputPath); 57 | Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); 58 | 59 | Console.WriteLine(DecorateMessage($"Generating stubs for project: {projectPath}")); 60 | 61 | try 62 | { 63 | string stubsCode = diModule.StubsGenerator.GenerateStubs(projectPath, configuration, platform, visualStudioVersion).Result; 64 | Console.WriteLine(DecorateMessage($"Writing stubs to file: {outputPath}")); 65 | File.WriteAllText(outputPath, stubsCode); 66 | } 67 | catch (ReflectionTypeLoadException ex) 68 | { 69 | StringBuilder sb = new StringBuilder(); 70 | foreach (Exception exSub in ex.LoaderExceptions) 71 | { 72 | sb.AppendLine(exSub.Message); 73 | FileNotFoundException exFileNotFound = exSub as FileNotFoundException; 74 | if (exFileNotFound != null) 75 | { 76 | if (!string.IsNullOrEmpty(exFileNotFound.FusionLog)) 77 | { 78 | sb.AppendLine("Fusion Log:"); 79 | sb.AppendLine(exFileNotFound.FusionLog); 80 | } 81 | } 82 | sb.AppendLine(); 83 | } 84 | string errorMessage = sb.ToString(); 85 | Console.WriteLine(DecorateMessage($"Failed to generate stubs: {errorMessage}")); 86 | } 87 | catch(Exception e) 88 | { 89 | Console.WriteLine(DecorateMessage($"Failed to generate stubs: {e.ToString()}")); 90 | } 91 | } 92 | 93 | private static string DecorateMessage(string message) 94 | { 95 | return "SimpleStubs: " + message; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | 9 | [assembly: AssemblyTitle("SimpleStubs")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("Etg.SimpleStubs.CodeGen")] 14 | [assembly: AssemblyCopyright("Copyright © 2016")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // Setting ComVisible to false makes the types in this assembly not visible 19 | // to COM components. If you need to access a type in this assembly from 20 | // COM, set the ComVisible attribute to true on that type. 21 | 22 | [assembly: ComVisible(false)] 23 | 24 | // The following GUID is for the ID of the typelib if this project is exposed to COM 25 | 26 | [assembly: Guid("3e9c520a-94cf-46d0-864b-4293d439c92a")] 27 | 28 | // Version information for an assembly consists of the following four values: 29 | // 30 | // Major Version 31 | // Minor Version 32 | // Build Number 33 | // Revision 34 | // 35 | // You can specify all the values or you can default the Build and Revision Numbers 36 | // by using the '*' as shown below: 37 | // [assembly: AssemblyVersion("1.0.*")] 38 | 39 | [assembly: AssemblyVersion("0.0.1.0")] 40 | [assembly: InternalsVisibleTo("TestClassLibraryTest")] 41 | [assembly: InternalsVisibleTo("DebuggingConsoleApp")] -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/Utils/NamingUtils.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace Etg.SimpleStubs.CodeGen.Utils 6 | { 7 | internal class NamingUtils 8 | { 9 | public string GetInterfaceStubName(string interfaceName) 10 | { 11 | return "Stub" + interfaceName; 12 | } 13 | 14 | public static string GetSetupMethodName(IMethodSymbol methodSymbol) 15 | { 16 | if (methodSymbol.IsPropertyGetter()) 17 | { 18 | return methodSymbol.Name.Substring(4) + "_Get"; 19 | } 20 | if (methodSymbol.IsPropertySetter()) 21 | { 22 | return methodSymbol.Name.Substring(4) + "_Set"; 23 | } 24 | return methodSymbol.GetGenericName(); 25 | } 26 | 27 | public static string GetDelegateTypeName(IMethodSymbol methodSymbol, INamedTypeSymbol targetInterface) 28 | { 29 | string methodName = methodSymbol.Name; 30 | if (methodSymbol.IsPropertyGetter()) 31 | { 32 | methodName = methodName.Substring(4) + "_Get"; 33 | } 34 | else if (methodSymbol.IsPropertySetter()) 35 | { 36 | methodName = methodName.Substring(4) + "_Set"; 37 | } 38 | 39 | // only prefix inherited members 40 | if (targetInterface.GetGenericName() != methodSymbol.ContainingSymbol.GetGenericName()) 41 | { 42 | methodName = SerializeName(methodSymbol.ContainingSymbol) + "_" + methodName; 43 | } 44 | 45 | if (methodSymbol.IsOrdinaryMethod() || methodSymbol.IsIndexerAccessor()) 46 | { 47 | if (methodSymbol.Parameters.Any()) 48 | { 49 | methodName = methodName + "_" + string.Join("_", methodSymbol.Parameters.Select(SerializeName)); 50 | } 51 | } 52 | 53 | methodName += "_Delegate"; 54 | 55 | if (methodSymbol.IsGenericMethod) 56 | { 57 | methodName = 58 | $"{methodName}<{string.Join(",", methodSymbol.TypeParameters.Select(symbol => symbol.Name))}>"; 59 | } 60 | return methodName; 61 | } 62 | 63 | public static string SerializeName(ISymbol param) 64 | { 65 | StringBuilder sb = new StringBuilder(); 66 | foreach (var part in param.ToDisplayParts(SymbolDisplayFormat.MinimallyQualifiedFormat)) 67 | { 68 | switch (part.ToString()) 69 | { 70 | case "<": 71 | sb.Append("Of"); 72 | break; 73 | case ">": 74 | break; 75 | case "]": 76 | break; 77 | case "[": 78 | sb.Append("Array"); 79 | break; 80 | default: 81 | if (part.Symbol != null && part.Kind != SymbolDisplayPartKind.ParameterName) 82 | { 83 | sb.Append(part.Symbol.Name); 84 | } 85 | break; 86 | } 87 | } 88 | 89 | return sb.ToString(); 90 | } 91 | 92 | 93 | public static string GetStubName(string interfaceName) 94 | { 95 | return "Stub" + interfaceName; 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/Utils/RoslynExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using System; 6 | using System.Collections.Generic; 7 | 8 | namespace Etg.SimpleStubs.CodeGen.Utils 9 | { 10 | internal static class RoslynExtensions 11 | { 12 | public static SymbolDisplayFormat QualifiedFormat = new SymbolDisplayFormat( 13 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); 14 | 15 | public static bool IsEvent(this IMethodSymbol methodSymbol) 16 | { 17 | return methodSymbol.IsEventAdd() || methodSymbol.IsEventRemove(); 18 | } 19 | 20 | public static bool IsEventAdd(this IMethodSymbol methodSymbol) 21 | { 22 | return methodSymbol.MethodKind == MethodKind.EventAdd; 23 | } 24 | 25 | public static bool IsEventRemove(this IMethodSymbol methodSymbol) 26 | { 27 | return methodSymbol.MethodKind == MethodKind.EventRemove; 28 | } 29 | 30 | public static bool IsPropertyAccessor(this IMethodSymbol methodSymbol) 31 | { 32 | return methodSymbol.IsPropertyGetter() || methodSymbol.IsPropertySetter(); 33 | } 34 | 35 | public static bool IsPropertySetter(this IMethodSymbol methodSymbol) 36 | { 37 | return methodSymbol.MethodKind == MethodKind.PropertySet; 38 | } 39 | 40 | public static bool IsPropertyGetter(this IMethodSymbol methodSymbol) 41 | { 42 | return methodSymbol.MethodKind == MethodKind.PropertyGet; 43 | } 44 | 45 | public static bool IsIndexerGetter(this IMethodSymbol methodSymbol) 46 | { 47 | return methodSymbol.Name == "get_Item"; 48 | } 49 | 50 | public static bool IsIndexerSetter(this IMethodSymbol methodSymbol) 51 | { 52 | return methodSymbol.Name == "set_Item"; 53 | } 54 | 55 | public static bool IsIndexerAccessor(this IMethodSymbol methodSymbol) 56 | { 57 | IPropertySymbol propertySymbol = methodSymbol.AssociatedSymbol as IPropertySymbol; 58 | return propertySymbol != null && propertySymbol.IsIndexer; 59 | } 60 | 61 | public static string GetGenericName(this IMethodSymbol methodSymbol) 62 | { 63 | string name = methodSymbol.Name; 64 | if (methodSymbol.IsGenericMethod) 65 | { 66 | name = $"{name}<{string.Join(",", methodSymbol.TypeParameters.Select(p => p.Name))}>"; 67 | } 68 | return name; 69 | } 70 | 71 | public static string GetContainingInterfaceGenericQualifiedName(this ISymbol methodSymbol) 72 | { 73 | return methodSymbol.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 74 | } 75 | 76 | public static string GetGenericName(this ISymbol namedType) 77 | { 78 | return namedType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); 79 | } 80 | 81 | public static string GetFullyQualifiedName(this ITypeSymbol symbol) 82 | { 83 | return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 84 | } 85 | 86 | public static string GetQualifiedName(this ITypeSymbol symbol) 87 | { 88 | return 89 | symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) 90 | .Split(new[] {"::"}, StringSplitOptions.RemoveEmptyEntries) 91 | .Last(); 92 | } 93 | 94 | public static string GetMinimallyQualifiedName(this ITypeSymbol symbol) 95 | { 96 | return symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); 97 | } 98 | 99 | public static bool IsOrdinaryMethod(this IMethodSymbol methodSymbol) 100 | { 101 | return methodSymbol.MethodKind == MethodKind.Ordinary; 102 | } 103 | 104 | public static bool IsPublic(this TypeDeclarationSyntax typeDclr) 105 | { 106 | return 107 | typeDclr.Modifiers.Any( 108 | modifier => modifier.RawKind.Equals(SyntaxFactory.Token(SyntaxKind.PublicKeyword).RawKind)); 109 | } 110 | 111 | public static bool IsInternal(this TypeDeclarationSyntax typeDclr) 112 | { 113 | var nonInternalModifiers = new List 114 | { 115 | SyntaxFactory.Token(SyntaxKind.PublicKeyword).RawKind, 116 | SyntaxFactory.Token(SyntaxKind.PrivateKeyword).RawKind, 117 | SyntaxFactory.Token(SyntaxKind.ProtectedKeyword).RawKind 118 | }; 119 | return !typeDclr.Modifiers.Any(modifier => nonInternalModifiers.Contains(modifier.RawKind)); 120 | } 121 | 122 | public static BasePropertyDeclarationSyntax AddAccessorListAccessors(this BasePropertyDeclarationSyntax baseDclr, params AccessorDeclarationSyntax[] accessors) 123 | { 124 | var propDclr = baseDclr as PropertyDeclarationSyntax; 125 | if (propDclr != null) 126 | { 127 | return propDclr.AddAccessorListAccessors(accessors); 128 | } 129 | 130 | var indexerDclr = baseDclr as IndexerDeclarationSyntax; 131 | if (indexerDclr != null) 132 | { 133 | return indexerDclr.AddAccessorListAccessors(accessors); 134 | } 135 | throw new InvalidOperationException(); 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/Utils/RoslynUtils.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis.CSharp; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 7 | 8 | namespace Etg.SimpleStubs.CodeGen.Utils 9 | { 10 | internal static class RoslynUtils 11 | { 12 | public static UsingDirectiveSyntax UsingDirective(string nameSpace) 13 | { 14 | return SF.UsingDirective(SF.IdentifierName(nameSpace)); 15 | } 16 | 17 | public static IEnumerable GetMethodDeclarations(TypeDeclarationSyntax interfaceNode) 18 | { 19 | return interfaceNode.DescendantNodes().OfType(); 20 | } 21 | 22 | public static ParameterSyntax CreateParameter(string type, string name) 23 | { 24 | return SF.Parameter(new SyntaxList(), new SyntaxTokenList(), SF.IdentifierName(type), 25 | SF.Identifier(new SyntaxTriviaList().Add(SF.Space), name, new SyntaxTriviaList()), null); 26 | } 27 | 28 | public static BaseListSyntax BaseList(params string[] names) 29 | { 30 | return 31 | SF.BaseList( 32 | SF.SeparatedList(names.Select(name => SF.SimpleBaseType(SF.IdentifierName(name))))); 33 | } 34 | 35 | public static List GetMethodParameterSyntaxList(IMethodSymbol methodSymbol) 36 | { 37 | var paramsSyntaxList = new List(); 38 | foreach (IParameterSymbol param in methodSymbol.Parameters) 39 | { 40 | ParameterSyntax paramSyntax = SF.Parameter(SF.Identifier(param.Name)) 41 | .WithType(SF.ParseTypeName(param.Type.GetFullyQualifiedName())); 42 | 43 | if (param.RefKind == RefKind.Out) 44 | { 45 | paramSyntax = paramSyntax.WithModifiers(SyntaxTokenList.Create(SF.Token(SyntaxKind.OutKeyword))); 46 | } 47 | else if (param.RefKind == RefKind.Ref) 48 | { 49 | paramSyntax = paramSyntax.WithModifiers(SyntaxTokenList.Create(SF.Token(SyntaxKind.RefKeyword))); 50 | } 51 | 52 | paramsSyntaxList.Add(paramSyntax); 53 | } 54 | 55 | return paramsSyntaxList; 56 | } 57 | 58 | public static List GetAllMembers(INamedTypeSymbol interfaceType) 59 | { 60 | var methodsToStub = new List(interfaceType.GetMembers().OfType()); 61 | methodsToStub.AddRange(GetAllInheritedMethods(interfaceType)); 62 | return methodsToStub; 63 | } 64 | 65 | public static IEnumerable GetAllInheritedMethods(ITypeSymbol typeSymbol) 66 | { 67 | var methods = new List(); 68 | if (typeSymbol.AllInterfaces.Any()) 69 | { 70 | foreach (var baseInterfaceType in typeSymbol.AllInterfaces) 71 | { 72 | methods.AddRange(baseInterfaceType.GetMembers().OfType()); 73 | } 74 | } 75 | 76 | return methods; 77 | } 78 | 79 | public static SyntaxKind GetVisibilityKeyword(ISymbol stubbedInterface) 80 | { 81 | return stubbedInterface.DeclaredAccessibility == 82 | Accessibility.Internal 83 | ? SyntaxKind.InternalKeyword 84 | : SyntaxKind.PublicKeyword; 85 | } 86 | 87 | /// 88 | /// 89 | /// 90 | /// Can be a or /> 91 | /// Can be a 92 | /// or or 93 | public static dynamic CopyGenericConstraints(dynamic symbolType, dynamic declarationSyntax) 94 | { 95 | if (!IsGenericType(symbolType)) 96 | { 97 | return declarationSyntax; 98 | } 99 | foreach (ITypeParameterSymbol typeParameter in GetTypeParameters(symbolType)) 100 | { 101 | if (!HasConstraints(typeParameter)) 102 | { 103 | continue; 104 | } 105 | 106 | TypeParameterConstraintClauseSyntax constraintSyntax = SF.TypeParameterConstraintClause(typeParameter.Name); 107 | if (typeParameter.HasReferenceTypeConstraint) 108 | { 109 | constraintSyntax = constraintSyntax.AddConstraints(SF.ClassOrStructConstraint(SyntaxKind.ClassConstraint)); 110 | } 111 | if (typeParameter.HasValueTypeConstraint) 112 | { 113 | constraintSyntax = constraintSyntax.AddConstraints(SF.ClassOrStructConstraint(SyntaxKind.StructConstraint)); 114 | } 115 | IEnumerable typeConstraints = 116 | typeParameter.ConstraintTypes.Select(symbol => SF.TypeConstraint(SF.IdentifierName(symbol.GetFullyQualifiedName()))); 117 | constraintSyntax = constraintSyntax.AddConstraints(typeConstraints.ToArray()); 118 | 119 | if (typeParameter.HasConstructorConstraint) 120 | { 121 | constraintSyntax = constraintSyntax.AddConstraints(SF.ConstructorConstraint()); 122 | } 123 | 124 | declarationSyntax = 125 | declarationSyntax.AddConstraintClauses(constraintSyntax); 126 | } 127 | return declarationSyntax; 128 | } 129 | 130 | 131 | public static MethodDeclarationSyntax ParseMethod(string text) 132 | { 133 | SyntaxTree syntaxTree = SF.ParseSyntaxTree(text); 134 | return syntaxTree.GetRoot().DescendantNodes().OfType().First(); 135 | } 136 | 137 | private static bool HasConstraints(ITypeParameterSymbol typeParameter) 138 | { 139 | return typeParameter.HasConstructorConstraint || typeParameter.HasReferenceTypeConstraint || 140 | typeParameter.HasValueTypeConstraint || typeParameter.ConstraintTypes.Any(); 141 | } 142 | 143 | private static bool IsGenericType(INamedTypeSymbol symbol) 144 | { 145 | return symbol.IsGenericType; 146 | } 147 | 148 | private static bool IsGenericType(IMethodSymbol symbol) 149 | { 150 | return symbol.IsGenericMethod; 151 | } 152 | 153 | private static IEnumerable GetTypeParameters(INamedTypeSymbol symbol) 154 | { 155 | return symbol.TypeParameters; 156 | } 157 | 158 | private static IEnumerable GetTypeParameters(IMethodSymbol symbol) 159 | { 160 | return symbol.TypeParameters; 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/Utils/StubbingUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; 5 | using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 6 | 7 | namespace Etg.SimpleStubs.CodeGen.Utils 8 | { 9 | internal static class StubbingUtils 10 | { 11 | public static BlockSyntax GetInvocationBlockSyntax(string delegateTypeName, string methodName, string parameters, 12 | IEnumerable outParameters, ITypeSymbol returnType, SemanticModel semanticModel) 13 | { 14 | var voidType = semanticModel.Compilation.GetTypeByMetadataName("System.Void"); 15 | bool returnsVoid = returnType.Equals(voidType); 16 | var statements = new List(); 17 | string returnStatement = returnsVoid ? string.Empty : "return "; 18 | 19 | statements.Add(SF.ParseStatement($"{delegateTypeName} del;{System.Environment.NewLine}")); 20 | 21 | // Prepare if/else block to determine how uninitialized stubs are handled. 22 | var ifStrictExpression = SF.ParseExpression("MockBehavior == MockBehavior.Strict"); 23 | var ifStrictTrueSyntax = SF.Block(SF.ParseStatement($"del = _stubs.GetMethodStub<{delegateTypeName}>(\"{methodName}\");{System.Environment.NewLine}")); 24 | 25 | var defaultReturnInvocation = outParameters.Select(p => 26 | SF.ParseStatement($"{p.Name} = default ({p.Type.GetGenericName()});")).ToList(); 27 | 28 | if (!returnsVoid) 29 | { 30 | defaultReturnInvocation.Add(SF.ParseStatement(GetDefaultReturnInvocationStatement(returnType, semanticModel))); 31 | } 32 | else 33 | { 34 | defaultReturnInvocation.Add(SF.ParseStatement($"return;{System.Environment.NewLine}")); 35 | } 36 | 37 | var ifTryGetMethodStubExpression = SF.ParseExpression($"!_stubs.TryGetMethodStub<{delegateTypeName}>(\"{methodName}\", out del)"); 38 | 39 | var ifStrictFalseSyntax = SF.Block(SF.IfStatement(ifTryGetMethodStubExpression, SF.Block(defaultReturnInvocation))); 40 | 41 | statements.Add(SF.IfStatement(ifStrictExpression, ifStrictTrueSyntax, SF.ElseClause(ifStrictFalseSyntax))); 42 | 43 | // Add default invocation. 44 | statements.Add(SF.ParseStatement($"{returnStatement}del.Invoke({parameters});{System.Environment.NewLine}")); 45 | 46 | return SF.Block(statements.ToArray()); 47 | } 48 | 49 | private static string GetDefaultReturnInvocationStatement(ITypeSymbol returnType, SemanticModel semanticModel) 50 | { 51 | var genericTaskType = semanticModel.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1"); 52 | var taskType = semanticModel.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task"); 53 | var asyncActionType = semanticModel.Compilation.GetTypeByMetadataName("Windows.Foundation.IAsyncAction"); 54 | var asyncOperationType = semanticModel.Compilation.GetTypeByMetadataName("Windows.Foundation.IAsyncOperation`1"); 55 | 56 | if (returnType.MetadataName.Equals(genericTaskType.MetadataName)) 57 | { 58 | var namedReturnType = (INamedTypeSymbol) returnType; 59 | var genericReturnType = namedReturnType.TypeArguments.First(); 60 | return $"return System.Threading.Tasks.Task.FromResult(default({genericReturnType.GetFullyQualifiedName()}));{System.Environment.NewLine}"; 61 | } 62 | else if (returnType.MetadataName.Equals(taskType.MetadataName)) 63 | { 64 | // do not use Task.CompletedTask to stay compatible with .Net 4.5 65 | return $"return System.Threading.Tasks.Task.FromResult(true);{System.Environment.NewLine}"; 66 | } 67 | else if (asyncActionType != null && returnType.MetadataName.Equals(asyncActionType.MetadataName)) 68 | { 69 | // do not use Task.CompletedTask to stay compatible with .Net 4.5 70 | return $"return System.Threading.Tasks.Task.FromResult(true).AsAsyncAction();{System.Environment.NewLine}"; 71 | } 72 | else if (asyncOperationType != null && returnType.MetadataName.Equals(asyncOperationType.MetadataName)) 73 | { 74 | var namedReturnType = (INamedTypeSymbol)returnType; 75 | var genericReturnType = namedReturnType.TypeArguments.First(); 76 | return $"return System.Threading.Tasks.Task.FromResult(default({genericReturnType.GetFullyQualifiedName()})).AsAsyncOperation();{System.Environment.NewLine}"; 77 | } 78 | else 79 | { 80 | return $"return default({returnType.GetFullyQualifiedName()});{System.Environment.NewLine}"; 81 | } 82 | } 83 | 84 | public static string FormatParameters(IMethodSymbol methodSymbol) 85 | { 86 | return string.Join(", ", methodSymbol.Parameters.Select(p => 87 | { 88 | if (p.RefKind == RefKind.Out) 89 | { 90 | return $"out {p.Name}"; 91 | } 92 | if (p.RefKind == RefKind.Ref) 93 | { 94 | return $"ref {p.Name}"; 95 | } 96 | return p.Name; 97 | })); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/SimpleStubs.CodeGen/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/SimpleStubs/MockBehavior.cs: -------------------------------------------------------------------------------- 1 | namespace Etg.SimpleStubs 2 | { 3 | /// 4 | /// Defines the types of behavior that can be applied to stub members that have not had a behavior applied. 5 | /// 6 | public enum MockBehavior 7 | { 8 | /// 9 | /// Methods and properties on a mock instance in strict mode throw an exception when called before any behaviors have been assigned. 10 | /// 11 | Strict, 12 | /// 13 | /// Methods and properties on a mock instance in loose mode use a default behavior when called before any behaviors have been assigned. 14 | /// 15 | Loose 16 | } 17 | } -------------------------------------------------------------------------------- /src/SimpleStubs/SequenceContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Etg.SimpleStubs 6 | { 7 | public class SequenceContainer 8 | { 9 | private readonly List> _sequence = new List>(); 10 | 11 | public virtual SequenceContainer Repeat(T element, int count) 12 | { 13 | _sequence.Add(new SequenceElement{Element = element, Count = count}); 14 | return this; 15 | } 16 | 17 | public int CallCount { get; private set; } = 0; 18 | 19 | public SequenceContainer Forever(T element) 20 | { 21 | return Repeat(element, int.MaxValue); 22 | } 23 | 24 | public SequenceContainer Once(T element) 25 | { 26 | return Repeat(element, 1); 27 | } 28 | 29 | public SequenceContainer Twice(T element) 30 | { 31 | return Repeat(element, 2); 32 | } 33 | 34 | public virtual T Next 35 | { 36 | get 37 | { 38 | lock (_sequence) 39 | { 40 | if (!_sequence.Any()) 41 | { 42 | throw new SequenceContainerException( 43 | "The stub sequence has been called more than expected"); 44 | } 45 | 46 | var se = _sequence[0]; 47 | T e = se.Element; 48 | if (--se.Count == 0) 49 | { 50 | _sequence.RemoveAt(0); 51 | } 52 | 53 | CallCount++; 54 | return e; 55 | } 56 | } 57 | } 58 | 59 | private class SequenceElement 60 | { 61 | public TElement Element 62 | { 63 | get; 64 | set; 65 | } 66 | 67 | public int Count 68 | { 69 | get; 70 | set; 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/SimpleStubs/SequenceContainerException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Etg.SimpleStubs 4 | { 5 | public class SequenceContainerException : Exception 6 | { 7 | public SequenceContainerException(string msg) : base(msg) 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/SimpleStubs/SimpleStubs.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard1.4 5 | Etg.SimpleStubs 6 | Etg.SimpleStubs 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/SimpleStubs/SimpleStubsException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Etg.SimpleStubs 4 | { 5 | public class SimpleStubsException : Exception 6 | { 7 | public SimpleStubsException(string message) : base(message) 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/SimpleStubs/StubContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Etg.SimpleStubs 4 | { 5 | /// 6 | /// Holds the stubs for a given interface. 7 | /// This class is intended to be used by the generated stubs code, not user code. 8 | /// 9 | public class StubContainer 10 | { 11 | private readonly Dictionary> _stubs = new Dictionary>(); 12 | private readonly string _stubTypeName; 13 | 14 | public StubContainer() 15 | { 16 | _stubTypeName = typeof(TStub).ToString(); 17 | } 18 | 19 | public TDelegate GetMethodStub(string methodName) 20 | { 21 | TDelegate del; 22 | if (!TryGetMethodStub(methodName, out del)) 23 | { 24 | throw new SimpleStubsException( 25 | $"The stub {_stubTypeName} does not contain a stub for the method {methodName}"); 26 | } 27 | 28 | return del; 29 | } 30 | 31 | public bool TryGetMethodStub(string methodName, out TDelegate del) 32 | { 33 | string key = ToUniqueId(); 34 | SequenceContainer sequenceContainer; 35 | _stubs.TryGetValue(key, out sequenceContainer); 36 | if (sequenceContainer == null) 37 | { 38 | del = default(TDelegate); 39 | 40 | return false; 41 | } 42 | 43 | try 44 | { 45 | del = (TDelegate)sequenceContainer.Next; 46 | } 47 | catch (SequenceContainerException) 48 | { 49 | throw new SimpleStubsException( 50 | $"The stub of method {methodName} was called more than expected"); 51 | } 52 | 53 | return true; 54 | } 55 | 56 | public void SetMethodStub(TDelegate del, int count, bool overwrite) 57 | { 58 | string key = ToUniqueId(); 59 | 60 | SequenceContainer sequenceContainer; 61 | if (!overwrite) 62 | { 63 | _stubs.TryGetValue(key, out sequenceContainer); 64 | if (sequenceContainer == null) 65 | { 66 | sequenceContainer = new SequenceContainer(); 67 | } 68 | } 69 | else 70 | { 71 | sequenceContainer = new SequenceContainer(); 72 | } 73 | 74 | sequenceContainer.Repeat(del, count); 75 | _stubs[key] = sequenceContainer; 76 | } 77 | 78 | private static string ToUniqueId() 79 | { 80 | return typeof(T).ToString(); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/SimpleStubs/StubsUtils.cs: -------------------------------------------------------------------------------- 1 | namespace Etg.SimpleStubs 2 | { 3 | public static class StubsUtils 4 | { 5 | public static SequenceContainer Sequence() 6 | { 7 | return new SequenceContainer(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/SimpleStubs/Times.cs: -------------------------------------------------------------------------------- 1 | namespace Etg.SimpleStubs 2 | { 3 | public class Times 4 | { 5 | public const int Once = 1; 6 | public const int Twice = 2; 7 | public const int Forever = int.MaxValue; 8 | } 9 | } -------------------------------------------------------------------------------- /test/TestClassLibrary/ITestInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | // used to test that static usings are handled correctly. 6 | using static System.String; 7 | 8 | using foo = System.String; 9 | 10 | namespace TestClassLibrary 11 | { 12 | public interface IAppConfigPeriodicRefreshHandler : IDisposable 13 | { 14 | void Start(); 15 | } 16 | 17 | public interface IPhoneBook 18 | { 19 | long GetContactPhoneNumber(string firstName, string lastName); 20 | 21 | Task GetContactPhoneNumberAsync(string firstName, string lastName); 22 | 23 | long MyNumber { get; set; } 24 | 25 | int ContactsCount { get; } 26 | 27 | event EventHandler PhoneNumberChanged; 28 | } 29 | 30 | // just to make sure all inherited members are stubbed 31 | public interface IPhoneBookSpecial : IPhoneBook 32 | { 33 | } 34 | 35 | public interface IContainer 36 | { 37 | T GetElement(int index); 38 | 39 | Task GetElementAsync(int index); 40 | 41 | void SetElement(int index, T value); 42 | 43 | Task SetElementAsync(int index, T value); 44 | 45 | bool GetElement(int index, out object value); 46 | } 47 | 48 | public interface IRefUtils 49 | { 50 | void Swap(ref T v1, ref T v2); 51 | } 52 | 53 | public interface ITestInterface : IDisposable 54 | { 55 | void DoSomething(); 56 | 57 | void DoSomething(string s, int x); 58 | 59 | void DoSomething(List> list); 60 | 61 | void DoSomething(string[] array); 62 | 63 | Task DoSomethingAsync(); 64 | 65 | Task> DoSomethingAsync(int parameter); 66 | 67 | void SetDictionary(Dictionary dict); 68 | 69 | Task SetDictionaryAsync(Dictionary dict); 70 | 71 | string Prop1 { get; } 72 | 73 | string Prop2 { set; } 74 | 75 | string Prop3 { get; set; } 76 | 77 | event EventHandler Changed; 78 | 79 | event EventHandler OtherEvent; 80 | 81 | event EventHandler> eventWihGenericArg; 82 | 83 | List GetGenericList(); 84 | 85 | void SetGenericValue(T value); 86 | 87 | void SetCoordinates((float x, float y) coordinates); 88 | (float x, float y) GetCoordinates(); 89 | 90 | List<(float x, float y)> GetTrajectory(); 91 | 92 | void SetTrajectory(List<(float x, float y)> coordinatesList); 93 | } 94 | 95 | public interface IIgnoredInterface : IDisposable 96 | { 97 | } 98 | 99 | internal interface IInternalInterface 100 | { 101 | void DoSomethingInternal(); 102 | } 103 | 104 | public interface IGenericInterface 105 | where T : class, IDisposable, new() 106 | where A : struct 107 | where B : struct, Enum 108 | where C : Enum 109 | { 110 | T GetX(); 111 | A GetA(); 112 | B? GetB(); 113 | C GetC(); 114 | } 115 | 116 | public interface IUpdatable : IEquatable where T : IUpdatable 117 | { 118 | void Update(T newItem); 119 | } 120 | 121 | public interface IGenericContainer 122 | { 123 | T this[int index] { get; set; } 124 | 125 | T this[string key, int n] { get; } 126 | } 127 | 128 | // just to make sure inherited indexers are stubbed 129 | public interface IGenericContainerSubInterface : IGenericContainer 130 | { 131 | } 132 | 133 | public interface IInterfaceWithGenericMethod 134 | { 135 | T GetFoo() where T : class; 136 | 137 | T GetBar() where T : struct; 138 | 139 | void SetBoo(T t, A a) where T : class, IDisposable, new() where A : new(); 140 | } 141 | 142 | public delegate void CustomDelegateBasedHandler(int arg1, string arg2, object arg3); 143 | public delegate string CustomDelegateBasedHandlerWithReturnType(object sender, EventArgs eventArgs); 144 | public delegate string CustomDelegateBasedHandlerWithReturnType(T eventArgs); 145 | 146 | public interface ICustomDelegateBasedEventExample 147 | { 148 | event CustomDelegateBasedHandler CustomDelegateEventOccurred; 149 | 150 | event CustomDelegateBasedHandlerWithReturnType CustomDelegateWithReturnTypeEventOccurred; 151 | event CustomDelegateBasedHandlerWithReturnType CustomDelegateWithReturnTypeWithParameterEventOccurred; 152 | } 153 | } 154 | 155 | //------------------------------------------------------------------------------------------------------------- 156 | // The following interfaces are used to test that same class name in different namespaces doesn't cause conflict. 157 | namespace A 158 | { 159 | public interface IFoo 160 | { 161 | 162 | } 163 | } 164 | 165 | namespace B 166 | { 167 | public interface IFoo 168 | { 169 | 170 | } 171 | } 172 | 173 | namespace C 174 | { 175 | public interface IBar 176 | { 177 | A.IFoo GetFoo(); 178 | void DoSomething(B.IFoo foo); 179 | } 180 | } 181 | //------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /test/TestClassLibrary/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | 9 | [assembly: AssemblyTitle("TestClassLibrary")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("TestClassLibrary")] 14 | [assembly: AssemblyCopyright("Copyright © 2016")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // Setting ComVisible to false makes the types in this assembly not visible 19 | // to COM components. If you need to access a type in this assembly from 20 | // COM, set the ComVisible attribute to true on that type. 21 | 22 | [assembly: ComVisible(false)] 23 | 24 | // The following GUID is for the ID of the typelib if this project is exposed to COM 25 | 26 | [assembly: Guid("ecbcbae6-949e-4e9c-84e1-614d97909b6c")] 27 | 28 | // Version information for an assembly consists of the following four values: 29 | // 30 | // Major Version 31 | // Minor Version 32 | // Build Number 33 | // Revision 34 | // 35 | // You can specify all the values or you can default the Build and Revision Numbers 36 | // by using the '*' as shown below: 37 | // [assembly: AssemblyVersion("1.0.*")] 38 | 39 | [assembly: AssemblyVersion("1.0.0.0")] 40 | [assembly: AssemblyFileVersion("1.0.0.0")] 41 | [assembly: InternalsVisibleTo("TestClassLibraryTest")] -------------------------------------------------------------------------------- /test/TestClassLibrary/TestClassLibrary.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {ECBCBAE6-949E-4E9C-84E1-614D97909B6C} 8 | Library 9 | Properties 10 | TestClassLibrary 11 | TestClassLibrary 12 | v4.7.2 13 | 512 14 | 15 | latest 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 56 | -------------------------------------------------------------------------------- /test/TestClassLibraryTest/AsyncMethodStubsTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Etg.SimpleStubs; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using TestClassLibrary; 5 | 6 | namespace TestClassLibraryTest 7 | { 8 | [TestClass] 9 | public class AsyncMethodStubsTest 10 | { 11 | // Test with initialized stubs 12 | 13 | [TestMethod] 14 | public async Task TestCallSequence_Async() 15 | { 16 | var stub = new StubIPhoneBook() 17 | .GetContactPhoneNumberAsync(async (p1, p2) => await Task.FromResult(12345678), Times.Once) // first call 18 | .GetContactPhoneNumberAsync(async (p1, p2) => await Task.FromResult(11122233), Times.Twice) // next two calls 19 | .GetContactPhoneNumberAsync(async (p1, p2) => await Task.FromResult(22233556), Times.Forever); // rest of the calls 20 | 21 | IPhoneBook phoneBook = stub; 22 | Assert.AreEqual(12345678, await phoneBook.GetContactPhoneNumberAsync("John", "Smith")); 23 | Assert.AreEqual(11122233, await phoneBook.GetContactPhoneNumberAsync("John", "Smith")); 24 | Assert.AreEqual(11122233, await phoneBook.GetContactPhoneNumberAsync("John", "Smith")); 25 | Assert.AreEqual(22233556, await phoneBook.GetContactPhoneNumberAsync("John", "Smith")); 26 | Assert.AreEqual(22233556, await phoneBook.GetContactPhoneNumberAsync("John", "Smith")); 27 | Assert.AreEqual(22233556, await phoneBook.GetContactPhoneNumberAsync("John", "Smith")); 28 | } 29 | 30 | [TestMethod] 31 | public async Task TestMethod_WithReturnType_WithParameters_Async() 32 | { 33 | long number = 6041234567; 34 | string firstName = null; 35 | string lastName = null; 36 | var stub = new StubIPhoneBook(); 37 | stub.GetContactPhoneNumberAsync(async (fn, ln) => 38 | { 39 | firstName = fn; 40 | lastName = ln; 41 | return await Task.FromResult(number); 42 | }); 43 | IPhoneBook phoneBook = stub; 44 | long actualNumber = await phoneBook.GetContactPhoneNumberAsync("John", "Smith"); 45 | Assert.AreEqual(number, actualNumber); 46 | Assert.AreEqual("John", firstName); 47 | Assert.AreEqual("Smith", lastName); 48 | } 49 | 50 | [TestMethod] 51 | [ExpectedException(typeof(SimpleStubsException))] 52 | public async Task TestThatExceptionIsThrownWhenMethodIsCalledMoreThanExpected_Async() 53 | { 54 | var stub = new StubIPhoneBook() 55 | .GetContactPhoneNumberAsync(async (p1, p2) => await Task.FromResult(12345678), Times.Once); 56 | IPhoneBook phoneBook = stub; 57 | await phoneBook.GetContactPhoneNumberAsync("John", "Smith"); 58 | await phoneBook.GetContactPhoneNumberAsync("John", "Smith"); 59 | } 60 | 61 | [TestMethod] 62 | public async Task TestThatMethodStubCanBeOverwritten_Async() 63 | { 64 | var stub = new StubIPhoneBook(). 65 | GetContactPhoneNumberAsync(async (p1, p2) => await Task.FromResult(12345678)); 66 | stub.GetContactPhoneNumberAsync(async (p1, p2) => await Task.FromResult(11122233), overwrite: true); 67 | 68 | IPhoneBook phoneBook = stub; 69 | Assert.AreEqual(11122233, await phoneBook.GetContactPhoneNumberAsync("John", "Smith")); 70 | } 71 | 72 | [TestMethod] 73 | public async Task TestGenericMethod_WithReturnType_WithParameter_Async() 74 | { 75 | int value = -1; 76 | var stub = new StubIContainer() 77 | .GetElementAsync(async (index) => await Task.FromResult(value)) 78 | .SetElementAsync(async (i, v) => { 79 | await Task.Run(() => value = v); 80 | }); 81 | 82 | IContainer container = stub; 83 | await container.SetElementAsync(0, 5); 84 | Assert.AreEqual(5, await container.GetElementAsync(1)); 85 | } 86 | 87 | [TestMethod] 88 | public async Task TestMethod_Void_WithNoParameters_Async() 89 | { 90 | var stub = new StubITestInterface(); 91 | bool wasDelegateCalled = false; 92 | stub.DoSomethingAsync(async () => { await Task.Run(() => wasDelegateCalled = true); }); 93 | ITestInterface testInterface = stub; 94 | await testInterface.DoSomethingAsync(); 95 | Assert.IsTrue(wasDelegateCalled); 96 | } 97 | 98 | // Test with uninitialized stubs (strict mode) 99 | 100 | [TestMethod] 101 | [ExpectedException(typeof(SimpleStubsException))] 102 | public async Task TestMethod_WithReturnType_WithParameters_DefaultBehavior_Strict_Async() 103 | { 104 | var stub = new StubIPhoneBook(MockBehavior.Strict); 105 | IPhoneBook phoneBook = stub; 106 | Assert.AreEqual(0, await phoneBook.GetContactPhoneNumberAsync("John", "Smith")); 107 | } 108 | 109 | [TestMethod] 110 | [ExpectedException(typeof(SimpleStubsException))] 111 | public async Task TestMethod_Void_WithNoParameters_DefaultBehavior_Strict_Async() 112 | { 113 | var stub = new StubITestInterface(MockBehavior.Strict); 114 | ITestInterface testInterface = stub; 115 | await testInterface.DoSomethingAsync(); 116 | } 117 | 118 | // Test with uninitialized stubs (loose mode) 119 | 120 | [TestMethod] 121 | public async Task TestThatDefaultBehaviorIsUsedWhenStubIsNotSetup_Async() 122 | { 123 | var stub = new StubIPhoneBook(); 124 | IPhoneBook phoneBook = stub; 125 | var result = await phoneBook.GetContactPhoneNumberAsync("John", "Smith"); 126 | Assert.AreEqual(0, result); 127 | } 128 | 129 | [TestMethod] 130 | public async Task TestMethod_WithReturnType_WithParameters_DefaultBehavior_Loose_Async() 131 | { 132 | var stub = new StubIPhoneBook(MockBehavior.Loose); 133 | IPhoneBook phoneBook = stub; 134 | Assert.AreEqual(0, await phoneBook.GetContactPhoneNumberAsync("John", "Smith")); 135 | } 136 | 137 | [TestMethod] 138 | public async Task TestMethod_Void_WithNoParameters_DefaultBehavior_Loose_Async() 139 | { 140 | var stub = new StubITestInterface(MockBehavior.Loose); 141 | ITestInterface testInterface = stub; 142 | await testInterface.DoSomethingAsync(); 143 | } 144 | 145 | [TestMethod] 146 | public async Task TestThatLooseMockBehaviorsAreAlwaysOverridden_Async() 147 | { 148 | var stub = new StubIPhoneBook(MockBehavior.Loose) 149 | .GetContactPhoneNumberAsync(async (p1, p2) => await Task.FromResult(12345678)); 150 | 151 | IPhoneBook phoneBook = stub; 152 | Assert.AreEqual(12345678, await phoneBook.GetContactPhoneNumberAsync("John", "Smith")); 153 | } 154 | 155 | [TestMethod] 156 | [ExpectedException(typeof(SimpleStubsException))] 157 | public async Task TestThatExceptionIsThrownWhenMethodIsCalledMoreThanExpected_DefaultBehavior_Loose_Async() 158 | { 159 | var stub = new StubIPhoneBook(MockBehavior.Loose) 160 | .GetContactPhoneNumberAsync(async (p1, p2) => await Task.FromResult(12345678), Times.Once); 161 | IPhoneBook phoneBook = stub; 162 | await phoneBook.GetContactPhoneNumberAsync("John", "Smith"); 163 | await phoneBook.GetContactPhoneNumberAsync("John", "Smith"); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /test/TestClassLibraryTest/Etg.SimpleStubs.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | CoreCompile;XamlPreCompile 5 | ResolveReferences 6 | $(IntermediateOutputPath)SimpleStubs.generated.cs 7 | true 8 | 9 | 10 | 11 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/TestClassLibraryTest/EventStubsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using TestClassLibrary; 6 | 7 | namespace TestClassLibraryTest 8 | { 9 | [TestClass] 10 | public class EventStubsTest 11 | { 12 | [TestMethod] 13 | public void TestEventStub() 14 | { 15 | object sender = null; 16 | long newNumber = 0; 17 | var stub = new StubIPhoneBook(); 18 | stub.PhoneNumberChanged += (s, num) => 19 | { 20 | sender = s; 21 | newNumber = num; 22 | }; 23 | 24 | stub.PhoneNumberChanged_Raise(this, 55); 25 | Assert.AreEqual(55, newNumber); 26 | Assert.AreEqual(this, sender); 27 | } 28 | 29 | [TestMethod] 30 | public void TestCustomDelegateBasedEventStub() 31 | { 32 | int arg1 = 0; 33 | string arg2 = null; 34 | object arg3 = null; 35 | var stub = new StubICustomDelegateBasedEventExample(); 36 | stub.CustomDelegateEventOccurred += (i, s, o) => 37 | { 38 | arg1 = i; 39 | arg2 = s; 40 | arg3 = o; 41 | }; 42 | 43 | stub.CustomDelegateEventOccurred_Raise(55, "test", new Random(1)); 44 | 45 | Assert.AreEqual(55, arg1); 46 | Assert.AreEqual("test", arg2); 47 | Assert.AreEqual(typeof(Random), arg3.GetType()); 48 | } 49 | 50 | [TestMethod] 51 | public void TestCustomDelegateBasedWithReturnTypeEventStub() 52 | { 53 | var stub = new StubICustomDelegateBasedEventExample(); 54 | stub.CustomDelegateWithReturnTypeEventOccurred += (sender, args) => "Teststring1"; 55 | stub.CustomDelegateWithReturnTypeEventOccurred += (sender, args) => "Teststring2"; 56 | stub.CustomDelegateWithReturnTypeEventOccurred += (sender, args) => "Teststring3"; 57 | 58 | var receivedList = stub.CustomDelegateWithReturnTypeEventOccurred_Raise(stub, EventArgs.Empty).ToList(); 59 | 60 | Assert.AreEqual(receivedList.Count, 3); 61 | Assert.AreEqual(receivedList[0], "Teststring1"); 62 | Assert.AreEqual(receivedList[1], "Teststring2"); 63 | Assert.AreEqual(receivedList[2], "Teststring3"); 64 | } 65 | 66 | [TestMethod] 67 | public void TestCustomDelegateBasedWithReturnTypeWithParameterEventStub() 68 | { 69 | var stub = new StubICustomDelegateBasedEventExample(); 70 | stub.CustomDelegateWithReturnTypeWithParameterEventOccurred += args => args + "Teststring1"; 71 | stub.CustomDelegateWithReturnTypeWithParameterEventOccurred += args => args + "Teststring2"; 72 | stub.CustomDelegateWithReturnTypeWithParameterEventOccurred += args => args + "Teststring3"; 73 | 74 | var receivedList = stub.CustomDelegateWithReturnTypeWithParameterEventOccurred_Raise("My").ToList(); 75 | 76 | Assert.AreEqual(receivedList.Count, 3); 77 | Assert.AreEqual(receivedList[0], "MyTeststring1"); 78 | Assert.AreEqual(receivedList[1], "MyTeststring2"); 79 | Assert.AreEqual(receivedList[2], "MyTeststring3"); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /test/TestClassLibraryTest/IndexerStubsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestClassLibrary; 4 | 5 | namespace TestClassLibraryTest 6 | { 7 | [TestClass] 8 | public class IndexerStubsTest 9 | { 10 | [TestMethod] 11 | public void TestIndexerGet() 12 | { 13 | var stub = new StubIGenericContainer(); 14 | stub.Item_Get(index => 15 | { 16 | switch (index) 17 | { 18 | case 0: 19 | return 13; 20 | case 1: 21 | return 5; 22 | default: 23 | throw new IndexOutOfRangeException(); 24 | } 25 | }); 26 | 27 | IGenericContainer container = stub; 28 | Assert.AreEqual(13, container[0]); 29 | Assert.AreEqual(5, container[1]); 30 | } 31 | 32 | [TestMethod] 33 | public void TestIndexerSet() 34 | { 35 | var stub = new StubIGenericContainer(); 36 | int res = -1; 37 | stub.Item_Set((index, value) => 38 | { 39 | if (index != 0) throw new IndexOutOfRangeException(); 40 | res = value; 41 | }); 42 | 43 | IGenericContainer container = stub; 44 | container[0] = 13; 45 | 46 | Assert.AreEqual(13, res); 47 | } 48 | 49 | [TestMethod] 50 | public void TestThatMultipleIndexerDontConflict() 51 | { 52 | var stub = new StubIGenericContainer(); 53 | stub.Item_Get(index => 12).Item_Get((key, i) => 3); 54 | 55 | IGenericContainer container = stub; 56 | Assert.AreEqual(12, container[0]); 57 | Assert.AreEqual(3, container["foo", 0]); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /test/TestClassLibraryTest/MethodStubsTest.cs: -------------------------------------------------------------------------------- 1 | using Etg.SimpleStubs; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestClassLibrary; 4 | 5 | namespace TestClassLibraryTest 6 | { 7 | [TestClass] 8 | public class MethodStubsTest 9 | { 10 | // Test with initialized stubs 11 | [TestMethod] 12 | public void TestCallSequence() 13 | { 14 | var stub = new StubIPhoneBook() 15 | .GetContactPhoneNumber((p1, p2) => 12345678, Times.Once) // first call 16 | .GetContactPhoneNumber((p1, p2) => 11122233, Times.Twice) // next two calls 17 | .GetContactPhoneNumber((p1, p2) => 22233556, Times.Forever); // rest of the calls 18 | 19 | IPhoneBook phoneBook = stub; 20 | Assert.AreEqual(12345678, phoneBook.GetContactPhoneNumber("John", "Smith")); 21 | Assert.AreEqual(11122233, phoneBook.GetContactPhoneNumber("John", "Smith")); 22 | Assert.AreEqual(11122233, phoneBook.GetContactPhoneNumber("John", "Smith")); 23 | Assert.AreEqual(22233556, phoneBook.GetContactPhoneNumber("John", "Smith")); 24 | Assert.AreEqual(22233556, phoneBook.GetContactPhoneNumber("John", "Smith")); 25 | Assert.AreEqual(22233556, phoneBook.GetContactPhoneNumber("John", "Smith")); 26 | } 27 | 28 | [TestMethod] 29 | public void TestMethod_WithReturnType_WithParameters() 30 | { 31 | long number = 6041234567; 32 | string firstName = null; 33 | string lastName = null; 34 | var stub = new StubIPhoneBook(); 35 | stub.GetContactPhoneNumber((fn, ln) => 36 | { 37 | firstName = fn; 38 | lastName = ln; 39 | return number; 40 | }); 41 | IPhoneBook phoneBook = stub; 42 | long actualNumber = phoneBook.GetContactPhoneNumber("John", "Smith"); 43 | Assert.AreEqual(number, actualNumber); 44 | Assert.AreEqual("John", firstName); 45 | Assert.AreEqual("Smith", lastName); 46 | } 47 | 48 | [TestMethod] 49 | [ExpectedException(typeof(SimpleStubsException))] 50 | public void TestThatExceptionIsThrownWhenMethodIsCalledMoreThanExpected() 51 | { 52 | var stub = new StubIPhoneBook().GetContactPhoneNumber((p1, p2) => 12345678, Times.Once); 53 | IPhoneBook phoneBook = stub; 54 | phoneBook.GetContactPhoneNumber("John", "Smith"); 55 | phoneBook.GetContactPhoneNumber("John", "Smith"); 56 | } 57 | 58 | [TestMethod] 59 | public void TestThatMethodStubCanBeOverwritten() 60 | { 61 | var stub = new StubIPhoneBook().GetContactPhoneNumber((p1, p2) => 12345678); 62 | stub.GetContactPhoneNumber((p1, p2) => 11122233, overwrite: true); 63 | 64 | IPhoneBook phoneBook = stub; 65 | Assert.AreEqual(11122233, phoneBook.GetContactPhoneNumber("John", "Smith")); 66 | } 67 | 68 | [TestMethod] 69 | public void TestGenericMethod_WithReturnType_WithParameter() 70 | { 71 | int value = -1; 72 | var stub = new StubIContainer() 73 | .GetElement(index => value) 74 | .SetElement((i, v) => { value = v; }); 75 | 76 | IContainer container = stub; 77 | container.SetElement(0, 5); 78 | Assert.AreEqual(5, container.GetElement(1)); 79 | } 80 | 81 | [TestMethod] 82 | public void TestMethod_WithReturnType_IncludesOutParameter() 83 | { 84 | object someObj = "test"; 85 | var stub = new StubIContainer() 86 | .GetElement((int index, out object value) => 87 | { 88 | value = someObj; 89 | return true; 90 | }); 91 | 92 | IContainer container = stub; 93 | object result; 94 | container.GetElement(0, out result); 95 | Assert.AreEqual(someObj, result); 96 | } 97 | 98 | [TestMethod] 99 | public void TestMethod_Void_IncludesRefParameters() 100 | { 101 | var stub = new StubIRefUtils() 102 | .Swap((ref int v1, ref int v2) => 103 | { 104 | int temp = v1; 105 | v1 = v2; 106 | v2 = temp; 107 | }); 108 | 109 | int i1 = 1; 110 | int i2 = 2; 111 | 112 | ((IRefUtils)stub).Swap(ref i1, ref i2); 113 | Assert.AreEqual(2, i1); 114 | Assert.AreEqual(1, i2); 115 | } 116 | 117 | [TestMethod] 118 | public void TestMethod_Void_WithNoParameters() 119 | { 120 | var stub = new StubITestInterface(); 121 | bool wasDelegateCalled = false; 122 | stub.DoSomething(() => { wasDelegateCalled = true; }); 123 | ITestInterface testInterface = stub; 124 | testInterface.DoSomething(); 125 | Assert.IsTrue(wasDelegateCalled); 126 | } 127 | 128 | // Test with uninitialized stubs (strict mode) 129 | 130 | [TestMethod] 131 | [ExpectedException(typeof(SimpleStubsException))] 132 | public void TestMethod_WithReturnType_WithParameters_DefaultBehavior_Strict() 133 | { 134 | var stub = new StubIPhoneBook(MockBehavior.Strict); 135 | IPhoneBook phoneBook = stub; 136 | Assert.AreEqual(0, phoneBook.GetContactPhoneNumber("John", "Smith")); 137 | } 138 | 139 | [TestMethod] 140 | [ExpectedException(typeof(SimpleStubsException))] 141 | public void TestMethod_Void_WithNoParameters_DefaultBehavior_Strict() 142 | { 143 | var stub = new StubITestInterface(MockBehavior.Strict); 144 | ITestInterface testInterface = stub; 145 | testInterface.DoSomething(); 146 | } 147 | 148 | // Test with uninitialized stubs (loose mode) 149 | 150 | [TestMethod] 151 | public void TestThatDefaultBehaviorIsUsedWhenStubIsNotSetup() 152 | { 153 | var stub = new StubIPhoneBook(); 154 | IPhoneBook phoneBook = stub; 155 | var result = phoneBook.GetContactPhoneNumber("John", "Smith"); 156 | Assert.AreEqual(0, result); 157 | } 158 | 159 | [TestMethod] 160 | public void TestMethod_WithReturnType_WithParameters_DefaultBehavior_Loose() 161 | { 162 | var stub = new StubIPhoneBook(MockBehavior.Loose); 163 | IPhoneBook phoneBook = stub; 164 | Assert.AreEqual(0, phoneBook.GetContactPhoneNumber("John", "Smith")); 165 | } 166 | 167 | [TestMethod] 168 | public void TestMethod_Void_WithNoParameters_DefaultBehavior_Loose() 169 | { 170 | var stub = new StubITestInterface(MockBehavior.Loose); 171 | ITestInterface testInterface = stub; 172 | testInterface.DoSomething(); 173 | } 174 | 175 | [TestMethod] 176 | public void TestMethod_WithReturnType_IncludesOutParameter_MockBehavior_Loose() 177 | { 178 | var stub = new StubIContainer(MockBehavior.Loose); 179 | IContainer container = stub; 180 | object outParam; 181 | Assert.AreEqual(false, container.GetElement(1, out outParam)); 182 | Assert.AreEqual(null, outParam); 183 | } 184 | 185 | [TestMethod] 186 | public void TestMethod_WithReturnType_IncludesRefParameter_MockBehavior_Loose() 187 | { 188 | var stub = new StubIRefUtils(MockBehavior.Loose); 189 | IRefUtils refUtils = stub; 190 | int i1 = 1; 191 | int i2 = 2; 192 | refUtils.Swap(ref i1, ref i2); 193 | Assert.AreEqual(1, i1); 194 | Assert.AreEqual(2, i2); 195 | } 196 | 197 | [TestMethod] 198 | public void TestThatLooseMockBehaviorsAreAlwaysOverwritten() 199 | { 200 | var stub = new StubIPhoneBook(MockBehavior.Loose) 201 | .GetContactPhoneNumber((p1, p2) => 12345678); 202 | 203 | IPhoneBook phoneBook = stub; 204 | Assert.AreEqual(12345678, phoneBook.GetContactPhoneNumber("John", "Smith")); 205 | } 206 | 207 | [TestMethod] 208 | [ExpectedException(typeof(SimpleStubsException))] 209 | public void TestThatExceptionIsThrownWhenMethodIsCalledMoreThanExpected_DefaultBehavior_Loose() 210 | { 211 | var stub = new StubIPhoneBook(MockBehavior.Loose) 212 | .GetContactPhoneNumber((p1, p2) => 12345678, Times.Once); 213 | IPhoneBook phoneBook = stub; 214 | phoneBook.GetContactPhoneNumber("John", "Smith"); 215 | phoneBook.GetContactPhoneNumber("John", "Smith"); 216 | } 217 | 218 | [TestMethod] 219 | public void TestTupleParametersAndReturnType() 220 | { 221 | (float x, float y) coordinates = (0.0f, 0.0f); 222 | var stub = new StubITestInterface() 223 | .SetCoordinates((coords) => coordinates = coords) 224 | .GetCoordinates(() => coordinates); 225 | 226 | ITestInterface testInterface = (ITestInterface)stub; 227 | 228 | testInterface.SetCoordinates((1.0f, 2.0f)); 229 | Assert.AreEqual((1.0f, 2.0f), coordinates); 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /test/TestClassLibraryTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | 8 | [assembly: AssemblyTitle("TestClassLibraryTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TestClassLibraryTest")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | 25 | [assembly: Guid("cb81f60f-1374-4b46-bb64-d848b5103a58")] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [assembly: AssemblyVersion("1.0.*")] 37 | 38 | [assembly: AssemblyVersion("1.0.0.0")] 39 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /test/TestClassLibraryTest/PropertyStubsTest.cs: -------------------------------------------------------------------------------- 1 | using Etg.SimpleStubs; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestClassLibrary; 4 | 5 | namespace TestClassLibraryTest 6 | { 7 | [TestClass] 8 | public class PropertyStubsTest 9 | { 10 | [TestMethod] 11 | public void TestPropertyStubWithGetterAndSetter() 12 | { 13 | long myNumber = 6041234567; 14 | long newNumber = 0; 15 | 16 | var stub = new StubIPhoneBook() 17 | .MyNumber_Get(() => myNumber) 18 | .MyNumber_Set(value => newNumber = value); 19 | 20 | IPhoneBook phoneBook = stub; 21 | Assert.AreEqual(myNumber, phoneBook.MyNumber); 22 | phoneBook.MyNumber = 13; 23 | Assert.AreEqual(13, newNumber); 24 | } 25 | 26 | [TestMethod] 27 | public void TestPropertyStubWithGetterOnly() 28 | { 29 | int contactsCount = 55; 30 | var stub = new StubIPhoneBook().ContactsCount_Get(() => contactsCount); 31 | IPhoneBook phoneBook = stub; 32 | Assert.AreEqual(contactsCount, phoneBook.ContactsCount); 33 | } 34 | 35 | [TestMethod] 36 | public void TestPropertyStubWithGetterAndSetter_Get_MockBehavior_Loose() 37 | { 38 | var stub = new StubITestInterface(MockBehavior.Loose); 39 | ITestInterface testInterface = stub; 40 | string returnedValue = testInterface.Prop3; 41 | Assert.IsNull(returnedValue); 42 | } 43 | 44 | [TestMethod] 45 | public void TestPropertyStubWithGetterAndSetter_Set_MockBehavior_Loose() 46 | { 47 | var stub = new StubITestInterface(MockBehavior.Loose); 48 | ITestInterface testInterface = stub; 49 | testInterface.Prop3 = string.Empty; 50 | } 51 | 52 | [TestMethod] 53 | public void TestPropertyStubWithGetterOnly_Get_MockBehavior_Loose() 54 | { 55 | var stub = new StubITestInterface(MockBehavior.Loose); 56 | ITestInterface testInterface = stub; 57 | string returnedValue = testInterface.Prop1; 58 | Assert.IsNull(returnedValue); 59 | } 60 | 61 | [TestMethod] 62 | public void TestPropertyStubWithSetterOnly_Set_MockBehavior_Loose() 63 | { 64 | var stub = new StubITestInterface(MockBehavior.Loose); 65 | ITestInterface testInterface = stub; 66 | testInterface.Prop2 = string.Empty; 67 | } 68 | 69 | [TestMethod] 70 | [ExpectedException(typeof(SimpleStubsException))] 71 | public void TestPropertyStubWithGetterAndSetter_Get_MockBehavior_Strict() 72 | { 73 | var stub = new StubITestInterface(MockBehavior.Strict); 74 | ITestInterface testInterface = stub; 75 | string returnedValue = testInterface.Prop3; 76 | } 77 | 78 | [TestMethod] 79 | [ExpectedException(typeof(SimpleStubsException))] 80 | public void TestPropertyStubWithGetterAndSetter_Set_MockBehavior_Strict() 81 | { 82 | var stub = new StubITestInterface(MockBehavior.Strict); 83 | ITestInterface testInterface = stub; 84 | testInterface.Prop3 = string.Empty; 85 | } 86 | 87 | [TestMethod] 88 | [ExpectedException(typeof(SimpleStubsException))] 89 | public void TestPropertyStubWithGetterOnly_Get_MockBehavior_Strict() 90 | { 91 | var stub = new StubITestInterface(MockBehavior.Strict); 92 | ITestInterface testInterface = stub; 93 | string returnedValue = testInterface.Prop1; 94 | } 95 | 96 | [TestMethod] 97 | [ExpectedException(typeof(SimpleStubsException))] 98 | public void TestPropertyStubWithSetterOnly_Set_MockBehavior_Strict() 99 | { 100 | var stub = new StubITestInterface(MockBehavior.Strict); 101 | ITestInterface testInterface = stub; 102 | testInterface.Prop2 = string.Empty; 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /test/TestClassLibraryTest/SimpleStubs.json: -------------------------------------------------------------------------------- 1 | { 2 | "IgnoredProjects": [ 3 | "SimpleStubs", 4 | "SimpleStubs.CodeGen" 5 | ], 6 | "IgnoredInterfaces": [ 7 | "TestClassLibrary.IIgnoredInterface" 8 | ], 9 | "StubInternalInterfaces": true, 10 | "DefaultMockBehavior": "Loose" 11 | } -------------------------------------------------------------------------------- /test/TestClassLibraryTest/StubGeneratorTest.cs: -------------------------------------------------------------------------------- 1 | using Etg.SimpleStubs; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestClassLibrary; 4 | 5 | namespace TestClassLibraryTest 6 | { 7 | [TestClass] 8 | public class StubGeneratorTest 9 | { 10 | [TestMethod] 11 | public void TestMockBehaviorProperty_MatchesConstructorSuppliedValue_Loose() 12 | { 13 | StubITestInterface stub = new StubITestInterface(MockBehavior.Loose); 14 | Assert.AreEqual(MockBehavior.Loose, stub.MockBehavior); 15 | } 16 | 17 | [TestMethod] 18 | public void TestMockBehaviorProperty_MatchesConstructorSuppliedValue_Strict() 19 | { 20 | StubITestInterface stub = new StubITestInterface(MockBehavior.Strict); 21 | Assert.AreEqual(MockBehavior.Strict, stub.MockBehavior); 22 | } 23 | 24 | [TestMethod] 25 | public void TestMockBehaviorProperty_CanBeChangedAtRuntime() 26 | { 27 | StubITestInterface stub = new StubITestInterface(MockBehavior.Strict); 28 | ITestInterface testInterface = stub; 29 | bool wasExceptionThrownInStrictMode = false; 30 | bool wasExceptionThrownInStrictMode2ndTime = false; 31 | 32 | // Check for exception in strict mode 33 | try 34 | { 35 | testInterface.DoSomething(); 36 | } 37 | catch (SimpleStubsException) 38 | { 39 | wasExceptionThrownInStrictMode = true; 40 | } 41 | 42 | // Switch to loose mode, do not expect exception 43 | stub.MockBehavior = MockBehavior.Loose; 44 | testInterface.DoSomething(); 45 | 46 | // Switch back to strict mode and check for exception 47 | stub.MockBehavior = MockBehavior.Strict; 48 | try 49 | { 50 | testInterface.DoSomething(); 51 | } 52 | catch (SimpleStubsException) 53 | { 54 | wasExceptionThrownInStrictMode2ndTime = true; 55 | } 56 | 57 | Assert.AreEqual(true, wasExceptionThrownInStrictMode); 58 | Assert.AreEqual(true, wasExceptionThrownInStrictMode2ndTime); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /test/TestClassLibraryTest/TestClassLibraryTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {CB81F60F-1374-4B46-BB64-D848B5103A58} 9 | Library 10 | Properties 11 | TestClassLibraryTest 12 | TestClassLibraryTest 13 | v4.7.2 14 | 512 15 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | 10.0 17 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 18 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 19 | False 20 | UnitTest 21 | 22 | latest 23 | 24 | 25 | 26 | 27 | true 28 | full 29 | false 30 | bin\Debug\ 31 | DEBUG;TRACE 32 | prompt 33 | 4 34 | 35 | 36 | pdbonly 37 | true 38 | bin\Release\ 39 | TRACE 40 | prompt 41 | 4 42 | 43 | 44 | OnBuildSuccess 45 | 46 | 47 | 48 | ..\..\packages\Autofac.6.3.0\lib\netstandard2.0\Autofac.dll 49 | 50 | 51 | ..\..\packages\Humanizer.Core.2.2.0\lib\netstandard1.0\Humanizer.dll 52 | 53 | 54 | ..\..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll 55 | 56 | 57 | ..\..\packages\Microsoft.CodeAnalysis.Common.4.1.0\lib\netstandard2.0\Microsoft.CodeAnalysis.dll 58 | 59 | 60 | ..\..\packages\Microsoft.CodeAnalysis.CSharp.4.1.0\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.dll 61 | 62 | 63 | ..\..\packages\Microsoft.CodeAnalysis.CSharp.Workspaces.4.1.0\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.Workspaces.dll 64 | 65 | 66 | ..\..\packages\Microsoft.CodeAnalysis.Workspaces.Common.4.1.0\lib\netstandard2.0\Microsoft.CodeAnalysis.Workspaces.dll 67 | 68 | 69 | 70 | ..\..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll 71 | True 72 | True 73 | 74 | 75 | ..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll 76 | 77 | 78 | ..\..\packages\System.Collections.Immutable.6.0.0\lib\net461\System.Collections.Immutable.dll 79 | 80 | 81 | 82 | ..\..\packages\System.Composition.AttributedModel.6.0.0\lib\net461\System.Composition.AttributedModel.dll 83 | 84 | 85 | ..\..\packages\System.Composition.Convention.6.0.0\lib\net461\System.Composition.Convention.dll 86 | 87 | 88 | ..\..\packages\System.Composition.Hosting.6.0.0\lib\net461\System.Composition.Hosting.dll 89 | 90 | 91 | ..\..\packages\System.Composition.Runtime.6.0.0\lib\net461\System.Composition.Runtime.dll 92 | 93 | 94 | ..\..\packages\System.Composition.TypedParts.6.0.0\lib\net461\System.Composition.TypedParts.dll 95 | 96 | 97 | ..\..\packages\System.Console.4.3.1\lib\net46\System.Console.dll 98 | True 99 | True 100 | 101 | 102 | ..\..\packages\System.Diagnostics.DiagnosticSource.4.7.1\lib\net46\System.Diagnostics.DiagnosticSource.dll 103 | 104 | 105 | ..\..\packages\System.Diagnostics.FileVersionInfo.4.3.0\lib\net46\System.Diagnostics.FileVersionInfo.dll 106 | True 107 | True 108 | 109 | 110 | ..\..\packages\System.Diagnostics.StackTrace.4.3.0\lib\net46\System.Diagnostics.StackTrace.dll 111 | True 112 | True 113 | 114 | 115 | ..\..\packages\System.IO.4.3.0\lib\net462\System.IO.dll 116 | True 117 | True 118 | 119 | 120 | ..\..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll 121 | True 122 | True 123 | 124 | 125 | ..\..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll 126 | True 127 | True 128 | 129 | 130 | ..\..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll 131 | True 132 | True 133 | 134 | 135 | ..\..\packages\System.IO.Pipelines.5.0.1\lib\net461\System.IO.Pipelines.dll 136 | 137 | 138 | ..\..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll 139 | True 140 | True 141 | 142 | 143 | ..\..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll 144 | True 145 | True 146 | 147 | 148 | ..\..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll 149 | 150 | 151 | 152 | ..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll 153 | 154 | 155 | ..\..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll 156 | True 157 | True 158 | 159 | 160 | ..\..\packages\System.Reflection.Metadata.6.0.0\lib\net461\System.Reflection.Metadata.dll 161 | 162 | 163 | ..\..\packages\System.Runtime.4.3.1\lib\net462\System.Runtime.dll 164 | True 165 | True 166 | 167 | 168 | ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll 169 | 170 | 171 | ..\..\packages\System.Runtime.Extensions.4.3.1\lib\net462\System.Runtime.Extensions.dll 172 | True 173 | True 174 | 175 | 176 | ..\..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll 177 | True 178 | True 179 | 180 | 181 | ..\..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net463\System.Security.Cryptography.Algorithms.dll 182 | True 183 | True 184 | 185 | 186 | ..\..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll 187 | True 188 | True 189 | 190 | 191 | ..\..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll 192 | True 193 | True 194 | 195 | 196 | ..\..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll 197 | True 198 | True 199 | 200 | 201 | ..\..\packages\System.Text.Encoding.CodePages.6.0.0\lib\net461\System.Text.Encoding.CodePages.dll 202 | 203 | 204 | ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll 205 | 206 | 207 | ..\..\packages\System.Threading.Thread.4.3.0\lib\net46\System.Threading.Thread.dll 208 | True 209 | True 210 | 211 | 212 | ..\..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll 213 | 214 | 215 | 216 | 217 | ..\..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll 218 | True 219 | True 220 | 221 | 222 | ..\..\packages\System.Xml.XmlDocument.4.3.0\lib\net46\System.Xml.XmlDocument.dll 223 | True 224 | True 225 | 226 | 227 | ..\..\packages\System.Xml.XPath.4.3.0\lib\net46\System.Xml.XPath.dll 228 | True 229 | True 230 | 231 | 232 | ..\..\packages\System.Xml.XPath.XDocument.4.3.0\lib\net46\System.Xml.XPath.XDocument.dll 233 | True 234 | True 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | {3e9c520a-94cf-46d0-864b-4293d439c92a} 261 | SimpleStubs.CodeGen 262 | 263 | 264 | {56be6aef-6db7-4fa6-89bb-b50c5dbbc675} 265 | SimpleStubs 266 | 267 | 268 | {ecbcbae6-949e-4e9c-84e1-614d97909b6c} 269 | TestClassLibrary 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | False 286 | 287 | 288 | False 289 | 290 | 291 | False 292 | 293 | 294 | False 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 312 | 313 | 314 | 315 | 316 | 323 | -------------------------------------------------------------------------------- /test/TestClassLibraryTest/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /test/TestClassLibraryTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | --------------------------------------------------------------------------------