Example #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);
        }
Example #2
0
        public void GetOrAdd_IgnoresCachedValue_IfGlobalFileWasChangedSinceCacheWasCreated(
            RazorFileInfo viewStartRazorFileInfo, IFileInfo globalFileInfo)
        {
            // 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 fileProvider = new TestFileProvider();

            fileProvider.AddFile(fileInfo.PhysicalPath, fileInfo);
            fileProvider.AddFile(viewStartRazorFileInfo.RelativePath, globalFileInfo);
            var viewCollection = new ViewCollection();
            var cache          = new CompilerCache(new[] { viewCollection }, TestLoadContext, fileProvider);

            // Act
            var result = cache.GetOrAdd(fileInfo.PhysicalPath,
                                        compile: _ => CompilationResult.Successful(expectedType));

            // Assert
            Assert.NotSame(CompilerCacheResult.FileNotFound, result);
            var actual = result.CompilationResult;

            Assert.NotNull(actual);
            Assert.Equal(expectedType, actual.CompiledType);
        }
Example #3
0
 /// <summary>
 /// Initializes a new instance of <see cref="CompilerCacheEntry"/> for a file that was precompiled.
 /// </summary>
 /// <param name="info">Metadata about the precompiled file.</param>
 /// <param name="compiledType">The compiled <see cref="Type"/>.</param>
 public CompilerCacheEntry([NotNull] RazorFileInfo info, [NotNull] Type compiledType)
 {
     CompiledType = compiledType;
     RelativePath = info.RelativePath;
     Length       = info.Length;
     LastModified = info.LastModified;
     Hash         = info.Hash;
 }
 protected virtual string GenerateFile([NotNull] RazorFileInfo fileInfo)
 {
     return(string.Format(FileFormat,
                          fileInfo.LastModified.ToFileTimeUtc(),
                          fileInfo.Length,
                          fileInfo.RelativePath,
                          fileInfo.FullTypeName,
                          fileInfo.Hash));
 }
Example #5
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);
        }
 /// <summary>
 /// Initializes a new instance of <see cref="CompilerCacheEntry"/> for a file that was precompiled.
 /// </summary>
 /// <param name="info">Metadata about the precompiled file.</param>
 /// <param name="compiledType">The compiled <see cref="Type"/>.</param>
 public CompilerCacheEntry([NotNull] RazorFileInfo info, [NotNull] Type compiledType)
 {
     CompiledType         = compiledType;
     RelativePath         = info.RelativePath;
     Length               = info.Length;
     LastModified         = info.LastModified;
     Hash                 = info.Hash;
     HashAlgorithmVersion = info.HashAlgorithmVersion;
     IsPreCompiled        = true;
 }
Example #7
0
 /// <summary>
 /// Initializes a new instance of <see cref="CompilerCacheEntry"/> for a file that was precompiled.
 /// </summary>
 /// <param name="info">Metadata about the precompiled file.</param>
 /// <param name="compiledType">The compiled <see cref="Type"/>.</param>
 public CompilerCacheEntry([NotNull] RazorFileInfo info, [NotNull] Type compiledType)
 {
     CompiledType = compiledType;
     RelativePath = info.RelativePath;
     Length       = info.Length;
     LastModified = info.LastModified;
     Hash         = info.Hash;
     // Precompiled views are always instrumented.
     IsInstrumented = true;
 }
Example #8
0
        protected virtual RazorFileInfoCollection CreateFileInfoCollection()
        {
            var filesToProcess = new List <RelativeFileInfo>();

            GetFileInfosRecursive(root: string.Empty, razorFiles: filesToProcess);
            if (filesToProcess.Count == 0)
            {
                return(null);
            }

            var razorFiles      = new RazorFileInfo[filesToProcess.Count];
            var syntaxTrees     = new SyntaxTree[filesToProcess.Count];
            var parallelOptions = new ParallelOptions {
                MaxDegreeOfParallelism = MaxDegreesOfParallelism
            };
            var diagnosticsLock = new object();
            var hasErrors       = false;

            Parallel.For(0, filesToProcess.Count, parallelOptions, index =>
            {
                var file       = filesToProcess[index];
                var cacheEntry = PreCompilationCache.GetOrSet(file.RelativePath,
                                                              file,
                                                              OnCacheMiss);
                if (cacheEntry != null)
                {
                    if (cacheEntry.Success)
                    {
                        syntaxTrees[index] = cacheEntry.SyntaxTree;
                        razorFiles[index]  = cacheEntry.FileInfo;
                    }
                    else
                    {
                        hasErrors = true;
                        lock (diagnosticsLock)
                        {
                            AddRange(CompileContext.Diagnostics, cacheEntry.Diagnostics);
                        }
                    }
                }
            });

            if (hasErrors)
            {
                // If any of the Razor files had syntax errors, don't emit the precompiled views assembly.
                return(null);
            }

            return(GeneratePrecompiledAssembly(syntaxTrees.Where(tree => tree != null),
                                               razorFiles.Where(file => file != null)));
        }
