Beispiel #1
0
        [InlineData(10000)] // expected failure: same time and length
        public void GetOrAdd_UsesFilesFromCache_IfTimestampDiffers_ButContentAndLengthAreTheSame(long fileTimeUTC)
        {
            // Arrange
            var instance   = new RuntimeCompileIdentical();
            var length     = Encoding.UTF8.GetByteCount(instance.Content);
            var collection = new ViewCollection();
            var fileSystem = new TestFileSystem();
            var cache      = new CompilerCache(new[] { new ViewCollection() }, fileSystem);

            var fileInfo = new TestFileInfo
            {
                Length       = length,
                LastModified = DateTime.FromFileTimeUtc(fileTimeUTC),
                Content      = instance.Content
            };

            var runtimeFileInfo = new RelativeFileInfo(fileInfo, "ab");

            var precompiledContent = new PreCompile().Content;
            var razorFileInfo      = new RazorFileInfo
            {
                FullTypeName = typeof(PreCompile).FullName,
                Hash         = RazorFileHash.GetHash(GetMemoryStream(precompiledContent)),
                LastModified = DateTime.FromFileTimeUtc(10000),
                Length       = Encoding.UTF8.GetByteCount(precompiledContent),
                RelativePath = "ab",
            };

            // Act
            var actual = cache.GetOrAdd(runtimeFileInfo,
                                        compile: _ => { throw new Exception("Shouldn't be called."); });

            // Assert
            Assert.Equal(typeof(PreCompile), actual.CompiledType);
        }
        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);
            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);
        }
Beispiel #3
0
        private IEnumerable <RelativeFileInfo> GetFileInfosRecursive(string currentPath)
        {
            IEnumerable <IFileInfo> fileInfos;
            string path = currentPath;

            if (!_fileSystem.TryGetDirectoryContents(path, out fileInfos))
            {
                yield break;
            }

            foreach (var fileInfo in fileInfos)
            {
                if (fileInfo.IsDirectory)
                {
                    var subPath = Path.Combine(path, fileInfo.Name);

                    foreach (var info in GetFileInfosRecursive(subPath))
                    {
                        yield return(info);
                    }
                }
                else if (Path.GetExtension(fileInfo.Name)
                         .Equals(FileExtension, StringComparison.OrdinalIgnoreCase))
                {
                    var info = new RelativeFileInfo()
                    {
                        FileInfo     = fileInfo,
                        RelativePath = Path.Combine(currentPath, fileInfo.Name),
                    };

                    yield return(info);
                }
            }
        }
Beispiel #4
0
        public void GetOrAdd_DoesNotCacheCompiledContent_OnCallsAfterInitial()
        {
            // Arrange
            var lastModified = DateTime.UtcNow;
            var cache        = new CompilerCache(Enumerable.Empty <RazorFileInfoCollection>(), new TestFileSystem());
            var fileInfo     = new TestFileInfo
            {
                PhysicalPath = "test",
                LastModified = lastModified
            };
            var type           = GetType();
            var uncachedResult = UncachedCompilationResult.Successful(type, "hello world");

            var runtimeFileInfo = new RelativeFileInfo(fileInfo, "test");

            // Act
            cache.GetOrAdd(runtimeFileInfo, _ => uncachedResult);
            var actual1 = cache.GetOrAdd(runtimeFileInfo, _ => uncachedResult);
            var actual2 = cache.GetOrAdd(runtimeFileInfo, _ => uncachedResult);

            // Assert
            Assert.NotSame(uncachedResult, actual1);
            Assert.NotSame(uncachedResult, actual2);
            var result = Assert.IsType <CompilationResult>(actual1);

            Assert.Null(actual1.CompiledContent);
            Assert.Same(type, actual1.CompiledType);

            result = Assert.IsType <CompilationResult>(actual2);
            Assert.Null(actual2.CompiledContent);
            Assert.Same(type, actual2.CompiledType);
        }
 /// <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;
 }
        /// <inheritdoc />
        public IRazorPage CreateInstance([NotNull] string relativePath)
        {
            if (relativePath.StartsWith("~/", StringComparison.Ordinal))
            {
                // For tilde slash paths, drop the leading ~ to make it work with the underlying IFileSystem.
                relativePath = relativePath.Substring(1);
            }

            var fileInfo = _fileSystemCache.GetFileInfo(relativePath);

            if (fileInfo.Exists)
            {
                var relativeFileInfo = new RelativeFileInfo(fileInfo, relativePath);

                var result = _compilerCache.GetOrAdd(
                    relativeFileInfo,
                    RazorCompilationService.Compile);

                var page = (IRazorPage)_activator.CreateInstance(_serviceProvider, result.CompiledType);
                page.Path = relativePath;

                return(page);
            }

            return(null);
        }
