diff --git a/src/Combined/DateTime.php b/src/Combined/DateTime.php
index a807a39..50532a5 100644
--- a/src/Combined/DateTime.php
+++ b/src/Combined/DateTime.php
@@ -108,7 +108,12 @@ class DateTime
if (preg_match("/(h|m|s|ms)/", (string)$timestamp)) {
return (string)$timestamp;
}
- list($timestamp, $ms) = array_pad(explode('.', (string)round((float)$timestamp, 4)), 2, null);
+ // split to 6 (nano seconds)
+ list($timestamp, $ms) = array_pad(explode('.', (string)round((float)$timestamp, 6)), 2, null);
+ // if micro seconds is on and we have none, set to 0
+ if ($show_micro && $ms === null) {
+ $ms = 0;
+ }
// if negative remember
$negative = false;
if ((int)$timestamp < 0) {
@@ -120,6 +125,10 @@ class DateTime
$time_string = '';
// if timestamp is zero, return zero string
if ($timestamp == 0) {
+ // if no seconds and we have no microseconds either, show no micro seconds
+ if ($ms == 0) {
+ $ms = null;
+ }
$time_string = '0s';
} else {
for ($i = 0, $iMax = count($timegroups); $i < $iMax; $i++) {
@@ -133,11 +142,8 @@ class DateTime
}
// only add ms if we have an ms value
if ($ms !== null) {
- // if we have ms and it has leading zeros, remove them, but only if it is nut just 0
- $ms = preg_replace("/^0+(\d+)$/", '${1}', $ms);
- if (!is_string($ms) || empty($ms)) {
- $ms = '0';
- }
+ // prefix the milliseoncds with 0. and round it max 3 digits and then convert to int
+ $ms = round((float)('0.' . $ms), 3) * 1000;
// add ms if there
if ($show_micro) {
$time_string .= ' ' . $ms . 'ms';
@@ -151,6 +157,240 @@ class DateTime
return (string)$time_string;
}
+ /**
+ * update timeStringFormat with year and month support
+ *
+ * The following flags have to be set to be timeStringFormat compatible.
+ * Not that on seconds overflow this method will throw an exception, timeStringFormat returned -1s
+ * show_only_days: true,
+ * skip_zero: false,
+ * skip_last_zero: false,
+ * truncate_nanoseconds: true,
+ * truncate_zero_seconds_if_microseconds: false
+ *
+ * @param int|float $seconds Seconds to convert, maxium 6 decimals,
+ * else \UnexpectedValueException will be thrown
+ * if days too large or years too large \LengthException is thrown
+ * @param string $truncate_after [=''] Truncate after which time name, will not round, hard end
+ * values are parts names or interval short names (y, d, f, ...)
+ * if illegal value \UnexpectedValueException is thrown
+ * @param bool $natural_seperator [=false] use ',' and 'and', if off use space
+ * @param bool $name_space_seperator [=false] add a space between the number and the time name
+ * @param bool $show_microseconds [=true] show microseconds
+ * @param bool $short_time_name [=true] use the short time names (eg s instead of seconds)
+ * @param bool $skip_last_zero [=true] skip all trailing zero values, eg 5m 0s => 5m
+ * @param bool $skip_zero [=true] do not show zero values anywhere, eg 1h 0m 20s => 1h 20s
+ * @param bool $show_only_days [=false] do not show years or months, show only days
+ * if truncate after is set to year or month
+ * throws \UnexpectedValueException
+ * @param bool $auto_fix_microseconds [=false] if the micro seconds decimals are more than 6, round them
+ * on defaul throw \UnexpectedValueException
+ * @param bool $truncate_nanoseconds [=false] if microseconds decimals >3 then normal we show 123.4ms
+ * cut the .4 is set to true
+ * @param bool $truncate_zero_seconds_if_microseconds [=true] if we have 0.123 seconds then if true no seconds
+ * will be shown
+ * @return string
+ * @throws \UnexpectedValueException if seconds has more than 6 decimals
+ * if truncate has an illegal value
+ * if truncate is set to year or month and show_only_days is turned on
+ * @throws \LengthException if seconds is too large and show_days_only is selected and days is negetive
+ * or if years is negativ
+ */
+ public static function intervalStringFormat(
+ int|float $seconds,
+ string $truncate_after = '',
+ bool $natural_seperator = false,
+ bool $name_space_seperator = false,
+ bool $show_microseconds = true,
+ bool $short_time_name = true,
+ bool $skip_last_zero = true,
+ bool $skip_zero = true,
+ bool $show_only_days = false,
+ bool $auto_fix_microseconds = false,
+ bool $truncate_nanoseconds = false,
+ bool $truncate_zero_seconds_if_microseconds = true,
+ ): string {
+ // auto fix long seconds, else \UnexpectedValueException will be thrown on error
+ // check if we have float and -> round to 6
+ if ($auto_fix_microseconds === true && is_float($seconds)) {
+ $seconds = round($seconds, 6);
+ }
+ // flag negative + set abs
+ $negative = $seconds < 0 ? '-' : '';
+ $seconds = abs($seconds);
+ // create base time
+ $date_now = new \DateTime("@0");
+ try {
+ $date_seconds = new \DateTime("@$seconds");
+ } catch (\Exception $e) {
+ throw new \UnexpectedValueException(
+ 'Seconds value is invalid, too large or more than six decimals: ' . $seconds,
+ 1,
+ $e
+ );
+ }
+ $interval = date_diff($date_now, $date_seconds);
+ // if show_only_days and negative but input postive alert that this has to be done in y/m/d ...
+ if ($interval->y < 0) {
+ throw new \LengthException('Input seconds value is too large for years output: ' . $seconds, 2);
+ } elseif ($interval->days < 0 && $show_only_days === true) {
+ throw new \LengthException('Input seconds value is too large for days output: ' . $seconds, 3);
+ }
+ $parts = [
+ 'years' => 'y',
+ 'months' => 'm',
+ 'days' => 'd',
+ 'hours' => 'h',
+ 'minutes' => 'i',
+ 'seconds' => 's',
+ 'microseconds' => 'f',
+ ];
+ $short_name = [
+ 'years' => 'y', 'months' => 'm', 'days' => 'd',
+ 'hours' => 'h', 'minutes' => 'm', 'seconds' => 's',
+ 'microseconds' => 'ms'
+ ];
+ // $skip = false;
+ if (!empty($truncate_after)) {
+ // if truncate after not in key or value in parts
+ if (!in_array($truncate_after, array_keys($parts)) && !in_array($truncate_after, array_values($parts))) {
+ throw new \UnexpectedValueException(
+ 'truncate_after has an invalid value: ' . $truncate_after,
+ 4
+ );
+ }
+ // if truncate after is y or m and we have show_only_days, throw exception
+ if ($show_only_days === true && in_array($truncate_after, ['y', 'years', 'm', 'months'])) {
+ throw new \UnexpectedValueException(
+ 'If show_only_days is turned on, the truncate_after cannot be years or months: '
+ . $truncate_after,
+ 5
+ );
+ }
+ // $skip = true;
+ }
+ $formatted = [];
+ $zero_formatted = [];
+ $value_set = false;
+ $add_zero_seconds = false;
+ foreach ($parts as $time_name => $part) {
+ if (
+ // skip for micro seconds
+ ($show_microseconds === false && $part == 'f') ||
+ // skip for if days only and we have year or month
+ ($show_only_days === true && in_array($part, ['y', 'm']))
+ ) {
+ continue;
+ }
+ $add_value = 0;
+ if ($show_only_days === true && $part == 'd') {
+ $value = $interval->days;
+ } else {
+ $value = $interval->$part;
+ }
+ // print "-> V: $value | $part, $time_name"
+ // . " | Set: " . ($value_set ? 'Y' : 'N') . ", SkipZ: " . ($skip_zero ? 'Y' : 'N')
+ // . " | SkipLZ: " . ($skip_last_zero ? 'Y' : 'N')
+ // . " | " . ($value != 0 ? 'Not zero' : 'ZERO') . "
";
+ if ($value != 0) {
+ if ($part == 'f') {
+ if ($truncate_nanoseconds === true) {
+ $value = round($value, 3);
+ }
+ $value *= 1000;
+ // anything above that is nano seconds?
+ }
+ if ($value) {
+ $value_set = true;
+ }
+ $add_value = 1;
+ } elseif (
+ $value == 0 &&
+ $value_set === true && (
+ $skip_last_zero === false ||
+ $skip_zero === false
+ )
+ ) {
+ $add_value = 2;
+ }
+ // echo "ADD VALUE: $add_value
";
+ if ($add_value) {
+ // build format
+ $format = "$value";
+ if ($name_space_seperator) {
+ $format .= " ";
+ }
+ if ($short_time_name) {
+ $format .= $short_name[$time_name];
+ } elseif ($value == 1) {
+ $format .= substr($time_name, 0, -1);
+ } else {
+ $format .= $time_name;
+ }
+ if ($add_value == 1) {
+ if (count($zero_formatted) && $skip_zero === false) {
+ $formatted = array_merge($formatted, $zero_formatted);
+ }
+ $zero_formatted = [];
+ $formatted[] = $format;
+ } elseif ($add_value == 2) {
+ $zero_formatted[] = $format;
+ }
+ }
+ // if seconds is zero
+ if (
+ $part == 's' && $value == 0 &&
+ $show_microseconds === true &&
+ $truncate_zero_seconds_if_microseconds === false
+ ) {
+ $add_zero_seconds = true;
+ }
+ // stop after a truncate is matching
+ if ($part == $truncate_after || $truncate_after == $time_name) {
+ break;
+ }
+ }
+ // add all zero entries if we have skip last off
+ if (count($zero_formatted) && $skip_last_zero === false) {
+ $formatted = array_merge($formatted, $zero_formatted);
+ }
+ // print "=> F: " . print_r($formatted, true)
+ // . " | Z: " . print_r($zero_list, true)
+ // . " | ZL: " . print_r($zero_last_list, true)
+ // . "
";
+ if (count($formatted) == 0) {
+ // if we have truncate on, then we assume nothing was found
+ if (!empty($truncate_after)) {
+ if (in_array($truncate_after, array_values($parts))) {
+ $truncate_after = array_flip($parts)[$truncate_after];
+ }
+ $time_name = $truncate_after;
+ } else {
+ $time_name = 'seconds';
+ }
+ return '0' . ($name_space_seperator ? ' ' : '')
+ . ($short_time_name ? $short_name[$time_name] : $time_name);
+ } elseif (count($formatted) == 1) {
+ return $negative .
+ ($add_zero_seconds ?
+ '0'
+ . ($name_space_seperator ? ' ' : '')
+ . ($short_time_name ? $short_name['seconds'] : 'seconds')
+ . ' '
+ : ''
+ )
+ . $formatted[0];
+ } elseif ($natural_seperator === false) {
+ return $negative . implode(' ', $formatted);
+ } else {
+ $str = implode(', ', array_slice($formatted, 0, -1));
+ if (!empty($formatted[count($formatted) - 1])) {
+ $str .= ' and ' . $formatted[count($formatted) - 1];
+ }
+ return $negative . $str;
+ }
+ }
+
/**
* does a reverse of the timeStringFormat and converts the string from
* xd xh xm xs xms to a timestamp.microtime format
diff --git a/src/Convert/Byte.php b/src/Convert/Byte.php
index ebec727..b0d05ec 100644
--- a/src/Convert/Byte.php
+++ b/src/Convert/Byte.php
@@ -37,7 +37,7 @@ class Byte
* BYTE_FORMAT_ADJUST: sprintf adjusted two 2 decimals
* BYTE_FORMAT_SI: use 1000 instead of 1024
* @return string converted byte number (float) with suffix
- * @throws \Exception 1: no valid flag set
+ * @throws \InvalidArgumentException 1: no valid flag set
*/
public static function humanReadableByteFormat(string|int|float $bytes, int $flags = 0): string
{
@@ -63,7 +63,7 @@ class Byte
$si = false;
}
if ($flags > 7) {
- throw new \Exception("Invalid flags parameter: $flags", 1);
+ throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
}
// si or normal
@@ -119,7 +119,7 @@ class Byte
* @param int $flags bitwise flag with use space turned on
* BYTE_FORMAT_SI: use 1000 instead of 1024
* @return string|int|float converted value or original value
- * @throws \Exception 1: no valid flag set
+ * @throws \InvalidArgumentException 1: no valid flag set
*/
public static function stringByteFormat(string|int|float $number, int $flags = 0): string|int|float
{
@@ -130,7 +130,7 @@ class Byte
$si = false;
}
if ($flags != 0 && $flags != 4) {
- throw new \Exception("Invalid flags parameter: $flags", 1);
+ throw new \InvalidArgumentException("Invalid flags parameter: $flags", 1);
}
// matches in regex
$matches = [];
diff --git a/src/Debug/Support.php b/src/Debug/Support.php
index 456be11..affec0f 100644
--- a/src/Debug/Support.php
+++ b/src/Debug/Support.php
@@ -295,8 +295,7 @@ class Support
* Will start with start_level to skip unwanted from stack
* Defaults to skip level 0 wich is this methid
*
- * @param integer $start_level From what level on, as defaul starts with 1
- * to exclude self
+ * @param integer $start_level [=1] From what level on, starts with 1 to exclude self
* @return array All method names in list where max is last called
*/
public static function getCallerMethodList(int $start_level = 1): array
@@ -304,15 +303,46 @@ class Support
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$methods = [];
foreach ($traces as $level => $data) {
- if ($level >= $start_level) {
- if (!empty($data['function'])) {
- array_unshift($methods, $data['function']);
- }
+ if ($level < $start_level) {
+ continue;
+ }
+ if (!empty($data['function'])) {
+ array_unshift($methods, $data['function']);
}
}
return $methods;
}
+ /**
+ * Get the full call stack from a certain starting level
+ * The return string is
+ * file:line:class->method
+ *
+ * Note that '::' is used for static calls
+ *
+ * @param int $start_level [=1] starts with 1 to exclude itself
+ * @return array string with file, line, class and method
+ */
+ public static function getCallStack(int $start_level = 1): array
+ {
+ $call_stack = [];
+ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
+ foreach ($backtrace as $level => $call_trace) {
+ if ($level < $start_level) {
+ continue;
+ }
+ $call_stack[] =
+ ($call_trace['file'] ?? 'n/f') . ':'
+ . ($call_trace['line'] ?? '-') . ':'
+ . (!empty($call_trace['class']) ?
+ $call_trace['class'] . ($call_trace['type'] ?? '') :
+ ''
+ )
+ . $call_trace['function'];
+ }
+ return $call_stack;
+ }
+
/**
* Get the current class where this function is called
* Is mostly used in debug log statements to get the class where the debug
diff --git a/src/Output/Image.php b/src/Output/Image.php
index 897e649..4114494 100644
--- a/src/Output/Image.php
+++ b/src/Output/Image.php
@@ -78,7 +78,7 @@ class Image
if (!empty($dummy) && file_exists($filename) && is_file($filename)) {
$return_data = $filename;
} else {
- throw new \Exception('Could not set dummy return file: ' . $dummy . ' in ' . $filename);
+ throw new \RuntimeException('Could not set dummy return file: ' . $dummy . ' in ' . $filename);
}
} else {
$return_data = $dummy;
diff --git a/test/phpunit/Combined/CoreLibsCombinedDateTimeTest.php b/test/phpunit/Combined/CoreLibsCombinedDateTimeTest.php
index 7bd68d6..d7efa9b 100644
--- a/test/phpunit/Combined/CoreLibsCombinedDateTimeTest.php
+++ b/test/phpunit/Combined/CoreLibsCombinedDateTimeTest.php
@@ -66,6 +66,34 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
];
}
+ /**
+ * date string convert test
+ *
+ * @covers ::dateStringFormat
+ * @dataProvider timestampProvider
+ * @testdox dateStringFormat $input (microtime $flag) will be $expected [$_dataName]
+ *
+ * @param int|float $input
+ * @param bool $flag
+ * @param string $expected
+ * @return void
+ */
+ public function testDateStringFormat(
+ $input,
+ bool $flag_show_micro,
+ bool $flag_micro_as_float,
+ string $expected
+ ): void {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Combined\DateTime::dateStringFormat(
+ $input,
+ $flag_show_micro,
+ $flag_micro_as_float
+ )
+ );
+ }
+
/**
* interval for both directions
*
@@ -74,6 +102,11 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
public function intervalProvider(): array
{
return [
+ 'on hour' => [
+ 3600,
+ false,
+ '1h 0m 0s'
+ ],
'interval no microtime' => [
1641515890,
false,
@@ -82,7 +115,7 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'interval with microtime' => [
1641515890,
true,
- '18999d 0h 38m 10s',
+ '18999d 0h 38m 10s 0ms',
],
'micro interval no microtime' => [
1641515890.123456,
@@ -92,7 +125,7 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'micro interval with microtime' => [
1641515890.123456,
true,
- '18999d 0h 38m 10s 1235ms',
+ '18999d 0h 38m 10s 124ms',
],
'negative interval no microtime' => [
-1641515890,
@@ -103,27 +136,27 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
'microtime only' => [
0.123456,
true,
- '0s 1235ms',
+ '0s 123ms',
],
'seconds only' => [
30.123456,
true,
- '30s 1235ms',
+ '30s 123ms',
],
'minutes only' => [
90.123456,
true,
- '1m 30s 1235ms',
+ '1m 30s 123ms',
],
'hours only' => [
3690.123456,
true,
- '1h 1m 30s 1235ms',
+ '1h 1m 30s 123ms',
],
'days only' => [
90090.123456,
true,
- '1d 1h 1m 30s 1235ms',
+ '1d 1h 1m 30s 123ms',
],
'already set' => [
'1d 1h 1m 30s 1235ms',
@@ -143,6 +176,306 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
];
}
+ /**
+ * time seconds convert test
+ *
+ * @covers ::timeStringFormat
+ * @dataProvider intervalProvider
+ * @testdox timeStringFormat $input (microtime $flag) will be $expected [$_dataName]
+ *
+ * @param string|int|float $input
+ * @param bool $flag
+ * @param string $expected
+ * @return void
+ */
+ public function testTimeStringFormat(string|int|float $input, bool $flag, string $expected): void
+ {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Combined\DateTime::timeStringFormat($input, $flag)
+ );
+ }
+
+ /**
+ * interval seconds convert
+ *
+ * @covers ::intervalStringFormat
+ * @dataProvider intervalProvider
+ * @testdox intervalStringFormat $input (microtime $show_micro) will be $expected [$_dataName]
+ *
+ * @param string|int|float $input
+ * @param bool $show_micro
+ * @param string $expected
+ * @return void
+ */
+ public function testIntervalStringFormat(string|int|float $input, bool $show_micro, string $expected): void
+ {
+ // we skip string input, that is not allowed
+ if (is_string($input)) {
+ $this->assertTrue(true, 'Skip strings');
+ return;
+ }
+ // invalid values throw exception in default
+ if ($input == 999999999999999) {
+ $this->expectException(\LengthException::class);
+ }
+ // below is equal to timeStringFormat
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Combined\DateTime::intervalStringFormat(
+ $input,
+ show_microseconds: $show_micro,
+ show_only_days: true,
+ skip_zero: false,
+ skip_last_zero: false,
+ truncate_nanoseconds: true,
+ truncate_zero_seconds_if_microseconds: false
+ )
+ );
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function intervalExtendedProvider(): array
+ {
+ return [
+ // A
+ '(60) default value' => [
+ [
+ 'seconds' => 60,
+ ],
+ 'expected' => '1m',
+ 'exception' => null
+ ],
+ '(60) default value, skip_last_zero:false' => [
+ [
+ 'seconds' => 60,
+ 'skip_last_zero' => false,
+ ],
+ 'expected' => '1m 0s 0ms',
+ 'exception' => null
+ ],
+ // B
+ '(120.1) default value' => [
+ [
+ 'seconds' => 120.1,
+ ],
+ 'expected' => '2m 100ms',
+ 'exception' => null
+ ],
+ '(120.1) default value, skip_zero:false' => [
+ [
+ 'seconds' => 120.1,
+ 'skip_zero' => false,
+ ],
+ 'expected' => '2m 0s 100ms',
+ 'exception' => null
+ ],
+ '(120.1) default value, skip_last_zero:false' => [
+ [
+ 'seconds' => 120.1,
+ 'skip_last_zero' => false,
+ ],
+ 'expected' => '2m 100ms',
+ 'exception' => null
+ ],
+ // C
+ '(3601) default value' => [
+ [
+ 'seconds' => 3601,
+ ],
+ 'expected' => '1h 1s',
+ 'exception' => null
+ ],
+ '(3601) default value, skip_zero:false' => [
+ [
+ 'seconds' => 3601,
+ 'skip_zero' => false,
+ ],
+ 'expected' => '1h 0m 1s',
+ 'exception' => null
+ ],
+ '(3601) default value, skip_last_zero:false' => [
+ [
+ 'seconds' => 3601,
+ 'skip_last_zero' => false,
+ ],
+ 'expected' => '1h 1s 0ms',
+ 'exception' => null
+ ],
+ // TODO create unit tests for ALL edge cases
+ // CREATE abort tests, simple, all others are handled in exception tests
+ 'exception: \UnexpectedValueException:1' => [
+ [
+ 'seconds' => 99999999999999999999999
+ ],
+ 'expected' => null,
+ 'exception' => [
+ 'class' => \UnexpectedValueException::class,
+ 'code' => 1,
+ ],
+ ]
+ ];
+ }
+
+ /**
+ * test all options for interval conversion
+ *
+ * @covers ::intervalStringFormat
+ * @dataProvider intervalExtendedProvider
+ * @testdox intervalStringFormat $input will be $expected / $exception [$_dataName]
+ *
+ * @param array $parameter_list
+ * @param string $expected
+ * @param array $exception
+ * @return void
+ */
+ public function testExtendedIntervalStringFormat(
+ array $parameter_list,
+ ?string $expected,
+ ?array $exception
+ ): void {
+ if ($expected === null && $exception === null) {
+ $this->assertFalse(true, 'Cannot have expected and exception null in test data');
+ }
+ $parameters = [];
+ foreach (
+ [
+ 'seconds' => null,
+ 'truncate_after' => '',
+ 'natural_seperator' => false,
+ 'name_space_seperator' => false,
+ 'show_microseconds' => true,
+ 'short_time_name' => true,
+ 'skip_last_zero' => true,
+ 'skip_zero' => true,
+ 'show_only_days' => false,
+ 'auto_fix_microseconds' => false,
+ 'truncate_nanoseconds' => false,
+ 'truncate_zero_seconds_if_microseconds' => true,
+ ] as $param => $default
+ ) {
+ if (empty($parameter_list[$param]) && $default === null) {
+ $this->assertFalse(true, 'Parameter ' . $param . ' is mandatory ');
+ } elseif (!isset($parameter_list[$param]) || $parameter_list[$param] === null) {
+ $parameters[] = $default;
+ } else {
+ $parameters[] = $parameter_list[$param];
+ }
+ }
+ if ($expected !== null) {
+ $this->assertEquals(
+ $expected,
+ call_user_func_array('CoreLibs\Combined\DateTime::intervalStringFormat', $parameters)
+ );
+ } else {
+ if (empty($exception['class']) || empty($exception['code'])) {
+ $this->assertFalse(true, 'Exception tests need Exception name and Code');
+ }
+ $this->expectException($exception['class']);
+ $this->expectExceptionCode($exception['code']);
+ // if we have a message, must be regex
+ if (!empty($exception['message'])) {
+ $this->expectExceptionMessageMatches($exception['message']);
+ }
+ call_user_func_array('CoreLibs\Combined\DateTime::intervalStringFormat', $parameters);
+ }
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function exceptionsIntervalProvider(): array
+ {
+ return [
+ 'UnexpectedValueException: 1 A' => [
+ 'seconds' => 99999999999999999999999,
+ 'params' => [],
+ 'exception' => \UnexpectedValueException::class,
+ 'exception_message' => "/^Seconds value is invalid, too large or more than six decimals: /",
+ 'excpetion_code' => 1,
+ ],
+ 'UnexpectedValueException: 1 B' => [
+ 'seconds' => 123.1234567,
+ 'params' => [],
+ 'exception' => \UnexpectedValueException::class,
+ 'exception_message' => "/^Seconds value is invalid, too large or more than six decimals: /",
+ 'excpetion_code' => 1,
+ ],
+ // exception 2 is very likely covered by exception 1
+ 'LengthException: 3' => [
+ 'seconds' => 999999999999999999,
+ 'params' => [
+ 'show_only_days',
+ ],
+ 'exception' => \LengthException::class,
+ 'exception_message' => "/^Input seconds value is too large for days output: /",
+ 'excpetion_code' => 3,
+ ],
+ 'UnexpectedValueException: 4' => [
+ 'seconds' => 1234567,
+ 'params' => [
+ 'truncate_after'
+ ],
+ 'exception' => \UnexpectedValueException::class,
+ 'exception_message' => "/^truncate_after has an invalid value: /",
+ 'excpetion_code' => 4,
+ ],
+ 'UnexpectedValueException: 5' => [
+ 'seconds' => 1234567,
+ 'params' => [
+ 'show_only_days:truncate_after'
+ ],
+ 'exception' => \UnexpectedValueException::class,
+ 'exception_message' =>
+ "/^If show_only_days is turned on, the truncate_after cannot be years or months: /",
+ 'excpetion_code' => 5,
+ ]
+ ];
+ }
+
+ /**
+ * Test all exceptions
+ *
+ * @covers ::intervalStringFormat
+ * @dataProvider exceptionsIntervalProvider
+ * @testdox intervalStringFormat: test Exceptions
+ *
+ * @param int|float $seconds
+ * @param array $params
+ * @param string $exception
+ * @param string $exception_message
+ * @param int $excpetion_code
+ * @return void
+ */
+ public function testExceptionsIntervalStringFormat(
+ int|float $seconds,
+ array $params,
+ string $exception,
+ string $exception_message,
+ int $excpetion_code,
+ ): void {
+ $this->expectException($exception);
+ $this->expectExceptionMessageMatches($exception_message);
+ $this->expectExceptionCode($excpetion_code);
+ if (empty($params)) {
+ \CoreLibs\Combined\DateTime::intervalStringFormat($seconds);
+ } else {
+ if (in_array('show_only_days', $params)) {
+ \CoreLibs\Combined\DateTime::intervalStringFormat($seconds, show_only_days:true);
+ } elseif (in_array('truncate_after', $params)) {
+ \CoreLibs\Combined\DateTime::intervalStringFormat($seconds, truncate_after: 'v');
+ } elseif (in_array('show_only_days:truncate_after', $params)) {
+ \CoreLibs\Combined\DateTime::intervalStringFormat($seconds, show_only_days:true, truncate_after: 'y');
+ }
+ }
+ }
+
/**
* Undocumented function
*
@@ -203,6 +536,25 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
];
}
+ /**
+ * Undocumented function
+ *
+ * @covers ::stringToTime
+ * @dataProvider reverseIntervalProvider
+ * @testdox stringToTime $input will be $expected [$_dataName]
+ *
+ * @param string|int|float $input
+ * @param string|int|float $expected
+ * @return void
+ */
+ public function testStringToTime($input, $expected): void
+ {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Combined\DateTime::stringToTime($input)
+ );
+ }
+
/**
* Undocumented function
*
@@ -238,6 +590,25 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
];
}
+ /**
+ * Undocumented function
+ *
+ * @covers ::checkDate
+ * @dataProvider dateProvider
+ * @testdox checkDate $input will be $expected [$_dataName]
+ *
+ * @param string $input
+ * @param bool $expected
+ * @return void
+ */
+ public function testCheckDate(string $input, bool $expected): void
+ {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Combined\DateTime::checkDate($input)
+ );
+ }
+
/**
* Undocumented function
*
@@ -297,6 +668,25 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
];
}
+ /**
+ * Undocumented function
+ *
+ * @covers ::checkDateTime
+ * @dataProvider dateTimeProvider
+ * @testdox checkDateTime $input will be $expected [$_dataName]
+ *
+ * @param string $input
+ * @param bool $expected
+ * @return void
+ */
+ public function testCheckDateTime(string $input, bool $expected): void
+ {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Combined\DateTime::checkDateTime($input)
+ );
+ }
+
/**
* Undocumented function
*
@@ -371,6 +761,37 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
];
}
+ /**
+ * Undocumented function
+ *
+ * @covers ::compareDate
+ * @dataProvider dateCompareProvider
+ * @testdox compareDate $input_a compared to $input_b will be $expected [$_dataName]
+ *
+ * @param string $input_a
+ * @param string $input_b
+ * @param int|bool $expected
+ * @param string|null $exception
+ * @param int|null $exception_code
+ * @return void
+ */
+ public function testCompareDate(
+ string $input_a,
+ string $input_b,
+ int|bool $expected,
+ ?string $exception,
+ ?int $exception_code
+ ): void {
+ if ($expected === false) {
+ $this->expectException($exception);
+ $this->expectExceptionCode($exception_code);
+ }
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Combined\DateTime::compareDate($input_a, $input_b)
+ );
+ }
+
/**
* Undocumented function
*
@@ -466,6 +887,37 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
];
}
+ /**
+ * Undocumented function
+ *
+ * @covers ::compareDateTime
+ * @dataProvider dateTimeCompareProvider
+ * @testdox compareDateTime $input_a compared to $input_b will be $expected [$_dataName]
+ *
+ * @param string $input_a
+ * @param string $input_b
+ * @param int|bool $expected
+ * @param string|null $exception
+ * @param int|null $exception_code
+ * @return void
+ */
+ public function testCompareDateTime(
+ string $input_a,
+ string $input_b,
+ int|bool $expected,
+ ?string $exception,
+ ?int $exception_code
+ ): void {
+ if ($expected === false) {
+ $this->expectException($exception);
+ $this->expectExceptionCode($exception_code);
+ }
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Combined\DateTime::compareDateTime($input_a, $input_b)
+ );
+ }
+
/**
* Undocumented function
*
@@ -520,214 +972,6 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
];
}
- /**
- * Undocumented function
- *
- * @return array
- */
- public function dateRangeHasWeekendProvider(): array
- {
- return [
- 'no weekend' => [
- '2023-07-03',
- '2023-07-04',
- false
- ],
- 'start weekend sat' => [
- '2023-07-01',
- '2023-07-04',
- true
- ],
- 'start weekend sun' => [
- '2023-07-02',
- '2023-07-04',
- true
- ],
- 'end weekend sat' => [
- '2023-07-03',
- '2023-07-08',
- true
- ],
- 'end weekend sun' => [
- '2023-07-03',
- '2023-07-09',
- true
- ],
- 'long period > 6 days' => [
- '2023-07-03',
- '2023-07-27',
- true
- ]
- ];
- }
-
- /**
- * date string convert test
- *
- * @covers ::dateStringFormat
- * @dataProvider timestampProvider
- * @testdox dateStringFormat $input (microtime $flag) will be $expected [$_dataName]
- *
- * @param int|float $input
- * @param bool $flag
- * @param string $expected
- * @return void
- */
- public function testDateStringFormat(
- $input,
- bool $flag_show_micro,
- bool $flag_micro_as_float,
- string $expected
- ): void {
- $this->assertEquals(
- $expected,
- \CoreLibs\Combined\DateTime::dateStringFormat(
- $input,
- $flag_show_micro,
- $flag_micro_as_float
- )
- );
- }
-
- /**
- * interval convert test
- *
- * @covers ::timeStringFormat
- * @dataProvider intervalProvider
- * @testdox timeStringFormat $input (microtime $flag) will be $expected [$_dataName]
- *
- * @param int|float $input
- * @param bool $flag
- * @param string $expected
- * @return void
- */
- public function testTimeStringFormat($input, bool $flag, string $expected): void
- {
- $this->assertEquals(
- $expected,
- \CoreLibs\Combined\DateTime::timeStringFormat($input, $flag)
- );
- }
-
- /**
- * Undocumented function
- *
- * @covers ::stringToTime
- * @dataProvider reverseIntervalProvider
- * @testdox stringToTime $input will be $expected [$_dataName]
- *
- * @param string|int|float $input
- * @param string|int|float $expected
- * @return void
- */
- public function testStringToTime($input, $expected): void
- {
- $this->assertEquals(
- $expected,
- \CoreLibs\Combined\DateTime::stringToTime($input)
- );
- }
-
- /**
- * Undocumented function
- *
- * @covers ::checkDate
- * @dataProvider dateProvider
- * @testdox checkDate $input will be $expected [$_dataName]
- *
- * @param string $input
- * @param bool $expected
- * @return void
- */
- public function testCheckDate(string $input, bool $expected): void
- {
- $this->assertEquals(
- $expected,
- \CoreLibs\Combined\DateTime::checkDate($input)
- );
- }
-
- /**
- * Undocumented function
- *
- * @covers ::checkDateTime
- * @dataProvider dateTimeProvider
- * @testdox checkDateTime $input will be $expected [$_dataName]
- *
- * @param string $input
- * @param bool $expected
- * @return void
- */
- public function testCheckDateTime(string $input, bool $expected): void
- {
- $this->assertEquals(
- $expected,
- \CoreLibs\Combined\DateTime::checkDateTime($input)
- );
- }
-
- /**
- * Undocumented function
- *
- * @covers ::compareDate
- * @dataProvider dateCompareProvider
- * @testdox compareDate $input_a compared to $input_b will be $expected [$_dataName]
- *
- * @param string $input_a
- * @param string $input_b
- * @param int|bool $expected
- * @param string|null $exception
- * @param int|null $exception_code
- * @return void
- */
- public function testCompareDate(
- string $input_a,
- string $input_b,
- int|bool $expected,
- ?string $exception,
- ?int $exception_code
- ): void {
- if ($expected === false) {
- $this->expectException($exception);
- $this->expectExceptionCode($exception_code);
- }
- $this->assertEquals(
- $expected,
- \CoreLibs\Combined\DateTime::compareDate($input_a, $input_b)
- );
- }
-
- /**
- * Undocumented function
- *
- * @covers ::compareDateTime
- * @dataProvider dateTimeCompareProvider
- * @testdox compareDateTime $input_a compared to $input_b will be $expected [$_dataName]
- *
- * @param string $input_a
- * @param string $input_b
- * @param int|bool $expected
- * @param string|null $exception
- * @param int|null $exception_code
- * @return void
- */
- public function testCompareDateTime(
- string $input_a,
- string $input_b,
- int|bool $expected,
- ?string $exception,
- ?int $exception_code
- ): void {
- if ($expected === false) {
- $this->expectException($exception);
- $this->expectExceptionCode($exception_code);
- }
- $this->assertEquals(
- $expected,
- \CoreLibs\Combined\DateTime::compareDateTime($input_a, $input_b)
- );
- }
-
/**
* Undocumented function
*
@@ -906,6 +1150,47 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
);
}
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function dateRangeHasWeekendProvider(): array
+ {
+ return [
+ 'no weekend' => [
+ '2023-07-03',
+ '2023-07-04',
+ false
+ ],
+ 'start weekend sat' => [
+ '2023-07-01',
+ '2023-07-04',
+ true
+ ],
+ 'start weekend sun' => [
+ '2023-07-02',
+ '2023-07-04',
+ true
+ ],
+ 'end weekend sat' => [
+ '2023-07-03',
+ '2023-07-08',
+ true
+ ],
+ 'end weekend sun' => [
+ '2023-07-03',
+ '2023-07-09',
+ true
+ ],
+ 'long period > 6 days' => [
+ '2023-07-03',
+ '2023-07-27',
+ true
+ ]
+ ];
+ }
+
/**
* Undocumented function
*
diff --git a/test/phpunit/Convert/CoreLibsConvertByteTest.php b/test/phpunit/Convert/CoreLibsConvertByteTest.php
index bf49af3..9583888 100644
--- a/test/phpunit/Convert/CoreLibsConvertByteTest.php
+++ b/test/phpunit/Convert/CoreLibsConvertByteTest.php
@@ -253,7 +253,8 @@ final class CoreLibsConvertByteTest extends TestCase
*/
public function testHumanReadableByteFormatException(int $flag): void
{
- $this->expectException(\Exception::class);
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionCode(1);
\CoreLibs\Convert\Byte::humanReadableByteFormat(12, $flag);
}
@@ -272,7 +273,8 @@ final class CoreLibsConvertByteTest extends TestCase
*/
public function testStringByteFormatException(int $flag): void
{
- $this->expectException(\Exception::class);
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionCode(1);
\CoreLibs\Convert\Byte::stringByteFormat(12, $flag);
}
}
diff --git a/test/phpunit/Debug/CoreLibsDebugSupportTest.php b/test/phpunit/Debug/CoreLibsDebugSupportTest.php
index ab74df7..74ed67e 100644
--- a/test/phpunit/Debug/CoreLibsDebugSupportTest.php
+++ b/test/phpunit/Debug/CoreLibsDebugSupportTest.php
@@ -513,7 +513,7 @@ final class CoreLibsDebugSupportTest extends TestCase
public function testGetCallerMethodList(array $expected): void
{
$compare = Support::getCallerMethodList();
- // 10: legact
+ // 10: legacy
// 11: direct
// 12: full call
switch (count($compare)) {
@@ -571,6 +571,31 @@ final class CoreLibsDebugSupportTest extends TestCase
}
}
+ /**
+ * Undocumented function
+ *
+ * @cover ::getCallStack
+ * @testdox getCallStack check if it returns data [$_dataName]
+ *
+ * @return void
+ */
+ public function testGetCallStack(): void
+ {
+ $call_stack = Support::getCallStack();
+ // print "Get CALL: " . print_r(Support::getCallStack(), true) . "\n";
+ if ($call_stack < 8) {
+ $this->assertFalse(true, 'getCallStack too low: 8');
+ } else {
+ $this->assertTrue(true, 'getCallSteck ok');
+ }
+ // just test top entry
+ $first = array_shift($call_stack);
+ $this->assertStringEndsWith(
+ ':tests\CoreLibsDebugSupportTest->testGetCallStack',
+ $first,
+ );
+ }
+
/**
* test the lowest one (one above base)
*