File 9122-Write-tests.patch of Package erlang

From 7d8d38cbdcc044f5c46e5bd86b00b327a4c44d2e Mon Sep 17 00:00:00 2001
From: Daniel Kukula <daniel.kuku@gmail.com>
Date: Mon, 8 Dec 2025 17:20:38 +0100
Subject: [PATCH 2/4] Write tests

---
 lib/stdlib/src/calendar.erl                   |  4 +-
 lib/stdlib/test/calendar_SUITE.erl            | 95 +++++++++++++++++--
 lib/stdlib/test/calendar_prop_SUITE.erl       | 34 ++++++-
 .../test/property_test/calendar_prop.erl      | 73 ++++++++++++++
 4 files changed, 191 insertions(+), 15 deletions(-)

diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl
index e2521dbcf2..db6c5bed1e 100644
--- a/lib/stdlib/src/calendar.erl
+++ b/lib/stdlib/src/calendar.erl
@@ -237,7 +237,7 @@ date_to_gregorian_days(Year, Month, Day) when is_integer(Day), Day > 0 ->
     Era = if Y >= 0 -> Y div ?YEARS_PER_ERA; true -> (Y - 399) div ?YEARS_PER_ERA end,
     YearOfEra = Y - Era * ?YEARS_PER_ERA,
     MonthPrime = if Month > 2 -> Month - 3; true -> Month + 9 end,
-    DayOfYear = ?DAYS_PER_5_MONTHS * MonthPrime div ?MONTHS_PER_CYCLE + Day - 1,
+    DayOfYear = (?DAYS_PER_5_MONTHS * MonthPrime + 2) div ?MONTHS_PER_CYCLE + Day - 1,
     DayOfEra = ?DAYS_PER_YEAR * YearOfEra + YearOfEra div 4 - YearOfEra div 100 + DayOfYear,
     Era * ?DAYS_PER_ERA + DayOfEra + ?MARCH_1_YEAR_0.
 
@@ -307,7 +307,7 @@ gregorian_days_to_date(Days) ->
                  - DayOfEra div (?DAYS_PER_ERA - 1)) div ?DAYS_PER_YEAR,
     DayOfYear = DayOfEra - (?DAYS_PER_YEAR * YearOfEra + YearOfEra div 4 - YearOfEra div 100),
     MonthPrime = (?MONTHS_PER_CYCLE * DayOfYear + 2) div ?DAYS_PER_5_MONTHS,
-    Day = DayOfYear - ?DAYS_PER_5_MONTHS * MonthPrime div ?MONTHS_PER_CYCLE + 1,
+    Day = DayOfYear - (?DAYS_PER_5_MONTHS * MonthPrime + 2) div ?MONTHS_PER_CYCLE + 1,
     Month = if MonthPrime < 10 -> MonthPrime + 3; true -> MonthPrime - 9 end,
     Y = YearOfEra + Era * ?YEARS_PER_ERA,
     Year = if Month =< 2 -> Y + 1; true -> Y end,
diff --git a/lib/stdlib/test/calendar_SUITE.erl b/lib/stdlib/test/calendar_SUITE.erl
index e73a9e3c17..82f3590283 100644
--- a/lib/stdlib/test/calendar_SUITE.erl
+++ b/lib/stdlib/test/calendar_SUITE.erl
@@ -23,10 +23,11 @@
 
 -include_lib("common_test/include/ct.hrl").
 
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
-	 init_per_group/2,end_per_group/2, 
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+	 init_per_group/2,end_per_group/2,
 	 gregorian_days/1,
 	 big_gregorian_days/1,
+	 gregorian_days_edge_cases/1,
 	 gregorian_seconds/1,
 	 day_of_the_week/1,
 	 day_of_the_week_calibrate/1,
@@ -36,21 +37,22 @@
 	 iso_week_number/1,
          system_time/1, rfc3339/1]).
 
