Files
chapter-organizer/Tests/Parsers/EventOccurrenceParser_Tests.cs
T
poprhythm 5fdd5fadba Enhance event occurrence parsing with new location patterns and improved issue handling
This commit updates the LocationParsingConfiguration to include additional location patterns such as "Exhibit Hall *", "Mtg. Room *", and "Online". The EventOccurrenceParser has been enhanced to better handle parsing issues, including skipping comment and continuation lines, and cleaning up location text. New methods for analyzing location parsing failures and categorizing issues have been added to improve reporting. Additionally, the UI has been updated to support larger input sizes for event occurrence text, ensuring a smoother user experience during data import.
2026-01-08 08:08:36 -05:00

620 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Core.Entities;
using Core.Models;
using Core.Parsers;
namespace Tests.Parsers;
/// <summary>
/// Integration tests for EventOccurrenceParser using real test data files.
/// </summary>
public class EventOccurrenceParser_Tests
{
#region Helper Methods
/// <summary>
/// Checks if a line contains a High School event marker.
/// </summary>
private static bool IsHighSchoolEvent(string line)
{
return line.Contains(" HS", StringComparison.OrdinalIgnoreCase) ||
line.Contains(" - HS", StringComparison.OrdinalIgnoreCase) ||
line.Contains("- HS", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Checks if a line contains a Middle School event marker.
/// </summary>
private static bool IsMiddleSchoolEvent(string line)
{
return line.Contains(" MS", StringComparison.OrdinalIgnoreCase) ||
line.Contains(" - MS", StringComparison.OrdinalIgnoreCase) ||
line.Contains("- MS", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Determines if an issue is expected (HS-related) or fixable.
/// </summary>
private static bool IsExpectedIssue(ParsingIssue issue, List<string> fileLines, int currentLineIndex)
{
// Check if the issue line itself is an HS event header
if (IsHighSchoolEvent(issue.LineContent))
return true;
// For MissingEventDefinition issues, check if we're in an HS section
if (issue.IssueType == ParsingIssueType.MissingEventDefinition)
{
// Look backwards to find the most recent section header
for (int i = currentLineIndex - 1; i >= 0 && i >= currentLineIndex - 20; i--)
{
if (i < fileLines.Count)
{
var line = fileLines[i].Trim();
if (IsHighSchoolEvent(line))
return true;
if (IsMiddleSchoolEvent(line))
return false; // Found MS section, so this is fixable
}
}
}
return false;
}
/// <summary>
/// Categorizes issues into expected (HS-related) and fixable.
/// </summary>
private static (List<ParsingIssue> Expected, List<ParsingIssue> Fixable) CategorizeIssues(
List<ParsingIssue> issues, List<string> fileLines)
{
var expected = new List<ParsingIssue>();
var fixable = new List<ParsingIssue>();
foreach (var issue in issues)
{
var lineIndex = issue.LineNumber - 1; // Convert to 0-based index
if (IsExpectedIssue(issue, fileLines, lineIndex))
{
expected.Add(issue);
}
else
{
fixable.Add(issue);
}
}
return (expected, fixable);
}
/// <summary>
/// Gets sample lines for a list of issues.
/// </summary>
private static List<string> GetSampleLines(List<ParsingIssue> issues, int count = 5)
{
return issues
.Take(count)
.Select(i => $" Line {i.LineNumber}: {i.LineContent}")
.ToList();
}
/// <summary>
/// Analyzes location parsing failures and extracts common patterns.
/// </summary>
private static Dictionary<string, int> AnalyzeLocationFailures(
List<ParsingIssue> locationIssues, List<string> fileLines)
{
var locationPatterns = new Dictionary<string, int>();
foreach (var issue in locationIssues)
{
// Try to extract the location part from the line
// The format is typically: "EventName Month Day Time Location"
var parts = issue.LineContent.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
// Look for location-like strings (usually after time)
// This is a heuristic - we'll look for parts that don't match date/time patterns
var timeRegex = new System.Text.RegularExpressions.Regex(
@"\d{1,2}:?\d{0,2}\s*[AaPp]\.?[Mm]\.?|NOON");
bool foundTime = false;
var locationParts = new List<string>();
foreach (var part in parts)
{
if (timeRegex.IsMatch(part) || part == "NOON")
{
foundTime = true;
continue;
}
if (foundTime && !string.IsNullOrWhiteSpace(part))
{
locationParts.Add(part);
}
}
if (locationParts.Any())
{
var location = string.Join(" ", locationParts).Trim();
if (!string.IsNullOrWhiteSpace(location))
{
locationPatterns.TryGetValue(location, out var count);
locationPatterns[location] = count + 1;
}
}
}
return locationPatterns;
}
/// <summary>
/// Counts HS vs MS event sections in the file.
/// </summary>
private static (int HighSchool, int MiddleSchool) CountEventSections(List<string> fileLines)
{
int hsCount = 0;
int msCount = 0;
foreach (var line in fileLines)
{
var trimmed = line.Trim();
if (IsHighSchoolEvent(trimmed))
hsCount++;
else if (IsMiddleSchoolEvent(trimmed))
msCount++;
}
return (hsCount, msCount);
}
#endregion
[Test]
public void ParseNationalsTest()
{
var events = TestEntityHandler.GetEvents();
var parser = new EventOccurrenceParser(TestEntityHandler.GetEventOccurrenceNationalsFileInfo(), events);
var result = parser.Parse();
var dictionary = result.Occurrences;
Console.WriteLine($"Occurrence, Month, Date, Time, Location");
foreach (var @event in events)
{
Console.WriteLine($"{@event.Name}");
if (!dictionary.ContainsKey(@event))
{
Console.WriteLine("!!! eventDefinition not found " + @event.Name);
continue;
}
var eventOccurrences = dictionary[@event];
foreach (var eo in eventOccurrences)
{
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
}
}
Console.WriteLine("General Schedule");
if (dictionary.ContainsKey(EventDefinition.GeneralSchedule))
{
foreach (var eo in dictionary[EventDefinition.GeneralSchedule].OrderBy(occurrence => occurrence.StartTime))
{
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
}
}
Console.WriteLine("Meet the Candidates");
if (dictionary.ContainsKey(EventDefinition.MeetTheCandidates))
{
foreach (var eo in dictionary[EventDefinition.MeetTheCandidates])
{
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
}
}
Console.WriteLine("Chapter Officer Meeting");
if (dictionary.ContainsKey(EventDefinition.ChapterOfficerMeeting))
{
foreach (var eo in dictionary[EventDefinition.ChapterOfficerMeeting])
{
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
}
}
Console.WriteLine("Voting Delegate Meeting");
if (dictionary.ContainsKey(EventDefinition.VotingDelegateMeeting))
{
foreach (var eo in dictionary[EventDefinition.VotingDelegateMeeting])
{
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
}
}
Assert.Pass();
}
[Test]
public void ParseStatesTest()
{
var events = TestEntityHandler.GetEvents();
var parser = new EventOccurrenceParser(TestEntityHandler.GetEventOccurrenceStateFileInfo(), events);
var result = parser.Parse();
var dictionary = result.Occurrences;
Console.WriteLine($"Occurrence, Month, Date, Time, Location");
foreach (var @event in events)
{
Console.WriteLine($"{@event.Name}");
if (!dictionary.ContainsKey(@event))
{
Console.WriteLine("!!! eventDefinition not found " + @event.Name);
continue;
}
var eventOccurrences = dictionary[@event];
foreach (var eo in eventOccurrences)
{
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
}
}
Console.WriteLine("General Schedule");
if (dictionary.ContainsKey(EventDefinition.GeneralSchedule))
{
foreach (var eo in dictionary[EventDefinition.GeneralSchedule].OrderBy(occurrence => occurrence.StartTime))
{
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
}
}
Console.WriteLine("Meet the Candidates");
if (dictionary.ContainsKey(EventDefinition.MeetTheCandidates))
{
foreach (var eo in dictionary[EventDefinition.MeetTheCandidates])
{
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
}
}
Console.WriteLine("Chapter Officer Meeting");
if (dictionary.ContainsKey(EventDefinition.ChapterOfficerMeeting))
{
foreach (var eo in dictionary[EventDefinition.ChapterOfficerMeeting])
{
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
}
}
Console.WriteLine("Voting Delegate Meeting");
if (dictionary.ContainsKey(EventDefinition.VotingDelegateMeeting))
{
foreach (var eo in dictionary[EventDefinition.VotingDelegateMeeting])
{
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
}
}
Assert.Pass();
}
[Test]
public void Analyze_2025Nationals_ParsingResults()
{
// Arrange
var events = TestEntityHandler.GetEvents();
var fileInfo = TestEntityHandler.GetEventOccurrenceNationalsFileInfo();
var locationConfig = LocationParsingConfiguration.Default;
var parser = new EventOccurrenceParser(fileInfo, events, locationConfig);
// Act
var result = parser.Parse();
// Assert - Should parse without exceptions
Assert.That(result, Is.Not.Null, "Parser should return a result");
// Load file lines for analysis
var fileLines = File.ReadAllLines(fileInfo.FullName).ToList();
var totalLines = fileLines.Count;
var totalParsed = result.Occurrences.Values.Sum(list => list.Count);
// Categorize issues
var (expectedIssues, fixableIssues) = CategorizeIssues(result.Issues, fileLines);
// Count event sections
var (hsSections, msSections) = CountEventSections(fileLines);
// Group issues by type
var issuesByType = result.Issues.GroupBy(i => i.IssueType).ToDictionary(g => g.Key, g => g.ToList());
var expectedByType = expectedIssues.GroupBy(i => i.IssueType).ToDictionary(g => g.Key, g => g.Count());
var fixableByType = fixableIssues.GroupBy(i => i.IssueType).ToDictionary(g => g.Key, g => g.Count());
// Output analysis
Console.WriteLine($"\n=== 2025 TSA Nationals Competition Event Times Analysis ===");
Console.WriteLine($"\n--- Summary Statistics ---");
Console.WriteLine($"Total lines in file: {totalLines}");
Console.WriteLine($"Total occurrences parsed: {totalParsed}");
Console.WriteLine($"Total issues found: {result.Issues.Count}");
Console.WriteLine($" Expected issues (HS-related): {expectedIssues.Count} ({100.0 * expectedIssues.Count / Math.Max(1, result.Issues.Count):F1}%)");
Console.WriteLine($" Fixable issues: {fixableIssues.Count} ({100.0 * fixableIssues.Count / Math.Max(1, result.Issues.Count):F1}%)");
Console.WriteLine($"Event sections: HS={hsSections}, MS={msSections}");
Console.WriteLine($"Events with occurrences: {result.Occurrences.Count}");
Console.WriteLine($"\n--- Special Events Found ---");
if (result.Occurrences.ContainsKey(EventDefinition.GeneralSchedule))
Console.WriteLine($" GeneralSchedule: {result.Occurrences[EventDefinition.GeneralSchedule].Count} occurrences");
if (result.Occurrences.ContainsKey(EventDefinition.MeetTheCandidates))
Console.WriteLine($" MeetTheCandidates: {result.Occurrences[EventDefinition.MeetTheCandidates].Count} occurrences");
if (result.Occurrences.ContainsKey(EventDefinition.ChapterOfficerMeeting))
Console.WriteLine($" ChapterOfficerMeeting: {result.Occurrences[EventDefinition.ChapterOfficerMeeting].Count} occurrences");
if (result.Occurrences.ContainsKey(EventDefinition.VotingDelegateMeeting))
Console.WriteLine($" VotingDelegateMeeting: {result.Occurrences[EventDefinition.VotingDelegateMeeting].Count} occurrences");
Console.WriteLine($"\n--- Issue Breakdown by Type ---");
foreach (var kvp in issuesByType.OrderByDescending(x => x.Value.Count))
{
var issueType = kvp.Key;
var allIssues = kvp.Value;
expectedByType.TryGetValue(issueType, out var expectedCount);
fixableByType.TryGetValue(issueType, out var fixableCount);
Console.WriteLine($"\n{issueType}:");
Console.WriteLine($" Total: {allIssues.Count} (Expected: {expectedCount}, Fixable: {fixableCount})");
if (fixableCount > 0)
{
var fixableOfType = fixableIssues.Where(i => i.IssueType == issueType).ToList();
var samples = GetSampleLines(fixableOfType, 5);
Console.WriteLine($" Sample fixable issues:");
foreach (var sample in samples)
{
Console.WriteLine(sample);
}
}
}
// Pattern Analysis
var locationFailures = fixableIssues.Where(i => i.IssueType == ParsingIssueType.LocationParseFailure).ToList();
if (locationFailures.Any())
{
Console.WriteLine($"\n--- Location Parse Failure Analysis ---");
var locationPatterns = AnalyzeLocationFailures(locationFailures, fileLines);
var topLocations = locationPatterns.OrderByDescending(x => x.Value).Take(10);
Console.WriteLine($"Top unmatched location strings:");
foreach (var loc in topLocations)
{
Console.WriteLine($" \"{loc.Key}\" (appears {loc.Value} times)");
}
}
var unmatchedLines = fixableIssues.Where(i => i.IssueType == ParsingIssueType.UnmatchedLine).ToList();
if (unmatchedLines.Any())
{
Console.WriteLine($"\n--- Unmatched Line Analysis ---");
var unmatchedPatterns = unmatchedLines
.GroupBy(i => i.LineContent.Trim())
.OrderByDescending(g => g.Count())
.Take(10);
Console.WriteLine($"Top unmatched line formats:");
foreach (var pattern in unmatchedPatterns)
{
Console.WriteLine($" \"{pattern.Key}\" (appears {pattern.Count()} times)");
}
}
// Test passes if no exceptions were thrown
Assert.Pass($"Successfully parsed {totalParsed} occurrences with {result.Issues.Count} issues ({fixableIssues.Count} fixable)");
}
[Test]
public void Analyze_2025State_ParsingResults()
{
// Arrange
var events = TestEntityHandler.GetEvents();
var fileInfo = TestEntityHandler.GetEventOccurrenceStateFileInfo();
var locationConfig = LocationParsingConfiguration.Default;
var parser = new EventOccurrenceParser(fileInfo, events, locationConfig);
// Act
var result = parser.Parse();
// Assert - Should parse without exceptions
Assert.That(result, Is.Not.Null, "Parser should return a result");
// Load file lines for analysis
var fileLines = File.ReadAllLines(fileInfo.FullName).ToList();
var totalLines = fileLines.Count;
var totalParsed = result.Occurrences.Values.Sum(list => list.Count);
// Categorize issues
var (expectedIssues, fixableIssues) = CategorizeIssues(result.Issues, fileLines);
// Count event sections
var (hsSections, msSections) = CountEventSections(fileLines);
// Group issues by type
var issuesByType = result.Issues.GroupBy(i => i.IssueType).ToDictionary(g => g.Key, g => g.ToList());
var expectedByType = expectedIssues.GroupBy(i => i.IssueType).ToDictionary(g => g.Key, g => g.Count());
var fixableByType = fixableIssues.GroupBy(i => i.IssueType).ToDictionary(g => g.Key, g => g.Count());
// Output analysis
Console.WriteLine($"\n=== 2025 TN TSA State Competition Event Times Analysis ===");
Console.WriteLine($"\n--- Summary Statistics ---");
Console.WriteLine($"Total lines in file: {totalLines}");
Console.WriteLine($"Total occurrences parsed: {totalParsed}");
Console.WriteLine($"Total issues found: {result.Issues.Count}");
Console.WriteLine($" Expected issues (HS-related): {expectedIssues.Count} ({100.0 * expectedIssues.Count / Math.Max(1, result.Issues.Count):F1}%)");
Console.WriteLine($" Fixable issues: {fixableIssues.Count} ({100.0 * fixableIssues.Count / Math.Max(1, result.Issues.Count):F1}%)");
Console.WriteLine($"Event sections: HS={hsSections}, MS={msSections}");
Console.WriteLine($"Events with occurrences: {result.Occurrences.Count}");
Console.WriteLine($"\n--- Special Events Found ---");
if (result.Occurrences.ContainsKey(EventDefinition.GeneralSchedule))
Console.WriteLine($" GeneralSchedule: {result.Occurrences[EventDefinition.GeneralSchedule].Count} occurrences");
if (result.Occurrences.ContainsKey(EventDefinition.MeetTheCandidates))
Console.WriteLine($" MeetTheCandidates: {result.Occurrences[EventDefinition.MeetTheCandidates].Count} occurrences");
if (result.Occurrences.ContainsKey(EventDefinition.ChapterOfficerMeeting))
Console.WriteLine($" ChapterOfficerMeeting: {result.Occurrences[EventDefinition.ChapterOfficerMeeting].Count} occurrences");
if (result.Occurrences.ContainsKey(EventDefinition.VotingDelegateMeeting))
Console.WriteLine($" VotingDelegateMeeting: {result.Occurrences[EventDefinition.VotingDelegateMeeting].Count} occurrences");
Console.WriteLine($"\n--- Issue Breakdown by Type ---");
foreach (var kvp in issuesByType.OrderByDescending(x => x.Value.Count))
{
var issueType = kvp.Key;
var allIssues = kvp.Value;
expectedByType.TryGetValue(issueType, out var expectedCount);
fixableByType.TryGetValue(issueType, out var fixableCount);
Console.WriteLine($"\n{issueType}:");
Console.WriteLine($" Total: {allIssues.Count} (Expected: {expectedCount}, Fixable: {fixableCount})");
if (fixableCount > 0)
{
var fixableOfType = fixableIssues.Where(i => i.IssueType == issueType).ToList();
var samples = GetSampleLines(fixableOfType, 5);
Console.WriteLine($" Sample fixable issues:");
foreach (var sample in samples)
{
Console.WriteLine(sample);
}
}
}
// Pattern Analysis
var locationFailures = fixableIssues.Where(i => i.IssueType == ParsingIssueType.LocationParseFailure).ToList();
if (locationFailures.Any())
{
Console.WriteLine($"\n--- Location Parse Failure Analysis ---");
var locationPatterns = AnalyzeLocationFailures(locationFailures, fileLines);
var topLocations = locationPatterns.OrderByDescending(x => x.Value).Take(10);
Console.WriteLine($"Top unmatched location strings:");
foreach (var loc in topLocations)
{
Console.WriteLine($" \"{loc.Key}\" (appears {loc.Value} times)");
}
}
var unmatchedLines = fixableIssues.Where(i => i.IssueType == ParsingIssueType.UnmatchedLine).ToList();
if (unmatchedLines.Any())
{
Console.WriteLine($"\n--- Unmatched Line Analysis ---");
var unmatchedPatterns = unmatchedLines
.GroupBy(i => i.LineContent.Trim())
.OrderByDescending(g => g.Count())
.Take(10);
Console.WriteLine($"Top unmatched line formats:");
foreach (var pattern in unmatchedPatterns)
{
Console.WriteLine($" \"{pattern.Key}\" (appears {pattern.Count()} times)");
}
}
// Test passes if no exceptions were thrown
Assert.Pass($"Successfully parsed {totalParsed} occurrences with {result.Issues.Count} issues ({fixableIssues.Count} fixable)");
}
[Test]
public void Analyze_2024State_ParsingResults()
{
// Arrange
var events = TestEntityHandler.GetEvents();
var fileInfo = TestEntityHandler.GetEventOccurrenceState2024FileInfo();
var locationConfig = LocationParsingConfiguration.Default;
var parser = new EventOccurrenceParser(fileInfo, events, locationConfig);
// Act
var result = parser.Parse();
// Assert - Should parse without exceptions
Assert.That(result, Is.Not.Null, "Parser should return a result");
// Load file lines for analysis
var fileLines = File.ReadAllLines(fileInfo.FullName).ToList();
var totalLines = fileLines.Count;
var totalParsed = result.Occurrences.Values.Sum(list => list.Count);
// Categorize issues
var (expectedIssues, fixableIssues) = CategorizeIssues(result.Issues, fileLines);
// Count event sections
var (hsSections, msSections) = CountEventSections(fileLines);
// Group issues by type
var issuesByType = result.Issues.GroupBy(i => i.IssueType).ToDictionary(g => g.Key, g => g.ToList());
var expectedByType = expectedIssues.GroupBy(i => i.IssueType).ToDictionary(g => g.Key, g => g.Count());
var fixableByType = fixableIssues.GroupBy(i => i.IssueType).ToDictionary(g => g.Key, g => g.Count());
// Output analysis
Console.WriteLine($"\n=== 2024 TN TSA State Competition Event Times Analysis ===");
Console.WriteLine($"\n--- Summary Statistics ---");
Console.WriteLine($"Total lines in file: {totalLines}");
Console.WriteLine($"Total occurrences parsed: {totalParsed}");
Console.WriteLine($"Total issues found: {result.Issues.Count}");
Console.WriteLine($" Expected issues (HS-related): {expectedIssues.Count} ({100.0 * expectedIssues.Count / Math.Max(1, result.Issues.Count):F1}%)");
Console.WriteLine($" Fixable issues: {fixableIssues.Count} ({100.0 * fixableIssues.Count / Math.Max(1, result.Issues.Count):F1}%)");
Console.WriteLine($"Event sections: HS={hsSections}, MS={msSections}");
Console.WriteLine($"Events with occurrences: {result.Occurrences.Count}");
Console.WriteLine($"\n--- Special Events Found ---");
if (result.Occurrences.ContainsKey(EventDefinition.GeneralSchedule))
Console.WriteLine($" GeneralSchedule: {result.Occurrences[EventDefinition.GeneralSchedule].Count} occurrences");
if (result.Occurrences.ContainsKey(EventDefinition.MeetTheCandidates))
Console.WriteLine($" MeetTheCandidates: {result.Occurrences[EventDefinition.MeetTheCandidates].Count} occurrences");
if (result.Occurrences.ContainsKey(EventDefinition.ChapterOfficerMeeting))
Console.WriteLine($" ChapterOfficerMeeting: {result.Occurrences[EventDefinition.ChapterOfficerMeeting].Count} occurrences");
if (result.Occurrences.ContainsKey(EventDefinition.VotingDelegateMeeting))
Console.WriteLine($" VotingDelegateMeeting: {result.Occurrences[EventDefinition.VotingDelegateMeeting].Count} occurrences");
Console.WriteLine($"\n--- Issue Breakdown by Type ---");
foreach (var kvp in issuesByType.OrderByDescending(x => x.Value.Count))
{
var issueType = kvp.Key;
var allIssues = kvp.Value;
expectedByType.TryGetValue(issueType, out var expectedCount);
fixableByType.TryGetValue(issueType, out var fixableCount);
Console.WriteLine($"\n{issueType}:");
Console.WriteLine($" Total: {allIssues.Count} (Expected: {expectedCount}, Fixable: {fixableCount})");
if (fixableCount > 0)
{
var fixableOfType = fixableIssues.Where(i => i.IssueType == issueType).ToList();
var samples = GetSampleLines(fixableOfType, 5);
Console.WriteLine($" Sample fixable issues:");
foreach (var sample in samples)
{
Console.WriteLine(sample);
}
}
}
// Pattern Analysis
var locationFailures = fixableIssues.Where(i => i.IssueType == ParsingIssueType.LocationParseFailure).ToList();
if (locationFailures.Any())
{
Console.WriteLine($"\n--- Location Parse Failure Analysis ---");
var locationPatterns = AnalyzeLocationFailures(locationFailures, fileLines);
var topLocations = locationPatterns.OrderByDescending(x => x.Value).Take(10);
Console.WriteLine($"Top unmatched location strings:");
foreach (var loc in topLocations)
{
Console.WriteLine($" \"{loc.Key}\" (appears {loc.Value} times)");
}
}
var unmatchedLines = fixableIssues.Where(i => i.IssueType == ParsingIssueType.UnmatchedLine).ToList();
if (unmatchedLines.Any())
{
Console.WriteLine($"\n--- Unmatched Line Analysis ---");
var unmatchedPatterns = unmatchedLines
.GroupBy(i => i.LineContent.Trim())
.OrderByDescending(g => g.Count())
.Take(10);
Console.WriteLine($"Top unmatched line formats:");
foreach (var pattern in unmatchedPatterns)
{
Console.WriteLine($" \"{pattern.Key}\" (appears {pattern.Count()} times)");
}
}
// Test passes if no exceptions were thrown
Assert.Pass($"Successfully parsed {totalParsed} occurrences with {result.Issues.Count} issues ({fixableIssues.Count} fixable)");
}
}