Example #9
0
        public void GetOrAdd_UsesValueFromCache_IfGlobalHasNotChanged()
        {
            // Arrange
            var instance     = new PreCompile();
            var length       = Encoding.UTF8.GetByteCount(instance.Content);
            var fileProvider = new TestFileProvider();

            var lastModified = DateTime.UtcNow;

            var fileInfo = new TestFileInfo
            {
                Length       = length,
                LastModified = lastModified,
                Content      = instance.Content
            };

            fileProvider.AddFile(ViewPath, fileInfo);

            var globalContent  = "global-content";
            var globalFileInfo = new TestFileInfo
            {
                Content      = globalContent,
                LastModified = DateTime.UtcNow
            };

            fileProvider.AddFile("_GlobalImport.cshtml", globalFileInfo);
            var globalRazorFileInfo = new RazorFileInfo
            {
                Hash = Crc32.Calculate(GetMemoryStream(globalContent)).ToString(CultureInfo.InvariantCulture),
                HashAlgorithmVersion = 1,
                LastModified         = globalFileInfo.LastModified,
                Length       = globalFileInfo.Length,
                RelativePath = "_GlobalImport.cshtml",
                FullTypeName = typeof(RuntimeCompileIdentical).FullName
            };
            var precompiledViews = new ViewCollection();

            precompiledViews.Add(globalRazorFileInfo);
            var cache = new CompilerCache(new[] { precompiledViews }, TestLoadContext, fileProvider);

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

            // Assert
            Assert.NotSame(CompilerCacheResult.FileNotFound, result);
            var actual = result.CompilationResult;

            Assert.NotNull(actual);
            Assert.Equal(typeof(PreCompile), actual.CompiledType);
        }
Example #10
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);
        }
Example #11
0
        protected virtual IEnumerable <RazorFileInfo> CreateCompilationDescriptors(
            [NotNull] IBeforeCompileContext context)
        {
            var filesToProcess = new List <RelativeFileInfo>();

            GetFileInfosRecursive(root: string.Empty, razorFiles: filesToProcess);

            var razorFiles      = new RazorFileInfo[filesToProcess.Count];
            var syntaxTrees     = new SyntaxTree[filesToProcess.Count];
            var parallelOptions = new ParallelOptions {
                MaxDegreeOfParallelism = MaxDegreesOfParallelism
            };
            var diagnosticsLock = new object();

            Parallel.For(0, filesToProcess.Count, parallelOptions, index =>
            {
                var file       = filesToProcess[index];
                var cacheEntry = PreCompilationCache.GetOrSet(file.RelativePath,
                                                              file,
                                                              OnCacheMiss);
                if (cacheEntry != null)
                {
                    if (cacheEntry.Success)
                    {
                        syntaxTrees[index] = cacheEntry.SyntaxTree;
                        razorFiles[index]  = cacheEntry.FileInfo;
                    }
                    else
                    {
                        lock (diagnosticsLock)
                        {
                            foreach (var diagnostic in cacheEntry.Diagnostics)
                            {
                                context.Diagnostics.Add(diagnostic);
                            }
                        }
                    }
                }
            });

            context.CSharpCompilation = context.CSharpCompilation
                                        .AddSyntaxTrees(syntaxTrees.Where(tree => tree != null));
            return(razorFiles.Where(file => file != null));
        }
Example #12
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);
        }
Example #13
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);
        }
Example #14
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);
        }
Example #15
0
 public void Add(RazorFileInfo fileInfo)
 {
     _fileInfos.Add(fileInfo);
 }
Example #16
0
        public void GetOrAdd_IgnoresCachedValueIfFileIsIdentical_ButGlobalWasDeletedSinceCacheWasCreated()
        {
            // Arrange
            var expectedType = typeof(RuntimeCompileDifferent);
            var lastModified = DateTime.UtcNow;
            var fileProvider = new TestFileProvider();

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

            fileProvider.AddFile(viewFileInfo.PhysicalPath, viewFileInfo);

            var globalFileInfo = new TestFileInfo
            {
                PhysicalPath = "Views\\_GlobalImport.cshtml",
                Content      = "viewstart-content",
                LastModified = lastModified
            };
            var globalFile = new RazorFileInfo
            {
                FullTypeName         = typeof(RuntimeCompileIdentical).FullName,
                RelativePath         = globalFileInfo.PhysicalPath,
                LastModified         = globalFileInfo.LastModified,
                Hash                 = RazorFileHash.GetHash(globalFileInfo, hashAlgorithmVersion: 1),
                HashAlgorithmVersion = 1,
                Length               = globalFileInfo.Length
            };

            fileProvider.AddFile(globalFileInfo.PhysicalPath, globalFileInfo);

            viewCollection.Add(globalFile);
            var cache = new CompilerCache(new[] { viewCollection }, TestLoadContext, fileProvider);

            // Act 1
            var result1 = cache.GetOrAdd(viewFileInfo.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 trigger = fileProvider.GetTrigger(globalFileInfo.PhysicalPath);

            trigger.IsExpired = true;
            var result2 = cache.GetOrAdd(viewFileInfo.PhysicalPath,
                                         compile: _ => CompilationResult.Successful(expectedType));

            // Assert 2
            Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
            var actual2 = result2.CompilationResult;

            Assert.NotNull(actual2);
            Assert.Equal(expectedType, actual2.CompiledType);
        }
Example #17
0
 /// <summary>
 /// Initializes a new instance of <see cref="PrecompilationCacheEntry"/> for a successful parse.
 /// </summary>
 /// <param name="fileInfo">The <see cref="RazorFileInfo"/> of the file being cached.</param>
 /// <param name="syntaxTree">The <see cref="CodeAnalysis.SyntaxTree"/> to cache.</param>
 public PrecompilationCacheEntry([NotNull] RazorFileInfo fileInfo,
                                 [NotNull] SyntaxTree syntaxTree)
 {
     FileInfo   = fileInfo;
     SyntaxTree = syntaxTree;
 }