Skip to content

Commit 6e16189

Browse files
authored
Merge pull request #29 from magic5644/refactore_code_and_optimize
Refactor exception handling and improve project extension matching
2 parents 350c452 + 6847219 commit 6e16189

File tree

6 files changed

+95
-58
lines changed

6 files changed

+95
-58
lines changed

CodeLineCounter.Tests/DependencyGraphGeneratorTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,31 @@ public async Task generate_graph_with_empty_dependencies_creates_empty_graph()
8585

8686
}
8787

88+
[Fact]
89+
public async Task CompileGraphAndWriteToFile_HandlesInvalidOutputPathGracefully()
90+
{
91+
// Arrange
92+
var dependencies = new List<DependencyRelation>
93+
{
94+
new DependencyRelation { SourceClass = "ClassA", SourceNamespace = "NamespaceA",
95+
SourceAssembly = "AssemblyA", TargetClass = "ClassB", TargetNamespace = "NamespaceB",
96+
TargetAssembly = "AssemblyB", FilePath = "path/to/file", StartLine = 1 }
97+
};
98+
DotGraph graph = DependencyGraphGenerator.GenerateGraphOnly(dependencies);
99+
100+
// Invalid path with characters that aren't allowed in file paths
101+
string invalidOutputPath = Path.Combine(_testDirectory, "invalid_path*?:|<>\\");
102+
string fileName = "test.dot";
103+
104+
// Act & Assert
105+
// The method should handle the exception internally and not throw it to the caller
106+
await DependencyGraphGenerator.CompileGraphAndWriteToFile(fileName, invalidOutputPath, graph);
107+
108+
// Verify the file wasn't created due to the invalid path
109+
Assert.False(File.Exists(Path.Combine(invalidOutputPath, fileName)));
110+
}
111+
112+
88113
[Fact]
89114
public void create_node_sets_correct_fillcolor_and_style_incoming_greater()
90115
{

CodeLineCounter.Tests/FileUtilsTests.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public void get_solution_files_throws_exception_for_nonexistent_directory()
4848
var nonExistentPath = Path.Combine(_testDirectory, Guid.NewGuid().ToString());
4949

5050
// Act & Assert
51-
Assert.Throws<UnauthorizedAccessException>(() => FileUtils.GetSolutionFiles(nonExistentPath));
51+
Assert.Throws<DirectoryNotFoundException>(() => FileUtils.GetSolutionFiles(nonExistentPath));
5252
}
5353

5454
// Throws UnauthorizedAccessException when solution file does not exist
@@ -59,29 +59,23 @@ public void get_project_files_throws_when_file_not_exists()
5959
var nonExistentPath = Path.Combine(_testDirectory, "nonexistent.sln");
6060

6161
// Act & Assert
62-
var exception = Assert.Throws<UnauthorizedAccessException>(() =>
62+
var exception = Assert.Throws<FileNotFoundException>(() =>
6363
FileUtils.GetProjectFiles(nonExistentPath));
6464

6565
Assert.Contains(nonExistentPath, exception.Message);
6666
}
6767

6868
protected override void Dispose(bool disposing)
6969
{
70-
7170
// Ensure the file is closed before attempting to delete it
72-
if (disposing)
71+
if (disposing && Directory.Exists(_testDirectory))
7372
{
74-
if (Directory.Exists(_testDirectory))
75-
{
76-
// Dispose managed resources
77-
Directory.Delete(_testDirectory, true);
78-
}
73+
// Dispose managed resources
74+
Directory.Delete(_testDirectory, true);
7975
}
8076

8177
// Dispose unmanaged resources (if any)
8278
base.Dispose(disposing);
83-
8479
}
85-
8680
}
87-
}
81+
}

CodeLineCounter.Tests/JsonHandlerTests.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,14 @@ public void deserialize_valid_json_file_returns_expected_objects()
4747

4848
protected override void Dispose(bool disposing)
4949
{
50-
if (disposing)
50+
if (disposing && Directory.Exists(_testDirectory))
5151
{
52-
if (Directory.Exists(_testDirectory))
53-
{
54-
// Dispose managed resources
55-
Directory.Delete(_testDirectory, true);
56-
}
52+
// Dispose managed resources
53+
Directory.Delete(_testDirectory, true);
5754
}
5855

5956
// Dispose unmanaged resources (if any)
6057
base.Dispose(disposing);
61-
6258
}
6359

