Skip to content

Commit a591ff3

Browse files
authored
Merge pull request #11 from IntelliTect/updateListingManager
Update listing manager
2 parents b34ea99 + da274f7 commit a591ff3

7 files changed

Lines changed: 376 additions & 254 deletions

File tree

ListingManager.Tests/ListingManager.Tests.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@
44
<TargetFramework>netcoreapp3.1</TargetFramework>
55

66
<IsPackable>false</IsPackable>
7+
8+
<LangVersion>10</LangVersion>
9+
10+
<Nullable>enable</Nullable>
711
</PropertyGroup>
812

913
<ItemGroup>
1014
<PackageReference Include="IntelliTect.TestTools.Console" Version="1.0.0-CI-20181030-214503" />
1115
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
1216
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
1317
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
18+
<PackageReference Include="Polly" Version="7.2.3" />
19+
<PackageReference Include="xunit" Version="2.4.1" />
1420
</ItemGroup>
1521

1622
<ItemGroup>

ListingManager.Tests/ListingManagerTests.cs

Lines changed: 161 additions & 219 deletions
Large diffs are not rendered by default.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading;
7+
using Polly;
8+
using Xunit.Sdk;
9+
10+
namespace ListingManager.Tests
11+
{
12+
public abstract class TempFileTestBase : IDisposable
13+
{
14+
private readonly Lazy<DirectoryInfo> _WorkingDirectory = new(() =>
15+
{
16+
DirectoryInfo working = new(Path.Combine(Path.GetTempPath(),
17+
typeof(TempFileTestBase).Assembly.GetName().Name!,
18+
Path.GetRandomFileName()));
19+
20+
if (working.Exists)
21+
{
22+
working.Delete(recursive: true);
23+
}
24+
25+
working.Create();
26+
return working;
27+
}, LazyThreadSafetyMode.ExecutionAndPublication);
28+
29+
private readonly List<FileInfo> _TempFiles = new();
30+
private readonly List<DirectoryInfo> _TempDirectories = new();
31+
protected DirectoryInfo TempDirectory => _WorkingDirectory.Value;
32+
private bool _Disposed;
33+
34+
35+
private FileInfo CreateTempFileWithContent(DirectoryInfo? parentDirectory, string? name = null, byte[]? fileContents = null, string? extension = null)
36+
{
37+
var tempFile = new FileInfo(GetPath(parentDirectory, name, extension, false));
38+
39+
using (FileStream stream = tempFile.OpenWrite())
40+
{
41+
if (fileContents != null)
42+
{
43+
stream.Write(fileContents, 0, fileContents.Length);
44+
}
45+
}
46+
47+
_TempFiles.Add(tempFile);
48+
return tempFile;
49+
}
50+
51+
protected FileInfo CreateTempFile(DirectoryInfo? parentDirectory = null, string? name = null, string? contents = null,
52+
string? extension = null)
53+
{
54+
return CreateTempFileWithContent(parentDirectory, name, contents is null ? null : Encoding.ASCII.GetBytes(contents), extension);
55+
}
56+
57+
public DirectoryInfo CreateTempDirectory(DirectoryInfo? parentDirectory = null, string? name = null)
58+
{
59+
var tempDir = new DirectoryInfo(GetPath(parentDirectory, name, null, false));
60+
tempDir.Create();
61+
_TempDirectories.Add(tempDir);
62+
return tempDir;
63+
}
64+
65+
protected void AddTempDirectory(DirectoryInfo directory)
66+
{
67+
_ = directory ?? throw new ArgumentNullException(nameof(directory));
68+
_TempDirectories.Add(directory);
69+
}
70+
71+
protected void AddTempFile(FileInfo file)
72+
{
73+
_ = file ?? throw new ArgumentNullException(nameof(file));
74+
_TempFiles.Add(file);
75+
}
76+
77+
78+
private string GetPath(DirectoryInfo? parentDirectory, string? name, string? extension, bool randomPrefix)
79+
{
80+
DirectoryInfo directory = parentDirectory ?? _WorkingDirectory.Value;
81+
82+
string prefix = randomPrefix && !string.IsNullOrEmpty(name)
83+
? Path.GetFileNameWithoutExtension(Path.GetRandomFileName())
84+
: "";
85+
86+
string fileName = prefix + (name ?? Path.GetRandomFileName());
87+
88+
if (!string.IsNullOrEmpty(extension))
89+
{
90+
fileName = Path.ChangeExtension(fileName, extension);
91+
}
92+
93+
return Path.Combine(directory.FullName, fileName);
94+
}
95+
96+
protected virtual void Dispose(bool disposing)
97+
{
98+
if (_Disposed || !disposing)
99+
{
100+
return;
101+
}
102+
103+
_Disposed = true;
104+
105+
ExceptionAggregator aggregator = new();
106+
107+
IEnumerable<FileSystemInfo> items = _TempFiles
108+
.Cast<FileSystemInfo>()
109+
.Concat(_TempDirectories)
110+
.Concat(_WorkingDirectory.IsValueCreated ? new[] { _WorkingDirectory.Value } : Enumerable.Empty<DirectoryInfo>());
111+
112+
foreach (FileSystemInfo fsi in items)
113+
{
114+
fsi.Refresh();
115+
if (!fsi.Exists) continue;
116+
117+
Action? action = fsi switch
118+
{
119+
FileInfo file => () => file.Delete(),
120+
DirectoryInfo dir => () => dir.Delete(recursive: true),
121+
_ => null,
122+
};
123+
124+
if (action is null) continue;
125+
126+
aggregator.Run(() =>
127+
{
128+
Policy
129+
.Handle<Exception>()
130+
.WaitAndRetry(retryCount: 100, _ => TimeSpan.FromMilliseconds(10))
131+
.Execute(action);
132+
});
133+
}
134+
135+
if (aggregator.HasExceptions)
136+
{
137+
throw aggregator.ToException();
138+
}
139+
}
140+
141+
public void Dispose()
142+
{
143+
Dispose(disposing: true);
144+
GC.SuppressFinalize(this);
145+
}
146+
}
147+
}

