tests(calendar): Add calendar render tests (#3)
- updates calendar to support overriding the current day when rendering - supports current culture when rendering - renames Calendar to CalendarGenerator
This commit is contained in:
parent
ecb0a20cbb
commit
3547e32b6e
29 changed files with 542 additions and 19 deletions
|
|
@ -3,6 +3,7 @@
|
|||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
|
||||
<PackageVersion Include="System.IO.Packaging" Version="10.0.10" />
|
||||
<PackageVersion Include="System.Security.Cryptography.Xml" Version="10.0.10" />
|
||||
<PackageVersion Include="Microsoft.PowerShell.SDK" Version="7.6.4" />
|
||||
|
|
@ -10,5 +11,7 @@
|
|||
<PackageVersion Include="System.Management.Automation" Version="7.6.4" />
|
||||
<PackageVersion Include="Cake.Frosting" Version="6.2.0" />
|
||||
<PackageVersion Include="Cake.Powershell" Version="4.0.0" />
|
||||
<PackageVersion Include="Verify.XunitV3" Version="31.27.0" />
|
||||
<PackageVersion Include="xunit.v3" Version="3.2.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -12,6 +12,9 @@
|
|||
<Folder Name="/solutionItems/">
|
||||
<File Path="Directory.Packages.props" />
|
||||
</Folder>
|
||||
<Folder Name="/tests/">
|
||||
<Project Path="tests/ModuleTests/ModuleTests.csproj" />
|
||||
</Folder>
|
||||
<Project Path="src/ModuleCore/ModuleCore.csproj" />
|
||||
<Project Path="src/PowershellModule/PowershellModule.csproj" />
|
||||
</Solution>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
using System.Globalization;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Text;
|
||||
|
||||
namespace ModuleCore.Calendar;
|
||||
|
||||
public sealed class Calendar
|
||||
public sealed class CalendarGenerator
|
||||
{
|
||||
public const string DefaultMarkedOffSymbol = "x";
|
||||
public const DayOfWeek DefaultStartOfWeek = DayOfWeek.Monday;
|
||||
|
|
@ -30,7 +29,7 @@ public sealed class Calendar
|
|||
|
||||
private readonly string _rolloverDaySymbol = "";
|
||||
|
||||
public Calendar(string? markedDaySymbol = null, DayOfWeek? startOfWeek = null)
|
||||
public CalendarGenerator(string? markedDaySymbol = null, DayOfWeek? startOfWeek = null)
|
||||
{
|
||||
_markedOffSymbol = markedDaySymbol ?? DefaultMarkedOffSymbol;
|
||||
_startOfWeek = startOfWeek ?? DefaultStartOfWeek;
|
||||
|
|
@ -38,10 +37,7 @@ public sealed class Calendar
|
|||
// Generate localised day of week lookup table
|
||||
_daysOfWeekLookup = new List<string>(7);
|
||||
_dayWidth = _markedOffSymbol.Length;
|
||||
// TODO: make this passed in from configuration, and if its invalid default it to
|
||||
// CultureInfo.CurrentCulture
|
||||
// TODO: test if its possible to determine if a culture string is invalid and what happens
|
||||
_cultureInfo = new CultureInfo("en-AU");
|
||||
_cultureInfo = CultureInfo.CurrentCulture;
|
||||
for (var i = 0; i < 7; i++)
|
||||
{
|
||||
var localisedDayAbbreviation = _cultureInfo.DateTimeFormat.GetAbbreviatedDayName((DayOfWeek)i);
|
||||
|
|
@ -68,13 +64,19 @@ public sealed class Calendar
|
|||
_dayWidth += 2;
|
||||
}
|
||||
|
||||
public string RenderCalendar(DateTime month)
|
||||
/// <summary>
|
||||
/// Renders the configured calendar for the given datetime
|
||||
/// </summary>
|
||||
/// <param name="month"></param>
|
||||
/// <param name="todayOverride">Defaults to <see cref="DateTime.Now"/>, used to display elapsed days</param>
|
||||
/// <returns></returns>
|
||||
public string Render(DateTime month, DateTime? todayOverride = null)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
// Generate the header for the month/days of week
|
||||
GenerateHeader(sb, month);
|
||||
|
||||
var calendarForMonth = GenerateCalendarForMonth(month, _markedOffSymbol, _startOfWeek, _rolloverDaySymbol);
|
||||
var calendarForMonth = GenerateCalendarForMonth(month, _markedOffSymbol, _startOfWeek, _rolloverDaySymbol, todayOverride ?? DateTime.Now);
|
||||
|
||||
for (var i = 0; i < calendarForMonth.WeeksInMonth; i++)
|
||||
{
|
||||
|
|
@ -168,9 +170,10 @@ public sealed class Calendar
|
|||
}
|
||||
}
|
||||
|
||||
private MonthCalendar GenerateCalendarForMonth(DateTime month, string markedOffSymbol, DayOfWeek calendarStartOfWeek, string rolloverDaySymbol)
|
||||
private MonthCalendar GenerateCalendarForMonth(DateTime month, string markedOffSymbol, DayOfWeek calendarStartOfWeek, string rolloverDaySymbol, DateTime today)
|
||||
{
|
||||
// TODO: use startOfWeek to offset the start of the calendar later
|
||||
// TODO: I think I meant the calendar name header?
|
||||
|
||||
// Reset the month to the start
|
||||
var startOfMonth = new DateTime(month.Year, month.Month, 1);
|
||||
|
|
@ -218,7 +221,6 @@ public sealed class Calendar
|
|||
cal.Add(rolloverDaySymbol);
|
||||
}
|
||||
|
||||
var today = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
|
||||
// If we're in the same year and month, any days already elapsed should be marked off,
|
||||
// otherwise we display all days.
|
||||
// If the DateTime we're rendering is in the past we render all days as marked off
|
||||
|
|
@ -8,10 +8,10 @@ class Program
|
|||
static void Main(string[] args)
|
||||
{
|
||||
Console.OutputEncoding = Encoding.Unicode;
|
||||
var a = new Calendar();
|
||||
var lastMonth = a.RenderCalendar(DateTime.Now.AddMonths(-1));
|
||||
var now = a.RenderCalendar(DateTime.Now);
|
||||
var august = a.RenderCalendar(DateTime.Now.AddMonths(1));
|
||||
var a = new CalendarGenerator();
|
||||
var lastMonth = a.Render(DateTime.Now.AddMonths(-1));
|
||||
var now = a.Render(DateTime.Now);
|
||||
var august = a.Render(DateTime.Now.AddMonths(1));
|
||||
|
||||
Console.Write(lastMonth);
|
||||
Console.Write(now);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Management.Automation;
|
||||
using ModuleCore.Calendar;
|
||||
|
||||
namespace PowershellModule.Calendar
|
||||
{
|
||||
|
|
@ -42,7 +43,7 @@ namespace PowershellModule.Calendar
|
|||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
|
||||
// Guaranteed to be initialised in BeginProcessing. If it's not, something is cooked in some other way
|
||||
private ModuleCore.Calendar.Calendar _calendar = null!;
|
||||
private CalendarGenerator _calendar = null!;
|
||||
private const string Noun = "Calendar";
|
||||
public const string FullName = $"{VerbsCommon.Get}-{Noun}";
|
||||
|
||||
|
|
@ -80,7 +81,7 @@ namespace PowershellModule.Calendar
|
|||
{
|
||||
if (dayOfWeek is null)
|
||||
{
|
||||
return ModuleCore.Calendar.Calendar.DefaultStartOfWeek;
|
||||
return CalendarGenerator.DefaultStartOfWeek;
|
||||
}
|
||||
|
||||
return dayOfWeek.ToLowerInvariant() switch
|
||||
|
|
@ -92,13 +93,13 @@ namespace PowershellModule.Calendar
|
|||
"thursday" => DayOfWeek.Thursday,
|
||||
"friday" => DayOfWeek.Friday,
|
||||
"saturday" => DayOfWeek.Saturday,
|
||||
_ => ModuleCore.Calendar.Calendar.DefaultStartOfWeek
|
||||
_ => CalendarGenerator.DefaultStartOfWeek
|
||||
};
|
||||
}
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var calendar = _calendar.RenderCalendar(_dateToRender);
|
||||
var calendar = _calendar.Render(_dateToRender);
|
||||
|
||||
// From _very_ basic testing, string split allocates the same as making a span and iterating over it.
|
||||
// I'm assuming this is because a lot of the span code is what string.Split() does internally and the rest
|
||||
|
|
|
|||
48
tests/ModuleTests/Calendar/BasicRenderTest.cs
Normal file
48
tests/ModuleTests/Calendar/BasicRenderTest.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using ModuleCore.Calendar;
|
||||
using ModuleTests.Calendar.TestData;
|
||||
using System.Globalization;
|
||||
|
||||
namespace ModuleTests.Calendar;
|
||||
|
||||
public class BasicRenderTest
|
||||
{
|
||||
private static readonly VerifySettings Settings;
|
||||
|
||||
static BasicRenderTest()
|
||||
{
|
||||
Settings = new VerifySettings();
|
||||
var testBaseDirectory = Path.Join(TestConstants.SnapshotFolderName, nameof(BasicRenderTest));
|
||||
|
||||
Settings.UseDirectory(testBaseDirectory);
|
||||
Settings.DisableDiff();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ClassData(typeof(CalendarTestDates))]
|
||||
public Task BasicCalendarRender(DateTime month)
|
||||
{
|
||||
Settings.UseFileName($"{month:yyyy-MM-dd}");
|
||||
|
||||
var calendar = new CalendarGenerator();
|
||||
// Render the calendar with the start of the month as the today date so no elapsed marks are shown
|
||||
var renderedMonth = calendar.Render(month, new DateTime(month.Year, month.Month, 1));
|
||||
|
||||
return Verify(renderedMonth, Settings);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ClassData(typeof(CultureCodeTestData))]
|
||||
public Task CultureInfoCalendarRender(string cultureCode)
|
||||
{
|
||||
Settings.UseFileName($"{cultureCode}");
|
||||
|
||||
Thread.CurrentThread.CurrentCulture = new CultureInfo(cultureCode);
|
||||
|
||||
var month = new DateTime(2026, 8, 1);
|
||||
var calendar = new CalendarGenerator();
|
||||
// Render the calendar with the start of the month as the today date so no elapsed marks are shown
|
||||
var renderedMonth = calendar.Render(month, new DateTime(month.Year, month.Month, 1));
|
||||
|
||||
return Verify(renderedMonth, Settings);
|
||||
}
|
||||
}
|
||||
86
tests/ModuleTests/Calendar/MarkedDayRenderTests.cs
Normal file
86
tests/ModuleTests/Calendar/MarkedDayRenderTests.cs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
using System.Text;
|
||||
using ModuleCore.Calendar;
|
||||
|
||||
namespace ModuleTests.Calendar;
|
||||
|
||||
public class MarkedDayRenderTests
|
||||
{
|
||||
private static readonly VerifySettings Settings;
|
||||
|
||||
static MarkedDayRenderTests()
|
||||
{
|
||||
Settings = new VerifySettings();
|
||||
var testBaseDirectory = Path.Join(TestConstants.SnapshotFolderName, nameof(MarkedDayRenderTests));
|
||||
|
||||
Settings.UseDirectory(testBaseDirectory);
|
||||
Settings.DisableDiff();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task DefaultMarkedDayRender()
|
||||
{
|
||||
Settings.UseFileName(nameof(DefaultMarkedDayRender));
|
||||
|
||||
var date = new DateTime(2026, 8, 1);
|
||||
var calendar = new CalendarGenerator();
|
||||
var renderedMonth = calendar.Render(date, date.AddDays(15));
|
||||
|
||||
return Verify(renderedMonth, Settings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task SimpleMarkedDayRender()
|
||||
{
|
||||
Settings.UseFileName(nameof(SimpleMarkedDayRender));
|
||||
|
||||
var date = new DateTime(2026, 8, 1);
|
||||
var calendar = new CalendarGenerator("|");
|
||||
var renderedMonth = calendar.Render(date, date.AddDays(15));
|
||||
|
||||
return Verify(renderedMonth, Settings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task LongMarkedDayRender()
|
||||
{
|
||||
Settings.UseFileName(nameof(LongMarkedDayRender));
|
||||
|
||||
var date = new DateTime(2026, 8, 1);
|
||||
var calendar = new CalendarGenerator("passed");
|
||||
var renderedMonth = calendar.Render(date, date.AddDays(15));
|
||||
|
||||
return Verify(renderedMonth, Settings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task EmojiMarkedDayRender()
|
||||
{
|
||||
Settings.UseFileName(nameof(EmojiMarkedDayRender));
|
||||
|
||||
var date = new DateTime(2026, 8, 1);
|
||||
var calendar = new CalendarGenerator("🤫");
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("Emojis will display incorrectly in text but output correctly in terminals with UTF-8 encoding.")
|
||||
.AppendLine(calendar.Render(date, date.AddDays(15)));
|
||||
|
||||
return Verify(sb, Settings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task DoubleWideMarkedDayRender()
|
||||
{
|
||||
Settings.UseFileName(nameof(DoubleWideMarkedDayRender));
|
||||
|
||||
var date = new DateTime(2026, 8, 1);
|
||||
// Using an asian-character to have a test covering the unfortunate reality of character rendering of
|
||||
// characters that are visually 1 character in width, but render as double width or some other cursed form.
|
||||
// Written language was a mistake but trying to render it on computers alongside a different one
|
||||
// is arguably worse.
|
||||
var calendar = new CalendarGenerator("火");
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("Double-wide characters will display incorrectly but still tested until rendering can be improved")
|
||||
.AppendLine(calendar.Render(date, date.AddDays(15)));
|
||||
|
||||
return Verify(sb, Settings);
|
||||
}
|
||||
}
|
||||
35
tests/ModuleTests/Calendar/StartDayOfWeekTests.cs
Normal file
35
tests/ModuleTests/Calendar/StartDayOfWeekTests.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using System.Text;
|
||||
using ModuleCore.Calendar;
|
||||
|
||||
namespace ModuleTests.Calendar;
|
||||
|
||||
public class StartDayOfWeekTests
|
||||
{
|
||||
private static readonly VerifySettings Settings;
|
||||
|
||||
static StartDayOfWeekTests()
|
||||
{
|
||||
Settings = new VerifySettings();
|
||||
var testBaseDirectory = Path.Join(TestConstants.SnapshotFolderName, nameof(StartDayOfWeekTests));
|
||||
|
||||
Settings.UseDirectory(testBaseDirectory);
|
||||
Settings.DisableDiff();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task StartOfDayRender()
|
||||
{
|
||||
Settings.UseFileName(nameof(StartOfDayRender));
|
||||
|
||||
var date = new DateTime(2026, 8, 1);
|
||||
var sb = new StringBuilder();
|
||||
foreach (var dayOfWeek in Enum.GetValues<DayOfWeek>())
|
||||
{
|
||||
var calendar = new CalendarGenerator(startOfWeek: dayOfWeek);
|
||||
sb.AppendLine($"Start Day: {dayOfWeek}");
|
||||
sb.AppendLine(calendar.Render(date, date));
|
||||
}
|
||||
|
||||
return Verify(sb, Settings);
|
||||
}
|
||||
}
|
||||
14
tests/ModuleTests/Calendar/TestData/CalendarTestDates.cs
Normal file
14
tests/ModuleTests/Calendar/TestData/CalendarTestDates.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
namespace ModuleTests.Calendar.TestData;
|
||||
|
||||
public class CalendarTestDates : TestDataEnumerator<DateTime>
|
||||
{
|
||||
public CalendarTestDates()
|
||||
{
|
||||
Data =
|
||||
[
|
||||
new DateTime(2026, 06, 01),
|
||||
new DateTime(2026, 07, 01),
|
||||
new DateTime(2026, 08, 01),
|
||||
];
|
||||
}
|
||||
}
|
||||
19
tests/ModuleTests/Calendar/TestData/CultureCodeTestData.cs
Normal file
19
tests/ModuleTests/Calendar/TestData/CultureCodeTestData.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
namespace ModuleTests.Calendar.TestData;
|
||||
|
||||
public class CultureCodeTestData : TestDataEnumerator<string>
|
||||
{
|
||||
public CultureCodeTestData()
|
||||
{
|
||||
Data =
|
||||
[
|
||||
"da-DK",
|
||||
"en-AU",
|
||||
"cs-CZ",
|
||||
"es-PR",
|
||||
"fr-LU",
|
||||
"nl-NL",
|
||||
// Will render unaligned due to the character encoding, here to catch if I fix this later
|
||||
"te-IN"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
┌─────────────────────────────────────────┐
|
||||
│ June 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
|
||||
│ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │
|
||||
│ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │
|
||||
│ 22 │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 │
|
||||
│ 29 │ 30 │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
┌─────────────────────────────────────────┐
|
||||
│ July 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ 1 │ 2 │ 3 │ 4 │ 5 │
|
||||
│ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │
|
||||
│ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │
|
||||
│ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 │
|
||||
│ 27 │ 28 │ 29 │ 30 │ 31 │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ │ │ 1 │ 2 │
|
||||
│ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
|
||||
│ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌──────────────────────────────────┐
|
||||
│ srpen 2026 │
|
||||
├────┬────┬────┬────┬────┬────┬────┤
|
||||
│ po │ út │ st │ čt │ pá │ so │ ne │
|
||||
├────┼────┼────┼────┼────┼────┼────┤
|
||||
│ │ │ │ │ │ 1 │ 2 │
|
||||
│ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
|
||||
│ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└────┴────┴────┴────┴────┴────┴────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌─────────────────────────────────────────┐
|
||||
│ august 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ man │ tir │ ons │ tor │ fre │ lør │ søn │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ │ │ 1 │ 2 │
|
||||
│ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
|
||||
│ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ │ │ 1 │ 2 │
|
||||
│ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
|
||||
│ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌────────────────────────────────────────────────┐
|
||||
│ agosto 2026 │
|
||||
├──────┬──────┬──────┬──────┬──────┬──────┬──────┤
|
||||
│ lun. │ mar. │ mié. │ jue. │ vie. │ sáb. │ dom. │
|
||||
├──────┼──────┼──────┼──────┼──────┼──────┼──────┤
|
||||
│ │ │ │ │ │ 1 │ 2 │
|
||||
│ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
|
||||
│ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└──────┴──────┴──────┴──────┴──────┴──────┴──────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌────────────────────────────────────────────────┐
|
||||
│ août 2026 │
|
||||
├──────┬──────┬──────┬──────┬──────┬──────┬──────┤
|
||||
│ lun. │ mar. │ mer. │ jeu. │ ven. │ sam. │ dim. │
|
||||
├──────┼──────┼──────┼──────┼──────┼──────┼──────┤
|
||||
│ │ │ │ │ │ 1 │ 2 │
|
||||
│ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
|
||||
│ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└──────┴──────┴──────┴──────┴──────┴──────┴──────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌──────────────────────────────────┐
|
||||
│ augustus 2026 │
|
||||
├────┬────┬────┬────┬────┬────┬────┤
|
||||
│ ma │ di │ wo │ do │ vr │ za │ zo │
|
||||
├────┼────┼────┼────┼────┼────┼────┤
|
||||
│ │ │ │ │ │ 1 │ 2 │
|
||||
│ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
|
||||
│ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└────┴────┴────┴────┴────┴────┴────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌───────────────────────────────────────────────────────┐
|
||||
│ ఆగస్టు 2026 │
|
||||
├───────┬───────┬───────┬───────┬───────┬───────┬───────┤
|
||||
│ సోమ │ మంగళ │ బుధ │ గురు │ శుక్ర │ శని │ ఆది │
|
||||
├───────┼───────┼───────┼───────┼───────┼───────┼───────┤
|
||||
│ │ │ │ │ │ 1 │ 2 │
|
||||
│ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
|
||||
│ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└───────┴───────┴───────┴───────┴───────┴───────┴───────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ │ │ x │ x │
|
||||
│ x │ x │ x │ x │ x │ x │ x │
|
||||
│ x │ x │ x │ x │ x │ x │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
Double-wide characters will display incorrectly but still tested until rendering can be improved
|
||||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ │ │ 火 │ 火 │
|
||||
│ 火 │ 火 │ 火 │ 火 │ 火 │ 火 │ 火 │
|
||||
│ 火 │ 火 │ 火 │ 火 │ 火 │ 火 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
Emojis will display incorrectly in text but output correctly in terminals with UTF-8 encoding.
|
||||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ │ │ 🤫 │ 🤫 │
|
||||
│ 🤫 │ 🤫 │ 🤫 │ 🤫 │ 🤫 │ 🤫 │ 🤫 │
|
||||
│ 🤫 │ 🤫 │ 🤫 │ 🤫 │ 🤫 │ 🤫 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├────────┬────────┬────────┬────────┬────────┬────────┬────────┤
|
||||
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │
|
||||
├────────┼────────┼────────┼────────┼────────┼────────┼────────┤
|
||||
│ │ │ │ │ │ passed │ passed │
|
||||
│ passed │ passed │ passed │ passed │ passed │ passed │ passed │
|
||||
│ passed │ passed │ passed │ passed │ passed │ passed │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└────────┴────────┴────────┴────────┴────────┴────────┴────────┘
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ │ │ | │ | │
|
||||
│ | │ | │ | │ | │ | │ | │ | │
|
||||
│ | │ | │ | │ | │ | │ | │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
Start Day: Sunday
|
||||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ │ │ │ 1 │
|
||||
│ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │
|
||||
│ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │
|
||||
│ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │
|
||||
│ 23 │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │
|
||||
│ 30 │ 31 │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
Start Day: Monday
|
||||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ │ │ 1 │ 2 │
|
||||
│ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
|
||||
│ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │
|
||||
│ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │
|
||||
│ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
|
||||
│ 31 │ │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
Start Day: Tuesday
|
||||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Tue │ Wed │ Thu │ Fri │ Sat │ Sun │ Mon │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ │ 1 │ 2 │ 3 │
|
||||
│ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │
|
||||
│ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │
|
||||
│ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │
|
||||
│ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
Start Day: Wednesday
|
||||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Wed │ Thu │ Fri │ Sat │ Sun │ Mon │ Tue │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ │ 1 │ 2 │ 3 │ 4 │
|
||||
│ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
|
||||
│ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │
|
||||
│ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │
|
||||
│ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
Start Day: Thursday
|
||||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Thu │ Fri │ Sat │ Sun │ Mon │ Tue │ Wed │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ │ 1 │ 2 │ 3 │ 4 │ 5 │
|
||||
│ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │
|
||||
│ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │
|
||||
│ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 │
|
||||
│ 27 │ 28 │ 29 │ 30 │ 31 │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
Start Day: Friday
|
||||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Fri │ Sat │ Sun │ Mon │ Tue │ Wed │ Thu │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │
|
||||
│ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │
|
||||
│ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │
|
||||
│ 21 │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 │
|
||||
│ 28 │ 29 │ 30 │ 31 │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
Start Day: Saturday
|
||||
┌─────────────────────────────────────────┐
|
||||
│ August 2026 │
|
||||
├─────┬─────┬─────┬─────┬─────┬─────┬─────┤
|
||||
│ Sat │ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │
|
||||
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
|
||||
│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
|
||||
│ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │
|
||||
│ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │
|
||||
│ 22 │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 │
|
||||
│ 29 │ 30 │ 31 │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
25
tests/ModuleTests/ModuleTests.csproj
Normal file
25
tests/ModuleTests/ModuleTests.csproj
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Verify.XunitV3" />
|
||||
<PackageReference Include="xunit.v3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\ModuleCore\ModuleCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
6
tests/ModuleTests/TestConstants.cs
Normal file
6
tests/ModuleTests/TestConstants.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
namespace ModuleTests;
|
||||
|
||||
public class TestConstants
|
||||
{
|
||||
public const string SnapshotFolderName = "Snapshots";
|
||||
}
|
||||
15
tests/ModuleTests/TestDataEnumerator.cs
Normal file
15
tests/ModuleTests/TestDataEnumerator.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System.Collections;
|
||||
|
||||
namespace ModuleTests;
|
||||
|
||||
public abstract class TestDataEnumerator<T> : IEnumerable<TheoryDataRow<T>>
|
||||
{
|
||||
protected IEnumerable<T> Data { get; init; } = [];
|
||||
|
||||
public IEnumerator<TheoryDataRow<T>> GetEnumerator()
|
||||
{
|
||||
return Data.Select(data => new TheoryDataRow<T>(data)).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
Loading…
Reference in a new issue