public void When_workspace_is_null_then_the_transformer_throw_exception() { var processor = new BufferInliningTransformer(); Func <Task> extraction = () => processor.TransformAsync(null); extraction.Should().Throw <ArgumentNullException>(); }
public async Task Buffer_can_be_injected_before_region() { var original = new Workspace( files: new[] { new Protocol.File("Program.cs", SourceCodeProvider.ConsoleProgramSingleRegion) }, buffers: new[] { new Buffer("Program.cs@alpha[before]", "var newValue = 1000;".EnforceLF()) }); var processor = new BufferInliningTransformer(); var processed = await processor.TransformAsync(original); processed.Should().NotBeNull(); processed.Files.Should().NotBeEmpty(); var newCode = processed.Files.ElementAt(0).Text; newCode.Should().NotBe(original.Files.ElementAt(0).Text); newCode.Should().Contain("var newValue = 1000;"); newCode.Should().Contain("var a = 10;"); original.Buffers.ElementAt(0).Position.Should().Be(0); processed.Buffers.Length.Should().Be(original.Buffers.Length); processed.Buffers.ElementAt(0).Position.Should().Be(original.Buffers.ElementAt(0).Position); processed.Buffers.ElementAt(0).AbsolutePosition.Should().Be(155); }
public async Task Processed_workspace_files_are_replaced_by_buffer_when_id_is_just_file_name() { var ws = new Workspace( files: new[] { new Protocol.File("Program.cs", SourceCodeProvider.ConsoleProgramSingleRegion) }, buffers: new[] { new Buffer("Program.cs", "var newValue = 1000;", 0) }); var processor = new BufferInliningTransformer(); var processed = await processor.TransformAsync(ws); processed.Should().NotBeNull(); processed.Files.Should().NotBeEmpty(); var newCode = processed.Files.ElementAt(0).Text; newCode.Should().NotBe(ws.Files.ElementAt(0).Text); newCode.Should().Be("var newValue = 1000;"); processed.Buffers.Length.Should().Be(ws.Buffers.Length); processed.Buffers.ElementAt(0).Position.Should().Be(0); }
public async Task Processed_workspace_with_single_buffer_with_empty_id_generates_a_program_file() { var ws = new Workspace( buffers: new[] { new Buffer("", SourceCodeProvider.ConsoleProgramSingleRegion, 0) }); var processor = new BufferInliningTransformer(); var processed = await processor.TransformAsync(ws); processed.Should().NotBeNull(); processed.Files.Should().NotBeEmpty(); var newCode = processed.Files.ElementAt(0).Text; newCode.Should().Contain(SourceCodeProvider.ConsoleProgramSingleRegion); }
public async Task If_workspace_contains_with_multiple_buffers_targeting_single_file_generates_a_single_file() { var expectedCode = @"using System; namespace ConsoleProgramSingleRegion { public class Program { public static void Main(string[] args) { #region alpha var newValueA = ""as string""; Console.Write(newValueA); #endregion #region beta var newValueB = ""as different string""; Console.Write(newValueA + newValueB); #endregion } } }".EnforceLF(); var ws = new Workspace( files: new[] { new Protocol.File("Program.cs", SourceCodeProvider.ConsoleProgramMultipleRegions) }, buffers: new[] { new Buffer(new BufferId("Program.cs", "alpha"), "var newValueA = \"as string\";\nConsole.Write(newValueA);", 0), new Buffer(new BufferId("Program.cs", "beta"), "var newValueB = \"as different string\";\nConsole.Write(newValueA + newValueB);", 0) }); var processor = new BufferInliningTransformer(); var processed = await processor.TransformAsync(ws); processed.Should().NotBeNull(); processed.Files.Should().NotBeEmpty(); var newCode = processed.Files.ElementAt(0).Text.EnforceLF(); newCode.Should().Be(expectedCode); }
public async Task If_workspace_contains_buffers_whose_file_names_are_absolute_paths_the_contents_are_read_from_disk() { using (var directory = DisposableDirectory.Create()) { var filePath = Path.Combine(directory.Directory.FullName, "Program.cs"); var fileContent = @"using System; namespace Code{{ public static class Program{{ public static void Main(){{ #region region one #endregion }} }} }}".EnforceLF(); var expectedFileContent = @"using System; namespace Code{{ public static class Program{{ public static void Main(){{ #region region one Console.Write(2); #endregion }} }} }}".EnforceLF(); File.WriteAllText(filePath, fileContent); var ws = new Workspace( buffers: new[] { new Buffer(new BufferId(filePath, "region one"), "Console.Write(2);"), } ); var processor = new BufferInliningTransformer(); var processed = await processor.TransformAsync(ws); processed.Files[0].Text.EnforceLF().Should().Be(expectedFileContent); } }
public async Task If_workspace_contains_files_whose_names_are_absolute_paths_and_they_have_no_content_then_the_contents_are_read_from_disk() { using (var directory = DisposableDirectory.Create()) { var filePath = Path.Combine(directory.Directory.FullName, "Program.cs"); var content = @"using System;"; File.WriteAllText(filePath, content); var ws = new Workspace( files: new[] { new Protocol.File(filePath, null) } ); var processor = new BufferInliningTransformer(); var processed = await processor.TransformAsync(ws); processed.Files[0].Text.Should().Be(content); } }
public async Task Buffers_can_be_injected_according_to_injection_points() { var original = new Workspace( files: new[] { new Protocol.File("Program.cs", SourceCodeProvider.ConsoleProgramSingleRegion) }, buffers: new[] { new Buffer("Program.cs@alpha[before]", "var before = 1000;".EnforceLF()), new Buffer("Program.cs@alpha[after]", "var after = 1000;".EnforceLF()), new Buffer("Program.cs@alpha", "var inlined = 1000;".EnforceLF()) }); var processor = new BufferInliningTransformer(); var processed = await processor.TransformAsync(original); processed.Should().NotBeNull(); processed.Files.Should().NotBeEmpty(); var newCode = processed.Files.ElementAt(0).Text; newCode.Should().NotBe(original.Files.ElementAt(0).Text); newCode.Should().Contain("var before = 1000;"); newCode.Should().Contain("var inlined = 1000;"); newCode.Should().Contain("var after = 1000;"); var beforeBuffer = processed.Buffers.First(b => b.Id.RegionName.Contains("before")); beforeBuffer.AbsolutePosition.Should().Be(155); var inlinedBuffer = processed.Buffers.First(b => b.Id.RegionName == "alpha"); inlinedBuffer.AbsolutePosition.Should().Be(188); var afterBuffer = processed.Buffers.First(b => b.Id.RegionName.Contains("after")); afterBuffer.AbsolutePosition.Should().Be(235); }
public static async Task <int> Do( VerifyOptions verifyOptions, IConsole console, StartupOptions startupOptions = null) { var directoryAccessor = verifyOptions.RootDirectory; var packageRegistry = PackageRegistry.CreateForTryMode(directoryAccessor); var markdownProject = new MarkdownProject( directoryAccessor, packageRegistry, startupOptions); var errorCount = 0; var workspaceServer = new Lazy <IWorkspaceServer>(() => new WorkspaceServerMultiplexer(packageRegistry)); var markdownFiles = markdownProject.GetAllMarkdownFiles().ToArray(); if (markdownFiles.Length == 0) { console.Error.WriteLine($"No markdown files found under {directoryAccessor.GetFullyQualifiedRoot()}"); return(-1); } foreach (var markdownFile in markdownFiles) { var fullName = directoryAccessor.GetFullyQualifiedPath(markdownFile.Path).FullName; var markdownFileDirectoryAccessor = directoryAccessor.GetDirectoryAccessorForRelativePath(markdownFile.Path.Directory); console.Out.WriteLine(); console.Out.WriteLine(fullName); console.Out.WriteLine(new string('-', fullName.Length)); var codeLinkBlocks = await markdownFile.GetAnnotatedCodeBlocks(); var sessions = codeLinkBlocks.GroupBy(block => block.Annotations?.Session); foreach (var session in sessions) { if (session.Select(block => block.ProjectOrPackageName()).Distinct().Count() != 1) { SetError(); console.Out.WriteLine($"Session cannot span projects or packages: --session {session.Key}"); continue; } foreach (var codeLinkBlock in session) { ReportCodeLinkageResults(codeLinkBlock, markdownFileDirectoryAccessor); } Console.ResetColor(); if (!session.Any(block => block.Diagnostics.Any())) { var(buffersToInclude, filesToInclude) = await markdownFile.GetIncludes(markdownFileDirectoryAccessor); await ReportCompileResults( session, markdownFile, filesToInclude, buffersToInclude, markdownFileDirectoryAccessor); } Console.ResetColor(); } } if (errorCount > 0) { SetError(false); } else { SetOk(); } console.Out.WriteLine($"\n\ndotnet try verify found {errorCount} error(s)"); Console.ResetColor(); return(errorCount == 0 ? 0 : 1); void SetError(bool incrementCount = true) { Console.ForegroundColor = ConsoleColor.Red; if (incrementCount) { errorCount++; } } void SetOk() { Console.ForegroundColor = ConsoleColor.Green; } async Task ReportCompileResults( IGrouping <string, AnnotatedCodeBlock> session, MarkdownFile markdownFile, Dictionary <string, File[]> filesToInclude, IReadOnlyDictionary <string, Buffer[]> buffersToInclude, IDirectoryAccessor accessor) { var description = session.Count() == 1 || string.IsNullOrWhiteSpace(session.Key) ? $"region \"{session.Select(s => s.Annotations.Region).Distinct().First()}\"" : $"session \"{session.Key}\""; console.Out.WriteLine($"\n Compiling samples for {description}\n"); var projectOrPackageName = session .Select(b => b.ProjectOrPackageName()) .FirstOrDefault(name => !string.IsNullOrWhiteSpace(name)); var language = session .Select(b => b.Language()) .FirstOrDefault(name => !string.IsNullOrWhiteSpace(name)); if (!ProjectIsCompatibleWithLanguage(new UriOrFileInfo(projectOrPackageName), language)) { SetError(); console.Out.WriteLine($" Build failed as project {projectOrPackageName} is not compatible with language {language}"); } var editableCodeBlocks = session.Where(b => b.Annotations.Editable).ToList(); var buffers = editableCodeBlocks .Select(block => block.GetBufferAsync(accessor, markdownFile)) .ToList(); var files = new List <File>(); if (filesToInclude.TryGetValue("global", out var globalIncludes)) { files.AddRange(globalIncludes); } if (!string.IsNullOrWhiteSpace(session.Key) && filesToInclude.TryGetValue(session.Key, out var sessionIncludes)) { files.AddRange(sessionIncludes); } if (buffersToInclude.TryGetValue("global", out var globalSessionBuffersToInclude)) { buffers.AddRange(globalSessionBuffersToInclude); } if (!string.IsNullOrWhiteSpace(session.Key) && buffersToInclude.TryGetValue(session.Key, out var localSessionBuffersToInclude)) { buffers.AddRange(localSessionBuffersToInclude); } var workspace = new Workspace( workspaceType: projectOrPackageName, language: language, files: files.ToArray(), buffers: buffers.ToArray()); var mergeTransformer = new CodeMergeTransformer(); var inliningTransformer = new BufferInliningTransformer(); var processed = await mergeTransformer.TransformAsync(workspace); processed = await inliningTransformer.TransformAsync(processed); processed = new Workspace(usings: processed.Usings, workspaceType: processed.WorkspaceType, language: processed.Language, files: processed.Files); var result = await workspaceServer.Value.Compile(new WorkspaceRequest(processed)); var projectDiagnostics = result.GetFeature <ProjectDiagnostics>() .Where(e => e.Severity == DiagnosticSeverity.Error) .ToArray(); if (projectDiagnostics.Any()) { SetError(); console.Out.WriteLine($" Build failed for project {projectOrPackageName}"); foreach (var diagnostic in projectDiagnostics) { console.Out.WriteLine($"\t\t{diagnostic.Location}: {diagnostic.Message}"); } } else { var symbol = !result.Succeeded ? "X" : "✓"; if (result.Succeeded) { SetOk(); console.Out.WriteLine($" {symbol} No errors found within samples for {description}"); } else { SetError(); console.Out.WriteLine($" {symbol} Errors found within samples for {description}"); foreach (var diagnostic in result.GetFeature <Diagnostics>()) { console.Out.WriteLine($"\t\t{diagnostic.Message}"); } } } } void ReportCodeLinkageResults( AnnotatedCodeBlock codeLinkBlock, IDirectoryAccessor accessor) { var diagnostics = codeLinkBlock.Diagnostics.ToArray(); Console.ResetColor(); console.Out.WriteLine(" Checking Markdown..."); if (diagnostics.Any()) { SetError(); } else { Console.ForegroundColor = ConsoleColor.Green; } var blockOptions = (LocalCodeBlockAnnotations)codeLinkBlock.Annotations; var file = blockOptions?.SourceFile ?? blockOptions?.DestinationFile; var fullyQualifiedPath = file != null ? accessor.GetFullyQualifiedPath(file).FullName : "UNKNOWN"; var project = codeLinkBlock.ProjectOrPackageName() ?? "UNKNOWN"; var symbol = diagnostics.Any() ? "X" : "✓"; console.Out.WriteLine($" {symbol} Line {codeLinkBlock.Line + 1}:\t{fullyQualifiedPath} (in project {project})"); foreach (var diagnostic in diagnostics) { console.Out.WriteLine($"\t\t{diagnostic}"); } } }