diff --git a/tests/hard_coded/calendar_date_time_conv.exp b/tests/hard_coded/calendar_date_time_conv.exp index 7b98ef568..523ec662e 100644 --- a/tests/hard_coded/calendar_date_time_conv.exp +++ b/tests/hard_coded/calendar_date_time_conv.exp @@ -1,225 +1,368 @@ -=== Testing date_time_from_string/2 with valid inputs === +=== Testing with valid inputs === -date_time_from_string("2024-01-15 12:30:00") ===> TEST PASSED (accepted: date_time(2024, 1, 15, 12, 30, 0, 0)) -date_time_from_string("1977-10-09 12:00:00") ===> TEST PASSED (accepted: date_time(1977, 10, 9, 12, 0, 0, 0)) -date_time_from_string("0000-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(0, 1, 1, 0, 0, 0, 0)) -date_time_from_string("0000-12-31 23:59:59") ===> TEST PASSED (accepted: date_time(0, 12, 31, 23, 59, 59, 0)) -date_time_from_string("0001-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(1, 1, 1, 0, 0, 0, 0)) -date_time_from_string("9999-12-31 23:59:59") ===> TEST PASSED (accepted: date_time(9999, 12, 31, 23, 59, 59, 0)) -date_time_from_string("-0001-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(-1, 1, 1, 0, 0, 0, 0)) -date_time_from_string("-0001-12-31 00:00:00") ===> TEST PASSED (accepted: date_time(-1, 12, 31, 0, 0, 0, 0)) -date_time_from_string("-4713-11-24 00:00:00") ===> TEST PASSED (accepted: date_time(-4713, 11, 24, 0, 0, 0, 0)) -date_time_from_string("10000-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(10000, 1, 1, 0, 0, 0, 0)) -date_time_from_string("100000-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(100000, 1, 1, 0, 0, 0, 0)) -date_time_from_string("2000-02-29 00:00:00") ===> TEST PASSED (accepted: date_time(2000, 2, 29, 0, 0, 0, 0)) -date_time_from_string("2024-02-29 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 2, 29, 0, 0, 0, 0)) -date_time_from_string("0000-02-29 00:00:00") ===> TEST PASSED (accepted: date_time(0, 2, 29, 0, 0, 0, 0)) -date_time_from_string("-0004-02-29 00:00:00") ===> TEST PASSED (accepted: date_time(-4, 2, 29, 0, 0, 0, 0)) -date_time_from_string("2023-02-28 00:00:00") ===> TEST PASSED (accepted: date_time(2023, 2, 28, 0, 0, 0, 0)) -date_time_from_string("1900-02-28 00:00:00") ===> TEST PASSED (accepted: date_time(1900, 2, 28, 0, 0, 0, 0)) -date_time_from_string("2024-01-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 1, 31, 0, 0, 0, 0)) -date_time_from_string("2024-03-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 3, 31, 0, 0, 0, 0)) -date_time_from_string("2024-04-30 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 4, 30, 0, 0, 0, 0)) -date_time_from_string("2024-05-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 5, 31, 0, 0, 0, 0)) -date_time_from_string("2024-06-30 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 6, 30, 0, 0, 0, 0)) -date_time_from_string("2024-07-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 7, 31, 0, 0, 0, 0)) -date_time_from_string("2024-08-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 8, 31, 0, 0, 0, 0)) -date_time_from_string("2024-09-30 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 9, 30, 0, 0, 0, 0)) -date_time_from_string("2024-10-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 10, 31, 0, 0, 0, 0)) -date_time_from_string("2024-11-30 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 11, 30, 0, 0, 0, 0)) -date_time_from_string("2024-12-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 12, 31, 0, 0, 0, 0)) -date_time_from_string("2024-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 0)) -date_time_from_string("2024-01-01 23:59:59") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 23, 59, 59, 0)) -date_time_from_string("2024-01-01 00:00:60") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 60, 0)) -date_time_from_string("2024-02-29 23:59:60") ===> TEST PASSED (accepted: date_time(2024, 2, 29, 23, 59, 60, 0)) -date_time_from_string("2024-01-01 00:00:00.1") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 100000)) -date_time_from_string("2024-01-01 00:00:00.12") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 120000)) -date_time_from_string("2024-01-01 00:00:00.123") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123000)) -date_time_from_string("2024-01-01 00:00:00.1234") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123400)) -date_time_from_string("2024-01-01 00:00:00.12345") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123450)) -date_time_from_string("2024-01-01 00:00:00.123456") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123456)) -date_time_from_string("2024-01-01 00:00:00.000001") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 1)) -date_time_from_string("2024-01-01 00:00:00.999999") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 999999)) -date_time_from_string("2024-12-31 23:59:60.999999") ===> TEST PASSED (accepted: date_time(2024, 12, 31, 23, 59, 60, 999999)) -date_time_from_string("1970-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(1970, 1, 1, 0, 0, 0, 0)) -date_time_from_string("1582-10-15 00:00:00") ===> TEST PASSED (accepted: date_time(1582, 10, 15, 0, 0, 0, 0)) -date_time_from_string("2024-01-01 00:00:00.10") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 100000): to-string: "2024-01-01 00:00:00.1")) -date_time_from_string("2024-01-01 00:00:00.100") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 100000): to-string: "2024-01-01 00:00:00.1")) -date_time_from_string("2024-01-01 00:00:00.1000") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 100000): to-string: "2024-01-01 00:00:00.1")) -date_time_from_string("2024-01-01 00:00:00.10000") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 100000): to-string: "2024-01-01 00:00:00.1")) -date_time_from_string("2024-01-01 00:00:00.100000") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 100000): to-string: "2024-01-01 00:00:00.1")) -date_time_from_string("2024-01-01 00:00:00.120000") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 120000): to-string: "2024-01-01 00:00:00.12")) -date_time_from_string("2024-01-01 00:00:00.123000") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123000): to-string: "2024-01-01 00:00:00.123")) -date_time_from_string("2024-01-01 00:00:00.123400") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123400): to-string: "2024-01-01 00:00:00.1234")) -date_time_from_string("2024-01-01 00:00:00.123450") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123450): to-string: "2024-01-01 00:00:00.12345")) -date_time_from_string("2024-01-01 00:00:00.0") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 0): to-string: "2024-01-01 00:00:00")) -date_time_from_string("2024-01-01 00:00:00.000000") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 0): to-string: "2024-01-01 00:00:00")) -date_time_from_string("00000-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(0, 1, 1, 0, 0, 0, 0): to-string: "0000-01-01 00:00:00")) -date_time_from_string("00001-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(1, 1, 1, 0, 0, 0, 0): to-string: "0001-01-01 00:00:00")) +"2024-01-15 12:30:00": unremarkable mid-month date +PASS date_time(2024, 1, 15, 12, 30, 0, 0) -=== Testing date_time_from_string/2 with invalid inputs === +"1977-10-09 12:00:00": arbitrary date in the past +PASS date_time(1977, 10, 9, 12, 0, 0, 0) -date_time_from_string("") ===> TEST PASSED (rejected: empty string) -date_time_from_string(" ") ===> TEST PASSED (rejected: blank string) -date_time_from_string("not a date") ===> TEST PASSED (rejected: not a date) -date_time_from_string("2024-01-01") ===> TEST PASSED (rejected: missing time component) -date_time_from_string("12:00:00") ===> TEST PASSED (rejected: missing date component) -date_time_from_string("2024/01/01 00:00:00") ===> TEST PASSED (rejected: incorrect date separator) -date_time_from_string("2024-01-01T00:00:00") ===> TEST PASSED (rejected: ISO 8601 T separator) -date_time_from_string("2024-01-01 00:00") ===> TEST PASSED (rejected: missing seconds) -date_time_from_string("2024-01-01 00:00:00 ") ===> TEST PASSED (rejected: trailing space) -date_time_from_string(" 2024-01-01 00:00:00") ===> TEST PASSED (rejected: leading space) -date_time_from_string("2024-01-01 00:00:00Z") ===> TEST PASSED (rejected: trailing timezone indicator) -date_time_from_string("2024-01-01 00:00:00+10:00") ===> TEST PASSED (rejected: trailing UTC offset) -date_time_from_string("99-01-01 00:00:00") ===> TEST PASSED (rejected: two-digit year) -date_time_from_string("999-01-01 00:00:00") ===> TEST PASSED (rejected: three-digit year) -date_time_from_string("2024-1-01 00:00:00") ===> TEST PASSED (rejected: one-digit month) -date_time_from_string("2024-01-1 00:00:00") ===> TEST PASSED (rejected: one-digit day) -date_time_from_string("2024-01-01 0:00:00") ===> TEST PASSED (rejected: one-digit hour) -date_time_from_string("2024-01-01 00:0:00") ===> TEST PASSED (rejected: one-digit minute) -date_time_from_string("2024-01-01 00:00:0") ===> TEST PASSED (rejected: one-digit second) -date_time_from_string("2024-00-01 00:00:00") ===> TEST PASSED (rejected: month zero) -date_time_from_string("2024-13-01 00:00:00") ===> TEST PASSED (rejected: month 13) -date_time_from_string("2024-01-00 00:00:00") ===> TEST PASSED (rejected: day zero) -date_time_from_string("2024-01-32 00:00:00") ===> TEST PASSED (rejected: day 32 in a 31-day month) -date_time_from_string("2023-02-29 00:00:00") ===> TEST PASSED (rejected: Feb 29 in a non-leap year) -date_time_from_string("1900-02-29 00:00:00") ===> TEST PASSED (rejected: Feb 29 in a century non-leap year) -date_time_from_string("2024-04-31 00:00:00") ===> TEST PASSED (rejected: day 31 in a 30-day month) -date_time_from_string("2024-01-01 24:00:00") ===> TEST PASSED (rejected: hour 24) -date_time_from_string("2024-01-01 00:60:00") ===> TEST PASSED (rejected: minute 60) -date_time_from_string("2024-01-01 00:00:61") ===> TEST PASSED (rejected: second 61) -date_time_from_string("2024-01-01 00:00:00.") ===> TEST PASSED (rejected: trailing dot with no digits) -date_time_from_string("2024-01-01 00:00:00.1234567") ===> TEST PASSED (rejected: seven fractional digits) -date_time_from_string("-01-01 00:00:00") ===> TEST PASSED (rejected: negative sign but only two-digit year) -date_time_from_string("-0001-13-01 00:00:00") ===> TEST PASSED (rejected: negative year with out-of-range month) -date_time_from_string("2024-ab-01 00:00:00") ===> TEST PASSED (rejected: letter in month field) -date_time_from_string("2024-01-01 xx:00:00") ===> TEST PASSED (rejected: letters in hour field) -date_time_from_string("--0001-01-01 00:00:00") ===> TEST PASSED (rejected: double negative prefix) -date_time_from_string("2024--01-01 00:00:00") ===> TEST PASSED (rejected: negative sign on month) -date_time_from_string("2024-01-01 -01:00:00") ===> TEST PASSED (rejected: negative sign on hour) -date_time_from_string("2024-01-01 00:00:-01") ===> TEST PASSED (rejected: negative sign on second) -date_time_from_string("2024-01-01 00:00:00") ===> TEST PASSED (rejected: double space between date and time) -date_time_from_string("2024-01-01 00:00:00.123.456") ===> TEST PASSED (rejected: two decimal points) -date_time_from_string("2024-01-01 00:00:00.123456.7") ===> TEST PASSED (rejected: decimal point in microseconds follow by more) -date_time_from_string("2024-001-01 00:00:00") ===> TEST PASSED (rejected: three-digit month) -date_time_from_string("2024-01-001 00:00:00") ===> TEST PASSED (rejected: three-digit day) -date_time_from_string("2024-01-01 000:00:00") ===> TEST PASSED (rejected: three-digit hour) -date_time_from_string("2024-01-01 00:001:00") ===> TEST PASSED (rejected: three-digit minute) -date_time_from_string("2024-01-01 00:00:001") ===> TEST PASSED (rejected: three-digit second) -date_time_from_string("2024-01-01 00:00:00.0000000") ===> TEST PASSED (rejected: seven zeros in microseconds) -date_time_from_string("2024-01-01 00 :00:00") ===> TEST PASSED (rejected: space within time component) -date_time_from_string("2024 -01-01 00:00:00") ===> TEST PASSED (rejected: space within date component) -date_time_from_string("2024:01:01 00:00:00") ===> TEST PASSED (rejected: colons in date part) -date_time_from_string("2024-01-01 00-00-00") ===> TEST PASSED (rejected: dashes in time part) -date_time_from_string("2024--01 00:00:00") ===> TEST PASSED (rejected: missing month) -date_time_from_string("2024-01- 00:00:00") ===> TEST PASSED (rejected: missing day) -date_time_from_string("2024-01-01 :00:00") ===> TEST PASSED (rejected: missing hour) -date_time_from_string("2024-01-01 00::00") ===> TEST PASSED (rejected: missing minute) -date_time_from_string("2024-01-01 00:00:") ===> TEST PASSED (rejected: trailing colon with no second digits) +"0000-01-01 00:00:00": first day of year zero +PASS date_time(0, 1, 1, 0, 0, 0, 0) -=== Testing det_date_from_string/1 with valid inputs === +"0000-12-31 23:59:59": last day of year zero +PASS date_time(0, 12, 31, 23, 59, 59, 0) -det_date_time_from_string("2024-01-15 12:30:00") ===> TEST PASSED (accepted: date_time(2024, 1, 15, 12, 30, 0, 0)) -det_date_time_from_string("1977-10-09 12:00:00") ===> TEST PASSED (accepted: date_time(1977, 10, 9, 12, 0, 0, 0)) -det_date_time_from_string("0000-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(0, 1, 1, 0, 0, 0, 0)) -det_date_time_from_string("0000-12-31 23:59:59") ===> TEST PASSED (accepted: date_time(0, 12, 31, 23, 59, 59, 0)) -det_date_time_from_string("0001-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(1, 1, 1, 0, 0, 0, 0)) -det_date_time_from_string("9999-12-31 23:59:59") ===> TEST PASSED (accepted: date_time(9999, 12, 31, 23, 59, 59, 0)) -det_date_time_from_string("-0001-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(-1, 1, 1, 0, 0, 0, 0)) -det_date_time_from_string("-0001-12-31 00:00:00") ===> TEST PASSED (accepted: date_time(-1, 12, 31, 0, 0, 0, 0)) -det_date_time_from_string("-4713-11-24 00:00:00") ===> TEST PASSED (accepted: date_time(-4713, 11, 24, 0, 0, 0, 0)) -det_date_time_from_string("10000-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(10000, 1, 1, 0, 0, 0, 0)) -det_date_time_from_string("100000-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(100000, 1, 1, 0, 0, 0, 0)) -det_date_time_from_string("2000-02-29 00:00:00") ===> TEST PASSED (accepted: date_time(2000, 2, 29, 0, 0, 0, 0)) -det_date_time_from_string("2024-02-29 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 2, 29, 0, 0, 0, 0)) -det_date_time_from_string("0000-02-29 00:00:00") ===> TEST PASSED (accepted: date_time(0, 2, 29, 0, 0, 0, 0)) -det_date_time_from_string("-0004-02-29 00:00:00") ===> TEST PASSED (accepted: date_time(-4, 2, 29, 0, 0, 0, 0)) -det_date_time_from_string("2023-02-28 00:00:00") ===> TEST PASSED (accepted: date_time(2023, 2, 28, 0, 0, 0, 0)) -det_date_time_from_string("1900-02-28 00:00:00") ===> TEST PASSED (accepted: date_time(1900, 2, 28, 0, 0, 0, 0)) -det_date_time_from_string("2024-01-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 1, 31, 0, 0, 0, 0)) -det_date_time_from_string("2024-03-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 3, 31, 0, 0, 0, 0)) -det_date_time_from_string("2024-04-30 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 4, 30, 0, 0, 0, 0)) -det_date_time_from_string("2024-05-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 5, 31, 0, 0, 0, 0)) -det_date_time_from_string("2024-06-30 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 6, 30, 0, 0, 0, 0)) -det_date_time_from_string("2024-07-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 7, 31, 0, 0, 0, 0)) -det_date_time_from_string("2024-08-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 8, 31, 0, 0, 0, 0)) -det_date_time_from_string("2024-09-30 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 9, 30, 0, 0, 0, 0)) -det_date_time_from_string("2024-10-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 10, 31, 0, 0, 0, 0)) -det_date_time_from_string("2024-11-30 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 11, 30, 0, 0, 0, 0)) -det_date_time_from_string("2024-12-31 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 12, 31, 0, 0, 0, 0)) -det_date_time_from_string("2024-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 0)) -det_date_time_from_string("2024-01-01 23:59:59") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 23, 59, 59, 0)) -det_date_time_from_string("2024-01-01 00:00:60") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 60, 0)) -det_date_time_from_string("2024-02-29 23:59:60") ===> TEST PASSED (accepted: date_time(2024, 2, 29, 23, 59, 60, 0)) -det_date_time_from_string("2024-01-01 00:00:00.1") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 100000)) -det_date_time_from_string("2024-01-01 00:00:00.12") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 120000)) -det_date_time_from_string("2024-01-01 00:00:00.123") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123000)) -det_date_time_from_string("2024-01-01 00:00:00.1234") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123400)) -det_date_time_from_string("2024-01-01 00:00:00.12345") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123450)) -det_date_time_from_string("2024-01-01 00:00:00.123456") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123456)) -det_date_time_from_string("2024-01-01 00:00:00.000001") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 1)) -det_date_time_from_string("2024-01-01 00:00:00.999999") ===> TEST PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 999999)) -det_date_time_from_string("2024-12-31 23:59:60.999999") ===> TEST PASSED (accepted: date_time(2024, 12, 31, 23, 59, 60, 999999)) -det_date_time_from_string("1970-01-01 00:00:00") ===> TEST PASSED (accepted: date_time(1970, 1, 1, 0, 0, 0, 0)) -det_date_time_from_string("1582-10-15 00:00:00") ===> TEST PASSED (accepted: date_time(1582, 10, 15, 0, 0, 0, 0)) +"0001-01-01 00:00:00": first day of year one +PASS date_time(1, 1, 1, 0, 0, 0, 0) -=== Testing det_date_from_string/1 with invalid inputs === +"9999-12-31 23:59:59": last four-digit year +PASS date_time(9999, 12, 31, 23, 59, 59, 0) -det_date_time_from_string("") ===> TEST PASSED (exception: empty string) -det_date_time_from_string(" ") ===> TEST PASSED (exception: blank string) -det_date_time_from_string("not a date") ===> TEST PASSED (exception: not a date) -det_date_time_from_string("2024-01-01") ===> TEST PASSED (exception: missing time component) -det_date_time_from_string("12:00:00") ===> TEST PASSED (exception: missing date component) -det_date_time_from_string("2024/01/01 00:00:00") ===> TEST PASSED (exception: incorrect date separator) -det_date_time_from_string("2024-01-01T00:00:00") ===> TEST PASSED (exception: ISO 8601 T separator) -det_date_time_from_string("2024-01-01 00:00") ===> TEST PASSED (exception: missing seconds) -det_date_time_from_string("2024-01-01 00:00:00 ") ===> TEST PASSED (exception: trailing space) -det_date_time_from_string(" 2024-01-01 00:00:00") ===> TEST PASSED (exception: leading space) -det_date_time_from_string("2024-01-01 00:00:00Z") ===> TEST PASSED (exception: trailing timezone indicator) -det_date_time_from_string("2024-01-01 00:00:00+10:00") ===> TEST PASSED (exception: trailing UTC offset) -det_date_time_from_string("99-01-01 00:00:00") ===> TEST PASSED (exception: two-digit year) -det_date_time_from_string("999-01-01 00:00:00") ===> TEST PASSED (exception: three-digit year) -det_date_time_from_string("2024-1-01 00:00:00") ===> TEST PASSED (exception: one-digit month) -det_date_time_from_string("2024-01-1 00:00:00") ===> TEST PASSED (exception: one-digit day) -det_date_time_from_string("2024-01-01 0:00:00") ===> TEST PASSED (exception: one-digit hour) -det_date_time_from_string("2024-01-01 00:0:00") ===> TEST PASSED (exception: one-digit minute) -det_date_time_from_string("2024-01-01 00:00:0") ===> TEST PASSED (exception: one-digit second) -det_date_time_from_string("2024-00-01 00:00:00") ===> TEST PASSED (exception: month zero) -det_date_time_from_string("2024-13-01 00:00:00") ===> TEST PASSED (exception: month 13) -det_date_time_from_string("2024-01-00 00:00:00") ===> TEST PASSED (exception: day zero) -det_date_time_from_string("2024-01-32 00:00:00") ===> TEST PASSED (exception: day 32 in a 31-day month) -det_date_time_from_string("2023-02-29 00:00:00") ===> TEST PASSED (exception: Feb 29 in a non-leap year) -det_date_time_from_string("1900-02-29 00:00:00") ===> TEST PASSED (exception: Feb 29 in a century non-leap year) -det_date_time_from_string("2024-04-31 00:00:00") ===> TEST PASSED (exception: day 31 in a 30-day month) -det_date_time_from_string("2024-01-01 24:00:00") ===> TEST PASSED (exception: hour 24) -det_date_time_from_string("2024-01-01 00:60:00") ===> TEST PASSED (exception: minute 60) -det_date_time_from_string("2024-01-01 00:00:61") ===> TEST PASSED (exception: second 61) -det_date_time_from_string("2024-01-01 00:00:00.") ===> TEST PASSED (exception: trailing dot with no digits) -det_date_time_from_string("2024-01-01 00:00:00.1234567") ===> TEST PASSED (exception: seven fractional digits) -det_date_time_from_string("-01-01 00:00:00") ===> TEST PASSED (exception: negative sign but only two-digit year) -det_date_time_from_string("-0001-13-01 00:00:00") ===> TEST PASSED (exception: negative year with out-of-range month) -det_date_time_from_string("2024-ab-01 00:00:00") ===> TEST PASSED (exception: letter in month field) -det_date_time_from_string("2024-01-01 xx:00:00") ===> TEST PASSED (exception: letters in hour field) -det_date_time_from_string("--0001-01-01 00:00:00") ===> TEST PASSED (exception: double negative prefix) -det_date_time_from_string("2024--01-01 00:00:00") ===> TEST PASSED (exception: negative sign on month) -det_date_time_from_string("2024-01-01 -01:00:00") ===> TEST PASSED (exception: negative sign on hour) -det_date_time_from_string("2024-01-01 00:00:-01") ===> TEST PASSED (exception: negative sign on second) -det_date_time_from_string("2024-01-01 00:00:00") ===> TEST PASSED (exception: double space between date and time) -det_date_time_from_string("2024-01-01 00:00:00.123.456") ===> TEST PASSED (exception: two decimal points) -det_date_time_from_string("2024-01-01 00:00:00.123456.7") ===> TEST PASSED (exception: decimal point in microseconds follow by more) -det_date_time_from_string("2024-001-01 00:00:00") ===> TEST PASSED (exception: three-digit month) -det_date_time_from_string("2024-01-001 00:00:00") ===> TEST PASSED (exception: three-digit day) -det_date_time_from_string("2024-01-01 000:00:00") ===> TEST PASSED (exception: three-digit hour) -det_date_time_from_string("2024-01-01 00:001:00") ===> TEST PASSED (exception: three-digit minute) -det_date_time_from_string("2024-01-01 00:00:001") ===> TEST PASSED (exception: three-digit second) -det_date_time_from_string("2024-01-01 00:00:00.0000000") ===> TEST PASSED (exception: seven zeros in microseconds) -det_date_time_from_string("2024-01-01 00 :00:00") ===> TEST PASSED (exception: space within time component) -det_date_time_from_string("2024 -01-01 00:00:00") ===> TEST PASSED (exception: space within date component) -det_date_time_from_string("2024:01:01 00:00:00") ===> TEST PASSED (exception: colons in date part) -det_date_time_from_string("2024-01-01 00-00-00") ===> TEST PASSED (exception: dashes in time part) -det_date_time_from_string("2024--01 00:00:00") ===> TEST PASSED (exception: missing month) -det_date_time_from_string("2024-01- 00:00:00") ===> TEST PASSED (exception: missing day) -det_date_time_from_string("2024-01-01 :00:00") ===> TEST PASSED (exception: missing hour) -det_date_time_from_string("2024-01-01 00::00") ===> TEST PASSED (exception: missing minute) -det_date_time_from_string("2024-01-01 00:00:") ===> TEST PASSED (exception: trailing colon with no second digits) +"-0001-01-01 00:00:00": year -1 +PASS date_time(-1, 1, 1, 0, 0, 0, 0) +"-0001-12-31 00:00:00": end of year -1 +PASS date_time(-1, 12, 31, 0, 0, 0, 0) + +"-4713-11-24 00:00:00": Julian origin in proleptic Gregorian calendar +PASS date_time(-4713, 11, 24, 0, 0, 0, 0) + +"10000-01-01 00:00:00": five-digit year +PASS date_time(10000, 1, 1, 0, 0, 0, 0) + +"100000-01-01 00:00:00": six-digit year +PASS date_time(100000, 1, 1, 0, 0, 0, 0) + +"2000-02-29 00:00:00": 400-year leap year +PASS date_time(2000, 2, 29, 0, 0, 0, 0) + +"2024-02-29 00:00:00": ordinary leap year +PASS date_time(2024, 2, 29, 0, 0, 0, 0) + +"0000-02-29 00:00:00": year zero is a leap year +PASS date_time(0, 2, 29, 0, 0, 0, 0) + +"-0004-02-29 00:00:00": negative leap year +PASS date_time(-4, 2, 29, 0, 0, 0, 0) + +"2023-02-28 00:00:00": last day of Feb in a non-leap year +PASS date_time(2023, 2, 28, 0, 0, 0, 0) + +"1900-02-28 00:00:00": last day of Feb in a century non-leap year +PASS date_time(1900, 2, 28, 0, 0, 0, 0) + +"2024-01-31 00:00:00": end of Jan +PASS date_time(2024, 1, 31, 0, 0, 0, 0) + +"2024-03-31 00:00:00": end of March +PASS date_time(2024, 3, 31, 0, 0, 0, 0) + +"2024-04-30 00:00:00": end of April +PASS date_time(2024, 4, 30, 0, 0, 0, 0) + +"2024-05-31 00:00:00": end of May +PASS date_time(2024, 5, 31, 0, 0, 0, 0) + +"2024-06-30 00:00:00": end of June +PASS date_time(2024, 6, 30, 0, 0, 0, 0) + +"2024-07-31 00:00:00": end of July +PASS date_time(2024, 7, 31, 0, 0, 0, 0) + +"2024-08-31 00:00:00": end of Aug +PASS date_time(2024, 8, 31, 0, 0, 0, 0) + +"2024-09-30 00:00:00": end of Sep +PASS date_time(2024, 9, 30, 0, 0, 0, 0) + +"2024-10-31 00:00:00": end of Oct +PASS date_time(2024, 10, 31, 0, 0, 0, 0) + +"2024-11-30 00:00:00": end of Nov +PASS date_time(2024, 11, 30, 0, 0, 0, 0) + +"2024-12-31 00:00:00": end of Dec +PASS date_time(2024, 12, 31, 0, 0, 0, 0) + +"2024-01-01 00:00:00": midnight +PASS date_time(2024, 1, 1, 0, 0, 0, 0) + +"2024-01-01 23:59:59": last second of the day +PASS date_time(2024, 1, 1, 23, 59, 59, 0) + +"2024-01-01 00:00:60": leap second +PASS date_time(2024, 1, 1, 0, 0, 60, 0) + +"2024-02-29 23:59:60": leap second on leap day +PASS date_time(2024, 2, 29, 23, 59, 60, 0) + +"2024-01-01 00:00:00.1": one fractional digit +PASS date_time(2024, 1, 1, 0, 0, 0, 100000) + +"2024-01-01 00:00:00.12": two fractional digits +PASS date_time(2024, 1, 1, 0, 0, 0, 120000) + +"2024-01-01 00:00:00.123": three fractional digits +PASS date_time(2024, 1, 1, 0, 0, 0, 123000) + +"2024-01-01 00:00:00.1234": four fractional digits +PASS date_time(2024, 1, 1, 0, 0, 0, 123400) + +"2024-01-01 00:00:00.12345": five fractional digits +PASS date_time(2024, 1, 1, 0, 0, 0, 123450) + +"2024-01-01 00:00:00.123456": six fractional digits +PASS date_time(2024, 1, 1, 0, 0, 0, 123456) + +"2024-01-01 00:00:00.000001": smallest nonzero microsecond +PASS date_time(2024, 1, 1, 0, 0, 0, 1) + +"2024-01-01 00:00:00.999999": largest microsecond value +PASS date_time(2024, 1, 1, 0, 0, 0, 999999) + +"2024-12-31 23:59:60.999999": all maximum +PASS date_time(2024, 12, 31, 23, 59, 60, 999999) + +"1970-01-01 00:00:00": Unix epoch +PASS date_time(1970, 1, 1, 0, 0, 0, 0) + +"1582-10-15 00:00:00": first day of the Gregorian calendar +PASS date_time(1582, 10, 15, 0, 0, 0, 0) + +"2024-01-01 00:00:00.10": one fractional trailing zero +PASS date_time(2024, 1, 1, 0, 0, 0, 100000) +TEST 2024-01-01 00:00:00.10 +TRIP 2024-01-01 00:00:00.1 + +"2024-01-01 00:00:00.100": two fractional trailing zeros +PASS date_time(2024, 1, 1, 0, 0, 0, 100000) +TEST 2024-01-01 00:00:00.100 +TRIP 2024-01-01 00:00:00.1 + +"2024-01-01 00:00:00.1000": three fractional trailing zeros +PASS date_time(2024, 1, 1, 0, 0, 0, 100000) +TEST 2024-01-01 00:00:00.1000 +TRIP 2024-01-01 00:00:00.1 + +"2024-01-01 00:00:00.10000": four fractional trailing zeros +PASS date_time(2024, 1, 1, 0, 0, 0, 100000) +TEST 2024-01-01 00:00:00.10000 +TRIP 2024-01-01 00:00:00.1 + +"2024-01-01 00:00:00.100000": five fractional trailing zeros +PASS date_time(2024, 1, 1, 0, 0, 0, 100000) +TEST 2024-01-01 00:00:00.100000 +TRIP 2024-01-01 00:00:00.1 + +"2024-01-01 00:00:00.120000": trailing zeros after two fractional digits +PASS date_time(2024, 1, 1, 0, 0, 0, 120000) +TEST 2024-01-01 00:00:00.120000 +TRIP 2024-01-01 00:00:00.12 + +"2024-01-01 00:00:00.123000": trailing zeros after three fractional digits +PASS date_time(2024, 1, 1, 0, 0, 0, 123000) +TEST 2024-01-01 00:00:00.123000 +TRIP 2024-01-01 00:00:00.123 + +"2024-01-01 00:00:00.123400": trailing zeros after four fractional digits +PASS date_time(2024, 1, 1, 0, 0, 0, 123400) +TEST 2024-01-01 00:00:00.123400 +TRIP 2024-01-01 00:00:00.1234 + +"2024-01-01 00:00:00.123450": trailing zeros after five fractional digits +PASS date_time(2024, 1, 1, 0, 0, 0, 123450) +TEST 2024-01-01 00:00:00.123450 +TRIP 2024-01-01 00:00:00.12345 + +"2024-01-01 00:00:00.0": single zero fractional digit +PASS date_time(2024, 1, 1, 0, 0, 0, 0) +TEST 2024-01-01 00:00:00.0 +TRIP 2024-01-01 00:00:00 + +"2024-01-01 00:00:00.000000": all zero fractional digits +PASS date_time(2024, 1, 1, 0, 0, 0, 0) +TEST 2024-01-01 00:00:00.000000 +TRIP 2024-01-01 00:00:00 + +"00000-01-01 00:00:00": five digit year zero +PASS date_time(0, 1, 1, 0, 0, 0, 0) +TEST 00000-01-01 00:00:00 +TRIP 0000-01-01 00:00:00 + +"00001-01-01 00:00:00": five digit year one +PASS date_time(1, 1, 1, 0, 0, 0, 0) +TEST 00001-01-01 00:00:00 +TRIP 0001-01-01 00:00:00 + +=== Testing with invalid inputs === + +"": empty string +PASS: ERROR DETECTED + +" ": blank string +PASS: ERROR DETECTED + +"not a date": not a date +PASS: ERROR DETECTED + +"2024-01-01": missing time component +PASS: ERROR DETECTED + +"12:00:00": missing date component +PASS: ERROR DETECTED + +"2024/01/01 00:00:00": incorrect date separator +PASS: ERROR DETECTED + +"2024-01-01T00:00:00": ISO 8601 T separator +PASS: ERROR DETECTED + +"2024-01-01 00:00": missing seconds +PASS: ERROR DETECTED + +"2024-01-01 00:00:00 ": trailing space +PASS: ERROR DETECTED + +" 2024-01-01 00:00:00": leading space +PASS: ERROR DETECTED + +"2024-01-01 00:00:00Z": trailing timezone indicator +PASS: ERROR DETECTED + +"2024-01-01 00:00:00+10:00": trailing UTC offset +PASS: ERROR DETECTED + +"99-01-01 00:00:00": two-digit year +PASS: ERROR DETECTED + +"999-01-01 00:00:00": three-digit year +PASS: ERROR DETECTED + +"2024-1-01 00:00:00": one-digit month +PASS: ERROR DETECTED + +"2024-01-1 00:00:00": one-digit day +PASS: ERROR DETECTED + +"2024-01-01 0:00:00": one-digit hour +PASS: ERROR DETECTED + +"2024-01-01 00:0:00": one-digit minute +PASS: ERROR DETECTED + +"2024-01-01 00:00:0": one-digit second +PASS: ERROR DETECTED + +"2024-00-01 00:00:00": month zero +PASS: ERROR DETECTED + +"2024-13-01 00:00:00": month 13 +PASS: ERROR DETECTED + +"2024-01-00 00:00:00": day zero +PASS: ERROR DETECTED + +"2024-01-32 00:00:00": day 32 in a 31-day month +PASS: ERROR DETECTED + +"2023-02-29 00:00:00": Feb 29 in a non-leap year +PASS: ERROR DETECTED + +"1900-02-29 00:00:00": Feb 29 in a century non-leap year +PASS: ERROR DETECTED + +"2024-04-31 00:00:00": day 31 in a 30-day month +PASS: ERROR DETECTED + +"2024-01-01 24:00:00": hour 24 +PASS: ERROR DETECTED + +"2024-01-01 00:60:00": minute 60 +PASS: ERROR DETECTED + +"2024-01-01 00:00:61": second 61 +PASS: ERROR DETECTED + +"2024-01-01 00:00:00.": trailing dot with no digits +PASS: ERROR DETECTED + +"2024-01-01 00:00:00.1234567": seven fractional digits +PASS: ERROR DETECTED + +"-01-01 00:00:00": negative sign but only two-digit year +PASS: ERROR DETECTED + +"-0001-13-01 00:00:00": negative year with out-of-range month +PASS: ERROR DETECTED + +"2024-ab-01 00:00:00": letter in month field +PASS: ERROR DETECTED + +"2024-01-01 xx:00:00": letters in hour field +PASS: ERROR DETECTED + +"--0001-01-01 00:00:00": double negative prefix +PASS: ERROR DETECTED + +"2024--01-01 00:00:00": negative sign on month +PASS: ERROR DETECTED + +"2024-01-01 -01:00:00": negative sign on hour +PASS: ERROR DETECTED + +"2024-01-01 00:00:-01": negative sign on second +PASS: ERROR DETECTED + +"2024-01-01 00:00:00": double space between date and time +PASS: ERROR DETECTED + +"2024-01-01 00:00:00.123.456": two decimal points +PASS: ERROR DETECTED + +"2024-01-01 00:00:00.123456.7": decimal point in microseconds follow by more +PASS: ERROR DETECTED + +"2024-001-01 00:00:00": three-digit month +PASS: ERROR DETECTED + +"2024-01-001 00:00:00": three-digit day +PASS: ERROR DETECTED + +"2024-01-01 000:00:00": three-digit hour +PASS: ERROR DETECTED + +"2024-01-01 00:001:00": three-digit minute +PASS: ERROR DETECTED + +"2024-01-01 00:00:001": three-digit second +PASS: ERROR DETECTED + +"2024-01-01 00:00:00.0000000": seven zeros in microseconds +PASS: ERROR DETECTED + +"2024-01-01 00 :00:00": space within time component +PASS: ERROR DETECTED + +"2024 -01-01 00:00:00": space within date component +PASS: ERROR DETECTED + +"2024:01:01 00:00:00": colons in date part +PASS: ERROR DETECTED + +"2024-01-01 00-00-00": dashes in time part +PASS: ERROR DETECTED + +"2024--01 00:00:00": missing month +PASS: ERROR DETECTED + +"2024-01- 00:00:00": missing day +PASS: ERROR DETECTED + +"2024-01-01 :00:00": missing hour +PASS: ERROR DETECTED + +"2024-01-01 00::00": missing minute +PASS: ERROR DETECTED + +"2024-01-01 00:00:": trailing colon with no second digits +PASS: ERROR DETECTED diff --git a/tests/hard_coded/calendar_date_time_conv.m b/tests/hard_coded/calendar_date_time_conv.m index f7482f7a9..4a3376767 100644 --- a/tests/hard_coded/calendar_date_time_conv.m +++ b/tests/hard_coded/calendar_date_time_conv.m @@ -22,127 +22,90 @@ :- import_module bool. :- import_module calendar. :- import_module list. +:- import_module maybe. :- import_module string. %---------------------------------------------------------------------------% main(!IO) :- - test_valid_date_times(!IO), - test_invalid_date_times(!IO), - test_exception_valid_date_times(!IO), - test_exception_invalid_date_times(!IO). + io.write_string("=== Testing with valid inputs ===\n", !IO), + list.foldl(test_valid_date_time(yes), valid_date_times, !IO), + list.foldl(test_valid_date_time(no), valid_no_roundtrip_date_times, !IO), + + io.nl(!IO), + io.write_string("=== Testing with invalid inputs ===\n", !IO), + list.foldl(test_invalid_date_time, invalid_date_times, !IO). %---------------------------------------------------------------------------% +% +% In both test_valid_duration and test_invalid_duration, we exercise +% both the semidet predicate duration_from_string/2, and its det function +% version, duration_from_string/1. +% +% We always print the results from the semidet predicate version. +% We print the results from the det function version ONLY if it differs +% from the result of the semidet predicate version on the same input. +% +%---------------------------------------------------------------------------% -:- pred test_valid_date_times(io::di, io::uo) is det. +:- pred test_valid_date_time(bool::in, dt_conv_test::in, + io::di, io::uo) is cc_multi. -test_valid_date_times(!IO) :- - io.write_string( - "=== Testing date_time_from_string/2 with valid inputs ===\n\n", - !IO), - list.foldl(do_test_valid_date_time(yes), valid_date_times, !IO), - list.foldl(do_test_valid_date_time(no), valid_no_roundtrip_date_times, - !IO), - io.nl(!IO). - -:- pred do_test_valid_date_time(bool::in, dt_conv_test::in, - io::di, io::uo) is det. - -do_test_valid_date_time(CheckRoundTrip, Test, !IO) :- - Test = dt_conv_test(Desc, TestString), - io.format("date_time_from_string(\"%s\") ===> ", [s(TestString)], !IO), - ( if date_time_from_string(TestString, DateTime) then - RoundTripString = date_time_to_string(DateTime), +test_valid_date_time(CheckRoundTrip, Test, !IO) :- + Test = dt_conv_test(Desc, TestStr), + write_test_call(tsl_30, TestStr, Desc, !IO), + ( if date_time_from_string(TestStr, DateTimeP) then + MaybeDateTimeP = yes(DateTimeP), + RoundTripStr = date_time_to_string(DateTimeP), ( CheckRoundTrip = yes, - ( if TestString = RoundTripString then - io.format("TEST PASSED (accepted: %s)\n", - [s(string(DateTime))], !IO) + ( if TestStr = RoundTripStr then + io.format("PASS %s\n", [s(string(DateTimeP))], !IO) else - io.write_string("TEST FAILED (roundtrip failed)\n", !IO) + io.format("FAIL %s\nTEST %s\nTRIP %s\n", + [s(string(DateTimeP)), s(TestStr), s(RoundTripStr)], !IO) ) ; CheckRoundTrip = no, - io.format("TEST PASSED (accepted: %s: to-string: \"%s\"))\n", - [s(string(DateTime)), s(RoundTripString)], !IO) + io.format("PASS %s\nTEST %s\nTRIP %s\n", + [s(string(DateTimeP)), s(TestStr), s(RoundTripStr)], !IO) ) else - io.format("TEST FAILED (rejected: %s)\n", [s(Desc)], !IO) - ). + MaybeDateTimeP = no, + io.format("FAIL reject\n", [], !IO) + ), + ( try [] + DateTimeF = det_date_time_from_string(TestStr) + then + MaybeDateTimeF = yes(DateTimeF) + catch_any _ -> + MaybeDateTimeF = no + ), + report_any_disagrement(MaybeDateTimeP, MaybeDateTimeF, !IO). %---------------------------------------------------------------------------% -:- pred test_invalid_date_times(io::di, io::uo) is det. +:- pred test_invalid_date_time(dt_conv_test::in, io::di, io::uo) is cc_multi. -test_invalid_date_times(!IO) :- - io.write_string( - "=== Testing date_time_from_string/2 with invalid inputs ===\n\n", - !IO), - list.foldl(do_test_invalid_date_time, invalid_date_times, !IO), - io.nl(!IO). - -:- pred do_test_invalid_date_time(dt_conv_test::in, io::di, io::uo) is det. - -do_test_invalid_date_time(Test, !IO) :- - Test = dt_conv_test(Desc, TestString), - io.format("date_time_from_string(\"%s\") ===> ", [s(TestString)], !IO), - ( if date_time_from_string(TestString, DateTime) then - DateTimeString = date_time_to_string(DateTime), - io.format("TEST FAILED (accepted: %s)\n", - [s(DateTimeString)], !IO) +test_invalid_date_time(Test, !IO) :- + Test = dt_conv_test(Desc, TestStr), + write_test_call(tsl_30, TestStr, Desc, !IO), + ( if date_time_from_string(TestStr, DateTimeP) then + MaybeDateTimeP = yes(DateTimeP), + DateTimePStr = date_time_to_string(DateTimeP), + io.format("FAIL: ERROR NOT DETECTED %s\n", [s(DateTimePStr)], !IO) else - io.format("TEST PASSED (rejected: %s)\n", [s(Desc)], !IO) - ). - -%---------------------------------------------------------------------------% - -:- pred test_exception_valid_date_times(io::di, io::uo) is cc_multi. - -test_exception_valid_date_times(!IO) :- - io.write_string( - "=== Testing det_date_from_string/1 with valid inputs ===\n\n", !IO), - list.foldl(do_test_exception_valid_date_time, valid_date_times, !IO), - io.nl(!IO). - -:- pred do_test_exception_valid_date_time(dt_conv_test::in, io::di, io::uo) - is cc_multi. - -do_test_exception_valid_date_time(Test, !IO) :- - Test = dt_conv_test(Desc, TestString), - io.format("det_date_time_from_string(\"%s\") ===> ", [s(TestString)], !IO), + MaybeDateTimeP = no, + io.format("PASS: ERROR DETECTED\n", [], !IO) + ), ( try [] - DateTime = det_date_time_from_string(TestString) + DateTimeF = det_date_time_from_string(TestStr) then - io.format("TEST PASSED (accepted: %s)\n", - [s(string(DateTime))], !IO) + MaybeDateTimeF = yes(DateTimeF) catch_any _ -> - io.format("TEST FAILED (exception: %s)\n", [s(Desc)], !IO) - ). - -%---------------------------------------------------------------------------% - -:- pred test_exception_invalid_date_times(io::di, io::uo) is cc_multi. - -test_exception_invalid_date_times(!IO) :- - io.write_string( - "=== Testing det_date_from_string/1 with invalid inputs ===\n\n", !IO), - list.foldl(do_test_exception_invalid_date_time, invalid_date_times, !IO), - io.nl(!IO). - -:- pred do_test_exception_invalid_date_time(dt_conv_test::in, io::di, io::uo) - is cc_multi. - -do_test_exception_invalid_date_time(Test, !IO) :- - Test = dt_conv_test(Desc, TestString), - io.format("det_date_time_from_string(\"%s\") ===> ", [s(TestString)], !IO), - ( try [] - DateTime = det_date_time_from_string(TestString) - then - io.format("TEST FAILED (accepted: %s)\n", - [s(string(DateTime))], !IO) - catch_any _ -> - io.format("TEST PASSED (exception: %s)\n", [s(Desc)], !IO) - ). + MaybeDateTimeF = no + ), + report_any_disagrement(MaybeDateTimeP, MaybeDateTimeF, !IO). %---------------------------------------------------------------------------% @@ -173,7 +136,7 @@ valid_date_times = [ dt_conv_test("year -1", "-0001-01-01 00:00:00"), dt_conv_test("end of year -1", "-0001-12-31 00:00:00"), - dt_conv_test("Julian period origin in proleptic Gregorian calendar", + dt_conv_test("Julian origin in proleptic Gregorian calendar", "-4713-11-24 00:00:00"), dt_conv_test("five-digit year", "10000-01-01 00:00:00"), @@ -331,10 +294,42 @@ invalid_date_times = [ dt_conv_test("missing day", "2024-01- 00:00:00"), dt_conv_test("missing hour", "2024-01-01 :00:00"), dt_conv_test("missing minute", "2024-01-01 00::00"), - dt_conv_test("trailing colon with no second digits", - "2024-01-01 00:00:") + dt_conv_test("trailing colon with no second digits", "2024-01-01 00:00:") ]. +%---------------------------------------------------------------------------% + +:- pred report_any_disagrement(maybe(date_time)::in, maybe(date_time)::in, + io::di, io::uo) is det. + +report_any_disagrement(MaybeDateTimeP, MaybeDateTimeF, !IO) :- + ( if MaybeDateTimeP = MaybeDateTimeF then + true + else + io.format("DISAGREEMENT: pred %s, func %s\n", + [s(string.string(MaybeDateTimeP)), + s(string.string(MaybeDateTimeF))], !IO) + ). + +%---------------------------------------------------------------------------% + +:- type test_str_len + ---> tsl_18 + ; tsl_30. + +:- pred write_test_call(test_str_len::in, string::in, string::in, + io::di, io::uo) is det. + +write_test_call(TestStrLen, TestStr, Desc, !IO) :- + string.format("\n\"%s\":", [s(TestStr)], TestId), + ( + TestStrLen = tsl_18, + io.format("%-18s %s\n", [s(TestId), s(Desc)], !IO) + ; + TestStrLen = tsl_30, + io.format("%-30s %s\n", [s(TestId), s(Desc)], !IO) + ). + %---------------------------------------------------------------------------% :- end_module calendar_date_time_conv. %---------------------------------------------------------------------------% diff --git a/tests/hard_coded/calendar_duration_conv.exp b/tests/hard_coded/calendar_duration_conv.exp index d51df5023..a2f31333d 100644 --- a/tests/hard_coded/calendar_duration_conv.exp +++ b/tests/hard_coded/calendar_duration_conv.exp @@ -1,182 +1,272 @@ -=== Testing valid test data === +=== Testing with valid inputs === -pred "P1Y": PASS accepted duration(12, 0, 0, 0) -func "P1Y": PASS accepted duration(12, 0, 0, 0) -pred "P1M": PASS accepted duration(1, 0, 0, 0) -func "P1M": PASS accepted duration(1, 0, 0, 0) -pred "P1D": PASS accepted duration(0, 1, 0, 0) -func "P1D": PASS accepted duration(0, 1, 0, 0) -pred "P1Y2M": PASS accepted duration(14, 0, 0, 0) -func "P1Y2M": PASS accepted duration(14, 0, 0, 0) -pred "P1Y2D": PASS accepted duration(12, 2, 0, 0) -func "P1Y2D": PASS accepted duration(12, 2, 0, 0) -pred "P1M2D": PASS accepted duration(1, 2, 0, 0) -func "P1M2D": PASS accepted duration(1, 2, 0, 0) -pred "P1Y2M3D": PASS accepted duration(14, 3, 0, 0) -func "P1Y2M3D": PASS accepted duration(14, 3, 0, 0) -pred "PT1H": PASS accepted duration(0, 0, 3600, 0) -func "PT1H": PASS accepted duration(0, 0, 3600, 0) -pred "PT1M": PASS accepted duration(0, 0, 60, 0) -func "PT1M": PASS accepted duration(0, 0, 60, 0) -pred "PT1S": PASS accepted duration(0, 0, 1, 0) -func "PT1S": PASS accepted duration(0, 0, 1, 0) -pred "PT1H2M": PASS accepted duration(0, 0, 3720, 0) -func "PT1H2M": PASS accepted duration(0, 0, 3720, 0) -pred "PT1H2S": PASS accepted duration(0, 0, 3602, 0) -func "PT1H2S": PASS accepted duration(0, 0, 3602, 0) -pred "PT1M2S": PASS accepted duration(0, 0, 62, 0) -func "PT1M2S": PASS accepted duration(0, 0, 62, 0) -pred "PT1H2M3S": PASS accepted duration(0, 0, 3723, 0) -func "PT1H2M3S": PASS accepted duration(0, 0, 3723, 0) -pred "P1DT1H": PASS accepted duration(0, 1, 3600, 0) -func "P1DT1H": PASS accepted duration(0, 1, 3600, 0) -pred "P1Y2M3DT4H5M6S": PASS accepted duration(14, 3, 14706, 0) -func "P1Y2M3DT4H5M6S": PASS accepted duration(14, 3, 14706, 0) -pred "-P1D": PASS accepted duration(0, -1, 0, 0) -func "-P1D": PASS accepted duration(0, -1, 0, 0) -pred "-P1Y2M3DT4H5M6S": PASS accepted duration(-14, -3, -14706, 0) -func "-P1Y2M3DT4H5M6S": PASS accepted duration(-14, -3, -14706, 0) -pred "PT1.1S": PASS accepted duration(0, 0, 1, 100000) -func "PT1.1S": PASS accepted duration(0, 0, 1, 100000) -pred "PT1.12S": PASS accepted duration(0, 0, 1, 120000) -func "PT1.12S": PASS accepted duration(0, 0, 1, 120000) -pred "PT1.123S": PASS accepted duration(0, 0, 1, 123000) -func "PT1.123S": PASS accepted duration(0, 0, 1, 123000) -pred "PT1.1234S": PASS accepted duration(0, 0, 1, 123400) -func "PT1.1234S": PASS accepted duration(0, 0, 1, 123400) -pred "PT1.12345S": PASS accepted duration(0, 0, 1, 123450) -func "PT1.12345S": PASS accepted duration(0, 0, 1, 123450) -pred "PT1.123456S": PASS accepted duration(0, 0, 1, 123456) -func "PT1.123456S": PASS accepted duration(0, 0, 1, 123456) -pred "PT0.000001S": PASS accepted duration(0, 0, 0, 1) -func "PT0.000001S": PASS accepted duration(0, 0, 0, 1) -pred "PT0.999999S": PASS accepted duration(0, 0, 0, 999999) -func "PT0.999999S": PASS accepted duration(0, 0, 0, 999999) -pred "PT0.5S": PASS accepted duration(0, 0, 0, 500000) -func "PT0.5S": PASS accepted duration(0, 0, 0, 500000) -pred "PT0.0003S": PASS accepted duration(0, 0, 0, 300) -func "PT0.0003S": PASS accepted duration(0, 0, 0, 300) -pred "P0D": PASS accepted duration(0, 0, 0, 0) -func "P0D": PASS accepted duration(0, 0, 0, 0) -pred "P18M": PASS accepted duration(18, 0, 0, 0) - to-string "P1Y6M" -func "P18M": PASS accepted duration(18, 0, 0, 0) -pred "PT25H": PASS accepted duration(0, 1, 3600, 0) - to-string "P1DT1H" -func "PT25H": PASS accepted duration(0, 1, 3600, 0) -pred "PT90S": PASS accepted duration(0, 0, 90, 0) - to-string "PT1M30S" -func "PT90S": PASS accepted duration(0, 0, 90, 0) -pred "P1Y18M100DT10H15M90.0003S": PASS accepted duration(30, 100, 36990, 300) - to-string "P2Y6M100DT10H16M30.0003S" -func "P1Y18M100DT10H15M90.0003S": PASS accepted duration(30, 100, 36990, 300) -pred "PT0S": PASS accepted duration(0, 0, 0, 0) - to-string "P0D" -func "PT0S": PASS accepted duration(0, 0, 0, 0) -pred "PT0.0S": PASS accepted duration(0, 0, 0, 0) - to-string "P0D" -func "PT0.0S": PASS accepted duration(0, 0, 0, 0) -pred "PT0M": PASS accepted duration(0, 0, 0, 0) - to-string "P0D" -func "PT0M": PASS accepted duration(0, 0, 0, 0) -pred "PT0H": PASS accepted duration(0, 0, 0, 0) - to-string "P0D" -func "PT0H": PASS accepted duration(0, 0, 0, 0) -pred "P0M": PASS accepted duration(0, 0, 0, 0) - to-string "P0D" -func "P0M": PASS accepted duration(0, 0, 0, 0) -pred "P0Y0M0DT0H0M0S": PASS accepted duration(0, 0, 0, 0) - to-string "P0D" -func "P0Y0M0DT0H0M0S": PASS accepted duration(0, 0, 0, 0) +"P1Y": years only +PASS duration(12, 0, 0, 0) -=== Testing invalid test data === +"P1M": months only +PASS duration(1, 0, 0, 0) -pred "": PASS rejected empty string -func "": PASS exception empty string -pred " ": PASS rejected blank string -func " ": PASS exception blank string -pred "not a duration": PASS rejected not a duration -func "not a duration": PASS exception not a duration -pred "1Y2M3D": PASS rejected missing P prefix -func "1Y2M3D": PASS exception missing P prefix -pred "P": PASS rejected P with no components -func "P": PASS exception P with no components -pred " P": PASS rejected P with no components and leading whitespace -func " P": PASS exception P with no components and leading whitespace -pred "P ": PASS rejected P with no components and trailing whitespace -func "P ": PASS exception P with no components and trailing whitespace -pred "T1H": PASS rejected missing P prefix, starts with T -func "T1H": PASS exception missing P prefix, starts with T -pred "PT": PASS rejected P and T but no time components -func "PT": PASS exception P and T but no time components -pred "-P": PASS rejected negative P with no components -func "-P": PASS exception negative P with no components -pred "p1Y": PASS rejected lowercase p -func "p1Y": PASS exception lowercase p -pred "P1y": PASS rejected lowercase unit designator -func "P1y": PASS exception lowercase unit designator -pred "P1DT1H30MS": PASS rejected bare S with no digits -func "P1DT1H30MS": PASS exception bare S with no digits -pred "P1H": PASS rejected hours without T separator -func "P1H": PASS exception hours without T separator -pred "P1S": PASS rejected seconds without T separator -func "P1S": PASS exception seconds without T separator -pred "P1DT1H2M3Y": PASS rejected years after time components -func "P1DT1H2M3Y": PASS exception years after time components -pred "P1M1Y": PASS rejected years after months -func "P1M1Y": PASS exception years after months -pred "PT1S1H": PASS rejected hours after seconds -func "PT1S1H": PASS exception hours after seconds -pred "PT1S1M": PASS rejected minutes after seconds -func "PT1S1M": PASS exception minutes after seconds -pred "PT1M1H": PASS rejected hours after minutes -func "PT1M1H": PASS exception hours after minutes -pred "P1Y1Y": PASS rejected years specified twice -func "P1Y1Y": PASS exception years specified twice -pred "P1D1D": PASS rejected days specified twice -func "P1D1D": PASS exception days specified twice -pred "PT1H1H": PASS rejected hours specified twice -func "PT1H1H": PASS exception hours specified twice -pred "P-1Y": PASS rejected negative number after P -func "P-1Y": PASS exception negative number after P -pred "PT-1H": PASS rejected negative number after T -func "PT-1H": PASS exception negative number after T -pred "--P1D": PASS rejected double negative prefix -func "--P1D": PASS exception double negative prefix -pred "-P-1D": PASS rejected negative prefix and negative component -func "-P-1D": PASS exception negative prefix and negative component -pred "P1.5Y": PASS rejected fractional years -func "P1.5Y": PASS exception fractional years -pred "P1.5M": PASS rejected fractional months -func "P1.5M": PASS exception fractional months -pred "P1.5D": PASS rejected fractional days -func "P1.5D": PASS exception fractional days -pred "PT1.5H": PASS rejected fractional hours -func "PT1.5H": PASS exception fractional hours -pred "PT1.5M": PASS rejected fractional minutes -func "PT1.5M": PASS exception fractional minutes -pred "PT1.1234567S": PASS rejected seven fractional digits on seconds -func "PT1.1234567S": PASS exception seven fractional digits on seconds -pred "PT1.S": PASS rejected decimal point with no fraction digits -func "PT1.S": PASS exception decimal point with no fraction digits -pred "PT.5S": PASS rejected no integer part before decimal point -func "PT.5S": PASS exception no integer part before decimal point -pred "P1D ": PASS rejected trailing space -func "P1D ": PASS exception trailing space -pred " P1D": PASS rejected leading space -func " P1D": PASS exception leading space -pred "P 1Y": PASS rejected whitespace between P and components -func "P 1Y": PASS exception whitespace between P and components -pred "P1DT1H30M extra": PASS rejected trailing text -func "P1DT1H30M extra": PASS exception trailing text -pred "P1DX": PASS rejected invalid character after component -func "P1DX": PASS exception invalid character after component -pred "P1DT ": PASS rejected T followed by whitespace -func "P1DT ": PASS exception T followed by whitespace -pred "PxY": PASS rejected letter where number expected -func "PxY": PASS exception letter where number expected -pred "PT1HxM": PASS rejected letter in time component -func "PT1HxM": PASS exception letter in time component -pred "P1DT": PASS rejected T with no time components -func "P1DT": PASS exception T with no time components +"P1D": days only +PASS duration(0, 1, 0, 0) +"P1Y2M": years and months +PASS duration(14, 0, 0, 0) + +"P1Y2D": years and days +PASS duration(12, 2, 0, 0) + +"P1M2D": months and days +PASS duration(1, 2, 0, 0) + +"P1Y2M3D": all date components +PASS duration(14, 3, 0, 0) + +"PT1H": hours only +PASS duration(0, 0, 3600, 0) + +"PT1M": minutes only +PASS duration(0, 0, 60, 0) + +"PT1S": seconds only +PASS duration(0, 0, 1, 0) + +"PT1H2M": hours and minutes +PASS duration(0, 0, 3720, 0) + +"PT1H2S": hours and seconds +PASS duration(0, 0, 3602, 0) + +"PT1M2S": minutes and seconds +PASS duration(0, 0, 62, 0) + +"PT1H2M3S": all time components +PASS duration(0, 0, 3723, 0) + +"P1DT1H": days and hours +PASS duration(0, 1, 3600, 0) + +"P1Y2M3DT4H5M6S": all components +PASS duration(14, 3, 14706, 0) + +"-P1D": negative days +PASS duration(0, -1, 0, 0) + +"-P1Y2M3DT4H5M6S": negative all components +PASS duration(-14, -3, -14706, 0) + +"PT1.1S": one fractional digit +PASS duration(0, 0, 1, 100000) + +"PT1.12S": two fractional digits +PASS duration(0, 0, 1, 120000) + +"PT1.123S": three fractional digits +PASS duration(0, 0, 1, 123000) + +"PT1.1234S": four fractional digits +PASS duration(0, 0, 1, 123400) + +"PT1.12345S": five fractional digits +PASS duration(0, 0, 1, 123450) + +"PT1.123456S": six fractional digits +PASS duration(0, 0, 1, 123456) + +"PT0.000001S": smallest nonzero microsecond +PASS duration(0, 0, 0, 1) + +"PT0.999999S": largest microsecond value +PASS duration(0, 0, 0, 999999) + +"PT0.5S": fractional seconds only +PASS duration(0, 0, 0, 500000) + +"PT0.0003S": zero seconds with fraction +PASS duration(0, 0, 0, 300) + +"P0D": zero days (canonical zero form) +PASS duration(0, 0, 0, 0) + +"P18M": large months -> 1Y 6M +PASS duration(18, 0, 0, 0) +TEST P18M +TRIP P1Y6M + +"PT25H": large hours -> 1D 1H +PASS duration(0, 1, 3600, 0) +TEST PT25H +TRIP P1DT1H + +"PT90S": large seconds -> 1M 30S +PASS duration(0, 0, 90, 0) +TEST PT90S +TRIP PT1M30S + +"P1Y18M100DT10H15M90.0003S": doc example: P1Y18M100DT10H15M90.0003S -> P2Y6M100DT10H16M30.0003S +PASS duration(30, 100, 36990, 300) +TEST P1Y18M100DT10H15M90.0003S +TRIP P2Y6M100DT10H16M30.0003S + +"PT0S": zero seconds +PASS duration(0, 0, 0, 0) +TEST PT0S +TRIP P0D + +"PT0.0S": zero seconds with explicit fraction +PASS duration(0, 0, 0, 0) +TEST PT0.0S +TRIP P0D + +"PT0M": zero minutes +PASS duration(0, 0, 0, 0) +TEST PT0M +TRIP P0D + +"PT0H": zero hours +PASS duration(0, 0, 0, 0) +TEST PT0H +TRIP P0D + +"P0M": zero months +PASS duration(0, 0, 0, 0) +TEST P0M +TRIP P0D + +"P0Y0M0DT0H0M0S": all explicit zeros +PASS duration(0, 0, 0, 0) +TEST P0Y0M0DT0H0M0S +TRIP P0D + +=== Testing with invalid inputs === + +"": empty string +PASS: ERROR DETECTED + +" ": blank string +PASS: ERROR DETECTED + +"not a duration": not a duration +PASS: ERROR DETECTED + +"1Y2M3D": missing P prefix +PASS: ERROR DETECTED + +"P": P with no components +PASS: ERROR DETECTED + +" P": P with no components and leading whitespace +PASS: ERROR DETECTED + +"P ": P with no components and trailing whitespace +PASS: ERROR DETECTED + +"T1H": missing P prefix, starts with T +PASS: ERROR DETECTED + +"PT": P and T but no time components +PASS: ERROR DETECTED + +"-P": negative P with no components +PASS: ERROR DETECTED + +"p1Y": lowercase p +PASS: ERROR DETECTED + +"P1y": lowercase unit designator +PASS: ERROR DETECTED + +"P1DT1H30MS": bare S with no digits +PASS: ERROR DETECTED + +"P1H": hours without T separator +PASS: ERROR DETECTED + +"P1S": seconds without T separator +PASS: ERROR DETECTED + +"P1DT1H2M3Y": years after time components +PASS: ERROR DETECTED + +"P1M1Y": years after months +PASS: ERROR DETECTED + +"PT1S1H": hours after seconds +PASS: ERROR DETECTED + +"PT1S1M": minutes after seconds +PASS: ERROR DETECTED + +"PT1M1H": hours after minutes +PASS: ERROR DETECTED + +"P1Y1Y": years specified twice +PASS: ERROR DETECTED + +"P1D1D": days specified twice +PASS: ERROR DETECTED + +"PT1H1H": hours specified twice +PASS: ERROR DETECTED + +"P-1Y": negative number after P +PASS: ERROR DETECTED + +"PT-1H": negative number after T +PASS: ERROR DETECTED + +"--P1D": double negative prefix +PASS: ERROR DETECTED + +"-P-1D": negative prefix and negative component +PASS: ERROR DETECTED + +"P1.5Y": fractional years +PASS: ERROR DETECTED + +"P1.5M": fractional months +PASS: ERROR DETECTED + +"P1.5D": fractional days +PASS: ERROR DETECTED + +"PT1.5H": fractional hours +PASS: ERROR DETECTED + +"PT1.5M": fractional minutes +PASS: ERROR DETECTED + +"PT1.1234567S": seven fractional digits on seconds +PASS: ERROR DETECTED + +"PT1.S": decimal point with no fraction digits +PASS: ERROR DETECTED + +"PT.5S": no integer part before decimal point +PASS: ERROR DETECTED + +"P1D ": trailing space +PASS: ERROR DETECTED + +" P1D": leading space +PASS: ERROR DETECTED + +"P 1Y": whitespace between P and components +PASS: ERROR DETECTED + +"P1DT1H30M extra": trailing text +PASS: ERROR DETECTED + +"P1DX": invalid character after component +PASS: ERROR DETECTED + +"P1DT ": T followed by whitespace +PASS: ERROR DETECTED + +"PxY": letter where number expected +PASS: ERROR DETECTED + +"PT1HxM": letter in time component +PASS: ERROR DETECTED + +"P1DT": T with no time components +PASS: ERROR DETECTED diff --git a/tests/hard_coded/calendar_duration_conv.m b/tests/hard_coded/calendar_duration_conv.m index c8421f736..e79f9a35f 100644 --- a/tests/hard_coded/calendar_duration_conv.m +++ b/tests/hard_coded/calendar_duration_conv.m @@ -22,79 +22,92 @@ :- import_module bool. :- import_module calendar. :- import_module list. +:- import_module maybe. :- import_module string. %---------------------------------------------------------------------------% main(!IO) :- - io.write_string("=== Testing valid test data ===\n\n", !IO), - list.foldl(do_test_valid_duration(yes), valid_durations, !IO), - list.foldl(do_test_valid_duration(no), valid_no_roundtrip_durations, !IO), + io.write_string("=== Testing with valid inputs ===\n", !IO), + list.foldl(test_valid_duration(yes), valid_durations, !IO), + list.foldl(test_valid_duration(no), valid_no_roundtrip_durations, !IO), + io.nl(!IO), + io.write_string("=== Testing with invalid inputs ===\n", !IO), + list.foldl(test_invalid_duration, invalid_durations, !IO). - io.write_string("=== Testing invalid test data ===\n\n", !IO), - list.foldl(do_test_invalid_duration, invalid_durations, !IO), - io.nl(!IO). +%---------------------------------------------------------------------------% +% +% In both test_valid_duration and test_invalid_duration, we exercise +% both the semidet predicate duration_from_string/2, and its det function +% version, duration_from_string/1. +% +% We always print the results from the semidet predicate version. +% We print the results from the det function version ONLY if it differs +% from the result of the semidet predicate version on the same input. +% +%---------------------------------------------------------------------------% -:- pred do_test_valid_duration(bool::in, duration_test::in, +:- pred test_valid_duration(bool::in, duration_test::in, io::di, io::uo) is cc_multi. -do_test_valid_duration(CheckRoundTrip, Test, !IO) :- - Test = duration_test(Desc, TestString), - write_test_call("pred", TestString, !IO), - ( if duration_from_string(TestString, DurationP) then - RoundTripString = duration_to_string(DurationP), +test_valid_duration(CheckRoundTrip, Test, !IO) :- + Test = duration_test(Desc, TestStr), + write_test_call(TestStr, Desc, !IO), + ( if duration_from_string(TestStr, DurationP) then + MaybeDurationP = yes(DurationP), + RoundTripStr = duration_to_string(DurationP), ( CheckRoundTrip = yes, - ( if TestString = RoundTripString then - io.format("PASS accepted %s\n", - [s(string(DurationP))], !IO) + ( if TestStr = RoundTripStr then + io.format("PASS %s\n", [s(string(DurationP))], !IO) else - io.format("FAIL roundtrip \"%s\"\n", - [s(RoundTripString)], !IO) + io.format("FAIL %s\nTEST %s\nTRIP %s\n", + [s(string(DurationP)), s(TestStr), s(RoundTripStr)], !IO) ) ; CheckRoundTrip = no, - io.format("PASS accepted %s\n%33s to-string \"%s\"\n", - [s(string(DurationP)), s(""), s(RoundTripString)], !IO) + io.format("PASS %s\nTEST %s\nTRIP %s\n", + [s(string(DurationP)), s(TestStr), s(RoundTripStr)], !IO) ) else - io.format("FAIL reject %s\n", [s(Desc)], !IO) + MaybeDurationP = no, + io.format("FAIL reject\n", [], !IO) ), - - write_test_call("func", TestString, !IO), ( try [] - DurationF = det_duration_from_string(TestString) + DurationF = det_duration_from_string(TestStr) then - io.format("PASS accepted %s\n", [s(string(DurationF))], !IO) + MaybeDurationF = yes(DurationF) catch_any _ -> - io.format("FAIL exception %s\n", [s(Desc)], !IO) - ). + MaybeDurationF = no + ), + report_any_disagrement(MaybeDurationP, MaybeDurationF, !IO). %---------------------------------------------------------------------------% -:- pred do_test_invalid_duration(duration_test::in, io::di, io::uo) - is cc_multi. +:- pred test_invalid_duration(duration_test::in, io::di, io::uo) is cc_multi. -do_test_invalid_duration(Test, !IO) :- - Test = duration_test(Desc, TestString), - write_test_call("pred", TestString, !IO), - ( if duration_from_string(TestString, DurationP) then - DurationStringP = duration_to_string(DurationP), - io.format("FAIL accepted %s\n", [s(DurationStringP)], !IO) +test_invalid_duration(Test, !IO) :- + Test = duration_test(Desc, TestStr), + write_test_call(TestStr, Desc, !IO), + ( if duration_from_string(TestStr, DurationP) then + MaybeDurationP = yes(DurationP), + DurationPStr = duration_to_string(DurationP), + io.format("FAIL: ERROR NOT DETECTED %s\n", [s(DurationPStr)], !IO) else - io.format("PASS rejected %s\n", [s(Desc)], !IO) + % Since we are invoked only on intended-to-be-invalid test data, + % this is the path successful tests should take. + MaybeDurationP = no, + io.format("PASS: ERROR DETECTED\n", [], !IO) ), - - write_test_call("func", TestString, !IO), ( try [] - DurationF = det_duration_from_string(TestString) + DurationF = det_duration_from_string(TestStr) then - io.format("FAIL accepted %s\n", - [s(string(DurationF))], !IO) + MaybeDurationF = yes(DurationF) catch_any _ -> - io.format("PASS exception %s\n", [s(Desc)], !IO) - ). + MaybeDurationF = no + ), + report_any_disagrement(MaybeDurationP, MaybeDurationF, !IO). %---------------------------------------------------------------------------% @@ -107,9 +120,7 @@ do_test_invalid_duration(Test, !IO) :- %---------------------------------------------------------------------------% % Valid durations created from strings are mostly round-trippable through -% duration_to_string/1. The exceptions are durations that are not already -% in normal form (whose components overflow their unit) and zero durations -% in any spelling other than "P0D". +% duration_to_string/1. The exceptions are listed separately below. % These can be round-tripped through duration_to_string/1. % @@ -242,8 +253,7 @@ invalid_durations = [ duration_test("fractional minutes", "PT1.5M"), % Fractional seconds errors. - duration_test("seven fractional digits on seconds", - "PT1.1234567S"), + duration_test("seven fractional digits on seconds", "PT1.1234567S"), duration_test("decimal point with no fraction digits", "PT1.S"), duration_test("no integer part before decimal point", "PT.5S"), @@ -265,11 +275,25 @@ invalid_durations = [ %---------------------------------------------------------------------------% +:- pred report_any_disagrement(maybe(duration)::in, maybe(duration)::in, + io::di, io::uo) is det. + +report_any_disagrement(MaybeDateTimeP, MaybeDateTimeF, !IO) :- + ( if MaybeDateTimeP = MaybeDateTimeF then + true + else + io.format("DISAGREEMENT: pred %s, func %s\n", + [s(string.string(MaybeDateTimeP)), + s(string.string(MaybeDateTimeF))], !IO) + ). + +%---------------------------------------------------------------------------% + :- pred write_test_call(string::in, string::in, io::di, io::uo) is det. -write_test_call(Prefix, TestStr, !IO) :- - string.format("%s \"%s\":", [s(Prefix), s(TestStr)], TestId), - io.format("%-33s ", [s(TestId)], !IO). +write_test_call(TestStr, Desc, !IO) :- + string.format("\n\"%s\":", [s(TestStr)], TestId), + io.format("%-36s %s\n", [s(TestId), s(Desc)], !IO). %---------------------------------------------------------------------------% :- end_module calendar_duration_conv.