Testing
Guide to testing, validation, and accuracy guarantees for the Kurdish Calendar library.
Test Coverage
Test Statistics
- Total Tests: 150+ unit tests
- Code Coverage: >95%
- Validated Range: Years 2000-2030 (astronomical)
- Tested Dialects: All 10 combinations
Test Categories
- Date Creation — Constructor validation and edge cases
- Date Conversion — Kurdish ↔ Gregorian round-trip
- Date Arithmetic — Adding/subtracting days, months, years
- Leap Years — Both simplified and astronomical methods
- Formatting — All dialects and scripts
- Parsing — All supported input formats
- Astronomical Calculations — Equinox accuracy
- Edge Cases — Boundary conditions and limits
Validation Method
Astronomical Calculations
Algorithm Source:
- Jean Meeus, “Astronomical Algorithms” (1991/1998), Chapter 27
Validation Source:
- Fred Espenak, “Solstice & Equinox Tables”
- www.Astropixels.com/ephemeris/soleq2001.html
Validation Range:
- Years 2000-2030 tested against published ephemeris data
- Accuracy: ±1 minute for years 1800-2200
Example Validation:
Year | Published Equinox (UTC) | Calculated Equinox (UTC) | Difference
-----|--------------------------|--------------------------|------------
2020 | 2020-03-20 03:50:00 | 2020-03-20 03:50:23 | +23 seconds
2025 | 2025-03-20 09:01:00 | 2025-03-20 09:01:18 | +18 seconds
2030 | 2030-03-20 14:51:00 | 2030-03-20 14:51:12 | +12 seconds
Round-Trip Conversion
All conversions validated for consistency:
[Test]
public void RoundTripConversion_MaintainsDate()
{
// Create Kurdish date
KurdishDate original = new KurdishDate(2725, 6, 15);
// Convert to Gregorian
DateTime gregorian = original.ToDateTime();
// Convert back to Kurdish
KurdishDate roundTrip = KurdishDate.FromDateTime(gregorian);
// Verify equality
Assert.AreEqual(original, roundTrip);
}
Leap Year Validation
[Test]
public void LeapYear_SimplifiedMethod_FollowsPattern()
{
// 33-year cycle: years 1, 5, 9, 13, 17, 22, 26, 30
int[] expectedLeapYears = { 1, 5, 9, 13, 17, 22, 26, 30 };
for (int cycleYear = 1; cycleYear <= 33; cycleYear++)
{
int testYear = 2700 + cycleYear;
var date = new KurdishDate(testYear, 1, 1);
bool expected = expectedLeapYears.Contains(cycleYear);
Assert.AreEqual(expected, date.IsLeapYear);
}
}
Accuracy Guarantees
Simplified Dates (KurdishDate)
✅ Guaranteed Accurate For:
- Date arithmetic (adding/subtracting days, months, years)
- Round-trip conversions (Kurdish ↔ Gregorian)
- Leap year pattern (33-year cycle)
- Internal consistency
⚠️ Known Limitations:
- Uses fixed 21 March for Nowruz (actual equinox varies 19-22 March)
- May differ from astronomical dates by 1-2 days
- Historical dates before 1800 calculated but not validated
Astronomical Dates (KurdishAstronomicalDate)
✅ Guaranteed Accurate For:
- Equinox timing: ±1 minute (years 1800-2200)
- Validated years: 2000-2030 (tested against published data)
- Longitude-based date determination
- Astronomical leap year calculation
⚠️ Known Limitations:
- Historical dates before 1800: calculated but not validated
- Future dates after 2200: slight accuracy degradation
- Requires 4+ decimal places for city-level longitude precision
Month and Day Names
✅ Verified Sources:
- Sorani/Kurmanji Gregorian: Kurdistan Regional Government (gov.krd)
- Hawrami Days: D.N. MacKenzie (1966)
- Hawrami Months: Zaniary.com
⚠️ Note:
- Regional variations may exist
- Community verification recommended for specific localities
Running Tests
Prerequisites
# Install .NET 10.0 SDK
dotnet --version # Should be 10.0 or higher
Clone Repository
git clone https://github.com/57951/KurdishCalendar
cd KurdishCalendar
Run All Tests
cd KurdishCalendar.Tests
dotnet test
Expected Output:
Starting test execution, please wait...
Total tests: 150+
Passed: 150+
Failed: 0
Skipped: 0
Total time: ~2.5 seconds
Run Specific Test Category
# Date conversion tests
dotnet test --filter "Category=Conversion"
# Astronomical tests
dotnet test --filter "Category=Astronomical"
# Formatting tests
dotnet test --filter "Category=Formatting"
# Parsing tests
dotnet test --filter "Category=Parsing"
Run with Detailed Output
dotnet test --logger "console;verbosity=detailed"
Test Examples
Date Creation Tests
[Test]
public void Constructor_ValidDate_CreatesDate()
{
var date = new KurdishDate(2725, 6, 15);
Assert.AreEqual(2725, date.Year);
Assert.AreEqual(6, date.Month);
Assert.AreEqual(15, date.Day);
}
[Test]
public void Constructor_InvalidMonth_ThrowsException()
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
var date = new KurdishDate(2725, 13, 1); // Month 13 doesn't exist
});
}
[Test]
public void Constructor_InvalidDay_ThrowsException()
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
var date = new KurdishDate(2725, 7, 31); // Month 7 has only 30 days
});
}
Conversion Tests
[Test]
public void Newroz2725_ConvertsToMarch21()
{
var kurdish = new KurdishDate(2725, 1, 1);
DateTime gregorian = kurdish.ToDateTime();
Assert.AreEqual(2025, gregorian.Year);
Assert.AreEqual(3, gregorian.Month);
Assert.AreEqual(21, gregorian.Day);
}
[Test]
public void March21_2025_ConvertsToNewroz2725()
{
DateTime gregorian = new DateTime(2025, 3, 21);
var kurdish = KurdishDate.FromDateTime(gregorian);
Assert.AreEqual(2725, kurdish.Year);
Assert.AreEqual(1, kurdish.Month);
Assert.AreEqual(1, kurdish.Day);
}
Arithmetic Tests
[Test]
public void AddDays_OneDay_IncrementsCorrectly()
{
var date = new KurdishDate(2725, 1, 1);
var tomorrow = date.AddDays(1);
Assert.AreEqual(2725, tomorrow.Year);
Assert.AreEqual(1, tomorrow.Month);
Assert.AreEqual(2, tomorrow.Day);
}
[Test]
public void AddMonths_CrossesYearBoundary()
{
var date = new KurdishDate(2724, 12, 15);
var nextMonth = date.AddMonths(1);
Assert.AreEqual(2725, nextMonth.Year);
Assert.AreEqual(1, nextMonth.Month);
Assert.AreEqual(15, nextMonth.Day);
}
Formatting Tests
[Test]
public void Format_SoraniLatin_LongDate()
{
var date = new KurdishDate(2725, 6, 15);
string formatted = date.ToString("D", KurdishDialect.SoraniLatin);
Assert.AreEqual("15 Xermanan 2725", formatted);
}
[Test]
public void Format_SoraniArabic_UsesArabicNumerals()
{
var date = new KurdishDate(2725, 6, 15);
string formatted = date.ToString("d", KurdishDialect.SoraniArabic);
// Eastern Arabic-Indic numerals
Assert.AreEqual("١٥/٠٦/٢٧٢٥", formatted);
}
Parsing Tests
[Test]
public void Parse_LongFormat_ParsesCorrectly()
{
string input = "15 Xermanan 2725";
var parsed = KurdishDate.Parse(input, KurdishDialect.SoraniLatin);
Assert.AreEqual(2725, parsed.Year);
Assert.AreEqual(6, parsed.Month);
Assert.AreEqual(15, parsed.Day);
}
[Test]
public void Parse_InvalidFormat_ThrowsException()
{
Assert.Throws<FormatException>(() =>
{
KurdishDate.Parse("invalid", KurdishDialect.SoraniLatin);
});
}
[Test]
public void TryParse_InvalidFormat_ReturnsFalse()
{
bool success = KurdishDate.TryParse(
"invalid",
KurdishDialect.SoraniLatin,
out KurdishDate result
);
Assert.IsFalse(success);
}
Astronomical Tests
[Test]
public void AstronomicalDate_2025Equinox_WithinOneMinute()
{
// Fred Espenak's published value: 2025-03-20 09:01:00 UTC
DateTime expected = new DateTime(2025, 3, 20, 9, 1, 0, DateTimeKind.Utc);
var newroz = KurdishAstronomicalDate.FromUtc(2725, 1, 1);
DateTime calculated = newroz.ToDateTime();
TimeSpan difference = (calculated - expected).Duration();
Assert.IsTrue(difference.TotalMinutes <= 1.0);
}
[Test]
public void AstronomicalLeapYear_Calculated_From366DayYear()
{
var date = KurdishAstronomicalDate.FromErbil(2725, 1, 1);
// Determine leap year by counting days between consecutive Nowruz dates
DateTime thisNowruz = date.ToDateTime();
var nextYear = KurdishAstronomicalDate.FromErbil(2726, 1, 1);
DateTime nextNowruz = nextYear.ToDateTime();
int daysBetween = (nextNowruz.Date - thisNowruz.Date).Days;
bool expectedLeap = (daysBetween == 366);
Assert.AreEqual(expectedLeap, date.IsLeapYear);
}
Continuous Integration
GitHub Actions Workflow
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '10.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
Performance Testing
Benchmarks
[Benchmark]
public void SimplifiedDateCreation()
{
var date = new KurdishDate(2725, 6, 15);
}
// Result: ~5μs per operation
[Benchmark]
public void AstronomicalDateCreation_Uncached()
{
KurdishAstronomicalDate.ClearEquinoxCache();
var date = KurdishAstronomicalDate.FromErbil(2725, 6, 15);
}
// Result: ~50μs per operation
[Benchmark]
public void AstronomicalDateCreation_Cached()
{
var date = KurdishAstronomicalDate.FromErbil(2725, 6, 15);
}
// Result: ~0.1μs per operation
Edge Cases Tested
Boundary Dates
[Test]
public void LastDayOfYear_NonLeap()
{
var date = new KurdishDate(2724, 12, 29); // Non-leap year
var nextDay = date.AddDays(1);
Assert.AreEqual(2725, nextDay.Year);
Assert.AreEqual(1, nextDay.Month);
Assert.AreEqual(1, nextDay.Day);
}
[Test]
public void LastDayOfYear_Leap()
{
var date = new KurdishDate(2725, 12, 30); // Leap year
var nextDay = date.AddDays(1);
Assert.AreEqual(2726, nextDay.Year);
Assert.AreEqual(1, nextDay.Month);
Assert.AreEqual(1, nextDay.Day);
}
Month Transitions
[Test]
public void AddMonths_From31DayMonth_To30DayMonth()
{
var date = new KurdishDate(2725, 6, 31); // Xermanan (31 days)
var nextMonth = date.AddMonths(1); // Rezber (30 days)
// Should adjust to last day of month
Assert.AreEqual(30, nextMonth.Day);
}
Year Boundaries
[Test]
public void SubtractDays_CrossesYearBoundary()
{
var date = new KurdishDate(2725, 1, 1);
var previousDay = date.AddDays(-1);
Assert.AreEqual(2724, previousDay.Year);
Assert.AreEqual(12, previousDay.Month);
Assert.AreEqual(29, previousDay.Day); // Or 30 if leap year
}
Reporting Issues
Bug Reports
When reporting bugs, please include:
- Library version: Check package version
- Code snippet: Minimal reproducible example
- Expected behaviour: What should happen
- Actual behaviour: What actually happens
- Environment: OS, .NET version, etc.
Example:
- Version: 1.0.0
- Environment: .NET 10.0, Windows 11
Code:
var date = new KurdishDate(2725, 13, 1);
- Expected: ArgumentOutOfRangeException
- Actual: Date created with invalid month
- Additional Context: …
Feature Requests
For feature requests, please describe:
- Use case: Why is this feature needed?
- Proposed API: How should it work?
- Alternatives considered: Other approaches tried
Contributing Tests
When adding new features, please include:
- Unit tests for all new functionality
- Edge case tests for boundary conditions
- Documentation in code comments
- Examples in documentation files
See Contributing Guide for more details.
Test Data Sources
Astronomical Reference Data
- Fred Espenak, “Solstice & Equinox Tables” (www.Astropixels.com)
- NASA JPL Horizons System
- Jean Meeus, “Astronomical Algorithms” (1991/1998)
Linguistic Reference Data
- Kurdistan Regional Government (gov.krd) publications
- D.N. MacKenzie, “The Dialect of Awroman” (1966)
- Zaniary.com Kurdish language resources
See Also
- API Reference — Complete API documentation
- Contributing — Contribution guidelines
- Examples — Practical code examples
بەختێکی باش! (Good luck!)