diff --git a/CodeLineCounter.Tests/DependencyGraphGeneratorTests.cs b/CodeLineCounter.Tests/DependencyGraphGeneratorTests.cs index b99eb3c..b097174 100644 --- a/CodeLineCounter.Tests/DependencyGraphGeneratorTests.cs +++ b/CodeLineCounter.Tests/DependencyGraphGeneratorTests.cs @@ -85,6 +85,31 @@ public async Task generate_graph_with_empty_dependencies_creates_empty_graph() } + [Fact] + public async Task CompileGraphAndWriteToFile_HandlesInvalidOutputPathGracefully() + { + // Arrange + var dependencies = new List + { + new DependencyRelation { SourceClass = "ClassA", SourceNamespace = "NamespaceA", + SourceAssembly = "AssemblyA", TargetClass = "ClassB", TargetNamespace = "NamespaceB", + TargetAssembly = "AssemblyB", FilePath = "path/to/file", StartLine = 1 } + }; + DotGraph graph = DependencyGraphGenerator.GenerateGraphOnly(dependencies); + + // Invalid path with characters that aren't allowed in file paths + string invalidOutputPath = Path.Combine(_testDirectory, "invalid_path*?:|<>\\"); + string fileName = "test.dot"; + + // Act & Assert + // The method should handle the exception internally and not throw it to the caller + await DependencyGraphGenerator.CompileGraphAndWriteToFile(fileName, invalidOutputPath, graph); + + // Verify the file wasn't created due to the invalid path + Assert.False(File.Exists(Path.Combine(invalidOutputPath, fileName))); + } + + [Fact] public void create_node_sets_correct_fillcolor_and_style_incoming_greater() { diff --git a/CodeLineCounter.Tests/FileUtilsTests.cs b/CodeLineCounter.Tests/FileUtilsTests.cs index c21e091..470eabe 100644 --- a/CodeLineCounter.Tests/FileUtilsTests.cs +++ b/CodeLineCounter.Tests/FileUtilsTests.cs @@ -48,7 +48,7 @@ public void get_solution_files_throws_exception_for_nonexistent_directory() var nonExistentPath = Path.Combine(_testDirectory, Guid.NewGuid().ToString()); // Act & Assert - Assert.Throws(() => FileUtils.GetSolutionFiles(nonExistentPath)); + Assert.Throws(() => FileUtils.GetSolutionFiles(nonExistentPath)); } // Throws UnauthorizedAccessException when solution file does not exist @@ -59,7 +59,7 @@ public void get_project_files_throws_when_file_not_exists() var nonExistentPath = Path.Combine(_testDirectory, "nonexistent.sln"); // Act & Assert - var exception = Assert.Throws(() => + var exception = Assert.Throws(() => FileUtils.GetProjectFiles(nonExistentPath)); Assert.Contains(nonExistentPath, exception.Message); @@ -67,21 +67,15 @@ public void get_project_files_throws_when_file_not_exists() protected override void Dispose(bool disposing) { - // Ensure the file is closed before attempting to delete it - if (disposing) + if (disposing && Directory.Exists(_testDirectory)) { - if (Directory.Exists(_testDirectory)) - { - // Dispose managed resources - Directory.Delete(_testDirectory, true); - } + // Dispose managed resources + Directory.Delete(_testDirectory, true); } // Dispose unmanaged resources (if any) base.Dispose(disposing); - } - } -} \ No newline at end of file +} diff --git a/CodeLineCounter.Tests/JsonHandlerTests.cs b/CodeLineCounter.Tests/JsonHandlerTests.cs index 612e93b..68f6d68 100644 --- a/CodeLineCounter.Tests/JsonHandlerTests.cs +++ b/CodeLineCounter.Tests/JsonHandlerTests.cs @@ -47,18 +47,14 @@ public void deserialize_valid_json_file_returns_expected_objects() protected override void Dispose(bool disposing) { - if (disposing) + if (disposing && Directory.Exists(_testDirectory)) { - if (Directory.Exists(_testDirectory)) - { - // Dispose managed resources - Directory.Delete(_testDirectory, true); - } + // Dispose managed resources + Directory.Delete(_testDirectory, true); } // Dispose unmanaged resources (if any) base.Dispose(disposing); - } diff --git a/CodeLineCounter.Tests/SolutionAnalyzerTests.cs b/CodeLineCounter.Tests/SolutionAnalyzerTests.cs index 7d327e3..b59c2d2 100644 --- a/CodeLineCounter.Tests/SolutionAnalyzerTests.cs +++ b/CodeLineCounter.Tests/SolutionAnalyzerTests.cs @@ -41,8 +41,6 @@ public void analyze_and_export_solution_succeeds_with_valid_inputs() var format = CoreUtils.ExportFormat.CSV; var outputPath = _outputPath; - - try { @@ -80,10 +78,10 @@ public void analyze_and_export_solution_throws_on_invalid_path() var format = CoreUtils.ExportFormat.JSON; // Act & Assert - var exception = Assert.Throws(() => + var exception = Assert.Throws(() => SolutionAnalyzer.AnalyzeAndExportSolution(invalidPath, verbose, format)); - Assert.Contains("Access to the path '' is denied.", exception.Message); + Assert.Contains("File '' not found.", exception.Message); } @@ -147,8 +145,7 @@ public void OutputDetailedMetrics_ShouldPrintMetricsAndProjectTotals() // Arrange var metrics = new List { - new NamespaceMetrics - { + new() { ProjectName = "Project1", ProjectPath = "/path/to/project1", NamespaceName = "Namespace1", @@ -157,7 +154,7 @@ public void OutputDetailedMetrics_ShouldPrintMetricsAndProjectTotals() LineCount = 100, CyclomaticComplexity = 10 }, - new NamespaceMetrics + new () { ProjectName = "Project2", ProjectPath = "/path/to/project2", @@ -233,14 +230,11 @@ public void export_results_with_valid_input_exports_all_files() protected override void Dispose(bool disposing) { - if (disposing) + if (disposing && Directory.Exists(_testDirectory)) { - if (Directory.Exists(_testDirectory)) - { - // Dispose managed resources - File.Delete(_testSolutionPath); - Directory.Delete(_testDirectory, true); - } + // Dispose managed resources + File.Delete(_testSolutionPath); + Directory.Delete(_testDirectory, true); } // Dispose unmanaged resources (if any) diff --git a/CodeLineCounter/Services/DependencyGraphGenerator.cs b/CodeLineCounter/Services/DependencyGraphGenerator.cs index df2685c..4596b52 100644 --- a/CodeLineCounter/Services/DependencyGraphGenerator.cs +++ b/CodeLineCounter/Services/DependencyGraphGenerator.cs @@ -163,21 +163,36 @@ public static DotNode CreateNode(Dictionary GetAllCsFiles(string rootPath) + // Default file extensions + private const string DEFAULT_CODE_EXTENSION = "*.cs"; + private const string DEFAULT_SOLUTION_EXTENSION = "*.sln"; + private const string DEFAULT_PROJECT_EXTENSION = ".csproj"; + + public static List GetAllCodeFiles(string rootPath, string fileExtension = DEFAULT_CODE_EXTENSION) { - return Directory.GetFiles(rootPath, "*.cs", SearchOption.AllDirectories) - .Where(f => !f.Contains(@"\obj\")) - .ToList(); + var excludeFolders = new[] { @"\obj\", @"\bin\", @"\.*" }; + return Directory.GetFiles(rootPath, fileExtension, SearchOption.AllDirectories) + .Where(f => !excludeFolders.Any(ef => f.Contains(ef))) + .ToList(); } - public static List GetSolutionFiles(string rootPath) + // For backward compatibility + public static List GetAllCsFiles(string rootPath) => GetAllCodeFiles(rootPath); + + public static List GetSolutionFiles(string rootPath, string fileExtension = DEFAULT_SOLUTION_EXTENSION) { if (!Directory.Exists(rootPath)) { - throw new UnauthorizedAccessException($"Access to the path '{rootPath}' is denied."); + throw new DirectoryNotFoundException($"Directory '{rootPath}' not found."); } - return Directory.GetFiles(rootPath, "*.sln", SearchOption.TopDirectoryOnly).ToList(); + return Directory.GetFiles(rootPath, fileExtension, SearchOption.TopDirectoryOnly).ToList(); } - public static List GetProjectFiles(string solutionFilePath) + public static List GetProjectFiles(string solutionFilePath, string projectExtension = DEFAULT_PROJECT_EXTENSION) { if (!File.Exists(solutionFilePath)) { - throw new UnauthorizedAccessException($"Access to the path '{solutionFilePath}' is denied."); + throw new FileNotFoundException($"File '{solutionFilePath}' not found."); } var projectFiles = new List(); @@ -33,8 +42,8 @@ public static List GetProjectFiles(string solutionFilePath) foreach (var line in lines) { - // Search for lines containing projects (Project("...") = "...", "...", "...") - var match = MyRegex().Match(line); + // Search for lines containing projects with the specified extension + var match = GenerateProjectRegex(projectExtension).Match(line); if (match.Success) { var relativePath = match.Groups[1].Value; @@ -48,11 +57,15 @@ public static List GetProjectFiles(string solutionFilePath) public static string GetBasePath() { - // Arrange return Path.GetDirectoryName(AppContext.BaseDirectory) ?? string.Empty; } + private static Regex GenerateProjectRegex(string projectExtension) + { + return new Regex($@"Project\(""{{.*}}""\) = "".*"", ""(.*{Regex.Escape(projectExtension)})""", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100)); + } + [GeneratedRegex(@"Project\(""{.*}""\) = "".*"", ""(.*\.csproj)""")] private static partial Regex MyRegex(); } -} +} \ No newline at end of file