Beispiel #7
0
        /// <inheritdoc />
        public CompilationResult GetOrAdd([NotNull] RelativeFileInfo fileInfo,
                                          [NotNull] Func <RelativeFileInfo, CompilationResult> compile)
        {
            CompilationResult result;
            var entry = GetOrAdd(fileInfo, compile, out result);

            return(result);
        }
Beispiel #8
0
        public void GetOrAdd_IgnoresCachedValueIfFileIsIdentical_ButViewStartWasDeletedSinceCacheWasCreated()
        {
            // Arrange
            var expectedType = typeof(RuntimeCompileDifferent);
            var lastModified = DateTime.UtcNow;
            var fileSystem   = new TestFileSystem();

            var viewCollection  = new ViewCollection();
            var precompiledView = viewCollection.FileInfos[0];

            precompiledView.RelativePath = "Views\\Index.cshtml";
            var viewFileInfo = new TestFileInfo
            {
                Content      = new PreCompile().Content,
                LastModified = precompiledView.LastModified,
                PhysicalPath = precompiledView.RelativePath
            };

            fileSystem.AddFile(viewFileInfo.PhysicalPath, viewFileInfo);

            var viewStartFileInfo = new TestFileInfo
            {
                PhysicalPath = "Views\\_ViewStart.cshtml",
                Content      = "viewstart-content",
                LastModified = lastModified
            };
            var viewStart = new RazorFileInfo
            {
                FullTypeName = typeof(RuntimeCompileIdentical).FullName,
                RelativePath = viewStartFileInfo.PhysicalPath,
                LastModified = viewStartFileInfo.LastModified,
                Hash         = RazorFileHash.GetHash(viewStartFileInfo),
                Length       = viewStartFileInfo.Length
            };

            fileSystem.AddFile(viewStartFileInfo.PhysicalPath, viewStartFileInfo);

            viewCollection.Add(viewStart);
            var cache    = new CompilerCache(new[] { viewCollection }, fileSystem);
            var fileInfo = new RelativeFileInfo(viewFileInfo, viewFileInfo.PhysicalPath);

            // Act 1
            var actual1 = cache.GetOrAdd(fileInfo,
                                         compile: _ => { throw new Exception("should not be called"); });

            // Assert 1
            Assert.Equal(typeof(PreCompile), actual1.CompiledType);

            // Act 2
            fileSystem.DeleteFile(viewStartFileInfo.PhysicalPath);
            var actual2 = cache.GetOrAdd(fileInfo,
                                         compile: _ => CompilationResult.Successful(expectedType));

            // Assert 2
            Assert.Equal(expectedType, actual2.CompiledType);
        }
Beispiel #9
0
        private CompilationResult OnCacheMiss(RelativeFileInfo file,
                                              bool isInstrumented,
                                              Func <CompilationResult> compile)
        {
            var result = compile();

            var cacheEntry = new CompilerCacheEntry(file, result.CompiledType, isInstrumented);

            _cache[NormalizePath(file.RelativePath)] = cacheEntry;

            return(result);
        }
