public void Compile_RunsCallback() { var content = "public class MyTestType {}"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; RoslynCompilationContext usedCompilation = null; var mvcRazorHost = new Mock <IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns(string.Empty); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost.Object, GetOptions(callback: c => usedCompilation = c), GetFileProviderAccessor(), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); Assert.NotNull(usedCompilation); Assert.NotNull(usedCompilation.Compilation); Assert.Equal(1, usedCompilation.Compilation.SyntaxTrees.Length); }
/// <summary> /// Initializes a new instance of <see cref="CompilerCacheEntry"/> for a file that was dynamically compiled. /// </summary> /// <param name="info">Metadata about the file that was compiled.</param> /// <param name="compiledType">The compiled <see cref="Type"/>.</param> public CompilerCacheEntry([NotNull] RelativeFileInfo info, [NotNull] Type compiledType) { CompiledType = compiledType; RelativePath = info.RelativePath; Length = info.FileInfo.Length; LastModified = info.FileInfo.LastModified; }
public void Compile_ReturnsUncachedCompilationResultWithCompiledContent() { // Arrange var content = @" public class MyTestType {}"; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryExporter = GetLibraryExporter(); var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions()); var mvcRazorHost = new Mock<IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns(string.Empty); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryExporter, compilerOptionsProvider.Object, mvcRazorHost.Object, GetOptions()); var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert var uncachedResult = Assert.IsType<UncachedCompilationResult>(result); Assert.Equal("MyTestType", result.CompiledType.Name); Assert.Equal(content, uncachedResult.CompiledContent); }
public void Compile_ReturnsSingleTypeThatStartsWithMainClassNamePrefix() { // Arrange var content = @" public class RazorPrefixType {} public class NotRazorPrefixType {}"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; var mvcRazorHost = new Mock <IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns("RazorPrefix"); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost.Object, GetOptions(), GetFileProviderAccessor(), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.NotNull(result.CompiledType); Assert.Equal("RazorPrefixType", result.CompiledType.Name); }
/// <inheritdoc /> public CompilationResult Compile(RelativeFileInfo file) { if (file == null) { throw new ArgumentNullException(nameof(file)); } GeneratorResults results; using (var inputStream = file.FileInfo.CreateReadStream()) { _logger.RazorFileToCodeCompilationStart(file.RelativePath); var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0; results = GenerateCode(file.RelativePath, inputStream); _logger.RazorFileToCodeCompilationEnd(file.RelativePath, startTimestamp); } if (!results.Success) { return(GetCompilationFailedResult(file, results.ParserErrors)); } return(_compilationService.Compile(file, results.GeneratedCode)); }
public void Compile_ReturnsGeneratedCodePath_IfLinePragmaIsNotAvailable() { // Arrange var fileContent = "file content"; var content = @"this should fail"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; var mvcRazorHost = Mock.Of <IMvcRazorHost>(); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost, GetOptions(), GetFileProviderAccessor(), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { Content = fileContent }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType <CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal("Generated Code", compilationFailure.SourceFilePath); Assert.Equal(content, compilationFailure.SourceFileContent); }
public void CompileCalculatesRootRelativePath(string appPath, string viewPath) { // Arrange var host = new Mock <IMvcRazorHost>(); host.Setup(h => h.GenerateCode(@"Views\index\home.cshtml", It.IsAny <Stream>())) .Returns(GetGeneratorResult()) .Verifiable(); var fileInfo = new Mock <IFileInfo>(); fileInfo.Setup(f => f.PhysicalPath).Returns(viewPath); fileInfo.Setup(f => f.CreateReadStream()).Returns(Stream.Null); var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml"); var compiler = new Mock <ICompilationService>(); compiler.Setup(c => c.Compile(relativeFileInfo, It.IsAny <string>())) .Returns(CompilationResult.Successful(typeof(RazorCompilationServiceTest))); var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions()); // Act razorService.Compile(relativeFileInfo); // Assert host.Verify(); }
public void Compile_ReturnsCompilationResult() { // Arrange var content = @" public class MyTestType {}"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; var mvcRazorHost = new Mock<IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns(string.Empty); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost.Object, GetOptions(), GetFileProviderAccessor(), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.Equal("MyTestType", result.CompiledType.Name); }
public void CompileCalculatesRootRelativePath(string appPath, string viewPath) { // Arrange var host = new Mock<IMvcRazorHost>(); host.Setup(h => h.GenerateCode(@"Views\index\home.cshtml", It.IsAny<Stream>())) .Returns(GetGeneratorResult()) .Verifiable(); var fileInfo = new Mock<IFileInfo>(); fileInfo.Setup(f => f.PhysicalPath).Returns(viewPath); fileInfo.Setup(f => f.CreateReadStream()).Returns(Stream.Null); var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml"); var compiler = new Mock<ICompilationService>(); compiler.Setup(c => c.Compile(relativeFileInfo, It.IsAny<string>())) .Returns(CompilationResult.Successful(typeof(RazorCompilationServiceTest))); var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions()); // Act razorService.Compile(relativeFileInfo); // Assert host.Verify(); }
/// <inheritdoc /> public CompilationResult Compile(RelativeFileInfo file) { if (file == null) { throw new ArgumentNullException(nameof(file)); } GeneratorResults results; using (var inputStream = file.FileInfo.CreateReadStream()) { _logger.RazorFileToCodeCompilationStart(file.RelativePath); var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0; results = GenerateCode(file.RelativePath, inputStream); _logger.RazorFileToCodeCompilationEnd(file.RelativePath, startTimestamp); } if (!results.Success) { return GetCompilationFailedResult(file, results.ParserErrors); } return _compilationService.Compile(file, results.GeneratedCode); }
// Internal for unit testing internal CompilationResult GetCompilationFailedResult(RelativeFileInfo file, IEnumerable <RazorError> errors) { // If a SourceLocation does not specify a file path, assume it is produced // from parsing the current file. var messageGroups = errors .GroupBy(razorError => razorError.Location.FilePath ?? file.RelativePath, StringComparer.Ordinal); var failures = new List <CompilationFailure>(); foreach (var group in messageGroups) { var filePath = group.Key; var fileContent = ReadFileContentsSafely(filePath); var compilationFailure = new CompilationFailure( filePath, fileContent, compiledContent: string.Empty, messages: group.Select(parserError => CreateDiagnosticMessage(parserError, filePath))); failures.Add(compilationFailure); } return(new CompilationResult(failures)); }
private CompilerCacheResult CreateCacheEntry( string normalizedPath, Func <RelativeFileInfo, CompilationResult> compile) { CompilerCacheResult cacheResult; var fileInfo = _fileProvider.GetFileInfo(normalizedPath); MemoryCacheEntryOptions cacheEntryOptions; CompilerCacheResult cacheResultToCache; if (!fileInfo.Exists) { cacheResultToCache = CompilerCacheResult.FileNotFound; cacheResult = CompilerCacheResult.FileNotFound; cacheEntryOptions = new MemoryCacheEntryOptions(); cacheEntryOptions.AddExpirationToken(_fileProvider.Watch(normalizedPath)); } else { var relativeFileInfo = new RelativeFileInfo(fileInfo, normalizedPath); var compilationResult = compile(relativeFileInfo).EnsureSuccessful(); cacheEntryOptions = GetMemoryCacheEntryOptions(normalizedPath); // By default the CompilationResult returned by IRoslynCompiler is an instance of // UncachedCompilationResult. This type has the generated code as a string property and do not want // to cache it. We'll instead cache the unwrapped result. cacheResultToCache = new CompilerCacheResult( CompilationResult.Successful(compilationResult.CompiledType)); cacheResult = new CompilerCacheResult(compilationResult); } _cache.Set(normalizedPath, cacheResultToCache, cacheEntryOptions); return(cacheResult); }
public void Compile_ReturnsCompilationFailureWithPathsFromLinePragmas() { // Arrange var viewPath = "some-relative-path"; var fileContent = "test file content"; var content = $@" #line 1 ""{viewPath}"" this should fail"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; var mvcRazorHost = Mock.Of<IMvcRazorHost>(); var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(viewPath, fileContent); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost, GetOptions(), GetFileProviderAccessor(fileProvider), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType<CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal(relativeFileInfo.RelativePath, compilationFailure.SourceFilePath); Assert.Equal(fileContent, compilationFailure.SourceFileContent); }
public void Compile_ReturnsCompilationFailureWithPathsFromLinePragmas() { // Arrange var viewPath = "some-relative-path"; var fileContent = "test file content"; var content = $@" #line 1 ""{viewPath}"" this should fail"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; var mvcRazorHost = Mock.Of <IMvcRazorHost>(); var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(viewPath, fileContent); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost, GetOptions(), GetFileProviderAccessor(fileProvider), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType <CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal(relativeFileInfo.RelativePath, compilationFailure.SourceFilePath); Assert.Equal(fileContent, compilationFailure.SourceFileContent); }
/// <inheritdoc /> public CompilationResult Compile([NotNull] RelativeFileInfo file) { GeneratorResults results; using (var inputStream = file.FileInfo.CreateReadStream()) { results = _razorHost.GenerateCode(file.RelativePath, inputStream); } if (!results.Success) { return(GetCompilationFailedResult(file, results.ParserErrors)); } return(_compilationService.Compile(file, results.GeneratedCode)); }
public void Compile_DoesNotThrow_IfFileCannotBeRead() { // Arrange var path = "some-relative-path"; var content = $@" #line 1 ""{path}"" this should fail"; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryManager = GetLibraryManager(); var compilerOptionsProvider = new Mock <ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions()); var mvcRazorHost = Mock.Of <IMvcRazorHost>(); var mockFileInfo = new Mock <IFileInfo>(); mockFileInfo.Setup(f => f.CreateReadStream()) .Throws(new Exception()); var fileProvider = new TestFileProvider(); fileProvider.AddFile(path, mockFileInfo.Object); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryManager, compilerOptionsProvider.Object, mvcRazorHost, GetOptions(fileProvider)); var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType <CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal(path, compilationFailure.SourceFilePath); Assert.Null(compilationFailure.SourceFileContent); }
public void Compile_UsesApplicationsCompilationSettings_ForParsingAndCompilation() { // Arrange var content = @" #if MY_CUSTOM_DEFINE public class MyCustomDefinedClass {} #else public class MyNonCustomDefinedClass {} #endif "; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryManager = GetLibraryManager(); var compilerOptionsProvider = new Mock <ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions { Defines = new[] { "MY_CUSTOM_DEFINE" } }); var mvcRazorHost = new Mock <IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns("My"); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryManager, compilerOptionsProvider.Object, mvcRazorHost.Object, GetOptions()); var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.NotNull(result.CompiledType); Assert.Equal("MyCustomDefinedClass", result.CompiledType.Name); }
public void GetOrAdd_IgnoresCachedValueIfFileIsIdentical_ButViewImportsWasAdedSinceTheCacheWasCreated() { // Arrange var expectedType = typeof(RuntimeCompileDifferent); var fileProvider = new TestFileProvider(); var collection = new ViewCollection(); var precompiledFile = collection.FileInfos[0]; precompiledFile.RelativePath = "Views\\home\\index.cshtml"; var cache = new CompilerCache(new[] { collection }, TestLoadContext, fileProvider); var testFile = new TestFileInfo { Content = new PreCompile().Content, LastModified = precompiledFile.LastModified, PhysicalPath = precompiledFile.RelativePath }; fileProvider.AddFile(precompiledFile.RelativePath, testFile); var relativeFile = new RelativeFileInfo(testFile, testFile.PhysicalPath); // Act 1 var result1 = cache.GetOrAdd(testFile.PhysicalPath, compile: _ => { throw new Exception("should not be called"); }); // Assert 1 Assert.NotSame(CompilerCacheResult.FileNotFound, result1); var actual1 = result1.CompilationResult; Assert.NotNull(actual1); Assert.Equal(typeof(PreCompile), actual1.CompiledType); // Act 2 var globalTrigger = fileProvider.GetTrigger("Views\\_ViewImports.cshtml"); globalTrigger.IsExpired = true; var result2 = cache.GetOrAdd(testFile.PhysicalPath, compile: _ => CompilationResult.Successful(expectedType)); // Assert 2 Assert.NotSame(CompilerCacheResult.FileNotFound, result2); var actual2 = result2.CompilationResult; Assert.NotNull(actual2); Assert.Equal(expectedType, actual2.CompiledType); }
private GetOrAddResult OnCacheMiss(RelativeFileInfo file, string normalizedPath, Func <RelativeFileInfo, CompilationResult> compile) { var compilationResult = compile(file).EnsureSuccessful(); // Concurrent addition to MemoryCache with the same key result in safe race. var compilerCacheEntry = new CompilerCacheEntry(file, compilationResult.CompiledType); var cacheEntry = _cache.Set <CompilerCacheEntry>( normalizedPath, compilerCacheEntry, GetMemoryCacheEntryOptions(compilerCacheEntry.RelativePath)); return(new GetOrAddResult { CompilationResult = compilationResult, CompilerCacheEntry = cacheEntry }); }
public void Compile_ReturnsFailedResultIfParseFails() { // Arrange var errorSink = new ErrorSink(); errorSink.OnError(new RazorError("some message", 1, 1, 1, 1)); var generatorResult = new GeneratorResults( new Block(new BlockBuilder { Type = BlockType.Comment }), Enumerable.Empty <TagHelperDescriptor>(), errorSink, new CodeGeneratorResult("", new LineMapping[0]), new ChunkTree()); var host = new Mock <IMvcRazorHost>(); host.Setup(h => h.GenerateCode(It.IsAny <string>(), It.IsAny <Stream>())) .Returns(generatorResult) .Verifiable(); var fileInfo = new Mock <IFileInfo>(); fileInfo.Setup(f => f.CreateReadStream()) .Returns(Stream.Null); var compiler = new Mock <ICompilationService>(MockBehavior.Strict); var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml"); var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions()); // Act var result = razorService.Compile(relativeFileInfo); // Assert Assert.NotNull(result.CompilationFailures); Assert.Collection(result.CompilationFailures, failure => { var message = Assert.Single(failure.Messages); Assert.Equal("some message", message.Message); }); host.Verify(); }
/// <inheritdoc /> public CompilationResult Compile(RelativeFileInfo file) { if (file == null) { throw new ArgumentNullException(nameof(file)); } GeneratorResults results; using (var inputStream = file.FileInfo.CreateReadStream()) { results = GenerateCode(file.RelativePath, inputStream); } if (!results.Success) { return GetCompilationFailedResult(file, results.ParserErrors); } return _compilationService.Compile(file, results.GeneratedCode); }
public void Compile_ReturnsCompilationFailureWithPathsFromLinePragmas() { // Arrange var viewPath = "some-relative-path"; var fileContent = "test file content"; var content = $@" #line 1 ""{viewPath}"" this should fail"; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryManager = GetLibraryManager(); var compilerOptionsProvider = new Mock <ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions()); var mvcRazorHost = Mock.Of <IMvcRazorHost>(); var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(viewPath, fileContent); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryManager, compilerOptionsProvider.Object, mvcRazorHost, GetOptions(fileProvider)); var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType <CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal(relativeFileInfo.RelativePath, compilationFailure.SourceFilePath); Assert.Equal(fileContent, compilationFailure.SourceFileContent); }
public void Compile_UsesApplicationsCompilationSettings_ForParsingAndCompilation() { // Arrange var content = @" #if MY_CUSTOM_DEFINE public class MyCustomDefinedClass {} #else public class MyNonCustomDefinedClass {} #endif "; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; var mvcRazorHost = new Mock <IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns("My"); var options = GetOptions(); options.Value.ParseOptions = options.Value.ParseOptions.WithPreprocessorSymbols("MY_CUSTOM_DEFINE"); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost.Object, options, GetFileProviderAccessor(), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.NotNull(result.CompiledType); Assert.Equal("MyCustomDefinedClass", result.CompiledType.Name); }
/// <inheritdoc /> public CompilationResult Compile(RelativeFileInfo file) { if (file == null) { throw new ArgumentNullException(nameof(file)); } GeneratorResults results; using (var inputStream = file.FileInfo.CreateReadStream()) { results = GenerateCode(file.RelativePath, inputStream); } if (!results.Success) { return(GetCompilationFailedResult(file, results.ParserErrors)); } return(_compilationService.Compile(file, results.GeneratedCode)); }
public void Compile_DoesNotThrow_IfFileCannotBeRead() { // Arrange var path = "some-relative-path"; var content = $@" #line 1 ""{path}"" this should fail"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; var mvcRazorHost = Mock.Of <IMvcRazorHost>(); var mockFileInfo = new Mock <IFileInfo>(); mockFileInfo.Setup(f => f.CreateReadStream()) .Throws(new Exception()); var fileProvider = new TestFileProvider(); fileProvider.AddFile(path, mockFileInfo.Object); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost, GetOptions(), GetFileProviderAccessor(fileProvider), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType <CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal(path, compilationFailure.SourceFilePath); Assert.Null(compilationFailure.SourceFileContent); }
public void Compile_ReturnsSingleTypeThatStartsWithMainClassNamePrefix() { // Arrange var content = @" public class RazorPrefixType {} public class NotRazorPrefixType {}"; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryManager = GetLibraryManager(); var compilerOptionsProvider = new Mock <ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions()); var mvcRazorHost = new Mock <IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns("RazorPrefix"); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryManager, compilerOptionsProvider.Object, mvcRazorHost.Object, GetOptions()); var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.NotNull(result.CompiledType); Assert.Equal("RazorPrefixType", result.CompiledType.Name); }
public void Compile_ReturnsUncachedCompilationResultWithCompiledContent() { // Arrange var content = @" public class MyTestType {}"; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryManager = GetLibraryManager(); var compilerOptionsProvider = new Mock <ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions()); var mvcRazorHost = new Mock <IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns(string.Empty); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryManager, compilerOptionsProvider.Object, mvcRazorHost.Object, GetOptions()); var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert var uncachedResult = Assert.IsType <UncachedCompilationResult>(result); Assert.Equal("MyTestType", result.CompiledType.Name); Assert.Equal(content, uncachedResult.CompiledContent); }
public void Compile_ReturnsGeneratedCodePath_IfLinePragmaIsNotAvailable() { // Arrange var fileContent = "file content"; var content = @"this should fail"; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryManager = GetLibraryManager(); var compilerOptionsProvider = new Mock <ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions()); var mvcRazorHost = Mock.Of <IMvcRazorHost>(); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryManager, compilerOptionsProvider.Object, mvcRazorHost, GetOptions()); var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { Content = fileContent }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType <CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal("Generated Code", compilationFailure.SourceFilePath); Assert.Equal(content, compilationFailure.SourceFileContent); }
public void Compile_ReturnsCompilationFailureWithPathsFromLinePragmas() { // Arrange var viewPath = "some-relative-path"; var fileContent = "test file content"; var content = $@" #line 1 ""{viewPath}"" this should fail"; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryExporter = GetLibraryExporter(); var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions()); var mvcRazorHost = Mock.Of<IMvcRazorHost>(); var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(viewPath, fileContent); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryExporter, compilerOptionsProvider.Object, mvcRazorHost, GetOptions(fileProvider)); var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType<CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal(relativeFileInfo.RelativePath, compilationFailure.SourceFilePath); Assert.Equal(fileContent, compilationFailure.SourceFileContent); }
/// <inheritdoc /> public CompilerCacheResult GetOrAdd( [NotNull] string relativePath, [NotNull] Func <RelativeFileInfo, CompilationResult> compile) { var normalizedPath = NormalizePath(relativePath); CompilerCacheResult cacheResult; if (!_cache.TryGetValue(normalizedPath, out cacheResult)) { var fileInfo = _fileProvider.GetFileInfo(relativePath); MemoryCacheEntryOptions cacheEntryOptions; CompilerCacheResult cacheResultToCache; if (!fileInfo.Exists) { cacheResultToCache = CompilerCacheResult.FileNotFound; cacheResult = CompilerCacheResult.FileNotFound; cacheEntryOptions = new MemoryCacheEntryOptions(); cacheEntryOptions.AddExpirationTrigger(_fileProvider.Watch(relativePath)); } else { var relativeFileInfo = new RelativeFileInfo(fileInfo, relativePath); var compilationResult = compile(relativeFileInfo).EnsureSuccessful(); cacheEntryOptions = GetMemoryCacheEntryOptions(relativePath); // By default the CompilationResult returned by IRoslynCompiler is an instance of // UncachedCompilationResult. This type has the generated code as a string property and do not want // to cache it. We'll instead cache the unwrapped result. cacheResultToCache = new CompilerCacheResult( CompilationResult.Successful(compilationResult.CompiledType)); cacheResult = new CompilerCacheResult(compilationResult); } _cache.Set(normalizedPath, cacheResultToCache, cacheEntryOptions); } return(cacheResult); }
public void Compile_ReturnsResultFromCompilationServiceIfParseSucceeds() { // Arrange var code = "compiled-content"; var generatorResult = new GeneratorResults( new Block(new BlockBuilder { Type = BlockType.Comment }), Enumerable.Empty <TagHelperDescriptor>(), new ErrorSink(), new CodeGeneratorResult(code, new LineMapping[0]), new ChunkTree()); var host = new Mock <IMvcRazorHost>(); host.Setup(h => h.GenerateCode(It.IsAny <string>(), It.IsAny <Stream>())) .Returns(generatorResult); var fileInfo = new Mock <IFileInfo>(); fileInfo.Setup(f => f.CreateReadStream()) .Returns(Stream.Null); var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml"); var compilationResult = CompilationResult.Successful(typeof(object)); var compiler = new Mock <ICompilationService>(); compiler.Setup(c => c.Compile(relativeFileInfo, code)) .Returns(compilationResult) .Verifiable(); var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions()); // Act var result = razorService.Compile(relativeFileInfo); // Assert Assert.Same(compilationResult, result); compiler.Verify(); }
/// <inheritdoc /> public CompilerCacheResult GetOrAdd( [NotNull] string relativePath, [NotNull] Func<RelativeFileInfo, CompilationResult> compile) { var normalizedPath = NormalizePath(relativePath); CompilerCacheResult cacheResult; if (!_cache.TryGetValue(normalizedPath, out cacheResult)) { var fileInfo = _fileProvider.GetFileInfo(relativePath); MemoryCacheEntryOptions cacheEntryOptions; CompilerCacheResult cacheResultToCache; if (!fileInfo.Exists) { cacheResultToCache = CompilerCacheResult.FileNotFound; cacheResult = CompilerCacheResult.FileNotFound; cacheEntryOptions = new MemoryCacheEntryOptions(); cacheEntryOptions.AddExpirationTrigger(_fileProvider.Watch(relativePath)); } else { var relativeFileInfo = new RelativeFileInfo(fileInfo, relativePath); var compilationResult = compile(relativeFileInfo).EnsureSuccessful(); cacheEntryOptions = GetMemoryCacheEntryOptions(relativePath); // By default the CompilationResult returned by IRoslynCompiler is an instance of // UncachedCompilationResult. This type has the generated code as a string property and do not want // to cache it. We'll instead cache the unwrapped result. cacheResultToCache = new CompilerCacheResult( CompilationResult.Successful(compilationResult.CompiledType)); cacheResult = new CompilerCacheResult(compilationResult); } _cache.Set(normalizedPath, cacheResultToCache, cacheEntryOptions); } return cacheResult; }
// Internal for unit testing internal CompilationResult GetCompilationFailedResult(RelativeFileInfo file, IEnumerable<RazorError> errors) { // If a SourceLocation does not specify a file path, assume it is produced // from parsing the current file. var messageGroups = errors .GroupBy(razorError => razorError.Location.FilePath ?? file.RelativePath, StringComparer.Ordinal); var failures = new List<CompilationFailure>(); foreach (var group in messageGroups) { var filePath = group.Key; var fileContent = ReadFileContentsSafely(filePath); var compilationFailure = new CompilationFailure( filePath, fileContent, group.Select(parserError => CreateDiagnosticMessage(parserError, filePath))); failures.Add(compilationFailure); } return CompilationResult.Failed(failures); }
public void Compile_ReturnsFailedResultIfParseFails() { // Arrange var errorSink = new ErrorSink(); errorSink.OnError(new RazorError("some message", 1, 1, 1, 1)); var generatorResult = new GeneratorResults( new Block(new BlockBuilder { Type = BlockType.Comment }), Enumerable.Empty<TagHelperDescriptor>(), errorSink, new CodeGeneratorResult("", new LineMapping[0]), new ChunkTree()); var host = new Mock<IMvcRazorHost>(); host.Setup(h => h.GenerateCode(It.IsAny<string>(), It.IsAny<Stream>())) .Returns(generatorResult) .Verifiable(); var fileInfo = new Mock<IFileInfo>(); fileInfo.Setup(f => f.CreateReadStream()) .Returns(Stream.Null); var compiler = new Mock<ICompilationService>(MockBehavior.Strict); var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml"); var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions()); // Act var result = razorService.Compile(relativeFileInfo); // Assert Assert.NotNull(result.CompilationFailures); Assert.Collection(result.CompilationFailures, failure => { var message = Assert.Single(failure.Messages); Assert.Equal("some message", message.Message); }); host.Verify(); }
/// <inheritdoc /> public CompilationResult Compile(RelativeFileInfo fileInfo, string compilationContent) { if (fileInfo == null) { throw new ArgumentNullException(nameof(fileInfo)); } if (compilationContent == null) { throw new ArgumentNullException(nameof(compilationContent)); } var assemblyName = Path.GetRandomFileName(); var compilationSettings = _compilerOptionsProvider.GetCompilationSettings(_environment); var syntaxTree = SyntaxTreeGenerator.Generate( compilationContent, assemblyName, compilationSettings); var references = _applicationReferences.Value; var compilationOptions = compilationSettings .CompilationOptions .WithOutputKind(OutputKind.DynamicallyLinkedLibrary); var compilation = CSharpCompilation.Create( assemblyName, options: compilationOptions, syntaxTrees: new[] { syntaxTree }, references: references); compilation = Rewrite(compilation); using (var ms = new MemoryStream()) { using (var pdb = new MemoryStream()) { EmitResult result; if (_supportsPdbGeneration.Value) { result = compilation.Emit(ms, pdbStream: pdb); } else { result = compilation.Emit(ms); } if (!result.Success) { return GetCompilationFailedResult( fileInfo.RelativePath, compilationContent, assemblyName, result.Diagnostics); } Assembly assembly; ms.Seek(0, SeekOrigin.Begin); if (_supportsPdbGeneration.Value) { pdb.Seek(0, SeekOrigin.Begin); assembly = LoadStream(ms, pdb); } else { assembly = LoadStream(ms, assemblySymbols: null); } var type = assembly.GetExportedTypes() .First(t => t.Name.StartsWith(_classPrefix, StringComparison.Ordinal)); return UncachedCompilationResult.Successful(type, compilationContent); } } }
private GetOrAddResult OnCacheMiss(RelativeFileInfo file, string normalizedPath, Func<RelativeFileInfo, CompilationResult> compile) { var compilationResult = compile(file).EnsureSuccessful(); // Concurrent addition to MemoryCache with the same key result in safe race. var compilerCacheEntry = new CompilerCacheEntry(file, compilationResult.CompiledType); var cacheEntry = _cache.Set<CompilerCacheEntry>( normalizedPath, compilerCacheEntry, GetMemoryCacheEntryOptions(compilerCacheEntry.RelativePath)); return new GetOrAddResult { CompilationResult = compilationResult, CompilerCacheEntry = cacheEntry }; }
public void Compile_DoesNotThrow_IfFileCannotBeRead() { // Arrange var path = "some-relative-path"; var content = $@" #line 1 ""{path}"" this should fail"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; var mvcRazorHost = Mock.Of<IMvcRazorHost>(); var mockFileInfo = new Mock<IFileInfo>(); mockFileInfo.Setup(f => f.CreateReadStream()) .Throws(new Exception()); var fileProvider = new TestFileProvider(); fileProvider.AddFile(path, mockFileInfo.Object); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost, GetOptions(), GetFileProviderAccessor(fileProvider), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType<CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal(path, compilationFailure.SourceFilePath); Assert.Null(compilationFailure.SourceFileContent); }
public void GetCompilationFailedResult_ReturnsCompilationResult_WithGroupedMessages() { // Arrange var viewPath = @"views/index.razor"; var viewImportsPath = @"views/global.import.cshtml"; var host = Mock.Of <IMvcRazorHost>(); var fileProvider = new TestFileProvider(); var file = fileProvider.AddFile(viewPath, "View Content"); fileProvider.AddFile(viewImportsPath, "Global Import Content"); var relativeFileInfo = new RelativeFileInfo(file, viewPath); var razorService = new RazorCompilationService( Mock.Of <ICompilationService>(), Mock.Of <IMvcRazorHost>(), GetOptions(fileProvider)); var errors = new[] { new RazorError("message-1", new SourceLocation(1, 2, 17), length: 1), new RazorError("message-2", new SourceLocation(viewPath, 1, 4, 6), 7), new RazorError { Message = "message-3" }, new RazorError("message-4", new SourceLocation(viewImportsPath, 1, 3, 8), 4), }; // Act var result = razorService.GetCompilationFailedResult(relativeFileInfo, errors); // Assert Assert.NotNull(result.CompilationFailures); Assert.Collection(result.CompilationFailures, failure => { Assert.Equal(viewPath, failure.SourceFilePath); Assert.Equal("View Content", failure.SourceFileContent); Assert.Collection(failure.Messages, message => { Assert.Equal(errors[0].Message, message.Message); Assert.Equal(viewPath, message.SourceFilePath); Assert.Equal(3, message.StartLine); Assert.Equal(17, message.StartColumn); Assert.Equal(3, message.EndLine); Assert.Equal(18, message.EndColumn); }, message => { Assert.Equal(errors[1].Message, message.Message); Assert.Equal(viewPath, message.SourceFilePath); Assert.Equal(5, message.StartLine); Assert.Equal(6, message.StartColumn); Assert.Equal(5, message.EndLine); Assert.Equal(13, message.EndColumn); }, message => { Assert.Equal(errors[2].Message, message.Message); Assert.Equal(viewPath, message.SourceFilePath); Assert.Equal(0, message.StartLine); Assert.Equal(-1, message.StartColumn); Assert.Equal(0, message.EndLine); Assert.Equal(-2, message.EndColumn); }); }, failure => { Assert.Equal(viewImportsPath, failure.SourceFilePath); Assert.Equal("Global Import Content", failure.SourceFileContent); Assert.Collection(failure.Messages, message => { Assert.Equal(errors[3].Message, message.Message); Assert.Equal(viewImportsPath, message.SourceFilePath); Assert.Equal(4, message.StartLine); Assert.Equal(8, message.StartColumn); Assert.Equal(4, message.EndLine); Assert.Equal(12, message.EndColumn); }); }); }
public void Compile_RunsCallback() { var content = "public class MyTestType {}"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; RoslynCompilationContext usedCompilation = null; var mvcRazorHost = new Mock<IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns(string.Empty); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost.Object, GetOptions(callback: c => usedCompilation = c), GetFileProviderAccessor(), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); Assert.NotNull(usedCompilation); Assert.NotNull(usedCompilation.Compilation); Assert.Equal(1, usedCompilation.Compilation.SyntaxTrees.Length); }
private CompilationResult ThrowsIfCalled(RelativeFileInfo file) { throw new Exception("Shouldn't be called"); }
public void Compile_DoesNotThrow_IfFileCannotBeRead() { // Arrange var path = "some-relative-path"; var content = $@" #line 1 ""{path}"" this should fail"; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryExporter = GetLibraryExporter(); var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions()); var mvcRazorHost = Mock.Of<IMvcRazorHost>(); var mockFileInfo = new Mock<IFileInfo>(); mockFileInfo.Setup(f => f.CreateReadStream()) .Throws(new Exception()); var fileProvider = new TestFileProvider(); fileProvider.AddFile(path, mockFileInfo.Object); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryExporter, compilerOptionsProvider.Object, mvcRazorHost, GetOptions(fileProvider)); var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType<CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal(path, compilationFailure.SourceFilePath); Assert.Null(compilationFailure.SourceFileContent); }
public void Compile_UsesApplicationsCompilationSettings_ForParsingAndCompilation() { // Arrange var content = @" #if MY_CUSTOM_DEFINE public class MyCustomDefinedClass {} #else public class MyNonCustomDefinedClass {} #endif "; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryExporter = GetLibraryExporter(); var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions { Defines = new[] { "MY_CUSTOM_DEFINE" } }); var mvcRazorHost = new Mock<IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns("My"); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryExporter, compilerOptionsProvider.Object, mvcRazorHost.Object, GetOptions()); var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.NotNull(result.CompiledType); Assert.Equal("MyCustomDefinedClass", result.CompiledType.Name); }
public void Compile_ReturnsSingleTypeThatStartsWithMainClassNamePrefix() { // Arrange var content = @" public class RazorPrefixType {} public class NotRazorPrefixType {}"; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryExporter = GetLibraryExporter(); var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions()); var mvcRazorHost = new Mock<IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns("RazorPrefix"); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryExporter, compilerOptionsProvider.Object, mvcRazorHost.Object, GetOptions()); var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.NotNull(result.CompiledType); Assert.Equal("RazorPrefixType", result.CompiledType.Name); }
public void Compile_ReturnsGeneratedCodePath_IfLinePragmaIsNotAvailable() { // Arrange var fileContent = "file content"; var content = @"this should fail"; var applicationEnvironment = GetApplicationEnvironment(); var accessor = GetLoadContextAccessor(); var libraryExporter = GetLibraryExporter(); var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>(); compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName, applicationEnvironment.RuntimeFramework, applicationEnvironment.Configuration)) .Returns(new CompilerOptions()); var mvcRazorHost = Mock.Of<IMvcRazorHost>(); var compilationService = new RoslynCompilationService(applicationEnvironment, accessor, libraryExporter, compilerOptionsProvider.Object, mvcRazorHost, GetOptions()); var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { Content = fileContent }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType<CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal("Generated Code", compilationFailure.SourceFilePath); Assert.Equal(content, compilationFailure.SourceFileContent); }
/// <inheritdoc /> public CompilationResult Compile([NotNull] RelativeFileInfo fileInfo, [NotNull] string compilationContent) { var assemblyName = Path.GetRandomFileName(); var compilationSettings = _compilerOptionsProvider.GetCompilationSettings(_environment); var syntaxTree = SyntaxTreeGenerator.Generate(compilationContent, assemblyName, compilationSettings); var references = _applicationReferences.Value; var compilationOptions = compilationSettings.CompilationOptions .WithOutputKind(OutputKind.DynamicallyLinkedLibrary); var compilation = CSharpCompilation.Create(assemblyName, options: compilationOptions, syntaxTrees: new[] { syntaxTree }, references: references); using (var ms = new MemoryStream()) { using (var pdb = new MemoryStream()) { EmitResult result; if (_supportsPdbGeneration.Value) { result = compilation.Emit(ms, pdbStream: pdb); } else { result = compilation.Emit(ms); } if (!result.Success) { return(GetCompilationFailedResult( fileInfo.RelativePath, compilationContent, assemblyName, result.Diagnostics)); } Assembly assembly; ms.Seek(0, SeekOrigin.Begin); if (_supportsPdbGeneration.Value) { pdb.Seek(0, SeekOrigin.Begin); assembly = _loader.LoadStream(ms, pdb); } else { assembly = _loader.LoadStream(ms, assemblySymbols: null); } var type = assembly.GetExportedTypes() .First(t => t.Name.StartsWith(_classPrefix, StringComparison.Ordinal)); return(UncachedCompilationResult.Successful(type, compilationContent)); } } }
private GetOrAddResult GetOrAddCore(string relativePath, Func <RelativeFileInfo, CompilationResult> compile) { var normalizedPath = NormalizePath(relativePath); var cacheEntry = _cache.Get <CompilerCacheEntry>(normalizedPath); if (cacheEntry == null) { var fileInfo = _fileProvider.GetFileInfo(relativePath); if (!fileInfo.Exists) { return(null); } var relativeFileInfo = new RelativeFileInfo(fileInfo, relativePath); return(OnCacheMiss(relativeFileInfo, normalizedPath, compile)); } else if (cacheEntry.IsPreCompiled && !cacheEntry.IsValidatedPreCompiled) { // For precompiled views, the first time the entry is read, we need to ensure that no changes were made // either to the file associated with this entry, or any _ViewImports associated with it between the time // the View was precompiled and the time EnsureInitialized was called. For later iterations, we can // rely on expiration triggers ensuring the validity of the entry. var fileInfo = _fileProvider.GetFileInfo(relativePath); if (!fileInfo.Exists) { return(null); } var relativeFileInfo = new RelativeFileInfo(fileInfo, relativePath); if (cacheEntry.Length != fileInfo.Length) { // Recompile if the file lengths differ return(OnCacheMiss(relativeFileInfo, normalizedPath, compile)); } if (AssociatedGlobalFilesChanged(cacheEntry, compile)) { // Recompile if _ViewImports have changed since the entry was created. return(OnCacheMiss(relativeFileInfo, normalizedPath, compile)); } if (cacheEntry.LastModified == fileInfo.LastModified) { // Assigning to IsValidatedPreCompiled is an atomic operation and will result in a safe race // if it is being concurrently updated and read. cacheEntry.IsValidatedPreCompiled = true; return(new GetOrAddResult { CompilationResult = CompilationResult.Successful(cacheEntry.CompiledType), CompilerCacheEntry = cacheEntry }); } // Timestamp doesn't match but it might be because of deployment, compare the hash. if (cacheEntry.IsPreCompiled && string.Equals(cacheEntry.Hash, RazorFileHash.GetHash(fileInfo, cacheEntry.HashAlgorithmVersion), StringComparison.Ordinal)) { // Cache hit, but we need to update the entry. // Assigning to LastModified and IsValidatedPreCompiled are atomic operations and will result in safe race // if the entry is being concurrently read or updated. cacheEntry.LastModified = fileInfo.LastModified; cacheEntry.IsValidatedPreCompiled = true; return(new GetOrAddResult { CompilationResult = CompilationResult.Successful(cacheEntry.CompiledType), CompilerCacheEntry = cacheEntry }); } // it's not a match, recompile return(OnCacheMiss(relativeFileInfo, normalizedPath, compile)); } return(new GetOrAddResult { CompilationResult = CompilationResult.Successful(cacheEntry.CompiledType), CompilerCacheEntry = cacheEntry }); }
private CompilerCacheResult CreateCacheEntry( string normalizedPath, Func<RelativeFileInfo, CompilationResult> compile) { CompilerCacheResult cacheResult; var fileInfo = _fileProvider.GetFileInfo(normalizedPath); MemoryCacheEntryOptions cacheEntryOptions; CompilerCacheResult cacheResultToCache; if (!fileInfo.Exists) { cacheResultToCache = CompilerCacheResult.FileNotFound; cacheResult = CompilerCacheResult.FileNotFound; cacheEntryOptions = new MemoryCacheEntryOptions(); cacheEntryOptions.AddExpirationToken(_fileProvider.Watch(normalizedPath)); } else { var relativeFileInfo = new RelativeFileInfo(fileInfo, normalizedPath); var compilationResult = compile(relativeFileInfo).EnsureSuccessful(); cacheEntryOptions = GetMemoryCacheEntryOptions(normalizedPath); // By default the CompilationResult returned by IRoslynCompiler is an instance of // UncachedCompilationResult. This type has the generated code as a string property and do not want // to cache it. We'll instead cache the unwrapped result. cacheResultToCache = new CompilerCacheResult( CompilationResult.Successful(compilationResult.CompiledType)); cacheResult = new CompilerCacheResult(compilationResult); } _cache.Set(normalizedPath, cacheResultToCache, cacheEntryOptions); return cacheResult; }
/// <inheritdoc /> public CompilationResult Compile(RelativeFileInfo fileInfo, string compilationContent) { if (fileInfo == null) { throw new ArgumentNullException(nameof(fileInfo)); } if (compilationContent == null) { throw new ArgumentNullException(nameof(compilationContent)); } _logger.GeneratedCodeToAssemblyCompilationStart(fileInfo.RelativePath); var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0; var assemblyName = Path.GetRandomFileName(); var syntaxTree = SyntaxTreeGenerator.Generate( compilationContent, assemblyName, _parseOptions); var references = _applicationReferences.Value; var compilation = CSharpCompilation.Create( assemblyName, options: _compilationOptions, syntaxTrees: new[] { syntaxTree }, references: references); compilation = Rewrite(compilation); var compilationContext = new RoslynCompilationContext(compilation); _compilationCallback(compilationContext); compilation = compilationContext.Compilation; using (var ms = new MemoryStream()) { using (var pdb = new MemoryStream()) { EmitResult result; if (_supportsPdbGeneration.Value) { result = compilation.Emit(ms, pdbStream: pdb); } else { result = compilation.Emit(ms); } if (!result.Success) { return(GetCompilationFailedResult( fileInfo.RelativePath, compilationContent, assemblyName, result.Diagnostics)); } Assembly assembly; ms.Seek(0, SeekOrigin.Begin); if (_supportsPdbGeneration.Value) { pdb.Seek(0, SeekOrigin.Begin); assembly = LoadStream(ms, pdb); } else { assembly = LoadStream(ms, assemblySymbols: null); } var type = assembly.GetExportedTypes() .First(t => t.Name.StartsWith(_classPrefix, StringComparison.Ordinal)); _logger.GeneratedCodeToAssemblyCompilationEnd(fileInfo.RelativePath, startTimestamp); return(new CompilationResult(type)); } } }
/// <inheritdoc /> public CompilationResult Compile(RelativeFileInfo fileInfo, string compilationContent) { if (fileInfo == null) { throw new ArgumentNullException(nameof(fileInfo)); } if (compilationContent == null) { throw new ArgumentNullException(nameof(compilationContent)); } _logger.GeneratedCodeToAssemblyCompilationStart(fileInfo.RelativePath); var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0; var assemblyName = Path.GetRandomFileName(); var syntaxTree = SyntaxTreeGenerator.Generate( compilationContent, assemblyName, _parseOptions); var references = _applicationReferences.Value; var compilation = CSharpCompilation.Create( assemblyName, options: _compilationOptions, syntaxTrees: new[] { syntaxTree }, references: references); compilation = Rewrite(compilation); var compilationContext = new RoslynCompilationContext(compilation); _compilationCallback(compilationContext); compilation = compilationContext.Compilation; using (var ms = new MemoryStream()) { using (var pdb = new MemoryStream()) { EmitResult result; if (_supportsPdbGeneration.Value) { result = compilation.Emit(ms, pdbStream: pdb); } else { result = compilation.Emit(ms); } if (!result.Success) { return GetCompilationFailedResult( fileInfo.RelativePath, compilationContent, assemblyName, result.Diagnostics); } Assembly assembly; ms.Seek(0, SeekOrigin.Begin); if (_supportsPdbGeneration.Value) { pdb.Seek(0, SeekOrigin.Begin); assembly = LoadStream(ms, pdb); } else { assembly = LoadStream(ms, assemblySymbols: null); } var type = assembly.GetExportedTypes() .First(t => t.Name.StartsWith(_classPrefix, StringComparison.Ordinal)); _logger.GeneratedCodeToAssemblyCompilationEnd(fileInfo.RelativePath, startTimestamp); return new CompilationResult(type); } } }
public void Compile_ReturnsGeneratedCodePath_IfLinePragmaIsNotAvailable() { // Arrange var fileContent = "file content"; var content = @"this should fail"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; var mvcRazorHost = Mock.Of<IMvcRazorHost>(); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost, GetOptions(), GetFileProviderAccessor(), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { Content = fileContent }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.IsType<CompilationResult>(result); Assert.Null(result.CompiledType); var compilationFailure = Assert.Single(result.CompilationFailures); Assert.Equal("Generated Code", compilationFailure.SourceFilePath); Assert.Equal(content, compilationFailure.SourceFileContent); }
private Task <CompilerCacheResult> CreateCacheEntry( string normalizedPath, Func <RelativeFileInfo, CompilationResult> compile) { TaskCompletionSource <CompilerCacheResult> compilationTaskSource = null; MemoryCacheEntryOptions cacheEntryOptions = null; IFileInfo fileInfo = null; Task <CompilerCacheResult> cacheEntry; // Safe races cannot be allowed when compiling Razor pages. To ensure only one compilation request succeeds // per file, we'll lock the creation of a cache entry. Creating the cache entry should be very quick. The // actual work for compiling files happens outside the critical section. lock (_cacheLock) { if (_cache.TryGetValue(normalizedPath, out cacheEntry)) { return(cacheEntry); } fileInfo = _fileProvider.GetFileInfo(normalizedPath); if (!fileInfo.Exists) { var expirationToken = _fileProvider.Watch(normalizedPath); cacheEntry = Task.FromResult(new CompilerCacheResult(new[] { expirationToken })); cacheEntryOptions = new MemoryCacheEntryOptions(); cacheEntryOptions.AddExpirationToken(expirationToken); } else { cacheEntryOptions = GetMemoryCacheEntryOptions(normalizedPath); // A file exists and needs to be compiled. compilationTaskSource = new TaskCompletionSource <CompilerCacheResult>(); cacheEntry = compilationTaskSource.Task; } cacheEntry = _cache.Set <Task <CompilerCacheResult> >(normalizedPath, cacheEntry, cacheEntryOptions); } if (compilationTaskSource != null) { // Indicates that the file was found and needs to be compiled. Debug.Assert(fileInfo != null && fileInfo.Exists); Debug.Assert(cacheEntryOptions != null); var relativeFileInfo = new RelativeFileInfo(fileInfo, normalizedPath); try { var compilationResult = compile(relativeFileInfo); compilationResult.EnsureSuccessful(); compilationTaskSource.SetResult( new CompilerCacheResult(compilationResult, cacheEntryOptions.ExpirationTokens)); } catch (Exception ex) { compilationTaskSource.SetException(ex); } } return(cacheEntry); }
private CompilationResult ThrowsIfCalled(RelativeFileInfo file) { throw new Exception("Shouldn't be called"); }
private Task<CompilerCacheResult> CreateCacheEntry( string normalizedPath, Func<RelativeFileInfo, CompilationResult> compile) { TaskCompletionSource<CompilerCacheResult> compilationTaskSource = null; MemoryCacheEntryOptions cacheEntryOptions = null; IFileInfo fileInfo = null; Task<CompilerCacheResult> cacheEntry; // Safe races cannot be allowed when compiling Razor pages. To ensure only one compilation request succeeds // per file, we'll lock the creation of a cache entry. Creating the cache entry should be very quick. The // actual work for compiling files happens outside the critical section. lock (_cacheLock) { if (_cache.TryGetValue(normalizedPath, out cacheEntry)) { return cacheEntry; } fileInfo = _fileProvider.GetFileInfo(normalizedPath); if (!fileInfo.Exists) { var expirationToken = _fileProvider.Watch(normalizedPath); cacheEntry = Task.FromResult(new CompilerCacheResult(new[] { expirationToken })); cacheEntryOptions = new MemoryCacheEntryOptions(); cacheEntryOptions.AddExpirationToken(expirationToken); } else { cacheEntryOptions = GetMemoryCacheEntryOptions(normalizedPath); // A file exists and needs to be compiled. compilationTaskSource = new TaskCompletionSource<CompilerCacheResult>(); cacheEntry = compilationTaskSource.Task; } cacheEntry = _cache.Set<Task<CompilerCacheResult>>(normalizedPath, cacheEntry, cacheEntryOptions); } if (compilationTaskSource != null) { // Indicates that the file was found and needs to be compiled. Debug.Assert(fileInfo != null && fileInfo.Exists); Debug.Assert(cacheEntryOptions != null); var relativeFileInfo = new RelativeFileInfo(fileInfo, normalizedPath); try { var compilationResult = compile(relativeFileInfo); compilationResult.EnsureSuccessful(); compilationTaskSource.SetResult( new CompilerCacheResult(compilationResult, cacheEntryOptions.ExpirationTokens)); } catch (Exception ex) { compilationTaskSource.SetException(ex); } } return cacheEntry; }
public void Compile_UsesApplicationsCompilationSettings_ForParsingAndCompilation() { // Arrange var content = @" #if MY_CUSTOM_DEFINE public class MyCustomDefinedClass {} #else public class MyNonCustomDefinedClass {} #endif "; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; var mvcRazorHost = new Mock<IMvcRazorHost>(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns("My"); var options = GetOptions(); options.Value.ParseOptions = options.Value.ParseOptions.WithPreprocessorSymbols("MY_CUSTOM_DEFINE"); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, mvcRazorHost.Object, options, GetFileProviderAccessor(), NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.NotNull(result.CompiledType); Assert.Equal("MyCustomDefinedClass", result.CompiledType.Name); }
private GetOrAddResult GetOrAddCore(string relativePath, Func<RelativeFileInfo, CompilationResult> compile) { var normalizedPath = NormalizePath(relativePath); var cacheEntry = _cache.Get<CompilerCacheEntry>(normalizedPath); if (cacheEntry == null) { var fileInfo = _fileProvider.GetFileInfo(relativePath); if (!fileInfo.Exists) { return null; } var relativeFileInfo = new RelativeFileInfo(fileInfo, relativePath); return OnCacheMiss(relativeFileInfo, normalizedPath, compile); } else if (cacheEntry.IsPreCompiled && !cacheEntry.IsValidatedPreCompiled) { // For precompiled views, the first time the entry is read, we need to ensure that no changes were made // either to the file associated with this entry, or any _ViewImports associated with it between the time // the View was precompiled and the time EnsureInitialized was called. For later iterations, we can // rely on expiration triggers ensuring the validity of the entry. var fileInfo = _fileProvider.GetFileInfo(relativePath); if (!fileInfo.Exists) { return null; } var relativeFileInfo = new RelativeFileInfo(fileInfo, relativePath); if (cacheEntry.Length != fileInfo.Length) { // Recompile if the file lengths differ return OnCacheMiss(relativeFileInfo, normalizedPath, compile); } if (AssociatedGlobalFilesChanged(cacheEntry, compile)) { // Recompile if _ViewImports have changed since the entry was created. return OnCacheMiss(relativeFileInfo, normalizedPath, compile); } if (cacheEntry.LastModified == fileInfo.LastModified) { // Assigning to IsValidatedPreCompiled is an atomic operation and will result in a safe race // if it is being concurrently updated and read. cacheEntry.IsValidatedPreCompiled = true; return new GetOrAddResult { CompilationResult = CompilationResult.Successful(cacheEntry.CompiledType), CompilerCacheEntry = cacheEntry }; } // Timestamp doesn't match but it might be because of deployment, compare the hash. if (cacheEntry.IsPreCompiled && string.Equals(cacheEntry.Hash, RazorFileHash.GetHash(fileInfo, cacheEntry.HashAlgorithmVersion), StringComparison.Ordinal)) { // Cache hit, but we need to update the entry. // Assigning to LastModified and IsValidatedPreCompiled are atomic operations and will result in safe race // if the entry is being concurrently read or updated. cacheEntry.LastModified = fileInfo.LastModified; cacheEntry.IsValidatedPreCompiled = true; return new GetOrAddResult { CompilationResult = CompilationResult.Successful(cacheEntry.CompiledType), CompilerCacheEntry = cacheEntry }; } // it's not a match, recompile return OnCacheMiss(relativeFileInfo, normalizedPath, compile); } return new GetOrAddResult { CompilationResult = CompilationResult.Successful(cacheEntry.CompiledType), CompilerCacheEntry = cacheEntry }; }
public void GetCompilationFailedResult_ReturnsCompilationResult_WithGroupedMessages() { // Arrange var viewPath = @"views/index.razor"; var viewImportsPath = @"views/global.import.cshtml"; var host = Mock.Of<IMvcRazorHost>(); var fileProvider = new TestFileProvider(); var file = fileProvider.AddFile(viewPath, "View Content"); fileProvider.AddFile(viewImportsPath, "Global Import Content"); var relativeFileInfo = new RelativeFileInfo(file, viewPath); var razorService = new RazorCompilationService( Mock.Of<ICompilationService>(), Mock.Of<IMvcRazorHost>(), GetOptions(fileProvider)); var errors = new[] { new RazorError("message-1", new SourceLocation(1, 2, 17)), new RazorError("message-2", new SourceLocation(viewPath, 1, 4, 6), 7), new RazorError { Message = "message-3" }, new RazorError("message-4", new SourceLocation(viewImportsPath, 1, 3, 8), 4), }; // Act var result = razorService.GetCompilationFailedResult(relativeFileInfo, errors); // Assert Assert.NotNull(result.CompilationFailures); Assert.Collection(result.CompilationFailures, failure => { Assert.Equal(viewPath, failure.SourceFilePath); Assert.Equal("View Content", failure.SourceFileContent); Assert.Collection(failure.Messages, message => { Assert.Equal(errors[0].Message, message.Message); Assert.Equal(viewPath, message.SourceFilePath); Assert.Equal(3, message.StartLine); Assert.Equal(17, message.StartColumn); Assert.Equal(3, message.EndLine); Assert.Equal(18, message.EndColumn); }, message => { Assert.Equal(errors[1].Message, message.Message); Assert.Equal(viewPath, message.SourceFilePath); Assert.Equal(5, message.StartLine); Assert.Equal(6, message.StartColumn); Assert.Equal(5, message.EndLine); Assert.Equal(13, message.EndColumn); }, message => { Assert.Equal(errors[2].Message, message.Message); Assert.Equal(viewPath, message.SourceFilePath); Assert.Equal(0, message.StartLine); Assert.Equal(-1, message.StartColumn); Assert.Equal(0, message.EndLine); Assert.Equal(0, message.EndColumn); }); }, failure => { Assert.Equal(viewImportsPath, failure.SourceFilePath); Assert.Equal("Global Import Content", failure.SourceFileContent); Assert.Collection(failure.Messages, message => { Assert.Equal(errors[3].Message, message.Message); Assert.Equal(viewImportsPath, message.SourceFilePath); Assert.Equal(4, message.StartLine); Assert.Equal(8, message.StartColumn); Assert.Equal(4, message.EndLine); Assert.Equal(12, message.EndColumn); }); }); }
public void Compile_ReturnsResultFromCompilationServiceIfParseSucceeds() { // Arrange var code = "compiled-content"; var generatorResult = new GeneratorResults( new Block(new BlockBuilder { Type = BlockType.Comment }), Enumerable.Empty<TagHelperDescriptor>(), new ErrorSink(), new CodeGeneratorResult(code, new LineMapping[0]), new ChunkTree()); var host = new Mock<IMvcRazorHost>(); host.Setup(h => h.GenerateCode(It.IsAny<string>(), It.IsAny<Stream>())) .Returns(generatorResult); var fileInfo = new Mock<IFileInfo>(); fileInfo.Setup(f => f.CreateReadStream()) .Returns(Stream.Null); var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml"); var compilationResult = CompilationResult.Successful(typeof(object)); var compiler = new Mock<ICompilationService>(); compiler.Setup(c => c.Compile(relativeFileInfo, code)) .Returns(compilationResult) .Verifiable(); var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions()); // Act var result = razorService.Compile(relativeFileInfo); // Assert Assert.Same(compilationResult, result); compiler.Verify(); }