コード例 #1
0
        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();
        }
コード例 #2
0
        /// <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);
        }
コード例 #3
0
        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);
        }
コード例 #4
0
        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);
        }
コード例 #5
0
        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();
        }
コード例 #6
0
        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);
        }
コード例 #7
0
        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);
        }
コード例 #8
0
        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);
        }
コード例 #9
0
        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);
        }
コード例 #10
0
        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();
        }
コード例 #11
0
        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);
        }
コード例 #12
0
        /// <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);
                }
            }
        }
コード例 #13
0
        // 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);
        }
コード例 #14
0
ファイル: CompilerCacheTest.cs プロジェクト: xuchrist/Mvc
 private CompilationResult ThrowsIfCalled(RelativeFileInfo file)
 {
     throw new Exception("Shouldn't be called");
 }
コード例 #15
0
        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);
                        });
                });
        }
コード例 #16
0
        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);
        }
コード例 #17
0
        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);
        }
コード例 #18
0
        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);
        }
コード例 #19
0
 CompilationResult ICompilationService.Compile(RelativeFileInfo fileInfo, 
     string compilationContent)
 {
     return base.Compile(fileInfo, compilationContent);
 }
コード例 #20
0
ファイル: CompilerCache.cs プロジェクト: ymd1223/Mvc
        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;
        }