Beispiel #10
0
        private CompilerCacheEntry GetOrAdd(RelativeFileInfo relativeFileInfo,
                                            Func <RelativeFileInfo, CompilationResult> compile,
                                            out CompilationResult result)
        {
            CompilerCacheEntry cacheEntry;
            var normalizedPath = NormalizePath(relativeFileInfo.RelativePath);

            if (!_cache.TryGetValue(normalizedPath, out cacheEntry))
            {
                return(OnCacheMiss(relativeFileInfo, normalizedPath, compile, out result));
            }
            else
            {
                var fileInfo = relativeFileInfo.FileInfo;
                if (cacheEntry.Length != fileInfo.Length)
                {
                    // Recompile if the file lengths differ
                    return(OnCacheMiss(relativeFileInfo, normalizedPath, compile, out result));
                }

                if (AssociatedViewStartsChanged(cacheEntry, compile))
                {
                    // Recompile if the view starts have changed since the entry was created.
                    return(OnCacheMiss(relativeFileInfo, normalizedPath, compile, out result));
                }

                if (cacheEntry.LastModified == fileInfo.LastModified)
                {
                    result = CompilationResult.Successful(cacheEntry.CompiledType);
                    return(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), StringComparison.Ordinal))
                {
                    // Cache hit, but we need to update the entry.
                    // Assigning to LastModified is an atomic operation and will result in a safe race if it is
                    // being concurrently read and written or updated concurrently.
                    cacheEntry.LastModified = fileInfo.LastModified;
                    result = CompilationResult.Successful(cacheEntry.CompiledType);

                    return(cacheEntry);
                }

                // it's not a match, recompile
                return(OnCacheMiss(relativeFileInfo, normalizedPath, compile, out result));
            }
        }
Beispiel #11
0
        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 cacheEntry = _cache.Set(normalizedPath,
                                        new CompilerCacheEntry(file, compilationResult.CompiledType),
                                        PopulateCacheSetContext);

            return(new GetOrAddResult
            {
                CompilationResult = compilationResult,
                CompilerCacheEntry = cacheEntry
            });
        }
Beispiel #12
0
        public void GetOrAdd_UsesValueFromCache_IfViewStartHasNotChanged()
        {
            // Arrange
            var instance   = (View)Activator.CreateInstance(typeof(PreCompile));
            var length     = Encoding.UTF8.GetByteCount(instance.Content);
            var fileSystem = new TestFileSystem();

            var lastModified = DateTime.UtcNow;

            var fileInfo = new TestFileInfo
            {
                Length       = length,
                LastModified = lastModified,
                Content      = instance.Content
            };
            var runtimeFileInfo = new RelativeFileInfo(fileInfo, "ab");

            var viewStartContent  = "viewstart-content";
            var viewStartFileInfo = new TestFileInfo
            {
                Content      = viewStartContent,
                LastModified = DateTime.UtcNow
            };

            fileSystem.AddFile("_ViewStart.cshtml", viewStartFileInfo);
            var viewStartRazorFileInfo = new RazorFileInfo
            {
                Hash         = RazorFileHash.GetHash(GetMemoryStream(viewStartContent)),
                LastModified = viewStartFileInfo.LastModified,
                Length       = viewStartFileInfo.Length,
                RelativePath = "_ViewStart.cshtml",
                FullTypeName = typeof(RuntimeCompileIdentical).FullName
            };

            var precompiledViews = new ViewCollection();

            precompiledViews.Add(viewStartRazorFileInfo);
            var cache = new CompilerCache(new[] { precompiledViews }, fileSystem);

            // Act
            var actual = cache.GetOrAdd(runtimeFileInfo,
                                        compile: _ => { throw new Exception("shouldn't be invoked"); });

            // Assert
            Assert.Equal(typeof(PreCompile), actual.CompiledType);
        }
Beispiel #13
0
        private CompilerCacheEntry OnCacheMiss(RelativeFileInfo file,
                                               string normalizedPath,
                                               Func <RelativeFileInfo, CompilationResult> compile,
                                               out CompilationResult result)
        {
            result = compile(file);

            var cacheEntry = new CompilerCacheEntry(file, result.CompiledType)
            {
                AssociatedViewStartEntry = GetCompositeViewStartEntry(normalizedPath, compile)
            };

            // The cache is a concurrent dictionary, so concurrent addition to it with the same key would result in a
            // safe race.
            _cache[normalizedPath] = cacheEntry;
            return(cacheEntry);
        }
