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(new CompilationResult(typeof(RazorCompilationServiceTest))); var razorService = new RazorCompilationService(compiler.Object, host.Object, GetFileProviderAccessor(), NullLoggerFactory.Instance); // 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); }
public void Compile_ReturnsCompilationFailureWithPathsFromLinePragmas() { // Arrange var viewPath = "some-relative-path"; var fileContent = "test file content"; var content = $@" #line 1 ""{viewPath}"" this should fail"; var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(viewPath, fileContent); var compilationService = new DefaultRoslynCompilationService( GetDependencyContext(), 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_ReturnsCompilationResult() { // Arrange var content = @" public class MyTestType {}"; var compilationService = new DefaultRoslynCompilationService( GetDependencyContext(), 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 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, GetFileProviderAccessor(), NullLoggerFactory.Instance); // 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(); }
public void Compile_DoesNotThrowIfReferencesWereClearedInCallback() { // Arrange var options = GetOptions(context => { context.Compilation = context.Compilation.RemoveAllReferences(); }); var content = "public class MyTestType {}"; var compilationService = new DefaultRoslynCompilationService( dependencyContext: GetDependencyContext(), viewEngineOptions: options, fileProviderAccessor: GetFileProviderAccessor(), loggerFactory: NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path.cshtml"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.Single(result.CompilationFailures); }
public void Compile_ThrowsIfDependencyContextReturnsNoReferencesAndTheApplicationFailsToCompile() { // Arrange var content = "public class MyTestType {}"; var dependencyContext = new DependencyContext( new TargetInfo("framework", "runtime", "signature", isPortable: true), Extensions.DependencyModel.CompilationOptions.Default, new CompilationLibrary[0], new RuntimeLibrary[0], Enumerable.Empty<RuntimeFallbacks>()); var compilationService = new DefaultRoslynCompilationService( dependencyContext: dependencyContext, viewEngineOptions: GetOptions(), fileProviderAccessor: GetFileProviderAccessor(), loggerFactory: NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path.cshtml"); var expected = "The Razor page 'some-relative-path.cshtml' failed to compile. Ensure that your " + "application's project.json sets the 'preserveCompilationContext' compilation property."; // Act and Assert var ex = Assert.Throws<InvalidOperationException>(() => compilationService.Compile(relativeFileInfo, content)); Assert.Equal(expected, ex.Message); }
public void Compile_ThrowsIfDependencyContextIsNullAndTheApplicationFailsToCompileWithNoReferences() { // Arrange var content = "public class MyTestType {}"; var compilationService = new DefaultRoslynCompilationService( dependencyContext: null, viewEngineOptions: GetOptions(), fileProviderAccessor: GetFileProviderAccessor(), loggerFactory: NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path.cshtml"); var expected = "The Razor page 'some-relative-path.cshtml' failed to compile. Ensure that your " + "application's project.json sets the 'preserveCompilationContext' compilation property."; // Act and Assert var ex = Assert.Throws<InvalidOperationException>(() => compilationService.Compile(relativeFileInfo, content)); Assert.Equal(expected, ex.Message); }
public void Compile_RunsCallback() { // Arrange var content = "public class MyTestType {}"; RoslynCompilationContext usedCompilation = null; var compilationService = new DefaultRoslynCompilationService( GetDependencyContext(), 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.Single(usedCompilation.Compilation.SyntaxTrees); }
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 = new CompilationResult(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, GetFileProviderAccessor(), NullLoggerFactory.Instance); // Act var result = razorService.Compile(relativeFileInfo); // Assert Assert.Same(compilationResult.CompiledType, result.CompiledType); compiler.Verify(); }
public void Compile_ReturnsGeneratedCodePath_IfLinePragmaIsNotAvailable() { // Arrange var fileContent = "file content"; var content = @"this should fail"; var compilationService = new DefaultRoslynCompilationService( GetDependencyContext(), 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); }
/// <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 sourceText = SourceText.From(compilationContent, Encoding.UTF8); var syntaxTree = CSharpSyntaxTree.ParseText( sourceText, path: assemblyName, options: _parseOptions); var compilation = CSharpCompilation.Create( assemblyName, options: _compilationOptions, syntaxTrees: new[] { syntaxTree }, references: _applicationReferences.Value); compilation = Rewrite(compilation); var compilationContext = new RoslynCompilationContext(compilation); _compilationCallback(compilationContext); compilation = compilationContext.Compilation; using (var assemblyStream = new MemoryStream()) { using (var pdbStream = new MemoryStream()) { var result = compilation.Emit( assemblyStream, pdbStream, options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb)); if (!result.Success) { if (!compilation.References.Any() && !_applicationReferences.Value.Any()) { // DependencyModel had no references specified and the user did not use the // CompilationCallback to add extra references. It is likely that the user did not specify // preserveCompilationContext in the app's project.json. throw new InvalidOperationException( Resources.FormatCompilation_DependencyContextIsNotSpecified( fileInfo.RelativePath, "project.json", "preserveCompilationContext")); } return GetCompilationFailedResult( fileInfo.RelativePath, compilationContent, assemblyName, result.Diagnostics); } assemblyStream.Seek(0, SeekOrigin.Begin); pdbStream.Seek(0, SeekOrigin.Begin); var assembly = LoadStream(assemblyStream, pdbStream); var type = assembly.GetExportedTypes().FirstOrDefault(a => !a.IsNested); _logger.GeneratedCodeToAssemblyCompilationEnd(fileInfo.RelativePath, startTimestamp); return new CompilationResult(type); } } }
// 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 CompilationResult ThrowsIfCalled(RelativeFileInfo file) { throw new Exception("Shouldn't be called"); }
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>(), GetFileProviderAccessor(fileProvider), NullLoggerFactory.Instance); 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_SucceedsIfReferencesAreAddedInCallback() { // Arrange var options = GetOptions(context => { var assemblyLocation = typeof(object).GetTypeInfo().Assembly.Location; context.Compilation = context .Compilation .AddReferences(MetadataReference.CreateFromFile(assemblyLocation)); }); var content = "public class MyTestType {}"; var compilationService = new DefaultRoslynCompilationService( dependencyContext: null, viewEngineOptions: options, fileProviderAccessor: GetFileProviderAccessor(), loggerFactory: NullLoggerFactory.Instance); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path.cshtml"); // Act var result = compilationService.Compile(relativeFileInfo, content); // Assert Assert.Null(result.CompilationFailures); Assert.NotNull(result.CompiledType); }
public void Compile_DoesNotThrow_IfFileCannotBeRead() { // Arrange var path = "some-relative-path"; var content = $@" #line 1 ""{path}"" this should fail"; 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 DefaultRoslynCompilationService( GetDependencyContext(), GetOptions(), GetFileProviderAccessor(), 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_UsesApplicationsCompilationSettings_ForParsingAndCompilation() { // Arrange var content = @" #if MY_CUSTOM_DEFINE public class MyCustomDefinedClass {} #else public class MyNonCustomDefinedClass {} #endif "; var options = GetOptions(); options.ParseOptions = options.ParseOptions.WithPreprocessorSymbols("MY_CUSTOM_DEFINE"); var compilationService = new DefaultRoslynCompilationService( GetDependencyContext(), 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); }
CompilationResult ICompilationService.Compile(RelativeFileInfo fileInfo, string compilationContent) { return base.Compile(fileInfo, compilationContent); }
private Task<CompilerCacheResult> CreateCacheEntry( string relativePath, 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(relativePath, compilationResult, cacheEntryOptions.ExpirationTokens)); } catch (Exception ex) { compilationTaskSource.SetException(ex); } } return cacheEntry; }