Skip to content

Commit f864ef3

Browse files
feat: Add ability to scan for missing tests (#103)
* Add scanning for missing tests * Update to LangVersion 12.0 * Update net8.0 Update build-and-test.yml
1 parent 0b13d6b commit f864ef3

12 files changed

+217
-105
lines changed

.github/workflows/build-and-test.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ jobs:
3535
- name: Setup .NET
3636
uses: actions/setup-dotnet@v3
3737
with:
38-
dotnet-version: |
39-
3.1.x
40-
6.x
38+
dotnet-version: 8.x
4139
- name: Setup Dependency Caching
4240
uses: actions/cache@v3
4341
id: nuget-cache

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<PackageReadmeFile>README.md</PackageReadmeFile>
1717
</PropertyGroup>
1818
<PropertyGroup>
19-
<LangVersion>11.0</LangVersion>
19+
<LangVersion>12.0</LangVersion>
2020
<ImplicitUsings>true</ImplicitUsings>
2121
<Nullable>enable</Nullable>
2222
</PropertyGroup>

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
<PackageVersion Include="xunit" Version="2.6.1" />
1515
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.3" />
1616
</ItemGroup>
17-
</Project>
17+
</Project>

EssentialCSharp.ListingManager.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EssentialCSharp.ListingMana
77
EndProject
88
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EssentialCSharp.ListingManager.Tests", "ListingManager.Tests\EssentialCSharp.ListingManager.Tests.csproj", "{4DAA97AA-4025-428B-9DBD-B69F8203C424}"
99
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FD87F126-37E4-4B0D-BE9E-089F62B62206}"
11+
ProjectSection(SolutionItems) = preProject
12+
Directory.Build.props = Directory.Build.props
13+
Directory.Packages.props = Directory.Packages.props
14+
README.md = README.md
15+
EndProjectSection
16+
EndProject
1017
Global
1118
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1219
Debug|Any CPU = Debug|Any CPU

ListingManager.Tests/EssentialCSharp.ListingManager.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net7.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55
<IsPackable>false</IsPackable>
66
</PropertyGroup>
77