Beispiel #14
0
        public void GetOrAdd_IgnoresCachedValueIfFileIsIdentical_ButGlobalImportWasAdedSinceTheCacheWasCreated()
        {
            // 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\\_GlobalImport.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);
        }
Beispiel #15
0
        /// <inheritdoc />
        public CompilationResult Compile([NotNull] RelativeFileInfo file)
        {
            GeneratorResults results;

            using (var inputStream = file.FileInfo.CreateReadStream())
            {
                results = _razorHost.GenerateCode(
                    file.RelativePath, inputStream);
            }

            if (!results.Success)
            {
                var messages = results.ParserErrors.Select(e => new CompilationMessage(e.Message));
                return(CompilationResult.Failed(file.FileInfo, results.GeneratedCode, messages));
            }

            return(_compilationService.Compile(file.FileInfo, results.GeneratedCode));
        }
        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);
            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);
        }
Beispiel #17
0
        protected virtual PrecompilationCacheEntry GetCacheEntry([NotNull] RelativeFileInfo fileInfo)
        {
            using (var stream = fileInfo.FileInfo.CreateReadStream())
            {
                var host    = GetRazorHost();
                var results = host.GenerateCode(fileInfo.RelativePath, stream);

                if (results.Success)
                {
                    var syntaxTree = SyntaxTreeGenerator.Generate(results.GeneratedCode,
                                                                  fileInfo.FileInfo.PhysicalPath,
                                                                  CompilationSettings);
                    var fullTypeName = results.GetMainClassName(host, syntaxTree);

                    if (fullTypeName != null)
                    {
                        var hashAlgorithmVersion = RazorFileHash.HashAlgorithmVersion1;
                        var hash          = RazorFileHash.GetHash(fileInfo.FileInfo, hashAlgorithmVersion);
                        var razorFileInfo = new RazorFileInfo
                        {
                            RelativePath         = fileInfo.RelativePath,
                            LastModified         = fileInfo.FileInfo.LastModified,
                            Length               = fileInfo.FileInfo.Length,
                            FullTypeName         = fullTypeName,
                            Hash                 = hash,
                            HashAlgorithmVersion = hashAlgorithmVersion
                        };

                        return(new PrecompilationCacheEntry(razorFileInfo, syntaxTree));
                    }
                }
                else
                {
                    var diagnostics = results.ParserErrors
                                      .Select(error => error.ToDiagnostics(fileInfo.FileInfo.PhysicalPath))
                                      .ToList();

                    return(new PrecompilationCacheEntry(diagnostics));
                }
            }

            return(null);
        }
Beispiel #18
0
        // Returns the entry for the nearest _ViewStart that the file inherits directives from. Since _ViewStart
        // entries are affected by other _ViewStart entries that are in the path hierarchy, the returned value
        // represents the composite result of performing a cache check on individual _ViewStart entries.
        private CompilerCacheEntry GetCompositeViewStartEntry(string relativePath,
                                                              Func <RelativeFileInfo, CompilationResult> compile)
        {
            var viewStartLocations = ViewStartUtility.GetViewStartLocations(relativePath);

            foreach (var viewStartLocation in viewStartLocations)
            {
                var viewStartFileInfo = _fileProvider.GetFileInfo(viewStartLocation);
                if (viewStartFileInfo.Exists)
                {
                    var relativeFileInfo = new RelativeFileInfo(viewStartFileInfo, viewStartLocation);
                    CompilationResult result;
                    return(GetOrAdd(relativeFileInfo, compile, out result));
                }
            }

            // No _ViewStarts discovered.
            return(null);
        }
