From 11faeaa7e723d01583ec1524cc9adc2ee40b750f Mon Sep 17 00:00:00 2001 From: Joshua Ramon Enslin Date: Thu, 11 Jul 2024 14:53:05 +0200 Subject: [PATCH] Improve test coverage --- phpunit.xml | 2 +- src/MD_STD.php | 45 +- src/MD_STD_IN.php | 2 +- src/testing/MD_STD_TEST_PROVIDERS.php | 6 +- tests/MDFormatterTest.php | 107 +++++ tests/MD_STD_IN_Test.php | 286 +++++++----- tests/MD_STD_Test.php | 650 +++++++++++++++++++++++++- tests/bootstrap.php | 2 +- 8 files changed, 944 insertions(+), 156 deletions(-) create mode 100644 tests/MDFormatterTest.php diff --git a/phpunit.xml b/phpunit.xml index 7b86f75..64d7769 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,5 +1,5 @@ - + tests/ diff --git a/src/MD_STD.php b/src/MD_STD.php index 2ac7089..919b52b 100644 --- a/src/MD_STD.php +++ b/src/MD_STD.php @@ -110,7 +110,7 @@ final class MD_STD { public static function unlink(string $filename):void { if (\unlink($filename) === false) { - throw new Exception("Failed to delete: $filename"); + throw new MDFileDoesNotExist("Failed to delete: $filename"); } } @@ -173,26 +173,6 @@ final class MD_STD { } - /** - * Function checking if a string starts with another. - * DEPRECATED. Can be replaced by PHP8's str_starts_with. - * - * @param non-empty-string $haystack String to check. - * @param non-empty-string $needle Potential start of $haystack. - * - * @return boolean - */ - public static function startsWith(string $haystack, string $needle):bool { - - if (substr($haystack, 0, \strlen($needle)) === $needle) { - return true; - } - else { - return false; - } - - } - /** * Function checking if a string starts with any input from the input array. * @@ -470,12 +450,9 @@ final class MD_STD { */ public static function checkUrlIsReachable(string $url):bool { - if (empty($url)) { + if (empty($url = MD_STD_IN::sanitize_url($url))) { throw new MDInvalidUrl("Input URL cannot be empty"); } - if (filter_var($url, FILTER_VALIDATE_URL) === false) { - throw new MDInvalidUrl("URL to check (" . $url . ") does not seem to be a valid URL"); - } $ch = self::curl_init($url, 5000); curl_setopt_array($ch, [ @@ -869,7 +846,11 @@ final class MD_STD { */ public static function string_to_color_code(string $str):string { - return \substr(\dechex(\crc32($str)), 0, 6); + $output = \dechex(\crc32($str)); + + if (\strlen($output) < 6) return '000000'; + + return \substr($output, 0, 6); } @@ -911,10 +892,12 @@ final class MD_STD { $max = count($input); $offset = 0; + $sizePerEntry = max($size - 1, 1); // Size - 1 is expected, but size below 1 ends in endless loops + while ($offset < $max) { - $cur = array_slice($input, $offset, $size - 1); + $cur = array_slice($input, $offset, $sizePerEntry); if (!empty($cur)) $output[] = $cur; - $offset += $size - 1; + $offset += $sizePerEntry; } return $output; @@ -940,10 +923,12 @@ final class MD_STD { $max = count($input); $offset = 0; + $sizePerEntry = max($size - 1, 1); // Size - 1 is expected, but size below 1 ends in endless loops + while ($offset < $max) { - $cur = array_slice($input, $offset, $size - 1); + $cur = array_slice($input, $offset, $sizePerEntry); if (!empty($cur)) $output[] = $cur; - $offset += $size - 1; + $offset += $sizePerEntry; } return $output; diff --git a/src/MD_STD_IN.php b/src/MD_STD_IN.php index 8dc23ef..54b060a 100644 --- a/src/MD_STD_IN.php +++ b/src/MD_STD_IN.php @@ -351,7 +351,7 @@ final class MD_STD_IN { $output = \str_replace(",", ".", $input); if (($output = \filter_var($output, FILTER_VALIDATE_FLOAT)) === false) { - throw new MDgenericInvalidInputsException("Input is readable as a floating point value"); + throw new MDgenericInvalidInputsException("Input is not readable as a floating point value"); } return $output; diff --git a/src/testing/MD_STD_TEST_PROVIDERS.php b/src/testing/MD_STD_TEST_PROVIDERS.php index bfb23a1..da93673 100644 --- a/src/testing/MD_STD_TEST_PROVIDERS.php +++ b/src/testing/MD_STD_TEST_PROVIDERS.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; * Tests for the manifest. */ final class MD_STD_TEST_PROVIDERS { - /** * Data provider for returning invalid URLs. * @@ -21,7 +20,7 @@ final class MD_STD_TEST_PROVIDERS { */ public static function invalid_url_provider():array { - $output = [ + return [ 'Space in protocol name' => ["h ttps://www.museum-digital.org"], 'Unwanted protocol' => ["telegram://www.museum-digital.org"], 'String without protocol' => ["www.museum-digital.org"], @@ -32,8 +31,6 @@ final class MD_STD_TEST_PROVIDERS { 'Overly long URL (> 10000 chars)' => ["https://www.museum-digital.org/" . str_repeat('a', 10000)], ]; - return $output; - } /** @@ -145,5 +142,4 @@ final class MD_STD_TEST_PROVIDERS { return $output; } - } diff --git a/tests/MDFormatterTest.php b/tests/MDFormatterTest.php new file mode 100644 index 0000000..8ca4572 --- /dev/null +++ b/tests/MDFormatterTest.php @@ -0,0 +1,107 @@ + + */ +declare(strict_types = 1); + +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\CoversClass; + +/** + * Tests for MD_STD_IN. + */ +#[small] +#[CoversClass(\MDFormatter::class)] +final class MDFormatterTest extends TestCase { + /** + * Function for testing formatMarkdownHeadline1(). + * + * @return void + */ + public function testFormatMarkdownHeadline1():void { + + self::assertEquals("a" . PHP_EOL . "=" . PHP_EOL . PHP_EOL, MDFormatter::formatMarkdownHeadline1("a")); + + } + + /** + * Function for testing formatMarkdownHeadline2(). + * + * @return void + */ + public function testFormatMarkdownHeadline2():void { + + self::assertEquals("a" . PHP_EOL . "-" . PHP_EOL . PHP_EOL, MDFormatter::formatMarkdownHeadline2("a")); + + } + + /** + * Function for testing formatMarkdownHeadline3(). + * + * @return void + */ + public function testFormatMarkdownHeadline3():void { + + self::assertEquals("### a" . PHP_EOL, MDFormatter::formatMarkdownHeadline3("a")); + + } + + /** + * Function for testing formatMarkdownHorizontalRule(). + * + * @return void + */ + public function testFormatMarkdownHorizontalRule():void { + + self::assertEquals("___" . PHP_EOL, MDFormatter::formatMarkdownHorizontalRule()); + + } + + /** + * Function for testing formatMarkdownNamedLink(). + * + * @return void + */ + public function testFormatMarkdownNamedLink():void { + + self::assertEquals("[a](https://example.com)", MDFormatter::formatMarkdownNamedLink("a", "https://example.com")); + + } + + /** + * Function for testing formatMarkdownBlockQuote(). + * + * @return void + */ + public function testFormatMarkdownBlockQuote():void { + + self::assertEquals("> Test" . PHP_EOL . "> test" . PHP_EOL, MDFormatter::formatMarkdownBlockQuote("Test" . PHP_EOL . "test")); + + } + + /** + * Function for testing formatMarkdownCodeBlock(). + * + * @return void + */ + public function testFormatMarkdownCodeBlock():void { + + self::assertEquals("```" . PHP_EOL . "Test" . PHP_EOL . "test" . PHP_EOL . "```" . PHP_EOL, MDFormatter::formatMarkdownCodeBlock("Test" . PHP_EOL . "test")); + + } + + /** + * Function for testing formatMarkdownUnorderedListItem(). + * + * @return void + */ + public function testFormatMarkdownUnorderedListItem():void { + + self::assertEquals("- a" . PHP_EOL, MDFormatter::formatMarkdownUnorderedListItem("a")); + self::assertEquals("- a" . PHP_EOL . " a" . PHP_EOL, MDFormatter::formatMarkdownUnorderedListItem("a" . PHP_EOL . "a")); + + } +} diff --git a/tests/MD_STD_IN_Test.php b/tests/MD_STD_IN_Test.php index 6b3e430..27e4aff 100644 --- a/tests/MD_STD_IN_Test.php +++ b/tests/MD_STD_IN_Test.php @@ -7,10 +7,16 @@ declare(strict_types = 1); use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\CoversClass; /** * Tests for MD_STD_IN. */ +#[small] +#[CoversClass(\MD_STD_IN::class)] final class MD_STD_IN_Test extends TestCase { /** * Data provider for valid IDs. @@ -34,17 +40,112 @@ final class MD_STD_IN_Test extends TestCase { } /** - * Function for testing sanitize_id(). + * Data provider for valid longitudes. * - * @small - * @covers \MD_STD_IN::sanitize_id - * @dataProvider \MD_STD_IN_Test::valid_id_provider + * @return array + */ + public static function valid_longitude_provider():array { + + $values = [ + ["1", 1.0], + ["12", 12.0], + [12, 12.0], + [12.0, 12.0], + [95, 95.0], + [-95, -95.0], + ]; + + $output = []; + foreach ($values as $value) { + $output[gettype($value[0]) . ': ' . var_export($value[0], true)] = $value; + } + return $output; + + } + + /** + * Data provider for invalid longitudes. + * + * @return array + */ + public static function invalid_longitude_provider():array { + + $values = [ + ["test", MDgenericInvalidInputsException::class], + ["test1", MDgenericInvalidInputsException::class], + ["", MDgenericInvalidInputsException::class], + ["1900", MDCoordinateOutOfRange::class], + [1900, MDCoordinateOutOfRange::class], + [-1900, MDCoordinateOutOfRange::class], + [185, MDCoordinateOutOfRange::class], + [-185, MDCoordinateOutOfRange::class], + ]; + + foreach ($values as $value) { + $output[gettype($value[0]) . ': ' . var_export($value[0], true)] = $value; + } + return $output; + + } + + /** + * Data provider for valid latitudes. + * + * @return array + */ + public static function valid_latitude_provider():array { + + $values = [ + ["1", 1.0], + ["12", 12.0], + [12, 12.0], + [12.0, 12.0], + [85, 85.0], + [-85, -85.0], + ]; + + $output = []; + foreach ($values as $value) { + $output[gettype($value[0]) . ': ' . var_export($value[0], true)] = $value; + } + return $output; + + } + + /** + * Data provider for invalid latitudes. + * + * @return array + */ + public static function invalid_latitude_provider():array { + + $values = [ + ["test", MDgenericInvalidInputsException::class], + ["test1", MDgenericInvalidInputsException::class], + ["", MDgenericInvalidInputsException::class], + ["1900", MDCoordinateOutOfRange::class], + [1900, MDCoordinateOutOfRange::class], + [-1900, MDCoordinateOutOfRange::class], + [95, MDCoordinateOutOfRange::class], + [-95, MDCoordinateOutOfRange::class], + ]; + + foreach ($values as $value) { + $output[gettype($value[0]) . ': ' . var_export($value[0], true)] = $value; + } + return $output; + + } + + /** + * Function for testing sanitize_id(). * * @param mixed $to_validate Input to validate. * @param integer $expected Expected output. * * @return void */ + #[DataProvider('valid_id_provider')] public function test_sanitize_id_works(mixed $to_validate, int $expected):void { self::assertEquals($expected, MD_STD_IN::sanitize_id($to_validate)); @@ -66,14 +167,11 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_id(). * - * @small - * @covers \MD_STD_IN::sanitize_id - * @dataProvider \MD_STD_IN_Test::invalid_id_provider - * * @param mixed $to_validate Input to validate. * * @return void */ + #[DataProvider('invalid_id_provider')] public function test_sanitize_id_fails(mixed $to_validate):void { self::expectException(MDpageParameterNotNumericException::class); @@ -98,15 +196,12 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_id_or_zero(). * - * @small - * @covers \MD_STD_IN::sanitize_id_or_zero - * @dataProvider \MD_STD_IN_Test::valid_id_or_zero_provider - * * @param mixed $to_validate Input to validate. * @param integer $expected Expected output. * * @return void */ + #[DataProvider('valid_id_or_zero_provider')] public function test_sanitize_id_or_zero_works(mixed $to_validate, int $expected):void { self::assertEquals($expected, MD_STD_IN::sanitize_id_or_zero($to_validate)); @@ -132,14 +227,11 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_id_or_zero(). * - * @small - * @covers \MD_STD_IN::sanitize_id_or_zero - * @dataProvider \MD_STD_IN_Test::invalid_id_provider_without_zero - * * @param mixed $to_validate Input to validate. * * @return void */ + #[DataProvider('invalid_id_provider_without_zero')] public function test_sanitize_id_or_zero_fails(mixed $to_validate):void { self::expectException(MDpageParameterNotNumericException::class); @@ -171,15 +263,12 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_text(). * - * @small - * @covers \MD_STD_IN::sanitize_text - * @dataProvider \MD_STD_IN_Test::text_with_expected_return_value_provider - * * @param mixed $to_validate Input to validate. * @param string $expected Expected output. * * @return void */ + #[DataProvider('text_with_expected_return_value_provider')] public function test_sanitize_text(mixed $to_validate, string $expected):void { self::assertEquals($expected, MD_STD_IN::sanitize_text($to_validate)); @@ -230,15 +319,12 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_rgb_color(). * - * @small - * @covers \MD_STD_IN::sanitize_rgb_color - * @dataProvider \MD_STD_IN_Test::valid_rgb_colors_provider - * * @param mixed $to_validate Input to validate. * @param string $expected Expected output. * * @return void */ + #[DataProvider('valid_rgb_colors_provider')] public function test_sanitize_rgb_color_works(mixed $to_validate, string $expected):void { self::assertEquals($expected, MD_STD_IN::sanitize_rgb_color($to_validate)); @@ -248,14 +334,11 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_rgb_color()'s failure modes. * - * @small - * @covers \MD_STD_IN::sanitize_rgb_color - * @dataProvider \MD_STD_IN_Test::invalid_rgb_colors_provider - * * @param mixed $to_validate Input to validate. * * @return void */ + #[DataProvider('invalid_rgb_colors_provider')] public function test_sanitize_rgb_color_fails(mixed $to_validate):void { self::expectException(MDInvalidColorCode::class); @@ -264,10 +347,22 @@ final class MD_STD_IN_Test extends TestCase { } /** - * Function for testing sanitize_id_array(). + * Function for testing sanitize_text_array(). * - * @small - * @covers \MD_STD_IN::sanitize_id_array + * @return void + */ + public function test_sanitize_text_array():void { + + self::assertEquals(["1"], MD_STD_IN::sanitize_text_array([1, ''])); + self::assertEquals(["1", "2"], MD_STD_IN::sanitize_text_array(["1", 2])); + + self::expectException(MDpageParameterNotNumericException::class); + MD_STD_IN::sanitize_id_array([[]]); + + } + + /** + * Function for testing sanitize_id_array(). * * @return void */ @@ -294,9 +389,6 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing get_http_input_text(). * - * @small - * @covers \MD_STD_IN::get_http_input_text - * * @return void */ public function test_get_http_input_text():void { @@ -323,9 +415,6 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing get_http_post_text(). * - * @small - * @covers \MD_STD_IN::get_http_post_text - * * @return void */ public function test_get_http_post_text():void { @@ -348,9 +437,6 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_url(). * - * @small - * @covers \MD_STD_IN::sanitize_url - * * @return void */ public function test_sanitize_url_with_empty_string():void { @@ -363,15 +449,12 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_url(). * - * @small - * @covers \MD_STD_IN::sanitize_url - * @dataProvider \MD_STD_TEST_PROVIDERS::valid_url_provider - * * @param string $to_validate Input to validate. * @param string $expected Expected output. * * @return void */ + #[DataProviderExternal(\MD_STD_TEST_PROVIDERS::class, 'valid_url_provider')] public function test_sanitize_url_works(string $to_validate, string $expected):void { self::assertEquals($expected, MD_STD_IN::sanitize_url($to_validate)); @@ -381,15 +464,11 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_url(). * - * @small - * @covers \MD_STD_IN::sanitize_url - * @dataProvider \MD_STD_TEST_PROVIDERS::invalid_url_provider - * * @param string $to_validate Input to validate. * - * * @return void */ + #[DataProviderExternal(\MD_STD_TEST_PROVIDERS::class, 'invalid_url_provider')] public function test_sanitize_url_fails(string $to_validate):void { self::expectException(MDInvalidUrl::class); @@ -400,9 +479,6 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_email(). * - * @small - * @covers \MD_STD_IN::sanitize_email - * * @return void */ public function test_sanitize_email_empty():void { @@ -414,15 +490,12 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_email(). * - * @small - * @covers \MD_STD_IN::sanitize_email - * @dataProvider \MD_STD_TEST_PROVIDERS::valid_email_provider - * * @param string $to_validate Input to validate. * @param string $expected Expected output. * * @return void */ + #[DataProviderExternal(\MD_STD_TEST_PROVIDERS::class, 'valid_email_provider')] public function test_sanitize_email_works(string $to_validate, string $expected):void { self::assertEquals($expected, MD_STD_IN::sanitize_email($to_validate)); @@ -431,14 +504,11 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_email() fails when it should. * - * @small - * @covers \MD_STD_IN::sanitize_email - * @dataProvider \MD_STD_TEST_PROVIDERS::invalid_email_provider - * * @param string $to_validate Input to validate. * * @return void */ + #[DataProviderExternal(\MD_STD_TEST_PROVIDERS::class, 'invalid_email_provider')] public function test_sanitize_email_fails(string $to_validate):void { self::expectException(MDInvalidEmail::class); @@ -449,9 +519,6 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing validate_password(). * - * @small - * @covers \MD_STD_IN::validate_password - * * @return void */ public function test_validate_password():void { @@ -466,9 +533,6 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing validate_phone_number(). * - * @small - * @covers \MD_STD_IN::validate_phone_number - * * @return void */ public function test_validate_phone_number():void { @@ -488,9 +552,6 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing sanitize_float(). * - * @small - * @covers \MD_STD_IN::sanitize_float - * * @return void */ public function test_sanitize_float():void { @@ -510,69 +571,68 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing validate_longitude(). * - * @small - * @covers \MD_STD_IN::validate_longitude + * @param mixed $to_validate Input to validate. + * @param float $expected Expected output. * * @return void */ - public function test_validate_longitude():void { + #[DataProvider('valid_longitude_provider')] + public function test_validate_longitude_with_valid_entries(mixed $to_validate, float $expected):void { - self::assertEquals(0, MD_STD_IN::validate_longitude("0")); - self::assertEquals(12, MD_STD_IN::validate_longitude("12")); - self::assertEquals(12, MD_STD_IN::validate_longitude(12)); - self::assertEquals(12.12, MD_STD_IN::validate_longitude("12.12")); - self::assertEquals(12.12, MD_STD_IN::validate_longitude("12,12")); - self::assertEquals(12.12, MD_STD_IN::validate_longitude(12.12)); + self::assertEquals($expected, MD_STD_IN::validate_longitude($to_validate)); - self::expectException(MDgenericInvalidInputsException::class); - MD_STD_IN::validate_longitude("test"); - self::expectException(MDgenericInvalidInputsException::class); - MD_STD_IN::validate_longitude(""); - self::expectException(MDCoordinateOutOfRange::class); - MD_STD_IN::validate_longitude("1900"); - self::expectException(MDCoordinateOutOfRange::class); - MD_STD_IN::validate_longitude(1900); - self::expectException(MDCoordinateOutOfRange::class); - MD_STD_IN::validate_longitude(-1900); + } + + /** + * Function for testing validate_longitude(). + * + * @param mixed $to_validate Input to validate. + * @param string $exceptionClass Exception class. + * + * @return void + */ + #[DataProvider('invalid_longitude_provider')] + public function test_validate_longitude_with_invalid_entries(mixed $to_validate, string $exceptionClass):void { + + self::expectException($exceptionClass); + MD_STD_IN::validate_longitude($to_validate); } /** * Function for testing validate_latitude(). * - * @small - * @covers \MD_STD_IN::validate_latitude + * @param mixed $to_validate Input to validate. + * @param float $expected Expected output. * * @return void */ - public function test_validate_latitude():void { + #[DataProvider('valid_latitude_provider')] + public function test_validate_latitude_with_valid_entries(mixed $to_validate, float $expected):void { - self::assertEquals(0, MD_STD_IN::validate_latitude("0")); - self::assertEquals(12, MD_STD_IN::validate_latitude("12")); - self::assertEquals(12, MD_STD_IN::validate_latitude(12)); - self::assertEquals(12.12, MD_STD_IN::validate_latitude("12.12")); - self::assertEquals(12.12, MD_STD_IN::validate_latitude("12,12")); - self::assertEquals(12.12, MD_STD_IN::validate_latitude(12.12)); + self::assertEquals($expected, MD_STD_IN::validate_latitude($to_validate)); - self::expectException(MDgenericInvalidInputsException::class); - MD_STD_IN::validate_latitude("test"); - self::expectException(MDgenericInvalidInputsException::class); - MD_STD_IN::validate_latitude(""); - self::expectException(MDCoordinateOutOfRange::class); - MD_STD_IN::validate_latitude("1900"); - self::expectException(MDCoordinateOutOfRange::class); - MD_STD_IN::validate_latitude(1900); - self::expectException(MDCoordinateOutOfRange::class); - MD_STD_IN::validate_latitude(-1900); + } + + /** + * Function for testing validate_latitude(). + * + * @param mixed $to_validate Input to validate. + * @param string $exceptionClass Exception class. + * + * @return void + */ + #[DataProvider('invalid_latitude_provider')] + public function test_validate_latitude_with_invalid_entries(mixed $to_validate, string $exceptionClass):void { + + self::expectException($exceptionClass); + MD_STD_IN::validate_latitude($to_validate); } /** * Function for testing validate_isbn(). * - * @small - * @covers \MD_STD_IN::validate_isbn - * * @return void */ public function test_validate_isbn():void { @@ -596,9 +656,6 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing validate_zip_code(). * - * @small - * @covers \MD_STD_IN::validate_zip_code - * * @return void */ public function test_validate_zip_code():void { @@ -614,9 +671,6 @@ final class MD_STD_IN_Test extends TestCase { /** * Function for testing ensureStringIsUtf8(). * - * @small - * @covers \MD_STD_IN::ensureStringIsUtf8 - * * @return void */ public function test_ensureStringIsUtf8():void { diff --git a/tests/MD_STD_Test.php b/tests/MD_STD_Test.php index e8977f6..c931ccd 100644 --- a/tests/MD_STD_Test.php +++ b/tests/MD_STD_Test.php @@ -7,11 +7,77 @@ declare(strict_types = 1); use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DataProviderExternal; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\Attributes\CoversClass; /** * Tests for MD_STD. */ +#[small] +#[CoversClass(\MD_STD::class)] final class MD_STD_Test extends TestCase { + /** + * Data provider for returning a tmp file. + * + * @return array + */ + public static function file_name_in_tmp_provider():array { + + $temp_file = \tempnam(\sys_get_temp_dir(), 'test_md_std'); + if (!$temp_file) throw new Exception("Failed to get tmp dir"); + + return [ + $temp_file => [$temp_file], + ]; + + } + + /** + * Data provider for invalid directories. + * + * @return array + */ + public static function invalid_directory_provider():array { + + $non_existent = __DIR__ . '/' . uniqid(); + + return [ + "File, not directory" => [__FILE__], + "Non-existent directory" => [$non_existent], + ]; + + } + + /** + * Data provider for invalid files for file_get_contents. + * + * @return array + */ + public static function invalid_files_for_file_get_contents():array { + + return [ + 'External path: FTP' => ["ftp://test", MDFileDoesNotExist::class], // Invalid external + 'Non-existent file' => ["jklsdjfklasdjfklasdkl.jpg", MDFileDoesNotExist::class], // Non-existent file + ]; + + } + + /** + * Data provider for invalid files for file_get_contents. + * + * @return array, 2: string}> + */ + public static function failure_cases_ensure_file():array { + + return [ + 'Non-existent file' => ["jklsdjfklasdjfklasdkl.jpg", [], MDFileDoesNotExist::class], // Non-existent file + 'Invalid mime type' => [__FILE__, ["image/jpeg"], MDWrongFileType::class], // Non-existent file + ]; + + } + /** * Returns sample dates and their equivalent integer values according * to MD_STD::date_to_int(). @@ -39,15 +105,456 @@ final class MD_STD_Test extends TestCase { } /** - * Checks if dates can be translated to int and back. + * Checks if a file can be read. * - * @dataProvider \MD_STD_Test::date_to_int_provider + * @return void + */ + public function test_file_get_contents_works():void { + + self::assertNotEmpty(MD_STD::file_get_contents(__DIR__ . '/../phpunit.xml')); + + } + + /** + * Checks if a file can be written. + * + * @param string $temp_file Tmp file. + * + * @return void + */ + #[DataProvider('file_name_in_tmp_provider')] + public function test_file_put_contents_works(string $temp_file):void { + + MD_STD::file_put_contents($temp_file, "Test"); + self::assertEquals("Test", MD_STD::file_get_contents($temp_file)); + + MD_STD::unlink($temp_file); + + } + + /** + * Check MD_STD::realpath returns absolute file path of an existing file. + * + * @return void + */ + public function test_realpath_works():void { + + self::assertEquals(__FILE__, MD_STD::realpath(__FILE__)); + + } + + /** + * Check MD_STD::realpath throws exception on non-existing path. + * + * @param string $to_validate Input to validate. + * @param string $exceptionClass Exception class. + * + * @return void + */ + #[DataProvider('invalid_files_for_file_get_contents')] + public function test_realpath_fails(string $to_validate, string $exceptionClass):void { + + self::expectException($exceptionClass); + MD_STD::realpath($to_validate); + + } + + /** + * Checks if a file can be read. + * + * @param string $to_validate Input to validate. + * @param string $exceptionClass Exception class. + * + * @return void + */ + #[DataProvider('invalid_files_for_file_get_contents')] + public function test_file_get_contents_fails_as_expected(string $to_validate, string $exceptionClass):void { + + self::expectException($exceptionClass); + MD_STD::file_get_contents($to_validate); + + } + + /** + * Checks that json_encode works. + * + * @return void + */ + public function test_json_encode_works():void { + + self::assertEquals('[0]', MD_STD::json_encode([0])); + + } + + /** + * Checks that json_encode_object works. + * + * @return void + */ + public function test_json_encode_object_works():void { + + self::assertEquals('{"0":0}', MD_STD::json_encode_object((object)[0])); + + } + + /** + * Checks that mkdir works. + * + * @return void + */ + public function test_mkdir_works():void { + + $testDir = sys_get_temp_dir() . '/' . uniqid("_test_"); + if (file_exists($testDir)) { + throw new Exception("Test dir already exists"); + } + + MD_STD::mkdir($testDir); + self::assertTrue(is_dir($testDir)); + + } + + /** + * Checks that nothing happens with mkdir. + * + * @return void + */ + public function test_mkdir_works_with_invalid():void { + + MD_STD::mkdir(__DIR__); + self::assertTrue(is_dir(__DIR__)); + + } + + /** + * Checks if nothing happens if a file does not exist. + * + * @param string $temp_file Tmp file. + * + * @return void + */ + #[DataProvider('file_name_in_tmp_provider')] + public function test_unlink_if_exists_passes_if_not_exists(string $temp_file):void { + + if (file_exists($temp_file)) { + MD_STD::unlink($temp_file); + } + + MD_STD::unlink_if_exists($temp_file); + self::assertFalse(is_file($temp_file)); + + } + + /** + * Checks unlink_if_exists works. + * + * @param string $temp_file Tmp file. + * + * @return void + */ + #[DataProvider('file_name_in_tmp_provider')] + public function test_unlink_if_exists_works(string $temp_file):void { + + touch($temp_file); + MD_STD::unlink_if_exists($temp_file); + self::assertFalse(is_file($temp_file)); + + } + + /** + * Checks scandir works. + * + * @return void + */ + public function test_scandir():void { + + $files = scandir(__DIR__); + $filesChecked = MD_STD::scandir(__DIR__); + + foreach ($filesChecked as $file) { + self::assertTrue(in_array($file, $files, true)); + } + + foreach (['.', '..', '.git'] as $excluded) { + self::assertFalse(in_array($excluded, $filesChecked, true)); + } + + } + + /** + * Checks invalid directory. + * + * @param string $dir Dir name. + * + * @return void + */ + #[DataProvider('invalid_directory_provider')] + public function test_invalid_directory_throws_error_on_scandir(string $dir):void { + + self::expectException(MDFileDoesNotExist::class); + MD_STD::scandir($dir); + + } + + /** + * Checks that startsWithAny works. + * + * @return void + */ + public function test_startsWithAny():void { + + self::assertTrue(MD_STD::startsWithAny("abc", ["a"])); + self::assertFalse(MD_STD::startsWithAny("abc", ["b"])); + + } + + /** + * Checks that stri_contains works. + * + * @return void + */ + public function test_stri_contains():void { + + self::assertTrue(MD_STD::stri_contains("abc", "a")); + self::assertTrue(MD_STD::stri_contains("abc", "b")); + self::assertTrue(MD_STD::stri_contains("abc", "C")); + + self::assertFalse(MD_STD::stri_contains("abc", "u")); + + } + + /** + * Checks that stri_contains_any works. + * + * @return void + */ + public function test_stri_contains_any():void { + + self::assertTrue(MD_STD::stri_contains_any("abc", ["a"])); + self::assertTrue(MD_STD::stri_contains_any("abc", ["b"])); + self::assertTrue(MD_STD::stri_contains_any("abc", ["C"])); + self::assertTrue(MD_STD::stri_contains_any("abc", ["C", "u"])); + + self::assertFalse(MD_STD::stri_contains_any("abc", ["u"])); + self::assertFalse(MD_STD::stri_contains_any("abc", ["u", "z"])); + + } + + /** + * Checks that strtotime works. + * + * @return void + */ + public function test_strtotime_works():void { + + self::assertEquals(strtotime("2024-01-01"), MD_STD::strtotime("2024-01-01")); + + } + + /** + * Checks that strtotime works. + * + * @return void + */ + public function test_strtotime_fails_as_expected():void { + + self::expectException(MDInvalidInputDate::class); + MD_STD::strtotime("test"); + + } + + /** + * Checks that strtotime_for_public_apis works. + * + * @return void + */ + public function test_strtotime_for_pulic_apis_works():void { + + self::assertEquals(strtotime("2024-01-01"), MD_STD::strtotime_for_public_apis("2024-01-01")); + + } + + /** + * Checks that strtotime_for_public_apis works. + * + * @return void + */ + public function test_strtotime_for_pulic_apis_fails_as_expected():void { + + self::expectException(MDParseException::class); + MD_STD::strtotime_for_public_apis("test"); + + } + + /** + * Checks that curl_init works. + * + * @return void + */ + public function test_curl_init():void { + + self::expectNotToPerformAssertions(); + MD_STD::curl_init("https://example.com", 2000); + + } + + /** + * Checks that runCurl works. + * + * @return void + */ + public function test_runCurl():void { + + self::assertNotEmpty(MD_STD::runCurl("https://example.com", headers: ['X-TEST: Test'])); + + } + + /** + * Checks that runCurl works. + * + * @return void + */ + public function test_runCurlMulti():void { + + $output = MD_STD::runCurlMulti(["https://example.com" => "https://example.com"], headers: ['X-TEST: Test']); + self::assertEquals(["https://example.com"], array_keys($output)); + + } + + /** + * Checks that checkUrlIsReachable works. + * + * @return void + */ + public function test_checkUrlIsReachable():void { + + self::assertTrue(MD_STD::checkUrlIsReachable("https://example.com")); + self::assertFalse(MD_STD::checkUrlIsReachable("https://example.com/404")); + + } + + /** + * Checks that checkUrlIsReachable fails as expected. + * + * @param string $invalid_url Input to validate. + * + * @return void + */ + #[DataProviderExternal(\MD_STD_TEST_PROVIDERS::class, 'invalid_url_provider')] + public function test_checkUrlIsReachable_fails_as_expected(string $invalid_url):void { + + self::expectException(MDInvalidUrl::class); + self::assertTrue(MD_STD::checkUrlIsReachable($invalid_url)); + + } + + /** + * Checks that checkUrlIsReachable fails as expected. + * + * @return void + */ + public function test_checkUrlIsReachable_fails_as_expected_on_empty_input():void { + + self::expectException(MDInvalidUrl::class); + self::assertTrue(MD_STD::checkUrlIsReachable("")); + + } + + /** + * Checks that filesize works. + * + * @return void + */ + public function test_filesize():void { + + self::assertNotEmpty(MD_STD::filesize(__FILE__)); + + } + + /** + * Checks that human_filesize works. + * + * @return void + */ + public function test_human_filesize_works():void { + + self::assertEquals("2.00B", MD_STD::human_filesize(2)); + self::assertEquals("2.00kB", MD_STD::human_filesize(2048)); + + } + + /** + * Checks that minimizeHTMLString works. + * + * @return void + */ + public function test_minimizeHTMLString():void { + + self::assertEquals("hi" . PHP_EOL . "hi" . PHP_EOL, MD_STD::minimizeHTMLString(" hi" . PHP_EOL . " hi")); + + } + + /** + * Checks that createTextSnippet works. + * + * @return void + */ + public function test_createTextSnippet():void { + + self::assertEquals("Hallo...", MD_STD::createTextSnippet("Hallo Hallo, jkfljksdlajkfas", 8)); + + } + + /** + * Checks that ensure_file works. + * + * @return void + */ + public function test_ensure_file_works():void { + + self::expectNotToPerformAssertions(); + MD_STD::ensure_file(__FILE__); + + } + + /** + * Checks that minimizeHTMLString works. + * + * @param string $filepath File path. + * @param array $mime_types Mime types expected. + * @param string $exception Exception class name. + * + * @return void + */ + #[DataProvider('failure_cases_ensure_file')] + public function test_ensure_file_fails_as_expected(string $filepath, array $mime_types, string $exception):void { + + self::expectException($exception); + MD_STD::ensure_file($filepath, $mime_types); + + } + + /** + * Checks that levenshtein works. + * + * @return void + */ + public function test_levensthein_works():void { + + self::expectNotToPerformAssertions(); + MD_STD::levenshtein(str_repeat("hi", 500), str_repeat("ho", 500)); + + } + + /** + * Checks if dates can be translated to int and back. * * @param string $date Date to translate. * @param integer $expectedInt Expected integer value for it. * * @return void */ + #[DataProvider('date_to_int_provider')] public function test_date_to_int(string $date, int $expectedInt):void { $toInt = MD_STD::date_to_int($date); @@ -57,4 +564,143 @@ final class MD_STD_Test extends TestCase { self::assertEquals($date, $toStr); } + + /** + * Checks check_is_writable does not work with non-existent or non-directory paths. + * + * @param string $dir Dir name. + * + * @return void + */ + #[DataProvider('invalid_directory_provider')] + public function test_invalid_directory_throws_error_on_check_is_writable(string $dir):void { + + self::expectException(MDFileDoesNotExist::class); + MD_STD::check_is_writable($dir); + + } + + /** + * Checks check_is_writable does not work with non-existent or non-directory paths. + * + * @return void + */ + public function test_is_writable_returns_false_without_permissions():void { + + self::expectException(MDFileIsNotWritable::class); + MD_STD::check_is_writable("/etc"); + + } + + /** + * Checks check_is_writable does not work with non-existent or non-directory paths. + * + * @return void + */ + public function test_remote_mime_content_type_works():void { + + self::assertEquals("text/html", MD_STD::remote_mime_content_type("https://example.com")); + + } + + /** + * Checks string_to_color_code works. + * + * @return void + */ + public function test_string_to_color_code():void { + + self::assertEquals(6, strlen(MD_STD::string_to_color_code(""))); + self::assertEquals(6, strlen(MD_STD::string_to_color_code("a"))); + self::assertEquals(6, strlen(MD_STD::string_to_color_code("dsaf"))); + + } + + /** + * Checks split_int_array_into_sized_parts works. + * + * @return void + */ + public function test_split_int_array_into_sized_parts():void { + + self::assertEquals([[0 => 1], [0 => 1], [0 => 1], [0 => 1]], MD_STD::split_int_array_into_sized_parts([1, 1, 1, 1], 1)); + + } + + /** + * Checks split_string_array_into_sized_parts works. + * + * @return void + */ + public function test_split_string_array_into_sized_parts():void { + + self::assertEquals([[0 => "1"], [0 => "1"], [0 => "1"], [0 => "1"]], MD_STD::split_string_array_into_sized_parts(["1", "1", "1", "1"], 1)); + + } + + /** + * Checks openssl_random_pseudo_bytes works. + * + * @return void + */ + public function test_openssl_random_pseudo_bytes():void { + + self::assertNotEmpty(MD_STD::openssl_random_pseudo_bytes(12)); + + } + + /** + * Checks lang_getfrombrowser works by getting value from HTTP header HTTP_ACCEPT_LANGUAGE. + * + * @return void + */ + public function test_lang_getfrombrowser_via_http_header():void { + + $_SERVER = [ + 'HTTP_ACCEPT_LANGUAGE' => 'de', + ]; + self::assertEquals("de", MD_STD::lang_getfrombrowser(["de", "en"], "en", "")); + + $_SERVER = [ + 'HTTP_ACCEPT_LANGUAGE' => 'es_ES', + ]; + self::assertEquals("en", MD_STD::lang_getfrombrowser(["de", "en"], "en", "")); + + } + + /** + * Checks lang_getfrombrowser returns default without any further information being provided. + * + * @return void + */ + public function test_lang_getfrombrowser_get_default():void { + + self::assertEquals("en", MD_STD::lang_getfrombrowser(["de", "en"], "en", "")); + + } + + /** + * Checks get_user_lang_no_cookie works with GET variable set. + * + * @return void + */ + public function test_get_user_lang_no_cookie_works_from_get_var():void { + + $_GET = [ + 'navlang' => 'de', + ]; + self::assertEquals("de", MD_STD::get_user_lang_no_cookie(["de", "en"], "en")); + + } + + /** + * Checks get_user_lang_no_cookie works with GET variable set. + * + * @return void + */ + public function test_get_user_lang_no_cookie_works_without_get():void { + + self::assertEquals("en", MD_STD::get_user_lang_no_cookie(["de", "en"], "en")); + + } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 3d4b66a..0915775 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -12,7 +12,7 @@ declare(strict_types = 1); // Try using class map as defined through /scripts/buildClassMap.php - foreach (array_merge([__DIR__ . '/../tests', __DIR__ . '/../src', __DIR__ . '/../src/testing', __DIR__ . '/../../MDErrorReporter', __DIR__ . '/../../MDErrorReporter/exceptions', __DIR__ . '/../../MDErrorReporter/exceptions/generic', __DIR__ . '/../../MDErrorReporter/exceptions/updates', __DIR__ . '/../../MDErrorReporter/exceptions/page']) as $classDir) { + foreach (array_merge([__DIR__ . '/../tests', __DIR__ . '/../src', __DIR__ . '/../exceptions', __DIR__ . '/../src/testing', __DIR__ . '/../../MDErrorReporter', __DIR__ . '/../../MDErrorReporter/exceptions', __DIR__ . '/../../MDErrorReporter/exceptions/generic', __DIR__ . '/../../MDErrorReporter/exceptions/updates', __DIR__ . '/../../MDErrorReporter/exceptions/page']) as $classDir) { if (\file_exists("$classDir/$className.php")) { include "$classDir/$className.php";