Skip to the content.

Testing

Guide to testing, validation, and accuracy guarantees for the Kurdish Calendar library.

Test Coverage

Test Statistics

Test Categories

  1. Date Creation — Constructor validation and edge cases
  2. Date Conversion — Kurdish ↔ Gregorian round-trip
  3. Date Arithmetic — Adding/subtracting days, months, years
  4. Leap Years — Both simplified and astronomical methods
  5. Formatting — All dialects and scripts
  6. Parsing — All supported input formats
  7. Astronomical Calculations — Equinox accuracy
  8. Edge Cases — Boundary conditions and limits

Validation Method

Astronomical Calculations

Algorithm Source:

Validation Source:

Validation Range:

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:

⚠️ Known Limitations:

Astronomical Dates (KurdishAstronomicalDate)

Guaranteed Accurate For:

⚠️ Known Limitations:

Month and Day Names

Verified Sources:

⚠️ Note:

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:

  1. Library version: Check package version
  2. Code snippet: Minimal reproducible example
  3. Expected behaviour: What should happen
  4. Actual behaviour: What actually happens
  5. Environment: OS, .NET version, etc.

Example:

Code:

var date = new KurdishDate(2725, 13, 1);

Feature Requests

For feature requests, please describe:

  1. Use case: Why is this feature needed?
  2. Proposed API: How should it work?
  3. Alternatives considered: Other approaches tried

Contributing Tests

When adding new features, please include:

  1. Unit tests for all new functionality
  2. Edge case tests for boundary conditions
  3. Documentation in code comments
  4. Examples in documentation files

See Contributing Guide for more details.

Test Data Sources

Astronomical Reference Data

Linguistic Reference Data

See Also


بەختێکی باش! (Good luck!)