ListingManager/FileManager.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.IO;
34

45
namespace ListingManager
56
{
7+
/// <summary>
8+
/// A path utility class
9+
/// </summary>
610
public static class FileManager
711
{
8-
public static IEnumerable<string> GetAllFilesAtPath(string pathToSearch, bool recursive = false, string searchPattern = "*")
12+
/// <summary>
13+
/// Retrieves all files in the target directory <paramref name="pathToSearch"/> that match the specified pattern <paramref name="searchPattern"/>
14+
/// </summary>
15+
/// <param name="pathToSearch">The target directory</param>
16+
/// <param name="recursive">Whether to recursively descend from the target directory</param>
17+
/// <param name="searchPattern">The search string to match against the names of files in the target path</param>
18+
/// <returns></returns>
19+
public static IEnumerable<string> GetAllFilesAtPath(string pathToSearch, bool recursive = false,
20+
string searchPattern = "*")
921
{
1022
return Directory.EnumerateFiles(pathToSearch,
1123
searchPattern,
1224
recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
1325
}
1426

27+
/// <summary>
28+
/// Parses the numerical value of the chapter number from the specified path <paramref name="pathToChapter"/>.
29+
/// </summary>
30+
/// <param name="pathToChapter">The target chapter</param>
31+
/// <returns></returns>
1532
public static int GetFolderChapterNumber(string pathToChapter)
1633
{
1734
string chapterText = "Chapter";
18-
int startOfChapterNumber = pathToChapter.IndexOf(chapterText) + chapterText.Length;
35+
int startOfChapterNumber =
36+
pathToChapter.IndexOf(chapterText, StringComparison.Ordinal) + chapterText.Length;
1937

2038
return int.Parse(pathToChapter.Substring(startOfChapterNumber, 2));
2139
}

ListingManager/ListingManager.cs

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
namespace ListingManager
1010
{
11+
/// <summary>
12+
/// A utility class providing means to rename listings, namespaces, and corresponding unit tests.
13+
/// </summary>
1114
public static class ListingManager
1215
{
1316
public static IEnumerable<string> GetAllExtraListings(string pathToStartFrom)
@@ -21,7 +24,7 @@ public static IEnumerable<string> GetAllExtraListings(string pathToStartFrom)
2124
}
2225
}
2326

24-
private static bool TryGetListing(string listingPath, out ListingInformation listingData)
27+
private static bool TryGetListing(string listingPath, out ListingInformation? listingData)
2528
{
2629
listingData = null;
2730

@@ -39,10 +42,19 @@ private static bool TryGetListing(string listingPath, out ListingInformation lis
3942
return true;
4043
}
4144

45+
/// <summary>
46+
/// Updates the namespace, file names, and corresponding test file of the target listing. This has a cascading
47+
/// effect, resulting in the renaming of subsequent listings in the same chapter.
48+
/// </summary>
49+
/// <param name="pathToChapter">Path to the target chapter</param>
50+
/// <param name="verbose">When true, enables verbose console output</param>
51+
/// <param name="preview">When true, leaves files in place and only print console output</param>
52+
/// <param name="byFolder">Changes a listing's chapter based on the chapter number in the chapter's path</param>
53+
/// <param name="chapterOnly">Changes only the chapter of the listing, leaving the listing number unchanged. Use with <paramref name="byFolder"/></param>
4254
public static void UpdateChapterListingNumbers(string pathToChapter,
43-
bool verboseMode = false, bool preview = false, bool byFolder = false, bool chapterOnly = false)
55+
bool verbose = false, bool preview = false, bool byFolder = false, bool chapterOnly = false)
4456
{
45-
var listingData = new List<ListingInformation>();
57+
var listingData = new List<ListingInformation?>();
4658

4759

4860
List<string> allListings = FileManager.GetAllFilesAtPath(pathToChapter)
@@ -59,8 +71,9 @@ public static void UpdateChapterListingNumbers(string pathToChapter,
5971
string cur = allListings[i];
6072

6173
var curListingData = listingData[i];
74+
6275

63-
if (!chapterOnly && !byFolder && listingNumber == curListingData.ListingNumber) { continue; } //default
76+
if (curListingData is null || !chapterOnly && !byFolder && listingNumber == curListingData.ListingNumber) { continue; } //default
6477

6578
string completeListingNumber = listingNumber + ""; //default
6679
int listingChapterNumber = curListingData.ChapterNumber; //default
@@ -78,14 +91,14 @@ public static void UpdateChapterListingNumbers(string pathToChapter,
7891

7992
UpdateListingNamespace(cur, listingChapterNumber,
8093
completeListingNumber,
81-
curListingData.ListingDescription, verboseMode, preview);
94+
curListingData.ListingDescription, verbose, preview);
8295

8396
if (GetPathToAccompanyingUnitTest(cur, out string pathToTest))
8497
{
8598
Console.Write("Updating test. ");
8699
UpdateListingNamespace(pathToTest, listingChapterNumber,
87100
completeListingNumber,
88-
curListingData.ListingDescription, verboseMode, preview);
101+
string.IsNullOrEmpty(curListingData.ListingDescription) ? "Tests" : curListingData.ListingDescription + ".Tests", verbose, preview);
89102
}
90103

91104
}
@@ -96,14 +109,22 @@ public static bool IsExtraListing(string path,
96109
{
97110
Regex fileNameRegex = new Regex(regexNamespace);
98111

99-
string directoryNameFull = Path.GetDirectoryName(path);
100-
112+
string directoryNameFull = Path.GetDirectoryName(path) ?? string.Empty;
101113
string directoryName = Path.GetFileName(directoryNameFull);
102114

103115
return fileNameRegex.IsMatch(path) && !directoryName.Contains(".Tests");
104116
}
105117

106-
public static void UpdateListingNamespace(string path, int chapterNumber, string listingNumber,
118+
/// <summary>
119+
/// Updates the namespace and file name of the listing at <paramref name="path"/>
120+
/// </summary>
121+
/// <param name="path">The path to the target listing</param>
122+
/// <param name="chapterNumber">The chapter the listing belongs to</param>
123+
/// <param name="listingNumber">The updated listing number</param>
124+
/// <param name="listingData">The name of the listing to be included in the namespace/path</param>
125+
/// <param name="verbose">When true, enables verbose console output</param>
126+
/// <param name="preview">When true, leaves files in place and only print console output</param>
127+
private static void UpdateListingNamespace(string path, int chapterNumber, string listingNumber,
107128
string listingData, bool verbose = false, bool preview = false)
108129
{
109130
string paddedChapterNumber = chapterNumber.ToString("00");
@@ -127,7 +148,7 @@ public static void UpdateListingNamespace(string path, int chapterNumber, string
127148
string newFileName = string.Format(newFileNameTemplate,
128149
paddedChapterNumber,
129150
paddedListingNumber,
130-
string.IsNullOrWhiteSpace(listingData) ? "" : $".{listingData}");
151+
string.IsNullOrWhiteSpace(listingData) || string.IsNullOrEmpty(listingData) ? "" : $".{listingData}");
131152

132153
if (verbose)
133154
{
@@ -137,19 +158,19 @@ public static void UpdateListingNamespace(string path, int chapterNumber, string
137158
if (!preview) UpdateNamespaceOfPath(path, newNamespace, newFileName);
138159
}
139160

140-
public static bool UpdateNamespaceOfPath(string path, string newNamespace, string newFileName = "")
161+
private static void UpdateNamespaceOfPath(string path, string newNamespace, string newFileName = "")
141162
{
142163
if (Path.GetExtension(path) != ".cs")
143164
{
144-
return false;
165+
return;
145166
}
146167

147168
// read file into memory
148169
string[] allLinesInFile = File.ReadAllLines(path);
149170

150171
File.Delete(path);
151172

152-
string targetPath = Path.Combine(Path.GetDirectoryName(path), newFileName) ?? path;
173+
string targetPath = Path.Combine(Path.GetDirectoryName(path) ?? string.Empty, newFileName) ?? path;
153174

154175
using (TextWriter textWriter = new StreamWriter(targetPath, true))
155176
{
@@ -165,8 +186,6 @@ public static bool UpdateNamespaceOfPath(string path, string newNamespace, strin
165186
}
166187
}
167188
}
168-
169-
return true;
170189
}
171190

172191
public static bool GetPathToAccompanyingUnitTest(string listingPath, out string pathToTest)
@@ -200,7 +219,7 @@ public static bool GetPathToAccompanyingUnitTest(string listingPath, out string
200219
return false;
201220
}
202221

203-
public static string GetTestLayout(string chapterNumber, string listingNumber)
222+
private static string GetTestLayout(string chapterNumber, string listingNumber)
204223
{
205224
return string.Format(TestHeaderLayout,
206225
chapterNumber.PadLeft(2, '0'),
@@ -226,7 +245,7 @@ public void UnitTest1()
226245
}
227246
}";
228247

229-
public static IEnumerable<string> GetAllMissingUnitTests(string pathToChapter)
248+
private static IEnumerable<string> GetAllMissingUnitTests(string pathToChapter)
230249
{
231250
foreach (string file in FileManager.GetAllFilesAtPath(pathToChapter).OrderBy(x => x))
232251
{
@@ -237,7 +256,7 @@ public static IEnumerable<string> GetAllMissingUnitTests(string pathToChapter)
237256
}
238257
}
239258

240-
public static ICollection<string> GenerateUnitTests(string pathToChapter, Func<string, bool> action = null,
259+
public static ICollection<string> GenerateUnitTests(string pathToChapter, Func<string, bool>? action = null,
241260
bool verbose = false)
242261
{
243262
var toReturn = new List<string>();
@@ -267,7 +286,7 @@ public static ICollection<string> GenerateUnitTests(string pathToChapter, Func<s
267286
return toReturn;
268287
}
269288

270-
public static bool GenerateUnitTest(string pathToTest)
289+
private static bool GenerateUnitTest(string pathToTest)
271290
{
272291
if (File.Exists(pathToTest))
273292
{

ListingManager/ListingManager.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
<PackageId>IntelliTect.EssentialCSharp.ListingManager</PackageId>
1111
<Description>Tool used to expose useful functionality to IntelliTect/EssentialCSharp collaborators</Description>
1212
<PackageLicenseExpression>MIT</PackageLicenseExpression>
13+
<LangVersion>10</LangVersion>
14+
<Nullable>enable</Nullable>
15+
<NoWarn>CS1591</NoWarn>
1316
</PropertyGroup>
1417

1518
<ItemGroup>
16-
<PackageReference Include="System.CommandLine.DragonFruit" Version="0.3.0-alpha.19405.1" />
19+
<PackageReference Include="System.CommandLine.DragonFruit" Version="0.4.0-alpha.22114.1" />
1720
</ItemGroup>
1821

1922
</Project>

0 commit comments

Comments
 (0)