--define(START_YEAR, 1947).			
--define(END_YEAR, 2012).
+-define(START_YEAR, 1947).
+-define(END_YEAR, 2032).
 
 -define(BIG_START_YEAR, 20000000).
 -define(BIG_END_YEAR, 20000020).
 
 suite() -> [{ct_hooks,[ts_install_cth]}].
 
-all() -> 
+all() ->
     [gregorian_days, gregorian_seconds, day_of_the_week,
      day_of_the_week_calibrate, leap_years,
      last_day_of_the_month, local_time_to_universal_time_dst,
-     iso_week_number, system_time, rfc3339, big_gregorian_days].
+     iso_week_number, system_time, rfc3339, big_gregorian_days,
+     gregorian_days_edge_cases].
 
-groups() -> 
+groups() ->
     [].
 
 init_per_suite(Config) ->
@@ -81,6 +83,79 @@ big_gregorian_days(Config) when is_list(Config) ->
     MaxDays = calendar:date_to_gregorian_days({?BIG_END_YEAR, 1, 1}),
     check_gregorian_days(Days, MaxDays).
 
+%% Tests edge cases for the Neri-Schneider algorithm.
+%% This includes epoch boundaries, leap years, century boundaries,
+%% and 400-year era boundaries.
+gregorian_days_edge_cases(Config) when is_list(Config) ->
+    %% Test epoch (day 0 = Jan 1, year 0)
+    0 = calendar:date_to_gregorian_days(0, 1, 1),
+    {0, 1, 1} = calendar:gregorian_days_to_date(0),
+
+    %% Test year 0 boundaries (year 0 is a leap year)
+    0 = calendar:date_to_gregorian_days({0, 1, 1}),
+    30 = calendar:date_to_gregorian_days({0, 1, 31}),
+    31 = calendar:date_to_gregorian_days({0, 2, 1}),
+    58 = calendar:date_to_gregorian_days({0, 2, 28}),
+    59 = calendar:date_to_gregorian_days({0, 2, 29}),  % Leap day
+    60 = calendar:date_to_gregorian_days({0, 3, 1}),
+    365 = calendar:date_to_gregorian_days({0, 12, 31}),
+
+    %% Test year 1 (not a leap year)
+    366 = calendar:date_to_gregorian_days({1, 1, 1}),
+    {1, 1, 1} = calendar:gregorian_days_to_date(366),
+    730 = calendar:date_to_gregorian_days({1, 12, 31}),
+
+    %% Test century boundaries (1900 is not a leap year, 2000 is)
+    693961 = calendar:date_to_gregorian_days({1900, 1, 1}),
+    {1900, 1, 1} = calendar:gregorian_days_to_date(693961),
+    694325 = calendar:date_to_gregorian_days({1900, 12, 31}),  % 365 days (not leap)
+
+    730485 = calendar:date_to_gregorian_days({2000, 1, 1}),
+    {2000, 1, 1} = calendar:gregorian_days_to_date(730485),
+    730850 = calendar:date_to_gregorian_days({2000, 12, 31}),  % 366 days (leap)
+
+    %% Verify 1900 is not a leap year (Feb has 28 days, Mar 1 is next day)
+    694019 = calendar:date_to_gregorian_days({1900, 2, 28}),
+    694020 = calendar:date_to_gregorian_days({1900, 3, 1}),
+
+    %% Verify 2000 is a leap year (Feb has 29 days)
+    730544 = calendar:date_to_gregorian_days({2000, 2, 29}),
+    730545 = calendar:date_to_gregorian_days({2000, 3, 1}),
+
+    %% Test 400-year era boundaries
+    146097 = calendar:date_to_gregorian_days({400, 1, 1}),
+    {400, 1, 1} = calendar:gregorian_days_to_date(146097),
+    292194 = calendar:date_to_gregorian_days({800, 1, 1}),
+    {800, 1, 1} = calendar:gregorian_days_to_date(292194),
+
+    %% Test specific known dates
+    %% July 4, 1776 (US Independence Day)
+    648856 = calendar:date_to_gregorian_days({1776, 7, 4}),
+    {1776, 7, 4} = calendar:gregorian_days_to_date(648856),
+
+    %% December 7, 2025 (a Sunday)
+    739957 = calendar:date_to_gregorian_days({2025, 12, 7}),
+    {2025, 12, 7} = calendar:gregorian_days_to_date(739957),
+    7 = calendar:day_of_the_week({2025, 12, 7}),  % Sunday
+
+    %% Test far future date
+    3652424 = calendar:date_to_gregorian_days({9999, 12, 31}),
+    {9999, 12, 31} = calendar:gregorian_days_to_date(3652424),
+
+    %% Test roundtrip for sampled days across entire valid range
+    check_roundtrip_samples(),
+
+    ok.
+
+%% Helper: check roundtrip for sampled days
+check_roundtrip_samples() ->
+    %% Sample every 10000 days from 0 to 4000000 (covers year 0 to ~10950)
+    lists:foreach(
+      fun(Days) ->
+              Date = calendar:gregorian_days_to_date(Days),
+              Days = calendar:date_to_gregorian_days(Date)
+      end, lists:seq(0, 4000000, 10000)).
+
 %% Tests that datetime_to_gregorian_seconds and
 %% gregorian_seconds_to_date are each others inverses for a sampled
 %% number of seconds from ?START_YEAR-01-01 up to ?END_YEAR-01-01: We check
