From 123089d5c78ce8fd78110889127e1b07cdef01ba Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 7 Mar 2014 11:21:46 -0800 Subject: [PATCH] General razor clean up * Change Razor compilation to use ApplicationBasePath to determine the app root * Change class name generation to be based on app-relative path. --- WebFx.sln | 14 ++++++ .../IMvcRazorHost.cs | 2 +- .../MvcRazorHost.cs | 36 ++------------ .../Properties/Resources.Designer.cs | 32 +++++++++++++ .../Razor/IRazorCompilationService.cs | 2 +- .../Razor/RazorCompilationService.cs | 40 ++++++++++------ src/Microsoft.AspNet.Mvc.Razor/RazorView.cs | 27 ++++------- src/Microsoft.AspNet.Mvc.Razor/Resources.resx | 6 +++ .../ViewEngine/PathBasedViewFactory.cs | 10 ++-- .../ViewEngine/RazorViewEngine.cs | 12 ++--- .../project.json | 1 + .../RazorCompilationServiceTest.cs | 48 +++++++++++++++++++ .../project.json | 15 ++++++ .../CultureReplacer.cs | 0 .../ExceptionAssert.cs | 0 test/TestCommon/project.json | 10 ++++ 16 files changed, 176 insertions(+), 79 deletions(-) create mode 100644 test/Microsoft.AspNet.Mvc.Razor.Test/RazorCompilationServiceTest.cs create mode 100644 test/Microsoft.AspNet.Mvc.Razor.Test/project.json rename test/{Microsoft.AspNet.Mvc.ModelBinding.Test => TestCommon}/CultureReplacer.cs (100%) rename test/{Microsoft.AspNet.Mvc.ModelBinding.Test => TestCommon}/ExceptionAssert.cs (100%) create mode 100644 test/TestCommon/project.json diff --git a/WebFx.sln b/WebFx.sln index 43f9f28cfc..b099129a1a 100644 --- a/WebFx.sln +++ b/WebFx.sln @@ -55,6 +55,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common.net45", "src\Common\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common.k10", "src\Common\Common.k10.csproj", "{D4576205-A5B5-4382-BB34-19DE9855FE94}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Mvc.Razor.Test.net45", "test\Microsoft.AspNet.Mvc.Razor.Test\Microsoft.AspNet.Mvc.Razor.Test.net45.csproj", "{3EB2CFF9-6E67-4C03-9AC4-2DD169024938}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestCommon.net45", "test\TestCommon\TestCommon.net45.csproj", "{75A07B53-C5EE-4995-A55B-27562C23BCCD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -137,6 +141,14 @@ Global {D4576205-A5B5-4382-BB34-19DE9855FE94}.Debug|Any CPU.Build.0 = Debug|Any CPU {D4576205-A5B5-4382-BB34-19DE9855FE94}.Release|Any CPU.ActiveCfg = Release|Any CPU {D4576205-A5B5-4382-BB34-19DE9855FE94}.Release|Any CPU.Build.0 = Release|Any CPU + {3EB2CFF9-6E67-4C03-9AC4-2DD169024938}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3EB2CFF9-6E67-4C03-9AC4-2DD169024938}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3EB2CFF9-6E67-4C03-9AC4-2DD169024938}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3EB2CFF9-6E67-4C03-9AC4-2DD169024938}.Release|Any CPU.Build.0 = Release|Any CPU + {75A07B53-C5EE-4995-A55B-27562C23BCCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75A07B53-C5EE-4995-A55B-27562C23BCCD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75A07B53-C5EE-4995-A55B-27562C23BCCD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75A07B53-C5EE-4995-A55B-27562C23BCCD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -165,5 +177,7 @@ Global {A7D7CD66-A407-4144-8AB7-07F895F87137} = {CE037E26-9EB5-48E2-B73B-06C6FF6CC9F5} {42195A56-42C0-4CFF-A982-B6E24EFC6356} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} {537CC0EE-4B62-4789-9AE9-94BE28E0D25A} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} + {3EB2CFF9-6E67-4C03-9AC4-2DD169024938} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} + {75A07B53-C5EE-4995-A55B-27562C23BCCD} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/IMvcRazorHost.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/IMvcRazorHost.cs index cec090fdc0..ee49e182b4 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/IMvcRazorHost.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/IMvcRazorHost.cs @@ -5,6 +5,6 @@ namespace Microsoft.AspNet.Mvc.Razor { public interface IMvcRazorHost { - GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream); + GeneratorResults GenerateCode(string rootNamespace, string rootRelativePath, Stream inputStream); } } diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs index f2dba8f026..9589c5b8cb 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Text; using Microsoft.AspNet.Razor; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Parser; @@ -50,19 +49,13 @@ namespace Microsoft.AspNet.Mvc.Razor } } - public GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream) + public GeneratorResults GenerateCode(string rootNamespace, string rootRelativePath, Stream inputStream) { - string className = Path.GetFileNameWithoutExtension(rootRelativePath); - if (rootRelativePath.StartsWith("~/", StringComparison.Ordinal)) - { - rootRelativePath = rootRelativePath.Substring(2); - } - string classNamespace = GenerateNamespace(rootRelativePath); - + string className = ParserHelpers.SanitizeClassName(rootRelativePath); using (var reader = new StreamReader(inputStream)) { var engine = new RazorTemplateEngine(this); - return engine.GenerateCode(reader, className, classNamespace, rootRelativePath); + return engine.GenerateCode(reader, className, rootNamespace, rootRelativePath); } } @@ -70,28 +63,5 @@ namespace Microsoft.AspNet.Mvc.Razor { return new MvcRazorCodeParser(_baseType); } - - private static string GenerateNamespace(string rootRelativePath) - { - var namespaceBuilder = new StringBuilder(rootRelativePath.Length); - rootRelativePath = Path.GetDirectoryName(rootRelativePath); - for (int i = 0; i < rootRelativePath.Length; i++) - { - char c = rootRelativePath[i]; - if (c == Path.DirectorySeparatorChar) - { - namespaceBuilder.Append('.'); - } - else if (!Char.IsLetterOrDigit(c)) - { - namespaceBuilder.Append('_'); - } - else - { - namespaceBuilder.Append(c); - } - } - return namespaceBuilder.ToString(); - } } } diff --git a/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs index ff0f6928c7..daa99ccf2a 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs @@ -26,6 +26,38 @@ namespace Microsoft.AspNet.Mvc.Razor return GetString("ArgumentCannotBeNullOrEmpty"); } + /// + /// The layout view '{0}' could not be located. + /// + internal static string LayoutCannotBeLocated + { + get { return GetString("LayoutCannotBeLocated"); } + } + + /// + /// The layout view '{0}' could not be located. + /// + internal static string FormatLayoutCannotBeLocated(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("LayoutCannotBeLocated"), p0); + } + + /// + /// RenderBody can only be called from a layout page. + /// + internal static string RenderBodyCannotBeCalled + { + get { return GetString("RenderBodyCannotBeCalled"); } + } + + /// + /// RenderBody can only be called from a layout page. + /// + internal static string FormatRenderBodyCannotBeCalled() + { + return GetString("RenderBodyCannotBeCalled"); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNet.Mvc.Razor/Razor/IRazorCompilationService.cs b/src/Microsoft.AspNet.Mvc.Razor/Razor/IRazorCompilationService.cs index 0db3d7cebc..b22c9af035 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Razor/IRazorCompilationService.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Razor/IRazorCompilationService.cs @@ -5,6 +5,6 @@ namespace Microsoft.AspNet.Mvc.Razor { public interface IRazorCompilationService { - Task Compile(string appRoot, IFileInfo fileInfo); + Task Compile(IFileInfo fileInfo); } } diff --git a/src/Microsoft.AspNet.Mvc.Razor/Razor/RazorCompilationService.cs b/src/Microsoft.AspNet.Mvc.Razor/Razor/RazorCompilationService.cs index 68d90dfdd3..9703ec6779 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Razor/RazorCompilationService.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Razor/RazorCompilationService.cs @@ -5,48 +5,60 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.FileSystems; using Microsoft.AspNet.Razor; +using Microsoft.Net.Runtime; namespace Microsoft.AspNet.Mvc.Razor { public class RazorCompilationService : IRazorCompilationService { private static readonly CompilerCache _cache = new CompilerCache(); + private readonly IApplicationEnvironment _environment; private readonly ICompilationService _baseCompilationService; private readonly IMvcRazorHost _razorHost; + private readonly string _appRoot; - public RazorCompilationService(ICompilationService compilationService, IMvcRazorHost razorHost) + public RazorCompilationService(IApplicationEnvironment environment, + ICompilationService compilationService, + IMvcRazorHost razorHost) { + _environment = environment; _baseCompilationService = compilationService; _razorHost = razorHost; + _appRoot = EnsureTrailingSlash(environment.ApplicationBasePath); } - public Task Compile(string appRoot, IFileInfo file) + public Task Compile([NotNull]IFileInfo file) { - return _cache.GetOrAdd(file, () => CompileCore(appRoot, file)); + return _cache.GetOrAdd(file, () => CompileCore(file)); } - private async Task CompileCore(string appRoot, IFileInfo file) + // TODO: Make this internal + public async Task CompileCore(IFileInfo file) { GeneratorResults results; using (Stream inputStream = file.CreateReadStream()) { - Contract.Assert(file.PhysicalPath.StartsWith(appRoot, StringComparison.OrdinalIgnoreCase)); - // Remove the app name segment so that it appears as part of the root relative path: - // work/src/myapp/ -> work/src - // root relative path: myapp/views/home/index.cshtml - // TODO: The root namespace might be a property we'd have to read via configuration since it - // affects other things such as resx files. - appRoot = Path.GetDirectoryName(appRoot.TrimEnd(Path.DirectorySeparatorChar)); - string rootRelativePath = file.PhysicalPath.Substring(appRoot.Length).TrimStart(Path.DirectorySeparatorChar); - results = _razorHost.GenerateCode(rootRelativePath, inputStream); + Contract.Assert(file.PhysicalPath.StartsWith(_appRoot, StringComparison.OrdinalIgnoreCase)); + var rootRelativePath = file.PhysicalPath.Substring(_appRoot.Length); + results = _razorHost.GenerateCode(_environment.ApplicationName, rootRelativePath, inputStream); } if (!results.Success) { - return CompilationResult.Failed(results.GeneratedCode, results.ParserErrors.Select(e => new CompilationMessage(e.Message))); + var messages = results.ParserErrors.Select(e => new CompilationMessage(e.Message)); + throw new CompilationFailedException(messages, results.GeneratedCode); } return await _baseCompilationService.Compile(results.GeneratedCode); } + + private static string EnsureTrailingSlash([NotNull]string path) + { + if (!path.EndsWith(Path.DirectorySeparatorChar.ToString())) + { + path += Path.DirectorySeparatorChar; + } + return path; + } } } diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs index 986e40f936..2b7e8c6847 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs @@ -1,5 +1,4 @@ using System; -using System.Globalization; using System.IO; using System.Net; using System.Text; @@ -29,8 +28,8 @@ namespace Microsoft.AspNet.Mvc.Razor Execute(); } - string bodyContent = contentBuilder.ToString(); - if (!String.IsNullOrEmpty(Layout)) + var bodyContent = contentBuilder.ToString(); + if (!string.IsNullOrEmpty(Layout)) { await RenderLayoutAsync(context, writer, bodyContent); } @@ -43,10 +42,11 @@ namespace Microsoft.AspNet.Mvc.Razor private async Task RenderLayoutAsync(ViewContext context, TextWriter writer, string bodyContent) { var virtualPathFactory = context.ServiceProvider.GetService(); - RazorView layoutView = (RazorView)(await virtualPathFactory.CreateInstance(Layout)); + var layoutView = (RazorView)(await virtualPathFactory.CreateInstance(Layout)); + if (layoutView == null) { - string message = String.Format(CultureInfo.CurrentCulture, "The layout view '{0}' could not be located.", Layout); + var message = Resources.FormatLayoutCannotBeLocated(Layout); throw new InvalidOperationException(message); } @@ -66,18 +66,9 @@ namespace Microsoft.AspNet.Mvc.Razor if (content != null) { var htmlString = content as HtmlString; - if (htmlString != null) - { - writer.Write(content.ToString()); - } - else - { -#if NET45 - WebUtility.HtmlEncode(content.ToString(), writer); -#else - writer.Write(WebUtility.HtmlEncode(content.ToString())); -#endif - } + var contentToWrite = htmlString != null ? content.ToString() : + WebUtility.HtmlEncode(content.ToString()); + writer.Write(contentToWrite); } } @@ -196,7 +187,7 @@ namespace Microsoft.AspNet.Mvc.Razor { if (BodyContent == null) { - throw new InvalidOperationException("RenderBody cannot be called at this point because you're not executing a layout"); + throw new InvalidOperationException(Resources.RenderBodyCannotBeCalled); } return new HtmlString(BodyContent); } diff --git a/src/Microsoft.AspNet.Mvc.Razor/Resources.resx b/src/Microsoft.AspNet.Mvc.Razor/Resources.resx index 445b35c1ec..15174f1865 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.Razor/Resources.resx @@ -120,4 +120,10 @@ Value cannot be null or empty. + + The layout view '{0}' could not be located. + + + RenderBody can only be called from a layout page. + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/PathBasedViewFactory.cs b/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/PathBasedViewFactory.cs index 4301988418..0be334ccbc 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/PathBasedViewFactory.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/PathBasedViewFactory.cs @@ -10,22 +10,20 @@ namespace Microsoft.AspNet.Mvc.Razor private readonly PhysicalFileSystem _fileSystem; private readonly IRazorCompilationService _compilationService; - public VirtualPathViewFactory(IApplicationEnvironment env, IRazorCompilationService compilationService) + public VirtualPathViewFactory(IApplicationEnvironment env, + IRazorCompilationService compilationService) { // TODO: Continue to inject the IFileSystem but only when we get it from the host _fileSystem = new PhysicalFileSystem(env.ApplicationBasePath); _compilationService = compilationService; } - public async Task CreateInstance(string virtualPath) + public async Task CreateInstance([NotNull]string virtualPath) { - // TODO: We need to glean the approot from HttpContext - var appRoot = _fileSystem.Root; - IFileInfo fileInfo; if (_fileSystem.TryGetFileInfo(virtualPath, out fileInfo)) { - CompilationResult result = await _compilationService.Compile(appRoot, fileInfo); + CompilationResult result = await _compilationService.Compile(fileInfo); return (IView)Activator.CreateInstance(result.CompiledType); } diff --git a/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/RazorViewEngine.cs b/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/RazorViewEngine.cs index 6af2bff458..8111be514f 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/RazorViewEngine.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/RazorViewEngine.cs @@ -34,34 +34,34 @@ namespace Microsoft.AspNet.Mvc.Razor // TODO: We plan to change this on the next CR, so we don't have a strong depenedency directly on the specific // type of the action descriptor - ActionDescriptor actionDescriptor = actionContext.ActionDescriptor; + var actionDescriptor = actionContext.ActionDescriptor; if (actionDescriptor == null) { return null; } - if (String.IsNullOrEmpty(viewName)) + if (string.IsNullOrEmpty(viewName)) { viewName = actionDescriptor.Name; } - if (String.IsNullOrEmpty(viewName)) + if (string.IsNullOrEmpty(viewName)) { throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "viewName"); } - bool nameRepresentsPath = IsSpecificPath(viewName); + var nameRepresentsPath = IsSpecificPath(viewName); if (nameRepresentsPath) { - IView view = await _virtualPathFactory.CreateInstance(viewName); + var view = await _virtualPathFactory.CreateInstance(viewName); return view != null ? ViewEngineResult.Found(view) : ViewEngineResult.NotFound(new[] { viewName }); } else { - string controllerName = actionDescriptor.Path; + var controllerName = actionDescriptor.Path; var searchedLocations = new List(_viewLocationFormats.Length); for (int i = 0; i < _viewLocationFormats.Length; i++) { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/project.json b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/project.json index b64eb65a71..832b5fcddf 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/project.json +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/project.json @@ -4,6 +4,7 @@ "Microsoft.AspNet.Abstractions": "0.1-alpha-*", "Microsoft.AspNet.PipelineCore": "0.1-alpha-*", "Microsoft.AspNet.Mvc.ModelBinding" : "", + "TestCommon" : "", "Moq": "4.0.10827", "Xunit": "1.9.1", "Xunit.extensions": "1.9.1" diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorCompilationServiceTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorCompilationServiceTest.cs new file mode 100644 index 0000000000..2fce33891f --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorCompilationServiceTest.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.FileSystems; +using Microsoft.AspNet.Razor; +using Microsoft.AspNet.Razor.Generator.Compiler; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.Net.Runtime; +using Moq; +using Xunit.Extensions; + +namespace Microsoft.AspNet.Mvc.Razor.Test +{ + public class RazorCompilationServiceTest + { + [Theory] + [InlineData(@"src\work\myapp", @"src\work\myapp\views\index\home.cshtml")] + [InlineData(@"src\work\myapp\", @"src\work\myapp\views\index\home.cshtml")] + public void CompileCoreCalculatesRootRelativePath(string appPath, string viewPath) + { + // Arrange + var env = new Mock(); + env.SetupGet(e => e.ApplicationName).Returns("MyTestApplication"); + env.SetupGet(e => e.ApplicationBasePath).Returns(appPath); + var host = new Mock(); + host.Setup(h => h.GenerateCode("MyTestApplication", @"views\index\home.cshtml", It.IsAny())) + .Returns(new GeneratorResults(new Block(new BlockBuilder { Type = BlockType.Comment }), new RazorError[0], new CodeBuilderResult("", new LineMapping[0]))) + .Verifiable(); + var compiler = new Mock(); + compiler.Setup(c => c.Compile(It.IsAny())) + .Returns(Task.FromResult(CompilationResult.Successful("", typeof(RazorCompilationServiceTest)))); + + var razorService = new RazorCompilationService(env.Object, compiler.Object, host.Object); + var fileInfo = new Mock(); + fileInfo.Setup(f => f.PhysicalPath).Returns(viewPath); + fileInfo.Setup(f => f.CreateReadStream()).Returns(Stream.Null); + + // Act + razorService.CompileCore(fileInfo.Object).Wait(); + + // Assert + host.Verify(); + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/project.json b/test/Microsoft.AspNet.Mvc.Razor.Test/project.json new file mode 100644 index 0000000000..2d0d0eb006 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/project.json @@ -0,0 +1,15 @@ +{ + "version" : "0.1-alpha-*", + "dependencies": { + "Microsoft.AspNet.FileSystems": "0.1-alpha-*", + "Microsoft.AspNet.Razor": "0.1-alpha-*", + "Microsoft.AspNet.Mvc.Razor" : "", + "Microsoft.AspNet.Mvc.Razor.Host" : "", + "Moq": "4.0.10827", + "Xunit": "1.9.1", + "Xunit.extensions": "1.9.1" + }, + "configurations": { + "net45": { } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/CultureReplacer.cs b/test/TestCommon/CultureReplacer.cs similarity index 100% rename from test/Microsoft.AspNet.Mvc.ModelBinding.Test/CultureReplacer.cs rename to test/TestCommon/CultureReplacer.cs diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/ExceptionAssert.cs b/test/TestCommon/ExceptionAssert.cs similarity index 100% rename from test/Microsoft.AspNet.Mvc.ModelBinding.Test/ExceptionAssert.cs rename to test/TestCommon/ExceptionAssert.cs diff --git a/test/TestCommon/project.json b/test/TestCommon/project.json new file mode 100644 index 0000000000..03ab8c1121 --- /dev/null +++ b/test/TestCommon/project.json @@ -0,0 +1,10 @@ +{ + "shared": "*.cs", + "dependencies": { + "Xunit": "1.9.1", + "Xunit.extensions": "1.9.1" + }, + "configurations": { + "net45": { } + } +} \ No newline at end of file