Beispiel #19
0
        /// <inheritdoc />
        public CompilationResult GetOrAdd([NotNull] RelativeFileInfo fileInfo,
                                          bool enableInstrumentation,
                                          [NotNull] Func <CompilationResult> compile)
        {
            CompilerCacheEntry cacheEntry;

            if (!_cache.TryGetValue(NormalizePath(fileInfo.RelativePath), out cacheEntry))
            {
                return(OnCacheMiss(fileInfo, enableInstrumentation, compile));
            }
            else
            {
                if ((cacheEntry.Length != fileInfo.FileInfo.Length) ||
                    (enableInstrumentation && !cacheEntry.IsInstrumented))
                {
                    // Recompile if
                    // (a) If the file lengths differ
                    // (b) If the compiled type is not instrumented but we require it to be instrumented.
                    return(OnCacheMiss(fileInfo, enableInstrumentation, compile));
                }

                if (cacheEntry.LastModified == fileInfo.FileInfo.LastModified)
                {
                    // Match, not update needed
                    return(CompilationResult.Successful(cacheEntry.CompiledType));
                }

                var hash = RazorFileHash.GetHash(fileInfo.FileInfo);

                // Timestamp doesn't match but it might be because of deployment, compare the hash.
                if (cacheEntry.IsPreCompiled &&
                    string.Equals(cacheEntry.Hash, hash, StringComparison.Ordinal))
                {
                    // Cache hit, but we need to update the entry
                    return(OnCacheMiss(fileInfo,
                                       enableInstrumentation,
                                       () => CompilationResult.Successful(cacheEntry.CompiledType)));
                }

                // it's not a match, recompile
                return(OnCacheMiss(fileInfo, enableInstrumentation, compile));
            }
        }
Beispiel #20
0
        private void GetFileInfosRecursive(string root, List <RelativeFileInfo> razorFiles)
        {
            var fileInfos = _fileProvider.GetDirectoryContents(root);

            foreach (var fileInfo in fileInfos)
            {
                if (fileInfo.IsDirectory)
                {
                    var subPath = Path.Combine(root, fileInfo.Name);
                    GetFileInfosRecursive(subPath, razorFiles);
                }
                else if (Path.GetExtension(fileInfo.Name)
                         .Equals(FileExtension, StringComparison.OrdinalIgnoreCase))
                {
                    var relativePath = Path.Combine(root, fileInfo.Name);
                    var info         = new RelativeFileInfo(fileInfo, relativePath);
                    razorFiles.Add(info);
                }
            }
        }
Beispiel #21
0
        protected virtual RazorFileInfo ParseView([NotNull] RelativeFileInfo fileInfo,
                                                  [NotNull] IBeforeCompileContext context,
                                                  [NotNull] CSharpParseOptions options)
        {
            using (var stream = fileInfo.FileInfo.CreateReadStream())
            {
                var results = _host.GenerateCode(fileInfo.RelativePath, stream);

                foreach (var parserError in results.ParserErrors)
                {
                    var diagnostic = parserError.ToDiagnostics(fileInfo.FileInfo.PhysicalPath);
                    context.Diagnostics.Add(diagnostic);
                }

                var generatedCode = results.GeneratedCode;

                if (generatedCode != null)
                {
                    var syntaxTree   = SyntaxTreeGenerator.Generate(generatedCode, fileInfo.FileInfo.PhysicalPath, options);
                    var fullTypeName = results.GetMainClassName(_host, syntaxTree);

                    if (fullTypeName != null)
                    {
                        context.CSharpCompilation = context.CSharpCompilation.AddSyntaxTrees(syntaxTree);

                        var hash = RazorFileHash.GetHash(fileInfo.FileInfo);

                        return(new RazorFileInfo()
                        {
                            FullTypeName = fullTypeName,
                            RelativePath = fileInfo.RelativePath,
                            LastModified = fileInfo.FileInfo.LastModified,
                            Length = fileInfo.FileInfo.Length,
                            Hash = hash,
                        });
                    }
                }
            }

            return(null);
        }