6460

CodeLineCounter.Tests/SolutionAnalyzerTests.cs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ public void analyze_and_export_solution_succeeds_with_valid_inputs()
4141
var format = CoreUtils.ExportFormat.CSV;
4242
var outputPath = _outputPath;
4343

44-
45-
4644
try
4745
{
4846

@@ -80,10 +78,10 @@ public void analyze_and_export_solution_throws_on_invalid_path()
8078
var format = CoreUtils.ExportFormat.JSON;
8179

8280
// Act & Assert
83-
var exception = Assert.Throws<UnauthorizedAccessException>(() =>
81+
var exception = Assert.Throws<FileNotFoundException>(() =>
8482
SolutionAnalyzer.AnalyzeAndExportSolution(invalidPath, verbose, format));
8583

86-
Assert.Contains("Access to the path '' is denied.", exception.Message);
84+
Assert.Contains("File '' not found.", exception.Message);
8785

8886
}
8987

@@ -147,8 +145,7 @@ public void OutputDetailedMetrics_ShouldPrintMetricsAndProjectTotals()
147145
// Arrange
148146
var metrics = new List<NamespaceMetrics>
149147
{
150-
new NamespaceMetrics
151-
{
148+
new() {
152149
ProjectName = "Project1",
153150
ProjectPath = "/path/to/project1",
154151
NamespaceName = "Namespace1",
@@ -157,7 +154,7 @@ public void OutputDetailedMetrics_ShouldPrintMetricsAndProjectTotals()
157154
LineCount = 100,
158155
CyclomaticComplexity = 10
159156
},
160-
new NamespaceMetrics
157+
new ()
161158
{
162159
ProjectName = "Project2",
163160
ProjectPath = "/path/to/project2",
@@ -233,14 +230,11 @@ public void export_results_with_valid_input_exports_all_files()
233230
protected override void Dispose(bool disposing)
234231
{
235232

236-
if (disposing)
233+
if (disposing && Directory.Exists(_testDirectory))
237234
{
238-
if (Directory.Exists(_testDirectory))
239-
{
240-
// Dispose managed resources
241-
File.Delete(_testSolutionPath);
242-
Directory.Delete(_testDirectory, true);
243-
}
235+
// Dispose managed resources
236+
File.Delete(_testSolutionPath);
237+
Directory.Delete(_testDirectory, true);
244238
}
245239

246240
// Dispose unmanaged resources (if any)

CodeLineCounter/Services/DependencyGraphGenerator.cs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -163,21 +163,36 @@ public static DotNode CreateNode(Dictionary<string, (int incoming, int outgoing)
163163

164164
public static async Task CompileGraphAndWriteToFile(string fileName, string outputPath, DotGraph graph)
165165
{
166-
// Use memory buffer
167-
using var memoryStream = MemoryStreamManager.GetStream();
168-
using var writer = new StreamWriter(memoryStream);
169166

170-
var options = new CompilationOptions { Indented = true };
171-
var context = new CompilationContext(writer, options);
172-
graph.Directed = true;
173-
context.DirectedGraph = true;
167+
// Use memory buffer
168+
using var memoryStream = MemoryStreamManager.GetStream();
169+
using var writer = new StreamWriter(memoryStream);
170+
171+
var options = new CompilationOptions { Indented = true };
172+
var context = new CompilationContext(writer, options);
173+
graph.Directed = true;
174+
context.DirectedGraph = true;
175+
176+
await graph.CompileAsync(context);
177+
await writer.FlushAsync(); // Ensure all data is written to memory
174178

175-
await graph.CompileAsync(context);
176-
await writer.FlushAsync(); // Ensure all data is written to memory
179+
memoryStream.Position = 0; // Reset position to start
180+
await WriteMemoryStreamToFile(fileName, outputPath, memoryStream);
181+
182+
}
177183

178-
memoryStream.Position = 0; // Reset position to start
179-
using var fileStream = File.Create(Path.Combine(outputPath, fileName));
180-
await memoryStream.CopyToAsync(fileStream); // Write complete buffer to file
184+
private static async Task WriteMemoryStreamToFile(string fileName, string outputPath, RecyclableMemoryStream memoryStream)
185+
{
186+
try
187+
{
188+
using var fileStream = File.Create(Path.Combine(outputPath, fileName));
189+
await memoryStream.CopyToAsync(fileStream); // Write complete buffer to file
190+
}
191+
catch (Exception ex)
192+
{
193+
// Log or handle the exception as needed
194+
await Console.Error.WriteLineAsync($"Error writing memory stream to file: {ex.Message}");
195+
}
181196
}
182197

183198
public static string EncloseNotEmptyOrNullStringInQuotes(string? str)

CodeLineCounter/Utils/FileUtils.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,46 @@ namespace CodeLineCounter.Utils
44
{
55
public static partial class FileUtils
66
{
7-
public static List<string> GetAllCsFiles(string rootPath)
7+
// Default file extensions
8+
private const string DEFAULT_CODE_EXTENSION = "*.cs";
9+
private const string DEFAULT_SOLUTION_EXTENSION = "*.sln";
10+
private const string DEFAULT_PROJECT_EXTENSION = ".csproj";
11+
12+
public static List<string> GetAllCodeFiles(string rootPath, string fileExtension = DEFAULT_CODE_EXTENSION)
813
{
9-
return Directory.GetFiles(rootPath, "*.cs", SearchOption.AllDirectories)
10-
.Where(f => !f.Contains(@"\obj\"))
11-
.ToList();
14+
var excludeFolders = new[] { @"\obj\", @"\bin\", @"\.*" };
15+
return Directory.GetFiles(rootPath, fileExtension, SearchOption.AllDirectories)
16+
.Where(f => !excludeFolders.Any(ef => f.Contains(ef)))
17+
.ToList();
1218
}
1319

14-
public static List<string> GetSolutionFiles(string rootPath)
20+
// For backward compatibility
21+
public static List<string> GetAllCsFiles(string rootPath) => GetAllCodeFiles(rootPath);
22+
23+
public static List<string> GetSolutionFiles(string rootPath, string fileExtension = DEFAULT_SOLUTION_EXTENSION)
1524
{
1625
if (!Directory.Exists(rootPath))
1726
{
18-
throw new UnauthorizedAccessException($"Access to the path '{rootPath}' is denied.");
27+
throw new DirectoryNotFoundException($"Directory '{rootPath}' not found.");
1928
}
2029

21-
return Directory.GetFiles(rootPath, "*.sln", SearchOption.TopDirectoryOnly).ToList();
30+
return Directory.GetFiles(rootPath, fileExtension, SearchOption.TopDirectoryOnly).ToList();
2231
}
2332

24-
public static List<string> GetProjectFiles(string solutionFilePath)
33+
public static List<string> GetProjectFiles(string solutionFilePath, string projectExtension = DEFAULT_PROJECT_EXTENSION)
2534
{
2635
if (!File.Exists(solutionFilePath))
2736
{
28-
throw new UnauthorizedAccessException($"Access to the path '{solutionFilePath}' is denied.");
37+
throw new FileNotFoundException($"File '{solutionFilePath}' not found.");
2938
}
3039

3140
var projectFiles = new List<string>();
3241
var lines = File.ReadAllLines(solutionFilePath);
3342

3443
foreach (var line in lines)
3544
{
36-
// Search for lines containing projects (Project("...") = "...", "...", "...")
37-
var match = MyRegex().Match(line);
45+
// Search for lines containing projects with the specified extension
46+
var match = GenerateProjectRegex(projectExtension).Match(line);
3847
if (match.Success)
3948
{
4049
var relativePath = match.Groups[1].Value;
@@ -48,11 +57,15 @@ public static List<string> GetProjectFiles(string solutionFilePath)
4857

4958
public static string GetBasePath()
5059
{
51-
// Arrange
5260
return Path.GetDirectoryName(AppContext.BaseDirectory) ?? string.Empty;
5361
}
5462

63+
private static Regex GenerateProjectRegex(string projectExtension)
64+
{
65+
return new Regex($@"Project\(""{{.*}}""\) = "".*"", ""(.*{Regex.Escape(projectExtension)})""", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
66+
}
67+
5568
[GeneratedRegex(@"Project\(""{.*}""\) = "".*"", ""(.*\.csproj)""")]
5669
private static partial Regex MyRegex();
5770
}
58-
}
71+
}

0 commit comments

Comments
 (0)