* @copyright 2007-2022 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
* @link https://kigkonsult.se
* @license Subject matter of licence is the software iCalcreator.
* The above copyright, link, package and version notices,
* this licence notice and the invariant [rfc5545] PRODID result use
* as implemented and invoked in iCalcreator shall be included in
* all copies or substantial portions of the iCalcreator.
*
* iCalcreator is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* iCalcreator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with iCalcreator. If not, see .
*/
namespace Kigkonsult\Icalcreator;
use DateTime;
use DateTimeInterface;
use Exception;
use IntlTimeZone;
use Kigkonsult\Icalcreator\Util\DateTimeFactory;
use Kigkonsult\Icalcreator\Util\DateTimeZoneFactory;
use Kigkonsult\Icalcreator\Util\StringFactory;
use Kigkonsult\Icalcreator\Util\Util;
use Kigkonsult\Icalcreator\Xml\Formatter as XmlFormatter;
use Kigkonsult\Icalcreator\Xml\Parser as XmlParser;
use PHPUnit\Framework\TestCase;
use SimpleXMLElement;
/**
* class DtBase
*
* @since 2.41.44 2022-04-27
*/
abstract class DtBase extends TestCase
{
use GetPropMethodNamesTrait;
protected static function getErrMsg(
? string $spec = null,
int|string $case,
string $testFcn,
? string $inst = null,
? string $method = null,
mixed $inValue = null,
mixed $inParams = null
)
{
static $ERRFMT = "Error %s in case #%s, %s <%s>->%s";
$output = sprintf( $ERRFMT, ( $spec ?? '' ), $case, $testFcn, $inst, $method );
if( ! empty( $inValue )) {
$output .= PHP_EOL . ' inValue : ' . str_replace( PHP_EOL, '', var_export( $inValue, true ));
}
if( ! empty( $inParams )) {
$output .= PHP_EOL . ' inParams : ' . str_replace( PHP_EOL, '', var_export( $inParams, true ));
}
return $output;
}
/**
* The test method, case suffix '-1xx', test prop create-, delete-, get- is- and set-methods
*
* @param int $case
* @param array $compsProps
* @param mixed $value
* @param mixed $params
* @param Pc $expectedGet
* @param string $expectedString
* @throws Exception
* @since 2.41.47 2022-04-29
*/
public function thePropTest(
int $case,
array $compsProps,
mixed $value,
mixed $params,
Pc $expectedGet,
string $expectedString
) : void
{
// error_log( __METHOD__ . ' IN 1 ' . $case . ' ' . var_export( $value, true )); // test ###
$c = new Vcalendar();
$pcInput = $firstLastmodifiedLoad = false;
foreach( $compsProps as $theComp => $props ) {
$newMethod = 'new' . $theComp;
$comp = match ( true ) {
IcalInterface::PARTICIPANT === $theComp => $c->newVevent()->{$newMethod}()
->setDtstamp( $value, $params ),
IcalInterface::AVAILABLE === $theComp => $c->newVavailability()->{$newMethod}(),
default => $c->{$newMethod}(),
};
foreach( $props as $propName ) {
[ $createMethod, $deleteMethod, $getMethod, $isMethod, $setMethod ] = self::getPropMethodnames( $propName );
if( IcalInterface::LAST_MODIFIED === $propName ) {
if( ! $firstLastmodifiedLoad ) {
$this->assertFalse(
$c->{$isMethod}(),
self::getErrMsg( null, $case . '-110', __FUNCTION__, Vcalendar::VCALENDAR, $isMethod ) .
PHP_EOL . $c->createCalendar() // test ###
);
}
$c->setLastmodified( $value, $params );
$firstLastmodifiedLoad = true;
$this->assertTrue(
$c->{$isMethod}(),
self::getErrMsg( null, $case . '-111', __FUNCTION__, Vcalendar::VCALENDAR, $isMethod )
);
} // end if last-mod...
// error_log( __METHOD__ . ' #' . $case . ' start <' . $theComp . '>->' . $propName . ' value IN : ' . var_export( $value, true )); // test ###
if( $propName !== IcalInterface::DTSTAMP ) {
$this->assertFalse(
$comp->{$isMethod}(),
self::getErrMsg( null, $case . '-112', __FUNCTION__, $theComp, $isMethod )
);
}
if( in_array( $propName, [ IcalInterface::EXDATE, IcalInterface::RDATE ], true )) {
$comp->{$setMethod}( [ $value ], $params );
$getValue = $comp->{$getMethod}( null, true );
if( ! empty( $getValue->value )) {
$getValue->value = reset( $getValue->value );
}
}
else {
if( in_array( $propName, [ IcalInterface::DTEND, IcalInterface::DUE, IcalInterface::RECURRENCE_ID, ], true ) ) {
$comp->setDtstart( $value, $params );
}
$comp->{$setMethod}( $value, $params );
$getValue = $comp->{$getMethod}( true );
} // end else
if( null !== $value ) {
$this->assertTrue(
$comp->{$isMethod}(),
self::getErrMsg( null, $case . '-113', __FUNCTION__, $theComp, $isMethod )
);
}
if( $expectedGet->value instanceof DateTime && $getValue->value instanceof DateTime ) {
if( $expectedGet->hasParamKey( IcalInterface::TZID ) &&
$getValue->hasParamKey( IcalInterface::TZID )) {
// has same offset (TZID might differ)
$this->assertEquals(
DateTimeFactory::factory(
null,
$expectedGet->getParams( IcalInterface::TZID )
)->getOffset(),
DateTimeFactory::factory(
null,
$getValue->getParams( IcalInterface::TZID )
)->getOffset(),
self::getErrMsg( null, $case . '-114-1', __FUNCTION__, $theComp, $getMethod )
);
}
else {
$getValue->removeParam( IcalInterface::ISLOCALTIME );
$this->assertEquals(
var_export( $expectedGet->params, true ),
var_export( $getValue->params, true ),
self::getErrMsg( null, $case . '-114-2', __FUNCTION__, $theComp, $getMethod, $value, $params )
);
}
$fmt = $expectedGet->hasParamValue( IcalInterface::DATE )
? DateTimeFactory::$Ymd
: DateTimeFactory::$YmdHis;
$this->assertEquals(
$expectedGet->value->format( $fmt ),
$getValue->value->format( $fmt ),
self::getErrMsg( null, $case . '-114-3', __FUNCTION__, $theComp, $getMethod, $value, $params )
);
$this->assertEquals( // in case of diff. timezones but with equal offset
$expectedGet->value->getOffset(),
$getValue->value->getOffset(),
self::getErrMsg( null, $case . '-114-4', __FUNCTION__, $theComp, $getMethod, $value, $params )
);
} // end if ..DateTime..
$this->assertEquals(
strtoupper( $propName ) . $expectedString,
trim( $comp->{$createMethod}() ),
self::getErrMsg( null, $case . '-116', __FUNCTION__, $theComp, $createMethod )
);
if( method_exists( $comp, $deleteMethod )) { // Dtstamp/Uid has NO deleteMethod
$comp->{$deleteMethod}();
}
if( IcalInterface::DTSTAMP === $propName ) {
$this->assertTrue(
$comp->{$isMethod}(),
self::getErrMsg( null, $case . '-117', __FUNCTION__, $theComp, $isMethod )
);
$this->assertNotFalse(
$comp->{$getMethod}(),
self::getErrMsg( '(after delete) ', $case . '-118', __FUNCTION__, $theComp, $getMethod )
);
} // end if
else {
$this->assertFalse(
$comp->{$isMethod}(),
self::getErrMsg( null, $case . '-119', __FUNCTION__, $theComp, $isMethod )
);
$this->assertFalse(
$comp->{$getMethod}(),
self::getErrMsg( '(after delete) ', $case . '-120', __FUNCTION__, $theComp, $getMethod )
);
} // end else
if( $pcInput ) {
$comp->{$setMethod}( Pc::factory( $value, $params ));
}
else {
$comp->{$setMethod}( $value, $params );
}
if( null !== $value ) {
$this->assertTrue(
$comp->{$isMethod}(),
self::getErrMsg( null, $case . '-121', __FUNCTION__, $theComp, $isMethod )
);
}
$pcInput = ! $pcInput;
if( IcalInterface::Z === substr( $expectedString, -1 )) {
$this->parseCalendarTest( $case, $c, $expectedString, $theComp, $propName ); // test ###
}
} // end foreach .. => propName
} // end foreach comp => props
$this->parseCalendarTest( $case, $c, $expectedString, $theComp, $propName );
}
/**
* The test method, case suffix '-2xx', test prop get--method without params
*
* @param int $case
* @param array $compsProps
* @param mixed $value
* @param mixed $params
* @param Pc $expectedGet
* @throws Exception
* @since 2.41.44 2022-04-21
*/
public function propGetNoParamsTest(
int $case,
array $compsProps,
mixed $value,
mixed $params,
Pc $expectedGet
) : void
{
$c = new Vcalendar();
foreach( $compsProps as $theComp => $props ) {
$newMethod = 'new' . $theComp;
$comp = match ( true ) {
IcalInterface::PARTICIPANT === $theComp => $c->newVevent()->{$newMethod}()
->setDtstamp( $value, $params ),
IcalInterface::AVAILABLE === $theComp => $c->newVavailability()->{$newMethod}(),
default => $c->{$newMethod}(),
};
foreach( $props as $propName ) {
[ , , $getMethod, $isMethod, $setMethod ] = self::getPropMethodnames( $propName );
if( IcalInterface::DTSTAMP !== $propName ) {
$this->assertFalse(
$comp->{$isMethod}(),
self::getErrMsg( null, $case . '-211', __FUNCTION__, $theComp, $isMethod )
);
}
if( in_array( $propName, [ IcalInterface::EXDATE, IcalInterface::RDATE ], true )) {
$comp->{$setMethod}( [ $value ], $params );
$getValue = $comp->{$getMethod}();
if( ! empty( $getValue )) {
$getValue = reset( $getValue );
}
}
else {
if( in_array( $propName, [ IcalInterface::DTEND, IcalInterface::DUE, IcalInterface::RECURRENCE_ID, ], true ) ) {
$comp->setDtstart( $value, $params );
}
$comp->{$setMethod}( $value, $params );
$getValue = $comp->{$getMethod}();
} // end else
$this->assertSame(
! empty( $value ),
$comp->{$isMethod}(),
self::getErrMsg( null, $case . '-212', __FUNCTION__, $theComp, $isMethod )
. ', exp ' . empty( $value ) ? Vcalendar::FALSE : Vcalendar::TRUE
);
if( $expectedGet->value instanceof DateTime && $getValue instanceof DateTime ) {
$this->assertEquals(
$expectedGet->value->format( DateTimeFactory::$YmdHis ),
$getValue->format( DateTimeFactory::$YmdHis ),
self::getErrMsg( null, $case . '-213', __FUNCTION__, $theComp, $getMethod )
);
} // end if
else {
$this->assertEquals(
$expectedGet->value ?? '',
$getValue,
self::getErrMsg( null, $case . '-214', __FUNCTION__, $theComp, $getMethod )
);
}
} // end foreach props...
} // end foreach $compsProps...
}
/**
* The test method suffix -1b... , single EXDATE + RDATE, case prefix '-1bx' also multi EXDATE + RDATE
*
* @param int|string $case
* @param array $compsProps
* @param mixed $value
* @param mixed $params
* @param pc $expectedGet
* @param string $expectedString
* @throws Exception
*/
public function exdateRdateSpecTest(
int | string $case,
array $compsProps,
mixed $value,
mixed $params,
Pc $expectedGet,
string $expectedString
) : void
{
$c = new Vcalendar();
$pcInput = false;
foreach( $compsProps as $theComp => $props ) {
$newMethod = 'new' . $theComp;
if( IcalInterface::AVAILABLE === $theComp ) {
$comp = $c->newVavailability()->{$newMethod}();
}
else {
$comp = $c->{$newMethod}();
}
foreach( $props as $propName ) {
[ $createMethod, $deleteMethod, $getMethod, $isMethod, $setMethod ] = self::getPropMethodnames( $propName );
$this->assertFalse(
$comp->{$isMethod}(),
self::getErrMsg( null, $case . '-1b2', __FUNCTION__, $theComp, $isMethod )
);
if( $pcInput ) {
$comp->{$setMethod}( Pc::factory( $value, $params ));
}
else {
$comp->{$setMethod}( $value, $params );
}
$pcInput = ! $pcInput;
$this->assertSame(
! empty( $value ),
$comp->{$isMethod}(),
self::getErrMsg( null, $case . '-1b1', __FUNCTION__, $theComp, $isMethod )
. ', exp ' . empty( $value ) ? Vcalendar::FALSE : Vcalendar::TRUE
);
$getValue = $comp->{$getMethod}( null, true );
$getValue->removeParam( IcalInterface::ISLOCALTIME );
$this->assertEquals(
$expectedGet->params,
$getValue->params,
self::getErrMsg( null, $case . '-1b2', __FUNCTION__, $theComp, $isMethod )
);
if( ! empty( $expectedGet->value )) {
$expVal = $expectedGet->value;
switch( true ) {
case $expectedGet->hasParamValue(IcalInterface::DATE ) :
$fmt = DateTimeFactory::$Ymd;
break;
case $getValue->hasParamKey( IcalInterface::ISLOCALTIME ) :
$fmt = DateTimeFactory::$YmdHis;
break;
default :
$fmt = DateTimeFactory::$YMDHISe;
break;
}
while( is_array( $expVal ) && ! $expVal instanceof DateTime ) {
$expVal = reset( $expVal );
}
$expGet = $expVal->format( $fmt );
$getVal = reset( $getValue->value );
while( is_array( $getVal ) && ! $getVal instanceof DateTime ) { // exDate/Rdate
$getVal = reset( $getVal );
}
$getVal = $getVal->format( $fmt );
} // end if
else {
$expGet = $expectedGet;
$getVal = $getValue;
}
$this->assertEquals(
$expGet,
$getVal,
self::getErrMsg( null, $case . '-1b3', __FUNCTION__, $theComp, $getMethod )
);
$this->assertEquals(
strtoupper( $propName ) . $expectedString,
trim( $comp->{$createMethod}() ),
self::getErrMsg( null, $case . '-1b4', __FUNCTION__, $theComp, $createMethod )
);
$comp->{$deleteMethod}();
if( IcalInterface::DTSTAMP === $propName ) {
$this->assertNotFalse(
$comp->{$getMethod}(),
self::getErrMsg( '(after delete) ', $case . '-1b5', __FUNCTION__, $theComp, $getMethod )
);
}
else {
$this->assertFalse(
$comp->{$getMethod}(),
self::getErrMsg( '(after delete) ', $case . '-1b6', __FUNCTION__, $theComp, $getMethod )
);
}
$comp->{$setMethod}( $value, $params );
if( ! empty( $value ) && ! is_array( $value )) {
$comp->{$setMethod}( [ $value, $value ], $params ); // set aray of (single) values
}
} // end foreach
} // end foreach
$this->parseCalendarTest( $case, $c, $expectedString );
}
/**
* Testing calendar parse and (-re-)create, case prefix '-3x'
*
* @param int|string $case
* @param Vcalendar $calendar
* @param string|null $expectedString
* @param mixed|null $theComp
* @param string|null $propName
* @throws Exception
*/
public function parseCalendarTest(
int | string $case,
Vcalendar $calendar,
string $expectedString = null,
mixed $theComp = null,
string $propName = null
) : void
{
static $xmlStartChars = "\n