@@ -164,7 +239,7 @@ local_time_to_universal_time_dst_x(Config) when is_list(Config) ->
 	{{1969,12,31},{23,59,59}} ->
 	    %% It seems that Apple has no intention of fixing this bug in
 	    %% Mac OS 10.3.9, and we have no intention of implementing a
-	    %% workaround. 
+	    %% workaround.
 	    {comment,"Bug in mktime() in this OS"}
     end.
 
@@ -383,7 +458,7 @@ do_format(Time, Options) ->
     calendar:system_time_to_rfc3339(Time, Options).
 
 %% check_gregorian_days
-%% 
+%%
 check_gregorian_days(Days, MaxDays) when Days < MaxDays ->
     Date = calendar:gregorian_days_to_date(Days),
     true = calendar:valid_date(Date),
@@ -393,7 +468,7 @@ check_gregorian_days(_Days, _MaxDays) ->
     ok.
 
 %% check_gregorian_seconds
-%% 
+%%
 %% We increment with something prime (172801 = 2 days + 1 second).
 %%
 check_gregorian_seconds(Secs, MaxSecs) when Secs < MaxSecs ->
diff --git a/lib/stdlib/test/calendar_prop_SUITE.erl b/lib/stdlib/test/calendar_prop_SUITE.erl
index 7c92263df4..0d91134623 100644
--- a/lib/stdlib/test/calendar_prop_SUITE.erl
+++ b/lib/stdlib/test/calendar_prop_SUITE.erl
@@ -21,13 +21,24 @@
 
 -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
 	 init_per_group/2, end_per_group/2,
-         rfc3339_lists_binaries/1]).
+         rfc3339_lists_binaries/1,
+         local_time_system_time_symmetry/1,
+         gregorian_days_roundtrip/1,
+         gregorian_days_monotonic/1,
+         day_of_week_cycle/1,
+         year_length/1]).
 
 suite() ->
     [{ct_hooks,[ts_install_cth]}].
 
 all() ->
-    [rfc3339_lists_binaries].
+    [rfc3339_lists_binaries,
+     universal_time_system_time_symmetry,
+     local_time_system_time_symmetry,
+     gregorian_days_roundtrip,
+     gregorian_days_monotonic,
+     day_of_week_cycle,
+     year_length].
 
 groups() ->
     [].
@@ -48,3 +59,23 @@ rfc3339_lists_binaries(Config) when is_list(Config) ->
     ct_property_test:quickcheck(
         calendar_prop:rfc3339_lists_binaries(),
         Config).