ListingManager.Tests/ListingManagerTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public void IsIncorrectListingFromPath_FindsIncorrectListing(string fileName, bo
1717
{
1818
string path = Path.Join(TempDirectory.ToString(), fileName);
1919

20-
bool actualResult = ListingManager.IsExtraListing(path);
20+
bool actualResult = EssentialCSharp.ListingManager.ListingManagerHelpers.IsExtraListing(path);
2121

2222
Assert.Equal(expectedResult, actualResult);
2323
}
@@ -31,7 +31,7 @@ public void ListingsInTestDirectories_AreNotCountedAsExtraListings(string parent
3131

3232
string path = Path.Join(directory.FullName, fileName);
3333

34-
bool actualResult = ListingManager.IsExtraListing(path);
34+
bool actualResult = EssentialCSharp.ListingManager.ListingManagerHelpers.IsExtraListing(path);
3535

3636
Assert.Equal(expectedResult, actualResult);
3737
}
@@ -833,7 +833,7 @@ public void PopulateListingDataFromPath_GivenDirectoryOfListings_PopulateListing
833833
WriteFiles(TempDirectory, filesToMake, toWrite);
834834
expectedFiles = ConvertFileNamesToFullPath(expectedFiles, null).ToList();
835835

836-
List<ListingInformation> listingInformation = ListingManager.PopulateListingDataFromPath(TempDirectory.FullName, true);
836+
List<ListingInformation> listingInformation = EssentialCSharp.ListingManager.ListingManagerHelpers.PopulateListingDataFromPath(TempDirectory.FullName, true);
837837
Assert.Equal(4, listingInformation.Count);
838838
Assert.All(listingInformation, item => Assert.Equal(01, item.OriginalChapterNumber));
839839
Assert.Equal(Path.Join(TempDirectory.FullName, filesToMake[0]), listingInformation[0].Path);
@@ -873,7 +873,7 @@ public void PopulateListingDataFromPath_GivenDirectoryOfListingsAndTests_Associa
873873
CreateTempDirectory(tempDir, name: "Chapter18.Tests");
874874
WriteFiles(tempDir, filesToMake, toWrite);
875875

876-
List<ListingInformation> listingInformation = ListingManager.PopulateListingDataFromPath(tempDir.FullName + $"\\Chapter18", false);
876+
List<ListingInformation> listingInformation = EssentialCSharp.ListingManager.ListingManagerHelpers.PopulateListingDataFromPath(tempDir.FullName + $"\\Chapter18", false);
877877
Assert.Equal(5, listingInformation.Count);
878878
Assert.All(listingInformation, item => Assert.Equal(18, item.OriginalChapterNumber));
879879
Assert.Equal(Path.Join(tempDir.FullName, filesToMake[1]), listingInformation[0].Path);
@@ -917,7 +917,7 @@ public void PopulateListingDataFromPath_GivenDirectoryOfListingsAndTests_UpdateT
917917
CreateTempDirectory(tempDir, name: "Chapter18.Tests");
918918
WriteFiles(tempDir, filesToMake, toWrite);
919919

920-
List<ListingInformation> listingInformation = ListingManager.PopulateListingDataFromPath(tempDir.FullName + $"\\Chapter18", false);
920+
List<ListingInformation> listingInformation = EssentialCSharp.ListingManager.ListingManagerHelpers.PopulateListingDataFromPath(tempDir.FullName + $"\\Chapter18", false);
921921
Assert.Single(listingInformation);
922922
Assert.NotNull(listingInformation.First().AssociatedTest);
923923
Assert.Equal(expected, listingInformation.First().AssociatedTest!.Caption);
@@ -954,7 +954,7 @@ public void PopulateListingDataFromPath_GivenSingleDirectoryOfListingsAndTests_A
954954
DirectoryInfo tempDir = CreateTempDirectory();
955955
WriteFiles(tempDir, filesToMake, toWrite);
956956

957-
List<ListingInformation> listingInformation = ListingManager.PopulateListingDataFromPath(tempDir.FullName, true);
957+
List<ListingInformation> listingInformation = EssentialCSharp.ListingManager.ListingManagerHelpers.PopulateListingDataFromPath(tempDir.FullName, true);
958958
Assert.Equal(5, listingInformation.Count);
959959
Assert.All(listingInformation, item => Assert.Equal(18, item.OriginalChapterNumber));
960960
Assert.Equal(tempDir.FullName + "\\" + filesToMake[1], listingInformation[0].Path);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using IntelliTect.TestTools.Console;
2+
using Xunit;
3+
4+
namespace EssentialCSharp.ListingManager.Tests;
5+
6+
public class ScanManagerTests : TempFileTestBase
7+
{
8+
[Fact]
9+
public void ScanForMissingTests()
10+
{
11+
List<string> filesToMake = new()
12+
{
13+
Path.Join("Chapter01","Listing01.01.SpecifyingLiteralValues.cs"),
14+
Path.Join("Chapter01","Listing01.03.cs"),
15+
Path.Join("Chapter01.Tests","Listing01.03.Tests.cs"),
16+
Path.Join("Chapter02","Listing02.04.cs"),
17+
Path.Join("Chapter02","Listing02.06.Something.cs"),
18+
Path.Join("Chapter02.Tests","Listing02.06.Something.Tests.cs")
19+
};
20+
21+
const string expected = @"Missing test for 1.1
22+
Missing test for 2.4";
23+
24+
List<string> toWrite =
25+
[
26+
"namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter01.Listing01_01",
27+
"{",
28+
" using System;",
29+
" using System.Reflection;",
30+
" public class Program { }",
31+
"}"
32+
];
33+
DirectoryInfo tempDir = CreateTempDirectory();
34+
CreateTempDirectory(tempDir, "Chapter01");
35+
CreateTempDirectory(tempDir, "Chapter01.Tests");
36+
CreateTempDirectory(tempDir, "Chapter02");
37+
CreateTempDirectory(tempDir, "Chapter02.Tests");
38+
WriteFiles(tempDir, filesToMake, toWrite);
39+
40+
ConsoleAssert.Expect(expected, () => ScanManager.ScanForAllMissingTests(tempDir, false));
41+
}
42+
}

ListingManager/EssentialCSharp.ListingManager.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net7.0</TargetFramework>
5+
<TargetFramework>net8.0</TargetFramework>
66
<PackAsTool>true</PackAsTool>
77
<ToolCommandName>ListingManager</ToolCommandName>
88
<RepositoryType>git</RepositoryType>

ListingManager/ListingManager.cs

Lines changed: 5 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -26,36 +26,13 @@ public static IEnumerable<string> GetAllExtraListings(string pathToStartFrom)
2626
{
2727
foreach (string file in FileManager.GetAllFilesAtPath(pathToStartFrom, true))
2828
{
29-
if (IsExtraListing(file))
29+
if (ListingManagerHelpers.IsExtraListing(file))
3030
{
3131
yield return file;
3232
}
3333
}
3434
}
3535

36-
private static bool TryGetListing(string listingPath, [NotNullWhen(true)] out ListingInformation? listingData)
37-
{
38-
return TryGetListing(listingPath, out listingData, false);
39-
}
40-
41-
private static bool TryGetListing(string listingPath, [NotNullWhen(true)] out ListingInformation? listingData, bool isTest)
42-
{
43-
listingData = null;
44-
45-
if (!ListingInformation.ApprovedFileTypes.Contains(Path.GetExtension(listingPath))) return false;
46-
47-
try
48-
{
49-
listingData = new ListingInformation(listingPath, isTest);
50-
}
51-
catch (Exception) // don't care about the type of exception here. If things didn't go perfectly, abort
52-
{
53-
return false;
54-
}
55-
56-
return true;
57-
}
58-
5936
/// <summary>
6037
/// Updates the namespace, file names, and corresponding test file of the target listing. This has a cascading
6138
/// effect, resulting in the renaming of subsequent listings in the same chapter.
@@ -68,7 +45,7 @@ private static bool TryGetListing(string listingPath, [NotNullWhen(true)] out Li
6845
public void UpdateChapterListingNumbers(DirectoryInfo pathToChapter,
6946
bool verbose = false, bool preview = false, bool byFolder = false, bool singleDir = false)
7047
{
71-
List<ListingInformation> listingData = PopulateListingDataFromPath(pathToChapter.FullName, singleDir);
48+
List<ListingInformation> listingData = ListingManagerHelpers.PopulateListingDataFromPath(pathToChapter.FullName, singleDir);
7249
for (int i = 0, listingNumber = 1; i < listingData.Count; i++, listingNumber++)
7350
{
7451
ListingInformation curListingData = listingData[i] ?? throw new InvalidOperationException($"Listing data is null for an index of {i}");
@@ -185,81 +162,19 @@ public static bool GetPathToAccompanyingUnitTest(string listingPath, out string
185162
return false;
186163
}
187164

188-
public static List<ListingInformation> PopulateListingDataFromPath(string pathToChapter, bool singleDir)
189-
{
190-
List<ListingInformation> listingData = new();
191-
List<ListingInformation> testListingData = new();
192-
var listingFiles = FileManager.GetAllFilesAtPath(pathToChapter)
193-
.OrderBy(x => x);
194-
foreach (string fileName in listingFiles)
195-
{
196-
if (TryGetListing(fileName, out ListingInformation? data))
197-
{
198-
if (data.IsTest)
199-
{
200-
testListingData.Add(data);
201-
}
202-
else
203-
{
204-
listingData.Add(data);
205-
}
206-
}
207-
}
208-
209-
if (!singleDir)
210-
{
211-
var listingTestFiles = FileManager.GetAllFilesAtPath($"{pathToChapter}.Tests")
212-
.OrderBy(x => x);
213-
foreach (string fileName in listingTestFiles)
214-
{
215-
if (TryGetListing(fileName, out ListingInformation? data, true))
216-
{
217-
testListingData.Add(data);
218-
}
219-
}
220-
}
221-
222-
foreach (ListingInformation testListingInformation in testListingData)
223-
{
224-
ListingInformation listingInformation = listingData.First(x => x.OriginalListingNumber == testListingInformation.OriginalListingNumber
225-
&& x.OriginalChapterNumber == testListingInformation.OriginalChapterNumber
226-
&& x.OriginalListingNumberSuffix == testListingInformation.OriginalListingNumberSuffix);
227-
228-
if (string.Equals(testListingInformation.Caption, "Tests", StringComparison.InvariantCultureIgnoreCase) && listingInformation.Caption != string.Empty)
229-
{
230-
testListingInformation.Caption = listingInformation.Caption + ".Tests";
231-
}
232-
listingInformation.AssociatedTest = testListingInformation;
233-
}
234-
235-
return listingData;
236-
}
237-
238-
public static bool IsExtraListing(string path, string regexNamespace = @".*Listing\d{2}\.\d{2}(A|B|C|D).*\.cs$")
239-
{
240-
Regex fileNameRegex = new(regexNamespace);
241-
242-
string directoryNameFull = Path.GetDirectoryName(path) ?? string.Empty;
243-
string directoryName = Path.GetFileName(directoryNameFull);
244-
245-
return fileNameRegex.IsMatch(path) && !directoryName.Contains(".Tests");
246-
}
247-
248165
[GeneratedRegex("((Listing\\d{2}\\.\\d{2})([A-Z]?)((\\.Tests)?)).*\\.cs.tmp$")]
249166
private static partial Regex TemporaryListingTestFile();
250167

251-
public void UpdateAllChapterListingNumbers(DirectoryInfo pathToChapter,
168+
public void UpdateAllChapterListingNumbers(DirectoryInfo pathToDirectoryOfChapters,
252169
bool verbose = false, bool preview = false, bool byFolder = false, bool singleDir = false)
253170
{
254-
IEnumerable<DirectoryInfo> directoryInfos = Directory.EnumerateDirectories(pathToChapter.FullName)
171+
IEnumerable<DirectoryInfo> directoryInfos = Directory.EnumerateDirectories(pathToDirectoryOfChapters.FullName)
255172
.Select(x => new DirectoryInfo(x))
256-
.Where(x => ChapterDir().IsMatch(x.Name));
173+
.Where(x => ListingManagerHelpers.ChapterDir().IsMatch(x.Name));
257174

258175
foreach (DirectoryInfo directoryInfo in directoryInfos)
259176
{
260177
UpdateChapterListingNumbers(directoryInfo, verbose, preview, byFolder, singleDir);
261178
}
262179
}
263-
[GeneratedRegex("^Chapter\\d{2}$")]
264-
private static partial Regex ChapterDir();
265180
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using System.Text.RegularExpressions;
3+
4+
namespace EssentialCSharp.ListingManager;
5+
6+
public static partial class ListingManagerHelpers
7+
{
8+
private static bool TryGetListing(string listingPath, [NotNullWhen(true)] out ListingInformation? listingData)
9+
{
10+
return TryGetListing(listingPath, out listingData, false);
11+
}
12+
13+
private static bool TryGetListing(string listingPath, [NotNullWhen(true)] out ListingInformation? listingData, bool isTest)
14+
{
15+
listingData = null;
16+
17+
if (!ListingInformation.ApprovedFileTypes.Contains(Path.GetExtension(listingPath))) return false;
18+
19+
try
20+
{
21+
listingData = new ListingInformation(listingPath, isTest);
22+
}
23+
catch (Exception) // don't care about the type of exception here. If things didn't go perfectly, abort
24+
{
25+
return false;
26+
}
27+
28+
return true;
29+
}
30+
public static bool IsExtraListing(string path, string regexNamespace = @".*Listing\d{2}\.\d{2}(A|B|C|D).*\.cs$")
31+
{
32+
Regex fileNameRegex = new(regexNamespace);
33+
34+
string directoryNameFull = Path.GetDirectoryName(path) ?? string.Empty;
35+
string directoryName = Path.GetFileName(directoryNameFull);
36+
37+
return fileNameRegex.IsMatch(path) && !directoryName.Contains(".Tests");
38+
}
39+
40+
public static List<ListingInformation> PopulateListingDataFromPath(string pathToChapter, bool singleDir)
41+
{
42+
List<ListingInformation> listingData = new();
43+
List<ListingInformation> testListingData = new();
44+
var listingFiles = FileManager.GetAllFilesAtPath(pathToChapter)
45+
.OrderBy(x => x);
46+
foreach (string fileName in listingFiles)
47+
{
48+
if (TryGetListing(fileName, out ListingInformation? data))
49+
{
50+
if (data.IsTest)
51+
{
52+
testListingData.Add(data);
53+
}
54+
else
55+
{
56+
listingData.Add(data);
57+
}
58+
}
59+
}
60+
61+
if (!singleDir)
62+
{
63+
var listingTestFiles = FileManager.GetAllFilesAtPath($"{pathToChapter}.Tests")
64+
.OrderBy(x => x);
65+
foreach (string fileName in listingTestFiles)
66+
{
67+
if (TryGetListing(fileName, out ListingInformation? data, true))
68+
{
69+
testListingData.Add(data);
70+
}
71+
}
72+
}
73+
74+
foreach (ListingInformation testListingInformation in testListingData)
75+
{
76+
ListingInformation listingInformation = listingData.First(x => x.OriginalListingNumber == testListingInformation.OriginalListingNumber
77+
&& x.OriginalChapterNumber == testListingInformation.OriginalChapterNumber
78+
&& x.OriginalListingNumberSuffix == testListingInformation.OriginalListingNumberSuffix);
79+
80+
if (string.Equals(testListingInformation.Caption, "Tests", StringComparison.InvariantCultureIgnoreCase) && listingInformation.Caption != string.Empty)
81+
{
82+
testListingInformation.Caption = listingInformation.Caption + ".Tests";
83+
}
84+
listingInformation.AssociatedTest = testListingInformation;
85+
}
86+
87+
return listingData;
88+
}
89+
90+
[GeneratedRegex("^Chapter\\d{2}$")]
91+
public static partial Regex ChapterDir();
92+
}

0 commit comments

Comments
 (0)