-
Notifications
You must be signed in to change notification settings - Fork 744
Add comprehensive test framework for TraceParserGen #2308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Copilot
wants to merge
7
commits into
main
Choose a base branch
from
copilot/add-test-framework-for-traceparsergen-2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 5 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
13adf2c
Initial plan
Copilot 58b03b8
Create initial TraceParserGen.Tests project with test framework
Copilot 1d97363
Add README and platform-aware test skipping
Copilot 3fd081c
Address PR feedback: improve test framework with trace file validation
Copilot 8e71d4e
Remove else path - always create parser and process trace
Copilot c609979
Simplify test project: target net462 only, use TEMP dir, remove Trace…
Copilot 81bdbb3
Remove FindInputDir and use ProjectReference for TraceEvent in test apps
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,297 @@ | ||
| using System; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
| using System.Runtime.InteropServices; | ||
| using System.Text; | ||
| using Xunit; | ||
| using Xunit.Abstractions; | ||
|
|
||
| namespace TraceParserGen.Tests | ||
| { | ||
| /// <summary> | ||
| /// Tests for TraceParserGen.exe that validate it can generate parsers from manifests | ||
| /// </summary> | ||
| public class ParserGenerationTests : TestBase | ||
| { | ||
| public ParserGenerationTests(ITestOutputHelper output) : base(output) | ||
| { | ||
| } | ||
|
|
||
| [Fact] | ||
| public void CanGenerateParserFromManifest() | ||
| { | ||
| // Skip on non-Windows platforms since TraceParserGen.exe is a .NET Framework app | ||
| // In a real environment, this would run on Windows with proper .NET Framework support | ||
| if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
| { | ||
| Output.WriteLine("Skipping test on non-Windows platform. TraceParserGen.exe requires .NET Framework."); | ||
| return; | ||
| } | ||
|
|
||
| // Arrange | ||
| string manifestPath = Path.Combine(TestDataDir, "SimpleTest.manifest.xml"); | ||
| string outputCsPath = Path.Combine(OutputDir, "SimpleTestParser.cs"); | ||
|
|
||
| Output.WriteLine($"Manifest: {manifestPath}"); | ||
| Output.WriteLine($"Output: {outputCsPath}"); | ||
|
|
||
| Assert.True(File.Exists(manifestPath), $"Manifest file not found: {manifestPath}"); | ||
|
|
||
| // Act - Step 1: Run TraceParserGen.exe | ||
| string traceParserGenPath = GetTraceParserGenExePath(); | ||
| Output.WriteLine($"TraceParserGen.exe: {traceParserGenPath}"); | ||
|
|
||
| var exitCode = RunTraceParserGen(traceParserGenPath, manifestPath, outputCsPath); | ||
|
|
||
| // Assert - Step 1: Verify TraceParserGen succeeded | ||
| Assert.Equal(0, exitCode); | ||
| Assert.True(File.Exists(outputCsPath), $"Generated C# file not found: {outputCsPath}"); | ||
|
|
||
| // Verify the generated file has expected content | ||
| string generatedContent = File.ReadAllText(outputCsPath); | ||
| Assert.Contains("class", generatedContent); | ||
| Assert.Contains("TraceEventParser", generatedContent); | ||
|
|
||
| Output.WriteLine("Successfully generated parser from manifest"); | ||
|
|
||
| // Act - Step 2: Create and build a test console application | ||
| string testProjectDir = Path.Combine(OutputDir, "TestApp"); | ||
| Directory.CreateDirectory(testProjectDir); | ||
|
|
||
| CreateTestConsoleApp(testProjectDir, outputCsPath); | ||
|
|
||
| // Act - Step 3: Build the test application | ||
| var buildExitCode = BuildTestApp(testProjectDir); | ||
| Assert.Equal(0, buildExitCode); | ||
|
|
||
| // Act - Step 4: Run the test application | ||
| var runExitCode = RunTestApp(testProjectDir); | ||
|
|
||
| // Assert - Step 4: Verify test app ran successfully (no crashes, no asserts) | ||
| Assert.Equal(0, runExitCode); | ||
|
|
||
| Output.WriteLine("Test completed successfully"); | ||
| } | ||
|
|
||
| private int RunTraceParserGen(string exePath, string manifestPath, string outputPath) | ||
| { | ||
| var startInfo = new ProcessStartInfo | ||
| { | ||
| FileName = exePath, | ||
| Arguments = $"\"{manifestPath}\" \"{outputPath}\"", | ||
| UseShellExecute = false, | ||
| RedirectStandardOutput = true, | ||
| RedirectStandardError = true, | ||
| CreateNoWindow = true | ||
| }; | ||
|
|
||
| Output.WriteLine($"Running: {startInfo.FileName} {startInfo.Arguments}"); | ||
|
|
||
| using (var process = Process.Start(startInfo)) | ||
| { | ||
| var output = process.StandardOutput.ReadToEnd(); | ||
| var error = process.StandardError.ReadToEnd(); | ||
|
|
||
| process.WaitForExit(); | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(output)) | ||
| { | ||
| Output.WriteLine("STDOUT:"); | ||
| Output.WriteLine(output); | ||
| } | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(error)) | ||
| { | ||
| Output.WriteLine("STDERR:"); | ||
| Output.WriteLine(error); | ||
| } | ||
|
|
||
| return process.ExitCode; | ||
| } | ||
| } | ||
|
|
||
| private void CreateTestConsoleApp(string projectDir, string generatedParserPath) | ||
| { | ||
| // Get the path to TraceEvent assembly - it's in the test project's output directory | ||
| // since we have a ProjectReference | ||
| string traceEventAssembly = Path.Combine(Environment.CurrentDirectory, "Microsoft.Diagnostics.Tracing.TraceEvent.dll"); | ||
|
|
||
| // Create the .csproj file | ||
| string csprojContent = $@"<Project Sdk=""Microsoft.NET.Sdk""> | ||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net462</TargetFramework> | ||
| <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <Reference Include=""Microsoft.Diagnostics.Tracing.TraceEvent""> | ||
| <HintPath>{traceEventAssembly}</HintPath> | ||
| </Reference> | ||
| </ItemGroup> | ||
| </Project>"; | ||
|
|
||
| File.WriteAllText(Path.Combine(projectDir, "TestApp.csproj"), csprojContent); | ||
|
|
||
| // Copy the generated parser file | ||
| string destParserPath = Path.Combine(projectDir, Path.GetFileName(generatedParserPath)); | ||
| File.Copy(generatedParserPath, destParserPath, true); | ||
|
|
||
| // Create a simple trace file to use for testing | ||
| // We'll use one of the existing test trace files | ||
| string sampleTracePath = Path.Combine(TestDataDir, "..", "..", "TraceEvent", "TraceEvent.Tests", "inputs", "net.4.5.x86.etl.zip"); | ||
|
|
||
| // Create Program.cs that uses the generated parser with a real trace file | ||
| string programContent = $@"using System; | ||
| using System.Linq; | ||
| using System.Reflection; | ||
| using Microsoft.Diagnostics.Tracing; | ||
|
|
||
| class Program | ||
| {{ | ||
| static int Main(string[] args) | ||
| {{ | ||
| try | ||
| {{ | ||
| Console.WriteLine(""Starting parser test...""); | ||
|
|
||
| // Find all TraceEventParser-derived types in the current assembly | ||
| var assembly = Assembly.GetExecutingAssembly(); | ||
| var parserTypes = assembly.GetTypes() | ||
| .Where(t => typeof(TraceEventParser).IsAssignableFrom(t) && !t.IsAbstract) | ||
| .ToList(); | ||
|
|
||
| Console.WriteLine($""Found {{parserTypes.Count}} parser type(s)""); | ||
|
|
||
| if (parserTypes.Count == 0) | ||
| {{ | ||
| Console.WriteLine(""ERROR: No parser types found""); | ||
| return 1; | ||
| }} | ||
|
|
||
| // Get trace file path (from args or use default) | ||
| string traceFilePath = args.Length > 0 ? args[0] : ""{sampleTracePath.Replace("\\", "\\\\")}""; | ||
|
|
||
| if (!System.IO.File.Exists(traceFilePath)) | ||
| {{ | ||
| Console.WriteLine($""ERROR: Trace file not found: {{traceFilePath}}""); | ||
| return 1; | ||
| }} | ||
|
|
||
| Console.WriteLine($""Using trace file: {{traceFilePath}}""); | ||
|
|
||
| using (var source = TraceEventDispatcher.GetDispatcherFromFileName(traceFilePath)) | ||
| {{ | ||
| foreach (var parserType in parserTypes) | ||
| {{ | ||
| Console.WriteLine($"" Testing parser: {{parserType.Name}}""); | ||
|
|
||
| // Create an instance of the parser | ||
| var parser = (TraceEventParser)Activator.CreateInstance(parserType, source); | ||
|
|
||
| int eventCount = 0; | ||
|
|
||
| // Hook the All event to count events processed by this parser | ||
| parser.All += (TraceEvent data) => | ||
| {{ | ||
| eventCount++; | ||
| }}; | ||
|
|
||
| // Process the trace (this will trigger events if any match) | ||
| source.Process(); | ||
|
|
||
| Console.WriteLine($"" Processed {{eventCount}} event(s) from this parser""); | ||
| }} | ||
| }} | ||
|
|
||
| Console.WriteLine(""Parser test completed successfully""); | ||
| return 0; | ||
| }} | ||
| catch (Exception ex) | ||
| {{ | ||
| Console.WriteLine($""ERROR: {{ex.Message}}""); | ||
| Console.WriteLine(ex.StackTrace); | ||
| return 1; | ||
| }} | ||
| }} | ||
| }}"; | ||
|
|
||
| File.WriteAllText(Path.Combine(projectDir, "Program.cs"), programContent); | ||
| } | ||
|
|
||
| private int BuildTestApp(string projectDir) | ||
| { | ||
| var startInfo = new ProcessStartInfo | ||
| { | ||
| FileName = "dotnet", | ||
| Arguments = "build -c Release", | ||
| WorkingDirectory = projectDir, | ||
| UseShellExecute = false, | ||
| RedirectStandardOutput = true, | ||
| RedirectStandardError = true, | ||
| CreateNoWindow = true | ||
| }; | ||
|
|
||
| Output.WriteLine($"Building test app in: {projectDir}"); | ||
|
|
||
| using (var process = Process.Start(startInfo)) | ||
| { | ||
| var output = process.StandardOutput.ReadToEnd(); | ||
| var error = process.StandardError.ReadToEnd(); | ||
|
|
||
| process.WaitForExit(); | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(output)) | ||
| { | ||
| Output.WriteLine("Build STDOUT:"); | ||
| Output.WriteLine(output); | ||
| } | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(error)) | ||
| { | ||
| Output.WriteLine("Build STDERR:"); | ||
| Output.WriteLine(error); | ||
| } | ||
|
|
||
| return process.ExitCode; | ||
| } | ||
| } | ||
|
|
||
| private int RunTestApp(string projectDir) | ||
| { | ||
| var startInfo = new ProcessStartInfo | ||
| { | ||
| FileName = "dotnet", | ||
| Arguments = "run -c Release", | ||
| WorkingDirectory = projectDir, | ||
| UseShellExecute = false, | ||
| RedirectStandardOutput = true, | ||
| RedirectStandardError = true, | ||
| CreateNoWindow = true | ||
| }; | ||
|
|
||
| Output.WriteLine($"Running test app in: {projectDir}"); | ||
|
|
||
| using (var process = Process.Start(startInfo)) | ||
| { | ||
| var output = process.StandardOutput.ReadToEnd(); | ||
| var error = process.StandardError.ReadToEnd(); | ||
|
|
||
| process.WaitForExit(); | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(output)) | ||
| { | ||
| Output.WriteLine("Run STDOUT:"); | ||
| Output.WriteLine(output); | ||
| } | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(error)) | ||
| { | ||
| Output.WriteLine("Run STDERR:"); | ||
| Output.WriteLine(error); | ||
| } | ||
|
|
||
| return process.ExitCode; | ||
| } | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than using a
<Reference>tag, this should be a<ProjectReference>tag pointing at TraceEvent.csproj in the repo. You'll need to implement this by using a relative path (e.g. ....\TraceEvent\TraceEvent.csproj). This might not be the right relative path, but you can tell by finding the source code at runtime. You'll need to do this because TraceEvent.dll won't be the only dependency needed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to use
<ProjectReference>tag pointing to TraceEvent.csproj. The path is found at runtime by navigating from the test assembly location to the source directory. Commit: 0bc8f45