+
+gregorian_days_roundtrip(Config) when is_list(Config) ->
+    ct_property_test:quickcheck(
+        calendar_prop:gregorian_days_roundtrip(),
+        Config).
+
+gregorian_days_monotonic(Config) when is_list(Config) ->
+    ct_property_test:quickcheck(
+        calendar_prop:gregorian_days_monotonic(),
+        Config).
+
+day_of_week_cycle(Config) when is_list(Config) ->
+    ct_property_test:quickcheck(
+        calendar_prop:day_of_week_cycle(),
+        Config).
+
+year_length(Config) when is_list(Config) ->
+    ct_property_test:quickcheck(
+        calendar_prop:year_length(),
+        Config).
diff --git a/lib/stdlib/test/property_test/calendar_prop.erl b/lib/stdlib/test/property_test/calendar_prop.erl
index 0c329d31a7..e968bea7b5 100644
--- a/lib/stdlib/test/property_test/calendar_prop.erl
+++ b/lib/stdlib/test/property_test/calendar_prop.erl
@@ -44,3 +44,76 @@ rfc3339_lists_binaries() ->
             DateTimeBin =:= ListToBinary andalso FromStr =:= FromBin
         end
     ).
+
+%% Property: date_to_gregorian_days and gregorian_days_to_date are inverses
+gregorian_days_roundtrip() ->
+    ?FORALL(
+        Days,
+        integer(0, 4_000_000),  % Covers year 0 to ~10950
+        begin
+            Date = calendar:gregorian_days_to_date(Days),
+            Days =:= calendar:date_to_gregorian_days(Date)
+        end
+    ).
+
+%% Property: date_to_gregorian_days produces strictly increasing values
+gregorian_days_monotonic() ->
+    ?FORALL(
+        {Year, Month, Day},
+        valid_date(),
+        begin
+            Days1 = calendar:date_to_gregorian_days(Year, Month, Day),
+            %% Next day should be Days1 + 1
+            {Y2, M2, D2} = next_day(Year, Month, Day),
+            Days2 = calendar:date_to_gregorian_days(Y2, M2, D2),
+            Days2 =:= Days1 + 1
+        end
+    ).
+
+%% Property: day_of_the_week cycles correctly (1-7, Monday-Sunday)
+day_of_week_cycle() ->
+    ?FORALL(
+        Days,
+        integer(0, 1_000_000),
+        begin
+            DOW1 = calendar:day_of_the_week(calendar:gregorian_days_to_date(Days)),
+            DOW2 = calendar:day_of_the_week(calendar:gregorian_days_to_date(Days + 7)),
+            DOW1 =:= DOW2 andalso DOW1 >= 1 andalso DOW1 =< 7
+        end
+    ).
+
+%% Property: leap years have 366 days, non-leap years have 365 days
+year_length() ->
+    ?FORALL(
+        Year,
+        integer(0, 10000),
+        begin
+            Jan1 = calendar:date_to_gregorian_days(Year, 1, 1),
+            Dec31 = calendar:date_to_gregorian_days(Year, 12, 31),
+            YearLength = Dec31 - Jan1 + 1,
+            ExpectedLength = case calendar:is_leap_year(Year) of
+                                 true -> 366;
+                                 false -> 365
+                             end,
+            YearLength =:= ExpectedLength
+        end
+    ).
+
+%% Generator for valid dates
+valid_date() ->
+    ?LET(Year, integer(0, 9999),
+         ?LET(Month, integer(1, 12),
+              ?LET(Day, integer(1, calendar:last_day_of_the_month(Year, Month)),
+                   {Year, Month, Day}))).
+
+%% Helper: compute next day
+next_day(Year, Month, Day) ->
+    LastDay = calendar:last_day_of_the_month(Year, Month),
+    if
+        Day < LastDay ->
+            {Year, Month, Day + 1};
+        Month < 12 ->
+            {Year, Month + 1, 1};
+        true ->
+            {Year + 1, 1, 1}
+    end.
-- 
2.51.0

openSUSE Build Service is sponsored by