Beispiel #22
0
        public void GetOrAdd_IgnoresCachedValue_IfViewStartWasChangedSinceCacheWasCreated(
            RazorFileInfo viewStartRazorFileInfo, TestFileInfo viewStartFileInfo)
        {
            // Arrange
            var expectedType          = typeof(RuntimeCompileDifferent);
            var lastModified          = DateTime.UtcNow;
            var viewStartLastModified = DateTime.UtcNow;
            var content  = "some content";
            var fileInfo = new TestFileInfo
            {
                Length       = 1020,
                Content      = content,
                LastModified = lastModified,
                PhysicalPath = "Views\\home\\index.cshtml"
            };

            var runtimeFileInfo = new RelativeFileInfo(fileInfo, fileInfo.PhysicalPath);

            var razorFileInfo = new RazorFileInfo
            {
                FullTypeName = typeof(PreCompile).FullName,
                Hash         = RazorFileHash.GetHash(fileInfo),
                LastModified = lastModified,
                Length       = Encoding.UTF8.GetByteCount(content),
                RelativePath = fileInfo.PhysicalPath,
            };

            var fileSystem = new TestFileSystem();

            fileSystem.AddFile(viewStartRazorFileInfo.RelativePath, viewStartFileInfo);
            var viewCollection = new ViewCollection();
            var cache          = new CompilerCache(new[] { viewCollection }, fileSystem);

            // Act
            var actual = cache.GetOrAdd(runtimeFileInfo,
                                        compile: _ => CompilationResult.Successful(expectedType));

            // Assert
            Assert.Equal(expectedType, actual.CompiledType);
        }
        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);
            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 = 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);

            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_ReturnsCompilationFailureWithRelativePath()
        {
            // Arrange
            var fileContent            = "test 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);
            var fileInfo = new TestFileInfo
            {
                Content      = fileContent,
                PhysicalPath = "physical path"
            };
            var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");

            // Act
            var result = compilationService.Compile(relativeFileInfo, content);

            // Assert
            Assert.IsType <CompilationResult>(result);
            Assert.Null(result.CompiledType);
            Assert.Equal(relativeFileInfo.RelativePath, result.CompilationFailure.SourceFilePath);
            Assert.Equal(fileContent, result.CompilationFailure.SourceFileContent);
        }
Beispiel #26
0
        public void GetOrAdd_ReturnsCompilationResultFromFactory()
        {
            // Arrange
            var fileSystem = new TestFileSystem();
            var cache      = new CompilerCache(Enumerable.Empty <RazorFileInfoCollection>(), fileSystem);
            var fileInfo   = new TestFileInfo
            {
                LastModified = DateTime.FromFileTimeUtc(10000)
            };

            var type     = GetType();
            var expected = UncachedCompilationResult.Successful(type, "hello world");

            var runtimeFileInfo = new RelativeFileInfo(fileInfo, "ab");

            // Act
            var actual = cache.GetOrAdd(runtimeFileInfo, _ => expected);

            // Assert
            Assert.Same(expected, actual);
            Assert.Equal("hello world", actual.CompiledContent);
            Assert.Same(type, actual.CompiledType);
        }
Beispiel #27
0
        public void GetOrAdd_RecompilesFile_IfContentAndLengthAreChanged(
            Type resultViewType,
            long fileTimeUTC)
        {
            // Arrange
            var instance   = (View)Activator.CreateInstance(resultViewType);
            var length     = Encoding.UTF8.GetByteCount(instance.Content);
            var collection = new ViewCollection();
            var fileSystem = new TestFileSystem();
            var cache      = new CompilerCache(new[] { new ViewCollection() }, fileSystem);

            var fileInfo = new TestFileInfo
            {
                Length       = length,
                LastModified = DateTime.FromFileTimeUtc(fileTimeUTC),
                Content      = instance.Content
            };

            var runtimeFileInfo = new RelativeFileInfo(fileInfo, "ab");

            var precompiledContent = new PreCompile().Content;
            var razorFileInfo      = new RazorFileInfo
            {
                FullTypeName = typeof(PreCompile).FullName,
                Hash         = RazorFileHash.GetHash(GetMemoryStream(precompiledContent)),
                LastModified = DateTime.FromFileTimeUtc(10000),
                Length       = Encoding.UTF8.GetByteCount(precompiledContent),
                RelativePath = "ab",
            };

            // Act
            var actual = cache.GetOrAdd(runtimeFileInfo,
                                        compile: _ => CompilationResult.Successful(resultViewType));

            // Assert
            Assert.Equal(resultViewType, actual.CompiledType);
        }
        public void Compile_ReturnsCompilationFailureWithRelativePath()
        {
            // Arrange
            var fileContent = "test 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);
            var fileInfo = new TestFileInfo
            {
                Content = fileContent,
                PhysicalPath = "physical path"
            };
            var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");

            // Act
            var result = compilationService.Compile(relativeFileInfo, content);

            // Assert
            Assert.IsType<CompilationResult>(result);
            Assert.Null(result.CompiledType);
            Assert.Equal(relativeFileInfo.RelativePath, result.CompilationFailure.SourceFilePath);
            Assert.Equal(fileContent, result.CompilationFailure.SourceFileContent);
        }
