From d265739f7d659fab5fbbf86a146c87955f5bf682 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Wed, 20 Mar 2024 14:15:26 +0800 Subject: [PATCH 1/3] MDL-81074 core: Add assertTimeStringMatches PHPUnit Assertion Also raised as https://github.com/sebastianbergmann/phpunit/issues/5757 --- lib/phpunit/classes/base_testcase.php | 25 +++++++++ lib/phpunit/tests/basic_test.php | 78 +++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/lib/phpunit/classes/base_testcase.php b/lib/phpunit/classes/base_testcase.php index 1ce5e56db0e..3ef4aa5cc04 100644 --- a/lib/phpunit/classes/base_testcase.php +++ b/lib/phpunit/classes/base_testcase.php @@ -121,6 +121,31 @@ abstract class base_testcase extends PHPUnit\Framework\TestCase { return $hash; } + /** + * Assert that two Date/Time strings are equal. + * + * The strings generated by \DateTime, \strtotime, \date, \time, etc. are generated outside of our control. + * From time-to-time string changes are made. + * One such example is from ICU 72.1 which changed the time format to include a narrow-non-breaking-space (U+202F) + * between the time and AM/PM. + * + * We should not update our tests to match these changes, as it is not our code that is + * generating the strings and they may change again. + * In addition, the changes are not equal amongst all systems as they depend on the version of ICU installed. + * + * @param string $expected + * @param string $actual + * @param string $message + */ + public function assertEqualsIgnoringWhitespace($expected, $actual, string $message = ''): void { + // ICU 72.1 introduced the use of a narrow-non-breaking-space (U+202F) between the time and the AM/PM. + // Normalise all whitespace when performing the comparison. + $expected = preg_replace('/\s+/u', ' ', $expected); + $actual = preg_replace('/\s+/u', ' ', $actual); + + $this->assertEquals($expected, $actual, $message); + } + /** * Parse out the options from the tag using DOM object tree. * diff --git a/lib/phpunit/tests/basic_test.php b/lib/phpunit/tests/basic_test.php index 190807b17c6..88ab7bd6ecb 100644 --- a/lib/phpunit/tests/basic_test.php +++ b/lib/phpunit/tests/basic_test.php @@ -145,6 +145,84 @@ STRING; self::assertTag(['id' => 'testid'], "
"); } + /** + * Tests for assertEqualsIgnoringWhitespace. + * + * @param string $expected + * @param string $actual + * @param bool $expectationvalid + * @dataProvider equals_ignoring_whitespace_provider + */ + public function test_assertEqualsIgnoringWhitespace( // phpcs:ignore + string $expected, + string $actual, + bool $expectationvalid, + ): void { + if (!$expectationvalid) { + $this->expectException(\PHPUnit\Framework\ExpectationFailedException::class); + } + self::assertEqualsIgnoringWhitespace($expected, $actual); + } + + /** + * Data provider for assertEqualsIgnoringWhitespace tests + * + * @return array + */ + public static function equals_ignoring_whitespace_provider(): array { + return [ + 'equal' => ['a b c', 'a b c', true], + 'equal with whitespace' => ["a b c", "a\nb c", true], + 'equal with extra whitespace' => ["a b c", "a\nb c", true], + 'whitespace missing' => ["ab c", "a\nb c", false], + 'not equal' => ['a b c', 'a b d', false], + 'various space types' => [ + implode(' ', [ + '20', // Regular space. + "a0", // No-Break Space (NBSP). + "80", // Ogham Space Mark. + "0", // En Quad. + "1", // Em Quad. + "2", // En Space. + "3", // Em Space. + "4", // Three-Per-Em Space. + "5", // Four-Per-Em Space. + "6", // Six-Per-Em Space. + "7", // Figure Space. + "8", // Punctuation Space. + "9", // Thin Space. + "0a", // Hair Space. + "2f", // Narrow No-Break Space (NNBSP). + "5f", // Medium Mathematical Space. + "3000", // Ideographic Space. + ".", + ]), + implode('', [ + // All space chars taken from https://www.compart.com/en/unicode/category/Zs. + "20\u{0020}", // Regular space. + "a0\u{00a0}", // No-Break Space (NBSP). + "80\u{1680}", // Ogham Space Mark. + "0\u{2000}", // En Quad. + "1\u{2001}", // Em Quad. + "2\u{2002}", // En Space. + "3\u{2003}", // Em Space. + "4\u{2004}", // Three-Per-Em Space. + "5\u{2005}", // Four-Per-Em Space. + "6\u{2006}", // Six-Per-Em Space. + "7\u{2007}", // Figure Space. + "8\u{2008}", // Punctuation Space. + "9\u{2009}", // Thin Space. + "0a\u{200a}", // Hair Space. + "2f\u{202f}", // Narrow No-Break Space (NNBSP). + "5f\u{205f}", // Medium Mathematical Space. + "3000\u{3000}", // Ideographic Space. + ".", + ]), + true, + ], + ]; + } + // Uncomment following tests to see logging of unexpected changes in global state and database. /* public function test_db_modification() { From bd4c8e3a70770144dc262db8f0541c63600f8544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luca=20B=C3=B6sch?= Date: Wed, 28 Feb 2024 10:44:27 +0100 Subject: [PATCH 2/3] MDL-81074 core: align core_date::strftime results for numeric and string --- lib/classes/date.php | 4 +-- lib/tests/date_test.php | 61 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/lib/classes/date.php b/lib/classes/date.php index 01b8ac0b664..bcc575ca25a 100644 --- a/lib/classes/date.php +++ b/lib/classes/date.php @@ -717,10 +717,10 @@ class core_date { // Windows format. $locale = $locale ?: get_string('locale', 'langconfig'); - // The following code is taken from https://github.com/alphp/strftime without modifications. + // The following code is taken from https://github.com/alphp/strftime. // phpcs:disable if (!($timestamp instanceof DateTimeInterface)) { - $timestamp = is_int($timestamp) ? '@' . $timestamp : (string) $timestamp; + $timestamp = is_numeric($timestamp) ? '@' . $timestamp : (string) $timestamp; try { $timestamp = new DateTime($timestamp); diff --git a/lib/tests/date_test.php b/lib/tests/date_test.php index 74060fbe07e..c7dc89caef4 100644 --- a/lib/tests/date_test.php +++ b/lib/tests/date_test.php @@ -614,4 +614,65 @@ class date_test extends advanced_testcase { $this->assertSame($zone, $tz->getName()); } } + + /** + * Data provider for the values for test_core_strftime(). + * + * @return array + */ + public static function get_strftime_provider(): array { + return [ + 'string_c' => [ + "1708405742", + null, + "20 February 2024 at 6:09 pm", + ], + 'numeric_c' => [ + 1708405742, + null, + "20 February 2024 at 6:09 pm", + ], + 'string_strftimedatetime' => [ + "1708405742", + "strftimedatetime", + "20 February 2024, 06:09 PM", + ], + 'numeric_strftimedatetime' => [ + 1708405742, + "strftimedatetime", + "20 February 2024, 06:09 PM", + ], + 'string_strftimedatetimeshortaccurate' => [ + "1708405742", + "strftimedatetimeshortaccurate", + "20/02/24, 18:09:02", + ], + 'numeric_strftimedatetimeshortaccurate' => [ + 1708405742, + "strftimedatetimeshortaccurate", + "20/02/24, 18:09:02", + ], + ]; + } + + /** + * Test \core_date::strftime function. + * + * @dataProvider get_strftime_provider + * @param mixed $input Input passed to strftime + * @param string|null $format The date format to pass to strftime, falls back to '%c' if null + * @param string $expected The output generated by strftime + * + * @covers \core_date::strftime + */ + public function test_core_strftime(mixed $input, string|null $format, string $expected): void { + $this->resetAfterTest(); + $this->setTimezone('Pacific/Auckland', 'Pacific/Auckland'); + if (!$format) { + $format = "%c"; + } else { + $format = get_string($format, 'langconfig'); + } + $this->assertSame($expected, core_date::strftime($format, $input)); + } } From 054d15a42f55b54eacd65b640d899fee404b176a Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 18 Mar 2024 23:41:10 +0800 Subject: [PATCH 3/3] MDL-81074 core: Simplify strftime tests --- lib/tests/date_test.php | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/lib/tests/date_test.php b/lib/tests/date_test.php index c7dc89caef4..ed825e84d4e 100644 --- a/lib/tests/date_test.php +++ b/lib/tests/date_test.php @@ -624,33 +624,33 @@ class date_test extends advanced_testcase { return [ 'string_c' => [ "1708405742", - null, - "20 February 2024 at 6:09 pm", + "%c", + "20 February 2024 at 1:09 pm", ], 'numeric_c' => [ 1708405742, - null, - "20 February 2024 at 6:09 pm", + "%c", + "20 February 2024 at 1:09 pm", ], 'string_strftimedatetime' => [ "1708405742", - "strftimedatetime", - "20 February 2024, 06:09 PM", + get_string("strftimedatetime", 'langconfig'), + "20 February 2024, 01:09 PM", ], 'numeric_strftimedatetime' => [ 1708405742, - "strftimedatetime", - "20 February 2024, 06:09 PM", + get_string("strftimedatetime", 'langconfig'), + "20 February 2024, 01:09 PM", ], 'string_strftimedatetimeshortaccurate' => [ "1708405742", - "strftimedatetimeshortaccurate", - "20/02/24, 18:09:02", + get_string("strftimedatetimeshortaccurate", 'langconfig'), + "20/02/24, 13:09:02", ], 'numeric_strftimedatetimeshortaccurate' => [ 1708405742, - "strftimedatetimeshortaccurate", - "20/02/24, 18:09:02", + get_string("strftimedatetimeshortaccurate", 'langconfig'), + "20/02/24, 13:09:02", ], ]; } @@ -658,21 +658,12 @@ class date_test extends advanced_testcase { /** * Test \core_date::strftime function. * - * @dataProvider get_strftime_provider + * @dataProvider get_strftime_provider * @param mixed $input Input passed to strftime - * @param string|null $format The date format to pass to strftime, falls back to '%c' if null + * @param string $format The date format to pass to strftime, falls back to '%c' if null * @param string $expected The output generated by strftime - * - * @covers \core_date::strftime */ - public function test_core_strftime(mixed $input, string|null $format, string $expected): void { - $this->resetAfterTest(); - $this->setTimezone('Pacific/Auckland', 'Pacific/Auckland'); - if (!$format) { - $format = "%c"; - } else { - $format = get_string($format, 'langconfig'); - } - $this->assertSame($expected, core_date::strftime($format, $input)); + public function test_strftime(mixed $input, string $format, string $expected): void { + $this->assertEqualsIgnoringWhitespace($expected, core_date::strftime($format, $input)); } }