Beispiel #29
0
        /// <inheritdoc />
        public CompilationResult Compile([NotNull] RelativeFileInfo file)
        {
            GeneratorResults results;

            using (var inputStream = file.FileInfo.CreateReadStream())
            {
                results = _razorHost.GenerateCode(
                    file.RelativePath, inputStream);
            }

            if (!results.Success)
            {
                var messages = results.ParserErrors
                               .Select(parseError => new RazorCompilationMessage(parseError, file.RelativePath));
                var failure = new RazorCompilationFailure(
                    file.RelativePath,
                    ReadFileContentsSafely(file.FileInfo),
                    messages);

                return(CompilationResult.Failed(failure));
            }

            return(_compilationService.Compile(file, results.GeneratedCode));
        }
Beispiel #30
0
        public void GetOrAdd_IgnoresCachedValueIfFileIsIdentical_ButViewStartWasAdedSinceTheCacheWasCreated()
        {
            // Arrange
            var expectedType    = typeof(RuntimeCompileDifferent);
            var lastModified    = DateTime.UtcNow;
            var fileSystem      = new TestFileSystem();
            var collection      = new ViewCollection();
            var precompiledFile = collection.FileInfos[0];

            precompiledFile.RelativePath = "Views\\home\\index.cshtml";
            var cache    = new CompilerCache(new[] { collection }, fileSystem);
            var testFile = new TestFileInfo
            {
                Content      = new PreCompile().Content,
                LastModified = precompiledFile.LastModified,
                PhysicalPath = precompiledFile.RelativePath
            };

            fileSystem.AddFile(precompiledFile.RelativePath, testFile);
            var relativeFile = new RelativeFileInfo(testFile, testFile.PhysicalPath);

            // Act 1
            var actual1 = cache.GetOrAdd(relativeFile,
                                         compile: _ => { throw new Exception("should not be called"); });

            // Assert 1
            Assert.Equal(typeof(PreCompile), actual1.CompiledType);

            // Act 2
            fileSystem.AddFile("Views\\_ViewStart.cshtml", "");
            var actual2 = cache.GetOrAdd(relativeFile,
                                         compile: _ => CompilationResult.Successful(expectedType));

            // Assert 2
            Assert.Equal(expectedType, actual2.CompiledType);
        }
        public void Compile_DoesNotThrow_IfFileCannotBeRead()
        {
            // Arrange
            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);
            var mockFileInfo = new Mock <IFileInfo>();

            mockFileInfo.Setup(f => f.CreateReadStream())
            .Throws(new Exception());
            var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, "some-relative-path");

            // Act
            var result = compilationService.Compile(relativeFileInfo, content);

            // Assert
            Assert.IsType <CompilationResult>(result);
            Assert.Null(result.CompiledType);
            Assert.Equal("some-relative-path", result.CompilationFailure.SourceFilePath);
            Assert.Null(result.CompilationFailure.SourceFileContent);
        }
Beispiel #32
0
        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 _GlobalImport 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 _GlobalImports 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 Compile_DoesNotThrow_IfFileCannotBeRead()
        {
            // Arrange
            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);
            var mockFileInfo = new Mock<IFileInfo>();
            mockFileInfo.Setup(f => f.CreateReadStream())
                        .Throws(new Exception());
            var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, "some-relative-path");

            // Act
            var result = compilationService.Compile(relativeFileInfo, content);

            // Assert
            Assert.IsType<CompilationResult>(result);
            Assert.Null(result.CompiledType);
            Assert.Equal("some-relative-path", result.CompilationFailure.SourceFilePath);
            Assert.Null(result.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);
            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 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);

            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);
        }